xab 4.0.0 → 6.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 +97 -22
  2. package/package.json +3 -2
package/dist/index.js CHANGED
@@ -531,11 +531,18 @@ ${opts.applicationStrategy}
531
531
  - If the target already has a different version of the same logic, merge both intents
532
532
  - Preserve the target's existing improvements \u2014 do not regress
533
533
  - Create or modify files as needed; delete files if the source commit deleted them
534
- - After applying, run exactly: git add -A && git commit -m "${commitMsg.replace(/"/g, "\\\"")}"
535
- - You MUST create exactly ONE commit. Not zero, not two.
536
534
  - No conflict markers, dead code, or TODO placeholders
537
535
  - If impossible to apply cleanly, explain why in notes
538
536
 
537
+ ## CRITICAL \u2014 you MUST commit your changes
538
+ After making all file changes, you MUST run these two commands as your FINAL action:
539
+
540
+ git add -A
541
+ git commit -m "${commitMsg.replace(/"/g, "\\\"")}"
542
+
543
+ If you do not run both commands, your work will be discarded. This is not optional.
544
+ The validation system checks for exactly one new git commit. Zero commits = failure.
545
+
539
546
  Report what you did.`;
540
547
  const firstPrompt = `${MERGE_PREAMBLE}
541
548
  ${opts.repoContext ? `## Repository context
@@ -605,9 +612,16 @@ ${opts.reviewIssues.map((issue, i) => `${i + 1}. ${issue}`).join(`
605
612
  - Read the affected files to understand the current state
606
613
  - Fix every issue the reviewer raised
607
614
  - Do NOT introduce new problems while fixing
608
- - After fixing, amend the commit: git add -A && git commit --amend -m "${commitMsg.replace(/"/g, "\\\"")}"
609
615
  - The result must be a single clean commit with no issues
610
616
 
