Skip to content

Commit 308bf2e

Browse files
committed
feat: fix site template, add QoL features, update docs and scaffold
Fix parse_skills() to handle YAML frontmatter correctly. Add collapsible categories, search/filter, back-to-top, toast, mobile nav. Fix install steps rendering raw HTML (safe filter). Update README/AGENTS/CLAUDE/CONTRIBUTING/SECURITY for site-template. Rewrite scaffold pages.yml.j2 for unified template, add site.json.j2 and mcp-tools.json.j2. Upgrade validate.yml to enforce full registry.json schema. Made-with: Cursor
1 parent 29a8e66 commit 308bf2e

File tree

15 files changed

+555
-367
lines changed

15 files changed

+555
-367
lines changed

.cursorrules

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@ You are working on Developer Tools Directory, a meta-repository that catalogs, s
55
- `registry.json` -- single source of truth for all 9 tool repos (name, type, counts, links, status)
66
- `standards/` -- 9 Markdown docs defining conventions (folder structure, CI/CD, manifests, pages, commits, README, AGENTS.md, versioning)
77
- `scaffold/create-tool.py` -- Python repo generator using Jinja2 templates
8-
- `scaffold/templates/` -- 18 Jinja2 templates producing a standards-compliant repo
8+
- `scaffold/templates/` -- Jinja2 templates producing a standards-compliant repo
9+
- `site-template/` -- shared GitHub Pages build system for tool repos (build_site.py + template.html.j2 + fonts)
910
- `docs/` -- static GitHub Pages catalog site (index.html, style.css, script.js)
1011
- `assets/` -- logo image
1112
- `.github/workflows/` -- CI/CD for this repo (validate, pages, stale, codeql, dependency-review, release, release-drafter, label-sync)
@@ -54,6 +55,13 @@ Use conventional commits. Prefix determines changelog category:
5455
- Test locally: `python scaffold/create-tool.py --name "Test" --description "Test" --mcp-server --skills 2 --rules 1 --output /tmp/test`
5556
- CI runs a dry-run test on every push.
5657

58+
## When editing site-template/
59+
60+
- `site-template/build_site.py` generates GitHub Pages for tool repos. It parses plugin.json, site.json, skills/, rules/, and mcp-tools.json.
61+
- `site-template/template.html.j2` is the Jinja2 HTML template with collapsible categories, search, mobile nav.
62+
- Tool repos clone this directory in CI via sparse checkout and run `build_site.py --repo-root . --out docs`.
63+
- Test locally: `python site-template/build_site.py --repo-root /path/to/tool-repo --out /path/to/tool-repo/docs`
64+
5765
## When editing docs/
5866

5967
- `docs/index.html` is the catalog site entry point.

.github/workflows/validate.yml

