1
Claude Rio[github]
1
2
Deterministic matcher system for Claude Code that improves
3
activation of skills, agents, and commands through explicit
4
keyword-based matching.
5
6
Lightweight architecture with no external dependencies, featuring
7
shell preprocessing in 10-20ms and cross-platform support for
8
macOS, Linux, and Windows.
9
10
11
12
without rio with rio
13
─────────────────────────────────── ───────────────────────────────────
14
> ▌ > ▌
15
16
17
18
19
20
21
22
activation [███░░░░░░░░░] sometimes activation [███████████░] usually
23
illustrative — claude still makes the final call
24
26
27
module.exports = function (context) {
28
// Keywords for a Docker helper skill
29
// In your matcher, replace these with keywords relevant to your skill
30
const keywords = [
31
'docker',
32
'container',
33
'dockerfile',
34
'docker-compose',
35
'compose',
36
'image',
37
'containerize',
38
];
39
40
// Convert prompt to lowercase for case-insensitive matching
41
const prompt = context.prompt.toLowerCase();
42
43
// Count how many keywords are present in the prompt
44
const matchCount = keywords.filter((keyword) => prompt.includes(keyword)).length;
45
46
// IMPORTANT: All fields are MANDATORY and must not be undefined/null
47
return {
48
version: '2.0', // Required: always "2.0"
49
matchCount: matchCount, // Required: number of matches (0+)
50
type: 'skill', // Required: "skill" or "agent"
51
};
52
};
53
module.exports = function (context) {
// Keywords for a Docker helper skill
// In your matcher, replace these with keywords relevant to your skill
const keywords = [
'docker',
'container',
'dockerfile',
'docker-compose',
'compose',
'image',
'containerize',
];
// Convert prompt to lowercase for case-insensitive matching
const prompt = context.prompt.toLowerCase();
// Count how many keywords are present in the prompt
const matchCount = keywords.filter((keyword) => prompt.includes(keyword)).length;
// IMPORTANT: All fields are MANDATORY and must not be undefined/null
return {
version: '2.0', // Required: always "2.0"
matchCount: matchCount, // Required: number of matches (0+)
type: 'skill', // Required: "skill" or "agent"
};
};
module.exports = function (context) {
const prompt = context.prompt.toLowerCase();
// TODO: Haiku fills this array with relevant keywords
const keywords = [
// Keywords will be inserted here by setup command
];
// Count matching keywords
const matchCount = keywords.filter((keyword) => prompt.includes(keyword)).length;
// IMPORTANT: All fields are MANDATORY and must not be undefined/null
return {
version: '2.0', // Required: always "2.0"
matchCount: matchCount, // Required: number of matches (0+)
type: 'skill', // TODO: Haiku sets to 'skill' or 'agent' based on context
};
};
/**
* Get conversation history from transcript (cached).
* Returns array of {role: 'user'|'assistant', content: string} objects.
*
* @param {string} transcriptPath - Path to transcript file
* @returns {Promise<Array<{role: string, content: string}>>}
*/
async function getConversationHistory(transcriptPath) {
if (!cache.conversationHistory) {
cache.conversationHistory = await parseConversationHistory(transcriptPath);
}
return cache.conversationHistory;
}
/**
* Get tool usage from transcript (cached).
* Returns array of {tool: string, input: object, timestamp: string} objects.
*
* @param {string} transcriptPath - Path to transcript file
* @returns {Promise<Array<{tool: string, input: object, timestamp: string}>>}
*/
async function getToolUsage(transcriptPath) {
if (!cache.toolUsage) {
cache.toolUsage = await parseToolUsage(transcriptPath);
}
return cache.toolUsage;
}
/**
* Create a test context for matcher validation
* This simulates the context that matchers receive in real usage
*
* @returns {Object} Test context object
*/
function createTestContext() {
return {
prompt: 'test keywords build typescript compile check',
cwd: process.cwd(),
transcriptPath: '/tmp/test-transcript.jsonl',
sessionId: 'test-session-id',
permissionMode: 'ask',
meta: {
schemaVersion: '2.0',
},
transcript: {
getConversationHistory: async () => [],
getToolUsage: async () => [],
getInitialMessage: async () => null,
getAllMessages: async () => [],
},
};
}