@nyxa/nyx-agent 0.3.5 → 0.4.1

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
@@ -160,10 +173,28 @@ changes_requested = "execution"
160
173
  [[phases]]
161
174
  id = "closure"
162
175
  prompt = "prompts/closure.md"
163
- next = "next_iteration"
176
+ output_schema = "schemas/closure.schema.json"
177
+ required_output = true
164
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"
165
192
  ```
166
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
+
167
198
  ### Config Semantics
168
199
 
169
200
  - `workflow.max_iterations` is the maximum number of distinct confirmed work
@@ -181,6 +212,27 @@ max_visits_per_iteration = 1
181
212
  - `work_items.max_candidates` defaults to `50` and caps the inventory sent to
182
213
  the initial selection prompt.
183
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.
184
236
  - At run start, the engine scans the configured provider, normalizes candidates
185
237
  into `available_work_items`, and sends that inventory to the selection phase.
186
238
  - The selection phase returns an ordered recommended `work_items` queue.
@@ -205,6 +257,51 @@ max_visits_per_iteration = 1
205
257
  and may include optional `selection_groups` for user review. Groups may cover
206
258
  the full available inventory, not only the recommended queue.
207
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
+
208
305
  ## Workflow Model
209
306
 
210
307
  The workflow is phase based.
@@ -438,13 +535,21 @@ At run start, if the project is a Git repository, the engine records:
438
535
  The engine does not commit. The default workflow reserves commits for the
439
536
  `closure` prompt after review approval.
440
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
+
441
543
  Default phase policy:
442
544
 
443
545
  - `selection`: read-only behavior by prompt/harness
444
546
  - `execution`: may modify code and run tests, must not commit or close work
445
547
  items
446
548
  - `review`: read-only behavior by prompt/harness
447
- - `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
448
553
 
449
554
  ## Default Prompt Policy
450
555
 
@@ -488,6 +593,35 @@ Closure:
488
593
  - inspect final diff/status
489
594
  - commit when appropriate
490
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
491
625
 
492
626
  ## Init Modes
493
627
 
@@ -496,11 +630,34 @@ Closure:
496
630
  - harness preset: `codex`, `claude`, or custom
497
631
  - model name
498
632
  - reasoning level
633
+ - review strategy (see below)
499
634
  - max iterations
500
635
  - work item source template: `local` or `github`
501
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
+
502
652
  For `local`, init asks for a work item path.
503
- 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.
504
661
 
505
662
  Default path selection:
506
663
 
@@ -510,6 +667,34 @@ Default path selection:
510
667
  If the chosen local work item path does not exist, init creates the directory but
511
668
  does not create sample work items.
512
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
+
513
698
  ## Implementation Stack
514
699
 
515
700
  Recommended TypeScript stack:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nyxa/nyx-agent",
3
- "version": "0.3.5",
3
+ "version": "0.4.1",
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,30 @@
1
- Close the selected work item after review approval.
1
+ Close the selected work item now that its implementation is complete.
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
+ If a per-task review ran this iteration (`phase_results.review` in the runtime
5
+ state), it already approved the work — the workflow only routes to closure after an
6
+ approval, so there is no need to re-confirm it. If no review phase ran (the workflow
7
+ may use a global end-of-run review, or no review at all), run a lightweight final
8
+ validation yourself when it is cheap and relevant. A missing review phase is expected
9
+ in those modes and is not a reason to block closure.
6
10
 
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.
11
+ Commit only the selected work item's changes, using the project's commit message
12
+ conventions.
10
13
 
11
- If closure cannot be completed safely, stop and explain what remains.
14
+ Then close the work item according to its source (see `work_item` in the runtime
15
+ state):
16
+
17
+ - GitHub issue (`work_item.source.type` is `"github"`, locator is `owner/repo#<number>`):
18
+ run `gh issue close <number> --repo <owner/repo>`. Add `--comment "<short note>"`
19
+ when it helps. This step needs network access; the closure phase is configured for it.
20
+ - Local file (`work_item.source.type` is `"local"`): mark it done following the
21
+ project's convention for finished work.
22
+
23
+ Report the outcome in the structured result:
24
+
25
+ - `closed` when the commit succeeded and the issue/work item was actually closed.
26
+ Set `committed` and `issue_closed` to reflect what truly happened.
27
+ - `failed` only when something genuinely blocked closure (e.g. `gh issue close`
28
+ errored, or the diff does not apply). Explain what remains in `summary`. Do not
29
+ report `failed` merely because no review phase ran. Do not pretend the item is
30
+ closed when it is not — a `failed` 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
+ }