Skip to content

Refactor context module: extract utilities, consolidate error handling#112

Merged
alexkroman merged 1 commit into
mainfrom
claude/peaceful-hawking-m5scnp
Jun 12, 2026
Merged

Refactor context module: extract utilities, consolidate error handling#112
alexkroman merged 1 commit into
mainfrom
claude/peaceful-hawking-m5scnp

Conversation

@alexkroman

Copy link
Copy Markdown
Collaborator

Summary

This PR refactors the aai_cli/context.py module to improve code organization and reduce duplication. The changes extract reusable utilities into dedicated modules, consolidate error-handling patterns, and remove thin adapter functions that are no longer needed.

Key Changes

  • Extract stdio utilities: Move interactive_stdio() from main.py to a new aai_cli/stdio.py module (alongside existing piped_stdin_text() and stdin_is_piped()), making the "is this a real terminal?" predicate reusable across the codebase.

  • Extract subprocess spawning: Create aai_cli/procs.py with spawn_detached() to centralize the detached-subprocess recipe used by both telemetry and update-check, eliminating duplication and ensuring consistency.

  • Consolidate error handling: Introduce _fail() helper in context.py to unify the error-emit-and-exit pattern used throughout run_command() and _auto_login_and_exit(), replacing repeated output.emit_error() + typer.Exit() sequences.

  • Lazy-load auth module: Move the from aai_cli.auth import run_login_flow import inside persist_browser_login() to avoid importing the auth package (httpx, Stytch, loopback server) on every command's import path—only when a browser login actually starts.

  • Remove thin adapters: Delete module-level resolve_profile(), resolve_environment(), resolve_session(), and env_override_warning() functions that merely delegated to AppState methods; callers now invoke the methods directly.

  • Simplify speak_exec.py: Use the new stdio.piped_stdin_text() helper and extract a _emit() function to deduplicate JSON/human output logic in _emit_single() and _emit_multi().

  • Update main.py: Replace the hand-maintained _ROOT_ONLY_FLAGS list with dynamic flag discovery from the root callback's parameter declarations, ensuring new global flags automatically get placement hints without manual updates.

  • Refactor llm.py: Simplify _list_models() by removing the higher-order function wrapper and inlining the body directly into the command handler.

  • Extract render_check_lines(): Move the doctor-check rendering logic to aai_cli/commands/doctor.py so the onboarding wizard can reuse it without duplication.

  • Update test mocks: Adjust all test patches to target aai_cli.auth.run_login_flow (the new import location) instead of aai_cli.context.run_login_flow.

  • Consolidate error factories: Extract _no_project_error() in auth/flow.py to avoid repeating the same error message in two branches.

  • Simplify config validation: Remove the private _validate_profile() wrapper; callers now use validate_profile() directly.

Notable Implementation Details

  • The _fail() helper uses from None to suppress the exception chain for user-facing errors (keeping output clean), but the internal-error path in run_command() preserves the chain with from exc for debugging.

  • stdio.interactive_stdio() is now the single source of truth for "can we prompt the user?" across the setup wizard, onboarding prompter, and assembly init template picker.

  • The procs.spawn_detached() recipe ensures detached children have their own session, discarded stdio, and a self-disabling env var to prevent recursive spawning.

  • argscan.JSON_FLAGS is now a module-level constant shared with main.py's misplaced-flag hint logic, preventing drift between the two.

https://claude.ai/code/session_01APHW6krqHMsPQ7MsMuBR9n

Quality pass from a four-angle review (reuse, simplification, efficiency,
altitude) over aai_cli/:

- stdio.interactive_stdio(): one TTY predicate for the bare-`assembly` setup
  offer, the onboarding prompter, and the init template picker (was three
  inline copies, one shadowing context's different _interactive_session)
- context: drop the four module-level resolve_* pass-throughs in favor of the
  AppState methods; fold the six emit_error+Exit pairs into one _fail helper;
  lazy-import the auth package so the login stack stays off every command's
  import path
- procs.spawn_detached(): the shared detached-spawn recipe for the telemetry
  flusher and update-check refresh (was two hand-synced Popen incantations)
- environments.SANDBOX_ENV: single source for the sandbox name; --env help is
  derived from ENVIRONMENTS
- main: derive the misplaced-global-flag hint set from the root callback's own
  declarations (the hand list had already drifted — --version got no hint);
  JSON flag spellings shared via argscan.JSON_FLAGS
- doctor.render_check_lines(): shared with the onboarding wizard's environment
  section (was a verbatim copy including the symbol map)
- output.hidden_note(): one phrasing for the "Hidden: N …" footnotes in
  usage/audit
- speak_exec: reuse stdio.piped_stdin_text; merge the duplicated single/multi
  emit fork
- config: collapse the validate_profile pass-through; flow: single
  _no_project_error; llm: collapse the --list-models closure factory
- tests: contract test pinning that the -o field maps cover every
  TranscriptOutput member exactly

https://claude.ai/code/session_01APHW6krqHMsPQ7MsMuBR9n
@alexkroman alexkroman enabled auto-merge (squash) June 12, 2026 14:45
@alexkroman alexkroman merged commit 4804e12 into main Jun 12, 2026
16 checks passed
@alexkroman alexkroman deleted the claude/peaceful-hawking-m5scnp branch June 12, 2026 14:48
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants