feat(coding-agent): add --skills CLI flag for filtering skills

Adds glob pattern support for skill filtering:
- --skills <patterns> CLI flag (comma-separated glob patterns)
- includeSkills setting in settings.json
- ignoredSkills now supports glob patterns
- ignoredSkills takes precedence over includeSkills and --skills

Closes #268
This commit is contained in:
Mario Zechner 2025-12-21 20:58:15 +01:00
parent 70440f7591
commit d95a5c4186
8 changed files with 148 additions and 4 deletions

View file

@ -22,7 +22,8 @@ export interface SkillsSettings {
enablePiUser?: boolean; // default: true
enablePiProject?: boolean; // default: true
customDirectories?: string[]; // default: []
ignoredSkills?: string[]; // default: []
ignoredSkills?: string[]; // default: [] (glob patterns to exclude; takes precedence over includeSkills)
includeSkills?: string[]; // default: [] (empty = include all; glob patterns to filter)
}
export interface TerminalSettings {
@ -270,6 +271,7 @@ export class SettingsManager {
enablePiProject: this.settings.skills?.enablePiProject ?? true,
customDirectories: this.settings.skills?.customDirectories ?? [],
ignoredSkills: this.settings.skills?.ignoredSkills ?? [],
includeSkills: this.settings.skills?.includeSkills ?? [],
};
}

View file

@ -1,4 +1,5 @@
import { existsSync, readdirSync, readFileSync } from "fs";
import { minimatch } from "minimatch";
import { homedir } from "os";
import { basename, dirname, join, resolve } from "path";
import { CONFIG_DIR_NAME } from "../config.js";
@ -325,16 +326,34 @@ export function loadSkills(options: SkillsSettings = {}): LoadSkillsResult {
enablePiProject = true,
customDirectories = [],
ignoredSkills = [],
includeSkills = [],
} = options;
const skillMap = new Map<string, Skill>();
const allWarnings: SkillWarning[] = [];
const collisionWarnings: SkillWarning[] = [];
// Check if skill name matches any of the include patterns
function matchesIncludePatterns(name: string): boolean {
if (includeSkills.length === 0) return true; // No filter = include all
return includeSkills.some((pattern) => minimatch(name, pattern));
}
// Check if skill name matches any of the ignore patterns
function matchesIgnorePatterns(name: string): boolean {
if (ignoredSkills.length === 0) return false;
return ignoredSkills.some((pattern) => minimatch(name, pattern));
}
function addSkills(result: LoadSkillsResult) {
allWarnings.push(...result.warnings);
for (const skill of result.skills) {
if (ignoredSkills.includes(skill.name)) {
// Apply ignore filter (glob patterns) - takes precedence over include
if (matchesIgnorePatterns(skill.name)) {
continue;
}
// Apply include filter (glob patterns)
if (!matchesIncludePatterns(skill.name)) {
continue;
}
const existing = skillMap.get(skill.name);