Skip to content

Commit e66dd6d

Browse files
JoshJosh
authored andcommitted
Auto-generate skills from SKILL.md frontmatter
- Add YAML frontmatter to all 12 skills (id, name, category, etc.) - Build script reads frontmatter and generates src/skills-data.js - app.jsx imports from generated file instead of hardcoded array - Categories auto-derived from skill data - lastUpdated uses git log (accurate on CI) - Build fails if any skill has missing/invalid frontmatter - Skill cards now show GitHub link + curl fetch command - Adding a SKILL.md is the only step needed to add a skill
1 parent 56573c5 commit e66dd6d

17 files changed

Lines changed: 268 additions & 172 deletions

File tree

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,4 @@ node_modules
22
dist
33
.DS_Store
44
*.local
5+
src/skills-data.js

CONTRIBUTING.md

Lines changed: 5 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -46,25 +46,11 @@ Step-by-step commands to deploy locally and on mainnet.
4646
Concrete commands to confirm the implementation is correct.
4747
```
4848

49-
### 3. Register the skill on the website
50-
51-
Both files must be updated in the same PR. The `SKILL.md` is served via the API. The `SKILLS` array entry is what makes the skill appear on the website — stats (skill count, operations count) are computed automatically from this array.
52-
53-
Add an entry to the `SKILLS` array in `src/app.jsx`:
54-
55-
```javascript
56-
{
57-
id: "your-skill-id", // matches the directory name
58-
name: "Human-Readable Name",
59-
category: "Category", // DeFi, Auth, Architecture, Tokens, etc.
60-
description: "One-line description for the card.",
61-
endpoints: 6, // number of operations/endpoints covered
62-
lastUpdated: "2026-02-24", // date of last content change
63-
version: "1.0.0", // semver, matches the SKILL.md header
64-
status: "stable", // stable | beta | experimental
65-
dependencies: [], // IDs of other skills this depends on
66-
}
67-
```
49+
### 3. That's it — the website auto-discovers skills
50+
51+
The website is automatically generated from the SKILL.md frontmatter at build time. You do **not** need to edit `app.jsx` or any other source file. The build script (`scripts/generate-skills.js`) scans all `skills/*/SKILL.md` files, parses their frontmatter, and generates the data the site uses.
52+
53+
Stats (skill count, operations, categories) all update automatically.
6854

6955
### 4. Submit a PR
7056

package.json

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,9 @@
66
"license": "MIT",
77
"type": "module",
88
"scripts": {
9-
"dev": "vite",
10-
"build": "vite build",
9+
"generate": "node scripts/generate-skills.js",
10+
"dev": "npm run generate && vite",
11+
"build": "npm run generate && vite build",
1112
"preview": "vite preview"
1213
},
1314
"dependencies": {

scripts/generate-skills.js

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
#!/usr/bin/env node
2+
// Reads all skills SKILL.md files, parses YAML frontmatter,
3+
// and writes src/skills-data.js so the site auto-discovers skills.
4+
// Run: node scripts/generate-skills.js
5+
6+
import { readdirSync, readFileSync, writeFileSync, statSync } from "fs";
7+
import { execFileSync } from "child_process";
8+
import { join, dirname } from "path";
9+
import { fileURLToPath } from "url";
10+
11+
const __dirname = dirname(fileURLToPath(import.meta.url));
12+
const ROOT = join(__dirname, "..");
13+
const SKILLS_DIR = join(ROOT, "skills");
14+
const OUTPUT = join(ROOT, "src", "skills-data.js");
15+
16+
function parseFrontmatter(content) {
17+
const match = content.match(/^---\n([\s\S]*?)\n---/);
18+
if (!match) return null;
19+
20+
const yaml = match[1];
21+
const data = {};
22+
23+
for (const line of yaml.split("\n")) {
24+
const idx = line.indexOf(":");
25+
if (idx === -1) continue;
26+
const key = line.slice(0, idx).trim();
27+
let val = line.slice(idx + 1).trim();
28+
29+
// Parse arrays: [a, b, c]
30+
if (val.startsWith("[") && val.endsWith("]")) {
31+
val = val.slice(1, -1).split(",").map((s) => s.trim()).filter(Boolean);
32+
}
33+
// Parse numbers
34+
else if (/^\d+$/.test(val)) {
35+
val = parseInt(val, 10);
36+
}
37+
// Strip quotes
38+
else if ((val.startsWith('"') && val.endsWith('"')) || (val.startsWith("'") && val.endsWith("'"))) {
39+
val = val.slice(1, -1);
40+
}
41+
42+
data[key] = val;
43+
}
44+
45+
return data;
46+
}
47+
48+
// Use git log for accurate dates (file mtime is unreliable on CI)
49+
function getLastUpdated(filePath) {
50+
try {
51+
const date = execFileSync("git", ["log", "-1", "--format=%cs", "--", filePath], { cwd: ROOT, encoding: "utf-8" }).trim();
52+
if (date) return date;
53+
} catch {}
54+
// Fallback to file mtime if not in a git repo
55+
return statSync(filePath).mtime.toISOString().split("T")[0];
56+
}
57+
58+
const dirs = readdirSync(SKILLS_DIR).filter((d) => {
59+
try {
60+
return statSync(join(SKILLS_DIR, d, "SKILL.md")).isFile();
61+
} catch {
62+
return false;
63+
}
64+
});
65+
66+
const skills = [];
67+
const errors = [];
68+
69+
for (const dir of dirs) {
70+
const filePath = join(SKILLS_DIR, dir, "SKILL.md");
71+
const content = readFileSync(filePath, "utf-8");
72+
const meta = parseFrontmatter(content);
73+
74+
if (!meta) {
75+
errors.push(`${dir}/SKILL.md: missing YAML frontmatter (---)`);
76+
continue;
77+
}
78+
79+
const missing = ["id", "name", "category"].filter((f) => !meta[f]);
80+
if (missing.length) {
81+
errors.push(`${dir}/SKILL.md: missing required fields: ${missing.join(", ")}`);
82+
continue;
83+
}
84+
85+
skills.push({
86+
id: meta.id,
87+
name: meta.name,
88+
category: meta.category,
89+
description: meta.description || "",
90+
endpoints: meta.endpoints || 0,
91+
lastUpdated: getLastUpdated(filePath),
92+
version: meta.version || "1.0.0",
93+
status: meta.status || "stable",
94+
dependencies: Array.isArray(meta.dependencies) ? meta.dependencies : [],
95+
});
96+
}
97+
98+
if (errors.length) {
99+
console.error("ERRORS in skill files:");
100+
errors.forEach((e) => console.error(` - ${e}`));
101+
process.exit(1);
102+
}
103+
104+
// Sort alphabetically by name for consistent output
105+
skills.sort((a, b) => a.name.localeCompare(b.name));
106+
107+
const output = `// Auto-generated by scripts/generate-skills.js — do not edit manually
108+
// To update: edit the YAML frontmatter in skills/*/SKILL.md and rebuild
109+
export const SKILLS = ${JSON.stringify(skills, null, 2)};
110+
`;
111+
112+
writeFileSync(OUTPUT, output);
113+
console.log(`Generated ${skills.length} skills -> src/skills-data.js`);

skills/asset-canister/SKILL.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,14 @@
1+
---
2+
id: asset-canister
3+
name: "Asset Canister & Frontend"
4+
category: Frontend
5+
description: "Deploy frontend assets to the IC. Certified assets, custom domains, SPA routing, and content encoding."
6+
endpoints: 5
7+
version: 3.2.0
8+
status: stable
9+
dependencies: []
10+
---
11+
112
# Asset Canister & Frontend Hosting
213
> version: 1.0.0 | requires: [dfx >= 0.24]
314

skills/certified-variables/SKILL.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,14 @@
1+
---
2+
id: certified-variables
3+
name: Certified Variables
4+
category: Security
5+
description: "Serve verified responses from query calls. Merkle tree construction, certificate validation, and certified asset patterns."
6+
endpoints: 4
7+
version: 1.2.0
8+
status: stable
9+
dependencies: []
10+
---
11+
112
# Certified Variables & Certified Assets
213
> version: 1.0.0 | requires: [dfx >= 0.24.0, ic-certified-map (Rust) or CertifiedData (Motoko)]
314

skills/ckbtc/SKILL.md

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,12 @@
11
---
2-
name: ckbtc
3-
description: Use when building apps that accept, send, or manage Bitcoin on the Internet Computer. Covers ckBTC deposit/withdrawal flows, ICRC-1 transfers, subaccount generation, and minter interaction. Essential for any ICP project integrating Bitcoin payments.
2+
id: ckbtc
3+
name: ckBTC Integration
4+
category: DeFi
5+
description: "Accept, send, and manage ckBTC in your canister. Covers minting, transfers, balance checks, and UTXO management."
6+
endpoints: 14
7+
version: 2.1.0
8+
status: stable
9+
dependencies: [icrc-ledger, wallet]
410
---
511

612
# Chain-Key Bitcoin (ckBTC) Integration

skills/evm-rpc/SKILL.md

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,12 @@
11
---
2-
name: evm-rpc
3-
description: Use when building apps that read from or write to Ethereum and EVM-compatible chains from the Internet Computer. Covers EVM RPC canister setup, eth_getBalance, eth_call, eth_sendRawTransaction, multi-provider consensus, and cycle costs.
2+
id: evm-rpc
3+
name: EVM RPC Integration
4+
category: Integration
5+
description: "Call Ethereum and EVM chains from IC canisters. JSON-RPC, transaction signing, and cross-chain workflows."
6+
endpoints: 9
7+
version: 1.1.0
8+
status: stable
9+
dependencies: [https-outcalls]
410
---
511

612
# EVM RPC Canister — Calling Ethereum from IC

skills/https-outcalls/SKILL.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,14 @@
1+
---
2+
id: https-outcalls
3+
name: HTTPS Outcalls
4+
category: Integration
5+
description: "Make HTTP requests from canisters to external APIs. Consensus-safe request patterns, transform functions, and cost management."
6+
endpoints: 4
7+
version: 1.5.0
8+
status: stable
9+
dependencies: []
10+
---
11+
112
# HTTPS Outcalls
213
> version: 1.0.0 | requires: [dfx >= 0.24]
314

skills/icrc-ledger/SKILL.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,14 @@
1+
---
2+
id: icrc-ledger
3+
name: ICRC Ledger Standard
4+
category: Tokens
5+
description: "Deploy and interact with ICRC-1/ICRC-2 token ledgers. Minting, approvals, transfers, and metadata."
6+
endpoints: 11
7+
version: 2.3.0
8+
status: stable
9+
dependencies: []
10+
---
11+
112
# ICRC Ledger Standards
213
> version: 1.0.0 | requires: [dfx >= 0.24.0, mops, ic-cdk >= 0.18]
314

0 commit comments

Comments
 (0)