dsh-git-worktree 0.7.5 → 0.8.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 (117) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/README.en.md +7 -3
  3. package/README.md +7 -3
  4. package/docs/i18n.md +63 -0
  5. package/lib/adapters/files.d.ts +0 -7
  6. package/lib/adapters/files.js +4 -3
  7. package/lib/adapters/files.js.map +1 -1
  8. package/lib/adapters/git.d.ts +0 -10
  9. package/lib/adapters/git.js +6 -5
  10. package/lib/adapters/git.js.map +1 -1
  11. package/lib/adapters/registry.d.ts +0 -7
  12. package/lib/adapters/registry.js +2 -1
  13. package/lib/adapters/registry.js.map +1 -1
  14. package/lib/adapters/safe-file.d.ts +0 -7
  15. package/lib/adapters/safe-file.js +5 -4
  16. package/lib/adapters/safe-file.js.map +1 -1
  17. package/lib/client/WorktreeCreateRow.js +4 -2
  18. package/lib/client/WorktreeCreateRow.js.map +1 -1
  19. package/lib/client/WorktreeReviewRow.js +10 -8
  20. package/lib/client/WorktreeReviewRow.js.map +1 -1
  21. package/lib/client/actions.d.ts +3 -2
  22. package/lib/client/actions.js +17 -13
  23. package/lib/client/actions.js.map +1 -1
  24. package/lib/client/console-remote/adapter.d.ts +2 -1
  25. package/lib/client/console-remote/adapter.js +35 -32
  26. package/lib/client/console-remote/adapter.js.map +1 -1
  27. package/lib/client/console-remote/index.js +6 -2
  28. package/lib/client/console-remote/index.js.map +1 -1
  29. package/lib/client/i18n.d.ts +25 -0
  30. package/lib/client/i18n.js +43 -0
  31. package/lib/client/i18n.js.map +1 -0
  32. package/lib/client/index.d.ts +1 -1
  33. package/lib/client/index.js +17 -4
  34. package/lib/client/index.js.map +1 -1
  35. package/lib/client/model.d.ts +3 -2
  36. package/lib/client/model.js +8 -8
  37. package/lib/client/model.js.map +1 -1
  38. package/lib/client/pre-session/PreSessionWorktreeToggle.js +14 -12
  39. package/lib/client/pre-session/PreSessionWorktreeToggle.js.map +1 -1
  40. package/lib/client/pre-session/controller.js +11 -9
  41. package/lib/client/pre-session/controller.js.map +1 -1
  42. package/lib/client/pre-session/index.d.ts +12 -1
  43. package/lib/client/pre-session/index.js +9 -1
  44. package/lib/client/pre-session/index.js.map +1 -1
  45. package/lib/client/review-console/DeliveryProof.js +15 -12
  46. package/lib/client/review-console/DeliveryProof.js.map +1 -1
  47. package/lib/client/review-console/PreflightStatus.js +14 -8
  48. package/lib/client/review-console/PreflightStatus.js.map +1 -1
  49. package/lib/client/review-console/ReviewActions.js +97 -92
  50. package/lib/client/review-console/ReviewActions.js.map +1 -1
  51. package/lib/client/review-console/WorktreeReviewPanel.d.ts +1 -1
  52. package/lib/client/review-console/WorktreeReviewPanel.js +19 -16
  53. package/lib/client/review-console/WorktreeReviewPanel.js.map +1 -1
  54. package/lib/client/review-console/recovery-continuation.d.ts +2 -1
  55. package/lib/client/review-console/recovery-continuation.js +12 -8
  56. package/lib/client/review-console/recovery-continuation.js.map +1 -1
  57. package/lib/client/target-console/TargetStatusAction.js +28 -25
  58. package/lib/client/target-console/TargetStatusAction.js.map +1 -1
  59. package/lib/client/target-console/WorktreeConsoleView.js +40 -36
  60. package/lib/client/target-console/WorktreeConsoleView.js.map +1 -1
  61. package/lib/client/target-console/WorktreeManagerModal.js +3 -1
  62. package/lib/client/target-console/WorktreeManagerModal.js.map +1 -1
  63. package/lib/client/target-console/WorktreeReviewStatus.js +14 -12
  64. package/lib/client/target-console/WorktreeReviewStatus.js.map +1 -1
  65. package/lib/client/workspace-sidebar/index.js +4 -2
  66. package/lib/client/workspace-sidebar/index.js.map +1 -1
  67. package/lib/client/workspace-sidebar/model.d.ts +2 -1
  68. package/lib/client/workspace-sidebar/model.js +14 -13
  69. package/lib/client/workspace-sidebar/model.js.map +1 -1
  70. package/lib/client.js +2173 -477
  71. package/lib/commands.js +32 -35
  72. package/lib/commands.js.map +1 -1
  73. package/lib/console-host/control-plane.js +82 -81
  74. package/lib/console-host/control-plane.js.map +1 -1
  75. package/lib/console-host/errors.js +2 -1
  76. package/lib/console-host/errors.js.map +1 -1
  77. package/lib/console-host/projection.js +3 -1
  78. package/lib/console-host/projection.js.map +1 -1
  79. package/lib/console-host/review-diff.js +6 -5
  80. package/lib/console-host/review-diff.js.map +1 -1
  81. package/lib/console-host/service.d.ts +24 -22
  82. package/lib/console-host/service.js +54 -51
  83. package/lib/console-host/service.js.map +1 -1
  84. package/lib/console-remote/descriptors.js +2 -2
  85. package/lib/console-remote/descriptors.js.map +1 -1
  86. package/lib/console-remote/remote.d.ts +22 -21
  87. package/lib/console-remote/remote.js.map +1 -1
  88. package/lib/console-remote/schemas.d.ts +4 -0
  89. package/lib/console-remote/schemas.js +1 -0
  90. package/lib/console-remote/schemas.js.map +1 -1
  91. package/lib/i18n/client-messages.d.ts +1523 -0
  92. package/lib/i18n/client-messages.js +1431 -0
  93. package/lib/i18n/client-messages.js.map +1 -0
  94. package/lib/i18n/core.d.ts +12 -0
  95. package/lib/i18n/core.js +10 -0
  96. package/lib/i18n/core.js.map +1 -0
  97. package/lib/i18n/host-messages.d.ts +1463 -0
  98. package/lib/i18n/host-messages.js +1474 -0
  99. package/lib/i18n/host-messages.js.map +1 -0
  100. package/lib/i18n/host.d.ts +9 -0
  101. package/lib/i18n/host.js +34 -0
  102. package/lib/i18n/host.js.map +1 -0
  103. package/lib/i18n/transport-messages.d.ts +10 -0
  104. package/lib/i18n/transport-messages.js +11 -0
  105. package/lib/i18n/transport-messages.js.map +1 -0
  106. package/lib/session-checkout-apply.js +93 -92
  107. package/lib/session-checkout-apply.js.map +1 -1
  108. package/lib/session-checkout-module.js +232 -229
  109. package/lib/session-checkout-module.js.map +1 -1
  110. package/lib/session-target-context.js +2 -1
  111. package/lib/session-target-context.js.map +1 -1
  112. package/lib/tools.d.ts +0 -8
  113. package/lib/tools.js +108 -96
  114. package/lib/tools.js.map +1 -1
  115. package/package.json +2 -1
  116. package/scripts/dev-dsh-lib.mjs +1 -1
  117. package/scripts/workspace-sidebar-upstream.mjs +1 -1
@@ -1,3 +1,4 @@
1
+ import { hostMessage } from './i18n/host.js';
1
2
  import { spawn } from 'node:child_process';
2
3
  import { createHash, randomUUID } from 'node:crypto';
3
4
  import { realpathSync } from 'node:fs';
@@ -8,7 +9,7 @@ class GitCommandFailure extends Error {
8
9
  args;
9
10
  result;
10
11
  constructor(args, result) {
11
- super(result.stderr || `git 命令退出码为 ${result.exitCode}`);
12
+ super(result.stderr || hostMessage('gitExitedWithCode', { p0: result.exitCode }));
12
13
  this.args = args;
13
14
  this.result = result;
14
15
  }
@@ -53,7 +54,7 @@ async function runGit(cwd, args, options = {}) {
53
54
  });
