@nyxa/nyx-agent 0.3.4 → 0.4.0

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.
@@ -48,10 +48,18 @@ Initial commands:
48
48
  nyxagent init
49
49
  nyxagent init --missing
50
50
  nyxagent run
51
+ nyxagent update
51
52
  ```
52
53
 
53
54
  `nyxagent init` is interactive by default and scriptable through flags.
54
55
 
56
+ `nyxagent update [version]` self-updates the global install. It resolves the
57
+ target from the npm registry (`latest` by default, or an explicit version /
58
+ dist-tag), guards against accidental downgrades when following a moving tag, and
59
+ reinstalls globally with the detected package manager (npm/pnpm/yarn/bun,
60
+ overridable with `--package-manager`). Use `--check` to report without
61
+ installing and `-y`/`--yes` to skip the confirmation prompt.
62
+
55
63
  If `.nyxagent/` already exists:
56
64
 
57
65
  - default behavior: refuse and explain
@@ -68,12 +76,17 @@ If `.nyxagent/` already exists:
68
76
  selection.md
69
77
  execution.md
70
78
  review.md
79
+ revision.md
71
80
  closure.md
81
+ pull-request.md
72
82
  repair-result.md
73
83
  schemas/
74
84
  selection.schema.json
75
85
  review.schema.json
86
+ closure.schema.json
87
+ pull-request.schema.json
76
88
  runs/
89
+ worktrees/ # created when [git].mode = "worktree"
77
90
  ```
78
91
 
79
92
  Configuration, prompts, schemas, and run artifacts are kept under
@@ -137,9 +150,6 @@ prompt = "prompts/execution.md"
137
150
  next = "review"
138
151
  max_visits_per_iteration = 3
139
152
 
140
- [phases.model]
141
- reasoning_level = "high"
142
-
143
153
  [[phases]]
144
154
  id = "review"
145
155
  prompt = "prompts/review.md"
@@ -147,9 +157,6 @@ output_schema = "schemas/review.schema.json"
147
157
  required_output = true
148
158
  max_visits_per_iteration = 3
149
159
 
150
- [phases.model]
151
- reasoning_level = "high"
152
-
153
160
  [phases.harness]
