@lifeaitools/rdc-skills 0.24.17 → 0.24.18
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/plugin.json +1 -1
- package/README.md +3 -2
- package/bin/rdc-skills-mcp.mjs +7 -6
- package/commands/help.md +1 -1
- package/git-sha.json +1 -1
- package/lib/catalog.mjs +29 -3
- package/package.json +1 -1
- package/skills/help/SKILL.md +1 -1
- package/tests/help-surface.test.mjs +1 -0
- package/tests/mcp.test.mjs +11 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "rdc",
|
|
3
|
-
"version": "0.24.
|
|
3
|
+
"version": "0.24.18",
|
|
4
4
|
"description": "RDC typed-agent dispatch skill suite for Claude Code — plan, build, review, overnight unattended builds with work-item tracking and TDD enforcement.",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "LIFEAI",
|
package/README.md
CHANGED
|
@@ -117,13 +117,14 @@ curl -s -X POST https://rdc-skills.regendevcorp.com/mcp \
|
|
|
117
117
|
-d '{"jsonrpc":"2.0","id":4,"method":"tools/call","params":{"name":"rdc_skill_search","arguments":{"query":"turn this article into social posts"}}}'
|
|
118
118
|
```
|
|
119
119
|
|
|
120
|
-
Fetch a skill body for a specific caller surface
|
|
120
|
+
Fetch a skill body for a specific caller surface. `name` accepts either the
|
|
121
|
+
catalog `name` (`build`) or the visible slash form (`rdc:build`):
|
|
121
122
|
|
|
122
123
|
```bash
|
|
123
124
|
curl -s -X POST https://rdc-skills.regendevcorp.com/mcp \
|
|
124
125
|
-H 'Content-Type: application/json' \
|
|
125
126
|
-H 'Accept: application/json, text/event-stream' \
|
|
126
|
-
-d '{"jsonrpc":"2.0","id":5,"method":"tools/call","params":{"name":"rdc_skill_get","arguments":{"name":"build","variant":"cli"}}}'
|
|
127
|
+
-d '{"jsonrpc":"2.0","id":5,"method":"tools/call","params":{"name":"rdc_skill_get","arguments":{"name":"rdc:build","variant":"cli"}}}'
|
|
127
128
|
```
|
|
128
129
|
|
|
129
130
|
Use `variant:"cli"` for Claude Code/Codex/local terminal instructions. Use
|
package/bin/rdc-skills-mcp.mjs
CHANGED
|
@@ -46,7 +46,7 @@ import {
|
|
|
46
46
|
getSkill,
|
|
47
47
|
getSkillBody,
|
|
48
48
|
getCloudOverride,
|
|
49
|
-
|
|
49
|
+
resolveSkillName,
|
|
50
50
|
searchSkills,
|
|
51
51
|
} from '../lib/catalog.mjs';
|
|
52
52
|
import { toCloudBody } from '../lib/cloud-rewrite.mjs';
|
|
@@ -161,21 +161,22 @@ function buildMcpServer() {
|
|
|
161
161
|
description:
|
|
162
162
|
"Get a skill's SKILL.md body rendered for the caller. variant 'cli' returns the body unchanged; 'cloud' rewrites local-shell/clauth-daemon steps for the claude.ai web client. Omit variant to use auto-detection (defaults to cloud).",
|
|
163
163
|
inputSchema: {
|
|
164
|
-
name: z.string().describe('Skill name (e.g. "deploy", "build"). See rdc_skill_list.'),
|
|
164
|
+
name: z.string().describe('Skill name or slash form (e.g. "deploy", "rdc:build", "rdc:brochurify", "lifeai-brochure-author"). See rdc_skill_list.'),
|
|
165
165
|
variant: z.enum(['cli', 'cloud']).optional().describe('Force the rendered variant; overrides caller detection.'),
|
|
166
166
|
},
|
|
167
167
|
},
|
|
168
168
|
async ({ name, variant }) => {
|
|
169
|
-
const
|
|
170
|
-
if (!
|
|
169
|
+
const resolvedName = resolveSkillName(name);
|
|
170
|
+
if (!resolvedName) {
|
|
171
|
+
const valid = listSkills().map((s) => `${s.name} (${s.slash})`);
|
|
171
172
|
return textResult(
|
|
172
173
|
`Unknown skill '${name}'. Valid skills (${valid.length}): ${valid.join(', ')}`,
|
|
173
174
|
);
|
|
174
175
|
}
|
|
175
176
|
const resolved = variant || lastDetectedVariant || 'cloud';
|
|
176
|
-
const rendered = renderSkill(
|
|
177
|
+
const rendered = renderSkill(resolvedName, resolved);
|
|
177
178
|
if (!rendered) {
|
|
178
|
-
return textResult(`Skill '${
|
|
179
|
+
return textResult(`Skill '${resolvedName}' exists in the catalog but has no SKILL.md body on disk.`);
|
|
179
180
|
}
|
|
180
181
|
return textResult(`${rendered.header}\n\n${rendered.body}`);
|
|
181
182
|
},
|
package/commands/help.md
CHANGED
|
@@ -64,7 +64,7 @@ curl -s -X POST https://rdc-skills.regendevcorp.com/mcp \
|
|
|
64
64
|
curl -s -X POST https://rdc-skills.regendevcorp.com/mcp \
|
|
65
65
|
-H 'Content-Type: application/json' \
|
|
66
66
|
-H 'Accept: application/json, text/event-stream' \
|
|
67
|
-
-d '{"jsonrpc":"2.0","id":3,"method":"tools/call","params":{"name":"rdc_skill_get","arguments":{"name":"build","variant":"cli"}}}'
|
|
67
|
+
-d '{"jsonrpc":"2.0","id":3,"method":"tools/call","params":{"name":"rdc_skill_get","arguments":{"name":"rdc:build","variant":"cli"}}}'
|
|
68
68
|
```
|
|
69
69
|
|
|
70
70
|
## Hard Rules
|
package/git-sha.json
CHANGED
package/lib/catalog.mjs
CHANGED
|
@@ -115,7 +115,8 @@ export function listSkills() {
|
|
|
115
115
|
|
|
116
116
|
/** Full catalog row for one skill, or null if unknown. */
|
|
117
117
|
export function getSkill(name) {
|
|
118
|
-
|
|
118
|
+
const resolved = resolveSkillName(name);
|
|
119
|
+
return resolved ? getCatalog().find((s) => s.name === resolved) || null : null;
|
|
119
120
|
}
|
|
120
121
|
|
|
121
122
|
/** List of valid skill names (for error messages). */
|
|
@@ -123,12 +124,35 @@ export function skillNames() {
|
|
|
123
124
|
return getCatalog().map((s) => s.name);
|
|
124
125
|
}
|
|
125
126
|
|
|
127
|
+
/** Return the canonical skill directory name for a bare name, slash, or usage token. */
|
|
128
|
+
export function resolveSkillName(input) {
|
|
129
|
+
const raw = String(input || '').trim();
|
|
130
|
+
if (!raw) return null;
|
|
131
|
+
const token = raw.split(/\s+/)[0];
|
|
132
|
+
const catalog = getCatalog();
|
|
133
|
+
const direct = catalog.find((s) => s.name === token || s.slash === token);
|
|
134
|
+
if (direct) return direct.name;
|
|
135
|
+
if (token.startsWith('/')) {
|
|
136
|
+
const withoutSlash = token.slice(1);
|
|
137
|
+
const slashMatch = catalog.find((s) => s.slash === withoutSlash);
|
|
138
|
+
if (slashMatch) return slashMatch.name;
|
|
139
|
+
}
|
|
140
|
+
if (token.startsWith('rdc:')) {
|
|
141
|
+
const bare = token.slice(4);
|
|
142
|
+
const bareMatch = catalog.find((s) => s.name === bare || s.name === `rdc-${bare}`);
|
|
143
|
+
if (bareMatch) return bareMatch.name;
|
|
144
|
+
}
|
|
145
|
+
return null;
|
|
146
|
+
}
|
|
147
|
+
|
|
126
148
|
/**
|
|
127
149
|
* Read the raw SKILL.md body (frontmatter stripped) for a skill, live from disk.
|
|
128
150
|
* Returns null if the file does not exist.
|
|
129
151
|
*/
|
|
130
152
|
export function getSkillBody(name) {
|
|
131
|
-
const
|
|
153
|
+
const resolved = resolveSkillName(name);
|
|
154
|
+
if (!resolved) return null;
|
|
155
|
+
const skillMd = path.join(SKILLS_DIR, resolved, 'SKILL.md');
|
|
132
156
|
if (!fs.existsSync(skillMd)) return null;
|
|
133
157
|
const raw = fs.readFileSync(skillMd, 'utf8').replace(/\r\n/g, '\n');
|
|
134
158
|
// Strip the leading frontmatter block if present.
|
|
@@ -138,7 +162,9 @@ export function getSkillBody(name) {
|
|
|
138
162
|
|
|
139
163
|
/** Absolute path to a hand-tuned cloud override, or null if absent. */
|
|
140
164
|
export function cloudOverridePath(name) {
|
|
141
|
-
const
|
|
165
|
+
const resolved = resolveSkillName(name);
|
|
166
|
+
if (!resolved) return null;
|
|
167
|
+
const p = path.join(SKILLS_DIR, resolved, 'SKILL.cloud.md');
|
|
142
168
|
return fs.existsSync(p) ? p : null;
|
|
143
169
|
}
|
|
144
170
|
|
package/package.json
CHANGED
package/skills/help/SKILL.md
CHANGED
|
@@ -111,7 +111,7 @@ RDC SKILLS — manifest: .claude-plugin/plugin.json @ v{version}
|
|
|
111
111
|
curl -s -X POST https://rdc-skills.regendevcorp.com/mcp \
|
|
112
112
|
-H 'Content-Type: application/json' \
|
|
113
113
|
-H 'Accept: application/json, text/event-stream' \
|
|
114
|
-
-d '{"jsonrpc":"2.0","id":3,"method":"tools/call","params":{"name":"rdc_skill_get","arguments":{"name":"build","variant":"cli"}}}'
|
|
114
|
+
-d '{"jsonrpc":"2.0","id":3,"method":"tools/call","params":{"name":"rdc_skill_get","arguments":{"name":"rdc:build","variant":"cli"}}}'
|
|
115
115
|
|
|
116
116
|
## Hard rules
|
|
117
117
|
|
|
@@ -24,6 +24,7 @@ for (const [name, text] of Object.entries(docs)) {
|
|
|
24
24
|
assert.match(text, /rdc_skill_search/, `${name} must mention rdc_skill_search`);
|
|
25
25
|
assert.match(text, /rdc_skill_get/, `${name} must mention rdc_skill_get`);
|
|
26
26
|
assert.match(text, /turn this article into social posts/, `${name} must include a natural-language search example`);
|
|
27
|
+
assert.match(text, /"name":"rdc:build"/, `${name} must show rdc_skill_get accepts visible slash names`);
|
|
27
28
|
assert.doesNotMatch(text, /https:\/\/rdc-skills\.dev\.regendevcorp\.com\/mcp/, `${name} must not point callers at dev MCP`);
|
|
28
29
|
}
|
|
29
30
|
|
package/tests/mcp.test.mjs
CHANGED
|
@@ -32,6 +32,7 @@ import {
|
|
|
32
32
|
getSkillBody,
|
|
33
33
|
getCloudOverride,
|
|
34
34
|
searchSkills,
|
|
35
|
+
resolveSkillName,
|
|
35
36
|
} from '../lib/catalog.mjs';
|
|
36
37
|
import { toCloudBody } from '../lib/cloud-rewrite.mjs';
|
|
37
38
|
|
|
@@ -96,6 +97,11 @@ function unitTests() {
|
|
|
96
97
|
check('unit: getSkill(nonexistent) is null', getSkill('___nope___') === null);
|
|
97
98
|
check('unit: search ranks exact name first', searchSkills('deploy')[0]?.name === 'deploy');
|
|
98
99
|
check('unit: empty search returns []', searchSkills('').length === 0);
|
|
100
|
+
check('unit: resolves bare skill name', resolveSkillName('build') === 'build');
|
|
101
|
+
check('unit: resolves rdc slash skill name', resolveSkillName('rdc:build') === 'build');
|
|
102
|
+
check('unit: resolves leading slash command form', resolveSkillName('/rdc:build') === 'build');
|
|
103
|
+
check('unit: resolves specialist slash alias', resolveSkillName('rdc:brochurify') === 'rdc-brochurify');
|
|
104
|
+
check('unit: resolves non-rdc specialist name', resolveSkillName('lifeai-brochure-author') === 'lifeai-brochure-author');
|
|
99
105
|
|
|
100
106
|
// Searchability gate — the dimension the first "100% coverage" missed. Every
|
|
101
107
|
// catalog entry MUST carry triggers + usage (frontmatter or skills_meta) so
|
|
@@ -175,6 +181,11 @@ async function sweep(url, label, { compareSource }) {
|
|
|
175
181
|
const unk = await mcp(url, { jsonrpc: '2.0', id: 20, method: 'tools/call', params: { name: 'rdc_skill_get', arguments: { name: '___nope___' } } });
|
|
176
182
|
check(`${label}: unknown skill returns helpful error`, /unknown skill/i.test(callText(unk.json)));
|
|
177
183
|
|
|
184
|
+
// Alias path: callers naturally copy the slash form from rdc_skill_list.
|
|
185
|
+
const aliasGet = await mcp(url, { jsonrpc: '2.0', id: 22, method: 'tools/call', params: { name: 'rdc_skill_get', arguments: { name: 'rdc:brochurify', variant: 'cli' } } });
|
|
186
|
+
const aliasBody = strip(callText(aliasGet.json));
|
|
187
|
+
check(`${label}: rdc_skill_get accepts slash aliases`, /rdc:brochurify Orchestrator/.test(aliasBody));
|
|
188
|
+
|
|
178
189
|
// search
|
|
179
190
|
const se = await mcp(url, { jsonrpc: '2.0', id: 21, method: 'tools/call', params: { name: 'rdc_skill_search', arguments: { query: 'deploy' } } });
|
|
180
191
|
let sr; try { sr = JSON.parse(callText(se.json)); } catch { sr = { results: [] }; }
|