---
name: acahti
description: Use Acahti git, pull requests, pipelines, commit checks, packages, teams, and the optional Work, Argos, and Chat modules. Use when committing or pushing to an Acahti remote, opening or merging a PR, reading pipelines, managing secrets or team membership, or when the user asks for Acahti identity or CI. Connect MCP at <instance>/mcp with OAuth; shell agents use the acahti CLI.
---

# Use Acahti

This is the product skill, published at https://acahti.benchyard.com/skill.md. If you are reading a cached copy, fetch that URL now and follow it. Everything instance-specific comes from the instance itself, through `whoami`.

## Find the instance

An Acahti instance is one deployment, such as `https://acahti.example.com`. Take its URL from the first of these that exists:

1. The user names it.
2. An MCP server already connected to `<instance>/mcp`.
3. `ACAHTI_ROOT_URL`, or the host stored by `acahti auth login`.
4. The HTTPS URL of the git remote named `acahti`.

Do not guess it. Once connected, call `whoami`. It returns `root_url`, `org`, `clone_url_template`, `git_name`, `git_email`, `setup_local`, `apply_when_remote_host`, `org_admin`, and `modules`. Below, `<instance>` is `whoami.root_url` and `<org>` is `whoami.org`.

## Connect

**MCP (IDE agents).** Streamable HTTP at `<instance>/mcp`. Complete OAuth when the client challenges. Discover tools this session; do not trust a cached tool list.

**CLI (shell agents).** Install with `npm install -g @benchyard/acahti`. It needs two values: `ACAHTI_ROOT_URL` (the instance) and `ACAHTI_TOKEN` (an access token, never the account password). Pipeline jobs have both. Otherwise ask the person for a token from the avatar menu → Access tokens and store it with `acahti auth login --hostname <instance> --with-token` on stdin. `acahti auth login` without `--with-token` is the browser flow for a person at the keyboard.

`acahti whoami`, `acahti pr`, `acahti pipeline`, `acahti checks`, `acahti inbox`. Anything else: `acahti api METHOD /path`. Every MCP tool has the same arguments through `acahti api`.

**Accounts.** A person without an account opens `<instance>/join` with an invite from an admin. Existing accounts log in at `<instance>/login`.

Do not paste tokens into chat. Do not open a browser unless a person is at the keyboard. Do not `git config --global`.

## Git

HTTPS only: `<instance>/<org>/<repo>.git` (`whoami.clone_url_template`). Username is the Acahti login. Password is the account password or an access token; agents use the system credential helper with the token. `ssh://` URLs, custom SSH ports, and bare IP addresses are not Acahti git.

A team grants access; it is not a clone path. Clone is always `<org>/<repo>`.

If another host is `origin`, keep it. Add Acahti as the remote named `acahti`, and fetch and push it by name. Do not `git fetch --all`.

### Commit identity

This applies only to commits intended for Acahti and pushes to Acahti. Another remote pointing at Acahti does not change a commit bound elsewhere.

1. Resolve the actual push destination from the user's request, an explicit remote or URL, or Git's configured push destination (including push URLs). Never infer it from the directory name, remote name, or whichever remote is listed first. If more than one destination is plausible, ask before changing identity.
2. For an Acahti destination, call a fresh `whoami` before each commit-producing operation (commit, merge, amend, rebase, cherry-pick, revert, and their `--continue`). Compare the destination HTTPS host with `apply_when_remote_host`.
3. Run `setup_local` in that repository (local config only). Author is `git_name`, email is `git_email`; `login` is the account, not a name. If `whoami` fails, stop and diagnose before committing.
4. Verify `git var GIT_AUTHOR_IDENT` and `git var GIT_COMMITTER_IDENT`, accounting for `git -c`, `--author`, and `GIT_AUTHOR_*` / `GIT_COMMITTER_*`. New commits, merges, and reverts match the verified identity on both. Amend, rebase, and cherry-pick keep the original author and use the verified identity as committer. Inspect outgoing commits before pushing.
5. For any other destination, change nothing. If you set `setup_local` temporarily in a mixed-remote repository, restore the previous local identity afterwards.

### Branches

Protection is per repository. Before pushing, `repo_get` and `branch_list`. Push directly to branches with `protected: false`. For a protected branch, push a topic branch and open a PR (`pr_create`; `base` defaults to `repo_get.default_branch`). If the remote declines a push, open a PR to the default branch. Do not assume branch names. Operators set protection on the repository's Branches page.

## After push

Call each verb as an MCP tool or the CLI command in parentheses.

