AGENTS.md
AGENTS.md
This file provides guidance to coding agents when working with code in this repository.
About
Personal website for Saverio Ferrara — a Software Engineer & Technology Lead based in Rome, Italy. The site serves as a resume, portfolio, and blog. Its primary goal is to stay relevant in the community and support employability. Built with Jekyll and hosted on GitHub Pages.
Repository Structure
.
├── _config.yml # Jekyll config: collections, theme, plugins, permalinks
├── Gemfile # Ruby deps (github-pages gem, minima theme, jekyll plugins)
├── Makefile # Build/serve targets (see Build & Serve)
├── index.md # Home page
├── about.md # ┐
├── resume.md # ├ Top-level pages (also the nav order via header_pages)
├── work.md # │
├── contact.md # ┘
├── archive.md # ┐
├── categories.md # ├ Blog index pages
├── tags.md # ┘
├── _data/
│ ├── links.yml # Canonical internal links (site.data.links)
│ └── skills.yml # Canonical skill vocabulary (site.data.skills)
├── _posts/ # Blog posts, organized into year folders (2006–2026, 46 posts)
├── _drafts/ # Unpublished blog post drafts
├── _templates/ # Reusable entry scaffolds (ignored by Jekyll)
├── _resume/ # Collection: resume entries
│ ├── education/ # one folder per qualification
│ └── experience/ # one folder per role
├── _work/ # Collection: portfolio entries
│ ├── projects/ # featured under "Featured" in work.md
│ ├── certifications/ # ┐
│ ├── courses/ # ├ shown under "Learnings" in work.md
│ ├── books/ # │
│ └── talks/ # ┘
├── pages/ # Static pages (404.html, legal/)
├── assets/
│ ├── css/ # Small custom CSS files (headshot, CTA button, style)
│ ├── img/ # Images
│ ├── downloads/ # Downloadable files (resume PDFs, etc.)
│ ├── logo/ # Logo assets
│ └── main.scss # SCSS entry point
├── specs/ # Task specs (spec-manager) for site changes
├── .devcontainer/ # Dev container definition
└── _site/ # Generated output (gitignored — never edit by hand)
Build & Serve
make tools # one-time: install bundler
make # install deps + serve locally (http://localhost:4000)
make install # bundle install (vendors gems to vendor/bundle/)
make serve # jekyll serve --incremental --baseurl=""
make clean # remove _site, .jekyll-cache, .sass-cache, .jekyll-metadata
make nuke # clean + remove vendor/bundle
make update # bundle update github-pages
After editing _config.yml, restart the server (incremental serve won’t pick up config changes).
Ruby version is pinned to 3.1.0 (.ruby-version).
Architecture
Jekyll site using the minima theme (~> 2.5) via the github-pages gem (~> 232). Active plugins: jekyll-feed, jekyll-seo-tag, jekyll-sitemap. There are no _layouts/ or _includes/ directories — the site relies entirely on minima’s defaults plus per-type layout defaults declared in _config.yml (page, post, default).
Collections
Two custom collections (declared in _config.yml) alongside the standard _posts/:
-
_resume/— Education and work experience entries. Each.mdhas front matter withtitle,org,start-date,end-date,employment-type,location,location-type,highlights(list),skills(list), andcategories(eitherexperienceoreducation). Rendered byresume.mdusing Liquid loops. Skills MUST use the canonical vocabulary defined in_data/skills.yml. -
_work/— Projects, certifications, courses, books, and talks. Each.mdhastitle,date, andcategories(one of:projects,certifications,courses,books,talks). Items with categoryprojectsare listed under “Featured” inwork.md; all others appear under “Learnings”. Binary assets (images, PDFs) sit alongside their.mdfiles. -
_posts/— Standard Jekyll blog, organized into year subfolders (_posts/2006/…_posts/2026/), 46 posts at time of writing. Surfaced througharchive.md,categories.md, andtags.md.
Key Files
_config.yml— Collections, permalinks (pretty),header_pagesnav order, plugin list, per-type layout defaults_data/links.yml— Canonical internal links used across pages viasite.data.links_data/skills.yml— Canonical skill vocabulary, exposed assite.data.skills; the only allowed source for resume/projectskillsfront matterabout.md,resume.md,work.md,contact.md— Top-level pages (also the nav order viaheader_pages)archive.md,tags.md,categories.md— Blog index pagesassets/css/— Small custom CSS files (headshot, CTA button); main styling from minima_drafts/— Unpublished blog post drafts_templates/— Reusable entry scaffolds (template.md,template-project.md,template-certification.md,template-education.md). Like_share/, it starts with_so Jekyll ignores it and it never leaks into_site/
Conventions
- Permalinks use
prettystyle:/collection/path/(trailing slash, no.html) - Work items reference showcase images using a Liquid snippet that derives the image filename from the page’s own filename, e.g. `` (see
_templates/template-project.md). Project showcase assets are a mix of.pngand.jpg; match the asset that exists alongside the page. - Internal cross-references between pages use
site.data.linksrather than hardcoded paths - The
work.md“Playgrounds” section auto-lists the owner’s public GitHub repos viasite.github.public_repositories(populated by GitHub Pages metadata at build time)
AI Skills (.claude/skills/)
Three project skills automate the blog authoring and distribution pipeline. They live in .claude/skills/ and are discovered automatically when running claude from the repo root. All three read VOICE.md (repo root) as the source of truth for Saverio’s blog voice before writing or reviewing prose.
| Skill | Trigger | What it does |
|---|---|---|
blog-post-writer |
“draft a post about X”, “blog about X”, “turn these notes into a post”, “park a post idea” | Expands a one-liner or public source into a parked draft at _drafts/<slug>.md. Never publishes. Source must be public only — never vault/private content. |
blog-post-reviewer |
“review the draft”, “validate the post”, “is this ready”, “any broken links”, “promote this draft” | Read-only review: front-matter validation, broken-link check, convention check, voice heuristic, make serve build probe. Returns a checklist report. Provides promote commands only after all blocking issues clear. |
blog-post-promoter |
“promote this post”, “make a teaser”, “drip series”, “repurpose for LinkedIn”, “atomize this post” | Atomizes one live _posts/ file into a parked share-pack at _share/<slug>/: teaser.md (LinkedIn link-out), standalone.md (LinkedIn in-feed), drip/NN.md (one per ## section), cross-post.md (full body, Liquid resolved, canonical header). Text only — posting is manual paste. |
Distribution front-matter block
Posts may include this optional block. Jekyll ignores unknown keys; it is safe to add to any post.
canonical_url: https://fsferrara.github.io/<year>/<month>/<day>/<slug>/ # self-canonical; jekyll-seo-tag emits <link rel=canonical>
syndicate:
devto: false # flip to true after Dev.to paste
medium: false # flip to true after Medium import
linkedin: false # flip to true after posting teaser/standalone
canonical_url ensures SEO credit stays with the blog regardless of where copies live. syndicate: is a greppable distribution ledger — grep -r "devto: false" _posts/ shows what is still unsynced.
_share/ directory
_share/<slug>/ holds share-packs generated by blog-post-promoter. It starts with _ so Jekyll ignores it and it never leaks into _site/. Post any snippet from _share/ whenever a spare moment appears — the pack is generated once and drips over weeks.
Development Environment
- Dev container available (
.devcontainer/devcontainer.json) based onmcr.microsoft.com/devcontainers/jekyll:2-bullseye - VS Code spell-checking configured for English (
.vscode/settings.json) - Ruby deps vendored to
vendor/bundle/(gitignored);Gemfile.lockis also gitignored because GitHub Pages builds with its own pinned gem versions