617
+ ## CRITICAL \u2014 you MUST amend the commit after fixing
618
+ After making all fixes, you MUST run these two commands as your FINAL action:
619
+
620
+ git add -A
621
+ git commit --amend -m "${commitMsg.replace(/"/g, "\\\"")}"
622
+
623
+ If you do not run both commands, your fixes will be discarded. This is not optional.
624
+
611
625
  Report what you fixed.`;
612
626
  const turn = await thread.run(prompt, { outputSchema: applyResultSchema });
613
627
  return parseJson(turn.finalResponse, {
@@ -1166,17 +1180,17 @@ async function validateApply(worktreeGit, beforeHash) {
1166
1180
  const notAdded = status.not_added.filter((f) => !isOurs(f));
1167
1181
  const conflicted = status.conflicted.filter((f) => !isOurs(f));
1168
1182
  const worktreeClean = modified.length === 0 && created.length === 0 && deleted.length === 0 && conflicted.length === 0 && notAdded.length === 0;
1183
+ const dirtyFiles = [];
1169
1184
  if (!worktreeClean) {
1170
- const parts = [];
1171
- if (modified.length)
1172
- parts.push(`${modified.length} modified`);
1173
- if (notAdded.length)
1174
- parts.push(`${notAdded.length} untracked`);
1175
- if (deleted.length)
1176
- parts.push(`${deleted.length} deleted`);
1177
- if (conflicted.length)
1178
- parts.push(`${conflicted.length} conflicted`);
1179
- errors.push(`Working tree not clean: ${parts.join(", ")}`);
1185
+ for (const f of modified)
1186
+ dirtyFiles.push(`M ${f}`);
1187
+ for (const f of notAdded)
1188
+ dirtyFiles.push(`? ${f}`);
1189
+ for (const f of deleted)
1190
+ dirtyFiles.push(`D ${f}`);
1191
+ for (const f of conflicted)
1192
+ dirtyFiles.push(`C ${f}`);
1193
+ errors.push(`Working tree not clean (${dirtyFiles.length} files): ${dirtyFiles.join(", ")}`);
1180
1194
  }
1181
1195
  const conflictMarkers = [];
1182
1196
  if (newCommitHash) {
@@ -1196,7 +1210,15 @@ async function validateApply(worktreeGit, beforeHash) {
1196
1210
  if (conflictMarkers.length > 0) {
1197
1211
  errors.push(`Conflict markers in: ${conflictMarkers.join(", ")}`);
1198
1212
  }
1199
- return { valid: errors.length === 0, newCommitHash, newCommitCount, worktreeClean, conflictMarkers, errors };
1213
+ return {
1214
+ valid: errors.length === 0,
1215
+ newCommitHash,
1216
+ newCommitCount,
1217
+ worktreeClean,
1218
+ conflictMarkers,
1219
+ dirtyFiles,
1220
+ errors
1221
+ };
1200
1222
  }
1201
1223
  async function getAppliedDiff(worktreeGit, beforeHash) {
1202
1224
  return worktreeGit.raw(["diff", beforeHash, "HEAD"]);
@@ -1552,6 +1574,11 @@ async function processOneCommit(o) {
1552
1574
  const validation = await validateApply(o.wtGit, headBefore);
1553
1575
  if (!validation.valid) {
1554
1576
  cb.onLog(`Validation failed: ${validation.errors.join("; ")}`, "red");
1577
+ if (validation.dirtyFiles.length > 0) {
1578
+ for (const f of validation.dirtyFiles) {
1579
+ cb.onLog(` ${f}`, "red");
1580
+ }
1581
+ }
1555
1582
  await resetHard(o.wtGit, headBefore);
1556
1583
  if (attempt === o.maxAttempts)
1557
1584
  return mkFailed(commit, "validation", validation.errors.join("; "), start);
@@ -1751,6 +1778,16 @@ __export(exports_batch, {
1751
1778
  runBatch: () => runBatch
1752
1779
  });
1753
1780
  import chalk from "chalk";
1781
+ import { readFileSync as readFileSync5 } from "fs";
1782
+ import { join as join6 } from "path";
1783
+ function getVersion() {
1784
+ try {
1785
+ const pkg = JSON.parse(readFileSync5(join6(import.meta.dir, "..", "package.json"), "utf-8"));
1786
+ return pkg.version ?? "?";
1787
+ } catch {
1788
+ return "?";
1789
+ }
1790
+ }
1754
1791
  function shortHash2(h) {
1755
1792
  return h.slice(0, 8);
1756
1793
  }
@@ -1806,8 +1843,9 @@ function emitJsonl(obj) {
1806
1843
  async function runBatch(opts) {
1807
1844
  const jsonl = opts.jsonl ?? false;
1808
1845
  const startTime = Date.now();
1846
+ const version = getVersion();
1809
1847
  log("");
1810
- log(` ${chalk.cyan.bold("xab")} ${chalk.dim("\u2014 curated branch reconciliation")}`);
1848
+ log(` ${chalk.cyan.bold("xab")} ${chalk.dim(`v${version}`)} ${chalk.dim("\u2014 curated branch reconciliation")}`);
1811
1849
  log(` ${chalk.magenta(opts.sourceRef)} ${chalk.dim("\u2192")} ${chalk.green(opts.targetRef)}`);
1812
1850
  if (opts.workBranch)
1813
1851
  log(` ${chalk.dim("work branch:")} ${chalk.cyan(opts.workBranch)}`);
@@ -1844,7 +1882,13 @@ async function runBatch(opts) {
1844
1882
  if (jsonl)
1845
1883
  emitJsonl({ event: "analysis", hash: commit.hash, result: analysis });
1846
1884
  log(` ${ts()} ${chalk.dim("analysis:")} ${analysisBadge(analysis.alreadyInTarget)}`);
1847
- log(` ${chalk.dim(" summary:")} ${analysis.summary.slice(0, 120)}`);
1885
+ log(` ${chalk.dim(" summary:")} ${analysis.summary}`);
1886
+ if (analysis.reasoning) {
1887
+ log(` ${chalk.dim(" reasoning:")} ${analysis.reasoning}`);
1888
+ }
1889
+ if (analysis.applicationStrategy && analysis.alreadyInTarget !== "yes") {
1890
+ log(` ${chalk.dim(" strategy:")} ${analysis.applicationStrategy}`);
1891
+ }
1848
1892
  if (analysis.affectedComponents.length > 0) {
1849
1893
  log(` ${chalk.dim(" components:")} ${analysis.affectedComponents.join(", ")}`);
1850
1894
  }
@@ -1863,13 +1907,24 @@ async function runBatch(opts) {
1863
1907
  });
1864
1908
  const duration = chalk.dim(`${(decision.durationMs / 1000).toFixed(1)}s`);
1865
1909
  log(` ${ts()} ${decisionBadge(decision.kind)} ${duration}`);
1910
+ if (decision.newCommitHash) {
1911
+ log(` ${chalk.dim(" commit:")} ${decision.newCommitHash.slice(0, 8)}`);
1912
+ }
1866
1913
  if (decision.kind === "failed" && decision.error) {
1867
- log(` ${chalk.red(` error: ${decision.error.slice(0, 150)}`)}`);
1914
+ log(` ${chalk.red(` error: ${decision.error}`)}`);
1915
+ }
1916
+ if (decision.reason && decision.kind !== "failed") {
1917
+ log(` ${chalk.dim(" reason:")} ${decision.reason}`);
1868
1918
  }
1869
1919
  if (decision.filesChanged && decision.filesChanged.length > 0) {
1870
- const shown = decision.filesChanged.slice(0, 5).join(", ");
1871
- const extra = decision.filesChanged.length > 5 ? chalk.dim(` +${decision.filesChanged.length - 5} more`) : "";
1872
- log(` ${chalk.dim(` files: ${shown}`)}${extra}`);
1920
+ for (const f of decision.filesChanged) {
1921
+ log(` ${chalk.dim(` \xB7 ${f}`)}`);
1922
+ }
1923
+ }
1924
+ if (decision.opsNotes && decision.opsNotes.length > 0) {
1925
+ for (const note of decision.opsNotes) {
1926
+ log(` ${chalk.yellow(` ops: ${note}`)}`);
1927
+ }
1873
1928
  }
1874
1929
  },
1875
1930
  onReview(commit, review) {
@@ -1984,7 +2039,16 @@ import { useState, useEffect, useCallback, useRef } from "react";
1984
2039
  import { Box, Text, useInput, useApp, Static, Newline } from "ink";
1985
2040
  import SelectInput from "ink-select-input";
1986
2041
  import Spinner from "ink-spinner";
2042
+ import { readFileSync as readFileSync4 } from "fs";
2043
+ import { join as join5 } from "path";
1987
2044
  import { jsxDEV, Fragment } from "react/jsx-dev-runtime";
2045
+ var XAB_VERSION = (() => {
2046
+ try {
2047
+ return JSON.parse(readFileSync4(join5(import.meta.dir, "..", "package.json"), "utf-8")).version ?? "?";
2048
+ } catch {
2049
+ return "?";
2050
+ }
2051
+ })();
1988
2052
  function shortHash(h) {
1989
2053
  return h.slice(0, 8);
1990
2054
  }
@@ -2009,8 +2073,15 @@ function Header({
2009
2073
  /* @__PURE__ */ jsxDEV(Text, {
2010
2074
  bold: true,
2011
2075
  color: "cyan",
2012
- children: "\u256D\u2500 backmerge"
2076
+ children: "\u256D\u2500 xab"
2013
2077
  }, undefined, false, undefined, this),
2078
+ /* @__PURE__ */ jsxDEV(Text, {
2079
+ dimColor: true,
2080
+ children: [
2081
+ " v",
2082
+ XAB_VERSION
2083
+ ]
2084
+ }, undefined, true, undefined, this),
2014
2085
  /* @__PURE__ */ jsxDEV(Text, {
2015
2086
  color: "gray",
2016
2087
  children: " \u2014 curated branch reconciliation"
@@ -2782,8 +2853,12 @@ for (let i = 0;i < args.length; i++) {
2782
2853
  repoPath = arg;
2783
2854
  }
2784
2855
  if (showHelp) {
2856
+ let version = "?";
2857
+ try {
2858
+ version = JSON.parse(await Bun.file(new URL("./package.json", import.meta.url).pathname).text()).version;
2859
+ } catch {}
2785
2860
  console.log(`
2786
- xab \u2014 AI-powered curated branch reconciliation
2861
+ xab v${version} \u2014 AI-powered curated branch reconciliation
2787
2862
 
2788
2863
  Usage:
2789
2864
  xab [repo-path] [options]
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "xab",
3
- "version": "4.0.0",
3
+ "version": "6.0.0",
4
4
  "description": "AI-powered curated branch reconciliation engine",
5
5
  "type": "module",
6
6
  "bin": {
@@ -32,6 +32,7 @@
32
32
  "ink-spinner": "^5.0.0",
33
33
  "ink-text-input": "^6.0.0",
34
34
  "react": "18.3.1",
35
- "simple-git": "^3.33.0"
35
+ "simple-git": "^3.33.0",
36
+ "xab": "^5.0.0"
36
37
  }
37
38
  }