Lines changed: 44 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -26,21 +26,50 @@ jobs:
2626
import json, sys
2727
data = json.load(open("registry.json"))
2828
assert isinstance(data, list), "registry.json must be an array"
29-
required = ["name", "repo", "slug", "description", "type", "skills", "rules", "mcpTools", "status"]
30-
valid_types = ["cursor-plugin", "mcp-server"]
29+
SCHEMA = {
30+
"name": str,
31+
"repo": str,
32+
"slug": str,
33+
"description": str,
34+
"type": str,
35+
"homepage": str,
36+
"skills": int,
37+
"rules": int,
38+
"mcpTools": int,
39+
"extras": dict,
40+
"topics": list,
41+
"status": str,
42+
"version": str,
43+
"language": str,
44+
"license": str,
45+
"pagesType": str,
46+
"hasCI": bool,
47+
}
48+
OPTIONAL = {"npm": str}
49+
VALID_TYPES = {"cursor-plugin", "mcp-server"}
50+
VALID_STATUS = {"active", "beta", "deprecated"}
51+
VALID_PAGES = {"static", "mkdocs", "none"}
3152
errors = []
3253
for i, tool in enumerate(data):
33-
for field in required:
54+
label = "Entry %d (%s)" % (i, tool.get("name", "?"))
55+
for field, expected_type in SCHEMA.items():
3456
if field not in tool:
35-
errors.append("Entry %d (%s): missing '%s'" % (i, tool.get("name", "?"), field))
36-
if tool.get("type") not in valid_types:
37-
errors.append("Entry %d (%s): invalid type '%s'" % (i, tool.get("name", "?"), tool.get("type")))
38-
if not isinstance(tool.get("skills", 0), int):
39-
errors.append("Entry %d (%s): skills must be int" % (i, tool.get("name", "?")))
40-
if not isinstance(tool.get("rules", 0), int):
41-
errors.append("Entry %d (%s): rules must be int" % (i, tool.get("name", "?")))
42-
if not isinstance(tool.get("mcpTools", 0), int):
43-
errors.append("Entry %d (%s): mcpTools must be int" % (i, tool.get("name", "?")))
57+
errors.append("%s: missing required field '%s'" % (label, field))
58+
elif not isinstance(tool[field], expected_type):
59+
errors.append("%s: '%s' must be %s, got %s" % (label, field, expected_type.__name__, type(tool[field]).__name__))
60+
for field, expected_type in OPTIONAL.items():
61+
if field in tool and not isinstance(tool[field], expected_type):
62+
errors.append("%s: optional '%s' must be %s" % (label, field, expected_type.__name__))
63+
if tool.get("type") not in VALID_TYPES:
64+
errors.append("%s: type must be one of %s" % (label, VALID_TYPES))
65+
if tool.get("status") not in VALID_STATUS:
66+
errors.append("%s: status must be one of %s" % (label, VALID_STATUS))
67+
if tool.get("pagesType") not in VALID_PAGES:
68+
errors.append("%s: pagesType must be one of %s" % (label, VALID_PAGES))
69+
if not isinstance(tool.get("topics", []), list):
70+
pass
71+
elif not all(isinstance(t, str) for t in tool.get("topics", [])):
72+
errors.append("%s: topics must be array of strings" % label)
4473
if errors:
4574
for e in errors:
4675
print("::error::" + e, file=sys.stderr)
@@ -91,5 +120,8 @@ jobs:
91120
92121
test -f /tmp/scaffold-test/ci-test-plugin/.cursor-plugin/plugin.json
93122
test -f /tmp/scaffold-test/ci-test-plugin/.github/workflows/validate.yml
123+
test -f /tmp/scaffold-test/ci-test-plugin/.github/workflows/pages.yml
124+
test -f /tmp/scaffold-test/ci-test-plugin/site.json
125+
test -f /tmp/scaffold-test/ci-test-plugin/mcp-tools.json
94126
test -f /tmp/scaffold-test/ci-test-plugin/mcp-server/server.py
95127
echo "Scaffold test passed"

.gitignore

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,4 +11,3 @@ build/
1111
Thumbs.db
1212
node_modules/
1313
output/
14-
site-template/SETUP-PROMPT.md

AGENTS.md

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@ This is a **meta-repository** -- it does not contain a Cursor plugin or MCP serv
1010

1111
- **`registry.json`** -- single source of truth for all tool repos (9 entries). The catalog site and README tables are derived from it.
1212
- **`standards/`** -- 9 Markdown docs defining conventions for folder structure, CI/CD, plugin manifests, GitHub Pages, commit conventions, README format, AGENTS.md format, and versioning.
13-
- **`scaffold/`** -- Python repo generator (`create-tool.py`) with 18 Jinja2 templates that produce a fully standards-compliant new tool repo.
13+
- **`scaffold/`** -- Python repo generator (`create-tool.py`) with Jinja2 templates that produce a fully standards-compliant new tool repo.
14+
- **`site-template/`** -- shared GitHub Pages build system for tool repos. `build_site.py` reads `.cursor-plugin/plugin.json`, `site.json`, `skills/`, `rules/`, and `mcp-tools.json` from a tool repo and renders `docs/index.html` via `template.html.j2`. Self-hosts Inter and JetBrains Mono fonts. Tool repos clone this directory in CI and run the build script at deploy time.
1415
- **`docs/`** -- static GitHub Pages catalog site (vanilla HTML/CSS/JS, no build step). Reads `registry.json` at runtime to render tool cards.
1516
- **`assets/`** -- logo image.
1617
- **`.github/workflows/`** -- CI/CD for this repo (validate, pages, release, release-drafter, stale, codeql, dependency-review, label-sync).
@@ -178,6 +179,19 @@ Pure documentation -- no code. Each file documents a convention derived from ana
178179
- CI runs a scaffold dry-run test on every push. If you add a new required file, add a `test -f` check to the `validate.yml` scaffold test step.
179180
- The `LICENSE.j2` template has conditional logic for MIT, Apache-2.0, and CC-BY-NC-ND-4.0. If adding a new license option, update both the template and the `LICENSE_FILES`/`SPDX` dicts in `create-tool.py`.
180181

