forgeo-cli 0.5.0__tar.gz → 0.7.0__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.
- {forgeo_cli-0.5.0 → forgeo_cli-0.7.0}/CHANGELOG.md +61 -1
- {forgeo_cli-0.5.0 → forgeo_cli-0.7.0}/PKG-INFO +80 -34
- forgeo_cli-0.7.0/README.md +153 -0
- {forgeo_cli-0.5.0 → forgeo_cli-0.7.0}/docs/agent-contract.md +23 -11
- {forgeo_cli-0.5.0 → forgeo_cli-0.7.0}/docs/backlog.md +77 -11
- {forgeo_cli-0.5.0 → forgeo_cli-0.7.0}/docs/cli-reference.md +61 -36
- {forgeo_cli-0.5.0 → forgeo_cli-0.7.0}/docs/configuration.md +99 -32
- {forgeo_cli-0.5.0 → forgeo_cli-0.7.0}/docs/getting-started.md +13 -1
- forgeo_cli-0.7.0/docs/index.md +67 -0
- {forgeo_cli-0.5.0 → forgeo_cli-0.7.0}/docs/web-console-api.md +18 -4
- {forgeo_cli-0.5.0 → forgeo_cli-0.7.0}/install.sh +1 -1
- {forgeo_cli-0.5.0 → forgeo_cli-0.7.0}/pyproject.toml +1 -1
- {forgeo_cli-0.5.0 → forgeo_cli-0.7.0}/src/forgeo/__init__.py +1 -1
- {forgeo_cli-0.5.0 → forgeo_cli-0.7.0}/src/forgeo/agent.py +8 -3
- {forgeo_cli-0.5.0 → forgeo_cli-0.7.0}/src/forgeo/backlog.py +134 -62
- forgeo_cli-0.7.0/src/forgeo/backlog_http.py +149 -0
- {forgeo_cli-0.5.0 → forgeo_cli-0.7.0}/src/forgeo/central.py +148 -93
- {forgeo_cli-0.5.0 → forgeo_cli-0.7.0}/src/forgeo/cli.py +127 -57
- {forgeo_cli-0.5.0 → forgeo_cli-0.7.0}/src/forgeo/config.py +20 -3
- {forgeo_cli-0.5.0 → forgeo_cli-0.7.0}/src/forgeo/daemon.py +12 -6
- {forgeo_cli-0.5.0 → forgeo_cli-0.7.0}/src/forgeo/daemon_control.py +39 -17
- {forgeo_cli-0.5.0 → forgeo_cli-0.7.0}/src/forgeo/forgeo.py +137 -27
- {forgeo_cli-0.5.0 → forgeo_cli-0.7.0}/src/forgeo/instances.py +2 -1
- {forgeo_cli-0.5.0 → forgeo_cli-0.7.0}/src/forgeo/models.py +115 -3
- {forgeo_cli-0.5.0 → forgeo_cli-0.7.0}/src/forgeo/notify.py +29 -22
- forgeo_cli-0.7.0/src/forgeo/oauth.py +151 -0
- forgeo_cli-0.7.0/src/forgeo/paths.py +80 -0
- {forgeo_cli-0.5.0 → forgeo_cli-0.7.0}/src/forgeo/runs.py +4 -13
- {forgeo_cli-0.5.0 → forgeo_cli-0.7.0}/src/forgeo/setup.py +3 -1
- {forgeo_cli-0.5.0 → forgeo_cli-0.7.0}/src/forgeo/update.py +1 -12
- {forgeo_cli-0.5.0 → forgeo_cli-0.7.0}/src/forgeo/validate.py +59 -10
- {forgeo_cli-0.5.0 → forgeo_cli-0.7.0}/src/forgeo/web/central/central.js +2 -1
- {forgeo_cli-0.5.0 → forgeo_cli-0.7.0}/tests/conftest.py +87 -2
- {forgeo_cli-0.5.0 → forgeo_cli-0.7.0}/tests/test_agent.py +16 -0
- {forgeo_cli-0.5.0 → forgeo_cli-0.7.0}/tests/test_backlog.py +4 -58
- forgeo_cli-0.7.0/tests/test_backlog_http.py +309 -0
- {forgeo_cli-0.5.0 → forgeo_cli-0.7.0}/tests/test_cli.py +218 -10
- {forgeo_cli-0.5.0 → forgeo_cli-0.7.0}/tests/test_factory.py +136 -0
- {forgeo_cli-0.5.0 → forgeo_cli-0.7.0}/tests/test_install.py +1 -1
- {forgeo_cli-0.5.0 → forgeo_cli-0.7.0}/tests/test_models.py +103 -0
- forgeo_cli-0.7.0/tests/test_oauth.py +186 -0
- forgeo_cli-0.7.0/tests/test_paths.py +98 -0
- forgeo_cli-0.7.0/tests/test_remote_backlog_cycle.py +89 -0
- {forgeo_cli-0.5.0 → forgeo_cli-0.7.0}/tests/test_runs.py +17 -16
- {forgeo_cli-0.5.0 → forgeo_cli-0.7.0}/tests/test_update.py +7 -17
- {forgeo_cli-0.5.0 → forgeo_cli-0.7.0}/tests/test_web.py +112 -1
- {forgeo_cli-0.5.0 → forgeo_cli-0.7.0}/www/index.html +24 -0
- forgeo_cli-0.5.0/README.md +0 -107
- forgeo_cli-0.5.0/docs/index.md +0 -125
- {forgeo_cli-0.5.0 → forgeo_cli-0.7.0}/.github/workflows/ci.yml +0 -0
- {forgeo_cli-0.5.0 → forgeo_cli-0.7.0}/.gitignore +0 -0
- {forgeo_cli-0.5.0 → forgeo_cli-0.7.0}/CONTRIBUTING.md +0 -0
- {forgeo_cli-0.5.0 → forgeo_cli-0.7.0}/LICENSE +0 -0
- {forgeo_cli-0.5.0 → forgeo_cli-0.7.0}/config/nginx-forgeo.conf +0 -0
- {forgeo_cli-0.5.0 → forgeo_cli-0.7.0}/docs/img/console.png +0 -0
- {forgeo_cli-0.5.0 → forgeo_cli-0.7.0}/docs/img/logo.png +0 -0
- {forgeo_cli-0.5.0 → forgeo_cli-0.7.0}/docs/img/title.svg +0 -0
- {forgeo_cli-0.5.0 → forgeo_cli-0.7.0}/forgeo.spec +0 -0
- {forgeo_cli-0.5.0 → forgeo_cli-0.7.0}/mkdocs.yml +0 -0
- {forgeo_cli-0.5.0 → forgeo_cli-0.7.0}/scripts/__init__.py +0 -0
- {forgeo_cli-0.5.0 → forgeo_cli-0.7.0}/scripts/render_homebrew_formula.py +0 -0
- {forgeo_cli-0.5.0 → forgeo_cli-0.7.0}/src/forgeo/__main__.py +0 -0
- {forgeo_cli-0.5.0 → forgeo_cli-0.7.0}/src/forgeo/git.py +0 -0
- {forgeo_cli-0.5.0 → forgeo_cli-0.7.0}/src/forgeo/io.py +0 -0
- {forgeo_cli-0.5.0 → forgeo_cli-0.7.0}/src/forgeo/web/central/central.css +0 -0
- {forgeo_cli-0.5.0 → forgeo_cli-0.7.0}/src/forgeo/web/central/index.html +0 -0
- {forgeo_cli-0.5.0 → forgeo_cli-0.7.0}/src/forgeo/web/central/instance.html +0 -0
- {forgeo_cli-0.5.0 → forgeo_cli-0.7.0}/src/forgeo/web/central/login.html +0 -0
- {forgeo_cli-0.5.0 → forgeo_cli-0.7.0}/src/forgeo/web/style.css +0 -0
- {forgeo_cli-0.5.0 → forgeo_cli-0.7.0}/src/forgeo/web_common.py +0 -0
- {forgeo_cli-0.5.0 → forgeo_cli-0.7.0}/tests/test_daemon.py +0 -0
- {forgeo_cli-0.5.0 → forgeo_cli-0.7.0}/tests/test_git.py +0 -0
- {forgeo_cli-0.5.0 → forgeo_cli-0.7.0}/tests/test_instances.py +0 -0
- {forgeo_cli-0.5.0 → forgeo_cli-0.7.0}/tests/test_io.py +0 -0
- {forgeo_cli-0.5.0 → forgeo_cli-0.7.0}/tests/test_render_homebrew.py +0 -0
- {forgeo_cli-0.5.0 → forgeo_cli-0.7.0}/tests/test_setup.py +0 -0
- {forgeo_cli-0.5.0 → forgeo_cli-0.7.0}/tests/test_web_common.py +0 -0
- {forgeo_cli-0.5.0 → forgeo_cli-0.7.0}/tests/test_web_lock.py +0 -0
- {forgeo_cli-0.5.0 → forgeo_cli-0.7.0}/www/404.html +0 -0
|
@@ -7,6 +7,64 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.7.0] - 2026-08-16
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
|
|
14
|
+
- `task_context` config key: a path to a file (e.g. `CONTEXT.md`) whose
|
|
15
|
+
contents are prepended to every agent instruction — tasks and refactoring
|
|
16
|
+
runs alike — before the task description. The agent gets the high-level
|
|
17
|
+
project overview instead of only the isolated task; the file is re-read on
|
|
18
|
+
every run, so the agent's own updates are picked up on the next cycle. A
|
|
19
|
+
missing or unreadable file never fails a cycle: it is logged, `forgeo
|
|
20
|
+
validate` reports it as a warning, and the run proceeds with the bare
|
|
21
|
+
instruction.
|
|
22
|
+
- The default agent prompt (used by `forgeo init`) now tells the agent to
|
|
23
|
+
read `AGENTS.md` (and `CONTEXT.md` if present) at the start of the session
|
|
24
|
+
and to keep them updated when a change materially affects the project
|
|
25
|
+
overview.
|
|
26
|
+
|
|
27
|
+
- `forgeo run --task <id>` runs exactly one specific `OPEN` task by id and
|
|
28
|
+
exits, instead of letting `forgeo once` pick the oldest one — for triage:
|
|
29
|
+
rerun a `FAILED` task (after reopening it) or try a risky task now. It
|
|
30
|
+
shares the same per-forgeo lock as the daemon and `forgeo once`, so it
|
|
31
|
+
never overlaps them; it refuses with a clear error when the task does not
|
|
32
|
+
exist or is not `OPEN`, and while another daemon/`once`/`run` holds the
|
|
33
|
+
lock.
|
|
34
|
+
|
|
35
|
+
## [0.6.0] - 2026-08-14
|
|
36
|
+
|
|
37
|
+
### Added
|
|
38
|
+
|
|
39
|
+
- The backlog can now live in another application instead of a file: set
|
|
40
|
+
`backlog:` to an `http(s)` URL and Forgeo reads the whole document with
|
|
41
|
+
`GET` on every read and writes it back with `POST` on every change, using
|
|
42
|
+
the same JSON shape as `backlog.json`. The endpoint replaces its task list
|
|
43
|
+
with the body it receives. A request that fails (network error, 5xx,
|
|
44
|
+
malformed body) fails the cycle and is retried on the next interval — it is
|
|
45
|
+
never read as an empty backlog, which would start a refactoring pass and let
|
|
46
|
+
the next `POST` overwrite the remote task list with nothing.
|
|
47
|
+
|
|
48
|
+
- `backlog_auth`: OAuth2 client-credentials access for a backlog URL behind an
|
|
49
|
+
identity provider such as Keycloak. Forgeo obtains an access token for a
|
|
50
|
+
confidential client (a service account, not a human login) and sends it as a
|
|
51
|
+
bearer on every backlog request; tokens are cached in memory, renewed before
|
|
52
|
+
they expire, and refreshed once with a retry when the endpoint answers 401 or
|
|
53
|
+
403. The client secret is never a config value: `client_secret_env` names the
|
|
54
|
+
environment variable holding it, so it stays out of `forgeo.yaml`.
|
|
55
|
+
|
|
56
|
+
- `state_dir`: where Forgeo's own runtime files go (`backlog.lock`,
|
|
57
|
+
`backlog.run`, `backlog.state.json`, `backlog.update.json`, `runs.jsonl`).
|
|
58
|
+
It only matters with a backlog URL, where there is no backlog file for them
|
|
59
|
+
to sit beside; it then defaults to the directory holding `forgeo.yaml`. With
|
|
60
|
+
a backlog file those paths are unchanged.
|
|
61
|
+
|
|
62
|
+
- `forgeo validate` now checks a backlog URL by fetching it once (a plain
|
|
63
|
+
`GET`, with `backlog_auth` credentials when configured), so an unreachable
|
|
64
|
+
endpoint or a rejected token is reported by the dry run instead of by the
|
|
65
|
+
first cycle. A file backlog is still read from disk, and nothing is written
|
|
66
|
+
either way.
|
|
67
|
+
|
|
10
68
|
## [0.5.0] - 2026-08-14
|
|
11
69
|
|
|
12
70
|
### Added
|
|
@@ -250,7 +308,9 @@ Initial release of the scheduled, agent-driven software forgeo.
|
|
|
250
308
|
overlapping-run skipping.
|
|
251
309
|
- Dogfooding docs removed; local configs kept out of the repository.
|
|
252
310
|
|
|
253
|
-
[Unreleased]: https://github.com/lucaGazzola/forgeo/compare/v0.
|
|
311
|
+
[Unreleased]: https://github.com/lucaGazzola/forgeo/compare/v0.7.0...HEAD
|
|
312
|
+
[0.7.0]: https://github.com/lucaGazzola/forgeo/compare/v0.6.0...v0.7.0
|
|
313
|
+
[0.6.0]: https://github.com/lucaGazzola/forgeo/compare/v0.5.0...v0.6.0
|
|
254
314
|
[0.5.0]: https://github.com/lucaGazzola/forgeo/compare/v0.4.0...v0.5.0
|
|
255
315
|
[0.4.0]: https://github.com/lucaGazzola/forgeo/compare/v0.3.0...v0.4.0
|
|
256
316
|
[0.3.0]: https://github.com/lucaGazzola/forgeo/compare/v0.2.0...v0.3.0
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: forgeo-cli
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.7.0
|
|
4
4
|
Summary: A scheduled software forgeo: executes backlog tasks on main, refactors when idle, and writes BLOCKER.md when it needs human input.
|
|
5
5
|
Project-URL: Homepage, https://forgeo.org
|
|
6
6
|
Project-URL: Documentation, https://forgeo.org
|
|
@@ -83,50 +83,93 @@ agent CLI.
|
|
|
83
83
|
|
|
84
84
|
The full walkthrough is in [Getting started](docs/getting-started.md).
|
|
85
85
|
|
|
86
|
-
|
|
87
|
-
|
|
86
|
+
### 1. Install the CLI
|
|
87
|
+
|
|
88
|
+
Pick any one installer (no root needed; re-running it upgrades Forgeo).
|
|
88
89
|
|
|
89
|
-
|
|
90
|
+
```bash
|
|
91
|
+
# Homebrew (macOS / Linux): prebuilt binary, no Python required
|
|
90
92
|
brew install lucaGazzola/forgeo/forgeo
|
|
91
93
|
|
|
92
|
-
#
|
|
94
|
+
# One-liner (Linux / macOS / Windows): prebuilt binary, falls back to pip
|
|
93
95
|
curl -fsSL https://forgeo.org/install.sh | bash
|
|
94
96
|
|
|
95
|
-
#
|
|
97
|
+
# pip (Python 3.11+)
|
|
96
98
|
pipx install forgeo-cli
|
|
99
|
+
```
|
|
97
100
|
|
|
98
|
-
|
|
99
|
-
forgeo init
|
|
101
|
+
### 2. Create your Forgeo
|
|
100
102
|
|
|
101
|
-
|
|
102
|
-
forgeo
|
|
103
|
-
forgeo stop # stop the daemon again
|
|
103
|
+
```bash
|
|
104
|
+
forgeo init
|
|
104
105
|
```
|
|
105
106
|
|
|
106
|
-
|
|
107
|
-
and
|
|
108
|
-
|
|
109
|
-
|
|
107
|
+
Guided wizard, run from your project root. Writes `forgeo.yaml` (the
|
|
108
|
+
config) and a `.forgeo/` folder for the backlog, logs and blocker files.
|
|
109
|
+
|
|
110
|
+
The base flow is then three steps: fill the backlog, check the
|
|
111
|
+
configuration, start the daemon.
|
|
112
|
+
|
|
113
|
+
### 3. Fill the backlog
|
|
114
|
+
|
|
115
|
+
```bash
|
|
116
|
+
# Either: edit the backlog file by hand — a plain JSON task list (see
|
|
117
|
+
# Backlog format), created on first use:
|
|
118
|
+
# .forgeo/backlog.json
|
|
119
|
+
|
|
120
|
+
# Or: add tasks from the web console once your forgeo is registered
|
|
121
|
+
# (first `forgeo start` registers it automatically):
|
|
122
|
+
forgeo web # dashboard at http://0.0.0.0:8790, or keep it on with `forgeo web -d`
|
|
123
|
+
```
|
|
110
124
|
|
|
111
125
|

