@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.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rdc",
3
- "version": "0.24.17",
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
@@ -46,7 +46,7 @@ import {
46
46
  getSkill,
47
47
  getSkillBody,
48
48
  getCloudOverride,
49
- skillNames,
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 valid = skillNames();
170
- if (!valid.includes(name)) {
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(name, resolved);
177
+ const rendered = renderSkill(resolvedName, resolved);
177
178
  if (!rendered) {
178
- return textResult(`Skill '${name}' exists in the catalog but has no SKILL.md body on disk.`);
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
@@ -1,3 +1,3 @@
1
1
  {
2
- "sha": "f24f53db78dac2ce5a7f79d6815d8fb9ceb30c54"
2
+ "sha": "2a43eed5f23588339a213ea05dc96cdcbce3b4b6"
3
3
  }
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
- return getCatalog().find((s) => s.name === name) || null;
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 skillMd = path.join(SKILLS_DIR, name, 'SKILL.md');
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 p = path.join(SKILLS_DIR, name, 'SKILL.cloud.md');
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lifeaitools/rdc-skills",
3
- "version": "0.24.17",
3
+ "version": "0.24.18",
4
4
  "description": "RDC typed-agent dispatch skill suite for Claude Code - plan, build, review, overnight builds",
5
5
  "keywords": [
6
6
  "claude-code",
@@ -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
 
@@ -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: [] }; }