Skip to content
Back to skills

Mf

ASecurity

Use mf (metafunctor) to manage papers, projects, series, and content for the metafunctor.com Hugo site. Also covers site architecture, taxonomies, front matter conventions. For cross-posting, use the crier plugin. Invoke from ANY repo; mf works globally.

  • 2 stars
  • 0 votes
  • 0 copies
  • 0 views
  • Added October 19, 2026
ai-agentspythongobashgitdatabasedocumentation

Works with

  • cli

Security analysis

A100/100

Scanned October 19, 2026

npx -y skills add queelius/claude-anvil --skill mf --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Mf?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Mf
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/queelius-claude-anvil/badge)](https://www.skillsdirectory.com/skills/queelius-claude-anvil)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: mf
description: Use mf (metafunctor) to manage papers, projects, series, and content for the metafunctor.com Hugo site. Also covers site architecture, taxonomies, front matter conventions. For cross-posting, use the crier plugin. Invoke from ANY repo; mf works globally.
argument-hint: "[intent, e.g. 'new post about X', 'sync papers', 'health check']"
---

# mf — Metafunctor Site Management

A CLI toolkit + site architecture guide for metafunctor.com. Works from any directory.

**Division of labor:**
- **mf** does mechanics: database CRUD, Hugo content generation, metadata operations
- **Claude** does judgment: choosing commands, interpreting intent, sequencing workflows, writing content

**Install location:** `~/github/repos/mf` (install on PATH: `uv tool install -e ~/github/repos/mf`)
**Site root:** `~/github/repos/metafunctor`
**Site URL:** `https://metafunctor.com` (mirror: `https://queelius.github.io/`)
**Engine:** Hugo with Ananke theme
**Deployment:** `docs/` committed to master, served by GitHub Pages

## Preflight: make sure `mf` is the right binary

TeX Live ships a `/usr/bin/mf` (Metafont) that shadows the metafunctor CLI when the latter is not earlier on PATH. Before the first `mf` command in a session, run `mf --version` and expect `mf, version ...`. Anything else (Metafont prints a `This is METAFONT` banner or waits for input) means the metafunctor mf CLI is not on PATH: stop and tell the user to install it with `uv tool install -e ~/github/repos/mf` (which puts it in `~/.local/bin`), rather than running commands against the wrong program.

## Site Architecture

### Content Sections

| Directory | Type | Notes |
|-----------|------|-------|
| `content/post/` | Blog posts | Date-prefixed dirs: `YYYY-MM-DD-slug/index.md` (what `mf posts create` writes; older posts use the legacy `YYYY-MM-slug` form) |
| `content/papers/` | Research papers | Generated from paper_db via `mf papers generate` |
| `content/publications/` | Publication lifecycle registry | Independent pubs_db managed via `mf pubs` (add, update, log, pull); NOT derived from paper_db at runtime |
| `content/projects/` | Software projects | Generated from projects_db via `mf projects generate` |
| `content/series/` | Multi-part series | Landing pages with `_index.md` |
| `content/writing/` | Long-form fiction and books | `writing_type` values in use: `novel`, `novella`, `novelette`, `nonfiction`, `short-story-collection` (check `content/writing/` before adding a new one) |
| `content/essays/` | Essays | Essays live here, not under `writing/` |
| `content/medical/` | Medical records | Custom sidebar layout, Chart.js for labs |
| `content/research/` | Research overviews | |
| `content/probsets/` | Problem sets | Organized by course |
| `content/media/` | Book/resource reviews | |

Other sections exist (`computer-science/`, `reading/`, `arcade/`, `youtube/`, `packages/`, `featured/`, `infinigram/`, plus site pages like `about/`). `ls content/` in the site root is the authoritative list; read a section's `_index.md` before adding to it.

**Content type separation is strict:** fiction goes in `/writing`, not `/papers`.

### Taxonomies

| Taxonomy | URL | Purpose |
|----------|-----|---------|
| `tags` | `/tags/` | Technical keywords (kebab-case) |
| `categories` | `/categories/` | Broad content categories |
| `genres` | `/genres/` | Document types (paper, novel, etc.) |
| `series` | `/series/` | Multi-part content grouping |
| `linked_project` | `/linked_project/` | Links content to projects |
| `linked_book` | `/linked-books/` | Links posts to books in `content/writing/` |

**Critical:** Use `linked_project` (NOT `projects`) — URL must not conflict with `content/projects/`.
Use slugs, not paths:
```yaml
# Correct
linked_project: ["likelihood.model"]
# Wrong — causes Hugo panic
linked_project: ["/projects/likelihood.model/"]
```

### Front Matter Patterns

**Posts:**
```yaml
title: "Post Title"
date: 2026-02-13
description: "Card preview text"
categories: ["Computer Science"]
tags: ["algorithms", "data-structures"]
series: ["series-slug"]
series_weight: 5
featured: true
featured_image: "cover.png"   # checked by `mf health images`
toc: true
```

**Papers:** `stars` (1-5), `pdf_file` (filename in `static/latex/`), `html_path`, `authors`, `abstract`
**Projects:** run `mf projects fields` for the authoritative field list, types, and allowed values (it includes newer fields such as `documentation_url`); `mf papers fields`, `mf series fields`, and `mf packages fields` do the same for their databases.
**Series membership (in posts):** `series: ["slug"]`, `series_weight: N`

### Bundle Types

- **Leaf bundle** (`index.md`): Most content. Self-contained page.
- **Branch bundle** (`_index.md`): Rich projects, series landing pages. Can have child pages.

Rich projects use `_index.md`. Regular projects use `index.md`.

### Build & Deploy

```bash
make serve          # Dev server with drafts (localhost:1313)
make build          # Build to docs/ (the production output dir, per hugo.toml publishDir)
make deploy         # Build to docs/ for production (also refreshes static/CNAME and docs/.nojekyll)
make push           # deploy, then commit docs/ and git push to GitHub Pages
```

There is no separate test output dir: `make build` overwrites `docs/`. Use `make serve` (or `hugo --gc --minify --destination /tmp/<dir>`) to check a build without touching `docs/`.

`relativeURLs = false`: the production build uses `baseURL = https://metafunctor.com/`. The `queelius.github.io` mirror is built separately by the site's GitHub Action (`.github/workflows/mirror-to-user-site.yml`) with its own `--baseURL`.

### Content Source Model

| Content | Ground Truth | mf Role | Has Database? |
|---------|-------------|---------|---------------|
| Projects | GitHub repos | DB + overrides + generate | Yes (projects_db.json) |
| Papers | LaTeX sources | DB + sync + generate | Yes (paper_db.json) |
| Series | mf series_db | DB + sync + landing pages | Yes (series_db.json) |
| Publications | Venue lifecycle (submissions, acceptances, DOIs) | `mf pubs` add/update/log/pull/check | Yes (pubs_db.json, independent of paper_db) |
| Packages | PyPI/CRAN registries | DB + sync + generate | Yes (packages_db.json) |
| Posts | The .md file | `mf posts` (list, create, set, tag, feature) | No |
| Writing | The .md file | None | No |
| Medical | The .md file | None | No |

**Key principle:** Databases exist only for content whose ground truth lives elsewhere.

## Intent Mapping

When users describe what they want, map to the right mf workflow:

| User says... | Workflow |
|-------------|----------|
| "new paper", "add a paper" | Paper workflow (below) |
| "new project", "add repo" | Project workflow (below) |
| "new post", "write about X" | Post workflow (below) |
| "health check", "audit site" | Health pass (below) |
| "deploy", "push to prod" | Pre-deploy (below) |
| "feature X", "star this paper" | `mf {type} feature <slug>` |
| "tag X with Y" | `mf {type} tag <slug> --add Y` |
| "sync papers" | `mf papers status` (staleness), then `mf papers ingest <slug>` + `mf papers generate --slug <slug>` |
| "sync projects" | `mf projects sync` |
| "cross-post", "share on socials" | Use `/crier` skill instead |
| "what content do I have" | `mf analytics summary` |
| "find broken links" | `mf health links` |
| "clean up tags" | `mf taxonomy audit`, then `mf taxonomy normalize --from <old> --to <new> [--field tags\|categories] --dry-run`, then rerun without `--dry-run` |

### New Paper
1. Build the paper in its own repo first (`make html pdf` there); the slug must already exist in paper_db
2. `mf papers ingest <slug>` (pulls the built HTML/PDF into the site)
3. `mf papers set <slug> stars 5` (and any other metadata)
4. `mf papers generate --slug <slug>`
5. If peer-reviewed, register it in the independent publications registry: `mf pubs add <slug>` / `mf pubs update`, then `mf pubs generate` (the registry does not render on its own)
6. `make deploy`
7. Cross-post: use `/crier`

Check staleness anytime with `mf papers status`; preview drift with `mf papers diff`.

### New Project
1. Add to `.mf/projects_db.json` (or `mf projects import`)
2. `mf projects sync`
3. `hugo --gc --minify` (verify)
4. `make deploy`

### New Post
1. `mf posts create --title "Title" --tag x --category Y`
2. Edit `content/post/YYYY-MM-DD-slug/index.md` — write content
3. `mf posts set <slug> draft false` (when ready)
4. `make deploy`
5. Cross-post: use `/crier`

### Health Pass
1. `mf health links` — broken internal links
2. `mf health descriptions` — missing descriptions
3. `mf health images` — missing featured images
4. `mf health stale` — content diverged from DB
5. `mf integrity check` — database consistency
6. `mf content audit --extended` — pluggable content checks
7. `mf taxonomy audit` — near-duplicate terms

### Pre-Deploy
1. `mf status`: any render drift between DBs and generated pages?
2. `mf integrity check`: database ok?
3. `hugo --gc --minify`: Hugo build errors?
4. `mf health links`: no broken links?
5. `make deploy`: build to docs/
6. `make push`: push to GitHub Pages

## Decision Guidance

### Command Group Cheat Sheet

| Group | Key verbs | Notes |
|-------|-----------|-------|
Most DB groups share a CRUD core: `list`, `show`, `set`, `unset`, `feature`, `tag`, `fields`, `stats`, `diff`. The table lists each group's distinctive verbs on top of that core; `mf <group> --help` is authoritative.

| Group | Distinctive verbs | Notes |
|-------|-------------------|-------|
| `papers` | ingest, generate, status, zenodo, fetch-cff, cff-status | Built paper repo → DB → Hugo |
| `projects` | import, sync, generate, refresh, hide, make-rich, list-rich, clean, fetch-codemeta, codemeta-status, rate-limit | GitHub → DB → Hugo |
| `series` | create, sync, scan, add, remove, delete, add-featured, add-link, add-related, artifacts, audit, audit-nav, classify-frontmatter | DB → landing pages; `sync` pulls by default (`--push` to push, `--ours`/`--theirs` for conflicts, `--interactive`) |
| `packages` | add, remove, sync, generate | PyPI/CRAN → DB → Hugo |
| `posts` | create, list, set, unset, tag, feature | Direct front matter edits (no DB) |
| `pubs` | add, update, log, pull, check, generate, zenodo | Independent lifecycle registry (`migrate` is a one-time paper_db seed) |
| `content` | match-projects, about, list-projects, audit | Cross-content linking |
| `taxonomy` | audit, orphans, stats, normalize | Tag/category hygiene |
| `health` | links, descriptions, images, drafts, stale | Content quality checks |
| `analytics` | summary, gaps, tags, timeline, suggestions, projects | Content insights |
| `integrity` | check, fix, orphans | Database consistency |
| `backup` | list, rollback, clean, status | Backup management |
| `config` | show, get, set, path, reset | Configuration |
| (top level) | `init`, `status` | `init` creates `.mf/`; `status` is the read-only render-drift dashboard |

### Generate vs Sync

- **generate** = rebuild Hugo pages from database (safe, idempotent)
- **sync** = pull from external sources into database, then generate (may change DB)
- When in doubt, `generate` first; it's read-only on the database
- Preview drift before acting: `mf status` (render-drift dashboard across papers, projects, packages, pubs) or `mf <group> diff` for one group

### Ordering Rules

- Always `sync` before `generate` if you want fresh data
- Always `generate` (or `sync`) before `make deploy`
- Papers and publications are independent registries at runtime: a paper change does NOT auto-update pubs (use `mf pubs update`/`log` when venue status changes; `mf pubs migrate` exists only as a one-time seed from paper_db)

### Tips

- `--regenerate` (auto-rebuild Hugo pages after the edit) exists on `papers set` and `projects set` only
- Use `mf --help` and `mf <group> --help` at runtime for exact flags
- Dry-run is the global `mf -n`/`mf --dry-run` flag (plus select subcommands like `content audit`)
- `--json` output available on most `list` and health commands

## Cross-Repo Usage

The mf CLI resolves the site root in this order:
1. `MF_SITE_ROOT` environment variable (highest priority)
2. Walk up from cwd looking for `.mf/` directory
3. Global config at `~/.config/mf/config.yaml`

```bash
# One-time setup (from any directory; or run `mf init` in the site repo):
mkdir -p ~/.config/mf
echo "site_root: ~/github/repos/metafunctor" > ~/.config/mf/config.yaml

# Now all commands work from any directory:
cd ~/github/repos/likelihood.model
mf posts create --title "New release"     # Creates in metafunctor/content/post/
mf posts list --tag python                # Lists metafunctor posts
```

## Hand-off to crier

Content flows **mf (create/update) → Hugo (render) → crier (distribute)**. After content changes are deployed, suggest cross-posting and hand off to the crier plugin (`/crier`); it owns the publishing workflow, rewrites, and failure handling.

## Gotchas

- `docs/` is committed to git (GitHub Pages). Don't gitignore it.
- Rich projects use `_index.md` (branch bundle) vs regular use `index.md`.
- `linked_project` taxonomy, NOT `projects` — URL collision with content section.
- Series landing pages: external source takes priority over local `_index.md`.
- PyPI names sometimes differ from repo names (e.g., `soprano` → `soprano-tts`).
- R-universe URLs: `queelius.r-universe.dev/PACKAGE` (author subdomain, not package).
- Posts use `content/post` (singular), not `content/posts`.

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.