Skip to content

Commit a8ab649

Browse files
committed
docs: extract agent skills from AGENTS.md and README
- Add .github/skills/ with five Agent Skills: - add-blog-post: step-by-step guide for creating a new blog post - add-component: guide for adding a new Svelte UI component - update-contributors: guide for syncing the contributors list - deploy-site: automatic and manual deployment instructions - run-checks: quality checklist to run before committing - Refactor AGENTS.md: replace verbose procedural sections with pointers to the relevant skill files; add Skills index at the top; add authoring note about the 160-char description limit - Update README.md: slim down Managing Posts section; skills list lives only in AGENTS.md
1 parent 3e8cda5 commit a8ab649

7 files changed

Lines changed: 446 additions & 230 deletions

File tree

Lines changed: 180 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,180 @@
1+
---
2+
name: add-blog-post
3+
description: Creates a new blog post for the Torrust website. Use when asked to write, add, create, or publish a new blog post or article.
4+
---
5+
6+
# Add Blog Post
7+
8+
## When to use this skill
9+
10+
Use this skill when asked to create, write, or publish a new blog post on the Torrust website.
11+
12+
## Directory structure
13+
14+
Each post lives in its own directory under `src/routes/blog/`:
15+
16+
```
17+
src/routes/blog/<post-slug>/
18+
├── metadata.ts # Post metadata (title, date, tags, etc.)
19+
├── +page.server.ts # Server-side data loader (identical boilerplate for every post)
20+
└── +page.svelte # Post content as a Svelte component
21+
```
22+
23+
## Steps
24+
25+
### 1. Create the post directory
26+
27+
```bash
28+
mkdir src/routes/blog/<post-slug>
29+
```
30+
31+
The slug must be kebab-case and must match the `slug` field in `metadata.ts`.
32+
33+
### 2. Create `metadata.ts`
34+
35+
```typescript
36+
export const metadata = {
37+
title: 'Post Title',
38+
slug: 'post-slug', // must match the directory name exactly
39+
contributor: 'Author Name',
40+
contributorSlug: 'author-slug', // matches a directory under src/routes/contributor/
41+
date: '2024-01-15T12:00:00.000Z', // ISO 8601 format
42+
coverImage: '/images/posts/post-slug/cover.webp',
43+
excerpt: 'Brief description shown in listings and used for SEO.',
44+
tags: ['Rust', 'BitTorrent'] // title-case tags
45+
};
46+
```
47+
48+
### 3. Create `+page.server.ts` (copy verbatim)
49+
50+
This boilerplate is identical for every post — never modify it:
51+
52+
```typescript
53+
import { getMetadata } from '$lib/data/metadata';
54+
import type { PageServerLoad } from './$types';
55+
56+
export const load: PageServerLoad = async ({ url }) => {
57+
const slug = url.pathname.split('/').filter(Boolean).pop();
58+
if (!slug) throw new Error('Slug could not be determined.');
59+
60+
const metadata = await getMetadata();
61+
const currentPost = metadata.find((post) => post.slug === slug);
62+
63+
if (!currentPost) throw new Error(`Post not found: ${slug}`);
64+
65+
return { currentPost, allPosts: metadata };
66+
};
67+
```
68+
69+
### 4. Create `+page.svelte`
70+
71+
Use this template as the starting point:
72+
73+
```svelte
74+
<script lang="ts">
75+
import BlogPreview from '$lib/components/molecules/BlogPreview.svelte';
76+
import Toc from '$lib/components/atoms/Toc.svelte';
77+
import Post from '$lib/components/organisms/Post.svelte';
78+
import PagesWrapper from '$lib/components/atoms/PagesWrapper.svelte';
79+
import PrevNextPost from '$lib/components/singletons/PrevNextPost.svelte';
80+
import Callout from '$lib/components/molecules/Callout.svelte';
81+
82+
let { data } = $props();
83+
let currentPost = $derived(data.currentPost);
84+
let allPosts = $derived(data.allPosts);
85+
</script>
86+
87+
<Post
88+
title={currentPost.title}
89+
slug={currentPost.slug}
90+
coverImage={currentPost.coverImage}
91+
date={currentPost.date}
92+
tags={currentPost.tags}
93+
excerpt={currentPost.excerpt}
94+
contributor={currentPost.contributor}
95+
contributorSlug={currentPost.contributorSlug}
96+
>
97+
<PagesWrapper>
98+
<div class="wrapper">
99+
<Toc class="toc" />
100+
<div id="toc-contents" class="content-preview">
101+
<!-- Post content goes here -->
102+
<h2 id="introduction">Introduction</h2>
103+
<p>Your intro paragraph...</p>
104+
</div>
105+
</div>
106+
</PagesWrapper>
107+
<PrevNextPost currentPage={currentPost.slug} {allPosts} />
108+
<div class="related-posts-container">
109+
<h2>Related Posts:</h2>
110+
<div class="grid">
111+
{#each data.allPosts.slice(0, 3) as post}
112+
<a href="/blog/{post.slug}">
113+
<BlogPreview post_data={post} />
114+
</a>
115+
{/each}
116+
</div>
117+
</div>
118+
</Post>
119+
120+
<style lang="scss">
121+
@use '$lib/scss/breakpoints.scss' as bp;
122+
123+
.wrapper {
124+
/* layout styles here */
125+
}
126+
</style>
127+
```
128+
129+
### 5. Add a cover image
130+
131+
Place the cover image at:
132+
133+
```
134+
static/images/posts/<post-slug>/cover.webp
135+
```
136+
137+
- Preferred format: WebP
138+
- Additional post images go in the same folder
139+
- Reference images in content as `/images/posts/<post-slug>/filename.ext`
140+
- Use `<Image src="..." alt="..." />` instead of `<img />` for automatic WebP/AVIF optimisation
141+
142+
### 6. Regenerate blog metadata
143+
144+
`static/blogMetadata.json` drives the blog listing page and search. Run this after adding or modifying any post:
145+
146+
```bash
147+
npx tsx scripts/generateMetadata.ts
148+
```
149+
150+
Without this step the new post will **not** appear at `/blog`.
151+
152+
### 7. Verify and lint
153+
154+
```bash
155+
npm run dev # check the post at http://localhost:5173/blog/<post-slug>
156+
npm run check # TypeScript + Svelte type checking
157+
npm run lint # Prettier + ESLint
158+
```
159+
160+
## Content guidelines
161+
162+
### Headings and Table of Contents
163+
164+
`<Toc />` auto-generates a table of contents from `<h2>` and `<h3>` elements inside the `id="toc-contents"` div. Every heading **must** have a matching `id` attribute:
165+
166+
```svelte
167+
<h2 id="my-section">My Section</h2><h3 id="my-subsection">My Subsection</h3>
168+
```
169+
170+
### Available content components
171+
172+
| Component | Import | Usage |
173+
| ------------- | -------------------------------------------- | ------------------------------------------------------------------------- |
174+
| `<Callout>` | `$lib/components/molecules/Callout.svelte` | `<Callout type="info">...</Callout>` (types: `info`, `warning`, `danger`) |
175+
| `<CodeBlock>` | `$lib/components/molecules/CodeBlock.svelte` | Fenced code with syntax highlighting |
176+
| `<Image>` | `$lib/components/atoms/Image.svelte` | Optimised images (preferred over `<img>`) |
177+
178+
## Reference
179+
180+
Look at `src/routes/blog/vortex-rust-bittorrent-client-review/` as a complete reference implementation.
Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
---
2+
name: add-component
3+
description: Creates a new Svelte UI component for the Torrust website. Use when asked to add, create, or build a new reusable UI component, widget, or UI element.
4+
---
5+
6+
# Add Component
7+
8+
## When to use this skill
9+
10+
Use this skill when asked to create a new reusable UI component for the Torrust website.
11+
12+
## Component tiers
13+
14+
Place the new component in the correct directory based on its complexity:
15+
16+
| Tier | Directory | Description |
17+
| ------------- | -------------------------------- | ------------------------------------------------------- |
18+
| **Atom** | `src/lib/components/atoms/` | Basic building blocks: Button, Card, Image, Tag, etc. |
19+
| **Molecule** | `src/lib/components/molecules/` | Combinations of atoms: BlogPostCard, Callout, CodeBlock |
20+
| **Organism** | `src/lib/components/organisms/` | Complex sections: Header, Footer, Hero, Post |
21+
| **Singleton** | `src/lib/components/singletons/` | Unique, one-off components: SearchBar, ShareButton |
22+
23+
## Steps
24+
25+
### 1. Create the component file
26+
27+
Use kebab-case for the filename. Inside the component, use **Svelte 5 runes syntax**:
28+
29+
```svelte
30+
<script lang="ts">
31+
// Use $props() instead of export let
32+
interface Props {
33+
label: string;
34+
variant?: 'primary' | 'secondary';
35+
}
36+
37+
let { label, variant = 'primary' }: Props = $props();
38+
39+
// Use $state, $derived, $effect for reactivity
40+
let count = $state(0);
41+
let doubled = $derived(count * 2);
42+
</script>
43+
44+
<div class="my-component">
45+
{label}
46+
</div>
47+
48+
<style lang="scss">
49+
@use '$lib/scss/breakpoints.scss' as bp;
50+
51+
.my-component {
52+
/* component styles */
53+
}
54+
</style>
55+
```
56+
57+
**Style guidelines:**
58+
59+
- Use Tailwind utility classes where possible
60+
- Keep custom `<style>` blocks minimal
61+
- Use Sass (`lang="scss"`) only when needed for variables/mixins
62+
63+
**TypeScript guidelines:**
64+
65+
- Always type props via an interface
66+
- Always type function parameters and return values (strict mode is on)
67+
- Single quotes, no semicolons (Prettier enforces this)
68+
69+
### 2. Add a Histoire story (optional but recommended)
70+
71+
Create a `*.story.svelte` file alongside the component to preview it in isolation:
72+
73+
```svelte
74+
<script lang="ts">
75+
import { Story, Template } from 'histoire/client';
76+
import MyComponent from './MyComponent.svelte';
77+
</script>
78+
79+
<Story name="Default">
80+
<Template>
81+
<MyComponent label="Hello" />
82+
</Template>
83+
</Story>
84+
```
85+
86+
Run `npm run story:dev` to open the Histoire storybook.
87+
88+
### 3. Export the component (if publicly available)
89+
90+
If the component should be importable as `$lib/...`, export it from `src/lib/index.ts`:
91+
92+
```typescript
93+
export { default as MyComponent } from './components/atoms/MyComponent.svelte';
94+
```
95+
96+
### 4. Verify and lint
97+
98+
```bash
99+
npm run check # TypeScript + Svelte type checking
100+
npm run lint # Prettier + ESLint
101+
```
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
---
2+
name: deploy-site
3+
description: Deploys the Torrust website to GitHub Pages. Use when asked to deploy, publish, or release the site, or to set up the deployment workflow.
4+
---
5+
6+
# Deploy Site
7+
8+
## When to use this skill
9+
10+
Use this skill when asked to deploy the Torrust website to production (GitHub Pages).
11+
12+
## Automatic deployment (recommended)
13+
14+
Deployment is triggered automatically when you push to the `develop` branch:
15+
16+
1. The `.github/workflows/deploy.yml` workflow runs
17+
2. Installs dependencies and runs `npm run build`
18+
3. Creates a `.nojekyll` file in `build/`
19+
4. Uploads the build artifact and deploys to GitHub Pages
20+
21+
The live site is available at <https://torrust.com/>.
22+
23+
## Manual deployment
24+
25+
```bash
26+
npm run build && npm run deploy
27+
```
28+
29+
## Requirements
30+
31+
- GitHub Pages must be enabled in the repository settings
32+
- A `CNAME` file must exist at the repo root for the custom domain
33+
- The workflow requires `pages: write` and `id-token: write` permissions
34+
35+
## Verify the deployment
36+
37+
After deploying, check <https://torrust.com/> to confirm the changes are live.

