minifactory 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.
Files changed (84) hide show
  1. minifactory-0.0.1/LICENSE +21 -0
  2. minifactory-0.0.1/PKG-INFO +249 -0
  3. minifactory-0.0.1/README.md +219 -0
  4. minifactory-0.0.1/pyproject.toml +62 -0
  5. minifactory-0.0.1/setup.cfg +4 -0
  6. minifactory-0.0.1/src/minifactory/__init__.py +1 -0
  7. minifactory-0.0.1/src/minifactory/__main__.py +3 -0
  8. minifactory-0.0.1/src/minifactory/catalog.py +130 -0
  9. minifactory-0.0.1/src/minifactory/cli.py +380 -0
  10. minifactory-0.0.1/src/minifactory/core/__init__.py +0 -0
  11. minifactory-0.0.1/src/minifactory/core/context.py +43 -0
  12. minifactory-0.0.1/src/minifactory/core/engine.py +167 -0
  13. minifactory-0.0.1/src/minifactory/core/errors.py +18 -0
  14. minifactory-0.0.1/src/minifactory/core/events.py +55 -0
  15. minifactory-0.0.1/src/minifactory/core/expr.py +109 -0
  16. minifactory-0.0.1/src/minifactory/core/models.py +76 -0
  17. minifactory-0.0.1/src/minifactory/core/ports.py +48 -0
  18. minifactory-0.0.1/src/minifactory/core/registry.py +47 -0
  19. minifactory-0.0.1/src/minifactory/core/workflow.py +130 -0
  20. minifactory-0.0.1/src/minifactory/demo.py +72 -0
  21. minifactory-0.0.1/src/minifactory/plugins/__init__.py +0 -0
  22. minifactory-0.0.1/src/minifactory/plugins/builtin/__init__.py +18 -0
  23. minifactory-0.0.1/src/minifactory/plugins/builtin/fake_flutter.py +112 -0
  24. minifactory-0.0.1/src/minifactory/plugins/builtin/flutter_tool.py +45 -0
  25. minifactory-0.0.1/src/minifactory/plugins/builtin/intakes.py +118 -0
  26. minifactory-0.0.1/src/minifactory/plugins/builtin/providers_bedrock.py +77 -0
  27. minifactory-0.0.1/src/minifactory/plugins/builtin/providers_github.py +102 -0
  28. minifactory-0.0.1/src/minifactory/plugins/builtin/providers_llm.py +106 -0
  29. minifactory-0.0.1/src/minifactory/plugins/builtin/providers_misc.py +85 -0
  30. minifactory-0.0.1/src/minifactory/plugins/builtin/providers_scm.py +125 -0
  31. minifactory-0.0.1/src/minifactory/plugins/builtin/routers.py +102 -0
  32. minifactory-0.0.1/src/minifactory/plugins/builtin/steps_agent.py +212 -0
  33. minifactory-0.0.1/src/minifactory/plugins/builtin/steps_build.py +61 -0
  34. minifactory-0.0.1/src/minifactory/plugins/builtin/steps_existing.py +128 -0
  35. minifactory-0.0.1/src/minifactory/plugins/builtin/steps_flutter.py +123 -0
  36. minifactory-0.0.1/src/minifactory/plugins/builtin/steps_scm.py +92 -0
  37. minifactory-0.0.1/src/minifactory/plugins/builtin/steps_workflows.py +87 -0
  38. minifactory-0.0.1/src/minifactory/plugins/builtin/util.py +191 -0
  39. minifactory-0.0.1/src/minifactory/py.typed +0 -0
  40. minifactory-0.0.1/src/minifactory/review.py +48 -0
  41. minifactory-0.0.1/src/minifactory/router.py +85 -0
  42. minifactory-0.0.1/src/minifactory/templates/create-workflow/workflow.yaml +72 -0
  43. minifactory-0.0.1/src/minifactory/templates/fix-bug/assets/tasks/README.md +9 -0
  44. minifactory-0.0.1/src/minifactory/templates/fix-bug/workflow.yaml +117 -0
  45. minifactory-0.0.1/src/minifactory/templates/flutter-app-factory/assets/fixtures/flutter/bad_spec/plan.json +1 -0
  46. minifactory-0.0.1/src/minifactory/templates/flutter-app-factory/assets/fixtures/flutter/counter_needs_repair/implement/counter.txt +42 -0
  47. minifactory-0.0.1/src/minifactory/templates/flutter-app-factory/assets/fixtures/flutter/counter_needs_repair/plan.json +3 -0
  48. minifactory-0.0.1/src/minifactory/templates/flutter-app-factory/assets/fixtures/flutter/counter_needs_repair/repair/1.txt +42 -0
  49. minifactory-0.0.1/src/minifactory/templates/flutter-app-factory/assets/fixtures/flutter/notes_low_coverage/implement/screen.txt +25 -0
  50. minifactory-0.0.1/src/minifactory/templates/flutter-app-factory/assets/fixtures/flutter/notes_low_coverage/implement/storage.txt +8 -0
  51. minifactory-0.0.1/src/minifactory/templates/flutter-app-factory/assets/fixtures/flutter/notes_low_coverage/plan.json +4 -0
  52. minifactory-0.0.1/src/minifactory/templates/flutter-app-factory/assets/fixtures/flutter/todo_app/implement/list_screen.txt +35 -0
  53. minifactory-0.0.1/src/minifactory/templates/flutter-app-factory/assets/fixtures/flutter/todo_app/implement/model.txt +20 -0
  54. minifactory-0.0.1/src/minifactory/templates/flutter-app-factory/assets/fixtures/flutter/todo_app/plan.json +4 -0
  55. minifactory-0.0.1/src/minifactory/templates/flutter-app-factory/assets/specs/bad_spec.yaml +3 -0
  56. minifactory-0.0.1/src/minifactory/templates/flutter-app-factory/assets/specs/counter_needs_repair.yaml +4 -0
  57. minifactory-0.0.1/src/minifactory/templates/flutter-app-factory/assets/specs/notes_low_coverage.yaml +5 -0
  58. minifactory-0.0.1/src/minifactory/templates/flutter-app-factory/assets/specs/todo_app.yaml +5 -0
  59. minifactory-0.0.1/src/minifactory/templates/flutter-app-factory/workflow.yaml +80 -0
  60. minifactory-0.0.1/src/minifactory/templates/flutter-app-factory-bedrock/assets/specs/todo_app.yaml +5 -0
  61. minifactory-0.0.1/src/minifactory/templates/flutter-app-factory-bedrock/workflow.yaml +97 -0
  62. minifactory-0.0.1/src/minifactory/templates/java-migration-demo/workflow.yaml +86 -0
  63. minifactory-0.0.1/src/minifactory/templates.py +57 -0
  64. minifactory-0.0.1/src/minifactory/ui.py +190 -0
  65. minifactory-0.0.1/src/minifactory/workspace.py +107 -0
  66. minifactory-0.0.1/src/minifactory.egg-info/PKG-INFO +249 -0
  67. minifactory-0.0.1/src/minifactory.egg-info/SOURCES.txt +82 -0
  68. minifactory-0.0.1/src/minifactory.egg-info/dependency_links.txt +1 -0
  69. minifactory-0.0.1/src/minifactory.egg-info/entry_points.txt +2 -0
  70. minifactory-0.0.1/src/minifactory.egg-info/requires.txt +13 -0
  71. minifactory-0.0.1/src/minifactory.egg-info/top_level.txt +1 -0
  72. minifactory-0.0.1/tests/test_bedrock.py +63 -0
  73. minifactory-0.0.1/tests/test_create_workflow.py +206 -0
  74. minifactory-0.0.1/tests/test_demo.py +78 -0
  75. minifactory-0.0.1/tests/test_engine.py +126 -0
  76. minifactory-0.0.1/tests/test_existing.py +231 -0
  77. minifactory-0.0.1/tests/test_expr.py +33 -0
  78. minifactory-0.0.1/tests/test_flutter.py +170 -0
  79. minifactory-0.0.1/tests/test_output.py +175 -0
  80. minifactory-0.0.1/tests/test_patches.py +70 -0
  81. minifactory-0.0.1/tests/test_router.py +259 -0
  82. minifactory-0.0.1/tests/test_ui.py +45 -0
  83. minifactory-0.0.1/tests/test_workflow.py +55 -0
  84. minifactory-0.0.1/tests/test_workspace.py +195 -0
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Juan Campuzano
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,249 @@
1
+ Metadata-Version: 2.4
2
+ Name: minifactory
3
+ Version: 0.0.1
4
+ Summary: A tiny, pluggable software factory: declarative workflows made of plugin steps
5
+ Author: Juan Campuzano
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/juan-campuzano/minifactory
8
+ Project-URL: Issues, https://github.com/juan-campuzano/minifactory/issues
9
+ Keywords: workflow,agents,llm,automation,software-factory
10
+ Classifier: Development Status :: 3 - Alpha
11
+ Classifier: Intended Audience :: Developers
12
+ Classifier: Programming Language :: Python :: 3
13
+ Classifier: Programming Language :: Python :: 3.11
14
+ Classifier: Programming Language :: Python :: 3.13
15
+ Requires-Python: >=3.11
16
+ Description-Content-Type: text/markdown
17
+ License-File: LICENSE
18
+ Requires-Dist: pydantic>=2
19
+ Requires-Dist: pyyaml>=6
20
+ Requires-Dist: rich>=13
21
+ Requires-Dist: certifi
22
+ Provides-Extra: aws
23
+ Requires-Dist: boto3; extra == "aws"
24
+ Provides-Extra: dev
25
+ Requires-Dist: pytest; extra == "dev"
26
+ Requires-Dist: ruff; extra == "dev"
27
+ Requires-Dist: mypy; extra == "dev"
28
+ Requires-Dist: types-PyYAML; extra == "dev"
29
+ Dynamic: license-file
30
+
31
+ <p align="center">
32
+ <picture>
33
+ <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/juan-campuzano/minifactory/main/assets/brand/png/minifactory-horizontal-dark.png">
34
+ <img src="https://raw.githubusercontent.com/juan-campuzano/minifactory/main/assets/brand/png/minifactory-horizontal-light.png" alt="minifactory" width="420">
35
+ </picture>
36
+ </p>
37
+
38
+ A tiny, pluggable **software factory**. You describe what you need, a **workflow** does the work, and every
39
+ step an AI agent takes is fenced in by deterministic checks, budgets and a human-reviewable result.
40
+
41
+ - **Declarative workflows**: YAML made of plugin steps, validated before anything runs.
42
+ - **Agents on a leash**: the LLM is a step, never the boss. Its output is untrusted, bounded by budgets and
43
+ followed by real gates (a compiler, a test run, a diff-size limit).
44
+ - **Everything environment-specific sits behind ports** (git host, LLM, secrets, job source, executor), so
45
+ swapping GitHub for another host or Bedrock for another model is a one-line change.
46
+ - **Ask in plain language**: `minifactory ask "..."` picks the right workflow, drafts its input, and asks before it runs.
47
+
48
+ > Status: **alpha**. The engine, the CLI and three example workflows work and are tested; see
49
+ > [Limitations](#limitations) for what has and has not been verified.
50
+
51
+ ```
52
+ ┌──────────────── core (src/minifactory/core) ────────────────┐
53
+ │ engine · workflow validation · registry · events · expr │ knows nothing about
54
+ └───────────────────────────┬─────────────────────────────┘ languages, git hosts or LLMs
55
+ ports: scm · llm · secrets · source · executor · router · intake
56
+ │
57
+ ┌───────────── plugins (src/minifactory/plugins/builtin) ─────┐
58
+ │ github · local-git · bedrock · openai · jev router │
59
+ │ shell.run · agent.edit · flutter.* · scm.open_pr · ... │
60
+ └─────────────────────────────────────────────────────────┘
61
+ ```
62
+
63
+ The rule that keeps it from becoming spaghetti: **the core never imports a plugin**; plugins register themselves.
64
+
65
+ ## Install
66
+
67
+ ```bash
68
+ git clone https://github.com/juan-campuzano/minifactory && cd minifactory
69
+ python -m venv .venv && source .venv/bin/activate
70
+ pip install -e ".[dev]" # add ",aws" for the Amazon Bedrock LLM (installs boto3)
71
+ ```
72
+
73
+ Python 3.11+. Workflows live in a **workspace**, not in the package (see below).
74
+
75
+ ## Quick start (offline, no keys, no SDK)
76
+
77
+ ```bash
78
+ minifactory init my-factory && cd my-factory # a workspace: minifactory.yaml + workflows/
79
+ minifactory workflows list # templates that ship with the package
80
+ minifactory workflows copy flutter-app-factory # copies the workflow and its sample inputs
81
+ minifactory validate flutter-app-factory # fail-fast checks, runs nothing
82
+ minifactory run flutter-app-factory # runs 4 sample jobs with canned LLM replies
83
+ ```
84
+
85
+ This example factory turns an app spec into a project: plan, implement, build/test gates, an automatic repair
86
+ loop, a non-blocking coverage check, then delivery. The offline run uses a *simulated* `flutter` and canned LLM
87
+ replies, so what it produces is not a real project. It exists to show every path the engine can take:
88
+
89
+ | sample job | what it exercises |
90
+ |---|---|
91
+ | `todo_app` | happy path |
92
+ | `counter_needs_repair` | the first attempt breaks the build; the repair loop fixes it |
93
+ | `notes_low_coverage` | coverage below the threshold never blocks delivery, it annotates it |
94
+ | `bad_spec` | the plan is not valid JSON, so the job fails early and nothing is built |
95
+
96
+ Each job leaves `state.json`, `result.json` and its working copy in `.minifactory/runs/<workflow>/<job>/`;
97
+ `events.jsonl` records every step and LLM call (tokens and cost when the provider reports them).
98
+ `minifactory run ... --resume` restarts from the first step that did not finish `ok`.
99
+
100
+ ## Workspaces
101
+
102
+ Code ships in the package; your content lives in a workspace, found like git (nearest `minifactory.yaml` going up from
103
+ the current directory, else `~/.config/minifactory`):
104
+
105
+ ```
106
+ my-factory/
107
+ minifactory.yaml optional settings: runs_dir, output_dir, router, threshold, plugins
108
+ workflows/ your workflows, the ones `minifactory ask` can choose from
109
+ ... anything your workflows reference (specs/, fixtures/...), relative to this folder
110
+ ```
111
+
112
+ Relative paths inside a workflow resolve from the workspace root, wherever you run `minifactory` inside it.
113
+ Command-line flags override `minifactory.yaml`, which overrides the defaults.
114
+
115
+ ## Example workflows (templates)
116
+
117
+ Copy one into your workspace with `minifactory workflows copy NAME`; only copied workflows are routable by `ask`.
118
+
119
+ | template | what it does | needs |
120
+ |---|---|---|
121
+ | `flutter-app-factory` | the offline demo above | nothing |
122
+ | `flutter-app-factory-bedrock` | creates a **new** Flutter app from a one-line request, sized to the request ("minimal" means few features) | AWS credentials (Bedrock), Flutter SDK |
123
+ | `fix-bug` | fixes a bug in an **existing** GitHub repo: reproduces it with a failing test, fixes it minimally, opens a PR | `gh auth login`, AWS credentials, Flutter SDK |
124
+ | `create-workflow` | drafts a **new workflow** from a plain-language goal, using only the steps and plugins that exist (see below) | AWS credentials (Bedrock) |
125
+ | `java-migration-demo` | a batch transform over toy repos: deterministic branch vs LLM branch, baseline gate, repair loop | nothing |
126
+
127
+ ## Describe what you need: `minifactory ask`
128
+
129
+ ```bash
130
+ export TYPESAFE_API_KEY=... # for the default router (Jev); `--router keyword` works offline
131
+ minifactory ask "a simple app to track my expenses" --dry-run # picks a workflow, drafts its input, runs nothing
132
+ minifactory ask "a simple app to track my expenses" # same, then asks before running
133
+ ```
134
+
135
+ 1. A **router** plugin chooses among the workflows that declare a `route:` block, and says how confident it is.
136
+ 2. Policy lives in code, not in the model: with `--yes` it only acts when the router is confident, the request is
137
+ actionable and a workflow fits; otherwise it exits without doing anything.
138
+ 3. The workflow's **intake** plugin drafts its input from your text (the text is treated as data, the result is
139
+ validated), and you confirm before anything runs.
140
+ 4. **Where results go** is your choice for new projects: `--output local` (a plain git folder) or
141
+ `--output github` (a new private repo plus a pull request; `--public` to change that).
142
+
143
+ ## Fix a bug in an existing repo
144
+
145
+ ```bash
146
+ minifactory workflows copy fix-bug
147
+ minifactory ask "the counter goes up by 2 instead of 1 in acme/todo_app" --dry-run
148
+ ```
149
+
150
+ `fix-bug` clones the repo with `gh`, requires the repo to analyse cleanly, selects the relevant files, writes a test
151
+ that reproduces the bug (and stops without changing anything if that test does not fail), fixes it with an
152
+ exact-match patch, re-runs the checks, enforces a size limit on the change, and opens a pull request.
153
+ It never touches the default branch.
154
+
155
+ ## Let the factory write workflows
156
+
157
+ ```bash
158
+ minifactory workflows copy create-workflow
159
+ minifactory ask "create a workflow that clones a GitHub repo, runs dart format and opens a pull request"
160
+ minifactory workflows list # the result is listed under "drafts"
161
+ minifactory workflows enable dart-format-pr # shows what it would do, asks, then activates it
162
+ ```
163
+
164
+ The agent reads a catalog generated from the plugins installed *right now* (`minifactory plugins --schema` prints the same
165
+ text), writes `draft.yaml`, and must pass the same validator as any workflow; errors go back to it through the usual repair
166
+ loop. If the goal needs something that does not exist it declines with `MISSING: <what>` instead of inventing a step.
167
+ The result is only ever a **draft**: it is not routable by `ask` and never runs until you enable it, and `enable` first lists
168
+ every shell command, repository write and paid LLM call it contains.
169
+
170
+ ## Design principles
171
+
172
+ - **Untrusted agent output.** Edits are parsed strictly: a `PATCH` must match exactly once or nothing is written;
173
+ paths cannot escape the repo; plans are validated with pydantic; the Bedrock provider rejects a reply cut off by the token limit.
174
+ - **Bounded loops.** Repair attempts, unproductive attempts, features per plan and context size all have budgets.
175
+ - **Deterministic gates after every agent step.** `on_fail: discard` stops a job without changing anything;
176
+ `blocking: false` lets enrichment steps annotate a result without stopping it.
177
+ - **Generated workflows are drafts.** A person reviews and enables them; nothing an LLM wrote runs on its own.
178
+ - **Proposals, not pushes.** Changes to existing repos arrive as a pull request on a new branch.
179
+ - **No silent fakes.** The simulated `flutter` is opt-in (`tool: fake` or `FACTORY_FLUTTER=fake`); by default the
180
+ real SDK is required and a missing one is an error.
181
+
182
+ ## The workflow format
183
+
184
+ Deliberately small: sequence, `when`, `on_fail: fail|discard|continue`, `blocking: false`, and `${...}` templating
185
+ over `job`, `steps`, `vars` and `env`. No loops, no calls, no arithmetic: if you need that, write a step in Python.
186
+ Everything checkable is checked at load time: unknown plugins, typos in parameters, missing providers, bad `when`
187
+ syntax, references to steps that run later.
188
+
189
+ ## Write a plugin
190
+
191
+ No core changes needed. A step is a class with typed parameters:
192
+
193
+ ```python
194
+ # my_plugin.py
195
+ from minifactory.core.context import Step, StrictParams
196
+ from minifactory.core.models import StepResult
197
+ from minifactory.core.registry import register
198
+
199
+ class Params(StrictParams): # unknown keys in the YAML are errors
200
+ message: str
201
+
202
+ @register("step", "demo.hello")
203
+ class Hello(Step):
204
+ Params = Params
205
+ def run(self, ctx, p):
206
+ return StepResult.ok(f"hello {ctx.job.name}: {p.message}")
207
+ ```
208
+
209
+ ```bash
210
+ minifactory run my-workflow --plugin my_plugin
211
+ ```
212
+
213
+ Installed packages can instead expose the `minifactory.plugins` entry point and are discovered automatically.
214
+ Plugin kinds: `step`, `source`, `executor`, `scm`, `llm`, `secrets`, `router`, `intake`. Built in:
215
+
216
+ | kind | built in |
217
+ |---|---|
218
+ | `scm` | `github` (via the `gh` CLI), `local-git`, `local-dir` |
219
+ | `llm` | `bedrock`, `openai` (any OpenAI-compatible endpoint), `fixture`, `scripted` |
220
+ | `router` | `jev` (TypeSafe), `keyword` |
221
+ | `intake` | `app-spec`, `repo-task` |
222
+ | `source` | `app-specs`, `repo-tasks`, `catalog`, `static` |
223
+ | `executor` | `local`, `threads` |
224
+ | `secrets` | `env` |
225
+
226
+ See [`docs/plugins.md`](docs/plugins.md) for each kind's interface, and [`AGENTS.md`](AGENTS.md) if you are an AI
227
+ coding agent (or work with one) on this repo.
228
+
229
+ ## Limitations
230
+
231
+ - **Alpha.** Only Python 3.11 and 3.13 are tested in CI.
232
+ - The example workflows target Flutter/Dart. Other stacks need their own steps (or `shell.run`).
233
+ - The real-model runs were small: a one-line app, and a seeded one-line bug in a small repo. Larger apps, where
234
+ features must agree with each other, are not solved: expect failures there.
235
+ - The `github` provider has been exercised against a fake `gh` in tests and has created repos in manual runs; the
236
+ existing-repo flow has only been run against a local git remote, not against a real GitHub repository.
237
+ - No built-in retry or timeout policy at the engine level, and no persistent state store beyond local JSON files.
238
+
239
+ ## Development
240
+
241
+ ```bash
242
+ pip install -e ".[dev]"
243
+ ruff check src tests examples
244
+ pytest -q # no network, credentials or Flutter SDK needed
245
+ ```
246
+
247
+ ## License
248
+
249
+ MIT, see [`LICENSE`](LICENSE).
@@ -0,0 +1,219 @@
1
+ <p align="center">
2
+ <picture>
3
+ <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/juan-campuzano/minifactory/main/assets/brand/png/minifactory-horizontal-dark.png">
4
+ <img src="https://raw.githubusercontent.com/juan-campuzano/minifactory/main/assets/brand/png/minifactory-horizontal-light.png" alt="minifactory" width="420">
5
+ </picture>
6
+ </p>
7
+
8
+ A tiny, pluggable **software factory**. You describe what you need, a **workflow** does the work, and every
9
+ step an AI agent takes is fenced in by deterministic checks, budgets and a human-reviewable result.
10
+
11
+ - **Declarative workflows**: YAML made of plugin steps, validated before anything runs.
12
+ - **Agents on a leash**: the LLM is a step, never the boss. Its output is untrusted, bounded by budgets and
13
+ followed by real gates (a compiler, a test run, a diff-size limit).
14
+ - **Everything environment-specific sits behind ports** (git host, LLM, secrets, job source, executor), so
15
+ swapping GitHub for another host or Bedrock for another model is a one-line change.
16
+ - **Ask in plain language**: `minifactory ask "..."` picks the right workflow, drafts its input, and asks before it runs.
17
+
18
+ > Status: **alpha**. The engine, the CLI and three example workflows work and are tested; see
19
+ > [Limitations](#limitations) for what has and has not been verified.
20
+
21
+ ```
22
+ ┌──────────────── core (src/minifactory/core) ────────────────┐
23
+ │ engine · workflow validation · registry · events · expr │ knows nothing about
24
+ └───────────────────────────┬─────────────────────────────┘ languages, git hosts or LLMs
25
+ ports: scm · llm · secrets · source · executor · router · intake
26
+ │
27
+ ┌───────────── plugins (src/minifactory/plugins/builtin) ─────┐
28
+ │ github · local-git · bedrock · openai · jev router │
29
+ │ shell.run · agent.edit · flutter.* · scm.open_pr · ... │
30
+ └─────────────────────────────────────────────────────────┘
31
+ ```
32
+
33
+ The rule that keeps it from becoming spaghetti: **the core never imports a plugin**; plugins register themselves.
34
+
35
+ ## Install
36
+
37
+ ```bash
38
+ git clone https://github.com/juan-campuzano/minifactory && cd minifactory
39
+ python -m venv .venv && source .venv/bin/activate
40
+ pip install -e ".[dev]" # add ",aws" for the Amazon Bedrock LLM (installs boto3)
41
+ ```
42
+
43
+ Python 3.11+. Workflows live in a **workspace**, not in the package (see below).
44
+
45
+ ## Quick start (offline, no keys, no SDK)
46
+
47
+ ```bash
48
+ minifactory init my-factory && cd my-factory # a workspace: minifactory.yaml + workflows/
49
+ minifactory workflows list # templates that ship with the package
50
+ minifactory workflows copy flutter-app-factory # copies the workflow and its sample inputs
51
+ minifactory validate flutter-app-factory # fail-fast checks, runs nothing
52
+ minifactory run flutter-app-factory # runs 4 sample jobs with canned LLM replies
53
+ ```
54
+
55
+ This example factory turns an app spec into a project: plan, implement, build/test gates, an automatic repair
56
+ loop, a non-blocking coverage check, then delivery. The offline run uses a *simulated* `flutter` and canned LLM
57
+ replies, so what it produces is not a real project. It exists to show every path the engine can take:
58
+
59
+ | sample job | what it exercises |
60
+ |---|---|
61
+ | `todo_app` | happy path |
62
+ | `counter_needs_repair` | the first attempt breaks the build; the repair loop fixes it |
63
+ | `notes_low_coverage` | coverage below the threshold never blocks delivery, it annotates it |
64
+ | `bad_spec` | the plan is not valid JSON, so the job fails early and nothing is built |
65
+
66
+ Each job leaves `state.json`, `result.json` and its working copy in `.minifactory/runs/<workflow>/<job>/`;
67
+ `events.jsonl` records every step and LLM call (tokens and cost when the provider reports them).
68
+ `minifactory run ... --resume` restarts from the first step that did not finish `ok`.
69
+
70
+ ## Workspaces
71
+
72
+ Code ships in the package; your content lives in a workspace, found like git (nearest `minifactory.yaml` going up from
73
+ the current directory, else `~/.config/minifactory`):
74
+
75
+ ```
76
+ my-factory/
77
+ minifactory.yaml optional settings: runs_dir, output_dir, router, threshold, plugins
78
+ workflows/ your workflows, the ones `minifactory ask` can choose from
79
+ ... anything your workflows reference (specs/, fixtures/...), relative to this folder
80
+ ```
81
+
82
+ Relative paths inside a workflow resolve from the workspace root, wherever you run `minifactory` inside it.
83
+ Command-line flags override `minifactory.yaml`, which overrides the defaults.
84
+
85
+ ## Example workflows (templates)
86
+
87
+ Copy one into your workspace with `minifactory workflows copy NAME`; only copied workflows are routable by `ask`.
88
+
89
+ | template | what it does | needs |
90
+ |---|---|---|
91
+ | `flutter-app-factory` | the offline demo above | nothing |
92
+ | `flutter-app-factory-bedrock` | creates a **new** Flutter app from a one-line request, sized to the request ("minimal" means few features) | AWS credentials (Bedrock), Flutter SDK |
93
+ | `fix-bug` | fixes a bug in an **existing** GitHub repo: reproduces it with a failing test, fixes it minimally, opens a PR | `gh auth login`, AWS credentials, Flutter SDK |
94
+ | `create-workflow` | drafts a **new workflow** from a plain-language goal, using only the steps and plugins that exist (see below) | AWS credentials (Bedrock) |
95
+ | `java-migration-demo` | a batch transform over toy repos: deterministic branch vs LLM branch, baseline gate, repair loop | nothing |
96
+
97
+ ## Describe what you need: `minifactory ask`
98
+
99
+ ```bash
100
+ export TYPESAFE_API_KEY=... # for the default router (Jev); `--router keyword` works offline
101
+ minifactory ask "a simple app to track my expenses" --dry-run # picks a workflow, drafts its input, runs nothing
102
+ minifactory ask "a simple app to track my expenses" # same, then asks before running
103
+ ```
104
+
105
+ 1. A **router** plugin chooses among the workflows that declare a `route:` block, and says how confident it is.
106
+ 2. Policy lives in code, not in the model: with `--yes` it only acts when the router is confident, the request is
107
+ actionable and a workflow fits; otherwise it exits without doing anything.
108
+ 3. The workflow's **intake** plugin drafts its input from your text (the text is treated as data, the result is
109
+ validated), and you confirm before anything runs.
110
+ 4. **Where results go** is your choice for new projects: `--output local` (a plain git folder) or
111
+ `--output github` (a new private repo plus a pull request; `--public` to change that).
112
+
113
+ ## Fix a bug in an existing repo
114
+
115
+ ```bash
116
+ minifactory workflows copy fix-bug
117
+ minifactory ask "the counter goes up by 2 instead of 1 in acme/todo_app" --dry-run
118
+ ```
119
+
120
+ `fix-bug` clones the repo with `gh`, requires the repo to analyse cleanly, selects the relevant files, writes a test
121
+ that reproduces the bug (and stops without changing anything if that test does not fail), fixes it with an
122
+ exact-match patch, re-runs the checks, enforces a size limit on the change, and opens a pull request.
123
+ It never touches the default branch.
124
+
125
+ ## Let the factory write workflows
126
+
127
+ ```bash
128
+ minifactory workflows copy create-workflow
129
+ minifactory ask "create a workflow that clones a GitHub repo, runs dart format and opens a pull request"
130
+ minifactory workflows list # the result is listed under "drafts"
131
+ minifactory workflows enable dart-format-pr # shows what it would do, asks, then activates it
132
+ ```
133
+
134
+ The agent reads a catalog generated from the plugins installed *right now* (`minifactory plugins --schema` prints the same
135
+ text), writes `draft.yaml`, and must pass the same validator as any workflow; errors go back to it through the usual repair
136
+ loop. If the goal needs something that does not exist it declines with `MISSING: <what>` instead of inventing a step.
137
+ The result is only ever a **draft**: it is not routable by `ask` and never runs until you enable it, and `enable` first lists
138
+ every shell command, repository write and paid LLM call it contains.
139
+
140
+ ## Design principles
141
+
142
+ - **Untrusted agent output.** Edits are parsed strictly: a `PATCH` must match exactly once or nothing is written;
143
+ paths cannot escape the repo; plans are validated with pydantic; the Bedrock provider rejects a reply cut off by the token limit.
144
+ - **Bounded loops.** Repair attempts, unproductive attempts, features per plan and context size all have budgets.
145
+ - **Deterministic gates after every agent step.** `on_fail: discard` stops a job without changing anything;
146
+ `blocking: false` lets enrichment steps annotate a result without stopping it.
147
+ - **Generated workflows are drafts.** A person reviews and enables them; nothing an LLM wrote runs on its own.
148
+ - **Proposals, not pushes.** Changes to existing repos arrive as a pull request on a new branch.
149
+ - **No silent fakes.** The simulated `flutter` is opt-in (`tool: fake` or `FACTORY_FLUTTER=fake`); by default the
150
+ real SDK is required and a missing one is an error.
151
+
152
+ ## The workflow format
153
+
154
+ Deliberately small: sequence, `when`, `on_fail: fail|discard|continue`, `blocking: false`, and `${...}` templating
155
+ over `job`, `steps`, `vars` and `env`. No loops, no calls, no arithmetic: if you need that, write a step in Python.
156
+ Everything checkable is checked at load time: unknown plugins, typos in parameters, missing providers, bad `when`
157
+ syntax, references to steps that run later.
158
+
159
+ ## Write a plugin
160
+
161
+ No core changes needed. A step is a class with typed parameters:
162
+
163
+ ```python
164
+ # my_plugin.py
165
+ from minifactory.core.context import Step, StrictParams
166
+ from minifactory.core.models import StepResult
167
+ from minifactory.core.registry import register
168
+
169
+ class Params(StrictParams): # unknown keys in the YAML are errors
170
+ message: str
171
+
172
+ @register("step", "demo.hello")
173
+ class Hello(Step):
174
+ Params = Params
175
+ def run(self, ctx, p):
176
+ return StepResult.ok(f"hello {ctx.job.name}: {p.message}")
177
+ ```
178
+
179
+ ```bash
180
+ minifactory run my-workflow --plugin my_plugin
181
+ ```
182
+
183
+ Installed packages can instead expose the `minifactory.plugins` entry point and are discovered automatically.
184
+ Plugin kinds: `step`, `source`, `executor`, `scm`, `llm`, `secrets`, `router`, `intake`. Built in:
185
+
186
+ | kind | built in |
187
+ |---|---|
188
+ | `scm` | `github` (via the `gh` CLI), `local-git`, `local-dir` |
189
+ | `llm` | `bedrock`, `openai` (any OpenAI-compatible endpoint), `fixture`, `scripted` |
190
+ | `router` | `jev` (TypeSafe), `keyword` |
191
+ | `intake` | `app-spec`, `repo-task` |
192
+ | `source` | `app-specs`, `repo-tasks`, `catalog`, `static` |
193
+ | `executor` | `local`, `threads` |
194
+ | `secrets` | `env` |
195
+
196
+ See [`docs/plugins.md`](docs/plugins.md) for each kind's interface, and [`AGENTS.md`](AGENTS.md) if you are an AI
197
+ coding agent (or work with one) on this repo.
198
+
199
+ ## Limitations
200
+
201
+ - **Alpha.** Only Python 3.11 and 3.13 are tested in CI.
202
+ - The example workflows target Flutter/Dart. Other stacks need their own steps (or `shell.run`).
203
+ - The real-model runs were small: a one-line app, and a seeded one-line bug in a small repo. Larger apps, where
204
+ features must agree with each other, are not solved: expect failures there.
205
+ - The `github` provider has been exercised against a fake `gh` in tests and has created repos in manual runs; the
206
+ existing-repo flow has only been run against a local git remote, not against a real GitHub repository.
207
+ - No built-in retry or timeout policy at the engine level, and no persistent state store beyond local JSON files.
208
+
209
+ ## Development
210
+
211
+ ```bash
212
+ pip install -e ".[dev]"
213
+ ruff check src tests examples
214
+ pytest -q # no network, credentials or Flutter SDK needed
215
+ ```
216
+
217
+ ## License
218
+
219
+ MIT, see [`LICENSE`](LICENSE).
@@ -0,0 +1,62 @@
1
+ [build-system]
2
+ requires = ["setuptools>=77"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "minifactory"
7
+ version = "0.0.1"
8
+ description = "A tiny, pluggable software factory: declarative workflows made of plugin steps"
9
+ requires-python = ">=3.11"
10
+ license = "MIT"
11
+ readme = "README.md"
12
+ authors = [{ name = "Juan Campuzano" }]
13
+ keywords = ["workflow", "agents", "llm", "automation", "software-factory"]
14
+ classifiers = [
15
+ "Development Status :: 3 - Alpha",
16
+ "Intended Audience :: Developers",
17
+ "Programming Language :: Python :: 3",
18
+ "Programming Language :: Python :: 3.11",
19
+ "Programming Language :: Python :: 3.13",
20
+ ]
21
+ license-files = ["LICENSE"]
22
+ dependencies = ["pydantic>=2", "pyyaml>=6", "rich>=13", "certifi"]
23
+
24
+ [project.urls]
25
+ Homepage = "https://github.com/juan-campuzano/minifactory"
26
+ Issues = "https://github.com/juan-campuzano/minifactory/issues"
27
+
28
+ [project.optional-dependencies]
29
+ aws = ["boto3"]
30
+ dev = ["pytest", "ruff", "mypy", "types-PyYAML"]
31
+
32
+ [project.scripts]
33
+ minifactory = "minifactory.cli:main"
34
+
35
+ # Third-party packages add themselves like this (no change to the core):
36
+ # [project.entry-points."minifactory.plugins"]
37
+ # my_plugin = "my_package.factory_plugin"
38
+
39
+ [tool.setuptools.packages.find]
40
+ where = ["src"]
41
+
42
+ [tool.setuptools.package-data]
43
+ minifactory = ["py.typed", "templates/**/*"]
44
+
45
+ [tool.pytest.ini_options]
46
+ testpaths = ["tests"]
47
+ pythonpath = ["src"]
48
+
49
+ [tool.ruff]
50
+ line-length = 120
51
+ target-version = "py311"
52
+
53
+ [tool.ruff.lint]
54
+ # UP035/UP037/B905 are ignored only because the pre-existing core predates them.
55
+ select = ["E", "F", "I", "B", "UP", "SIM"]
56
+ ignore = ["UP035", "UP037", "B905"]
57
+
58
+ [tool.mypy]
59
+ python_version = "3.11"
60
+ strict = true
61
+ files = ["src"]
62
+ plugins = ["pydantic.mypy"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1 @@
1
+ __version__ = "0.0.1"
@@ -0,0 +1,3 @@
1
+ from minifactory.cli import main
2
+
3
+ raise SystemExit(main())