@nyxa/nyx-agent 0.1.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.
@@ -0,0 +1,488 @@
1
+ # NyxAgent v0 Spec
2
+
3
+ ## Purpose
4
+
5
+ NyxAgent is a lightweight orchestration CLI for repeatedly launching coding
6
+ agents against work items without carrying long-lived agent context across
7
+ tasks or phases.
8
+
9
+ The tool is primarily built for personal use, but the v0 architecture should be
10
+ simple, explicit, and configurable enough for other repositories and harnesses.
11
+
12
+ NyxAgent is an orchestrator. It does not own project-specific development,
13
+ review, tracking, or closure policy. Those policies live in prompts and project
14
+ configuration.
15
+
16
+ ## Goals
17
+
18
+ - Install a `.nyxagent/` folder into a project with sensible templates.
19
+ - Run a configurable phase workflow for up to `max_iterations` work items.
20
+ - Launch a fresh harness process for each phase.
21
+ - Keep workflow structure generic through phase transitions and outcomes.
22
+ - Keep prompts focused on agent behavior, not runtime mechanics.
23
+ - Capture complete run artifacts for audit and debugging.
24
+ - Support Codex and Claude-style harnesses through configurable commands.
25
+ - Support structured phase results through a common XML result contract.
26
+
27
+ ## Non-Goals For v0
28
+
29
+ - No built-in GitHub, Jira, Linear, or local task tracker adapter.
30
+ - No native Git commit or issue close logic in the engine.
31
+ - No resume command.
32
+ - No fully general DAG/workflow engine.
33
+ - No local `loop.ts` copied into `.nyxagent/`.
34
+ - No automatic mutation of work item files by the engine.
35
+
36
+ ## CLI
37
+
38
+ Package and binary naming:
39
+
40
+ - npm package: `nyx-agent` or `@nyxa/nyx-agent`
41
+ - CLI binary: `nyxagent`
42
+ - project directory: `.nyxagent`
43
+
44
+ Initial commands:
45
+
46
+ ```bash
47
+ nyxagent init
48
+ nyxagent init --missing
49
+ nyxagent run
50
+ ```
51
+
52
+ `nyxagent init` is interactive by default and scriptable through flags.
53
+
54
+ If `.nyxagent/` already exists:
55
+
56
+ - default behavior: refuse and explain
57
+ - `--missing`: add only missing template files
58
+ - no `--force` in v0
59
+
60
+ ## Installed Project Layout
61
+
62
+ ```text
63
+ .nyxagent/
64
+ config.toml
65
+ prompts/
66
+ selection.md
67
+ execution.md
68
+ review.md
69
+ closure.md
70
+ repair-result.md
71
+ schemas/
72
+ selection.schema.json
73
+ review.schema.json
74
+ runs/
75
+ ```
76
+
77
+ Configuration, prompts, schemas, and run artifacts are kept under
78
+ `.nyxagent/`.
79
+
80
+ Work items may live outside `.nyxagent/` when configured through
81
+ `[work_items]`.
82
+
83
+ ## Configuration
84
+
85
+ Primary config format: TOML.
86
+
87
+ Secrets are not stored in `config.toml`. v0 does not require `.env`.
88
+
89
+ Example:
90
+
91
+ ```toml
92
+ [workflow]
93
+ entry_phase = "selection"
94
+ max_iterations = 5
95
+
96
+ [model]
97
+ name = "gpt-5-codex"
98
+ reasoning_level = "medium"
99
+
100
+ [harness]
101
+ preset = "codex"
102
+ command = "codex"
103
+ args = [
104
+ "exec",
105
+ "--model", "{{model.name}}",
106
+ "-c", "model_reasoning_effort=\"{{model.reasoning_level}}\"",
107
+ "-"
108
+ ]
109
+ prompt_input = "stdin"
110
+
111
+ [repair]
112
+ max_attempts = 1
113
+ prompt = "prompts/repair-result.md"
114
+
115
+ [work_items]
116
+ source = "local-markdown"
117
+ path = "issues"
118
+
119
+ [[phases]]
120
+ id = "selection"
121
+ prompt = "prompts/selection.md"
122
+ output_schema = "schemas/selection.schema.json"
123
+ required_output = true
124
+ max_visits_per_iteration = 1
125
+
126
+ [phases.transitions]
127
+ selected = "execution"
128
+ no_work = "stop_run"
129
+
130
+ [[phases]]
131
+ id = "execution"
132
+ prompt = "prompts/execution.md"
133
+ next = "review"
134
+ max_visits_per_iteration = 3
135
+
136
+ [phases.model]
137
+ reasoning_level = "high"
138
+
139
+ [[phases]]
140
+ id = "review"
141
+ prompt = "prompts/review.md"
142
+ output_schema = "schemas/review.schema.json"
143
+ required_output = true
144
+ max_visits_per_iteration = 3
145
+
146
+ [phases.model]
147
+ reasoning_level = "high"
148
+
149
+ [phases.harness]
150
+ args = [
151
+ "exec",
152
+ "--model", "{{model.name}}",
153
+ "-c", "model_reasoning_effort=\"{{model.reasoning_level}}\"",
154
+ "--sandbox", "read-only",
155
+ "-"
156
+ ]
157
+
158
+ [phases.transitions]
159
+ approved = "closure"
160
+ changes_requested = "execution"
161
+
162
+ [[phases]]
163
+ id = "closure"
164
+ prompt = "prompts/closure.md"
165
+ next = "next_iteration"
166
+ max_visits_per_iteration = 1
167
+ ```
168
+
169
+ ### Config Semantics
170
+
171
+ - `workflow.max_iterations` is the maximum number of distinct work items in a
172
+ run.
173
+ - `phases[*].max_visits_per_iteration` prevents infinite loops inside one work
174
+ item.
175
+ - `model.reasoning_level` is a harness-neutral string.
176
+ - Harness args are declarative and may interpolate config/runtime variables.
177
+ - Per-phase `model` and `harness` blocks override global values.
178
+ - `work_items` is informative in v0. It is injected into runtime context and
179
+ used by prompts, not scanned by the engine.
180
+
181
+ ## Workflow Model
182
+
183
+ The workflow is phase based.
184
+
185
+ Each phase has either:
186
+
187
+ - `next`: a static next target
188
+ - `transitions`: a map from result `outcome` to next target
189
+
190
+ Reserved next targets:
191
+
192
+ - `stop_run`
193
+ - `stop_iteration`
194
+ - `next_iteration`
195
+
196
+ The engine does not know about development, review, approval, or closure. It
197
+ only knows phases, outcomes, transitions, and visit limits.
198
+
199
+ The default template expresses the standard loop:
200
+
201
+ ```text
202
+ selection -> execution -> review
203
+ review.approved -> closure -> next_iteration
204
+ review.changes_requested -> execution
205
+ selection.no_work -> stop_run
206
+ ```
207
+
208
+ ## Structured Result Contract
209
+
210
+ Agents do not write result JSON files directly.
211
+
212
+ When a phase requires structured output, the harness must return a JSON object
213
+ inside the last `<nyxagent_result>` XML block in stdout:
214
+
215
+ ```xml
216
+ <nyxagent_result>
217
+ {
218
+ "outcome": "approved",
219
+ "approved": true,
220
+ "summary": "Implementation matches the task and tests pass."
221
+ }
222
+ </nyxagent_result>
223
+ ```
224
+
225
+ Engine behavior:
226
+
227
+ 1. Capture stdout and stderr.
228
+ 2. Extract the last `<nyxagent_result>...</nyxagent_result>` block.
229
+ 3. Parse the block as JSON.
230
+ 4. Validate it against `output_schema` when configured.
231
+ 5. Write the validated object to `result.json`.
232
+ 6. Merge relevant result data into iteration state.
233
+
234
+ If a phase declares `transitions`, its structured result must contain
235
+ `outcome`.
236
+
237
+ If multiple result blocks exist, the last block wins.
238
+
239
+ ## Repair
240
+
241
+ Repair is only for malformed structured results.
242
+
243
+ If the harness exits with code `0` but the result block is missing, invalid
244
+ JSON, or schema-invalid, NyxAgent launches a repair phase.
245
+
246
+ The repair prompt receives:
247
+
248
+ - rendered original prompt
249
+ - stdout and stderr from the failed attempt
250
+ - expected schema
251
+ - validation or parsing error
252
+
253
+ The repair agent must return only a valid `<nyxagent_result>` block. It must not
254
+ redo the development or mutate the project.
255
+
256
+ If the harness exits non-zero, this is a phase failure, not a result repair.
257
+ Phase retry behavior can be added later.
258
+
259
+ ## Runtime Prompt
260
+
261
+ NyxAgent renders a final prompt for each phase by prepending a runtime contract
262
+ to the user prompt.
263
+
264
+ The runtime contract includes:
265
+
266
+ - project root
267
+ - run directory
268
+ - iteration directory
269
+ - phase directory
270
+ - current state file
271
+ - phase id
272
+ - configured transitions
273
+ - required structured output contract
274
+ - work item context, when selected
275
+ - work item config from `[work_items]`
276
+
277
+ The user prompt remains focused on domain behavior.
278
+
279
+ Prompts may use simple interpolation:
280
+
281
+ ```text
282
+ {{project_root}}
283
+ {{run_dir}}
284
+ {{iteration_dir}}
285
+ {{phase_dir}}
286
+ {{state_file}}
287
+ {{work_item.key}}
288
+ {{work_item.title}}
289
+ {{model.name}}
290
+ {{model.reasoning_level}}
291
+ ```
292
+
293
+ The template language is intentionally small: dotted path lookup only.
294
+
295
+ ## Artifacts
296
+
297
+ Each run creates a timestamped directory:
298
+
299
+ ```text
300
+ .nyxagent/runs/2026-05-23T12-30-00/
301
+ run.json
302
+ state.json
303
+ iterations/
304
+ 001/
305
+ state.json
306
+ phases/
307
+ selection/
308
+ attempt-001/
309
+ prompt.md
310
+ stdout.log
311
+ stderr.log
312
+ meta.json
313
+ result.json
314
+ execution/
315
+ attempt-001/
316
+ prompt.md
317
+ stdout.log
318
+ stderr.log
319
+ meta.json
320
+ result.json
321
+ review/
322
+ attempt-001/
323
+ prompt.md
324
+ stdout.log
325
+ stderr.log
326
+ meta.json
327
+ repair-001/
328
+ prompt.md
329
+ stdout.log
330
+ stderr.log
331
+ meta.json
332
+ result.json
333
+ ```
334
+
335
+ `run.json` records immutable run metadata:
336
+
337
+ - run id
338
+ - project root
339
+ - started at
340
+ - config path
341
+ - harness preset and command
342
+ - initial Git snapshot when available
343
+
344
+ `run/state.json` records global current state:
345
+
346
+ - run status
347
+ - current iteration
348
+ - completed iterations
349
+ - seen work item keys
350
+
351
+ `iterations/NNN/state.json` records per-work-item state:
352
+
353
+ - iteration number
354
+ - work item
355
+ - phase results
356
+ - phase visit counts
357
+ - current phase status
358
+
359
+ `meta.json` for each attempt records:
360
+
361
+ - rendered command with secrets redacted
362
+ - start and end timestamps
363
+ - duration
364
+ - exit code
365
+ - parse/schema errors when present
366
+ - Git status before and after phase when available
367
+
368
+ ## Git Behavior
369
+
370
+ NyxAgent does not require a clean worktree in v0.
371
+
372
+ At run start, if the project is a Git repository, the engine records:
373
+
374
+ - branch
375
+ - HEAD commit
376
+ - `git status --short`
377
+
378
+ The engine does not commit. The default workflow reserves commits for the
379
+ `closure` prompt after review approval.
380
+
381
+ Default phase policy:
382
+
383
+ - `selection`: read-only behavior by prompt/harness
384
+ - `execution`: may modify code and run tests, must not commit or close work
385
+ items
386
+ - `review`: read-only behavior by prompt/harness
387
+ - `closure`: may commit and close or mark done according to project prompt
388
+
389
+ ## Default Prompt Policy
390
+
391
+ Default prompts should be concise but operational.
392
+
393
+ Selection:
394
+
395
+ - inspect configured work item source
396
+ - choose one work item
397
+ - avoid keys already present in `seen_work_item_keys`
398
+ - return `selected` or `no_work`
399
+
400
+ Execution:
401
+
402
+ - work only on the selected item
403
+ - use a red-green-refactor style when practical
404
+ - run targeted validation
405
+ - do not commit
406
+ - do not close the work item
407
+
408
+ Review:
409
+
410
+ - stay read-only
411
+ - check alignment with selected work item
412
+ - check tests and validation evidence
413
+ - check architecture/design fit
414
+ - check obvious security or data safety concerns
415
+ - return `approved` or `changes_requested`
416
+
417
+ Closure:
418
+
419
+ - run only after approval
420
+ - inspect final diff/status
421
+ - commit when appropriate
422
+ - close or mark done according to work item source and project conventions
423
+
424
+ ## Init Modes
425
+
426
+ `nyxagent init` asks for:
427
+
428
+ - harness preset: `codex`, `claude`, or custom
429
+ - model name
430
+ - reasoning level
431
+ - max iterations
432
+ - work item source template: `local-markdown`, `github`, or `custom`
433
+
434
+ For `local-markdown`, init asks for a task path.
435
+
436
+ Default path selection:
437
+
438
+ - use `issues/` if it exists
439
+ - otherwise suggest `.nyxagent/tasks/`
440
+
441
+ If the chosen local task path does not exist, init may create it and add a
442
+ sample task.
443
+
444
+ ## Implementation Stack
445
+
446
+ Recommended TypeScript stack:
447
+
448
+ - Node ESM
449
+ - `commander` for CLI commands
450
+ - `@inquirer/prompts` for interactive init
451
+ - `smol-toml` for TOML parsing/writing
452
+ - `zod` for internal config validation
453
+ - `ajv` for JSON Schema validation
454
+ - `execa` for process execution
455
+ - `tsx` for development
456
+ - `tsc` or `tsup` for build
457
+
458
+ Recommended source layout:
459
+
460
+ ```text
461
+ src/
462
+ cli.ts
463
+ commands/
464
+ init.ts
465
+ run.ts
466
+ config/
467
+ loadConfig.ts
468
+ schema.ts
469
+ runtime/
470
+ renderPrompt.ts
471
+ runWorkflow.ts
472
+ runPhase.ts
473
+ parseResult.ts
474
+ templates/
475
+ default/
476
+ ```
477
+
478
+ ## Future Work
479
+
480
+ - `nyxagent resume`
481
+ - `nyxagent import-tasks`
482
+ - tracker adapters for GitHub, Jira, Linear, or local frontmatter tasks
483
+ - explicit Git commit adapter
484
+ - stricter artifact redaction
485
+ - richer phase retry policy
486
+ - local runner eject command
487
+ - JSON event stream output
488
+ - human approval gates between phases
package/package.json ADDED
@@ -0,0 +1,37 @@
1
+ {
2
+ "name": "@nyxa/nyx-agent",
3
+ "version": "0.1.0",
4
+ "description": "A lightweight phase orchestrator for repeatedly launching coding agents with fresh context.",
5
+ "type": "module",
6
+ "bin": {
7
+ "nyxagent": "dist/cli.js"
8
+ },
9
+ "files": [
10
+ "dist",
11
+ "templates",
12
+ "docs"
13
+ ],
14
+ "scripts": {
15
+ "build": "tsc -p tsconfig.json",
16
+ "dev": "tsx src/cli.ts",
17
+ "test": "tsx --test tests/**/*.test.ts",
18
+ "typecheck": "tsc -p tsconfig.json --noEmit"
19
+ },
20
+ "engines": {
21
+ "node": ">=20"
22
+ },
23
+ "dependencies": {
24
+ "@inquirer/prompts": "^7.0.0",
25
+ "ajv": "^8.17.0",
26
+ "commander": "^14.0.0",
27
+ "execa": "^9.6.0",
28
+ "picocolors": "^1.1.0",
29
+ "smol-toml": "^1.3.0",
30
+ "zod": "^4.0.0"
31
+ },
32
+ "devDependencies": {
33
+ "@types/node": "^24.0.0",
34
+ "tsx": "^4.20.0",
35
+ "typescript": "^5.9.0"
36
+ }
37
+ }
@@ -0,0 +1,11 @@
1
+ Close the selected work item after review approval.
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.
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.
10
+
11
+ If closure cannot be completed safely, stop and explain what remains.
@@ -0,0 +1,11 @@
1
+ Implement the selected work item.
2
+
3
+ Work only on the selected item from the runtime state. Keep changes focused.
4
+ Use a red-green-refactor loop when practical:
5
+
6
+ 1. Reproduce or cover the expected behavior with a focused test.
7
+ 2. Implement the smallest coherent change.
8
+ 3. Run targeted validation and tidy the result.
9
+
10
+ Do not commit. Do not close or mark the work item done. Leave clear validation
11
+ evidence in your final response.
@@ -0,0 +1,29 @@
1
+ The previous phase attempt completed, but NyxAgent could not parse or validate
2
+ its structured result.
3
+
4
+ Validation error:
5
+
6
+ ```text
7
+ {{validation_error}}
8
+ ```
9
+
10
+ Original phase prompt:
11
+
12
+ ```md
13
+ {{original_prompt}}
14
+ ```
15
+
16
+ Original stdout:
17
+
18
+ ```text
19
+ {{original_stdout}}
20
+ ```
21
+
22
+ Original stderr:
23
+
24
+ ```text
25
+ {{original_stderr}}
26
+ ```
27
+
28
+ Reconstruct the intended structured result from the transcript. Return only the
29
+ final `<nyxagent_result>` block. Do not redo the phase work.
@@ -0,0 +1,18 @@
1
+ Review the implementation for the selected work item.
2
+
3
+ Stay read-only. Do not modify project files.
4
+
5
+ Focus on:
6
+
7
+ - alignment with the selected work item
8
+ - correctness and regression risk
9
+ - tests or validation evidence
10
+ - design and architecture fit
11
+ - security or data-safety concerns
12
+
13
+ Return one of these outcomes:
14
+
15
+ - `approved`: the work is ready for closure
16
+ - `changes_requested`: include concrete required changes
17
+
18
+ Keep the review concise and actionable.
@@ -0,0 +1,19 @@
1
+ Select exactly one work item for this iteration.
2
+
3
+ Use the work item configuration from the runtime contract. Prefer open,
4
+ well-scoped work that has not already been selected in this run. Avoid any item
5
+ whose key appears in `seen_work_item_keys`.
6
+
7
+ If no suitable work item exists, return `no_work`.
8
+
9
+ Return one of these outcomes:
10
+
11
+ - `selected`: include a stable `work_item`
12
+ - `no_work`: include a short `reason`
13
+
14
+ The selected work item must have a stable key. Examples:
15
+
16
+ - `github:owner/repo#42`
17
+ - `local:issues/TASK-0007.md`
18
+
19
+ Do not modify project files or task files during selection.
@@ -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
+ }