Skip to main content

Setup Wizard — Implementation Reference

Overview

adjutant setup is a Python interactive wizard that handles both fresh installs and repairs of an existing Adjutant installation. It is split across modules under src/adjutant/setup/.

src/adjutant/setup/
├── wizard.py # Entry point, orchestration, completion banner
├── install.py # Fresh install logic
├── repair.py # Repair path — health checks and fix actions
├── uninstall.py # Uninstall logic
└── steps/
├── prerequisites.py # Step 1: read-only dependency checks (no side effects)
├── install_path.py # Step 2: resolve/create install directory
├── backend.py # Step 3: LLM backend selection, writes llm.backend to adjutant.yaml
├── identity.py # Step 4: soul.md, heart.md, registry.md
├── messaging.py # Step 5: Telegram token + chat ID, .env write
├── features.py # Step 6: news_config.json, feature flags in adjutant.yaml
├── service.py # Step 7: chmod, rc-file alias, launchd/systemd/cron
├── autonomy.py # Step 8: heartbeat.enabled, schedule entries
├── kb_wizard.py # KB creation sub-wizard (called from wizard.py)
└── schedule_wizard.py # Schedule add sub-wizard

Execution paths

  • Fresh install (ADJ_DIR unset or pointing to a non-existent directory): runs all 8 steps in order.
  • Repair (existing install detected): runs health checks and offers to fix each issue found.

Wizard UI helpers (wizard.py)

All interactive UI is funnelled through helper functions. These write prompt text to the terminal and return the user's choice. They handle non-interactive contexts (CI, piped stdin) gracefully by accepting defaults.

FunctionWhat it does
wiz_confirm(prompt, default)Yes/No prompt — returns True/False
wiz_choose(prompt, *options)Select from a numbered list — returns 1-based index
wiz_input(prompt, default)Free-text input — returns entered string
wiz_multiline(prompt)Multi-line input — returns full text block
wiz_secret(prompt)Hidden input (no echo) — returns secret string

Note: wiz_choose returns a 1-based index (minimum 1). Always compare against 1, 2, etc. — never 0.


--dry-run flag

Activation

adjutant setup --dry-run

Behavior contract

CategoryNormalDry-run
PromptsInteractiveInteractive (unchanged)
Filesystem writesExecutedSuppressed; [DRY RUN] Would: ... printed inline
chmod callsExecutedSuppressed; printed inline
HTTP callsExecutedSuppressed; printed inline
LLM runExecutedSuppressed; printed inline
rc-file appendsExecutedSuppressed; printed inline
launchctl/systemctlExecutedSuppressed; printed inline
crontab editsExecutedSuppressed; printed inline

Guard pattern for side-effectful actions

Every action that would modify the filesystem, network, or system config is wrapped:

if dry_run:
wiz_info(f"[DRY RUN] Would write {target_file}")
else:
target_file.write_text(content)

Known edge cases and gotchas

Default install path resolves to cwd

When ADJ_DIR is empty (fresh install simulation), the install path step resolves the default to the current working directory. In a non-interactive shell (e.g. a dry-run launched from /Users), this prints /Users as the default path. This is cosmetically odd but functionally correct — no directory is actually created in dry-run mode.

Repair path

The wizard detects an existing installation by checking whether ADJ_DIR points to a directory that contains adjutant.yaml. If yes, it runs the repair path. If no, it runs the fresh install steps.

To force the fresh install path during dry-run testing:

ADJ_DIR="" adjutant setup --dry-run

To test the repair path on a live install:

adjutant setup --dry-run

KB wizard sub-wizard

The KB creation wizard (kb_wizard.py) is invoked from the main wizard as an optional step. It prompts for name, path, description, model tier, and access level. It calls kb_scaffold() from capabilities/kb/manage.py to create the directory structure.

Schedule wizard sub-wizard

schedule_wizard.py prompts for name, description, script path or KB operation, schedule, and log file. It calls schedule_append() from capabilities/schedule/manage.py to register the job, then schedule_install() to install the crontab entry.