182+
## When editing site-template/
183+
184+
- `site-template/build_site.py` is the build script that generates GitHub Pages sites for tool repos. It reads data from the tool repo and renders HTML via `template.html.j2`.
185+
- `site-template/template.html.j2` is a Jinja2 template producing a single-page site with: sticky nav, hero with animated stats, collapsible MCP tool categories, search/filter, back-to-top, toast copy feedback, and mobile hamburger nav.
186+
- `site-template/fonts/` contains self-hosted Inter (400/500/700) and JetBrains Mono (400) woff2 files.
187+
- `site-template/SETUP-PROMPT.md` is a copy-paste prompt for applying the template to a new tool repo.
188+
- Tool repos consume this template via their `pages.yml` workflow, which clones Developer-Tools-Directory with sparse checkout and runs `build_site.py --repo-root . --out docs`.
189+
- Data sources in each tool repo: `.cursor-plugin/plugin.json` (metadata), `site.json` (branding/colors), `skills/*/SKILL.md` (parsed for name/description via frontmatter), `rules/*.mdc` (parsed for name/scope/description), `mcp-tools.json` (manually maintained tool list).
190+
- Changes to the template or build script affect all tool repos on their next deploy. Test locally before pushing:
191+
```
192+
python site-template/build_site.py --repo-root /path/to/tool-repo --out /path/to/tool-repo/docs
193+
```
194+
181195
## When editing docs/ (catalog site)
182196

183197
- `docs/index.html` is the single-page catalog site. It embeds a copy of `registry.json` inside a `<script type="application/json" id="registry-data">` tag as a fallback for when the direct fetch fails.

CLAUDE.md

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,12 @@ Developer-Tools-Directory/
2020
standards/ # 9 convention docs (folder structure, CI/CD, manifests, etc.)
2121
scaffold/
2222
create-tool.py # Python repo generator (Jinja2)
23-
templates/ # 18 .j2 templates for new repos
23+
templates/ # Jinja2 templates for new repos
24+
site-template/
25+
build_site.py # Builds GitHub Pages for tool repos from their metadata
26+
template.html.j2 # Shared HTML template with configurable branding
27+
fonts/ # Self-hosted Inter + JetBrains Mono woff2
28+
SETUP-PROMPT.md # Copy-paste prompt for applying template to a repo
2429
docs/
2530
index.html # GitHub Pages catalog site
2631
style.css # Dark theme, responsive, card layout
@@ -76,7 +81,7 @@ Static HTML/CSS/JS. No build step. No external CDN. The `pages.yml` workflow cop
7681
| Steam Cursor Plugin | Plugin | 30 | 9 | 25 |
7782
| Steam MCP Server | MCP Server | 0 | 0 | 25 |
7883

79-
**Totals:** 186 skills, 77 rules, 377 MCP tools
84+
**Totals:** 177 skills, 71 rules, 371 MCP tools
8085

8186
## Development Workflow
8287

CONTRIBUTING.md

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,11 @@ Developer-Tools-Directory/
2727
standards/ # Convention documentation (9 docs)
2828
scaffold/
2929
create-tool.py # Repo generator script
30-
templates/ # Jinja2 templates (18 files)
30+
templates/ # Jinja2 templates for new repos
31+
site-template/
32+
build_site.py # Shared GitHub Pages build for tool repos
33+
template.html.j2 # HTML template with configurable branding
34+
fonts/ # Self-hosted Inter + JetBrains Mono woff2
3135
docs/ # GitHub Pages catalog site
3236
assets/ # Logo image
3337
.github/workflows/ # CI/CD automation
@@ -49,6 +53,7 @@ Developer-Tools-Directory/
4953
"rules": 0,
5054
"mcpTools": 0,
5155
"extras": {},
56+
"npm": "",
5257
"topics": ["topic-1", "topic-2"],
5358
"status": "active",
5459
"version": "0.1.0",

