xab 4.0.0 → 5.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 +70 -13
  2. package/package.json +1 -1
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,16 +1180,29 @@ 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
1185
  const parts = [];
1171
- if (modified.length)
1186
+ if (modified.length) {
1172
1187
  parts.push(`${modified.length} modified`);
1173
- if (notAdded.length)
1188
+ for (const f of modified)
1189
+ dirtyFiles.push(`M ${f}`);
1190
+ }
1191
+ if (notAdded.length) {
1174
1192
  parts.push(`${notAdded.length} untracked`);
1175
- if (deleted.length)
1193
+ for (const f of notAdded)
1194
+ dirtyFiles.push(`? ${f}`);
1195
+ }
1196
+ if (deleted.length) {
1176
1197
  parts.push(`${deleted.length} deleted`);
1177
- if (conflicted.length)
1198
+ for (const f of deleted)
1199
+ dirtyFiles.push(`D ${f}`);
1200
+ }
1201
+ if (conflicted.length) {
1178
1202
  parts.push(`${conflicted.length} conflicted`);
1203
+ for (const f of conflicted)
1204
+ dirtyFiles.push(`C ${f}`);
1205
+ }
1179
1206
  errors.push(`Working tree not clean: ${parts.join(", ")}`);
1180
1207
  }
1181
1208
  const conflictMarkers = [];
@@ -1196,7 +1223,15 @@ async function validateApply(worktreeGit, beforeHash) {
1196
1223
  if (conflictMarkers.length > 0) {
1197
1224
  errors.push(`Conflict markers in: ${conflictMarkers.join(", ")}`);
1198
1225
  }
1199
- return { valid: errors.length === 0, newCommitHash, newCommitCount, worktreeClean, conflictMarkers, errors };
1226
+ return {
1227
+ valid: errors.length === 0,
1228
+ newCommitHash,
1229
+ newCommitCount,
1230
+ worktreeClean,
1231
+ conflictMarkers,
1232
+ dirtyFiles,
1233
+ errors
1234
+ };
1200
1235
  }
1201
1236
  async function getAppliedDiff(worktreeGit, beforeHash) {
1202
1237
  return worktreeGit.raw(["diff", beforeHash, "HEAD"]);
@@ -1552,6 +1587,11 @@ async function processOneCommit(o) {
1552
1587
  const validation = await validateApply(o.wtGit, headBefore);
1553
1588
  if (!validation.valid) {
1554
1589
  cb.onLog(`Validation failed: ${validation.errors.join("; ")}`, "red");
1590
+ if (validation.dirtyFiles.length > 0) {
1591
+ for (const f of validation.dirtyFiles) {
1592
+ cb.onLog(` ${f}`, "red");
1593
+ }
1594
+ }
1555
1595
  await resetHard(o.wtGit, headBefore);
1556
1596
  if (attempt === o.maxAttempts)
1557
1597
  return mkFailed(commit, "validation", validation.errors.join("; "), start);
@@ -1844,7 +1884,13 @@ async function runBatch(opts) {
1844
1884
  if (jsonl)
1845
1885
  emitJsonl({ event: "analysis", hash: commit.hash, result: analysis });
1846
1886
  log(` ${ts()} ${chalk.dim("analysis:")} ${analysisBadge(analysis.alreadyInTarget)}`);
1847
- log(` ${chalk.dim(" summary:")} ${analysis.summary.slice(0, 120)}`);
1887
+ log(` ${chalk.dim(" summary:")} ${analysis.summary}`);
1888
+ if (analysis.reasoning) {
1889
+ log(` ${chalk.dim(" reasoning:")} ${analysis.reasoning}`);
1890
+ }
1891
+ if (analysis.applicationStrategy && analysis.alreadyInTarget !== "yes") {
1892
+ log(` ${chalk.dim(" strategy:")} ${analysis.applicationStrategy}`);
1893
+ }
1848
1894
  if (analysis.affectedComponents.length > 0) {
1849
1895
  log(` ${chalk.dim(" components:")} ${analysis.affectedComponents.join(", ")}`);
1850
1896
  }
@@ -1863,13 +1909,24 @@ async function runBatch(opts) {
1863
1909
  });
1864
1910
  const duration = chalk.dim(`${(decision.durationMs / 1000).toFixed(1)}s`);
1865
1911
  log(` ${ts()} ${decisionBadge(decision.kind)} ${duration}`);
1912
+ if (decision.newCommitHash) {
1913
+ log(` ${chalk.dim(" commit:")} ${decision.newCommitHash.slice(0, 8)}`);
1914
+ }
1866
1915
  if (decision.kind === "failed" && decision.error) {
1867
- log(` ${chalk.red(` error: ${decision.error.slice(0, 150)}`)}`);
1916
+ log(` ${chalk.red(` error: ${decision.error}`)}`);
1917
+ }
1918
+ if (decision.reason && decision.kind !== "failed") {
1919
+ log(` ${chalk.dim(" reason:")} ${decision.reason}`);
1868
1920
  }
1869
1921
  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}`);
1922
+ for (const f of decision.filesChanged) {
1923
+ log(` ${chalk.dim(` \xB7 ${f}`)}`);
1924
+ }
1925
+ }
1926
+ if (decision.opsNotes && decision.opsNotes.length > 0) {
1927
+ for (const note of decision.opsNotes) {
1928
+ log(` ${chalk.yellow(` ops: ${note}`)}`);
1929
+ }
1873
1930
  }
1874
1931
  },
1875
1932
  onReview(commit, review) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "xab",
3
- "version": "4.0.0",
3
+ "version": "5.0.0",
4
4
  "description": "AI-powered curated branch reconciliation engine",
5
5
  "type": "module",
6
6
  "bin": {