forgeo-cli 0.4.0__tar.gz → 0.6.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 (90) hide show
  1. {forgeo_cli-0.4.0 → forgeo_cli-0.6.0}/.github/workflows/ci.yml +60 -1
  2. {forgeo_cli-0.4.0 → forgeo_cli-0.6.0}/.gitignore +1 -0
  3. forgeo_cli-0.6.0/CHANGELOG.md +293 -0
  4. {forgeo_cli-0.4.0 → forgeo_cli-0.6.0}/CONTRIBUTING.md +10 -3
  5. {forgeo_cli-0.4.0 → forgeo_cli-0.6.0}/PKG-INFO +32 -11
  6. {forgeo_cli-0.4.0 → forgeo_cli-0.6.0}/README.md +30 -9
  7. {forgeo_cli-0.4.0 → forgeo_cli-0.6.0}/docs/agent-contract.md +30 -2
  8. forgeo_cli-0.6.0/docs/backlog.md +350 -0
  9. forgeo_cli-0.6.0/docs/cli-reference.md +346 -0
  10. forgeo_cli-0.6.0/docs/configuration.md +356 -0
  11. {forgeo_cli-0.4.0 → forgeo_cli-0.6.0}/docs/getting-started.md +53 -21
  12. {forgeo_cli-0.4.0 → forgeo_cli-0.6.0}/docs/index.md +35 -13
  13. {forgeo_cli-0.4.0 → forgeo_cli-0.6.0}/docs/web-console-api.md +218 -45
  14. {forgeo_cli-0.4.0 → forgeo_cli-0.6.0}/install.sh +29 -3
  15. {forgeo_cli-0.4.0 → forgeo_cli-0.6.0}/pyproject.toml +1 -1
  16. forgeo_cli-0.6.0/scripts/__init__.py +0 -0
  17. forgeo_cli-0.6.0/scripts/render_homebrew_formula.py +119 -0
  18. {forgeo_cli-0.4.0 → forgeo_cli-0.6.0}/src/forgeo/__init__.py +1 -1
  19. {forgeo_cli-0.4.0 → forgeo_cli-0.6.0}/src/forgeo/agent.py +25 -1
  20. forgeo_cli-0.6.0/src/forgeo/backlog.py +630 -0
  21. forgeo_cli-0.6.0/src/forgeo/backlog_http.py +149 -0
  22. {forgeo_cli-0.4.0 → forgeo_cli-0.6.0}/src/forgeo/central.py +586 -69
  23. {forgeo_cli-0.4.0 → forgeo_cli-0.6.0}/src/forgeo/cli.py +378 -50
  24. {forgeo_cli-0.4.0 → forgeo_cli-0.6.0}/src/forgeo/config.py +18 -3
  25. forgeo_cli-0.6.0/src/forgeo/daemon.py +432 -0
  26. {forgeo_cli-0.4.0 → forgeo_cli-0.6.0}/src/forgeo/daemon_control.py +49 -20
  27. {forgeo_cli-0.4.0 → forgeo_cli-0.6.0}/src/forgeo/forgeo.py +238 -42
  28. {forgeo_cli-0.4.0 → forgeo_cli-0.6.0}/src/forgeo/git.py +14 -0
  29. {forgeo_cli-0.4.0 → forgeo_cli-0.6.0}/src/forgeo/instances.py +2 -1
  30. forgeo_cli-0.6.0/src/forgeo/models.py +526 -0
  31. forgeo_cli-0.6.0/src/forgeo/notify.py +131 -0
  32. forgeo_cli-0.6.0/src/forgeo/oauth.py +151 -0
  33. forgeo_cli-0.6.0/src/forgeo/paths.py +80 -0
  34. forgeo_cli-0.6.0/src/forgeo/runs.py +126 -0
  35. {forgeo_cli-0.4.0 → forgeo_cli-0.6.0}/src/forgeo/setup.py +23 -3
  36. forgeo_cli-0.6.0/src/forgeo/update.py +167 -0
  37. forgeo_cli-0.6.0/src/forgeo/validate.py +249 -0
  38. {forgeo_cli-0.4.0 → forgeo_cli-0.6.0}/src/forgeo/web/central/central.css +329 -1
  39. {forgeo_cli-0.4.0 → forgeo_cli-0.6.0}/src/forgeo/web/central/central.js +449 -59
  40. {forgeo_cli-0.4.0 → forgeo_cli-0.6.0}/src/forgeo/web/central/instance.html +61 -13
  41. forgeo_cli-0.6.0/src/forgeo/web/central/login.html +76 -0
  42. {forgeo_cli-0.4.0 → forgeo_cli-0.6.0}/src/forgeo/web/style.css +7 -1
  43. {forgeo_cli-0.4.0 → forgeo_cli-0.6.0}/src/forgeo/web_common.py +2 -0
  44. {forgeo_cli-0.4.0 → forgeo_cli-0.6.0}/tests/conftest.py +83 -2
  45. {forgeo_cli-0.4.0 → forgeo_cli-0.6.0}/tests/test_agent.py +27 -0
  46. forgeo_cli-0.6.0/tests/test_backlog.py +899 -0
  47. forgeo_cli-0.6.0/tests/test_backlog_http.py +309 -0
  48. {forgeo_cli-0.4.0 → forgeo_cli-0.6.0}/tests/test_cli.py +389 -15
  49. forgeo_cli-0.6.0/tests/test_daemon.py +383 -0
  50. forgeo_cli-0.6.0/tests/test_factory.py +947 -0
  51. {forgeo_cli-0.4.0 → forgeo_cli-0.6.0}/tests/test_git.py +20 -0
  52. {forgeo_cli-0.4.0 → forgeo_cli-0.6.0}/tests/test_install.py +3 -2
  53. {forgeo_cli-0.4.0 → forgeo_cli-0.6.0}/tests/test_models.py +191 -0
  54. forgeo_cli-0.6.0/tests/test_oauth.py +186 -0
  55. forgeo_cli-0.6.0/tests/test_paths.py +98 -0
  56. forgeo_cli-0.6.0/tests/test_remote_backlog_cycle.py +89 -0
  57. forgeo_cli-0.6.0/tests/test_render_homebrew.py +83 -0
  58. forgeo_cli-0.6.0/tests/test_runs.py +516 -0
  59. {forgeo_cli-0.4.0 → forgeo_cli-0.6.0}/tests/test_setup.py +16 -1
  60. forgeo_cli-0.6.0/tests/test_update.py +200 -0
  61. {forgeo_cli-0.4.0 → forgeo_cli-0.6.0}/tests/test_web.py +623 -9
  62. forgeo_cli-0.6.0/tests/test_web_lock.py +521 -0
  63. {forgeo_cli-0.4.0 → forgeo_cli-0.6.0}/www/index.html +27 -3
  64. forgeo_cli-0.4.0/CHANGELOG.md +0 -150
  65. forgeo_cli-0.4.0/docs/backlog.md +0 -135
  66. forgeo_cli-0.4.0/docs/cli-reference.md +0 -211
  67. forgeo_cli-0.4.0/docs/configuration.md +0 -195
  68. forgeo_cli-0.4.0/src/forgeo/backlog.py +0 -296
  69. forgeo_cli-0.4.0/src/forgeo/daemon.py +0 -212
  70. forgeo_cli-0.4.0/src/forgeo/models.py +0 -269
  71. forgeo_cli-0.4.0/src/forgeo/notify.py +0 -77
  72. forgeo_cli-0.4.0/src/forgeo/runs.py +0 -73
  73. forgeo_cli-0.4.0/tests/test_backlog.py +0 -373
  74. forgeo_cli-0.4.0/tests/test_daemon.py +0 -162
  75. forgeo_cli-0.4.0/tests/test_factory.py +0 -427
  76. forgeo_cli-0.4.0/tests/test_runs.py +0 -240
  77. {forgeo_cli-0.4.0 → forgeo_cli-0.6.0}/LICENSE +0 -0
  78. {forgeo_cli-0.4.0 → forgeo_cli-0.6.0}/config/nginx-forgeo.conf +0 -0
  79. {forgeo_cli-0.4.0 → forgeo_cli-0.6.0}/docs/img/console.png +0 -0
  80. {forgeo_cli-0.4.0 → forgeo_cli-0.6.0}/docs/img/logo.png +0 -0
  81. {forgeo_cli-0.4.0 → forgeo_cli-0.6.0}/docs/img/title.svg +0 -0
  82. {forgeo_cli-0.4.0 → forgeo_cli-0.6.0}/forgeo.spec +0 -0
  83. {forgeo_cli-0.4.0 → forgeo_cli-0.6.0}/mkdocs.yml +0 -0
  84. {forgeo_cli-0.4.0 → forgeo_cli-0.6.0}/src/forgeo/__main__.py +0 -0
  85. {forgeo_cli-0.4.0 → forgeo_cli-0.6.0}/src/forgeo/io.py +0 -0
  86. {forgeo_cli-0.4.0 → forgeo_cli-0.6.0}/src/forgeo/web/central/index.html +0 -0
  87. {forgeo_cli-0.4.0 → forgeo_cli-0.6.0}/tests/test_instances.py +0 -0
  88. {forgeo_cli-0.4.0 → forgeo_cli-0.6.0}/tests/test_io.py +0 -0
  89. {forgeo_cli-0.4.0 → forgeo_cli-0.6.0}/tests/test_web_common.py +0 -0
  90. {forgeo_cli-0.4.0 → forgeo_cli-0.6.0}/www/404.html +0 -0
