feat(system): S80: CLI init double-prefix fix + watchdog auto-start prompt

Co-Authored-By: @devpulse <devpulse@aipass>
This commit is contained in:
AIOSAI
2026-04-08 21:38:19 -07:00
co-authored by @devpulse
parent 366978e341
commit 1252bf5b9d
3 changed files with 128 additions and 23 deletions
+114 -19
View File
@@ -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,
+13 -4
View File
@@ -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()
@@ -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