Initial commit
This commit is contained in:
14
.claude-plugin/plugin.json
Normal file
14
.claude-plugin/plugin.json
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
{
|
||||||
|
"name": "skill-activation",
|
||||||
|
"description": "Centralized skill activation system that automatically suggests relevant skills from installed plugins based on user prompts and file context",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"author": {
|
||||||
|
"name": "boneskull"
|
||||||
|
},
|
||||||
|
"skills": [
|
||||||
|
"./skills"
|
||||||
|
],
|
||||||
|
"hooks": [
|
||||||
|
"./hooks"
|
||||||
|
]
|
||||||
|
}
|
||||||
3
README.md
Normal file
3
README.md
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
# skill-activation
|
||||||
|
|
||||||
|
Centralized skill activation system that automatically suggests relevant skills from installed plugins based on user prompts and file context
|
||||||
14
hooks/hooks.json
Normal file
14
hooks/hooks.json
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
{
|
||||||
|
"hooks": {
|
||||||
|
"UserPromptSubmit": [
|
||||||
|
{
|
||||||
|
"hooks": [
|
||||||
|
{
|
||||||
|
"type": "command",
|
||||||
|
"command": "npx tsx ${CLAUDE_PLUGIN_ROOT}/hooks/scripts/skill-activation-prompt.ts"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
17
hooks/package.json
Normal file
17
hooks/package.json
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
{
|
||||||
|
"name": "@skill-activation/hooks",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"type": "module",
|
||||||
|
"description": "Hook scripts for skill-activation plugin",
|
||||||
|
"private": true,
|
||||||
|
"scripts": {
|
||||||
|
"test": "echo '{ \"prompt\": \"help me write a test\", \"cwd\": \".\", \"session_id\": \"test\", \"transcript_path\": \"/tmp/test\", \"permission_mode\": \"auto\" }' | npx tsx scripts/skill-activation-prompt.ts"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"tsx": "^4.19.2"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/node": "^24.0.0",
|
||||||
|
"typescript": "^5.7.2"
|
||||||
|
}
|
||||||
|
}
|
||||||
387
hooks/scripts/skill-activation-prompt.ts
Normal file
387
hooks/scripts/skill-activation-prompt.ts
Normal file
@@ -0,0 +1,387 @@
|
|||||||
|
#!/usr/bin/env node
|
||||||
|
|
||||||
|
import { accessSync, readFileSync } from 'fs';
|
||||||
|
import { homedir } from 'os';
|
||||||
|
import { join } from 'path';
|
||||||
|
|
||||||
|
interface HookInput {
|
||||||
|
session_id: string;
|
||||||
|
transcript_path: string;
|
||||||
|
cwd: string;
|
||||||
|
permission_mode: string;
|
||||||
|
prompt: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface PromptTriggers {
|
||||||
|
keywords?: string[];
|
||||||
|
intentPatterns?: string[];
|
||||||
|
}
|
||||||
|
|
||||||
|
interface FileTriggers {
|
||||||
|
pathPatterns?: string[];
|
||||||
|
pathExclusions?: string[];
|
||||||
|
contentPatterns?: string[];
|
||||||
|
}
|
||||||
|
|
||||||
|
interface SkipConditions {
|
||||||
|
sessionSkillUsed?: boolean;
|
||||||
|
fileMarkers?: string[];
|
||||||
|
envOverride?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface SkillRule {
|
||||||
|
type: 'guardrail' | 'domain';
|
||||||
|
enforcement: 'block' | 'suggest' | 'warn';
|
||||||
|
priority: 'critical' | 'high' | 'medium' | 'low';
|
||||||
|
description?: string;
|
||||||
|
promptTriggers?: PromptTriggers;
|
||||||
|
fileTriggers?: FileTriggers;
|
||||||
|
blockMessage?: string;
|
||||||
|
skipConditions?: SkipConditions;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface SkillRules {
|
||||||
|
version: string;
|
||||||
|
description?: string;
|
||||||
|
skills: Record<string, SkillRule>;
|
||||||
|
notes?: Record<string, any>;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface InstalledPlugin {
|
||||||
|
version: string;
|
||||||
|
installedAt: string;
|
||||||
|
lastUpdated: string;
|
||||||
|
installPath: string;
|
||||||
|
gitCommitSha?: string;
|
||||||
|
isLocal?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface InstalledPlugins {
|
||||||
|
version: number;
|
||||||
|
plugins: Record<string, InstalledPlugin>;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface MatchedSkill {
|
||||||
|
name: string;
|
||||||
|
displayName: string;
|
||||||
|
matchType: 'keyword' | 'intent';
|
||||||
|
config: SkillRule;
|
||||||
|
skillPath: string | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse a skill reference in format "plugin@marketplace:skill-name" Returns
|
||||||
|
* null if the reference is in legacy format (no plugin qualifier)
|
||||||
|
*/
|
||||||
|
function parseSkillRef(
|
||||||
|
skillRef: string,
|
||||||
|
): { pluginId: string; skillName: string } | null {
|
||||||
|
const match = skillRef.match(/^(.+?)@(.+?):(.+)$/);
|
||||||
|
if (!match) {
|
||||||
|
return null; // Legacy format (local project skill)
|
||||||
|
}
|
||||||
|
const [, pluginName, marketplace, skillName] = match;
|
||||||
|
return {
|
||||||
|
pluginId: `${pluginName}@${marketplace}`,
|
||||||
|
skillName: skillName!,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resolve a skill reference to an absolute file path Returns null if:
|
||||||
|
*
|
||||||
|
* - Plugin is not installed
|
||||||
|
* - Skill file doesn't exist in the plugin
|
||||||
|
*
|
||||||
|
* For legacy format (no plugin qualifier), looks in project .claude/skills/
|
||||||
|
*/
|
||||||
|
function resolveSkillPath(
|
||||||
|
skillRef: string,
|
||||||
|
installedPlugins: InstalledPlugins,
|
||||||
|
): string | null {
|
||||||
|
const parsed = parseSkillRef(skillRef);
|
||||||
|
|
||||||
|
if (!parsed) {
|
||||||
|
// Legacy format: local project skill
|
||||||
|
const projectDir = process.env.CLAUDE_PROJECT_DIR || process.cwd();
|
||||||
|
const skillPath = join(
|
||||||
|
projectDir,
|
||||||
|
'.claude',
|
||||||
|
'skills',
|
||||||
|
skillRef,
|
||||||
|
'SKILL.md',
|
||||||
|
);
|
||||||
|
|
||||||
|
try {
|
||||||
|
accessSync(skillPath);
|
||||||
|
return skillPath;
|
||||||
|
} catch {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const { pluginId, skillName } = parsed;
|
||||||
|
|
||||||
|
// Check if plugin is installed
|
||||||
|
const plugin = installedPlugins.plugins[pluginId];
|
||||||
|
if (!plugin) {
|
||||||
|
return null; // Plugin not installed - gracefully skip
|
||||||
|
}
|
||||||
|
|
||||||
|
// Construct skill path
|
||||||
|
const skillPath = join(plugin.installPath, 'skills', skillName, 'SKILL.md');
|
||||||
|
|
||||||
|
// Verify skill exists
|
||||||
|
try {
|
||||||
|
accessSync(skillPath);
|
||||||
|
return skillPath;
|
||||||
|
} catch {
|
||||||
|
return null; // Skill doesn't exist - gracefully skip
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Load installed plugins metadata from Claude's global config
|
||||||
|
*/
|
||||||
|
function loadInstalledPlugins(): InstalledPlugins {
|
||||||
|
const pluginsPath = join(
|
||||||
|
homedir(),
|
||||||
|
'.claude',
|
||||||
|
'plugins',
|
||||||
|
'installed_plugins.json',
|
||||||
|
);
|
||||||
|
|
||||||
|
try {
|
||||||
|
const content = readFileSync(pluginsPath, 'utf-8');
|
||||||
|
return JSON.parse(content);
|
||||||
|
} catch (err) {
|
||||||
|
// If we can't read installed plugins, return empty structure
|
||||||
|
console.error('Warning: Could not load installed plugins:', err);
|
||||||
|
return { version: 1, plugins: {} };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a display name for the skill For plugin skills:
|
||||||
|
* "plugin-name:skill-name" For local skills: just "skill-name"
|
||||||
|
*/
|
||||||
|
const getDisplayName = (skillRef: string): string => {
|
||||||
|
const parsed = parseSkillRef(skillRef);
|
||||||
|
if (!parsed) {
|
||||||
|
return skillRef; // Legacy format
|
||||||
|
}
|
||||||
|
|
||||||
|
const { pluginId, skillName } = parsed;
|
||||||
|
// Extract just the plugin name (before @)
|
||||||
|
const pluginName = pluginId.split('@')[0];
|
||||||
|
return `${pluginName}:${skillName}`;
|
||||||
|
};
|
||||||
|
|
||||||
|
const loadSkillRules = (rulesPath: string): SkillRules | undefined => {
|
||||||
|
try {
|
||||||
|
accessSync(rulesPath);
|
||||||
|
const pluginRules: SkillRules = JSON.parse(
|
||||||
|
readFileSync(rulesPath, 'utf-8'),
|
||||||
|
);
|
||||||
|
|
||||||
|
return pluginRules;
|
||||||
|
} catch {}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Load and merge skill rules from all sources in priority order:
|
||||||
|
*
|
||||||
|
* 1. Plugin-defined rules (lowest priority - defaults)
|
||||||
|
* 2. Global user rules ~/.claude/skill-rules.json (middle priority)
|
||||||
|
* 3. Project rules .claude/skill-rules.json (highest priority)
|
||||||
|
*
|
||||||
|
* Higher priority rules override lower priority rules
|
||||||
|
*/
|
||||||
|
const loadAllSkillRules = (
|
||||||
|
installedPlugins: InstalledPlugins,
|
||||||
|
projectDir: string,
|
||||||
|
): SkillRules => {
|
||||||
|
const mergedRules: SkillRules = {
|
||||||
|
version: '1.0',
|
||||||
|
skills: {},
|
||||||
|
};
|
||||||
|
|
||||||
|
// 1. Load plugin-defined rules (lowest priority - defaults)
|
||||||
|
let pluginRulesLoaded = 0;
|
||||||
|
for (const plugin of Object.values(installedPlugins.plugins)) {
|
||||||
|
const rulesPath = join(plugin.installPath, 'skills', 'skill-rules.json');
|
||||||
|
|
||||||
|
const pluginRules = loadSkillRules(rulesPath);
|
||||||
|
if (pluginRules) {
|
||||||
|
Object.assign(mergedRules.skills, pluginRules.skills);
|
||||||
|
pluginRulesLoaded++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. Load global user rules (middle priority - overrides plugin defaults)
|
||||||
|
let globalRulesLoaded = false;
|
||||||
|
const globalRulesPath = join(homedir(), '.claude', 'skill-rules.json');
|
||||||
|
const globalRules = loadSkillRules(globalRulesPath);
|
||||||
|
if (globalRules) {
|
||||||
|
Object.assign(mergedRules.skills, globalRules.skills);
|
||||||
|
globalRulesLoaded = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. Load project rules (highest priority - overrides everything)
|
||||||
|
const projectRulesPath = join(projectDir, '.claude', 'skill-rules.json');
|
||||||
|
let projectRulesLoaded = false;
|
||||||
|
const projectRules = loadSkillRules(projectRulesPath);
|
||||||
|
if (projectRules) {
|
||||||
|
Object.assign(mergedRules.skills, projectRules.skills);
|
||||||
|
projectRulesLoaded = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Debug info (only if no rules found at all)
|
||||||
|
if (pluginRulesLoaded === 0 && !globalRulesLoaded && !projectRulesLoaded) {
|
||||||
|
console.error(
|
||||||
|
'Warning: No skill rules found in plugins, global config, or project config',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return mergedRules;
|
||||||
|
};
|
||||||
|
|
||||||
|
const main = async () => {
|
||||||
|
// Read input from stdin
|
||||||
|
const input = readFileSync(0, 'utf-8');
|
||||||
|
const data: HookInput = JSON.parse(input);
|
||||||
|
const prompt = data.prompt.toLowerCase();
|
||||||
|
|
||||||
|
// Load installed plugins
|
||||||
|
const installedPlugins = loadInstalledPlugins();
|
||||||
|
|
||||||
|
// Get project directory
|
||||||
|
const projectDir = process.env.CLAUDE_PROJECT_DIR || data.cwd;
|
||||||
|
|
||||||
|
// Load and merge all skill rules
|
||||||
|
const rules = loadAllSkillRules(installedPlugins, projectDir);
|
||||||
|
|
||||||
|
const matchedSkills: MatchedSkill[] = [];
|
||||||
|
|
||||||
|
// Check each skill for matches
|
||||||
|
for (const [skillRef, config] of Object.entries(rules.skills)) {
|
||||||
|
const triggers = config.promptTriggers;
|
||||||
|
if (!triggers) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let matched = false;
|
||||||
|
let matchType: 'keyword' | 'intent' = 'keyword';
|
||||||
|
|
||||||
|
// Keyword matching
|
||||||
|
if (triggers.keywords) {
|
||||||
|
const keywordMatch = triggers.keywords.some((kw) =>
|
||||||
|
prompt.includes(kw.toLowerCase()),
|
||||||
|
);
|
||||||
|
if (keywordMatch) {
|
||||||
|
matched = true;
|
||||||
|
matchType = 'keyword';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Intent pattern matching (only if not already matched by keyword)
|
||||||
|
if (!matched && triggers.intentPatterns) {
|
||||||
|
const intentMatch = triggers.intentPatterns.some((pattern) => {
|
||||||
|
const regex = new RegExp(pattern, 'i');
|
||||||
|
return regex.test(prompt);
|
||||||
|
});
|
||||||
|
if (intentMatch) {
|
||||||
|
matched = true;
|
||||||
|
matchType = 'intent';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (matched) {
|
||||||
|
// Resolve skill path
|
||||||
|
const skillPath = resolveSkillPath(skillRef, installedPlugins);
|
||||||
|
|
||||||
|
// Only skip if skill doesn't exist
|
||||||
|
// Still show in output but mark as unavailable
|
||||||
|
matchedSkills.push({
|
||||||
|
name: skillRef,
|
||||||
|
displayName: getDisplayName(skillRef),
|
||||||
|
matchType,
|
||||||
|
config,
|
||||||
|
skillPath,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate output if matches found
|
||||||
|
if (matchedSkills.length > 0) {
|
||||||
|
let output = '━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n';
|
||||||
|
output += '🎯 SKILL ACTIVATION CHECK\n';
|
||||||
|
output += '━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n\n';
|
||||||
|
|
||||||
|
// Group by priority
|
||||||
|
const critical = matchedSkills.filter(
|
||||||
|
(s) => s.config.priority === 'critical',
|
||||||
|
);
|
||||||
|
const high = matchedSkills.filter((s) => s.config.priority === 'high');
|
||||||
|
const medium = matchedSkills.filter((s) => s.config.priority === 'medium');
|
||||||
|
const low = matchedSkills.filter((s) => s.config.priority === 'low');
|
||||||
|
|
||||||
|
// Helper to format skill with availability status
|
||||||
|
const formatSkill = (s: MatchedSkill): string => {
|
||||||
|
if (s.skillPath === null) {
|
||||||
|
return ` → ${s.displayName} ⚠️ (plugin not installed)`;
|
||||||
|
}
|
||||||
|
return ` → ${s.displayName}`;
|
||||||
|
};
|
||||||
|
|
||||||
|
if (critical.length > 0) {
|
||||||
|
output += '⚠️ CRITICAL SKILLS (REQUIRED):\n';
|
||||||
|
critical.forEach((s) => (output += formatSkill(s) + '\n'));
|
||||||
|
output += '\n';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (high.length > 0) {
|
||||||
|
output += '📚 RECOMMENDED SKILLS:\n';
|
||||||
|
high.forEach((s) => (output += formatSkill(s) + '\n'));
|
||||||
|
output += '\n';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (medium.length > 0) {
|
||||||
|
output += '💡 SUGGESTED SKILLS:\n';
|
||||||
|
medium.forEach((s) => (output += formatSkill(s) + '\n'));
|
||||||
|
output += '\n';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (low.length > 0) {
|
||||||
|
output += '📌 OPTIONAL SKILLS:\n';
|
||||||
|
low.forEach((s) => (output += formatSkill(s) + '\n'));
|
||||||
|
output += '\n';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if any skills are unavailable
|
||||||
|
const unavailable = matchedSkills.filter((s) => s.skillPath === null);
|
||||||
|
if (unavailable.length > 0) {
|
||||||
|
output += '💡 TIP: Some skills require installing additional plugins\n';
|
||||||
|
}
|
||||||
|
|
||||||
|
output += 'ACTION: Use Skill tool BEFORE responding\n';
|
||||||
|
output += '━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n';
|
||||||
|
|
||||||
|
// Output JSON for UserPromptSubmit hook
|
||||||
|
const hookOutput = {
|
||||||
|
systemMessage: output, // Displayed directly to the user
|
||||||
|
hookSpecificOutput: {
|
||||||
|
hookEventName: 'UserPromptSubmit',
|
||||||
|
additionalContext: output, // Added to Claude's context
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
console.log(JSON.stringify(hookOutput, null, 2));
|
||||||
|
}
|
||||||
|
// No output when no skills match (allow prompt to proceed normally)
|
||||||
|
};
|
||||||
|
|
||||||
|
main().catch((err) => {
|
||||||
|
console.error('Uncaught error:', err);
|
||||||
|
process.exitCode = 1;
|
||||||
|
});
|
||||||
57
plugin.lock.json
Normal file
57
plugin.lock.json
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
{
|
||||||
|
"$schema": "internal://schemas/plugin.lock.v1.json",
|
||||||
|
"pluginId": "gh:boneskull/claude-plugins:plugins/skill-activation",
|
||||||
|
"normalized": {
|
||||||
|
"repo": null,
|
||||||
|
"ref": "refs/tags/v20251128.0",
|
||||||
|
"commit": "63c51e5b56db9c94f5323b0793bbee48e5489d0e",
|
||||||
|
"treeHash": "2fb12c47453ca0cb09c8c8353e09abc519ccba6a840c6d1222241a88a0edc076",
|
||||||
|
"generatedAt": "2025-11-28T10:14:20.025401Z",
|
||||||
|
"toolVersion": "publish_plugins.py@0.2.0"
|
||||||
|
},
|
||||||
|
"origin": {
|
||||||
|
"remote": "git@github.com:zhongweili/42plugin-data.git",
|
||||||
|
"branch": "master",
|
||||||
|
"commit": "aa1497ed0949fd50e99e70d6324a29c5b34f9390",
|
||||||
|
"repoRoot": "/Users/zhongweili/projects/openmind/42plugin-data"
|
||||||
|
},
|
||||||
|
"manifest": {
|
||||||
|
"name": "skill-activation",
|
||||||
|
"description": "Centralized skill activation system that automatically suggests relevant skills from installed plugins based on user prompts and file context",
|
||||||
|
"version": "1.0.0"
|
||||||
|
},
|
||||||
|
"content": {
|
||||||
|
"files": [
|
||||||
|
{
|
||||||
|
"path": "README.md",
|
||||||
|
"sha256": "f84c2e959e9a1b21f1c111d73863dfe9345c698612dccbece94732f92e07f4bf"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "hooks/package.json",
|
||||||
|
"sha256": "944848092fda4a8007aaf0a83e240a8c523679a449ce6583f5b5d88821b9ee34"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "hooks/hooks.json",
|
||||||
|
"sha256": "d95d8e79fc061208085fb026fa208dc5643d516969a327ede7bb21164f7bda71"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "hooks/scripts/skill-activation-prompt.ts",
|
||||||
|
"sha256": "d66d6d9d14bb2575c7db1769ba4b391a42f024732e5b26c8a76814b5a5e9049a"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": ".claude-plugin/plugin.json",
|
||||||
|
"sha256": "b9701a162fc06b81d78f57c7323a3cdf170263603f5555dea982f92097679da0"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "skills/skill-rules.example.json",
|
||||||
|
"sha256": "337d606a42757f48a40c84b3167f32ffc9f7f3378bd4d74be713e6ae50d27c87"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"dirSha256": "2fb12c47453ca0cb09c8c8353e09abc519ccba6a840c6d1222241a88a0edc076"
|
||||||
|
},
|
||||||
|
"security": {
|
||||||
|
"scannedAt": null,
|
||||||
|
"scannerVersion": null,
|
||||||
|
"flags": []
|
||||||
|
}
|
||||||
|
}
|
||||||
120
skills/skill-rules.example.json
Normal file
120
skills/skill-rules.example.json
Normal file
@@ -0,0 +1,120 @@
|
|||||||
|
{
|
||||||
|
"description": "Example skill rules configuration for global (~/.claude/skill-rules.json) or project (.claude/skill-rules.json) use",
|
||||||
|
"notes": {
|
||||||
|
"usage": "Copy this to ~/.claude/skill-rules.json for global rules or .claude/skill-rules.json for project-specific rules",
|
||||||
|
"precedence": "PROJECT > GLOBAL > PLUGIN (project rules override global rules, which override plugin defaults)",
|
||||||
|
"partial_overrides": "You only need to specify properties you want to change - they merge with plugin defaults",
|
||||||
|
"third_party": "Use this to configure skills from marketplace plugins you don't control",
|
||||||
|
"extensibility": "Add any installed plugin's skills using the format: 'plugin@marketplace:skill-name'"
|
||||||
|
},
|
||||||
|
"skills": {
|
||||||
|
"_comment_third_party_plugins": "=== Rules for Third-Party Marketplace Plugins ===",
|
||||||
|
|
||||||
|
"superpowers@superpowers-marketplace:test-driven-development": {
|
||||||
|
"type": "guardrail",
|
||||||
|
"enforcement": "suggest",
|
||||||
|
"priority": "high",
|
||||||
|
"description": "TDD workflow: write test first, watch it fail, then implement",
|
||||||
|
"promptTriggers": {
|
||||||
|
"keywords": [
|
||||||
|
"write test",
|
||||||
|
"add test",
|
||||||
|
"test for",
|
||||||
|
"create test",
|
||||||
|
"test coverage",
|
||||||
|
"unit test",
|
||||||
|
"integration test",
|
||||||
|
"TDD"
|
||||||
|
],
|
||||||
|
"intentPatterns": [
|
||||||
|
"(create|write|add|implement).*test",
|
||||||
|
"test.*(feature|function|method|component)",
|
||||||
|
"(how to|how do I).*test"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
"superpowers@superpowers-marketplace:systematic-debugging": {
|
||||||
|
"type": "domain",
|
||||||
|
"enforcement": "suggest",
|
||||||
|
"priority": "high",
|
||||||
|
"description": "Four-phase debugging: root cause investigation, pattern analysis, hypothesis testing, implementation",
|
||||||
|
"promptTriggers": {
|
||||||
|
"keywords": [
|
||||||
|
"bug",
|
||||||
|
"debug",
|
||||||
|
"error",
|
||||||
|
"not working",
|
||||||
|
"broken",
|
||||||
|
"failing",
|
||||||
|
"issue",
|
||||||
|
"problem"
|
||||||
|
],
|
||||||
|
"intentPatterns": [
|
||||||
|
"(fix|debug|solve|investigate).*(bug|error|issue|problem)",
|
||||||
|
"(why|what).*(not working|broken|failing)",
|
||||||
|
"(figure out|find out).*why"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
"superpowers@superpowers-marketplace:brainstorming": {
|
||||||
|
"type": "domain",
|
||||||
|
"enforcement": "suggest",
|
||||||
|
"priority": "medium",
|
||||||
|
"description": "Interactive design refinement using Socratic method before implementation",
|
||||||
|
"promptTriggers": {
|
||||||
|
"keywords": [
|
||||||
|
"design",
|
||||||
|
"architecture",
|
||||||
|
"approach",
|
||||||
|
"plan",
|
||||||
|
"should I",
|
||||||
|
"what's the best way",
|
||||||
|
"how should I"
|
||||||
|
],
|
||||||
|
"intentPatterns": [
|
||||||
|
"(design|architect|plan).*feature",
|
||||||
|
"(what|how).*should.*(design|implement|build)",
|
||||||
|
"(best way|approach).*to"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
"elements-of-style@superpowers-marketplace:writing-clearly-and-concisely": {
|
||||||
|
"type": "domain",
|
||||||
|
"enforcement": "suggest",
|
||||||
|
"priority": "medium",
|
||||||
|
"description": "Apply Elements of Style writing rules to documentation, commit messages, and error messages",
|
||||||
|
"promptTriggers": {
|
||||||
|
"keywords": [
|
||||||
|
"write documentation",
|
||||||
|
"README",
|
||||||
|
"improve writing",
|
||||||
|
"edit documentation",
|
||||||
|
"write guide",
|
||||||
|
"documentation",
|
||||||
|
"docs"
|
||||||
|
],
|
||||||
|
"intentPatterns": [
|
||||||
|
"(write|create|update|improve).*(doc|documentation|readme|guide)",
|
||||||
|
"(improve|edit|refine).*(writing|prose|text)"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
"_comment_override_examples": "=== Examples of Overriding Plugin Defaults ===",
|
||||||
|
|
||||||
|
"tools@boneskull-plugins:git-commit-messages": {
|
||||||
|
"_comment": "Override to make git commit messages blocking instead of suggesting",
|
||||||
|
"priority": "critical",
|
||||||
|
"enforcement": "block"
|
||||||
|
},
|
||||||
|
|
||||||
|
"bupkis@boneskull-plugins:bupkis-assertion-patterns": {
|
||||||
|
"_comment": "Lower priority for projects that don't use bupkis heavily",
|
||||||
|
"priority": "low"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"version": "1.0"
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user