@@ -35,7 +35,10 @@ jobs:
35
35
  fail-fast: false
36
36
  matrix:
37
37
  include:
38
- - os: ubuntu-latest
38
+ # Linux is built on ubuntu-22.04 (glibc 2.35) so the binary also
39
+ # runs on older distros (Homebrew-on-Linux, Ubuntu 22.04+): a build
40
+ # on ubuntu-latest (24.04) would require glibc >= 2.38.
41
+ - os: ubuntu-22.04
39
42
  asset: forgeo-linux-amd64
40
43
  binary: dist/forgeo
41
44
  - os: macos-15-intel
@@ -90,6 +93,24 @@ jobs:
90
93
  with:
91
94
  path: binaries
92
95
  merge-multiple: true
96
+ - name: Extract release notes from CHANGELOG.md
97
+ id: notes
98
+ run: |
99
+ python - "${{ github.ref_name }}" <<'EOF'
100
+ import re
101
+ import sys
102
+ import pathlib
103
+
104
+ version = sys.argv[1].removeprefix("v")
105
+ text = pathlib.Path("CHANGELOG.md").read_text(encoding="utf-8")
106
+ pattern = re.compile(
107
+ rf"^## \[{re.escape(version)}\] .*?\n(.*?)(?=^## \[|\Z)",
108
+ re.MULTILINE | re.DOTALL,
109
+ )
110
+ match = pattern.search(text)
111
+ notes = match.group(1).strip() if match else ""
112
+ pathlib.Path("release-body.md").write_text(notes, encoding="utf-8")
113
+ EOF
93
114
  - name: Publish GitHub Release
