Skip to content

Commit a7282ef

Browse files
committed
feat: v1.3.0 content-rich pages
- Skill detail expansion with triggers and MCP tools parsing - Changelog section parsed from CHANGELOG.md (latest 2 releases) - Quick-start snippet with copy button - Compatibility pill badges in hero - Related tools cross-link card grid - Updated ROADMAP.md to mark v1.2.0 and v1.3.0 as released Made-with: Cursor
1 parent 5eaecc8 commit a7282ef

File tree

3 files changed

+280
-15
lines changed

3 files changed

+280
-15
lines changed

ROADMAP.md

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,17 +4,17 @@
44
55
## Current Status
66

7-
**v1.1.0** - Centralized directory with 10 registered tools, scaffold generator, unified site template, and GitHub Pages catalog.
7+
**v1.3.0** - Content-rich pages with skill detail expansion, changelog, quick-start snippets, compatibility badges, and related tools cross-links.
88

99
## Release Plan
1010

1111
| Version | Theme | Status |
1212
|---------|-------|--------|
1313
| v1.0.0 | Foundation | Released |
1414
| v1.1.0 | Unified Site Template | Released |
15-
| v1.2.0 | Cross-Repo Consistency | Next |
16-
| v1.3.0 | Content-Rich Pages | Planned |
17-
| v1.4.0 | Discovery and Performance | Planned |
15+
| v1.2.0 | Cross-Repo Consistency | Released |
16+
| v1.3.0 | Content-Rich Pages | Released |
17+
| v1.4.0 | Discovery and Performance | Next |
1818
| v1.5.0 | Infrastructure and Documentation | Planned |
1919

2020
---
@@ -160,6 +160,14 @@ Maintenance, documentation, and packaging consistency.
160160
- [x] `AGENTS.md` created in all 4 tool repos
161161
- [x] Monday CI workflow and dependency-review workflow added
162162
- [x] Monday README badges made clickable
163+
- [x] README style audit across all 4 tool repos (clickable badges, consistent structure)
164+
- [x] CI gaps filled: `label-sync.yml`, `release.yml`, `links.yml` added to all tool repos
165+
- [x] Directory catalog site polished (inline CSS/JS, dark/light mode, search, animations)
166+
- [x] Skill detail expansion (click-to-expand rows with triggers and MCP tools)
167+
- [x] Changelog section parsed from `CHANGELOG.md` (latest 2 releases, collapsible)
168+
- [x] Quick-start snippet with copy button (`quickStart` in `site.json`)
169+
- [x] Compatibility badges as pills in hero (`compatibility` in `site.json`)
170+
- [x] Related tools cross-links as card grid (`relatedTools` in `site.json`)
163171

164172
---
165173

site-template/build_site.py

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,40 @@ def parse_frontmatter(text: str) -> tuple[dict[str, str], str]:
4646
return meta, body
4747

4848

49+
def _extract_tools_from_frontmatter(lines: list[str]) -> list[str]:
50+
"""Parse a YAML list of tools from frontmatter lines (handles ``tools:`` key)."""
51+
tools: list[str] = []
52+
in_tools = False
53+
for line in lines:
54+
stripped = line.strip()
55+
if stripped.startswith("tools:"):
56+
in_tools = True
57+
continue
58+
if in_tools:
59+
if stripped.startswith("- "):
60+
tools.append(stripped[2:].strip())
61+
else:
62+
break
63+
return tools
64+
65+
66+
def _extract_trigger_section(body: str) -> list[str]:
67+
"""Pull bullet items from the ``## Trigger`` section of a SKILL.md body."""
68+
triggers: list[str] = []
69+
in_trigger = False
70+
for line in body.splitlines():
71+
stripped = line.strip()
72+
if re.match(r"^##\s+Trigger", stripped, re.IGNORECASE):
73+
in_trigger = True
74+
continue
75+
if in_trigger:
76+
if stripped.startswith("##"):
77+
break
78+
if stripped.startswith("- "):
79+
triggers.append(stripped[2:].strip())
80+
return triggers
81+
82+
4983
def parse_skills(repo_root: Path) -> list[dict]:
5084
skills_dir = repo_root / "skills"
5185
if not skills_dir.is_dir():
@@ -80,10 +114,16 @@ def parse_skills(repo_root: Path) -> list[dict]:
80114
name = re.sub(r"^#+\s*", "", stripped)
81115
break
82116

