feat(skills): add execute command opt-in runner flow
This commit is contained in:
@@ -25,6 +25,7 @@ import {
|
||||
noOpSkillInstallerCommandRunner,
|
||||
createShellSkillInstallerCommandRunner,
|
||||
resolveSkillInstallerCommandRunner,
|
||||
runSkillExecuteAction,
|
||||
runSkillInstallAction,
|
||||
} from './skills.js';
|
||||
import type { Skill } from '../skills/index.js';
|
||||
@@ -277,6 +278,26 @@ describe('skills CLI helpers', () => {
|
||||
expect(output).toContain('Would run:');
|
||||
expect(output).toContain('- brew install jq');
|
||||
expect(output).toContain('Skipped:');
|
||||
expect(output).toContain('Results:');
|
||||
});
|
||||
|
||||
it('renders execution report text when commands are executed', () => {
|
||||
const output = renderSkillInstallerExecutionStub({
|
||||
skill: { name: 'exec-stub', tier: 'bundled', version: '1.0.0' },
|
||||
execution: 'stub',
|
||||
mode: 'stub',
|
||||
confirmed: true,
|
||||
execution_enabled: true,
|
||||
executed: ['brew install jq'],
|
||||
reason: 'execution_enabled',
|
||||
attempted: [{ installer_type: 'brew', command: 'brew install jq' }],
|
||||
results: [{ installer_type: 'brew', command: 'brew install jq', status: 'succeeded', reason: 'runner_reported_success' }],
|
||||
wouldRun: ['brew install jq'],
|
||||
skipped: [],
|
||||
});
|
||||
|
||||
expect(output).toContain('Installer commands were executed.');
|
||||
expect(output).toContain('[brew] succeeded brew install jq (runner_reported_success)');
|
||||
});
|
||||
|
||||
it('derives execution stub view from preflight data', () => {
|
||||
@@ -756,6 +777,42 @@ describe('skills CLI helpers', () => {
|
||||
rmSync(root, { recursive: true, force: true });
|
||||
});
|
||||
|
||||
it('execute action honors opt-in execution and runner selection', () => {
|
||||
const skill = buildSkill({
|
||||
manifest: {
|
||||
name: 'execute-skill',
|
||||
description: 'Execute me',
|
||||
version: '1.0.0',
|
||||
tier: 'managed',
|
||||
installers: [{ type: 'download', url: 'https://example.com/execute.tgz' }],
|
||||
},
|
||||
});
|
||||
|
||||
const runner = {
|
||||
run: vi.fn((commands: string[]) =>
|
||||
commands.map((command) => ({ command, status: 'succeeded' as const, reason: 'runner_reported_success' })),
|
||||
),
|
||||
};
|
||||
const logSpy = vi.spyOn(console, 'log').mockImplementation(() => undefined);
|
||||
|
||||
const result = runSkillExecuteAction(skill, {
|
||||
asJson: true,
|
||||
confirmed: true,
|
||||
executionRequested: true,
|
||||
commandRunner: runner,
|
||||
});
|
||||
|
||||
expect(result.ok).toBe(true);
|
||||
expect(result.execution.execution_enabled).toBe(true);
|
||||
expect(result.execution.reason).toBe('execution_enabled');
|
||||
expect(runner.run).toHaveBeenCalledTimes(1);
|
||||
const payload = JSON.parse(String(logSpy.mock.calls[0]?.[0]));
|
||||
expect(payload.execution_enabled).toBe(true);
|
||||
expect(payload.executed).toEqual(['download https://example.com/execute.tgz -> <default destination>']);
|
||||
|
||||
logSpy.mockRestore();
|
||||
});
|
||||
|
||||
it('requires --yes confirmation for uninstall helper', () => {
|
||||
const root = mkdtempSync(join(tmpdir(), 'flynn-skills-cli-'));
|
||||
const installer = new SkillInstaller(join(root, 'managed'));
|
||||
|
||||
Reference in New Issue
Block a user