xab 10.0.0 → 12.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 +50 -53
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -476,7 +476,7 @@ async function runStreamedWithProgress(thread, prompt, onProgress, turnOpts) {
476
476
  const lines = output.split(`
477
477
  `).filter(Boolean);
478
478
  for (const line of lines.slice(-3)) {
479
- onProgress("exec", ` ${line.slice(0, 120)}`);
479
+ onProgress("output", ` ${line.slice(0, 120)}`);
480
480
  }
481
481
  }
482
482
  }
@@ -511,8 +511,10 @@ async function runStreamedWithProgress(thread, prompt, onProgress, turnOpts) {
511
511
  if (text) {
512
512
  const lines = text.split(`
513
513
  `).filter(Boolean);
514
- for (const line of lines.slice(0, 3)) {
515
- onProgress("think", line.slice(0, 150));
514
+ if (lines[0])
515
+ onProgress("think", lines[0].slice(0, 150));
516
+ for (const line of lines.slice(1, 3)) {
517
+ onProgress("output", ` ${line.slice(0, 150)}`);
516
518
  }
517
519
  }
518
520
  break;
@@ -1436,6 +1438,9 @@ async function runEngine(opts, cb) {
1436
1438
  autoSkip = true
1437
1439
  } = opts;
1438
1440
  const config = loadConfig(repoPath, opts.configPath);
1441
+ if (opts.hints?.length) {
1442
+ config.promptHints = [...config.promptHints ?? [], ...opts.hints];
1443
+ }
1439
1444
  const effectiveMaxAttempts = opts.maxAttempts ?? config.maxAttempts ?? Infinity;
1440
1445
  const effectiveWorkBranch = opts.workBranch ?? config.workBranch;
1441
1446
  const commitPrefix = config.commitPrefix ?? "backmerge:";
@@ -1451,6 +1456,12 @@ async function runEngine(opts, cb) {
1451
1456
  for (const l of logs)
1452
1457
  cb.onLog(` ${l}`, "gray");
1453
1458
  }
1459
+ if (config.promptHints && config.promptHints.length > 0) {
1460
+ cb.onLog(`Active hints (${config.promptHints.length}):`, "cyan");
1461
+ for (const h of config.promptHints) {
1462
+ cb.onLog(` \u2192 ${h}`, "cyan");
1463
+ }
1464
+ }
1454
1465
  cb.onStatus("Discovering repo structure & docs...");
1455
1466
  const repoCtx = buildRepoContext(repoPath, config);
1456
1467
  const instrCount = repoCtx.instructions.size;
@@ -1538,28 +1549,8 @@ async function runEngine(opts, cb) {
1538
1549
  await createDetachedWorktree(git, wtPath, wbHead);
1539
1550
  cb.onLog(`Eval worktree (detached): ${wtPath}`, "green");
1540
1551
  const wtGit = createGit(wtPath);
1541
- const runId = `run-${ts}`;
1542
- const audit = new AuditLog(repoPath, runId);
1543
- const runMeta = {
1544
- runId,
1545
- startedAt: new Date().toISOString(),
1546
- sourceRef,
1547
- targetRef,
1548
- workBranch: wbName,
1549
- mergeBase,
1550
- worktreePath: wtPath,
1551
- totalCandidates: commitsToProcess.length,
1552
- cherrySkipped: cherrySkipped.size,
1553
- dryRun,
1554
- repoPath
1555
- };
1556
- audit.runStart(runMeta);
1557
- for (const [hash, reason] of cherrySkipped) {
1558
- const c = allSourceCommits.find((x) => x.hash === hash);
1559
- if (c)
1560
- audit.cherrySkip(hash, c.message, reason);
1561
- }
1562
1552
  let resumeFromIndex = 0;
1553
+ let resumedRunId;
1563
1554
  if (opts.resume && effectiveWorkBranch) {
1564
1555
  const resumeInfo = findResumableRun(repoPath, effectiveWorkBranch);
1565
1556
  if (resumeInfo && resumeInfo.lastCommitIndex >= 0) {
@@ -1571,7 +1562,31 @@ async function runEngine(opts, cb) {
1571
1562
  resumeFromIndex = commitsToProcess.findIndex((c) => !prevHashes.has(c.hash));
1572
1563
  if (resumeFromIndex < 0)
1573
1564
  resumeFromIndex = commitsToProcess.length;
1574
- cb.onLog(`Resuming from commit ${resumeFromIndex + 1}/${commitsToProcess.length} (${resumeInfo.decisions.length} already decided)`, "green");
1565
+ resumedRunId = resumeInfo.runId;
1566
+ cb.onLog(`Resuming run ${resumeInfo.runId} from commit ${resumeFromIndex + 1}/${commitsToProcess.length} (${resumeInfo.decisions.length} already decided)`, "green");
1567
+ }
1568
+ }
1569
+ const runId = resumedRunId ?? `run-${ts}`;
1570
+ const audit = new AuditLog(repoPath, runId);
1571
+ if (!resumedRunId) {
1572
+ const runMeta = {
1573
+ runId,
1574
+ startedAt: new Date().toISOString(),
1575
+ sourceRef,
1576
+ targetRef,
1577
+ workBranch: wbName,
1578
+ mergeBase,
1579
+ worktreePath: wtPath,
1580
+ totalCandidates: commitsToProcess.length,
1581
+ cherrySkipped: cherrySkipped.size,
1582
+ dryRun,
1583
+ repoPath
1584
+ };
1585
+ audit.runStart(runMeta);
1586
+ for (const [hash, reason] of cherrySkipped) {
1587
+ const c = allSourceCommits.find((x) => x.hash === hash);
1588
+ if (c)
1589
+ audit.cherrySkip(hash, c.message, reason);
1575
1590
  }
1576
1591
  }
1577
1592
  const sourceLatestDiff = await getLatestCommitDiff(git, resolvedSource);
@@ -2050,43 +2065,20 @@ async function runBatch(opts) {
2050
2065
  const subMatch = msg.match(/^\[(\w+)\]\s*(.*)/);
2051
2066
  const sub = subMatch ? subMatch[1] : phase;
2052
2067
  const text = subMatch ? subMatch[2] : msg;
2053
- let icon;
2054
2068
  switch (sub) {
2055
- case "read":
2056
- icon = chalk.cyan("\uD83D\uDCD6");
2057
- break;
2058
- case "grep":
2059
- icon = chalk.cyan("\uD83D\uDD0D");
2060
- break;
2061
- case "glob":
2062
- icon = chalk.cyan("\uD83D\uDCC2");
2063
- break;
2064
- case "exec":
2065
- icon = chalk.yellow("\u26A1");
2066
- break;
2067
2069
  case "file":
2068
- icon = chalk.green("\u270F\uFE0F");
2070
+ log(` ${ts()} \u270F\uFE0F ${chalk.green(text)}`);
2069
2071
  break;
2070
2072
  case "think":
2071
- icon = chalk.blue("\uD83D\uDCAD");
2072
- break;
2073
- case "tool":
2074
- icon = chalk.dim("\uD83D\uDD27");
2073
+ log(` ${ts()} \uD83D\uDCAD ${chalk.blue(text)}`);
2075
2074
  break;
2076
- case "analyze":
2077
- icon = chalk.blue("\u25C6");
2078
- break;
2079
- case "apply":
2080
- icon = chalk.green("\u25B8");
2081
- break;
2082
- case "review":
2083
- icon = chalk.magenta("\u25CF");
2075
+ case "read":
2076
+ log(` ${ts()} \uD83D\uDCD6 ${chalk.dim(text)}`);
2084
2077
  break;
2085
2078
  default:
2086
- icon = chalk.dim("\xB7");
2079
+ log(` ${ts()} ${chalk.dim(text)}`);
2087
2080
  break;
2088
2081
  }
2089
- log(` ${ts()} ${icon} ${chalk.dim(text)}`);
2090
2082
  },
2091
2083
  onLog(msg, color) {
2092
2084
  if (jsonl)
@@ -3043,6 +3035,7 @@ var batch = false;
3043
3035
  var jsonl = false;
3044
3036
  var resume = true;
3045
3037
  var showHelp = false;
3038
+ var hints = [];
3046
3039
  for (let i = 0;i < args.length; i++) {
3047
3040
  const arg = args[i];
3048
3041
  if (arg === "--help" || arg === "-h")
@@ -3083,6 +3076,8 @@ for (let i = 0;i < args.length; i++) {
3083
3076
  maxAttempts = parseInt(args[++i], 10) || 2;
3084
3077
  else if (arg === "--config" && args[i + 1])
3085
3078
  configPath = args[++i];
3079
+ else if (arg === "--hint" && args[i + 1])
3080
+ hints.push(args[++i]);
3086
3081
  else if (!arg.startsWith("-"))
3087
3082
  repoPath = arg;
3088
3083
  }
@@ -3119,6 +3114,7 @@ Behavior:
3119
3114
  --no-auto-skip Don't auto-skip commits AI identifies as present
3120
3115
  --max-attempts <n> Max retries per commit (default: unlimited)
3121
3116
  --no-resume Don't resume from interrupted runs (default: auto-resume)
3117
+ --hint <text> Operator hint injected into AI prompts (repeatable)
3122
3118
  --config <path> Path to config file (default: auto-discover)
3123
3119
  --help, -h Show this help
3124
3120
 
@@ -3179,6 +3175,7 @@ var engineOpts = {
3179
3175
  review,
3180
3176
  autoSkip,
3181
3177
  resume,
3178
+ ...hints.length > 0 && { hints },
3182
3179
  maxAttempts,
3183
3180
  ...startAfter && { startAfter },
3184
3181
  ...limit > 0 && { limit },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "xab",
3
- "version": "10.0.0",
3
+ "version": "12.0.0",
4
4
  "description": "AI-powered curated branch reconciliation engine",
5
5
  "type": "module",
6
6
  "bin": {