117+
fm_lines = text.splitlines()
118+
tools = _extract_tools_from_frontmatter(fm_lines)
119+
triggers = _extract_trigger_section(body)
120+
83121
results.append({
84122
"name": name,
85123
"description": description,
86124
"category": skill_dir.name,
125+
"tools": tools,
126+
"triggers": triggers,
87127
})
88128

89129
return results
@@ -130,6 +170,60 @@ def parse_rules(repo_root: Path) -> list[dict]:
130170
return results
131171

132172

173+
def parse_changelog(repo_root: Path, max_entries: int = 2) -> list[dict]:
174+
"""Parse a Keep-a-Changelog formatted CHANGELOG.md.
175+
176+
Returns up to *max_entries* release entries (skipping ``[Unreleased]``),
177+
each as ``{ version, date, sections: [{ heading, items }] }``.
178+
"""
179+
changelog_path = repo_root / "CHANGELOG.md"
180+
if not changelog_path.is_file():
181+
return []
182+
183+
text = changelog_path.read_text(encoding="utf-8", errors="replace")
184+
entries: list[dict] = []
185+
current_entry: dict | None = None
186+
current_section: dict | None = None
187+
188+
for line in text.splitlines():
189+
heading_match = re.match(r"^##\s+\[(.+?)\](?:\s*-\s*(.+))?", line)
190+
if heading_match:
191+
version = heading_match.group(1)
192+
date = (heading_match.group(2) or "").strip()
193+
if version.lower() == "unreleased":
194+
continue
195+
if current_entry is not None:
196+
if current_section:
197+
current_entry["sections"].append(current_section)
198+
entries.append(current_entry)
199+
if len(entries) >= max_entries:
200+
break
201+
current_entry = {"version": version, "date": date, "sections": []}
202+
current_section = None
203+
continue
204+
205+
if current_entry is None:
206+
continue
207+
208+
sub_match = re.match(r"^###\s+(.+)", line)
209+
if sub_match:
210+
if current_section:
211+
current_entry["sections"].append(current_section)
212+
current_section = {"heading": sub_match.group(1).strip(), "entries": []}
213+
continue
214+
215+
if current_section is not None and line.strip().startswith("- "):
216+
current_section["entries"].append(line.strip()[2:].strip())
217+
218+
if current_entry is not None:
219+
if current_section:
220+
current_entry["sections"].append(current_section)
221+
if len(entries) < max_entries:
222+
entries.append(current_entry)
223+
224+
return entries
225+
226+
133227
def load_mcp_tools(repo_root: Path) -> list[dict]:
134228
mcp_file = repo_root / "mcp-tools.json"
135229
if not mcp_file.is_file():
@@ -185,6 +279,7 @@ def main():
185279
rules = parse_rules(repo_root)
186280
mcp_tools = load_mcp_tools(repo_root)
187281
mcp_grouped = group_by_category(mcp_tools)
282+
changelog = parse_changelog(repo_root)
188283

189284
context = {
190285
"plugin": plugin,
@@ -196,6 +291,8 @@ def main():
196291
"mcp_tools": mcp_tools,
197292
"mcp_tool_count": len(mcp_tools),
198293
"mcp_grouped": mcp_grouped,
294+
"changelog": changelog,
295+
"has_changelog": len(changelog) > 0,
199296
"build_date": datetime.date.today().isoformat(),
200297
}
201298

0 commit comments

Comments
 (0)