dsh-easygit-plugin 0.2.0 → 0.2.2

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.
package/lib/index.js CHANGED
@@ -329,6 +329,59 @@ var init_command_policy = __esm({
329
329
  function isRepositoryAction(action) {
330
330
  return REPOSITORY_ACTIONS.includes(action);
331
331
  }
332
+ function repositoryMutationCommand(action, body) {
333
+ const paths = Array.isArray(body.paths) && body.paths.every((path) => typeof path === "string") ? body.paths.map(quoteShellArg).join(" ") : "";
334
+ if (action === "stage-paths" && paths) return "git add -- " + paths;
335
+ if (action === "unstage-paths" && paths) return "git reset HEAD -- " + paths;
336
+ if (action === "stage-all") return "git add -A";
337
+ if (action === "unstage-all") return "git reset HEAD -- :/";
338
+ if (action === "commit" && typeof body.message === "string") return "git commit -m " + quoteShellArg(body.message.trim());
339
+ if (action === "create-branch" && typeof body.name === "string" && typeof body.base === "string") {
340
+ return "git switch -c " + quoteShellArg(body.name) + " " + quoteShellArg(body.base);
341
+ }
342
+ if (action === "switch-branch" && typeof body.name === "string") return "git switch " + quoteShellArg(body.name);
343
+ if (action === "delete-branch" && typeof body.name === "string") {
344
+ return "git branch " + (body.force === true ? "-D" : "-d") + " -- " + quoteShellArg(body.name);
345
+ }
346
+ if (action === "fetch" && typeof body.remote === "string") return "git fetch " + quoteShellArg(body.remote);
347
+ if (action === "pull") return "git pull --ff-only";
348
+ if (action === "push") {
349
+ if (body.setUpstream === true && typeof body.remote === "string" && typeof body.branch === "string") {
350
+ return "git push -u " + quoteShellArg(body.remote) + " " + quoteShellArg(body.branch);
351
+ }
352
+ return "git push";
353
+ }
354
+ if (action === "rebase" && typeof body.target === "string") return "git rebase " + quoteShellArg(body.target);
355
+ if (action === "rebase-continue") return "git -c core.editor=true rebase --continue";
356
+ if (action === "rebase-abort") return "git rebase --abort";
357
+ return null;
358
+ }
359
+ async function attachRepositoryRecovery(action, sessionId, body, result, context, dependencies) {
360
+ const failure = asRecord(result);
361
+ const syncAction = action === "fetch" || action === "pull" || action === "push" || action === "rebase" || action === "rebase-continue" || action === "rebase-abort";
362
+ const recoverable = failure.code === "GIT_FAILED" || failure.code === "TIMEOUT" || failure.reason === "BRANCH_EXISTS" || syncAction && failure.code === "STATE_CONFLICT";
363
+ if (failure.ok !== false || !recoverable) return result;
364
+ const command = repositoryMutationCommand(action, body);
365
+ const operationId = typeof body.operationId === "string" ? body.operationId : "";
366
+ if (!command || !context || !operationId) return result;
367
+ try {
368
+ const handled = await dependencies.recoverFailedCommand(
369
+ sessionId,
370
+ context.workdir,
371
+ operationId,
372
+ action,
373
+ command,
374
+ typeof failure.message === "string" ? failure.message : "Git \u64CD\u4F5C\u5931\u8D25",
375
+ typeof failure.diagnostics === "string" ? failure.diagnostics : "",
376
+ typeof failure.code === "string" ? failure.code : "GIT_FAILED",
377
+ typeof failure.reason === "string" ? failure.reason : ""
378
+ );
379
+ return handled ? { ...failure, ...handled } : result;
380
+ } catch (error) {
381
+ console.log("easygit \u4FEE\u6B63\u63D0\u8BAE\u751F\u6210\u5931\u8D25", errorMessage(error));
382
+ return result;
383
+ }
384
+ }
332
385
  function errorMessage(error) {
333
386
  return error instanceof Error ? error.message : String(error);
334
387
  }
@@ -367,8 +420,7 @@ function sendJson(res, status, data) {
367
420
  });
368
421
  res.end(JSON.stringify(data));
369
422
  }
