Today I published my first plugin on WordPress.org: TOCguide. It is a Gutenberg Table of Contents block with an optional Reading Guide. No accounts, no tracking, no outside APIs. The listing is live at version 1.6.2.

Why this took AI to finish
I am a dad of twins. I also have a full-time job and a freelance WordPress side gig. Evenings and weekends are short. Shipping a Plugin Directory product the old way — solo coding every item on your to-do list, every PHPCS pass, every readme tweak, every packaging step — would have sat on a list for years. In other words, time is precious and limited to have a work / life balance.
I used AI tools on purpose: Cursor (including cloud agents on the repo), Grok Bot for site and content work around matthummel.com, and normal Git + GitHub Actions so the machine checks stayed honest. I am not saying AI replaces judgment. I am saying it compressed weeks of typing and rework into hours I actually had.
People are right to be careful with AI. Used poorly, it invents APIs, ships insecure PHP, and fights the WordPress.org guidelines. Used with rules, reviews, and CI, it can raise daily output without lowering the bar. This post is my notes from the first time I took a plugin all the way to the directory. Feedback and pushback are welcome in the comments.
What TOCguide is
TOCguide is a single-purpose block plugin. Display name TOCguide. Slug, folder, text domain, shortcode, and CSS prefix are all tocguide. That one identity mattered for WordPress.org guideline 17 and for long-term support.
- Standard TOC: heading levels, nested lists, five Block Styles, sticky/collapse, scroll-spy, smooth scroll offset, auto-insert,
shortcode, accessible<nav>. - Optional Reading Guide: section previews, density bars, read-time badges, reading progress, author notes, localStorage reactions and citations — all from post content and meta, not remote services.
- Builder fallbacks for Elementor, Bricks, and HTML-based themes so headings stay linkable.
- License: GPLv2 or later. Privacy: no phone-home.
Docs live on GitHub Pages: matthummel-pa.github.io/tocguide. Source: github.com/matthummel-pa/tocguide.



