@hybridlabor-api/aos 4.2.0-beta.0 → 4.3.0

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 (133) hide show
  1. package/.agents/graph.md +3 -1
  2. package/.agents/nodes.json +4 -2
  3. package/.claude/workflows/startcycle-dispatch.mjs +18 -5
  4. package/.claude/workflows/teamwork-dispatch.mjs +287 -0
  5. package/CLAUDE.md +37 -84
  6. package/CODEX.md +31 -12
  7. package/GEMINI.md +51 -58
  8. package/README.de.md +13 -12
  9. package/README.md +13 -12
  10. package/README.pt.md +13 -12
  11. package/THIRD_PARTY_NOTICES.md +104 -0
  12. package/docs/skills_table.md +1 -0
  13. package/installer.js +27 -10
  14. package/package.json +6 -1
  15. package/scripts/validate-skills.mjs +402 -0
  16. package/skills/basic/bdbmediastorm/SKILL.md +7 -5
  17. package/skills/basic/godmode-engineering/SKILL.md +1 -1
  18. package/skills/basic/godmode-shipping/SKILL.md +1 -1
  19. package/skills/basic/startcycle/SKILL.md +3 -1
  20. package/skills/basic/startcycle-graph/SKILL.md +18 -2
  21. package/skills/basic/startcycle-graph-user/SKILL.md +3 -1
  22. package/skills/basic/teamwork-preview/SKILL.md +209 -0
  23. package/skills/bdbrainstorm/SKILL.md +4 -3
  24. package/skills/github-repo/SKILL.md +1 -0
  25. package/skills/global_config/agent-tool-builder/SKILL.md +5 -4
  26. package/skills/global_config/ai-product/SKILL.md +3 -2
  27. package/skills/global_config/ask-tim/SKILL.md +75 -6
  28. package/skills/global_config/{bdb-adobe-suite-mcp.md → bdb-adobe-suite-mcp/SKILL.md} +1 -0
  29. package/skills/global_config/{bdb-after-effects-mcp.md → bdb-after-effects-mcp/SKILL.md} +1 -0
  30. package/skills/global_config/{bdb-blender-mcp.md → bdb-blender-mcp/SKILL.md} +1 -0
  31. package/skills/global_config/{bdb-computer-use-mcp.md → bdb-computer-use-mcp/SKILL.md} +1 -0
  32. package/skills/global_config/{bdb-davinci-mcp.md → bdb-davinci-mcp/SKILL.md} +1 -0
  33. package/skills/global_config/bdb-ecosystem-health/SKILL.md +3 -3
  34. package/skills/global_config/{bdb-grandma3-mcp.md → bdb-grandma3-mcp/SKILL.md} +1 -0
  35. package/skills/global_config/{bdb-memb-mcp.md → bdb-memb-mcp/SKILL.md} +10 -0
  36. package/skills/global_config/{bdb-resolume-mcp.md → bdb-resolume-mcp/SKILL.md} +1 -0
  37. package/skills/global_config/{bdb-rhino-mcp.md → bdb-rhino-mcp/SKILL.md} +1 -0
  38. package/skills/global_config/{bdb-touchdesigner-mcp.md → bdb-touchdesigner-mcp/SKILL.md} +1 -0
  39. package/skills/global_config/{bdb-unreal-mcp.md → bdb-unreal-mcp/SKILL.md} +1 -0
  40. package/skills/global_config/{bdb-vectorworks-mcp.md → bdb-vectorworks-mcp/SKILL.md} +1 -0
  41. package/skills/global_config/bdbresilience/SKILL.md +216 -0
  42. package/skills/global_config/bdbresilience/contracts/nodes-integration.md +225 -0
  43. package/skills/global_config/bdbresilience/references/cicd-triage.md +179 -0
  44. package/skills/global_config/bdbresilience/references/distributed-locking.md +235 -0
  45. package/skills/global_config/bdbresilience/references/error-recovery.md +210 -0
  46. package/skills/global_config/bdbresilience/references/two-phase-go-gate.md +151 -0
  47. package/skills/global_config/bdbsaashost/SKILL.md +50 -50
  48. package/skills/global_config/browser-automation/SKILL.md +4 -4
  49. package/skills/global_config/crewai/SKILL.md +3 -2
  50. package/skills/global_config/debugger/SKILL.md +3 -6
  51. package/skills/global_config/domain-modeling/ADR-FORMAT.md +47 -0
  52. package/skills/global_config/domain-modeling/CONTEXT-FORMAT.md +60 -0
  53. package/skills/global_config/domain-modeling/SKILL.md +77 -0
  54. package/skills/global_config/git-advanced-workflows/SKILL.md +0 -1
  55. package/skills/global_config/github-actions-templates/SKILL.md +0 -2
  56. package/skills/global_config/google-sheets-automation/SKILL.md +2 -2
  57. package/skills/global_config/grill-me/SKILL.md +14 -0
  58. package/skills/global_config/grill-with-docs/SKILL.md +24 -0
  59. package/skills/global_config/grilling/SKILL.md +42 -0
  60. package/skills/global_config/neon-postgres/SKILL.md +3 -2
  61. package/skills/global_config/openwiki-skill/scripts/install_daemon.sh +55 -12
  62. package/skills/global_config/playwright-skill/SKILL.md +1 -1
  63. package/skills/global_config/posix-shell-pro/SKILL.md +0 -1
  64. package/skills/global_config/postgres-best-practices/SKILL.md +1 -1
  65. package/skills/global_config/prompt-engineering-patterns/SKILL.md +0 -1
  66. package/skills/global_config/rag-engineer/SKILL.md +4 -3
  67. package/skills/global_config/react-best-practices/SKILL.md +1 -1
  68. package/skills/global_config/remotion/SKILL.md +0 -1
  69. package/skills/global_config/seo/SKILL.md +6 -41
  70. package/skills/global_config/systematic-debugging/CREATION-LOG.md +1 -1
  71. package/skills/global_config/systematic-debugging/root-cause-tracing.md +1 -1
  72. package/skills/global_config/turborepo-caching/SKILL.md +0 -1
  73. package/skills/global_config/using-neon/SKILL.md +1 -47
  74. package/skills/global_config/vector-database-engineer/SKILL.md +0 -1
  75. package/skills/global_config/web-artifacts-builder/LICENSE.txt +1 -1
  76. package/skills/global_config/web-artifacts-builder/SKILL.md +1 -1
  77. package/skills/global_config/webapp-testing/LICENSE.txt +1 -1
  78. package/skills/global_config/webapp-testing/SKILL.md +1 -1
  79. package/.agents/skills/firecrawl/SKILL.md +0 -149
  80. package/.agents/skills/firecrawl/rules/install.md +0 -82
  81. package/.agents/skills/firecrawl/rules/security.md +0 -26
  82. package/.agents/skills/firecrawl-agent/SKILL.md +0 -58
  83. package/.agents/skills/firecrawl-build/SKILL.md +0 -39
  84. package/.agents/skills/firecrawl-build-interact/SKILL.md +0 -68
  85. package/.agents/skills/firecrawl-build-onboarding/SKILL.md +0 -103
  86. package/.agents/skills/firecrawl-build-onboarding/references/auth-flow.md +0 -39
  87. package/.agents/skills/firecrawl-build-onboarding/references/project-setup.md +0 -20
  88. package/.agents/skills/firecrawl-build-onboarding/references/sdk-installation.md +0 -17
  89. package/.agents/skills/firecrawl-build-scrape/SKILL.md +0 -69
  90. package/.agents/skills/firecrawl-build-search/SKILL.md +0 -69
  91. package/.agents/skills/firecrawl-crawl/SKILL.md +0 -59
  92. package/.agents/skills/firecrawl-download/SKILL.md +0 -70
  93. package/.agents/skills/firecrawl-interact/SKILL.md +0 -84
  94. package/.agents/skills/firecrawl-map/SKILL.md +0 -51
  95. package/.agents/skills/firecrawl-scrape/SKILL.md +0 -69
  96. package/.agents/skills/firecrawl-search/SKILL.md +0 -60
  97. package/mcps/RhinoMCP/cc-plugin/.claude/settings.json +0 -10
  98. package/mcps/after-effects-mcp/build/index.js +0 -840
  99. package/mcps/after-effects-mcp/build/scripts/applyEffect.jsx +0 -153
  100. package/mcps/after-effects-mcp/build/scripts/applyEffectTemplate.jsx +0 -218
  101. package/mcps/after-effects-mcp/build/scripts/createComposition.jsx +0 -71
  102. package/mcps/after-effects-mcp/build/scripts/createShapeLayer.jsx +0 -147
  103. package/mcps/after-effects-mcp/build/scripts/createSolidLayer.jsx +0 -114
  104. package/mcps/after-effects-mcp/build/scripts/createTextLayer.jsx +0 -115
  105. package/mcps/after-effects-mcp/build/scripts/getLayerInfo.jsx +0 -192
  106. package/mcps/after-effects-mcp/build/scripts/getProjectInfo.jsx +0 -90
  107. package/mcps/after-effects-mcp/build/scripts/listCompositions.jsx +0 -50
  108. package/mcps/after-effects-mcp/build/scripts/mcp-bridge-auto.jsx +0 -1773
  109. package/mcps/after-effects-mcp/build/scripts/setLayerProperties.jsx +0 -160
  110. package/mcps/bdb-remoteos-mcp/queue.db +0 -0
  111. package/mcps/bdb-remoteos-mcp/src/bdb_remoteos_mcp/__pycache__/__init__.cpython-312.pyc +0 -0
  112. package/mcps/bdb-remoteos-mcp/src/bdb_remoteos_mcp/__pycache__/incus_client.cpython-312.pyc +0 -0
  113. package/mcps/bdb-remoteos-mcp/src/bdb_remoteos_mcp/__pycache__/main.cpython-312.pyc +0 -0
  114. package/mcps/bdb-remoteos-mcp/src/bdb_remoteos_mcp/__pycache__/queue.cpython-312.pyc +0 -0
  115. package/mcps/bdb-remoteos-mcp/src/bdb_remoteos_mcp/__pycache__/schemas.cpython-312.pyc +0 -0
  116. package/mcps/bdb-remoteos-mcp/src/bdb_remoteos_mcp/__pycache__/server.cpython-312.pyc +0 -0
  117. package/mcps/bdb-remoteos-mcp/src/bdb_remoteos_mcp/__pycache__/webhook.cpython-312.pyc +0 -0
  118. package/mcps/bdb-remoteos-mcp/tests/__pycache__/__init__.cpython-312.pyc +0 -0
  119. package/mcps/bdb-remoteos-mcp/tests/__pycache__/mock_incus.cpython-312.pyc +0 -0
  120. package/mcps/bdb-remoteos-mcp/tests/__pycache__/test_mcp_server.cpython-312-pytest-9.1.1.pyc +0 -0
  121. package/mcps/bdb-remoteos-mcp/tests/__pycache__/test_security_redteam.cpython-312-pytest-9.1.1.pyc +0 -0
  122. package/mcps/bdb-remoteos-mcp/tests/__pycache__/test_webhook.cpython-312-pytest-9.1.1.pyc +0 -0
  123. package/mcps/computer-use-mcp/dist/client.d.ts +0 -150
  124. package/mcps/computer-use-mcp/dist/client.js +0 -136
  125. package/mcps/computer-use-mcp/dist/entrypoint.d.ts +0 -16
  126. package/mcps/computer-use-mcp/dist/entrypoint.js +0 -26
  127. package/mcps/computer-use-mcp/dist/native.d.ts +0 -212
  128. package/mcps/computer-use-mcp/dist/native.js +0 -50
  129. package/mcps/computer-use-mcp/dist/server.d.ts +0 -32
  130. package/mcps/computer-use-mcp/dist/server.js +0 -342
  131. package/mcps/computer-use-mcp/dist/session.d.ts +0 -101
  132. package/mcps/computer-use-mcp/dist/session.js +0 -2372
  133. package/skills/bdbsaastraining/scripts/__pycache__/build_profile.cpython-314.pyc +0 -0
