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.
Files changed (79) hide show
  1. {forgeo_cli-0.5.0 → forgeo_cli-0.7.0}/CHANGELOG.md +61 -1
  2. {forgeo_cli-0.5.0 → forgeo_cli-0.7.0}/PKG-INFO +80 -34
  3. forgeo_cli-0.7.0/README.md +153 -0
  4. {forgeo_cli-0.5.0 → forgeo_cli-0.7.0}/docs/agent-contract.md +23 -11
  5. {forgeo_cli-0.5.0 → forgeo_cli-0.7.0}/docs/backlog.md +77 -11
  6. {forgeo_cli-0.5.0 → forgeo_cli-0.7.0}/docs/cli-reference.md +61 -36
  7. {forgeo_cli-0.5.0 → forgeo_cli-0.7.0}/docs/configuration.md +99 -32
  8. {forgeo_cli-0.5.0 → forgeo_cli-0.7.0}/docs/getting-started.md +13 -1
  9. forgeo_cli-0.7.0/docs/index.md +67 -0
  10. {forgeo_cli-0.5.0 → forgeo_cli-0.7.0}/docs/web-console-api.md +18 -4
  11. {forgeo_cli-0.5.0 → forgeo_cli-0.7.0}/install.sh +1 -1
  12. {forgeo_cli-0.5.0 → forgeo_cli-0.7.0}/pyproject.toml +1 -1
  13. {forgeo_cli-0.5.0 → forgeo_cli-0.7.0}/src/forgeo/__init__.py +1 -1
  14. {forgeo_cli-0.5.0 → forgeo_cli-0.7.0}/src/forgeo/agent.py +8 -3
  15. {forgeo_cli-0.5.0 → forgeo_cli-0.7.0}/src/forgeo/backlog.py +134 -62
  16. forgeo_cli-0.7.0/src/forgeo/backlog_http.py +149 -0
  17. {forgeo_cli-0.5.0 → forgeo_cli-0.7.0}/src/forgeo/central.py +148 -93
  18. {forgeo_cli-0.5.0 → forgeo_cli-0.7.0}/src/forgeo/cli.py +127 -57
  19. {forgeo_cli-0.5.0 → forgeo_cli-0.7.0}/src/forgeo/config.py +20 -3
  20. {forgeo_cli-0.5.0 → forgeo_cli-0.7.0}/src/forgeo/daemon.py +12 -6
  21. {forgeo_cli-0.5.0 → forgeo_cli-0.7.0}/src/forgeo/daemon_control.py +39 -17
  22. {forgeo_cli-0.5.0 → forgeo_cli-0.7.0}/src/forgeo/forgeo.py +137 -27
  23. {forgeo_cli-0.5.0 → forgeo_cli-0.7.0}/src/forgeo/instances.py +2 -1
  24. {forgeo_cli-0.5.0 → forgeo_cli-0.7.0}/src/forgeo/models.py +115 -3
  25. {forgeo_cli-0.5.0 → forgeo_cli-0.7.0}/src/forgeo/notify.py +29 -22
  26. forgeo_cli-0.7.0/src/forgeo/oauth.py +151 -0
  27. forgeo_cli-0.7.0/src/forgeo/paths.py +80 -0
  28. {forgeo_cli-0.5.0 → forgeo_cli-0.7.0}/src/forgeo/runs.py +4 -13
  29. {forgeo_cli-0.5.0 → forgeo_cli-0.7.0}/src/forgeo/setup.py +3 -1
  30. {forgeo_cli-0.5.0 → forgeo_cli-0.7.0}/src/forgeo/update.py +1 -12
  31. {forgeo_cli-0.5.0 → forgeo_cli-0.7.0}/src/forgeo/validate.py +59 -10
  32. {forgeo_cli-0.5.0 → forgeo_cli-0.7.0}/src/forgeo/web/central/central.js +2 -1
  33. {forgeo_cli-0.5.0 → forgeo_cli-0.7.0}/tests/conftest.py +87 -2
  34. {forgeo_cli-0.5.0 → forgeo_cli-0.7.0}/tests/test_agent.py +16 -0
  35. {forgeo_cli-0.5.0 → forgeo_cli-0.7.0}/tests/test_backlog.py +4 -58
  36. forgeo_cli-0.7.0/tests/test_backlog_http.py +309 -0
  37. {forgeo_cli-0.5.0 → forgeo_cli-0.7.0}/tests/test_cli.py +218 -10
  38. {forgeo_cli-0.5.0 → forgeo_cli-0.7.0}/tests/test_factory.py +136 -0
  39. {forgeo_cli-0.5.0 → forgeo_cli-0.7.0}/tests/test_install.py +1 -1
  40. {forgeo_cli-0.5.0 → forgeo_cli-0.7.0}/tests/test_models.py +103 -0
  41. forgeo_cli-0.7.0/tests/test_oauth.py +186 -0
  42. forgeo_cli-0.7.0/tests/test_paths.py +98 -0
  43. forgeo_cli-0.7.0/tests/test_remote_backlog_cycle.py +89 -0
  44. {forgeo_cli-0.5.0 → forgeo_cli-0.7.0}/tests/test_runs.py +17 -16
  45. {forgeo_cli-0.5.0 → forgeo_cli-0.7.0}/tests/test_update.py +7 -17
  46. {forgeo_cli-0.5.0 → forgeo_cli-0.7.0}/tests/test_web.py +112 -1
  47. {forgeo_cli-0.5.0 → forgeo_cli-0.7.0}/www/index.html +24 -0
  48. forgeo_cli-0.5.0/README.md +0 -107
  49. forgeo_cli-0.5.0/docs/index.md +0 -125
  50. {forgeo_cli-0.5.0 → forgeo_cli-0.7.0}/.github/workflows/ci.yml +0 -0
  51. {forgeo_cli-0.5.0 → forgeo_cli-0.7.0}/.gitignore +0 -0
  52. {forgeo_cli-0.5.0 → forgeo_cli-0.7.0}/CONTRIBUTING.md +0 -0
  53. {forgeo_cli-0.5.0 → forgeo_cli-0.7.0}/LICENSE +0 -0
  54. {forgeo_cli-0.5.0 → forgeo_cli-0.7.0}/config/nginx-forgeo.conf +0 -0
  55. {forgeo_cli-0.5.0 → forgeo_cli-0.7.0}/docs/img/console.png +0 -0
  56. {forgeo_cli-0.5.0 → forgeo_cli-0.7.0}/docs/img/logo.png +0 -0
  57. {forgeo_cli-0.5.0 → forgeo_cli-0.7.0}/docs/img/title.svg +0 -0
  58. {forgeo_cli-0.5.0 → forgeo_cli-0.7.0}/forgeo.spec +0 -0
  59. {forgeo_cli-0.5.0 → forgeo_cli-0.7.0}/mkdocs.yml +0 -0
  60. {forgeo_cli-0.5.0 → forgeo_cli-0.7.0}/scripts/__init__.py +0 -0
  61. {forgeo_cli-0.5.0 → forgeo_cli-0.7.0}/scripts/render_homebrew_formula.py +0 -0
  62. {forgeo_cli-0.5.0 → forgeo_cli-0.7.0}/src/forgeo/__main__.py +0 -0
  63. {forgeo_cli-0.5.0 → forgeo_cli-0.7.0}/src/forgeo/git.py +0 -0
  64. {forgeo_cli-0.5.0 → forgeo_cli-0.7.0}/src/forgeo/io.py +0 -0
  65. {forgeo_cli-0.5.0 → forgeo_cli-0.7.0}/src/forgeo/web/central/central.css +0 -0
  66. {forgeo_cli-0.5.0 → forgeo_cli-0.7.0}/src/forgeo/web/central/index.html +0 -0
  67. {forgeo_cli-0.5.0 → forgeo_cli-0.7.0}/src/forgeo/web/central/instance.html +0 -0
  68. {forgeo_cli-0.5.0 → forgeo_cli-0.7.0}/src/forgeo/web/central/login.html +0 -0
  69. {forgeo_cli-0.5.0 → forgeo_cli-0.7.0}/src/forgeo/web/style.css +0 -0
  70. {forgeo_cli-0.5.0 → forgeo_cli-0.7.0}/src/forgeo/web_common.py +0 -0
  71. {forgeo_cli-0.5.0 → forgeo_cli-0.7.0}/tests/test_daemon.py +0 -0
  72. {forgeo_cli-0.5.0 → forgeo_cli-0.7.0}/tests/test_git.py +0 -0
  73. {forgeo_cli-0.5.0 → forgeo_cli-0.7.0}/tests/test_instances.py +0 -0
  74. {forgeo_cli-0.5.0 → forgeo_cli-0.7.0}/tests/test_io.py +0 -0
  75. {forgeo_cli-0.5.0 → forgeo_cli-0.7.0}/tests/test_render_homebrew.py +0 -0
  76. {forgeo_cli-0.5.0 → forgeo_cli-0.7.0}/tests/test_setup.py +0 -0
  77. {forgeo_cli-0.5.0 → forgeo_cli-0.7.0}/tests/test_web_common.py +0 -0
  78. {forgeo_cli-0.5.0 → forgeo_cli-0.7.0}/tests/test_web_lock.py +0 -0
  79. {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.5.0...HEAD
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.5.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
- ```bash
87
- # 1. Install (any one of these)
86
+ ### 1. Install the CLI
87
+
88
+ Pick any one installer (no root needed; re-running it upgrades Forgeo).
88
89
 
89
- # Homebrew (macOS / Linux)
90
+ ```bash
91
+ # Homebrew (macOS / Linux): prebuilt binary, no Python required
90
92
  brew install lucaGazzola/forgeo/forgeo
91
93
 
92
- # or: the one-liner (prebuilt binary, no Python required)
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
- # or: pipx
97
+ # pip (Python 3.11+)
96
98
  pipx install forgeo-cli
99
+ ```
97
100
 
98
- # 2. Create your Forgeo (guided wizard, run from your project root)
99
- forgeo init
101
+ ### 2. Create your Forgeo
100
102
 
101
- # 3. Start Forgeo (detached in the background; exits immediately)
102
- forgeo start # every interval_minutes, implement the oldest OPEN task (dependencies first)
103
- forgeo stop # stop the daemon again
103
+ ```bash
104
+ forgeo init
104
105
  ```
105
106
 
106
- `forgeo init` writes `forgeo.yaml` and a `.forgeo/` folder for the backlog
107
- and logs. Fill the backlog with plain JSON tasks (see
108
- [Backlog format](docs/backlog.md)) or add them from the web console while
109
- it runs, Forgeo does the rest. Open the dashboard with `forgeo web` (default <http://0.0.0.0:8790>), or keep it always-on with `forgeo web -d` (stop it with `forgeo web stop`, check it with `forgeo web status`):
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
  ![Forgeo web console](docs/img/console.png)
112
126
 
113
- By default the dashboard is open to anyone who can reach the port. On a
114
- shared host, protect it with bearer-token auth: `forgeo web --token`
115
- generates a token (printed once, saved to `~/.config/forgeo/web.toml`) and
116
- requires `Authorization: Bearer <token>` on every `/api/*` route — see
117
- [Web console & HTTP API](docs/web-console-api.md).
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
- One-off commands: `forgeo once` (single cycle), `forgeo status` (summary),
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
- You can run several factories at once, one per repository, each config is
125
- fully independent (own backlog, logs, locks). Register each `forgeo.yaml`
126
- in the instance registry with `forgeo instance add NAME --config PATH`,
127
- manage any of them by name with `forgeo start/status/stop --name NAME`,
128
- list them all with `forgeo list`, and get one aggregate overview with the
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 is snapshotted
144
- (rotating `backlog.json.bak` files) before every agent run and on daemon
145
- startup, and restored automatically if it is ever found corrupt — a bad write
146
- never loses your tasks.
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
+ [![CI](https://github.com/lucaGazzola/forgeo/actions/workflows/ci.yml/badge.svg)](https://github.com/lucaGazzola/forgeo/actions/workflows/ci.yml)
11
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](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
+ ![Forgeo web console](docs/img/console.png)
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
- | `FORGEO_TASK` | The full instruction for this run: title, blank line, description, and an "Acceptance criteria:" list when present. |
23
- | `FORGEO_REPO` | The absolute path of the repository. |
24
- | `FORGEO_BRANCH` | The branch everything is committed to (default `main`). |
25
- | *every `agent_env` key* | Any extra variables from `agent_env` in the config. |
26
- | *inherited environment* | The daemon's own environment. |
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 task id `REFACTOR` and title
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
- | `0` | **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`. |
42
- | `no_changes_exit_code` (default `3`) | **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. |
43
- | `blocked_exit_code` (default `2`) | **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). |
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** you edit by hand. It lives wherever
4
- `backlog:` points in [forgeo.yaml](configuration.md) — by default
5
- `backlog.json` at the project root, and `.forgeo/backlog.json` when generated
6
- by `forgeo init`. Keep it outside the repository if you can so the agent never
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
- | `OPEN` | To be picked by Forgeo. |
82
- | `BLOCKED` | Waiting on a human decision; Forgeo pauses while any task is blocked. |
83
- | `COMPLETED` | The agent finished and the work was committed (and pushed). |
84
- | `FAILED` | The agent errored; changes were discarded and the reason is recorded in `failure_reason`. |
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 the
262
- forgeo starts from an empty store — nothing is silently discarded;
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.