forgeo-cli 0.12.0__tar.gz → 1.12.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {forgeo_cli-0.12.0 → forgeo_cli-1.12.1}/CHANGELOG.md +13 -1
- {forgeo_cli-0.12.0 → forgeo_cli-1.12.1}/PKG-INFO +4 -3
- {forgeo_cli-0.12.0 → forgeo_cli-1.12.1}/README.md +3 -2
- {forgeo_cli-0.12.0 → forgeo_cli-1.12.1}/docs/backlog.md +8 -8
- {forgeo_cli-0.12.0 → forgeo_cli-1.12.1}/docs/cli-reference.md +16 -5
- {forgeo_cli-0.12.0 → forgeo_cli-1.12.1}/docs/configuration.md +42 -5
- {forgeo_cli-0.12.0 → forgeo_cli-1.12.1}/docs/getting-started.md +15 -7
- {forgeo_cli-0.12.0 → forgeo_cli-1.12.1}/docs/web-console-api.md +2 -1
- {forgeo_cli-0.12.0 → forgeo_cli-1.12.1}/install.sh +1 -1
- {forgeo_cli-0.12.0 → forgeo_cli-1.12.1}/pyproject.toml +1 -1
- {forgeo_cli-0.12.0 → forgeo_cli-1.12.1}/src/forgeo/__init__.py +1 -1
- {forgeo_cli-0.12.0 → forgeo_cli-1.12.1}/src/forgeo/agent.py +12 -4
- {forgeo_cli-0.12.0 → forgeo_cli-1.12.1}/src/forgeo/backlog_github.py +7 -23
- {forgeo_cli-0.12.0 → forgeo_cli-1.12.1}/src/forgeo/backlog_gitlab.py +7 -23
- {forgeo_cli-0.12.0 → forgeo_cli-1.12.1}/src/forgeo/backlog_issue_base.py +25 -0
- {forgeo_cli-0.12.0 → forgeo_cli-1.12.1}/src/forgeo/cli.py +125 -68
- {forgeo_cli-0.12.0 → forgeo_cli-1.12.1}/src/forgeo/config.py +49 -1
- {forgeo_cli-0.12.0 → forgeo_cli-1.12.1}/src/forgeo/models.py +4 -11
- {forgeo_cli-0.12.0 → forgeo_cli-1.12.1}/src/forgeo/oauth_github.py +18 -7
- {forgeo_cli-0.12.0 → forgeo_cli-1.12.1}/src/forgeo/oauth_gitlab.py +18 -6
- {forgeo_cli-0.12.0 → forgeo_cli-1.12.1}/src/forgeo/oauth_jira.py +49 -27
- {forgeo_cli-0.12.0 → forgeo_cli-1.12.1}/src/forgeo/setup.py +63 -6
- {forgeo_cli-0.12.0 → forgeo_cli-1.12.1}/tests/conftest.py +37 -0
- {forgeo_cli-0.12.0 → forgeo_cli-1.12.1}/tests/test_agent.py +22 -1
- {forgeo_cli-0.12.0 → forgeo_cli-1.12.1}/tests/test_backlog_github.py +27 -0
- {forgeo_cli-0.12.0 → forgeo_cli-1.12.1}/tests/test_backlog_gitlab.py +27 -0
- {forgeo_cli-0.12.0 → forgeo_cli-1.12.1}/tests/test_cli.py +110 -24
- {forgeo_cli-0.12.0 → forgeo_cli-1.12.1}/tests/test_daemon.py +69 -64
- {forgeo_cli-0.12.0 → forgeo_cli-1.12.1}/tests/test_install.py +14 -3
- {forgeo_cli-0.12.0 → forgeo_cli-1.12.1}/tests/test_instances.py +2 -0
- {forgeo_cli-0.12.0 → forgeo_cli-1.12.1}/tests/test_models.py +44 -1
- {forgeo_cli-0.12.0 → forgeo_cli-1.12.1}/tests/test_oauth.py +29 -1
- {forgeo_cli-0.12.0 → forgeo_cli-1.12.1}/tests/test_setup.py +1 -1
- {forgeo_cli-0.12.0 → forgeo_cli-1.12.1}/tests/test_web.py +10 -3
- {forgeo_cli-0.12.0 → forgeo_cli-1.12.1}/tests/test_web_common.py +2 -0
- {forgeo_cli-0.12.0 → forgeo_cli-1.12.1}/tests/test_web_lock.py +8 -0
- {forgeo_cli-0.12.0 → forgeo_cli-1.12.1}/www/404.html +4 -1
- forgeo_cli-1.12.1/www/favicon.ico +0 -0
- {forgeo_cli-0.12.0 → forgeo_cli-1.12.1}/www/index.html +4 -1
- {forgeo_cli-0.12.0 → forgeo_cli-1.12.1}/.github/workflows/ci.yml +0 -0
- {forgeo_cli-0.12.0 → forgeo_cli-1.12.1}/.gitignore +0 -0
- {forgeo_cli-0.12.0 → forgeo_cli-1.12.1}/CONTRIBUTING.md +0 -0
- {forgeo_cli-0.12.0 → forgeo_cli-1.12.1}/LICENSE +0 -0
- {forgeo_cli-0.12.0 → forgeo_cli-1.12.1}/config/nginx-forgeo.conf +0 -0
- {forgeo_cli-0.12.0 → forgeo_cli-1.12.1}/docs/agent-contract.md +0 -0
- {forgeo_cli-0.12.0 → forgeo_cli-1.12.1}/docs/img/console.png +0 -0
- {forgeo_cli-0.12.0 → forgeo_cli-1.12.1}/docs/img/demo.gif +0 -0
- {forgeo_cli-0.12.0 → forgeo_cli-1.12.1}/docs/img/logo.png +0 -0
- {forgeo_cli-0.12.0 → forgeo_cli-1.12.1}/docs/img/og.png +0 -0
- {forgeo_cli-0.12.0 → forgeo_cli-1.12.1}/docs/img/title.svg +0 -0
- {forgeo_cli-0.12.0 → forgeo_cli-1.12.1}/docs/index.md +0 -0
- {forgeo_cli-0.12.0 → forgeo_cli-1.12.1}/forgeo.spec +0 -0
- {forgeo_cli-0.12.0 → forgeo_cli-1.12.1}/mkdocs.yml +0 -0
- {forgeo_cli-0.12.0 → forgeo_cli-1.12.1}/scripts/__init__.py +0 -0
- {forgeo_cli-0.12.0 → forgeo_cli-1.12.1}/scripts/render_homebrew_formula.py +0 -0
- {forgeo_cli-0.12.0 → forgeo_cli-1.12.1}/scripts/test-github-backlog-e2e.sh +0 -0
- {forgeo_cli-0.12.0 → forgeo_cli-1.12.1}/src/forgeo/__main__.py +0 -0
- {forgeo_cli-0.12.0 → forgeo_cli-1.12.1}/src/forgeo/backlog.py +0 -0
- {forgeo_cli-0.12.0 → forgeo_cli-1.12.1}/src/forgeo/backlog_http.py +0 -0
- {forgeo_cli-0.12.0 → forgeo_cli-1.12.1}/src/forgeo/backlog_jira.py +0 -0
- {forgeo_cli-0.12.0 → forgeo_cli-1.12.1}/src/forgeo/central.py +0 -0
- {forgeo_cli-0.12.0 → forgeo_cli-1.12.1}/src/forgeo/daemon.py +0 -0
- {forgeo_cli-0.12.0 → forgeo_cli-1.12.1}/src/forgeo/daemon_control.py +0 -0
- {forgeo_cli-0.12.0 → forgeo_cli-1.12.1}/src/forgeo/forgeo.py +0 -0
- {forgeo_cli-0.12.0 → forgeo_cli-1.12.1}/src/forgeo/git.py +0 -0
- {forgeo_cli-0.12.0 → forgeo_cli-1.12.1}/src/forgeo/instances.py +0 -0
- {forgeo_cli-0.12.0 → forgeo_cli-1.12.1}/src/forgeo/io.py +0 -0
- {forgeo_cli-0.12.0 → forgeo_cli-1.12.1}/src/forgeo/notify.py +0 -0
- {forgeo_cli-0.12.0 → forgeo_cli-1.12.1}/src/forgeo/oauth.py +0 -0
- {forgeo_cli-0.12.0 → forgeo_cli-1.12.1}/src/forgeo/oauth_common.py +0 -0
- {forgeo_cli-0.12.0 → forgeo_cli-1.12.1}/src/forgeo/paths.py +0 -0
- {forgeo_cli-0.12.0 → forgeo_cli-1.12.1}/src/forgeo/runs.py +0 -0
- {forgeo_cli-0.12.0 → forgeo_cli-1.12.1}/src/forgeo/update.py +0 -0
- {forgeo_cli-0.12.0 → forgeo_cli-1.12.1}/src/forgeo/validate.py +0 -0
- {forgeo_cli-0.12.0 → forgeo_cli-1.12.1}/src/forgeo/web/central/central.css +0 -0
- {forgeo_cli-0.12.0 → forgeo_cli-1.12.1}/src/forgeo/web/central/central.js +0 -0
- {forgeo_cli-0.12.0 → forgeo_cli-1.12.1}/src/forgeo/web/central/index.html +0 -0
- {forgeo_cli-0.12.0 → forgeo_cli-1.12.1}/src/forgeo/web/central/instance.html +0 -0
- {forgeo_cli-0.12.0 → forgeo_cli-1.12.1}/src/forgeo/web/central/login.html +0 -0
- {forgeo_cli-0.12.0 → forgeo_cli-1.12.1}/src/forgeo/web/style.css +0 -0
- {forgeo_cli-0.12.0 → forgeo_cli-1.12.1}/src/forgeo/web_common.py +0 -0
- {forgeo_cli-0.12.0 → forgeo_cli-1.12.1}/tests/test_backlog.py +0 -0
- {forgeo_cli-0.12.0 → forgeo_cli-1.12.1}/tests/test_backlog_http.py +0 -0
- {forgeo_cli-0.12.0 → forgeo_cli-1.12.1}/tests/test_backlog_jira.py +0 -0
- {forgeo_cli-0.12.0 → forgeo_cli-1.12.1}/tests/test_factory.py +0 -0
- {forgeo_cli-0.12.0 → forgeo_cli-1.12.1}/tests/test_git.py +0 -0
- {forgeo_cli-0.12.0 → forgeo_cli-1.12.1}/tests/test_io.py +0 -0
- {forgeo_cli-0.12.0 → forgeo_cli-1.12.1}/tests/test_paths.py +0 -0
- {forgeo_cli-0.12.0 → forgeo_cli-1.12.1}/tests/test_remote_backlog_cycle.py +0 -0
- {forgeo_cli-0.12.0 → forgeo_cli-1.12.1}/tests/test_render_homebrew.py +0 -0
- {forgeo_cli-0.12.0 → forgeo_cli-1.12.1}/tests/test_runs.py +0 -0
- {forgeo_cli-0.12.0 → forgeo_cli-1.12.1}/tests/test_update.py +0 -0
|
@@ -7,6 +7,17 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [1.12.1] - 2026-09-09
|
|
11
|
+
|
|
12
|
+
### Fixed
|
|
13
|
+
|
|
14
|
+
- GitHub/GitLab backlogs preserve per-task customization when a task is created.
|
|
15
|
+
- OAuth issue-backlog login supports browser URLs, callback ports, and config-relative tokens.
|
|
16
|
+
|
|
17
|
+
### Removed
|
|
18
|
+
|
|
19
|
+
- Unused `github.fields`/`gitlab.fields` field mapping (GitHub/GitLab issues have no custom fields).
|
|
20
|
+
|
|
10
21
|
## [0.12.0] - 2026-09-03
|
|
11
22
|
|
|
12
23
|
### Added
|
|
@@ -439,7 +450,8 @@ Initial release of the scheduled, agent-driven software forgeo.
|
|
|
439
450
|
overlapping-run skipping.
|
|
440
451
|
- Dogfooding docs removed; local configs kept out of the repository.
|
|
441
452
|
|
|
442
|
-
[Unreleased]: https://github.com/lucaGazzola/forgeo/compare/
|
|
453
|
+
[Unreleased]: https://github.com/lucaGazzola/forgeo/compare/v1.12.1...HEAD
|
|
454
|
+
[1.12.1]: https://github.com/lucaGazzola/forgeo/compare/v0.12.0...v1.12.1
|
|
443
455
|
[0.12.0]: https://github.com/lucaGazzola/forgeo/compare/v0.11.0...v0.12.0
|
|
444
456
|
[0.11.0]: https://github.com/lucaGazzola/forgeo/compare/v0.10.0...v0.11.0
|
|
445
457
|
[0.10.0]: https://github.com/lucaGazzola/forgeo/compare/v0.9.0...v0.10.0
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: forgeo-cli
|
|
3
|
-
Version:
|
|
3
|
+
Version: 1.12.1
|
|
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
|
|
@@ -99,7 +99,7 @@ pipx install forgeo-cli # Python 3.11+
|
|
|
99
99
|
forgeo init
|
|
100
100
|
```
|
|
101
101
|
|
|
102
|
-
Wizard in your project root. Creates `forgeo.yaml` and `.forgeo/` (backlog, logs, blockers). Prompts for backlog provider (`file` / `github` / `gitlab` / `jira` / `http`) and agent command. For
|
|
102
|
+
Wizard in your project root. Creates `forgeo.yaml` and `.forgeo/` (backlog, logs, blockers). Prompts for backlog provider (`file` / `github` / `gitlab` / `jira` / `http`) and agent command. For issue providers it offers a PAT or OAuth login (`forgeo auth login` → `~/.config/forgeo/tokens/`); GitHub repository detection is automatic when possible, while Jira asks for its URL and JQL.
|
|
103
103
|
|
|
104
104
|
### 3. Fill the backlog
|
|
105
105
|
|
|
@@ -129,7 +129,8 @@ Each cycle: pick task → run agent → commit/push (or `REVIEW` branch when `re
|
|
|
129
129
|
forgeo status # config, counts, next task, daemon state, last outcome
|
|
130
130
|
forgeo once # one cycle in foreground, no daemon
|
|
131
131
|
forgeo run --task TASK-012 # run a specific OPEN task now
|
|
132
|
-
forgeo stop
|
|
132
|
+
forgeo stop # stop daemon
|
|
133
|
+
forgeo restart # restart daemon
|
|
133
134
|
forgeo web # dashboard for all instances
|
|
134
135
|
```
|
|
135
136
|
|
|
@@ -45,7 +45,7 @@ pipx install forgeo-cli # Python 3.11+
|
|
|
45
45
|
forgeo init
|
|
46
46
|
```
|
|
47
47
|
|
|
48
|
-
Wizard in your project root. Creates `forgeo.yaml` and `.forgeo/` (backlog, logs, blockers). Prompts for backlog provider (`file` / `github` / `gitlab` / `jira` / `http`) and agent command. For
|
|
48
|
+
Wizard in your project root. Creates `forgeo.yaml` and `.forgeo/` (backlog, logs, blockers). Prompts for backlog provider (`file` / `github` / `gitlab` / `jira` / `http`) and agent command. For issue providers it offers a PAT or OAuth login (`forgeo auth login` → `~/.config/forgeo/tokens/`); GitHub repository detection is automatic when possible, while Jira asks for its URL and JQL.
|
|
49
49
|
|
|
50
50
|
### 3. Fill the backlog
|
|
51
51
|
|
|
@@ -75,7 +75,8 @@ Each cycle: pick task → run agent → commit/push (or `REVIEW` branch when `re
|
|
|
75
75
|
forgeo status # config, counts, next task, daemon state, last outcome
|
|
76
76
|
forgeo once # one cycle in foreground, no daemon
|
|
77
77
|
forgeo run --task TASK-012 # run a specific OPEN task now
|
|
78
|
-
forgeo stop
|
|
78
|
+
forgeo stop # stop daemon
|
|
79
|
+
forgeo restart # restart daemon
|
|
79
80
|
forgeo web # dashboard for all instances
|
|
80
81
|
```
|
|
81
82
|
|
|
@@ -150,7 +150,7 @@ backlog: https://api.example.com/backlog
|
|
|
150
150
|
| Every read | `GET <url>` returns `{"tasks": [...]}` |
|
|
151
151
|
| Every write | `POST <url>` sends the full document |
|
|
152
152
|
|
|
153
|
-
Add `backlog_auth` for OAuth2. Same schema and ordering. Endpoint must:
|
|
153
|
+
Add `backlog_auth` for OAuth2 client-credentials access to an HTTP backlog. This is a service-account flow, not the human browser login described for issue providers below. Same schema and ordering. Endpoint must:
|
|
154
154
|
|
|
155
155
|
- Return `{"tasks": [...]}`; non-object or non-list → empty.
|
|
156
156
|
- Replace, not append — POST body is the complete list.
|
|
@@ -178,7 +178,7 @@ jira:
|
|
|
178
178
|
# jira:
|
|
179
179
|
# jql: 'project = APP AND labels = forgeo'
|
|
180
180
|
# auth:
|
|
181
|
-
# oauth: {client_id: xxxx, client_secret_env: JIRA_CLIENT_SECRET, scope: "offline_access read:jira-user read:jira-work"}
|
|
181
|
+
# oauth: {client_id: xxxx, client_secret_env: JIRA_CLIENT_SECRET, scope: "offline_access read:jira-user read:jira-work", flow: browser}
|
|
182
182
|
# # then: forgeo auth login --provider jira --client-id xxxx
|
|
183
183
|
```
|
|
184
184
|
|
|
@@ -186,7 +186,7 @@ jira:
|
|
|
186
186
|
- `open_statuses` = pickable; `running_status` = claimed before agent runs; `completed_status`/`blocked_status`/`failed_status` = terminal (blocked/failed optional — labels `forgeo-blocked`/`forgeo-failed` always applied).
|
|
187
187
|
- Engine state (`blocker_reason`, `retry_count`, `agent_response`, etc.) in Jira issue property `forgeo`.
|
|
188
188
|
- Optional `jira.fields` maps custom fields for `acceptance_criteria`, `dependencies`, etc. Without `run_at` mapping, Jira `duedate` is used at midnight UTC. Dependencies also inferred from `blocks` issue links.
|
|
189
|
-
- Auth from
|
|
189
|
+
- Auth is exactly one of PAT from environment variables (`basic` username + token or `bearer` token) or Jira Cloud OAuth 3LO (`oauth`). Uses REST API v3 (`/search/jql` + cursor) by default; set `api_version: 2` for older servers. OAuth browser login stores the token outside the config and discovers `cloud_id`; keep the configured client-secret environment variable available for refresh.
|
|
190
190
|
- Daemon paginates JQL; stale claims released after `claim_timeout_seconds`. Unavailable Jira fails the cycle.
|
|
191
191
|
|
|
192
192
|
## A GitHub backlog
|
|
@@ -196,14 +196,14 @@ jira:
|
|
|
196
196
|
backlog_provider: github
|
|
197
197
|
backlog: https://api.github.com # or https://github.example.com/api/v3
|
|
198
198
|
github: {repo: owner/repo, auth: {token_env: GITHUB_TOKEN}}
|
|
199
|
-
# OAuth (browser
|
|
199
|
+
# OAuth (device or browser):
|
|
200
200
|
# github: {repo: owner/repo, auth: {oauth: {client_id: Iv1.xxxx, flow: device, scope: repo}}}
|
|
201
201
|
# # then: forgeo auth login --provider github --client-id Iv1.xxxx
|
|
202
202
|
```
|
|
203
203
|
|
|
204
204
|
- Issue numbers → task IDs; `title`/`body`/`created_at`/`updated_at` → task fields.
|
|
205
|
-
- `open` → `OPEN`, `closed` → `COMPLETED`; labels `forgeo-running`/`blocked`/`failed` for the rest.
|
|
206
|
-
-
|
|
205
|
+
- `open` → `OPEN`, `closed` → `COMPLETED`; labels `forgeo-running`/`forgeo-blocked`/`forgeo-failed` for the rest.
|
|
206
|
+
- Per-task customization (`agent_command`, `agent_timeout_seconds`, `acceptance_criteria`, `dependencies`, `files_to_modify`, `run_at`, `retries_left`, `review_required`) and engine state both live in a hidden `<!-- forgeo: {...} -->` block inside the body. There is no `github.fields` custom-field mapping — GitHub issues expose no custom fields on the REST issues API.
|
|
207
207
|
- Paginated `GET /repos/{owner}/{repo}/issues?state=all`; claiming adds `forgeo-running` + `claimed_at`.
|
|
208
208
|
|
|
209
209
|
Test live with `scripts/test-github-backlog-e2e.sh`:
|
|
@@ -219,12 +219,12 @@ GITHUB_TOKEN=... ./scripts/test-github-backlog-e2e.sh
|
|
|
219
219
|
backlog_provider: gitlab
|
|
220
220
|
backlog: https://gitlab.example.com # instance root, /api/v4 appended
|
|
221
221
|
gitlab: {repo: group/project, auth: {token_env: GITLAB_TOKEN}}
|
|
222
|
-
# OAuth (browser PKCE):
|
|
222
|
+
# OAuth (browser PKCE, device if enabled by the instance):
|
|
223
223
|
# gitlab: {repo: group/project, auth: {oauth: {client_id: abc, flow: browser, scope: api}}}
|
|
224
224
|
# # then: forgeo auth login --provider gitlab --client-id abc
|
|
225
225
|
```
|
|
226
226
|
|
|
227
|
-
- `iid` → task ID; `opened`/`closed` → `OPEN`/`COMPLETED`; same hidden-block and
|
|
227
|
+
- `iid` → task ID; `opened`/`closed` → `OPEN`/`COMPLETED`; labels `forgeo-running`/`forgeo-blocked`/`forgeo-failed`; same hidden-block mechanics as GitHub (per-task customization and engine state in a hidden `<!-- forgeo: {...} -->` block in the description). OAuth is an alternative to `token_env` and supports browser PKCE (or device flow when enabled by the GitLab instance).
|
|
228
228
|
- Paginated `GET /api/v4/projects/:id/issues?state=all`.
|
|
229
229
|
|
|
230
230
|
## Managing tasks
|
|
@@ -127,7 +127,9 @@ Table of all instances: name, daemon state, last outcome (from `runs.jsonl`). Ex
|
|
|
127
127
|
|
|
128
128
|
## `forgeo auth`
|
|
129
129
|
|
|
130
|
-
Browser/OAuth login for `github`/`gitlab`/`jira` (alternative to
|
|
130
|
+
Browser/OAuth login for `github`/`gitlab`/`jira` (alternative to a PAT in an environment variable). This is separate from `backlog_auth`, which uses OAuth2 client credentials for an HTTP backlog. Tokens are stored `0600` in `~/.config/forgeo/tokens/` and read by `forgeo validate`/`start`/`once`.
|
|
131
|
+
|
|
132
|
+
Create an OAuth application with the provider before logging in. GitHub defaults to the device flow, GitLab defaults to browser PKCE, and Jira Cloud supports browser PKCE only. Browser flows listen on `127.0.0.1`; the port is ephemeral unless `--callback-port` is supplied. If the provider requires an exact registered redirect URI, register `http://127.0.0.1:<port>/callback` and use that same port in the command.
|
|
131
133
|
|
|
132
134
|
### `forgeo auth login --provider <github|gitlab|jira>`
|
|
133
135
|
|
|
@@ -136,17 +138,26 @@ Browser/OAuth login for `github`/`gitlab`/`jira` (alternative to `*_TOKEN` PAT).
|
|
|
136
138
|
| `--provider` | `github` (default) / `gitlab` / `jira`. |
|
|
137
139
|
| `--config <file>` | `forgeo.yaml` to read `auth.oauth.client_id` (defaults to `./forgeo.yaml`). |
|
|
138
140
|
| `--client-id <id>` | OAuth client ID (overrides config; required if not in `forgeo.yaml`). |
|
|
139
|
-
| `--flow <device|browser>` | `device`
|
|
141
|
+
| `--flow <device|browser>` | `device` for GitHub, `browser` for GitLab/Jira. GitLab device flow is available only when enabled by the GitLab instance; Jira is browser-only. |
|
|
140
142
|
| `--scope <scope>` | Scope (default `repo` / `api` / `offline_access read:jira-user read:jira-work`). |
|
|
141
143
|
| `--token-file <path>` | Where to store token (default per-provider per-host `~/.config/forgeo/tokens/<provider>.json`). |
|
|
144
|
+
| `--callback-port <port>` | Fixed `127.0.0.1` port for browser callbacks. Default is an ephemeral port; use this when the OAuth app requires an exact callback URI. |
|
|
145
|
+
| `--cloud-id <id>` | Jira Cloud ID (overrides `jira.auth.oauth.cloud_id`). |
|
|
142
146
|
| `--api-base <url>` | Provider API base (default from `forgeo.yaml` backlog or `https://api.github.com`/`https://gitlab.com`). |
|
|
143
|
-
| `--no-open-browser` | Print URL instead of
|
|
147
|
+
| `--no-open-browser` | Print the authorization URL instead of opening it automatically. Applies to browser and device flows. |
|
|
144
148
|
|
|
145
|
-
Device flow: prints
|
|
149
|
+
Device flow: prints a verification URL and user code, then polls the provider token endpoint. Browser flow: opens (or prints) the provider authorization URL with a PKCE `code_challenge`, listens on loopback, and exchanges the callback code for an access token. Jira also discovers an Atlassian `cloud_id` and stores a refresh token when one is returned. If `client_secret_env` is configured for Jira, export that variable for token refreshes by the daemon. `forgeo init` with OAuth writes `auth.oauth` and offers to run login immediately.
|
|
146
150
|
|
|
147
151
|
### `forgeo auth status --provider <p>` / `forgeo auth logout --provider <p>`
|
|
148
152
|
|
|
149
|
-
|
|
153
|
+
```bash
|
|
154
|
+
forgeo auth status --provider github
|
|
155
|
+
forgeo auth status --provider gitlab
|
|
156
|
+
forgeo auth status --provider jira
|
|
157
|
+
forgeo auth logout --provider github
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
`status` masks the token and shows `scope`/`expires_in`/`cloud_id` (Jira). `logout` removes the file. Both honor `--token-file`/`--api-base`/`--config` and use the project-local `forgeo.yaml` when it exists; pass `--config` for a config elsewhere.
|
|
150
161
|
|
|
151
162
|
## `forgeo web`
|
|
152
163
|
|
|
@@ -96,13 +96,21 @@ jira:
|
|
|
96
96
|
# client_secret_env: JIRA_CLIENT_SECRET
|
|
97
97
|
# scope: offline_access read:jira-user read:jira-work
|
|
98
98
|
# token_file: ~/.config/forgeo/tokens/jira.json # optional
|
|
99
|
+
# callback_port: 8765 # optional fixed loopback port
|
|
99
100
|
# # then: forgeo auth login --provider jira --client-id xxxx
|
|
100
101
|
```
|
|
101
102
|
|
|
102
103
|
| Key | Default | Description |
|
|
103
104
|
| --- | --- | --- |
|
|
104
105
|
| `jira.jql` | — | JQL scope — include all lifecycle states. |
|
|
105
|
-
| `jira.auth` | — | `basic`
|
|
106
|
+
| `jira.auth` | — | Exactly one of PAT (`basic`/`bearer` with `token_env`) or `oauth` (Jira Cloud browser PKCE). |
|
|
107
|
+
| `jira.auth.oauth.client_id` | — | Atlassian OAuth app client ID. Required for OAuth. |
|
|
108
|
+
| `jira.auth.oauth.client_secret_env` | — | Optional environment variable containing the OAuth client secret; required for refresh when the app is confidential. |
|
|
109
|
+
| `jira.auth.oauth.scope` | — | Requested scopes; `offline_access read:jira-user read:jira-work` by default. |
|
|
110
|
+
| `jira.auth.oauth.token_file` | per-host path | Token JSON file, mode `0600`; relative paths resolve from `forgeo.yaml`. |
|
|
111
|
+
| `jira.auth.oauth.cloud_id` | — | Atlassian site ID; auto-detected after login when omitted. |
|
|
112
|
+
| `jira.auth.oauth.flow` | `browser` | Browser PKCE only. |
|
|
113
|
+
| `jira.auth.oauth.callback_port` | ephemeral | Fixed loopback port for browser login when the app requires an exact callback URI. |
|
|
106
114
|
| `jira.project_key` | — | Project for dashboard task creation. |
|
|
107
115
|
| `jira.issue_type` | `Task` | Issue type for creation. |
|
|
108
116
|
| `jira.api_version` | `3` | `3` = Cloud cursor pagination, `2` = offset. |
|
|
@@ -136,13 +144,20 @@ github:
|
|
|
136
144
|
# flow: device # device | browser
|
|
137
145
|
# scope: repo
|
|
138
146
|
# token_file: ~/.config/forgeo/tokens/github.json
|
|
147
|
+
# callback_port: 8765 # optional fixed loopback port for browser flow
|
|
139
148
|
# # then: forgeo auth login --provider github --client-id Iv1.xxxx
|
|
140
149
|
```
|
|
141
150
|
|
|
142
151
|
| Key | Default | Description |
|
|
143
152
|
| --- | --- | --- |
|
|
144
153
|
| `github.repo` | — | `owner/repo`. |
|
|
145
|
-
| `github.auth` | — | `token_env`
|
|
154
|
+
| `github.auth` | — | Exactly one of `token_env` (PAT) or `oauth`. |
|
|
155
|
+
| `github.auth.oauth.client_id` | — | OAuth app client ID. Required for OAuth. |
|
|
156
|
+
| `github.auth.oauth.flow` | `device` | `device` or `browser`. |
|
|
157
|
+
| `github.auth.oauth.scope` | `repo` | OAuth scope. |
|
|
158
|
+
| `github.auth.oauth.token_file` | per-host path | Token JSON file, mode `0600`; relative paths resolve from `forgeo.yaml`. |
|
|
159
|
+
| `github.auth.oauth.callback_port` | ephemeral | Fixed loopback port for browser login when the app requires an exact callback URI. |
|
|
160
|
+
| `github.auth.oauth.client_secret_env` | — | Optional environment variable containing a confidential browser-flow client secret. |
|
|
146
161
|
| `github.label_prefix` | `forgeo` | Label prefix. |
|
|
147
162
|
| `github.property_key` | `forgeo` | Marker key for hidden body block. |
|
|
148
163
|
| `github.page_size` | `30` | Issues per page. |
|
|
@@ -150,9 +165,8 @@ github:
|
|
|
150
165
|
| `github.timeout_seconds` | `30` | HTTP timeout. |
|
|
151
166
|
| `github.claim_timeout_seconds` | `86400` | Stale claim timeout. |
|
|
152
167
|
| `github.workflow` | defaults | State/label mapping. |
|
|
153
|
-
| `github.fields` | — | Field mappings for `acceptance_criteria`, `dependencies`, `files_to_modify`, `agent_command`, `agent_timeout_seconds`, `run_at`, `retries_left`. |
|
|
154
168
|
|
|
155
|
-
Issue numbers become task IDs; `open`/`closed` maps to `OPEN`/`COMPLETED`; `forgeo-running`/`blocked`/`failed` labels cover the rest.
|
|
169
|
+
Issue numbers become task IDs; `open`/`closed` maps to `OPEN`/`COMPLETED`; `forgeo-running`/`blocked`/`failed` labels cover the rest. GitHub issues expose no custom-field mechanism on the REST issues API, so per-task customization (`agent_command`, `agent_timeout_seconds`, `acceptance_criteria`, `dependencies`, `files_to_modify`, `run_at`, `retries_left`, `review_required`) and engine state both live in a hidden `<!-- forgeo: {...} -->` block in the issue body. Engine-managed fields (`retry_count`, `agent_response`, `failure_reason`, ...) are written by Forgeo and not meant to be edited by hand.
|
|
156
170
|
|
|
157
171
|
### GitLab
|
|
158
172
|
|
|
@@ -173,10 +187,33 @@ gitlab:
|
|
|
173
187
|
# flow: browser # browser | device
|
|
174
188
|
# scope: api
|
|
175
189
|
# token_file: ~/.config/forgeo/tokens/gitlab.json
|
|
190
|
+
# callback_port: 8765 # optional fixed loopback port for browser flow
|
|
176
191
|
# # then: forgeo auth login --provider gitlab --client-id abc123
|
|
177
192
|
```
|
|
178
193
|
|
|
179
|
-
Same keys as GitHub (`gitlab.*`)
|
|
194
|
+
Same backlog and task keys as GitHub (`gitlab.*`). Its auth must contain exactly one of `token_env` (PAT) or `oauth`; OAuth supports `client_id`, `flow`, `scope`, `token_file`, `callback_port`, and optional `client_secret_env`. Issue `iid` becomes task ID; `opened`/`closed` maps to `OPEN`/`COMPLETED`; per-task customization and engine state live in a hidden `<!-- forgeo: {...} -->` block in the issue description, as with GitHub.
|
|
195
|
+
|
|
196
|
+
### Browser OAuth details
|
|
197
|
+
|
|
198
|
+
For GitHub and GitLab, OAuth is an alternative to the provider PAT. For Jira, OAuth is available for Jira Cloud and is browser-only. Do not configure both `token_env` and `oauth` in the same provider; validation requires exactly one.
|
|
199
|
+
|
|
200
|
+
The token file is outside `forgeo.yaml`, is written with mode `0600`, and defaults to a provider/host-specific file under `~/.config/forgeo/tokens/`. A relative `token_file` is resolved relative to the config file. A `client_secret_env` value is only an environment variable name; the secret itself must be exported before login and kept available to the daemon for refreshes.
|
|
201
|
+
|
|
202
|
+
Browser login uses a loopback callback at `http://127.0.0.1:<port>/callback`. The default port is ephemeral. If the provider requires a pre-registered exact URI, register a fixed port in the OAuth application and pass it to login:
|
|
203
|
+
|
|
204
|
+
```bash
|
|
205
|
+
forgeo auth login --provider github --client-id Iv1.xxx --flow browser --callback-port 8765
|
|
206
|
+
forgeo auth login --provider gitlab --client-id abc123 --callback-port 8765
|
|
207
|
+
export JIRA_CLIENT_SECRET=...
|
|
208
|
+
forgeo auth login --provider jira --client-id xxxx --callback-port 8765
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
Use `--no-open-browser` to print the authorization URL without opening it. After login, verify the provider-specific token and start the daemon:
|
|
212
|
+
|
|
213
|
+
```bash
|
|
214
|
+
forgeo auth status --provider github # or gitlab / jira
|
|
215
|
+
forgeo validate
|
|
216
|
+
```
|
|
180
217
|
|
|
181
218
|
## Key details
|
|
182
219
|
|
|
@@ -28,11 +28,11 @@ forgeo init
|
|
|
28
28
|
The wizard asks for:
|
|
29
29
|
|
|
30
30
|
1. **Forgeo folder** — where backlog/logs live (default `.forgeo`, gitignored).
|
|
31
|
-
2. **Backlog provider** — `file` (local JSON), `github`/`gitlab`/`jira`/`http`. For
|
|
31
|
+
2. **Backlog provider** — `file` (local JSON), `github`/`gitlab`/`jira`/`http`. For GitHub it auto-detects `owner/repo` from `git remote` and can persist a pasted `GITHUB_TOKEN` to `~/.config/forgeo/github_token_env.sh`.
|
|
32
32
|
3. **Agent command** — bare command for your agent (default `opencode run --auto`). Forgeo appends the task prompt (`$FORGEO_TASK`); if your command already references `$FORGEO_TASK` it is kept verbatim.
|
|
33
33
|
4. **Refactor prompt** — used when the backlog is empty.
|
|
34
34
|
|
|
35
|
-
Writes `forgeo.yaml`, creates the folder, and appends it to `.gitignore` (opt-out available). With `github`/`gitlab`/`jira` it also sets `backlog_provider`, `backlog` URL, provider block, and `state_dir`. For `github`/`gitlab`/`jira` you can pick `PAT` or
|
|
35
|
+
Writes `forgeo.yaml`, creates the folder, and appends it to `.gitignore` (opt-out available). With `github`/`gitlab`/`jira` it also sets `backlog_provider`, `backlog` URL, provider block, and `state_dir`. For `github`/`gitlab`/`jira` you can pick `PAT` or OAuth. GitHub defaults to device flow; GitLab and Jira use browser PKCE. Browser tokens are stored in `~/.config/forgeo/tokens/`.
|
|
36
36
|
|
|
37
37
|
```bash
|
|
38
38
|
forgeo init --force # overwrite existing config
|
|
@@ -58,17 +58,25 @@ forgeo init --force # overwrite existing config
|
|
|
58
58
|
|
|
59
59
|
You can also add tasks from the dashboard once Forgeo is running — no file editing needed.
|
|
60
60
|
|
|
61
|
-
**Remote providers** — `forgeo.yaml` already points at the provider. Authenticate with PAT **or** browser:
|
|
61
|
+
**Remote providers** — `forgeo.yaml` already points at the provider. Authenticate with a PAT **or** browser OAuth:
|
|
62
62
|
|
|
63
63
|
```bash
|
|
64
64
|
# PAT:
|
|
65
|
-
export GITHUB_TOKEN=ghp_... # or GITLAB_TOKEN /
|
|
66
|
-
#
|
|
67
|
-
forgeo auth login --provider github --client-id Iv1.xxx
|
|
68
|
-
|
|
65
|
+
export GITHUB_TOKEN=ghp_... # or GITLAB_TOKEN / JIRA_TOKEN
|
|
66
|
+
# GitHub OAuth (device flow by default):
|
|
67
|
+
forgeo auth login --provider github --client-id Iv1.xxx
|
|
68
|
+
# GitLab OAuth (browser PKCE):
|
|
69
|
+
forgeo auth login --provider gitlab --client-id abc123
|
|
70
|
+
# Jira Cloud OAuth (browser PKCE; export the configured secret for refresh):
|
|
71
|
+
export JIRA_CLIENT_SECRET=...
|
|
72
|
+
forgeo auth login --provider jira --client-id xxxx
|
|
73
|
+
forgeo auth status --provider github # use gitlab or jira for those providers
|
|
74
|
+
forgeo auth logout --provider github
|
|
69
75
|
forgeo validate
|
|
70
76
|
```
|
|
71
77
|
|
|
78
|
+
Browser login uses an ephemeral loopback port by default. If the OAuth application requires an exact registered callback URL, register `http://127.0.0.1:8765/callback` and add `--callback-port 8765` to the login command. Use `--no-open-browser` when you want to open the printed authorization URL yourself. For a config outside the current directory, add `--config /path/to/forgeo.yaml` to the auth command.
|
|
79
|
+
|
|
72
80
|
See [Backlog format](backlog.md) for Jira/GitHub/GitLab details. The dashboard for issue providers is a read-mostly mirror — triage stays in the native tracker.
|
|
73
81
|
|
|
74
82
|
!!! tip
|
|
@@ -6,7 +6,8 @@ The **central dashboard** (`forgeo web`) is the only web interface. Daemons bind
|
|
|
6
6
|
forgeo web # 0.0.0.0:8790 foreground
|
|
7
7
|
forgeo web --port 9000 --host 127.0.0.1
|
|
8
8
|
forgeo web -d # background
|
|
9
|
-
forgeo web status
|
|
9
|
+
forgeo web status
|
|
10
|
+
forgeo web stop
|
|
10
11
|
forgeo web --token # bearer auth for /api/*
|
|
11
12
|
```
|
|
12
13
|
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "forgeo-cli"
|
|
7
|
-
version = "
|
|
7
|
+
version = "1.12.1"
|
|
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"
|
|
@@ -51,11 +51,19 @@ def _kill_process_group(proc: asyncio.subprocess.Process) -> None:
|
|
|
51
51
|
own session (``start_new_session=True``), so ``proc.pid`` is its process
|
|
52
52
|
group id and killing the group reaps the entire process tree. Falls back
|
|
53
53
|
to killing just the direct child when the group is already gone.
|
|
54
|
+
|
|
55
|
+
Platforms without process groups (Windows) fall back to killing the
|
|
56
|
+
direct child only.
|
|
54
57
|
"""
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
58
|
+
killpg = getattr(os, "killpg", None)
|
|
59
|
+
if killpg is not None:
|
|
60
|
+
sigkill = getattr(signal, "SIGKILL", signal.SIGTERM)
|
|
61
|
+
try:
|
|
62
|
+
killpg(proc.pid, sigkill)
|
|
63
|
+
except (ProcessLookupError, PermissionError):
|
|
64
|
+
proc.kill()
|
|
65
|
+
return
|
|
66
|
+
proc.kill()
|
|
59
67
|
|
|
60
68
|
|
|
61
69
|
class SandboxUnavailableError(RuntimeError):
|
|
@@ -39,6 +39,7 @@ from forgeo.backlog_issue_base import (
|
|
|
39
39
|
parse_numeric_issue_id,
|
|
40
40
|
parse_optional_datetime,
|
|
41
41
|
require_env_token,
|
|
42
|
+
task_engine_state,
|
|
42
43
|
)
|
|
43
44
|
from forgeo.models import ExecutionResult, GithubBacklogConfig, Task, TaskStatus
|
|
44
45
|
|
|
@@ -571,14 +572,8 @@ class GithubBacklog(IssueBacklogBase):
|
|
|
571
572
|
return task
|
|
572
573
|
|
|
573
574
|
async def create_task(self, task: Task) -> Task:
|
|
574
|
-
engine: dict[str, Any] = {
|
|
575
|
-
|
|
576
|
-
"acceptance_criteria": task.acceptance_criteria,
|
|
577
|
-
"dependencies": task.dependencies,
|
|
578
|
-
"files_to_modify": task.files_to_modify,
|
|
579
|
-
}
|
|
580
|
-
if task.review_required is not None:
|
|
581
|
-
engine["review_required"] = task.review_required
|
|
575
|
+
engine: dict[str, Any] = {"state": TaskStatus.OPEN.value}
|
|
576
|
+
engine.update(task_engine_state(task))
|
|
582
577
|
fields: dict[str, Any] = {
|
|
583
578
|
"title": task.title,
|
|
584
579
|
"body": embed_engine_state(task.description, engine),
|
|
@@ -590,7 +585,9 @@ class GithubBacklog(IssueBacklogBase):
|
|
|
590
585
|
if not isinstance(number, int):
|
|
591
586
|
raise GithubRequestError("GitHub create response did not contain an issue number")
|
|
592
587
|
issue_id = str(number)
|
|
593
|
-
|
|
588
|
+
# The full state (including the author-set per-task customization) is
|
|
589
|
+
# embedded in the body at creation; do not re-write the marker here,
|
|
590
|
+
# or a bare {"state": "OPEN"} would clobber those fields.
|
|
594
591
|
result = await self.get_task(issue_id)
|
|
595
592
|
if result is None:
|
|
596
593
|
raise GithubRequestError(f"Created GitHub issue {issue_id} could not be read back")
|
|
@@ -619,20 +616,7 @@ class GithubBacklog(IssueBacklogBase):
|
|
|
619
616
|
fields["title"] = candidate.title
|
|
620
617
|
if not ENGINE_STATE_FIELDS.isdisjoint(updates):
|
|
621
618
|
state = await self.get_engine_state(task_id)
|
|
622
|
-
state.update(
|
|
623
|
-
{
|
|
624
|
-
"acceptance_criteria": candidate.acceptance_criteria,
|
|
625
|
-
"dependencies": candidate.dependencies,
|
|
626
|
-
"files_to_modify": candidate.files_to_modify,
|
|
627
|
-
"agent_command": candidate.agent_command,
|
|
628
|
-
"agent_timeout_seconds": candidate.agent_timeout_seconds,
|
|
629
|
-
"run_at": candidate.run_at.isoformat() if candidate.run_at else None,
|
|
630
|
-
"retries_left": candidate.retries_left,
|
|
631
|
-
"review_required": candidate.review_required,
|
|
632
|
-
"review_branch": candidate.review_branch,
|
|
633
|
-
"review_commit_sha": candidate.review_commit_sha,
|
|
634
|
-
}
|
|
635
|
-
)
|
|
619
|
+
state.update(task_engine_state(candidate))
|
|
636
620
|
fields["body"] = embed_engine_state(candidate.description, state)
|
|
637
621
|
if fields:
|
|
638
622
|
await self._call(self.client.update_issue, number, fields)
|
|
@@ -39,6 +39,7 @@ from forgeo.backlog_issue_base import (
|
|
|
39
39
|
parse_numeric_issue_id,
|
|
40
40
|
parse_optional_datetime,
|
|
41
41
|
require_env_token,
|
|
42
|
+
task_engine_state,
|
|
42
43
|
)
|
|
43
44
|
from forgeo.models import ExecutionResult, GitlabBacklogConfig, Task, TaskStatus
|
|
44
45
|
|
|
@@ -550,14 +551,8 @@ class GitlabBacklog(IssueBacklogBase):
|
|
|
550
551
|
return task
|
|
551
552
|
|
|
552
553
|
async def create_task(self, task: Task) -> Task:
|
|
553
|
-
engine: dict[str, Any] = {
|
|
554
|
-
|
|
555
|
-
"acceptance_criteria": task.acceptance_criteria,
|
|
556
|
-
"dependencies": task.dependencies,
|
|
557
|
-
"files_to_modify": task.files_to_modify,
|
|
558
|
-
}
|
|
559
|
-
if task.review_required is not None:
|
|
560
|
-
engine["review_required"] = task.review_required
|
|
554
|
+
engine: dict[str, Any] = {"state": TaskStatus.OPEN.value}
|
|
555
|
+
engine.update(task_engine_state(task))
|
|
561
556
|
fields: dict[str, Any] = {
|
|
562
557
|
"title": task.title,
|
|
563
558
|
"description": embed_engine_state(task.description, engine),
|
|
@@ -569,7 +564,9 @@ class GitlabBacklog(IssueBacklogBase):
|
|
|
569
564
|
if not isinstance(iid, int):
|
|
570
565
|
raise GitlabRequestError("GitLab create response did not contain an issue iid")
|
|
571
566
|
issue_id = str(iid)
|
|
572
|
-
|
|
567
|
+
# The full state (including the author-set per-task customization) is
|
|
568
|
+
# embedded in the description at creation; do not re-write the marker
|
|
569
|
+
# here, or a bare {"state": "OPEN"} would clobber those fields.
|
|
573
570
|
result = await self.get_task(issue_id)
|
|
574
571
|
if result is None:
|
|
575
572
|
raise GitlabRequestError(f"Created GitLab issue {issue_id} could not be read back")
|
|
@@ -598,20 +595,7 @@ class GitlabBacklog(IssueBacklogBase):
|
|
|
598
595
|
fields["title"] = candidate.title
|
|
599
596
|
if not ENGINE_STATE_FIELDS.isdisjoint(updates):
|
|
600
597
|
state = await self.get_engine_state(task_id)
|
|
601
|
-
state.update(
|
|
602
|
-
{
|
|
603
|
-
"acceptance_criteria": candidate.acceptance_criteria,
|
|
604
|
-
"dependencies": candidate.dependencies,
|
|
605
|
-
"files_to_modify": candidate.files_to_modify,
|
|
606
|
-
"agent_command": candidate.agent_command,
|
|
607
|
-
"agent_timeout_seconds": candidate.agent_timeout_seconds,
|
|
608
|
-
"run_at": candidate.run_at.isoformat() if candidate.run_at else None,
|
|
609
|
-
"retries_left": candidate.retries_left,
|
|
610
|
-
"review_required": candidate.review_required,
|
|
611
|
-
"review_branch": candidate.review_branch,
|
|
612
|
-
"review_commit_sha": candidate.review_commit_sha,
|
|
613
|
-
}
|
|
614
|
-
)
|
|
598
|
+
state.update(task_engine_state(candidate))
|
|
615
599
|
fields["description"] = embed_engine_state(candidate.description, state)
|
|
616
600
|
if fields:
|
|
617
601
|
await self._call(self.client.update_issue, iid, fields)
|
|
@@ -300,6 +300,31 @@ ENGINE_STATE_FIELDS: frozenset[str] = frozenset(
|
|
|
300
300
|
)
|
|
301
301
|
|
|
302
302
|
|
|
303
|
+
def task_engine_state(task: Any) -> dict[str, Any]:
|
|
304
|
+
"""The author-controlled task fields stored in the hidden engine-state marker.
|
|
305
|
+
|
|
306
|
+
These are the per-task customization fields the human sets (as opposed to
|
|
307
|
+
engine-managed runtime state such as ``retry_count`` or ``agent_response``).
|
|
308
|
+
GitHub and GitLab issues expose no portable custom-field mechanism on their
|
|
309
|
+
REST issues API, so these fields travel in the hidden ``<!-- forgeo: ... -->``
|
|
310
|
+
marker; Jira maps them to custom fields instead. ``None`` values are kept
|
|
311
|
+
so a marker written by ``create_task`` and one refreshed by ``update_task``
|
|
312
|
+
stay identical, and absent values read back as ``None`` either way.
|
|
313
|
+
"""
|
|
314
|
+
return {
|
|
315
|
+
"acceptance_criteria": task.acceptance_criteria,
|
|
316
|
+
"dependencies": task.dependencies,
|
|
317
|
+
"files_to_modify": task.files_to_modify,
|
|
318
|
+
"agent_command": task.agent_command,
|
|
319
|
+
"agent_timeout_seconds": task.agent_timeout_seconds,
|
|
320
|
+
"run_at": task.run_at.isoformat() if task.run_at is not None else None,
|
|
321
|
+
"retries_left": task.retries_left,
|
|
322
|
+
"review_required": task.review_required,
|
|
323
|
+
"review_branch": task.review_branch,
|
|
324
|
+
"review_commit_sha": task.review_commit_sha,
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
|
|
303
328
|
# ------------------------------------------------------------------ #
|
|
304
329
|
# Shared HTTP / auth helpers #
|
|
305
330
|
# ------------------------------------------------------------------ #
|