@nathapp/nax 0.32.0 → 0.32.2
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.
- package/README.md +87 -6
- package/dist/nax.js +20 -5
- package/package.json +1 -1
- package/src/hooks/runner.ts +6 -2
- package/src/tdd/rectification-gate.ts +23 -2
package/README.md
CHANGED
|
@@ -300,15 +300,96 @@ Integrate notifications, CI triggers, or custom scripts via lifecycle hooks.
|
|
|
300
300
|
| Event | Fires when |
|
|
301
301
|
|:------|:-----------|
|
|
302
302
|
| `on-start` | Run begins |
|
|
303
|
-
| `on-story-start` | A story starts |
|
|
304
|
-
| `on-story-complete` | A story passes |
|
|
305
|
-
| `on-story-fail` | A story exhausts all attempts |
|
|
306
|
-
| `on-pause` | Run paused (awaiting input) |
|
|
307
|
-
| `on-
|
|
308
|
-
| `on-
|
|
303
|
+
| `on-story-start` | A story starts processing |
|
|
304
|
+
| `on-story-complete` | A story passes all checks |
|
|
305
|
+
| `on-story-fail` | A story exhausts all retry attempts |
|
|
306
|
+
| `on-pause` | Run paused (awaiting human input) |
|
|
307
|
+
| `on-resume` | Run resumed after pause |
|
|
308
|
+
| `on-session-end` | An agent session ends (per-session teardown) |
|
|
309
|
+
| `on-complete` | All stories finished successfully |
|
|
310
|
+
| `on-error` | Unhandled error terminates the run |
|
|
309
311
|
|
|
310
312
|
Each hook receives context via `NAX_*` environment variables and full JSON on stdin.
|
|
311
313
|
|
|
314
|
+
**Environment variables passed to hooks:**
|
|
315
|
+
|
|
316
|
+
| Variable | Description |
|
|
317
|
+
|:---------|:------------|
|
|
318
|
+
| `NAX_EVENT` | Event name (e.g., `on-story-complete`) |
|
|
319
|
+
| `NAX_FEATURE` | Feature name |
|
|
320
|
+
| `NAX_STORY_ID` | Current story ID (if applicable) |
|
|
321
|
+
| `NAX_STATUS` | Status (`pass`, `fail`, `paused`, `error`) |
|
|
322
|
+
| `NAX_REASON` | Reason for pause or error |
|
|
323
|
+
| `NAX_COST` | Accumulated cost in USD |
|
|
324
|
+
| `NAX_MODEL` | Current model |
|
|
325
|
+
| `NAX_AGENT` | Current agent |
|
|
326
|
+
| `NAX_ITERATION` | Current iteration number |
|
|
327
|
+
|
|
328
|
+
**Global vs project hooks:** Global hooks (`~/.nax/hooks.json`) fire alongside project hooks. Set `"skipGlobal": true` in your project `hooks.json` to disable global hooks.
|
|
329
|
+
|
|
330
|
+
---
|
|
331
|
+
|
|
332
|
+
## Interaction Triggers
|
|
333
|
+
|
|
334
|
+
nax can pause execution and prompt you for decisions at critical points. Configure triggers in `nax/config.json` (or `~/.nax/config.json` globally):
|
|
335
|
+
|
|
336
|
+
```json
|
|
337
|
+
{
|
|
338
|
+
"interaction": {
|
|
339
|
+
"plugin": "telegram",
|
|
340
|
+
"defaults": {
|
|
341
|
+
"timeout": 600000,
|
|
342
|
+
"fallback": "escalate"
|
|
343
|
+
},
|
|
344
|
+
"triggers": {
|
|
345
|
+
"security-review": true,
|
|
346
|
+
"cost-exceeded": true,
|
|
347
|
+
"cost-warning": true,
|
|
348
|
+
"max-retries": true,
|
|
349
|
+
"human-review": true,
|
|
350
|
+
"story-ambiguity": true,
|
|
351
|
+
"review-gate": true,
|
|
352
|
+
"pre-merge": false,
|
|
353
|
+
"merge-conflict": true
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
```
|
|
358
|
+
|
|
359
|
+
**Available triggers:**
|
|
360
|
+
|
|
361
|
+
| Trigger | Safety | Default Fallback | Description |
|
|
362
|
+
|:--------|:------:|:----------------:|:------------|
|
|
363
|
+
| `security-review` | 🔴 Red | `abort` | Critical security issues found during review |
|
|
364
|
+
| `cost-exceeded` | 🔴 Red | `abort` | Run cost exceeded the configured limit |
|
|
365
|
+
| `merge-conflict` | 🔴 Red | `abort` | Git merge conflict detected |
|
|
366
|
+
| `cost-warning` | 🟡 Yellow | `escalate` | Approaching cost limit — escalate to higher model tier? |
|
|
367
|
+
| `max-retries` | 🟡 Yellow | `skip` | Story exhausted all retry attempts — skip and continue? |
|
|
368
|
+
| `pre-merge` | 🟡 Yellow | `escalate` | Checkpoint before merging to main branch |
|
|
369
|
+
| `human-review` | 🟡 Yellow | `skip` | Human review required on critical failure |
|
|
370
|
+
| `story-ambiguity` | 🟢 Green | `continue` | Story requirements unclear — continue with best effort? |
|
|
371
|
+
| `review-gate` | 🟢 Green | `continue` | Code review checkpoint before proceeding |
|
|
372
|
+
|
|
373
|
+
**Safety tiers:**
|
|
374
|
+
- 🔴 **Red** — Critical; defaults to aborting if no response
|
|
375
|
+
- 🟡 **Yellow** — Caution; defaults to escalating or skipping
|
|
376
|
+
- 🟢 **Green** — Informational; defaults to continuing
|
|
377
|
+
|
|
378
|
+
**Fallback behaviors** (when interaction times out):
|
|
379
|
+
- `continue` — proceed as normal
|
|
380
|
+
- `skip` — skip the current story
|
|
381
|
+
- `escalate` — escalate to a higher model tier
|
|
382
|
+
- `abort` — stop the run
|
|
383
|
+
|
|
384
|
+
**Interaction plugins:**
|
|
385
|
+
|
|
386
|
+
| Plugin | Description |
|
|
387
|
+
|:-------|:------------|
|
|
388
|
+
| `telegram` | Send prompts via Telegram bot (recommended for remote runs) |
|
|
389
|
+
| `cli` | Interactive terminal prompts (for local runs) |
|
|
390
|
+
| `webhook` | POST interaction requests to a webhook URL |
|
|
391
|
+
| `auto` | Auto-respond based on fallback behavior (no human prompt) |
|
|
392
|
+
|
|
312
393
|
---
|
|
313
394
|
|
|
314
395
|
## Plugins
|
package/dist/nax.js
CHANGED
|
@@ -19531,7 +19531,7 @@ var package_default;
|
|
|
19531
19531
|
var init_package = __esm(() => {
|
|
19532
19532
|
package_default = {
|
|
19533
19533
|
name: "@nathapp/nax",
|
|
19534
|
-
version: "0.32.
|
|
19534
|
+
version: "0.32.2",
|
|
19535
19535
|
description: "AI Coding Agent Orchestrator \u2014 loops until done",
|
|
19536
19536
|
type: "module",
|
|
19537
19537
|
bin: {
|
|
@@ -19593,8 +19593,8 @@ var init_version = __esm(() => {
|
|
|
19593
19593
|
NAX_VERSION = package_default.version;
|
|
19594
19594
|
NAX_COMMIT = (() => {
|
|
19595
19595
|
try {
|
|
19596
|
-
if (/^[0-9a-f]{6,10}$/.test("
|
|
19597
|
-
return "
|
|
19596
|
+
if (/^[0-9a-f]{6,10}$/.test("1012b41"))
|
|
19597
|
+
return "1012b41";
|
|
19598
19598
|
} catch {}
|
|
19599
19599
|
try {
|
|
19600
19600
|
const result = Bun.spawnSync(["git", "rev-parse", "--short", "HEAD"], {
|
|
@@ -23625,7 +23625,21 @@ async function runFullSuiteGate(story, config2, workdir, agent, implementerTier,
|
|
|
23625
23625
|
if (testSummary.failed > 0) {
|
|
23626
23626
|
return await runRectificationLoop(story, config2, workdir, agent, implementerTier, contextMarkdown, lite, logger, testSummary, rectificationConfig, testCmd, fullSuiteTimeout);
|
|
23627
23627
|
}
|
|
23628
|
-
|
|
23628
|
+
if (testSummary.passed > 0) {
|
|
23629
|
+
logger.info("tdd", "Full suite gate passed (non-zero exit, 0 failures, tests detected)", {
|
|
23630
|
+
storyId: story.id,
|
|
23631
|
+
exitCode: fullSuiteResult.exitCode,
|
|
23632
|
+
passedTests: testSummary.passed
|
|
23633
|
+
});
|
|
23634
|
+
return true;
|
|
23635
|
+
}
|
|
23636
|
+
logger.warn("tdd", "Full suite gate inconclusive \u2014 no test results parsed from output (possible crash/OOM)", {
|
|
23637
|
+
storyId: story.id,
|
|
23638
|
+
exitCode: fullSuiteResult.exitCode,
|
|
23639
|
+
outputLength: fullSuiteResult.output.length,
|
|
23640
|
+
outputTail: fullSuiteResult.output.slice(-200)
|
|
23641
|
+
});
|
|
23642
|
+
return false;
|
|
23629
23643
|
}
|
|
23630
23644
|
if (fullSuitePassed) {
|
|
23631
23645
|
logger.info("tdd", "Full suite gate passed", { storyId: story.id });
|
|
@@ -27640,7 +27654,8 @@ function validateHookCommand(command) {
|
|
|
27640
27654
|
}
|
|
27641
27655
|
}
|
|
27642
27656
|
function parseCommandToArgv(command) {
|
|
27643
|
-
|
|
27657
|
+
const home = process.env.HOME ?? process.env.USERPROFILE ?? "";
|
|
27658
|
+
return command.trim().split(/\s+/).map((token) => token.startsWith("~/") ? home + token.slice(1) : token);
|
|
27644
27659
|
}
|
|
27645
27660
|
async function executeHook(hookDef, ctx, workdir) {
|
|
27646
27661
|
if (hookDef.enabled === false) {
|
package/package.json
CHANGED
package/src/hooks/runner.ts
CHANGED
|
@@ -144,8 +144,12 @@ export function validateHookCommand(command: string): void {
|
|
|
144
144
|
* @param command - Command string
|
|
145
145
|
* @returns Array of command and arguments
|
|
146
146
|
*/
|
|
147
|
-
function parseCommandToArgv(command: string): string[] {
|
|
148
|
-
|
|
147
|
+
export function parseCommandToArgv(command: string): string[] {
|
|
148
|
+
const home = process.env.HOME ?? process.env.USERPROFILE ?? "";
|
|
149
|
+
return command
|
|
150
|
+
.trim()
|
|
151
|
+
.split(/\s+/)
|
|
152
|
+
.map((token) => (token.startsWith("~/") ? home + token.slice(1) : token));
|
|
149
153
|
}
|
|
150
154
|
|
|
151
155
|
/**
|
|
@@ -69,8 +69,29 @@ export async function runFullSuiteGate(
|
|
|
69
69
|
fullSuiteTimeout,
|
|
70
70
|
);
|
|
71
71
|
}
|
|
72
|
-
|
|
73
|
-
|
|
72
|
+
|
|
73
|
+
// BUG-059: Non-zero exit with 0 parsed failures could mean:
|
|
74
|
+
// (a) Environmental noise (linter warning) — safe to pass
|
|
75
|
+
// (b) Bun crashed/OOM mid-run — truncated output, parser found nothing
|
|
76
|
+
// Distinguish by checking if any tests were actually detected in the output.
|
|
77
|
+
if (testSummary.passed > 0) {
|
|
78
|
+
// Tests ran and passed, but exit code was non-zero (environmental noise)
|
|
79
|
+
logger.info("tdd", "Full suite gate passed (non-zero exit, 0 failures, tests detected)", {
|
|
80
|
+
storyId: story.id,
|
|
81
|
+
exitCode: fullSuiteResult.exitCode,
|
|
82
|
+
passedTests: testSummary.passed,
|
|
83
|
+
});
|
|
84
|
+
return true;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// No tests passed AND no tests failed — output is likely truncated/crashed
|
|
88
|
+
logger.warn("tdd", "Full suite gate inconclusive — no test results parsed from output (possible crash/OOM)", {
|
|
89
|
+
storyId: story.id,
|
|
90
|
+
exitCode: fullSuiteResult.exitCode,
|
|
91
|
+
outputLength: fullSuiteResult.output.length,
|
|
92
|
+
outputTail: fullSuiteResult.output.slice(-200),
|
|
93
|
+
});
|
|
94
|
+
return false;
|
|
74
95
|
}
|
|
75
96
|
if (fullSuitePassed) {
|
|
76
97
|
logger.info("tdd", "Full suite gate passed", { storyId: story.id });
|