54
55
  const timeout = setTimeout(() => {
55
56
  child.kill('SIGTERM');
56
- finish(new Error(`git 命令超时:${args[0] ?? 'unknown'}`));
57
+ finish(new Error(hostMessage('gitCommandTimedOut', { p0: args[0] ?? 'unknown' })));
57
58
  }, GIT_TIMEOUT_MS);
58
59
  if (options.input !== undefined)
59
60
  child.stdin.end(options.input);
@@ -354,10 +355,10 @@ async function createUserCommit(checkoutPath, treeOid, parentOid, commitMessage)
354
355
  }
355
356
  async function prepareRollbackAssessment(tempRoot, sourceObjects, objectDirectory, localGitRoot, current, receipt) {
356
357
  if (current.headRef !== receipt.localHeadRef) {
357
- return { status: 'blocked', code: 'stale_local', message: 'Local branch 已变化,不能自动撤回 Preview' };
358
+ return { status: 'blocked', code: 'stale_local', message: hostMessage('theLocalBranchHasChangedPreviewCannotBeRolled') };
358
359
  }
359
360
  if (current.headOid !== receipt.localHeadOid && !await isAncestor(localGitRoot, receipt.localHeadOid, current.headOid)) {
360
- return { status: 'blocked', code: 'stale_local', message: 'Local HEAD 不是 Preview 基线的安全快进,不能自动撤回' };
361
+ return { status: 'blocked', code: 'stale_local', message: hostMessage('localHEADIsNotASafeFastForwardFrom') };
361
362
  }
362
363
  let rollbackBaselineTreeOid = receipt.localWorkingTreeOid;
363
364
  let expectedPreviewTreeOid = receipt.previewWorkingTreeOid;
@@ -367,7 +368,7 @@ async function prepareRollbackAssessment(tempRoot, sourceObjects, objectDirector
367
368
  if (advancedBaseline.status === 'conflict') {
368
369
  return {
369
370
  status: 'blocked', code: 'preview_modified',
370
- message: `Local 新提交与 Preview 前的本地修改冲突,无法安全撤回:${advancedBaseline.conflictingFiles.join('、')}`,
371
+ message: hostMessage('newLocalCommitsConflictWithPrePreviewLocalChanges', { p0: advancedBaseline.conflictingFiles.join('、') }),
371
372
  conflictingFiles: [...advancedBaseline.conflictingFiles],
372
373
  };
373
374
  }
@@ -375,21 +376,21 @@ async function prepareRollbackAssessment(tempRoot, sourceObjects, objectDirector
375
376
  if (previewAbsence.status === 'conflict') {
376
377
  return {
377
378
  status: 'blocked', code: 'preview_modified',
378
- message: `Local 新提交与 Preview 任务增量冲突,无法安全撤回:${previewAbsence.conflictingFiles.join('、')}`,
379
+ message: hostMessage('newLocalCommitsConflictWithThePreviewTaskChanges', { p0: previewAbsence.conflictingFiles.join('、') }),
379
380
  conflictingFiles: [...previewAbsence.conflictingFiles],
380
381
  };
381
382
  }
382
383
  if (previewAbsence.treeOid !== advancedBaseline.treeOid) {
383
384
  return {
384
385
  status: 'blocked', code: 'preview_modified',
385
- message: 'Local 新提交已经包含部分或全部 Preview 增量,不能通过撤回工作区改动来改写已提交历史',
386
+ message: hostMessage('newLocalCommitsAlreadyContainSomeOrAllPreview'),
386
387
  };
387
388
  }
388
389
  const advancedPreview = await computeTreeMerge(tempRoot, sourceObjects, objectDirectory, localGitRoot, 'preview-rollback-advanced-preview', receipt.localWorkingTreeOid, advancedBaseline.treeOid, receipt.previewWorkingTreeOid);
389
390
  if (advancedPreview.status === 'conflict') {
390
391
  return {
391
392
  status: 'blocked', code: 'preview_modified',
392
- message: `Local 新提交与 Preview 任务增量冲突,无法安全撤回:${advancedPreview.conflictingFiles.join('、')}`,
393
+ message: hostMessage('newLocalCommitsConflictWithThePreviewTaskChanges', { p0: advancedPreview.conflictingFiles.join('、') }),
393
394
  conflictingFiles: [...advancedPreview.conflictingFiles],
394
395
  };
395
396
  }
@@ -400,7 +401,7 @@ async function prepareRollbackAssessment(tempRoot, sourceObjects, objectDirector
400
401
  if (rollbackTree.status === 'conflict') {
401
402
  return {
402
403
  status: 'blocked', code: 'preview_modified',
403
- message: `Local 在 Preview 区域出现额外修改,无法安全撤回:${rollbackTree.conflictingFiles.join('、')}`,
404
+ message: hostMessage('localHasAdditionalChangesInThePreviewAreaSafe', { p0: rollbackTree.conflictingFiles.join('、') }),
404
405
  conflictingFiles: [...rollbackTree.conflictingFiles],
405
406
  };
406
407
  }
@@ -408,19 +409,19 @@ async function prepareRollbackAssessment(tempRoot, sourceObjects, objectDirector
408
409
  }
409
410
  async function prepareFinalizeAssessment(tempRoot, sourceObjects, objectDirectory, localGitRoot, current, receipt) {
410
411
  if (!receipt.localHeadRef?.startsWith('refs/heads/')) {
411
- return { status: 'blocked', code: 'operation_not_allowed', message: 'Local 当前不是普通分支,不能自动创建任务提交' };
412
+ return { status: 'blocked', code: 'operation_not_allowed', message: hostMessage('localIsNotOnANormalBranchATask') };
412
413
  }
413
414
  if (current.headRef !== receipt.localHeadRef) {
414
- return { status: 'blocked', code: 'stale_local', message: 'Local branch 已变化,不能完成 Preview 提交' };
415
+ return { status: 'blocked', code: 'stale_local', message: hostMessage('theLocalBranchHasChangedThePreviewCommitCannot') };
415
416
  }
416
417
  if (current.headOid !== receipt.localHeadOid && !await isAncestor(localGitRoot, receipt.localHeadOid, current.headOid)) {
417
- return { status: 'blocked', code: 'stale_local', message: 'Local HEAD 不是 Preview 基线的安全快进,不能完成 Preview 提交' };
418
+ return { status: 'blocked', code: 'stale_local', message: hostMessage('localHEADIsNotASafeFastForwardFrom281') };
418
419
  }
419
420
  const previewRemoval = await computeTreeMerge(tempRoot, sourceObjects, objectDirectory, localGitRoot, 'preview-finalize-separation', receipt.previewWorkingTreeOid, current.treeOid, receipt.localWorkingTreeOid);
420
421
  if (previewRemoval.status === 'conflict') {
421
422
  return {
422
423
  status: 'blocked', code: 'preview_modified',
423
- message: `Local 在 Preview 区域出现额外修改,无法可靠提交:${previewRemoval.conflictingFiles.join('、')}`,
424
+ message: hostMessage('localHasAdditionalChangesInThePreviewAreaA', { p0: previewRemoval.conflictingFiles.join('、') }),
424
425
  conflictingFiles: [...previewRemoval.conflictingFiles],
425
426
  };
426
427
  }
@@ -428,21 +429,21 @@ async function prepareFinalizeAssessment(tempRoot, sourceObjects, objectDirector
428
429
  if (taskTree.status === 'conflict') {
429
430
  return {
430
431
  status: 'blocked', code: 'commit_isolation_conflict',
431
- message: `Preview 任务增量无法与最新 Local HEAD 可靠拆分:${taskTree.conflictingFiles.join('、')}`,
432
+ message: hostMessage('previewTaskChangesCannotBeReliablySeparatedFromThe', { p0: taskTree.conflictingFiles.join('、') }),
432
433
  conflictingFiles: [...taskTree.conflictingFiles],
433
434
  };
434
435
  }
435
436
  if (taskTree.treeOid === current.headTreeOid && receipt.changedFiles.length > 0) {
436
437
  return {
437
438
  status: 'blocked', code: 'preview_modified',
438
- message: 'Preview 任务增量已经进入 Local HEAD;不会创建重复或空提交',
439
+ message: hostMessage('previewTaskChangesAreAlreadyInLocalHEADNo'),
439
440
  };
440
441
  }
441
442
  const finalIndexTree = await computeTreeMerge(tempRoot, sourceObjects, objectDirectory, localGitRoot, 'preview-index-preservation', current.headTreeOid, taskTree.treeOid, current.indexTreeOid);
442
443
  if (finalIndexTree.status === 'conflict') {
443
444
  return {
444
445
  status: 'blocked', code: 'commit_isolation_conflict',
445
- message: `Preview 提交与 Local staged 修改无法可靠分离:${finalIndexTree.conflictingFiles.join('、')}`,
446
+ message: hostMessage('thePreviewCommitCannotBeReliablySeparatedFromLocal', { p0: finalIndexTree.conflictingFiles.join('、') }),
446
447
  conflictingFiles: [...finalIndexTree.conflictingFiles],
447
448
  };
448
449
  }
@@ -529,7 +530,7 @@ class DefaultSessionCheckoutApplyEngine {
529
530
  async checkpoint(input) {
530
531
  const commitMessage = input.commitMessage.trim();
531
532
  if (!commitMessage || commitMessage.length > 500 || !OID_PATTERN.test(input.expectedHeadOid) || !input.expectedFingerprint.trim()) {
532
- return { status: 'error', error: { code: 'invalid_input', message: 'Checkpoint 输入无效' } };
533
+ return { status: 'error', error: { code: 'invalid_input', message: hostMessage('invalidCheckpointInput') } };
533
534
  }
534
535
  let tempRoot = null;
535
536
  let adjacentIndex = null;
@@ -540,22 +541,22 @@ class DefaultSessionCheckoutApplyEngine {
540
541
  const isolatedGitRoot = await resolveGitRoot(input.isolatedPath);
541
542
  const projectPathPrefix = projectPrefix(isolatedGitRoot, input.isolatedPath);
542
543
  if (projectPathPrefix === null) {
543
- return { status: 'error', error: { code: 'invalid_input', message: 'Checkpoint 项目目录不属于当前 Worktree' } };
544
+ return { status: 'error', error: { code: 'invalid_input', message: hostMessage('theCheckpointProjectDirectoryDoesNotBelongToThe') } };
544
545
  }
545
546
  const snapshot = await captureSnapshot(isolatedGitRoot, join(tempRoot, 'checkpoint.index'), null, null);
546
547
  if (snapshot.headRef !== null) {
547
- return { status: 'error', error: { code: 'operation_not_allowed', message: 'Checkpoint 只允许写入 detached managed Worktree' } };
548
+ return { status: 'error', error: { code: 'operation_not_allowed', message: hostMessage('checkpointCanOnlyWriteToADetachedManagedWorktree') } };
548
549
  }
549
550
  if (snapshot.headOid !== input.expectedHeadOid || snapshot.fingerprint !== input.expectedFingerprint) {
550
- return { status: 'error', error: { code: 'stale_isolated', message: 'Worktree 在准备验收后发生变化,不能保存阶段' } };
551
+ return { status: 'error', error: { code: 'stale_isolated', message: hostMessage('worktreeChangedAfterPreparingTheReviewTheCheckpointCannot') } };
551
552
  }
552
553
  const changedPaths = await changedTreePaths(isolatedGitRoot, snapshot.headOid, snapshot.treeOid, null, null);
553
554
  if (changedPaths.some((path) => !isProjectPath(path, projectPathPrefix))) {
554
- return { status: 'error', error: { code: 'invalid_input', message: 'Worktree 包含项目根目录外的变更,不能保存阶段' } };
555
+ return { status: 'error', error: { code: 'invalid_input', message: hostMessage('worktreeContainsChangesOutsideTheProjectRootTheCheckpoint') } };
555
556
  }
556
557
  const changedFiles = changedPaths.map((path) => toProjectPath(path, projectPathPrefix)).sort();
557
558
  if (changedFiles.length === 0) {
558
- return { status: 'error', error: { code: 'operation_not_allowed', message: '当前阶段没有可保存的新修改' } };
559
+ return { status: 'error', error: { code: 'operation_not_allowed', message: hostMessage('theCurrentCheckpointHasNoNewChangesToSave') } };
559
560
  }
560
561
  const commitOid = await createUserCommit(isolatedGitRoot, snapshot.treeOid, snapshot.headOid, commitMessage);
561
562
  await input.beforeCommit?.({ commitOid, parentOid: snapshot.headOid, indexTreeOid: snapshot.indexTreeOid, changedFiles });
@@ -572,7 +573,7 @@ class DefaultSessionCheckoutApplyEngine {
572
573
  await this.options.beforeFinalIsolatedValidation?.();
573
574
  const finalSnapshot = await captureSnapshot(isolatedGitRoot, join(tempRoot, 'final-checkpoint.index'), null, null);
574
575
  if (finalSnapshot.headOid !== snapshot.headOid || finalSnapshot.fingerprint !== snapshot.fingerprint) {
575
- return { status: 'error', error: { code: 'stale_isolated', message: 'Worktree 在保存阶段前发生变化,请重新准备验收' } };
576
+ return { status: 'error', error: { code: 'stale_isolated', message: hostMessage('worktreeChangedBeforeSavingTheCheckpointPrepareTheReview') } };
576
577
  }
577
578
  let headUpdated = false;
578
579
  try {
@@ -594,7 +595,7 @@ class DefaultSessionCheckoutApplyEngine {
594
595
  status: 'error',
595
596
  error: {
596
597
  code: 'git_error',
597
- message: `Checkpoint index 写入失败且 HEAD 无法回滚:${this.errorMessage(error)};${this.errorMessage(rollbackError)}`,
598
+ message: hostMessage('checkpointIndexWriteFailedAndHEADCouldNotBe', { p0: this.errorMessage(error), p1: this.errorMessage(rollbackError) }),
598
599
  recoveryState: 'uncertain',
599
600
  },
600
601
  };
@@ -604,7 +605,7 @@ class DefaultSessionCheckoutApplyEngine {
604
605
  status: 'error',
605
606
  error: {
606
607
  code: 'git_error',
607
- message: `Checkpoint 写入失败,已回滚:${this.errorMessage(error)}`,
608
+ message: hostMessage('checkpointWriteFailedAndWasRolledBack', { p0: this.errorMessage(error) }),
608
609
  recoveryState: 'unchanged',
609
610
  },
610
611
  };
@@ -619,7 +620,7 @@ class DefaultSessionCheckoutApplyEngine {
619
620
  || completed.treeOid !== snapshot.treeOid) {
620
621
  return {
621
622
  status: 'error',
622
- error: { code: 'git_error', message: 'Checkpoint 已写入,但 Worktree 未收敛到 clean 状态', recoveryState: 'uncertain' },
623
+ error: { code: 'git_error', message: hostMessage('checkpointWasWrittenButWorktreeDidNotReachA'), recoveryState: 'uncertain' },
623
624
  };
624
625
  }
625
626
  return {
@@ -635,7 +636,7 @@ class DefaultSessionCheckoutApplyEngine {
635
636
  status: 'error',
636
637
  error: {
637
638
  code: 'git_error',
638
- message: `Checkpoint 写后无法完成验证,需要保留现场确认:${this.errorMessage(error)}`,
639
+ message: hostMessage('cannotVerifyTheCheckpointAfterWritingTheEnvironmentMust', { p0: this.errorMessage(error) }),
639
640
  recoveryState: 'uncertain',
640
641
  },
641
642
  };
@@ -646,7 +647,7 @@ class DefaultSessionCheckoutApplyEngine {
646
647
  return {
647
648
  status: 'error',
648
649
  error: stale
649
- ? { code: 'stale_isolated', message: 'Worktree index 正在被其他 Git 操作更新,请重试' }
650
+ ? { code: 'stale_isolated', message: hostMessage('anotherGitOperationIsUpdatingTheWorktreeIndexRetry') }
650
651
  : { code: 'git_error', message: this.errorMessage(error) },
651
652
  };
652
653
  }
@@ -661,7 +662,7 @@ class DefaultSessionCheckoutApplyEngine {
661
662
  }
662
663
  async recoverCheckpoint(input) {
663
664
  if (!OID_PATTERN.test(input.commitOid) || !OID_PATTERN.test(input.parentOid) || !OID_PATTERN.test(input.expectedIndexTreeOid)) {
664
- return { status: 'error', error: { code: 'invalid_input', message: 'Checkpoint 恢复 OID 无效' } };
665
+ return { status: 'error', error: { code: 'invalid_input', message: hostMessage('invalidCheckpointRecoveryOID') } };
665
666
  }
666
667
  let tempRoot = null;
667
668
  let ownedIndexLock = null;
@@ -677,12 +678,12 @@ class DefaultSessionCheckoutApplyEngine {
677
678
  const markerOwned = await checkpointLockMarkerOwned(indexLockMarker, input.commitOid);
678
679
  const snapshot = await captureSnapshot(isolatedGitRoot, join(tempRoot, 'current.index'), null, null);
679
680
  if (snapshot.headRef !== null) {
680
- return { status: 'error', error: { code: 'stale_isolated', message: 'Worktree 已不再是 detached HEAD' } };
681
+ return { status: 'error', error: { code: 'stale_isolated', message: hostMessage('worktreeNoLongerHasADetachedHEAD') } };
681
682
  }
682
683
  if (snapshot.headOid === input.parentOid) {
683
684
  if (existingLock.exists) {
684
685
  if (!markerOwned || (existingLock.treeOid !== null && existingLock.treeOid !== targetTreeOid)) {
685
- return { status: 'error', error: { code: 'stale_isolated', message: '遗留 index.lock 无法证明属于当前 Checkpoint,已保留现场' } };
686
+ return { status: 'error', error: { code: 'stale_isolated', message: hostMessage('cannotProveTheRemainingIndexLockBelongsToThe') } };
686
687
  }
687
688
  await unlink(indexLockPath);
688
689
  }
@@ -691,19 +692,19 @@ class DefaultSessionCheckoutApplyEngine {
691
692
  return { status: 'checkpoint_aborted', isolatedFingerprint: snapshot.fingerprint };
692
693
  }
693
694
  if (snapshot.headOid !== input.commitOid) {
694
- return { status: 'error', error: { code: 'stale_isolated', message: 'Worktree HEAD 与待恢复 Checkpoint 不一致' } };
695
+ return { status: 'error', error: { code: 'stale_isolated', message: hostMessage('worktreeHEADDoesNotMatchTheCheckpointBeingRecovered') } };
695
696
  }
696
697
  if (snapshot.treeOid !== snapshot.headTreeOid) {
697
- return { status: 'error', error: { code: 'stale_isolated', message: 'Worktree 在 Checkpoint 中断后出现新修改,不能自动恢复 index' } };
698
+ return { status: 'error', error: { code: 'stale_isolated', message: hostMessage('worktreeHasNewChangesAfterCheckpointInterruptionTheIndex') } };
698
699
  }
699
700
  if (snapshot.indexTreeOid !== input.expectedIndexTreeOid && snapshot.indexTreeOid !== snapshot.headTreeOid) {
700
- return { status: 'error', error: { code: 'stale_isolated', message: 'Checkpoint 中断后 index 出现新 staged 修改,不能自动覆盖' } };
701
+ return { status: 'error', error: { code: 'stale_isolated', message: hostMessage('theIndexHasNewStagedChangesAfterCheckpointInterruption') } };
701
702
  }
702
703
  const cleanIndexPath = join(tempRoot, 'clean.index');
703
704
  await prepareIndexFromPatch(isolatedGitRoot, cleanIndexPath, input.commitOid, Buffer.alloc(0));
704
705
  if (existingLock.exists) {
705
706
  if (!markerOwned || (existingLock.treeOid !== null && existingLock.treeOid !== targetTreeOid)) {
706
- return { status: 'error', error: { code: 'stale_isolated', message: '遗留 index.lock 与 Checkpoint 目标不一致,已保留现场' } };
707
+ return { status: 'error', error: { code: 'stale_isolated', message: hostMessage('theRemainingIndexLockDoesNotMatchTheCheckpoint') } };
707
708
  }
708
709
  if (existingLock.treeOid === null)
709
710
  await copyFile(cleanIndexPath, indexLockPath);
@@ -723,7 +724,7 @@ class DefaultSessionCheckoutApplyEngine {
723
724
  if (lockedSnapshot.headOid !== snapshot.headOid
724
725
  || lockedSnapshot.fingerprint !== snapshot.fingerprint
725
726
  || (lockedSnapshot.indexTreeOid !== input.expectedIndexTreeOid && lockedSnapshot.indexTreeOid !== lockedSnapshot.headTreeOid)) {
726
- return { status: 'error', error: { code: 'stale_isolated', message: 'Checkpoint 恢复加锁前 Worktree 或 index 已变化' } };
727
+ return { status: 'error', error: { code: 'stale_isolated', message: hostMessage('worktreeOrItsIndexChangedBeforeAcquiringTheCheckpoint') } };
727
728
  }
728
729
  await rename(indexLockPath, realIndexPath);
729
730
  ownedIndexLock = null;
@@ -734,7 +735,7 @@ class DefaultSessionCheckoutApplyEngine {
734
735
  || completed.headRef !== null
735
736
  || completed.indexTreeOid !== completed.headTreeOid
736
737
  || completed.treeOid !== completed.headTreeOid) {
737
- return { status: 'error', error: { code: 'git_error', message: 'Checkpoint index 恢复后仍未收敛到 clean 状态', recoveryState: 'uncertain' } };
738
+ return { status: 'error', error: { code: 'git_error', message: hostMessage('worktreeIsStillNotCleanAfterCheckpointIndexRecovery'), recoveryState: 'uncertain' } };
738
739
  }
739
740
  return { status: 'checkpoint_recovered', isolatedFingerprint: completed.fingerprint };
740
741
  }
@@ -750,7 +751,7 @@ class DefaultSessionCheckoutApplyEngine {
750
751
  }
751
752
  async inspectReview(input) {
752
753
  if (!OID_PATTERN.test(input.baseOid)) {
753
- return { status: 'error', error: { code: 'invalid_input', message: 'Session Base OID 格式无效' } };
754
+ return { status: 'error', error: { code: 'invalid_input', message: hostMessage('invalidSessionBaseOIDFormat') } };
754
755
  }
755
756
  let tempRoot = null;
756
757
  try {
@@ -762,18 +763,18 @@ class DefaultSessionCheckoutApplyEngine {
762
763
  const localObjects = await sourceObjectDirectory(localGitRoot);
763
764
  const isolatedObjects = await sourceObjectDirectory(isolatedGitRoot);
764
765
  if (!pathsMatch(localObjects, isolatedObjects)) {
765
- return { status: 'error', error: { code: 'invalid_input', message: 'Local 与 Isolated 不属于同一 Git 仓库' } };
766
+ return { status: 'error', error: { code: 'invalid_input', message: hostMessage('localAndIsolatedDoNotBelongToTheSame') } };
766
767
  }
767
768
  const localPrefix = projectPrefix(localGitRoot, input.localPath);
768
769
  const isolatedPrefix = projectPrefix(isolatedGitRoot, input.isolatedPath);
769
770
  if (localPrefix === null || isolatedPrefix === null || localPrefix !== isolatedPrefix) {
770
- return { status: 'error', error: { code: 'invalid_input', message: 'Local 与 Isolated 的项目子目录不一致' } };
771
+ return { status: 'error', error: { code: 'invalid_input', message: hostMessage('theLocalAndIsolatedProjectSubdirectoriesDoNotMatch') } };
771
772
  }
772
773
  await runGit(localGitRoot, ['cat-file', '-e', `${input.baseOid}^{commit}`]);
773
774
  const isolated = await captureSnapshot(isolatedGitRoot, join(tempRoot, 'isolated.index'), objectDirectory, localObjects);
774
775
  const changedFiles = await changedTreePaths(isolatedGitRoot, input.baseOid, isolated.treeOid, objectDirectory, localObjects);
775
776
  if (changedFiles.some((path) => !isProjectPath(path, localPrefix))) {
776
- return { status: 'error', error: { code: 'invalid_input', message: 'Isolated 包含项目根目录外的变更,不能准备验收' } };
777
+ return { status: 'error', error: { code: 'invalid_input', message: hostMessage('isolatedContainsChangesOutsideTheProjectRootAReview') } };
777
778
  }
778
779
  return {
779
780
  status: 'ready',
@@ -798,7 +799,7 @@ class DefaultSessionCheckoutApplyEngine {
798
799
  }
799
800
  async calculatePlan(input, persistPlan) {
800
801
  if (!OID_PATTERN.test(input.baseOid)) {
801
- return { status: 'error', error: { code: 'invalid_input', message: 'Session Base OID 格式无效' } };
802
+ return { status: 'error', error: { code: 'invalid_input', message: hostMessage('invalidSessionBaseOIDFormat') } };
802
803
  }
803
804
  let tempRoot = null;
804
805
  try {
@@ -810,12 +811,12 @@ class DefaultSessionCheckoutApplyEngine {
810
811
  const localObjects = await sourceObjectDirectory(localGitRoot);
811
812
  const isolatedObjects = await sourceObjectDirectory(isolatedGitRoot);
812
813
  if (!pathsMatch(localObjects, isolatedObjects)) {
813
- return { status: 'error', error: { code: 'invalid_input', message: 'Local 与 Isolated 不属于同一 Git 仓库' } };
814
+ return { status: 'error', error: { code: 'invalid_input', message: hostMessage('localAndIsolatedDoNotBelongToTheSame') } };
814
815
  }
815
816
  const localPrefix = projectPrefix(localGitRoot, input.localPath);
816
817
  const isolatedPrefix = projectPrefix(isolatedGitRoot, input.isolatedPath);
817
818
  if (localPrefix === null || isolatedPrefix === null || localPrefix !== isolatedPrefix) {
818
- return { status: 'error', error: { code: 'invalid_input', message: 'Local 与 Isolated 的项目子目录不一致' } };
819
+ return { status: 'error', error: { code: 'invalid_input', message: hostMessage('theLocalAndIsolatedProjectSubdirectoriesDoNotMatch') } };
819
820
  }
820
821
  await runGit(localGitRoot, ['cat-file', '-e', `${input.baseOid}^{commit}`]);
821
822
  const local = await captureSnapshot(localGitRoot, join(tempRoot, 'local.index'), objectDirectory, localObjects);
@@ -825,7 +826,7 @@ class DefaultSessionCheckoutApplyEngine {
825
826
  if (isolatedChangedPaths.some((path) => !isProjectPath(path, localPrefix))) {
826
827
  return {
827
828
  status: 'error',
828
- error: { code: 'invalid_input', message: 'Isolated 包含项目根目录外的变更,已拒绝 Apply' },
829
+ error: { code: 'invalid_input', message: hostMessage('isolatedContainsChangesOutsideTheProjectRootApplyWas') },
829
830
  };
830
831
  }
831
832
  const merge = await computeMerge(tempRoot, localObjects, objectDirectory, effectiveBase.oid, local, isolated, localGitRoot, localPrefix);
@@ -880,7 +881,7 @@ class DefaultSessionCheckoutApplyEngine {
880
881
  async apply(plan) {
881
882
  const stored = this.plans.get(plan.revision);
882
883
  if (!stored || !planMatches(stored.plan, plan)) {
883
- return { status: 'error', error: { code: 'invalid_plan', message: 'Apply plan 不存在、已使用或已被修改' } };
884
+ return { status: 'error', error: { code: 'invalid_plan', message: hostMessage('theApplyPlanIsMissingAlreadyUsedOrModified') } };
884
885
  }
885
886
  let tempRoot = null;
886
887
  try {
@@ -889,21 +890,21 @@ class DefaultSessionCheckoutApplyEngine {
889
890
  await mkdir(objectDirectory, { recursive: true });
890
891
  const sourceObjects = await sourceObjectDirectory(stored.scope.localGitRoot);
891
892
  if (!pathsMatch(sourceObjects, stored.scope.sourceObjects)) {
892
- return { status: 'error', error: { code: 'invalid_plan', message: 'Apply plan 的 Git 仓库身份已变化' } };
893
+ return { status: 'error', error: { code: 'invalid_plan', message: hostMessage('theApplyPlanSGitRepositoryIdentityHasChanged') } };
893
894
  }
894
895
  const local = await captureSnapshot(stored.scope.localGitRoot, join(tempRoot, 'local.index'), objectDirectory, sourceObjects);
895
896
  if (local.headOid !== stored.plan.localHeadOid || local.fingerprint !== stored.plan.localFingerprint) {
896
- return { status: 'error', error: { code: 'stale_local', message: 'Local 在 plan 后发生变化,请重新计算' } };
897
+ return { status: 'error', error: { code: 'stale_local', message: hostMessage('localChangedAfterPlanningRecalculateThePlan') } };
897
898
  }
898
899
  const isolated = await captureSnapshot(stored.scope.isolatedGitRoot, join(tempRoot, 'isolated.index'), objectDirectory, sourceObjects);
899
900
  if (isolated.headOid !== stored.plan.isolatedHeadOid || isolated.fingerprint !== stored.plan.isolatedFingerprint) {
900
- return { status: 'error', error: { code: 'stale_isolated', message: 'Isolated 在 plan 后发生变化,请重新计算' } };
901
+ return { status: 'error', error: { code: 'stale_isolated', message: hostMessage('isolatedChangedAfterPlanningRecalculateThePlan') } };
901
902
  }
902
903
  // 将已审核的 Isolated 最终状态写成无 ref 的内部 commit,供同一 checkout 后续 Apply 去重。
903
904
  // 使用独立 index,不改变 Isolated 的真实 staged/working tree。
904
905
  const persistentIsolated = await captureSnapshot(stored.scope.isolatedGitRoot, join(tempRoot, 'persistent-isolated.index'), null, null);
905
906
  if (persistentIsolated.fingerprint !== stored.plan.isolatedFingerprint) {
906
- return { status: 'error', error: { code: 'stale_isolated', message: 'Isolated 在 plan 后发生变化,请重新计算' } };
907
+ return { status: 'error', error: { code: 'stale_isolated', message: hostMessage('isolatedChangedAfterPlanningRecalculateThePlan') } };
907
908
  }
908
909
  const nextBaseOid = await createSnapshotCommit(stored.scope.isolatedGitRoot, persistentIsolated.treeOid, stored.plan.effectiveBaseOid, null, null, 'Applied Isolated snapshot');
909
910
  await this.options.beforeFinalLocalValidation?.();
@@ -911,7 +912,7 @@ class DefaultSessionCheckoutApplyEngine {
911
912
  if (finalLocal.headOid !== stored.plan.localHeadOid || finalLocal.fingerprint !== stored.plan.localFingerprint) {
912
913
  return {
913
914
  status: 'error',
914
- error: { code: 'stale_local', message: 'Local 在 Apply 写入前发生变化,请重新计算' },
915
+ error: { code: 'stale_local', message: hostMessage('localChangedBeforeApplyWroteItsChangesRecalculateThe') },
915
916
  };
916
917
  }
917
918
  if (stored.patch.length > 0) {
@@ -931,10 +932,10 @@ class DefaultSessionCheckoutApplyEngine {
931
932
  async preview(plan, options) {
932
933
  const stored = this.plans.get(plan.revision);
933
934
  if (!stored || !planMatches(stored.plan, plan)) {
934
- return { status: 'error', error: { code: 'invalid_plan', message: 'Preview plan 不存在、已使用或已被修改' } };
935
+ return { status: 'error', error: { code: 'invalid_plan', message: hostMessage('thePreviewPlanIsMissingAlreadyUsedOrModified') } };
935
936
  }
936
937
  if (!options.previewId.trim() || !options.reviewId.trim() || !Number.isSafeInteger(options.iteration) || options.iteration < 1) {
937
- return { status: 'error', error: { code: 'invalid_input', message: 'Preview identity 无效' } };
938
+ return { status: 'error', error: { code: 'invalid_input', message: hostMessage('invalidPreviewIdentity') } };
938
939
  }
939
940
  let tempRoot = null;
940
941
  try {
@@ -943,23 +944,23 @@ class DefaultSessionCheckoutApplyEngine {
943
944
  await mkdir(objectDirectory, { recursive: true });
944
945
  const sourceObjects = await sourceObjectDirectory(stored.scope.localGitRoot);
945
946
  if (!pathsMatch(sourceObjects, stored.scope.sourceObjects)) {
946
- return { status: 'error', error: { code: 'invalid_plan', message: 'Preview plan 的 Git 仓库身份已变化' } };
947
+ return { status: 'error', error: { code: 'invalid_plan', message: hostMessage('thePreviewPlanSGitRepositoryIdentityHasChanged') } };
947
948
  }
948
949
  const local = await captureSnapshot(stored.scope.localGitRoot, join(tempRoot, 'local.index'), objectDirectory, sourceObjects);
949
950
  if (local.headOid !== stored.plan.localHeadOid
950
951
  || local.headRef !== stored.plan.localHeadRef
951
952
  || local.fingerprint !== stored.plan.localFingerprint) {
952
- return { status: 'error', error: { code: 'stale_local', message: 'Local 在 plan 后发生变化,请重新计算' } };
953
+ return { status: 'error', error: { code: 'stale_local', message: hostMessage('localChangedAfterPlanningRecalculateThePlan') } };
953
954
  }
954
955
  const isolated = await captureSnapshot(stored.scope.isolatedGitRoot, join(tempRoot, 'isolated.index'), objectDirectory, sourceObjects);
955
956
  if (isolated.headOid !== stored.plan.isolatedHeadOid || isolated.fingerprint !== stored.plan.isolatedFingerprint) {
956
- return { status: 'error', error: { code: 'stale_isolated', message: 'Isolated 在 plan 后发生变化,请重新计算' } };
957
+ return { status: 'error', error: { code: 'stale_isolated', message: hostMessage('isolatedChangedAfterPlanningRecalculateThePlan') } };
957
958
  }
958
959
  const persistentLocal = await captureSnapshot(stored.scope.localGitRoot, join(tempRoot, 'persistent-local.index'), null, null);
959
960
  const persistentIsolated = await captureSnapshot(stored.scope.isolatedGitRoot, join(tempRoot, 'persistent-isolated.index'), null, null);
960
961
  if (persistentLocal.fingerprint !== stored.plan.localFingerprint
961
962
  || persistentIsolated.fingerprint !== stored.plan.isolatedFingerprint) {
962
- return { status: 'error', error: { code: 'invalid_plan', message: 'Preview 持久快照与审核 plan 不一致' } };
963
+ return { status: 'error', error: { code: 'invalid_plan', message: hostMessage('thePersistentPreviewSnapshotDoesNotMatchTheReviewed') } };
963
964
  }
964
965
  const previewWorkingTreeOid = await prepareIndexFromPatch(stored.scope.localGitRoot, join(tempRoot, 'preview-working.index'), persistentLocal.treeOid, stored.patch);
965
966
  const isolatedSnapshotOid = await createSnapshotCommit(stored.scope.isolatedGitRoot, persistentIsolated.treeOid, stored.plan.effectiveBaseOid, null, null, 'Previewed Isolated snapshot');
@@ -986,7 +987,7 @@ class DefaultSessionCheckoutApplyEngine {
986
987
  await this.options.beforeFinalLocalValidation?.();
987
988
  const finalLocal = await captureSnapshot(stored.scope.localGitRoot, join(tempRoot, 'final-local.index'), objectDirectory, sourceObjects);
988
989
  if (finalLocal.headOid !== stored.plan.localHeadOid || finalLocal.fingerprint !== stored.plan.localFingerprint) {
989
- return { status: 'error', error: { code: 'stale_local', message: 'Local 在 Preview 写入前发生变化,请重新计算' } };
990
+ return { status: 'error', error: { code: 'stale_local', message: hostMessage('localChangedBeforePreviewWroteItsChangesRecalculateThe') } };
990
991
  }
991
992
  await options.beforeWrite?.(preparedReceipt);
992
993
  if (stored.patch.length > 0) {
@@ -996,7 +997,7 @@ class DefaultSessionCheckoutApplyEngine {
996
997
  if (previewedLocal.treeOid !== previewWorkingTreeOid) {
997
998
  return {
998
999
  status: 'error',
999
- error: { code: 'git_error', message: 'Preview 写入后的 Local snapshot 与准备结果不一致,需要恢复确认' },
1000
+ error: { code: 'git_error', message: hostMessage('theLocalSnapshotAfterPreviewDoesNotMatchThe') },
1000
1001
  };
1001
1002
  }
1002
1003
  this.plans.delete(plan.revision);
@@ -1066,7 +1067,7 @@ class DefaultSessionCheckoutApplyEngine {
1066
1067
  if (finalLocal.fingerprint !== current.fingerprint
1067
1068
  || finalLocal.headOid !== current.headOid
1068
1069
  || finalLocal.headRef !== current.headRef) {
1069
- return { status: 'error', error: { code: 'stale_local', message: 'Local 在撤回 Preview 前发生变化,请重试' } };
1070
+ return { status: 'error', error: { code: 'stale_local', message: hostMessage('localChangedBeforePreviewRollbackRetry') } };
1070
1071
  }
1071
1072
  if (rollbackPatch.length > 0) {
1072
1073
  await runGit(localGitRoot, ['apply', '--binary', '--whitespace=nowarn'], { input: rollbackPatch });
@@ -1081,7 +1082,7 @@ class DefaultSessionCheckoutApplyEngine {
1081
1082
  status: 'error',
1082
1083
  error: {
1083
1084
  code: 'git_error',
1084
- message: `Preview 撤回写后无法完成验证,需要保留现场确认:${this.errorMessage(error)}`,
1085
+ message: hostMessage('cannotVerifyAfterWritingPreviewRollbackTheEnvironmentMust', { p0: this.errorMessage(error) }),
1085
1086
  recoveryState: 'uncertain',
1086
1087
  },
1087
1088
  };
@@ -1096,7 +1097,7 @@ class DefaultSessionCheckoutApplyEngine {
1096
1097
  status: 'error',
1097
1098
  error: {
1098
1099
  code: 'git_error',
1099
- message: 'Preview 撤回后的 Local snapshot 与安全恢复结果不一致,需要保留现场确认',
1100
+ message: hostMessage('theLocalSnapshotAfterPreviewRollbackDoesNotMatch'),
1100
1101
  recoveryState: 'uncertain',
1101
1102
  },
1102
1103
  };
@@ -1114,7 +1115,7 @@ class DefaultSessionCheckoutApplyEngine {
1114
1115
  async finalize(input) {
1115
1116
  const commitMessage = input.commitMessage.trim();
1116
1117
  if (!commitMessage) {
1117
- return { status: 'error', error: { code: 'invalid_input', message: '提交信息不能为空' } };
1118
+ return { status: 'error', error: { code: 'invalid_input', message: hostMessage('theCommitMessageMustNotBeEmpty') } };
1118
1119
  }
1119
1120
  let tempRoot = null;
1120
1121
  let ownedIndexLock = null;
@@ -1139,7 +1140,7 @@ class DefaultSessionCheckoutApplyEngine {
1139
1140
  if (finalLocal.headOid !== current.headOid
1140
1141
  || finalLocal.headRef !== current.headRef
1141
1142
  || finalLocal.fingerprint !== current.fingerprint) {
1142
- return { status: 'error', error: { code: 'stale_local', message: 'Local 在完成 Preview 前发生变化,请重试' } };
1143
+ return { status: 'error', error: { code: 'stale_local', message: hostMessage('localChangedBeforeFinalizingPreviewRetry') } };
1143
1144
  }
1144
1145
  return {
1145
1146
  status: 'finished',
@@ -1152,13 +1153,13 @@ class DefaultSessionCheckoutApplyEngine {
1152
1153
  const finalIndexPatch = await treePatch(localGitRoot, assessment.taskTreeOid, assessment.finalIndexTreeOid, objectDirectory, sourceObjects);
1153
1154
  const actualTaskTreeOid = await prepareIndexFromPatch(localGitRoot, join(tempRoot, 'task.index'), current.headOid, taskPatch);
1154
1155
  if (actualTaskTreeOid !== assessment.taskTreeOid) {
1155
- return { status: 'error', error: { code: 'git_error', message: 'Preview 任务提交 tree 与恢复评估不一致' } };
1156
+ return { status: 'error', error: { code: 'git_error', message: hostMessage('thePreviewTaskCommitTreeDoesNotMatchThe') } };
1156
1157
  }
1157
1158
  const commitOid = await createUserCommit(localGitRoot, actualTaskTreeOid, current.headOid, commitMessage);
1158
1159
  const finalIndexPath = join(tempRoot, 'final.index');
1159
1160
  const actualFinalIndexTreeOid = await prepareIndexFromPatch(localGitRoot, finalIndexPath, commitOid, finalIndexPatch);
1160
1161
  if (actualFinalIndexTreeOid !== assessment.finalIndexTreeOid) {
1161
- return { status: 'error', error: { code: 'git_error', message: 'Preview 最终 index tree 与恢复评估不一致' } };
1162
+ return { status: 'error', error: { code: 'git_error', message: hostMessage('thePreviewFinalIndexTreeDoesNotMatchThe') } };
1162
1163
  }
1163
1164
  const expectedIndexEntries = (await runGit(localGitRoot, ['ls-files', '--stage', '-z'], { env: { GIT_INDEX_FILE: finalIndexPath } })).stdout;
1164
1165
  const intendedIndexBytes = await readFile(finalIndexPath);
@@ -1166,7 +1167,7 @@ class DefaultSessionCheckoutApplyEngine {
1166
1167
  await input.beforeCommit?.(commitOid);
1167
1168
  const branchRef = current.headRef;
1168
1169
  if (!branchRef?.startsWith('refs/heads/')) {
1169
- return { status: 'error', error: { code: 'operation_not_allowed', message: 'Local 当前不是普通分支,不能自动创建任务提交' } };
1170
+ return { status: 'error', error: { code: 'operation_not_allowed', message: hostMessage('localIsNotOnANormalBranchATask') } };
1170
1171
  }
1171
1172
  backupIndex = `${realIndexPath}.domi-backup-${randomUUID()}`;
1172
1173
  await copyFile(realIndexPath, backupIndex);
@@ -1178,7 +1179,7 @@ class DefaultSessionCheckoutApplyEngine {
1178
1179
  }
1179
1180
  catch (error) {
1180
1181
  if (isIndexLockContention(error)) {
1181
- return { status: 'error', error: { code: 'stale_local', message: 'Local index 正在被其他 Git 操作更新,请重试' } };
1182
+ return { status: 'error', error: { code: 'stale_local', message: hostMessage('anotherGitOperationIsUpdatingTheLocalIndexRetry') } };
1182
1183
  }
1183
1184
  throw error;
1184
1185
  }
@@ -1188,7 +1189,7 @@ class DefaultSessionCheckoutApplyEngine {
1188
1189
  || finalLocal.headRef !== current.headRef
1189
1190
  || finalLocal.fingerprint !== current.fingerprint
1190
1191
  || !finalIndexBytes.equals(originalIndexBytes)) {
1191
- return { status: 'error', error: { code: 'stale_local', message: 'Local 在 Preview 提交写入前发生变化,请重试' } };
1192
+ return { status: 'error', error: { code: 'stale_local', message: hostMessage('localChangedBeforeThePreviewCommitWasWrittenRetry') } };
1192
1193
  }
1193
1194
  let refUpdated = false;
1194
1195
  let indexReplaced = false;
@@ -1223,7 +1224,7 @@ class DefaultSessionCheckoutApplyEngine {
1223
1224
  nextBaseOid: input.receipt.isolatedSnapshotOid,
1224
1225
  };
1225
1226
  }
1226
- writeError = new Error('Preview 提交写后验证失败');
1227
+ writeError = new Error(hostMessage('previewCommitVerificationFailedAfterWriting'));
1227
1228
  }
1228
1229
  catch (error) {
1229
1230
  writeError = error;
@@ -1251,7 +1252,7 @@ class DefaultSessionCheckoutApplyEngine {
1251
1252
  ownedIndexLock = await writeIndexLock(realIndexPath, backupBytes);
1252
1253
  const currentIndexBytes = await readFile(realIndexPath);
1253
1254
  if (!currentIndexBytes.equals(intendedIndexBytes)) {
1254
- rollbackErrors.push('index: Local index 在 Preview 写入后发生变化,拒绝覆盖并发 staged 状态');
1255
+ rollbackErrors.push(hostMessage('indexLocalIndexChangedAfterPreviewWroteItsChanges'));
1255
1256
  }
1256
1257
  else {
1257
1258
  await runGit(localGitRoot, ['update-ref', branchRef, current.headOid, commitOid]);
@@ -1266,7 +1267,7 @@ class DefaultSessionCheckoutApplyEngine {
1266
1267
  }
1267
1268
  }
1268
1269
  else if (refUpdated) {
1269
- rollbackErrors.push('ref: 缺少可验证的 index 补偿证据,拒绝部分回滚');
1270
+ rollbackErrors.push(hostMessage('refVerifiableIndexCompensationEvidenceIsMissingPartialRollback'));
1270
1271
  }
1271
1272
  let restored = false;
1272
1273
  if (rollbackErrors.length === 0) {
@@ -1291,7 +1292,7 @@ class DefaultSessionCheckoutApplyEngine {
1291
1292
  status: 'error',
1292
1293
  error: {
1293
1294
  code: 'git_error',
1294
- message: `Preview 提交写入未能验证,已完整回滚:${this.errorMessage(writeError)}`,
1295
+ message: hostMessage('thePreviewCommitWriteCouldNotBeVerifiedAnd', { p0: this.errorMessage(writeError) }),
1295
1296
  recoveryState: 'unchanged',
1296
1297
  },
1297
1298
  };
@@ -1302,7 +1303,7 @@ class DefaultSessionCheckoutApplyEngine {
1302
1303
  status: 'error',
1303
1304
  error: {
1304
1305
  code: 'git_error',
1305
- message: `Preview 提交写入后无法证明成功或完整回滚,需要保留现场确认:${this.errorMessage(writeError)}${rollbackDetail}`,
1306
+ message: hostMessage('cannotProveSuccessOrCompleteRollbackAfterWritingThe', { p0: this.errorMessage(writeError), p1: rollbackDetail }),
1306
1307
  recoveryState: 'uncertain',
1307
1308
  },
1308
1309
  };
@@ -1321,16 +1322,16 @@ class DefaultSessionCheckoutApplyEngine {
1321
1322
  async finish(plan, options) {
1322
1323
  const stored = this.plans.get(plan.revision);
1323
1324
  if (!stored || !planMatches(stored.plan, plan)) {
1324
- return { status: 'error', error: { code: 'invalid_plan', message: 'Finish plan 不存在、已使用或已被修改' } };
1325
+ return { status: 'error', error: { code: 'invalid_plan', message: hostMessage('theFinishPlanIsMissingAlreadyUsedOrModified') } };
1325
1326
  }
1326
1327
  const commitMessage = options.commitMessage.trim();
1327
1328
  if (!commitMessage) {
1328
- return { status: 'error', error: { code: 'invalid_input', message: '提交信息不能为空' } };
1329
+ return { status: 'error', error: { code: 'invalid_input', message: hostMessage('theCommitMessageMustNotBeEmpty') } };
1329
1330
  }
1330
1331
  if (!stored.plan.localHeadRef?.startsWith('refs/heads/')) {
1331
1332
  return {
1332
1333
  status: 'error',
1333
- error: { code: 'operation_not_allowed', message: 'Local 当前不是普通分支,不能自动创建任务提交' },
1334
+ error: { code: 'operation_not_allowed', message: hostMessage('localIsNotOnANormalBranchATask') },
1334
1335
  };
1335
1336
  }
1336
1337
  let tempRoot = null;
@@ -1341,39 +1342,39 @@ class DefaultSessionCheckoutApplyEngine {
1341
1342
  await mkdir(objectDirectory, { recursive: true });
1342
1343
  const sourceObjects = await sourceObjectDirectory(stored.scope.localGitRoot);
1343
1344
  if (!pathsMatch(sourceObjects, stored.scope.sourceObjects)) {
1344
- return { status: 'error', error: { code: 'invalid_plan', message: 'Finish plan 的 Git 仓库身份已变化' } };
1345
+ return { status: 'error', error: { code: 'invalid_plan', message: hostMessage('theFinishPlanSGitRepositoryIdentityHasChanged') } };
1345
1346
  }
1346
1347
  const local = await captureSnapshot(stored.scope.localGitRoot, join(tempRoot, 'local.index'), objectDirectory, sourceObjects);
1347
1348
  if (local.headOid !== stored.plan.localHeadOid
1348
1349
  || local.headRef !== stored.plan.localHeadRef
1349
1350
  || local.fingerprint !== stored.plan.localFingerprint) {
1350
- return { status: 'error', error: { code: 'stale_local', message: 'Local 在 plan 后发生变化,请重新计算' } };
1351
+ return { status: 'error', error: { code: 'stale_local', message: hostMessage('localChangedAfterPlanningRecalculateThePlan') } };
1351
1352
  }
1352
1353
  const isolated = await captureSnapshot(stored.scope.isolatedGitRoot, join(tempRoot, 'isolated.index'), objectDirectory, sourceObjects);
1353
1354
  if (isolated.headOid !== stored.plan.isolatedHeadOid || isolated.fingerprint !== stored.plan.isolatedFingerprint) {
1354
- return { status: 'error', error: { code: 'stale_isolated', message: 'Isolated 在 plan 后发生变化,请重新计算' } };
1355
+ return { status: 'error', error: { code: 'stale_isolated', message: hostMessage('isolatedChangedAfterPlanningRecalculateThePlan') } };
1355
1356
  }
1356
1357
  const merge = await computeMerge(tempRoot, sourceObjects, objectDirectory, stored.plan.effectiveBaseOid, local, isolated, stored.scope.localGitRoot, stored.scope.projectPrefix);
1357
1358
  if (merge.status === 'conflict') {
1358
1359
  return {
1359
1360
  status: 'error',
1360
- error: { code: 'invalid_plan', message: 'Finish 复验得到与已审核 plan 不一致的冲突' },
1361
+ error: { code: 'invalid_plan', message: hostMessage('finishRevalidationFoundConflictsInconsistentWithTheReviewedPlan') },
1361
1362
  };
1362
1363
  }
1363
1364
  if (merge.changedFiles.length !== stored.plan.changedFiles.length
1364
1365
  || merge.changedFiles.some((path, index) => path !== stored.plan.changedFiles[index])) {
1365
- return { status: 'error', error: { code: 'invalid_plan', message: 'Finish 复验的文件集合已变化' } };
1366
+ return { status: 'error', error: { code: 'invalid_plan', message: hostMessage('theFileSetChangedDuringFinishRevalidation') } };
1366
1367
  }
1367
1368
  const persistentIsolated = await captureSnapshot(stored.scope.isolatedGitRoot, join(tempRoot, 'persistent-isolated.index'), null, null);
1368
1369
  if (persistentIsolated.fingerprint !== stored.plan.isolatedFingerprint) {
1369
- return { status: 'error', error: { code: 'stale_isolated', message: 'Isolated 在 plan 后发生变化,请重新计算' } };
1370
+ return { status: 'error', error: { code: 'stale_isolated', message: hostMessage('isolatedChangedAfterPlanningRecalculateThePlan') } };
1370
1371
  }
1371
1372
  const nextBaseOid = await createSnapshotCommit(stored.scope.isolatedGitRoot, persistentIsolated.treeOid, stored.plan.effectiveBaseOid, null, null, 'Finished Isolated snapshot');
1372
1373
  if (merge.changedFiles.length === 0) {
1373
1374
  await this.options.beforeFinalLocalValidation?.();
1374
1375
  const finalLocal = await captureSnapshot(stored.scope.localGitRoot, join(tempRoot, 'final-local.index'), objectDirectory, sourceObjects);
1375
1376
  if (finalLocal.fingerprint !== stored.plan.localFingerprint) {
1376
- return { status: 'error', error: { code: 'stale_local', message: 'Local 在 Finish 前发生变化,请重新计算' } };
1377
+ return { status: 'error', error: { code: 'stale_local', message: hostMessage('localChangedBeforeFinishRecalculateThePlan') } };
1377
1378
  }
1378
1379
  this.plans.delete(plan.revision);
1379
1380
  return { status: 'finished', changedFiles: [], commitOid: null, nextBaseOid };
@@ -1385,7 +1386,7 @@ class DefaultSessionCheckoutApplyEngine {
1385
1386
  status: 'error',
1386
1387
  error: {
1387
1388
  code: 'commit_isolation_conflict',
1388
- message: `任务增量无法与 Local 原有修改可靠拆分:${taskTree.conflictingFiles.join('、')}`,
1389
+ message: hostMessage('taskChangesCannotBeReliablySeparatedFromExistingLocal', { p0: taskTree.conflictingFiles.join('、') }),
1389
1390
  },
1390
1391
  };
1391
1392
  }
@@ -1396,7 +1397,7 @@ class DefaultSessionCheckoutApplyEngine {
1396
1397
  status: 'error',
1397
1398
  error: {
1398
1399
  code: 'commit_isolation_conflict',
1399
- message: `任务提交与 Local 原有 staged 修改无法可靠分离:${finalIndexTree.conflictingFiles.join('、')}`,
1400
+ message: hostMessage('theTaskCommitCannotBeReliablySeparatedFromExisting', { p0: finalIndexTree.conflictingFiles.join('、') }),
1400
1401
  },
1401
1402
  };
1402
1403
  }
@@ -1412,7 +1413,7 @@ class DefaultSessionCheckoutApplyEngine {
1412
1413
  if (finalLocal.headOid !== stored.plan.localHeadOid
1413
1414
  || finalLocal.headRef !== stored.plan.localHeadRef
1414
1415
  || finalLocal.fingerprint !== stored.plan.localFingerprint) {
1415
- return { status: 'error', error: { code: 'stale_local', message: 'Local 在 Finish 写入前发生变化,请重新计算' } };
1416
+ return { status: 'error', error: { code: 'stale_local', message: hostMessage('localChangedBeforeFinishWroteItsChangesRecalculateThe') } };
1416
1417
  }
1417
1418
  const realIndexPath = await resolveIndexPath(stored.scope.localGitRoot);
1418
1419
  adjacentIndex = `${realIndexPath}.domi-${randomUUID()}`;
@@ -1463,11 +1464,11 @@ class DefaultSessionCheckoutApplyEngine {
1463
1464
  status: 'error',
1464
1465
  error: {
1465
1466
  code: 'git_error',
1466
- message: `Finish 写入失败且无法证明完整回滚:${detail};${rollbackErrors.join(';')}`,
1467
+ message: hostMessage('finishWriteFailedAndCompleteRollbackCouldNotBe', { p0: detail, p1: rollbackErrors.join(';') }),
1467
1468
  },
1468
1469
  };
1469
1470
  }
1470
- return { status: 'error', error: { code: 'git_error', message: `Finish 写入失败,已回滚:${detail}` } };
1471
+ return { status: 'error', error: { code: 'git_error', message: hostMessage('finishWriteFailedAndWasRolledBack', { p0: detail }) } };
1471
1472
  }
1472
1473
  this.plans.delete(plan.revision);
1473
1474
  return {
@@ -1488,9 +1489,9 @@ class DefaultSessionCheckoutApplyEngine {
1488
1489
  }
1489
1490
  errorMessage(error) {
1490
1491
  if (error instanceof GitCommandFailure) {
1491
- return `Git 操作失败(${error.args[0] ?? 'unknown'}):${error.message}`;
1492
+ return hostMessage('gitOperationFailed', { p0: error.args[0] ?? 'unknown', p1: error.message });
1492
1493
  }
1493
- return error instanceof Error ? error.message : '未知 Git 错误';
1494
+ return error instanceof Error ? error.message : hostMessage('unknownGitError');
1494
1495
  }
1495
1496
  async cleanup(path) {
1496
1497
  try {
@@ -1498,7 +1499,7 @@ class DefaultSessionCheckoutApplyEngine {
1498
1499
  }
1499
1500
  catch (error) {
1500
1501
  // 清理失败不能掩盖 plan/apply 的主结果。
1501
- console.warn('[session-checkout-apply] 临时目录清理失败:', error);
1502
+ console.warn(hostMessage('tempDirectoryCleanupFailed'), error);
1502
1503
  }
1503
1504
  }
1504
1505
  }