Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 16 additions & 13 deletions src/cli/commands/plugin-skills.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,7 @@ import chalk from 'chalk';
import { command, positional, option, flag, string, optional, restPositionals } from 'cmd-ts';
import { syncWorkspace, syncUserWorkspace } from '../../core/sync.js';
import {
addDisabledSkill,
removeDisabledSkill,
removeEnabledSkill,
addEnabledSkill,
addPlugin,
hasPlugin,
Expand All @@ -16,9 +14,7 @@ import {
upsertGitHubPluginSourceAllowlist,
} from '../../core/workspace-modify.js';
import {
addUserDisabledSkill,
removeUserDisabledSkill,
removeUserEnabledSkill,
addUserEnabledSkill,
isUserConfigPath,
addUserPlugin,
Expand Down Expand Up @@ -55,6 +51,7 @@ import {
} from '../../core/marketplace.js';
import { parseMarketplaceManifest, resolvePluginSourcePath } from '../../utils/marketplace-manifest-parser.js';
import { formatSyncHeader, formatSyncSummary, formatVerboseSyncLines } from '../format-sync.js';
import { removeInstalledSkill } from '../skill-removal.js';
import type { SyncResult } from '../../core/sync.js';

/**
Expand Down Expand Up @@ -336,10 +333,10 @@ const removeCmd = command({
const workspacePath = isUser ? getHomeDir() : process.cwd();

// Find the skill
const matches = await findSkillByName(skill, workspacePath);
const allSkills = await getAllSkillsFromPlugins(workspacePath);
const matches = allSkills.filter((candidate) => candidate.name === skill);

if (matches.length === 0) {
const allSkills = await getAllSkillsFromPlugins(workspacePath);
const skillNames = [...new Set(allSkills.map((s) => s.name))].join(', ');
const error = `Skill '${skill}' not found in any installed plugin.\n\nAvailable skills: ${skillNames || 'none'}`;
if (isJsonMode()) {
Expand Down Expand Up @@ -391,12 +388,12 @@ const removeCmd = command({
return;
}

const skillKey = `${targetSkill.pluginName}:${skill}`;

const result = targetSkill.pluginSkillsMode === 'allowlist'
? isUser ? await removeUserEnabledSkill(skillKey) : await removeEnabledSkill(skillKey, workspacePath)
: isUser ? await addUserDisabledSkill(skillKey) : await addDisabledSkill(skillKey, workspacePath);

const result = await removeInstalledSkill({
targetSkill,
isUser,
workspacePath,
allSkills,
});
if (!result.success) {
if (isJsonMode()) {
jsonOutput({ success: false, command: 'skill remove', error: result.error ?? 'Unknown error' });
Expand All @@ -407,7 +404,13 @@ const removeCmd = command({
}

if (!isJsonMode()) {
console.log(`\u2713 Disabled skill: ${skill} (${targetSkill.pluginName})`);
if (result.action === 'removed-plugin') {
console.log(`\u2713 Removed plugin: ${targetSkill.pluginSource}`);
} else if (result.action === 'removed-skill') {
console.log(`\u2713 Removed skill: ${skill} (${targetSkill.pluginName})`);
} else {
console.log(`\u2713 Disabled skill: ${skill} (${targetSkill.pluginName})`);
}
}

const syncResult = isUser ? await syncUserWorkspace() : await syncWorkspace(workspacePath);
Expand Down
55 changes: 55 additions & 0 deletions src/cli/skill-removal.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import { addDisabledSkill, removeEnabledSkill, removePlugin } from '../core/workspace-modify.js';
import { getAllSkillsFromPlugins, type SkillInfo } from '../core/skills.js';
import {
addUserDisabledSkill,
removeUserEnabledSkill,
removeUserPlugin,
} from '../core/user-workspace.js';

export interface RemoveInstalledSkillOptions {
targetSkill: Pick<SkillInfo, 'name' | 'pluginName' | 'pluginSource' | 'pluginSkillsMode'>;
isUser: boolean;
workspacePath: string;
allSkills?: SkillInfo[];
}

export interface RemoveInstalledSkillResult {
success: boolean;
error?: string;
action?: 'removed-plugin' | 'removed-skill' | 'disabled-skill';
}

export async function removeInstalledSkill(
options: RemoveInstalledSkillOptions,
): Promise<RemoveInstalledSkillResult> {
const { targetSkill, isUser, workspacePath } = options;
const allSkills = options.allSkills ?? await getAllSkillsFromPlugins(workspacePath);
const pluginSkills = allSkills.filter((skill) => skill.pluginSource === targetSkill.pluginSource);
const remainingEnabledSkills = pluginSkills.filter(
(skill) => !skill.disabled && skill.name !== targetSkill.name,
);

if (remainingEnabledSkills.length === 0) {
const result = isUser
? await removeUserPlugin(targetSkill.pluginSource)
: await removePlugin(targetSkill.pluginSource, workspacePath);

return result.success
? { success: true, action: 'removed-plugin' }
: { success: false, error: result.error ?? 'Unknown error' };
}

const skillKey = `${targetSkill.pluginName}:${targetSkill.name}`;
const result = targetSkill.pluginSkillsMode === 'allowlist'
? isUser ? await removeUserEnabledSkill(skillKey) : await removeEnabledSkill(skillKey, workspacePath)
: isUser ? await addUserDisabledSkill(skillKey) : await addDisabledSkill(skillKey, workspacePath);

if (!result.success) {
return { success: false, error: result.error ?? 'Unknown error' };
}

return {
success: true,
action: targetSkill.pluginSkillsMode === 'allowlist' ? 'removed-skill' : 'disabled-skill',
};
}
24 changes: 7 additions & 17 deletions src/cli/tui/actions/plugins.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
import * as p from '@clack/prompts';
import { addPlugin, removePlugin, addDisabledSkill, removeDisabledSkill, addEnabledSkill, removeEnabledSkill, setPluginSkillsMode } from '../../../core/workspace-modify.js';
import { addPlugin, removePlugin, removeDisabledSkill, addEnabledSkill, setPluginSkillsMode } from '../../../core/workspace-modify.js';
import {
addUserPlugin,
removeUserPlugin,
addUserDisabledSkill,
removeUserDisabledSkill,
addUserEnabledSkill,
removeUserEnabledSkill,
setUserPluginSkillsMode,
getInstalledUserPlugins,
getInstalledProjectPlugins,
Expand All @@ -32,6 +30,7 @@ import { getAllSkillsFromPlugins, discoverSkillNames } from '../../../core/skill
import { getHomeDir } from '../../../constants.js';
import type { TuiContext } from '../context.js';
import type { TuiCache } from '../cache.js';
import { removeInstalledSkill } from '../../skill-removal.js';

const { select, text, confirm, multiselect, autocomplete } = p;

Expand Down Expand Up @@ -565,20 +564,11 @@ export async function runBrowsePluginSkills(

// Disable newly unchecked skills
for (const skill of toDisable) {
const skillKey = `${skill.pluginName}:${skill.name}`;
if (skill.pluginSkillsMode === 'allowlist') {
if (scope === 'user') {
await removeUserEnabledSkill(skillKey);
} else if (context.workspacePath) {
await removeEnabledSkill(skillKey, context.workspacePath);
}
} else {
if (scope === 'user') {
await addUserDisabledSkill(skillKey);
} else if (context.workspacePath) {
await addDisabledSkill(skillKey, context.workspacePath);
}
}
await removeInstalledSkill({
targetSkill: skill,
isUser: scope === 'user',
workspacePath,
});
}

// Enable newly checked skills
Expand Down
24 changes: 8 additions & 16 deletions src/cli/tui/actions/skills.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,13 @@
import * as p from '@clack/prompts';
import { getAllSkillsFromPlugins, discoverSkillNames, type SkillInfo } from '../../../core/skills.js';
import {
addDisabledSkill,
removeDisabledSkill,
addEnabledSkill,
removeEnabledSkill,
hasPlugin,
} from '../../../core/workspace-modify.js';
import {
addUserDisabledSkill,
removeUserDisabledSkill,
addUserEnabledSkill,
removeUserEnabledSkill,
isUserConfigPath,
hasUserPlugin,
} from '../../../core/user-workspace.js';
Expand All @@ -25,6 +21,7 @@ import { getHomeDir } from '../../../constants.js';
import type { TuiContext } from '../context.js';
import type { TuiCache } from '../cache.js';
import { installSelectedPlugin, runBrowsePluginSkills } from './plugins.js';
import { removeInstalledSkill } from '../../skill-removal.js';

const { multiselect, select, autocomplete, text } = p;

Expand Down Expand Up @@ -223,18 +220,13 @@ async function runToggleSkills(

// Disable newly unchecked skills
for (const skill of toDisable) {
if (skill.pluginSkillsMode === 'allowlist') {
if (skill.scope === 'user') {
await removeUserEnabledSkill(skill.skillKey);
} else if (context.workspacePath) {
await removeEnabledSkill(skill.skillKey, context.workspacePath);
}
} else {
if (skill.scope === 'user') {
await addUserDisabledSkill(skill.skillKey);
} else if (context.workspacePath) {
await addDisabledSkill(skill.skillKey, context.workspacePath);
}
const effectivePath = skill.scope === 'user' ? getHomeDir() : context.workspacePath;
if (effectivePath) {
await removeInstalledSkill({
targetSkill: skill,
isUser: skill.scope === 'user',
workspacePath: effectivePath,
});
}
if (skill.scope === 'user') changedUser = true;
else changedProject = true;
Expand Down
Loading