feat(system): Gemini CLI integration: GEMINI.md, hooks, skills

Co-Authored-By: @devpulse <devpulse@aipass>
This commit is contained in:
AIOSAI
2026-04-05 21:41:52 -07:00
co-authored by @devpulse
parent 6df129c672
commit 3361ee90ef
6 changed files with 351 additions and 0 deletions
+44
View File
@@ -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()
+97
View File
@@ -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()
+86
View File
@@ -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()
+28
View File
@@ -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.
+56
View File
@@ -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.
```
+40
View File
@@ -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.