一键导入
freek-dev-blog
// Create and schedule link posts on freek.dev blog. Use when asked to publish, post, or share a link on freek.dev. Handles post creation, updating, and scheduling via the blog API.
// Create and schedule link posts on freek.dev blog. Use when asked to publish, post, or share a link on freek.dev. Handles post creation, updating, and scheduling via the blog API.
Create, schedule, and manage social media posts via Typefully. ALWAYS use this skill when asked to draft, schedule, post, or check tweets, posts, threads, or social media content for Twitter/X, LinkedIn, Threads, Bluesky, or Mastodon.
Update Spatie package documentation on spatie.be by re-importing docs for a given repo. Use after merging a PR that touches docs/, or when the user says 'update docs for [package]', 'rebuild docs for [repo]', 'import docs for [repo]', or invokes /update-spatie-docs [repo-name]. Takes a repo name argument (e.g., 'backup', 'laravel-medialibrary', 'laravel-pdf').
Generate beautiful code snippet images (PNG) for social media, tweets, LinkedIn, and documentation. Creates light-themed, macOS-style window chrome screenshots from code using HTML templates and Chrome headless. Use when asked to create code images, tweet images, snippet screenshots, code cards, or visual code examples for sharing. Supports single snippets, multi-image sets, and 2x2 grid composites.
Flux UI component library for Livewire. Use when building UIs with Flux components like buttons, inputs, modals, tables, forms, navigation, or any Livewire interface using the flux: Blade component prefix. Triggers include "Flux component", "flux:button", "flux:input", "Livewire UI components", or building admin/dashboard interfaces.
Laravel PHP framework conventions and best practices. Use when building Laravel applications, writing controllers, models, migrations, routes, middleware, form requests, policies, jobs, events, or any Laravel-specific code. Triggers include "Laravel", "Eloquent", "Artisan", "php artisan", routing, migrations, or working with Laravel projects.
Build Livewire 4 components and applications. Use when creating reactive Laravel interfaces, single-file components, multi-file components, page components, forms, actions, or any Livewire-based UI. Triggers include "Livewire component", "wire:model", "wire:click", building interactive Laravel UIs, or working with Livewire 4 projects.
| name | freek-dev-blog |
| description | Create and schedule link posts on freek.dev blog. Use when asked to publish, post, or share a link on freek.dev. Handles post creation, updating, and scheduling via the blog API. |
All post operations use the freek.dev Blog Posts API. No browser automation needed.
https://freek.dev/api
Bearer token in the Authorization header.
Token location: .secrets/blog-freek-dev.md
Authorization: Bearer <token>
List posts — GET /api/posts
Query parameters (all optional):
published (0 or 1): filter by published statusoriginal_content (0 or 1): filter by original contenttag (string): filter by tag namesearch (string): search by titleReturns paginated results.
Get a post — GET /api/posts/{id}
Create a post — POST /api/posts
Body (JSON):
title (string, required)text (string, required, markdown)publish_date (ISO 8601 date, nullable)published (boolean, default false)original_content (boolean, default false)external_url (URL string, nullable)tags (array of strings)send_automated_tweet (boolean, default false)author_twitter_handle (string, nullable)series_slug (string, nullable)Returns 201 with the created post.
Update a post — PUT /api/posts/{id}
Same fields as create, all optional. Only send fields you want to change.
Delete a post — DELETE /api/posts/{id}
Returns 204 No Content.
All responses are wrapped in a data key:
{
"data": {
"id": 1,
"title": "...",
"slug": "...",
"text": "...",
"html": "...",
"publish_date": "2026-03-02T14:00:00+00:00",
"published": true,
"original_content": false,
"external_url": null,
"series_slug": null,
"author_twitter_handle": null,
"send_automated_tweet": false,
"tags": ["laravel", "php"],
"url": "https://freek.dev/1-post-slug",
"preview_url": "https://freek.dev/1-post-slug?preview_secret=abc123",
"created_at": "2026-03-02T12:00:00+00:00",
"updated_at": "2026-03-02T12:00:00+00:00"
}
}
Create a link post:
curl -X POST https://freek.dev/api/posts \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"title": "Article title here",
"text": "Short summary of the linked content.",
"external_url": "https://example.com/article",
"send_automated_tweet": true
}'
Schedule a post (set publish_date + published):
curl -X PUT https://freek.dev/api/posts/3040 \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"publish_date": "2026-03-03T13:30:00+00:00",
"published": true
}'
Update post text:
curl -X PUT https://freek.dev/api/posts/3040 \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"text": "Updated markdown content here."}'
90% of posts are link posts (external content with a summary).
POST /api/posts with:
title: Use the original article title or a clear varianttext: 2 sentences max summarizing the linked content. No links in the text.external_url: The full URL being linkedoriginal_content: false (it's a link post)send_automated_tweet: trueGET /api/posts?published=0) and look at publish_date values. Do not assume the next slot is today.publish_date to the chosen future time. Do NOT set published: true (the blog auto-publishes when the date arrives).For posts written by Freek (package announcements, tutorials, opinions).
POST /api/posts with:
title: The post titletext: Full markdown content (see write-freek-dev-blogpost skill for writing style)original_content: truesend_automated_tweet: true (usually)tags: Relevant tags as array of stringspreview_url from the response for reviewWhen Freek shares a YouTube link for the blog.
dQw4w9WgXcQ from youtube.com/watch?v=dQw4w9WgXcQ)youtube.com/oembed?url=VIDEO_URL&format=jsonPOST /api/posts with:
title: Use the video titletext: Summary text followed by a blank line and the YouTube iframe embed:
Summary text here.
<iframe width="560" height="315" src="https://www.youtube.com/embed/VIDEO_ID" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe>
external_url: null (the embed IS the content)original_content: falseauthor_twitter_handle: Set if knownsend_automated_tweet: truet=24 or start=24), add ?start=24 to the embed srcwidth="560" height="315" as standard dimensionsWhen Freek wants to embed a Twitter/X thread on the blog.
POST /api/posts with:
title: Descriptive title (e.g. "A Twitter thread about laravel-permission v7")text: Minimal blockquotes with data-conversation="none":
<blockquote class="twitter-tweet" data-conversation="none">
<a href="https://twitter.com/USERNAME/status/TWEET_ID"></a>
</blockquote>
<blockquote class="twitter-tweet" data-conversation="none">
<a href="https://twitter.com/USERNAME/status/TWEET_ID_2"></a>
</blockquote>
<script async src="https://platform.twitter.com/widgets.js"></script>
external_url: nulloriginal_content: true (it's Freek's own content)author_twitter_handle: Tweet author (e.g. freekmurze)send_automated_tweet: false (the tweets already exist)data-conversation="none" prevents showing the parent tweet above each reply<script> tag at the end, not per-blockquoteStandard scheduling: sequential daily at ~14:30 CET (13:30 UTC in winter, 12:30 UTC in summer).
⚠️ CRITICAL: NEVER set published: true for scheduled posts. Setting published: true publishes IMMEDIATELY and triggers the automated tweet. The blog auto-publishes posts when their publish_date arrives.
To schedule a new post: Create with published: false (or omit, it defaults to false) and set publish_date to the desired future date.
To schedule an existing post: Update with published: false and publish_date:
PUT /api/posts/{id}
{"published": false, "publish_date": "2026-03-03T13:30:00+00:00"}
Title: SQL performance improvements: automatic detection & regression testing
Text:
The final part of Oh Dear's series on SQL performance. Mattias introduces phpunit-query-count-assertions, a package that catches N+1 queries, duplicate queries, and missing indexes in your test suite.
External url: https://ohdear.app/news-and-updates/...