Setup & Install
Depth:
ait setup
Cross-platform dependency installer and configuration tool. This is typically the first command to run after installing aitasks.
Run from the project root. Both
ait setupand the curl installer must be executed from the root of your git repository (the directory containing.git/). aitasks stores task files, plans, skills, and configuration inside the repo and relies on git for task IDs, locking, and multi-machine synchronization.
ait setup
Run ait setup --help for the full option list, including the opt-in dependency tiers described below.
Auto-bootstrap: When run via the global shim (~/.local/bin/ait) in a directory without an existing aitasks installation, ait setup automatically downloads and installs the latest release before running the setup flow. This lets you bootstrap new projects with a single command — no need to run the curl | bash installer separately. A help request is the one exception: ait setup --help outside a project never bootstraps — it tells you the options ship with the framework and how to install it first.
Guided setup flow:
OS detection — Automatically detects: macOS, Arch Linux, Debian/Ubuntu, Fedora/RHEL, WSL
CLI tools — Installs missing tools (
fzf,gh/glab/bkt,jq,git,zstd) via the platform’s package manager (pacman, apt, dnf, brew). Auto-detects git remote platform to install the right CLI tool (ghfor GitHub,glabfor GitLab,bktfor Bitbucket). On macOS, requires Homebrew and also installs bash 5.x, Python 3, and coreutilsVersion checks — Verifies Bash >= 4.0 and Python >= 3.9. On macOS, offers to install/upgrade via Homebrew if versions are too old
Git repo — Verifies you are inside a git repository. If no
.git/is found, explains that aitasks is tightly integrated with git and asks to confirm this is the correct project directory before offering to rungit initDraft directory — Creates
aitasks/new/for local draft tasks and adds it to.gitignoreso drafts stay local-onlyTask ID counter — Initializes the
aitask-idscounter branch on the remote for atomic task numbering. This prevents duplicate task IDs when multiple PCs create tasks against the same repoPython venv — Creates a virtual environment in the per-user root (
~/.aitasks/venv/by default) and installstextual(>=8.1),pyyaml,linkify-it-py,tomli,plotext(forait stats-tuichart panes), and other dependencies with pinned versions. After installing, setup validates that every dependency imports and matches its pinned version range, repairing the venv if not. Recreates the venv if existing Python is too old. For an opt-in PyPy 3.11 venv that speeds upait board(ait setup --with-pypy), see PyPy Runtime.Setup also has two further opt-in dependency tiers, neither installed by default. Each is remembered after the first opt-in, so later plain
ait setupruns revalidate and repair it:ait setup --with-chat— the chat adapter SDKs (discord.py,slack-bolt,slack-sdk)ait setup --with-dev—pytestandpytest-xdist, which give the Python test suite a parallel lane. Contributors only; the suite runs on the standard library’sunittestwithout them — see Testing Changes for the lane’s environment knobs and how to opt out
Global shim — Installs
aitshim at~/.local/bin/aitthat finds the nearest project-localaitdispatcher by walking up the directory tree. Warns if~/.local/binis not in PATHClaude Code permissions — Shows the recommended permission entries, then prompts Y/n to install them into
.claude/settings.local.json. If settings already exist, merges permissions (union of allow-lists)Session hook — Its own Y/n prompt, separate from the permissions one: installs a SessionStart hook that records each agent’s session id so a frozen agent can be restored.
ait setup --hooks-onlyruns this step alone. See Session HooksPer-user root — Moves a
~/.aitask/directory into~/.aitasks/and leaves~/.aitaskbehind as a symlink to it. See Per-user rootVersion check — Compares local version against latest GitHub release and suggests update if newer
Setup also ensures the shared project config file exists at aitasks/metadata/project_config.yaml. That file is seeded from seed/project_config.yaml, tracked in git, and includes project-wide workflow settings such as:
codeagent_coauthor_domain: aitasks.io— the email domain used for custom code-agentCo-Authored-Bytrailersverify_build— the post-implementation build verification command or command list
If you re-run ait setup after customizing codeagent_coauthor_domain, the existing value is preserved.
Claude Code Permissions
When you run ait setup, it offers to install default Claude Code permissions into .claude/settings.local.json. These permissions allow aitask skills to execute common operations (file listing, git commands, aiscript invocations) without prompting for manual approval each time.
The default permissions are defined in seed/claude_settings.local.json and stored at aitasks/metadata/claude_settings.seed.json during installation. If a .claude/settings.local.json already exists, the setup merges permissions (union of both allow-lists, preserving any existing entries). You can decline the permissions prompt and configure them manually later.
Re-run ait setup at any time to add the default permissions if you skipped them initially.
Session Hooks
Setup also offers a SessionStart hook, .aitask-scripts/aitask_session_hook.sh. It runs when a code agent’s session starts in the project and records that session’s id, which is what lets an agent you freeze later be restored rather than only re-picked. The hook writes nothing into the agent’s session and always exits successfully, so a broken hook can never block a session.
It has its own Y/n prompt, separate from the permissions one — a permission allow-list and a script that runs at every session start are different decisions. A non-interactive run auto-accepts. Your answer is not remembered: declining means the offer comes back on the next ait setup. Once the hook is installed it is not offered again, and a .claude/settings.json that is not valid JSON is reported instead of being merged into.
Claude Code. The hook goes into .claude/settings.json (not settings.local.json) as a hooks.SessionStart entry matching startup|resume. If the file does not exist it is created from the seed. If it does, only hooks.SessionStart is merged in — every other key, including your own hooks for other events, is preserved verbatim. Entries are deduplicated by the script they run, however the path is spelled, so re-running setup never installs a second copy.
Codex CLI. The Codex hook is part of the “Install Codex CLI skills and config?” prompt, which appears only when Codex CLI is installed. It is merged into .codex/config.toml by parsing that file and writing it back out, so any comments in .codex/config.toml are lost in the merge. Codex also requires the project to be trusted — trust_level = "trusted" under [projects."<path>"] in $CODEX_HOME/config.toml — which setup never writes, since it does not touch your Codex home. Codex’s interactive TUI fires the hook at a session’s first turn, not at launch (measured on Codex 0.163.0; Codex 0.153.4 fires it only under codex exec). On Linux, an older Codex agent has its session captured when it is frozen instead, from the transcript its process holds open — which exists only once it has taken a turn. An agent frozen before its first turn has no session to resume, and is restored by re-picking its task. On Linux a resumed Codex agent is verified from the transcript its process holds open, so its restores do not wait for the hook. Elsewhere they report as unverified.
Opting out means answering n at the prompt. Deleting the installed entry by hand is not an opt-out: the next accepted ait setup sees it missing and adds it back.
Installing only the hook
ait upgrade stages the hook but never installs it — that stays behind the setup prompt. A project that was set up before the hook existed and has only been upgraded since therefore has no hook, and ait upgrade says so when it finishes. ait ide repeats the note when it lists frozen agents whose viewer is gone.
To install just the hook, without re-running the rest of setup:
ait setup --hooks-only # asks the same Y/n question
ait setup --hooks-only --yes # accept without a prompt, e.g. from a script
Without a terminal, --hooks-only refuses unless you pass --yes: the shortcut never installs an executable hook without an explicit answer. It does nothing when the hook is already installed, and it stops before writing anything when the hook could not work on this machine — the hook runs python3 from your PATH directly, so that command has to work, and the session store needs a Python that meets the framework minimum. When Python is missing, or the framework’s own ~/.aitask/bin/python3 wrapper is broken, it tells you to run the full ait setup, which installs or repairs it; a different python3 that does not run is yours to fix. If AIT_PYTHON points at a Python older than the minimum, it says so instead: that override wins over every other interpreter, so a full setup would not fix it — unset AIT_PYTHON or point it at a newer Python.
The hook only helps Claude Code agents started after it is installed. An agent that was already frozen without a recorded session stays view-only — you can still re-pick it if it has a task — and an agent that is running now needs a restart before its session is recorded.
Per-user root
The files ait setup installs for your user rather than for one project — the Python venv, the optional PyPy venv, the user-scoped Python, uv, the python3 wrappers, the dev-tier marker and the test map engine — live in one directory, ~/.aitasks/. Set AITASKS_HOME to put it somewhere else.
When ait setup finds a ~/.aitask/ directory holding them instead, its last step moves the contents into ~/.aitasks/ and leaves ~/.aitask behind as a symlink to it. Every ~/.aitask/... path keeps working, so the commands in these docs use that spelling: it is right whether or not the move has happened.
The move is all or nothing. Setup checks everything first (an unknown file, a name that already exists in ~/.aitasks/, a different filesystem, a foreign symlink) and, if anything is in the way, changes nothing, prints the reasons and finishes. Run ait engine home to see the report again, then ait engine home --migrate (or ait setup) once the obstacle is gone. If a step fails partway, setup puts everything back. In the rare case that cannot be done safely, setup stops and prints HOME_RESTORE: commands to run in order.
To keep the old layout, pass --no-home-migration, or set AIT_HOME_MIGRATE=0 in your environment. ait upgrade never moves the directory; it only reports when an old one is still there.
ait upgrade
Update the aitasks framework to a new version.
ait upgrade # Upgrade to latest release
ait upgrade latest # Same as above
ait upgrade 0.2.1 # Upgrade to specific version
How it works:
- Resolves the target version (queries the GitHub API for
latest, with a rate-limit-freegit ls-remotefallback — see Bounded version lookup below — or validates the provided version number) - Checks if already up to date (skips if versions match)
- Downloads
install.shfrom the target version’s git tag - Runs the installer with
--force, which shows the changelog between current and target versions and asks for confirmation - Performs the full installation (tarball download, skill installation, setup) and commits the framework files it changed. Framework files that already had uncommitted changes before the upgrade — edited, staged, deleted or new — are left uncommitted and listed, so your in-progress work never lands in the framework commit. It also rewrites the data entries
.aitask-data,aitasksandaiplansin.gitignore— in their trailing-slash or bare form — to the root-anchored form/.aitask-data,/aitasksand/aiplans. That form matches the entries at the repository root, including the symlinks linked task worktrees carry, without ignoring nested directories of the same name (such asdocs/aitasks/); any such nested paths that now show as untracked are listed so you can review them before a broadgit add. In a project whose task data lives on the separateaitask-databranch, any of the three root-anchored entries missing from.gitignoreis added back. The rewrite and any added entries are committed only when.gitignorehad no uncommitted changes - Offers newer models for code-agent defaults the framework has superseded (see below)
- Stops the monitor status servers started from this project, so open monitors pick up the new code (see Monitor status servers below)
- Clears the update check cache
Superseded model defaults:
An upgrade never changes a configured model on its own. After the framework files are committed, it looks for operations in codeagent_config.json (and your codeagent_config.local.json) whose model a newer framework model supersedes, and asks once per superseded model whether to switch those operations. A model nothing supersedes is never offered a switch, whether it is a framework default or a custom choice, and a declined offer is not repeated until a newer successor exists. Without a terminal it only lists the offers. Run ait codeagent check-superseded at any time to review them; see Superseded model defaults.
Monitor status servers:
Monitor, minimonitor and the App Linker read pane status from a shared background status server, which keeps running the code it was started with. After installing the new files, ait upgrade stops every status server that was started from this project, on any tmux server, and prints a MONITOR_SERVER: line for each server it stops or fails to stop. Open monitors, minimonitors and the App Linker reconnect automatically to a fresh server. Another checkout on the same framework version can start that replacement first, so if monitor behaviour still looks stale afterwards, check script= in ait monitor --server-status. Servers started from other projects keep running. When a server does not stop, the output ends with a hint line; How to Restart the Shared Status Server explains how to end it by hand.
To leave the servers running, set AIT_MONITOR_RESTART=0:
AIT_MONITOR_RESTART=0 ait upgrade latest
ait setup does not restart status servers. Run ait monitor --restart-server when you need one replaced.
Bounded version lookup:
Resolving latest reaches the network. The GitHub REST API is tried first; when it answers with a rate-limit error, the version is resolved from git ls-remote instead, which is exempt from the REST quota. That git lookup is hard-bounded — 10 seconds by default — because a remote that blackholes the connection instead of refusing it would otherwise stall the command indefinitely. Override the bound with AIT_GIT_LSREMOTE_TIMEOUT, in whole seconds:
AIT_GIT_LSREMOTE_TIMEOUT=30 ait upgrade latest
An empty, zero, negative, or non-numeric value falls back to the 10-second default. When the bound trips, the lookup yields no version rather than hanging, and ait upgrade stops with the rate-limit message it was recovering from, including its GH_TOKEN hint.
Two other paths use the same bounded lookup and honor the same variable, and they report an expiry differently:
- The version check at the end of
ait setupis silent — it simply prints no “update available” notice. - The
curl | bashinstaller resolves the latest release from git tags before it touches the REST API, so an expiry is visible: afterResolving latest aitasks release...it announcesFetching latest release via the GitHub API...and continues there. If that fallback cannot resolve a release either, the installer stops withError: Could not find release tarball.and a link to the releases page.
Automatic update check:
The ait dispatcher checks for new versions once per day (at most) — a direct API call with its own short timeout, independent of the bounded git lookup above. When a newer version is available, it shows a brief notice suggesting ait upgrade latest. The check runs in the background to avoid adding latency. It is skipped for help, version, upgrade, and setup commands.
Next: Task Management