xab 2.0.0 → 3.0.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/index.js +75 -9
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -473,7 +473,14 @@ You are looking at a worktree based on the TARGET branch "${opts.targetBranch}".
473
473
  - "no" = missing
474
474
  4. If not fully present, describe a step-by-step strategy for applying cleanly
475
475
  5. List which top-level components/directories are affected
476
- 6. Consider: does this change make sense for the target? Is it useful?`;
476
+ 6. Consider: does this change make sense for the target? Is it useful?
477
+ 7. Check if this commit requires any operator action beyond a normal code deploy:
478
+ - New environment variables added to .env / .env.example? \u2192 note which ones
479
+ - Database schema changes or migrations? \u2192 note what to run
480
+ - New services, containers, or infrastructure to deploy? \u2192 note what
481
+ - Config files that need manual updates on servers? \u2192 note which
482
+ - Dependencies on external services being added or removed? \u2192 note what
483
+ - If the commit is just normal code changes that only need a deploy+restart, leave opsNotes as []`;
477
484
  let turn;
478
485
  if (diffChunks.length > 1) {
479
486
  await thread.run(firstPrompt);
@@ -500,7 +507,8 @@ You now have the complete diff. Analyze and produce your structured response.`,
500
507
  alreadyInTarget: "no",
501
508
  reasoning: "Could not parse structured output",
502
509
  applicationStrategy: "Manual review recommended",
503
- affectedComponents: []
510
+ affectedComponents: [],
511
+ opsNotes: []
504
512
  });
505
513
  }
506
514
  async function applyCommit(opts) {
@@ -642,9 +650,14 @@ var init_codex = __esm(() => {
642
650
  type: "array",
643
651
  items: { type: "string" },
644
652
  description: "Top-level directories/components affected (e.g. 'api', 'frontend', 'contracts')"
653
+ },
654
+ opsNotes: {
655
+ type: "array",
656
+ items: { type: "string" },
657
+ description: "Operator action items ONLY if this commit requires something beyond a standard code deploy+restart. Examples: new env vars to add, database migrations to run, new services to deploy, infrastructure changes, config file updates on servers. Leave as empty array [] if no operator action is needed \u2014 a normal code deploy does NOT count."
645
658
  }
646
659
  },
647
- required: ["summary", "alreadyInTarget", "reasoning", "applicationStrategy", "affectedComponents"],
660
+ required: ["summary", "alreadyInTarget", "reasoning", "applicationStrategy", "affectedComponents", "opsNotes"],
648
661
  additionalProperties: false
649
662
  };
650
663
  applyResultSchema = {
@@ -1260,7 +1273,8 @@ async function runEngine(opts, cb) {
1260
1273
  worktreePath: "",
1261
1274
  workBranch: "",
1262
1275
  auditDir: "",
1263
- commits: []
1276
+ commits: [],
1277
+ opsNotes: []
1264
1278
  };
1265
1279
  }
1266
1280
  cb.onStatus("Detecting already cherry-picked commits...");
@@ -1293,7 +1307,8 @@ async function runEngine(opts, cb) {
1293
1307
  worktreePath: "",
1294
1308
  workBranch: "",
1295
1309
  auditDir: "",
1296
- commits: commitsToProcess
1310
+ commits: commitsToProcess,
1311
+ opsNotes: []
1297
1312
  };
1298
1313
  }
1299
1314
  const repoName = repoPath.split("/").pop() ?? "repo";
@@ -1394,13 +1409,26 @@ async function runEngine(opts, cb) {
1394
1409
  cb.onDecision(commit, decision);
1395
1410
  }
1396
1411
  audit.runEnd(summary, decisions);
1412
+ const opsNotes = decisions.filter((d) => d.opsNotes && d.opsNotes.length > 0).map((d) => ({ commitHash: d.commitHash, commitMessage: d.commitMessage, notes: d.opsNotes }));
1413
+ if (opsNotes.length > 0) {
1414
+ cb.onLog("", "gray");
1415
+ cb.onLog("\u2550\u2550\u2550 OPERATOR NOTES \u2550\u2550\u2550", "yellow");
1416
+ for (const entry of opsNotes) {
1417
+ cb.onLog(` ${entry.commitHash.slice(0, 8)} ${entry.commitMessage}:`, "yellow");
1418
+ for (const note of entry.notes) {
1419
+ cb.onLog(` \u2192 ${note}`, "yellow");
1420
+ }
1421
+ }
1422
+ cb.onLog("\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550", "yellow");
1423
+ }
1397
1424
  return {
1398
1425
  summary,
1399
1426
  decisions,
1400
1427
  worktreePath: wtPath,
1401
1428
  workBranch: wbName,
1402
1429
  auditDir: audit.runDir,
1403
- commits: commitsToProcess
1430
+ commits: commitsToProcess,
1431
+ opsNotes
1404
1432
  };
1405
1433
  }
1406
1434
  async function processOneCommit(o) {
@@ -1452,12 +1480,12 @@ async function processOneCommit(o) {
1452
1480
  };
1453
1481
  }
1454
1482
  if (o.dryRun) {
1455
- const kind = analysis.alreadyInTarget === "partial" ? "would_apply" : "would_apply";
1456
1483
  return {
1457
- kind,
1484
+ kind: "would_apply",
1458
1485
  commitHash: commit.hash,
1459
1486
  commitMessage: commit.message,
1460
1487
  reason: analysis.applicationStrategy.slice(0, 300),
1488
+ opsNotes: analysis.opsNotes.length > 0 ? analysis.opsNotes : undefined,
1461
1489
  durationMs: Date.now() - start
1462
1490
  };
1463
1491
  }
@@ -1656,6 +1684,7 @@ async function processOneCommit(o) {
1656
1684
  newCommitHash: validation.newCommitHash ?? undefined,
1657
1685
  filesChanged: applyResult.filesChanged,
1658
1686
  reviewApproved: o.review ? true : undefined,
1687
+ opsNotes: analysis.opsNotes.length > 0 ? analysis.opsNotes : undefined,
1659
1688
  durationMs: Date.now() - start
1660
1689
  };
1661
1690
  const invariantErrors = validateDecision(d, headBefore, headAfter, o.dryRun);
@@ -1739,7 +1768,8 @@ async function runBatch(opts) {
1739
1768
  summary: result.summary,
1740
1769
  worktree: result.worktreePath,
1741
1770
  branch: result.workBranch,
1742
- auditDir: result.auditDir
1771
+ auditDir: result.auditDir,
1772
+ opsNotes: result.opsNotes
1743
1773
  });
1744
1774
  const { summary } = result;
1745
1775
  if (summary.failed > 0)
@@ -2433,6 +2463,42 @@ function App({ repoPath, engineOpts }) {
2433
2463
  }, undefined, true, undefined, this)
2434
2464
  ]
2435
2465
  }, undefined, true, undefined, this),
2466
+ result.opsNotes.length > 0 && /* @__PURE__ */ jsxDEV(Box, {
2467
+ flexDirection: "column",
2468
+ borderStyle: "round",
2469
+ borderColor: "yellow",
2470
+ paddingX: 1,
2471
+ marginTop: 1,
2472
+ children: [
2473
+ /* @__PURE__ */ jsxDEV(Text, {
2474
+ bold: true,
2475
+ color: "yellow",
2476
+ children: "Operator Notes"
2477
+ }, undefined, false, undefined, this),
2478
+ /* @__PURE__ */ jsxDEV(Newline, {}, undefined, false, undefined, this),
2479
+ result.opsNotes.map((entry) => /* @__PURE__ */ jsxDEV(Box, {
2480
+ flexDirection: "column",
2481
+ children: [
2482
+ /* @__PURE__ */ jsxDEV(Text, {
2483
+ color: "yellow",
2484
+ children: [
2485
+ entry.commitHash.slice(0, 8),
2486
+ " ",
2487
+ entry.commitMessage
2488
+ ]
2489
+ }, undefined, true, undefined, this),
2490
+ entry.notes.map((note, i) => /* @__PURE__ */ jsxDEV(Text, {
2491
+ color: "yellow",
2492
+ children: [
2493
+ " ",
2494
+ "\u2192 ",
2495
+ note
2496
+ ]
2497
+ }, i, true, undefined, this))
2498
+ ]
2499
+ }, entry.commitHash, true, undefined, this))
2500
+ ]
2501
+ }, undefined, true, undefined, this),
2436
2502
  /* @__PURE__ */ jsxDEV(ActionBar, {
2437
2503
  actions: [{ key: "q", label: "Exit", color: "gray" }]
2438
2504
  }, undefined, false, undefined, this)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "xab",
3
- "version": "2.0.0",
3
+ "version": "3.0.0",
4
4
  "description": "AI-powered curated branch reconciliation engine",
5
5
  "type": "module",
6
6
  "bin": {