The Command System: Two Commands That Run Everything
My entire publishing workflow is two commands: /write-post and /publish-post. Everything else is orchestration.
The Philosophy
Commands should do one thing well. But that one thing can be complex.
/write-post handles everything about creating content.
/publish-post handles everything about deploying it.
Clear separation. No overlap. Run them in sequence or independently.
Command 1: /write-post
Purpose: Create a new blog post from scratch or from an outline.
What it does:
/write-post "Title of My Post"
│
▼
┌─────────────────────────────────────┐
│ 1. CHECK EXISTING CONTENT │
│ - Does this post already exist? │
│ - Is there an outline to use? │
└─────────────────────────────────────┘
│
▼
┌─────────────────────────────────────┐
│ 2. GENERATE FRONTMATTER │
│ - title: from argument │
│ - date: today's date │
│ - part: ask or infer │
│ - readTime: estimate from length │
│ - tags: suggest relevant ones │
└─────────────────────────────────────┘
│
▼
┌─────────────────────────────────────┐
│ 3. CREATE FILE │
│ - Next available number │
│ - Slugified title │
│ - Path: posts/XX-slug.md │
└─────────────────────────────────────┘
│
▼
┌─────────────────────────────────────┐
│ 4. DRAFT CONTENT │
│ - If outline exists, follow it │
│ - If not, create structure │
│ - Match blog voice/style │
└─────────────────────────────────────┘
│
▼
┌─────────────────────────────────────┐
│ 5. REVIEW │
│ - Show the draft │
│ - Ask for changes │
│ - Iterate until approved │
└─────────────────────────────────────┘
Example:
User: /write-post "Rate Limiting Without State"
PAI: I found an outline for this topic in outlines-and-drafts/.
Using that as the foundation.
Creating: blog-site/src/content/posts/44-rate-limiting-stateless.md
Frontmatter:
- title: "Rate Limiting Without State: The MCP Paradox"
- date: "2026-01-20"
- part: 4
- readTime: "12 min"
- tags: ["mcp", "rate-limiting", "architecture"]
[Drafts the post...]
Ready for review. Want me to make any changes?
Command 2: /publish-post
Purpose: Deploy a post to the blog and optionally cross-post to Substack.
What it does:
/publish-post [path-or-number]
│
▼
┌─────────────────────────────────────┐
│ 1. IDENTIFY THE POST │
│ - From argument: path or # │
│ - Or: most recently modified │
└─────────────────────────────────────┘
│
▼
┌─────────────────────────────────────┐
│ 2. VALIDATE │
│ - Frontmatter complete? │
│ - Required fields present? │
│ - Build succeeds? │
└─────────────────────────────────────┘
│
▼
┌─────────────────────────────────────┐
│ 3. DEPLOY TO BLOG │
│ - git add │
│ - git commit │
│ - git push │
│ - Wait for Cloudflare deploy │
└─────────────────────────────────────┘
│
▼
┌─────────────────────────────────────┐
│ 4. CROSS-POST (if requested) │
│ - Open Substack │
│ - Create post │
│ - Paste content │
│ - Upload image │
│ - Publish │
└─────────────────────────────────────┘
│
▼
┌─────────────────────────────────────┐
│ 5. VERIFY │
│ - Blog URL live? │
│ - Substack URL live? │
│ - Report results │
└─────────────────────────────────────┘
Example:
User: /publish-post 43
PAI: Publishing post #43: "The Command System"
✓ Frontmatter valid
✓ Build succeeds
✓ Git commit: "Add post #43: The Command System"
✓ Pushed to origin
✓ Cloudflare deploy triggered
Cross-post to Substack?
User: Yes
PAI: ✓ Substack post created
✓ Content pasted
✓ Image uploaded
✓ Published
Live at:
- Blog: https://operationalsemantics.dev/posts/43-blog-meta-command-system
- Substack: https://myronkoch.substack.com/p/the-command-system
AutoRAG will index within 10 minutes.
The Outlines Workflow
Posts often start as outlines in outlines-and-drafts/:
outlines-and-drafts/
├── cloudflare-ai-pipeline-series-outline.md
├── blog-meta-tutorial-series-outline.md
├── local-voice-pai-outline.md
└── fire-for-prometheus-outline.md
When /write-post runs, it checks this directory first. If an outline exists for the topic, it uses that structure instead of starting from scratch.
The flow:
Idea → Outline (in outlines-and-drafts/)
│
▼
/write-post uses outline
│
▼
Draft post
│
▼
Review/edit
│
▼
/publish-post
│
▼
Move outline to published/
Outlines that become posts get moved to outlines-and-drafts/published/ to track what’s been used.
Frontmatter Auto-Completion
The commands handle frontmatter intelligently:
| Field | Auto-generated |
|---|---|
title | From command argument |
date | Today’s date |
part | Inferred from content or asked |
readTime | Calculated from word count |
description | Summarized from content |
tags | Suggested based on content |
author | Default: “Myron Koch” |
If something’s missing or wrong, the command asks before proceeding.
Build Verification
Before committing, /publish-post runs the build:
cd blog-site && npm run build
This catches:
- Frontmatter validation errors (Zod schema)
- Broken internal links
- Missing images
- Syntax errors in components
If the build fails, the command stops and shows the error. No broken deploys.
The Git Commit Format
Commits follow a consistent format:
Add post #43: The Command System
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This makes the git log readable:
60d5377 Add post #43: The Command System
04d2f62 Add post #42: AI-Powered Cross-Posting
94b5187 Add post #41: The Chatbot Architecture
02d2b03 Add post #40: Astro + Cloudflare Pages
9af3057 Add post #39: The Pipeline Vision
Error Handling at Each Phase
| Phase | Possible Error | Handling |
|---|---|---|
| Validate | Missing frontmatter | Prompt to add it |
| Build | Schema violation | Show error, fix, retry |
| Git | Merge conflict | Ask for resolution |
| Push | Auth failed | Prompt to re-auth |
| Substack | Login expired | Ask user to login |
| Upload | File not found | Ask for correct path |
| Publish | Rate limited | Wait and retry |
Each phase can fail independently. The command reports where it failed and what to do.
Silent vs. Loud Publishing
Two modes:
Silent drop: Publish without promotion
/publish-post 43 --silent
- Publishes to blog
- Publishes to Substack (if requested)
- Does NOT send email to subscribers
- Does NOT post to social
Loud launch: Full promotion
/publish-post 43 --announce
- Publishes everywhere
- Sends Substack email
- Drafts social posts
- Triggers any notification hooks
Default is silent. You choose when to make noise.
Batch Operations
For bulk publishing:
/publish-post 39 40 41 42 43 --silent
Publishes all five posts sequentially without email notifications. Useful for backfilling or silent drops.
Command Location
Commands live in the Claude Code skills directory:
~/.claude/
├── commands/
│ ├── write-post/
│ │ └── prompt.md # /write-post definition
│ └── publish-post/
│ └── prompt.md # /publish-post definition
└── skills/
└── substack-images/
└── SKILL.md # Image generation workflow
Each command is a prompt file that defines:
- What the command does
- The steps to execute
- Error handling
- Example usage
Why Commands, Not Scripts
Why not just write bash scripts?
Commands are conversational.
- They can ask clarifying questions
- They adapt to unexpected situations
- They explain what they’re doing
Scripts are rigid.
- They fail on edge cases
- They can’t make judgment calls
- They require exact inputs
The command system combines the reliability of automation with the flexibility of an assistant.
Adding New Commands
To add a new command:
- Create
~/.claude/commands/your-command/prompt.md - Define the command behavior
- It’s immediately available as
/your-command
No deployment. No compilation. Just add the file.
Summary
| Command | Purpose | Key Actions |
|---|---|---|
/write-post | Create content | Draft, frontmatter, review |
/publish-post | Deploy content | Validate, build, git, cross-post |
Two commands. Entire workflow.
The complexity is hidden. The interface is simple.
Next up: When RSS Parsing Ships - The last manual step disappears.