Skip to content

Commit 99828a1

Browse files
committed
fix(cli): make uninstall target resolution context-aware
1 parent 9121d78 commit 99828a1

2 files changed

Lines changed: 29 additions & 2 deletions

File tree

src/cli/commands/__tests__/init.test.ts

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -243,6 +243,26 @@ describe('init command', () => {
243243

244244
stdoutSpy.mockRestore();
245245
});
246+
247+
it('gracefully reports no installed skills when auto-detect finds no clients', async () => {
248+
const emptyHome = join(tempDir, 'empty-home-uninstall');
249+
mkdirSync(emptyHome, { recursive: true });
250+
mockedHomedir.mockReturnValue(emptyHome);
251+
252+
const yargs = (await import('yargs')).default;
253+
const mod = await loadInitModule();
254+
255+
const app = yargs(['init', '--uninstall']).scriptName('').fail(false);
256+
mod.registerInitCommand(app);
257+
258+
const stdoutSpy = vi.spyOn(process.stdout, 'write').mockImplementation(() => true);
259+
await app.parseAsync();
260+
261+
const output = stdoutSpy.mock.calls.map((c) => String(c[0])).join('');
262+
expect(output).toContain('No installed skill directories found');
263+
264+
stdoutSpy.mockRestore();
265+
});
246266
});
247267

248268
describe('--print', () => {
@@ -311,7 +331,7 @@ describe('init command', () => {
311331
mod.registerInitCommand(app);
312332

313333
await expect(app.parseAsync()).rejects.toThrow(
314-
'Refusing to install skills into filesystem root',
334+
'Refusing to use filesystem root as skills destination',
315335
);
316336
});
317337

src/cli/commands/init.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -162,12 +162,13 @@ function uninstallSkill(
162162
function resolveTargets(
163163
clientFlag: string | undefined,
164164
destFlag: string | undefined,
165+
operation: 'install' | 'uninstall',
165166
): ClientInfo[] {
166167
if (destFlag) {
167168
const resolvedDest = path.resolve(destFlag);
168169
if (resolvedDest === path.parse(resolvedDest).root) {
169170
throw new Error(
170-
'Refusing to install skills into filesystem root. Use a dedicated directory.',
171+
'Refusing to use filesystem root as skills destination. Use a dedicated directory.',
171172
);
172173
}
173174
return [{ name: 'Custom', id: 'custom', skillsDir: resolvedDest }];
@@ -184,6 +185,10 @@ function resolveTargets(
184185

185186
const detected = detectClients();
186187
if (detected.length === 0) {
188+
if (operation === 'uninstall') {
189+
return [];
190+
}
191+
187192
throw new Error(
188193
'No supported AI clients detected.\n' +
189194
'Use --client to specify a client, --dest to specify a custom path, or --print to output the skill content.',
@@ -248,6 +253,7 @@ export function registerInitCommand(app: Argv): void {
248253
const targets = resolveTargets(
249254
argv.client as string | undefined,
250255
argv.dest as string | undefined,
256+
'uninstall',
251257
);
252258
let anyRemoved = false;
253259

@@ -274,6 +280,7 @@ export function registerInitCommand(app: Argv): void {
274280
const targets = resolveTargets(
275281
argv.client as string | undefined,
276282
argv.dest as string | undefined,
283+
'install',
277284
);
278285

279286
const results: InstallResult[] = [];

0 commit comments

Comments
 (0)