94
115
  uses: softprops/action-gh-release@v2
95
116
  with:
@@ -97,6 +118,44 @@ jobs:
97
118
  dist/*.whl
98
119
  dist/*.tar.gz
99
120
  binaries/*
121
+ body_path: release-body.md
122
+
123
+ publish-homebrew:
124
+ if: startsWith(github.ref, 'refs/tags/v')
125
+ needs: build-binaries
126
+ runs-on: ubuntu-latest
127
+ steps:
128
+ - uses: actions/checkout@v4
129
+ - uses: actions/setup-python@v5
130
+ with:
131
+ python-version: "3.11"
132
+ - name: Download binary artifacts
133
+ uses: actions/download-artifact@v4
134
+ with:
135
+ path: binaries
136
+ merge-multiple: true
137
+ - name: Render the Homebrew formula
138
+ run: python scripts/render_homebrew_formula.py "${{ github.ref_name }}" binaries > /tmp/forgeo.rb
139
+ - name: Publish the formula to the homebrew-forgeo tap
140
+ env:
141
+ TAP_REPO_TOKEN: ${{ secrets.HOMEBREW_TAP_TOKEN }}
142
+ run: |
143
+ set -eu
144
+ if [ -z "$TAP_REPO_TOKEN" ]; then
145
+ echo "::error::HOMEBREW_TAP_TOKEN secret is not set. Create a PAT with write access to lucaGazzola/homebrew-forgeo and store it as HOMEBREW_TAP_TOKEN; without it the Homebrew tap is not updated for this release."
146
+ exit 1
147
+ fi
148
+ git clone "https://x-access-token:${TAP_REPO_TOKEN}@github.com/lucaGazzola/homebrew-forgeo.git" tap
149
+ cp /tmp/forgeo.rb tap/Formula/forgeo.rb
150
+ git -C tap config user.name "Forgeo release bot"
151
+ git -C tap config user.email "forgeo-releases@users.noreply.github.com"
152
+ if git -C tap diff --quiet; then
153
+ echo "Formula unchanged; nothing to push."
154
+ else
155
+ git -C tap add Formula/forgeo.rb
156
+ git -C tap commit -m "forgeo ${{ github.ref_name }}"
157
+ git -C tap push origin main
158
+ fi
100
159
 
101
160
  publish-pypi:
102
161
  if: startsWith(github.ref, 'refs/tags/v')
@@ -27,6 +27,7 @@ build/
27
27
  dist/
28
28
 
29
29
  # AI assistant context
30
+ AGENTS.md
30
31
  CONTEXT.md
31
32
 
32
33
  # Local deploy script (server-specific paths)
@@ -0,0 +1,293 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [Unreleased]
9
+
10
+ ## [0.6.0] - 2026-08-14
11
+
12
+ ### Added
13
+
14
+ - The backlog can now live in another application instead of a file: set
15
+ `backlog:` to an `http(s)` URL and Forgeo reads the whole document with
16
+ `GET` on every read and writes it back with `POST` on every change, using
17
+ the same JSON shape as `backlog.json`. The endpoint replaces its task list
18
+ with the body it receives. A request that fails (network error, 5xx,
19
+ malformed body) fails the cycle and is retried on the next interval — it is
20
+ never read as an empty backlog, which would start a refactoring pass and let
21
+ the next `POST` overwrite the remote task list with nothing.
22
+
23
+ - `backlog_auth`: OAuth2 client-credentials access for a backlog URL behind an
24
+ identity provider such as Keycloak. Forgeo obtains an access token for a
25
+ confidential client (a service account, not a human login) and sends it as a
26
+ bearer on every backlog request; tokens are cached in memory, renewed before
27
+ they expire, and refreshed once with a retry when the endpoint answers 401 or
28
+ 403. The client secret is never a config value: `client_secret_env` names the
29
+ environment variable holding it, so it stays out of `forgeo.yaml`.
30
+
31
+ - `state_dir`: where Forgeo's own runtime files go (`backlog.lock`,
32
+ `backlog.run`, `backlog.state.json`, `backlog.update.json`, `runs.jsonl`).
33
+ It only matters with a backlog URL, where there is no backlog file for them
34
+ to sit beside; it then defaults to the directory holding `forgeo.yaml`. With
35
+ a backlog file those paths are unchanged.
36
+
37
+ - `forgeo validate` now checks a backlog URL by fetching it once (a plain
38
+ `GET`, with `backlog_auth` credentials when configured), so an unreachable
39
+ endpoint or a rejected token is reported by the dry run instead of by the
40
+ first cycle. A file backlog is still read from disk, and nothing is written
41
+ either way.
42
+
43
+ ## [0.5.0] - 2026-08-14
44
+
45
+ ### Added
46
+
47
+ - `forgeo validate` (and the pre-flight check before a detached
48
+ `forgeo start`) distinguishes a repository with no commits yet: a clean
49
+ tree is now a warning — the first cycle creates the initial commit — while
50
+ a non-clean tree is a problem that names the fix
51
+ (`git add -A && git commit -m "Initial commit"`), since every file is
52
+ untracked and every cycle would otherwise refuse as dirty. Previously
53
+ "no commits" was always a hard problem with a misleading message.
54
+
55
+ - `forgeo start` now starts the daemon **detached in the background and
56
+ exits**, like `forgeo restart` and the web console's start button already
57
+ did; the daemon is managed with `forgeo stop`/`forgeo restart`/`forgeo
58
+ status`. `forgeo start -f` (`--foreground`) keeps the historical
59
+ foreground behavior. A detached start refuses while the per-forgeo lock is
60
+ held and runs the same read-only checks as `forgeo validate` first, so a
61
+ broken config fails fast instead of leaving a silently dead daemon.
62
+ `--interval-minutes` is forwarded to the detached daemon.
63
+
64
+ - The daemon reloads `forgeo.yaml` on the next cycle boundary when the file
65
+ changes (or on `SIGHUP`): a valid change is revalidated, logged, and used
66
+ from the next cycle; an invalid change is logged and the last valid config
67
+ stays in use. The web console's config save reflects this
68
+ (`restart_required: false`). Path changes (`repo`, `backlog`,
69
+ `blocker_file`, `log_file`) stay pinned to the daemon's startup values and
70
+ still need `forgeo restart`, so the daemon's lock files are never detached
71
+ from the config.
72
+
73
+ - Optional bearer-token auth for the central web dashboard (`forgeo web`):
74
+ `forgeo web --token` (or a `token` key in `~/.config/forgeo/web.toml`)
75
+ requires `Authorization: Bearer <token>` on every `/api/*` route and
76
+ answers `401` otherwise. `forgeo web --token` with no value generates a
77
+ token, prints it once on startup, and saves it (mode `0600`); a generated
78
+ token is only ever printed once. Static assets and the new token prompt
79
+ page (`/central/login.html`) stay reachable without a token, and a
80
+ `?token=...` URL signs the browser in automatically. With no flag and no
81
+ token file the dashboard keeps its historical open-by-default behavior.
82
+
83
+ - `forgeo validate` — a read-only dry run that checks whether a forgeo is
84
+ ready to run before starting it: the config schema, the repository (exists,
85
+ is a git repo, `git` on PATH), the branch and remote resolution, the
86
+ backlog parsing, a non-blank agent command, and the run lock state. It
87
+ reports every problem at once, never invokes the agent, and makes no writes
88
+ (no lock, no backlog changes). Exit code `0` when healthy, `1` with a
89
+ summary of problems otherwise. Supports `--config` and `--name`.
90
+
91
+ - `run_history_keep` config key: `runs.jsonl` is trimmed to that many records
92
+ on append (default `2000`), so a busy Forgeo's run history never grows
93
+ forever. Trimming is atomic (temp file + rename) and a failed trim is
94
+ logged and skipped, never fatal to the cycle; `run_history_keep: 0`
95
+ disables retention entirely (the previous grow-forever behavior).
96
+
97
+ - Persisted agent output per run: each `RunRecord` now stores a bounded tail
98
+ of the agent's stdout/stderr (last `run_output_lines` lines, default `200`),
99
+ so failed and blocked runs keep the full tail of what the agent said. The
100
+ web console's **History** tab shows it in a read-only, monospace,
101
+ collapsible view; `run_output_lines: 0` disables persistence, and records
102
+ written before the field existed render as empty.
103
+
104
+ - Backlog snapshots: before every agent run (and on daemon startup) Forgeo
105
+ copies the backlog to a rotating snapshot (`backlog.json.bak`,
106
+ `backlog.json.bak.1`, ... keeping the last 2 by default), and a read that
107
+ finds the backlog corrupt restores the newest valid snapshot in place —
108
+ with the corrupt file still preserved — instead of falling back to an
109
+ empty store. A missing backlog is a no-op.
110
+
111
+ - Task dependencies are now enforced when picking the next task: Forgeo picks
112
+ the oldest `OPEN` task whose `dependencies` are all `COMPLETED` instead of
113
+ the plain oldest `OPEN` task, so a task never runs before the work it
114
+ depends on. Unsatisfied dependencies (including ids that don't exist in the
115
+ backlog) are surfaced on `forgeo status` (`waiting on:` line) and in the web
116
+ console's task detail modal (*Waiting on dependencies* banner); the
117
+ `GET /api/instances/<name>/tasks*` responses annotate each task with an
118
+ `unsatisfied_dependencies` field.
119
+
120
+ - Homebrew install support: `brew install lucaGazzola/forgeo/forgeo`
121
+ installs the prebuilt binary on macOS (arm64/Intel) and Linux (Intel). The
122
+ `publish-homebrew` CI job re-renders the tap formula (sha256 + version)
123
+ from `scripts/render_homebrew_formula.py` on every release; it needs the
124
+ `HOMEBREW_TAP_TOKEN` repository secret (PAT with write access to
125
+ `lucaGazzola/homebrew-forgeo`). The update notification now also names
126
+ `brew upgrade lucaGazzola/forgeo/forgeo`.
127
+
128
+ - Update notification: when `forgeo start` or `forgeo once` begins a cycle,
129
+ Forgeo checks PyPI at most once a day and, if a newer `forgeo-cli` release
130
+ exists, prints/logs a short notice with the upgrade command. The check is
131
+ best-effort (short timeout, failures logged and skipped), never modifies
132
+ the install, and can be disabled with `FORGEO_UPDATE_CHECK=0`.
133
+
134
+ - Automatic retries for `FAILED` tasks: `failed_retry_max` config key (default
135
+ `0`, unchanged behavior) plus `failed_retry_wait_cycles` (default `1`) let a
136
+ transiently failed task move back to `OPEN` after a backoff and be run
137
+ again. A task that exhausts its budget stays `FAILED` with its original
138
+ `failure_reason`; a per-task `retries_left` field overrides the budget for
139
+ one task. `BLOCKED` tasks are never auto-retried. The retry count is
140
+ recorded in `runs.jsonl` (the run record that succeeds carries it), shown
141
+ in the web console (task cards/modal and a History-tab **retry** column),
142
+ and exposed by the tasks/runs API.
143
+
144
+ ### Fixed
145
+
146
+ - The Linux prebuilt binary is now built on Ubuntu 22.04 (glibc 2.35) instead
147
+ of 24.04 (glibc 2.38), so it runs on older distros (e.g. Ubuntu 22.04,
148
+ Debian 12, Homebrew-on-Linux). The 0.4.0 `forgeo-linux-amd64` release
149
+ asset was rebuilt and re-uploaded with the same version number.
150
+
151
+ ## [0.4.0] - 2026-08-10
152
+
153
+ ### Added
154
+
155
+ - Delete OPEN tasks from the web console: a Delete button (with
156
+ confirmation) in the task detail modal, backed by `DELETE
157
+ /api/instances/<name>/tasks/<id>` and `JSONBacklog.delete_task`.
158
+ - Resolve BLOCKED tasks from the web console: the task modal shows the
159
+ blocker reason and can reopen the task (back to `OPEN`) via `POST
160
+ /api/instances/<name>/tasks/<id>/reopen`.
161
+ - The web console stays usable with many tasks: non-OPEN columns collapse
162
+ behind count badges with an expand toggle, so a long backlog no longer
163
+ renders every task as a tall card up front.
164
+ - The failure/block reason is shown prominently in the task detail modal.
165
+ - Config editing from the web console: `PUT /api/instances/<name>/config`
166
+ validates and persists `forgeo.yaml` changes, and a new Config tab in the
167
+ instance page edits the fields in a form (with a restart hint).
168
+ - Daemon control from the web console: start, stop, and restart an
169
+ instance's daemon from the top bar via `POST
170
+ /api/instances/<name>/{start,stop,restart}`.
171
+ - `forgeo init` now asks only for the bare agent command and appends the
172
+ task prompt automatically.
173
+
174
+ ### Changed
175
+
176
+ - Commit messages no longer carry the `forgeo: ` prefix.
177
+ - The instance is registered before the run lock is taken, so a `--name`
178
+ lookup never fails while starting.
179
+ - README revised for clarity and formatting.
180
+
181
+ ## [0.3.0] - 2026-08-07
182
+
183
+ ### Added
184
+
185
+ - `forgeo web [--host HOST] [--port PORT]` — a standalone central dashboard
186
+ (default `0.0.0.0:8790`, foreground like `forgeo start`) that aggregates
187
+ every registered instance. It reads each instance's data straight from its
188
+ files (`backlog.json`, `runs.jsonl`, `forgeo.log`, `BLOCKER.md`), so it
189
+ works whether or not that instance's daemon is running. Home page at `/`,
190
+ per-instance pages at `/instances/<name>/` (kanban backlog plus logs,
191
+ runs, blocker and config tabs), and a per-instance API under
192
+ `/api/instances/<name>/`.
193
+ - Shared web-server helpers (`forgeo.web_common`) used by the central
194
+ dashboard.
195
+ - Task editing in the web console: the task detail modal gained an **Edit**
196
+ mode (Save/Cancel), backed by a new `PATCH
197
+ /api/instances/<name>/tasks/<id>` endpoint and `JSONBacklog.update_task`.
198
+ - PyPI publishing: tagging a release now also publishes the `forgeo-cli`
199
+ wheel and sdist to PyPI via trusted publishing.
200
+
201
+ ### Changed
202
+
203
+ - **The embedded per-daemon web server is gone.** `forgeo start` no longer
204
+ binds any port (`web_host`/`web_port` config keys are removed). The daemon
205
+ instead writes its live state (pid, started at, last outcome, next run) to
206
+ `daemon.state.json` next to the backlog after every cycle.
207
+ - The central dashboard (`forgeo web`) is now the **only** web interface:
208
+ it reads the daemon state files for accurate status, and it gained the
209
+ instance backlog's write endpoints, `POST
210
+ /api/instances/<name>/tasks` (with the web form on each instance page) and
211
+ `PATCH /api/instances/<name>/tasks/<id>`, so no feature was lost.
212
+ - `forgeo.web_common` docstring/API updated; `forgeo.server` module
213
+ removed.
214
+
215
+ ## [0.2.1] - 2026-08-05
216
+
217
+ ### Added
218
+
219
+ - `web_host` config option: the web dashboard/API bind address. Default
220
+ `127.0.0.1` (unchanged behavior); set `0.0.0.0` to reach it from other
221
+ hosts on the local network.
222
+ - `install.sh` now prefers a prebuilt standalone binary downloaded from the
223
+ matching GitHub Release for the host OS/arch — **no Python required**.
224
+ The pipx/pip fallback remains, used only when no prebuilt binary matches
225
+ the platform and a Python >= 3.11 is available.
226
+ - Tag-triggered CI builds single-file executables with PyInstaller on
227
+ Linux (amd64), macOS (amd64/arm64), and Windows (amd64) and attaches them
228
+ to the GitHub Release (`forgeo.spec`).
229
+ - Installer tests cover the binary-download path and the pipx/pip fallback
230
+ with stubs (no network).
231
+
232
+ ## [0.2.0] - 2026-08-04
233
+
234
+ ### Added
235
+
236
+ - `CHANGELOG.md` in Keep a Changelog format, with the `0.1.0` history
237
+ backfilled.
238
+ - Tag-triggered CI job that builds the wheel and sdist and attaches them to a
239
+ GitHub Release.
240
+ - Release steps documented in `CONTRIBUTING.md`.
241
+ - Web console frontend in `src/forgeo/web/`: a self-contained
242
+ HTML/CSS/JS dashboard (no framework, no build step, no external assets)
243
+ served at `/` showing the backlog grouped by status and daemon status,
244
+ auto-refreshing every 30 seconds.
245
+ - `install.sh` is now hosted on the project's own server and served from
246
+ <https://forgeo.org/install.sh>; README and docs use it in the one-liner.
247
+
248
+ ## [0.1.0] - 2026-08-03
249
+
250
+ Initial release of the scheduled, agent-driven software forgeo.
251
+
252
+ ### Added
253
+
254
+ - `forgeo start` persistent daemon: every `interval_minutes` it picks the
255
+ oldest `OPEN` task, runs it through the configured agent command, and commits
256
+ and pushes the result directly on `main`.
257
+ - Refactoring mode: when the backlog is empty, runs the agent with the
258
+ configured `refactor_prompt`.
259
+ - Blocker flow: an agent exiting with `blocked_exit_code` commits partial work,
260
+ writes `BLOCKER.md`, and pauses Forgeo until the task is reopened.
261
+ - Guided first-time setup: `forgeo init` wizard.
262
+ - `forgeo once` command to run a single cycle and exit.
263
+ - `forgeo status`, `forgeo stop`, and `forgeo restart` commands.
264
+ - `--auto` flag for the agent command for unattended runs.
265
+ - Local web dashboard and HTTP API served by the daemon.
266
+ - Durable run history recorded to `runs.jsonl` and exposed through the API.
267
+ - Telegram notification when a task is marked `BLOCKED`.
268
+ - Curl-to-bash one-liner installer (`install.sh`), pipx-first.
269
+ - MkDocs documentation website, published at <https://forgeo.org/>.
270
+ - GitHub Actions CI running `pytest`, `ruff`, and `mypy` on Python 3.11-3.13.
271
+ - Optional Docker sandbox for agent execution.
272
+ - Per-task `agent_command` override for cheap/expensive model routing.
273
+ - Project renamed to Forgeo, with MIT `LICENSE` and `CONTRIBUTING.md`.
274
+
275
+ ### Changed
276
+
277
+ - Project slimmed down to a single-purpose scheduled worker; the interactive
278
+ backlog generator utility was removed.
279
+ - Duplicated commit/blocker handling unified between task and refactor runs.
280
+ - Agent stdout/stderr streamed into run logs instead of buffered.
281
+ - Corrupt backlog files preserved instead of silently discarded.
282
+ - Git command timeout made configurable; agent timeout made optional with
283
+ overlapping-run skipping.
284
+ - Dogfooding docs removed; local configs kept out of the repository.
285
+
286
+ [Unreleased]: https://github.com/lucaGazzola/forgeo/compare/v0.6.0...HEAD
287
+ [0.6.0]: https://github.com/lucaGazzola/forgeo/compare/v0.5.0...v0.6.0
288
+ [0.5.0]: https://github.com/lucaGazzola/forgeo/compare/v0.4.0...v0.5.0
289
+ [0.4.0]: https://github.com/lucaGazzola/forgeo/compare/v0.3.0...v0.4.0
290
+ [0.3.0]: https://github.com/lucaGazzola/forgeo/compare/v0.2.0...v0.3.0
291
+ [0.2.1]: https://github.com/lucaGazzola/forgeo/compare/v0.2.0...v0.2.1
292
+ [0.2.0]: https://github.com/lucaGazzola/forgeo/compare/v0.1.0...v0.2.0
293
+ [0.1.0]: https://github.com/lucaGazzola/forgeo/releases/tag/v0.1.0
@@ -80,15 +80,20 @@ Human contributions use the normal GitHub flow:
80
80
 
81
81
  Releases are cut from `main` and published as GitHub Releases. Tagging the
82
82
  repo triggers CI, which builds the wheel, sdist, **and prebuilt standalone
83
- binaries** and attaches them to the release — there is no PyPI publishing yet,
84
- and `install.sh` downloads the matching prebuilt binary from the release
85
- (`pipx`/`pip` fallback only when no binary matches the platform).
83
+ binaries** and attaches them to the release; `install.sh` downloads the
84
+ matching prebuilt binary from the release (`pipx`/`pip` fallback only when no
85
+ binary matches the platform), and the `publish-homebrew` job re-renders and
86
+ pushes the formula of the `lucaGazzola/homebrew-forgeo` tap.
86
87
 
87
88
  > Patch and minor releases **must** include the built binaries, otherwise the
88
89
  > `install.sh` binary path (the default, no-Python install) breaks. The CI
89
90
  > `build-binaries` job builds them automatically on any `v*` tag, but make
90
91
  > sure the release actually carries them — the `forgeo-<os>-<arch>` assets
91
92
  > listed below are what the installer downloads.
93
+ >
94
+ > The `publish-homebrew` job needs the `HOMEBREW_TAP_TOKEN` repository secret
95
+ > (a PAT with write access to `lucaGazzola/homebrew-forgeo`); a release cut
96
+ > without it fails that job and leaves the tap outdated until re-run.
92
97
 
93
98
  1. Confirm the [quality gates](#quality-gates) are green on `main`.
94
99
  2. Bump the version in `pyproject.toml` (`version = "x.y.z"`) and in
@@ -114,6 +119,8 @@ and `install.sh` downloads the matching prebuilt binary from the release
114
119
  `forgeo-linux-amd64`, `forgeo-darwin-amd64`, `forgeo-darwin-arm64`,
115
120
  `forgeo-windows-amd64.exe` binaries) are listed under
116
121
  <https://github.com/lucaGazzola/forgeo/releases>.
122
+ 8. Confirm the `publish-homebrew` job updated the tap: `brew update` and
123
+ `brew upgrade lucaGazzola/forgeo/forgeo` should now install the new version.
117
124
 
118
125
  ## License
119
126
 
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.4
1
+ Metadata-Version: 2.5
2
2
  Name: forgeo-cli
3
- Version: 0.4.0
3
+ Version: 0.6.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
@@ -71,7 +71,10 @@ a backlog, and it decides what to work on next, runs your
71
71
  agent on it, and commits the result. Progress, pending decisions, and history
72
72
  are tracked in plain files you can inspect at any time, plus a web dashboard.
73
73
  Forgeo only interrupts you when a decision is genuinely yours to
74
- make, everything else happens autonomously.
74
+ make, everything else happens autonomously. Transient failures (a network
75
+ blip, a flaky test) are retried automatically when the retry policy is
76
+ enabled, and only a task that keeps failing or genuinely needs a human
77
+ decision ever reaches you.
75
78
 
76
79
  All you need is basic comfort with a terminal, a git repository, and any coding
77
80
  agent CLI.
@@ -81,29 +84,41 @@ agent CLI.
81
84
  The full walkthrough is in [Getting started](docs/getting-started.md).
82
85
 
83
86
  ```bash
84
- # 1. Install
85
- curl -fsSL https://forgeo.org/install.sh | bash
87
+ # 1. Install (any one of these)
88
+
89
+ # Homebrew (macOS / Linux)
90
+ brew install lucaGazzola/forgeo/forgeo
86
91
 
87
- or
92
+ # or: the one-liner (prebuilt binary, no Python required)
93
+ curl -fsSL https://forgeo.org/install.sh | bash
88
94
 
95
+ # or: pipx
89
96
  pipx install forgeo-cli
90
97
 
91
98
  # 2. Create your Forgeo (guided wizard, run from your project root)
92
99
  forgeo init
93
100
 
94
- # 3. Start Forgeo
95
- forgeo start # run forever: every interval_minutes, implement the oldest OPEN task
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
96
104
  ```
97
105
 
98
106
  `forgeo init` writes `forgeo.yaml` and a `.forgeo/` folder for the backlog
99
107
  and logs. Fill the backlog with plain JSON tasks (see
100
108
  [Backlog format](docs/backlog.md)) or add them from the web console while
101
- it runs, Forgeo does the rest. Open the dashboard with `forgeo web` (default <http://0.0.0.0:8790>):
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`):
102
110
 
103
111
  ![Forgeo web console](docs/img/console.png)
104
112
 
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).
118
+
105
119
  One-off commands: `forgeo once` (single cycle), `forgeo status` (summary),
106
- `forgeo stop`, `forgeo restart`, every command is in the
120
+ `forgeo validate` (read-only dry run before starting), `forgeo stop`,
121
+ `forgeo restart`, every command is in the
107
122
  [CLI reference](docs/cli-reference.md).
108
123
 
109
124
  You can run several factories at once, one per repository, each config is
@@ -125,7 +140,13 @@ central dashboard, `forgeo web`.
125
140
  | Web dashboard & HTTP API | [Web console & HTTP API](docs/web-console-api.md) |
126
141
 
127
142
  Everything is stored in plain files: the backlog, `forgeo.log`, and
128
- `BLOCKER.md` whenever a decision is pending.
143
+ `BLOCKER.md` whenever a decision is pending. The backlog can also live in
144
+ another application behind an `http(s)` URL — Forgeo reads the whole task
145
+ document with `GET` and writes it back with `POST`, with optional OAuth2
146
+ client-credentials auth (see [Backlog format](docs/backlog.md)). A *file*
147
+ backlog is snapshotted (rotating `backlog.json.bak` files) before every
148
+ agent run and on daemon startup, and restored automatically if it is ever
149
+ found corrupt — a bad write never loses your tasks.
129
150
 
130
151
  ## Develop
131
152
 
@@ -17,7 +17,10 @@ a backlog, and it decides what to work on next, runs your
17
17
  agent on it, and commits the result. Progress, pending decisions, and history
18
18
  are tracked in plain files you can inspect at any time, plus a web dashboard.
19
19
  Forgeo only interrupts you when a decision is genuinely yours to
20
- make, everything else happens autonomously.
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.
21
24
 
22
25
  All you need is basic comfort with a terminal, a git repository, and any coding
23
26
  agent CLI.
@@ -27,29 +30,41 @@ agent CLI.
27
30
  The full walkthrough is in [Getting started](docs/getting-started.md).
28
31
 
29
32
  ```bash
30
- # 1. Install
31
- curl -fsSL https://forgeo.org/install.sh | bash
33
+ # 1. Install (any one of these)
34
+
35
+ # Homebrew (macOS / Linux)
36
+ brew install lucaGazzola/forgeo/forgeo
32
37
 
33
- or
38
+ # or: the one-liner (prebuilt binary, no Python required)
39
+ curl -fsSL https://forgeo.org/install.sh | bash
34
40
 
41
+ # or: pipx
35
42
  pipx install forgeo-cli
36
43
 
37
44
  # 2. Create your Forgeo (guided wizard, run from your project root)
38
45
  forgeo init
39
46
 
40
- # 3. Start Forgeo
41
- forgeo start # run forever: every interval_minutes, implement the oldest OPEN task
47
+ # 3. Start Forgeo (detached in the background; exits immediately)
48
+ forgeo start # every interval_minutes, implement the oldest OPEN task (dependencies first)
49
+ forgeo stop # stop the daemon again
42
50
  ```
43
51
 
44
52
  `forgeo init` writes `forgeo.yaml` and a `.forgeo/` folder for the backlog
45
53
  and logs. Fill the backlog with plain JSON tasks (see
46
54
  [Backlog format](docs/backlog.md)) or add them from the web console while
47
- it runs, Forgeo does the rest. Open the dashboard with `forgeo web` (default <http://0.0.0.0:8790>):
55
+ 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`):
48
56
 
49
57
  ![Forgeo web console](docs/img/console.png)
50
58
 
59
+ By default the dashboard is open to anyone who can reach the port. On a
60
+ shared host, protect it with bearer-token auth: `forgeo web --token`
61
+ generates a token (printed once, saved to `~/.config/forgeo/web.toml`) and
62
+ requires `Authorization: Bearer <token>` on every `/api/*` route — see
63
+ [Web console & HTTP API](docs/web-console-api.md).
64
+
51
65
  One-off commands: `forgeo once` (single cycle), `forgeo status` (summary),
52
- `forgeo stop`, `forgeo restart`, every command is in the
66
+ `forgeo validate` (read-only dry run before starting), `forgeo stop`,
67
+ `forgeo restart`, every command is in the
53
68
  [CLI reference](docs/cli-reference.md).
54
69
 
55
70
  You can run several factories at once, one per repository, each config is
@@ -71,7 +86,13 @@ central dashboard, `forgeo web`.
71
86
  | Web dashboard & HTTP API | [Web console & HTTP API](docs/web-console-api.md) |
72
87
 
73
88
  Everything is stored in plain files: the backlog, `forgeo.log`, and
74
- `BLOCKER.md` whenever a decision is pending.
89
+ `BLOCKER.md` whenever a decision is pending. The backlog can also live in
90
+ another application behind an `http(s)` URL — Forgeo reads the whole task
91
+ document with `GET` and writes it back with `POST`, with optional OAuth2
92
+ client-credentials auth (see [Backlog format](docs/backlog.md)). A *file*
93
+ backlog is snapshotted (rotating `backlog.json.bak` files) before every
94
+ agent run and on daemon startup, and restored automatically if it is ever
95
+ found corrupt — a bad write never loses your tasks.
75
96
 
76
97
  ## Develop
77
98
 
@@ -39,11 +39,37 @@ The exit code decides the outcome of the run:
39
39
  | Exit code | Outcome | What happens |
40
40
  | --- | --- | --- |
41
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
- | `blocked_exit_code` (default `2`) | **BLOCKED** | The agent needs a human decision. Partial work is committed as `<title> (#<id>) [partial]`, the agent's reason is persisted on the task (`blocker_reason`), an optional Telegram notification is 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). |
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). |
43
44
  | anything else | **ERROR** | Changes are discarded (`git reset --hard` + `git clean -fd`), the failure is logged, and the task is marked `FAILED`. |
44
45
 
45
46
  The blocked exit code is configurable via `blocked_exit_code` in
46
- [forgeo.yaml](configuration.md).
47
+ [forgeo.yaml](configuration.md), and the no-change exit code via
48
+ `no_changes_exit_code`.
49
+
50
+ A `FAILED` task stays `FAILED` until a human reopens it — unless the retry
51
+ policy is enabled (`failed_retry_max`, see [Configuration](configuration.md)),
52
+ in which case Forgeo moves the task back to `OPEN` after
53
+ `failed_retry_wait_cycles` cycles and runs it again, incrementing its retry
54
+ count. `BLOCKED` is never retried automatically: it always waits for a human.
55
+
56
+ ## The no-change contract
57
+
58
+ Forgeo cannot tell "the agent deliberately made no changes" from "the agent
59
+ did nothing". A `SUCCESS` exit that produces **no changes is therefore not a
60
+ valid completion for a task**:
61
+
62
+ - exiting `0` while leaving the working tree **unchanged** fails the task
63
+ (`FAILED`, reason: *"Agent exited 0 but produced no changes"*);
64
+ - to complete a task **without touching the code**, exit
65
+ `no_changes_exit_code` (default `3`). The working tree must be clean — an
66
+ agent that reports "no changes" while leaving uncommitted work behind fails
67
+ instead.
68
+
69
+ Refactoring passes are the exception: when the backlog is empty, a refactor
70
+ that finds nothing to improve is a normal, successful run (the default
71
+ refactor prompt already says "if nothing needs refactoring, make no
72
+ changes").
47
73
 
48
74
  ## Timeouts
49
75
 
@@ -84,6 +110,8 @@ that, based on the exit code. The working contract is:
84
110
  - make your changes in the repository;
85
111
  - **do not** run `git add`, `git commit`, `git push`, or reset the tree;
86
112
  - exit `0` to have your changes committed and pushed as one commit;
113
+ - exit `no_changes_exit_code` when the task needs no code change (never exit
114
+ `0` with an empty tree — that fails the task);
87
115
  - exit `blocked_exit_code` to have partial work preserved and a blocker
88
116
  written;
89
117
  - exit anything else to have your changes discarded.