.github/skills/run-checks/SKILL.md

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
---
2+
name: run-checks
3+
description: Runs the full quality check suite for the Torrust website. Use before committing or opening a PR to verify types, linting, and that the build succeeds.
4+
---
5+
6+
# Run Checks
7+
8+
## When to use this skill
9+
10+
Use this skill before committing changes or opening a pull request to ensure everything passes.
11+
12+
## Steps
13+
14+
Run these in order:
15+
16+
```bash
17+
# 1. Test locally in dev mode
18+
npm run dev
19+
# Open http://localhost:5173/ and verify the changes look correct
20+
21+
# 2. Type check (TypeScript + Svelte)
22+
npm run check
23+
24+
# 3. Lint (Prettier format check + ESLint)
25+
npm run lint
26+
27+
# 4. Production build
28+
npm run build
29+
30+
# 5. Preview the production build
31+
npm run preview
32+
# Open http://localhost:4173/ and verify again
33+
```
34+
35+
## Fix common issues
36+
37+
- **Type errors** — fix reported TypeScript/Svelte errors, then re-run `npm run check`
38+
- **Lint errors** — run `npm run format` to auto-fix formatting, then re-run `npm run lint`
39+
- **Build failures** — read the Vite output carefully; usually caused by type errors or missing imports
40+
41+
## Quick pre-commit shortcut
42+
43+
If you only need to verify types and lint (no full build):
44+
45+
```bash
46+
npm run check && npm run lint
47+
```

0 commit comments

Comments
 (0)