feat(system): AGENTS.md/GEMINI.md startup instructions + prax Codex branch detection from CWD (#192)
Co-authored-by: @devpulse <devpulse@aipass>
This commit is contained in:
@@ -28,9 +28,19 @@ drone @ai_mail inbox # Check email
|
||||
drone @ai_mail email @target "Subject" "Body" # Send email
|
||||
drone @flow list open # Active plans
|
||||
|
||||
## Startup
|
||||
|
||||
On any greeting or first message, silently read these files and respond with status:
|
||||
- `.trinity/passport.json` — your identity, role, purpose, principles
|
||||
- `.trinity/local.json` — session history and key learnings
|
||||
- `STATUS.local.md` — current work, issues, todos
|
||||
- Check if `.ai_mail.local/inbox.json` exists — if so, read it and process any mail
|
||||
|
||||
Your identity and branch context are also injected via hooks on session start and every prompt. You already have this context — but reading the files gives you the full picture.
|
||||
|
||||
## Identity
|
||||
|
||||
Read .trinity/passport.json to understand your role. Read .trinity/local.json for session history. Read STATUS.local.md for current work.
|
||||
You are a citizen of AIPass. Your `.trinity/passport.json` defines who you are. Read it first — before writing anything, before making decisions. Your role, purpose, and principles are in that file.
|
||||
|
||||
## Key Principles
|
||||
|
||||
|
||||
@@ -28,9 +28,19 @@ drone @ai_mail inbox # Check email
|
||||
drone @ai_mail email @target "Subject" "Body" # Send email
|
||||
drone @flow list open # Active plans
|
||||
|
||||
## Startup
|
||||
|
||||
On any greeting or first message, silently read these files and respond with status:
|
||||
- `.trinity/passport.json` — your identity, role, purpose, principles
|
||||
- `.trinity/local.json` — session history and key learnings
|
||||
- `STATUS.local.md` — current work, issues, todos
|
||||
- Check if `.ai_mail.local/inbox.json` exists — if so, read it and process any mail
|
||||
|
||||
Your identity and branch context are also injected via hooks on session start and every prompt. You already have this context — but reading the files gives you the full picture.
|
||||
|
||||
## Identity
|
||||
|
||||
Read .trinity/passport.json to understand your role. Read .trinity/local.json for session history. Read STATUS.local.md for current work.
|
||||
You are a citizen of AIPass. Your `.trinity/passport.json` defines who you are. Read it first — before writing anything, before making decisions. Your role, purpose, and principles are in that file.
|
||||
|
||||
## Key Principles
|
||||
|
||||
|
||||
@@ -72,6 +72,10 @@ class MonitoringFileHandler(FileSystemEventHandler):
|
||||
self._last_agent_action: Dict[str, str] = {}
|
||||
# Track model per session file for display tags
|
||||
self._session_models: Dict[str, str] = {}
|
||||
# Track detected branch per CLI session file (from session metadata)
|
||||
self._session_branches: Dict[str, str] = {}
|
||||
# Track files we've already warned about for branch detection
|
||||
self._branch_warn_logged: set = set()
|
||||
|
||||
# -- public property so the module can swap queues after construction ------
|
||||
@property
|
||||
@@ -191,6 +195,84 @@ class MonitoringFileHandler(FileSystemEventHandler):
|
||||
|
||||
return None
|
||||
|
||||
# =========================================================================
|
||||
# CLI SESSION BRANCH DETECTION
|
||||
# =========================================================================
|
||||
|
||||
@staticmethod
|
||||
def _branch_from_cwd(cwd: str) -> Optional[str]:
|
||||
"""Extract branch name from a CWD path like /path/to/src/aipass/devpulse."""
|
||||
if not cwd:
|
||||
return None
|
||||
parts = Path(cwd).parts
|
||||
if 'aipass' in parts:
|
||||
idx = parts.index('aipass')
|
||||
if idx + 1 < len(parts):
|
||||
return parts[idx + 1].upper()
|
||||
# Fallback: check src/{name} for branches outside aipass namespace
|
||||
if 'src' in parts:
|
||||
idx = parts.index('src')
|
||||
if idx + 1 < len(parts):
|
||||
return parts[idx + 1].upper()
|
||||
return None
|
||||
|
||||
def _get_codex_branch(self, file_path, path_key: str) -> str:
|
||||
"""Get branch for a Codex session, reading session_meta if needed."""
|
||||
if path_key in self._session_branches:
|
||||
return self._session_branches[path_key]
|
||||
|
||||
# Read first line of JSONL for session_meta
|
||||
try:
|
||||
with open(file_path, 'r', encoding='utf-8', errors='ignore') as f:
|
||||
first_line = f.readline().strip()
|
||||
if first_line:
|
||||
meta = _json.loads(first_line)
|
||||
if meta.get('type') == 'session_meta':
|
||||
cwd = meta.get('payload', {}).get('cwd', '')
|
||||
branch = self._branch_from_cwd(cwd)
|
||||
if branch:
|
||||
self._session_branches[path_key] = branch
|
||||
return branch
|
||||
except (OSError, _json.JSONDecodeError) as e:
|
||||
if path_key not in self._branch_warn_logged:
|
||||
logger.info(f"[monitor] Could not read Codex session_meta: {e}")
|
||||
self._branch_warn_logged.add(path_key)
|
||||
|
||||
self._session_branches[path_key] = 'CODEX'
|
||||
return 'CODEX'
|
||||
|
||||
def _get_gemini_branch(self, file_path, path_key: str) -> str:
|
||||
"""Get branch for a Gemini session from project slug in path."""
|
||||
if path_key in self._session_branches:
|
||||
return self._session_branches[path_key]
|
||||
|
||||
# Path format: ~/.gemini/tmp/<project-slug>/chats/session-*.json
|
||||
# Project slug maps to a directory via ~/.gemini/projects.json
|
||||
parts = Path(file_path).parts
|
||||
if 'tmp' in parts:
|
||||
idx = parts.index('tmp')
|
||||
if idx + 1 < len(parts):
|
||||
slug = parts[idx + 1]
|
||||
# Try to resolve slug to real branch via projects.json
|
||||
projects_file = Path.home() / '.gemini' / 'projects.json'
|
||||
if projects_file.exists():
|
||||
try:
|
||||
data = _json.loads(projects_file.read_text())
|
||||
for project_path, project_slug in data.get('projects', {}).items():
|
||||
if project_slug == slug:
|
||||
branch = self._branch_from_cwd(project_path)
|
||||
if branch:
|
||||
self._session_branches[path_key] = branch
|
||||
return branch
|
||||
except (OSError, _json.JSONDecodeError):
|
||||
pass
|
||||
# Fallback: use slug as branch name
|
||||
self._session_branches[path_key] = slug.upper()
|
||||
return slug.upper()
|
||||
|
||||
self._session_branches[path_key] = 'GEMINI'
|
||||
return 'GEMINI'
|
||||
|
||||
# =========================================================================
|
||||
# MODEL TAG HELPERS
|
||||
# =========================================================================
|
||||
@@ -542,13 +624,13 @@ class MonitoringFileHandler(FileSystemEventHandler):
|
||||
|
||||
# Codex JSONL files: parse agent activity
|
||||
if file_path.suffix == '.jsonl' and '.codex/sessions/' in path_str:
|
||||
codex_branch = 'CODEX'
|
||||
codex_branch = self._get_codex_branch(file_path, path_str)
|
||||
if self._parse_codex_activity(file_path, codex_branch):
|
||||
return
|
||||
|
||||
# Gemini JSON session files: parse agent activity
|
||||
if file_path.suffix == '.json' and '.gemini/tmp/' in path_str and '/chats/' in path_str:
|
||||
gemini_branch = 'GEMINI'
|
||||
gemini_branch = self._get_gemini_branch(file_path, path_str)
|
||||
if self._parse_gemini_activity(file_path, gemini_branch):
|
||||
return
|
||||
|
||||
|
||||
Reference in New Issue
Block a user