Skip to main content

software-factory

Set up an OpenHands "software factory" on GitHub fast: ask only for the repositories and where tickets live (GitHub issues, Jira or Linear), create a working issue-to-PR and PR-review pipeline from the automation catalog with defaults, show what runs, then let the user stop or extend it one step at a time (tune settings, AGENTS.md maintainer, auto-merge, stale-CI and QA add-ons).

Source facts

Repository
OpenHands/extensions
Last source activity
October 29, 2026 at 15:46
Detected SKILL.md language
English
Stars
175
Forks
119

Install options

The review-first prompt is selected by default. You can switch to a direct command or download a local copy.

Review the source files

Read SKILL.md and any companion files shown by SkillsMP before deciding whether to install.

File Explorer
7 files

Showing SKILL.md

SKILL.md
Source instructions · Read-only preview
name
software-factory
description
Set up an OpenHands "software factory" on GitHub fast: ask only for the repositories and where tickets live (GitHub issues, Jira or Linear), create a working issue-to-PR and PR-review pipeline from the automation catalog with defaults, show what runs, then let the user stop or extend it one step at a time (tune settings, AGENTS.md maintainer, auto-merge, stale-CI and QA add-ons).
triggers
["/software-factory","software factory"]
# Software Factory Build a GitHub software factory from unmodified automation catalog bundles (`automations/catalog/<id>/manifest.json`); a Jira or Linear station is created by its own skill. This skill adds no automation logic. Two phases: **Phase 1** gets a working factory after one question. **Phase 2** extends it in short rounds, only as far as the user wants. After Phase 1 and after every round, the **Checkpoint** shows what exists and lets the user stop. Code lives on GitHub. Tickets can come from GitHub issues (default), Jira or Linear: only the issue to PR station changes (see "Ticket source"). GitLab and Bitbucket code hosts are not part of this flow. ## Stations | Station | Catalog id | Phase | Needs agent profile | |---|---|---|---| | Issue to PR | `github-issue-to-pr`, or the Jira or Linear station | 1 | yes (GitHub issues only) | | PR reviewer | `github-pr-reviewer` | 1 | yes | | Issue triage | `github-issue-triage`, or `linear-triage-assistant` | 1 | yes (GitHub issues only) | | AGENTS.md maintainer | `github-agents-md-maintainer` | add-on | no | | Auto-merge watchdog | `github-delivery-watchdog` | add-on | no | | Stale-CI PR closer | `github-stale-ci-pr-closer` | add-on | no | | QA changes | `qa-changes` | add-on | no | ## Helper script `scripts/factory.py` in this skill's directory does every catalog and API step and prints one JSON document; it exits non-zero with `{"error": ...}` on failure. Its usage is below: do not read its source, and do not load the `openhands-automation` skill for Phase 1. Set `OPENHANDS_HOST` to the **Ingress** URL in `<RUNTIME_SERVICES>` on a local Agent Canvas (it serves both `/api/automation` and `/api`; the automation backend URL does not), or to `<HOST>` on OpenHands Cloud. Run every command as: ```bash GITHUB_PERSONAL_ACCESS_TOKEN="$GITHUB_PERSONAL_ACCESS_TOKEN" \ OPENHANDS_AUTOMATION_API_KEY="$OPENHANDS_AUTOMATION_API_KEY" \ OPENHANDS_API_KEY="$OPENHANDS_API_KEY" \ python3 <this skill's directory>/scripts/factory.py <command> ... ``` `$F` below stands for that whole line. Keep the secret names in it: the agent server passes a saved secret into a command only when the command names it. The script reads the token and never prints it. | Command | Does | |---|---| | `$F preflight --repos a/b,c/d` | Which stations this deployment supports, the agent profile, the GitHub login, push access per repository. | | `$F create --repos a/b --ids ID,ID [--set FIELD=VALUE] [--set ID.FIELD=VALUE]` | Fills each station's form (answers, then derived values, then manifest defaults), uploads the bundle and creates it disabled with the `software-factory` tag. `FIELD=VALUE` applies to every listed station with that field. Stations that already exist are reported under `existing`, not recreated, and get the tag added. | | `$F tune --repos a/b --ids ID,ID [--set FIELD=VALUE] [--set ID.FIELD=VALUE]` | Applies new settings to an existing station by updating it in place. Use this to change a value that is fixed at creation. | | `$F tag ID...` | Adds the `software-factory` tag to automations created outside the script, so the Automations list can filter to the factory. Lists any it could not tag under `not_tagged`, each with its `reason`. | | `$F verify ID... [--no-wait ID...]` | Dispatches once, enables the ones whose run `COMPLETED` and disables the rest, reporting their `error_detail`. `--no-wait` enables without waiting. | | `$F status ID...` | The latest run of each. | | `$F prs --repos a/b [--branch-prefix P]` | Open pull requests whose branch starts with `P` (default `openhands/agents-md`, the AGENTS.md maintainer's). | | `$F reset-token` | Deletes the saved `GITHUB_PERSONAL_ACCESS_TOKEN` (local Agent Canvas), so `request_secret` opens its form again. | `create` and `verify` return each automation's `url`, its page in Agent Canvas. Trust the script's output: do not open pages in the browser to check them, and do not use the task tracker for this flow. Add `--profile NAME` to `preflight` or `create` to use a profile other than the active one. ## Rules - **Supported stations**: create only those `preflight` reports `ok`; say why the others are skipped. - **Derived values** (the script fills them; never ask): every `githubTokenSecret` is `GITHUB_PERSONAL_ACCESS_TOKEN`. The watchdog's `branchPrefix` must equal issue to PR's: when it was changed, pass `--set github-delivery-watchdog.branchPrefix=<same value>`. - **Agent profile**: if `preflight` reports a profile error, skip the stations that need one and point the user to the station's catalog card in Agent Canvas (Automations), which has a profile picker. - **Token**: never ask for its value in chat, echo it, or put it in a tool argument or file. - **Failures**: if `create` fails, its output lists the stations it already created; report them with the error and ask whether to retry or delete them (`DELETE ${OPENHANDS_HOST}/api/automation/v1/{id}`). - **Existing stations**: a station `create` reports under `existing` was already set up by an earlier run and was left unchanged. Say so rather than presenting it as new, and change it with `$F tune` if the user wants different settings. - **Factory tag**: every station carries the `software-factory` tag, which the Automations list filters on. `create` adds it; for a station another skill creates (Jira or Linear issue to PR, Linear triage, QA changes), run `$F tag <id>` once it exists. A station listed under `not_tagged` works but has no tag, and its `reason` says why. `only its creator can change its tags`: another user created it, so tell the user they can ask that person to add the tag. `this automation service does not support tags yet`: the deployment's automation service predates tags; tell the user, and run `$F tag <id>` for those stations once it is upgraded. ## Phase 1 - Working factory 1. Ask, in one message: "Which GitHub repositories (`owner/repo`)? Where do tickets live: GitHub issues (default), Jira or Linear?" Say that everything else uses defaults that can be changed afterwards. If the answer names only repositories, use GitHub issues; do not ask again. 2. Run `$F preflight --repos <repos>`. Drop repositories without `push`, and say which. 3. If `github_login` is null, act on `token_error`: - `not_set`, and the `request_secret` tool is available: call it once with `name: "GITHUB_PERSONAL_ACCESS_TOKEN"` and a `reason` asking for Contents, Issues, Pull requests and Workflows read and write (classic PAT: `repo`, `workflow`), and saying a token can be created at https://github.com/settings/personal-access-tokens. End your turn, wait for the `[secret-request]` message, then rerun `preflight`. - `rejected`: GitHub refuses the saved token (invalid or expired), and `request_secret` cannot replace it: it answers `saved` at once for a name that exists. Say so in one sentence and ask "Shall I remove it and open the form for a new one?" On yes, run `$F reset-token`, then handle it as `not_set`. If `reset-token` fails, tell the user to create a token at https://github.com/settings/personal-access-tokens, replace it in Settings -> Secrets, and say when done. - `not_set` without the tool: tell the user to create a token at https://github.com/settings/personal-access-tokens and add it in Settings -> Secrets, then stop. 4. `$F create --repos <repos> --ids github-pr-reviewer`, adding `github-issue-to-pr,github-issue-triage` for GitHub issues. For Jira or Linear, also set up the issue to PR station as "Ticket source" describes; for Linear, set up triage with the `linear-triage` skill (Jira has none). 5. `$F verify <reviewer id> [<issue to PR id>] [<triage id>]`. 6. Go to the Checkpoint. ## Checkpoint Write one short message, in this order: 1. One plain sentence on what runs and how to use it: "Your factory is running on `<repos>`. Add the `<trigger label>` label to a ticket to get a draft pull request; the reviewer reviews it and you merge it." (On Jira, add that the ticket body must name the `owner/repo`.) 2. If the AGENTS.md maintainer was added, the `AGENTS.md` pull requests from `$F prs --repos <repos>`, asking the user to merge them: they give the build agent the repository's build, test and lint commands. One opened before this setup started (`created_at`) is "still open from an earlier run", not new; the maintainer works once a week per repository. If there are none yet, say they are on their way. 3. A small table of the automations, each linked to its `url`: | Automation | Schedule | Status | |---|---|---| | [Issue to PR](<url>) | every 15 minutes | on | Status is `on`, or `off` with the reason in a few words. No automation ids. 4. The settings in effect, as a short list (the Tune table's current values). Mention that the reviewer also reviews any open, non-draft, green pull request with no review yet (at most 2 new per scan). 5. End with only a list of possible next steps, one line each, and nothing after it. Mark the first one not done yet as recommended: - **Try it**: I open a small test issue with the `<trigger label>` label in `<repo>`, only if you say so, and report the pull request. - **Tune**: change a setting from the list above. - **Add a station**: AGENTS.md maintainer, auto-merge watchdog, stale-CI closer (QA only if `preflight` reports it `ok`). - **Stop here**: the factory keeps running as it is. ## Phase 2 - Extend, one round at a time Do only what the user picked. Ask only the questions for it, in one message, each with its current value. Apply, then return to the Checkpoint. ### Tune | Setting | Default | `--set` | |---|---|---| | Issue trigger label (GitHub issues) | `openhands` | `github-issue-to-pr.triggerLabel` | | Draft or ready pull requests | `draft` | `pullRequestMode` (issue to PR, AGENTS.md maintainer) | | Branch prefix (GitHub issues) | `openhands/issue` | `github-issue-to-pr.branchPrefix` (and the watchdog's, if present) | | Which pull requests get reviewed | label `openhands-review`, reviewer `all-hands-bot` | `github-pr-reviewer.triggerLabel`, `triggerReviewer` | | Review tone: concise, thorough, friendly | `concise` | `reviewTone` | | Human reviewers requested after agent approval (two or more logins) | none | `maintainers` | | Schedules and timezone | issue to PR `*/15 * * * *`, reviewer `*/5 * * * *`, triage `*/5 * * * *`, add-ons per manifest; `UTC` | `ID.schedule`, `timezone` | | Agent profile | active profile | `--profile NAME` | Apply: - Schedule or timezone only: `PATCH ${OPENHANDS_HOST}/api/automation/v1/{id}` with `{"trigger": {...}}`. - Jira or Linear station settings: change them as its skill describes. - Anything else is fixed at creation: `$F tune --repos <repos> --ids <id>` with the new `--set` values (repeat every earlier non-default value), then `$F verify <id>`. `tune` updates the station in place, so there is no replacement to delete and no template-id collision. ### Add a station Ask only the question below, then `$F create` it and `$F verify` it. - **Auto-merge watchdog** (`github-delivery-watchdog`, GitHub issues only, since it merges only branches with the issue to PR prefix): confirm the user wants the agent to merge pull requests itself once review, tests and CI are green. - **Stale-CI closer** (`github-stale-ci-pr-closer`): confirm it may warn on, then close, pull requests whose CI keeps failing. - **AGENTS.md maintainer** (`github-agents-md-maintainer`): nothing. Verify it with `--no-wait`; its run opens one `AGENTS.md` pull request per repository. - **QA changes**: which base branch (default `main`). Offer QA only if `preflight` reports it `ok` and the `openhands-automation` webhook reachability check passes. It has no bundle, so the script cannot create it: create it as the `openhands-automation` skill describes for an event-triggered prompt preset, with its manifest `prompt` and `filter`, one per repository (it takes a single `repository`). It runs only on pull request events, so leave it enabled and skip `verify`. ## Ticket source - **GitHub issues**: `github-issue-to-pr`, created with the others in Phase 1. - **Jira**: follow the `jira-issue-to-pr` skill with `git_provider` `github` and its defaults (label `create-pr`, `*/5 * * * *`). Ask only what it cannot look up (the Jira site if several, or the API-token details when Atlassian Rovo is not connected). Tell the user each ticket body must name its `owner/repo`. - **Linear**: follow the `ticket-to-code-change` skill with GitHub as the provider. Ask its questions in one message, proposing the repositories above and `*/15 * * * *` as defaults. Once its skill has created the station, run `$F tag <id>` and then `$F verify <id>` on it. ## Troubleshooting | Symptom | Cause and fix | |---|---| | Create returns 422 | A value does not match the manifest `setup` block; fix the field at the path the error names. | | Issue to PR fails at push | The token lacks Contents or Workflows write, or does not cover that repository. | | Reviewer never reviews | The pull request has neither the trigger label nor a request for the trigger reviewer, and is not green. | | Watchdog never merges | Its branch prefix differs from issue to PR's, or the pull request lacks an independent approval or green CI. |
View on GitHub