OrcAI.Tool
0.8.0
dotnet tool install --global OrcAI.Tool --version 0.8.0
dotnet new tool-manifest
dotnet tool install --local OrcAI.Tool --version 0.8.0
#tool dotnet:?package=OrcAI.Tool&version=0.8.0
nuke :add-package OrcAI.Tool --version 0.8.0
OrcAI CLI
<p align="center"> <img src="assets/orcai_banner.png" alt="OrcAI" /> </p>
A CLI tool for orchestrating bulk GitHub work across many repositories. From a single YAML config, OrcAI creates a GitHub Project, opens templated issues in every target repo, and hands them off to whoever (or whatever) does the work — a human teammate, a bot, or an AI agent like GitHub Copilot or OpenCode.
Features
- Declarative YAML jobs — one config defines the project, target repos, issue template, assignment behaviour, nudge policy, and notification template.
- Bulk, idempotent issue creation across any number of repos — a lock file makes re-runs free; glob and brace expansion (
"jobs/**/*.{yml,yaml}") for fanning out. - GitHub Project auto-management — finds or creates the board and links every issue to it.
- Assign to anyone —
assign.toaccepts any GitHub user, bot, or GitHub App handle. Default is@copilot, but humans, OpenCode, and custom App bots all work. - Comment-based triggers —
assign.via: commentposts a slash command (e.g./opencode) instead of assigning, for agents that listen for mentions.comment-and-assigndoes both. - Tag anyone in templated comments —
assign.comment,nudge.comment, andnotifyall support{assignee},{job.owner}, and{repo.codeowners}tokens, resolved from YAML and CODEOWNERS files at runtime. orcai nudge— re-trigger stale issues (no linked PR yet) by reassignment, comment, or both.orcai notify— broadcast a templated comment to issues and/or PRs from the lock file; filter by state, dry-run, and inject extra--data key=valuetemplate variables.- Auto issue-body updates — when the Markdown template changes, existing issues' bodies are updated without re-running the structural work (hash-based detection).
- Robust at scale — built-in rate limiting (60 writes/min, configurable) with exponential-backoff retry; closed-issue policy (
create/reopen/skip/fail); concurrency control;--continue-on-error; JSON output for CI. - Multiple auth methods — ambient
ghCLI, PAT, or GitHub App (manifest flow supported viaorcai auth create-app). A PAT is only required when the assignee is@copilot.
Installation
OrcAI is distributed as a .NET global tool. Requires .NET 10 or later.
dotnet tool install --global OrcAI.Tool
Then run it as orcai.
Prerequisites
ghCLI: Install from cli.github.com — must be installed and onPATH- Authentication: The easiest option is to ensure
ghis authenticated (gh auth login). OrcAI will use it automatically. For other methods see docs/cli-reference.md.
Quick start
1. Authenticate
The simplest option — if you already use the gh CLI, just make sure it's authenticated:
gh auth login
That's it. OrcAI will pick up the token automatically.
For PAT, GitHub App, or environment variable auth see docs/cli-reference.md.
2. Run a job
# Single config file
orcai run jobs/my-upgrade.yml
# All configs in a directory (quote the glob to prevent shell expansion)
orcai run "jobs/*.yml" --continue-on-error --json
# Limit concurrency to avoid rate limits
orcai run "jobs/*.yml" --max-concurrency 2
run finds or creates a GitHub Project, creates issues from your template, adds them to the project, and triggers the configured assignee — whether that's @copilot, another bot, an AI agent like OpenCode, or a human teammate. Triggering can be via assignment, a templated comment (e.g. a slash command), or both. On success a lock file (<basename>.lock.json) is written alongside the YAML for fast idempotent re-runs.
Commands
| Command | Description |
|---|---|
orcai auth pat/app/create-app/switch |
Store credentials or switch profiles for all other commands |
orcai generate |
Scaffold a YAML job config and stub issue template |
orcai run |
Execute a bulk upgrade job (supports globs, concurrency control, JSON output) |
orcai nudge |
Re-trigger stale issues with no linked PR (reassign, comment, or both) |
orcai notify |
Post a templated comment to issues and/or PRs from the lock file |
orcai validate |
Validate YAML config(s) and verify all repos are accessible |
orcai info |
Display the current state of a job |
orcai cleanup |
Tear down everything created by run |
For full flag details, output formats, lock file schema, and advanced usage see docs/cli-reference.md. For config file settings see docs/config.md.
The original Nushell scripts (orca.nu, cleanup.nu) are documented in docs/nushell-scripts.md.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
This package has no dependencies.
| Version | Downloads | Last Updated |
|---|---|---|
| 0.8.0 | 0 | 6/9/2026 |
| 0.7.5-beta6 | 0 | 6/9/2026 |
| 0.7.4-beta5 | 90 | 5/21/2026 |
| 0.7.3-beta4 | 102 | 5/19/2026 |
| 0.7.2-beta3 | 96 | 5/18/2026 |
| 0.7.1-beta2 | 107 | 5/18/2026 |
| 0.7.0-beta1 | 94 | 5/13/2026 |
| 0.6.0 | 111 | 5/7/2026 |
| 0.5.1 | 253 | 3/17/2026 |
| 0.5.0 | 127 | 3/16/2026 |
| 0.4.4 | 127 | 3/16/2026 |
| 0.4.3 | 117 | 3/16/2026 |
| 0.4.2 | 126 | 3/16/2026 |
| 0.4.1 | 119 | 3/15/2026 |
| 0.4.0 | 115 | 3/13/2026 |
| 0.3.0 | 120 | 3/11/2026 |
`orcai run --dryrun` — preview what would be created, reopened, or updated without making any GitHub API calls or writing the lock file. Read-only lookups still run so the preview reflects current state. Outcomes are reported per repo as `would create`, `would reopen`, or `would update`, with a summary line and `dryRunWouldCreate` / `dryRunWouldReopen` / `dryRunWouldUpdate` counts in `--json`.
`orcai notify` command — posts a templated comment to issues and/or PRs recorded in the lock file. Supports the same `{assignee}`, `{job.owner}`, and `{repo.codeowners}` template tokens as `nudge.comment`.
`--target issues|prs|both` — which lock file items to notify (default: `issues`).
`--state open|closed|all` — filter by current GitHub state before commenting (default: `open`); `closed` matches both closed and merged PRs; `all` skips the live state check entirely.
`--dryrun` — preview which items would be notified without posting any comments.
`--verbose` — print per-item progress to stderr.
`--template <string>` — inline comment template supplied directly on the CLI; overrides `notify.comment` from YAML/config.
`--data key=value` — inject an extra template variable (repeatable). E.g. `--data sprint=42`.
`--json-data <json>` — inject extra template variables as a JSON object string. Merged with `--data`; `--data` takes precedence on key conflicts. User-supplied values override built-in tokens (`{assignee}` etc.) when the same key is used.
`notify` block in YAML job config and global/local JSON config — configures the comment template for `orcai notify`.
`notify.comment` — comment body template. Supports the same `{assignee}`, `{job.owner}`, and `{repo.codeowners}` tokens as `nudge.comment`.
`orcai run` records repos that were skipped because they are archived in a new `skippedRepos` field in the lock file. The run summary and `--json` output include a `skippedArchived` count and status.
`orcai run` detects when the lock file points to a deleted or transferred issue and recreates the issue in place instead of failing. New `staleIssueRecreated` count/status in the summary and `--json` output; the lock file is rewritten with the new issue numbers.
`orcai run` now persists error information in the lock file when a repo fails to process, allowing errors to be surfaced in subsequent runs instead of being silently ignored as "not created". New `failures` field in the lock file maps repo, attempts, and action that failed.
`orcai nudge` and `orcai notify` rename `--dry-run` to `--dryrun` for consistency with `cleanup` (and the new `run --dryrun`). The old spelling is no longer accepted.
Comment-building logic (template variable resolution + `PostComment`) extracted from `RunCommand` and `NudgeCommand` into a shared internal `Comments` module, used by all three comment-posting paths.
`orcai run` now automatically updates issue bodies when the Markdown template changes. A `templateHash` field is stored in the lock file alongside the existing `yamlHash`, allowing the tool to detect which changed:
Either `.yml` or `.md` changed → structural re-run via `runFull`, honouring `onClosedIssue` policy. If the template hash changed, issue bodies are refreshed for any repos reconciled as `AlreadyExisted` or `Reopened`.
Neither changed → fast path, zero network calls (unchanged from before).
`--skip-lock` → structural re-run plus unconditional body refresh for `AlreadyExisted` / `Reopened`.
Old lock files without `templateHash` are treated as changed, triggering a one-time body sync on next run.
`assign` block in YAML job config and global/local JSON config — configures who receives the issue and how they are triggered. Applies to both `orcai run` and `orcai nudge`.
`assign.to` — assignee handle (default: `@copilot`). Accepts any GitHub user, bot, or GitHub App bot handle. Note: assigning `@copilot` requires a PAT (`ORCAI_PAT`) regardless of primary auth method, as GitHub Copilot can only be assigned via a user-level token.
`assign.via` — trigger method: `assign` (default), `comment`, or `comment-and-assign`. Use `comment` for agents triggered by slash commands (e.g. OpenCode's `/opencode`).
`assign.comment` — comment body posted when `via` includes `comment`. Supports template tokens (see below).
`nudge` block in YAML job config and global/local JSON config — configures how `orcai nudge` re-triggers the assignee on stale issues.
`nudge.mode` — `reassign` (default), `comment-only`, or `comment-and-reassign`.
`nudge.comment` — comment body posted on nudge. Supports template tokens (see below).
Dynamic template tokens in `assign.comment` and `nudge.comment` — placeholders resolved at runtime:
`{assignee}` — the configured `assign.to` handle.
`{job.owner}` — who owns the orcai job. Resolved from `job.owner` in the YAML (highest priority), then the catch-all `*` owner from a `CODEOWNERS` file in the current repository (checked at `CODEOWNERS`, `.github/CODEOWNERS`, `docs/CODEOWNERS`). Left unreplaced if neither is found.
`{repo.codeowners}` — the catch-all `*` owner from the target repository's `CODEOWNERS` file (fetched from GitHub). Left unreplaced if no `CODEOWNERS` is present or it has no `*` rule.
`job.owner` field in the YAML `job` block — statically sets the job owner for use in comment templates via `{job.owner}`. Overrides any CODEOWNERS-based discovery.
`orcai nudge` command now documented in the CLI reference.
Generated YAML scaffold now includes commented-out `assign:` and `nudge:` example blocks instead of the unused `copilot:` block.
The `copilot:` block previously scaffolded by `orcai generate` has been removed. It was never parsed and is superseded by the `assign:` block.
`--skip-copilot` is superseded by `assign.via: comment` (skips assignment while still allowing a trigger comment). The flag remains supported for backwards compatibility.
`ORCAI_LOG_LEVEL` environment variable — controls log verbosity. Accepts any `Microsoft.Extensions.Logging.LogLevel` name (`Trace`, `Debug`, `Information`, `Warning`, `Error`, `Critical`, `None`). Defaults to `Warning`.
Lock file schema: new `skippedRepos: string[]` field. Old lock files without this field still load (treated as empty); the field is populated on the next run.
Template bumps now go through `runFull`, honouring `onClosedIssue` policy and refreshing the body of reopened issues. Previously, editing only the MD template could silently rewrite the body of a closed issue and skip the body refresh for reopened issues.
`orcai run` no longer creates a duplicate issue when the GitHub API lookup itself fails. Transient `gh` errors during open- or closed-issue lookup (rate limits, network resets, exhausted retries) are now surfaced as a per-repo error instead of being silently treated as "no matching issue". This also restores `--on-closed-issue` semantics on lookup failures — the configured action (`reopen` / `skip` / `fail`) is no longer bypassed when the closed-issue query errors.
Assignment via GitHub App auth now only requires a PAT (`ORCAI_PAT`) when the assignee is `@copilot`. Assigning human users or other bots with a GitHub App (which has `issues: write` permission) no longer warns or skips — the PAT constraint was previously applied to all assignees, not just Copilot.
`orcai cleanup` no longer fails when a project, issue, or PR has already been deleted — the operation is treated as success and a warning is emitted instead.
Issue lookup now uses GitHub's title search (`in:title`) with a 100-result limit, preventing missed matches on repos with more than 30 open or closed issues.
PR lookup for an issue now queries GitHub's GraphQL API (`Issue.closingPullRequests`) instead of listing all PRs in the repo and filtering in memory — fixes silent data loss on repos with more than 30 PRs.
`orcai run` no longer errors out on archived repositories. Each repo is pre-checked with `gh repo view --json isArchived`; archived repos are skipped with a single informational line instead of cascading `Repository was archived so is read-only` errors from label and issue writes.
`orcai run --auto-create-labels` no longer produces spurious errors when a label already exists. Two fixes: (1) `gh label list` now uses `--limit 1000` so labels past page 1 are detected by the pre-check, and (2) `CreateLabel` is idempotent — a GitHub "already exists" / "already been taken" response is downgraded to success with a warning log.