@lifeaitools/rdc-skills 0.24.17 → 0.24.19

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.19",
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
@@ -90,6 +90,19 @@ curl -s -X POST https://rdc-skills.regendevcorp.com/mcp \
90
90
  -d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"curl","version":"1"}}}'
91
91
  ```
92
92
 
93
+ Responses come back as Server-Sent Events. For plain curl usage, extract the
94
+ JSON-RPC envelope from the `data:` line:
95
+
96
+ ```bash
97
+ curl -s -X POST https://rdc-skills.regendevcorp.com/mcp \
98
+ -H 'Content-Type: application/json' \
99
+ -H 'Accept: application/json, text/event-stream' \
100
+ -d '{"jsonrpc":"2.0","id":2,"method":"tools/list"}' \
101
+ | sed -n 's/^data: //p'
102
+ ```
103
+
104
+ Tool results put the human-readable payload in `result.content[0].text`.
105
+
93
106
  List tools:
94
107
 
95
108
  ```bash
@@ -117,13 +130,14 @@ curl -s -X POST https://rdc-skills.regendevcorp.com/mcp \
117
130
  -d '{"jsonrpc":"2.0","id":4,"method":"tools/call","params":{"name":"rdc_skill_search","arguments":{"query":"turn this article into social posts"}}}'
118
131
  ```
119
132
 
120
- Fetch a skill body for a specific caller surface:
133
+ Fetch a skill body for a specific caller surface. `name` accepts either the
134
+ catalog `name` (`build`) or the visible slash form (`rdc:build`):
121
135
 
122
136
  ```bash
123
137
  curl -s -X POST https://rdc-skills.regendevcorp.com/mcp \
124
138
  -H 'Content-Type: application/json' \
125
139
  -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"}}}'
140
+ -d '{"jsonrpc":"2.0","id":5,"method":"tools/call","params":{"name":"rdc_skill_get","arguments":{"name":"rdc:build","variant":"cli"}}}'
127
141
  ```
128
142
 
129
143
  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
@@ -46,11 +46,18 @@ Health: curl -s https://rdc-skills.regendevcorp.com/health
46
46
  Header: Accept: application/json, text/event-stream
47
47
  Tools: rdc_skill_list, rdc_skill_search, rdc_skill_get
48
48
  Variants: cli for Claude Code/Codex/local terminal; cloud for claude.ai
49
+ Response: SSE `data:` line contains the JSON-RPC envelope; tool text is result.content[0].text
49
50
  ```
50
51
 
51
52
  Minimal curl examples:
52
53
 
53
54
  ```bash
55
+ curl -s -X POST https://rdc-skills.regendevcorp.com/mcp \
56
+ -H 'Content-Type: application/json' \
57
+ -H 'Accept: application/json, text/event-stream' \
58
+ -d '{"jsonrpc":"2.0","id":0,"method":"tools/list"}' \
59
+ | sed -n 's/^data: //p'
60
+
54
61
  curl -s -X POST https://rdc-skills.regendevcorp.com/mcp \
55
62
  -H 'Content-Type: application/json' \
56
63
  -H 'Accept: application/json, text/event-stream' \
@@ -64,7 +71,7 @@ curl -s -X POST https://rdc-skills.regendevcorp.com/mcp \
64
71
  curl -s -X POST https://rdc-skills.regendevcorp.com/mcp \
65
72
  -H 'Content-Type: application/json' \
66
73
  -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"}}}'
74
+ -d '{"jsonrpc":"2.0","id":3,"method":"tools/call","params":{"name":"rdc_skill_get","arguments":{"name":"rdc:build","variant":"cli"}}}'
68
75
  ```
69
76
 
70
77
  ## Hard Rules
package/git-sha.json CHANGED
@@ -1,3 +1,3 @@
1
1
  {
2
- "sha": "f24f53db78dac2ce5a7f79d6815d8fb9ceb30c54"
2
+ "sha": "9c73b2cb3e0d6caedfd64996cc3ce587344d5859"
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.19",
4
4
  "description": "RDC typed-agent dispatch skill suite for Claude Code - plan, build, review, overnight builds",
5
5
  "keywords": [
6
6
  "claude-code",
@@ -94,6 +94,14 @@ RDC SKILLS — manifest: .claude-plugin/plugin.json @ v{version}
94
94
  Header: Accept: application/json, text/event-stream
95
95
  Tools: rdc_skill_list, rdc_skill_search, rdc_skill_get
96
96
  Variants: cli for Claude Code/Codex/local terminal; cloud for claude.ai
97
+ Response: SSE `data:` line contains the JSON-RPC envelope; tool text is result.content[0].text
98
+
99
+ Extract the JSON-RPC envelope:
100
+ curl -s -X POST https://rdc-skills.regendevcorp.com/mcp \
101
+ -H 'Content-Type: application/json' \
102
+ -H 'Accept: application/json, text/event-stream' \
103
+ -d '{"jsonrpc":"2.0","id":0,"method":"tools/list"}' \
104
+ | sed -n 's/^data: //p'
97
105
 
98
106
  List skills:
99
107
  curl -s -X POST https://rdc-skills.regendevcorp.com/mcp \
@@ -111,7 +119,7 @@ RDC SKILLS — manifest: .claude-plugin/plugin.json @ v{version}
111
119
  curl -s -X POST https://rdc-skills.regendevcorp.com/mcp \
112
120
  -H 'Content-Type: application/json' \
113
121
  -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"}}}'
122
+ -d '{"jsonrpc":"2.0","id":3,"method":"tools/call","params":{"name":"rdc_skill_get","arguments":{"name":"rdc:build","variant":"cli"}}}'
115
123
 
116
124
  ## Hard rules
117
125
 
@@ -20,10 +20,14 @@ const docs = Object.fromEntries(
20
20
  for (const [name, text] of Object.entries(docs)) {
21
21
  assert.match(text, /https:\/\/rdc-skills\.regendevcorp\.com\/mcp/, `${name} must expose production MCP endpoint`);
22
22
  assert.match(text, /Accept: application\/json, text\/event-stream/, `${name} must show Streamable HTTP Accept header`);
23
+ assert.match(text, /data:/, `${name} must explain Streamable HTTP SSE data lines`);
24
+ assert.match(text, /result\.content\[0\]\.text/, `${name} must explain where tool text lives`);
25
+ assert.match(text, /sed -n 's\/\^data: \/\/p'/, `${name} must include a plain curl SSE extraction example`);
23
26
  assert.match(text, /rdc_skill_list/, `${name} must mention rdc_skill_list`);
24
27
  assert.match(text, /rdc_skill_search/, `${name} must mention rdc_skill_search`);
25
28
  assert.match(text, /rdc_skill_get/, `${name} must mention rdc_skill_get`);
26
29
  assert.match(text, /turn this article into social posts/, `${name} must include a natural-language search example`);
30
+ assert.match(text, /"name":"rdc:build"/, `${name} must show rdc_skill_get accepts visible slash names`);
27
31
  assert.doesNotMatch(text, /https:\/\/rdc-skills\.dev\.regendevcorp\.com\/mcp/, `${name} must not point callers at dev MCP`);
28
32
  }
29
33
 
@@ -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: [] }; }