@nathapp/nax 0.89.0 → 0.90.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.
Files changed (2) hide show
  1. package/dist/nax.js +90 -34
  2. package/package.json +2 -2
package/dist/nax.js CHANGED
@@ -48669,7 +48669,7 @@ var package_default;
48669
48669
  var init_package = __esm(() => {
48670
48670
  package_default = {
48671
48671
  name: "@nathapp/nax",
48672
- version: "0.89.0",
48672
+ version: "0.90.0",
48673
48673
  description: "AI Coding Agent Orchestrator \u2014 loops until done",
48674
48674
  type: "module",
48675
48675
  bin: {
@@ -48755,7 +48755,7 @@ var init_package = __esm(() => {
48755
48755
  "@agentclientprotocol/sdk": "~1.7.0",
48756
48756
  "@anthropic-ai/sandbox-runtime": "0.0.77",
48757
48757
  "@modelcontextprotocol/sdk": "^1.32.1",
48758
- "@nathapp/nax-ai": "0.89.0",
48758
+ "@nathapp/nax-ai": "0.90.0",
48759
48759
  chalk: "^5.6.2",
48760
48760
  commander: "^13.1.0",
48761
48761
  ink: "^6.7.0",
@@ -73074,8 +73074,8 @@ var init_version = __esm(() => {
73074
73074
  NAX_AI_VERSION = CATALOG_VERSION;
73075
73075
  NAX_COMMIT = (() => {
73076
73076
  try {
73077
- if (/^[0-9a-f]{6,10}$/.test("26921a9"))
73078
- return "26921a9";
73077
+ if (/^[0-9a-f]{6,10}$/.test("297c3c7"))
73078
+ return "297c3c7";
73079
73079
  } catch {}
73080
73080
  try {
73081
73081
  const result = Bun.spawnSync(["git", "rev-parse", "--short", "HEAD"], {
@@ -136471,6 +136471,48 @@ var init_trust2 = __esm(() => {
136471
136471
  };
136472
136472
  });
136473
136473
 
136474
+ // src/workflow/supplementary-items.ts
136475
+ function listedDecisionIds(openItems) {
136476
+ return new Set(openItems.flatMap((item) => [item.id, ...item.ruling?.decisionId ? [item.ruling.decisionId] : []]));
136477
+ }
136478
+ async function advisorItems2(dir, feature, openItems, outputDir) {
136479
+ const listed = listedDecisionIds(openItems);
136480
+ return (await readTrustedDecisions(dir, feature, outputDir)).filter((d) => !listed.has(d.id)).map((d) => ({ id: d.id, kind: "advisor", issue: oneLine3(`${d.kind} ${d.action.type}: ${d.rationale}`) }));
136481
+ }
136482
+ function cannotVerifyItems(lines) {
136483
+ const counts = new Map;
136484
+ return lines.map((line) => {
136485
+ const n = (counts.get(line.storyId) ?? 0) + 1;
136486
+ counts.set(line.storyId, n);
136487
+ return { id: `${line.storyId}:cv${n}`, kind: "cannot-verify", issue: line.text };
136488
+ });
136489
+ }
136490
+ async function orNone(source, read) {
136491
+ try {
136492
+ return await read();
136493
+ } catch (err) {
136494
+ getSafeLogger2()?.warn("workflow", "Skipping unreadable supplementary items", {
136495
+ storyId: "_run",
136496
+ source,
136497
+ error: errorMessage(err)
136498
+ });
136499
+ return [];
136500
+ }
136501
+ }
136502
+ async function loadSupplementaryItems(dir, feature, openItems, outputDir) {
136503
+ const advisor = await orNone("advisor ledger", () => advisorItems2(dir, feature, openItems, outputDir));
136504
+ const ledger = await orNone("run ledgers", async () => cannotVerifyItems((await collectLedgerItems(workflowFeatureDir(dir, feature))).cannotVerify));
136505
+ return [...advisor, ...ledger];
136506
+ }
136507
+ var oneLine3 = (text) => text.replace(/\s+/g, " ").trim();
136508
+ var init_supplementary_items = __esm(() => {
136509
+ init_internal();
136510
+ init_advisor();
136511
+ init_logger2();
136512
+ init_ledger_items();
136513
+ init_paths5();
136514
+ });
136515
+
136474
136516
  // src/workflow/run-verdict.ts
136475
136517
  import { basename as basename34 } from "path";
136476
136518
  async function newestResult(dir, feature) {
@@ -136493,7 +136535,7 @@ function noResult(feature) {
136493
136535
  feature
136494
136536
  });
136495
136537
  }
136496
- async function loadRunVerdict(dir, feature) {
136538
+ async function loadRunVerdict(dir, feature, resolveOutputDir) {
136497
136539
  validateFeatureName(feature);
136498
136540
  const found = await newestResult(dir, feature);
136499
136541
  if (!found)
@@ -136503,13 +136545,15 @@ async function loadRunVerdict(dir, feature) {
136503
136545
  throw notStarted(feature, found.runId);
136504
136546
  const filled = fillStoryStates(record, found.result);
136505
136547
  const verdict = buildVerdict(filled);
136548
+ const openItems = verdict?.openItems ?? buildOpenItems(filled);
136506
136549
  return {
136507
136550
  runId: found.runId,
136508
136551
  runDir: found.runDir,
136509
136552
  result: found.result,
136510
136553
  record,
136511
- openItems: verdict?.openItems ?? buildOpenItems(filled),
136512
- reasons: record.reasons
136554
+ openItems,
136555
+ reasons: record.reasons,
136556
+ supplementaryItems: await loadSupplementaryItems(dir, feature, openItems, await resolveOutputDir(dir))
136513
136557
  };
136514
136558
  }
136515
136559
  var init_run_verdict = __esm(() => {
@@ -136518,17 +136562,25 @@ var init_run_verdict = __esm(() => {
136518
136562
  init_open_items();
136519
136563
  init_paths5();
136520
136564
  init_result();
136565
+ init_supplementary_items();
136521
136566
  });
136522
136567
 
136523
136568
  // src/cli/workflow-forge.ts
136569
+ import { basename as basename35 } from "path";
136524
136570
  var _workflowForgeDeps;
136525
136571
  var init_workflow_forge = __esm(() => {
136572
+ init_config();
136526
136573
  init_forge();
136574
+ init_runtime3();
136527
136575
  init_ci_wait();
136528
136576
  _workflowForgeDeps = {
136529
136577
  forge: defaultForgeDeps,
136530
136578
  now: () => new Date,
136531
- waitForChecks
136579
+ waitForChecks,
136580
+ resolveOutputDir: async (dir) => {
136581
+ const config = await loadConfig(dir).catch(() => null);
136582
+ return projectOutputDir(config?.name?.trim() || basename35(dir), config?.outputDir);
136583
+ }
136532
136584
  };
136533
136585
  });
136534
136586
 
@@ -136591,8 +136643,8 @@ function sidecarKindFor(item) {
136591
136643
  function openIds(items) {
136592
136644
  return items.length === 0 ? "none" : items.map((item) => item.id).join(", ");
136593
136645
  }
136594
- function checkEntriesAgainstItems(entries, items) {
136595
- const byId = new Map(items.map((item) => [item.id, item]));
136646
+ function checkEntriesAgainstItems(entries, items, supplementary = []) {
136647
+ const byId = new Map([...supplementary, ...items].map((item) => [item.id, item]));
136596
136648
  for (const entry of entries) {
136597
136649
  const item = byId.get(entry.id);
136598
136650
  if (item === undefined) {
@@ -136955,13 +137007,13 @@ function reportOf(input) {
136955
137007
  }
136956
137008
  async function promote(opts, forgeDeps) {
136957
137009
  validateFeatureName(opts.feature);
136958
- const loaded = await loadRunVerdict(opts.dir, opts.feature);
137010
+ const loaded = await loadRunVerdict(opts.dir, opts.feature, forgeDeps.resolveOutputDir);
136959
137011
  const url = draftUrl(loaded, opts.feature);
136960
137012
  const forge = await draftForge(forgeDeps, opts.dir, url);
136961
137013
  const expectedSha = await headSha2(forgeDeps.forge, opts.dir);
136962
137014
  const found = await newestSidecar(loaded.runDir);
136963
137015
  if (found)
136964
- checkEntriesAgainstItems(found.sidecar.decisions, loaded.openItems);
137016
+ checkEntriesAgainstItems(found.sidecar.decisions, loaded.openItems, loaded.supplementaryItems);
136965
137017
  const checks = [itemsRuled(loaded, found), await ciGreen({ forgeDeps, forge, url, dir: opts.dir, expectedSha })];
136966
137018
  const target = { opts, loaded, url, forge, forgeDeps, found };
136967
137019
  if (!opts.yes || !allPassed(checks))
@@ -137076,7 +137128,7 @@ function updatedSidecar(existing, input) {
137076
137128
  async function writeRecorded(input) {
137077
137129
  const found = await newestSidecar(input.loaded.runDir);
137078
137130
  if (found) {
137079
- checkEntriesAgainstItems(found.sidecar.decisions, input.loaded.openItems);
137131
+ checkEntriesAgainstItems(found.sidecar.decisions, input.loaded.openItems, input.loaded.supplementaryItems);
137080
137132
  const sidecar = updatedSidecar(found.sidecar, input);
137081
137133
  await writeSidecar(found.path, sidecar);
137082
137134
  return { path: found.path, sidecar };
@@ -137104,9 +137156,9 @@ async function record4(opts, forgeDeps) {
137104
137156
  validateFeatureName(opts.feature);
137105
137157
  const outcome = outcomeOf2(opts);
137106
137158
  const ctx = { dir: opts.dir, forgeDeps };
137107
- const loaded = await loadRunVerdict(opts.dir, opts.feature);
137159
+ const loaded = await loadRunVerdict(opts.dir, opts.feature, forgeDeps.resolveOutputDir);
137108
137160
  const entries = opts.decisions === undefined ? [] : await readDecisionsFile(opts.decisions);
137109
- checkEntriesAgainstItems(entries, loaded.openItems);
137161
+ checkEntriesAgainstItems(entries, loaded.openItems, loaded.supplementaryItems);
137110
137162
  const prUrl = outcome !== undefined && PR_OUTCOMES.includes(outcome) ? await readyPrUrl(outcome, loaded, ctx) : undefined;
137111
137163
  const written = await writeRecorded({ opts, loaded, entries, outcome, prUrl, now: forgeDeps.now() });
137112
137164
  return reportOf2(opts, loaded, written);
@@ -137159,11 +137211,12 @@ function reportFor2(dir, feature, loaded) {
137159
137211
  ...record.ci === undefined ? {} : { ci: record.ci },
137160
137212
  ...record.url === undefined ? {} : { url: record.url },
137161
137213
  reasons: record.reasons,
137162
- openItems: loaded.openItems
137214
+ openItems: loaded.openItems,
137215
+ supplementaryItems: loaded.supplementaryItems
137163
137216
  };
137164
137217
  }
137165
- async function buildReport(dir, feature) {
137166
- return reportFor2(dir, feature, await loadRunVerdict(dir, feature));
137218
+ async function buildReport(dir, feature, forgeDeps) {
137219
+ return reportFor2(dir, feature, await loadRunVerdict(dir, feature, forgeDeps.resolveOutputDir));
137167
137220
  }
137168
137221
  function exitCodeFor(report) {
137169
137222
  return report.outcome === "ready" && report.openItems.length === 0 ? 0 : 2;
@@ -137171,16 +137224,19 @@ function exitCodeFor(report) {
137171
137224
  function printPlain4(report, deps) {
137172
137225
  const outcome = report.outcome ?? NOT_PUBLISHED2;
137173
137226
  deps.log(`${report.feature}: ${outcome}${report.url ? ` ${report.url}` : ""}`);
137174
- if (report.openItems.length === 0) {
137227
+ if (report.openItems.length === 0)
137175
137228
  deps.log(NO_ITEMS);
137176
- return;
137177
- }
137178
137229
  for (const item of report.openItems)
137179
- deps.log(`- [${item.kind}] ${item.id} \u2014 ${item.issue}`);
137230
+ deps.log(itemLine(item));
137231
+ if (report.supplementaryItems.length === 0)
137232
+ return;
137233
+ deps.log(SUPPLEMENTARY_HEADING);
137234
+ for (const item of report.supplementaryItems)
137235
+ deps.log(itemLine(item));
137180
137236
  }
137181
- async function workflowStatusCommand(opts, deps = _workflowCliDeps) {
137237
+ async function workflowStatusCommand(opts, deps = _workflowCliDeps, forgeDeps = _workflowForgeDeps) {
137182
137238
  try {
137183
- const report = await buildReport(opts.dir, opts.feature);
137239
+ const report = await buildReport(opts.dir, opts.feature, forgeDeps);
137184
137240
  if (opts.json)
137185
137241
  deps.log(JSON.stringify(report));
137186
137242
  else
@@ -137205,7 +137261,7 @@ async function guarded4(deps, body) {
137205
137261
  }
137206
137262
  function registerWorkflowCommand(program, deps = _workflowCliDeps, forgeDeps = _workflowForgeDeps) {
137207
137263
  const group = program.command("workflow").description("Inspect a feature's workflow run");
137208
- group.command("status").description("Print the verdict for the feature's newest workflow run").requiredOption("-f, --feature <name>", "Feature name").option("-d, --dir <path>", "Project directory", process.cwd()).option("--json", "Emit one machine-readable JSON document", false).action((o) => guarded4(deps, () => workflowStatusCommand({ dir: o.dir, feature: o.feature, json: o.json === true }, deps)));
137264
+ group.command("status").description("Print the verdict for the feature's newest workflow run").requiredOption("-f, --feature <name>", "Feature name").option("-d, --dir <path>", "Project directory", process.cwd()).option("--json", "Emit one machine-readable JSON document", false).action((o) => guarded4(deps, () => workflowStatusCommand({ dir: o.dir, feature: o.feature, json: o.json === true }, deps, forgeDeps)));
137209
137265
  group.command("record").description("Record a human's rulings and the run's outcome in the decisions sidecar").requiredOption("-f, --feature <name>", "Feature name").option("--decisions <file>", "A JSON array of decision entries").option("--outcome <outcome>", "One of pending, promoted, verified-ready, abandoned").option("-d, --dir <path>", "Project directory", process.cwd()).option("--json", "Emit one machine-readable JSON document", false).action((o) => guarded4(deps, () => workflowRecordCommand({
137210
137266
  dir: o.dir,
137211
137267
  feature: o.feature,
@@ -137216,7 +137272,7 @@ function registerWorkflowCommand(program, deps = _workflowCliDeps, forgeDeps = _
137216
137272
  group.command("promote").description("Check a draft run and report whether its PR can be promoted").requiredOption("-f, --feature <name>", "Feature name").option("--yes", "Promote for real once every check passes", false).option("-d, --dir <path>", "Project directory", process.cwd()).option("--json", "Emit one machine-readable JSON document", false).action((o) => guarded4(deps, () => workflowPromoteCommand({ dir: o.dir, feature: o.feature, yes: o.yes === true, json: o.json === true }, deps, forgeDeps)));
137217
137273
  return group;
137218
137274
  }
137219
- var _workflowCliDeps, NOT_PUBLISHED2 = "not published", NO_ITEMS = "No open items.";
137275
+ var _workflowCliDeps, NOT_PUBLISHED2 = "not published", NO_ITEMS = "No open items.", SUPPLEMENTARY_HEADING = "Also to verify (not blocking):", itemLine = (item) => `- [${item.kind}] ${item.id} \u2014 ${item.issue}`;
137220
137276
  var init_workflow2 = __esm(() => {
137221
137277
  init_internal();
137222
137278
  init_open_items();
@@ -149849,7 +149905,7 @@ var init_worktree_id2 = __esm(() => {
149849
149905
  });
149850
149906
 
149851
149907
  // src/bakeoff/contestant.ts
149852
- import { basename as basename38, join as join193 } from "path";
149908
+ import { basename as basename39, join as join193 } from "path";
149853
149909
  function aggregateTotals(metrics) {
149854
149910
  let costUsd = 0;
149855
149911
  let wallTimeMs = 0;
@@ -149879,7 +149935,7 @@ async function runContestant(agent, options, deps) {
149879
149935
  }
149880
149936
  };
149881
149937
  const worktree = storyWorktreePath(options.projectRoot, storyId);
149882
- const projectKey = options.config.name?.trim() || basename38(options.projectRoot);
149938
+ const projectKey = options.config.name?.trim() || basename39(options.projectRoot);
149883
149939
  const outputDir = join193(projectOutputDir(projectKey, options.outputDir), "bakeoff", feature, agent);
149884
149940
  const context = {
149885
149941
  profile: agent,
@@ -151773,7 +151829,7 @@ init_checkpoint();
151773
151829
  init_runtime3();
151774
151830
  init_feature_name();
151775
151831
  import { existsSync as existsSync45 } from "fs";
151776
- import { basename as basename35, join as join188 } from "path";
151832
+ import { basename as basename36, join as join188 } from "path";
151777
151833
  async function defaultCheckpointExists(featureDir) {
151778
151834
  if (!featureDir || !existsSync45(featureDir))
151779
151835
  return false;
@@ -151870,7 +151926,7 @@ function registerResumeCommand(program) {
151870
151926
  const globalNaxDir = globalConfigDir();
151871
151927
  const hooks = await loadHooksConfig(naxDir, globalNaxDir);
151872
151928
  applyResumeModeDeps(opts.featureDir ?? "", "auto");
151873
- const projectKey = config.name?.trim() || basename35(dir);
151929
+ const projectKey = config.name?.trim() || basename36(dir);
151874
151930
  const outputDir = projectOutputDir(projectKey, config.outputDir);
151875
151931
  const statusFilePath = join188(outputDir, "status.json");
151876
151932
  const runsDir = join188(outputDir, "features", feature, "runs");
@@ -152052,7 +152108,7 @@ init_execution2();
152052
152108
  init_logger2();
152053
152109
  init_runtime3();
152054
152110
  import { mkdir as mkdir50, unlink as unlink13 } from "fs/promises";
152055
- import { basename as basename36, join as join190 } from "path";
152111
+ import { basename as basename37, join as join190 } from "path";
152056
152112
  var _unlockDeps = {
152057
152113
  findProjectDir,
152058
152114
  loadConfig,
@@ -152083,7 +152139,7 @@ function parseFeatureLockRecord(raw) {
152083
152139
  async function resolveOutputDir3(workdir) {
152084
152140
  const naxDir = _unlockDeps.findProjectDir(workdir);
152085
152141
  const config = await _unlockDeps.loadConfig(naxDir ?? undefined);
152086
- const projectKey = config.name?.trim() || basename36(workdir);
152142
+ const projectKey = config.name?.trim() || basename37(workdir);
152087
152143
  const outputDir = _unlockDeps.projectOutputDir(projectKey, config.outputDir);
152088
152144
  return { outputDir, naxDir };
152089
152145
  }
@@ -159895,7 +159951,7 @@ init_version();
159895
159951
  init_source();
159896
159952
  init_cli2();
159897
159953
  import { existsSync as existsSync47, mkdirSync as mkdirSync9 } from "fs";
159898
- import { basename as basename37, join as join192 } from "path";
159954
+ import { basename as basename38, join as join192 } from "path";
159899
159955
 
159900
159956
  // src/cli/run-max-iterations.ts
159901
159957
  function parseMaxIterationsFlag(raw) {
@@ -160268,7 +160324,7 @@ async function displayPlanConfirmation(prd, options) {
160268
160324
  }
160269
160325
  function initRunLogging(params) {
160270
160326
  const { options, config, workdir, isTTY, logLevel, formatterMode } = params;
160271
- const projectKey = config.name?.trim() || basename37(workdir);
160327
+ const projectKey = config.name?.trim() || basename38(workdir);
160272
160328
  const outputDir = projectOutputDir(projectKey, config.outputDir);
160273
160329
  const runsDir = join192(outputDir, "features", options.feature, "runs");
160274
160330
  mkdirSync9(runsDir, { recursive: true });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nathapp/nax",
3
- "version": "0.89.0",
3
+ "version": "0.90.0",
4
4
  "description": "AI Coding Agent Orchestrator — loops until done",
5
5
  "type": "module",
6
6
  "bin": {
@@ -86,7 +86,7 @@
86
86
  "@agentclientprotocol/sdk": "~1.7.0",
87
87
  "@anthropic-ai/sandbox-runtime": "0.0.77",
88
88
  "@modelcontextprotocol/sdk": "^1.32.1",
89
- "@nathapp/nax-ai": "0.89.0",
89
+ "@nathapp/nax-ai": "0.90.0",
90
90
  "chalk": "^5.6.2",
91
91
  "commander": "^13.1.0",
92
92
  "ink": "^6.7.0",