Software Factory
Set up an OpenHands "software factory" on GitHub fast: ask only for the repositories and where ti…
by openhands · MIT
The skill
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, triage, auto-merge, stale-CI and QA add-ons).
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 |
| AGENTS.md maintainer | github-agents-md-maintainer |
1 | no |
| Issue triage | github-issue-triage, or linear-triage-assistant |
add-on | yes (GitHub issues only) |
| 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:
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. FIELD=VALUE applies to every listed station with that field. Stations that already exist are reported under existing, not recreated. |
$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 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
preflightreportsok; say why the others are skipped. - Derived values (the script fills them; never ask): every
githubTokenSecretisGITHUB_PERSONAL_ACCESS_TOKEN. The watchdog'sbranchPrefixmust equal issue to PR's: when it was changed, pass--set github-delivery-watchdog.branchPrefix=<same value>. - Agent profile: if
preflightreports 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
createfails, 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
createreports underexistingwas already set up by an earlier run and was left unchanged. Say so rather than presenting it as new, and change it with$F tuneif the user wants different settings.
Phase 1 - Working factory
- 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. - Run
$F preflight --repos <repos>. Drop repositories withoutpush, and say which. - If
github_loginis null, act ontoken_error:not_set, and therequest_secrettool is available: call it once withname: "GITHUB_PERSONAL_ACCESS_TOKEN"and areasonasking for Contents, Issues, Pull requests and Workflows read and write (classic PAT:repo,workflow). End your turn, wait for the[secret-request]message, then rerunpreflight.rejected: GitHub refuses the saved token (invalid or expired), andrequest_secretcannot replace it: it answerssavedat 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 asnot_set. Ifreset-tokenfails, tell the user to replace it in Settings -> Secrets and say when done.not_setwithout the tool: tell the user to add it in Settings -> Secrets and stop.
$F create --repos <repos> --ids github-pr-reviewer,github-agents-md-maintainer, addinggithub-issue-to-prfor GitHub issues. For Jira or Linear, also set up the issue to PR station as "Ticket source" describes.$F verify <reviewer id> [<issue to PR id>] --no-wait <maintainer id>. The maintainer's run opens oneAGENTS.mdpull request per repository.- Go to the Checkpoint.
Checkpoint
Write one short message, in this order:
-
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 theowner/repo.) -
The
AGENTS.mdpull 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. -
A small table of the automations, each linked to its
url:Automation Schedule Status Issue to PR every 15 minutes on Status is
on, oroffwith the reason in a few words. No automation ids. -
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).
-
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: triage, auto-merge watchdog, stale-CI closer (QA only
if
preflightreports itok). - Stop here: the factory keeps running as it is.
- Try it: I open a small test issue with the
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 * * * *, AGENTS.md maintainer 0 9 * * 1, 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--setvalues (repeat every earlier non-default value), then$F verify <id>.tuneupdates 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. - Triage (
github-issue-triage): nothing. For Linear, follow thelinear-triageskill instead; for Jira there is none. - QA changes: which base branch (default
main). Offer QA only ifpreflightreports itokand theopenhands-automationwebhook reachability check passes. It has no bundle, so the script cannot create it: create it as theopenhands-automationskill describes for an event-triggered prompt preset, with its manifestpromptandfilter, one per repository (it takes a singlerepository). It runs only on pull request events, so leave it enabled and skipverify.
Ticket source
- GitHub issues:
github-issue-to-pr, created with the others in Phase 1. - Jira: follow the
jira-issue-to-prskill withgit_providergithuband its defaults (labelcreate-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 itsowner/repo. - Linear: follow the
ticket-to-code-changeskill 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 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. |
Use this skill
Connect your agent to the Skill Store MCP server and ask for it by ref: openhands/software-factory-44908200616.
Details
- Publisher
- openhands
- Category
- Software Development › Source control & code review
- License
- MIT
- Updated
- 2026-10-19
References
More from openhands
- Build Setup Set up build environment and test fixtures for COBOL-to-Java migrations.
- Cobol Modernization End-to-end COBOL to Java migration workflow.
- Mainframe Planning Create a transformation guide for replacing mainframe-specific COBOL constructs with standard COB…
- Mainframe Removal Apply mainframe dependency transformations to COBOL code using a pre-generated transformation guide.