@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.
Files changed (107) hide show
  1. package/.claude-plugin/plugin.json +180 -44
  2. package/.github/workflows/self-test.yml +34 -34
  3. package/CHANGELOG.md +393 -393
  4. package/MANIFEST.md +224 -224
  5. package/README.md +10 -3
  6. package/clauth-plugin.json +1 -1
  7. package/commands/analyze-tests.md +1 -4
  8. package/commands/build.md +1 -2
  9. package/commands/check-clean-code.md +1 -3
  10. package/commands/check-packages.md +1 -3
  11. package/commands/collab.md +1 -2
  12. package/commands/compare-compliance.md +1 -4
  13. package/commands/deploy.md +1 -2
  14. package/commands/design.md +1 -2
  15. package/commands/edit.md +1 -2
  16. package/commands/fixit.md +1 -2
  17. package/commands/flow.md +1 -2
  18. package/commands/full-analysis.md +1 -7
  19. package/commands/get-refactoring-plan.md +1 -5
  20. package/commands/handoff.md +1 -2
  21. package/commands/help.md +1 -3
  22. package/commands/mode.md +1 -2
  23. package/commands/overnight.md +1 -2
  24. package/commands/plan.md +1 -2
  25. package/commands/preplan.md +1 -2
  26. package/commands/prototype.md +1 -2
  27. package/commands/quick-check.md +1 -4
  28. package/commands/recover.md +1 -2
  29. package/commands/release.md +2 -2
  30. package/commands/report.md +1 -2
  31. package/commands/review-arch.md +1 -4
  32. package/commands/review.md +1 -5
  33. package/commands/self-test.md +1 -2
  34. package/commands/status.md +1 -2
  35. package/commands/suggest-patterns.md +1 -4
  36. package/commands/validate-solid.md +1 -3
  37. package/commands/watch.md +1 -2
  38. package/commands/workitems.md +1 -2
  39. package/guides/agents/backend.md +102 -102
  40. package/guides/agents/content.md +94 -94
  41. package/guides/agents/cs2.md +56 -56
  42. package/guides/agents/data.md +86 -86
  43. package/guides/agents/design.md +77 -77
  44. package/guides/agents/frontend.md +91 -91
  45. package/guides/agents/infrastructure.md +81 -81
  46. package/guides/agents/setup.md +272 -272
  47. package/guides/agents/verify.md +119 -119
  48. package/guides/agents/viz.md +106 -106
  49. package/guides/orchestration-epic.md +17 -17
  50. package/hooks/require-work-item-on-commit.js +69 -4
  51. package/package.json +1 -1
  52. package/scripts/gen-skill-descriptions.mjs +190 -0
  53. package/scripts/self-test.mjs +1459 -1459
  54. package/skills/architecture-reviewer/SKILL.md +1 -11
  55. package/skills/behavior-audit/SKILL.md +1 -1
  56. package/skills/behavior-audit/agents/openai.yaml +4 -4
  57. package/skills/brochure/SKILL.md +1 -1
  58. package/skills/brochurify/SKILL.md +1 -1
  59. package/skills/build/SKILL.md +1 -1
  60. package/skills/channel-formatter/SKILL.md +1 -1
  61. package/skills/clean-code-analyzer/SKILL.md +1 -6
  62. package/skills/co-develop/SKILL.md +1 -1
  63. package/skills/collab/SKILL.md +1 -1
  64. package/skills/convert/SKILL.md +1 -1
  65. package/skills/deploy/SKILL.md +1 -1
  66. package/skills/design/SKILL.md +1 -1
  67. package/skills/edit/SKILL.md +1 -1
  68. package/skills/env/SKILL.md +1 -1
  69. package/skills/extract-verifier-rules/SKILL.md +1 -1
  70. package/skills/fixit/SKILL.md +1 -1
  71. package/skills/fs-mcp/SKILL.md +1 -1
  72. package/skills/handoff/SKILL.md +1 -1
  73. package/skills/help/SKILL.md +1 -1
  74. package/skills/housekeeping/SKILL.md +1 -1
  75. package/skills/lifeai-brochure-author/SKILL.md +1 -1
  76. package/skills/new-model/SKILL.md +1 -7
  77. package/skills/onramp/SKILL.md +1 -1
  78. package/skills/overnight/SKILL.md +1 -1
  79. package/skills/package-design/SKILL.md +1 -4
  80. package/skills/pattern-advisor/SKILL.md +1 -6
  81. package/skills/pattern-refactoring-guide/SKILL.md +1 -4
  82. package/skills/plan/SKILL.md +1 -1
  83. package/skills/preplan/SKILL.md +1 -1
  84. package/skills/prototype/SKILL.md +1 -1
  85. package/skills/refactor/SKILL.md +1 -1
  86. package/skills/regen-media/SKILL.md +1 -1
  87. package/skills/release/SKILL.md +1 -1
  88. package/skills/report/SKILL.md +1 -1
  89. package/skills/review/SKILL.md +1 -1
  90. package/skills/rpms-filemap/SKILL.md +1 -1
  91. package/skills/self-test/SKILL.md +1 -1
  92. package/skills/solid-validator/SKILL.md +1 -6
  93. package/skills/status/SKILL.md +1 -1
  94. package/skills/terminal-config/SKILL.md +1 -1
  95. package/skills/testing-strategy/SKILL.md +1 -6
  96. package/skills/tests/onramp.test.json +101 -101
  97. package/skills/tests/rdc-env.test.json +12 -12
  98. package/skills/tests/rdc-new-model.test.json +12 -12
  99. package/skills/tests/rdc-refactor.test.json +29 -29
  100. package/skills/tests/rdc-regen-media.test.json +29 -29
  101. package/skills/watch/SKILL.md +1 -1
  102. package/skills/workitems/SKILL.md +1 -1
  103. package/tests/clauth-plugin-postinstall.test.mjs +14 -1
  104. package/tests/help-surface.test.mjs +2 -2
  105. package/tests/require-work-item-on-commit.test.mjs +54 -0
  106. package/commands/open.md +0 -84
  107. 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)`);