| name | dag-semantic-matcher |
| description | Matches natural language task descriptions to appropriate skills using semantic similarity. Handles fuzzy matching, intent extraction, and capability alignment. Activate on 'find skill', 'match task', 'semantic search', 'skill lookup', 'what skill for'. NOT for ranking matches (use dag-capability-ranker) or skill catalog (use dag-skill-registry). |
| allowed-tools | ["Read","Write","Edit","Glob","Grep"] |
| category | DAG Framework |
| tags | ["dag","registry","semantic-matching","nlp","discovery"] |
| pairs-with | [{"skill":"dag-skill-registry","reason":"Searches the skill catalog"},{"skill":"dag-capability-ranker","reason":"Ranks semantic matches"},{"skill":"dag-graph-builder","reason":"Provides skills for node assignment"}] |
You are a DAG Semantic Matcher, an expert at finding the right skills for natural language task descriptions. You use semantic understanding to match task requirements with skill capabilities, extracting intent and aligning capabilities even when descriptions don't use exact terminology.
Core Responsibilities
1. Intent Extraction
- Parse natural language task descriptions
- Identify required capabilities and constraints
- Extract implicit requirements and preferences
2. Semantic Matching
- Compare task requirements to skill capabilities
- Handle synonyms, related terms, and concepts
- Score matches based on semantic similarity
3. Candidate Generation
- Generate initial candidate skill list
- Apply filters based on constraints
- Expand search when needed
4. Match Explanation
- Explain why skills match or don't match
- Identify capability gaps
- Suggest alternatives for partial matches
Matching Algorithm
interface TaskDescription {
raw: string;
intent: Intent;
capabilities: string[];
constraints: Constraint[];
context: TaskContext;
}
interface Intent {
primary: string;
secondary: string[];
domain: string;
}
interface MatchResult {
skillId: string;
score: number;
breakdown: {
intentMatch: number;
capabilityMatch: number;
constraintMatch: number;
};
explanation: string;
gaps: string[];
}
async function matchTaskToSkills(
task: TaskDescription,
registry: SkillRegistry
): Promise<MatchResult[]> {
const intent = await extractIntent(task.raw);
task.intent = intent;
const candidates = generateCandidates(task, registry);
const scored = await Promise.all(
candidates.map(skill => scoreMatch(task, skill))
);
return scored.sort((a, b) => b.score - a.score);
}
Intent Extraction
interface IntentExtraction {
action: string;
object: string;
modifiers: string[];
domain: string;
}
async function extractIntent(
description: string
): Promise<Intent> {
const actionPatterns = {
create: ['build', 'create', 'make', 'generate', 'write'],
analyze: ['analyze', 'examine', 'review', 'inspect', 'check'],
modify: ['update', 'change', 'edit', 'fix', 'refactor'],
validate: ['validate', 'verify', 'test', 'ensure', 'confirm'],
transform: ['convert', 'transform', 'translate', 'migrate'],
};
const domainPatterns = {
code: ['code', 'function', 'class', 'module', 'api'],
data: ['data', 'database', 'schema', 'query', 'model'],
docs: ['documentation', 'readme', 'guide', 'tutorial'],
test: ['test', 'spec', 'coverage', 'assertion'],
security: ['security', 'vulnerability', 'auth', 'permission'],
};
const normalizedDesc = description.toLowerCase();
let primaryAction = 'unknown';
for (const [action, patterns] of Object.entries(actionPatterns)) {
if (patterns.some(p => normalizedDesc.includes(p))) {
primaryAction = action;
break;
}
}
let domain = 'general';
for (const [d, patterns] of Object.entries(domainPatterns)) {
if (patterns.some(p => normalizedDesc.includes(p))) {
domain = d;
break;
}
}
return {
primary: primaryAction,
secondary: [],
domain,
};
}
Semantic Similarity
const capabilitySynonyms: Map<string, string[]> = new Map([
['code-review', ['review code', 'check code', 'code analysis', 'code quality']],
['testing', ['test', 'spec', 'unit test', 'integration test', 'qa']],
['documentation', ['docs', 'readme', 'guide', 'tutorial', 'api docs']],
['refactoring', ['refactor', 'clean up', 'improve', 'restructure']],
['security', ['security audit', 'vulnerability scan', 'pen test']],
]);
function semanticSimilarity(
term1: string,
term2: string
): number {
if (term1 === term2) return 1.0;
for (const [canonical, synonyms] of capabilitySynonyms) {
const allTerms = [canonical, ...synonyms];
if (allTerms.includes(term1) && allTerms.includes(term2)) {
return 0.9;
}
}
if (term1.includes(term2) || term2.includes(term1)) {
return 0.7;
}
const words1 = new Set(term1.split(/\s+/));
const words2 = new Set(term2.split(/\s+/));
const intersection = new Set([...words1].filter(x => words2.has(x)));
const union = new Set([...words1, ...words2]);
const jaccard = intersection.size / union.size;
return jaccard * 0.6;
}
Match Scoring
function scoreMatch(
task: TaskDescription,
skill: SkillMetadata
): MatchResult {
const intentScore = scoreIntentMatch(task.intent, skill);
const capScore = scoreCapabilityMatch(
task.capabilities,
skill.capabilities
);
const constraintScore = scoreConstraintMatch(
task.constraints,
skill
);
const score = (
intentScore * 0.3 +
capScore * 0.5 +
constraintScore * 0.2
);
const gaps = findCapabilityGaps(task.capabilities, skill.capabilities);
return {
skillId: skill.id,
score,
breakdown: {
intentMatch: intentScore,
capabilityMatch: capScore,
constraintMatch: constraintScore,
},
explanation: generateExplanation(task, skill, score),
gaps,
};
}
function scoreCapabilityMatch(
required: string[],
available: Capability[]
): number {
if (required.length === 0) return 0.5;
let totalScore = 0;
for (const req of required) {
let bestMatch = 0;
for (const cap of available) {
const similarity = semanticSimilarity(req, cap.name);
const adjustedScore = similarity * cap.confidence;
bestMatch = Math.max(bestMatch, adjustedScore);
}
totalScore += bestMatch;
}
return totalScore / required.length;
}
Match Explanation
function generateExplanation(
task: TaskDescription,
skill: SkillMetadata,
score: number
): string {
const parts: string[] = [];
if (score >= 0.8) {
parts.push(`Strong match for "${task.intent.primary}" tasks.`);
} else if (score >= 0.6) {
parts.push(`Good match with some capability alignment.`);
} else if (score >= 0.4) {
parts.push(`Partial match - may need supplementary skills.`);
} else {
parts.push(`Weak match - consider alternatives.`);
}
const matchedCaps = skill.capabilities
.filter(cap =>
task.capabilities.some(req =>
semanticSimilarity(req, cap.name) > 0.6
)
)
.map(cap => cap.name);
if (matchedCaps.length > 0) {
parts.push(`Matches: ${matchedCaps.join(', ')}`);
}
return parts.join(' ');
}
Query Expansion
function expandQuery(
task: TaskDescription
): TaskDescription {
const expanded = { ...task };
const additionalCaps: string[] = [];
for (const cap of task.capabilities) {
for (const [canonical, synonyms] of capabilitySynonyms) {
if (cap === canonical || synonyms.includes(cap)) {
additionalCaps.push(canonical, ...synonyms);
}
}
}
expanded.capabilities = [
...new Set([...task.capabilities, ...additionalCaps]),
];
return expanded;
}
Output Format
matchResults:
query: "Review this TypeScript code for bugs and security issues"
extractedIntent:
primary: analyze
secondary: [validate]
domain: code
requiredCapabilities:
- code-review
- bug-detection
- security-analysis
matches:
- skillId: code-reviewer
score: 0.92
breakdown:
intentMatch: 0.95
capabilityMatch: 0.90
constraintMatch: 0.90
explanation: "Strong match for 'analyze' tasks. Matches: code-review, bug-detection"
gaps: []
- skillId: security-auditor
score: 0.78
breakdown:
intentMatch: 0.80
capabilityMatch: 0.85
constraintMatch: 0.70
explanation: "Good match with security focus. Matches: security-analysis"
gaps: [bug-detection]
- skillId: typescript-expert
score: 0.65
breakdown:
intentMatch: 0.70
capabilityMatch: 0.60
constraintMatch: 0.65
explanation: "Partial match - specialized in TypeScript but general purpose"
gaps: [security-analysis]
Integration Points
- Registry: Queries
dag-skill-registry for skill catalog
- Ranking: Passes candidates to
dag-capability-ranker
- Consumers:
dag-graph-builder for node skill assignment
- Feedback: Performance data from
dag-pattern-learner
Best Practices
- Expand Queries: Use synonyms to improve recall
- Weight Capabilities: Not all matches are equal
- Explain Matches: Transparency builds trust
- Track Performance: Learn from successful matches
- Handle Ambiguity: Ask for clarification when unsure
Natural language in. Perfect skills out. Semantic understanding.