A small, visual software factory for oh-my-pi (OMP): one /factory
skill orchestrates subagents, OpenSpec specs,
one git worktree per job, and markdown files, with a live HTML dashboard.
job → queued → OpenSpec propose → builder ⇄ 4 reviewers (max 2 rounds) → approver → integration queue
(slow) security/ux/ui/code (slow) rebase onto base, test, archive,
then stack onto base OR hand off
/factory dashboard serves a live board from factory/board.json. Stages run left to right;
jobs show type, branch → base, port, and per-round reviewer chips (security / ux / ui / code).
Click a job to open its drawer: OpenSpec artifacts, build.md, and every review file for
that round.
Needs omp, git and openspec (npm i -g @fission-ai/openspec). No build step: OMP
loads the TypeScript extension directly.
./install.sh # links this package as an OMP plugin: /factory in every repo
./install.sh ~/code/app # or: copy into ~/code/app/.omp/ (pinned to that repo)./install.sh runs omp plugin link <this dir>, so edits here are live everywhere (restart
omp to reload the extension). Turn it off with omp plugin disable omp-sw-factory
(omp plugin uninstall needs bun on PATH).
The pinned mode copies the skill and agents into <repo>/.omp/ and the
extension into <repo>/.omp/extensions/sw-factory/; commit them to share the factory
with everyone who opens that repo in OMP.
cd ~/code/app && omp
/factory init # asks base branch + merge/branch mode, profiles the repo, commits
/factory dashboard # serves http://localhost:8765/factory/dashboard.html
/factory add rate limiting to the public API # type inferred (feature)
/factory bugfix: crash when saving an empty form # explicit type
/factory hotfix: token leak in logs @release-2.3 # explicit type and base branch
/factory search filters | CSV export | dark mode # parallel jobs (max_parallel, default 3)
/factory next 2 # first 2 unchecked items in factory/backlog.md
/factory parallel 4
/factory config integration=branch push=true # see "Branches and integration"
/factory approve job-003-rate-limiting
/factory rework job-003-rate-limiting "use a sliding window, not fixed buckets"
/factory done job-003-rate-limiting # you merged its branch yourself
/factory status
/factory cleanup
Each repo has its own factory/ state, so separate OMP instances can run factories for
different repos at the same time. In the same repo (e.g. two omp consoles in
different tmux panes), each OMP session only sees its own jobs — so parallel features
do not mix in the console. Several jobs in one session stay visible together. The
dashboard still shows every job (with a short session badge); the debug CLI
(node src/cli.ts) sees all jobs.
-
Type
/factory(with the space) to get a dropdown of subcommands with their arguments and descriptions. Afterapprove,reworkordoneit offers the matching job ids. -
Type
/fxto list the short forms, one per subcommand, e.g.:/fx:init Set up the factory here: base branch, merge or branch mode, repo profile /fx:feature <request> [@base] Queue a feature job /fx:bugfix <request> [@base] Queue a bugfix job /fx:dash Serve the live dashboard /fx:rework <job-id> <note> Send a job back to its builder with a note -
/factoryalone (or/fx:help) prints the full list.
/fx:bugfix crash on save is the same as /factory bugfix: crash on save.
Set in factory/board.json under repo (by /factory init, /factory config, or the
factory tool's profile action):
| Key | Default | Meaning |
|---|---|---|
base_branch |
current branch at init | parent branch jobs start from and land on, e.g. Branch_A; per job: @<branch> |
integration |
merge |
merge: stack approved jobs onto the base. branch: leave each job as a tested branch for a human |
branch_pattern |
{type}/{base}/{slug} |
job branch name; placeholders {type} {base} {slug} {id} {number} |
protected_branches |
[] |
never merged automatically, in any mode (job goes to needs-human) |
push |
false |
branch mode: push the job branch to remote when it is ready |
remote |
origin |
remote for push |
Job types are feature, bugfix, change and hotfix, so branches look like
feature/Branch_A/login-form or hotfix/Branch_A/token-leak. A repeated name gets the
job number appended (feature/Branch_A/login-form-004).
Both modes integrate one job at a time and always rebase the job onto the current base and rerun the test command first, so a job never lands on (or is handed off against) a base it was not tested on.
mergeonly fast-forwards the base: no merge commits, no force pushes, and other jobs are rebased onto the new tip before they land. The main checkout can stay on another branch (e.g.mainwhile jobs stack ontoBranch_A). The factory never pushes in this mode.branchnever touches the base. The job ends in thereadystage with the spec archived on its branch; you review and merge it (PR or locally)./factory cleanupnotices branches merged into their base; for squash merges use/factory done <id>.
Git cannot have a branch and a folder of branches with the same name: with a branch
Branch_A, a pattern like {base}/{slug} (Branch_A/login-form) is impossible, which is
why the default puts the type first. /factory tells you when a pattern clashes.
| Path | What |
|---|---|
skills/factory/SKILL.md |
the orchestrator's loop: edit this to change the factory |
src/index.ts |
OMP extension: registers the factory tool (main session only; subagents are refused) |
src/commands.ts |
the /factory and /fx:* slash commands: subcommand list, autocomplete, help |
src/factory.ts |
the exact work behind it: board.json, ids, branch names, worktrees, ports, rounds, serial integration |
src/cli.ts |
the same actions from a shell, for debugging: node src/cli.ts status |
templates/ |
dashboard.html, backlog.md, the AGENTS.md section |
agents/ |
builder, review-security, review-ux, review-ui, review-code, approver |
In a target repo the factory creates:
AGENTS.md # factory rules section (between factory:begin/end markers)
.factory-worktrees/<job-id>/ # one worktree per active job (gitignored)
openspec/changes/<job-id>/ # on the job branch; archived into openspec/specs on that branch at integration
factory/board.json # state; drives the dashboard
factory/profile.md # language, install/test/dev commands, conventions
factory/backlog.md
factory/dashboard.html
factory/jobs/<job-id>/build.md, round-N/review-*.md, decision.md
Job ids look like job-003-rate-limiting: OpenSpec change names must start with a letter.
Agents use OMP roles (@slow for builder, security and approver; @default for the
other reviewers). Map them in ~/.omp/agent/config.yml:
modelRoles:
default: <provider/model>
slow: <provider/model>:high
task:
maxConcurrency: 6 # parallel subagents; 1 job needs up to 4 at once (reviewers)
maxRecursionDepth: 1 # orchestrator → agents is enoughasync.enabled must be true (the default) so the orchestrator can run jobs in parallel
and collect them with wait.
Node 23.6+ runs the TypeScript sources directly (OMP itself runs them under Bun).
npm install # typescript + @types/node, dev only
npm run check # tsc --noEmit, then node --test tests/*.test.ts
