@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.
- package/.agents/graph.md +3 -1
- package/.agents/nodes.json +4 -2
- package/.claude/workflows/startcycle-dispatch.mjs +18 -5
- package/.claude/workflows/teamwork-dispatch.mjs +287 -0
- package/CLAUDE.md +37 -84
- package/CODEX.md +31 -12
- package/GEMINI.md +51 -58
- package/README.de.md +13 -12
- package/README.md +13 -12
- package/README.pt.md +13 -12
- package/THIRD_PARTY_NOTICES.md +104 -0
- package/docs/skills_table.md +1 -0
- package/installer.js +27 -10
- package/package.json +6 -1
- package/scripts/validate-skills.mjs +402 -0
- package/skills/basic/bdbmediastorm/SKILL.md +7 -5
- package/skills/basic/godmode-engineering/SKILL.md +1 -1
- package/skills/basic/godmode-shipping/SKILL.md +1 -1
- package/skills/basic/startcycle/SKILL.md +3 -1
- package/skills/basic/startcycle-graph/SKILL.md +18 -2
- package/skills/basic/startcycle-graph-user/SKILL.md +3 -1
- package/skills/basic/teamwork-preview/SKILL.md +209 -0
- package/skills/bdbrainstorm/SKILL.md +4 -3
- package/skills/github-repo/SKILL.md +1 -0
- package/skills/global_config/agent-tool-builder/SKILL.md +5 -4
- package/skills/global_config/ai-product/SKILL.md +3 -2
- package/skills/global_config/ask-tim/SKILL.md +75 -6
- package/skills/global_config/{bdb-adobe-suite-mcp.md → bdb-adobe-suite-mcp/SKILL.md} +1 -0
- package/skills/global_config/{bdb-after-effects-mcp.md → bdb-after-effects-mcp/SKILL.md} +1 -0
- package/skills/global_config/{bdb-blender-mcp.md → bdb-blender-mcp/SKILL.md} +1 -0
- package/skills/global_config/{bdb-computer-use-mcp.md → bdb-computer-use-mcp/SKILL.md} +1 -0
- package/skills/global_config/{bdb-davinci-mcp.md → bdb-davinci-mcp/SKILL.md} +1 -0
- package/skills/global_config/bdb-ecosystem-health/SKILL.md +3 -3
- package/skills/global_config/{bdb-grandma3-mcp.md → bdb-grandma3-mcp/SKILL.md} +1 -0
- package/skills/global_config/{bdb-memb-mcp.md → bdb-memb-mcp/SKILL.md} +10 -0
- package/skills/global_config/{bdb-resolume-mcp.md → bdb-resolume-mcp/SKILL.md} +1 -0
- package/skills/global_config/{bdb-rhino-mcp.md → bdb-rhino-mcp/SKILL.md} +1 -0
- package/skills/global_config/{bdb-touchdesigner-mcp.md → bdb-touchdesigner-mcp/SKILL.md} +1 -0
- package/skills/global_config/{bdb-unreal-mcp.md → bdb-unreal-mcp/SKILL.md} +1 -0
- package/skills/global_config/{bdb-vectorworks-mcp.md → bdb-vectorworks-mcp/SKILL.md} +1 -0
- package/skills/global_config/bdbresilience/SKILL.md +216 -0
- package/skills/global_config/bdbresilience/contracts/nodes-integration.md +225 -0
- package/skills/global_config/bdbresilience/references/cicd-triage.md +179 -0
- package/skills/global_config/bdbresilience/references/distributed-locking.md +235 -0
- package/skills/global_config/bdbresilience/references/error-recovery.md +210 -0
- package/skills/global_config/bdbresilience/references/two-phase-go-gate.md +151 -0
- package/skills/global_config/bdbsaashost/SKILL.md +50 -50
- package/skills/global_config/browser-automation/SKILL.md +4 -4
- package/skills/global_config/crewai/SKILL.md +3 -2
- package/skills/global_config/debugger/SKILL.md +3 -6
- package/skills/global_config/domain-modeling/ADR-FORMAT.md +47 -0
- package/skills/global_config/domain-modeling/CONTEXT-FORMAT.md +60 -0
- package/skills/global_config/domain-modeling/SKILL.md +77 -0
- package/skills/global_config/git-advanced-workflows/SKILL.md +0 -1
- package/skills/global_config/github-actions-templates/SKILL.md +0 -2
- package/skills/global_config/google-sheets-automation/SKILL.md +2 -2
- package/skills/global_config/grill-me/SKILL.md +14 -0
- package/skills/global_config/grill-with-docs/SKILL.md +24 -0
- package/skills/global_config/grilling/SKILL.md +42 -0
- package/skills/global_config/neon-postgres/SKILL.md +3 -2
- package/skills/global_config/openwiki-skill/scripts/install_daemon.sh +55 -12
- package/skills/global_config/playwright-skill/SKILL.md +1 -1
- package/skills/global_config/posix-shell-pro/SKILL.md +0 -1
- package/skills/global_config/postgres-best-practices/SKILL.md +1 -1
- package/skills/global_config/prompt-engineering-patterns/SKILL.md +0 -1
- package/skills/global_config/rag-engineer/SKILL.md +4 -3
- package/skills/global_config/react-best-practices/SKILL.md +1 -1
- package/skills/global_config/remotion/SKILL.md +0 -1
- package/skills/global_config/seo/SKILL.md +6 -41
- package/skills/global_config/systematic-debugging/CREATION-LOG.md +1 -1
- package/skills/global_config/systematic-debugging/root-cause-tracing.md +1 -1
- package/skills/global_config/turborepo-caching/SKILL.md +0 -1
- package/skills/global_config/using-neon/SKILL.md +1 -47
- package/skills/global_config/vector-database-engineer/SKILL.md +0 -1
- package/skills/global_config/web-artifacts-builder/LICENSE.txt +1 -1
- package/skills/global_config/web-artifacts-builder/SKILL.md +1 -1
- package/skills/global_config/webapp-testing/LICENSE.txt +1 -1
- package/skills/global_config/webapp-testing/SKILL.md +1 -1
- package/.agents/skills/firecrawl/SKILL.md +0 -149
- package/.agents/skills/firecrawl/rules/install.md +0 -82
- package/.agents/skills/firecrawl/rules/security.md +0 -26
- package/.agents/skills/firecrawl-agent/SKILL.md +0 -58
- package/.agents/skills/firecrawl-build/SKILL.md +0 -39
- package/.agents/skills/firecrawl-build-interact/SKILL.md +0 -68
- package/.agents/skills/firecrawl-build-onboarding/SKILL.md +0 -103
- package/.agents/skills/firecrawl-build-onboarding/references/auth-flow.md +0 -39
- package/.agents/skills/firecrawl-build-onboarding/references/project-setup.md +0 -20
- package/.agents/skills/firecrawl-build-onboarding/references/sdk-installation.md +0 -17
- package/.agents/skills/firecrawl-build-scrape/SKILL.md +0 -69
- package/.agents/skills/firecrawl-build-search/SKILL.md +0 -69
- package/.agents/skills/firecrawl-crawl/SKILL.md +0 -59
- package/.agents/skills/firecrawl-download/SKILL.md +0 -70
- package/.agents/skills/firecrawl-interact/SKILL.md +0 -84
- package/.agents/skills/firecrawl-map/SKILL.md +0 -51
- package/.agents/skills/firecrawl-scrape/SKILL.md +0 -69
- package/.agents/skills/firecrawl-search/SKILL.md +0 -60
- package/mcps/RhinoMCP/cc-plugin/.claude/settings.json +0 -10
- package/mcps/after-effects-mcp/build/index.js +0 -840
- package/mcps/after-effects-mcp/build/scripts/applyEffect.jsx +0 -153
- package/mcps/after-effects-mcp/build/scripts/applyEffectTemplate.jsx +0 -218
- package/mcps/after-effects-mcp/build/scripts/createComposition.jsx +0 -71
- package/mcps/after-effects-mcp/build/scripts/createShapeLayer.jsx +0 -147
- package/mcps/after-effects-mcp/build/scripts/createSolidLayer.jsx +0 -114
- package/mcps/after-effects-mcp/build/scripts/createTextLayer.jsx +0 -115
- package/mcps/after-effects-mcp/build/scripts/getLayerInfo.jsx +0 -192
- package/mcps/after-effects-mcp/build/scripts/getProjectInfo.jsx +0 -90
- package/mcps/after-effects-mcp/build/scripts/listCompositions.jsx +0 -50
- package/mcps/after-effects-mcp/build/scripts/mcp-bridge-auto.jsx +0 -1773
- package/mcps/after-effects-mcp/build/scripts/setLayerProperties.jsx +0 -160
- package/mcps/bdb-remoteos-mcp/queue.db +0 -0
- package/mcps/bdb-remoteos-mcp/src/bdb_remoteos_mcp/__pycache__/__init__.cpython-312.pyc +0 -0
- package/mcps/bdb-remoteos-mcp/src/bdb_remoteos_mcp/__pycache__/incus_client.cpython-312.pyc +0 -0
- package/mcps/bdb-remoteos-mcp/src/bdb_remoteos_mcp/__pycache__/main.cpython-312.pyc +0 -0
- package/mcps/bdb-remoteos-mcp/src/bdb_remoteos_mcp/__pycache__/queue.cpython-312.pyc +0 -0
- package/mcps/bdb-remoteos-mcp/src/bdb_remoteos_mcp/__pycache__/schemas.cpython-312.pyc +0 -0
- package/mcps/bdb-remoteos-mcp/src/bdb_remoteos_mcp/__pycache__/server.cpython-312.pyc +0 -0
- package/mcps/bdb-remoteos-mcp/src/bdb_remoteos_mcp/__pycache__/webhook.cpython-312.pyc +0 -0
- package/mcps/bdb-remoteos-mcp/tests/__pycache__/__init__.cpython-312.pyc +0 -0
- package/mcps/bdb-remoteos-mcp/tests/__pycache__/mock_incus.cpython-312.pyc +0 -0
- package/mcps/bdb-remoteos-mcp/tests/__pycache__/test_mcp_server.cpython-312-pytest-9.1.1.pyc +0 -0
- package/mcps/bdb-remoteos-mcp/tests/__pycache__/test_security_redteam.cpython-312-pytest-9.1.1.pyc +0 -0
- package/mcps/bdb-remoteos-mcp/tests/__pycache__/test_webhook.cpython-312-pytest-9.1.1.pyc +0 -0
- package/mcps/computer-use-mcp/dist/client.d.ts +0 -150
- package/mcps/computer-use-mcp/dist/client.js +0 -136
- package/mcps/computer-use-mcp/dist/entrypoint.d.ts +0 -16
- package/mcps/computer-use-mcp/dist/entrypoint.js +0 -26
- package/mcps/computer-use-mcp/dist/native.d.ts +0 -212
- package/mcps/computer-use-mcp/dist/native.js +0 -50
- package/mcps/computer-use-mcp/dist/server.d.ts +0 -32
- package/mcps/computer-use-mcp/dist/server.js +0 -342
- package/mcps/computer-use-mcp/dist/session.d.ts +0 -101
- package/mcps/computer-use-mcp/dist/session.js +0 -2372
- 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
|
|
29
|
+
## 2. Interactive Technical Interview
|
|
30
30
|
|
|
31
|
-
Before drafting signal flow diagrams or system configs,
|
|
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
|
|
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
|
|
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
|
-
- [ ]
|
|
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:
|
|
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:
|
|
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
|
-
>
|
|
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.
|
|
60
|
-
|
|
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
|
-
|
|
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
|