Skip to content
Data
Hook

Hooks

What omega-memory runs automatically, and when. A hook is a command Claude Code fires at a fixed moment, without you asking for it.

From plugin
omega-memory
1981 skill22 hooks1 MCP
Install
$ npx -y skills add omega-memory/omega-memory --agent claude-code

Ships with omega-memory. Installing the plugin gets these hooks.

Where it lives

  • hooks/assistant_capture.pyGitHub
    Read the script
    #!/usr/bin/env python3
    """OMEGA Stop hook fallback — capture high-value assistant responses.
    
    Fires on every Stop event when the hook daemon is unavailable.
    Detects fix, decision, lesson, and recommendation patterns in
    ``last_assistant_message`` and stores them via bridge.auto_capture.
    
    This is the cold-path fallback. The fast path routes through the hook
    daemon to ``handle_assistant_capture`` in the hook_server package.
    """
    import json
    import re
    import sys
    
    # Patterns (mirrors assistant.py in hook_server)
    FIX_PATTERNS = [
        r"the (?:fix|issue|problem|bug) was\b",
        r"root cause (?:was|is)\b",
        r"the error (?:occurred|happens|was caused) because\b",
        r"fixed (?:by|this by)\b",
    ]
    
    DECISION_PATTERNS = [
        r"(?:decided|choosing) to\b",
        r"going with\b",
        r"switched to\b",
        r"using \S+ instead of\b",
        r"chose \S+ because\b",
    ]
    
    LESSON_PATTERNS = [
        r"(?:note|notice) that\b",
        r"important:\s",
        r"be careful\b",
        r"gotcha:\s",
        r"caveat:\s",
        r"key takeaway\b",
    ]
    
    MIN_MESSAGE_LENGTH = 200
    MIN_CONTENT_CHARS = 40
    MIN_CONTENT_WORDS = 8
    
    _FENCED_CODE_RE = re.compile(r"```[\s\S]*?```", re.DOTALL)
    _SENTENCE_SPLIT_RE = re.compile(r"(?<=[.!?])\s+|\n+")
    _INSIGHT_OPEN_RE = re.compile(r"[★✦]\s*Insight\s*─+", re.IGNORECASE)
    _INSIGHT_CLOSE_RE = re.compile(r"─{10,}")
    
    _captured_count = 0
    MAX_CAPTURES = 10
    
    
    def _clean(text):
        return _FENCED_CODE_RE.sub("", text).strip()
    
    
    def _find_match(text, patterns):
        for pat in patterns:
            compiled = re.compile(pat, re.IGNORECASE)
            for sentence in _SENTENCE_SPLIT_RE.split(text):
                sentence = sentence.strip()
                if compiled.search(sentence):
                    if len(sentence) >= MIN_CONTENT_CHARS and len(sentence.split()) >= MIN_CONTENT_WORDS:
                        return sentence
        return None
    
    
    def _extract_insight_blocks(text):
        """Extract ★ Insight delimited blocks from assistant text."""
        blocks = []
        search_start = 0
        while True:
            open_match = _INSIGHT_OPEN_RE.search(text, search_start)
            if not open_match:
                break
            body_start = open_match.end()
            close_match = _INSIGHT_CLOSE_RE.search(text, body_start)
            if not close_match:
                body = text[body_start:body_start + 2000].strip()
            else:
                body = text[body_start:close_match.start()].strip()
            if body and len(body) >= MIN_CONTENT_CHARS:
                blocks.append(body[:2000])
            search_start = close_match.end() if close_match else len(text)
        return blocks
    
    
    def main(data=None):
        global _captured_count
        if _captured_count >= MAX_CAPTURES:
            return
    
        if data is None:
            try:
                raw = sys.stdin.read()
                if not raw.strip():
                    return
                data = json.loads(raw)
            except (json.JSONDecodeError, Exception):
                return
    
        message = data.get("last_assistant_message", "")
        if not message or len(message) < MIN_MESSAGE_LENGTH:
            return
    
        session_id = data.get("session_id", "")
        cwd = data.get("cwd", data.get("project", ""))
    
        # Pre-pass: detect ★ Insight delimited blocks
        insight_blocks = _extract_insight_blocks(message)
        if insight_blocks and _captured_count < MAX_CAPTURES:
            for block in insight_blocks:
                if _captured_count >= MAX_CAPTURES:
                    break
                try:
                    from omega.bridge import auto_capture
    
                    auto_capture(
                        content=f"Insight: {block}",
                        event_type="advisor_insight",
                        metadata={"source": "assistant_capture_hook", "project": cwd, "capture_confidence": "high", "category": "system_insight"},
                        session_id=session_id,
                        project=cwd,
                    )
                    _captured_count += 1
                    preview = block[:80].replace("\n", " ").strip()
                    print(f"[LEARNED] insight: {preview}")
                except ImportError:
                    pass
                except Exception:
                    pass
            return
    
        cleaned = _clean(message)
        if not cleaned:
            return
    
        # Try pattern groups in priority order
        for label, event_type, patterns in [
            ("fix", "lesson_learned", FIX_PATTERNS),
            ("decision", "decision", DECISION_PATTERNS),
            ("lesson", "lesson_learned", LESSON_PATTERNS),
        ]:
            content = _find_match(cleaned, patterns)
            if content:
                try:
                    from omega.bridge import auto_capture
    
                    auto_capture(
                        content=f"Assistant {label}: {content[:500]}",
                        event_type=event_type,
                        metadata={"source": "assistant_capture_hook", "project": cwd},
                        session_id=session_id,
                        project=cwd,
                    )
                    _captured_count += 1
                    preview = content[:80].replace("\n", " ").strip()
                    print(f"[LEARNED] {label}: {preview}")
                except ImportError:
                    pass
                except Exception:
                    pass
                return
    
    
    if __name__ == "__main__":
        main()
    
  • hooks/auto_capture.pyGitHub
    Read the script
    #!/usr/bin/env python3
    """OMEGA UserPromptSubmit hook — Auto-capture decisions and lessons from user prompts.
    
    Fires on every user prompt. Detects decision and lesson patterns and stores them
    as 'decision' or 'lesson_learned' event type in OMEGA memory. Uses conservative
    matching to avoid noise.
    """
    import json
    import re
    import sys
    
    
    # Decision indicators (case-insensitive patterns)
    DECISION_PATTERNS = [
        r"\blet'?s?\s+(?:go\s+with|use|switch\s+to|stick\s+with|move\s+to)\b",
        r"\bi\s+(?:decided?|chose|picked|went\s+with|prefer)\b",
        r"\bwe\s+(?:should|will|are\s+going\s+to)\s+(?:use|go\s+with|switch|adopt|implement)\b",
        r"\b(?:decision|approach|strategy):\s*\S",
        r"\binstead\s+of\s+\S+[,\s]+(?:use|let'?s|we'?ll)\b",
        r"\bfrom\s+now\s+on\b",
        r"\bremember\s+(?:that|this)\b",
    ]
    
    # Lesson indicators (case-insensitive patterns)
    LESSON_PATTERNS = [
        r"\bi\s+learned\s+that\b",
        r"\bturns?\s+out\b",
        r"\bthe\s+trick\s+is\b",
        r"\bnote\s+to\s+self\b",
        r"\btil\b|\btoday\s+i\s+learned\b",
        r"\bthe\s+fix\s+was\b",
        r"\bthe\s+problem\s+was\b",
        r"\bdon'?t\s+forget\b",
        r"\bimportant:\s*\S",
        r"\bkey\s+(?:insight|takeaway|learning)\b",
        r"\bnever\s+(?:again|do|use)\b",
        r"\balways\s+(?:make\s+sure|remember|check)\b",
    ]
    
    # Minimum prompt length to avoid matching on short commands
    MIN_PROMPT_LENGTH = 20
    
    # Maximum prompts to process per session (avoid runaway storage)
    _captured_count = 0
    MAX_CAPTURES_PER_SESSION = 20
    
    
    def _summarize_content(prompt: str, max_len: int = 60) -> str:
        """Extract a concise summary from the prompt for the echo line."""
        # Strip common prefixes like "Decision: " or "Lesson: "
        text = re.sub(r"^(Decision|Lesson):\s*", "", prompt, flags=re.IGNORECASE).strip()
        # Take first sentence or first max_len chars
        first_sentence = re.split(r"[.!?\n]", text)[0].strip()
        if len(first_sentence) <= max_len:
            return first_sentence
        return first_sentence[:max_len].rsplit(" ", 1)[0] + "..."
    
    
    def _echo_capture(result: str, event_type: str, prompt: str):
        """Print a 1-line capture confirmation visible to the user.
    
        Parses bridge.auto_capture() return value to distinguish:
        - New capture → [OMEGA] Captured: decision about X
        - Evolution   → [OMEGA] Memory evolved: added insight to existing memory
        - Dedup/Block → silent (no output)
        """
        if not result:
            return
    
        summary = _summarize_content(prompt)
    
        if "Memory Evolved" in result:
            # Extract evolution number from "Evolution #N"
            evo_match = re.search(r"Evolution #(\d+)", result)
            evo_num = evo_match.group(1) if evo_match else "?"
            print(f"[OMEGA] Memory evolved: {event_type} updated (evolution #{evo_num}) — {summary}")
        elif "Memory Captured" in result:
            print(f"[OMEGA] Captured: {event_type} — {summary}")
        # Dedup/Blocked → stay silent
    
    
    def _detect_decision(prompt: str) -> bool:
        """Check if prompt contains a decision pattern."""
        if len(prompt) < MIN_PROMPT_LENGTH:
            return False
        prompt_lower = prompt.lower()
        return any(re.search(pat, prompt_lower) for pat in DECISION_PATTERNS)
    
    
    def _detect_lesson(prompt: str) -> bool:
        """Check if prompt contains a lesson/insight pattern."""
        if len(prompt) < MIN_PROMPT_LENGTH:
            return False
        prompt_lower = prompt.lower()
        return any(re.search(pat, prompt_lower) for pat in LESSON_PATTERNS)
    
    
    def main():
        global _captured_count
        if _captured_count >= MAX_CAPTURES_PER_SESSION:
            return
    
        # Read hook input from stdin
        try:
            raw = sys.stdin.read()
            if not raw.strip():
                return
            data = json.loads(raw)
        except (json.JSONDecodeError, Exception):
            return
    
        prompt = data.get("prompt", "")
        session_id = data.get("session_id", "")
        cwd = data.get("cwd", "")
    
        if not prompt:
            return
    
        # Decision takes priority if both match
        if _detect_decision(prompt):
            try:
                from omega.bridge import auto_capture
                result = auto_capture(
                    content=f"Decision: {prompt[:500]}",
                    event_type="decision",
                    metadata={"source": "auto_capture_hook", "project": cwd},
                    session_id=session_id,
                    project=cwd,
                )
                _captured_count += 1
                _echo_capture(result, "decision", prompt)
            except ImportError:
                pass
            except Exception:
                pass
            return
    
        if _detect_lesson(prompt):
            # Lesson quality gate: min 60 chars, >= 8 words, substance validation
            if len(prompt) < 60 or len(prompt.split()) < 8:
                return
            _tech_signals = ["/", "`", "Error", "error", ".py", ".js", ".ts", "import ", "def ", "class "]
            if len(prompt) < 100 and not any(s in prompt for s in _tech_signals):
                return
    
            try:
                from omega.bridge import auto_capture
                result = auto_capture(
                    content=f"Lesson: {prompt[:500]}",
                    event_type="lesson_learned",
                    metadata={"source": "auto_capture_hook", "project": cwd},
                    session_id=session_id,
                    project=cwd,
                )
                _captured_count += 1
                _echo_capture(result, "lesson", prompt)
            except ImportError:
                pass
            except Exception:
                pass
    
    
    if __name__ == "__main__":
        main()
    
  • hooks/auto_claim_file.pyGitHub
    Read the script
    #!/usr/bin/env python3
    """OMEGA Coordination PostToolUse hook — Auto-claim files on Edit/Write.
    
    Fires after Edit|Write. Automatically claims the edited file so other agents
    see it as taken, without requiring explicit omega_file_claim calls.
    """
    import json
    import os
    import traceback
    from datetime import datetime
    from pathlib import Path
    
    
    def _log_hook_error(hook_name, error):
        try:
            log_path = Path.home() / ".omega" / "hooks.log"
            log_path.parent.mkdir(parents=True, exist_ok=True, mode=0o700)
            timestamp = datetime.now().isoformat(timespec="seconds")
            tb = traceback.format_exc()
            data = f"[{timestamp}] {hook_name}: {error}\n{tb}\n"
            fd = os.open(str(log_path), os.O_WRONLY | os.O_CREAT | os.O_APPEND, 0o600)
            try:
                os.write(fd, data.encode("utf-8"))
            finally:
                os.close(fd)
        except Exception:
            pass
    
    
    def main():
        tool_name = os.environ.get("TOOL_NAME", "")
    
        session_id = os.environ.get("SESSION_ID", "")
        if not session_id:
            return
    
        try:
            input_data = json.loads(os.environ.get("TOOL_INPUT", "{}"))
        except (json.JSONDecodeError, TypeError):
            return
    
        file_path = input_data.get("file_path", input_data.get("notebook_path", ""))
        if not file_path:
            return
    
        # On Read: warn if the file is claimed by another agent (no claim, just visibility)
        if tool_name == "Read":
            try:
                from omega.coordination import get_manager
                mgr = get_manager()
                info = mgr.check_file(file_path)
                if info.get("claimed") and info.get("session_id") != session_id:
                    owner = info.get("session_id", "unknown")[:16]
                    owner_task = info.get("task") or "unknown task"
                    print(
                        f"[FILE-INFO] {os.path.basename(file_path)} is being edited by "
                        f"session {owner} ({owner_task}). Coordinate before modifying."
                    )
            except (ImportError, Exception):
                pass
            return
    
        # On Edit/Write/NotebookEdit: auto-claim
        if tool_name not in ("Edit", "Write", "NotebookEdit"):
            return
    
        try:
            from omega.coordination import get_manager
            mgr = get_manager()
            result = mgr.claim_file(session_id, file_path, task="auto-claimed on edit")
            if result.get("conflict"):
                owner = result["claimed_by"][:20]
                owner_task = result.get("task") or "unknown task"
                print(
                    f"[CONFLICT] {os.path.basename(file_path)} is claimed by session "
                    f"{owner} ({owner_task}). Coordinate before editing."
                )
            elif result.get("success"):
                # Auto-announce intent for coordination visibility
                try:
                    mgr.announce_intent(
                        session_id=session_id,
                        description=f"Editing {os.path.basename(file_path)}",
                        intent_type="edit",
                        target_files=[file_path],
                        ttl_minutes=5,
                    )
                except Exception:
                    pass  # Intent announcement is best-effort
        except ImportError:
            pass
        except Exception as e:
            error_str = str(e)
            if "already claimed" not in error_str.lower():
                _log_hook_error("auto_claim_file", e)
    
    
    if __name__ == "__main__":
        main()
    
  • hooks/coord_heartbeat.pyGitHub
    Read the script
    #!/usr/bin/env python3
    """OMEGA Coordination PostToolUse hook — Update session heartbeat."""
    import os
    import time
    import traceback
    from datetime import datetime
    from pathlib import Path
    
    
    def _log_hook_error(hook_name, error):
        try:
            log_path = Path.home() / ".omega" / "hooks.log"
            log_path.parent.mkdir(parents=True, exist_ok=True, mode=0o700)
            timestamp = datetime.now().isoformat(timespec="seconds")
            tb = traceback.format_exc()
            data = f"[{timestamp}] {hook_name}: {error}\n{tb}\n"
            fd = os.open(str(log_path), os.O_WRONLY | os.O_CREAT | os.O_APPEND, 0o600)
            try:
                os.write(fd, data.encode("utf-8"))
            finally:
                os.close(fd)
        except Exception:
            pass
    
    
    def main():
        session_id = os.environ.get("SESSION_ID", "")
        if not session_id:
            return
    
        try:
            from omega.coordination import get_manager
            mgr = get_manager()
            mgr.heartbeat(session_id)
        except ImportError:
            pass
        except Exception as e:
            _log_hook_error("coord_heartbeat", e)
    
    
    def _log_timing(hook_name, elapsed_ms):
        try:
            log_path = Path.home() / ".omega" / "hooks.log"
            log_path.parent.mkdir(parents=True, exist_ok=True, mode=0o700)
            timestamp = datetime.now().isoformat(timespec="seconds")
            data = f"[{timestamp}] {hook_name}: OK ({elapsed_ms:.0f}ms)\n"
            fd = os.open(str(log_path), os.O_WRONLY | os.O_CREAT | os.O_APPEND, 0o600)
            try:
                os.write(fd, data.encode("utf-8"))
            finally:
                os.close(fd)
        except Exception:
            pass
    
    
    if __name__ == "__main__":
        _t0 = time.monotonic()
        main()
        _log_timing("coord_heartbeat", (time.monotonic() - _t0) * 1000)
    
  • hooks/coord_session_start.pyGitHub
    Read the script
    #!/usr/bin/env python3
    """OMEGA Coordination SessionStart hook — Register agent session."""
    import os
    import time
    import traceback
    from datetime import datetime, timezone
    from pathlib import Path
    
    
    def _log_hook_error(hook_name, error):
        try:
            log_path = Path.home() / ".omega" / "hooks.log"
            log_path.parent.mkdir(parents=True, exist_ok=True, mode=0o700)
            timestamp = datetime.now().isoformat(timespec="seconds")
            tb = traceback.format_exc()
            data = f"[{timestamp}] {hook_name}: {error}\n{tb}\n"
            fd = os.open(str(log_path), os.O_WRONLY | os.O_CREAT | os.O_APPEND, 0o600)
            try:
                os.write(fd, data.encode("utf-8"))
            finally:
                os.close(fd)
        except Exception:
            pass
    
    
    def _kill_orphaned_mcp_servers():
        """Kill OMEGA MCP server processes whose parent has exited (PPID=1)."""
        import signal
        import subprocess
    
        try:
            # Find all omega MCP server processes
            result = subprocess.run(
                ["pgrep", "-f", "omega.server.mcp_server"],
                capture_output=True, text=True, timeout=5,
            )
            if result.returncode != 0 or not result.stdout.strip():
                return
    
            my_pid = os.getpid()
            pids = [int(p) for p in result.stdout.strip().split("\n") if p.strip()]
    
            killed = 0
            for pid in pids:
                if pid == my_pid:
                    continue
                # Check if this process is orphaned (PPID=1 on macOS means parent exited)
                ps_result = subprocess.run(
                    ["ps", "-o", "ppid=", "-p", str(pid)],
                    capture_output=True, text=True, timeout=5,
                )
                if ps_result.returncode != 0:
                    continue
                ppid = ps_result.stdout.strip()
                if ppid == "1":
                    try:
                        os.kill(pid, signal.SIGTERM)
                        killed += 1
                    except ProcessLookupError:
                        pass
                    except PermissionError:
                        pass
    
            if killed > 0:
                _log_hook_error("orphan_cleanup", f"Killed {killed} orphaned MCP server(s)")
    
        except subprocess.TimeoutExpired:
            pass
        except FileNotFoundError:
            pass  # pgrep not available
        except Exception as e:
            _log_hook_error("orphan_cleanup", e)
    
    
    def _clean_stale_socket():
        """Delete hook.sock if it exists but nothing is listening."""
        import socket as _socket
        sock_path = os.path.expanduser("~/.omega/hook.sock")
        if not os.path.exists(sock_path):
            return
        s = _socket.socket(_socket.AF_UNIX, _socket.SOCK_STREAM)
        s.settimeout(1.0)
        try:
            s.connect(sock_path)
            s.close()  # Socket is alive, leave it
        except (ConnectionRefusedError, OSError, _socket.timeout):
            try:
                os.unlink(sock_path)
            except OSError:
                pass
        finally:
            try:
                s.close()
            except Exception:
                pass
    
    
    def main():
        session_id = os.environ.get("SESSION_ID", "")
        project = os.environ.get("PROJECT_DIR", os.getcwd())
    
        if not session_id:
            return
    
        _clean_stale_socket()
        _kill_orphaned_mcp_servers()
    
        try:
            from omega.coordination import get_manager
            mgr = get_manager()
            mgr.list_sessions()  # Force-clean stale sessions (bypasses rate limit)
            result = mgr.register_session(
                session_id=session_id,
                pid=os.getppid(),
                project=project,
            )
            peers = result.get("peers_on_project", 0)
    
            # Rich peer roster (matches daemon quality)
            if peers > 0:
                print(f"[STANDUP] {peers} peer{'s' if peers != 1 else ''} active")
                _print_peer_roster(session_id, project, mgr)
    
            # Surface structured handoff from predecessor (takes priority)
            _surface_handoff(session_id, project, mgr)
    
            # Surface unread inbox messages (show content, not just count)
            if peers > 0:
                _surface_inbox(session_id, mgr)
    
            # Surface recent decisions from other sessions (catch-up mechanism)
            _surface_recent_peer_decisions(session_id, project)
    
            # Git sync check: detect upstream commits from uncoordinated agents
            # Run in background — git fetch is network I/O that shouldn't block session start
            import threading
            threading.Thread(
                target=_check_git_sync,
                args=(session_id, project, mgr),
                daemon=True,
            ).start()
    
            # Check for predecessor session snapshots
            _session_resume(session_id, project, mgr)
    
            # Surface running background processes (benchmarks, long scripts)
            _check_running_processes(project)
    
            # Surface pending tasks for this project, with staleness detection
            try:
                tasks = mgr.list_tasks(project=project, status="pending")
                if tasks:
                    # Cross-reference tasks against recent decisions to detect completed ones
                    stale_ids = set()
                    try:
                        from omega import bridge
                        for t in tasks[:5]:
                            title = t.get("title", "")
                            if not title:
                                continue
                            result_str = bridge.query(
                                title, limit=2, event_type="decision",
                                project=project,
                            )
                            if result_str and "No matching memories" not in result_str:
                                lower = result_str.lower()
                                if any(w in lower for w in [
                                    "committed", "shipped", "deployed", "done",
                                    "completed", "merged", "published", "live",
                                    "verified", "set up", "configured", "added",
                                ]):
                                    stale_ids.add(t["id"])
                    except Exce
  • hooks/coord_session_stop.pyGitHub
    Read the script
    #!/usr/bin/env python3
    """OMEGA Coordination Stop hook — Deregister session and release all claims."""
    import os
    import sys
    import time
    import traceback
    from datetime import datetime
    from pathlib import Path
    
    
    def _log_hook_error(hook_name, error):
        try:
            log_path = Path.home() / ".omega" / "hooks.log"
            log_path.parent.mkdir(parents=True, exist_ok=True, mode=0o700)
            timestamp = datetime.now().isoformat(timespec="seconds")
            tb = traceback.format_exc()
            data = f"[{timestamp}] {hook_name}: {error}\n{tb}\n"
            fd = os.open(str(log_path), os.O_WRONLY | os.O_CREAT | os.O_APPEND, 0o600)
            try:
                os.write(fd, data.encode("utf-8"))
            finally:
                os.close(fd)
        except Exception:
            pass
    
    
    def _broadcast_session_end(session_id, project, mgr):
        """Broadcast session summary to active peers before deregistering."""
        try:
            # Check if there are active peers to notify
            sessions = mgr.list_sessions(auto_clean=False)
            peers = [s for s in sessions if s.get("session_id") != session_id]
            if not peers:
                return
    
            # Build a compact summary of what this session did
            summary = _build_end_summary(session_id, project)
            if not summary:
                return
    
            # Broadcast to project (all active peers will see it in their inbox)
            mgr.send_message(
                from_session=session_id,
                subject=f"Session ended: {summary}",
                msg_type="complete",
                project=project,
                body=summary,
                ttl_minutes=120,  # 2 hours — enough for next session start
            )
        except Exception as e:
            _log_hook_error("broadcast_session_end", e)
    
    
    def _build_end_summary(session_id, project):
        """Build a compact summary of session activity for the broadcast."""
        try:
            from omega.bridge import query_structured
        except ImportError:
            return None
    
        decisions = query_structured(
            query_text="decisions made",
            limit=3,
            session_id=session_id,
            project=project,
            event_type="decision",
        )
    
        if not decisions:
            return None
    
        items = []
        for d in decisions[:3]:
            content = d.get("content", "")
            # Strip auto-capture prefixes
            for prefix in ("Plan/decision captured: ", "Decision: "):
                if content.startswith(prefix):
                    content = content[len(prefix):]
            # Skip JSON blobs
            stripped = content.lstrip()
            if stripped.startswith(("{", "[", '"filePath')):
                continue
            first_line = content.split("\n")[0].strip()
            if first_line and len(first_line) > 10:
                items.append(first_line[:120])
    
        if not items:
            return None
    
        return "; ".join(items)[:400]
    
    
    def _auto_handoff(session_id, project, mgr):
        """Auto-generate a structured handoff from session state. Best-effort."""
        try:
            from omega.bridge import query_structured
    
            # Gather decisions from this session
            decisions_raw = query_structured(
                query_text="decisions made",
                limit=5,
                session_id=session_id,
                project=project,
                event_type="decision",
            )
            decisions = []
            for d in (decisions_raw or []):
                content = d.get("content", "")
                for prefix in ("Plan/decision captured: ", "Decision: "):
                    if content.startswith(prefix):
                        content = content[len(prefix):]
                stripped = content.lstrip()
                if stripped.startswith(("{", "[", '"filePath')):
                    continue
                first_line = content.split("\n")[0].strip()
                if first_line and len(first_line) > 10:
                    decisions.append(first_line[:200])
                if len(decisions) >= 5:
                    break
    
            # Gather completed OMEGA tasks
            completed = []
            try:
                tasks = mgr.list_tasks(project=project, status="completed")
                for t in (tasks or [])[:5]:
                    if t.get("session_id") == session_id:
                        completed.append(t["title"])
            except Exception:
                pass
    
            mgr.create_handoff(
                session_id=session_id,
                project=project,
                completed_tasks=completed or None,
                decisions_made=decisions or None,
            )
        except ImportError:
            pass
        except Exception as e:
            _log_hook_error("auto_handoff", e)
    
    
    def _nudge_handoff(session_id, project, mgr):
        """Nudge agent about incomplete work being orphaned."""
        try:
            all_tasks = mgr.list_tasks(project=project, status="in_progress")
            my_tasks = [t for t in all_tasks if t.get("session_id") == session_id]
            if my_tasks:
                task_list = ", ".join(f"#{t['id']} {t['title']}" for t in my_tasks[:3])
                print(
                    f"[HANDOFF] Active work returned to queue: {task_list}\n"
                    "  Next time, use omega_handoff(action='create', ...) before ending "
                    "to give your successor structured context."
                )
        except Exception as e:
            _log_hook_error("nudge_handoff", e)
    
    
    def _extract_project_entity(file_path):
        """Extract project name from a file path."""
        import re
        match = re.search(r'/Projects/([^/]+)', file_path)
        return match.group(1) if match else None
    
    
    def _build_entity_links(claims, current_project):
        """Build cross-project entity links from file claims."""
        projects = set()
        for claim in claims:
            proj = _extract_project_entity(claim.get("file_path", ""))
            if proj and proj != current_project:
                projects.add(proj)
    
        return [
            {"from": current_project, "to": proj, "relationship": "depends_on"}
            for proj in sorted(projects)
        ]
    
    
    def _detect_drift(original_task, files_modified, commits):
        """Detect if session work drifted from declared task via keyword overlap."""
        if n
  • hooks/fast_hook.pyGitHub
  • hooks/post_edit_test.pyGitHub
  • hooks/pre_alignment_gate.pyGitHub
  • hooks/pre_commit_guard.pyGitHub
  • hooks/pre_deploy_guard.pyGitHub
  • hooks/pre_edit_surface.pyGitHub
  • hooks/pre_file_guard.pyGitHub
  • hooks/pre_protocol_gate.pyGitHub
  • hooks/pre_push_guard.pyGitHub
  • hooks/pre_review.pyGitHub
  • hooks/pre_task_guard.pyGitHub
  • hooks/session_start.pyGitHub
  • hooks/session_stop.pyGitHub
  • hooks/surface_memories.pyGitHub
  • hooks/trace_capture.pyGitHub
  • hooks/track_file_read.pyGitHub

All 22 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.

Ships withomega-memory

Cross-model memory for AI agents. Local-first. Works with Claude, GPT, Gemini, Cursor, Claw Code, and any MCP client. Your agent's brain shouldn't live on someone else's server, or be locked to one provider.

Get the whole plugin
Stats
198
Stars
27
Forks
Active
Maintenance
Python
Language
Apache-2.0
License
1d ago
Last commit
5mo ago
Created

Repo: omega-memory/omega-memory