From 1252bf5b9dc0407da10f2e87496ef01937999220 Mon Sep 17 00:00:00 2001 From: AIOSAI Date: Wed, 8 Apr 2026 21:38:19 -0700 Subject: [PATCH] feat(system): S80: CLI init double-prefix fix + watchdog auto-start prompt Co-Authored-By: @devpulse --- .../cli/apps/handlers/init/bootstrap.py | 133 +++++++++++++++--- src/aipass/cli/apps/modules/init_project.py | 17 ++- .../devpulse/.aipass/aipass_local_prompt.md | 1 + 3 files changed, 128 insertions(+), 23 deletions(-) diff --git a/src/aipass/cli/apps/handlers/init/bootstrap.py b/src/aipass/cli/apps/handlers/init/bootstrap.py index 3d297839..7ad92bbe 100644 --- a/src/aipass/cli/apps/handlers/init/bootstrap.py +++ b/src/aipass/cli/apps/handlers/init/bootstrap.py @@ -10,12 +10,14 @@ Init Bootstrap Handler - PRIVATE implementation Business logic for `aipass init`. Creates the project scaffold: - 1. {NAME}_REGISTRY.json — project registry with UUID - 2. .trinity/passport.json — project identity - 3. .trinity/local.json — local context (empty) - 4. .trinity/observations.json — observations (empty) + 1. {NAME}_REGISTRY.json — project registry with UUID + 2. .trinity/passport.json — project identity + 3. .trinity/local.json — session history (full schema) + 4. .trinity/observations.json — collaboration patterns (full schema) 5. .aipass/aipass_local_prompt.md — local prompt - 6. AIPASS.md — project prompt + 6. CLAUDE.md — project prompt (Claude Code reads this) + 7. README.md — project readme + 8. STATUS.local.md — project status RULES: - Pure Python only (no module/prax/cli imports) @@ -127,12 +129,80 @@ def init_project(target: Path, project_name: str | None = None) -> dict: ) created.append(str(passport_path)) - # local.json + observations.json - for filename in ("local.json", "observations.json"): - p = trinity_dir / filename - if not p.exists(): - p.write_text("{}\n", encoding="utf-8") - created.append(str(p)) + # local.json — session history with full schema + local_path = trinity_dir / "local.json" + if not local_path.exists(): + local_data = { + "document_metadata": { + "document_type": "session_history", + "document_name": f"{name}.LOCAL", + "version": "1.0.0", + "schema_version": "1.0.0", + "created": today, + "last_updated": today, + "managed_by": name.lower(), + "tags": ["session_tracking", "work_log"], + "limits": { + "max_sessions": 20, + "max_key_learnings": 25, + "session_summary_max_chars": 150, + "learning_value_max_chars": 200, + "note": "Auto-rollover when max_lines exceeded", + "max_lines": 600, + }, + "status": { + "health": "healthy", + "last_health_check": today, + "current_lines": 0, + }, + }, + "key_learnings": {}, + "sessions": [], + "active_tasks": { + "today_focus": today, + "recently_completed": [], + }, + } + local_path.write_text( + json.dumps(local_data, indent=2, ensure_ascii=False) + "\n", + encoding="utf-8", + ) + created.append(str(local_path)) + + # observations.json — collaboration patterns with full schema + obs_path = trinity_dir / "observations.json" + if not obs_path.exists(): + obs_data = { + "document_metadata": { + "document_type": "collaboration_patterns", + "document_name": f"{name}.OBSERVATIONS", + "version": "1.0.0", + "schema_version": "1.0.0", + "created": today, + "last_updated": today, + "managed_by": name.lower(), + "tags": ["collaboration", "patterns"], + "limits": { + "max_lines": 600, + "note": "Auto-rollover when max_lines exceeded", + }, + "status": { + "health": "healthy", + "current_lines": 0, + "last_health_check": today, + }, + }, + "guidelines": { + "purpose": "Capture collaboration patterns and experiential insights over time", + "chronological_order": "Newest entries at TOP, oldest at BOTTOM - NEVER reorder", + }, + "observations": [], + } + obs_path.write_text( + json.dumps(obs_data, indent=2, ensure_ascii=False) + "\n", + encoding="utf-8", + ) + created.append(str(obs_path)) # 3. .aipass/ aipass_dir = target / ".aipass" @@ -146,24 +216,49 @@ def init_project(target: Path, project_name: str | None = None) -> dict: ) created.append(str(prompt_path)) - # 4. AIPASS.md - aipass_md = target / "AIPASS.md" - if not aipass_md.exists(): - aipass_md.write_text( - "# AIPass — Project Prompt\n\n" - "Project-level instructions. Persists in context for the entire conversation.\n\n" + # 4. CLAUDE.md (project prompt — Claude Code reads this by default) + claude_md = target / "CLAUDE.md" + if not claude_md.exists(): + claude_md.write_text( + f"# {name}\n\n" + "**User:** Name\n\n" "## Startup\n\n" "On any greeting, silently read these files from CWD and run the commands " "— no narration, no announcing steps. Just do it and respond with the status.\n\n" "**Read:** `.trinity/passport.json`, `.trinity/local.json`, " - "`.trinity/observations.json`, `README.md`\n" + "`.trinity/observations.json`, `README.md`, `STATUS.local.md`\n" "**Run:** `git status`\n\n" "## Memories\n\n" "Update `.trinity/` at natural breakpoints, after milestones, and on `/memo`. " "If compaction hits before you save, it's gone.\n", encoding="utf-8", ) - created.append(str(aipass_md)) + created.append(str(claude_md)) + + # 5. README.md + readme_md = target / "README.md" + if not readme_md.exists(): + readme_md.write_text( + f"# {name}\n\n" + "## Getting Started\n\n" + "This project was initialized with `aipass init`.\n", + encoding="utf-8", + ) + created.append(str(readme_md)) + + # 6. STATUS.local.md + status_md = target / "STATUS.local.md" + if not status_md.exists(): + status_md.write_text( + f"# {name}\n\n" + "**State:** New\n" + f"**Last update:** {today}\n\n" + "## Current Work\n\n" + "## Known Issues\n" + "- None\n", + encoding="utf-8", + ) + created.append(str(status_md)) return { "registry_id": registry_id, diff --git a/src/aipass/cli/apps/modules/init_project.py b/src/aipass/cli/apps/modules/init_project.py index bc7afab0..672cc12b 100644 --- a/src/aipass/cli/apps/modules/init_project.py +++ b/src/aipass/cli/apps/modules/init_project.py @@ -95,14 +95,16 @@ def print_help(): console.print("[bold cyan]WHAT INIT CREATES:[/bold cyan]") console.print() - files_text = """[bold]Project scaffold (6 files):[/bold] + files_text = """[bold]Project scaffold (8 files):[/bold] [green]1.[/green] [yellow]{NAME}_REGISTRY.json[/yellow] Project registry with UUID [green]2.[/green] [yellow].trinity/passport.json[/yellow] Project identity (with registry_id) [green]3.[/green] [yellow].trinity/local.json[/yellow] Session history & learnings [green]4.[/green] [yellow].trinity/observations.json[/yellow] Collaboration patterns [green]5.[/green] [yellow].aipass/aipass_local_prompt.md[/yellow] Local prompt (injected every turn) - [green]6.[/green] [yellow]AIPASS.md[/yellow] Project prompt (persists in context)""" + [green]6.[/green] [yellow]CLAUDE.md[/yellow] Project prompt (Claude Code reads this) + [green]7.[/green] [yellow]README.md[/yellow] Project readme + [green]8.[/green] [yellow]STATUS.local.md[/yellow] Project status""" console.print(Panel(files_text, border_style="green", padding=(1, 2), box=box.ROUNDED)) console.print() @@ -143,6 +145,11 @@ def handle_command(command: str, args: List[str]) -> bool: Returns: True if handled, False if not this module's command """ + # Direct subcommand: 'aipass init /path' on PATH → command="init" + if command == "init": + return _handle_init(args) + + # Prefixed command: 'drone @cli aipass init' → command="aipass" if command != "aipass": return False @@ -266,14 +273,16 @@ def _print_init_help(): console.print("[bold cyan]WHAT IT CREATES:[/bold cyan]") console.print() - files_text = """[bold]Project scaffold (6 files):[/bold] + files_text = """[bold]Project scaffold (8 files):[/bold] [green]1.[/green] [yellow]{NAME}_REGISTRY.json[/yellow] Project registry with UUID [green]2.[/green] [yellow].trinity/passport.json[/yellow] Project identity (with registry_id) [green]3.[/green] [yellow].trinity/local.json[/yellow] Session history & learnings [green]4.[/green] [yellow].trinity/observations.json[/yellow] Collaboration patterns [green]5.[/green] [yellow].aipass/aipass_local_prompt.md[/yellow] Local prompt (injected every turn) - [green]6.[/green] [yellow]AIPASS.md[/yellow] Project prompt (persists in context)""" + [green]6.[/green] [yellow]CLAUDE.md[/yellow] Project prompt (Claude Code reads this) + [green]7.[/green] [yellow]README.md[/yellow] Project readme + [green]8.[/green] [yellow]STATUS.local.md[/yellow] Project status""" console.print(Panel(files_text, border_style="green", padding=(1, 2), box=box.ROUNDED)) console.print() diff --git a/src/aipass/devpulse/.aipass/aipass_local_prompt.md b/src/aipass/devpulse/.aipass/aipass_local_prompt.md index 717ea52c..1d12ded2 100644 --- a/src/aipass/devpulse/.aipass/aipass_local_prompt.md +++ b/src/aipass/devpulse/.aipass/aipass_local_prompt.md @@ -13,6 +13,7 @@ You are DEVPULSE — orchestration hub. Manager, not builder. Coordinate, plan, - Never block waiting on agents. Never burn context reading code across branches. - Use `drone @branch --help` for command syntax. Use `drone systems` for branch list. - **ALWAYS WAKE after sending dispatch emails.** Send email → wake. Every time. No asking. If the user wants something different, they will say so. +- **START WATCHDOG after any dispatch.** Run `python3 src/aipass/prax/tools/inbox_watchdog.py src/aipass/devpulse/.ai_mail.local/inbox.json --interval 30 &` after dispatching. Don't wait for Patrick to ask. ## Dispatch, Don't Do