1. `git rev-parse HEAD`.
2. `checks_wait` `{owner, name, sha}` (`acahti checks OWNER/REPO SHA`). It returns a snapshot of the latest pipeline round; poll until `done` is true. Do not pass `timeout_sec`.
3. Failed: `pipeline_list` `{repo, sha}` (`acahti pipeline list OWNER/REPO --sha SHA`), then `pipeline_get` (`acahti pipeline view OWNER/REPO N`), then `pipeline_log` without `step` for the failed steps (`acahti pipeline log OWNER/REPO N`).
4. Fix and push, or `pipeline_rerun`. Use `pipeline_cancel` only for a stuck run.
5. Green (`ok` and `done`): `pr_merge` (`acahti pr merge OWNER/REPO N`). It succeeds only when the newest pipeline on the head SHA is green. `blocked`: a person approves with `deploy_approve`.
6. Triage across repositories: `inbox` (`acahti inbox --section pipes` for the latest failed or blocked run per repo, `--section prs` for open PRs ready to merge).

Close leftover heads with `pr_close`, then `ref_delete`.

If a pipeline stays queued with no runner, stop and report it with the `pipeline_get` wait state. Do not claim a green result.

## Pipelines

YAML lives in `.acahti/pipelines/`. Call official pipes with `pipe: <name>@v1` and `with:`; one-off steps use `commands:`. The pipe catalog: https://acahti.benchyard.com/docs/pipelines.md.

Trigger CI with `git push`, a tag, or opening a PR. `pipeline_trigger` is a manual run, not a release.

Git tags are `vX.Y.Z`. Image tags never carry the `v`: use `${CI_COMMIT_TAG#v}`, not `${CI_COMMIT_TAG}`.

In `commands:`, write `$VAR` for step environment. `${VAR}` is interpolated before the shell runs and is empty unless it is job context such as `${CI_COMMIT_SHA}`.

## Secrets

Pipeline secrets hold credentials for systems outside Acahti: registries, kubeconfigs, object storage. YAML names them (`DOCKER_PASSWORD: registry_password`). Values never go in git, chat, or `with:`.

Acahti's own npm and PyPI registries use the identity of the person who triggered the run. Do not create an `NPM_TOKEN` or publish token for them.

With `repo_get.can_manage_secrets` or `whoami.org_admin`: `secret_list` (names only; repo scope is the effective set), then `secret_put`. Never print a value; there is no `secret_get`. Without admin rights, do not call `secret_*`: tell the operator which secret is missing and where to add it (repository → Secrets, or Admin → Org secrets).

## Packages

Same credentials as git.

- npm: `<instance>/api/packages/<org>/npm/`
- PyPI: `<instance>/api/packages/<org>/pypi/simple/`

On a laptop, authenticate with `~/.npmrc` (`username`, `_password` as base64 of the password or token, `always-auth=true`), or `UV_INDEX_<NAME>_USERNAME` / `UV_INDEX_<NAME>_PASSWORD`, or `~/.netrc`. In CI the `docker-build` pipe passes these as BuildKit secrets `npmrc` and `netrc`; Dockerfiles mount them at `/root/.npmrc` and `/root/.netrc`.

Publish with the `npm-publish-acahti` or `pypi-publish-acahti` pipes, or `pkg_publish`. Org admins remove versions with `pkg_delete`.

## Teams

`user_list` `{q}` matches login or display name. `team_list`, then `team_get` `{team}` for members (`read`, `write`, `admin`) and repositories. A membership covers every repository in the team.

Org admins: `team_member_set` `{team, login, permission}` (omit `permission` for `write`) and `team_member_remove` `{team, login}`. Do not add the same person as a collaborator on each repository.

## Optional modules

`whoami.modules` says which are on. All are off by default; do not invent tabs that are not there.

| Flag | Web app | Notes |
|---|---|---|
| `work` | `<instance>/tickets`, `/todos`, `/docs` | Tickets, todos, and documents |
| `argos` | `<instance>/argos` | Test-run browser. The Argos CLI uses `ARGOS_DASH_URL=<instance>`; people download `dash.env` from Argos → CLI |
| `chat` | header launcher, `<instance>/chat` on mobile | Org chat |

When a flag is on, the browser session may call that module's `/ui/work/*`, `/ui/argos/*`, or `/ui/chat/*`. Git, PRs, CI, and secrets always go through MCP or the CLI.

## Tools

`whoami` `repo_list` `repo_get` `repo_create` `branch_list` `ref_delete` `pr_create` `pr_list` `pr_get` `pr_comment` `pr_comments` `pr_merge` `pr_close` `checks_wait` `pipeline_list` `pipeline_get` `pipeline_log` `pipeline_rerun` `pipeline_trigger` `pipeline_cancel` `pipeline_delete` `inbox` `pkg_list` `pkg_publish` `pkg_delete` `agent_status` `deploy_approve` `secret_list` `secret_put` `secret_delete` `user_list` `team_list` `team_get` `team_member_set` `team_member_remove`

Arguments and descriptions: https://acahti.benchyard.com/docs/mcp.md.
