diff --git a/.gemini/hooks/pre_edit_gate.py b/.gemini/hooks/pre_edit_gate.py new file mode 100755 index 00000000..a76ce3f1 --- /dev/null +++ b/.gemini/hooks/pre_edit_gate.py @@ -0,0 +1,44 @@ +#!/usr/bin/env python3 +"""Gemini BeforeTool hook: gate file edits to protect critical files.""" +import json +import sys + + +PROTECTED_PATTERNS = [ + ".trinity/passport.json", + ".aipass/registry.json", + "setup.sh", +] + + +def main(): + try: + input_data = json.loads(sys.stdin.read()) + except Exception: + print(json.dumps({})) + return + + tool_input = input_data.get("input", {}) + file_path = tool_input.get("file_path", "") or tool_input.get("path", "") + + if not file_path: + print(json.dumps({})) + return + + for pattern in PROTECTED_PATTERNS: + if pattern in file_path: + output = { + "hookSpecificOutput": { + "hookEventName": "BeforeTool", + "permissionDecision": "deny" + }, + "systemMessage": f"Edit blocked: {pattern} is a protected file." + } + print(json.dumps(output)) + return + + print(json.dumps({})) + + +if __name__ == "__main__": + main() diff --git a/.gemini/hooks/prompt_inject.py b/.gemini/hooks/prompt_inject.py new file mode 100755 index 00000000..ef683fe0 --- /dev/null +++ b/.gemini/hooks/prompt_inject.py @@ -0,0 +1,97 @@ +#!/usr/bin/env python3 +"""Gemini BeforeModel hook: inject per-turn AIPass context.""" +import json +import sys +from datetime import datetime +from pathlib import Path + + +def find_repo_root(): + p = Path.cwd() + while p != p.parent: + if (p / ".git").exists(): + return p + p = p.parent + return None + + +def get_branch_from_cwd(repo_root): + cwd = Path.cwd() + try: + rel = cwd.relative_to(repo_root / "src" / "aipass") + return str(rel).split("/")[0] + except ValueError: + try: + rel = cwd.relative_to(repo_root / "src") + return str(rel).split("/")[0] + except ValueError: + return None + + +def main(): + try: + input_data = json.loads(sys.stdin.read()) + except Exception: + input_data = {} + + repo_root = find_repo_root() + if not repo_root: + print(json.dumps({})) + return + + context_parts = [] + + now = datetime.now().strftime("%A, %B %-d %Y — %-I:%M %p") + context_parts.append(f"# Current Time: {now}") + + branch = get_branch_from_cwd(repo_root) + if branch: + branch_dir = repo_root / "src" / "aipass" / branch + if not branch_dir.exists(): + branch_dir = repo_root / "src" / branch + + passport = branch_dir / ".trinity" / "passport.json" + if passport.exists(): + try: + data = json.loads(passport.read_text(encoding="utf-8")) + identity = data.get("identity", {}) + traits = data.get("traits", []) + context_parts.append( + f"# {branch.upper()} Identity\n" + f"Path: {data.get('branch_info', {}).get('path', 'unknown')}\n" + f"Role: {identity.get('role', 'unknown')}\n" + f"Traits: {' | '.join(traits)}\n" + f"Purpose: {identity.get('purpose', 'unknown')}" + ) + except Exception: + pass + + inbox = branch_dir / ".ai_mail.local" / "inbox.json" + if inbox.exists(): + try: + mail = json.loads(inbox.read_text(encoding="utf-8")) + unread = mail.get("unread_count", 0) + if unread > 0: + context_parts.append( + f"You have {unread} new emails - check with: " + f"drone @ai_mail inbox" + ) + except Exception: + pass + + if context_parts: + context = "\n\n".join(context_parts) + output = { + "hookSpecificOutput": { + "hookEventName": "BeforeModel", + "additionalContext": context + } + } + else: + output = {} + + print(json.dumps(output)) + + +if __name__ == "__main__": + main() diff --git a/.gemini/hooks/session_start_identity.py b/.gemini/hooks/session_start_identity.py new file mode 100755 index 00000000..9707bf9e --- /dev/null +++ b/.gemini/hooks/session_start_identity.py @@ -0,0 +1,86 @@ +#!/usr/bin/env python3 +"""Gemini SessionStart hook: inject AIPass identity context.""" +import json +import sys +from pathlib import Path + + +def find_repo_root(): + p = Path.cwd() + while p != p.parent: + if (p / ".git").exists(): + return p + p = p.parent + return None + + +def get_branch_from_cwd(repo_root): + cwd = Path.cwd() + try: + rel = cwd.relative_to(repo_root / "src" / "aipass") + return str(rel).split("/")[0] + except ValueError: + try: + rel = cwd.relative_to(repo_root / "src") + return str(rel).split("/")[0] + except ValueError: + return None + + +def main(): + try: + input_data = json.loads(sys.stdin.read()) + except Exception: + input_data = {} + + repo_root = find_repo_root() + if not repo_root: + print(json.dumps({})) + return + + context_parts = [] + + global_prompt = repo_root / ".aipass" / "aipass_global_prompt.md" + if global_prompt.exists(): + context_parts.append(global_prompt.read_text(encoding="utf-8")[:8000]) + + branch = get_branch_from_cwd(repo_root) + if branch: + branch_dir = repo_root / "src" / "aipass" / branch + if not branch_dir.exists(): + branch_dir = repo_root / "src" / branch + + passport = branch_dir / ".trinity" / "passport.json" + if passport.exists(): + try: + data = json.loads(passport.read_text(encoding="utf-8")) + identity = data.get("identity", {}) + context_parts.append( + f"# Branch Identity: {branch.upper()}\n" + f"Role: {identity.get('role', 'unknown')}\n" + f"Purpose: {identity.get('purpose', 'unknown')}\n" + f"Class: {identity.get('citizen_class', 'unknown')}" + ) + except Exception: + pass + + branch_prompt = branch_dir / ".aipass" / "aipass_local_prompt.md" + if branch_prompt.exists(): + context_parts.append(branch_prompt.read_text(encoding="utf-8")[:4000]) + + if context_parts: + context = "\n\n---\n\n".join(context_parts) + output = { + "hookSpecificOutput": { + "hookEventName": "SessionStart", + "additionalContext": context + } + } + else: + output = {} + + print(json.dumps(output)) + + +if __name__ == "__main__": + main() diff --git a/.gemini/skills/memo/SKILL.md b/.gemini/skills/memo/SKILL.md new file mode 100644 index 00000000..558fffaf --- /dev/null +++ b/.gemini/skills/memo/SKILL.md @@ -0,0 +1,28 @@ +--- +name: memo +description: Update branch memory files after completing work. Saves session history, key learnings, and collaboration observations to .trinity/ files. +--- + +# Memory Update + +Purpose: Update branch memory files after completing work this session. + +## Execution + +1. Read `.trinity/passport.json` first — re-absorb your identity, role, and principles before writing memories +2. Review what was done this session (context, recent changes, key decisions) +3. Update each file below as needed +4. Confirm completion — list files updated + +## What to Update + +### Always + +- **.trinity/local.json** — Add new session entry to `sessions` if significant work was done. Add new `key_learnings` for facts you'd need next time. Trim oldest sessions if over 20. +- **.trinity/observations.json** — Add notable collaboration insights: breakthrough moments, pattern corrections, flow states, friction points, preference discoveries. Skip if nothing notable this session. + +### If Relevant + +- **.trinity/passport.json** — Evolve identity when the branch's role, capabilities, or principles have genuinely changed. Don't update just to update — but don't leave placeholders forever either. +- **README.md** — Does it reflect current state? Update if stale. +- **STATUS.local.md** — Drop quick notes on issues, todos, or ideas in the Notepad section. diff --git a/.gemini/skills/prep/SKILL.md b/.gemini/skills/prep/SKILL.md new file mode 100644 index 00000000..61d2ad16 --- /dev/null +++ b/.gemini/skills/prep/SKILL.md @@ -0,0 +1,56 @@ +--- +name: prep +description: Session wrap-up. Update memories, check plans, review git state, check inbox, flag loose ends. Use before closing a session or compacting context. +--- + +# Session Wrap-Up + +Purpose: Button up everything at the end of a session — or before context compaction. Memories, plans, git — all tidy. + +## Execution + +1. Read `.trinity/passport.json` first — re-absorb your identity before writing anything +2. Do ALL of the following, then confirm what was updated + +## 1. Memories + +- **.trinity/local.json** — Add/update session entry with summary of work done. Add new key_learnings for anything learned this session. Trim oldest sessions if over 20. +- **.trinity/observations.json** — Add collaboration insights if anything notable happened. Skip if nothing new. +- **.trinity/passport.json** — Only update if role/purpose/principles genuinely changed this session. + +## 2. Active Plans + +- Check any DPLANs or FPLANs referenced in this session +- Update their execution logs, status, decision logs with current state +- If a plan was completed, note it (but don't close — the user does that) + +## 3. Git State + +- Run `git status` — report uncommitted changes +- If there's a logical commit waiting, suggest it (don't commit without asking) +- Note the current branch and any open PRs + +## 4. Inbox + +- Run `drone @ai_mail inbox 2>/dev/null` — report any unread emails +- Close any that were already processed but not formally closed + +## 5. Loose Ends + +- Flag anything in-flight: running background agents, dispatched branches waiting for replies, pending decisions +- If anything can't survive compaction, write it to STATUS.local.md Notepad + +## Confirm + +List everything updated. Format: +``` +Prep complete: +- local.json: [what was added] +- observations.json: [updated / skipped] +- Plans: [which ones updated] +- Git: [branch, uncommitted count, suggestion] +- Inbox: [count, action taken] +- Loose ends: [any flagged] + +Ready to close out or compact. +``` diff --git a/GEMINI.md b/GEMINI.md new file mode 100644 index 00000000..11f63545 --- /dev/null +++ b/GEMINI.md @@ -0,0 +1,40 @@ +# AIPass + +A multi-agent framework where autonomous citizens live in branches and deploy disposable agents to do work. + +## Branches + +Every branch follows this structure: +``` +src/aipass/{name}/ +├── .trinity/ # Identity & memory (passport.json, local.json, observations.json) +├── .aipass/ # System prompt +├── .ai_mail.local/ # Mailbox (inbox.json, sent/) +├── apps/ +│ ├── {name}.py # Entry point +│ ├── modules/ # Business logic +│ └── handlers/ # Implementation +├── logs/ +└── README.md +``` + +15 branches: drone, seedgo, prax, cli, flow, ai_mail, api, trigger, spawn, devpulse, backup, daemon, memory, commons, skills + +## Commands + +drone systems # List all branches +drone @seedgo audit aipass # Run standards audit +drone @ai_mail inbox # Check email +drone @ai_mail email @target "Subject" "Body" # Send email +drone @flow list open # Active plans + +## Identity + +Read .trinity/passport.json to understand your role. Read .trinity/local.json for session history. Read STATUS.local.md for current work. + +## Key Principles + +- Code is truth. Running code beats architecture. +- Memory is everything. Update .trinity/ often. +- Dispatch, don't do. Branches are experts in their domain. +- Fail honestly. Errors over silent fallbacks.