@robota-sdk/agent-command 3.0.0-beta.86 → 3.0.0-beta.88
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/CHANGELOG.md +64 -12
- package/README.md +6 -3
- package/dist/node/index.cjs +72 -70
- package/dist/node/index.d.cts +16 -8
- package/dist/node/index.d.cts.map +1 -1
- package/dist/node/index.d.ts +16 -8
- package/dist/node/index.d.ts.map +1 -1
- package/dist/node/index.js +51 -49
- package/dist/node/index.js.map +1 -1
- package/package.json +15 -13
- package/src/agent/__tests__/agent-command.test.ts +5 -5
- package/src/agent/agent-command-module.ts +1 -1
- package/src/background/__tests__/background-command-module.test.ts +6 -6
- package/src/default/__tests__/__snapshots__/model-exposure.test.ts.snap +15 -4
- package/src/default/__tests__/default-command-modules.test.ts +2 -2
- package/src/default/__tests__/model-exposure.test.ts +18 -5
- package/src/default/__tests__/org-policy-forwarding.test.ts +1 -1
- package/src/default/default-command-modules.ts +2 -2
- package/src/devices/devices-command-module.ts +1 -1
- package/src/doctor/__tests__/doctor-command-module.test.ts +10 -10
- package/src/doctor/__tests__/doctor-display-vocabulary.test.ts +1 -1
- package/src/doctor/__tests__/doctor-fixture.ts +14 -14
- package/src/doctor/__tests__/doctor-runner.test.ts +57 -57
- package/src/doctor/doctor-storage-probe.ts +1 -1
- package/src/doctor/doctor-types.ts +2 -2
- package/src/editor/__tests__/editor-command-functional.test.ts +1 -1
- package/src/fork/__tests__/fork-command.test.ts +1 -1
- package/src/fork/fork-command-module.ts +1 -1
- package/src/git/__tests__/git-command-module.test.ts +1 -1
- package/src/handoff/__tests__/handoff-command-module.test.ts +1 -1
- package/src/handoff/handoff-command-module.ts +1 -1
- package/src/keybindings/keybindings-command-module.ts +1 -1
- package/src/mcp-activation/__tests__/mcp-activation-command.test.ts +8 -8
- package/src/mcp-activation/__tests__/mcp-model-view.test.ts +43 -12
- package/src/mcp-activation/__tests__/mcp-skill-command.test.ts +141 -0
- package/src/mcp-activation/mcp-activation-command-module.ts +27 -2
- package/src/mcp-activation/mcp-activation-command.ts +149 -15
- package/src/mcp-activation/mcp-model-notice.ts +19 -5
- package/src/mcp-activation/mcp-model-status.ts +7 -2
- package/src/memory/__tests__/memory-command-module.test.ts +89 -16
- package/src/memory/memory-command-module.ts +4 -1
- package/src/memory/memory-command.ts +53 -1
- package/src/peers/__tests__/peers-command-module.test.ts +1 -1
- package/src/peers/peers-command-module.ts +1 -1
- package/src/permissions/__tests__/permissions-command-module.test.ts +4 -4
- package/src/plugin/__tests__/plugin-active-turn.test.ts +100 -0
- package/src/plugin/__tests__/plugin-command-module.test.ts +7 -7
- package/src/plugin/plugin-command-module.ts +4 -0
- package/src/provider/provider-startup.ts +4 -2
- package/src/remote-control/__tests__/remote-control-command.test.ts +2 -2
- package/src/rewind/__tests__/rewind-command-module.test.ts +4 -3
- package/src/rewind/rewind-command.ts +27 -21
- package/src/schedule/schedule-command-module.ts +1 -1
- package/src/session/__tests__/session-command-module.test.ts +2 -2
- package/src/skills/__tests__/skill-read-command.test.ts +64 -0
- package/src/skills/__tests__/skills-command-module.test.ts +2 -1
- package/src/skills/skill-read-command.ts +60 -0
- package/src/skills/skills-command-module.ts +3 -2
- package/src/terminal-client/__tests__/terminal-client-commands.test.ts +1 -1
- package/src/theme/__tests__/theme-command.test.ts +1 -1
- package/src/theme/theme-command-module.ts +1 -1
- package/src/user-local/__tests__/user-local-command.test.ts +16 -16
|
@@ -15,8 +15,13 @@ import type {
|
|
|
15
15
|
IEditCheckpointRestoreResult,
|
|
16
16
|
IEditCheckpointSummary,
|
|
17
17
|
} from '@robota-sdk/agent-framework';
|
|
18
|
+
import type { ICommandProductVocabulary } from '@robota-sdk/agent-framework';
|
|
18
19
|
import type { ICommandResult } from '@robota-sdk/agent-interface-command';
|
|
19
20
|
|
|
21
|
+
type TRewindCommandContext = ICommandHostCheckpoints & {
|
|
22
|
+
getCommandProductVocabulary?(): ICommandProductVocabulary | undefined;
|
|
23
|
+
};
|
|
24
|
+
|
|
20
25
|
const SUBCOMMAND_INDEX = 0;
|
|
21
26
|
const CHECKPOINT_ID_INDEX = 1;
|
|
22
27
|
const PROMPT_PREVIEW_LENGTH = 120;
|
|
@@ -123,17 +128,18 @@ function formatRollbackResult(result: IEditCheckpointRestoreResult): ICommandRes
|
|
|
123
128
|
}
|
|
124
129
|
|
|
125
130
|
/** A restricted workspace is the one case the user fixes with a command; the rest say why. */
|
|
126
|
-
function unavailableMessage(error: EditCheckpointsUnavailableError): string {
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
131
|
+
function unavailableMessage(error: EditCheckpointsUnavailableError, cliName?: string): string {
|
|
132
|
+
if (error.reason !== 'restricted-workspace') return error.message;
|
|
133
|
+
return cliName === undefined
|
|
134
|
+
? 'Edit checkpoints need a trusted workspace. Trust it from a terminal, then restart the session.'
|
|
135
|
+
: `Edit checkpoints need a trusted workspace: run ${cliName} trust --yes, then restart ${cliName}.`;
|
|
130
136
|
}
|
|
131
137
|
|
|
132
|
-
function formatError(error: Error | string): ICommandResult {
|
|
138
|
+
function formatError(error: Error | string, cliName?: string): ICommandResult {
|
|
133
139
|
return {
|
|
134
140
|
message:
|
|
135
141
|
error instanceof EditCheckpointsUnavailableError
|
|
136
|
-
? unavailableMessage(error)
|
|
142
|
+
? unavailableMessage(error, cliName)
|
|
137
143
|
: error instanceof Error
|
|
138
144
|
? error.message
|
|
139
145
|
: String(error),
|
|
@@ -141,53 +147,53 @@ function formatError(error: Error | string): ICommandResult {
|
|
|
141
147
|
};
|
|
142
148
|
}
|
|
143
149
|
|
|
144
|
-
function list(context:
|
|
150
|
+
function list(context: TRewindCommandContext): ICommandResult {
|
|
145
151
|
try {
|
|
146
152
|
return formatList(listCommandEditCheckpoints(context));
|
|
147
153
|
} catch (error) {
|
|
148
|
-
return formatError(error instanceof Error ? error : String(error));
|
|
154
|
+
return formatError(error instanceof Error ? error : String(error), context.getCommandProductVocabulary?.()?.cliName);
|
|
149
155
|
}
|
|
150
156
|
}
|
|
151
157
|
|
|
152
158
|
function inspect(
|
|
153
|
-
context:
|
|
159
|
+
context: TRewindCommandContext,
|
|
154
160
|
checkpointId: string | undefined,
|
|
155
161
|
): ICommandResult {
|
|
156
162
|
if (!checkpointId) return usage();
|
|
157
163
|
try {
|
|
158
164
|
return formatInspection(inspectCommandEditCheckpoint(context, checkpointId));
|
|
159
165
|
} catch (error) {
|
|
160
|
-
return formatError(error instanceof Error ? error : String(error));
|
|
166
|
+
return formatError(error instanceof Error ? error : String(error), context.getCommandProductVocabulary?.()?.cliName);
|
|
161
167
|
}
|
|
162
168
|
}
|
|
163
169
|
|
|
164
170
|
async function restore(
|
|
165
|
-
context:
|
|
171
|
+
context: TRewindCommandContext,
|
|
166
172
|
checkpointId: string | undefined,
|
|
167
173
|
): Promise<ICommandResult> {
|
|
168
174
|
if (!checkpointId) return usage();
|
|
169
175
|
try {
|
|
170
176
|
return formatRestoreResult(await restoreCommandEditCheckpoint(context, checkpointId));
|
|
171
177
|
} catch (error) {
|
|
172
|
-
return formatError(error instanceof Error ? error : String(error));
|
|
178
|
+
return formatError(error instanceof Error ? error : String(error), context.getCommandProductVocabulary?.()?.cliName);
|
|
173
179
|
}
|
|
174
180
|
}
|
|
175
181
|
|
|
176
182
|
async function rollback(
|
|
177
|
-
context:
|
|
183
|
+
context: TRewindCommandContext,
|
|
178
184
|
checkpointId: string | undefined,
|
|
179
185
|
): Promise<ICommandResult> {
|
|
180
186
|
if (!checkpointId) return usage();
|
|
181
187
|
try {
|
|
182
188
|
return formatRollbackResult(await rollbackCommandEditCheckpoint(context, checkpointId));
|
|
183
189
|
} catch (error) {
|
|
184
|
-
return formatError(error instanceof Error ? error : String(error));
|
|
190
|
+
return formatError(error instanceof Error ? error : String(error), context.getCommandProductVocabulary?.()?.cliName);
|
|
185
191
|
}
|
|
186
192
|
}
|
|
187
193
|
|
|
188
194
|
// SELFHOST-007: branching time-travel handlers.
|
|
189
195
|
async function fork(
|
|
190
|
-
context:
|
|
196
|
+
context: TRewindCommandContext,
|
|
191
197
|
checkpointId: string | undefined,
|
|
192
198
|
): Promise<ICommandResult> {
|
|
193
199
|
if (!checkpointId) return usage();
|
|
@@ -202,12 +208,12 @@ async function fork(
|
|
|
202
208
|
data: { target: result.target, restoredFileCount: result.restoredFileCount },
|
|
203
209
|
};
|
|
204
210
|
} catch (error) {
|
|
205
|
-
return formatError(error instanceof Error ? error : String(error));
|
|
211
|
+
return formatError(error instanceof Error ? error : String(error), context.getCommandProductVocabulary?.()?.cliName);
|
|
206
212
|
}
|
|
207
213
|
}
|
|
208
214
|
|
|
209
215
|
function switchBranch(
|
|
210
|
-
context:
|
|
216
|
+
context: TRewindCommandContext,
|
|
211
217
|
checkpointId: string | undefined,
|
|
212
218
|
): ICommandResult {
|
|
213
219
|
if (!checkpointId) return usage();
|
|
@@ -215,11 +221,11 @@ function switchBranch(
|
|
|
215
221
|
switchCommandEditCheckpointBranch(context, checkpointId);
|
|
216
222
|
return { message: `Switched to checkpoint branch ${checkpointId}.`, success: true };
|
|
217
223
|
} catch (error) {
|
|
218
|
-
return formatError(error instanceof Error ? error : String(error));
|
|
224
|
+
return formatError(error instanceof Error ? error : String(error), context.getCommandProductVocabulary?.()?.cliName);
|
|
219
225
|
}
|
|
220
226
|
}
|
|
221
227
|
|
|
222
|
-
function branches(context:
|
|
228
|
+
function branches(context: TRewindCommandContext): ICommandResult {
|
|
223
229
|
try {
|
|
224
230
|
const tips = listCommandEditCheckpointBranches(context);
|
|
225
231
|
return {
|
|
@@ -231,12 +237,12 @@ function branches(context: ICommandHostCheckpoints): ICommandResult {
|
|
|
231
237
|
data: { branches: tips },
|
|
232
238
|
};
|
|
233
239
|
} catch (error) {
|
|
234
|
-
return formatError(error instanceof Error ? error : String(error));
|
|
240
|
+
return formatError(error instanceof Error ? error : String(error), context.getCommandProductVocabulary?.()?.cliName);
|
|
235
241
|
}
|
|
236
242
|
}
|
|
237
243
|
|
|
238
244
|
export async function executeRewindCommand(
|
|
239
|
-
context:
|
|
245
|
+
context: TRewindCommandContext,
|
|
240
246
|
rawArgs: string,
|
|
241
247
|
): Promise<ICommandResult> {
|
|
242
248
|
const args = rawArgs.trim().split(/\s+/).filter(Boolean);
|
|
@@ -544,7 +544,7 @@ describe('createSessionCommandModule', () => {
|
|
|
544
544
|
const context = {
|
|
545
545
|
...createCommandContext(),
|
|
546
546
|
validateCurrentSessionReplayLog: () => ({
|
|
547
|
-
logFile: '/workspace/.
|
|
547
|
+
logFile: '/workspace/.fixture-state/logs/session_1.jsonl',
|
|
548
548
|
entryCount: 2,
|
|
549
549
|
validation: {
|
|
550
550
|
ok: false,
|
|
@@ -565,7 +565,7 @@ describe('createSessionCommandModule', () => {
|
|
|
565
565
|
expect(result?.success).toBe(false);
|
|
566
566
|
expect(result?.message).toContain('PROVIDER_NATIVE_RAW_PAYLOAD_MISSING');
|
|
567
567
|
expect(result?.data).toEqual({
|
|
568
|
-
logFile: '/workspace/.
|
|
568
|
+
logFile: '/workspace/.fixture-state/logs/session_1.jsonl',
|
|
569
569
|
entryCount: 2,
|
|
570
570
|
issueCount: 1,
|
|
571
571
|
ok: false,
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { expect, it, vi } from 'vitest';
|
|
2
|
+
import { SystemCommandExecutor } from '@robota-sdk/agent-framework';
|
|
3
|
+
import { createTestCommandHost } from '@robota-sdk/agent-framework/testing';
|
|
4
|
+
import { createSkillsCommandModule } from '../skills-command-module.js';
|
|
5
|
+
|
|
6
|
+
it('offers supporting reads to the model under the composed semantic command identity', async () => {
|
|
7
|
+
const module = createSkillsCommandModule({ contributionSources: [] });
|
|
8
|
+
const executor = new SystemCommandExecutor([...(module.systemCommands ?? [])]);
|
|
9
|
+
expect(executor.getSemanticRoles().skillResourceRead).toBe('skill-read');
|
|
10
|
+
expect(executor.isModelInvocable('skill-read')).toBe(true);
|
|
11
|
+
expect(
|
|
12
|
+
executor.listModelInvocableCommands().find((command) => command.name === 'skill-read')
|
|
13
|
+
?.description,
|
|
14
|
+
).toContain('already activated');
|
|
15
|
+
const resource = {
|
|
16
|
+
uri: 'skill://demo/reference%20file.txt',
|
|
17
|
+
digest: 'sha256:verified',
|
|
18
|
+
size: 7,
|
|
19
|
+
text: 'content',
|
|
20
|
+
};
|
|
21
|
+
const readSkillResource = vi.fn(async () => resource);
|
|
22
|
+
const context = createTestCommandHost({ overrides: { readSkillResource } });
|
|
23
|
+
const result = await executor.executeModelInvocable(
|
|
24
|
+
'skill-read',
|
|
25
|
+
context,
|
|
26
|
+
JSON.stringify(['demo', resource.uri]),
|
|
27
|
+
);
|
|
28
|
+
expect(readSkillResource).toHaveBeenCalledWith('demo', resource.uri);
|
|
29
|
+
expect(result).toMatchObject({ success: true, data: resource });
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
it.each(['', '{}', '["demo"]', '["demo", 3]', '["demo", "uri", "extra"]', '["", "uri"]'])(
|
|
33
|
+
'refuses malformed resource arguments without calling the host: %s',
|
|
34
|
+
async (args) => {
|
|
35
|
+
const executor = new SystemCommandExecutor([
|
|
36
|
+
...(createSkillsCommandModule({ contributionSources: [] }).systemCommands ?? []),
|
|
37
|
+
]);
|
|
38
|
+
const readSkillResource = vi.fn();
|
|
39
|
+
const result = await executor.executeModelInvocable(
|
|
40
|
+
'skill-read',
|
|
41
|
+
createTestCommandHost({ overrides: { readSkillResource } }),
|
|
42
|
+
args,
|
|
43
|
+
);
|
|
44
|
+
expect(result?.success).toBe(false);
|
|
45
|
+
expect(readSkillResource).not.toHaveBeenCalled();
|
|
46
|
+
},
|
|
47
|
+
);
|
|
48
|
+
|
|
49
|
+
it('reports an unavailable host instead of acquiring a skill or falling back to filesystem reads', async () => {
|
|
50
|
+
const executor = new SystemCommandExecutor([
|
|
51
|
+
...(createSkillsCommandModule({ contributionSources: [] }).systemCommands ?? []),
|
|
52
|
+
]);
|
|
53
|
+
const executeSkillCommandByName = vi.fn();
|
|
54
|
+
const result = await executor.executeModelInvocable(
|
|
55
|
+
'skill-read',
|
|
56
|
+
createTestCommandHost({ overrides: { executeSkillCommandByName } }),
|
|
57
|
+
'["demo", "skill://demo/file"]',
|
|
58
|
+
);
|
|
59
|
+
expect(result).toMatchObject({
|
|
60
|
+
success: false,
|
|
61
|
+
message: expect.stringContaining('does not provide'),
|
|
62
|
+
});
|
|
63
|
+
expect(executeSkillCommandByName).not.toHaveBeenCalled();
|
|
64
|
+
});
|
|
@@ -94,6 +94,7 @@ describe('createSkillsCommandModule', () => {
|
|
|
94
94
|
expect(module.name).toBe('agent-command-skills');
|
|
95
95
|
expect(module.commandSources?.[0]?.getCommands().map((entry) => entry.name)).toEqual([
|
|
96
96
|
'skills',
|
|
97
|
+
'skill-read',
|
|
97
98
|
]);
|
|
98
99
|
expect(command).toMatchObject({
|
|
99
100
|
name: 'skills',
|
|
@@ -164,7 +165,7 @@ describe('createSkillsCommandModule', () => {
|
|
|
164
165
|
});
|
|
165
166
|
|
|
166
167
|
it('lets the SDK normalize virtual /skill-name commands into the composed skills command', async () => {
|
|
167
|
-
const cwd = realpathSync(mkdtempSync(join(tmpdir(), '
|
|
168
|
+
const cwd = realpathSync(mkdtempSync(join(tmpdir(), 'agent-test-skills-command-module-')));
|
|
168
169
|
createTempSkill(cwd);
|
|
169
170
|
const parentSession = makeParentSession();
|
|
170
171
|
const session = new InteractiveSession({
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import type { ICommand, ICommandResult } from '@robota-sdk/agent-interface-command';
|
|
2
|
+
import type { ICommandHostCatalog, ISystemCommand } from '@robota-sdk/agent-framework';
|
|
3
|
+
|
|
4
|
+
export function createSkillReadCommandEntry(): ICommand {
|
|
5
|
+
return {
|
|
6
|
+
name: 'skill-read',
|
|
7
|
+
description: 'Read a supporting file from a skill active in this execution',
|
|
8
|
+
modelDescription:
|
|
9
|
+
'Read verified supporting bytes from a skill already activated in this execution. Use when its instructions refer to a resource in the returned manifest. Args are a JSON array ["<skill-name>", "<resource-uri>"]. Returns the verified URI, digest, size and text or base64 blob with optional MIME type. This does not activate nested skills or grant consent; unactivated skills, undeclared resources and ended executions are refused. Binary blobs are returned as encoded data, not a rendered image.',
|
|
10
|
+
argumentHint: '["<skill-name>", "<resource-uri>"]',
|
|
11
|
+
source: 'skills',
|
|
12
|
+
userInvocable: true,
|
|
13
|
+
modelInvocable: true,
|
|
14
|
+
safety: 'read-only',
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export async function executeSkillReadCommand(
|
|
19
|
+
context: Pick<ICommandHostCatalog, 'readSkillResource'>,
|
|
20
|
+
args = '',
|
|
21
|
+
): Promise<ICommandResult> {
|
|
22
|
+
let input: unknown;
|
|
23
|
+
try {
|
|
24
|
+
input = JSON.parse(args);
|
|
25
|
+
} catch {
|
|
26
|
+
return { success: false, message: 'Use /skill-read ["<skill-name>", "<resource-uri>"]' };
|
|
27
|
+
}
|
|
28
|
+
if (
|
|
29
|
+
!Array.isArray(input) ||
|
|
30
|
+
input.length !== 2 ||
|
|
31
|
+
input.some((value) => typeof value !== 'string' || !value.length)
|
|
32
|
+
)
|
|
33
|
+
return { success: false, message: 'Use /skill-read ["<skill-name>", "<resource-uri>"]' };
|
|
34
|
+
if (!context.readSkillResource)
|
|
35
|
+
return { success: false, message: 'This host does not provide active skill resource reads.' };
|
|
36
|
+
try {
|
|
37
|
+
const resource = await context.readSkillResource(input[0], input[1]);
|
|
38
|
+
return {
|
|
39
|
+
success: true,
|
|
40
|
+
message: `Read verified skill resource: ${resource.uri}`,
|
|
41
|
+
data: { ...resource },
|
|
42
|
+
};
|
|
43
|
+
} catch (error) {
|
|
44
|
+
return {
|
|
45
|
+
success: false,
|
|
46
|
+
message: `Skill resource read refused: ${error instanceof Error ? error.message : 'host refused the read'}`,
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export function createSkillReadSystemCommand(): ISystemCommand {
|
|
52
|
+
const entry = createSkillReadCommandEntry();
|
|
53
|
+
return {
|
|
54
|
+
...entry,
|
|
55
|
+
semanticRole: 'skillResourceRead',
|
|
56
|
+
requiresPermission: false,
|
|
57
|
+
lifecycle: 'inline',
|
|
58
|
+
execute: executeSkillReadCommand,
|
|
59
|
+
};
|
|
60
|
+
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { SkillCommandSource } from '@robota-sdk/agent-framework';
|
|
2
|
+
import { createSkillReadCommandEntry, createSkillReadSystemCommand } from './skill-read-command.js';
|
|
2
3
|
|
|
3
4
|
import {
|
|
4
5
|
executeSkillsCommand,
|
|
@@ -57,7 +58,7 @@ export class SkillsCommandSource implements ICommandSource {
|
|
|
57
58
|
readonly name = 'skills';
|
|
58
59
|
|
|
59
60
|
getCommands(): ICommand[] {
|
|
60
|
-
return [createSkillsCommandEntry()];
|
|
61
|
+
return [createSkillsCommandEntry(), createSkillReadCommandEntry()];
|
|
61
62
|
}
|
|
62
63
|
}
|
|
63
64
|
|
|
@@ -70,6 +71,6 @@ export function createSkillsCommandModule(options: ISkillsCommandModuleOptions):
|
|
|
70
71
|
return {
|
|
71
72
|
name: 'agent-command-skills',
|
|
72
73
|
commandSources,
|
|
73
|
-
systemCommands: [createSkillsSystemCommand()],
|
|
74
|
+
systemCommands: [createSkillsSystemCommand(), createSkillReadSystemCommand()],
|
|
74
75
|
};
|
|
75
76
|
}
|
|
@@ -35,7 +35,7 @@ import type {
|
|
|
35
35
|
} from '@robota-sdk/agent-interface-command';
|
|
36
36
|
|
|
37
37
|
/** User-local storage lives in a private per-run directory, never a fixed name under /tmp. */
|
|
38
|
-
const USER_LOCAL_STORAGE_ROOT = mkdtempSync(join(tmpdir(), '
|
|
38
|
+
const USER_LOCAL_STORAGE_ROOT = mkdtempSync(join(tmpdir(), 'agent-test-test-'));
|
|
39
39
|
afterAll(() => rmSync(USER_LOCAL_STORAGE_ROOT, { recursive: true, force: true }));
|
|
40
40
|
|
|
41
41
|
const providerDefinitions: readonly IProviderDefinition[] = [];
|
|
@@ -73,7 +73,7 @@ describe('/theme without a catalogue (SCREEN-2002 TC-09)', () => {
|
|
|
73
73
|
const result = executeThemeCommand(undefined, 'light');
|
|
74
74
|
|
|
75
75
|
expect(result.success).toBe(false);
|
|
76
|
-
expect(result.message).toMatch(/Run \/theme
|
|
76
|
+
expect(result.message).toMatch(/Run \/theme there/);
|
|
77
77
|
expect(result.hostActions).toBeUndefined();
|
|
78
78
|
});
|
|
79
79
|
});
|
|
@@ -19,7 +19,7 @@ const THEME_ARGUMENT_HINT = 'list | <theme-id> | syntax on|off | motion on|off';
|
|
|
19
19
|
// A host without a terminal (the desktop app's sidecar) still answers, so the command is never
|
|
20
20
|
// "unknown" there — it says where themes live instead.
|
|
21
21
|
const THEME_UNAVAILABLE =
|
|
22
|
-
'Themes
|
|
22
|
+
'Themes are available in an interactive terminal. Run /theme there.';
|
|
23
23
|
|
|
24
24
|
function formatToggle(value: boolean): string {
|
|
25
25
|
return value ? 'on' : 'off';
|
|
@@ -22,12 +22,12 @@ afterEach(async () => {
|
|
|
22
22
|
|
|
23
23
|
describe('user-local command', () => {
|
|
24
24
|
it('does not advertise a specific product from the neutral command entry', async () => {
|
|
25
|
-
const module = createUserLocalCommandModule(await createTempRoot('
|
|
26
|
-
expect(module.commandSources?.[0]?.getCommands()[0]?.description).not.toMatch(/
|
|
25
|
+
const module = createUserLocalCommandModule(await createTempRoot('agent-test-selected-storage-'));
|
|
26
|
+
expect(module.commandSources?.[0]?.getCommands()[0]?.description).not.toMatch(/ConversationAgent/i);
|
|
27
27
|
});
|
|
28
28
|
|
|
29
29
|
it('fails closed when a direct caller omits the storage root', async () => {
|
|
30
|
-
const workspace = await createTempRoot('
|
|
30
|
+
const workspace = await createTempRoot('agent-test-user-local-missing-root-');
|
|
31
31
|
const repo = path.join(workspace, 'repo');
|
|
32
32
|
await fs.mkdir(repo);
|
|
33
33
|
|
|
@@ -40,9 +40,9 @@ describe('user-local command', () => {
|
|
|
40
40
|
});
|
|
41
41
|
|
|
42
42
|
it('uses the explicit root for slash commands', async () => {
|
|
43
|
-
const workspace = await createTempRoot('
|
|
43
|
+
const workspace = await createTempRoot('agent-test-user-local-slash-root-');
|
|
44
44
|
const repo = path.join(workspace, 'repo');
|
|
45
|
-
const selectedRoot = path.join(workspace, 'selected', '.
|
|
45
|
+
const selectedRoot = path.join(workspace, 'selected', '.fixture-state');
|
|
46
46
|
await fs.mkdir(repo);
|
|
47
47
|
vi.stubEnv('HOME', path.join(workspace, 'ambient-home'));
|
|
48
48
|
|
|
@@ -65,7 +65,7 @@ describe('user-local command', () => {
|
|
|
65
65
|
});
|
|
66
66
|
|
|
67
67
|
it('prints storage inspection JSON without provider configuration', async () => {
|
|
68
|
-
const workspace = await createTempRoot('
|
|
68
|
+
const workspace = await createTempRoot('agent-test-user-local-command-');
|
|
69
69
|
const repo = path.join(workspace, 'repo');
|
|
70
70
|
const home = path.join(workspace, 'home');
|
|
71
71
|
await fs.mkdir(repo);
|
|
@@ -74,7 +74,7 @@ describe('user-local command', () => {
|
|
|
74
74
|
|
|
75
75
|
const result = await executeUserLocalDirectCommand({
|
|
76
76
|
cwd: repo,
|
|
77
|
-
storageRoot: path.join(home, '.
|
|
77
|
+
storageRoot: path.join(home, '.fixture-state'),
|
|
78
78
|
argv: ['storage', 'list'],
|
|
79
79
|
format: 'json',
|
|
80
80
|
});
|
|
@@ -84,7 +84,7 @@ describe('user-local command', () => {
|
|
|
84
84
|
};
|
|
85
85
|
|
|
86
86
|
expect(result.success).toBe(true);
|
|
87
|
-
expect(parsed.root).toBe(path.join(home, '.
|
|
87
|
+
expect(parsed.root).toBe(path.join(home, '.fixture-state'));
|
|
88
88
|
expect(parsed.categories.map((item) => item.category)).toEqual([
|
|
89
89
|
'preferences',
|
|
90
90
|
'view-state',
|
|
@@ -97,7 +97,7 @@ describe('user-local command', () => {
|
|
|
97
97
|
});
|
|
98
98
|
|
|
99
99
|
it('stores, lists, inspects, disables, and deletes user-local memory items', async () => {
|
|
100
|
-
const workspace = await createTempRoot('
|
|
100
|
+
const workspace = await createTempRoot('agent-test-user-local-memory-command-');
|
|
101
101
|
const repo = path.join(workspace, 'repo');
|
|
102
102
|
const home = path.join(workspace, 'home');
|
|
103
103
|
await fs.mkdir(repo);
|
|
@@ -106,7 +106,7 @@ describe('user-local command', () => {
|
|
|
106
106
|
|
|
107
107
|
const setResult = await executeUserLocalDirectCommand({
|
|
108
108
|
cwd: repo,
|
|
109
|
-
storageRoot: path.join(home, '.
|
|
109
|
+
storageRoot: path.join(home, '.fixture-state'),
|
|
110
110
|
argv: ['memory', 'set', 'view-preference', 'last-panel', 'background'],
|
|
111
111
|
summary: 'Open the background panel',
|
|
112
112
|
source: 'user-input',
|
|
@@ -116,7 +116,7 @@ describe('user-local command', () => {
|
|
|
116
116
|
|
|
117
117
|
const listResult = await executeUserLocalDirectCommand({
|
|
118
118
|
cwd: repo,
|
|
119
|
-
storageRoot: path.join(home, '.
|
|
119
|
+
storageRoot: path.join(home, '.fixture-state'),
|
|
120
120
|
argv: ['memory', 'list'],
|
|
121
121
|
format: 'json',
|
|
122
122
|
});
|
|
@@ -133,7 +133,7 @@ describe('user-local command', () => {
|
|
|
133
133
|
|
|
134
134
|
const inspectResult = await executeUserLocalDirectCommand({
|
|
135
135
|
cwd: repo,
|
|
136
|
-
storageRoot: path.join(home, '.
|
|
136
|
+
storageRoot: path.join(home, '.fixture-state'),
|
|
137
137
|
argv: ['memory', 'inspect', 'view-preference', 'last-panel'],
|
|
138
138
|
format: 'json',
|
|
139
139
|
});
|
|
@@ -147,14 +147,14 @@ describe('user-local command', () => {
|
|
|
147
147
|
|
|
148
148
|
const disableResult = await executeUserLocalDirectCommand({
|
|
149
149
|
cwd: repo,
|
|
150
|
-
storageRoot: path.join(home, '.
|
|
150
|
+
storageRoot: path.join(home, '.fixture-state'),
|
|
151
151
|
argv: ['memory', 'disable', 'view-preference', 'last-panel'],
|
|
152
152
|
});
|
|
153
153
|
expect(disableResult.success).toBe(true);
|
|
154
154
|
|
|
155
155
|
const disabledInspectResult = await executeUserLocalDirectCommand({
|
|
156
156
|
cwd: repo,
|
|
157
|
-
storageRoot: path.join(home, '.
|
|
157
|
+
storageRoot: path.join(home, '.fixture-state'),
|
|
158
158
|
argv: ['memory', 'inspect', 'view-preference', 'last-panel'],
|
|
159
159
|
format: 'json',
|
|
160
160
|
});
|
|
@@ -163,14 +163,14 @@ describe('user-local command', () => {
|
|
|
163
163
|
|
|
164
164
|
const deleteResult = await executeUserLocalDirectCommand({
|
|
165
165
|
cwd: repo,
|
|
166
|
-
storageRoot: path.join(home, '.
|
|
166
|
+
storageRoot: path.join(home, '.fixture-state'),
|
|
167
167
|
argv: ['memory', 'delete', 'view-preference', 'last-panel'],
|
|
168
168
|
});
|
|
169
169
|
expect(deleteResult.success).toBe(true);
|
|
170
170
|
|
|
171
171
|
const missingResult = await executeUserLocalDirectCommand({
|
|
172
172
|
cwd: repo,
|
|
173
|
-
storageRoot: path.join(home, '.
|
|
173
|
+
storageRoot: path.join(home, '.fixture-state'),
|
|
174
174
|
argv: ['memory', 'inspect', 'view-preference', 'last-panel'],
|
|
175
175
|
format: 'json',
|
|
176
176
|
});
|