Skip to content

Commit 7be5ab8

Browse files
committed
refactor: align skill install flow with command runner
1 parent 42c2f57 commit 7be5ab8

2 files changed

Lines changed: 24 additions & 33 deletions

File tree

src/index.ts

Lines changed: 9 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -419,27 +419,16 @@ async function runSkillCommand(
419419
const installationSpinner = spinner();
420420
installationSpinner.start(`Installing skill ${skill.value}`);
421421

422-
try {
423-
await x('npx', args, {
424-
throwOnError: true,
425-
nodeOptions: {
426-
cwd,
427-
stdio: 'pipe',
428-
},
429-
});
430-
} catch (error) {
422+
const result = await x('npx', args, {
423+
nodeOptions: {
424+
cwd,
425+
stdio: 'pipe',
426+
},
427+
});
428+
429+
if (result.exitCode !== 0) {
431430
installationSpinner.error(`Failed to install skill ${skill.value}`);
432-
const details = [
433-
error && typeof error === 'object' && 'output' in error
434-
? (error as { output?: { stderr?: string; stdout?: string } }).output?.stderr
435-
: undefined,
436-
error && typeof error === 'object' && 'output' in error
437-
? (error as { output?: { stderr?: string; stdout?: string } }).output?.stdout
438-
: undefined,
439-
]
440-
.filter(Boolean)
441-
.join('\n')
442-
.trim() || (error instanceof Error ? error.message : String(error));
431+
const details = [result.stderr, result.stdout].filter(Boolean).join('\n').trim();
443432
throw new Error(
444433
`Failed to install skill "${skill.value}" from "${skill.source}" using command: ${command}${details ? `\n${details}` : ''}`,
445434
);

test/skills.test.ts

Lines changed: 15 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -552,7 +552,11 @@ test('should filter extra skills by template and install using skill override',
552552
test('should throw with skill context when installation fails', async () => {
553553
const projectDir = path.join(testDir, 'skills-install-failure');
554554
createExecCommand(() => {
555-
throw new Error('install failed');
555+
return {
556+
stdout: '',
557+
stderr: 'install failed',
558+
exitCode: 1,
559+
};
556560
});
557561

558562
const error = await getCreateError(
@@ -602,16 +606,11 @@ test('should trim noisy skills cli output in install errors', async () => {
602606
◇ Found 6 skills
603607
604608
■ No matching skills found for: non-existent-skill`;
605-
createExecCommand(() => {
606-
const error = new Error('Process exited with non-zero status (1)') as Error & {
607-
output?: { stderr: string; stdout: string };
608-
};
609-
error.output = {
610-
stderr: '',
611-
stdout: rawStdout,
612-
};
613-
throw error;
614-
});
609+
createExecCommand(() => ({
610+
stdout: rawStdout,
611+
stderr: '',
612+
exitCode: 1,
613+
}));
615614

616615
const error = await getCreateError(
617616
create({
@@ -678,7 +677,7 @@ test('should include spawn errors when skill installation cannot start', async (
678677
throw new Error('spawn npx ENOENT');
679678
});
680679

681-
await expect(
680+
const error = await getCreateError(
682681
create({
683682
name: 'test',
684683
root: fixturesDir,
@@ -703,7 +702,10 @@ test('should include spawn errors when skill installation cannot start', async (
703702
'shared-docs',
704703
],
705704
}),
706-
).rejects.toThrow('spawn npx ENOENT');
705+
);
706+
707+
expect(error).toBeInstanceOf(Error);
708+
expect((error as Error).message).toBe('spawn npx ENOENT');
707709
});
708710

709711
test('should install skills with async spawn so spinner can render during installation', async () => {

0 commit comments

Comments
 (0)