The setup: Cursor, Git, CI, WordPress
Cursor rules and cloud agents
The repo is set up so an agent does not guess WordPress policy. Under .cursor/rules/ I keep always-on rules for block coding, brand typography, and the WordPress.org Plugin Directory FAQ plus the 18 guidelines. A CLAUDE.md file states the product intent in plain language: one focused block, dynamic render, helpers in includes/, never declare functions in src/render.php, freemium path without crippling the free plugin.
Cursor cloud agents handled large slices of work as pull requests — packaging for WordPress.org, naming and slug alignment, visual styling passes, and review fixes. I still read every PR. The agent drafts. I decide what merges.
Git and GitHub Actions
Main stays green with a CI workflow that runs on push and pull request: Node from .nvmrc, npm ci, JS and CSS lint, npm run build, PHP lint, and PHPCS via Composer. Tags like v1.6.2 fire a Release workflow that builds the plugin zip with @wordpress/scripts plugin-zip and attaches it to a GitHub Release. That zip is what goes through SVN to the directory after review.
name: Release
on:
push:
tags: ['v*.*.*']
jobs:
zip:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- uses: actions/setup-node@v7
with:
node-version-file: .nvmrc
cache: npm
- run: npm ci
- run: npm run build
- run: npm run plugin-zip
# attaches tocguide.zip to the GitHub Release for that tagExact file: .github/workflows/release.yml in the repo. CI twin is .github/workflows/ci.yml.
Block architecture
TOCguide is a dynamic block: block.json (apiVersion 3), save.js returns null, and src/render.php prints the nav. Heading maps and markup live in TOCguide_Headings. That split keeps render thin and PHPCS happier.
{
"apiVersion": 3,
"name": "tocguide/table-of-contents",
"title": "Table of Contents",
"textdomain": "tocguide",
"styles": [
{ "name": "default", "label": "Default", "isDefault": true },
{ "name": "minimal", "label": "Minimal" },
{ "name": "boxed", "label": "Boxed" },
{ "name": "underline", "label": "Underline" },
{ "name": "card", "label": "Card" }
],
"editorScript": "file:./index.js",
"viewScript": "file:./view.js",
"render": "file:./render.php"
}<?php
/**
* Server-side render for the Table of Contents block.
* Do not declare functions here — this file is included on every render.
*/
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
$tocguide_post_id = 0;
if ( isset( $block ) && $block instanceof WP_Block && ! empty( $block->context['postId'] ) ) {
$tocguide_post_id = (int) $block->context['postId'];
}
if ( ! $tocguide_post_id ) {
$tocguide_post_id = (int) get_the_ID();
}
if ( ! $tocguide_post_id ) {
return;
}
echo TOCguide_Headings::render_nav( $attributes, $tocguide_post_id, true );Bootstrap is ordinary WordPress: headers, constants, class requires, singleton tocguide()->boot().
define( 'TOCGUIDE_VERSION', '1.6.2' );
define( 'TOCGUIDE_FILE', __FILE__ );
define( 'TOCGUIDE_DIR', plugin_dir_path( __FILE__ ) );
require_once TOCGUIDE_DIR . 'includes/class-tocguide-settings.php';
require_once TOCGUIDE_DIR . 'includes/class-tocguide-headings.php';
require_once TOCGUIDE_DIR . 'includes/class-tocguide-plugin.php';
function tocguide() {
return TOCguide_Plugin::instance();
}
tocguide()->boot();
Where Grok Bot fits
Cursor owns the plugin code. Grok Bot owns a different lane for me: matthummel.com drafts, Rank Math, media, and turning repo facts into readable posts like this one. Same person reviewing both. Different tools for code vs. site content. That split keeps AI from rewriting production PHP because a blog draft asked for a metaphor.
What the work actually looked like
- Product core: dynamic TOC block, settings, shortcode, auto-insert, heading ID injection with
WP_HTML_Tag_Processor. - Reading Guide and study tools: server-side previews and read time, optional localStorage features, builder heading parsers.
- Directory readiness: Plugin Check cleanup, readme parser limits, trademark-safe tags, no front-end “powered by,” text domain literal
tocguide. - Naming pass: one slug everywhere after earlier display-name confusion — re-insert blocks after the 1.5.0 identity change.
- Design polish: theme-style conflicts (list counters printing “0.”), exclude-theme-styles mode, settings tabs with live preview (1.6.x).
- Release path: tag → zip → SVN assets (banner, icon, screenshots) → directory listing.
None of that is “prompt once and ship.” It is many short sessions: agent proposes, CI fails or Plugin Check complains, I fix the rule or the code, repeat. AI sped the loop. It did not remove the loop.
An honest take on AI
What helped:
- Written rules in the repo so agents follow Gutenberg and WordPress.org constraints instead of inventing patterns.
- CI as a second reviewer that never gets tired of PHPCS or lint.
- Cloud agents for packaging and repetitive compliance edits while I was with the kids or on client work.
What still needs a human:
- Product taste (what belongs in free vs later).
- Security and escaping reviews.
- Reading the official guidelines yourself before you argue with a reviewer.
- Saying no when a suggestion would phone home, load a CDN script, or lock features behind a paywall on .org.
I am not claiming AI is magic or that every developer should use the same stack. I am claiming that for a busy parent shipping a first directory plugin, the time cost used to be the blocker — and tooling that respects WordPress standards can change that. If your experience differs, I want to hear it.
Try it and talk back
Install TOCguide from WordPress.org, or grab a release zip from GitHub Releases. Docs and support notes are on the docs site. Issues and ideas: GitHub Issues.
If you build plugins with AI, or you refuse to, leave a comment. What worked, what broke review, what you would never trust an agent with — I read them. Same for agencies looking for quiet WordPress help in the background: the Hire page is the short version of how I work.
— Matt

Comments
No comments yet. ASCII, code, and plain punctuation are welcome.