154
161
  args = [
155
162
  "exec",
@@ -166,10 +173,28 @@ changes_requested = "execution"
166
173
  [[phases]]
167
174
  id = "closure"
168
175
  prompt = "prompts/closure.md"
169
- next = "next_iteration"
176
+ output_schema = "schemas/closure.schema.json"
177
+ required_output = true
170
178
  max_visits_per_iteration = 1
179
+
180
+ [phases.harness]
181
+ args = [
182
+ "exec",
183
+ "--model", "{{model.name}}",
184
+ "-c", "model_reasoning_effort=\"{{model.reasoning_level}}\"",
185
+ "-c", "sandbox_workspace_write.network_access=true",
186
+ "-"
187
+ ]
188
+
189
+ [phases.transitions]
190
+ closed = "next_iteration"
191
+ failed = "stop_run"
171
192
  ```
172
193
 
194
+ For GitHub sources, init also adds `workflow.final_phase = "pull_request"`, a
195
+ `[git]` block, and a `pull_request` phase (see "Git Lifecycle and the PRD Pull
196
+ Request").
197
+
173
198
  ### Config Semantics
174
199
 
175
200
  - `workflow.max_iterations` is the maximum number of distinct confirmed work
@@ -178,11 +203,36 @@ max_visits_per_iteration = 1
178
203
  item.
179
204
  - `model.reasoning_level` is a harness-neutral string.
180
205
  - Harness args are declarative and may interpolate config/runtime variables.
181
- - Per-phase `model` and `harness` blocks override global values.
206
+ - Per-phase `model` and `harness` blocks override global values when explicitly
207
+ declared.
208
+ - The initial selection phase is declared in `[[phases]]`, but the runtime treats
209
+ `workflow.entry_phase` as the selection step and expects the selection result
210
+ contract.
182
211
  - `work_items` supports only `local` and `github` in v0.
183
212
  - `work_items.max_candidates` defaults to `50` and caps the inventory sent to
184
213
  the initial selection prompt.
185
214
  - `work_items.excerpt_chars` defaults to `800` and bounds candidate excerpts.
215
+ - `workflow.final_phase` (optional) names the **entry phase of a finalization
216
+ flow** the engine runs after the iteration loop completes normally. It is
217
+ symmetric to `entry_phase`: the engine starts at `final_phase` and follows each
218
+ phase's `next`/`transitions` until it reaches a leaf phase (no `next`/no
219
+ `transitions`, which ends finalization successfully) or `stop_run` (which aborts
220
+ the run, keeping the worktree for debugging). This lets the finalization be more
221
+ than one step — e.g. `review` → (`changes_requested`) `revision` → back to
222
+ `review` → (`approved`) terminal `pull_request`/`closure`. Review/revision loops
223
+ are bounded by each phase's `max_visits_per_iteration`. A single leaf phase (the
224
+ default `pull_request`) simply runs once, unchanged. The flow receives run-level
225
+ state (the completed work items, the run branch); `runState.final_result` holds
226
+ the last phase's structured result. `next_iteration`/`stop_iteration` are
227
+ iteration-only targets and are rejected inside the finalization flow. If unset,
228
+ nothing runs after the loop.
229
+ - `[git]` (optional) drives the engine's generic git lifecycle. `mode` is `off`
230
+ (default, unchanged behavior), `branch` (a new branch checked out in place), or
231
+ `worktree` (an isolated worktree per run). `base` defaults to the current
232
+ branch. `branch_template` interpolates `{{run_id}}`. `worktree_dir` is the
233
+ gitignored parent of run worktrees. `cleanup` is `always`, `on_success`
234
+ (default, kept on failure for debugging), or `never`. The branch is always
235
+ kept; only the worktree directory is removed.
186
236
  - At run start, the engine scans the configured provider, normalizes candidates
187
237
  into `available_work_items`, and sends that inventory to the selection phase.
188
238
  - The selection phase returns an ordered recommended `work_items` queue.
@@ -207,6 +257,51 @@ max_visits_per_iteration = 1
207
257
  and may include optional `selection_groups` for user review. Groups may cover
208
258
  the full available inventory, not only the recommended queue.
209
259
 
260
+ ### Phase Capabilities (network and permissions)
261
+
262
+ The engine stays agnostic: it only runs `command + args` per phase. The
263
+ capability of each phase — read-only, write, or write-with-network — is encoded
264
+ in the harness args that `nyxagent init` generates per preset:
265
+
266
+ | Capability | Phases | Codex args | Claude args |
267
+ | --- | --- | --- | --- |
268
+ | read-only | selection, review, global_review, finalize | `--sandbox read-only` | `-p --permission-mode plan` |
269
+ | write | execution, revision, global_revision | workspace-write (no extra flag) | `-p --dangerously-skip-permissions` |
270
+ | write + network | closure, pull_request | `-c sandbox_workspace_write.network_access=true` | `-p --dangerously-skip-permissions` |
271
+
272
+ Why the network capability matters: codex's default `workspace-write` sandbox
273
+ keeps outbound network **off**. A `git commit` is local and succeeds, but
274
+ `gh issue close` is a GitHub API call and fails silently. That is why issues
275
+ were committed but never closed. The `write_network` capability re-enables the
276
+ network only for the phases that need it (closure, pull request). Claude has no
277
+ network sandbox; its blocker was the missing `-p` (headless) and permission
278
+ flags, which `write`/`write_network` now provide.
279
+
280
+ Users can adjust any phase's posture by editing its `[phases.harness] args`.
281
+
282
+ ### Git Lifecycle and the PRD Pull Request
283
+
284
+ When `[git].mode` is `worktree` (the default for GitHub init), the engine, once
285
+ per run:
286
+
287
+ 1. creates one branch for the whole run (the PRD) from `base`, in an isolated
288
+ worktree under `worktree_dir`;
289
+ 2. runs every iteration (execution → review → revision → closure) with the
290
+ harness `cwd` set to that worktree, while `.nyxagent` artifacts stay under the
291
+ project root;
292
+ 3. after the loop, runs the `workflow.final_phase` finalization flow in the
293
+ worktree (a single `pull_request` phase by default, or a multi-step flow such
294
+ as the generated global review: `global_review` → (`changes_requested`)
295
+ `global_revision` → back to `global_review` → (`approved`) `pull_request`) to
296
+ push the branch and open a single pull request;
297
+ 4. removes the worktree per `cleanup`, keeping the branch.
298
+
299
+ Per-work-item closure still closes each GitHub issue (`gh issue close`). The
300
+ pull request is the PRD-level deliverable; it is opened once, at the end, and is
301
+ GitHub-specific. The engine performs only generic git plumbing (branch, worktree,
302
+ cwd); all GitHub semantics live in the `closure.md` and `pull-request.md`
303
+ prompts, so the engine stays harness- and tracker-agnostic.
304
+
210
305
  ## Workflow Model
211
306
 
212
307
  The workflow is phase based.
@@ -440,13 +535,21 @@ At run start, if the project is a Git repository, the engine records:
440
535
  The engine does not commit. The default workflow reserves commits for the
441
536
  `closure` prompt after review approval.
442
537
 
538
+ When `[git].mode` is `branch` or `worktree`, the engine additionally manages a
539
+ run-scoped branch (and, for `worktree`, an isolated working directory) as
540
+ described under "Git Lifecycle and the PRD Pull Request". This is opt-in and off
541
+ by default; it performs only generic git plumbing, never GitHub actions.
542
+
443
543
  Default phase policy:
444
544
 
445
545
  - `selection`: read-only behavior by prompt/harness
446
546
  - `execution`: may modify code and run tests, must not commit or close work
447
547
  items
448
548
  - `review`: read-only behavior by prompt/harness
449
- - `closure`: may commit and close or mark done according to project prompt
549
+ - `closure`: may commit and close or mark done according to project prompt;
550
+ runs with the network capability so `gh issue close` works
551
+ - `pull_request` (final phase, GitHub PRD only): pushes the run branch and opens
552
+ one pull request; runs with the network capability
450
553
 
451
554
  ## Default Prompt Policy
452
555
 
@@ -490,6 +593,35 @@ Closure:
490
593
  - inspect final diff/status
491
594
  - commit when appropriate
492
595
  - close or mark done according to work item source and project conventions
596
+ - for GitHub, close the issue explicitly with `gh issue close <number> --repo
597
+ <owner/repo>` (needs the network capability)
598
+ - return a structured `closed` / `failed` outcome so a failed close stops the run
599
+ instead of silently marking the item done
600
+
601
+ Pull request (final phase, GitHub PRD only):
602
+
603
+ - run once after the iteration loop, on the run branch in the worktree
604
+ - push the branch and open one pull request referencing the addressed issues
605
+ - return `pr_opened` with `pr_url`, or `failed` with what remains
606
+
607
+ Global review (finalization, optional):
608
+
609
+ - stay read-only; review the whole run/PRD, not a single item
610
+ - focus on cross-cutting concerns a per-task review cannot see (integration,
611
+ regressions between items, overall design, gaps vs intent)
612
+ - inspect the combined diff (`git diff {{git.base}}...HEAD` when a branch exists)
613
+ - return `approved` or `changes_requested` (with `required_changes`)
614
+
615
+ Global revision (finalization, optional):
616
+
617
+ - apply `phase_results.global_review.required_changes` across affected items
618
+ - **commit** the corrections (no closure phase follows it), so they land in the
619
+ run branch / pull request
620
+
621
+ Finalize (finalization terminal leaf, when there is no pull request):
622
+
623
+ - read-only; make no changes — all work is already implemented, committed, closed
624
+ - summarize the completed work items and confirm the run is complete
493
625
 
494
626
  ## Init Modes
495
627
 
@@ -498,11 +630,34 @@ Closure:
498
630
  - harness preset: `codex`, `claude`, or custom
499
631
  - model name
500
632
  - reasoning level
633
+ - review strategy (see below)
501
634
  - max iterations
502
635
  - work item source template: `local` or `github`
503
636
 
637
+ The **review strategy** is a single choice (`--review-mode each|all|both|none`,
638
+ default `each`; the legacy `--review`/`--no-review` flags map to `each`/`none`):
639
+
640
+ - `each` — review + correction after **each** task, inside the iteration loop
641
+ (`execution → review → revision → closure`). This is the default and the
642
+ previous behavior.
643
+ - `all` — a single **global review + correction** of the whole run after the
644
+ loop, as a finalization sub-graph: `global_review` → (`changes_requested`)
645
+ `global_revision` → back to `global_review` → (`approved`) terminal. The
646
+ terminal is the `pull_request` phase for GitHub (with PR), otherwise a
647
+ read-only `finalize` leaf (local, or `--no-pull-request`). The loop is bounded
648
+ by `max_visits_per_iteration = 3`; exhausting it fails the run.
649
+ - `both` — per-task review **and** the final global review.
650
+ - `none` — no review at all (`execution → closure`).
651
+
504
652
  For `local`, init asks for a work item path.
505
- For `github`, init asks for a repository in `owner/repo` format.
653
+ For `github`, init asks for a repository in `owner/repo` format and, by default,
654
+ wires the pull request finalization flow (`--no-pull-request` to skip). This adds
655
+ a `[git] mode = "worktree"` block, a `pull_request` phase, and
656
+ `workflow.final_phase = "pull_request"`. When a global review is enabled it
657
+ becomes the finalization entry (`workflow.final_phase = "global_review"`) and the
658
+ pull request becomes its approved terminal. Pull requests are GitHub-specific, so
659
+ `local` sources never get a `pull_request` phase; their global review approves
660
+ into the `finalize` leaf instead.
506
661
 
507
662
  Default path selection:
508
663
 
@@ -512,6 +667,34 @@ Default path selection:
512
667
  If the chosen local work item path does not exist, init creates the directory but
513
668
  does not create sample work items.
514
669
 
670
+ ### Upgrading an existing config
671
+
672
+ `nyxagent init --missing` only adds missing template files; it does not rewrite an
673
+ existing `config.toml`. To fix issue closing on a config generated before the
674
+ network capability existed, add a network override to the `closure` phase:
675
+
676
+ ```toml
677
+ [[phases]]
678
+ id = "closure"
679
+ # ... existing keys ...
680
+
681
+ [phases.harness]
682
+ # codex:
683
+ args = [
684
+ "exec",
685
+ "--model", "{{model.name}}",
686
+ "-c", "model_reasoning_effort=\"{{model.reasoning_level}}\"",
687
+ "-c", "sandbox_workspace_write.network_access=true",
688
+ "-"
689
+ ]
690
+ # claude:
691
+ # args = ["-p", "--model", "{{model.name}}", "--output-format", "text", "--dangerously-skip-permissions"]
692
+ ```
693
+
694
+ To adopt the pull request flow, add a `[git]` block, a `pull_request` phase, and
695
+ `workflow.final_phase = "pull_request"` (see the sections above), or regenerate
696
+ the config with `nyxagent init`.
697
+
515
698
  ## Implementation Stack
516
699
 
517
700
  Recommended TypeScript stack:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nyxa/nyx-agent",
3
- "version": "0.3.4",
3
+ "version": "0.4.0",
4
4
  "description": "A lightweight phase orchestrator for repeatedly launching coding agents with fresh context.",
5
5
  "type": "module",
6
6
  "repository": {
@@ -1,11 +1,25 @@
1
- Close the selected work item after review approval.
1
+ Close the selected work item after the review approved it.
2
2
 
3
- Before committing or closing anything, inspect the current diff and status.
4
- Confirm the review phase approved the work. Run a lightweight final validation
5
- when it is cheap and relevant.
3
+ Before committing or closing anything, inspect the current diff and `git status`.
4
+ Confirm the review phase approved the work. Run a lightweight final validation when
5
+ it is cheap and relevant.
6
6
 
7
- Commit only the selected work item changes, using the project conventions for
8
- commit messages. Then close or mark the work item done according to its source
9
- and the project conventions.
7
+ Commit only the selected work item's changes, using the project's commit message
8
+ conventions.
10
9
 
11
- If closure cannot be completed safely, stop and explain what remains.
10
+ Then close the work item according to its source (see `work_item` in the runtime
11
+ state):
12
+
13
+ - GitHub issue (`work_item.source.type` is `"github"`, locator is `owner/repo#<number>`):
14
+ run `gh issue close <number> --repo <owner/repo>`. Add `--comment "<short note>"`
15
+ when it helps. This step needs network access; the closure phase is configured for it.
16
+ - Local file (`work_item.source.type` is `"local"`): mark it done following the
17
+ project's convention for finished work.
18
+
19
+ Report the outcome in the structured result:
20
+
21
+ - `closed` when the commit succeeded and the issue/work item was actually closed.
22
+ Set `committed` and `issue_closed` to reflect what truly happened.
23
+ - `failed` when something blocked closure (e.g. `gh issue close` errored). Explain what
24
+ remains in `summary`. Do not pretend the item is closed when it is not — a `failed`
25
+ outcome stops the run so the problem is visible.
@@ -0,0 +1,7 @@
1
+ The global review approved the run. Finalize without making any further changes.
2
+
3
+ Stay read-only. Do not modify project files; all work is already implemented,
4
+ committed, and closed.
5
+
6
+ Summarize the completed work items (`completed_work_item_keys` and
7
+ `selected_work_item_queue` in the runtime state) and confirm the run is complete.
@@ -0,0 +1,24 @@
1
+ Review the entire run (the whole PRD) now that every selected work item has been
2
+ implemented and closed.
3
+
4
+ Stay read-only. Do not modify project files.
5
+
6
+ Assess the work as a whole, focusing on cross-cutting concerns a per-task review
7
+ cannot see:
8
+
9
+ - coherence and integration across all completed work items
10
+ - regressions or conflicts introduced between items
11
+ - overall architecture and design consistency
12
+ - gaps against the original intent of the selected work items
13
+ - security or data-safety concerns spanning the changes
14
+
15
+ Inspect the combined set of changes. When a run branch is configured, review the
16
+ full diff (e.g. `git diff {{git.base}}...HEAD`). The completed items are in
17
+ `completed_work_item_keys` and `selected_work_item_queue` in the runtime state.
18
+
19
+ Return one of these outcomes:
20
+
21
+ - `approved`: the run as a whole is ready to finalize
22
+ - `changes_requested`: include concrete `required_changes` for the correction phase
23
+
24
+ Keep the review concise and actionable.
@@ -0,0 +1,9 @@
1
+ Apply the changes requested by the global review of the whole run.
2
+
3
+ Address the required changes recorded under `phase_results.global_review` in the
4
+ current state. Keep changes focused on exactly what the global review asked for,
5
+ across whichever work items are affected.
6
+
7
+ Commit your corrections using the project's commit message conventions, so they are
8
+ included in the run branch and the pull request. Do not reopen or re-close work
9
+ items; they are already closed by the closure phase.
@@ -0,0 +1,23 @@
1
+ Open a single pull request for all the work completed in this run (the PRD).
2
+
3
+ You are on the run branch `{{git.branch}}`, based on `{{git.base}}`. When a worktree is
4
+ configured, your working directory is the isolated worktree. All selected work items are
5
+ already implemented, committed, and their GitHub issues are already closed by the
6
+ closure phase. Your job is only to publish the branch and open the PR.
7
+
8
+ Steps:
9
+
10
+ 1. Confirm the branch has commits ahead of the base: `git log --oneline {{git.base}}..HEAD`.
11
+ 2. Push the branch: `git push -u origin {{git.branch}}`.
12
+ 3. Open the pull request with gh:
13
+ `gh pr create --base {{git.base}} --head {{git.branch}} --title "<concise PRD title>" --body "<summary>"`.
14
+ Derive the title and body from the completed work items in the runtime state
15
+ (`selected_work_item_queue` / `completed_work_item_keys`). Reference the issues that
16
+ were addressed (e.g. "#12, #34") in the body so the PR documents the PRD.
17
+
18
+ This phase needs network access; it is configured for it.
19
+
20
+ Report the outcome in the structured result:
21
+
22
+ - `pr_opened` with `pr_url` set to the created pull request URL.
23
+ - `failed` with `summary` describing what blocked the PR (e.g. push rejected, no remote).
@@ -0,0 +1,7 @@
1
+ Apply the changes requested by the review for the selected work item.
2
+
3
+ Address the required changes recorded under `phase_results.review` in the current
4
+ state. Keep changes focused on exactly what the review asked for.
5
+
6
+ Do not commit. Do not close or mark the work item done. Leave clear validation
7
+ evidence in your final response.
@@ -0,0 +1,35 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "type": "object",
4
+ "required": ["outcome"],
5
+ "properties": {
6
+ "outcome": {
7
+ "type": "string",
8
+ "enum": ["closed", "failed"]
9
+ },
10
+ "committed": {
11
+ "type": "boolean"
12
+ },
13
+ "issue_closed": {
14
+ "type": "boolean"
15
+ },
16
+ "summary": {
17
+ "type": "string"
18
+ }
19
+ },
20
+ "allOf": [
21
+ {
22
+ "if": {
23
+ "properties": {
24
+ "outcome": {
25
+ "const": "failed"
26
+ }
27
+ }
28
+ },
29
+ "then": {
30
+ "required": ["summary"]
31
+ }
32
+ }
33
+ ],
34
+ "additionalProperties": true
35
+ }
@@ -0,0 +1,60 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "type": "object",
4
+ "required": ["outcome", "approved", "summary"],
5
+ "properties": {
6
+ "outcome": {
7
+ "type": "string",
8
+ "enum": ["approved", "changes_requested"]
9
+ },
10
+ "approved": {
11
+ "type": "boolean"
12
+ },
13
+ "summary": {
14
+ "type": "string",
15
+ "minLength": 1
16
+ },
17
+ "required_changes": {
18
+ "type": "array",
19
+ "items": {
20
+ "type": "string"
21
+ }
22
+ }
23
+ },
24
+ "allOf": [
25
+ {
26
+ "if": {
27
+ "properties": {
28
+ "outcome": {
29
+ "const": "approved"
30
+ }
31
+ }
32
+ },
33
+ "then": {
34
+ "properties": {
35
+ "approved": {
36
+ "const": true
37
+ }
38
+ }
39
+ }
40
+ },
41
+ {
42
+ "if": {
43
+ "properties": {
44
+ "outcome": {
45
+ "const": "changes_requested"
46
+ }
47
+ }
48
+ },
49
+ "then": {
50
+ "properties": {
51
+ "approved": {
52
+ "const": false
53
+ }
54
+ },
55
+ "required": ["required_changes"]
56
+ }
57
+ }
58
+ ],
59
+ "additionalProperties": true
60
+ }
@@ -0,0 +1,44 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "type": "object",
4
+ "required": ["outcome"],
5
+ "properties": {
6
+ "outcome": {
7
+ "type": "string",
8
+ "enum": ["pr_opened", "failed"]
9
+ },
10
+ "pr_url": {
11
+ "type": "string"
12
+ },
13
+ "summary": {
14
+ "type": "string"
15
+ }
16
+ },
17
+ "allOf": [
18
+ {
19
+ "if": {
20
+ "properties": {
21
+ "outcome": {
22
+ "const": "pr_opened"
23
+ }
24
+ }
25
+ },
26
+ "then": {
27
+ "required": ["pr_url"]
28
+ }
29
+ },
30
+ {
31
+ "if": {
32
+ "properties": {
33
+ "outcome": {
34
+ "const": "failed"
35
+ }
36
+ }
37
+ },
38
+ "then": {
39
+ "required": ["summary"]
40
+ }
41
+ }
42
+ ],
43
+ "additionalProperties": true
44
+ }