README.md

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@
2929
---
3030

3131
<p align="center">
32-
9 repos &nbsp;&bull;&nbsp; 186 skills &nbsp;&bull;&nbsp; 77 rules &nbsp;&bull;&nbsp; 377 MCP tools
32+
9 repos &nbsp;&bull;&nbsp; 177 skills &nbsp;&bull;&nbsp; 71 rules &nbsp;&bull;&nbsp; 371 MCP tools
3333
</p>
3434

3535
## How It Works
@@ -40,9 +40,10 @@ flowchart LR
4040
A --> C["README tables\n(below)"]
4141
D["scaffold/\ncreate-tool.py"] --> E["New repo with\nall standards"]
4242
F["standards/\n(conventions)"] --> D
43+
G["site-template/\nbuild_site.py"] --> H["Tool repo\nGitHub Pages"]
4344
```
4445

45-
**Registry** tracks every tool repo. **Standards** document conventions for CI/CD, folder structure, manifests, and versioning. **Scaffold** generates new repos that follow those standards automatically.
46+
**Registry** tracks every tool repo. **Standards** document conventions for CI/CD, folder structure, manifests, and versioning. **Scaffold** generates new repos that follow those standards automatically. **Site template** provides a shared build system for tool repo GitHub Pages sites.
4647

4748
## Tools
4849

@@ -172,6 +173,11 @@ Developer-Tools-Directory/
172173
assets/ Logo
173174
docs/ GitHub Pages catalog site
174175
scaffold/ Repo generator + Jinja2 templates
176+
site-template/ Shared GitHub Pages build system for tool repos
177+
build_site.py Reads plugin.json, skills/, rules/, mcp-tools.json -> index.html
178+
template.html.j2 Jinja2 HTML template with configurable branding
179+
fonts/ Self-hosted Inter + JetBrains Mono woff2
180+
SETUP-PROMPT.md Copy-paste prompt for applying template to a repo
175181
standards/ Convention documentation (9 docs)
176182
registry.json Tool registry (source of truth)
177183
AGENTS.md AI agent guidance

SECURITY.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ This repository is a meta-repository containing:
2323
| registry.json | Schema poisoning via malicious PR (fake repo URLs, XSS in description fields) | CI validates schema on every PR. The catalog site renders text content via `textContent`, not `innerHTML`. |
2424
| GitHub Pages site | XSS via registry data rendered in the DOM | Tool descriptions are inserted via `textContent`. No `innerHTML` or `eval` is used with registry data. |
2525
| GitHub Actions workflows | Workflow injection via PR title/body in release-drafter | Release-drafter uses the official action with no custom script interpolation of PR content. |
26+
| Site template (supply chain) | Tool repos clone this repo in CI to build their Pages site. A compromised template could inject malicious HTML/JS into all tool repo sites. | Template changes are reviewed before merge. Tool repos pin to `main` branch. The build script does not execute arbitrary code from tool repos -- it only reads JSON and Markdown files. |
2627

2728
### Out of scope
2829

scaffold/create-tool.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -167,8 +167,9 @@ def main():
167167
write_file(output_dir, ".cursorrules", render_template(env, "cursorrules.j2", ctx))
168168
write_file(output_dir, ".gitignore", render_template(env, "gitignore.j2", ctx))
169169

170-
# GitHub Pages docs site
171-
write_file(output_dir, "docs/index.html", render_template(env, "index.html.j2", ctx))
170+
# GitHub Pages data files
171+
write_file(output_dir, "site.json", render_template(env, "site.json.j2", ctx))
172+
write_file(output_dir, "mcp-tools.json", render_template(env, "mcp-tools.json.j2", ctx))
172173

173174
# Assets placeholder
174175
(output_dir / "assets").mkdir(parents=True, exist_ok=True)
@@ -178,7 +179,7 @@ def main():
178179
# Skills
179180
for skill in skill_names:
180181
skill_content = f"""---
181-
title: {skill.replace('-', ' ').title()}
182+
name: {skill}
182183
description: TODO - describe this skill
183184
globs: ["**/*"]
184185
alwaysApply: false
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
[]

0 commit comments

Comments
 (0)