@@ -0,0 +1,402 @@
1
+ #!/usr/bin/env node
2
+ // Validates every skill in skills/ against the contract in AGENTS.md.
3
+ // Zero dependencies on purpose: CI runs this with no install step, and it must
4
+ // work on any harness, not just the one that happens to ship a hook runner.
5
+
6
+ import { readFileSync, readdirSync, statSync, existsSync } from 'node:fs';
7
+ import { join, dirname, basename, relative, resolve } from 'node:path';
8
+ import { fileURLToPath } from 'node:url';
9
+ import assert from 'node:assert/strict';
10
+
11
+ const REPO = resolve(dirname(fileURLToPath(import.meta.url)), '..');
12
+ const SKILLS = join(REPO, 'skills');
13
+
14
+ const CATEGORIES = new Set([
15
+ 'design-ui-ux', 'engineering-method', 'media-eventtech',
16
+ 'bdb-core', 'saas-ops', 'library',
17
+ ]);
18
+ const REQUIRED = ['name', 'description', 'category'];
19
+
20
+ // Names that are stand-ins or service accounts rather than a person. The rule
21
+ // is about leaking a human's username; `/home/agent` on the SaaS fleet is a
22
+ // role account and is meant to be written down.
23
+ const PLACEHOLDER_USERS = new Set([
24
+ 'username', '<username>', '{username}', '$USER', 'USER', 'user', 'you', 'your-username',
25
+ 'john', 'jane', 'example', 'me', 'name', '<user>', 'ubuntu', 'runner', 'YOUR_NAME',
26
+ 'agent', 'root', 'admin', 'deploy', 'app', 'node', 'git', 'www-data', 'service',
27
+ ]);
28
+
29
+ // ---------------------------------------------------------------- YAML scanner
30
+ // Not a general YAML parser — it resolves scalar boundaries, which is the only
31
+ // thing needed to tell a real key from text absorbed into the value above it.
32
+ // That distinction is the whole point: a `grep "^category:"` says these files
33
+ // are fine, because the line is physically present and semantically swallowed.
34
+
35
+ function findClosingQuote(s, from, q) {
36
+ for (let i = from; i < s.length; i++) {
37
+ if (s[i] !== q) continue;
38
+ if (q === "'" && s[i + 1] === "'") { i++; continue; } // '' escapes a quote
39
+ if (q === '"' && s[i - 1] === '\\') continue;
40
+ return i;
41
+ }
42
+ return -1;
43
+ }
44
+
45
+ const indentOf = (l) => l.length - l.trimStart().length;
46
+ const KEY_RE = /^([A-Za-z_][\w.-]*)\s*:(.*)$/;
47
+
48
+ export function scanFrontmatter(text) {
49
+ const lines = text.split(/\r?\n/);
50
+ if (lines[0]?.trim() !== '---') return { ok: false, reason: 'missing', keys: [], issues: [] };
51
+
52
+ let close = -1;
53
+ for (let i = 1; i < lines.length; i++) {
54
+ if (lines[i].trim() === '---') { close = i; break; }
55
+ }
56
+ if (close === -1) return { ok: false, reason: 'unterminated', keys: [], issues: [] };
57
+
58
+ const body = lines.slice(1, close);
59
+ const keys = [];
60
+ const issues = [];
61
+ let i = 0;
62
+
63
+ while (i < body.length) {
64
+ const line = body[i];
65
+ if (line.trim() === '' || line.trimStart().startsWith('#')) { i++; continue; }
66
+ if (indentOf(line) > 0) { i++; continue; }
67
+
68
+ const m = KEY_RE.exec(line);
69
+ if (!m) { i++; continue; }
70
+
71
+ const key = m[1];
72
+ const rest = m[2];
73
+ const restT = rest.trim();
74
+ const start = i;
75
+ const consumed = [rest];
76
+ let swallowed = [];
77
+
78
+ const eatIndented = (allowBlank) => {
79
+ i++;
80
+ while (i < body.length) {
81
+ const l = body[i];
82
+ if (l.trim() === '') { if (!allowBlank) break; consumed.push(l); i++; continue; }
83
+ if (indentOf(l) === 0) break;
84
+ consumed.push(l); i++;
85
+ }
86
+ };
87
+
88
+ if (/^[|>][-+]?\d*$/.test(restT)) {
89
+ eatIndented(true); // block scalar
90
+ } else if (restT === '') {
91
+ eatIndented(true); // nested mapping / empty
92
+ } else if (restT[0] === "'" || restT[0] === '"') {
93
+ const q = restT[0];
94
+ const offset = rest.indexOf(q);
95
+ if (findClosingQuote(rest, offset + 1, q) !== -1) {
96
+ i++; // closes on its own line
97
+ } else {
98
+ i++;
99
+ let closed = false;
100
+ while (i < body.length) {
101
+ const l = body[i];
102
+ consumed.push(l);
103
+ i++;
104
+ if (indentOf(l) === 0 && KEY_RE.test(l)) swallowed.push(KEY_RE.exec(l)[1]);
105
+ if (findClosingQuote(l, 0, q) !== -1) { closed = true; break; }
106
+ }
107
+ if (!closed) issues.push({ code: 'E-YAML02', key, msg: `quoted value for \`${key}\` is never closed` });
108
+ }
109
+ } else {
110
+ eatIndented(false); // plain scalar
111
+ }
112
+
113
+ if (swallowed.length) {
114
+ issues.push({
115
+ code: 'E-YAML03', key,
116
+ msg: `the quoted value of \`${key}\` swallows ${swallowed.map((k) => `\`${k}\``).join(', ')} — ` +
117
+ `${swallowed.length > 1 ? 'those keys are' : 'that key is'} not parsed at all`,
118
+ });
119
+ }
120
+
121
+ keys.push({
122
+ key,
123
+ line: start + 2, // +1 for the opening ---, +1 for 1-indexing
124
+ multiline: consumed.length > 1,
125
+ value: consumed.join(' ').trim().replace(/\s+/g, ' '),
126
+ });
127
+ }
128
+
129
+ return { ok: true, keys, issues, endLine: close + 1 };
130
+ }
131
+
132
+ function unquote(v) {
133
+ const t = v.trim();
134
+ if (t.length > 1 && (t[0] === '"' || t[0] === "'") && t.at(-1) === t[0]) return t.slice(1, -1).trim();
135
+ return t;
136
+ }
137
+
138
+ // ------------------------------------------------------------------ collectors
139
+
140
+ function walk(dir, out = []) {
141
+ for (const e of readdirSync(dir, { withFileTypes: true })) {
142
+ if (e.name === 'node_modules' || e.name.startsWith('.')) continue;
143
+ const p = join(dir, e.name);
144
+ if (e.isDirectory()) walk(p, out);
145
+ else out.push(p);
146
+ }
147
+ return out;
148
+ }
149
+
150
+ function collect() {
151
+ const files = walk(SKILLS);
152
+ const skills = files.filter((f) => basename(f) === 'SKILL.md');
153
+ // A .md sitting directly in a category dir is a skill nobody can load: every
154
+ // harness discovers skills as <dir>/SKILL.md.
155
+ const strays = files.filter((f) => {
156
+ if (basename(f) === 'SKILL.md' || !f.endsWith('.md')) return false;
157
+ const rel = relative(SKILLS, f);
158
+ return rel.split('/').length === 2;
159
+ });
160
+ return { skills, strays };
161
+ }
162
+
163
+ // ---------------------------------------------------------------------- checks
164
+
165
+ // A skill that documents the no-usernames rule has to be able to show a
166
+ // counter-example. `<!-- validate-skills-ignore -->` on the line, or the line
167
+ // above, says this one is deliberate.
168
+ const IGNORE = 'validate-skills-ignore';
169
+
170
+ function checkLeakedPaths(text, file, findings) {
171
+ const patterns = [/\/Users\/([^/\s`'")\],]+)/g, /\/home\/([^/\s`'")\],]+)/g, /C:\\Users\\([^\\\s`'")\],]+)/g];
172
+ const lines = text.split(/\r?\n/);
173
+ for (const re of patterns) {
174
+ for (let n = 0; n < lines.length; n++) {
175
+ if (lines[n].includes(IGNORE) || (n > 0 && lines[n - 1].includes(IGNORE))) continue;
176
+ for (const m of lines[n].matchAll(re)) {
177
+ const who = m[1].replace(/[<>{}]/g, '');
178
+ if (PLACEHOLDER_USERS.has(m[1]) || PLACEHOLDER_USERS.has(who)) continue;
179
+ findings.push({
180
+ level: 'error', code: 'E-PATH01', file, line: n + 1,
181
+ msg: `absolute home path leaks a username: \`${m[0]}\` — use \`~\` or \`$HOME\``,
182
+ });
183
+ }
184
+ }
185
+ }
186
+ }
187
+
188
+ function checkLocalRefs(text, skillDir, file, findings) {
189
+ const re = /`([a-zA-Z0-9_@-]+\/[a-zA-Z0-9_./@-]+\.(?:md|py|mjs|js|ts|sh|json))`/g;
190
+ const seen = new Set();
191
+ for (const m of text.matchAll(re)) {
192
+ const p = m[1];
193
+ if (seen.has(p) || p.startsWith('~') || p.startsWith('.') || p.includes('://')) continue;
194
+ if (!/^(references|scripts|assets|rules|contracts|resources|templates)\//.test(p)) continue;
195
+ seen.add(p);
196
+ if (!existsSync(join(skillDir, p))) {
197
+ findings.push({ level: 'warn', code: 'W-REF01', file, msg: `references \`${p}\`, which does not exist in this skill` });
198
+ }
199
+ }
200
+ }
201
+
202
+ // Only hyphenated `/slash-names` are checked. Single words in that position are
203
+ // nearly always something else — a route, a temp path, a pipeline stage in
204
+ // prose, a harness builtin — and flagging them buries the real hits.
205
+ const HARNESS_BUILTINS = new Set([
206
+ 'code-review', 'plan-mode', 'add-dir', 'bug-report', 'output-style', 'export-conversation',
207
+ ]);
208
+
209
+ function checkSkillRefs(text, file, known, self, findings) {
210
+ const seen = new Set();
211
+ for (const m of text.matchAll(/`\/([a-z][a-z0-9]*(?:-[a-z0-9]+)+)`/g)) {
212
+ const name = m[1];
213
+ if (seen.has(name) || name === self || known.has(name) || HARNESS_BUILTINS.has(name)) continue;
214
+ seen.add(name);
215
+ findings.push({ level: 'warn', code: 'W-REF02', file, msg: `references skill \`/${name}\`, which does not exist` });
216
+ }
217
+ }
218
+
219
+ function validate() {
220
+ const { skills, strays } = collect();
221
+ const known = new Set(skills.map((f) => basename(dirname(f))));
222
+ const findings = [];
223
+
224
+ for (const file of skills) {
225
+ const rel = relative(REPO, file);
226
+ const dir = dirname(file);
227
+ const dirName = basename(dir);
228
+ const text = readFileSync(file, 'utf8');
229
+ const fm = scanFrontmatter(text);
230
+
231
+ if (!fm.ok) {
232
+ findings.push({
233
+ level: 'error', code: 'E-FM01', file: rel,
234
+ msg: fm.reason === 'missing' ? 'no YAML frontmatter block' : 'frontmatter block is never closed',
235
+ });
236
+ continue;
237
+ }
238
+
239
+ for (const iss of fm.issues) findings.push({ level: 'error', code: iss.code, file: rel, msg: iss.msg });
240
+
241
+ const byKey = new Map(fm.keys.map((k) => [k.key, k]));
242
+
243
+ for (const req of REQUIRED) {
244
+ if (!byKey.has(req)) {
245
+ findings.push({ level: 'error', code: 'E-FM02', file: rel, msg: `missing required key \`${req}\`` });
246
+ }
247
+ }
248
+
249
+ const name = byKey.get('name');
250
+ if (name && unquote(name.value) !== dirName) {
251
+ findings.push({
252
+ level: 'error', code: 'E-FM04', file: rel, line: name.line,
253
+ msg: `\`name: ${unquote(name.value)}\` does not match its directory \`${dirName}\``,
254
+ });
255
+ }
256
+
257
+ const desc = byKey.get('description');
258
+ if (desc && unquote(desc.value) === '') {
259
+ findings.push({ level: 'error', code: 'E-FM05', file: rel, line: desc.line, msg: 'description is empty' });
260
+ }
261
+
262
+ const cat = byKey.get('category');
263
+ if (cat) {
264
+ const value = unquote(cat.value);
265
+ if (cat.multiline) {
266
+ findings.push({
267
+ level: 'error', code: 'E-YAML01', file: rel, line: cat.line,
268
+ msg: `\`category\` absorbs the line below it and parses as "${value.slice(0, 60)}${value.length > 60 ? '…' : ''}" ` +
269
+ '— quote the description above it so the value ends where you think it does',
270
+ });
271
+ } else if (!CATEGORIES.has(value)) {
272
+ findings.push({
273
+ level: 'error', code: 'E-FM03', file: rel, line: cat.line,
274
+ msg: `\`category: ${value}\` is not one of: ${[...CATEGORIES].join(', ')}`,
275
+ });
276
+ }
277
+ }
278
+
279
+ checkLeakedPaths(text, rel, findings);
280
+ checkLocalRefs(text, dir, rel, findings);
281
+ checkSkillRefs(text, rel, known, dirName, findings);
282
+
283
+ // A skill ships more than its SKILL.md, and a leaked path in a reference
284
+ // file is just as published as one in the entry point.
285
+ for (const aux of walk(dir)) {
286
+ if (aux === file || !/\.(md|py|mjs|cjs|js|ts|sh|json|ya?ml)$/.test(aux)) continue;
287
+ checkLeakedPaths(readFileSync(aux, 'utf8'), relative(REPO, aux), findings);
288
+ }
289
+ }
290
+
291
+ for (const file of strays) {
292
+ const rel = relative(REPO, file);
293
+ const fm = scanFrontmatter(readFileSync(file, 'utf8'));
294
+ if (!fm.ok || !fm.keys.some((k) => k.key === 'name')) continue;
295
+ findings.push({
296
+ level: 'error', code: 'E-STRAY01', file: rel,
297
+ msg: 'skill frontmatter in a bare .md file — no harness will discover this. ' +
298
+ `Move it to \`${relative(REPO, join(dirname(file), basename(file, '.md'), 'SKILL.md'))}\``,
299
+ });
300
+ }
301
+
302
+ return { findings, counts: { skills: skills.length, strays: strays.length } };
303
+ }
304
+
305
+ // ---------------------------------------------------------------------- report
306
+
307
+ function report(findings, counts, { json, quiet, strict }) {
308
+ const errors = findings.filter((f) => f.level === 'error');
309
+ const warns = findings.filter((f) => f.level === 'warn');
310
+
311
+ if (json) {
312
+ console.log(JSON.stringify({ ok: errors.length === 0 && (!strict || warns.length === 0), counts, findings }, null, 2));
313
+ return errors.length || (strict && warns.length) ? 1 : 0;
314
+ }
315
+
316
+ const tty = process.stdout.isTTY && !process.env.NO_COLOR;
317
+ const c = (code, s) => (tty ? `\x1b[${code}m${s}\x1b[0m` : s);
318
+ const red = (s) => c(31, s), yellow = (s) => c(33, s), green = (s) => c(32, s), dim = (s) => c(2, s);
319
+
320
+ const show = strict ? findings : (quiet ? errors : findings);
321
+ const byFile = new Map();
322
+ for (const f of show) {
323
+ if (!byFile.has(f.file)) byFile.set(f.file, []);
324
+ byFile.get(f.file).push(f);
325
+ }
326
+
327
+ for (const [file, list] of byFile) {
328
+ console.log(`\n${file}`);
329
+ for (const f of list) {
330
+ const tag = f.level === 'error' ? red('error') : yellow(' warn');
331
+ const at = f.line ? dim(`:${f.line}`) : '';
332
+ console.log(` ${tag} ${dim(f.code)}${at} ${f.msg}`);
333
+ }
334
+ }
335
+
336
+ const summary = `${counts.skills} skills checked · ${errors.length} error(s) · ${warns.length} warning(s)`;
337
+ console.log('');
338
+ if (errors.length === 0 && warns.length === 0) console.log(green(`✓ ${summary}`));
339
+ else if (errors.length === 0) console.log(yellow(`! ${summary}`));
340
+ else console.log(red(`✗ ${summary}`));
341
+
342
+ return errors.length || (strict && warns.length) ? 1 : 0;
343
+ }
344
+
345
+ // -------------------------------------------------------------------- selftest
346
+
347
+ function selftest() {
348
+ // The bug that shipped: an indented line folds into the value above it, so
349
+ // `category` parses as "library used by 60% ..." while grep still sees the key.
350
+ const folded = scanFrontmatter([
351
+ '---', 'name: crewai', 'description: Expert in CrewAI - the leading framework',
352
+ 'category: library', ' used by 60% of Fortune 500 companies.', '---',
353
+ ].join('\n'));
354
+ const cat = folded.keys.find((k) => k.key === 'category');
355
+ assert.ok(cat.multiline, 'folded category must be detected as multiline');
356
+ assert.equal(cat.value, 'library used by 60% of Fortune 500 companies.');
357
+
358
+ // The other shape: an unclosed quote eats the keys below it entirely.
359
+ const eaten = scanFrontmatter([
360
+ '---', "description: 'Debugging specialist for errors", 'category: library',
361
+ '', ' behavior. Use proactively.', '', " '", 'risk: safe', '---',
362
+ ].join('\n'));
363
+ assert.ok(!eaten.keys.some((k) => k.key === 'category'), 'swallowed key must not count as present');
364
+ assert.ok(eaten.issues.some((i) => i.code === 'E-YAML03'), 'swallowing must be reported');
365
+ assert.ok(eaten.keys.some((k) => k.key === 'risk'), 'parsing must resume after the closing quote');
366
+
367
+ // Legitimate YAML must not trip any of it.
368
+ const clean = scanFrontmatter([
369
+ '---', 'name: ask-tim', 'description: >-', ' A folded block scalar',
370
+ ' spanning several lines.', 'category: bdb-core',
371
+ 'metadata:', ' author: firecrawl', ' version: "0.1.0"', '---',
372
+ ].join('\n'));
373
+ const cleanCat = clean.keys.find((k) => k.key === 'category');
374
+ assert.equal(cleanCat.value, 'bdb-core');
375
+ assert.ok(!cleanCat.multiline, 'a clean category is single-line');
376
+ assert.ok(clean.keys.find((k) => k.key === 'description').multiline, 'block scalar spans lines');
377
+ assert.ok(clean.keys.some((k) => k.key === 'metadata'), 'nested mapping is a key');
378
+ assert.ok(!clean.keys.some((k) => k.key === 'author'), 'nested keys are not top-level');
379
+ assert.equal(clean.issues.length, 0);
380
+
381
+ // A quoted value that closes on its own line is fine.
382
+ const quoted = scanFrontmatter(['---', 'name: x', 'description: "it\'s fine: really"', 'category: library', '---'].join('\n'));
383
+ assert.equal(quoted.issues.length, 0);
384
+ assert.equal(quoted.keys.find((k) => k.key === 'category').value, 'library');
385
+
386
+ assert.equal(scanFrontmatter('# no frontmatter').ok, false);
387
+ assert.equal(scanFrontmatter('---\nname: x\n').reason, 'unterminated');
388
+
389
+ console.log('✓ selftest passed');
390
+ }
391
+
392
+ // ------------------------------------------------------------------------ main
393
+
394
+ const argv = process.argv.slice(2);
395
+ if (argv.includes('--selftest')) { selftest(); process.exit(0); }
396
+
397
+ const { findings, counts } = validate();
398
+ process.exit(report(findings, counts, {
399
+ json: argv.includes('--json'),
400
+ quiet: argv.includes('--quiet'),
401
+ strict: argv.includes('--strict'),
402
+ }));
@@ -26,9 +26,11 @@ Ideation must never be performed in isolation. Spawn specialized subagents to an
26
26
 
27
27
  ---
28
28
 
29
- ## 2. Interactive `/grill-me` Technical Interview
29
+ ## 2. Interactive Technical Interview
30
30
 
31
- Before drafting signal flow diagrams or system configs, execute a mandatory `/grill-me` interactive interview. Deeply challenge the user's technical assumptions and hardware readiness by asking targeted questions:
31
+ Before drafting signal flow diagrams or system configs, **invoke the `grill-with-docs` skill** (or `grill-me` when there is no working directory) and run it to completion. Those skills hold the interview protocol — design tree, frontier rounds, numbered questions each with a recommended answer — and it is not restated here.
32
+
33
+ What this domain adds to that protocol: deeply challenge the user's technical assumptions and hardware readiness. The frontier questions for a show-control build are:
32
34
 
33
35
  * **Signal & Network Protocols:**
34
36
  - What protocols govern data movement? (OSC, Art-Net, sACN, MIDI, SMPTE Timecode, NDI)?
@@ -48,7 +50,7 @@ Before drafting signal flow diagrams or system configs, execute a mandatory `/gr
48
50
 
49
51
  ## 3. Target Directory & Scaffolding
50
52
 
51
- After aligning on system architecture through the `/grill-me` process:
53
+ After aligning on system architecture through the grilling interview:
52
54
  1. **Confirm Output Directory:** Ask the user: *"In which project directory should the output show-control plan and architecture files be stored?"*
53
55
  2. **Scaffold Foundational Files:** Once confirmed, write the core show specification files (`agent.md`, `signal-flow.md`, `network-patch.json`, `failover-matrix.md`).
54
56
 
@@ -94,7 +96,7 @@ BDB MediaStorm is the master ideation and brainstorming engine for live show-con
94
96
  - **Exclude:** Do not use for generating video timelines or 3D meshes.
95
97
 
96
98
  ## Core Process
97
- 1. Run an interactive `/grill-me` session to challenge assumptions about protocols, hardware, and bandwidth.
99
+ 1. Run `grill-with-docs` (or `grill-me`) to challenge assumptions about protocols, hardware, and bandwidth.
98
100
  2. Scaffold foundational files (`agent.md`, `signal-flow.md`, `network-patch.json`).
99
101
  3. Generate a strict Mermaid.js signal flow diagram mapping all protocols.
100
102
  4. Document a main/backup redundancy and failover matrix.
@@ -115,7 +117,7 @@ BDB MediaStorm is the master ideation and brainstorming engine for live show-con
115
117
 
116
118
  ## Verification
117
119
 
118
- - [ ] `/grill-me` session was completed with answers regarding protocols and bandwidth.
120
+ - [ ] The grilling interview was completed with answers regarding protocols and bandwidth.
119
121
  - [ ] Output includes a Mermaid.js signal flow diagram.
120
122
  - [ ] A dedicated failover/blackout mechanism is documented.
121
123
 
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: godmode-engineering
3
3
  description: BDB Engineering Godmode, enforcing strict Domain-Driven Design, TypeScript strictness, and Clean Architecture. Use when implementing backend/fullstack features or running the systematic 5-step debugging triage.
4
- category: bdb-core
4
+ category: engineering-method
5
5
  ---
6
6
 
7
7
  # ⚙️ BDB Engineering Godmode
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: godmode-shipping
3
3
  description: BDB Shipping Godmode, the final gatekeeper for production releases. Use when running pre-launch checks, feature-flag rollouts, or rollback planning under Spec-Driven Development before a release ships.
4
- category: bdb-core
4
+ category: engineering-method
5
5
  disable-model-invocation: true
6
6
  ---
7
7
 
@@ -88,7 +88,9 @@ Run only the streams the goal actually needs. A plain backend feature does not n
88
88
  > does the work `startcycle-graph`'s script does for you: extract the
89
89
  > `--skill=` flag(s) from the invocation text before anything else runs,
90
90
  > confirm each name resolves to a real `SKILL.md` (under
91
- > `~/.claude/skills/<name>/` or this project's own `skills/` tree) — stop
91
+ > any harness's global skills directory — `~/.claude/skills/<name>/`,
92
+ > `~/.agents/skills/`, `~/.codex/skills/`, `~/.cursor/skills/` or `~/.roo/skills/`
93
+ > — or this project's own `skills/` tree) — stop
92
94
  > and tell the user if one doesn't, don't silently proceed without it —
93
95
  > note the validated list in `00_execution_plan.md`, and include it as a
94
96
  > **hard requirement, not a suggestion** in each Build stream's dispatch
@@ -56,8 +56,24 @@ rather than guessing one.
56
56
  (repeatable, quote a name with spaces) forces that skill into this run as a
57
57
  hard requirement for the build nodes, validated to exist before anything
58
58
  else runs — this is how you make the pipeline use your own private skill
59
- that isn't part of `.agents/nodes.json`'s registry. See
60
- [`.agents/graph.md`](../../../.agents/graph.md)'s "Mandatory Skill
59
+ that isn't part of `.agents/nodes.json`'s registry.
60
+
61
+ `<name>` is the **exact skill directory name**, not a description — `ui-component`,
62
+ not "the UI one". Two ways to find it without leaving the terminal:
63
+
64
+ - `/ask-tim` — the routing skill; start there when you know the *job* but not the name
65
+ - list the installed skills directly, if you half-remember the spelling. The installer
66
+ syncs the same set to every harness it detects, so use whichever path is yours:
67
+ `~/.claude/skills`, `~/.agents/skills`, `~/.codex/skills`, `~/.cursor/skills`, or
68
+ `~/.roo/skills`. `ls ~/.agents/skills` is the safest guess on an unknown machine —
69
+ that one is written on every install regardless of harness.
70
+
71
+ A name that does not resolve halts the run before any agent works, and the
72
+ error now lists installed near-misses rather than only saying "not found".
73
+ That is deliberate: silently running without a skill you explicitly demanded
74
+ is worse than stopping.
75
+
76
+ See [`.agents/graph.md`](../../../.agents/graph.md)'s "Mandatory Skill
61
77
  Injection" section for the full mechanics; nothing about it needs handling
62
78
  in this router file, since `args` is passed through as raw text either way.
63
79
 
@@ -36,7 +36,9 @@ with spaces) forces that skill into this run as a hard requirement — for a
36
36
  private skill of the user's own this throwaway graph would otherwise never
37
37
  know to reach for. Extract any `--skill=` flag(s) from the invocation text
38
38
  before step 1, confirm each name resolves to a real `SKILL.md` (under
39
- `~/.claude/skills/<name>/` or this project's own `skills/` tree if it has
39
+ any harness's global skills directory — `~/.claude/skills/<name>/`,
40
+ `~/.agents/skills/`, `~/.codex/skills/`, `~/.cursor/skills/`, `~/.roo/skills/` —
41
+ or this project's own `skills/` tree if it has
40
42
  one) — stop and tell the user if one doesn't, never silently proceed
41
43
  without it — and include it as a **hard requirement, not a suggestion** in
42
44
  the Plan node's and every Worker node's prompt. The Review node checks the