hunter-harness 0.2.13 → 0.2.15

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/bin.js +69 -10
  2. package/package.json +1 -1
package/dist/bin.js CHANGED
@@ -4388,30 +4388,70 @@ function assertPreviewAllowed(preview, skip) {
4388
4388
  }
4389
4389
  }
4390
4390
  var CREDENTIALS_HINT = "\u53EF\u5728\u4EA4\u4E92\u6A21\u5F0F\u4E0B\u5F55\u5165\uFF0C\u6216\u5199\u5165 .harness/credentials.local.yaml\uFF08\u52FF\u63D0\u4EA4 git\uFF09";
4391
- var STALE_BASELINE_MESSAGE = "\u670D\u52A1\u7AEF artifact \u5DF2\u66F4\u65B0\uFF0C\u8BF7\u5148\u6267\u884C npx hunter-harness update\uFF08\u51B2\u7A81\u53EF\u7528 update --resolve <path>=keep-local|accept-remote\uFF09\u518D\u63A8";
4391
+ var CONFLICT_PREVIEW_LIMIT = 8;
4392
+ function conflictGroup(path) {
4393
+ const knowledgeMatch = path.match(/^\.harness\/knowledge\/entries\/([^/]+)\//);
4394
+ if (knowledgeMatch?.[1] !== void 0) {
4395
+ return `knowledge/${knowledgeMatch[1]}`;
4396
+ }
4397
+ if (path.startsWith(".harness/knowledge/"))
4398
+ return "knowledge/other";
4399
+ if (path === ".harness/context-index.json")
4400
+ return "context-index";
4401
+ return path.split("/", 1)[0] ?? "other";
4402
+ }
4403
+ function conflictGroups(conflicts) {
4404
+ const groups = {};
4405
+ for (const conflict2 of conflicts) {
4406
+ const group = conflictGroup(conflict2.path);
4407
+ groups[group] = (groups[group] ?? 0) + 1;
4408
+ }
4409
+ return groups;
4410
+ }
4411
+ function formatStaleBaselineMessage(conflicts = []) {
4412
+ const lines = [
4413
+ "\u670D\u52A1\u7AEF\u5B58\u5728\u672C\u5730\u5C1A\u672A\u786E\u8BA4\u7684\u66F4\u65B0\uFF0Cpush \u5DF2\u6682\u505C\u4EE5\u907F\u514D\u8986\u76D6\u5E76\u884C\u4FEE\u6539\u3002"
4414
+ ];
4415
+ if (conflicts.length > 0) {
4416
+ lines.push(`\u68C0\u6D4B\u5230 ${conflicts.length} \u4E2A\u51B2\u7A81\uFF08\u4EC5\u5C55\u793A\u524D ${CONFLICT_PREVIEW_LIMIT} \u4E2A\uFF09\uFF1A`);
4417
+ for (const conflict2 of conflicts.slice(0, CONFLICT_PREVIEW_LIMIT)) {
4418
+ lines.push(` - ${conflict2.path}`);
4419
+ }
4420
+ if (conflicts.length > CONFLICT_PREVIEW_LIMIT) {
4421
+ lines.push(` - \u2026\u53E6 ${conflicts.length - CONFLICT_PREVIEW_LIMIT} \u4E2A`);
4422
+ }
4423
+ }
4424
+ lines.push("\u4FDD\u7559\u672C\u5730\u5E76\u7EE7\u7EED\uFF1Anpx hunter-harness update --conflict-strategy keep-local --yes", "\u63A5\u53D7\u670D\u52A1\u7AEF\u7248\u672C\uFF1Anpx hunter-harness update --conflict-strategy accept-remote --yes", "\u9010\u9879\u5904\u7406\uFF1Anpx hunter-harness update --resolve <path>=keep-local|accept-remote");
4425
+ return lines.join("\n");
4426
+ }
4392
4427
  function staleBaselineError(code, conflicts) {
4393
- const conflictHint = conflicts !== void 0 && conflicts.length > 0 ? " \u51B2\u7A81\u6587\u4EF6\uFF1A" + conflicts.map((item2) => item2.path).join(", ") : "";
4394
- return new PushWorkflowError(STALE_BASELINE_MESSAGE + conflictHint, 5, code);
4428
+ const conflictList = [...conflicts ?? []];
4429
+ return new PushWorkflowError(formatStaleBaselineMessage(conflictList), 5, code, conflictList.length === 0 ? void 0 : {
4430
+ conflicts: conflictList,
4431
+ conflict_count: conflictList.length,
4432
+ conflict_groups: conflictGroups(conflictList)
4433
+ });
4395
4434
  }
4396
- async function syncToLatest(root, project, baseline, client) {
4435
+ async function syncToLatest(root, project, baseline, client, confirmConflictStrategy) {
4397
4436
  const syncResult = await synchronizeArtifacts({
4398
4437
  projectRoot: root,
4399
4438
  project,
4400
4439
  client,
4401
4440
  requestId: uuidV7(),
4402
4441
  dryRun: false,
4403
- conflictStrategy: "manual"
4442
+ conflictStrategy: "manual",
4443
+ ...confirmConflictStrategy === void 0 ? {} : { confirmConflictStrategy }
4404
4444
  }, baseline);
4405
4445
  if (syncResult.conflicts.length > 0) {
4406
4446
  throw staleBaselineError("PROJECT_VERSION_CONFLICT", syncResult.conflicts);
4407
4447
  }
4408
4448
  return await readBaseline(root);
4409
4449
  }
4410
- async function autoRebaseIfServerAdvanced(root, project, baseline, client, remoteVersion) {
4450
+ async function autoRebaseIfServerAdvanced(root, project, baseline, client, remoteVersion, confirmConflictStrategy) {
4411
4451
  if (remoteVersion === baseline.complete_project_version) {
4412
4452
  return baseline;
4413
4453
  }
4414
- const updated = await syncToLatest(root, project, baseline, client);
4454
+ const updated = await syncToLatest(root, project, baseline, client, confirmConflictStrategy);
4415
4455
  if (remoteVersion !== null && updated.complete_project_version !== remoteVersion) {
4416
4456
  throw staleBaselineError("PROJECT_VERSION_CONFLICT");
4417
4457
  }
@@ -4508,7 +4548,7 @@ async function pushProject(options) {
4508
4548
  const boundAtStart = project.project.project_id;
4509
4549
  if (boundAtStart !== null) {
4510
4550
  const remote = await client.getProject(boundAtStart, uuidV7());
4511
- baseline = await autoRebaseIfServerAdvanced(root, project, baseline, client, remote.latest_project_version);
4551
+ baseline = await autoRebaseIfServerAdvanced(root, project, baseline, client, remote.latest_project_version, options.confirmConflictStrategy);
4512
4552
  preview = makePreview(baseline, await managedFiles(root, project), options.confirmedProjectLocal ?? [], installedPaths);
4513
4553
  }
4514
4554
  const initialSkip = await resolveSensitiveScanSkip(preview, options);
@@ -4628,7 +4668,7 @@ async function pushProject(options) {
4628
4668
  } catch (error) {
4629
4669
  if (error instanceof ApiError && (error.code === "STALE_PUSH" || error.code === "PROJECT_VERSION_CONFLICT") && !finalizeRetried) {
4630
4670
  finalizeRetried = true;
4631
- baseline = await syncToLatest(root, project, baseline, client);
4671
+ baseline = await syncToLatest(root, project, baseline, client, options.confirmConflictStrategy);
4632
4672
  workflow = resetSession(workflow, proposalManifestHash);
4633
4673
  await atomicWriteJson(workflowPath, workflow);
4634
4674
  session = await client.createProposalSession(projectId, {
@@ -5799,6 +5839,24 @@ async function runCleanup(options, dependencies) {
5799
5839
  }
5800
5840
 
5801
5841
  // src/commands/push.ts
5842
+ var CONFLICT_PREVIEW_LIMIT2 = 8;
5843
+ async function promptStaleConflictStrategy(conflicts, dependencies) {
5844
+ const lines = [`\u68C0\u6D4B\u5230 ${conflicts.length} \u4E2A\u670D\u52A1\u7AEF\u5E76\u53D1\u51B2\u7A81\uFF1A`];
5845
+ for (const conflict2 of conflicts.slice(0, CONFLICT_PREVIEW_LIMIT2)) {
5846
+ lines.push(` - ${conflict2.path}`);
5847
+ }
5848
+ if (conflicts.length > CONFLICT_PREVIEW_LIMIT2) {
5849
+ lines.push(` - \u2026\u53E6 ${conflicts.length - CONFLICT_PREVIEW_LIMIT2} \u4E2A`);
5850
+ }
5851
+ dependencies.stderr(lines.join("\n") + "\n");
5852
+ const answer = await dependencies.prompt(
5853
+ "\u8BF7\u9009\u62E9\u5904\u7406\u65B9\u5F0F\uFF1A\n 1. \u4FDD\u7559\u672C\u5730\u5185\u5BB9\u5E76\u7EE7\u7EED\u63A8\u9001\uFF08\u63A8\u8350\uFF09\n 2. \u63A5\u53D7\u670D\u52A1\u7AEF\u5185\u5BB9\u5E76\u7EE7\u7EED\u63A8\u9001\n 3. \u9000\u51FA\uFF0C\u7A0D\u540E\u9010\u9879\u5904\u7406\n\u8BF7\u9009\u62E9 [1]\uFF1A"
5854
+ );
5855
+ const selected = answer.trim();
5856
+ if (selected === "" || selected === "1") return "keep-local";
5857
+ if (selected === "2") return "accept-remote";
5858
+ return false;
5859
+ }
5802
5860
  function formatFindings(details) {
5803
5861
  if (details?.findings === void 0 || details.findings.length === 0) {
5804
5862
  return "";
@@ -5899,7 +5957,8 @@ async function runPush(options, dependencies) {
5899
5957
  const reasonAnswer = await dependencies.prompt("\u8DF3\u8FC7\u539F\u56E0\uFF08\u53EF\u9009\uFF0C\u56DE\u8F66\u8DF3\u8FC7\uFF09: ");
5900
5958
  const reason = reasonAnswer.trim();
5901
5959
  return reason === "" ? { skip: true } : { skip: true, reason };
5902
- } }
5960
+ } },
5961
+ ...options.nonInteractive === true ? {} : { confirmConflictStrategy: async (conflicts) => promptStaleConflictStrategy(conflicts, dependencies) }
5903
5962
  });
5904
5963
  if ("cancelled" in result && result.cancelled === true) {
5905
5964
  return 2;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hunter-harness",
3
- "version": "0.2.13",
3
+ "version": "0.2.15",
4
4
  "description": "Local-first, server-governed agent harness",
5
5
  "license": "MIT",
6
6
  "type": "module",