@robota-sdk/agent-command 3.0.0-beta.64
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/LICENSE +21 -0
- package/dist/node/index.cjs +30 -0
- package/dist/node/index.d.ts +293 -0
- package/dist/node/index.d.ts.map +1 -0
- package/dist/node/index.js +31 -0
- package/dist/node/index.js.map +1 -0
- package/package.json +48 -0
- package/src/agent/__tests__/agent-command.test.ts +504 -0
- package/src/agent/agent-command-module.ts +82 -0
- package/src/agent/agent-command-parser.ts +180 -0
- package/src/agent/agent-command.ts +235 -0
- package/src/agent/index.ts +7 -0
- package/src/background/__tests__/background-command-module.test.ts +255 -0
- package/src/background/background-command-module.ts +53 -0
- package/src/background/background-command.ts +63 -0
- package/src/background/index.ts +6 -0
- package/src/compact/__tests__/compact-command-module.test.ts +162 -0
- package/src/compact/compact-command-module.ts +51 -0
- package/src/compact/compact-command.ts +21 -0
- package/src/compact/index.ts +6 -0
- package/src/context/__tests__/context-command-module.test.ts +294 -0
- package/src/context/context-command-module.ts +54 -0
- package/src/context/context-command.ts +298 -0
- package/src/context/index.ts +6 -0
- package/src/exit/__tests__/exit-command-module.test.ts +35 -0
- package/src/exit/exit-command-module.ts +48 -0
- package/src/exit/exit-command.ts +10 -0
- package/src/exit/index.ts +6 -0
- package/src/help/__tests__/help-command-module.test.ts +106 -0
- package/src/help/help-command-module.ts +48 -0
- package/src/help/help-command.ts +9 -0
- package/src/help/index.ts +6 -0
- package/src/index.ts +20 -0
- package/src/language/__tests__/language-command-module.test.ts +105 -0
- package/src/language/index.ts +6 -0
- package/src/language/language-command-module.ts +56 -0
- package/src/language/language-command.ts +22 -0
- package/src/memory/__tests__/memory-command-module.test.ts +272 -0
- package/src/memory/index.ts +6 -0
- package/src/memory/memory-command-module.ts +57 -0
- package/src/memory/memory-command.ts +234 -0
- package/src/mode/__tests__/mode-command-module.test.ts +143 -0
- package/src/mode/index.ts +6 -0
- package/src/mode/mode-command-module.ts +56 -0
- package/src/mode/mode-command.ts +34 -0
- package/src/model/__tests__/model-command-module.test.ts +273 -0
- package/src/model/index.ts +6 -0
- package/src/model/model-command-module.ts +68 -0
- package/src/model/model-command.ts +40 -0
- package/src/permissions/__tests__/permissions-command-module.test.ts +164 -0
- package/src/permissions/index.ts +6 -0
- package/src/permissions/permissions-command-module.ts +56 -0
- package/src/permissions/permissions-command.ts +45 -0
- package/src/plugin/__tests__/plugin-command-module.test.ts +214 -0
- package/src/plugin/index.ts +7 -0
- package/src/plugin/plugin-command-module.ts +81 -0
- package/src/plugin/plugin-command.ts +230 -0
- package/src/provider/__tests__/provider-command-module.test.ts +488 -0
- package/src/provider/__tests__/provider-setup-flow.test.ts +43 -0
- package/src/provider/index.ts +30 -0
- package/src/provider/provider-command-execution.ts +150 -0
- package/src/provider/provider-command-module.ts +65 -0
- package/src/provider/provider-command-profile-lifecycle.ts +211 -0
- package/src/provider/provider-command-profile-operations.ts +198 -0
- package/src/provider/provider-command-profile.ts +109 -0
- package/src/provider/provider-command-setup.ts +104 -0
- package/src/provider/provider-setup-flow.ts +309 -0
- package/src/reset/__tests__/reset-command-module.test.ts +63 -0
- package/src/reset/index.ts +2 -0
- package/src/reset/reset-command-module.ts +49 -0
- package/src/reset/reset-command.ts +10 -0
- package/src/rewind/__tests__/rewind-command-module.test.ts +215 -0
- package/src/rewind/index.ts +2 -0
- package/src/rewind/rewind-command-module.ts +57 -0
- package/src/rewind/rewind-command.ts +184 -0
- package/src/session/__tests__/session-command-module.test.ts +339 -0
- package/src/session/index.ts +17 -0
- package/src/session/session-command-module.ts +168 -0
- package/src/session/session-command.ts +74 -0
- package/src/settings/index.ts +7 -0
- package/src/settings/settings-command-module.ts +50 -0
- package/src/skills/__tests__/skills-command-module.test.ts +157 -0
- package/src/skills/index.ts +6 -0
- package/src/skills/skills-command-module.ts +62 -0
- package/src/skills/skills-command.ts +110 -0
- package/src/statusline/__tests__/statusline-command-module.test.ts +95 -0
- package/src/statusline/index.ts +6 -0
- package/src/statusline/statusline-command-module.ts +56 -0
- package/src/statusline/statusline-command.ts +79 -0
- package/src/user-local/__tests__/user-local-command.test.ts +145 -0
- package/src/user-local/index.ts +13 -0
- package/src/user-local/user-local-command-constants.ts +5 -0
- package/src/user-local/user-local-command-module.ts +67 -0
- package/src/user-local/user-local-command.ts +205 -0
- package/src/user-local/user-local-memory-command.ts +147 -0
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
import { describe, expect, it, vi } from 'vitest';
|
|
2
|
+
import type {
|
|
3
|
+
IEditCheckpointInspection,
|
|
4
|
+
IEditCheckpointRestoreResult,
|
|
5
|
+
IEditCheckpointSummary,
|
|
6
|
+
} from '@robota-sdk/agent-framework';
|
|
7
|
+
import { InteractiveSession } from '@robota-sdk/agent-framework';
|
|
8
|
+
import { RewindCommandSource, createRewindCommandModule, executeRewindCommand } from '../index.js';
|
|
9
|
+
|
|
10
|
+
function createCheckpoint(overrides: Partial<IEditCheckpointSummary> = {}): IEditCheckpointSummary {
|
|
11
|
+
return {
|
|
12
|
+
id: 'turn-0001',
|
|
13
|
+
sessionId: 'test-session-id',
|
|
14
|
+
sequence: 1,
|
|
15
|
+
prompt: 'change files',
|
|
16
|
+
createdAt: '2026-05-02T00:00:00.000Z',
|
|
17
|
+
fileCount: 2,
|
|
18
|
+
...overrides,
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function createRestoreResult(
|
|
23
|
+
overrides: Partial<IEditCheckpointRestoreResult> = {},
|
|
24
|
+
): IEditCheckpointRestoreResult {
|
|
25
|
+
return {
|
|
26
|
+
target: createCheckpoint({ fileCount: 1 }),
|
|
27
|
+
restoredCheckpointCount: 2,
|
|
28
|
+
restoredFileCount: 3,
|
|
29
|
+
removedCheckpointCount: 2,
|
|
30
|
+
...overrides,
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function createInspection(
|
|
35
|
+
overrides: Partial<IEditCheckpointInspection> = {},
|
|
36
|
+
): IEditCheckpointInspection {
|
|
37
|
+
return {
|
|
38
|
+
target: createCheckpoint({ fileCount: 1 }),
|
|
39
|
+
capturedFiles: [
|
|
40
|
+
{
|
|
41
|
+
originalPath: '/workspace/example.ts',
|
|
42
|
+
relativePath: 'example.ts',
|
|
43
|
+
existed: true,
|
|
44
|
+
restoreAction: 'restore-preimage',
|
|
45
|
+
snapshotAvailable: true,
|
|
46
|
+
snapshotSizeBytes: 12,
|
|
47
|
+
},
|
|
48
|
+
],
|
|
49
|
+
restoreToCheckpoint: { checkpointIds: ['turn-0002'], fileCount: 1 },
|
|
50
|
+
rollbackThroughCheckpoint: { checkpointIds: ['turn-0001', 'turn-0002'], fileCount: 2 },
|
|
51
|
+
...overrides,
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function createMockRuntimeSession() {
|
|
56
|
+
return {
|
|
57
|
+
run: vi.fn().mockResolvedValue('mock response'),
|
|
58
|
+
abort: vi.fn(),
|
|
59
|
+
getHistory: vi.fn().mockReturnValue([]),
|
|
60
|
+
getContextState: vi.fn().mockReturnValue({
|
|
61
|
+
usedTokens: 5000,
|
|
62
|
+
maxTokens: 200000,
|
|
63
|
+
usedPercentage: 2.5,
|
|
64
|
+
}),
|
|
65
|
+
compact: vi.fn().mockResolvedValue(undefined),
|
|
66
|
+
injectMessage: vi.fn(),
|
|
67
|
+
getSessionId: vi.fn().mockReturnValue('test-session-id'),
|
|
68
|
+
getSystemMessage: vi.fn().mockReturnValue('mock system prompt'),
|
|
69
|
+
getToolSchemas: vi.fn().mockReturnValue([]),
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function createInteractiveSession(): InteractiveSession {
|
|
74
|
+
return new InteractiveSession({
|
|
75
|
+
session: createMockRuntimeSession() as never,
|
|
76
|
+
commandModules: [createRewindCommandModule()],
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
describe('createRewindCommandModule', () => {
|
|
81
|
+
it('contributes write-safe rewind metadata and executable command', () => {
|
|
82
|
+
const module = createRewindCommandModule();
|
|
83
|
+
const commands = module.commandSources?.flatMap((source) => source.getCommands()) ?? [];
|
|
84
|
+
|
|
85
|
+
expect(module.name).toBe('agent-command-rewind');
|
|
86
|
+
expect(commands.map((command) => command.name)).toEqual(['rewind']);
|
|
87
|
+
expect(commands[0]?.source).toBe('rewind');
|
|
88
|
+
expect(commands[0]?.modelInvocable).toBe(false);
|
|
89
|
+
expect(commands[0]?.safety).toBe('write');
|
|
90
|
+
expect(commands[0]?.subcommands?.map((command) => command.name)).toEqual([
|
|
91
|
+
'list',
|
|
92
|
+
'inspect',
|
|
93
|
+
'restore',
|
|
94
|
+
'code',
|
|
95
|
+
'rollback',
|
|
96
|
+
]);
|
|
97
|
+
expect(module.systemCommands?.map((command) => command.name)).toEqual(['rewind']);
|
|
98
|
+
expect(module.systemCommands?.[0]?.userInvocable).toBe(true);
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
it('provides a stable command source', () => {
|
|
102
|
+
const source = new RewindCommandSource();
|
|
103
|
+
|
|
104
|
+
expect(source.name).toBe('rewind');
|
|
105
|
+
expect(source.getCommands()).toHaveLength(1);
|
|
106
|
+
});
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
describe('executeRewindCommand', () => {
|
|
110
|
+
it('lists edit checkpoints', async () => {
|
|
111
|
+
const session = createInteractiveSession();
|
|
112
|
+
vi.spyOn(session, 'listEditCheckpoints').mockReturnValue([createCheckpoint()]);
|
|
113
|
+
|
|
114
|
+
const result = await session.executeCommand('rewind', 'list');
|
|
115
|
+
|
|
116
|
+
expect(result?.success).toBe(true);
|
|
117
|
+
expect(result?.message).toContain('turn-0001');
|
|
118
|
+
expect(result?.data?.count).toBe(1);
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
it('shows an empty checkpoint list', async () => {
|
|
122
|
+
const session = createInteractiveSession();
|
|
123
|
+
vi.spyOn(session, 'listEditCheckpoints').mockReturnValue([]);
|
|
124
|
+
|
|
125
|
+
const result = await session.executeCommand('rewind', '');
|
|
126
|
+
|
|
127
|
+
expect(result?.success).toBe(true);
|
|
128
|
+
expect(result?.message).toContain('(no edit checkpoints)');
|
|
129
|
+
expect(result?.data?.count).toBe(0);
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
it('inspects captured files and rollback plans', async () => {
|
|
133
|
+
const session = createInteractiveSession();
|
|
134
|
+
const inspectEditCheckpoint = vi
|
|
135
|
+
.spyOn(session, 'inspectEditCheckpoint')
|
|
136
|
+
.mockReturnValue(createInspection());
|
|
137
|
+
|
|
138
|
+
const result = await session.executeCommand('rewind', 'inspect turn-0001');
|
|
139
|
+
|
|
140
|
+
expect(result?.success).toBe(true);
|
|
141
|
+
expect(inspectEditCheckpoint).toHaveBeenCalledWith('turn-0001');
|
|
142
|
+
expect(result?.message).toContain('Captured files:');
|
|
143
|
+
expect(result?.message).toContain('example.ts');
|
|
144
|
+
expect(result?.message).toContain('Rollback through checkpoint');
|
|
145
|
+
expect(result?.data?.inspection).toEqual(createInspection());
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
it('delegates restore through session.executeCommand', async () => {
|
|
149
|
+
const session = createInteractiveSession();
|
|
150
|
+
const restoreEditCheckpoint = vi
|
|
151
|
+
.spyOn(session, 'restoreEditCheckpoint')
|
|
152
|
+
.mockResolvedValue(createRestoreResult());
|
|
153
|
+
|
|
154
|
+
const result = await session.executeCommand('rewind', 'restore turn-0001');
|
|
155
|
+
|
|
156
|
+
expect(result?.success).toBe(true);
|
|
157
|
+
expect(restoreEditCheckpoint).toHaveBeenCalledWith('turn-0001');
|
|
158
|
+
expect(result?.message).toContain('Restored code to turn-0001.');
|
|
159
|
+
expect(result?.data?.restoredFileCount).toBe(3);
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
it('treats code as a restore alias', async () => {
|
|
163
|
+
const session = createInteractiveSession();
|
|
164
|
+
const restoreEditCheckpoint = vi
|
|
165
|
+
.spyOn(session, 'restoreEditCheckpoint')
|
|
166
|
+
.mockResolvedValue(createRestoreResult());
|
|
167
|
+
|
|
168
|
+
const result = await session.executeCommand('rewind', 'code turn-0001');
|
|
169
|
+
|
|
170
|
+
expect(result?.success).toBe(true);
|
|
171
|
+
expect(restoreEditCheckpoint).toHaveBeenCalledWith('turn-0001');
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
it('delegates rollback through session.executeCommand', async () => {
|
|
175
|
+
const session = createInteractiveSession();
|
|
176
|
+
const rollbackEditCheckpoint = vi
|
|
177
|
+
.spyOn(session, 'rollbackEditCheckpoint')
|
|
178
|
+
.mockResolvedValue(
|
|
179
|
+
createRestoreResult({ restoredCheckpointCount: 1, removedCheckpointCount: 1 }),
|
|
180
|
+
);
|
|
181
|
+
|
|
182
|
+
const result = await session.executeCommand('rewind', 'rollback turn-0001');
|
|
183
|
+
|
|
184
|
+
expect(result?.success).toBe(true);
|
|
185
|
+
expect(rollbackEditCheckpoint).toHaveBeenCalledWith('turn-0001');
|
|
186
|
+
expect(result?.message).toContain('Rolled back code through turn-0001.');
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
it('returns usage for invalid arguments without mutating state', async () => {
|
|
190
|
+
const session = createInteractiveSession();
|
|
191
|
+
const restoreEditCheckpoint = vi.spyOn(session, 'restoreEditCheckpoint');
|
|
192
|
+
const rollbackEditCheckpoint = vi.spyOn(session, 'rollbackEditCheckpoint');
|
|
193
|
+
|
|
194
|
+
const result = await executeRewindCommand(session, 'restore');
|
|
195
|
+
const unknownResult = await executeRewindCommand(session, 'unknown');
|
|
196
|
+
|
|
197
|
+
expect(result.success).toBe(false);
|
|
198
|
+
expect(result.message).toContain('Usage: rewind');
|
|
199
|
+
expect(unknownResult.success).toBe(false);
|
|
200
|
+
expect(restoreEditCheckpoint).not.toHaveBeenCalled();
|
|
201
|
+
expect(rollbackEditCheckpoint).not.toHaveBeenCalled();
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
it('returns checkpoint API errors as command failures', async () => {
|
|
205
|
+
const session = createInteractiveSession();
|
|
206
|
+
vi.spyOn(session, 'restoreEditCheckpoint').mockRejectedValue(
|
|
207
|
+
new Error('Unknown edit checkpoint'),
|
|
208
|
+
);
|
|
209
|
+
|
|
210
|
+
const result = await session.executeCommand('rewind', 'restore missing');
|
|
211
|
+
|
|
212
|
+
expect(result?.success).toBe(false);
|
|
213
|
+
expect(result?.message).toBe('Unknown edit checkpoint');
|
|
214
|
+
});
|
|
215
|
+
});
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
ICommand,
|
|
3
|
+
ICommandModule,
|
|
4
|
+
ICommandSource,
|
|
5
|
+
ISystemCommand,
|
|
6
|
+
} from '@robota-sdk/agent-framework';
|
|
7
|
+
import {
|
|
8
|
+
REWIND_COMMAND_ARGUMENT_HINT,
|
|
9
|
+
REWIND_COMMAND_DESCRIPTION,
|
|
10
|
+
buildRewindCommandSubcommands,
|
|
11
|
+
} from '@robota-sdk/agent-framework';
|
|
12
|
+
import { executeRewindCommand } from './rewind-command.js';
|
|
13
|
+
|
|
14
|
+
export function createRewindCommandEntry(): ICommand {
|
|
15
|
+
return {
|
|
16
|
+
name: 'rewind',
|
|
17
|
+
displayName: 'Rewind History',
|
|
18
|
+
description: REWIND_COMMAND_DESCRIPTION,
|
|
19
|
+
source: 'rewind',
|
|
20
|
+
argumentHint: REWIND_COMMAND_ARGUMENT_HINT,
|
|
21
|
+
modelInvocable: false,
|
|
22
|
+
safety: 'write',
|
|
23
|
+
subcommands: buildRewindCommandSubcommands(),
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function createRewindSystemCommand(): ISystemCommand {
|
|
28
|
+
const entry = createRewindCommandEntry();
|
|
29
|
+
return {
|
|
30
|
+
name: entry.name,
|
|
31
|
+
displayName: entry.displayName,
|
|
32
|
+
description: entry.description,
|
|
33
|
+
requiresPermission: false,
|
|
34
|
+
argumentHint: entry.argumentHint,
|
|
35
|
+
userInvocable: true,
|
|
36
|
+
modelInvocable: false,
|
|
37
|
+
safety: 'write',
|
|
38
|
+
subcommands: entry.subcommands,
|
|
39
|
+
execute: executeRewindCommand,
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export class RewindCommandSource implements ICommandSource {
|
|
44
|
+
readonly name = 'rewind';
|
|
45
|
+
|
|
46
|
+
getCommands(): ICommand[] {
|
|
47
|
+
return [createRewindCommandEntry()];
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export function createRewindCommandModule(): ICommandModule {
|
|
52
|
+
return {
|
|
53
|
+
name: 'agent-command-rewind',
|
|
54
|
+
commandSources: [new RewindCommandSource()],
|
|
55
|
+
systemCommands: [createRewindSystemCommand()],
|
|
56
|
+
};
|
|
57
|
+
}
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
ICommandHostContext,
|
|
3
|
+
ICommandResult,
|
|
4
|
+
IEditCheckpointInspection,
|
|
5
|
+
IEditCheckpointRestoreResult,
|
|
6
|
+
IEditCheckpointSummary,
|
|
7
|
+
} from '@robota-sdk/agent-framework';
|
|
8
|
+
import {
|
|
9
|
+
inspectCommandEditCheckpoint,
|
|
10
|
+
listCommandEditCheckpoints,
|
|
11
|
+
restoreCommandEditCheckpoint,
|
|
12
|
+
rollbackCommandEditCheckpoint,
|
|
13
|
+
} from '@robota-sdk/agent-framework';
|
|
14
|
+
|
|
15
|
+
const SUBCOMMAND_INDEX = 0;
|
|
16
|
+
const CHECKPOINT_ID_INDEX = 1;
|
|
17
|
+
const PROMPT_PREVIEW_LENGTH = 120;
|
|
18
|
+
const ELLIPSIS_LENGTH = 3;
|
|
19
|
+
|
|
20
|
+
function usage(): ICommandResult {
|
|
21
|
+
return {
|
|
22
|
+
message:
|
|
23
|
+
'Usage: rewind [list] | rewind inspect <checkpoint-id> | rewind restore <checkpoint-id> | rewind code <checkpoint-id> | rewind rollback <checkpoint-id>',
|
|
24
|
+
success: false,
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function formatPrompt(prompt: string): string {
|
|
29
|
+
const compact = prompt.replace(/\s+/g, ' ').trim();
|
|
30
|
+
if (compact.length <= PROMPT_PREVIEW_LENGTH) return compact;
|
|
31
|
+
return `${compact.slice(0, PROMPT_PREVIEW_LENGTH - ELLIPSIS_LENGTH)}...`;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function formatList(checkpoints: readonly IEditCheckpointSummary[]): ICommandResult {
|
|
35
|
+
const lines =
|
|
36
|
+
checkpoints.length > 0
|
|
37
|
+
? checkpoints.map(
|
|
38
|
+
(checkpoint) =>
|
|
39
|
+
`- ${checkpoint.id} files=${checkpoint.fileCount} ${checkpoint.createdAt} ${formatPrompt(
|
|
40
|
+
checkpoint.prompt,
|
|
41
|
+
)}`,
|
|
42
|
+
)
|
|
43
|
+
: ['(no edit checkpoints)'];
|
|
44
|
+
|
|
45
|
+
return {
|
|
46
|
+
message: ['Edit checkpoints:', ...lines].join('\n'),
|
|
47
|
+
success: true,
|
|
48
|
+
data: { count: checkpoints.length, checkpoints: [...checkpoints] },
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function formatCheckpointIds(ids: readonly string[]): string {
|
|
53
|
+
return ids.length > 0 ? ids.join(', ') : '(none)';
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function formatInspection(inspection: IEditCheckpointInspection): ICommandResult {
|
|
57
|
+
const fileLines =
|
|
58
|
+
inspection.capturedFiles.length > 0
|
|
59
|
+
? inspection.capturedFiles.map((file) => {
|
|
60
|
+
const size =
|
|
61
|
+
file.snapshotSizeBytes === undefined ? '' : ` size=${file.snapshotSizeBytes}B`;
|
|
62
|
+
return `- ${file.relativePath} action=${file.restoreAction} snapshot=${String(
|
|
63
|
+
file.snapshotAvailable,
|
|
64
|
+
)}${size}`;
|
|
65
|
+
})
|
|
66
|
+
: ['(no files captured)'];
|
|
67
|
+
|
|
68
|
+
return {
|
|
69
|
+
message: [
|
|
70
|
+
`Checkpoint ${inspection.target.id}`,
|
|
71
|
+
`Prompt: ${formatPrompt(inspection.target.prompt)}`,
|
|
72
|
+
'Captured files:',
|
|
73
|
+
...fileLines,
|
|
74
|
+
`Restore later checkpoints: files=${inspection.restoreToCheckpoint.fileCount} checkpoints=${formatCheckpointIds(
|
|
75
|
+
inspection.restoreToCheckpoint.checkpointIds,
|
|
76
|
+
)}`,
|
|
77
|
+
`Rollback through checkpoint: files=${inspection.rollbackThroughCheckpoint.fileCount} checkpoints=${formatCheckpointIds(
|
|
78
|
+
inspection.rollbackThroughCheckpoint.checkpointIds,
|
|
79
|
+
)}`,
|
|
80
|
+
].join('\n'),
|
|
81
|
+
success: true,
|
|
82
|
+
data: { inspection },
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function formatRestoreResult(result: IEditCheckpointRestoreResult): ICommandResult {
|
|
87
|
+
return {
|
|
88
|
+
message: [
|
|
89
|
+
`Restored code to ${result.target.id}.`,
|
|
90
|
+
`Restored files: ${result.restoredFileCount}`,
|
|
91
|
+
`Rolled back checkpoints: ${result.restoredCheckpointCount}`,
|
|
92
|
+
].join('\n'),
|
|
93
|
+
success: true,
|
|
94
|
+
data: {
|
|
95
|
+
target: result.target,
|
|
96
|
+
restoredCheckpointCount: result.restoredCheckpointCount,
|
|
97
|
+
restoredFileCount: result.restoredFileCount,
|
|
98
|
+
removedCheckpointCount: result.removedCheckpointCount,
|
|
99
|
+
},
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function formatRollbackResult(result: IEditCheckpointRestoreResult): ICommandResult {
|
|
104
|
+
return {
|
|
105
|
+
message: [
|
|
106
|
+
`Rolled back code through ${result.target.id}.`,
|
|
107
|
+
`Restored files: ${result.restoredFileCount}`,
|
|
108
|
+
`Removed checkpoints: ${result.removedCheckpointCount}`,
|
|
109
|
+
].join('\n'),
|
|
110
|
+
success: true,
|
|
111
|
+
data: {
|
|
112
|
+
target: result.target,
|
|
113
|
+
restoredCheckpointCount: result.restoredCheckpointCount,
|
|
114
|
+
restoredFileCount: result.restoredFileCount,
|
|
115
|
+
removedCheckpointCount: result.removedCheckpointCount,
|
|
116
|
+
},
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
function formatError(error: Error | string): ICommandResult {
|
|
121
|
+
return {
|
|
122
|
+
message: error instanceof Error ? error.message : String(error),
|
|
123
|
+
success: false,
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
function inspect(context: ICommandHostContext, checkpointId: string | undefined): ICommandResult {
|
|
128
|
+
if (!checkpointId) return usage();
|
|
129
|
+
try {
|
|
130
|
+
return formatInspection(inspectCommandEditCheckpoint(context, checkpointId));
|
|
131
|
+
} catch (error) {
|
|
132
|
+
return formatError(error instanceof Error ? error : String(error));
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
async function restore(
|
|
137
|
+
context: ICommandHostContext,
|
|
138
|
+
checkpointId: string | undefined,
|
|
139
|
+
): Promise<ICommandResult> {
|
|
140
|
+
if (!checkpointId) return usage();
|
|
141
|
+
try {
|
|
142
|
+
return formatRestoreResult(await restoreCommandEditCheckpoint(context, checkpointId));
|
|
143
|
+
} catch (error) {
|
|
144
|
+
return formatError(error instanceof Error ? error : String(error));
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
async function rollback(
|
|
149
|
+
context: ICommandHostContext,
|
|
150
|
+
checkpointId: string | undefined,
|
|
151
|
+
): Promise<ICommandResult> {
|
|
152
|
+
if (!checkpointId) return usage();
|
|
153
|
+
try {
|
|
154
|
+
return formatRollbackResult(await rollbackCommandEditCheckpoint(context, checkpointId));
|
|
155
|
+
} catch (error) {
|
|
156
|
+
return formatError(error instanceof Error ? error : String(error));
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
export async function executeRewindCommand(
|
|
161
|
+
context: ICommandHostContext,
|
|
162
|
+
rawArgs: string,
|
|
163
|
+
): Promise<ICommandResult> {
|
|
164
|
+
const args = rawArgs.trim().split(/\s+/).filter(Boolean);
|
|
165
|
+
const subcommand = args[SUBCOMMAND_INDEX] ?? 'list';
|
|
166
|
+
|
|
167
|
+
if (subcommand === 'list') {
|
|
168
|
+
return formatList(listCommandEditCheckpoints(context));
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
if (subcommand === 'inspect') {
|
|
172
|
+
return inspect(context, args[CHECKPOINT_ID_INDEX]);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
if (subcommand === 'restore' || subcommand === 'code') {
|
|
176
|
+
return restore(context, args[CHECKPOINT_ID_INDEX]);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
if (subcommand === 'rollback') {
|
|
180
|
+
return rollback(context, args[CHECKPOINT_ID_INDEX]);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
return usage();
|
|
184
|
+
}
|