forgeo-cli 0.8.0__tar.gz → 0.10.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.8.0 → forgeo_cli-0.10.0}/CHANGELOG.md +50 -1
- {forgeo_cli-0.8.0 → forgeo_cli-0.10.0}/PKG-INFO +26 -8
- {forgeo_cli-0.8.0 → forgeo_cli-0.10.0}/README.md +25 -7
- {forgeo_cli-0.8.0 → forgeo_cli-0.10.0}/docs/backlog.md +96 -7
- {forgeo_cli-0.8.0 → forgeo_cli-0.10.0}/docs/cli-reference.md +2 -0
- {forgeo_cli-0.8.0 → forgeo_cli-0.10.0}/docs/configuration.md +54 -3
- {forgeo_cli-0.8.0 → forgeo_cli-0.10.0}/docs/getting-started.md +32 -16
- {forgeo_cli-0.8.0 → forgeo_cli-0.10.0}/docs/web-console-api.md +30 -9
- {forgeo_cli-0.8.0 → forgeo_cli-0.10.0}/install.sh +1 -1
- {forgeo_cli-0.8.0 → forgeo_cli-0.10.0}/pyproject.toml +1 -1
- {forgeo_cli-0.8.0 → forgeo_cli-0.10.0}/src/forgeo/__init__.py +1 -1
- {forgeo_cli-0.8.0 → forgeo_cli-0.10.0}/src/forgeo/agent.py +25 -1
- {forgeo_cli-0.8.0 → forgeo_cli-0.10.0}/src/forgeo/backlog.py +148 -178
- forgeo_cli-0.10.0/src/forgeo/backlog_github.py +639 -0
- forgeo_cli-0.10.0/src/forgeo/backlog_gitlab.py +617 -0
- {forgeo_cli-0.8.0 → forgeo_cli-0.10.0}/src/forgeo/backlog_http.py +2 -2
- forgeo_cli-0.10.0/src/forgeo/backlog_issue_base.py +183 -0
- {forgeo_cli-0.8.0 → forgeo_cli-0.10.0}/src/forgeo/backlog_jira.py +34 -151
- {forgeo_cli-0.8.0 → forgeo_cli-0.10.0}/src/forgeo/central.py +101 -4
- {forgeo_cli-0.8.0 → forgeo_cli-0.10.0}/src/forgeo/daemon_control.py +1 -1
- {forgeo_cli-0.8.0 → forgeo_cli-0.10.0}/src/forgeo/forgeo.py +1 -1
- {forgeo_cli-0.8.0 → forgeo_cli-0.10.0}/src/forgeo/models.py +220 -5
- forgeo_cli-0.10.0/src/forgeo/setup.py +462 -0
- {forgeo_cli-0.8.0 → forgeo_cli-0.10.0}/src/forgeo/validate.py +28 -27
- {forgeo_cli-0.8.0 → forgeo_cli-0.10.0}/src/forgeo/web/central/central.css +92 -0
- {forgeo_cli-0.8.0 → forgeo_cli-0.10.0}/src/forgeo/web/central/central.js +82 -2
- {forgeo_cli-0.8.0 → forgeo_cli-0.10.0}/src/forgeo/web/central/instance.html +16 -1
- {forgeo_cli-0.8.0 → forgeo_cli-0.10.0}/tests/conftest.py +20 -7
- forgeo_cli-0.10.0/tests/test_backlog_github.py +259 -0
- forgeo_cli-0.10.0/tests/test_backlog_gitlab.py +237 -0
- {forgeo_cli-0.8.0 → forgeo_cli-0.10.0}/tests/test_cli.py +7 -32
- {forgeo_cli-0.8.0 → forgeo_cli-0.10.0}/tests/test_setup.py +54 -3
- {forgeo_cli-0.8.0 → forgeo_cli-0.10.0}/tests/test_web.py +1 -1
- {forgeo_cli-0.8.0 → forgeo_cli-0.10.0}/tests/test_web_lock.py +1 -1
- forgeo_cli-0.8.0/src/forgeo/setup.py +0 -230
- {forgeo_cli-0.8.0 → forgeo_cli-0.10.0}/.github/workflows/ci.yml +0 -0
- {forgeo_cli-0.8.0 → forgeo_cli-0.10.0}/.gitignore +0 -0
- {forgeo_cli-0.8.0 → forgeo_cli-0.10.0}/CONTRIBUTING.md +0 -0
- {forgeo_cli-0.8.0 → forgeo_cli-0.10.0}/LICENSE +0 -0
- {forgeo_cli-0.8.0 → forgeo_cli-0.10.0}/config/nginx-forgeo.conf +0 -0
- {forgeo_cli-0.8.0 → forgeo_cli-0.10.0}/docs/agent-contract.md +0 -0
- {forgeo_cli-0.8.0 → forgeo_cli-0.10.0}/docs/img/console.png +0 -0
- {forgeo_cli-0.8.0 → forgeo_cli-0.10.0}/docs/img/demo.gif +0 -0
- {forgeo_cli-0.8.0 → forgeo_cli-0.10.0}/docs/img/logo.png +0 -0
- {forgeo_cli-0.8.0 → forgeo_cli-0.10.0}/docs/img/og.png +0 -0
- {forgeo_cli-0.8.0 → forgeo_cli-0.10.0}/docs/img/title.svg +0 -0
- {forgeo_cli-0.8.0 → forgeo_cli-0.10.0}/docs/index.md +0 -0
- {forgeo_cli-0.8.0 → forgeo_cli-0.10.0}/forgeo.spec +0 -0
- {forgeo_cli-0.8.0 → forgeo_cli-0.10.0}/mkdocs.yml +0 -0
- {forgeo_cli-0.8.0 → forgeo_cli-0.10.0}/scripts/__init__.py +0 -0
- {forgeo_cli-0.8.0 → forgeo_cli-0.10.0}/scripts/render_homebrew_formula.py +0 -0
- {forgeo_cli-0.8.0 → forgeo_cli-0.10.0}/src/forgeo/__main__.py +0 -0
- {forgeo_cli-0.8.0 → forgeo_cli-0.10.0}/src/forgeo/cli.py +0 -0
- {forgeo_cli-0.8.0 → forgeo_cli-0.10.0}/src/forgeo/config.py +0 -0
- {forgeo_cli-0.8.0 → forgeo_cli-0.10.0}/src/forgeo/daemon.py +0 -0
- {forgeo_cli-0.8.0 → forgeo_cli-0.10.0}/src/forgeo/git.py +0 -0
- {forgeo_cli-0.8.0 → forgeo_cli-0.10.0}/src/forgeo/instances.py +0 -0
- {forgeo_cli-0.8.0 → forgeo_cli-0.10.0}/src/forgeo/io.py +0 -0
- {forgeo_cli-0.8.0 → forgeo_cli-0.10.0}/src/forgeo/notify.py +0 -0
- {forgeo_cli-0.8.0 → forgeo_cli-0.10.0}/src/forgeo/oauth.py +0 -0
- {forgeo_cli-0.8.0 → forgeo_cli-0.10.0}/src/forgeo/paths.py +0 -0
- {forgeo_cli-0.8.0 → forgeo_cli-0.10.0}/src/forgeo/runs.py +0 -0
- {forgeo_cli-0.8.0 → forgeo_cli-0.10.0}/src/forgeo/update.py +0 -0
- {forgeo_cli-0.8.0 → forgeo_cli-0.10.0}/src/forgeo/web/central/index.html +0 -0
- {forgeo_cli-0.8.0 → forgeo_cli-0.10.0}/src/forgeo/web/central/login.html +0 -0
- {forgeo_cli-0.8.0 → forgeo_cli-0.10.0}/src/forgeo/web/style.css +0 -0
- {forgeo_cli-0.8.0 → forgeo_cli-0.10.0}/src/forgeo/web_common.py +0 -0
- {forgeo_cli-0.8.0 → forgeo_cli-0.10.0}/tests/test_agent.py +0 -0
- {forgeo_cli-0.8.0 → forgeo_cli-0.10.0}/tests/test_backlog.py +0 -0
- {forgeo_cli-0.8.0 → forgeo_cli-0.10.0}/tests/test_backlog_http.py +0 -0
- {forgeo_cli-0.8.0 → forgeo_cli-0.10.0}/tests/test_backlog_jira.py +0 -0
- {forgeo_cli-0.8.0 → forgeo_cli-0.10.0}/tests/test_daemon.py +0 -0
- {forgeo_cli-0.8.0 → forgeo_cli-0.10.0}/tests/test_factory.py +0 -0
- {forgeo_cli-0.8.0 → forgeo_cli-0.10.0}/tests/test_git.py +0 -0
- {forgeo_cli-0.8.0 → forgeo_cli-0.10.0}/tests/test_install.py +0 -0
- {forgeo_cli-0.8.0 → forgeo_cli-0.10.0}/tests/test_instances.py +0 -0
- {forgeo_cli-0.8.0 → forgeo_cli-0.10.0}/tests/test_io.py +0 -0
- {forgeo_cli-0.8.0 → forgeo_cli-0.10.0}/tests/test_models.py +0 -0
- {forgeo_cli-0.8.0 → forgeo_cli-0.10.0}/tests/test_oauth.py +0 -0
- {forgeo_cli-0.8.0 → forgeo_cli-0.10.0}/tests/test_paths.py +0 -0
- {forgeo_cli-0.8.0 → forgeo_cli-0.10.0}/tests/test_remote_backlog_cycle.py +0 -0
- {forgeo_cli-0.8.0 → forgeo_cli-0.10.0}/tests/test_render_homebrew.py +0 -0
- {forgeo_cli-0.8.0 → forgeo_cli-0.10.0}/tests/test_runs.py +0 -0
- {forgeo_cli-0.8.0 → forgeo_cli-0.10.0}/tests/test_update.py +0 -0
- {forgeo_cli-0.8.0 → forgeo_cli-0.10.0}/tests/test_web_common.py +0 -0
- {forgeo_cli-0.8.0 → forgeo_cli-0.10.0}/www/404.html +0 -0
- {forgeo_cli-0.8.0 → forgeo_cli-0.10.0}/www/index.html +0 -0
|
@@ -7,6 +7,53 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.10.0] - 2026-08-24
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
|
|
14
|
+
- `forgeo init` now asks for **backlog provider** (`file`/`github`/`gitlab`/`jira`/`http`). For `github` it auto-detects `owner/repo` from `git remote origin`, prompts for `token_env` (`GITHUB_TOKEN`) and can persist a pasted classic PAT (`ghp_...`, scope `repo`) to `~/.config/forgeo/github_token_env.sh` (600, wired to `~/.bashrc`). Same for `gitlab` (`GITLAB_TOKEN`, base URL) and `jira`/`http`. The wizard writes `backlog: https://api.github.com`, `backlog_provider: github`, `github: {repo, auth: {token_env}}` and `state_dir: .forgeo` automatically.
|
|
15
|
+
- Web console now treats `jira`/`github`/`gitlab` as a **read-mostly mirror** instead of a replacement: the home page cards show `Open in Jira/GitHub/GitLab ↗` (via `external_board_url`/`external_board_label`), the instance page shows a top banner linking to the native board, and each task card/modal links to the native issue (`external_url`). Document backlogs (`file`/`http`) keep the existing primary-editor behaviour. Creating/editing still works through the dashboard, but triage is expected in the native tracker where Forgeo-specific state (BLOCKED/FAILED reasons, `agent_response`, retry budget) is now surfaced on the mirror.
|
|
16
|
+
- Central API now exposes `backlog_provider`, `backlog`, `backlog_is_issue_provider`, `external_board_url`/`external_board_label` on `GET /api/instances` and `GET /api/instances/<name>/status`, and `external_url` per task on `GET /api/instances/<name>/tasks` (+ single-task) for issue providers. Covers `https://api.github.com` → `https://github.com` and `https://…/api/v3` → web base mapping for GitHub Enterprise, and `https://jira…/issues/?jql=` / `https://gitlab…/{repo}/-/issues` board links.
|
|
17
|
+
|
|
18
|
+
### Fixed
|
|
19
|
+
|
|
20
|
+
- GitHub provider now encodes `owner/repo` as two path segments (`quote` per segment, keeping `/`) instead of `quote(repo, safe='')` which produced `owner%2Frepo` and 404 on every `GET /repos/{owner%2Frepo}/issues`.
|
|
21
|
+
|
|
22
|
+
### Changed
|
|
23
|
+
|
|
24
|
+
- `README`, `docs/backlog.md`, `docs/getting-started.md` and `docs/web-console-api.md` document the mirror vs editor split and the new `external_*` API fields.
|
|
25
|
+
- `src/forgeo/central.py` deduplicates provider metadata via `_backlog_meta()` and centralises GitHub web-base handling; `instance-card` is now a `div[role=link]` to allow nested external links without invalid HTML.
|
|
26
|
+
- `README`, `docs/getting-started.md` and `docs/cli-reference.md` document the new backlog-provider step in `forgeo init` and the `GITHUB_TOKEN` PAT setup (`https://github.com/settings/tokens/new`, scope `repo`).
|
|
27
|
+
|
|
28
|
+
## [0.9.0] - 2026-08-23
|
|
29
|
+
|
|
30
|
+
### Added
|
|
31
|
+
|
|
32
|
+
- GitHub Issues and GitLab Issues task providers. Set `backlog_provider: github`
|
|
33
|
+
or `gitlab` and point `backlog:` at the API base URL
|
|
34
|
+
(`https://api.github.com` or `https://github.example.com/api/v3` for
|
|
35
|
+
Enterprise; `https://gitlab.com` or a self-hosted root for GitLab). Issue
|
|
36
|
+
numbers/`iid`s become Forgeo task ids, `open`/`opened` vs `closed` maps to
|
|
37
|
+
`OPEN`/`COMPLETED`, and `forgeo-running`/`forgeo-blocked`/`forgeo-failed`
|
|
38
|
+
labels (configurable via `label_prefix`) represent the remaining states.
|
|
39
|
+
Engine state — blocker and failure reasons, retry counters, claim time,
|
|
40
|
+
dependencies, and bounded agent output — is stored in a hidden
|
|
41
|
+
`<!-- forgeo: {...} -->` block inside the issue body/description, so the
|
|
42
|
+
visible text stays human-readable and no custom fields or issue properties
|
|
43
|
+
are required.
|
|
44
|
+
- `github` and `gitlab` config blocks: `repo` (`owner/repo` or project path/id),
|
|
45
|
+
`token_env` (PAT from an environment variable, never stored in the file),
|
|
46
|
+
`label_prefix`/`property_key`, pagination, timeouts, stale-claim recovery
|
|
47
|
+
via `claim_timeout_seconds`, and optional workflow/field mappings mirroring
|
|
48
|
+
the Jira provider.
|
|
49
|
+
- Shared issue-provider helpers extracted to `backlog_issue_base` and a new
|
|
50
|
+
`DocumentBacklogStore` / `IssueBacklogBase` split in `backlog.py`,
|
|
51
|
+
unifying claim, label, and engine-state handling across Jira, GitHub, and
|
|
52
|
+
GitLab. `forgeo validate` now checks any remote backlog with a
|
|
53
|
+
provider-specific message, and `config/forgeo.yaml`, the README, and the
|
|
54
|
+
backlog/configuration docs list and document all five providers
|
|
55
|
+
(`file`, `http`, `jira`, `github`, `gitlab`).
|
|
56
|
+
|
|
10
57
|
## [0.8.0] - 2026-08-21
|
|
11
58
|
|
|
12
59
|
### Added
|
|
@@ -360,7 +407,9 @@ Initial release of the scheduled, agent-driven software forgeo.
|
|
|
360
407
|
overlapping-run skipping.
|
|
361
408
|
- Dogfooding docs removed; local configs kept out of the repository.
|
|
362
409
|
|
|
363
|
-
[Unreleased]: https://github.com/lucaGazzola/forgeo/compare/v0.
|
|
410
|
+
[Unreleased]: https://github.com/lucaGazzola/forgeo/compare/v0.10.0...HEAD
|
|
411
|
+
[0.10.0]: https://github.com/lucaGazzola/forgeo/compare/v0.9.0...v0.10.0
|
|
412
|
+
[0.9.0]: https://github.com/lucaGazzola/forgeo/compare/v0.8.0...v0.9.0
|
|
364
413
|
[0.8.0]: https://github.com/lucaGazzola/forgeo/compare/v0.7.3...v0.8.0
|
|
365
414
|
[0.7.3]: https://github.com/lucaGazzola/forgeo/compare/v0.7.2...v0.7.3
|
|
366
415
|
[0.7.2]: https://github.com/lucaGazzola/forgeo/compare/v0.7.1...v0.7.2
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: forgeo-cli
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.10.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
|
|
@@ -110,6 +110,10 @@ forgeo init
|
|
|
110
110
|
|
|
111
111
|
Guided wizard, run from your project root. Writes `forgeo.yaml` (the
|
|
112
112
|
config) and a `.forgeo/` folder for the backlog, logs and blocker files.
|
|
113
|
+
The wizard asks for the backlog provider (`file` for a local JSON file, or
|
|
114
|
+
`github`/`gitlab`/`jira`/`http` for an external tracker — for `github` it
|
|
115
|
+
auto-detects `owner/repo` from `git remote` and can persist a pasted
|
|
116
|
+
`GITHUB_TOKEN` to `~/.config/forgeo/github_token_env.sh`).
|
|
113
117
|
|
|
114
118
|
The base flow is then three steps: fill the backlog, check the
|
|
115
119
|
configuration, start the daemon.
|
|
@@ -121,8 +125,10 @@ configuration, start the daemon.
|
|
|
121
125
|
# Backlog format), created on first use:
|
|
122
126
|
# .forgeo/backlog.json
|
|
123
127
|
|
|
124
|
-
# Or:
|
|
125
|
-
#
|
|
128
|
+
# Or: pick github/gitlab/jira in forgeo init (or configure forgeo.yaml
|
|
129
|
+
# manually): set backlog_provider: github and backlog: https://api.github.com
|
|
130
|
+
# with github.repo + token_env, then export GITHUB_TOKEN and run
|
|
131
|
+
# forgeo validate. See backlog docs for Jira/GitHub/GitLab.
|
|
126
132
|
|
|
127
133
|
# Or: add tasks from the web console once your forgeo is registered
|
|
128
134
|
# (first `forgeo start` registers it automatically):
|
|
@@ -211,15 +217,27 @@ and get one aggregate overview with the central dashboard, `forgeo web`.
|
|
|
211
217
|
| Web dashboard & HTTP API | [Web console & HTTP API](docs/web-console-api.md) |
|
|
212
218
|
|
|
213
219
|
Everything is stored in plain files: the local backlog, `forgeo.log`, and
|
|
214
|
-
`BLOCKER.md` whenever a decision is pending.
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
[
|
|
220
|
+
`BLOCKER.md` whenever a decision is pending. Backlog providers (see
|
|
221
|
+
[Backlog format](docs/backlog.md)):
|
|
222
|
+
|
|
223
|
+
- [JSON file](docs/backlog.md#a-json-file-backlog)
|
|
224
|
+
- [HTTP endpoint](docs/backlog.md#a-backlog-over-http)
|
|
225
|
+
- [Jira](docs/backlog.md#a-jira-backlog)
|
|
226
|
+
- [GitHub](docs/backlog.md#a-github-backlog)
|
|
227
|
+
- [GitLab](docs/backlog.md#a-gitlab-backlog)
|
|
228
|
+
|
|
229
|
+
File and HTTP backlogs exchange the complete task document; Jira/GitHub/GitLab issues are read and transitioned
|
|
230
|
+
individually with workflow state and engine metadata stored on the issue. A *file* backlog is snapshotted (rotating
|
|
219
231
|
`backlog.json.bak` files) before every agent run and on daemon startup, and
|
|
220
232
|
restored automatically if it is ever found corrupt — a bad write never loses
|
|
221
233
|
your tasks.
|
|
222
234
|
|
|
235
|
+
The central dashboard (`forgeo web`) mirrors every instance: for `file`/`http` it is the primary editor; for
|
|
236
|
+
`jira`/`github`/`gitlab` it is a read-mostly mirror of the native tracker — each task card and its detail modal link
|
|
237
|
+
to the native issue (`Open in Jira/GitHub/GitLab ↗`), a top banner links to the native board, and the board surfaces
|
|
238
|
+
Forgeo-specific state the native UI does not (BLOCKED/FAILED reasons, `agent_response`, retry budget) — triage stays in
|
|
239
|
+
the tracker (see [Web console & HTTP API](docs/web-console-api.md)).
|
|
240
|
+
|
|
223
241
|
## Develop
|
|
224
242
|
|
|
225
243
|
```bash
|
|
@@ -56,6 +56,10 @@ forgeo init
|
|
|
56
56
|
|
|
57
57
|
Guided wizard, run from your project root. Writes `forgeo.yaml` (the
|
|
58
58
|
config) and a `.forgeo/` folder for the backlog, logs and blocker files.
|
|
59
|
+
The wizard asks for the backlog provider (`file` for a local JSON file, or
|
|
60
|
+
`github`/`gitlab`/`jira`/`http` for an external tracker — for `github` it
|
|
61
|
+
auto-detects `owner/repo` from `git remote` and can persist a pasted
|
|
62
|
+
`GITHUB_TOKEN` to `~/.config/forgeo/github_token_env.sh`).
|
|
59
63
|
|
|
60
64
|
The base flow is then three steps: fill the backlog, check the
|
|
61
65
|
configuration, start the daemon.
|
|
@@ -67,8 +71,10 @@ configuration, start the daemon.
|
|
|
67
71
|
# Backlog format), created on first use:
|
|
68
72
|
# .forgeo/backlog.json
|
|
69
73
|
|
|
70
|
-
# Or:
|
|
71
|
-
#
|
|
74
|
+
# Or: pick github/gitlab/jira in forgeo init (or configure forgeo.yaml
|
|
75
|
+
# manually): set backlog_provider: github and backlog: https://api.github.com
|
|
76
|
+
# with github.repo + token_env, then export GITHUB_TOKEN and run
|
|
77
|
+
# forgeo validate. See backlog docs for Jira/GitHub/GitLab.
|
|
72
78
|
|
|
73
79
|
# Or: add tasks from the web console once your forgeo is registered
|
|
74
80
|
# (first `forgeo start` registers it automatically):
|
|
@@ -157,15 +163,27 @@ and get one aggregate overview with the central dashboard, `forgeo web`.
|
|
|
157
163
|
| Web dashboard & HTTP API | [Web console & HTTP API](docs/web-console-api.md) |
|
|
158
164
|
|
|
159
165
|
Everything is stored in plain files: the local backlog, `forgeo.log`, and
|
|
160
|
-
`BLOCKER.md` whenever a decision is pending.
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
[
|
|
166
|
+
`BLOCKER.md` whenever a decision is pending. Backlog providers (see
|
|
167
|
+
[Backlog format](docs/backlog.md)):
|
|
168
|
+
|
|
169
|
+
- [JSON file](docs/backlog.md#a-json-file-backlog)
|
|
170
|
+
- [HTTP endpoint](docs/backlog.md#a-backlog-over-http)
|
|
171
|
+
- [Jira](docs/backlog.md#a-jira-backlog)
|
|
172
|
+
- [GitHub](docs/backlog.md#a-github-backlog)
|
|
173
|
+
- [GitLab](docs/backlog.md#a-gitlab-backlog)
|
|
174
|
+
|
|
175
|
+
File and HTTP backlogs exchange the complete task document; Jira/GitHub/GitLab issues are read and transitioned
|
|
176
|
+
individually with workflow state and engine metadata stored on the issue. A *file* backlog is snapshotted (rotating
|
|
165
177
|
`backlog.json.bak` files) before every agent run and on daemon startup, and
|
|
166
178
|
restored automatically if it is ever found corrupt — a bad write never loses
|
|
167
179
|
your tasks.
|
|
168
180
|
|
|
181
|
+
The central dashboard (`forgeo web`) mirrors every instance: for `file`/`http` it is the primary editor; for
|
|
182
|
+
`jira`/`github`/`gitlab` it is a read-mostly mirror of the native tracker — each task card and its detail modal link
|
|
183
|
+
to the native issue (`Open in Jira/GitHub/GitLab ↗`), a top banner links to the native board, and the board surfaces
|
|
184
|
+
Forgeo-specific state the native UI does not (BLOCKED/FAILED reasons, `agent_response`, retry budget) — triage stays in
|
|
185
|
+
the tracker (see [Web console & HTTP API](docs/web-console-api.md)).
|
|
186
|
+
|
|
169
187
|
## Develop
|
|
170
188
|
|
|
171
189
|
```bash
|
|
@@ -1,12 +1,24 @@
|
|
|
1
1
|
# Backlog format
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
3
|
+
- [JSON file](#a-json-file-backlog)
|
|
4
|
+
- [HTTP endpoint](#a-backlog-over-http)
|
|
5
|
+
- [Jira](#a-jira-backlog)
|
|
6
|
+
- [GitHub](#a-github-backlog)
|
|
7
|
+
- [GitLab](#a-gitlab-backlog)
|
|
8
|
+
|
|
9
|
+
The backlog is a **plain JSON document** — a list of tasks Forgeo works through
|
|
10
|
+
one by one.
|
|
11
|
+
|
|
12
|
+
## A JSON file backlog
|
|
13
|
+
|
|
14
|
+
By default the backlog is a file you edit by hand, living wherever `backlog:`
|
|
15
|
+
points in [forgeo.yaml](configuration.md) — `backlog.json` at the project root,
|
|
16
|
+
or `.forgeo/backlog.json` when generated by `forgeo init`. Keep it outside the
|
|
17
|
+
repository if you can so the agent never touches it. It can also be
|
|
18
|
+
[served over HTTP](#a-backlog-over-http) by another application, in which case
|
|
19
|
+
the document below is exactly what that endpoint exchanges with Forgeo, or it
|
|
20
|
+
can be sourced directly from [Jira](#a-jira-backlog),
|
|
21
|
+
[GitHub](#a-github-backlog) or [GitLab](#a-gitlab-backlog).
|
|
10
22
|
|
|
11
23
|
```json
|
|
12
24
|
{
|
|
@@ -132,6 +144,11 @@ You add, remove, or reopen tasks by editing the file directly — or use the
|
|
|
132
144
|
the task detail modal's **Edit** button updates an existing task's fields
|
|
133
145
|
(`PATCH /api/instances/<name>/tasks/<id>`), while its **Delete** button
|
|
134
146
|
removes an `OPEN` or `BLOCKED` task (`DELETE /api/instances/<name>/tasks/<id>`).
|
|
147
|
+
For `file`/`http` backlogs this is the primary editor; for `jira`/`github`/`gitlab` the web console is a
|
|
148
|
+
read-mostly **mirror** of the native tracker — a top banner links to the external board, each task card/modal
|
|
149
|
+
links to the native issue (`Open in Jira/GitHub/GitLab ↗`), and the board surfaces Forgeo-specific state
|
|
150
|
+
(`blocker_reason`, `failure_reason`, `agent_response`, retry budget) that the tracker does not — triage stays in
|
|
151
|
+
Jira/GitHub/GitLab, Forgeo reflects it (see [Web console & HTTP API](web-console-api.md)).
|
|
135
152
|
|
|
136
153
|
### Resolving a blocked task
|
|
137
154
|
|
|
@@ -424,3 +441,75 @@ If a process dies while holding a claim, a later cycle releases claims older
|
|
|
424
441
|
than `claim_timeout_seconds` and returns them to the configured open status.
|
|
425
442
|
An unavailable Jira endpoint fails the cycle; it is never treated as an empty
|
|
426
443
|
backlog.
|
|
444
|
+
|
|
445
|
+
## A GitHub backlog
|
|
446
|
+
|
|
447
|
+
Set `backlog_provider: github` and point `backlog:` at the GitHub API base URL:
|
|
448
|
+
|
|
449
|
+
```yaml
|
|
450
|
+
backlog_provider: github
|
|
451
|
+
backlog: https://api.github.com
|
|
452
|
+
|
|
453
|
+
github:
|
|
454
|
+
repo: owner/repo
|
|
455
|
+
token_env: GITHUB_TOKEN
|
|
456
|
+
label_prefix: forgeo
|
|
457
|
+
```
|
|
458
|
+
|
|
459
|
+
Use `https://api.github.com` for github.com or `https://github.example.com/api/v3` for Enterprise.
|
|
460
|
+
|
|
461
|
+
### Mapping and lifecycle
|
|
462
|
+
|
|
463
|
+
- GitHub issue numbers are Forgeo task ids.
|
|
464
|
+
- `title`, `body` (visible part), `created_at` and `updated_at` map to task fields.
|
|
465
|
+
- `state` `open` maps to `OPEN`; `closed` maps to `COMPLETED`.
|
|
466
|
+
- Labels `forgeo-running`, `forgeo-blocked`, `forgeo-failed` (prefix configurable via `label_prefix`) represent running, blocked, and failed tasks. An open issue carrying `forgeo-running` is considered claimed and filtered from picking.
|
|
467
|
+
- Closing an issue completes its task; reopening it moves the task back to `OPEN`.
|
|
468
|
+
|
|
469
|
+
Forgeo stores blocker reasons, failure reasons, retry counters, claim time, dependencies, and bounded agent output in a hidden JSON block inside the issue body: `<!-- forgeo: {...} -->`. The visible body remains human-readable; the hidden block is stripped on read and merged on write. No GitHub issue property or custom field is required. Set `github.property_key` only for symmetry; the marker key is `forgeo` by default.
|
|
470
|
+
|
|
471
|
+
Dependencies are persisted via the hidden block's `dependencies` list; no GitHub issue links are required.
|
|
472
|
+
|
|
473
|
+
### Authentication
|
|
474
|
+
|
|
475
|
+
GitHub credentials are never stored in `forgeo.yaml`:
|
|
476
|
+
|
|
477
|
+
- `token_env` names the environment variable holding a personal-access token (classic or fine-grained). The token is sent as `Authorization: Bearer <token>`.
|
|
478
|
+
|
|
479
|
+
### Runtime behavior
|
|
480
|
+
|
|
481
|
+
The daemon lists GitHub issues with paginated `GET /repos/{owner}/{repo}/issues?state=all`. A task is claimed by adding the `forgeo-running` label and persisting `claimed_at` in the hidden block. If a process dies while holding a claim, a later cycle releases claims older than `claim_timeout_seconds` and removes the running label. An unavailable GitHub endpoint fails the cycle.
|
|
482
|
+
|
|
483
|
+
## A GitLab backlog
|
|
484
|
+
|
|
485
|
+
Set `backlog_provider: gitlab` and point `backlog:` at the GitLab base URL:
|
|
486
|
+
|
|
487
|
+
```yaml
|
|
488
|
+
backlog_provider: gitlab
|
|
489
|
+
backlog: https://gitlab.example.com
|
|
490
|
+
|
|
491
|
+
gitlab:
|
|
492
|
+
repo: group/project # or numeric project id
|
|
493
|
+
token_env: GITLAB_TOKEN
|
|
494
|
+
label_prefix: forgeo
|
|
495
|
+
```
|
|
496
|
+
|
|
497
|
+
GitLab base URL is the instance root (e.g. `https://gitlab.com`); the client appends `/api/v4`.
|
|
498
|
+
|
|
499
|
+
### Mapping and lifecycle
|
|
500
|
+
|
|
501
|
+
- GitLab issue `iid`s are Forgeo task ids.
|
|
502
|
+
- `title`, `description` (visible part), `created_at` and `updated_at` map to task fields.
|
|
503
|
+
- `state` `opened` maps to `OPEN`; `closed` maps to `COMPLETED`.
|
|
504
|
+
- Labels `forgeo-running`, `forgeo-blocked`, `forgeo-failed` represent running, blocked, and failed tasks, like GitHub. An `opened` issue with `forgeo-running` is filtered as claimed.
|
|
505
|
+
- Closing/reopening via `state_event` transitions the task to `COMPLETED`/`OPEN`.
|
|
506
|
+
|
|
507
|
+
Forgeo stores engine state the same way as GitHub: a hidden `<!-- forgeo: {...} -->` block inside `description`. Dependencies and other task attributes are kept there; no GitLab custom fields are required.
|
|
508
|
+
|
|
509
|
+
### Authentication
|
|
510
|
+
|
|
511
|
+
- `token_env` names the environment variable holding a personal-access token. Sent as `PRIVATE-TOKEN` and `Authorization: Bearer`.
|
|
512
|
+
|
|
513
|
+
### Runtime behavior
|
|
514
|
+
|
|
515
|
+
Paginated `GET /api/v4/projects/:id/issues?state=all`. Claiming adds `forgeo-running` and `claimed_at`; stale claims older than `claim_timeout_seconds` are released. An unavailable GitLab endpoint fails the cycle.
|
|
@@ -23,6 +23,8 @@ Guided first-time setup: interactively write a `forgeo.yaml`.
|
|
|
23
23
|
| <span style="white-space: nowrap">`--config <file>`</span> | Where to write the config (default `forgeo.yaml`). |
|
|
24
24
|
| <span style="white-space: nowrap">`--force`</span> | Overwrite an existing config file. |
|
|
25
25
|
|
|
26
|
+
The wizard asks for: Forgeo folder, backlog provider (`file`/`github`/`gitlab`/`jira`/`http` — for `github` it auto-detects `owner/repo` from `git remote` and can persist `GITHUB_TOKEN`), coding agent command, and refactor prompt.
|
|
27
|
+
|
|
26
28
|
Exit codes:
|
|
27
29
|
|
|
28
30
|
- `0` — config written.
|
|
@@ -22,11 +22,13 @@ paths), so `forgeo restart` is still used for those.
|
|
|
22
22
|
| <span style="white-space: nowrap">`interval_minutes`</span> | `60` | How often Forgeo runs (≥ 1). |
|
|
23
23
|
| <span style="white-space: nowrap">`branch`</span> | `main` | The single branch everything is committed to. |
|
|
24
24
|
| <span style="white-space: nowrap">`remote`</span> | — | Remote to push to (e.g. `origin`); omit to only commit locally. |
|
|
25
|
-
| <span style="white-space: nowrap">`backlog`</span> | `backlog.json` | The task backlog: the path of a JSON file, an HTTP endpoint serving the same document, or a
|
|
26
|
-
| <span style="white-space: nowrap">`backlog_provider`</span> | `auto` | `auto` infers file/HTTP from `backlog`, or
|
|
25
|
+
| <span style="white-space: nowrap">`backlog`</span> | `backlog.json` | The task backlog: the path of a JSON file, an HTTP endpoint serving the same document, or a base URL for `jira`/`github`/`gitlab` providers. |
|
|
26
|
+
| <span style="white-space: nowrap">`backlog_provider`</span> | `auto` | `auto` infers file/HTTP from `backlog`, or `jira`/`github`/`gitlab` when the corresponding block is present; explicitly choose `file`, `http`, `jira`, `github`, or `gitlab`. |
|
|
27
27
|
| <span style="white-space: nowrap">`state_dir`</span> | — | Directory for Forgeo's runtime files (locks, run history, daemon state). Remote backlogs default this to the directory of `forgeo.yaml`. |
|
|
28
|
-
| <span style="white-space: nowrap">`backlog_auth`</span> | — | OAuth2 client credentials for a backlog URL that requires them (see [below](#backlog_auth)). |
|
|
28
|
+
| <span style="white-space: nowrap">`backlog_auth`</span> | — | OAuth2 client credentials for a backlog URL that requires them (see [below](#backlog_auth)). Only for `http` provider. |
|
|
29
29
|
| <span style="white-space: nowrap">`jira`</span> | — | Jira REST, workflow, authentication, and custom-field settings. Required when `backlog_provider: jira`. |
|
|
30
|
+
| <span style="white-space: nowrap">`github`</span> | — | GitHub REST settings. Required when `backlog_provider: github`. |
|
|
31
|
+
| <span style="white-space: nowrap">`gitlab`</span> | — | GitLab REST settings. Required when `backlog_provider: gitlab`. |
|
|
30
32
|
| <span style="white-space: nowrap">`blocker_file`</span> | `BLOCKER.md` | Where `BLOCKER.md` is written. Keep it outside the repo so it is never committed. |
|
|
31
33
|
| <span style="white-space: nowrap">`agent_command`</span> | — | The coding agent: any shell command (string) or argv list. **Required.** |
|
|
32
34
|
| <span style="white-space: nowrap">`agent_timeout_seconds`</span> | — | Optional: kill the agent after this many seconds (`null` = never). |
|
|
@@ -140,6 +142,55 @@ truth for human changes.
|
|
|
140
142
|
| `jira.workflow` | defaults | Status ids or names for open, running, blocked, completed, and failed transitions. |
|
|
141
143
|
| `jira.fields` | — | Optional custom-field ids for task attributes such as acceptance criteria and dependencies. |
|
|
142
144
|
|
|
145
|
+
### GitHub backlog
|
|
146
|
+
|
|
147
|
+
Forgeo can read and update GitHub issues directly. Set `backlog_provider: github` and make `backlog` the GitHub API base URL. Issue numbers become task ids. Labels and a hidden JSON block in the issue body hold Forgeo's engine state.
|
|
148
|
+
|
|
149
|
+
```yaml
|
|
150
|
+
backlog_provider: github
|
|
151
|
+
backlog: https://api.github.com
|
|
152
|
+
github:
|
|
153
|
+
repo: owner/repo
|
|
154
|
+
token_env: GITHUB_TOKEN
|
|
155
|
+
label_prefix: forgeo
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
| Key | Default | Meaning |
|
|
159
|
+
| --- | --- | --- |
|
|
160
|
+
| `github.repo` | — | Required owner/repo. |
|
|
161
|
+
| `github.auth` | — | Required PAT env var `token_env`. |
|
|
162
|
+
| `github.label_prefix` | `forgeo` | Prefix for running/blocked/failed labels. |
|
|
163
|
+
| `github.property_key` | `forgeo` | Marker key for hidden body block (symmetry). |
|
|
164
|
+
| `github.page_size` | `30` | Issues per page. |
|
|
165
|
+
| `github.max_issues` | `1000` | Max issues read. |
|
|
166
|
+
| `github.timeout_seconds` | `30` | HTTP timeout. |
|
|
167
|
+
| `github.claim_timeout_seconds` | `86400` | Stale claim timeout. |
|
|
168
|
+
| `github.workflow` | defaults | State/label mapping. |
|
|
169
|
+
| `github.fields` | — | Optional field mappings. |
|
|
170
|
+
|
|
171
|
+
### GitLab backlog
|
|
172
|
+
|
|
173
|
+
```yaml
|
|
174
|
+
backlog_provider: gitlab
|
|
175
|
+
backlog: https://gitlab.example.com
|
|
176
|
+
gitlab:
|
|
177
|
+
repo: group/project
|
|
178
|
+
token_env: GITLAB_TOKEN
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
| Key | Default | Meaning |
|
|
182
|
+
| --- | --- | --- |
|
|
183
|
+
| `gitlab.repo` | — | Required project path or numeric id. |
|
|
184
|
+
| `gitlab.auth` | — | Required PAT env var `token_env`. |
|
|
185
|
+
| `gitlab.label_prefix` | `forgeo` | Prefix for labels. |
|
|
186
|
+
| `gitlab.property_key` | `forgeo` | Marker key. |
|
|
187
|
+
| `gitlab.page_size` | `30` | Issues per page. |
|
|
188
|
+
| `gitlab.max_issues` | `1000` | Max issues. |
|
|
189
|
+
| `gitlab.timeout_seconds` | `30` | HTTP timeout. |
|
|
190
|
+
| `gitlab.claim_timeout_seconds` | `86400` | Stale claim timeout. |
|
|
191
|
+
| `gitlab.workflow` | defaults | State mapping. |
|
|
192
|
+
| `gitlab.fields` | — | Optional field mappings. |
|
|
193
|
+
|
|
143
194
|
## Key details
|
|
144
195
|
|
|
145
196
|
### `backlog_auth`
|
|
@@ -56,20 +56,29 @@ Run the guided wizard from your project root:
|
|
|
56
56
|
forgeo init
|
|
57
57
|
```
|
|
58
58
|
|
|
59
|
-
The wizard asks for
|
|
59
|
+
The wizard asks for:
|
|
60
60
|
|
|
61
61
|
1. **Forgeo folder** — where the backlog, `BLOCKER.md` and the log live
|
|
62
62
|
(default `.forgeo`). It is gitignored by default.
|
|
63
|
-
2. **
|
|
63
|
+
2. **Backlog provider** — where tasks live: `file` (local `.forgeo/backlog.json`),
|
|
64
|
+
`github` / `gitlab` / `jira` / `http`. For `github` it auto-detects
|
|
65
|
+
`owner/repo` from `git remote origin`, asks for `token_env` (default
|
|
66
|
+
`GITHUB_TOKEN`) and can persist a pasted classic PAT (`ghp_...`, scope `repo`)
|
|
67
|
+
to `~/.config/forgeo/github_token_env.sh` (600, wired to `~/.bashrc`). Same
|
|
68
|
+
for `gitlab` (`GITLAB_TOKEN`, base URL) and `jira`/`http`.
|
|
69
|
+
3. **Coding agent command** — the bare command that launches your coding
|
|
64
70
|
agent (default `opencode run --auto`). Forgeo appends the standard task
|
|
65
71
|
prompt (which ends in `$FORGEO_TASK`) automatically, so you never type
|
|
66
72
|
it. Enter a command that already references `$FORGEO_TASK` and it is
|
|
67
73
|
kept verbatim.
|
|
68
|
-
|
|
74
|
+
4. **Refactor prompt** — the instruction used when the backlog is empty; the
|
|
69
75
|
default is offered, or you can paste a custom one.
|
|
70
76
|
|
|
71
77
|
`forgeo init` writes `forgeo.yaml`, creates Forgeo folder, and appends
|
|
72
|
-
`<folder>/` to `.gitignore` (unless you opt out).
|
|
78
|
+
`<folder>/` to `.gitignore` (unless you opt out). For `github`/`gitlab`/`jira`
|
|
79
|
+
set `backlog_provider` + `backlog` URL + provider block is written automatically
|
|
80
|
+
and `state_dir` is set to the Forgeo folder so runtime files stay beside the
|
|
81
|
+
config.
|
|
73
82
|
|
|
74
83
|
```bash
|
|
75
84
|
forgeo init --force # overwrite an existing forgeo.yaml
|
|
@@ -77,18 +86,25 @@ forgeo init --force # overwrite an existing forgeo.yaml
|
|
|
77
86
|
|
|
78
87
|
## 3. Create your first backlog
|
|
79
88
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
`jira
|
|
91
|
-
|
|
89
|
+
If you chose `file` in the wizard, the backlog is a plain JSON file (see
|
|
90
|
+
[Backlog format](backlog.md)) — by default `.forgeo/backlog.json`. Once Forgeo
|
|
91
|
+
is running you can also add tasks from the [web console](web-console-api.md) — no
|
|
92
|
+
file editing needed.
|
|
93
|
+
|
|
94
|
+
If you chose `github`/`gitlab`/`jira`/`http` in the wizard, your `forgeo.yaml`
|
|
95
|
+
already points at the provider (`backlog: https://api.github.com` etc.).
|
|
96
|
+
For `github` create a classic PAT at `https://github.com/settings/tokens/new`
|
|
97
|
+
(scope `repo`), `export GITHUB_TOKEN=ghp_...` (or let the wizard persist it),
|
|
98
|
+
then `forgeo validate` before `forgeo start`. Same for `gitlab` (`GITLAB_TOKEN`)
|
|
99
|
+
and `jira`. See [Backlog format](backlog.md) for provider details.
|
|
100
|
+
|
|
101
|
+
For `jira`/`github`/`gitlab`, set `backlog_provider:` to the provider, point `backlog:` at its base URL
|
|
102
|
+
(`https://jira.example.com`, `https://api.github.com` / `https://github.example.com/api/v3`,
|
|
103
|
+
`https://gitlab.example.com`), configure the provider block (`jira.jql` / `github.repo` / `gitlab.repo` and auth),
|
|
104
|
+
export the credentials named in `*_auth.token_env`, and run `forgeo validate` before starting the daemon.
|
|
105
|
+
The dashboard for these providers is a read-mostly mirror: a banner links to the native board, each card links to
|
|
106
|
+
the native issue, and Forgeo-specific state (BLOCKED/FAILED reasons, `agent_response`) is surfaced on the board — triage
|
|
107
|
+
stays in Jira/GitHub/GitLab. See [Backlog: Jira/GitHub/GitLab](backlog.md) for the complete configuration.
|
|
92
108
|
|
|
93
109
|
```json
|
|
94
110
|
{
|
|
@@ -66,11 +66,20 @@ open-by-default behavior.
|
|
|
66
66
|
|
|
67
67
|
- `GET /` — home page listing every registered instance: name, repository,
|
|
68
68
|
daemon state (lock held), last outcome, next run, and per-status backlog
|
|
69
|
-
counts, each linking to its instance page.
|
|
69
|
+
counts, each linking to its instance page. For issue-backed instances
|
|
70
|
+
(`jira`/`github`/`gitlab`) the card also shows an **Open in Jira/GitHub/GitLab ↗**
|
|
71
|
+
link to the native board.
|
|
70
72
|
- `GET /instances/<name>/` — one instance's page: a kanban backlog, a
|
|
71
73
|
**Create** tab with a form to add tasks (including an optional *Run at*
|
|
72
74
|
date/time input for a one-shot schedule), plus tabs for **logs**, **history**,
|
|
73
|
-
**blocker** and **config**.
|
|
75
|
+
**blocker** and **config**. For document backlogs (`file`/`http`) this board
|
|
76
|
+
is the primary editor; for issue providers (`jira`/`github`/`gitlab`) it is a
|
|
77
|
+
read-mostly **mirror** of the native tracker: a banner at the top links to
|
|
78
|
+
the external board (`Jira`/`GitHub`/`GitLab`), each task card and its detail
|
|
79
|
+
modal carry an **Open in external ↗** link to the native issue, and the board
|
|
80
|
+
surfaces Forgeo-specific state that the native UI does not (BLOCKED/FAILED
|
|
81
|
+
reasons, `agent_response`, retry budget). Creating/editing still works via
|
|
82
|
+
the dashboard, but triage is expected in the native tool. The header carries a **DAEMON** section with the
|
|
74
83
|
daemon status tag (`running`/`stopped`) and **Start**/**Stop**/**Restart**
|
|
75
84
|
buttons that call `POST /api/instances/<name>/start|stop|restart`; the
|
|
76
85
|
buttons reflect the current state (Start is disabled while running, Stop
|
|
@@ -152,15 +161,19 @@ outcome, next run, and backlog counts.
|
|
|
152
161
|
curl http://127.0.0.1:8790/api/instances
|
|
153
162
|
```
|
|
154
163
|
|
|
155
|
-
Each row also carries `
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
164
|
+
Each row also carries `backlog_provider` (`file`/`http`/`jira`/`github`/`gitlab`),
|
|
165
|
+
`backlog` (path or base URL), `backlog_is_issue_provider`, and for issue
|
|
166
|
+
providers `external_board_url`/`external_board_label` (the native board link
|
|
167
|
+
shown on the home cards). It also carries `backlog_error`: `null` normally,
|
|
168
|
+
and the reason when that instance's remote backlog could not be read. Its
|
|
169
|
+
counts are zero in that case — the row reports a backlog it could not reach,
|
|
170
|
+
not an empty one. One unreachable provider never fails the whole listing.
|
|
159
171
|
|
|
160
172
|
### `GET /api/instances/<name>/tasks`
|
|
161
173
|
|
|
162
174
|
List every task in that instance's backlog, in creation order. Each task
|
|
163
|
-
carries extra `unsatisfied_dependencies
|
|
175
|
+
carries extra `unsatisfied_dependencies`, retry fields (see below), and for
|
|
176
|
+
issue providers `external_url` (link to the native Jira/GitHub/GitLab issue).
|
|
164
177
|
|
|
165
178
|
```bash
|
|
166
179
|
curl http://127.0.0.1:8790/api/instances/my-repo/tasks
|
|
@@ -400,7 +413,10 @@ Errors:
|
|
|
400
413
|
### `GET /api/instances/<name>/status`
|
|
401
414
|
|
|
402
415
|
Daemon status: name, repo, interval, `daemon_running` (whether the instance's
|
|
403
|
-
lock is held), the recorded PID, `last_outcome`, and the `next_run_at`.
|
|
416
|
+
lock is held), the recorded PID, `last_outcome`, and the `next_run_at`. Also
|
|
417
|
+
carries `backlog_provider`, `backlog`, `backlog_is_issue_provider`,
|
|
418
|
+
`external_board_url`/`external_board_label` so the instance page can render the
|
|
419
|
+
issue-provider banner and per-task external links.
|
|
404
420
|
|
|
405
421
|
```bash
|
|
406
422
|
curl http://127.0.0.1:8790/api/instances/my-repo/status
|
|
@@ -414,7 +430,12 @@ curl http://127.0.0.1:8790/api/instances/my-repo/status
|
|
|
414
430
|
"daemon_running": true,
|
|
415
431
|
"pid": 4242,
|
|
416
432
|
"last_outcome": "task",
|
|
417
|
-
"next_run_at": "2026-08-01T12:00:00+00:00"
|
|
433
|
+
"next_run_at": "2026-08-01T12:00:00+00:00",
|
|
434
|
+
"backlog_provider": "github",
|
|
435
|
+
"backlog": "https://api.github.com",
|
|
436
|
+
"backlog_is_issue_provider": true,
|
|
437
|
+
"external_board_url": "https://github.com/owner/repo/issues",
|
|
438
|
+
"external_board_label": "GitHub"
|
|
418
439
|
}
|
|
419
440
|
```
|
|
420
441
|
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "forgeo-cli"
|
|
7
|
-
version = "0.
|
|
7
|
+
version = "0.10.0"
|
|
8
8
|
description = "A scheduled software forgeo: executes backlog tasks on main, refactors when idle, and writes BLOCKER.md when it needs human input."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.11"
|
|
@@ -206,7 +206,26 @@ class ShellAgent(BaseAgent):
|
|
|
206
206
|
except TimeoutError:
|
|
207
207
|
timed_out = True
|
|
208
208
|
_kill_process_group(proc)
|
|
209
|
-
|
|
209
|
+
# ``proc.wait()`` waits for both process exit *and* pipe closure
|
|
210
|
+
# (see ``BaseSubprocessTransport._try_finish``). A grandchild that
|
|
211
|
+
# called ``setsid()`` and holds the write end of the pipe keeps it
|
|
212
|
+
# open forever, so the wait would hang past the drain deadline.
|
|
213
|
+
# Bound it by ``drain_timeout_seconds`` and force-close the
|
|
214
|
+
# transport if it still hangs.
|
|
215
|
+
try:
|
|
216
|
+
await asyncio.wait_for(proc.wait(), timeout=self.drain_timeout_seconds)
|
|
217
|
+
except TimeoutError:
|
|
218
|
+
try:
|
|
219
|
+
proc._transport.close() # type: ignore[attr-defined]
|
|
220
|
+
except Exception: # noqa: BLE001, S110 - close must not fail the task
|
|
221
|
+
pass
|
|
222
|
+
try:
|
|
223
|
+
await asyncio.wait_for(proc.wait(), timeout=1.0)
|
|
224
|
+
except TimeoutError:
|
|
225
|
+
logs.append(
|
|
226
|
+
f"[{self.name}] Process did not exit within "
|
|
227
|
+
f"{self.drain_timeout_seconds:g}s after kill; proceeding."
|
|
228
|
+
)
|
|
210
229
|
# Always finish draining so lines already written (and any residual
|
|
211
230
|
# after kill) are captured before we build the result. Bounded: a
|
|
212
231
|
# grandchild that escaped the process group (daemonized agent, docker
|
|
@@ -215,6 +234,11 @@ class ShellAgent(BaseAgent):
|
|
|
215
234
|
try:
|
|
216
235
|
await asyncio.wait_for(readers, timeout=self.drain_timeout_seconds)
|
|
217
236
|
except TimeoutError:
|
|
237
|
+
readers.cancel()
|
|
238
|
+
try:
|
|
239
|
+
await readers
|
|
240
|
+
except asyncio.CancelledError:
|
|
241
|
+
pass
|
|
218
242
|
logs.append(
|
|
219
243
|
f"[{self.name}] Output streams stayed open beyond "
|
|
220
244
|
f"{self.drain_timeout_seconds:g}s; proceeding without them."
|