software-factory 0.0.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,23 @@
1
+ # Python-generated files
2
+ __pycache__/
3
+ *.py[oc]
4
+ build/
5
+ dist/
6
+ wheels/
7
+ *.egg-info
8
+
9
+ # Virtual environments
10
+ .venv
11
+
12
+ # Factory state (work items, worktrees, agent logs)
13
+ _FACTORY/
14
+
15
+ # Tool caches
16
+ .mypy_cache/
17
+ .pytest_cache/
18
+ .ruff_cache/
19
+
20
+ # Editor and agent scratch, local to whoever is working here
21
+ .zed/
22
+ _AGENT/
23
+ _FACTORY/
@@ -0,0 +1,26 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project are documented here. The format is
4
+ [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) and this project follows
5
+ [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6
+
7
+ ## [0.0.1rc1] - 2026-09-21
8
+
9
+ First published build, to TestPyPI. The engine works end to end; the interfaces are
10
+ still free to change.
11
+
12
+ ### Added
13
+ - `sf`, one global command for every repo: `init`, `submit`, `run`, `status`, `show`,
14
+ `replay`, `cancel`, `delete`, `prune`, `config`, `runners`, `doctor`.
15
+ - Pipelines as YAML, in a repo's `.sf/pipelines/` or in `~/.sf/pipelines/` for every repo,
16
+ with rework edges, review gates and a pass budget before a request parks for a human.
17
+ - Three built-in runners: `claude` (Claude Code, non-interactive), `shell`, and `typesafe`
18
+ (a typed Jev judgment, opt-in, off without `TYPESAFE_API_KEY`).
19
+ - A git worktree per request, on its own branch, so several run at once without touching
20
+ the tree you are editing.
21
+ - `sf --skill`, the skill that teaches an agent to operate the factory.
22
+
23
+ ## [0.0.1] - 2026-09-21
24
+
25
+ First stable release. The same build as 0.0.1rc1 - see that entry for what it
26
+ contains - published to PyPI rather than TestPyPI.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 StratoNext, Inc
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,296 @@
1
+ Metadata-Version: 2.5
2
+ Name: software-factory
3
+ Version: 0.0.1
4
+ Summary: An agent software factory: requests flow through YAML-defined pipelines of agent, command and judge steps.
5
+ Project-URL: Homepage, https://github.com/stratonext/software-factory
6
+ Project-URL: Repository, https://github.com/stratonext/software-factory
7
+ Project-URL: Issues, https://github.com/stratonext/software-factory/issues
8
+ Author-email: Stratonext <info@stratonext.com>
9
+ License-Expression: MIT
10
+ License-File: LICENSE
11
+ Keywords: agents,automation,claude,cli,code-review,pipeline
12
+ Classifier: Development Status :: 3 - Alpha
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: Programming Language :: Python :: 3.11
15
+ Classifier: Programming Language :: Python :: 3.12
16
+ Classifier: Programming Language :: Python :: 3.13
17
+ Classifier: Programming Language :: Python :: 3.14
18
+ Classifier: Topic :: Software Development
19
+ Classifier: Topic :: Software Development :: Build Tools
20
+ Requires-Python: >=3.11
21
+ Requires-Dist: pyyaml>=6
22
+ Requires-Dist: rich>=13
23
+ Requires-Dist: typer>=0.12.1
24
+ Description-Content-Type: text/markdown
25
+
26
+ <img src="assets/factory.svg" alt="" width="192" height="192">
27
+
28
+ Maintained by [StratoNext](https://www.stratonext.ai).
29
+
30
+ # Local Software Factory
31
+
32
+ A software factory is a system that turns software requests into finished work through a repeatable, automated process. Instead of handling every request manually, you define a pipeline of stages that moves the work from request to completion, with human review when needed.
33
+
34
+ This project aims to build a simple **local software factory**.
35
+
36
+ You can define multiple pipelines, each made up of multiple stages. A new request enters a pipeline and moves through its stages until it is completed or requires human review. Pipelines are defined in YAML, inspired by GitHub Actions, so the workflow itself can live alongside your code and be version controlled.
37
+
38
+ A pipeline can combine **coding agents and shell commands**. For example, one stage might ask a coding agent to implement a change, another might run tests, and a later stage might ask another agent to review the result.
39
+
40
+ Multiple requests can run through pipelines in parallel. By default, each request gets its own Git worktree, keeping work isolated so different requests do not interfere with each other.
41
+
42
+ The project is designed to run locally and reuse the coding agents you already have installed. Instead of requiring a separate API integration or metered API usage, each stage can use the agent CLI and subscription you already have.
43
+
44
+ The goal is simple: **bring the basic ideas of a software factory to your local machine, with pipelines defined as code and coding agents as workers.**
45
+
46
+ ![Sample pipelines](assets/sample-ss.png)
47
+
48
+ ## Install
49
+
50
+ `sf` is one global command for all your repos, like `docker`. Install it once:
51
+
52
+ ```bash
53
+ uv tool install software-factory # or: pipx install software-factory
54
+ pip install software-factory # or into a virtualenv you manage yourself
55
+ ```
56
+
57
+ Then create the installation — `~/.sf`, a starter `config.yaml` and the directories the
58
+ factory uses:
59
+
60
+ ```bash
61
+ sf init
62
+ ```
63
+
64
+ Stages that use a coding agent run that agent's CLI, so it has to be installed and
65
+ authenticated once. For the built-in `claude` runner:
66
+
67
+ ```bash
68
+ claude setup-token # needs a Claude subscription
69
+ export CLAUDE_CODE_OAUTH_TOKEN=... # the token it printed
70
+ sf doctor # checks git, the CLI, the token, the paths
71
+ ```
72
+
73
+ ## Quickstart
74
+
75
+ The first thing to do is to write a pipeline. Put it in `~/.sf/pipelines/` and every repo on the machine can use it:
76
+
77
+ ```bash
78
+ mkdir -p ~/.sf/pipelines/prompts
79
+
80
+ cat > ~/.sf/pipelines/quick.yaml <<'YAML'
81
+ name: quick
82
+ description: Implement the request with Claude Code, then commit it.
83
+ start: code
84
+
85
+ steps:
86
+ code:
87
+ uses: claude # the agent CLI you already have
88
+ with:
89
+ prompt: prompts/code.md # relative to this file
90
+ next: commit
91
+
92
+ commit:
93
+ uses: shell # an ordinary command, run in the request's worktree
94
+ with:
95
+ run: "git add -A && git diff --cached --quiet || git commit -m 'sf: worked by the factory'"
96
+ on: { pass: done } # `done` is the implicit terminal stage
97
+ YAML
98
+
99
+ cat > ~/.sf/pipelines/prompts/code.md <<'MD'
100
+ Implement the request below in this repository.
101
+
102
+ Make the smallest change that does it. Follow the conventions already in the code, and
103
+ leave the working tree clean - no scratch files, no commented-out code.
104
+ MD
105
+ ```
106
+
107
+ The request itself is not in the prompt file: the engine appends it, along with anything
108
+ earlier stages produced and any note a human left. You write the instructions, the factory
109
+ fills in the work.
110
+
111
+ Submit **from inside the repo**:
112
+
113
+ ```bash
114
+ sf submit "add rate limiting to /upload" --name rate-limit --pipeline quick # -> id 1
115
+ sf run # work everything that is queued
116
+ ```
117
+
118
+ And from anywhere, to see what is happening:
119
+
120
+ ```bash
121
+ sf status # everything in flight, across every repo
122
+ sf show 1 # the full state of one request
123
+ sf replay 1 # play the run back: every step, its route, its artifacts
124
+ ```
125
+
126
+ Each request works in its own git worktree under `~/.sf/worktrees/<repo>/<id>/`, so several
127
+ can run at once without stepping on each other or on what you are editing.
128
+
129
+ ## Driving the factory with an agent
130
+
131
+ The factory is a CLI, so the thing best placed to operate it is another agent. `sf` ships
132
+ with a skill that teaches one how — every command, the verdicts, how to answer a parked
133
+ request, and the shape of a pipeline file:
134
+
135
+ Load the skill in the agent (`--skill`), then ask it to break the work up and queue it:
136
+
137
+ > Read the roadmap in `docs/plan.md`, split it into requests small enough for one pipeline
138
+ > pass each, and `sf submit` them against `quick` pipeline. Then run the queue and tell me what
139
+ > lands.
140
+
141
+ Ask it to write the process, not just use it:
142
+
143
+ > We keep shipping changes with no tests. Write me a `.sf/pipelines/dev.yaml` that plans,
144
+ > implements, runs `pytest -q`, and sends the work back to the coder when it fails — two
145
+ > rework passes, then park for me.
146
+
147
+ And then leave it to watch: `sf status` is the whole state of the world in one call, so the
148
+ agent can poll until every request is `done`, `failed` or `needs_human`, `sf replay <id>`
149
+ the ones that went wrong, answer a parked one with `sf run <id> --note "..."`, re-enter an
150
+ earlier stage when a plan needs changing, and re-queue what failed — until the queue is
151
+ empty.
152
+
153
+ That is the whole point of the local factory. The agent you are talking to is the foreman,
154
+ not the worker: each request it queues is worked by its own agent, in its own git worktree,
155
+ on its own branch, several at a time, and none of them can touch the tree you are editing.
156
+ One conversation turns into a queue of parallel work you can watch, interrupt, and replay —
157
+ `sf cancel` stops it, and the foreman is never the one grading its own diff, because the
158
+ pipeline decides that with a test run or a [Jev judgment](#judging-with-jev).
159
+
160
+ ## Writing a pipeline
161
+
162
+ A pipeline is a YAML file in the repo's own `.sf/pipelines/<name>.yaml`, or in
163
+ `~/.sf/pipelines/` for every repo. The repo's own copy wins, so two repos can both have a
164
+ `dev` pipeline and mean different processes.
165
+
166
+ The Quickstart's `quick` is about as small as one gets. Here is the next step up — implement,
167
+ test, and send the work back to the coder if the tests fail:
168
+
169
+ ```yaml
170
+ name: dev
171
+ description: Implement a change, and only keep it if the tests pass.
172
+ start: code # which step a new request enters; defaults to the first
173
+ max_passes: 2 # rework round-trips before a human is asked instead
174
+
175
+ steps:
176
+ code:
177
+ uses: claude # which runner performs this step
178
+ with:
179
+ prompt: prompts/code.md # prompt file, relative to this pipeline
180
+ next: test # unconditional edge
181
+
182
+ test:
183
+ uses: shell
184
+ with:
185
+ run: "pytest -q" # exit 0 = pass, anything else = fail
186
+ on: { pass: done, fail: code } # a backwards edge is rework, and costs one pass
187
+ ```
188
+
189
+ Submit against it with `sf submit "..." --pipeline dev` — the name is the file's, so
190
+ `dev.yaml` is `--pipeline dev`. `~/.sf/pipelines/` serves every repo; a `.sf/pipelines/` in a
191
+ repo wins over it, which is how one repo keeps a process of its own. [`examples/`](examples/)
192
+ has four more to copy: implement-and-commit, the full reviewed line, a judged secret
193
+ gate, and one that opens the pull request.
194
+
195
+ A step says **who performs it** (`uses:`) and **what to hand them** (`with:`), and needs at
196
+ least one of `next:` or `on:`. `done` is the implicit terminal stage. Three runners ship
197
+ built in:
198
+
199
+ | runner | what it is |
200
+ |---|---|
201
+ | `claude` | Claude Code, non-interactive. `with: {prompt:, effort:, model:}` |
202
+ | `shell` | an ordinary command. `with: {run:}` |
203
+ | `typesafe` | a typed judgment instead of an agent. `with: {questions:}` |
204
+
205
+ `sf runners` lists every runner a step can name, whether its binary is on PATH, and what
206
+ each one supports. Adding one is a YAML file too, so a stage can run a different agent CLI.
207
+
208
+ Everything a pipeline can say — `input:`, `output:`, `review:` gates, judge steps,
209
+ concurrency, costs — is in [`docs/pipelines.md`](docs/pipelines.md), and
210
+ [`docs/pipeline-schema.yaml`](docs/pipeline-schema.yaml) is the annotated schema your editor
211
+ can use for completion.
212
+
213
+ ## Judging with Jev
214
+
215
+ `typesafe` is the third built-in runner, and the one that is not an agent. A step that
216
+ `uses: typesafe` sends the diff and the scratch files as **state**, asks the typed questions
217
+ you wrote, and gets typed answers back from [TypeSafe](https://typesafe.ai)'s System One
218
+ model, **Jev** — a probability, a position on an ordered scale, one of a set of choices. The
219
+ pipeline routes on those numbers with thresholds you declare, so the decision is data rather
220
+ than an agent's prose.
221
+
222
+ ```yaml
223
+ gate:
224
+ uses: typesafe
225
+ with:
226
+ questions: prompts/review-gate.yaml # the questions and the routing, beside the prompts
227
+ input: [plan.md]
228
+ output: gate.md
229
+ on: { pass: review, fail: code } # a cheap filter before the expensive reviewer
230
+ ```
231
+
232
+ ```yaml
233
+ # prompts/review-gate.yaml
234
+ state: { diff: "git diff" } # shell commands; stdout becomes a named state field
235
+ questions:
236
+ secret: { type: noul, instructions: "`diff` hardcodes a credential, key, token or password." }
237
+ scope: { type: score, instructions: "How far does `diff` go beyond `request`?",
238
+ criteria: ["exactly the request", "small extras", "large unrelated changes"] }
239
+ route: # first match wins; a rule with no `when` is default
240
+ - { when: secret, above: $secret, verdict: human, notes: "possible hardcoded credential" }
241
+ - { when: scope, above: 1.5, verdict: fail, notes: "goes well beyond the request" }
242
+ - { verdict: pass }
243
+ ```
244
+
245
+ Why bother, when an agent could be asked the same thing in English: a judgment costs about
246
+ **$0.0002** where a review agent costs **$1–2**, it answers in milliseconds, and it is not
247
+ the worker grading its own work. The notes the factory records carry the number that fired —
248
+ `secret 0.71 > 0.5 - possible hardcoded credential` — so a verdict can be argued with. Use it
249
+ as a gate in front of the expensive stages, not as a replacement for them.
250
+
251
+ `above: $secret` reads `typesafe.thresholds.secret` from `~/.sf/config.yaml`, so a gate that
252
+ turns out to be too eager is one edit for every pipeline that uses it; a rule that writes a
253
+ literal still wins. A `$name` the config does not define parks the request rather than being
254
+ read as zero.
255
+
256
+ The same model can also pick the process for you. `--pipeline auto` and `--effort auto` ask
257
+ Jev which pipeline a request belongs in and how hard the agent should think, in one call
258
+ before anything is queued, and flag a request too vague for anyone to start on:
259
+
260
+ ```bash
261
+ sf submit "the upload endpoint 500s on files over 2MB" --pipeline auto --effort auto
262
+ ```
263
+
264
+ All of it is **opt-in and off by default**: it needs `TYPESAFE_API_KEY`, and without one
265
+ nothing here is reached — submit behaves exactly as it always did. A step that *does* name
266
+ this runner and cannot ask — no key, an HTTP error, a rule about a question that was not
267
+ answered — returns `human` and parks the request. The factory never guesses a verdict, and a
268
+ service it could not reach is a decision nobody made.
269
+
270
+ The questions are fixed by the pipeline author, and only the diff and the scratch files
271
+ arrive as state. That separation is deliberate: an agent wrote the diff, so the diff is not
272
+ trusted input, and it must never be able to reach the model as an instruction.
273
+ [`docs/pipelines.md`](docs/pipelines.md) has the whole shape, question types included.
274
+
275
+
276
+ ## Commands
277
+
278
+ ```bash
279
+ sf init # create ~/.sf, its config.yaml and its directories
280
+ sf submit "..." --name x # queue a request against this repo (--file, --pipeline, --run)
281
+ sf run # work the queue (--repo, <id>..., --detach, --note, --stage)
282
+ sf status # what is in flight, across every repo
283
+ sf show 1 # full state of one request
284
+ sf replay 1 # play a run back (--step, --json)
285
+ sf config # the settings in effect, and where they would be changed
286
+ sf runners # every runner a step can use, and whether it is installed
287
+ sf cancel 1 2 # stop requests now; `sf run <id>` picks one back up
288
+ sf delete 1 2 # drop requests: item, artifacts and worktree
289
+ sf prune # clear finished requests in bulk (--status, --older-than)
290
+ sf doctor # is this installation able to work anything?
291
+ sf --version # what is installed
292
+ ```
293
+
294
+ Every command prints dense `key: value` for a person and JSON for a program, decided by
295
+ where it is going: a terminal gets the text, a pipe or a redirect gets the JSON, and
296
+ `--json` forces it anywhere.
@@ -0,0 +1,271 @@
1
+ <img src="assets/factory.svg" alt="" width="192" height="192">
2
+
3
+ Maintained by [StratoNext](https://www.stratonext.ai).
4
+
5
+ # Local Software Factory
6
+
7
+ A software factory is a system that turns software requests into finished work through a repeatable, automated process. Instead of handling every request manually, you define a pipeline of stages that moves the work from request to completion, with human review when needed.
8
+
9
+ This project aims to build a simple **local software factory**.
10
+
11
+ You can define multiple pipelines, each made up of multiple stages. A new request enters a pipeline and moves through its stages until it is completed or requires human review. Pipelines are defined in YAML, inspired by GitHub Actions, so the workflow itself can live alongside your code and be version controlled.
12
+
13
+ A pipeline can combine **coding agents and shell commands**. For example, one stage might ask a coding agent to implement a change, another might run tests, and a later stage might ask another agent to review the result.
14
+
15
+ Multiple requests can run through pipelines in parallel. By default, each request gets its own Git worktree, keeping work isolated so different requests do not interfere with each other.
16
+
17
+ The project is designed to run locally and reuse the coding agents you already have installed. Instead of requiring a separate API integration or metered API usage, each stage can use the agent CLI and subscription you already have.
18
+
19
+ The goal is simple: **bring the basic ideas of a software factory to your local machine, with pipelines defined as code and coding agents as workers.**
20
+
21
+ ![Sample pipelines](assets/sample-ss.png)
22
+
23
+ ## Install
24
+
25
+ `sf` is one global command for all your repos, like `docker`. Install it once:
26
+
27
+ ```bash
28
+ uv tool install software-factory # or: pipx install software-factory
29
+ pip install software-factory # or into a virtualenv you manage yourself
30
+ ```
31
+
32
+ Then create the installation — `~/.sf`, a starter `config.yaml` and the directories the
33
+ factory uses:
34
+
35
+ ```bash
36
+ sf init
37
+ ```
38
+
39
+ Stages that use a coding agent run that agent's CLI, so it has to be installed and
40
+ authenticated once. For the built-in `claude` runner:
41
+
42
+ ```bash
43
+ claude setup-token # needs a Claude subscription
44
+ export CLAUDE_CODE_OAUTH_TOKEN=... # the token it printed
45
+ sf doctor # checks git, the CLI, the token, the paths
46
+ ```
47
+
48
+ ## Quickstart
49
+
50
+ The first thing to do is to write a pipeline. Put it in `~/.sf/pipelines/` and every repo on the machine can use it:
51
+
52
+ ```bash
53
+ mkdir -p ~/.sf/pipelines/prompts
54
+
55
+ cat > ~/.sf/pipelines/quick.yaml <<'YAML'
56
+ name: quick
57
+ description: Implement the request with Claude Code, then commit it.
58
+ start: code
59
+
60
+ steps:
61
+ code:
62
+ uses: claude # the agent CLI you already have
63
+ with:
64
+ prompt: prompts/code.md # relative to this file
65
+ next: commit
66
+
67
+ commit:
68
+ uses: shell # an ordinary command, run in the request's worktree
69
+ with:
70
+ run: "git add -A && git diff --cached --quiet || git commit -m 'sf: worked by the factory'"
71
+ on: { pass: done } # `done` is the implicit terminal stage
72
+ YAML
73
+
74
+ cat > ~/.sf/pipelines/prompts/code.md <<'MD'
75
+ Implement the request below in this repository.
76
+
77
+ Make the smallest change that does it. Follow the conventions already in the code, and
78
+ leave the working tree clean - no scratch files, no commented-out code.
79
+ MD
80
+ ```
81
+
82
+ The request itself is not in the prompt file: the engine appends it, along with anything
83
+ earlier stages produced and any note a human left. You write the instructions, the factory
84
+ fills in the work.
85
+
86
+ Submit **from inside the repo**:
87
+
88
+ ```bash
89
+ sf submit "add rate limiting to /upload" --name rate-limit --pipeline quick # -> id 1
90
+ sf run # work everything that is queued
91
+ ```
92
+
93
+ And from anywhere, to see what is happening:
94
+
95
+ ```bash
96
+ sf status # everything in flight, across every repo
97
+ sf show 1 # the full state of one request
98
+ sf replay 1 # play the run back: every step, its route, its artifacts
99
+ ```
100
+
101
+ Each request works in its own git worktree under `~/.sf/worktrees/<repo>/<id>/`, so several
102
+ can run at once without stepping on each other or on what you are editing.
103
+
104
+ ## Driving the factory with an agent
105
+
106
+ The factory is a CLI, so the thing best placed to operate it is another agent. `sf` ships
107
+ with a skill that teaches one how — every command, the verdicts, how to answer a parked
108
+ request, and the shape of a pipeline file:
109
+
110
+ Load the skill in the agent (`--skill`), then ask it to break the work up and queue it:
111
+
112
+ > Read the roadmap in `docs/plan.md`, split it into requests small enough for one pipeline
113
+ > pass each, and `sf submit` them against `quick` pipeline. Then run the queue and tell me what
114
+ > lands.
115
+
116
+ Ask it to write the process, not just use it:
117
+
118
+ > We keep shipping changes with no tests. Write me a `.sf/pipelines/dev.yaml` that plans,
119
+ > implements, runs `pytest -q`, and sends the work back to the coder when it fails — two
120
+ > rework passes, then park for me.
121
+
122
+ And then leave it to watch: `sf status` is the whole state of the world in one call, so the
123
+ agent can poll until every request is `done`, `failed` or `needs_human`, `sf replay <id>`
124
+ the ones that went wrong, answer a parked one with `sf run <id> --note "..."`, re-enter an
125
+ earlier stage when a plan needs changing, and re-queue what failed — until the queue is
126
+ empty.
127
+
128
+ That is the whole point of the local factory. The agent you are talking to is the foreman,
129
+ not the worker: each request it queues is worked by its own agent, in its own git worktree,
130
+ on its own branch, several at a time, and none of them can touch the tree you are editing.
131
+ One conversation turns into a queue of parallel work you can watch, interrupt, and replay —
132
+ `sf cancel` stops it, and the foreman is never the one grading its own diff, because the
133
+ pipeline decides that with a test run or a [Jev judgment](#judging-with-jev).
134
+
135
+ ## Writing a pipeline
136
+
137
+ A pipeline is a YAML file in the repo's own `.sf/pipelines/<name>.yaml`, or in
138
+ `~/.sf/pipelines/` for every repo. The repo's own copy wins, so two repos can both have a
139
+ `dev` pipeline and mean different processes.
140
+
141
+ The Quickstart's `quick` is about as small as one gets. Here is the next step up — implement,
142
+ test, and send the work back to the coder if the tests fail:
143
+
144
+ ```yaml
145
+ name: dev
146
+ description: Implement a change, and only keep it if the tests pass.
147
+ start: code # which step a new request enters; defaults to the first
148
+ max_passes: 2 # rework round-trips before a human is asked instead
149
+
150
+ steps:
151
+ code:
152
+ uses: claude # which runner performs this step
153
+ with:
154
+ prompt: prompts/code.md # prompt file, relative to this pipeline
155
+ next: test # unconditional edge
156
+
157
+ test:
158
+ uses: shell
159
+ with:
160
+ run: "pytest -q" # exit 0 = pass, anything else = fail
161
+ on: { pass: done, fail: code } # a backwards edge is rework, and costs one pass
162
+ ```
163
+
164
+ Submit against it with `sf submit "..." --pipeline dev` — the name is the file's, so
165
+ `dev.yaml` is `--pipeline dev`. `~/.sf/pipelines/` serves every repo; a `.sf/pipelines/` in a
166
+ repo wins over it, which is how one repo keeps a process of its own. [`examples/`](examples/)
167
+ has four more to copy: implement-and-commit, the full reviewed line, a judged secret
168
+ gate, and one that opens the pull request.
169
+
170
+ A step says **who performs it** (`uses:`) and **what to hand them** (`with:`), and needs at
171
+ least one of `next:` or `on:`. `done` is the implicit terminal stage. Three runners ship
172
+ built in:
173
+
174
+ | runner | what it is |
175
+ |---|---|
176
+ | `claude` | Claude Code, non-interactive. `with: {prompt:, effort:, model:}` |
177
+ | `shell` | an ordinary command. `with: {run:}` |
178
+ | `typesafe` | a typed judgment instead of an agent. `with: {questions:}` |
179
+
180
+ `sf runners` lists every runner a step can name, whether its binary is on PATH, and what
181
+ each one supports. Adding one is a YAML file too, so a stage can run a different agent CLI.
182
+
183
+ Everything a pipeline can say — `input:`, `output:`, `review:` gates, judge steps,
184
+ concurrency, costs — is in [`docs/pipelines.md`](docs/pipelines.md), and
185
+ [`docs/pipeline-schema.yaml`](docs/pipeline-schema.yaml) is the annotated schema your editor
186
+ can use for completion.
187
+
188
+ ## Judging with Jev
189
+
190
+ `typesafe` is the third built-in runner, and the one that is not an agent. A step that
191
+ `uses: typesafe` sends the diff and the scratch files as **state**, asks the typed questions
192
+ you wrote, and gets typed answers back from [TypeSafe](https://typesafe.ai)'s System One
193
+ model, **Jev** — a probability, a position on an ordered scale, one of a set of choices. The
194
+ pipeline routes on those numbers with thresholds you declare, so the decision is data rather
195
+ than an agent's prose.
196
+
197
+ ```yaml
198
+ gate:
199
+ uses: typesafe
200
+ with:
201
+ questions: prompts/review-gate.yaml # the questions and the routing, beside the prompts
202
+ input: [plan.md]
203
+ output: gate.md
204
+ on: { pass: review, fail: code } # a cheap filter before the expensive reviewer
205
+ ```
206
+
207
+ ```yaml
208
+ # prompts/review-gate.yaml
209
+ state: { diff: "git diff" } # shell commands; stdout becomes a named state field
210
+ questions:
211
+ secret: { type: noul, instructions: "`diff` hardcodes a credential, key, token or password." }
212
+ scope: { type: score, instructions: "How far does `diff` go beyond `request`?",
213
+ criteria: ["exactly the request", "small extras", "large unrelated changes"] }
214
+ route: # first match wins; a rule with no `when` is default
215
+ - { when: secret, above: $secret, verdict: human, notes: "possible hardcoded credential" }
216
+ - { when: scope, above: 1.5, verdict: fail, notes: "goes well beyond the request" }
217
+ - { verdict: pass }
218
+ ```
219
+
220
+ Why bother, when an agent could be asked the same thing in English: a judgment costs about
221
+ **$0.0002** where a review agent costs **$1–2**, it answers in milliseconds, and it is not
222
+ the worker grading its own work. The notes the factory records carry the number that fired —
223
+ `secret 0.71 > 0.5 - possible hardcoded credential` — so a verdict can be argued with. Use it
224
+ as a gate in front of the expensive stages, not as a replacement for them.
225
+
226
+ `above: $secret` reads `typesafe.thresholds.secret` from `~/.sf/config.yaml`, so a gate that
227
+ turns out to be too eager is one edit for every pipeline that uses it; a rule that writes a
228
+ literal still wins. A `$name` the config does not define parks the request rather than being
229
+ read as zero.
230
+
231
+ The same model can also pick the process for you. `--pipeline auto` and `--effort auto` ask
232
+ Jev which pipeline a request belongs in and how hard the agent should think, in one call
233
+ before anything is queued, and flag a request too vague for anyone to start on:
234
+
235
+ ```bash
236
+ sf submit "the upload endpoint 500s on files over 2MB" --pipeline auto --effort auto
237
+ ```
238
+
239
+ All of it is **opt-in and off by default**: it needs `TYPESAFE_API_KEY`, and without one
240
+ nothing here is reached — submit behaves exactly as it always did. A step that *does* name
241
+ this runner and cannot ask — no key, an HTTP error, a rule about a question that was not
242
+ answered — returns `human` and parks the request. The factory never guesses a verdict, and a
243
+ service it could not reach is a decision nobody made.
244
+
245
+ The questions are fixed by the pipeline author, and only the diff and the scratch files
246
+ arrive as state. That separation is deliberate: an agent wrote the diff, so the diff is not
247
+ trusted input, and it must never be able to reach the model as an instruction.
248
+ [`docs/pipelines.md`](docs/pipelines.md) has the whole shape, question types included.
249
+
250
+
251
+ ## Commands
252
+
253
+ ```bash
254
+ sf init # create ~/.sf, its config.yaml and its directories
255
+ sf submit "..." --name x # queue a request against this repo (--file, --pipeline, --run)
256
+ sf run # work the queue (--repo, <id>..., --detach, --note, --stage)
257
+ sf status # what is in flight, across every repo
258
+ sf show 1 # full state of one request
259
+ sf replay 1 # play a run back (--step, --json)
260
+ sf config # the settings in effect, and where they would be changed
261
+ sf runners # every runner a step can use, and whether it is installed
262
+ sf cancel 1 2 # stop requests now; `sf run <id>` picks one back up
263
+ sf delete 1 2 # drop requests: item, artifacts and worktree
264
+ sf prune # clear finished requests in bulk (--status, --older-than)
265
+ sf doctor # is this installation able to work anything?
266
+ sf --version # what is installed
267
+ ```
268
+
269
+ Every command prints dense `key: value` for a person and JSON for a program, decided by
270
+ where it is going: a terminal gets the text, a pipe or a redirect gets the JSON, and
271
+ `--json` forces it anywhere.