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.
@@ -1,4 +1,4 @@
1
- import type { BranchSummary, CommitSummary, RepositoryFile } from '../shared/contracts';
1
+ import type { BranchSummary, CommitSummary, GitFailureContext, RepositoryFile } from '../shared/contracts';
2
2
  export type AnyRecord = Record<string, any>;
3
3
  export type RepositoryMutationAction = 'stage-paths' | 'unstage-paths' | 'stage-all' | 'unstage-all' | 'commit' | 'create-branch' | 'switch-branch' | 'delete-branch';
4
4
  export interface CommandLogEntry {
@@ -91,6 +91,17 @@ export declare function mutationCommand(action: string, payload?: AnyRecord): {
91
91
  label: string;
92
92
  command: string;
93
93
  } | null;
94
+ export declare function recoveryProposalId(response: unknown): string | null;
95
+ export declare function analysisProposalId(response: unknown): string | null;
96
+ export declare function failureContext(response: unknown): GitFailureContext | null;
97
+ export declare function buildAgentRepairPrompt(failure: GitFailureContext): string;
98
+ export declare function shouldShowAnalysisBanner(tab: string, pendingAnalysis: unknown): boolean;
99
+ export declare function canDismissFailedProposal(needsAgentAnalysis: unknown): boolean;
100
+ export declare function pendingProposalTransition(previousProposalId: string | null, proposal: unknown): {
101
+ proposalId: string | null;
102
+ shouldOpen: boolean;
103
+ };
104
+ export declare function openRecoveryProposal(response: unknown, open: () => void, schedule?: (callback: () => void, delayMs: number) => unknown): boolean;
94
105
  export declare function viewportWidth(): number;
95
106
  export declare function sidebarTrackWidth(layout: HostSplitLayout): number;
96
107
  export declare function findWorkbenchHostSplit(anchor: HTMLElement): HostSplitLayout | null;
@@ -18,14 +18,15 @@ interface ProposalVerification {
18
18
  message: string;
19
19
  changedState: string;
20
20
  }
21
- interface GitGuideActionDependencies {
21
+ interface RepositoryContext {
22
+ workdir: string;
23
+ policy: unknown;
24
+ }
25
+ interface EasyGitActionDependencies {
22
26
  repository: GitRepositoryService;
23
27
  proposalStorageReady: Promise<void>;
24
28
  shell: ShellService | null;
25
- repositoryContext(sessionId: string): {
26
- workdir: string;
27
- policy: unknown;
28
- } | null;
29
+ repositoryContext(sessionId: string): RepositoryContext | null;
29
30
  latestPending(sessionId: string): StoredProposal | null;
30
31
  findProposal(sessionId: string, proposalId: unknown): StoredProposal | undefined;
31
32
  proposalView(proposal: StoredProposal): ProposalView;
@@ -34,8 +35,9 @@ interface GitGuideActionDependencies {
34
35
  runChecks(shell: ShellService | null, workdir: string, checks: ReturnType<typeof deriveChecks>): Promise<string[]>;
35
36
  verifyProposal(shell: ShellService | null, proposal: StoredProposal): Promise<ProposalVerification>;
36
37
  executeProposal(shell: ShellService | null, proposal: StoredProposal, policy: unknown, persist: () => Promise<void>): Promise<UnknownRecord>;
38
+ recoverFailedCommand(sessionId: string, workdir: string, operationId: string, action: string, command: string, message: string, errorOutput: string, errorCode: string, reason: string): Promise<UnknownRecord | null>;
37
39
  resolveExecutionPolicy(sessionId: string): unknown;
38
40
  }
39
41
  /** Register the Client-to-Host POST dispatcher with a 1 MiB body limit. */
40
- export declare function registerGitGuideActions(webServer: WebServerService | null, dependencies: GitGuideActionDependencies): unknown;
42
+ export declare function registerEasyGitActions(webServer: WebServerService | null, dependencies: EasyGitActionDependencies): unknown;
41
43
  export {};
@@ -1,5 +1,5 @@
1
- import type { ActionResult, CommitDetail, CommitDiffResult, CommitSummary, DiffResult, RepositoryReferences, RepositorySummary, StashSummary } from '../shared/contracts';
2
- export type { BranchSummary, CommitDetail, CommitDiffResult, CommitFileChange, CommitRefSummary, CommitSummary, DiffResult, ReferenceSummary, RepositoryFile, RepositoryReferences, RepositorySummary, StashSummary, } from '../shared/contracts';
1
+ import type { ActionResult, CommitDetail, CommitDiffResult, CommitSummary, DiffResult, RepositoryReferences, RepositorySummary, StashSummary, SyncState } from '../shared/contracts';
2
+ export type { BranchSummary, CommitDetail, CommitDiffResult, CommitFileChange, CommitRefSummary, CommitSummary, DiffResult, ReferenceSummary, RepositoryFile, RepositoryReferences, RepositorySummary, StashSummary, SyncState, } from '../shared/contracts';
3
3
  export interface GitRunResult {
4
4
  exitCode: number | null;
5
5
  signal?: string | null;
@@ -39,6 +39,13 @@ export declare class GitRepositoryService {
39
39
  getCommitDetail(workdir: string, hash: unknown, signal?: AbortSignal, sandboxPolicy?: unknown): Promise<ActionResult<CommitDetail>>;
40
40
  getCommitDiff(workdir: string, hash: unknown, signal?: AbortSignal, sandboxPolicy?: unknown): Promise<ActionResult<CommitDiffResult>>;
41
41
  getStashes(workdir: string, signal?: AbortSignal, sandboxPolicy?: unknown): Promise<ActionResult<StashSummary[]>>;
42
+ getSyncState(workdir: string, signal?: AbortSignal, sandboxPolicy?: unknown): Promise<ActionResult<SyncState>>;
43
+ fetchRemote(request: MutationRequest, remote: unknown): Promise<ActionResult<SyncState>>;
44
+ pullFfOnly(request: MutationRequest): Promise<ActionResult<SyncState>>;
45
+ pushCurrent(request: MutationRequest, remote: unknown, branch: unknown, setUpstream: boolean): Promise<ActionResult<SyncState>>;
46
+ rebaseOnto(request: MutationRequest, target: unknown, confirmRisk: boolean): Promise<ActionResult<SyncState>>;
47
+ continueRebase(request: MutationRequest, confirmRisk: boolean): Promise<ActionResult<SyncState>>;
48
+ abortRebase(request: MutationRequest, confirmRisk: boolean): Promise<ActionResult<SyncState>>;
42
49
  stagePaths(request: MutationRequest, paths: unknown): Promise<ActionResult<RepositorySummary>>;
43
50
  unstagePaths(request: MutationRequest, paths: unknown): Promise<ActionResult<RepositorySummary>>;
44
51
  stageAll(request: MutationRequest): Promise<ActionResult<RepositorySummary>>;
@@ -49,5 +56,7 @@ export declare class GitRepositoryService {
49
56
  deleteBranch(request: MutationRequest, name: unknown, force: boolean, confirmRisk: boolean): Promise<ActionResult<RepositorySummary>>;
50
57
  private validatePaths;
51
58
  private mutate;
59
+ private mutateSync;
60
+ private mutateAndRead;
52
61
  private pruneOperations;
53
62
  }
@@ -3,13 +3,14 @@
3
3
  *
4
4
  * It is mounted as one profile composition entry and serves every DeepSeek
5
5
  * Harness session. It uses only ctx.tools.register for tools and a
6
- * ctx.webServer POST /git-guide route for Client-to-Host actions.
6
+ * ctx.webServer POST /easygit route for Client-to-Host actions.
7
7
  *
8
8
  * The exposed model tools are git_propose and git_repo_state. Repository changes
9
9
  * are only available through the Client action route.
10
10
  */
11
+ import type { GitFailureContext } from '../shared/contracts';
11
12
  import { ProposalService, type ProposalView, type StoredProposal } from './proposal-service';
12
- import { type ShellService } from './git-repository-service';
13
+ import { GitRepositoryService, type ShellService } from './git-repository-service';
13
14
  import { addPathsOf, classifyRisk, classifyStepsRisk, deriveChecks, modernizeCommand, parseCommand, quoteShellArg, redactAndLimit, redactSecrets, validateCommand, type ExpectedCheck } from './command-policy';
14
15
  type UnknownRecord = Record<string, unknown>;
15
16
  interface HostContext {
@@ -32,11 +33,15 @@ interface ProposalExecutionResult extends UnknownRecord {
32
33
  stdout: string;
33
34
  stderr: string;
34
35
  diagnostics: string;
36
+ failure?: GitFailureContext;
35
37
  recovery?: {
36
38
  suggestion: string;
37
39
  command: string;
38
40
  proposalId: string | null;
39
41
  } | null;
42
+ analysis?: {
43
+ proposalId: string;
44
+ };
40
45
  error: string;
41
46
  }
42
47
  interface ProposalVerification {
@@ -69,8 +74,20 @@ declare function executeRegisteredProposal(shell: ShellService | null | undefine
69
74
  * A null command means the condition requires manual remediation.
70
75
  */
71
76
  declare function buildRecovery(_proposal: StoredProposal, failedStep: StoredProposal['steps'][number] | undefined, diagnostics: string): RecoverySuggestion | null;
77
+ declare function buildRecoveryForCommand(cmd: string, text: string, reason?: string): RecoverySuggestion | null;
78
+ declare function recoverFailedCommand(activeShell: ShellService | null | undefined, sessionId: string, workdir: string, operationId: string, action: string, command: string, message: string, errorOutput: string, errorCode: string, reason: string): Promise<{
79
+ failure: GitFailureContext;
80
+ recovery?: {
81
+ suggestion: string;
82
+ command: string;
83
+ proposalId: string | null;
84
+ };
85
+ analysis?: {
86
+ proposalId: string;
87
+ };
88
+ } | null>;
72
89
  /** Register a recovery command as a pending proposal in the same session. */
73
- declare function registerRecoveryProposal(failedProposal: Pick<StoredProposal, 'sessionId' | 'workdir'>, recovery: RecoverySuggestion): StoredProposal | null;
90
+ declare function registerRecoveryProposal(failedProposal: Pick<StoredProposal, 'sessionId' | 'workdir'>, recovery: RecoverySuggestion, failure?: GitFailureContext): StoredProposal | null;
74
91
  declare function storeProposal(sessionId: string, proposal: StoredProposal): StoredProposal;
75
92
  declare function findProposal(sessionId: string, proposalId: unknown): StoredProposal | undefined;
76
93
  declare function proposalView(proposal: StoredProposal): ProposalView;
@@ -98,12 +115,15 @@ declare const _default: {
98
115
  executeProposalSteps: typeof executeProposalSteps;
99
116
  executeRegisteredProposal: typeof executeRegisteredProposal;
100
117
  buildRecovery: typeof buildRecovery;
118
+ buildRecoveryForCommand: typeof buildRecoveryForCommand;
119
+ recoverFailedCommand: typeof recoverFailedCommand;
101
120
  registerRecoveryProposal: typeof registerRecoveryProposal;
102
121
  storeProposal: typeof storeProposal;
103
122
  findProposal: typeof findProposal;
104
123
  proposalView: typeof proposalView;
105
124
  latestPending: typeof latestPending;
106
125
  ProposalService: typeof ProposalService;
126
+ GitRepositoryService: typeof GitRepositoryService;
107
127
  };
108
128
  };
109
129
  export = _default;
@@ -1,4 +1,4 @@
1
- import type { ProposalStatus, ProposalView } from '../shared/contracts';
1
+ import type { GitFailureContext, ProposalStatus, ProposalView } from '../shared/contracts';
2
2
  export type { ProposalView } from '../shared/contracts';
3
3
  import type { RiskLevel } from './command-policy';
4
4
  export interface ProposalStep {
@@ -23,6 +23,7 @@ export interface StoredProposal {
23
23
  fingerprint: string | null;
24
24
  verified: boolean;
25
25
  result: Record<string, unknown> | null;
26
+ failure?: GitFailureContext;
26
27
  [key: string]: unknown;
27
28
  }
28
29
  export interface ProposalStorageUnit {
@@ -1,7 +1,29 @@
1
1
  /** Shared Client-to-Host contracts. */
2
2
  export type ProposalStatus = 'pending' | 'running' | 'succeeded' | 'failed' | 'verified' | 'dismissed';
3
3
  export type ActionErrorCode = 'NOT_GIT_REPOSITORY' | 'SESSION_NOT_FOUND' | 'INVALID_ARGUMENT' | 'STATE_CONFLICT' | 'OPERATION_DUPLICATE' | 'GIT_FAILED' | 'TIMEOUT' | 'PERMISSION_DENIED' | 'INTERNAL_ERROR';
4
- export type ActionFailureReason = 'CURRENT_BRANCH' | 'BRANCH_NOT_FOUND' | 'UNMERGED_BRANCH';
4
+ export type ActionFailureReason = 'CURRENT_BRANCH' | 'BRANCH_NOT_FOUND' | 'BRANCH_EXISTS' | 'UNMERGED_BRANCH' | 'NO_REMOTE' | 'NO_UPSTREAM' | 'DETACHED_HEAD' | 'DIRTY_WORKTREE' | 'CONFLICTS_PRESENT' | 'REBASE_IN_PROGRESS' | 'NO_REBASE_IN_PROGRESS' | 'REF_NOT_FOUND';
5
+ export interface GitFailureContext {
6
+ source: 'workbench' | 'proposal';
7
+ code: string;
8
+ action: string;
9
+ command: string;
10
+ message: string;
11
+ stdout: string;
12
+ stderr: string;
13
+ diagnostics: string;
14
+ exitCode: number | null;
15
+ timedOut: boolean;
16
+ mayHavePartialChanges: boolean;
17
+ occurredAt: number;
18
+ }
19
+ export interface RecoveryProposal {
20
+ suggestion: string;
21
+ command: string;
22
+ proposalId: string | null;
23
+ }
24
+ export interface AgentAnalysisRequest {
25
+ proposalId: string;
26
+ }
5
27
  export type ActionResult<T> = {
6
28
  ok: true;
7
29
  data: T;
@@ -12,6 +34,9 @@ export type ActionResult<T> = {
12
34
  message: string;
13
35
  diagnostics?: string;
14
36
  reason?: ActionFailureReason;
37
+ failure?: GitFailureContext;
38
+ recovery?: RecoveryProposal;
39
+ analysis?: AgentAnalysisRequest;
15
40
  };
16
41
  export interface RepositoryFile {
17
42
  indexStatus: string;
@@ -27,6 +52,19 @@ export interface RepositorySummary {
27
52
  files: RepositoryFile[];
28
53
  stagedCount: number;
29
54
  }
55
+ export interface SyncState {
56
+ topLevel: string;
57
+ branch: string;
58
+ head: string;
59
+ upstream: string;
60
+ remotes: string[];
61
+ ahead: number;
62
+ behind: number;
63
+ dirty: boolean;
64
+ conflictCount: number;
65
+ rebaseInProgress: boolean;
66
+ files: RepositoryFile[];
67
+ }
30
68
  export interface BranchSummary {
31
69
  name: string;
32
70
  current: boolean;
@@ -120,6 +158,10 @@ export interface ProposalView {
120
158
  result: Record<string, unknown> | null;
121
159
  closed: boolean;
122
160
  copied: boolean;
161
+ failure?: GitFailureContext;
162
+ recoverySuggestion?: string;
163
+ needsAgentAnalysis?: boolean;
164
+ analysisRequestedAt?: number;
123
165
  }
124
166
  export type ProposalStateResponse = {
125
167
  ok: true;
@@ -156,13 +198,11 @@ export interface ProposalExecutionResponse extends ProposalCommandResponse {
156
198
  stdout?: string;
157
199
  stderr?: string;
158
200
  diagnostics?: string;
159
- recovery?: {
160
- suggestion: string;
161
- command: string;
162
- proposalId: string | null;
163
- } | null;
201
+ failure?: GitFailureContext;
202
+ recovery?: RecoveryProposal | null;
203
+ analysis?: AgentAnalysisRequest;
164
204
  }
165
- export interface GitGuideRequestMap {
205
+ export interface EasyGitRequestMap {
166
206
  'get-summary': {
167
207
  sessionId: string;
168
208
  };
@@ -189,6 +229,9 @@ export interface GitGuideRequestMap {
189
229
  'get-stashes': {
190
230
  sessionId: string;
191
231
  };
232
+ 'get-sync-state': {
233
+ sessionId: string;
234
+ };
192
235
  'stage-paths': {
193
236
  sessionId: string;
194
237
  operationId: string;
@@ -230,6 +273,42 @@ export interface GitGuideRequestMap {
230
273
  force?: boolean;
231
274
  confirmRisk?: boolean;
232
275
  };
276
+ fetch: {
277
+ sessionId: string;
278
+ operationId: string;
279
+ remote: string;
280
+ };
281
+ pull: {
282
+ sessionId: string;
283
+ operationId: string;
284
+ };
285
+ push: {
286
+ sessionId: string;
287
+ operationId: string;
288
+ remote?: string;
289
+ branch?: string;
290
+ setUpstream?: boolean;
291
+ };
292
+ rebase: {
293
+ sessionId: string;
294
+ operationId: string;
295
+ target: string;
296
+ confirmRisk?: boolean;
297
+ };
298
+ 'rebase-continue': {
299
+ sessionId: string;
300
+ operationId: string;
301
+ confirmRisk?: boolean;
302
+ };
303
+ 'rebase-abort': {
304
+ sessionId: string;
305
+ operationId: string;
306
+ confirmRisk?: boolean;
307
+ };
308
+ 'request-analysis': {
309
+ sessionId: string;
310
+ proposalId: string;
311
+ };
233
312
  state: {
234
313
  sessionId: string;
235
314
  };
@@ -253,7 +332,7 @@ export interface GitGuideRequestMap {
253
332
  confirm?: boolean;
254
333
  };
255
334
  }
256
- export interface GitGuideResponseMap {
335
+ export interface EasyGitResponseMap {
257
336
  'get-summary': ActionResult<RepositorySummary>;
258
337
  'get-diff': ActionResult<DiffResult>;
259
338
  'get-branches': ActionResult<RepositoryReferences>;
@@ -261,6 +340,7 @@ export interface GitGuideResponseMap {
261
340
  'get-commit-detail': ActionResult<CommitDetail>;
262
341
  'get-commit-diff': ActionResult<CommitDiffResult>;
263
342
  'get-stashes': ActionResult<StashSummary[]>;
343
+ 'get-sync-state': ActionResult<SyncState>;
264
344
  'stage-paths': ActionResult<RepositorySummary>;
265
345
  'unstage-paths': ActionResult<RepositorySummary>;
266
346
  'stage-all': ActionResult<RepositorySummary>;
@@ -269,14 +349,21 @@ export interface GitGuideResponseMap {
269
349
  'create-branch': ActionResult<RepositorySummary>;
270
350
  'switch-branch': ActionResult<RepositorySummary>;
271
351
  'delete-branch': ActionResult<RepositorySummary>;
352
+ fetch: ActionResult<SyncState>;
353
+ pull: ActionResult<SyncState>;
354
+ push: ActionResult<SyncState>;
355
+ rebase: ActionResult<SyncState>;
356
+ 'rebase-continue': ActionResult<SyncState>;
357
+ 'rebase-abort': ActionResult<SyncState>;
358
+ 'request-analysis': ProposalCommandResponse;
272
359
  state: ProposalStateResponse;
273
360
  dismiss: ProposalCommandResponse;
274
361
  'mark-copied': ProposalCommandResponse;
275
362
  verify: ProposalCommandResponse;
276
363
  execute: ProposalExecutionResponse;
277
364
  }
278
- export type GitGuideAction = keyof GitGuideRequestMap;
279
- export type GitGuideRequest<A extends GitGuideAction = GitGuideAction> = {
365
+ export type EasyGitAction = keyof EasyGitRequestMap;
366
+ export type EasyGitRequest<A extends EasyGitAction = EasyGitAction> = {
280
367
  action: A;
281
- } & GitGuideRequestMap[A];
282
- export type GitGuideResponse<A extends GitGuideAction> = GitGuideResponseMap[A];
368
+ } & EasyGitRequestMap[A];
369
+ export type EasyGitResponse<A extends EasyGitAction> = EasyGitResponseMap[A];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-easygit-plugin",
3
- "version": "0.2.0",
3
+ "version": "0.2.2",
4
4
  "description": "面向 DeepSeek Harness Web 的可视化 Git 工作台,支持查看仓库状态与提交记录、快捷执行常用 Git 操作,以及安全运行自然语言生成的分步骤命令提议。",
5
5
  "license": "MIT",
6
6
  "main": "lib/index.js",
@@ -18,12 +18,13 @@
18
18
  },
19
19
  "dsh": {
20
20
  "bundle": {
21
- "patch": "./git-guide.cordis.yml"
21
+ "patch": "./easygit.cordis.yml"
22
22
  },
23
23
  "client": {
24
24
  "platform": "web",
25
25
  "inject": [
26
26
  "@deepseek-ai/dsh-client-runtime",
27
+ "@deepseek-ai/dsh-client-connection",
27
28
  "@deepseek-ai/dsh-client-ui-conversation",
28
29
  "@deepseek-ai/dsh-client-ui-layout"
29
30
  ]
@@ -51,7 +52,7 @@
51
52
  "assets/preview.png",
52
53
  "lib/*.js",
53
54
  "lib/types/**/*.d.ts",
54
- "git-guide.cordis.yml",
55
+ "easygit.cordis.yml",
55
56
  "README.md",
56
57
  "README.zh-CN.md",
57
58
  "SECURITY.md",