370
- async function dispatchRepositoryAction(action, sessionId, body, dependencies) {
371
- const context = dependencies.repositoryContext(sessionId);
423
+ async function dispatchRepositoryAction(action, sessionId, body, context, dependencies) {
372
424
  if (!context) return { ok: false, code: "SESSION_NOT_FOUND", message: "\u65E0\u6CD5\u786E\u5B9A\u5F53\u524D\u4F1A\u8BDD\u7684\u4ED3\u5E93\u76EE\u5F55" };
373
425
  const repository = dependencies.repository;
374
426
  const base = { sessionId, workdir: context.workdir, operationId: body.operationId, sandboxPolicy: context.policy };
@@ -379,6 +431,7 @@ async function dispatchRepositoryAction(action, sessionId, body, dependencies) {
379
431
  if (action === "get-commit-detail") return repository.getCommitDetail(context.workdir, body.hash, void 0, context.policy);
380
432
  if (action === "get-commit-diff") return repository.getCommitDiff(context.workdir, body.hash, void 0, context.policy);
381
433
  if (action === "get-stashes") return repository.getStashes(context.workdir, void 0, context.policy);
434
+ if (action === "get-sync-state") return repository.getSyncState(context.workdir, void 0, context.policy);
382
435
  if (action === "stage-paths") return repository.stagePaths(base, body.paths);
383
436
  if (action === "unstage-paths") return repository.unstagePaths(base, body.paths);
384
437
  if (action === "stage-all") return repository.stageAll(base);
@@ -386,9 +439,24 @@ async function dispatchRepositoryAction(action, sessionId, body, dependencies) {
386
439
  if (action === "commit") return repository.commit(base, body.message);
387
440
  if (action === "create-branch") return repository.createBranch(base, body.name, body.base);
388
441
  if (action === "switch-branch") return repository.switchBranch(base, body.name);
389
- return repository.deleteBranch(base, body.name, body.force === true, body.confirmRisk === true);
442
+ if (action === "delete-branch") return repository.deleteBranch(base, body.name, body.force === true, body.confirmRisk === true);
443
+ if (action === "fetch") return repository.fetchRemote(base, body.remote);
444
+ if (action === "pull") return repository.pullFfOnly(base);
445
+ if (action === "push") return repository.pushCurrent(base, body.remote, body.branch, body.setUpstream === true);
446
+ if (action === "rebase") return repository.rebaseOnto(base, body.target, body.confirmRisk === true);
447
+ if (action === "rebase-continue") return repository.continueRebase(base, body.confirmRisk === true);
448
+ return repository.abortRebase(base, body.confirmRisk === true);
390
449
  }
391
450
  async function dispatchProposalAction(action, sessionId, body, dependencies) {
451
+ if (action === "request-analysis") {
452
+ const proposal = dependencies.findProposal(sessionId, body.proposalId);
453
+ if (!proposal || proposal.closed || proposal.status !== "failed" || proposal.needsAgentAnalysis !== true || !proposal.failure) {
454
+ return { status: 200, data: { ok: false, error: "\u8BE5\u5931\u8D25\u8BB0\u5F55\u5DF2\u4E0D\u518D\u7B49\u5F85 Agent \u5206\u6790" } };
455
+ }
456
+ proposal.analysisRequestedAt = Date.now();
457
+ await dependencies.flushProposal(sessionId);
458
+ return { status: 200, data: { ok: true } };
459
+ }
392
460
  if (action === "state") {
393
461
  const proposal = dependencies.latestPending(sessionId);
394
462
  if (proposal && proposal.status === "pending" && proposal.copied === true && proposal.fingerprint) {
@@ -452,16 +520,16 @@ async function dispatchProposalAction(action, sessionId, body, dependencies) {
452
520
  dependencies.resolveExecutionPolicy(sessionId),
453
521
  () => dependencies.flushProposal(sessionId)
454
522
  );
455
- console.log("git-guide HTTP execute", proposal.proposalId, "ok=", result.ok);
523
+ console.log("easygit HTTP execute", proposal.proposalId, "ok=", result.ok);
456
524
  return { status: 200, data: result };
457
525
  }
458
526
  return { status: 400, data: { ok: false, error: "unknown action: " + action } };
459
527
  }
460
- function registerGitGuideActions(webServer, dependencies) {
528
+ function registerEasyGitActions(webServer, dependencies) {
461
529
  if (!webServer) return void 0;
462
530
  return webServer.register({
463
531
  kind: "prefix",
464
- path: "/git-guide",
532
+ path: "/easygit",
465
533
  handler: async (req, res) => {
466
534
  if (req.method !== "POST") {
467
535
  sendJson(res, 405, { ok: false, error: "method not allowed" });
@@ -497,7 +565,9 @@ function registerGitGuideActions(webServer, dependencies) {
497
565
  try {
498
566
  await dependencies.proposalStorageReady;
499
567
  if (isRepositoryAction(action)) {
500
- sendJson(res, 200, await dispatchRepositoryAction(action, sessionId, body, dependencies));
568
+ const context = dependencies.repositoryContext(sessionId);
569
+ const result = await dispatchRepositoryAction(action, sessionId, body, context, dependencies);
570
+ sendJson(res, 200, await attachRepositoryRecovery(action, sessionId, body, result, context, dependencies));
501
571
  return;
502
572
  }
503
573
  const response = await dispatchProposalAction(action, sessionId, body, dependencies);
@@ -522,6 +592,7 @@ var init_actions = __esm({
522
592
  "get-commit-detail",
523
593
  "get-commit-diff",
524
594
  "get-stashes",
595
+ "get-sync-state",
525
596
  "stage-paths",
526
597
  "unstage-paths",
527
598
  "stage-all",
@@ -529,7 +600,13 @@ var init_actions = __esm({
529
600
  "commit",
530
601
  "create-branch",
531
602
  "switch-branch",
532
- "delete-branch"
603
+ "delete-branch",
604
+ "fetch",
605
+ "pull",
606
+ "push",
607
+ "rebase",
608
+ "rebase-continue",
609
+ "rebase-abort"
533
610
  ];
534
611
  }
535
612
  });
@@ -538,6 +615,10 @@ var init_actions = __esm({
538
615
  function isRecord(value) {
539
616
  return value !== null && typeof value === "object" && !Array.isArray(value);
540
617
  }
618
+ function isGitFailureContext(value) {
619
+ if (!isRecord(value)) return false;
620
+ return (value.source === "workbench" || value.source === "proposal") && typeof value.code === "string" && typeof value.action === "string" && typeof value.command === "string" && typeof value.message === "string" && typeof value.stdout === "string" && typeof value.stderr === "string" && typeof value.diagnostics === "string" && (value.exitCode === null || typeof value.exitCode === "number" && Number.isSafeInteger(value.exitCode)) && typeof value.timedOut === "boolean" && typeof value.mayHavePartialChanges === "boolean" && typeof value.occurredAt === "number" && Number.isSafeInteger(value.occurredAt);
621
+ }
541
622
  function isStoredProposal(value, sessionId) {
542
623
  if (!isRecord(value) || value.sessionId !== sessionId) return false;
543
624
  if (typeof value.proposalId !== "string" || !/^g-[0-9a-f-]{36}$/i.test(value.proposalId)) return false;
@@ -548,7 +629,7 @@ function isStoredProposal(value, sessionId) {
548
629
  if (!Array.isArray(value.reasons) || !value.reasons.every((reason) => typeof reason === "string")) return false;
549
630
  if (!Array.isArray(value.steps) || value.steps.length === 0 || value.steps.length > 10) return false;
550
631
  if (!value.steps.every((step) => isRecord(step) && typeof step.command === "string" && (step.result === null || isRecord(step.result)))) return false;
551
- return typeof value.confirmed === "boolean" && typeof value.closed === "boolean" && typeof value.copied === "boolean" && typeof value.verified === "boolean" && (value.fingerprint === null || typeof value.fingerprint === "string") && (value.result === null || isRecord(value.result));
632
+ return typeof value.confirmed === "boolean" && typeof value.closed === "boolean" && typeof value.copied === "boolean" && typeof value.verified === "boolean" && (value.fingerprint === null || typeof value.fingerprint === "string") && (value.result === null || isRecord(value.result)) && (value.failure === void 0 || isGitFailureContext(value.failure)) && (value.recoverySuggestion === void 0 || typeof value.recoverySuggestion === "string") && (value.needsAgentAnalysis === void 0 || typeof value.needsAgentAnalysis === "boolean") && (value.analysisRequestedAt === void 0 || typeof value.analysisRequestedAt === "number" && Number.isSafeInteger(value.analysisRequestedAt));
552
633
  }
553
634
  var import_node_crypto, DEFAULT_PROPOSALS_PER_SESSION, DEFAULT_MAX_SESSIONS, DEFAULT_SESSION_TTL_MS, ProposalService, proposalService;
554
635
  var init_proposal_service = __esm({
@@ -679,6 +760,10 @@ var init_proposal_service = __esm({
679
760
  result: proposal.result,
680
761
  closed: proposal.closed,
681
762
  copied: proposal.copied,
763
+ ...proposal.failure ? { failure: proposal.failure } : {},
764
+ ...typeof proposal.recoverySuggestion === "string" ? { recoverySuggestion: proposal.recoverySuggestion } : {},
765
+ ...proposal.needsAgentAnalysis === true ? { needsAgentAnalysis: true } : {},
766
+ ...typeof proposal.analysisRequestedAt === "number" ? { analysisRequestedAt: proposal.analysisRequestedAt } : {},
682
767
  status: proposal.status || (proposal.closed ? "dismissed" : "pending")
683
768
  };
684
769
  }
@@ -724,6 +809,15 @@ function validPathspec(path) {
724
809
  function validBranchName(name) {
725
810
  return typeof name === "string" && name.length > 0 && name.length <= 255 && !/[\0\r\n\s~^:?*\[\\]/.test(name) && !name.startsWith("-") && !name.startsWith(".") && !name.endsWith(".") && !name.includes("..") && !name.includes("@{") && !name.endsWith(".lock");
726
811
  }
812
+ function validRemoteName(name) {
813
+ return typeof name === "string" && name.length > 0 && name.length <= 255 && /^[A-Za-z0-9][A-Za-z0-9._/-]*$/.test(name) && !name.includes("..") && !name.includes("@{") && !name.endsWith(".lock");
814
+ }
815
+ function conflictCount(files) {
816
+ return files.filter((file) => {
817
+ const status = file.indexStatus + file.workTreeStatus;
818
+ return status.includes("U") || status === "AA" || status === "DD";
819
+ }).length;
820
+ }
727
821
  function validCommitHash(hash) {
728
822
  return typeof hash === "string" && /^(?:[0-9a-f]{40}|[0-9a-f]{64})$/i.test(hash);
729
823
  }
@@ -1007,6 +1101,103 @@ var init_git_repository_service = __esm({
1007
1101
  });
1008
1102
  return { ok: true, data: stashes };
1009
1103
  }
1104
+ async getSyncState(workdir, signal, sandboxPolicy) {
1105
+ const summary = await this.getSummary(workdir, signal, sandboxPolicy);
1106
+ if (!summary.ok) return summary;
1107
+ const [remoteResult, upstreamResult, rebaseResult] = await Promise.all([
1108
+ this.run(workdir, "git remote", 15e3, 2e4, signal, sandboxPolicy),
1109
+ this.run(workdir, "git rev-parse --abbrev-ref --symbolic-full-name @{upstream}", 15e3, 4096, signal, sandboxPolicy),
1110
+ this.run(workdir, 'test -d "$(git rev-parse --git-path rebase-merge)" || test -d "$(git rev-parse --git-path rebase-apply)"', 15e3, 4096, signal, sandboxPolicy)
1111
+ ]);
1112
+ if (remoteResult.exitCode !== 0) {
1113
+ return errorResult("GIT_FAILED", "\u65E0\u6CD5\u8BFB\u53D6\u8FDC\u7A0B\u4ED3\u5E93", redactAndLimit(outputOf(remoteResult), 8192));
1114
+ }
1115
+ const remotes = (remoteResult.stdout?.text ?? "").split("\n").map((entry) => redactAndLimit(entry.trim(), 255)).filter(Boolean);
1116
+ const upstream = upstreamResult.exitCode === 0 ? redactAndLimit(upstreamResult.stdout?.text ?? "", 512).trim() : "";
1117
+ let ahead = 0;
1118
+ let behind = 0;
1119
+ if (upstream) {
1120
+ const counts = await this.run(workdir, "git rev-list --left-right --count HEAD...@{upstream}", 15e3, 4096, signal, sandboxPolicy);
1121
+ if (counts.exitCode !== 0) return errorResult("GIT_FAILED", "\u65E0\u6CD5\u8BA1\u7B97\u672C\u5730\u4E0E\u4E0A\u6E38\u7684\u63D0\u4EA4\u5DEE\u5F02", redactAndLimit(outputOf(counts), 8192));
1122
+ const [aheadText = "0", behindText = "0"] = (counts.stdout?.text ?? "").trim().split(/\s+/);
1123
+ ahead = /^\d+$/.test(aheadText) ? Number(aheadText) : 0;
1124
+ behind = /^\d+$/.test(behindText) ? Number(behindText) : 0;
1125
+ }
1126
+ const conflicts = conflictCount(summary.data.files);
1127
+ return {
1128
+ ok: true,
1129
+ data: {
1130
+ topLevel: summary.data.topLevel,
1131
+ branch: summary.data.branch,
1132
+ head: summary.data.head,
1133
+ upstream,
1134
+ remotes,
1135
+ ahead,
1136
+ behind,
1137
+ dirty: summary.data.files.length > 0,
1138
+ conflictCount: conflicts,
1139
+ rebaseInProgress: rebaseResult.exitCode === 0,
1140
+ files: summary.data.files
1141
+ }
1142
+ };
1143
+ }
1144
+ async fetchRemote(request, remote) {
1145
+ if (!validRemoteName(remote)) return errorResult("INVALID_ARGUMENT", "\u8FDC\u7A0B\u4ED3\u5E93\u540D\u79F0\u4E0D\u5408\u6CD5");
1146
+ const state = await this.getSyncState(request.workdir, request.signal, request.sandboxPolicy);
1147
+ if (!state.ok) return state;
1148
+ if (!state.data.remotes.includes(remote)) return errorResult("STATE_CONFLICT", "\u8FDC\u7A0B\u4ED3\u5E93\u4E0D\u5B58\u5728", void 0, "NO_REMOTE");
1149
+ return this.mutateSync(request, "git fetch " + quoteShellArg(remote), "\u83B7\u53D6\u8FDC\u7A0B\u66F4\u65B0\u5931\u8D25");
1150
+ }
1151
+ async pullFfOnly(request) {
1152
+ const state = await this.getSyncState(request.workdir, request.signal, request.sandboxPolicy);
1153
+ if (!state.ok) return state;
1154
+ if (!state.data.branch) return errorResult("STATE_CONFLICT", "\u5206\u79BB HEAD \u72B6\u6001\u4E0D\u80FD\u76F4\u63A5\u62C9\u53D6", void 0, "DETACHED_HEAD");
1155
+ if (!state.data.upstream) return errorResult("STATE_CONFLICT", "\u5F53\u524D\u5206\u652F\u6CA1\u6709\u4E0A\u6E38\u8DDF\u8E2A\u5206\u652F", void 0, "NO_UPSTREAM");
1156
+ if (state.data.rebaseInProgress) return errorResult("STATE_CONFLICT", "Rebase \u8FDB\u884C\u4E2D\uFF0C\u8BF7\u5148\u7EE7\u7EED\u6216\u4E2D\u6B62", void 0, "REBASE_IN_PROGRESS");
1157
+ if (state.data.conflictCount > 0) return errorResult("STATE_CONFLICT", "\u5B58\u5728\u5C1A\u672A\u89E3\u51B3\u7684\u51B2\u7A81", void 0, "CONFLICTS_PRESENT");
1158
+ return this.mutateSync(request, "git pull --ff-only", "\u62C9\u53D6\u8FDC\u7A0B\u66F4\u65B0\u5931\u8D25");
1159
+ }
1160
+ async pushCurrent(request, remote, branch, setUpstream) {
1161
+ const state = await this.getSyncState(request.workdir, request.signal, request.sandboxPolicy);
1162
+ if (!state.ok) return state;
1163
+ if (!state.data.branch) return errorResult("STATE_CONFLICT", "\u5206\u79BB HEAD \u72B6\u6001\u4E0D\u80FD\u76F4\u63A5\u63A8\u9001", void 0, "DETACHED_HEAD");
1164
+ if (state.data.rebaseInProgress) return errorResult("STATE_CONFLICT", "Rebase \u8FDB\u884C\u4E2D\uFF0C\u8BF7\u5148\u7EE7\u7EED\u6216\u4E2D\u6B62", void 0, "REBASE_IN_PROGRESS");
1165
+ if (setUpstream) {
1166
+ if (state.data.upstream) return errorResult("STATE_CONFLICT", "\u5F53\u524D\u5206\u652F\u5DF2\u7ECF\u6709\u4E0A\u6E38\uFF0C\u8BF7\u4F7F\u7528\u666E\u901A\u63A8\u9001");
1167
+ if (!validRemoteName(remote) || !state.data.remotes.includes(remote)) return errorResult("STATE_CONFLICT", "\u8BF7\u9009\u62E9\u5B58\u5728\u7684\u8FDC\u7A0B\u4ED3\u5E93", void 0, "NO_REMOTE");
1168
+ if (!validBranchName(branch) || branch !== state.data.branch) return errorResult("INVALID_ARGUMENT", "\u53EA\u80FD\u4E3A\u5F53\u524D\u672C\u5730\u5206\u652F\u5EFA\u7ACB\u4E0A\u6E38");
1169
+ return this.mutateSync(request, "git push -u " + quoteShellArg(remote) + " " + quoteShellArg(branch), "\u63A8\u9001\u5E76\u5EFA\u7ACB\u4E0A\u6E38\u5931\u8D25");
1170
+ }
1171
+ if (!state.data.upstream) return errorResult("STATE_CONFLICT", "\u5F53\u524D\u5206\u652F\u6CA1\u6709\u4E0A\u6E38\u8DDF\u8E2A\u5206\u652F", void 0, "NO_UPSTREAM");
1172
+ return this.mutateSync(request, "git push", "\u63A8\u9001\u5931\u8D25");
1173
+ }
1174
+ async rebaseOnto(request, target, confirmRisk) {
1175
+ if (!validBranchName(target)) return errorResult("INVALID_ARGUMENT", "Rebase \u76EE\u6807\u5FC5\u987B\u662F\u5B89\u5168\u7684\u672C\u5730\u6216\u8FDC\u7A0B\u5206\u652F\u5F15\u7528");
1176
+ if (!confirmRisk) return errorResult("PERMISSION_DENIED", "Rebase \u4F1A\u91CD\u5199\u672C\u5730\u63D0\u4EA4\u5386\u53F2\uFF0C\u6267\u884C\u524D\u5FC5\u987B\u786E\u8BA4\u98CE\u9669");
1177
+ const state = await this.getSyncState(request.workdir, request.signal, request.sandboxPolicy);
1178
+ if (!state.ok) return state;
1179
+ if (!state.data.branch) return errorResult("STATE_CONFLICT", "\u5206\u79BB HEAD \u72B6\u6001\u4E0D\u80FD\u5F00\u59CB Rebase", void 0, "DETACHED_HEAD");
1180
+ if (state.data.rebaseInProgress) return errorResult("STATE_CONFLICT", "\u5DF2\u6709 Rebase \u6B63\u5728\u8FDB\u884C", void 0, "REBASE_IN_PROGRESS");
1181
+ if (state.data.dirty) return errorResult("STATE_CONFLICT", "Rebase \u524D\u9700\u8981\u63D0\u4EA4\u6216\u8D2E\u85CF\u5DE5\u4F5C\u533A\u6539\u52A8", void 0, "DIRTY_WORKTREE");
1182
+ const exists = await this.run(request.workdir, "git rev-parse --verify --quiet " + quoteShellArg(target + "^{commit}"), 15e3, 4096, request.signal, request.sandboxPolicy);
1183
+ if (exists.exitCode !== 0) return errorResult("STATE_CONFLICT", "Rebase \u76EE\u6807\u5F15\u7528\u4E0D\u5B58\u5728", void 0, "REF_NOT_FOUND");
1184
+ return this.mutateSync(request, "git rebase " + quoteShellArg(target), "Rebase \u5931\u8D25");
1185
+ }
1186
+ async continueRebase(request, confirmRisk) {
1187
+ if (!confirmRisk) return errorResult("PERMISSION_DENIED", "\u7EE7\u7EED Rebase \u524D\u5FC5\u987B\u786E\u8BA4\u5386\u53F2\u91CD\u5199\u98CE\u9669");
1188
+ const state = await this.getSyncState(request.workdir, request.signal, request.sandboxPolicy);
1189
+ if (!state.ok) return state;
1190
+ if (!state.data.rebaseInProgress) return errorResult("STATE_CONFLICT", "\u5F53\u524D\u6CA1\u6709\u6B63\u5728\u8FDB\u884C\u7684 Rebase", void 0, "NO_REBASE_IN_PROGRESS");
1191
+ if (state.data.conflictCount > 0) return errorResult("STATE_CONFLICT", "\u4ECD\u6709\u51B2\u7A81\u6587\u4EF6\uFF0C\u8BF7\u89E3\u51B3\u5E76\u6682\u5B58\u540E\u518D\u7EE7\u7EED", void 0, "CONFLICTS_PRESENT");
1192
+ return this.mutateSync(request, "git -c core.editor=true rebase --continue", "\u7EE7\u7EED Rebase \u5931\u8D25");
1193
+ }
1194
+ async abortRebase(request, confirmRisk) {
1195
+ if (!confirmRisk) return errorResult("PERMISSION_DENIED", "\u4E2D\u6B62 Rebase \u4F1A\u4E22\u5F03\u672C\u6B21\u53D8\u57FA\u8FC7\u7A0B\u4E2D\u7684\u4FEE\u6539\uFF0C\u6267\u884C\u524D\u5FC5\u987B\u786E\u8BA4\u98CE\u9669");
1196
+ const state = await this.getSyncState(request.workdir, request.signal, request.sandboxPolicy);
1197
+ if (!state.ok) return state;
1198
+ if (!state.data.rebaseInProgress) return errorResult("STATE_CONFLICT", "\u5F53\u524D\u6CA1\u6709\u6B63\u5728\u8FDB\u884C\u7684 Rebase", void 0, "NO_REBASE_IN_PROGRESS");
1199
+ return this.mutateSync(request, "git rebase --abort", "\u4E2D\u6B62 Rebase \u5931\u8D25");
1200
+ }
1010
1201
  async stagePaths(request, paths) {
1011
1202
  const valid = this.validatePaths(paths);
1012
1203
  if (!valid.ok) return valid;
@@ -1031,6 +1222,18 @@ var init_git_repository_service = __esm({
1031
1222
  }
1032
1223
  async createBranch(request, name, base) {
1033
1224
  if (!validBranchName(name) || !validBranchName(base)) return errorResult("INVALID_ARGUMENT", "\u5206\u652F\u540D\u548C\u57FA\u7840\u5206\u652F\u5FC5\u987B\u662F\u5B89\u5168\u7684\u672C\u5730 Git \u5F15\u7528");
1225
+ const repository = await this.getTopLevel(request.workdir, request.signal, request.sandboxPolicy);
1226
+ if (!repository.ok) return repository;
1227
+ const exists = await this.run(
1228
+ request.workdir,
1229
+ "git show-ref --verify --quiet " + quoteShellArg("refs/heads/" + name),
1230
+ 15e3,
1231
+ 4096,
1232
+ request.signal,
1233
+ request.sandboxPolicy
1234
+ );
1235
+ if (exists.exitCode === 0) return errorResult("STATE_CONFLICT", "\u540C\u540D\u672C\u5730\u5206\u652F\u5DF2\u7ECF\u5B58\u5728", void 0, "BRANCH_EXISTS");
1236
+ if (exists.exitCode !== 1) return errorResult("GIT_FAILED", "\u65E0\u6CD5\u68C0\u67E5\u76EE\u6807\u5206\u652F\u662F\u5426\u5B58\u5728", redactAndLimit(outputOf(exists), 8192));
1034
1237
  return this.mutate(request, "git switch -c " + quoteShellArg(name) + " " + quoteShellArg(base), false, "\u521B\u5EFA\u5206\u652F\u5931\u8D25");
1035
1238
  }
1036
1239
  async switchBranch(request, name) {
@@ -1066,7 +1269,13 @@ var init_git_repository_service = __esm({
1066
1269
  }
1067
1270
  return { ok: true, data: paths };
1068
1271
  }
1069
- async mutate(request, command, requiresStagedContent = false, failureMessage = "Git \u64CD\u4F5C\u5931\u8D25") {
1272
+ mutate(request, command, requiresStagedContent = false, failureMessage = "Git \u64CD\u4F5C\u5931\u8D25") {
1273
+ return this.mutateAndRead(request, command, failureMessage, requiresStagedContent, () => this.getSummary(request.workdir, request.signal, request.sandboxPolicy));
1274
+ }
1275
+ mutateSync(request, command, failureMessage) {
1276
+ return this.mutateAndRead(request, command, failureMessage, false, () => this.getSyncState(request.workdir, request.signal, request.sandboxPolicy));
1277
+ }
1278
+ async mutateAndRead(request, command, failureMessage, requiresStagedContent, readResult) {
1070
1279
  if (!request.sessionId || !request.workdir) return errorResult("SESSION_NOT_FOUND", "\u65E0\u6CD5\u786E\u5B9A\u5F53\u524D\u4F1A\u8BDD\u7684\u4ED3\u5E93\u76EE\u5F55");
1071
1280
  if (typeof request.operationId !== "string" || !/^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$/.test(request.operationId)) {
1072
1281
  return errorResult("INVALID_ARGUMENT", "operationId \u5FC5\u987B\u662F 1\u2013128 \u4E2A\u5B89\u5168\u5B57\u7B26");
@@ -1095,8 +1304,8 @@ var init_git_repository_service = __esm({
1095
1304
  }
1096
1305
  const executed = await this.run(request.workdir, command, 12e4, MUTATION_OUTPUT_MAX_CHARS, request.signal, request.sandboxPolicy);
1097
1306
  if (executed.exitCode !== 0) return errorResult(mutationErrorCode(executed), failureMessage, redactAndLimit(outputOf(executed), 8192));
1098
- const summary = await this.getSummary(request.workdir, request.signal, request.sandboxPolicy);
1099
- return summary.ok ? { ...summary, operationId: String(request.operationId) } : summary;
1307
+ const result2 = await readResult();
1308
+ return result2.ok ? { ...result2, operationId: String(request.operationId) } : result2;
1100
1309
  } finally {
1101
1310
  release();
1102
1311
  if (this.locks.get(lockKey) === queued) this.locks.delete(lockKey);
@@ -1330,10 +1539,28 @@ var require_plugin = __commonJS({
1330
1539
  const failedStep = proposal.steps.find((step) => step.result?.ok === false);
1331
1540
  const lastResult = failedStep ? failedStep.result : last?.result;
1332
1541
  const diagnostics = ok ? "" : await captureDiagnostics(shell, proposal.workdir);
1542
+ const error = ok ? "" : redactSecrets(lastResult && (lastResult.stderr || lastResult.stdout) || "git \u9000\u51FA\u7801 " + (lastResult ? lastResult.exitCode : -1));
1543
+ const failure = ok ? void 0 : {
1544
+ source: "proposal",
1545
+ code: lastResult?.timedOut === true ? "TIMEOUT" : "GIT_FAILED",
1546
+ action: "execute",
1547
+ command: failedStep?.command || proposal.command,
1548
+ message: error,
1549
+ stdout: redactSecrets(lastResult ? lastResult.stdout : ""),
1550
+ stderr: redactSecrets(lastResult ? lastResult.stderr : ""),
1551
+ diagnostics,
1552
+ exitCode: lastResult ? lastResult.exitCode : null,
1553
+ timedOut: lastResult?.timedOut === true,
1554
+ mayHavePartialChanges: proposal.steps.some((step) => step !== failedStep && step.result?.ok === true),
1555
+ occurredAt: Date.now()
1556
+ };
1557
+ proposal.failure = failure;
1333
1558
  const recovery = ok ? null : buildRecovery(proposal, failedStep, diagnostics);
1334
- if (recovery && recovery.command) {
1335
- const corrected = registerRecoveryProposal(proposal, recovery);
1559
+ if (recovery && recovery.command && failure) {
1560
+ const corrected = registerRecoveryProposal(proposal, recovery, failure);
1336
1561
  if (corrected) recovery.proposalId = corrected.proposalId;
1562
+ } else if (!ok && failure) {
1563
+ proposal.needsAgentAnalysis = true;
1337
1564
  }
1338
1565
  if (typeof persistStatus === "function") await persistStatus();
1339
1566
  return {
@@ -1347,14 +1574,19 @@ var require_plugin = __commonJS({
1347
1574
  stdout: redactSecrets(lastResult ? lastResult.stdout : ""),
1348
1575
  stderr: redactSecrets(lastResult ? lastResult.stderr : ""),
1349
1576
  diagnostics,
1577
+ ...failure ? { failure } : {},
1350
1578
  recovery: recovery ? { suggestion: recovery.suggestion, command: recovery.command || "", proposalId: recovery.proposalId || null } : null,
1351
- error: ok ? "" : redactSecrets(lastResult && (lastResult.stderr || lastResult.stdout) || "git \u9000\u51FA\u7801 " + (lastResult ? lastResult.exitCode : -1))
1579
+ ...!ok && failure && !recovery?.command ? { analysis: { proposalId: proposal.proposalId } } : {},
1580
+ error
1352
1581
  };
1353
1582
  }
1354
1583
  function buildRecovery(_proposal, failedStep, diagnostics) {
1355
1584
  if (!failedStep || !failedStep.result) return null;
1356
- const text = String(((failedStep.result.stderr || "") + " " + (failedStep.result.stdout || "") + " " + (diagnostics || "")).trim());
1357
1585
  const cmd = String(failedStep.command || "");
1586
+ const text = String(((failedStep.result.stderr || "") + " " + (failedStep.result.stdout || "") + " " + (diagnostics || "")).trim());
1587
+ return buildRecoveryForCommand(cmd, text);
1588
+ }
1589
+ function buildRecoveryForCommand(cmd, text, reason = "") {
1358
1590
  if (/只读文件系统|read-only file system|EROFS|cannot lock ref|cannot create .*\.lock/i.test(text)) {
1359
1591
  return { suggestion: "\u6267\u884C\u73AF\u5883\u5BF9\u76EE\u6807\u76EE\u5F55\u53EA\u8BFB\uFF08\u6C99\u7BB1\u7B56\u7565\u6216\u6302\u8F7D\u95EE\u9898\uFF09\uFF1A\u8BF7\u5728\u7EC8\u7AEF\u624B\u52A8\u6267\u884C\u8BE5\u547D\u4EE4\uFF0C\u6216\u8C03\u6574\u6267\u884C\u73AF\u5883\u7684\u6C99\u7BB1\u6743\u9650\u3002", command: null };
1360
1592
  }
@@ -1362,11 +1594,13 @@ var require_plugin = __commonJS({
1362
1594
  const corrected = cmd.replace(/git\s+add\s+/, "git add -f ");
1363
1595
  if (corrected !== cmd) return { suggestion: "\u76EE\u6807\u6587\u4EF6\u88AB .gitignore \u5FFD\u7565\uFF1A\u6539\u7528 -f \u5F3A\u5236\u52A0\u5165\uFF08\u4EC5\u9488\u5BF9\u660E\u786E\u5217\u51FA\u7684\u6587\u4EF6\uFF09\u3002", command: corrected };
1364
1596
  }
1365
- if (/already exists|分支.*已存在|already exist/i.test(text)) {
1366
- const m = cmd.match(/git\s+(?:switch\s+-c|checkout\s+-b)\s+([^\s]+)/);
1367
- if (m) {
1368
- const corrected = cmd.replace(/switch\s+-c/, "switch").replace(/checkout\s+-b/, "checkout");
1369
- return { suggestion: "\u5206\u652F " + m[1] + " \u5DF2\u5B58\u5728\uFF1A\u6539\u4E3A\u5207\u6362\u5230\u73B0\u6709\u5206\u652F\uFF08\u6216\u6362\u4E00\u4E2A\u5206\u652F\u540D\uFF09\u3002", command: corrected };
1597
+ if (reason === "BRANCH_EXISTS" || /already exist(?:s)?|分支.*已(?:经)?存在/i.test(text)) {
1598
+ const parsed = parseCommand(cmd);
1599
+ const createFlag = parsed.ok ? parsed.args.findIndex((argument) => argument === "-c" || argument === "-b") : -1;
1600
+ const branch = parsed.ok && createFlag >= 2 ? parsed.args[createFlag + 1] : void 0;
1601
+ if (branch) {
1602
+ const corrected = "git switch " + quoteShellArg(branch);
1603
+ return { suggestion: "\u5206\u652F " + branch + " \u5DF2\u5B58\u5728\uFF1A\u6539\u4E3A\u5207\u6362\u5230\u73B0\u6709\u5206\u652F\uFF08\u6216\u6362\u4E00\u4E2A\u5206\u652F\u540D\uFF09\u3002", command: corrected };
1370
1604
  }
1371
1605
  }
1372
1606
  if (/not a git repository|不是.*git 仓库|不是一个 git 仓库/i.test(text)) {
@@ -1389,7 +1623,51 @@ var require_plugin = __commonJS({
1389
1623
  }
1390
1624
  return null;
1391
1625
  }
1392
- function registerRecoveryProposal(failedProposal, recovery) {
1626
+ async function recoverFailedCommand(activeShell, sessionId, workdir, operationId, action, command, message, errorOutput, errorCode, reason) {
1627
+ const operationKey = workdir + "\0" + operationId;
1628
+ const existing = proposalService.list(sessionId)?.find((proposal2) => proposal2.recoveryOperationKey === operationKey);
1629
+ if (existing) {
1630
+ if (!existing.failure) return null;
1631
+ if (existing.needsAgentAnalysis === true) return { failure: existing.failure, analysis: { proposalId: existing.proposalId } };
1632
+ if (existing.closed || existing.status !== "pending") return { failure: existing.failure };
1633
+ return { failure: existing.failure, recovery: {
1634
+ suggestion: String(existing.recoverySuggestion || existing.explanation),
1635
+ command: existing.command,
1636
+ proposalId: existing.proposalId
1637
+ } };
1638
+ }
1639
+ const diagnostics = await captureDiagnostics(activeShell, workdir);
1640
+ const failure = {
1641
+ source: "workbench",
1642
+ code: errorCode,
1643
+ action,
1644
+ command,
1645
+ message,
1646
+ stdout: "",
1647
+ stderr: redactSecrets(errorOutput),
1648
+ diagnostics,
1649
+ exitCode: null,
1650
+ timedOut: errorCode === "TIMEOUT",
1651
+ mayHavePartialChanges: reason !== "BRANCH_EXISTS",
1652
+ occurredAt: Date.now()
1653
+ };
1654
+ const recovery = buildRecoveryForCommand(command, message + "\n" + errorOutput + "\n" + diagnostics, reason);
1655
+ if (!recovery?.command) {
1656
+ const failed = registerFailureProposal(sessionId, workdir, command, failure);
1657
+ if (failed) {
1658
+ failed.recoveryOperationKey = operationKey;
1659
+ await proposalService.flush(sessionId);
1660
+ }
1661
+ return { failure, ...failed ? { analysis: { proposalId: failed.proposalId } } : {} };
1662
+ }
1663
+ const proposal = registerRecoveryProposal({ sessionId, workdir }, recovery, failure);
1664
+ if (!proposal) return { failure };
1665
+ proposal.recoveryOperationKey = operationKey;
1666
+ proposal.recoverySuggestion = recovery.suggestion;
1667
+ await proposalService.flush(sessionId);
1668
+ return { failure, recovery: { suggestion: recovery.suggestion, command: recovery.command, proposalId: proposal.proposalId } };
1669
+ }
1670
+ function registerRecoveryProposal(failedProposal, recovery, failure) {
1393
1671
  if (!recovery.command) return null;
1394
1672
  const v = validateCommand(recovery.command);
1395
1673
  if (!v.ok) return null;
@@ -1403,7 +1681,7 @@ var require_plugin = __commonJS({
1403
1681
  intent: "\u4FEE\u6B63\u5EFA\u8BAE\uFF1A" + recovery.suggestion,
1404
1682
  command: recovery.command.trim(),
1405
1683
  steps: [{ command: recovery.command.trim(), result: null }],
1406
- explanation: recovery.suggestion + "\uFF08\u7531\u6267\u884C\u5931\u8D25\u81EA\u52A8\u751F\u6210\uFF0C\u8BF7\u786E\u8BA4\u540E\u6267\u884C\uFF09",
1684
+ explanation: failure ? "\u539F\u547D\u4EE4\uFF1A" + failure.command + "\n\u9519\u8BEF\uFF1A" + (failure.stderr || failure.message) + "\n\u4FEE\u6B63\u539F\u56E0\uFF1A" + recovery.suggestion : recovery.suggestion + "\uFF08\u7531\u6267\u884C\u5931\u8D25\u81EA\u52A8\u751F\u6210\uFF0C\u8BF7\u786E\u8BA4\u540E\u6267\u884C\uFF09",
1407
1685
  risk: risk.level,
1408
1686
  reasons: risk.reasons,
1409
1687
  confirmed: false,
@@ -1415,11 +1693,43 @@ var require_plugin = __commonJS({
1415
1693
  fingerprint: null,
1416
1694
  verified: false,
1417
1695
  status: "pending",
1418
- recovery: true
1696
+ recovery: true,
1697
+ recoverySuggestion: recovery.suggestion,
1698
+ ...failure ? { failure } : {}
1699
+ };
1700
+ proposalService.closeOpen(sessionId);
1701
+ storeProposal(sessionId, proposal);
1702
+ console.log("easygit \u4FEE\u6B63\u5EFA\u8BAE\u767B\u8BB0", proposal.proposalId, "session=", sessionId, "risk=", risk.level);
1703
+ return proposal;
1704
+ }
1705
+ function registerFailureProposal(sessionId, workdir, command, failure) {
1706
+ if (proposalService.hasRunning(sessionId)) return null;
1707
+ const risk = classifyRisk(command);
1708
+ const proposal = {
1709
+ proposalId: proposalService.newId(),
1710
+ sessionId,
1711
+ intent: "Git \u64CD\u4F5C\u5931\u8D25\uFF0C\u7B49\u5F85\u5206\u6790",
1712
+ command,
1713
+ steps: [{ command, result: { ok: false, stderr: failure.stderr, stdout: failure.stdout, exitCode: failure.exitCode } }],
1714
+ explanation: "\u539F\u547D\u4EE4\uFF1A" + command + "\n\u9519\u8BEF\uFF1A" + (failure.stderr || failure.message),
1715
+ risk: risk.level,
1716
+ reasons: risk.reasons,
1717
+ confirmed: false,
1718
+ workdir,
1719
+ createdAt: failure.occurredAt,
1720
+ result: { ok: false, error: failure.message },
1721
+ closed: false,
1722
+ copied: false,
1723
+ fingerprint: null,
1724
+ verified: false,
1725
+ status: "failed",
1726
+ failure,
1727
+ recovery: true,
1728
+ needsAgentAnalysis: true
1419
1729
  };
1420
1730
  proposalService.closeOpen(sessionId);
1421
1731
  storeProposal(sessionId, proposal);
1422
- console.log("git-guide \u4FEE\u6B63\u5EFA\u8BAE\u767B\u8BB0", proposal.proposalId, "session=", sessionId, "risk=", risk.level);
1732
+ console.log("easygit \u5931\u8D25\u4E0A\u4E0B\u6587\u767B\u8BB0", proposal.proposalId, "session=", sessionId);
1423
1733
  return proposal;
1424
1734
  }
1425
1735
  function storeProposal(sessionId, proposal) {
@@ -1447,7 +1757,7 @@ var require_plugin = __commonJS({
1447
1757
  const backend = storage.backend.get("json");
1448
1758
  if (!backend || !backend.kv || typeof backend.kv.open !== "function") throw new Error("JSON storage backend does not support key-value units");
1449
1759
  unit = await backend.kv.open({
1450
- name: "git_guide_proposals",
1760
+ name: "easygit_proposals",
1451
1761
  version: 1,
1452
1762
  tables: ["proposals"],
1453
1763
  hasGlobal: false
@@ -1460,7 +1770,7 @@ var require_plugin = __commonJS({
1460
1770
  return ready;
1461
1771
  }
1462
1772
  var plugin2 = {
1463
- name: "git-guide",
1773
+ name: "easygit",
1464
1774
  inject: ["shell", "tools"],
1465
1775
  apply(ctx) {
1466
1776
  const shell = ctx.get("shell");
@@ -1550,6 +1860,7 @@ var require_plugin = __commonJS({
1550
1860
  if (prev && prev.some((proposal2) => proposal2.status === "running")) {
1551
1861
  return { ok: false, proposalId: "", intent: String(args.intent || ""), command: "", steps: [], explanation: String(args.explanation || ""), risk: risk.level, reasons: risk.reasons, workdir: workdir || "", error: "\u540C\u4E00\u4F1A\u8BDD\u5DF2\u6709\u63D0\u8BAE\u6B63\u5728\u6267\u884C\uFF0C\u8BF7\u7B49\u5F85\u6267\u884C\u7ED3\u675F\u540E\u518D\u521B\u5EFA\u65B0\u63D0\u8BAE" };
1552
1862
  }
1863
+ const analysisSource = prev?.find((candidate) => !candidate.closed && candidate.needsAgentAnalysis === true && typeof candidate.analysisRequestedAt === "number" && !!candidate.failure);
1553
1864
  proposalService.closeOpen(sessionId);
1554
1865
  const proposal = {
1555
1866
  proposalId: proposalService.newId(),
@@ -1568,7 +1879,13 @@ var require_plugin = __commonJS({
1568
1879
  copied: false,
1569
1880
  fingerprint: null,
1570
1881
  verified: false,
1571
- status: "pending"
1882
+ status: "pending",
1883
+ ...analysisSource?.failure ? {
1884
+ failure: analysisSource.failure,
1885
+ recovery: true,
1886
+ recoverySuggestion: String(args.explanation || ""),
1887
+ analyzedFailureProposalId: analysisSource.proposalId
1888
+ } : {}
1572
1889
  };
1573
1890
  storeProposal(sessionId, proposal);
1574
1891
  await proposalService.flush(sessionId);
@@ -1645,7 +1962,7 @@ var require_plugin = __commonJS({
1645
1962
  }
1646
1963
  });
1647
1964
  }
1648
- const registerWebServer = (webServer) => registerGitGuideActions(webServer, {
1965
+ const registerWebServer = (webServer) => registerEasyGitActions(webServer, {
1649
1966
  repository,
1650
1967
  proposalStorageReady,
1651
1968
  shell,
@@ -1658,6 +1975,18 @@ var require_plugin = __commonJS({
1658
1975
  runChecks,
1659
1976
  verifyProposal,
1660
1977
  executeProposal: (activeShell, proposal, policy, persist) => executeRegisteredProposal(activeShell, proposal, void 0, policy, persist),
1978
+ recoverFailedCommand: (sessionId, workdir, operationId, action, command, message, errorOutput, errorCode, reason) => recoverFailedCommand(
1979
+ shell,
1980
+ sessionId,
1981
+ workdir,
1982
+ operationId,
1983
+ action,
1984
+ command,
1985
+ message,
1986
+ errorOutput,
1987
+ errorCode,
1988
+ reason
1989
+ ),
1661
1990
  resolveExecutionPolicy: (sessionId) => {
1662
1991
  const agents = ctx.get("agents");
1663
1992
  const agent = agents ? agents.get(sessionId) : void 0;
@@ -1689,12 +2018,15 @@ var require_plugin = __commonJS({
1689
2018
  executeProposalSteps,
1690
2019
  executeRegisteredProposal,
1691
2020
  buildRecovery,
2021
+ buildRecoveryForCommand,
2022
+ recoverFailedCommand,
1692
2023
  registerRecoveryProposal,
1693
2024
  storeProposal,
1694
2025
  findProposal,
1695
2026
  proposalView,
1696
2027
  latestPending,
1697
- ProposalService
2028
+ ProposalService,
2029
+ GitRepositoryService
1698
2030
  };
1699
2031
  module2.exports = Object.assign(plugin2, { helpers });
1700
2032
  }
@@ -1,5 +1,5 @@
1
1
  import { createPanelController } from './panel-controller';
2
- import { appendCommandLog, beginTrackedRequest, buildFileTree, cancelTrackedRequest, clampWorkbenchRatio, commitFileTone, deriveCommitGraph, filterLocalBranches, isCurrentCommitRequest, isLatestRequest, isTrackedRequestCurrent, mutationCommand, nextCommitSelection, parseReviewRows, repositoryName, type AnyRecord } from './view-model';
2
+ import { appendCommandLog, analysisProposalId, beginTrackedRequest, buildAgentRepairPrompt, canDismissFailedProposal, buildFileTree, cancelTrackedRequest, clampWorkbenchRatio, commitFileTone, deriveCommitGraph, filterLocalBranches, failureContext, isCurrentCommitRequest, isLatestRequest, isTrackedRequestCurrent, mutationCommand, nextCommitSelection, openRecoveryProposal, parseReviewRows, pendingProposalTransition, recoveryProposalId, repositoryName, shouldShowAnalysisBanner, type AnyRecord } from './view-model';
3
3
  type RefreshState = 'idle' | 'loading' | 'succeeded' | 'failed';
4
4
  declare function injectStyles(): () => void;
5
5
  declare function refreshButtonLabel(state: RefreshState): string;
@@ -22,6 +22,14 @@ declare const plugin: {
22
22
  mutationCommand: typeof mutationCommand;
23
23
  appendCommandLog: typeof appendCommandLog;
24
24
  refreshButtonLabel: typeof refreshButtonLabel;
25
+ recoveryProposalId: typeof recoveryProposalId;
26
+ openRecoveryProposal: typeof openRecoveryProposal;
27
+ analysisProposalId: typeof analysisProposalId;
28
+ failureContext: typeof failureContext;
29
+ buildAgentRepairPrompt: typeof buildAgentRepairPrompt;
30
+ shouldShowAnalysisBanner: typeof shouldShowAnalysisBanner;
31
+ canDismissFailedProposal: typeof canDismissFailedProposal;
32
+ pendingProposalTransition: typeof pendingProposalTransition;
25
33
  isCurrentCommitRequest: typeof isCurrentCommitRequest;
26
34
  nextCommitSelection: typeof nextCommitSelection;
27
35
  commitFileTone: typeof commitFileTone;