fix(aipass): bare 'aipass <command>' runs the command, not an introspection banner

aipass is a user-facing binary — 'aipass doctor' must run the health check,
not describe itself. All 7 modules (doctor, doctor_fix, doctor_wire, handoff,
help_chat, init_flow, profile) hit a no-args→introspection gate (a standard
meant for 'drone @branch <module>' discovery). Bare invocation now runs the
command or shows usage; introspection moved to --info. seedgo introspection
standard bypassed for these binary-invoked modules (documented). 424 tests pass.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
AIOSAI
2026-06-02 15:22:22 -07:00
co-authored by Claude Opus 4.8
parent ed58eb7aa6
commit f7d7f78c63
14 changed files with 145 additions and 45 deletions
+21
View File
@@ -8,6 +8,27 @@ and this project uses [Calendar Versioning](https://calver.org/) in the format
---
## [2026.W23] - 2026-06-02
### Fixed
- **`aipass <command>` runs instead of printing an introspection banner** —
`aipass` is a user-facing binary, so `aipass doctor` (and every other command)
must execute, not describe itself. All 7 modules (`doctor`, `doctor_fix`,
`doctor_wire`, `handoff`, `help_chat`, `init_flow`, `profile`) previously hit a
no-args→introspection gate (a standard meant for `drone @branch <module>`
discovery) and showed a banner on bare invocation. Now bare invocation runs the
command or shows usage; the introspection banner moved to `--info`. The seedgo
introspection standard is bypassed for these binary-invoked modules (documented).
- **Dashboard plan counts no longer zeroed on refresh** — a branch's
`active_plans` was reset to `0` by every `drone @prax dashboard refresh`, because
`PLANS.central.json` only held Flow's own plans (`location==FLOW_ROOT` filter).
The central file is now comprehensive: all plans grouped per-branch, so refresh
reports each branch's real count (e.g. devpulse now shows its 12 open plans
instead of 0).
---
## [2026.W22] - 2026-05-30
### Added
+35
View File
@@ -260,6 +260,41 @@
"file": "apps/handlers/init/scaffold_content.py",
"standard": "json_structure",
"reason": "scaffold_content.py is Pure Python only (no module/prax/cli imports) by design — pure string-returning template generators extracted from bootstrap.py. Same constraint as bootstrap.py."
},
{
"file": "apps/modules/doctor.py",
"standard": "introspection",
"reason": "aipass is binary-invoked: aipass doctor runs the command; introspection via --info"
},
{
"file": "apps/modules/doctor_fix.py",
"standard": "introspection",
"reason": "aipass is binary-invoked: bare invocation shows usage; introspection via --info"
},
{
"file": "apps/modules/doctor_wire.py",
"standard": "introspection",
"reason": "aipass is binary-invoked: bare invocation shows usage; introspection via --info"
},
{
"file": "apps/modules/handoff.py",
"standard": "introspection",
"reason": "aipass is binary-invoked: aipass handoff shows usage; introspection via --info"
},
{
"file": "apps/modules/help_chat.py",
"standard": "introspection",
"reason": "aipass is binary-invoked: aipass help shows usage; introspection via --info"
},
{
"file": "apps/modules/init_flow.py",
"standard": "introspection",
"reason": "aipass is binary-invoked: aipass init shows usage; introspection via --info"
},
{
"file": "apps/modules/profile.py",
"standard": "introspection",
"reason": "aipass is binary-invoked: aipass profile runs the command; introspection via --info"
}
]
}
+2 -6
View File
@@ -664,15 +664,11 @@ def handle_command(command: str, args: list[str]) -> bool:
if command != "doctor":
return False
if not args:
print_introspection()
return True
if args[0] in ("--help", "-h", "help"):
if args and args[0] in ("--help", "-h", "help"):
print_help()
return True
if args[0] == "--info":
if args and args[0] == "--info":
print_introspection()
return True
+3 -3
View File
@@ -304,12 +304,12 @@ def handle_command(command: str, args: list[str]) -> bool:
return False
if not args:
print_introspection()
json_handler.log_operation("doctor_fix_info", {"command": command})
console.print("[dim]Helper module — use: aipass doctor --fix [--json][/dim]")
json_handler.log_operation("doctor_fix_usage", {"command": command})
return True
if args[0] in ("--help", "-h", "help"):
print_introspection()
console.print("[dim]Helper module — use: aipass doctor --fix [--json][/dim]")
json_handler.log_operation("doctor_fix_help", {"command": command})
return True
@@ -283,13 +283,13 @@ def handle_command(command: str, args: list[str]) -> bool:
return False
if not args:
print_introspection()
json_handler.log_operation("doctor_wire_info", {"command": command})
console.print("[dim]Helper module — use: aipass doctor (auto-wire runs when needed)[/dim]")
json_handler.log_operation("doctor_wire_usage", {"command": command})
return True
if args[0] in ("--help", "-h", "help"):
print_introspection()
json_handler.log_operation("doctor_wire_info", {"command": command})
console.print("[dim]Helper module — use: aipass doctor (auto-wire runs when needed)[/dim]")
json_handler.log_operation("doctor_wire_help", {"command": command})
return True
if args[0] in ("--info", "info"):
+5 -1
View File
@@ -134,13 +134,17 @@ def handle_command(command: str, args: list[str]) -> bool:
return False
if not args:
print_introspection()
print_help()
return True
if args[0] in ("--help", "-h", "help"):
print_help()
return True
if args[0] == "--info":
print_introspection()
return True
if args[0] == "launch":
cli, cwd, flag_variant = _parse_launch_args(args[1:])
if cli not in CLI_CHOICES:
+19 -1
View File
@@ -54,6 +54,20 @@ def print_introspection() -> None:
console.print(f"[bold cyan]Version:[/bold cyan] {_VERSION}")
def print_help() -> None:
"""Print usage help for the help command."""
console.print()
console.print("[bold cyan]aipass help[/bold cyan] — README-backed Q&A")
console.print()
console.print("[yellow]USAGE:[/yellow]")
console.print(" [green]aipass help <question>[/green] [dim]# Search branch READMEs[/dim]")
console.print()
console.print("[yellow]EXAMPLES:[/yellow]")
console.print(" [green]aipass help what does drone do[/green]")
console.print(" [green]aipass help how does ai_mail work[/green]")
console.print()
# =============================================================================
# KEYWORD EXTRACTION
# =============================================================================
@@ -219,10 +233,14 @@ def handle_command(command: str, args: list[str]) -> bool:
json_handler.ensure_module_jsons(_MODULE_NAME)
if not args:
print_introspection()
print_help()
return True
if args[0] in ("--help", "-h", "help"):
print_help()
return True
if args[0] == "--info":
print_introspection()
return True
+12 -3
View File
@@ -645,13 +645,18 @@ def _write_init_report(agent_path: str, accumulated: Dict[str, Any], dry_run: bo
"cli_choice": accumulated.get("cli", "claude"),
"total_agents": 1,
"system": system_data,
"note": "You are the first agent created in this project. You are the orchestrator. After dispatching work to other agents, monitor them with: drone @devpulse watchdog agent @target",
"note": (
"You are the first agent created in this project. You are the orchestrator."
" After dispatching work to other agents, monitor them with:"
" drone @devpulse watchdog agent @target"
),
}
provider_gaps = accumulated.get("provider_gaps", {})
if provider_gaps:
report["provider_gaps"] = provider_gaps
report["provider_action"] = (
"Provider settings need configuring. Tell the user what is missing and point them to provider_manifest.json for details."
"Provider settings need configuring. Tell the user what is missing"
" and point them to provider_manifest.json for details."
)
report_path = dropbox / "init_report.json"
report_path.write_text(json.dumps(report, indent=2) + "\n", encoding="utf-8")
@@ -917,13 +922,17 @@ def handle_command(command: str, args: list[str]) -> bool:
return False
if not args:
print_introspection()
print_help()
return True
if args[0] in ("--help", "-h", "help"):
print_help()
return True
if args[0] == "--info":
print_introspection()
return True
if args[0] == "agent":
sys.exit(_handle_init_agent(args[1:]))
return True
@@ -141,6 +141,10 @@ def handle_command(command: str, args: list[str]) -> bool:
print_help()
return True
if args[0] == "--info":
print_introspection()
return True
if args[0] == "set":
if len(args) < 3:
error("Usage: aipass profile set <field> <value>")
+5 -4
View File
@@ -364,14 +364,15 @@ class TestDoctorFixHandleCommand:
assert handle_command("doctor", []) is False
assert handle_command("help", []) is False
def test_no_args_calls_introspection(self) -> None:
"""No args triggers print_introspection."""
def test_no_args_shows_usage(self) -> None:
"""No args shows usage message (not introspection banner)."""
from aipass.aipass.apps.modules.doctor_fix import handle_command
with patch("aipass.aipass.apps.modules.doctor_fix.print_introspection") as mock:
with patch("aipass.aipass.apps.modules.doctor_fix.console") as mock_console:
result = handle_command("doctor_fix", [])
assert result is True
mock.assert_called_once()
printed = " ".join(str(c) for c in mock_console.print.call_args_list)
assert "aipass doctor --fix" in printed
def test_info_flag(self) -> None:
"""--info triggers print_introspection."""
+14 -18
View File
@@ -52,20 +52,6 @@ _ENCODING = "utf-8"
# =============================================================================
def _call_handle_command_no_args():
"""Call handle_command('help', []) with json_handler and console mocked."""
mock_console = MagicMock()
patches = [
patch("aipass.aipass.apps.modules.help_chat.json_handler"),
patch("aipass.aipass.apps.modules.help_chat.console", mock_console),
]
with ExitStack() as stack:
for p in patches:
stack.enter_context(p)
result = handle_command("help", [])
return result, mock_console
def _call_handle_command_drone_question(readme_content: str, readme_path: Path):
"""Call handle_command for 'what does drone do' with file I/O mocked."""
patches = [
@@ -367,11 +353,21 @@ class TestHandleCommand:
"""COMMAND module constant must equal the string 'help'."""
assert COMMAND == "help"
def test_no_args_returns_true_and_calls_console(self):
"""handle_command('help', []) must return True and print usage via console."""
result, mock_console = _call_handle_command_no_args()
def test_no_args_returns_true_and_shows_help(self):
"""handle_command('help', []) must return True and print usage help."""
with patch("aipass.aipass.apps.modules.help_chat.print_help") as mock_help:
with patch("aipass.aipass.apps.modules.help_chat.json_handler"):
result = handle_command("help", [])
assert result is True
mock_console.print.assert_called()
mock_help.assert_called_once()
def test_info_flag_calls_introspection(self):
"""--info flag triggers print_introspection."""
with patch("aipass.aipass.apps.modules.help_chat.print_introspection") as mock_intro:
with patch("aipass.aipass.apps.modules.help_chat.json_handler"):
result = handle_command("help", ["--info"])
assert result is True
mock_intro.assert_called_once()
def test_valid_drone_question_returns_true(self):
"""A well-formed question about drone must return True."""
+10 -3
View File
@@ -198,11 +198,18 @@ class TestHandleCommand:
assert handle_command("doctor", []) is False
assert handle_command("profile", ["set", "name", "X"]) is False
def test_no_args_shows_introspection(self, tmp_local_json) -> None:
"""'init' with no args calls print_introspection."""
with patch("aipass.aipass.apps.modules.init_flow.print_introspection") as mock_intro:
def test_no_args_shows_help(self, tmp_local_json) -> None:
"""'init' with no args calls print_help (not introspection banner)."""
with patch("aipass.aipass.apps.modules.init_flow.print_help") as mock_help:
result = handle_command("init", [])
assert result is True
mock_help.assert_called_once()
def test_info_flag_calls_introspection(self, tmp_local_json) -> None:
"""--info flag calls print_introspection."""
with patch("aipass.aipass.apps.modules.init_flow.print_introspection") as mock_intro:
result = handle_command("init", ["--info"])
assert result is True
mock_intro.assert_called_once()
def test_help_flag(self) -> None:
+3 -1
View File
@@ -290,7 +290,9 @@ class TestReturnTypeContracts:
"""Doctor handle_command returns True for match, False otherwise."""
from aipass.aipass.apps.modules.doctor import handle_command as doctor_cmd
assert doctor_cmd("doctor", []) is True
with patch("aipass.aipass.apps.modules.doctor.run_doctor", return_value=0):
with patch("aipass.aipass.apps.modules.doctor.json_handler"):
assert doctor_cmd("doctor", []) is True
assert doctor_cmd("not_doctor", []) is False
def test_help_chat_handle_command_returns_bool(self):
+8 -1
View File
@@ -170,12 +170,19 @@ class TestHandleCommand:
assert handle_command("init", ["run"]) is False
def test_no_args_calls_introspection(self, tmp_local_json) -> None:
"""'profile' with no args calls print_introspection."""
"""'profile' with no args shows the profile (runs the command)."""
with patch("aipass.aipass.apps.modules.profile.print_introspection") as mock_pi:
result = handle_command("profile", [])
assert result is True
mock_pi.assert_called_once()
def test_info_flag_calls_introspection(self, tmp_local_json) -> None:
"""--info flag calls print_introspection."""
with patch("aipass.aipass.apps.modules.profile.print_introspection") as mock_pi:
result = handle_command("profile", ["--info"])
assert result is True
mock_pi.assert_called_once()
def test_help_flag_returns_true(self) -> None:
"""--help flag is handled."""
with patch("aipass.aipass.apps.modules.profile.print_help"):