superbee 0.4.4-pre.16 → 0.4.4-pre.17

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/superbee.mjs +87 -25
  2. package/package.json +1 -1
package/dist/superbee.mjs CHANGED
@@ -44,7 +44,7 @@ var __toESM = (mod, isNodeMode, target3) => (target3 = mod != null ? __create(__
44
44
  var define_SUPERBEE_BUILD_IDENTITY_default;
45
45
  var init_define_SUPERBEE_BUILD_IDENTITY = __esm({
46
46
  "<define:__SUPERBEE_BUILD_IDENTITY__>"() {
47
- define_SUPERBEE_BUILD_IDENTITY_default = { schema: "superbee.build-identity.v1", package: { name: "superbee", version: "0.4.4-pre.16" }, source: { commit: "66fdb860f1569884cb7b0c037feabd69afd25136", dirty: false }, artifact: { channel: "npm-package" }, compatibility_contracts: { skill: 1, hook: 1, mcp: 1 } };
47
+ define_SUPERBEE_BUILD_IDENTITY_default = { schema: "superbee.build-identity.v1", package: { name: "superbee", version: "0.4.4-pre.17" }, source: { commit: "a6b98ce916f5cfcaa684d6244573dc567820f1b7", dirty: false }, artifact: { channel: "npm-package" }, compatibility_contracts: { skill: 1, hook: 1, mcp: 1 } };
48
48
  }
49
49
  });
50
50
 
@@ -48474,27 +48474,54 @@ var init_claim_conflict = __esm({
48474
48474
  // ../cli/src/saved-conflicts.ts
48475
48475
  import { readFile as readFile3, rm as rm2 } from "node:fs/promises";
48476
48476
  import path27 from "node:path";
48477
- function differingFields(left, right, stamped) {
48478
- const keys2 = /* @__PURE__ */ new Set([...Object.keys(left), ...Object.keys(right)]);
48479
- return [...keys2].filter((key4) => !stamped.has(key4) && JSON.stringify(left[key4]) !== JSON.stringify(right[key4])).sort();
48480
- }
48481
48477
  function decodesLosslessly(bytes2) {
48482
48478
  return Buffer.from(bytes2.toString("utf8"), "utf8").equals(bytes2);
48483
48479
  }
48480
+ function comparableText(relPath, bytes2, ownerField) {
48481
+ if (!isConceptDocPath(relPath) || !decodesLosslessly(bytes2)) return null;
48482
+ const text7 = bytes2.toString("utf8");
48483
+ const close = text7.indexOf("\n---\n", 3);
48484
+ if (!text7.startsWith("---\n") || close === -1) return null;
48485
+ const lines = text7.slice(4, close).split("\n");
48486
+ const kept = [];
48487
+ const continues = (line2) => line2 !== void 0 && /^(?:\s|-(?:\s|$))/.test(line2);
48488
+ for (let index2 = 0; index2 < lines.length; index2++) {
48489
+ const line2 = lines[index2];
48490
+ const key4 = /^([^\s#:'"-][^\s:]*):(?:\s|$)/.exec(line2)?.[1];
48491
+ if (key4 !== void 0 && key4 === ownerField) {
48492
+ if (continues(lines[index2 + 1]) || !ONE_LINE_VALUE.test(line2.slice(key4.length + 1).trim())) return null;
48493
+ } else if (key4 !== void 0 && ENGINE_STAMPED_FIELDS.has(key4)) {
48494
+ if (line2.includes("#")) return null;
48495
+ while (continues(lines[index2 + 1])) {
48496
+ if (lines[++index2].includes("#")) return null;
48497
+ }
48498
+ } else {
48499
+ kept.push(line2);
48500
+ }
48501
+ }
48502
+ return `${kept.join("\n")}
48503
+ ---
48504
+ ${text7.slice(close + 5)}`;
48505
+ }
48484
48506
  function copyIsCarried(relPath, copy, current) {
48485
48507
  if (current === null) return false;
48486
48508
  if (copy.equals(current)) return true;
48487
- if (!isConceptDocPath(relPath)) return false;
48488
- if (!decodesLosslessly(copy) || !decodesLosslessly(current)) return false;
48489
- let saved;
48490
- let now;
48491
- try {
48492
- saved = parseMarkdown(copy.toString("utf8"), relPath);
48493
- now = parseMarkdown(current.toString("utf8"), relPath);
48509
+ const saved = comparableText(relPath, copy);
48510
+ return saved !== null && saved === comparableText(relPath, current);
48511
+ }
48512
+ function lostClaim(relPath, copy, current, policy) {
48513
+ try {
48514
+ const fields2 = parseMarkdown(current.toString("utf8"), relPath).frontmatter;
48515
+ const ownerField = policy.forType(fields2.type)?.ownerField;
48516
+ if (ownerField === void 0) return null;
48517
+ const saved = comparableText(relPath, copy, ownerField);
48518
+ if (saved === null || saved !== comparableText(relPath, current, ownerField)) return null;
48519
+ const owner = recordedOwner(fields2, ownerField);
48520
+ const yourOwner = recordedOwner(parseMarkdown(copy.toString("utf8"), relPath).frontmatter, ownerField);
48521
+ return { ...owner !== void 0 ? { owner } : {}, ...yourOwner !== void 0 ? { yourOwner } : {} };
48494
48522
  } catch {
48495
- return false;
48523
+ return null;
48496
48524
  }
48497
- return saved.body === now.body && differingFields(saved.frontmatter, now.frontmatter, ENGINE_STAMPED_FIELDS).length === 0;
48498
48525
  }
48499
48526
  async function readIfThere(file2) {
48500
48527
  try {
@@ -48508,6 +48535,8 @@ async function readIfThere(file2) {
48508
48535
  async function classify(boardPath, key4) {
48509
48536
  const byPath = /* @__PURE__ */ new Map();
48510
48537
  const carried = [];
48538
+ const lostClaims = [];
48539
+ let policy;
48511
48540
  for (const copy of listSavedCopies(defaultSyncStore.exportsDir(key4))) {
48512
48541
  let discardWith = "remove";
48513
48542
  let unreadable = false;
@@ -48519,6 +48548,17 @@ async function classify(boardPath, key4) {
48519
48548
  carried.push(copy);
48520
48549
  continue;
48521
48550
  }
48551
+ if (current !== null && isConceptDocPath(copy.relPath)) {
48552
+ policy ??= await loadClaimPolicy(boardPath);
48553
+ const lost = lostClaim(copy.relPath, bytes2, current, policy);
48554
+ if (lost !== null) {
48555
+ lostClaims.push({
48556
+ copy,
48557
+ claim: { id: conceptIdFromPath(copy.relPath), ...lost.owner !== void 0 ? { owner: lost.owner } : {}, ...lost.yourOwner !== void 0 ? { your_owner: lost.yourOwner } : {} }
48558
+ });
48559
+ continue;
48560
+ }
48561
+ }
48522
48562
  const upstream = readDocBytesAtRef(boardPath, `refs/remotes/${boardRefOf(boardPath)}`, copy.relPath);
48523
48563
  discardWith = current === null ? upstream === null ? "take" : "remove" : upstream === null || !current.equals(upstream) ? "revise" : "take";
48524
48564
  } catch {
@@ -48529,18 +48569,25 @@ async function classify(boardPath, key4) {
48529
48569
  entry.copies.push(copy);
48530
48570
  byPath.set(copy.relPath, entry);
48531
48571
  }
48532
- return { unsettled: [...byPath.values()], carried };
48572
+ return { unsettled: [...byPath.values()], carried, lostClaims };
48533
48573
  }
48534
48574
  async function unsettledConflicts(boardPath, key4) {
48535
48575
  return (await classify(boardPath, key4)).unsettled;
48536
48576
  }
48537
48577
  async function removeCarriedCopies(boardPath, key4) {
48538
- for (const copy of (await classify(boardPath, key4)).carried) {
48578
+ const { carried, lostClaims } = await classify(boardPath, key4);
48579
+ const settled = /* @__PURE__ */ new Map();
48580
+ for (const { copy, claim } of lostClaims) {
48581
+ carried.push(copy);
48582
+ settled.set(claim.id, claim);
48583
+ }
48584
+ for (const copy of carried) {
48539
48585
  await rm2(copy.file, { force: true }).catch(() => {
48540
48586
  });
48541
48587
  if (copy.bodyFile !== null) await rm2(copy.bodyFile, { force: true }).catch(() => {
48542
48588
  });
48543
48589
  }
48590
+ return [...settled.values()];
48544
48591
  }
48545
48592
  function inspectCommand(inv, id, dir) {
48546
48593
  const suffix = dir === void 0 ? commandFragment`` : commandFragment` --dir ${commandToken(dir)}`;
@@ -48567,6 +48614,7 @@ function unsettledMessage(unsettled) {
48567
48614
  const more = names.length > 5 ? ` and ${names.length - 5} more` : "";
48568
48615
  return `${names.length} saved conflict${names.length === 1 ? "" : "s"} still need${names.length === 1 ? "s" : ""} a decision: ${shown3}${more}`;
48569
48616
  }
48617
+ var ONE_LINE_VALUE;
48570
48618
  var init_saved_conflicts = __esm({
48571
48619
  "../cli/src/saved-conflicts.ts"() {
48572
48620
  "use strict";
@@ -48577,6 +48625,7 @@ var init_saved_conflicts = __esm({
48577
48625
  init_command_text();
48578
48626
  init_claim_conflict();
48579
48627
  init_cursor2();
48628
+ ONE_LINE_VALUE = /^(?:"[^"#]*"|'[^'#]*'|[^\s\[\]{}|>&*!#'"-][^#]*)?$/;
48580
48629
  }
48581
48630
  });
48582
48631
 
@@ -49795,7 +49844,7 @@ function convergeDocLine(c, claim, keptRefused) {
49795
49844
  return `${label} \u2014 teammate's version kept (your side deleted it; nothing to save); the document is back with their change, so carry it over if you moved it, then delete it again`;
49796
49845
  }
49797
49846
  if (!c.landed) {
49798
- const recreate = fixingVerb ? " \u2014 re-create with doc write" : "";
49847
+ const recreate = fixingVerb ? " \u2014 re-create with sync --resolve keep" : "";
49799
49848
  return `${label} \u2014 teammate's deletion kept; yours saved at ${c.exportPath}${recreate}`;
49800
49849
  }
49801
49850
  const reconcile = fixingVerb ? keptRefused ? `; doc update cannot read the kept version (${keptRefused.code}), so copy yours over it to reconcile` : " \u2014 reconcile with doc update" : "";
@@ -49812,8 +49861,8 @@ function convergeHelp(inv, id, bodyExportPath) {
49812
49861
  function copyOverHelp(inv, exportPath, file2) {
49813
49862
  return `cp ${commandToken(exportPath)} ${commandToken(file2)} (this discards the teammate's version from your working tree, and the sync replaces it on the board; it stays in the board's Git history) \u2192 ${inv} sync`;
49814
49863
  }
49815
- function recreateHelp(inv, id, bodyExportPath) {
49816
- return `${inv} doc write ${commandToken(id)} --type <Type> --body-file ${commandToken(bodyExportPath)} \u2192 ${inv} sync`;
49864
+ function recreateHelp(inv, id) {
49865
+ return `${inv} sync --resolve keep --doc ${commandToken(id)} \u2192 ${inv} sync`;
49817
49866
  }
49818
49867
  function pickHelp(inv, conflicts, analyses) {
49819
49868
  const usable = (c, i) => c.isDoc && c.bodyExportPath !== null && analyses[i]?.claim?.only !== true;
@@ -49824,7 +49873,7 @@ function pickHelp(inv, conflicts, analyses) {
49824
49873
  return keptRefused && reconcilable.exportPath !== null ? copyOverHelp(inv, reconcilable.exportPath, keptRefused.file) : convergeHelp(inv, reconcilable.entry, reconcilable.bodyExportPath);
49825
49874
  }
49826
49875
  const recreatable = conflicts.find(usable);
49827
- if (recreatable) return recreateHelp(inv, recreatable.entry, recreatable.bodyExportPath);
49876
+ if (recreatable) return recreateHelp(inv, recreatable.entry);
49828
49877
  return void 0;
49829
49878
  }
49830
49879
  function keptDocMeta(frontmatter) {
@@ -49963,7 +50012,7 @@ var init_converge = __esm({
49963
50012
  PUSH_FAIL_ACCESS_MESSAGE = "committed to the board locally \u2014 your work is saved. The remote did not accept the update to origin/board. Verify the active HTTPS/SSH identity, repository-specific Write access, and board-update policy; re-run sync only after one of those conditions changes.";
49964
50013
  PUSH_FAIL_NETWORK_MESSAGE = "committed to the board locally \u2014 your work is saved. The update to origin/board could not reach the remote; re-run sync after connectivity changes.";
49965
50014
  EMPTY_ANALYSIS = { meta: {}, frontmatterDiffers: [] };
49966
- SAVED_COPIES_NOTE = "each saved copy stays listed until its file is removed or sync --resolve settles it: revise before your merge is pushed, take after, which then leaves the document unchanged";
50015
+ SAVED_COPIES_NOTE = "each saved copy stays listed until its file is removed or sync --resolve settles it: revise before your merge is pushed, take after, which then leaves the document unchanged. A copy that differs only by a lost claim is settled by the next sync, which reports it";
49967
50016
  UNKNOWN_FIELD = "unknown";
49968
50017
  }
49969
50018
  });
@@ -56217,6 +56266,7 @@ async function receiptPhase(run, board, commitResult2, delta, pushed, establishA
56217
56266
  hookHint
56218
56267
  });
56219
56268
  if (pushed.gate) receipt5.gate = `passed: ${String(pushed.gate.command)} (attempt ${String(pushed.gate.attempt)})`;
56269
+ if (run.settledClaims?.length) receipt5.lost_claims_settled = cap(run.settledClaims.map((claim) => ({ ...claim })), run.limit);
56220
56270
  const unsettled = await unsettledConflicts(board.boardPath, board.key);
56221
56271
  const counts = { sent: pushed.documents, received: delta.originDelta.length };
56222
56272
  if (unsettled.length > 0) {
@@ -56310,13 +56360,22 @@ async function syncCommand(argv2, deps2 = {}) {
56310
56360
  }
56311
56361
  refuseOutgoingWorktreePrivateState(board.boardPath, "sync", `${run.inv} sync`);
56312
56362
  }
56313
- if (!run.pullOnly) await removeCarriedCopies(board.boardPath, board.key);
56363
+ if (!run.pullOnly) run.settledClaims = await removeCarriedCopies(board.boardPath, board.key);
56314
56364
  const commitResult2 = await commitPhase(board, run.pullOnly);
56315
56365
  if (commitResult2.held) {
56316
56366
  await heldRun(run, board, baseline, commitResult2.held);
56317
56367
  return;
56318
56368
  }
56319
- await pullPhase(run, board, commitResult2);
56369
+ try {
56370
+ await pullPhase(run, board, commitResult2);
56371
+ } catch (error51) {
56372
+ if (!(error51 instanceof CliError) || !run.settledClaims?.length) throw error51;
56373
+ throw new CliError(error51.code, error51.message, {
56374
+ details: { ...error51.details, lost_claims_settled: cap(run.settledClaims.map((claim) => ({ ...claim })), run.limit) },
56375
+ ...error51.help !== void 0 ? { help: error51.help } : {},
56376
+ handled: error51.handled
56377
+ });
56378
+ }
56320
56379
  if (!run.pullOnly) refuseMovedBoard(board.boardPath);
56321
56380
  const delta = await deltaPhase(board, baseline);
56322
56381
  if (!run.pullOnly) holdMalformedCommitted(run, board, delta);
@@ -56415,7 +56474,7 @@ enriched delta of docs that arrived this run (capped; --limit controls the row c
56415
56474
 
56416
56475
  When a doc changed on BOTH sides, sync CONVERGES: your teammate's version is kept on the board,
56417
56476
  YOUR version is saved to an export file named in the receipt, and the sync completes (the
56418
- board is never left mid-state; non-conflicted local changes still land). The run exits 5 with
56477
+ board is never left mid-state; non-conflicted local changes land on the local board, unpushed until the next sync). The run exits 5 with
56419
56478
  one row per conflicted doc and the reconcile chain: \`sync --show-incoming <id>\` to view the kept
56420
56479
  incoming version, \`doc update <id> --body-file <export-file>\` to write your merged version on
56421
56480
  top, then \`sync\` again to share it. The saved copy stays listed until you settle it.
@@ -56424,7 +56483,10 @@ A saved copy stays reported until it is settled: \`status\` reads \`needs_decisi
56424
56483
  document, and every later \`sync\` lists it in \`saved_conflicts\`, with the command that shows it
56425
56484
  and the one command that discards it. Only a new conflict exits 5; a copy from an earlier run does
56426
56485
  not change the exit code. A copy whose content the document already holds, apart from engine
56427
- stamps, is settled by itself: the next \`sync\` removes it. A repeat conflict on one document keeps
56486
+ stamps, is settled by itself: the next \`sync\` removes it. So is a lost claim race, a copy whose
56487
+ text differs only by the one-line owner field its kind declares, which that \`sync\` reports once in
56488
+ \`lost_claims_settled\` with the value the copy held; any other difference in the text, a comment or
56489
+ a state field included, stays listed. A repeat conflict on one document keeps
56428
56490
  the earlier copy and saves the new one beside it, unless its bytes match a saved copy, which is
56429
56491
  then that copy. Copies are kept per clone folder, so moving a clone leaves them under its old path.
56430
56492
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "superbee",
3
- "version": "0.4.4-pre.16",
3
+ "version": "0.4.4-pre.17",
4
4
  "type": "module",
5
5
  "description": "Agent-facing Superbee CLI for reading and writing local OKF knowledge bundles: context notes, docs, cross-links, and live bundle Views.",
6
6
  "keywords": [