sem-ai Command Line
sem-ai is an agent-first CLI for Semaphore CI/CD. It's designed for AI agents and automation, with structured JSON output, self-discovery, and composable commands. It can also run as an MCP server for direct tool integration.
Overview
sem-ai provides full control over your Semaphore CI/CD from the terminal or from AI agents. Every command returns structured JSON by default. Key capabilities:
- Diagnose CI failures with parsed test results
- Pipeline topology analysis (critical path, blast radius)
- Test intelligence (flaky detection, test summaries)
- Testbox: run commands in real CI environments before pushing
- MCP server mode for native AI agent integration
Installation
curl -fsSL https://raw.githubusercontent.com/semaphoreio/sem-ai/main/install.sh | sh
Installs the latest release for macOS / Linux on amd64 / arm64. The binary lands at $HOME/.local/bin/sem-ai (or $HOME/.semaphore-ai/bin/sem-ai if that's not on your $PATH). Re-run the same command to upgrade.
From source
Requires Go 1.25+.
git clone https://github.com/semaphoreio/sem-ai.git
cd sem-ai
make install
Setup
sem-ai connect
Connect to your Semaphore organization. You need an API token.
sem-ai connect <organization>.semaphoreci.com <API_TOKEN>
For example:
sem-ai connect myorg.semaphoreci.com NeUFkim46BCdpqCAyWXN
The token is stored in ~/.sem.yaml, shared with the Semaphore CLI. If you already have sem configured, sem-ai uses the same credentials automatically.
sem-ai signin
Sign in without pasting a token. signin runs a device flow: it shows a one-time code and a verification URL, opening a browser when one is available.
sem-ai signin
Enter the code in the browser, sign in — or create an account there, if you do not have one — and approve. The terminal finishes on its own. signup and login are aliases for the same flow. The resulting token is stored in ~/.sem.yaml the same way connect stores one.
Defaults to Semaphore Cloud. For another deployment, pass the host, and --id-host when the CLI-auth endpoints live elsewhere:
sem-ai signin my-onprem.example.com --id-host id.my-onprem.example.com
| Flag | Description |
|---|---|
--headless or --device | Never try to open a browser; print the code and URL |
--browser | Open the verification page even when the terminal looks non-interactive |
--id-host | Host serving the CLI-auth endpoints (defaults to <host>) |
--org | Also create a first organization with this name (new accounts only) |
--org-host | Host for the --org organization's context |
An account has a single API token. If yours already has one, approving the sign-in resets it, after an explicit confirmation in the browser. The previous token then stops working everywhere it is used — CI secrets, scripts, other machines. To authenticate with a token you already hold, use connect instead.
sem-ai context
Each organization you connect to is stored in ~/.sem.yaml as a named context.
List configured organizations:
sem-ai context list
Show the organization this invocation resolves to — the pin, if one is set, otherwise the active context:
sem-ai context show
Under a pin, show and list report different things by design: show names what this invocation will actually talk to, while list reports the file, marking the stored default active and the pinned entry pinned.
Change the default organization used by unpinned commands:
sem-ai context switch myorg_semaphoreci_com
context switch rewrites the shared active-context key in ~/.sem.yaml. Unpinned invocations that set no credential environment variables read that key — including invocations of the legacy sem CLI — so switching affects sessions other than your own. It is meant for a person changing their own default, not for scoping a command, a script, or an agent. To target an organization for one command without changing what anyone else sees, pin it instead.
Pinning an organization
--context selects a named context for one invocation, without writing to the config file. It takes a context name, not a host: connect names each context after its host with the dots replaced by underscores, so myorg.semaphoreci.com is stored as myorg_semaphoreci_com. Run sem-ai context list to see the exact names.
sem-ai --context myorg_semaphoreci_com project list
SEM_CONTEXT does the same for a whole shell session:
export SEM_CONTEXT=myorg_semaphoreci_com
sem-ai project list
A name that is not in ~/.sem.yaml fails immediately and lists the contexts that are, instead of quietly falling back to another organization.
Pinning isolates the read path: a pinned invocation neither reads nor writes active-context, so pinned sessions cannot flip each other's organization. It does not make every operation concurrency-safe. connect, signin, and context switch still write ~/.sem.yaml; each write replaces the whole file, and two overlapping writes are last-writer-wins, so a context one session adds can disappear when another lands. Pinning also covers sem-ai only — the legacy sem CLI reads neither --context nor SEM_CONTEXT and still follows active-context. When several agents run at once, sequence the commands that write, or give each agent its own HOME.
signin, context switch, and context list ignore the pin entirely, so a pin naming a context that does not exist yet cannot block the command that is about to create it. connect ignores it when resolving credentials, but refuses outright when the pin resolves to a different host than its <host> argument, since that names two organizations at once. connect names the context it creates after its host, so re-pin to that name once onboarding finishes.
Credential resolution order
sem-ai resolves a host and a token from these sources, in order:
| Priority | Source | Scope | Behavior |
|---|---|---|---|
| 1 | --context <name> | A single invocation | Replaces everything below |
| 2 | SEM_CONTEXT=<name> | A shell session | Replaces everything below |
| 3 | SEMAPHORE_HOST and/or SEMAPHORE_API_TOKEN | The process environment | Merged — each variable overrides only its own field |
| 4 | active-context in ~/.sem.yaml | Shared by every session on the machine | The starting point when nothing above applies |
The two halves of that table behave differently, and the difference matters.
A context named by --context or SEM_CONTEXT supplies the host and the token together and fully replaces the sources below it, so its credentials are never mixed with the environment variables.
Without a selector, rows 3 and 4 merge field by field. SEMAPHORE_HOST and SEMAPHORE_API_TOKEN are independent, so setting only one leaves the other coming from the active context. Exporting SEMAPHORE_API_TOKEN on its own — which the hosted MCP server setup instructs you to do — means every later sem-ai command in that shell sends that token to whichever host active-context currently names. Set both variables together, or pin a context, whenever the host and the token have to belong to the same organization.
General syntax
sem-ai <command> [subcommand] [flags]
Global flags:
| Flag | Description |
|---|---|
--context | Run against a named context from ~/.sem.yaml without changing the active one. See pinning an organization |
--format or -f | Output format: json (default), table, yaml |
--verbose or -v | Show HTTP requests for debugging |
--examples | Show usage examples for any command |
--help or -h | Help for any command |
Self-discovery
sem-ai discover
Returns a structured map of every command, its flags, and examples. Designed for AI agents to self-orient without documentation.
sem-ai discover
sem-ai discover --format table
sem-ai <command> --examples
Every command supports --examples to show usage examples:
sem-ai diagnose --examples
sem-ai pipeline promote --examples
Working with projects
sem-ai project list
List all projects in the organization:
sem-ai project list
sem-ai project show
Show project details:
sem-ai project show <project-name>
sem-ai project create
Create a project from a git repository. With no flags it uses the origin remote of the current directory and derives the name from the repo URL, then bootstraps an initial .semaphore/semaphore.yml (unless --skip-yaml). If a project with the same name already exists it returns the existing one, unless --fail-on-exists is set.
sem-ai project create
sem-ai project create --repo-url git@github.com:org/repo.git
sem-ai project create --name my-project --github-integration github_app
| Flag | Default | Description |
|---|---|---|
--repo-url | origin of cwd | git repository URL |
--name | derived from repo URL | project name |
--github-integration | github_token | GitHub integration: github_token or github_app |
--remote | origin | git remote to detect when --repo-url is not set |
--skip-yaml | false | don't generate .semaphore/semaphore.yml in cwd |
--fail-on-exists | false | exit non-zero if a project with the same name already exists |
sem-ai project update
Update project settings:
sem-ai project update <project-name> --visibility public
sem-ai project update <project-name> --description "My app"
sem-ai project delete
Delete a project:
sem-ai project delete <project-name>
Working with workflows
sem-ai workflow list
List workflows for a project:
sem-ai workflow list --project <project-name>
sem-ai workflow list --project <project-name> --branch main
sem-ai workflow show
Show workflow details:
sem-ai workflow show <workflow-id>
sem-ai workflow run
Trigger a new workflow run (reruns the latest workflow):
sem-ai workflow run --project <project-name>
sem-ai workflow run --project <project-name> --branch feature-x
sem-ai workflow rerun
Rerun a specific workflow:
sem-ai workflow rerun <workflow-id>
sem-ai workflow stop
Stop a running workflow:
sem-ai workflow stop <workflow-id>
Working with pipelines
sem-ai pipeline show
Show pipeline with blocks and jobs tree:
sem-ai pipeline show <pipeline-id>
sem-ai pipeline list
List pipelines for a project:
sem-ai pipeline list --project <project-name>
sem-ai pipeline stop
Stop a running pipeline:
sem-ai pipeline stop <pipeline-id>
sem-ai pipeline rebuild
Rebuild only failed blocks (partial rebuild):
sem-ai pipeline rebuild <pipeline-id>
sem-ai pipeline promote
Trigger a promotion (deployment). This is a safety-gated operation:
- Without
--confirm: dry run showing what would happen - With
--confirm: actually executes the promotion
# Dry run
sem-ai pipeline promote <pipeline-id> --target "Deploy to Staging"
# Execute
sem-ai pipeline promote <pipeline-id> --target "Deploy to Staging" --confirm
# Override conditions (promote despite failures)
sem-ai pipeline promote <pipeline-id> --target "Deploy to Staging" --confirm --override
# With parameters
sem-ai pipeline promote <pipeline-id> --target "Production" --confirm --param version=1.2.3
sem-ai pipeline topology
Show the block dependency graph:
sem-ai pipeline topology <pipeline-id>
Working with jobs
sem-ai job list
List jobs, optionally filtered by state:
sem-ai job list --states RUNNING --states QUEUED
sem-ai job list --states FINISHED
sem-ai job show
Show job details:
sem-ai job show <job-id>
sem-ai job log
Fetch structured job logs:
sem-ai job log <job-id>
sem-ai job log <job-id> --format table
sem-ai job stop
Stop a running job:
sem-ai job stop <job-id>
Managing organization members and roles
sem-ai org member list
List organization members, optionally filtered by member type:
sem-ai org member list
sem-ai org member list --type service_account
sem-ai org member list --type group
| Flag | Default | Description |
|---|---|---|
--type | user | filter by member type: user, service_account, or group |
sem-ai org member add
Invite a person to the organization by their SCM handle:
sem-ai org member add --provider github --handle octocat
sem-ai org member add --provider github --handle octocat --role <role-id> --name "Octo Cat" --email octo@example.com
sem-ai org member add --provider bitbucket --handle jdoe --uid 557058:1a2b3c
| Flag | Default | Description |
|---|---|---|
--handle | — | SCM login/handle of the person to invite (required) |
--provider | — | SCM provider: github, bitbucket, or gitlab (required) |
--uid | — | SCM user ID (required for bitbucket) |
--role | — | org role ID to assign |
--name | — | display name |
--email | — | email address |
sem-ai org member set-role
Assign or change a member's org-level role. This is an upsert — the same command creates the initial role assignment and changes it later.
sem-ai org member set-role <user-id> <role-id>
sem-ai org member set-role <service-account-id> <role-id>
sem-ai org member remove
Remove a member or service account from the organization:
sem-ai org member remove <user-id>
sem-ai org role list
List organization roles, including built-in and custom roles:
sem-ai org role list
sem-ai org role show
Show role details, including its permission set:
sem-ai org role show <role-id>
sem-ai org role create
Create a custom role:
sem-ai org role create deployer --permissions "project.view,project.job.rerun"
sem-ai org role create viewer --scope project --permissions "project.view"
| Flag | Default | Description |
|---|---|---|
--description | — | role description |
--scope | org | role scope: org or project |
--permissions | — | comma-separated permission names, e.g. organization.people.view,organization.people.manage |
sem-ai org role update
Update a custom role:
sem-ai org role update <role-id> --permissions "project.view,project.job.rerun"
| Flag | Default | Description |
|---|---|---|
--name | — | new role name |
--description | — | new role description |
--permissions | — | comma-separated permission names (replaces the existing set) |
sem-ai org role delete
Delete a custom role:
sem-ai org role delete <role-id>
sem-ai permission list
List available permissions, optionally filtered by scope:
sem-ai permission list
sem-ai permission list --scope project
| Flag | Default | Description |
|---|---|---|
--scope | — | filter by scope: org or project |
Managing groups
sem-ai group list
List groups in the organization:
sem-ai group list
sem-ai group create
Create a group:
sem-ai group create backend-team
sem-ai group create backend-team --description "Backend engineers" --members "id1,id2"
| Flag | Default | Description |
|---|---|---|
--description | — | group description |
--members | — | comma-separated member IDs to add on creation |
sem-ai group update
Update a group's name, description, or membership:
sem-ai group update <group-id> --add "id1,id2" --remove "id3"
sem-ai group update <group-id> --name "new-name"
| Flag | Default | Description |
|---|---|---|
--name | — | new group name |
--description | — | new group description |
--add | — | comma-separated member IDs to add |
--remove | — | comma-separated member IDs to remove |
sem-ai group delete
Delete a group:
sem-ai group delete <group-id>
Managing service accounts
sem-ai service-account list
List service accounts in the organization:
sem-ai service-account list
sem-ai service-account create
Create a service account. The API token is only ever returned in this create response — save it now, since service-account show never returns it again (use regenerate-token if it's lost).
sem-ai service-account create ci-bot
sem-ai service-account create ci-bot --description "Bot for CI pipelines"
| Flag | Default | Description |
|---|---|---|
--description | — | service account description |
sem-ai service-account show
Show service account details:
sem-ai service-account show <service-account-id>
sem-ai service-account update
Update a service account's name or description. The API replaces the whole record on update, so if --name is omitted, sem-ai first fetches the current name and resends it unchanged to avoid clearing it.
sem-ai service-account update <service-account-id> --name "new-name"
sem-ai service-account update <service-account-id> --description "new description"
| Flag | Default | Description |
|---|---|---|
--name | — | new service account name |
--description | — | new service account description |
sem-ai service-account delete
Delete a service account:
sem-ai service-account delete <service-account-id>
sem-ai service-account deactivate
Deactivate a service account, disabling its token without deleting the account:
sem-ai service-account deactivate <service-account-id>
sem-ai service-account reactivate
Reactivate a previously deactivated service account:
sem-ai service-account reactivate <service-account-id>
sem-ai service-account regenerate-token
Regenerate a service account's API token. The old token is invalidated and the new one is printed once in the response — save it immediately.
sem-ai service-account regenerate-token <service-account-id>
Managing project members
sem-ai project member list
List project members:
sem-ai project member list my-project
sem-ai project member set-role
Set a project-level role for a member:
sem-ai project member set-role my-project <user-id> <role-id>
sem-ai project member remove
Remove a member's project-level role:
sem-ai project member remove my-project <user-id>
Managing pre-flight checks
Pre-flight checks are commands Semaphore runs during pipeline initialization, before any block starts. There is one organization-wide check and one check per project; both run, and either can stop a pipeline. Scope follows --project: pass it for a project's check, omit it for the organization-wide one.
sem-ai pfc show
Show the commands, secrets, and agent of a pre-flight check:
# the organization-wide check
sem-ai pfc show
# one project's check
sem-ai pfc show --project my-project
sem-ai pfc apply
Create or replace a pre-flight check. This is a privileged change — the commands run at the start of every workflow in the scope, and a non-zero exit stops the pipeline before any block runs. Apply replaces the whole check rather than merging into it.
# organization-wide gate, two commands and one secret
sem-ai pfc apply --command checkout --command 'make security-scan' --secret scanner-token
# project-level gate on a specific agent
sem-ai pfc apply --project my-project \
--command './scripts/gate.sh' \
--machine-type e2-standard-2 --os-image ubuntu2204
The spec can come from a YAML or JSON file instead of flags, but not both:
sem-ai pfc apply --project my-project --from-file pfc.yml
commands:
- checkout
- make security-scan
secrets:
- scanner-token
agent:
machine_type: e2-standard-2
os_image: ubuntu2204
Requires organization.pre_flight_checks.manage or project.pre_flight_checks.manage.
sem-ai pfc delete
Remove a pre-flight check:
sem-ai pfc delete --project my-project
Compound commands
These commands compose multiple API calls into a single operation.
sem-ai status
Quick CI status for the current branch, a pull request, or a project. When run inside a git checkout, project and branch are auto-detected from the remote and HEAD, and status prefers the workflow for the exact HEAD commit, falling back to the latest run on the branch.
sem-ai status # current repo, branch, commit
sem-ai status --branch main
sem-ai status --pr 422 # match a pull request's workflow (overrides --branch)
sem-ai status --project <project-name> --branch feature-x
If the git remote maps to several Semaphore projects that each ran the commit, status returns all of them rather than guessing — pass --project to disambiguate.
With --exit-code, status sets a poll-friendly process exit code instead of requiring output parsing — useful for shell wait-loops:
| Exit | Meaning |
|---|---|
0 | passed |
1 | failed |
2 | ambiguous (multiple matching projects) |
3 | no workflow found / project not detected |
8 | pending / running |
until sem-ai status --exit-code; do sleep 20; done # wait until green
sem-ai diagnose
One-command failure diagnosis. Composes workflow lookup, pipeline details, failed jobs, log tails, and parsed test results into a single structured response.
sem-ai diagnose
sem-ai diagnose <workflow-id>
sem-ai diagnose --project <project-name> --branch main
Returns structured output with:
- Pipeline state and result
- Failed blocks and jobs
- Log tails with failed commands highlighted
- Parsed test results with file, line, and error message
stop_reasonwhen a job was stopped by a signal rather than a test failure — exit130(SIGINT),137(SIGKILL / OOM), or143(SIGTERM). This is most useful for exit130, where the job shows up asSTOPPEDand failure notifications are suppressed, so the cause is otherwise easy to miss.
sem-ai health
Project health summary with pass rates, failure trends, and deployment status:
sem-ai health --project <project-name>
sem-ai watch
Poll a workflow until it completes, streaming status updates:
sem-ai watch <workflow-id>
sem-ai watch <workflow-id> --interval 10s
sem-ai promote-and-wait
Promote a pipeline and block until the promoted pipeline finishes:
# Dry run
sem-ai promote-and-wait <pipeline-id> --target "Deploy to Staging"
# Execute and wait
sem-ai promote-and-wait <pipeline-id> --target "Deploy to Staging" --confirm
sem-ai open
Open the latest workflow for the current branch in the browser:
sem-ai open
sem-ai open --project my-app
sem-ai open --workflow <workflow-id>
sem-ai version
Print version information as JSON:
sem-ai version
sem-ai version --check # also check GitHub for a newer release
When sem-ai is installed via the Claude Code / Codex plugin, a SessionStart hook surfaces a one-line upgrade notice at most once every few hours. Opt out with export SEM_AI_NO_UPDATE_CHECK=1.
sem-ai rerun-failed
Rebuild only failed blocks in a pipeline:
sem-ai rerun-failed <pipeline-id>
sem-ai critical-path
Show the longest dependency chain (bottleneck) in a pipeline:
sem-ai critical-path <pipeline-id>
sem-ai blast-radius
Show which blocks failed as root causes vs which were canceled due to upstream failures:
sem-ai blast-radius <pipeline-id>
sem-ai blast-radius <pipeline-id> --block "Build"
Analytics
Historical pipeline and workflow analytics. All analytics commands share a common set of flags:
| Flag | Default | Description |
|---|---|---|
--project | auto-detected | Project name |
--branch | all branches | Filter by branch |
--days | 7 | Time window in days |
--limit | 100 | Max workflows to analyze |
sem-ai analytics summary
All-in-one analytics overview: pass rate, duration stats (avg/p50/p95), phase breakdown (compile, queue, execution), failing blocks, deploy count, and trigger distribution.
sem-ai analytics summary --project my-app
sem-ai analytics summary --project my-app --days 30 --branch main
sem-ai analytics duration
Pipeline duration trends with avg, p50, p95, min, and max, plus a phase breakdown showing where time is spent (compile, queue, execution).
sem-ai analytics duration --project my-app --days 30
sem-ai analytics failures
Block-level failure rates across analyzed pipelines, ranked by failure count. Also reports overall pass rate and failure reasons (test failure, stuck, canceled, etc.).
sem-ai analytics failures --project my-app --days 14
sem-ai analytics queue
Queue wait time analysis (avg, p50, p95, min, max) — measures time between a job being queued and starting execution.
sem-ai analytics queue --project my-app --days 7
sem-ai analytics deploys
Deploy frequency and promotion stats: total deploys, deploys per day, and deploys per week.
sem-ai analytics deploys --project my-app --days 30
sem-ai analytics trend
Week-over-week trends for pass rate, duration, queue time, failure reasons, and trigger distribution. Uses --weeks instead of --days.
| Flag | Default | Description |
|---|---|---|
--weeks | 4 | Number of weeks to analyze |
--limit | 200 | Max workflows to analyze |
sem-ai analytics trend --project my-app --weeks 4
sem-ai analytics trend --project my-app --weeks 8 --branch main
Returns an array of weekly buckets plus an overall trend field: improving, degrading, or stable.
Pipeline insights
Server-side pipeline insights, keyed by a specific pipeline YAML file. This is distinct from analytics, which sem-ai computes client-side from recent workflows: insights reads pre-aggregated metrics from Semaphore for one pipeline file.
All insights subcommands share these flags:
| Flag | Default | Description |
|---|---|---|
--project | auto-detected | project name or ID |
--pipeline-file | — | pipeline YAML path, e.g. .semaphore/semaphore.yml (required) |
--branch | all branches | branch name |
--from | — | start date YYYY-MM-DD |
--to | — | end date YYYY-MM-DD |
--aggregate | daily | aggregation: daily or range |
sem-ai insights performance
Pipeline duration metrics over time.
sem-ai insights performance --project my-app --pipeline-file .semaphore/semaphore.yml --branch main
sem-ai insights reliability
Pipeline pass/fail rate over time.
sem-ai insights reliability --project my-app --pipeline-file .semaphore/semaphore.yml --branch main
sem-ai insights frequency
Pipeline run frequency over time.
sem-ai insights frequency --project my-app --pipeline-file .semaphore/semaphore.yml --branch main
Test intelligence
sem-ai test summary
AI-friendly test summary for a pipeline. Parses test results from job logs and artifacts.
sem-ai test summary --pipeline <pipeline-id>
sem-ai test report
Detailed test results with individual test cases:
sem-ai test report --pipeline <pipeline-id>
sem-ai test flaky
Detect flaky tests by analyzing recent workflow runs:
sem-ai test flaky --project <project-name>
sem-ai test flaky --project <project-name> --branch main --count 10
Flaky tests
History-backed flaky-test signals for a project, sourced from Semaphore's flaky-test history (per-context pass rate, p95, disruption counts). This is distinct from test flaky, which is a quick single-pipeline snapshot computed from JUnit artifacts; the flaky command group reads the accumulated history instead.
All flaky subcommands require --project (name or ID).
sem-ai flaky list
List a project's flaky tests. The heavy per-test disruption_history histogram is omitted by default for compact output; pass --full to include it.
sem-ai flaky list --project my-app
sem-ai flaky list --project my-app --sort-field pass_rate --sort-dir asc
sem-ai flaky list --project my-app --full
| Flag | Default | Description |
|---|---|---|
--page | 1 | page number |
--page-size | 20 | results per page |
--sort-field | — | sort field, e.g. total_disruptions_count, pass_rate |
--sort-dir | — | sort direction (asc or desc) |
--full | false | include full disruption_history per test |
sem-ai flaky show
Show details for a single flaky test (per-context pass rate, p95, disruptions). test_id is positional.
sem-ai flaky show <test_id> --project my-app
sem-ai flaky disruptions
List the individual disruption occurrences for a flaky test.
sem-ai flaky disruptions <test_id> --project my-app --page-size 50
| Flag | Default | Description |
|---|---|---|
--page | 1 | page number |
--page-size | 10 | results per page |
sem-ai flaky failure
Show the real failure behind a flaky test: resolves its latest disruption's job, fetches that job's log, and extracts the failing assertion / message. Use --run-id to point at a specific job directly instead of resolving the latest disruption.
sem-ai flaky failure <test_id> --project my-app
sem-ai flaky failure <test_id> --project my-app --run-id <job-id>
sem-ai flaky trends
Project-level flaky / disruption count time series.
sem-ai flaky trends --project my-app
sem-ai flaky trends --project my-app --metric disruptions
| Flag | Default | Description |
|---|---|---|
--metric | flaky | series: flaky or disruptions |
Testbox
Testbox lets you run commands in a real Semaphore CI environment before pushing. It creates a warm VM with your project's machine type and syncs your local code.
sem-ai testbox warmup
Start a testbox:
sem-ai testbox warmup --project <project-name>
sem-ai testbox warmup --project <project-name> --machine f1-standard-4 --duration 30m
sem-ai testbox warmup --project <project-name> --os-image ubuntu2404
Defaults: --machine f1-standard-2, --os-image ubuntu2204, --duration 30m.
sem-ai testbox run
Sync local changes and run a command:
sem-ai testbox run --id <testbox-id> "go test ./..."
sem-ai testbox run --id <testbox-id> "make build"
sem-ai testbox ssh
Open an interactive SSH session:
sem-ai testbox ssh --id <testbox-id>
sem-ai testbox stop
Stop a running testbox:
sem-ai testbox stop --id <testbox-id>
Secrets
sem-ai secret list
List organization-level secrets, or project-level with --project:
sem-ai secret list
sem-ai secret list --project <project-name>
sem-ai secret show
Show secret details:
sem-ai secret show <secret-name>
sem-ai secret show <secret-name> --project <project-name>
sem-ai secret create
Create a secret with environment variables:
sem-ai secret create <secret-name> --env KEY=VALUE --env DB_URL=postgres://...
sem-ai secret create <secret-name> --project <project-name> --env API_KEY=abc123
sem-ai secret update
Update a secret (replaces env vars):
sem-ai secret update <secret-name> --env KEY=NEW_VALUE
sem-ai secret delete
Delete a secret:
sem-ai secret delete <secret-name>
Deployment targets
sem-ai deploy targets
List deployment targets:
sem-ai deploy targets --project <project-name>
sem-ai deploy show
Show deployment target details:
sem-ai deploy show <target-id>
sem-ai deploy history
Show deployment history:
sem-ai deploy history <target-id>
sem-ai deploy create
Create a deployment target:
sem-ai deploy create <name> --project <project-name> --url https://staging.example.com
sem-ai deploy activate / deactivate
Activate or deactivate a deployment target:
sem-ai deploy activate <target-id>
sem-ai deploy deactivate <target-id>
sem-ai deploy delete
Delete a deployment target:
sem-ai deploy delete <target-id>
Notifications
sem-ai notification list
List notification rules:
sem-ai notification list
sem-ai notification show
Show notification details:
sem-ai notification show <name>
sem-ai notification delete
Delete a notification rule:
sem-ai notification delete <name>
Scheduled tasks
sem-ai task list
List scheduled tasks:
sem-ai task list --project <project-name>
sem-ai task show
Show task details:
sem-ai task show <task-id>
sem-ai task create
Create a scheduled task:
sem-ai task create <name> --project <project-name> --branch main --file .semaphore/nightly.yml --cron "0 2 * * *"
sem-ai task run
Trigger a task to run now:
sem-ai task run <task-id>
sem-ai task delete
Delete a task:
sem-ai task delete <task-id>
Self-hosted agents
sem-ai agent types
List self-hosted agent types:
sem-ai agent types
sem-ai agent show
Show agent type details:
sem-ai agent show <type-name>
sem-ai agent list
List agents for a given type:
sem-ai agent list --type <type-name>
sem-ai agent delete
Delete an agent type:
sem-ai agent delete <type-name>
Artifacts
sem-ai artifact list
List artifacts for a job, workflow, or project:
sem-ai artifact list --scope jobs --id <job-id>
sem-ai artifact list --scope workflows --id <workflow-id>
sem-ai artifact get
Download an artifact:
sem-ai artifact get --scope jobs --id <job-id> --path test-results/junit.json --output ./results.json
Troubleshooting
Server-side diagnostics for workflows, pipelines, and jobs:
sem-ai troubleshoot workflow <id>
sem-ai troubleshoot pipeline <id>
sem-ai troubleshoot job <id>
YAML validation
Validate a pipeline YAML file against the Semaphore API:
sem-ai yaml validate --file .semaphore/semaphore.yml
MCP server
sem-ai can run as an MCP (Model Context Protocol) server, exposing all commands as native tools for AI agents.
Starting the server
sem-ai mcp
Claude Code configuration
Add to your project's .mcp.json:
{
"mcpServers": {
"sem-ai": {
"command": "sem-ai",
"args": ["mcp"]
}
}
}
Most commands become available as MCP tools (e.g., project_list, diagnose, status, blast-radius). The long-running commands watch and promote-and-wait are excluded, since they would block the single in-memory command tree; use status --exit-code in a poll loop instead. The onboarding commands signin and connect are excluded too — the device flow holds the server lock and hides the one-time code it prints, and connect's two positional arguments cannot be expressed as tool arguments — so run those in a shell. The server starts once and handles all tool calls in-process — no new process per call.
To tie a server to one organization, pass --context in its args, as described under pinning an organization. One tool is worth knowing about: context_switch remains available and rewrites the shared active-context key for every session on the machine. It ignores both the server pin and a per-call context argument, so never call it to scope a request — pass context on the call instead.
Agent skills
sem-ai ships its skills as a plugin for Claude Code and Codex. The plugin bundles the skills, the MCP server, and a SessionStart hook (release-update check + Semaphore-repo awareness).
Claude Code / Codex plugin
Claude Code:
/plugin marketplace add semaphoreio/sem-ai
/plugin install sem-ai@semaphoreio
Codex CLI:
codex plugin marketplace add semaphoreio/sem-ai
codex plugin add sem-ai@semaphoreio
The bundle includes these skills: debug-pipeline, deploy, fix-flaky, gha-to-semaphore, init, manage-infra, probe-agent-environment, project-health, sem-ai-bootstrap, semaphore-blocks, semaphore-ci, semaphore-promotions, semaphore-test-results, semaphore-toolbox, test-intelligence, testbox, and watch-after-push. They give agents context on when and how to use each sem-ai command without reading this reference.
npx skills
You can also install the skill bundle with the cross-agent skills tool, which supports Claude Code, Cursor, Codex, OpenCode, and many other agents. It discovers sem-ai's skills from the plugin manifest, so no clone is required:
npx skills add semaphoreio/sem-ai --list # list available skills
npx skills add semaphoreio/sem-ai --all # install all skills
npx skills add semaphoreio/sem-ai --skill semaphore-ci --skill watch-after-push
npx skills add semaphoreio/sem-ai --all -g # user level (all repos)
npx skills add semaphoreio/sem-ai --all --agent cursor opencode
This installs the skill instructions only — not the MCP server. The sem-ai binary must be installed and connected first, since every skill calls it.
Differences from sem CLI
| Feature | sem | sem-ai |
|---|---|---|
| Output format | Human text | JSON (default), table, yaml |
| Self-discovery | --help only | discover + --examples on every command |
| Failure diagnosis | Manual (multiple commands) | diagnose (one command, full root cause) |
| Test intelligence | None | test summary, test flaky, flaky history, insights |
| Pipeline topology | None | topology, critical-path, blast-radius |
| Testbox | sem debug (limited) | testbox warmup/run/ssh/stop with file sync |
| MCP server | None | sem-ai mcp |
| Health reports | None | health (pass rates, trends, verdict) |
| Deploy safety | Fire-and-forget | Dry run by default, --confirm required |
| Configuration | ~/.sem.yaml | ~/.sem.yaml (shared, compatible) |