@lifeaitools/rdc-skills 0.35.6 → 0.35.7
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 +180 -44
- package/.github/workflows/self-test.yml +34 -34
- package/CHANGELOG.md +393 -393
- package/MANIFEST.md +224 -224
- package/README.md +10 -3
- package/clauth-plugin.json +1 -1
- package/commands/analyze-tests.md +1 -4
- package/commands/build.md +1 -2
- package/commands/check-clean-code.md +1 -3
- package/commands/check-packages.md +1 -3
- package/commands/collab.md +1 -2
- package/commands/compare-compliance.md +1 -4
- package/commands/deploy.md +1 -2
- package/commands/design.md +1 -2
- package/commands/edit.md +1 -2
- package/commands/fixit.md +1 -2
- package/commands/flow.md +1 -2
- package/commands/full-analysis.md +1 -7
- package/commands/get-refactoring-plan.md +1 -5
- package/commands/handoff.md +1 -2
- package/commands/help.md +1 -3
- package/commands/mode.md +1 -2
- package/commands/overnight.md +1 -2
- package/commands/plan.md +1 -2
- package/commands/preplan.md +1 -2
- package/commands/prototype.md +1 -2
- package/commands/quick-check.md +1 -4
- package/commands/recover.md +1 -2
- package/commands/release.md +2 -2
- package/commands/report.md +1 -2
- package/commands/review-arch.md +1 -4
- package/commands/review.md +1 -5
- package/commands/self-test.md +1 -2
- package/commands/status.md +1 -2
- package/commands/suggest-patterns.md +1 -4
- package/commands/validate-solid.md +1 -3
- package/commands/watch.md +1 -2
- package/commands/workitems.md +1 -2
- package/guides/agents/backend.md +102 -102
- package/guides/agents/content.md +94 -94
- package/guides/agents/cs2.md +56 -56
- package/guides/agents/data.md +86 -86
- package/guides/agents/design.md +77 -77
- package/guides/agents/frontend.md +91 -91
- package/guides/agents/infrastructure.md +81 -81
- package/guides/agents/setup.md +272 -272
- package/guides/agents/verify.md +119 -119
- package/guides/agents/viz.md +106 -106
- package/guides/orchestration-epic.md +17 -17
- package/hooks/require-work-item-on-commit.js +69 -4
- package/package.json +1 -1
- package/scripts/gen-skill-descriptions.mjs +190 -0
- package/scripts/self-test.mjs +1459 -1459
- package/skills/architecture-reviewer/SKILL.md +1 -11
- package/skills/behavior-audit/SKILL.md +1 -1
- package/skills/behavior-audit/agents/openai.yaml +4 -4
- package/skills/brochure/SKILL.md +1 -1
- package/skills/brochurify/SKILL.md +1 -1
- package/skills/build/SKILL.md +1 -1
- package/skills/channel-formatter/SKILL.md +1 -1
- package/skills/clean-code-analyzer/SKILL.md +1 -6
- package/skills/co-develop/SKILL.md +1 -1
- package/skills/collab/SKILL.md +1 -1
- package/skills/convert/SKILL.md +1 -1
- package/skills/deploy/SKILL.md +1 -1
- package/skills/design/SKILL.md +1 -1
- package/skills/edit/SKILL.md +1 -1
- package/skills/env/SKILL.md +1 -1
- package/skills/extract-verifier-rules/SKILL.md +1 -1
- package/skills/fixit/SKILL.md +1 -1
- package/skills/fs-mcp/SKILL.md +1 -1
- package/skills/handoff/SKILL.md +1 -1
- package/skills/help/SKILL.md +1 -1
- package/skills/housekeeping/SKILL.md +1 -1
- package/skills/lifeai-brochure-author/SKILL.md +1 -1
- package/skills/new-model/SKILL.md +1 -7
- package/skills/onramp/SKILL.md +1 -1
- package/skills/overnight/SKILL.md +1 -1
- package/skills/package-design/SKILL.md +1 -4
- package/skills/pattern-advisor/SKILL.md +1 -6
- package/skills/pattern-refactoring-guide/SKILL.md +1 -4
- package/skills/plan/SKILL.md +1 -1
- package/skills/preplan/SKILL.md +1 -1
- package/skills/prototype/SKILL.md +1 -1
- package/skills/refactor/SKILL.md +1 -1
- package/skills/regen-media/SKILL.md +1 -1
- package/skills/release/SKILL.md +1 -1
- package/skills/report/SKILL.md +1 -1
- package/skills/review/SKILL.md +1 -1
- package/skills/rpms-filemap/SKILL.md +1 -1
- package/skills/self-test/SKILL.md +1 -1
- package/skills/solid-validator/SKILL.md +1 -6
- package/skills/status/SKILL.md +1 -1
- package/skills/terminal-config/SKILL.md +1 -1
- package/skills/testing-strategy/SKILL.md +1 -6
- package/skills/tests/onramp.test.json +101 -101
- package/skills/tests/rdc-env.test.json +12 -12
- package/skills/tests/rdc-new-model.test.json +12 -12
- package/skills/tests/rdc-refactor.test.json +29 -29
- package/skills/tests/rdc-regen-media.test.json +29 -29
- package/skills/watch/SKILL.md +1 -1
- package/skills/workitems/SKILL.md +1 -1
- package/tests/clauth-plugin-postinstall.test.mjs +14 -1
- package/tests/help-surface.test.mjs +2 -2
- package/tests/require-work-item-on-commit.test.mjs +54 -0
- package/commands/open.md +0 -84
- package/ecosystem.local.config.cjs +0 -18
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Generate every skill/command `description` from ONE source: plugin.json's
|
|
4
|
+
* `skills_meta`.
|
|
5
|
+
*
|
|
6
|
+
* WHY THIS EXISTS
|
|
7
|
+
*
|
|
8
|
+
* `/rdc:plan` appeared FOUR times in the command list with THREE different
|
|
9
|
+
* descriptions. Measured 2026-08-29:
|
|
10
|
+
*
|
|
11
|
+
* marketplaces/rdc-skills/commands/plan.md (179 lines)
|
|
12
|
+
* marketplaces/rdc-skills/skills/plan/SKILL.md (368 lines)
|
|
13
|
+
* cache/rdc-skills/.../latest/commands/plan.md
|
|
14
|
+
* cache/rdc-skills/.../latest/skills/plan/SKILL.md
|
|
15
|
+
*
|
|
16
|
+
* Two install locations (Claude Code's own plugin mechanism — marketplace is
|
|
17
|
+
* the source, cache is the installed copy) multiplied by two surfaces we ship
|
|
18
|
+
* ourselves. Nineteen verbs are shipped as BOTH a command and a skill, each
|
|
19
|
+
* with its own hand-written frontmatter, so the descriptions drifted apart.
|
|
20
|
+
*
|
|
21
|
+
* The duplicate SURFACES are a separate question with real content at stake.
|
|
22
|
+
* The duplicate DESCRIPTIONS are not: they are two hand-written copies of one
|
|
23
|
+
* fact. `skills_meta` already carries `slash`, `usage`, typed positional args
|
|
24
|
+
* and typed flags — everything needed to render the line. It just was not what
|
|
25
|
+
* rendered.
|
|
26
|
+
*
|
|
27
|
+
* THE SHAPE, per operator instruction 2026-08-29:
|
|
28
|
+
*
|
|
29
|
+
* rdc:build (epic-id) - [--no-review] — execute a planned epic
|
|
30
|
+
* rdc:deploy (slug, [action]) - [--fix, --hotfix] — ship to PM2 dev
|
|
31
|
+
*
|
|
32
|
+
* Required positionals bare, optional in brackets, flags after the dash. No
|
|
33
|
+
* paragraph. The long explanation belongs in the body, which is where someone
|
|
34
|
+
* who has already chosen the command is reading.
|
|
35
|
+
*
|
|
36
|
+
* `purpose` is authored in skills_meta. On first run this backfills it from the
|
|
37
|
+
* existing description so nothing is invented, then the render is deterministic
|
|
38
|
+
* forever after: change the meta, re-run, both surfaces agree.
|
|
39
|
+
*
|
|
40
|
+
* Run: node scripts/gen-skill-descriptions.mjs [--check]
|
|
41
|
+
* --check exit 1 if any file is out of date (for CI / the acceptance gate)
|
|
42
|
+
*/
|
|
43
|
+
import { readFileSync, writeFileSync, readdirSync, existsSync } from 'node:fs';
|
|
44
|
+
import path from 'node:path';
|
|
45
|
+
import { fileURLToPath } from 'node:url';
|
|
46
|
+
|
|
47
|
+
const ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
|
|
48
|
+
const PLUGIN = path.join(ROOT, '.claude-plugin', 'plugin.json');
|
|
49
|
+
const CHECK = process.argv.includes('--check');
|
|
50
|
+
|
|
51
|
+
/** Render the one-line description for a skill from its meta. */
|
|
52
|
+
export function renderDescription(meta) {
|
|
53
|
+
const slash = meta.slash || meta.name;
|
|
54
|
+
const positional = meta.args?.positional;
|
|
55
|
+
let head;
|
|
56
|
+
if (positional) {
|
|
57
|
+
const pos = positional.map((a) => (a.required ? a.name : `[${a.name}]`)).join(', ');
|
|
58
|
+
const flags = (meta.args?.flags ?? []).map((f) => f.name).join(', ');
|
|
59
|
+
head = `${slash} (${pos})${flags ? ` - [${flags}]` : ''}`;
|
|
60
|
+
} else {
|
|
61
|
+
// Command-only verbs have no typed arg table. Their `usage` string is the
|
|
62
|
+
// authored truth, so render its tail verbatim rather than inventing a typed
|
|
63
|
+
// signature — `rdc:mode ()` would be a confident lie about a verb that
|
|
64
|
+
// takes three forms.
|
|
65
|
+
const tail = String(meta.usage || '').replace(new RegExp(`^\\s*${slash}\\s*`), '').trim();
|
|
66
|
+
head = tail ? `${slash} ${tail}` : slash;
|
|
67
|
+
}
|
|
68
|
+
return meta.purpose ? `${head} — ${meta.purpose}` : head;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Best-effort one-line purpose from an existing verbose description.
|
|
73
|
+
*
|
|
74
|
+
* Only used to BACKFILL skills_meta once. Everything here is already-authored
|
|
75
|
+
* text being shortened — nothing is invented, because a generated description
|
|
76
|
+
* that states a capability nobody wrote is exactly the failure mode this repo
|
|
77
|
+
* spends its gates preventing.
|
|
78
|
+
*/
|
|
79
|
+
export function purposeFrom(description) {
|
|
80
|
+
let d = String(description || '').replace(/\s+/g, ' ').trim();
|
|
81
|
+
d = d.replace(/^Usage\s+`[^`]*`\s*[—-]\s*/i, ''); // drop a leading "Usage `x` —"
|
|
82
|
+
d = d.replace(/^`[^`]*`\s*[—-]\s*/, '');
|
|
83
|
+
d = d.replace(/^rdc:[a-z-]+\s*[—-]\s*/i, '');
|
|
84
|
+
const stop = d.search(/(?<=\.)\s+(?:Use|Called|Produces|Then|After|This)\b/);
|
|
85
|
+
if (stop > 0) d = d.slice(0, stop);
|
|
86
|
+
d = d.split(/\.\s/)[0].replace(/\.$/, '').trim();
|
|
87
|
+
if (d.length <= 110) return d;
|
|
88
|
+
// Cut at a word boundary. Slicing mid-word produced "...bearer-authe..." —
|
|
89
|
+
// a truncation that reads as a typo rather than as an elision.
|
|
90
|
+
const cut = d.slice(0, 107);
|
|
91
|
+
return `${cut.slice(0, cut.lastIndexOf(' ')).trimEnd()}...`;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/** Replace the frontmatter `description:` of a markdown file. Returns true if changed. */
|
|
95
|
+
function setDescription(file, description) {
|
|
96
|
+
const src = readFileSync(file, 'utf8');
|
|
97
|
+
const fm = src.match(/^---\r?\n([\s\S]*?)\r?\n---/);
|
|
98
|
+
if (!fm) return { changed: false, reason: 'no frontmatter' };
|
|
99
|
+
const body = fm[1];
|
|
100
|
+
// description: may be plain, quoted, or a >- folded block spanning lines.
|
|
101
|
+
const stripped = body.replace(/^description:\s*(?:>-?\s*\n(?:[ \t]+.*\n?)*|.*\n?)/m, '');
|
|
102
|
+
// Emit a PLAIN YAML scalar when it is safe to. JSON.stringify always quotes,
|
|
103
|
+
// and consumers that read the frontmatter naively then render the quotes as
|
|
104
|
+
// part of the description — the installed command list showed
|
|
105
|
+
// /rdc:build "rdc:build (epic-id) - [--no-review] — ..."
|
|
106
|
+
// quotes and all. A plain scalar is only unsafe if it starts with a YAML
|
|
107
|
+
// indicator or contains ": " (which would read as a key), so check for
|
|
108
|
+
// exactly that and quote only then.
|
|
109
|
+
const needsQuotes = /^[[{"'&*#|>%@`!-]/.test(description) || /:\s/.test(description) || /\s#/.test(description);
|
|
110
|
+
const rebuilt = `${stripped.replace(/\n+$/, '')}\ndescription: ${needsQuotes ? JSON.stringify(description) : description}`;
|
|
111
|
+
const out = src.replace(fm[0], `---\n${rebuilt}\n---`);
|
|
112
|
+
if (out === src) return { changed: false };
|
|
113
|
+
if (!CHECK) writeFileSync(file, out);
|
|
114
|
+
return { changed: true };
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
const plugin = JSON.parse(readFileSync(PLUGIN, 'utf8'));
|
|
118
|
+
const meta = plugin.skills_meta;
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Command-only verbs live in their OWN block, not in skills_meta.
|
|
122
|
+
*
|
|
123
|
+
* A first cut backfilled them straight into skills_meta and broke its
|
|
124
|
+
* invariant: `skills_meta` means "one entry per skill directory", asserted at
|
|
125
|
+
* 43, and adding thirteen commands made it 56. The acceptance suite caught it.
|
|
126
|
+
*
|
|
127
|
+
* That invariant is worth keeping — skills_meta feeds skill dispatch, and a
|
|
128
|
+
* command with no skill directory behind it would be a dangling entry. So the
|
|
129
|
+
* commands get a sibling block, and the generator reads both. One source of
|
|
130
|
+
* truth per surface, rather than one source of truth with a hole in it.
|
|
131
|
+
*
|
|
132
|
+
* Backfilled from each command's own authored `usage` line, so an entry only
|
|
133
|
+
* ever restates what the file already stated.
|
|
134
|
+
*/
|
|
135
|
+
plugin.commands_meta = plugin.commands_meta || {};
|
|
136
|
+
const cmdMeta = plugin.commands_meta;
|
|
137
|
+
for (const f of readdirSync(path.join(ROOT, 'commands')).filter((x) => x.endsWith('.md'))) {
|
|
138
|
+
const name = f.replace(/\.md$/, '');
|
|
139
|
+
if (meta[name] || cmdMeta[name]) continue;
|
|
140
|
+
const src = readFileSync(path.join(ROOT, 'commands', f), 'utf8');
|
|
141
|
+
const fm = src.match(/description:\s*(?:>-?\s*\n((?:[ \t]+.*\n?)*)|["']?(.*?)["']?\s*\n)/);
|
|
142
|
+
const raw = (fm?.[1] || fm?.[2] || '').replace(/\s+/g, ' ').trim();
|
|
143
|
+
const usage = raw.match(/Usage\s+`([^`]+)`/i)?.[1]?.trim() || `rdc:${name}`;
|
|
144
|
+
cmdMeta[name] = { name, slash: `rdc:${name}`, usage, purpose: purposeFrom(raw) };
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
const names = [...Object.keys(meta), ...Object.keys(cmdMeta)].filter((k) => !k.startsWith('$'));
|
|
148
|
+
const metaFor = (name) => meta[name] || cmdMeta[name];
|
|
149
|
+
|
|
150
|
+
let backfilled = 0;
|
|
151
|
+
let touched = 0;
|
|
152
|
+
const stale = [];
|
|
153
|
+
|
|
154
|
+
for (const name of names) {
|
|
155
|
+
const m = metaFor(name);
|
|
156
|
+
const skillFile = path.join(ROOT, 'skills', name, 'SKILL.md');
|
|
157
|
+
const cmdFile = path.join(ROOT, 'commands', `${name}.md`);
|
|
158
|
+
|
|
159
|
+
if (!m.purpose) {
|
|
160
|
+
const source = existsSync(skillFile) ? skillFile : (existsSync(cmdFile) ? cmdFile : null);
|
|
161
|
+
if (source) {
|
|
162
|
+
const fm = readFileSync(source, 'utf8').match(/description:\s*(?:>-?\s*\n((?:[ \t]+.*\n?)*)|["']?(.*?)["']?\s*\n)/);
|
|
163
|
+
const raw = (fm?.[1] || fm?.[2] || '').replace(/\s+/g, ' ').trim();
|
|
164
|
+
const p = purposeFrom(raw);
|
|
165
|
+
if (p) { m.purpose = p; backfilled++; }
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
const description = renderDescription(m);
|
|
170
|
+
for (const f of [skillFile, cmdFile]) {
|
|
171
|
+
if (!existsSync(f)) continue;
|
|
172
|
+
const r = setDescription(f, description);
|
|
173
|
+
if (r.changed) { touched++; stale.push(path.relative(ROOT, f)); }
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
if (backfilled && !CHECK) writeFileSync(PLUGIN, `${JSON.stringify(plugin, null, 2)}\n`);
|
|
178
|
+
|
|
179
|
+
if (CHECK) {
|
|
180
|
+
if (stale.length) {
|
|
181
|
+
console.error(`descriptions out of date (${stale.length}) — run: node scripts/gen-skill-descriptions.mjs`);
|
|
182
|
+
for (const f of stale.slice(0, 20)) console.error(` ${f}`);
|
|
183
|
+
process.exit(1);
|
|
184
|
+
}
|
|
185
|
+
console.log(`descriptions current across ${names.length} skills`);
|
|
186
|
+
process.exit(0);
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
console.log(`skills_meta: ${names.length} skills, ${backfilled} purpose(s) backfilled`);
|
|
190
|
+
console.log(`descriptions rewritten: ${touched} file(s)`);
|