|
|
112
126
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
127
|
+
### 4. Check the configuration
|
|
128
|
+
|
|
129
|
+
```bash
|
|
130
|
+
forgeo validate
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
Read-only dry run before the first start: verifies `forgeo.yaml`, the git
|
|
134
|
+
repo, branch and remote, that the backlog parses (fetching it once when it
|
|
135
|
+
is an HTTP endpoint), the agent command, and the lock state. Never invokes
|
|
136
|
+
the agent and writes nothing.
|
|
137
|
+
|
|
138
|
+
### 5. Start the daemon
|
|
139
|
+
|
|
140
|
+
```bash
|
|
141
|
+
forgeo start
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
Starts the daemon **detached in the background** and exits. Every
|
|
145
|
+
`interval_minutes` it runs one cycle: pick the oldest `OPEN` task whose
|
|
146
|
+
dependencies are all `COMPLETED`, run your coding agent on it, commit the
|
|
147
|
+
result. When the backlog is empty, the same agent runs a refactoring pass
|
|
148
|
+
over the codebase instead.
|
|
149
|
+
|
|
150
|
+
### Day-to-day commands
|
|
151
|
+
|
|
152
|
+
```bash
|
|
153
|
+
forgeo status # Config, backlog counts, next runnable task, daemon running?, last outcome
|
|
154
|
+
forgeo once # Run exactly one cycle in the foreground, no daemon left behind
|
|
155
|
+
forgeo run --task SELF-012 # Run one specific OPEN task now (triage)
|
|
156
|
+
forgeo stop # Stop the background daemon
|
|
157
|
+
forgeo restart # Stop and start again (re-reads forgeo.yaml after edits)
|
|
158
|
+
forgeo web # Dashboard: every instance's backlog, run history and logs
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
`forgeo web` defaults to an open dashboard on `http://0.0.0.0:8790`; on a
|
|
162
|
+
shared host protect it with `forgeo web --token` (requires
|
|
163
|
+
`Authorization: Bearer <token>` on every `/api/*` route — see
|
|
164
|
+
[Web console & HTTP API](docs/web-console-api.md)).
|
|
118
165
|
|
|
119
|
-
|
|
120
|
-
`forgeo validate` (read-only dry run before starting), `forgeo stop`,
|
|
121
|
-
`forgeo restart`, every command is in the
|
|
122
|
-
[CLI reference](docs/cli-reference.md).
|
|
166
|
+
### Multiple repositories (instances)
|
|
123
167
|
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
central dashboard, `forgeo web`.
|
|
168
|
+
Run several factories at once, one per repository; each config is fully
|
|
169
|
+
independent (own backlog, logs, locks). Register each `forgeo.yaml` with
|
|
170
|
+
`forgeo instance add NAME --config PATH`, manage any of them by name with
|
|
171
|
+
`forgeo start/status/stop --name NAME`, list them all with `forgeo list`,
|
|
172
|
+
and get one aggregate overview with the central dashboard, `forgeo web`.
|
|
130
173
|
|
|
131
174
|
## Documentation
|
|
132
175
|
|
|
@@ -140,10 +183,13 @@ central dashboard, `forgeo web`.
|
|
|
140
183
|
| Web dashboard & HTTP API | [Web console & HTTP API](docs/web-console-api.md) |
|
|
141
184
|
|
|
142
185
|
Everything is stored in plain files: the backlog, `forgeo.log`, and
|
|
143
|
-
`BLOCKER.md` whenever a decision is pending. The backlog
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
186
|
+
`BLOCKER.md` whenever a decision is pending. The backlog can also live in
|
|
187
|
+
another application behind an `http(s)` URL — Forgeo reads the whole task
|
|
188
|
+
document with `GET` and writes it back with `POST`, with optional OAuth2
|
|
189
|
+
client-credentials auth (see [Backlog format](docs/backlog.md)). A *file*
|
|
190
|
+
backlog is snapshotted (rotating `backlog.json.bak` files) before every
|
|
191
|
+
agent run and on daemon startup, and restored automatically if it is ever
|
|
192
|
+
found corrupt — a bad write never loses your tasks.
|
|
147
193
|
|
|
148
194
|
## Develop
|
|
149
195
|
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
<div align="center">
|
|
2
|
+
<img src="docs/img/logo.png" alt="Forgeo logo" width="128">
|
|
3
|
+
</div>
|
|
4
|
+
|
|
5
|
+
<div align="center">
|
|
6
|
+
<img src="docs/img/title.svg" alt="Forgeo" width="128">
|
|
7
|
+
</div>
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
[](https://github.com/lucaGazzola/forgeo/actions/workflows/ci.yml)
|
|
11
|
+
[](https://opensource.org/licenses/MIT)
|
|
12
|
+
|
|
13
|
+
**Forgeo is a software factory for your coding-agent.**
|
|
14
|
+
You're already working with an AI coding agent, prompting it task by task
|
|
15
|
+
or giving it a goal. Forgeo organizes your work in a structured way with
|
|
16
|
+
a backlog, and it decides what to work on next, runs your
|
|
17
|
+
agent on it, and commits the result. Progress, pending decisions, and history
|
|
18
|
+
are tracked in plain files you can inspect at any time, plus a web dashboard.
|
|
19
|
+
Forgeo only interrupts you when a decision is genuinely yours to
|
|
20
|
+
make, everything else happens autonomously. Transient failures (a network
|
|
21
|
+
blip, a flaky test) are retried automatically when the retry policy is
|
|
22
|
+
enabled, and only a task that keeps failing or genuinely needs a human
|
|
23
|
+
decision ever reaches you.
|
|
24
|
+
|
|
25
|
+
All you need is basic comfort with a terminal, a git repository, and any coding
|
|
26
|
+
agent CLI.
|
|
27
|
+
|
|
28
|
+
## Quickstart
|
|
29
|
+
|
|
30
|
+
The full walkthrough is in [Getting started](docs/getting-started.md).
|
|
31
|
+
|
|
32
|
+
### 1. Install the CLI
|
|
33
|
+
|
|
34
|
+
Pick any one installer (no root needed; re-running it upgrades Forgeo).
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
# Homebrew (macOS / Linux): prebuilt binary, no Python required
|
|
38
|
+
brew install lucaGazzola/forgeo/forgeo
|
|
39
|
+
|
|
40
|
+
# One-liner (Linux / macOS / Windows): prebuilt binary, falls back to pip
|
|
41
|
+
curl -fsSL https://forgeo.org/install.sh | bash
|
|
42
|
+
|
|
43
|
+
# pip (Python 3.11+)
|
|
44
|
+
pipx install forgeo-cli
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
### 2. Create your Forgeo
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
forgeo init
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
Guided wizard, run from your project root. Writes `forgeo.yaml` (the
|
|
54
|
+
config) and a `.forgeo/` folder for the backlog, logs and blocker files.
|
|
55
|
+
|
|
56
|
+
The base flow is then three steps: fill the backlog, check the
|
|
57
|
+
configuration, start the daemon.
|
|
58
|
+
|
|
59
|
+
### 3. Fill the backlog
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
# Either: edit the backlog file by hand — a plain JSON task list (see
|
|
63
|
+
# Backlog format), created on first use:
|
|
64
|
+
# .forgeo/backlog.json
|
|
65
|
+
|
|
66
|
+
# Or: add tasks from the web console once your forgeo is registered
|
|
67
|
+
# (first `forgeo start` registers it automatically):
|
|
68
|
+
forgeo web # dashboard at http://0.0.0.0:8790, or keep it on with `forgeo web -d`
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+

|
|
72
|
+
|
|
73
|
+
### 4. Check the configuration
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
forgeo validate
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
Read-only dry run before the first start: verifies `forgeo.yaml`, the git
|
|
80
|
+
repo, branch and remote, that the backlog parses (fetching it once when it
|
|
81
|
+
is an HTTP endpoint), the agent command, and the lock state. Never invokes
|
|
82
|
+
the agent and writes nothing.
|
|
83
|
+
|
|
84
|
+
### 5. Start the daemon
|
|
85
|
+
|
|
86
|
+
```bash
|
|
87
|
+
forgeo start
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
Starts the daemon **detached in the background** and exits. Every
|
|
91
|
+
`interval_minutes` it runs one cycle: pick the oldest `OPEN` task whose
|
|
92
|
+
dependencies are all `COMPLETED`, run your coding agent on it, commit the
|
|
93
|
+
result. When the backlog is empty, the same agent runs a refactoring pass
|
|
94
|
+
over the codebase instead.
|
|
95
|
+
|
|
96
|
+
### Day-to-day commands
|
|
97
|
+
|
|
98
|
+
```bash
|
|
99
|
+
forgeo status # Config, backlog counts, next runnable task, daemon running?, last outcome
|
|
100
|
+
forgeo once # Run exactly one cycle in the foreground, no daemon left behind
|
|
101
|
+
forgeo run --task SELF-012 # Run one specific OPEN task now (triage)
|
|
102
|
+
forgeo stop # Stop the background daemon
|
|
103
|
+
forgeo restart # Stop and start again (re-reads forgeo.yaml after edits)
|
|
104
|
+
forgeo web # Dashboard: every instance's backlog, run history and logs
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
`forgeo web` defaults to an open dashboard on `http://0.0.0.0:8790`; on a
|
|
108
|
+
shared host protect it with `forgeo web --token` (requires
|
|
109
|
+
`Authorization: Bearer <token>` on every `/api/*` route — see
|
|
110
|
+
[Web console & HTTP API](docs/web-console-api.md)).
|
|
111
|
+
|
|
112
|
+
### Multiple repositories (instances)
|
|
113
|
+
|
|
114
|
+
Run several factories at once, one per repository; each config is fully
|
|
115
|
+
independent (own backlog, logs, locks). Register each `forgeo.yaml` with
|
|
116
|
+
`forgeo instance add NAME --config PATH`, manage any of them by name with
|
|
117
|
+
`forgeo start/status/stop --name NAME`, list them all with `forgeo list`,
|
|
118
|
+
and get one aggregate overview with the central dashboard, `forgeo web`.
|
|
119
|
+
|
|
120
|
+
## Documentation
|
|
121
|
+
|
|
122
|
+
| Topic | Where |
|
|
123
|
+
| --- | --- |
|
|
124
|
+
| Install, init, first cycle | [Getting started](docs/getting-started.md) |
|
|
125
|
+
| Every `forgeo.yaml` key | [Configuration](docs/configuration.md) |
|
|
126
|
+
| Task schema and statuses | [Backlog format](docs/backlog.md) |
|
|
127
|
+
| How the agent is invoked (env, exit codes, timeouts) | [Agent contract](docs/agent-contract.md) |
|
|
128
|
+
| All CLI commands | [CLI reference](docs/cli-reference.md) |
|
|
129
|
+
| Web dashboard & HTTP API | [Web console & HTTP API](docs/web-console-api.md) |
|
|
130
|
+
|
|
131
|
+
Everything is stored in plain files: the backlog, `forgeo.log`, and
|
|
132
|
+
`BLOCKER.md` whenever a decision is pending. The backlog can also live in
|
|
133
|
+
another application behind an `http(s)` URL — Forgeo reads the whole task
|
|
134
|
+
document with `GET` and writes it back with `POST`, with optional OAuth2
|
|
135
|
+
client-credentials auth (see [Backlog format](docs/backlog.md)). A *file*
|
|
136
|
+
backlog is snapshotted (rotating `backlog.json.bak` files) before every
|
|
137
|
+
agent run and on daemon startup, and restored automatically if it is ever
|
|
138
|
+
found corrupt — a bad write never loses your tasks.
|
|
139
|
+
|
|
140
|
+
## Develop
|
|
141
|
+
|
|
142
|
+
```bash
|
|
143
|
+
pip install -e ".[dev]"
|
|
144
|
+
pytest
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
See [CONTRIBUTING.md](CONTRIBUTING.md) for the development setup, quality
|
|
148
|
+
gates (`pytest`, `ruff check`, `mypy src/forgeo`), and the pull-request
|
|
149
|
+
process.
|
|
150
|
+
|
|
151
|
+
## License
|
|
152
|
+
|
|
153
|
+
MIT — see [LICENSE](LICENSE).
|
|
@@ -19,18 +19,28 @@ the process environment is augmented as follows:
|
|
|
19
19
|
|
|
20
20
|
| Variable | Meaning |
|
|
21
21
|
| --- | --- |
|
|
22
|
-
|
|
|
23
|
-
|
|
|
24
|
-
|
|
|
25
|
-
|
|
|
26
|
-
|
|
|
22
|
+
| <span style="white-space: nowrap">`FORGEO_TASK`</span> | The full instruction for this run: the project context when `task_context` is configured, then title, blank line, description, and an "Acceptance criteria:" list when present. |
|
|
23
|
+
| <span style="white-space: nowrap">`FORGEO_REPO`</span> | The absolute path of the repository. |
|
|
24
|
+
| <span style="white-space: nowrap">`FORGEO_BRANCH`</span> | The branch everything is committed to (default `main`). |
|
|
25
|
+
| <span style="white-space: nowrap">*every `agent_env` key*</span> | Any extra variables from `agent_env` in the config. |
|
|
26
|
+
| <span style="white-space: nowrap">*inherited environment*</span> | The daemon's own environment. |
|
|
27
27
|
|
|
28
28
|
`FORGEO_*` variables are set unconditionally and take precedence over both the
|
|
29
29
|
inherited environment and `agent_env`.
|
|
30
30
|
|
|
31
|
+
### The task is not the whole picture
|
|
32
|
+
|
|
33
|
+
A task description is isolated by design: it describes one unit of work, not
|
|
34
|
+
the project. When `task_context` is set (see
|
|
35
|
+
[Configuration](configuration.md#task_context)), Forgeo prepends the contents
|
|
36
|
+
of that file — the high-level project overview — to `FORGEO_TASK` before the
|
|
37
|
+
task, under a `# Project context` heading, followed by the task under a
|
|
38
|
+
`# Task` heading. The file is re-read on every run, so an agent's own updates
|
|
39
|
+
to it are seen by the next cycle.
|
|
40
|
+
|
|
31
41
|
For a refactoring run (empty backlog) the same contract applies: the refactor
|
|
32
|
-
prompt arrives as `FORGEO_TASK` with the
|
|
33
|
-
"Refactoring pass".
|
|
42
|
+
prompt arrives as `FORGEO_TASK` (with the context prepended when configured),
|
|
43
|
+
with the task id `REFACTOR` and title "Refactoring pass".
|
|
34
44
|
|
|
35
45
|
## Exit codes
|
|
36
46
|
|
|
@@ -38,10 +48,10 @@ The exit code decides the outcome of the run:
|
|
|
38
48
|
|
|
39
49
|
| Exit code | Outcome | What happens |
|
|
40
50
|
| --- | --- | --- |
|
|
41
|
-
|
|
|
42
|
-
|
|
|
43
|
-
|
|
|
44
|
-
| anything else | **ERROR** | Changes are discarded (`git reset --hard` + `git clean -fd`), the failure is logged, and the task is marked `FAILED`. |
|
|
51
|
+
| <span style="white-space: nowrap">`0`</span> | **SUCCESS** | Everything is committed (`git add -A && git commit`) with the message `<title> (#<id>)`, pushed when a remote is set, and the task is marked `COMPLETED`. |
|
|
52
|
+
| <span style="white-space: nowrap">`no_changes_exit_code` (default `3`)</span> | **SUCCESS, no changes** | The agent explicitly reports the task needs **no code change**: the task is marked `COMPLETED` without a commit (and the run record notes why). Only accepted when the working tree is clean. |
|
|
53
|
+
| <span style="white-space: nowrap">`blocked_exit_code` (default `2`)</span> | **BLOCKED** | The agent needs a human decision. Partial work is committed as `<title> [partial]`, the agent's reason is persisted on the task (`blocker_reason`), optional Telegram and/or webhook notifications are sent, and the task is marked `BLOCKED`. `BLOCKER.md` is rendered from the backlog's `BLOCKED` tasks on the next cycle — real per-task reasons, never generic text — and disappears once the last one is resolved (reopen it from the web console). |
|
|
54
|
+
| <span style="white-space: nowrap">anything else</span> | **ERROR** | Changes are discarded (`git reset --hard` + `git clean -fd`), the failure is logged, and the task is marked `FAILED`. |
|
|
45
55
|
|
|
46
56
|
The blocked exit code is configurable via `blocked_exit_code` in
|
|
47
57
|
[forgeo.yaml](configuration.md), and the no-change exit code via
|
|
@@ -125,6 +135,8 @@ agent_command: >
|
|
|
125
135
|
Make the code changes requested below and nothing else. Do NOT run
|
|
126
136
|
git commit, git push, or git add -A — Forgeo commits your work.
|
|
127
137
|
Verify with the test suite where applicable.
|
|
138
|
+
Read AGENTS.md (and CONTEXT.md if present) at the start of the session;
|
|
139
|
+
if your change materially affects the project overview, keep them updated.
|
|
128
140
|
$FORGEO_TASK"
|
|
129
141
|
```
|
|
130
142
|
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
# Backlog format
|
|
2
2
|
|
|
3
|
-
The backlog is a **plain JSON file
|
|
4
|
-
`backlog:` points in [forgeo.yaml](configuration.md) —
|
|
5
|
-
`backlog.json` at the project root,
|
|
6
|
-
|
|
7
|
-
touches it.
|
|
3
|
+
The backlog is a **plain JSON document**. By default it is a file you edit by
|
|
4
|
+
hand, living wherever `backlog:` points in [forgeo.yaml](configuration.md) —
|
|
5
|
+
`backlog.json` at the project root, or `.forgeo/backlog.json` when generated by
|
|
6
|
+
`forgeo init`. Keep it outside the repository if you can so the agent never
|
|
7
|
+
touches it. It can also be [served over HTTP](#a-backlog-over-http) by another
|
|
8
|
+
application, in which case the document below is exactly what that endpoint
|
|
9
|
+
exchanges with Forgeo.
|
|
8
10
|
|
|
9
11
|
```json
|
|
10
12
|
{
|
|
@@ -78,10 +80,10 @@ cheap/fast model and hard ones to a frontier model:
|
|
|
78
80
|
|
|
79
81
|
| Status | Meaning |
|
|
80
82
|
| --- | --- |
|
|
81
|
-
|
|
|
82
|
-
|
|
|
83
|
-
|
|
|
84
|
-
|
|
|
83
|
+
| <span style="white-space: nowrap">`OPEN`</span> | To be picked by Forgeo. |
|
|
84
|
+
| <span style="white-space: nowrap">`BLOCKED`</span> | Waiting on a human decision; Forgeo pauses while any task is blocked. |
|
|
85
|
+
| <span style="white-space: nowrap">`COMPLETED`</span> | The agent finished and the work was committed (and pushed). |
|
|
86
|
+
| <span style="white-space: nowrap">`FAILED`</span> | The agent errored; changes were discarded and the reason is recorded in `failure_reason`. |
|
|
85
87
|
|
|
86
88
|
## Retrying a failed task
|
|
87
89
|
|
|
@@ -258,8 +260,9 @@ The backlog is the single source of truth, so it is guarded on both ends:
|
|
|
258
260
|
|
|
259
261
|
- a missing file is treated as an empty backlog (and is created on first
|
|
260
262
|
write);
|
|
261
|
-
- a corrupt file is renamed to `backlog.json.corrupt-<timestamp>` and
|
|
262
|
-
|
|
263
|
+
- a corrupt file is renamed to `backlog.json.corrupt-<timestamp>` and replaced
|
|
264
|
+
by the newest valid snapshot, or by an empty store when there is none —
|
|
265
|
+
nothing is silently discarded;
|
|
263
266
|
- an unparsable task row is kept as a `FAILED` task rather than killing the
|
|
264
267
|
whole store;
|
|
265
268
|
- before every agent run (and on daemon startup) the current backlog is
|
|
@@ -282,3 +285,66 @@ in place automatically and the corrupt file is preserved under
|
|
|
282
285
|
favor of an older valid one; when no snapshot exists, the forgeo falls back to
|
|
283
286
|
an empty store exactly as before. A missing backlog is a no-op — no snapshot
|
|
284
287
|
is created for a file that does not exist.
|
|
288
|
+
|
|
289
|
+
This whole section is about a backlog *file*. A backlog URL is owned by the
|
|
290
|
+
application serving it, which keeps its own history, so Forgeo neither
|
|
291
|
+
snapshots nor repairs it — see below.
|
|
292
|
+
|
|
293
|
+
## A backlog over HTTP
|
|
294
|
+
|
|
295
|
+
Setting `backlog:` to an `http(s)` URL moves the backlog into another
|
|
296
|
+
application — typically one that already displays and edits work items. Forgeo
|
|
297
|
+
then treats that endpoint exactly like the file:
|
|
298
|
+
|
|
299
|
+
| When | Request |
|
|
300
|
+
| --- | --- |
|
|
301
|
+
| <span style="white-space: nowrap">Every read</span> | `GET <url>` returns the whole document |
|
|
302
|
+
| <span style="white-space: nowrap">Every write</span> | `POST <url>` sends the whole document back |
|
|
303
|
+
|
|
304
|
+
```yaml
|
|
305
|
+
backlog: https://api.example.com/api/forgeo/backlog
|
|
306
|
+
```
|
|
307
|
+
|
|
308
|
+
Add [`backlog_auth`](configuration.md#backlog_auth) when the endpoint requires
|
|
309
|
+
a token. Everything else is unchanged: the same task schema, the same
|
|
310
|
+
oldest-first ordering, the same status transitions.
|
|
311
|
+
|
|
312
|
+
### What the endpoint must do
|
|
313
|
+
|
|
314
|
+
- **Return the document under `tasks`**, as above. A response that is not a
|
|
315
|
+
JSON object, or whose `tasks` is not a list, reads as an empty backlog.
|
|
316
|
+
- **Replace, never append.** The POST body is the complete task list as it
|
|
317
|
+
should be after the change; an endpoint that appends will duplicate every
|
|
318
|
+
task on every cycle.
|
|
319
|
+
- **Send dates as ISO-8601 strings**, not epoch numbers. Jackson (and several
|
|
320
|
+
other serializers) emit `java.time` values as numeric timestamps by default;
|
|
321
|
+
Forgeo would read those as Unix timestamps, dating every task to 1970 and
|
|
322
|
+
inverting the oldest-first ordering.
|
|
323
|
+
- **Never send `null` for a list field.** `dependencies`,
|
|
324
|
+
`acceptance_criteria`, `files_to_modify`, `blocker_reason` and
|
|
325
|
+
`failure_reason` accept a list or nothing at all — an explicit `null` makes
|
|
326
|
+
that row unparsable, and it comes back as a `FAILED` placeholder task.
|
|
327
|
+
- **Preserve `agent_command`'s shape.** A string is run through a shell, a list
|
|
328
|
+
is executed directly; turning `"claude -p ..."` into `["claude -p ..."]`
|
|
329
|
+
makes Forgeo look for a binary with that entire name.
|
|
330
|
+
- **Store the engine-managed fields it receives** (`status`, `updated_at`,
|
|
331
|
+
`blocker_reason`, `blocked_count`, `failure_reason`) and hand them back
|
|
332
|
+
unchanged. That is how a blocked task keeps its explanation.
|
|
333
|
+
|
|
334
|
+
### When the endpoint is down
|
|
335
|
+
|
|
336
|
+
A failed request **fails the cycle**: the daemon logs the error and retries on
|
|
337
|
+
the next interval, leaving the remote backlog untouched. It is never read as an
|
|
338
|
+
empty backlog — that would start a refactoring pass and let the POST at the end
|
|
339
|
+
of the cycle overwrite the real task list with nothing. `forgeo status` and
|
|
340
|
+
`forgeo once` report `Backlog unavailable: ...` and exit `1`; the web console
|
|
341
|
+
answers `502` for that instance's tasks and flags it on the home page instead
|
|
342
|
+
of showing an empty board.
|
|
343
|
+
|
|
344
|
+
### Runtime files
|
|
345
|
+
|
|
346
|
+
There is no backlog file for Forgeo's own runtime files to sit beside, so
|
|
347
|
+
`backlog.lock`, `backlog.run`, `backlog.state.json`, `backlog.update.json` and
|
|
348
|
+
`runs.jsonl` go into `state_dir`, which defaults to the directory holding
|
|
349
|
+
`forgeo.yaml`. No snapshots are written: the document belongs to the remote
|
|
350
|
+
application, so rolling it back is that application's job, not Forgeo's.
|