From 6b8f4ffb50f8c30b6ad58bc131c04ee298b0fa20 Mon Sep 17 00:00:00 2001 From: Prithvi Ramakrishnan Date: Sat, 9 May 2026 10:55:46 -0700 Subject: [PATCH 1/2] Use shared internal agent skills --- .agents/skills/agent-slack/SKILL.md | 253 -------------- .../skills/agent-slack/references/commands.md | 139 -------- .../skills/agent-slack/references/output.md | 83 ----- .../skills/agent-slack/references/targets.md | 80 ----- .claude/skills/agent-slack | 1 - .claude/skills/generate-article/SKILL.md | 196 ----------- .../references/style-guide.md | 79 ----- .claude/skills/launch-post/SKILL.md | 325 ------------------ .../references/launch-article-guide.md | 189 ---------- AGENTS.md | 13 + CLAUDE.md | 1 + 11 files changed, 14 insertions(+), 1345 deletions(-) delete mode 100644 .agents/skills/agent-slack/SKILL.md delete mode 100644 .agents/skills/agent-slack/references/commands.md delete mode 100644 .agents/skills/agent-slack/references/output.md delete mode 100644 .agents/skills/agent-slack/references/targets.md delete mode 120000 .claude/skills/agent-slack delete mode 100644 .claude/skills/generate-article/SKILL.md delete mode 100644 .claude/skills/generate-article/references/style-guide.md delete mode 100644 .claude/skills/launch-post/SKILL.md delete mode 100644 .claude/skills/launch-post/references/launch-article-guide.md create mode 100644 CLAUDE.md diff --git a/.agents/skills/agent-slack/SKILL.md b/.agents/skills/agent-slack/SKILL.md deleted file mode 100644 index 082fa281..00000000 --- a/.agents/skills/agent-slack/SKILL.md +++ /dev/null @@ -1,253 +0,0 @@ ---- -name: agent-slack -description: | - Slack automation CLI for AI agents. Use when: - - Reading a Slack message or thread (given a URL or channel+ts) - - Browsing recent channel messages / channel history - - Downloading Slack attachments (snippets, images, files) to local paths - - Searching Slack messages or files - - Sending, editing, or deleting a message; adding/removing reactions - - Listing channels/conversations; creating channels and inviting users - - Fetching a Slack canvas as markdown - - Looking up Slack users - - Marking channels/DMs as read - - Opening DM or group DM channels - - Discovering and running Slack workflows - Triggers: "slack message", "slack thread", "slack URL", "slack link", "read slack", "reply on slack", "search slack", "channel history", "recent messages", "channel messages", "latest messages", "mark as read", "mark read" ---- - -# Slack automation with `agent-slack` - -`agent-slack` is a CLI binary on `$PATH`. Invoke directly (e.g. `agent-slack user list`). - -## Installation - -If `agent-slack` is not found on `$PATH`, install it: - -- `curl -fsSL https://raw.githubusercontent.com/stablyai/agent-slack/main/install.sh | sh` (recommended) -- `npm i -g agent-slack` (requires Node >= 22.5) -- `nix run github:stablyai/agent-slack -- ` (no install needed, prefix all commands) - -## CRITICAL: Bash command formatting rules - -Claude Code's permission checker has security heuristics that force manual approval prompts. Avoid these patterns to keep commands auto-allowed. See: https://github.com/anthropics/claude-code/issues/34379 - -1. **No `#` anywhere in the command string.** Treated as a comment delimiter even inside quotes. Use bare channel names (`general` not `#general`). No `#` comments in inline scripts — use the Bash tool's `description` parameter instead. -2. **No `''` (consecutive single quotes) or `""` (consecutive double quotes).** Triggers "potential obfuscation" check. Avoid Python empty string literals like `d.get('key', '')` — use `d.get('key')` instead. -3. **Only `| jq` for filtering — no python3, no other commands.** `python3 -c` is not in the allow list and triggers prompts. `jq` with single-quote-only expressions (no `"` inside) is safe: - - WRONG: `agent-slack search ... | python3 -c "..."` (not allowed) - - WRONG: `agent-slack search ... | jq '.a + "x"'` (mixed quotes) - - RIGHT: `agent-slack search ... | jq '.a'` - - RIGHT: `agent-slack search ... | jq '.messages[] | .ts'` -4. **No `||` or `&&` chains.** Run multiple agent-slack commands as separate Bash tool calls. -5. **No file redirects (`>`, `>>`).** Process JSON output directly, don't write to files. - -## Quick start (auth) - -Authentication is automatic on macOS and Windows (Slack Desktop first, then Chrome/Firefox fallbacks on macOS). - -If credentials aren’t available, run one of: - -- Slack Desktop import (macOS/Windows): - -```bash -agent-slack auth import-desktop -agent-slack auth test -``` - -- Chrome fallback: - -```bash -agent-slack auth import-chrome -agent-slack auth test -``` - -- Firefox fallback: - -```bash -agent-slack auth import-firefox -agent-slack auth test -``` - -- Or set env vars (browser tokens; avoid pasting these into chat logs): - -```bash -export SLACK_TOKEN="xoxc-..." -export SLACK_COOKIE_D="xoxd-..." -agent-slack auth test -``` - -- Or set a standard token: - -```bash -export SLACK_TOKEN="xoxb-..." # or xoxp-... -agent-slack auth test -``` - -Check configured workspaces: - -```bash -agent-slack auth whoami -``` - -## Canonical workflow (given a Slack message URL) - -1. Fetch a single message (plus thread summary, if any): - -```bash -agent-slack message get "https://workspace.slack.com/archives/C123/p1700000000000000" -``` - -2. If you need the full thread: - -```bash -agent-slack message list "https://workspace.slack.com/archives/C123/p1700000000000000" -``` - -## Browse recent channel messages - -To see what's been posted recently in a channel (channel history): - -```bash -agent-slack message list "general" --limit 20 -agent-slack message list "C0123ABC" --limit 10 -agent-slack message list "general" --with-reaction eyes --oldest "1770165109.000000" --limit 20 -agent-slack message list "general" --without-reaction dart --oldest "1770165109.000000" --limit 20 -``` - -This returns the most recent messages in chronological order. Use `--limit` to control how many (default 25). -When using `--with-reaction` or `--without-reaction`, you must also pass `--oldest` to bound scanning. - -## Attachments (snippets/images/files) - -`message get/list` and `search` auto-download attachments and include file metadata in JSON output (typically under `message.files[]` / `files[]`), including `name` when available and `path` for the local download. Failed message attachment downloads keep the attachment entry, preserve a local `.download-error.txt` path, and include `message.files[].error` for `message get/list` or `messages[].files[].error` for `search messages|all`; `search files` skips files whose download fails. - -## Draft a message (browser editor) - -Opens a Slack-like rich-text editor in the browser for composing messages with formatting toolbar (bold, italic, strikethrough, links, lists, quotes, code, code blocks). After sending, shows a "View in Slack" link. - -```bash -agent-slack message draft "general" -agent-slack message draft "general" "initial text" -agent-slack message draft "https://workspace.slack.com/archives/C123/p1700000000000000" -``` - -## Send, edit, delete, or react - -```bash -agent-slack message send "https://workspace.slack.com/archives/C123/p1700000000000000" "I can take this." -agent-slack message send "alerts-staging" "here's the report" --attach ./report.md -agent-slack message edit "https://workspace.slack.com/archives/C123/p1700000000000000" "I can take this today." -agent-slack message delete "https://workspace.slack.com/archives/C123/p1700000000000000" - -agent-slack message send "general" "Here's the plan: -- Step 1: do the thing -- Step 2: verify it worked - - Sub-step: check logs" -agent-slack message react add "https://workspace.slack.com/archives/C123/p1700000000000000" "eyes" -agent-slack message react remove "https://workspace.slack.com/archives/C123/p1700000000000000" "eyes" -``` - -Channel mode for edit/delete requires `--ts`: - -```bash -agent-slack message edit "general" "Updated text" --workspace "myteam" --ts "1770165109.628379" -agent-slack message delete "general" --workspace "myteam" --ts "1770165109.628379" -``` - -Attach options for `message send`: - -- `--attach ` upload a local file (repeatable) - -## List channels + create/invite users - -```bash -agent-slack channel list -agent-slack channel list --user "@alice" --limit 50 -agent-slack channel list --all --limit 100 -agent-slack channel new --name "incident-war-room" -agent-slack channel new --name "incident-leads" --private -agent-slack channel invite --channel "incident-war-room" --users "U01AAAA,@alice,bob@example.com" -agent-slack channel invite --channel "incident-war-room" --users "partner@vendor.com" --external -agent-slack channel invite --channel "incident-war-room" --users "partner@vendor.com" --external --allow-external-user-invites -``` - -For `--external`, invite targets must be emails. By default, invitees are external-limited; add -`--allow-external-user-invites` to allow them to invite other users. - -## Search (messages + files) - -Prefer channel-scoped search for reliability: - -```bash -agent-slack search all "smoke tests failed" --channel "alerts" --after 2026-01-01 --before 2026-02-01 -agent-slack search messages "stably test" --user "@alice" --channel general -agent-slack search files "testing" --content-type snippet --limit 10 -``` - -## Multi-workspace guardrail (important) - -If you have multiple workspaces configured and you use a channel **name** (e.g. `general`), pass `--workspace` (or set `SLACK_WORKSPACE_URL`) to avoid ambiguity: - -```bash -agent-slack message get "general" --workspace "https://myteam.slack.com" --ts "1770165109.628379" -agent-slack message get "general" --workspace "myteam" --ts "1770165109.628379" -``` - -## DM / group DM channels - -Get the channel ID for a DM or group DM, useful for sending messages to a group of users: - -```bash -agent-slack user dm-open @alice @bob -agent-slack user dm-open U01AAAA U02BBBB U03CCCC -``` - -## Mark as read - -Mark a channel, DM, or group DM as read up to a given message: - -```bash -agent-slack channel mark "https://workspace.slack.com/archives/C123/p1700000000000000" -agent-slack channel mark "general" --workspace "myteam" --ts "1770165109.628379" -agent-slack channel mark "D0A04PB2QBW" --workspace "myteam" --ts "1770165109.628379" -``` - -To make a specific message appear unread, set `--ts` to just before it (subtract `0.000001`). This moves the read cursor so that message and everything after it appear as new: - -```bash -agent-slack channel mark "general" --workspace "myteam" --ts "1770165109.628378" -``` - -## Workflows - -Discover and run Slack workflows bookmarked in channels: - -```bash -# List workflows in a channel -agent-slack workflow list "#ops" - -# Preview trigger metadata (no side effects) -agent-slack workflow preview "Ft123ABC" - -# Get workflow definition including form fields and steps -agent-slack workflow get "Ft123ABC" -agent-slack workflow get "Wf456DEF" - -# Trip a workflow trigger -agent-slack workflow run "Ft123ABC" --channel "#ops" -``` - -## Canvas + Users - -```bash -agent-slack canvas get "https://workspace.slack.com/docs/T123/F456" -agent-slack user list --workspace "https://workspace.slack.com" --limit 100 -agent-slack user get "@alice" --workspace "https://workspace.slack.com" -``` - -## References - -- [references/commands.md](references/commands.md): full command map + all flags -- [references/targets.md](references/targets.md): URL vs `#channel` targeting rules -- [references/output.md](references/output.md): JSON output shapes + download paths diff --git a/.agents/skills/agent-slack/references/commands.md b/.agents/skills/agent-slack/references/commands.md deleted file mode 100644 index 289eb3d9..00000000 --- a/.agents/skills/agent-slack/references/commands.md +++ /dev/null @@ -1,139 +0,0 @@ -# `agent-slack` command map (reference) - -Run `agent-slack --help` (or `agent-slack --help`) for the full option list. - -## Auth - -- `agent-slack auth whoami` — show configured workspaces + token sources (secrets redacted) -- `agent-slack auth test [--workspace ]` — verify credentials (`auth.test`) -- `agent-slack auth import-desktop` — import browser-style creds from Slack Desktop (macOS/Windows) -- `agent-slack auth import-chrome` — import creds from Chrome (macOS) -- `agent-slack auth import-firefox` — import creds from Firefox profile storage (macOS/Linux) -- `agent-slack auth parse-curl` — read a copied Slack cURL command from stdin and save creds -- `agent-slack auth add --workspace-url [--token | --xoxc --xoxd ]` -- `agent-slack auth set-default ` -- `agent-slack auth remove ` - -## Messages / threads - -- `agent-slack message get ` - - ``: Slack message URL OR `#channel`/`channel`/channel id (`C...`) (see `targets.md`) - - Options: - - `--workspace ` (required when using a channel _name_ across multiple workspaces) - - `--ts .` (required when targeting a channel) - - `--thread-ts .` (optional hint for thread permalinks) - - `--max-body-chars ` (default `8000`, `-1` unlimited) - - `--include-reactions` - -- `agent-slack message list ` - - Lists recent channel messages (channel history), or fetches all thread replies - - **Channel history** (default when targeting a channel without `--thread-ts`): - - `agent-slack message list "general"` — latest 25 messages - - `agent-slack message list "general" --limit 50` — latest 50 messages - - **Thread mode** (when `--thread-ts` or `--ts` is provided, or target is a message URL): - - `agent-slack message list ""` — all replies in that thread - - `agent-slack message list "general" --thread-ts "1770165109.000001"` — thread replies - - Options: - - `--workspace ` (same rules as above) - - `--thread-ts .` (switches to thread mode; fetches replies) - - `--ts .` (resolve a message to its thread) - - `--limit ` (default `25`, max `200`; channel history mode only) - - `--oldest ` (only messages after this ts; channel history mode) - - `--latest ` (only messages before this ts; channel history mode) - - `--with-reaction ` (repeatable; include only messages that have this reaction; channel history mode; requires `--oldest`) - - `--without-reaction ` (repeatable; include only messages that do not have this reaction; channel history mode; requires `--oldest`) - - `--max-body-chars ` (default `8000`, `-1` unlimited) - - `--include-reactions` - -- `agent-slack message draft [text]` - - Opens a Slack-like WYSIWYG editor in the browser for composing and sending a message. - - Formatting toolbar: bold, italic, strikethrough, links, numbered/bulleted lists, quotes, inline code, code blocks. - - Toggle between rich-text editing and raw mrkdwn source view. - - After sending, shows a "View in Slack" permalink to the posted message. - - If `` is a Slack message URL, the draft will reply in that thread. - - Options: - - `--workspace ` (needed for channel _names_ across multiple workspaces) - - `--thread-ts .` (optional, channel mode only) - -- `agent-slack message send ` - - If `` is a Slack message URL, replies in that message’s thread. - - Otherwise posts to the channel/DM. - - Bullet lists (`- `, `* `, `• `, `1. `, etc.) are automatically converted to Slack’s native rich text format, so recipients see real editable bullets instead of plain-text dashes. - - Options: - - `--workspace ` (needed for channel _names_ across multiple workspaces) - - `--thread-ts .` (optional, channel mode only) - - `--attach ` (repeatable; upload local files as attachments) - -- `agent-slack message edit ` - - URL target edits that exact message. - - Channel target requires `--ts`. - - Options: - - `--workspace ` (needed for channel _names_ across multiple workspaces) - - `--ts .` (required for channel targets) - -- `agent-slack message delete ` - - URL target deletes that exact message. - - Channel target requires `--ts`. - - Options: - - `--workspace ` (needed for channel _names_ across multiple workspaces) - - `--ts .` (required for channel targets) - -- `agent-slack message react add ` -- `agent-slack message react remove ` - - Options (channel mode): - - `--workspace ` (needed for channel _names_ across multiple workspaces) - - `--ts .` (required for channel targets) - -## Channels - -- `agent-slack channel list [--workspace ] [--user | --all] [--limit ] [--cursor ]` - - Default mode calls `users.conversations` for the current user. - - `--user` resolves handles/ids and lists conversations for that user. - - `--all` switches to `conversations.list` (mutually exclusive with `--user`). - - Returns one page and optional `next_cursor`; pass `--cursor` to continue. -- `agent-slack channel new --name [--private] [--workspace ]` -- `agent-slack channel invite --channel --users "" [--workspace ]` - - Internal invite (default): resolves users (`U...`, `@handle`, `handle`, `email`) and uses `conversations.invite` - - External invite: add `--external` (email targets only) to use `conversations.inviteShared` - - Optional: `--allow-external-user-invites` sets `external_limited=false` for external invites -- `agent-slack channel mark [--ts .] [--workspace ]` - - Marks a channel/DM as read up to the given message timestamp (`conversations.mark`) - - URL target extracts channel, ts, and workspace automatically; `--ts` optionally overrides the URL timestamp; `--workspace` is rejected - - Channel name/ID target requires `--ts` - -## Search - -- `agent-slack search all ` — messages + files (default) -- `agent-slack search messages ` -- `agent-slack search files ` - -Common options: - -- `--workspace ` (recommended when using channel names across multiple workspaces) -- `--channel ` repeatable (`#name`, `name`, or id) -- `--user <@name|name|U...>` -- `--after YYYY-MM-DD` -- `--before YYYY-MM-DD` -- `--content-type any|text|image|snippet|file` -- `--limit ` (default `20`) -- `--max-content-chars ` (default `4000`, `-1` unlimited; messages only) - -## Canvas - -- `agent-slack canvas get ` - - Options: - - `--workspace ` (required when passing an id and multiple workspaces) - - `--max-chars ` (default `20000`, `-1` unlimited) - -## Workflows - -- `agent-slack workflow list [--workspace ]` — list workflows bookmarked or featured in a channel -- `agent-slack workflow preview [--workspace ]` — get workflow metadata from a trigger ID (no side effects) -- `agent-slack workflow get [--workspace ]` — get workflow definition including form fields and steps (accepts `Ft...` or `Wf...`) -- `agent-slack workflow run --channel [--workspace ]` — trip a workflow trigger - -## Users - -- `agent-slack user list [--workspace ] [--limit ] [--cursor ] [--include-bots]` -- `agent-slack user get [--workspace ]` -- `agent-slack user dm-open [--workspace ]` — get DM or group DM channel ID for one or more users (max 8) diff --git a/.agents/skills/agent-slack/references/output.md b/.agents/skills/agent-slack/references/output.md deleted file mode 100644 index 72e5c84a..00000000 --- a/.agents/skills/agent-slack/references/output.md +++ /dev/null @@ -1,83 +0,0 @@ -# Output + downloads (reference) - -## Output format - -All commands print JSON to stdout. - -- Empty values are pruned (`null`, `[]`, `{}` are removed where possible). -- `auth whoami` redacts secrets in its output. - -## Message shapes (high-level) - -- `message get` returns: - - `message: { ... }` - - `thread?: { ts, length }` (summary only; present when threaded) - -- `message list` returns: - - `messages: [ ... ]` (the full thread) - - Messages are compact and omit redundant fields on each item where possible. - -Use `--max-body-chars` to cap message bodies for token budget control. - -## Search shapes (high-level) - -- `search messages|all` returns `messages: [ ... ]` -- `search files|all` returns `files: [ ... ]` - -Use `--max-content-chars` (messages) and `--limit` to control size. - -## Channel shapes (high-level) - -- `channel list` returns: - - `channels: [ ... ]` - - `next_cursor?: string` (present when more pages are available) - -- `channel new` returns: - - `channel: { id, name, is_private }` - -- `channel invite` returns: - - Internal invite mode: - - `channel_id` - - `invited_user_ids: [ ... ]` - - `already_in_channel_user_ids?: [ ... ]` - - `unresolved_users?: [ ... ]` - - External invite mode (`--external`): - - `channel_id` - - `external: true` - - `external_limited: boolean` - - `invited_emails: [ ... ]` - - `already_invited_emails?: [ ... ]` - - `invalid_external_targets?: [ ... ]` - -- `channel mark` returns: - - `ok: boolean` - - `channel: string` (resolved channel ID) - - `ts: string` - -## File fields in compact messages - -When messages include file attachments, each file object contains: - -- `name` — the original filename (e.g. `"report.pdf"`), omitted if unavailable -- `mimetype` — MIME type (e.g. `"application/pdf"`) -- `mode` — Slack file mode (e.g. `"hosted"`, `"snippet"`) -- `path` — absolute local path to the downloaded file - -Only files with a successful download are included. - -## Attachment downloads - -Attachments are downloaded to an agent-friendly temp directory. - -- Successful downloads are returned as absolute paths in output. -- `message get/list` preserves failed attachment downloads with `message.files[].error` and keeps `message.files[].path` pointing to a local `.download-error.txt` file. -- Message results from `search messages|all` preserve failed attachment downloads with `messages[].files[].error` and keep `messages[].files[].path` pointing to a local `.download-error.txt` file. -- `search files` skips files whose download fails and continues returning the remaining matches. - -Default download root: - -- `~/.agent-slack/tmp/downloads/` - -If `XDG_RUNTIME_DIR` is set, downloads live under: - -- `$XDG_RUNTIME_DIR/agent-slack/tmp/downloads/` diff --git a/.agents/skills/agent-slack/references/targets.md b/.agents/skills/agent-slack/references/targets.md deleted file mode 100644 index ca38a17d..00000000 --- a/.agents/skills/agent-slack/references/targets.md +++ /dev/null @@ -1,80 +0,0 @@ -# Targets: URL vs channel (reference) - -`agent-slack` accepts either a **Slack message URL** (preferred) or a **channel reference**. - -## Preferred: Slack message URL - -Use the message permalink whenever you have it: - -```text -https://.slack.com/archives//p[?thread_ts=...] -``` - -Examples: - -- `agent-slack message get ""` -- `agent-slack message list ""` -- `agent-slack message send "" "reply text"` -- `agent-slack message edit "" "updated text"` -- `agent-slack message delete ""` -- `agent-slack message react add "" "eyes"` -- `agent-slack channel mark ""` - -## Channel targets (when you don’t have a URL) - -Channel references can be: - -- channel name: `general` (bare name, without `#` prefix) -- channel id: `C...` (or `G...`/`D...`) - -### `message get` by channel + `--ts` - -```bash -agent-slack message get "general" --ts "1770165109.628379" -``` - -### `message list` by channel + `--thread-ts` (or `--ts` to resolve) - -```bash -agent-slack message list "general" --thread-ts "1770165109.000001" -agent-slack message list "general" --ts "1770165109.628379" -agent-slack message list "general" --without-reaction dart --limit 20 -``` - -### Reactions by channel + `--ts` - -```bash -agent-slack message react add "general" "eyes" --ts "1770165109.628379" -``` - -### Edit/delete by channel + `--ts` - -```bash -agent-slack message edit "general" "updated text" --ts "1770165109.628379" -agent-slack message delete "general" --ts "1770165109.628379" -``` - -### Mark as read by channel + `--ts` - -```bash -agent-slack channel mark "general" --ts "1770165109.628379" -agent-slack channel mark "D0A04PB2QBW" --ts "1770165109.628379" -``` - -### Channel admin by id/name - -```bash -agent-slack channel invite --channel "general" --users "@alice,bob@example.com" -agent-slack channel invite --channel "C0123ABCDEF" --users "U01234567" -agent-slack channel invite --channel "shared-room" --users "partner@vendor.com" --external -agent-slack channel invite --channel "shared-room" --users "partner@vendor.com" --external --allow-external-user-invites -``` - -## Multi-workspace ambiguity (channel names only) - -If you have multiple workspaces configured and your target is a channel **name** (e.g. `general`), you must disambiguate: - -- pass `--workspace "https://myteam.slack.com"` (or a unique substring like `--workspace "myteam"`), or -- set `SLACK_WORKSPACE_URL` to the same selector format - -Channel IDs (`C...`/`G...`/`D...`) do not require `--workspace`. diff --git a/.claude/skills/agent-slack b/.claude/skills/agent-slack deleted file mode 120000 index 55119080..00000000 --- a/.claude/skills/agent-slack +++ /dev/null @@ -1 +0,0 @@ -../../.agents/skills/agent-slack \ No newline at end of file diff --git a/.claude/skills/generate-article/SKILL.md b/.claude/skills/generate-article/SKILL.md deleted file mode 100644 index 93e63b40..00000000 --- a/.claude/skills/generate-article/SKILL.md +++ /dev/null @@ -1,196 +0,0 @@ ---- -name: generate-article -description: | - Generate a blog article for the Promptless edu campaign. Researches a keyword via web search, writes a publication-ready MDX draft, commits it to a new branch, opens a GitHub PR, and posts the PR link to Slack. - Use this skill when: - - The user asks to generate, write, or draft a blog article or blog post - - The user mentions the "edu campaign" or "content pipeline" - - The user wants to create content for a keyword from the keyword list - - The user says "generate article", "write a blog post", "new blog draft", or similar - Even if the user just says something like "pick a keyword and write an article" or "run the content pipeline", use this skill. ---- - -# Generate Article (Edu Campaign) - -Automated content pipeline for the Promptless blog. This skill replaces the `generate-article.ts` script by using Claude Code's native tools (web search, file writing, git, gh CLI, agent-slack) instead of calling the Anthropic API programmatically. - -## Overview - -1. Pick a keyword (from user input or randomly from the keyword list) -2. Research the topic thoroughly using web search -3. Write a publication-ready MDX article -4. Commit to a new branch, push, and open a GitHub PR -5. Post the PR link to the `#gtm` Slack channel - -## Step 1: Pick a keyword - -If the user provides a keyword or topic, use it directly. - -Otherwise, read the keyword list at `scripts/edu_campaign/keywords.txt` (relative to repo root). Pick one at random. Lines starting with `#` are comments — skip them. Tell the user which keyword was selected. - -## Step 2: Research - -Use the WebSearch tool to research the keyword. Search for 5-8 of the best recent articles, studies, blog posts, and discussions on the topic. Focus on 2024-2026 material. For each source, identify: - -- The main angles already covered -- Data points or case studies cited -- Gaps in existing coverage -- What practitioners are actually struggling with - -Read the most relevant results thoroughly using WebFetch. - -After researching, extract from what you found: - -- **1-2 related keywords:** Terms or phrases that kept appearing alongside the main keyword — especially newer or more specific variants (e.g. searching "knowledge base" might surface "personal wiki" or "second brain"). These are candidates for targeting in the article instead of or alongside the original keyword. -- **1-2 key ideas:** Specific insights, trends, or framings from the research worth building the article around (1-2 sentences each). These should be concrete and grounded in what you actually read — not generic observations. - -Report these to the user before moving on. Use them in Step 3 to sharpen the article's angle. - -## Step 3: Plan the article - -Before writing, produce a detailed plan. The plan must answer: - -- **Keyword(s) to target:** Which keyword(s) — the original, the related ones from Step 2, or a combination — should this article target? Pick what gives the article the sharpest angle and best SEO fit. -- **Key ideas to build on:** Which 1-2 ideas from Step 2 will anchor the article? State them clearly. -- **Article format:** What format best serves this content and reader? Describe it concretely, e.g. "a comparison of two approaches to help readers make a technical decision" or "an explainer that teaches readers why X matters before showing them what to do about it." -- **Thesis:** The single main point this article makes, in one sentence. -- **Target reader:** Who specifically benefits — developers, technical writers, solutions engineers, product managers, developer advocates? What do they already know, and what gap does this fill? -- **Key points:** 4-6 specific claims or arguments the article will make, in order. Each should be concrete — not "discuss X" but "argue that X causes Y because Z." -- **Evidence:** For each key point, what data, case studies, or examples from the research support it, with links. -- **Promptless connection:** How the article's thesis connects to what Promptless does. Not a sales pitch — a logical continuation. E.g. "This article argues that docs go stale faster than teams notice, which is exactly the problem Promptless monitors for." - -Output the plan to the user for review before writing. - -## Step 4: Write the article - -Before writing, scan the existing articles in `src/content/blog/technical/` to: -- Confirm your topic doesn't duplicate an existing article -- Find posts to link to internally (read titles/descriptions of a few to understand voice) -- Calibrate your tone to match the existing body of work - -Then write a complete, publication-ready MDX article strictly following the plan and the style guide. - -### Writing style - -Read `references/style-guide.md` in this skill's directory for the complete writing style and structural conventions. - -Key points: -- 10th grade reading level. Short sentences, plain words. -- Direct and dry, like a Dutch engineer. No filler, no fluff. -- Grounded in concrete examples, real numbers, and cited evidence. -- Do NOT use em dashes. -- Do NOT use tricolon structures (three-item lists for rhetorical effect, e.g. "It's fast, reliable, and cheap"). -- Do NOT use "X is not Y, but Z" constructions. - -### Structural conventions - -- Use `##` for H2 sections, `###` for H3 -- Aim for 800-1400 words -- Lead with a hook that frames the specific problem — NOT a generic intro paragraph -- Do NOT include a generic conclusion that restates what was said -- Place `` at roughly the 40-50% mark (after the first major section) -- End the article with `` - -### MDX frontmatter format - -``` ---- -title: 'Article Title Here' -subtitle: Published {month} {year} -description: >- - One or two sentence SEO description, 120-160 characters. -date: '{YYYY-MM-DD}T00:00:00.000Z' -author: Frances -tag: {tag} -section: Use Cases -hidden: false ---- -import BlogNewsletterCTA from '@components/site/BlogNewsletterCTA.astro'; -import BlogRequestDemo from '@components/site/BlogRequestDemo.astro'; - -[article body starts here] -``` - -Fill in the current date and month/year. For `tag`, choose the best fit from: `Technical`, `Opinion`, `Customer Stories`, `Life at Promptless`. - -## Step 5: Edit pass - -Do a final editorial pass specifically rewriting every instance of: -1. "X is not Y, but Z" constructions — rewrite as a direct statement -2. Tricolon structures — break up or cut to the most important item -3. Em dashes — replace with periods, commas, or parentheses -4. Optimize SEO: update title, subtitle, and description to match what users would search for -5. Add relevant links to other Promptless blog posts where appropriate (check `src/content/blog/` for existing posts) - -## Step 6: Save the file - -The article file goes at: -``` -src/content/blog/technical/{slug}.mdx -``` - -Generate the slug from the article title: lowercase, remove special characters, replace spaces with hyphens, truncate to 60 characters. - -## Step 7: Create a branch and PR - -Use a git worktree so the skill doesn't interfere with whatever branch the user currently has checked out. This is especially important in remote tasks where the working directory may have uncommitted changes. - -```bash -# Fetch latest main -git fetch origin main - -# Create a worktree on a new branch -BRANCH="articles/{date}-{slug}" -WORKTREE=".worktrees/$(echo $BRANCH | tr '/' '-')" -git worktree add -b "$BRANCH" "$WORKTREE" origin/main -``` - -Write the article MDX file into the worktree (at `{worktree}/src/content/blog/technical/{slug}.mdx`), then commit and push: - -```bash -cd "$WORKTREE" -git add src/content/blog/technical/{slug}.mdx -git commit -m "content: Add blog article -- {title} - -Keyword: {keyword} -Generated by edu campaign skill" -git push -u origin "$BRANCH" -``` - -Create the PR from the worktree: -```bash -gh pr create --title "content: {title}" --body "$(cat <<'EOF' -**Keyword:** `{keyword}` - -## Article plan - -{paste the plan summary here} - -## File - -`src/content/blog/technical/{slug}.mdx` - -This is an AI-generated draft and needs human review before publishing. -EOF -)" -``` - -Then clean up: -```bash -cd - -git worktree remove --force "$WORKTREE" -``` - -## Step 8: Notify Slack - -Post the PR link to the `#gtm` channel using agent-slack: - -```bash -agent-slack message send --workspace promptless "#gtm" "New blog article draft ready for review: {title} (keyword: {keyword}) -- {pr_url}" -``` - -## Important notes - -- Always set `hidden: false` in the frontmatter. -- If any step fails (e.g. git push, PR creation, Slack), report the error but continue with remaining steps. -- The article should be genuinely useful content, not thinly-veiled marketing. Promptless connections should feel natural. diff --git a/.claude/skills/generate-article/references/style-guide.md b/.claude/skills/generate-article/references/style-guide.md deleted file mode 100644 index 20f4af9f..00000000 --- a/.claude/skills/generate-article/references/style-guide.md +++ /dev/null @@ -1,79 +0,0 @@ -# Promptless Blog Style Guide - -## Voice and tone - -Promptless's audience is technical writers, DevRel engineers, developer advocates, solutions engineers, and engineering managers at software companies with developer-facing products. - -### Reading level -10th grade. Short sentences, plain words. No jargon without explanation. - -### Personality -Direct and dry, like a Dutch engineer. Say what you mean. Cut anything that doesn't add information. No filler, no fluff. - -### Evidence -Ground every claim in concrete examples, real numbers, or cited evidence. Vague claims ("many companies struggle with...") are not acceptable without supporting data. - -## Banned patterns - -### Em dashes -Do not use em dashes (—) anywhere in the article. Use periods, commas, colons, or parentheses instead. - -### Tricolon structures -Do not use three-item lists for rhetorical effect. Examples of what NOT to do: -- "It's fast, reliable, and cheap" -- "Teams need clarity, consistency, and confidence" -- "This saves time, reduces errors, and improves quality" - -If you catch yourself writing three adjectives or three noun phrases in a row, cut to the most important one or restructure the sentence. - -### "X is not Y, but Z" constructions -Do not use this pattern. Examples of what NOT to do: -- "This isn't a tooling problem, it's a process problem" -- "Documentation isn't a one-time task, it's an ongoing commitment" -- "The issue isn't writing docs, it's keeping them current" - -Rewrite these as direct statements about what the thing IS, without the negation setup. - -## Structure - -### Headings -- `##` for H2 sections -- `###` for H3 subsections -- No H1 in the body (the title serves as H1) - -### Length -800-1400 words. Shorter is better if the topic doesn't require length. - -### Opening -Lead with a hook that frames the specific problem the article addresses. Do NOT write a generic intro paragraph like "In today's fast-paced world..." or "Documentation is important for every company..." - -Start with a concrete situation, a surprising data point, or a specific pain point that the target reader will immediately recognize. - -### Closing -Do NOT include a generic conclusion that restates what was said. The article should end with its final substantive point, followed by the `` component. - -### CTA placement -Place `` at roughly the 40-50% mark of the article, after the first major section. It should fall at a natural break point. - -End the article with `` on its own line. - -## MDX components - -The article must import and use these two components: - -```mdx -import BlogNewsletterCTA from '@components/site/BlogNewsletterCTA.astro'; -import BlogRequestDemo from '@components/site/BlogRequestDemo.astro'; -``` - -Place them on their own lines with blank lines above and below. - -## SEO - -- Title should be specific and contain the target keyword naturally -- Description should be 120-160 characters and accurately summarize the article -- Subtitle format: "Published {Month} {Year}" - -## Links - -Where relevant, link to other Promptless blog posts. Check `src/content/blog/` for existing articles. Also link to external sources cited in the research. diff --git a/.claude/skills/launch-post/SKILL.md b/.claude/skills/launch-post/SKILL.md deleted file mode 100644 index 42e16bfa..00000000 --- a/.claude/skills/launch-post/SKILL.md +++ /dev/null @@ -1,325 +0,0 @@ ---- -name: launch-post -description: | - Write Promptless launch articles by triaging recent changelog entries and - writing a focused, single-feature post for each one that meets the bar: - significantly improves UX for a non-trivial segment of users, OR adds - substantial new capabilities to the Promptless product. - - Intended to run as a weekly scheduled task — it scans the last 7 days of - commits to `src/content/changelog/changelogs/` for newly added entries, - decides which (if any) warrant a post, researches the backing PRs in - `Promptless/promptless`, and opens one PR per qualifying feature. - - Use this skill when: - - The weekly cron fires ("/launch-post", "run the weekly launch post check") - - The user says "any launch posts worth writing this week?" - - The user pastes a single changelog entry and asks for a launch post - - The user points at a specific feature and asks for a focused post - - The user asks to backfill a month of launch posts ("backfill March 2026", - "generate launch posts for February") - Always triage first — most changelog entries do not warrant a launch post. - A typical week produces zero or one post; a typical month produces 2–4. ---- - -# Launch Post - -Triages recent Promptless changelog entries, picks out the ones that genuinely warrant a launch article, and writes one focused single-feature article per qualifier. - -## Overview - -0. Determine the scan window and collect candidate changelog entries -1. Triage each entry against the bar -1.5. Filter out duplicates (features that already have a launch post or a draft PR) -2. For each qualifier: research the backing PR(s), write a focused single-feature article, open a PR, notify Slack - -For Mode D (monthly backfill), insert a **preview + confirm** step between 1.5 and 2 — see the Mode D section in Step 0. - -Most weeks: 0–1 qualifiers. Occasionally 2. More than 2 in a week is rare — if you're tempted to pick 3+, recheck the bar. - ---- - -## Step 0: Collect candidate changelog entries - -There are four invocation modes. Pick the one that matches the user's request. - -### Mode A — Weekly scheduled run (default) - -Scan commits on the changelog directory from the last 7 days: - -```bash -cd ~/dev/docs -git fetch origin main -git log --since="7 days ago" origin/main -- src/content/changelog/changelogs/ \ - --pretty=format:'%H%x09%ai%x09%s' -``` - -For each commit, read the diff to extract the newly added changelog entry: - -```bash -git show --format= "$SHA" -- src/content/changelog/changelogs/ -``` - -Each commit typically adds one entry. Collect the feature name, the short description, and any PR number referenced in the commit message or entry body. - -### Mode B — Single-entry request - -The user pasted one changelog entry and wants a post for it. Skip to Step 1 triage on just that entry. If it qualifies, write it. If it doesn't, say so and stop — don't write the post just because it was asked for. - -### Mode C — Named feature - -The user names a specific feature ("write a post for the AGENTS.md support"). Treat it as a single entry and assume the user has already decided it qualifies. Skip triage, go straight to research and writing. Still produce a single-feature post, not a roll-up. - -### Mode D — Monthly backfill - -The user asks to backfill a specific month ("backfill March 2026", "generate launch posts for February 2026"). Scan all commits that touched the changelog directory within that calendar month: - -```bash -cd ~/dev/docs -git fetch origin main -# Example: March 2026 -git log --since="2026-03-01" --until="2026-04-01" origin/main \ - -- src/content/changelog/changelogs/ \ - --pretty=format:'%H%x09%ai%x09%s' -``` - -**Cap the range at one calendar month.** If the user asks for a longer period (a quarter, a year), ask them to run it one month at a time — longer ranges produce too many PRs to review in one batch. - -Collect entries the same way as Mode A (one per commit, usually). - -After Step 1 triage completes, **before firing Step 2**, preview the plan and get the user's confirmation. See "Preview + confirm (Mode D only)" below. - ---- - -## Step 1: Triage each entry - -**The bar:** - -A changelog entry warrants a launch post if it meets at least one of these: -- **Significantly improves UX for a non-trivial segment of users** — a real, describable group of users can now do something meaningfully better or avoid something meaningfully painful. -- **Adds substantial new capabilities to the Promptless product** — a new trigger, integration, workflow, or configuration that unlocks something previously impossible. - -**Clear YES:** -- New trigger types, new integrations, new platform support -- New configuration that unlocks a workflow (e.g. version branch targeting for teams with versioned docs) -- New first-class capabilities (e.g. AGENTS.md as a style guide source) - -**Clear NO:** -- Bug fixes, even important ones (goes on the changelog, not in a launch post) -- Internal refactors, dependency upgrades, logging changes -- Copy tweaks, minor UI polish -- Performance improvements with no visible UX change - -**Borderline — default to NO:** -- Performance improvements with visible impact (only yes if the delta changes how users work) -- UI reorganizations (only yes if they materially change a core workflow) -- Expanded configuration on an existing capability (only yes if it opens a new use case) - -For each entry, output a one-line triage decision: `QUALIFIES` or `SKIP — {reason}`. Be explicit about which criterion from the bar is met for qualifiers. - -If **zero** entries qualify, stop. Report: -- The entries you considered -- Why each was skipped -- What kind of change would meet the bar next week - -Don't write a post just to have something to show. Zero is a valid answer. - ---- - -## Step 1.5: Check for duplicates - -**Mandatory for every mode.** Never write a launch post for a feature that already has one (or has an open draft PR). Running a second post on the same feature is a hard failure — catch it here. - -For each qualifier from Step 1, do the following checks in order: - -### 1. Scan existing posts - -List every existing launch post and read its frontmatter to see what feature it covers: - -```bash -ls src/content/blog/product-updates/ -``` - -For each `.mdx` file, read the frontmatter `title` and `description` (and skim the opening paragraph if the title is ambiguous). Compare **semantically** against the qualifier, not just by slug. A different filename can still describe the same feature. - -Examples of matches to catch: -- Qualifier "AGENTS.md support" vs. existing post titled "Using AGENTS.md as a Style Guide Source" → **duplicate** -- Qualifier "First-approval trigger" vs. existing post "Wait for First Review Before Generating Docs" → **duplicate** -- Qualifier "Starlight support" vs. existing post "Starlight (Astro) Docs Platform Support" → **duplicate** - -### 2. Check open draft PRs - -A post might already be in flight from an earlier skill run. Check open PRs that add to the product-updates directory: - -```bash -gh pr list --repo Promptless/promptless.ai --state open \ - --search "path:src/content/blog/product-updates/" \ - --json number,title,headRefName,files --limit 20 -``` - -Read each result's title and changed files. If any open PR already covers the qualifier, it's a duplicate. - -### 3. Decide - -- **Exact match** (same feature already has a merged post or an open PR) → drop the qualifier, log the duplicate in the triage table, continue with the rest. -- **Borderline match** (similar-but-distinct — e.g. "first-approval trigger" vs. an older post about "draft PR skipping") → pause and ask the user. Don't write speculatively. -- **No match** → proceed to Step 2. - -If a qualifier is dropped as a duplicate, still include it in the PR's "Entries considered this run" table with `SKIP — duplicate of {existing post title or PR #}` so the reviewer can audit the decision. - ---- - -## Preview + confirm (Mode D only) - -After Step 1.5, **before firing Step 2**, print the backfill plan and wait for confirmation. Monthly backfills can produce several PRs at once, and the reviewer needs a chance to drop entries or cap the count before the skill starts opening PRs and pinging Slack. - -Print: - -``` -Backfill plan — {Month Year} - -{N} commits in window. {M} entries considered. {K} qualifiers after dedup: - -Qualifiers (will write posts for these): - 1. {feature title} — {one-line reason} - 2. {feature title} — {one-line reason} - ... - -Skipped ({S}): - - {feature title} — {reason} - - {feature title} — {reason} - ... - -Dropped as duplicates ({D}): - - {feature title} — duplicate of {existing post or open PR} - ... - -Proceed with all {K} qualifiers? Reply with one of: - - "yes" to write all {K} posts - - "drop {1,2}" to remove specific entries by number - - "cap 2" to write only the top N qualifiers - - "no" to abort -``` - -Wait for the user's reply before proceeding. Do **not** open any PRs until confirmed. For Modes A, B, and C, skip this step and go straight to Step 2. - ---- - -## Step 2: For each qualifier — research, write, ship - -### 2a. Research the backing PR(s) - -```bash -gh pr list --repo Promptless/promptless --state merged \ - --search "{feature keyword}" --limit 10 \ - --json number,title,body,mergedAt,url - -gh pr view {number} --repo Promptless/promptless \ - --json title,body,mergedAt,url,files -``` - -For each feature, extract: -- **The problem it solves** — what was breaking or painful before -- **How it works** — user-visible behavior (not code) -- **Who benefits most** — which team type or workflow -- **Any setup required** — configuration, opt-in, caveats - -If a PR description is thin, check linked issues: `gh issue view {number} --repo Promptless/promptless`. The local repo at `~/dev/promptless` is available too, but `gh` on the remote is usually faster. - -Don't over-research. 2–3 concrete facts is enough. - -### 2b. Write the article - -Read `references/launch-article-guide.md` for voice, structure, MDX format, and the banned-pattern list. - -Core job of a launch article: answer three questions about **one** feature. -1. What changed? (one sentence) -2. How does it work now? (concrete, behavioral) -3. What does this mean for the user? (the implication — what they can do, avoid, or stop worrying about) - -One feature per article. No roll-ups. No "also in this release" sections — skipped entries stay skipped. - -### 2c. Save + open PR - -File location: -``` -src/content/blog/product-updates/{feature-slug}.mdx -``` - -Slug: lowercase, hyphens, derived from the feature name. Examples: `first-approval-trigger-mode.mdx`, `agents-md-support.mdx`, `version-branch-targeting.mdx`. **Do not** use month/quarter slugs. - -Branch + worktree (one per article): -```bash -git fetch origin main -BRANCH="articles/$(date +%Y-%m-%d)-{feature-slug}" -WORKTREE=".worktrees/$(echo $BRANCH | tr '/' '-')" -git worktree add -b "$BRANCH" "$WORKTREE" origin/main - -cd "$WORKTREE" -git add src/content/blog/product-updates/{feature-slug}.mdx -git commit -m "content: Launch post — {feature title} - -Generated by launch-post skill." -git push -u origin "$BRANCH" -``` - -PR: -```bash -gh pr create --title "content: Launch post — {feature title}" --body "$(cat <<'EOF' -## Feature -{one-sentence description} - -## Entries considered this run - -Window: {start date} → {end date}. - -| # | Entry | Decision | Reason | -|---|-------|----------|--------| -| 1 | **{feature name}** — {short description} | QUALIFIES | {which bar criterion was met} | -| 2 | **{feature name}** — {short description} | SKIP | {reason — bug fix / internal / polish / etc} | -| ... | ... | ... | ... | - -{N} commit(s) in window. {M} entries considered, {K} qualified. - -## Covered entry (full text) -> {quote the entry verbatim, including any markdown formatting} - -Source: commit [{short sha}]({commit url}). - -## PR(s) researched -- [Promptless/promptless#{num}]({url}) — {title} (merged {date}) - -## File -`src/content/blog/product-updates/{feature-slug}.mdx` - -AI-generated draft — needs human review before publishing. -EOF -)" -``` - -**Always include the full "Entries considered this run" table**, even when only one entry is in window or zero entries were skipped. The reviewer uses this to audit triage decisions — so they need to see every candidate, not just the ones that made it through. - -Cleanup: -```bash -cd - -git worktree remove --force "$WORKTREE" -``` - -### 2d. Notify Slack - -One message per PR: - -```bash -agent-slack message send --workspace promptless "#gtm" \ - "New launch post draft: {feature title} — {pr_url}" -``` - ---- - -## Notes - -- `hidden: false` in frontmatter always. -- If `gh`, `git`, or `agent-slack` fails, report the failure and continue with the remaining qualifiers. -- If no PR can be found for a feature, write what you can from the changelog entry. Don't block on research. -- Typical week: 0–1 posts. Typical month: 2–4. If your triage produces more, recheck the bar. -- The goal is a post a prospect could read to understand *one* thing Promptless shipped and why they should care. Not a press release. Not a monthly summary. diff --git a/.claude/skills/launch-post/references/launch-article-guide.md b/.claude/skills/launch-post/references/launch-article-guide.md deleted file mode 100644 index a3d3b3eb..00000000 --- a/.claude/skills/launch-post/references/launch-article-guide.md +++ /dev/null @@ -1,189 +0,0 @@ -# Launch Article Writing Guide - -Conventions, structure, and MDX format for a **single-feature** Promptless launch article. - -A launch article in this skill is always about **one** feature. If you're tempted to cover two, write two articles. - ---- - -## What a launch article is (and isn't) - -A launch article is a short news story about **one thing Promptless shipped**, written for two audiences at once: -- **Existing customers** who want to know what's new and what they can do with it -- **Prospects** evaluating Promptless who want to see concrete, useful shipping - -It's **not** a changelog. Changelogs list what changed. Launch articles explain what it means. - -A changelog says "First-Approval GitHub PR Trigger Mode." A launch article says "You can now tell Promptless to wait until code has been reviewed before generating documentation suggestions, so you're not processing drafts that might be rewritten." - -The test for every sentence: would a user reading this understand what they can do differently now? If not, rewrite it. - ---- - -## Voice and tone - -**Direct and dry, like a Dutch engineer.** Clear, concise, no flair. No filler, no fluff. State what shipped, why it matters, who it's for. Move on. - -- **Plain statements over conversational hooks.** No "If you've ever wished...", no "Here's the cool part...", no "that's now a single comment away". Just say what it does. -- **Concrete over abstract.** Name the actual workflow. "When a PR gets its first approval" beats "at a configurable trigger point." -- **Honest about tradeoffs.** If a feature fits certain teams better, say so. -- **Not salesy.** No "game-changing", "industry-leading", "best-in-class". Let the feature speak. -- **Humor is OK if it lands naturally.** Never force it. A dry aside works ("We use Starlight for our own docs, so adding support was overdue"). A try-hard one-liner doesn't. -- 10th-grade reading level. Short sentences. Plain words. - -**Banned patterns:** -- Em dashes (—). Use a period or comma. -- Tricolon structures ("fast, reliable, and scalable"). Cut to the most important one. -- "X is not Y, but Z." Rewrite as a direct statement. -- Vague adjectives: "powerful", "seamless", "robust", "streamlined", "intuitive". -- Hedging: "essentially", "basically", "in a sense". -- AI writing tells: "In today's fast-paced world...", "It's worth noting that..." -- Conversational hooks that try to create intimacy: "If you've ever wished...", "Picture this...", "Imagine you..." -- Cute asides and performative jokes. - ---- - -## Structure - -A single-feature launch article is ~500–900 words. Structure: - -### Opening (1–2 short paragraphs) - -Lead with the feature and why it matters. Name the specific workflow it touches. Don't preamble about documentation in general. - -Good: *"Promptless can now wait for a PR's first code review approval before generating documentation suggestions. If your team heavily rewrites PRs during review, your docs won't be based on code that's about to change."* - -Bad: *"Documentation is hard. Teams everywhere struggle with keeping their docs in sync with code..."* - -### The problem - -One paragraph naming the specific pain this feature addresses. Ground it in a real workflow scenario — a team doing X, hitting Y. - -### What it does now - -One or two paragraphs explaining the new behavior. Be concrete. Describe what the user sees, configures, or gets. Screenshot if available. - -### Who benefits most - -One paragraph naming the workflow or team type for whom this is most useful. Calling out the best fit is not a downside — it helps the right users self-identify. - -### How to use it - -If setup is required — a config option, a new setting, an opt-in — describe it briefly. A single code block or a short list is fine here. If no setup is needed, skip this section. - -### Closing (2–3 sentences) - -A brief "what's next" or a direct call to action. Don't restate what you just covered. - -**Do not** add an "also in this release" section. Other changelog entries belong in other articles or nowhere. - ---- - -## MDX frontmatter - -```mdx ---- -title: '{Feature name or outcome — concrete, searchable}' -subtitle: Published {Month} {Year} -description: >- - One or two sentence SEO description. 120–160 characters. Name the specific feature and the user it helps. -date: '{YYYY-MM-DD}T00:00:00.000Z' -author: Frances -tag: Product Updates -section: Featured -hidden: false ---- -import Frame from '@components/fern/Frame.astro'; -import BlogNewsletterCTA from '@components/site/BlogNewsletterCTA.astro'; -import BlogRequestDemo from '@components/site/BlogRequestDemo.astro'; -``` - -- `tag` must be `Product Updates`. -- `section` is typically `Featured`. -- Import `Frame` only if you have a screenshot to embed. -- Always import `BlogNewsletterCTA` and `BlogRequestDemo`. -- Always set `hidden: false`. - -**Title guidance:** -- Name the specific feature or outcome: "First-Approval Trigger Mode", "Version Branch Targeting for Versioned Docs", "AGENTS.md as a Style Guide Source". -- Avoid generic titles: "April 2026 Product Update", "What's New This Month", "A New Way to Work with Promptless". - ---- - -## Body layout - -The body is mostly prose. Use subheadings sparingly — a single-feature post rarely needs more than one or two `##` sections beyond the opening. - -Place `` once, after "What it does now" or "Who benefits most". End the article with ``. - ---- - -## Images - -If a screenshot exists, embed it near the "What it does now" section: - -```mdx - - {descriptive alt text} - -``` - -Screenshot URLs follow the pattern: -`https://promptless-customer-doc-assets.s3.amazonaws.com/docs-images/org_2lvkgU9erOFxYhtEVVC0ymPrPdF/{filename}` - -If you don't have a URL, skip. Don't add placeholders or broken links. - ---- - -## Length - -- **500–900 words** for a standard single-feature launch article. -- If the feature is genuinely substantial (major integration, new workflow with several distinct parts), you can go up to ~1,100 words. Rare. -- If you can't get to 500 words without padding, the feature probably doesn't clear the bar for a launch article. Go back to triage. - ---- - -## What to include vs. what to skip - -**Include:** -- The specific user pain this feature addresses, grounded in a real workflow -- What the user can do now that they couldn't before -- Any configuration or opt-in they need to know about -- Who benefits most (team type, workflow type) - -**Skip:** -- Other features shipped the same week (they get their own posts or none) -- Bug fixes shipped alongside the feature -- Internal implementation details -- Generic intros about the state of documentation, developer productivity, etc. - ---- - -## Example: good single-feature post (excerpt) - -``` -## The problem - -Teams that squash-merge or heavily rewrite PRs during review often found that -triggering on PR open meant Promptless generated suggestions for code that -changed significantly before merging. Reviewers would then see documentation -suggestions for commits that no longer existed on the final branch. - -## What changed - -You can now configure a trigger to fire when a PR gets its first approval -instead of when it opens. Documentation suggestions show up after the code -has been reviewed, not on every work-in-progress push. -``` - -## Example: bad (reads like a changelog) - -``` -## First-Approval GitHub PR Trigger Mode - -Configure Promptless to trigger when a pull request receives its first approval -instead of when it opens — useful for teams that want documentation suggestions -only after code has been reviewed. -``` - -The bad version tells you what the feature does but not why you'd use it or what it costs you if you don't. It also uses an em dash, which is banned. diff --git a/AGENTS.md b/AGENTS.md index bb3ab22d..d3746547 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -2,6 +2,10 @@ Astro + Starlight site. Deployed to Vercel. +Shared Promptless agent safety rules and reusable skills live in +`Promptless/agent-instructions` via the `promptless-internal` plugin. Keep this +file focused on website/docs repo facts. + ## Documentation Map Maintain a map of every file in `docs/` here. When you add, remove, or rename @@ -59,6 +63,15 @@ npm run test:smoke # smoke tests npm run build:diagrams # compile src/diagrams/*.mmd → public/mermaid/*.svg ``` +## Shared Agent Tools + +- Use the shared `promptless-internal` plugin for Slack and reusable Promptless + workflows. +- Use `generate-article` and `launch-post` from the shared plugin for the edu + campaign/blog pipeline. +- Do not add repo-local skills unless they truly cannot live in the central + plugin. + ## Diagrams Mermaid diagram sources live in `src/diagrams/*.mmd`. The compiled SVGs in diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 00000000..43c994c2 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1 @@ +@AGENTS.md From 96d901d1aa6c4ee7b7dc084d02e04d69e11431cb Mon Sep 17 00:00:00 2001 From: Prithvi Ramakrishnan Date: Sat, 9 May 2026 12:06:41 -0700 Subject: [PATCH 2/2] Keep website agent skills repo-local --- .agents/skills/generate-article/SKILL.md | 201 +++++++++++ .../references/style-guide.md | 79 +++++ .agents/skills/launch-post/SKILL.md | 330 ++++++++++++++++++ .../references/launch-article-guide.md | 189 ++++++++++ .claude/skills/generate-article | 1 + .claude/skills/launch-post | 1 + AGENTS.md | 12 +- 7 files changed, 807 insertions(+), 6 deletions(-) create mode 100644 .agents/skills/generate-article/SKILL.md create mode 100644 .agents/skills/generate-article/references/style-guide.md create mode 100644 .agents/skills/launch-post/SKILL.md create mode 100644 .agents/skills/launch-post/references/launch-article-guide.md create mode 120000 .claude/skills/generate-article create mode 120000 .claude/skills/launch-post diff --git a/.agents/skills/generate-article/SKILL.md b/.agents/skills/generate-article/SKILL.md new file mode 100644 index 00000000..6187cc83 --- /dev/null +++ b/.agents/skills/generate-article/SKILL.md @@ -0,0 +1,201 @@ +--- +name: generate-article +description: | + Generate a blog article for the Promptless edu campaign. Researches a keyword via web search, writes a publication-ready MDX draft, commits it to a new branch, opens a GitHub PR, and posts the PR link to Slack. + Use this skill when: + - The user asks to generate, write, or draft a blog article or blog post + - The user mentions the "edu campaign" or "content pipeline" + - The user wants to create content for a keyword from the keyword list + - The user says "generate article", "write a blog post", "new blog draft", or similar + Even if the user just says something like "pick a keyword and write an article" or "run the content pipeline", use this skill. +--- + +## Repository Context + +Use this skill from the `Promptless/promptless.ai` repository. If the current checkout is different, first locate the website/docs repo, usually `~/promptless/promptless.ai`, and run commands there. If that repo is unavailable, explain that this skill requires the Promptless website repo. + + +# Generate Article (Edu Campaign) + +Automated content pipeline for the Promptless blog. This skill replaces the `generate-article.ts` script by using Claude Code's native tools (web search, file writing, git, gh CLI, agent-slack) instead of calling the Anthropic API programmatically. + +## Overview + +1. Pick a keyword (from user input or randomly from the keyword list) +2. Research the topic thoroughly using web search +3. Write a publication-ready MDX article +4. Commit to a new branch, push, and open a GitHub PR +5. Post the PR link to the `#gtm` Slack channel + +## Step 1: Pick a keyword + +If the user provides a keyword or topic, use it directly. + +Otherwise, read the keyword list at `scripts/edu_campaign/keywords.txt` (relative to repo root). Pick one at random. Lines starting with `#` are comments — skip them. Tell the user which keyword was selected. + +## Step 2: Research + +Use the WebSearch tool to research the keyword. Search for 5-8 of the best recent articles, studies, blog posts, and discussions on the topic. Focus on 2024-2026 material. For each source, identify: + +- The main angles already covered +- Data points or case studies cited +- Gaps in existing coverage +- What practitioners are actually struggling with + +Read the most relevant results thoroughly using WebFetch. + +After researching, extract from what you found: + +- **1-2 related keywords:** Terms or phrases that kept appearing alongside the main keyword — especially newer or more specific variants (e.g. searching "knowledge base" might surface "personal wiki" or "second brain"). These are candidates for targeting in the article instead of or alongside the original keyword. +- **1-2 key ideas:** Specific insights, trends, or framings from the research worth building the article around (1-2 sentences each). These should be concrete and grounded in what you actually read — not generic observations. + +Report these to the user before moving on. Use them in Step 3 to sharpen the article's angle. + +## Step 3: Plan the article + +Before writing, produce a detailed plan. The plan must answer: + +- **Keyword(s) to target:** Which keyword(s) — the original, the related ones from Step 2, or a combination — should this article target? Pick what gives the article the sharpest angle and best SEO fit. +- **Key ideas to build on:** Which 1-2 ideas from Step 2 will anchor the article? State them clearly. +- **Article format:** What format best serves this content and reader? Describe it concretely, e.g. "a comparison of two approaches to help readers make a technical decision" or "an explainer that teaches readers why X matters before showing them what to do about it." +- **Thesis:** The single main point this article makes, in one sentence. +- **Target reader:** Who specifically benefits — developers, technical writers, solutions engineers, product managers, developer advocates? What do they already know, and what gap does this fill? +- **Key points:** 4-6 specific claims or arguments the article will make, in order. Each should be concrete — not "discuss X" but "argue that X causes Y because Z." +- **Evidence:** For each key point, what data, case studies, or examples from the research support it, with links. +- **Promptless connection:** How the article's thesis connects to what Promptless does. Not a sales pitch — a logical continuation. E.g. "This article argues that docs go stale faster than teams notice, which is exactly the problem Promptless monitors for." + +Output the plan to the user for review before writing. + +## Step 4: Write the article + +Before writing, scan the existing articles in `src/content/blog/technical/` to: +- Confirm your topic doesn't duplicate an existing article +- Find posts to link to internally (read titles/descriptions of a few to understand voice) +- Calibrate your tone to match the existing body of work + +Then write a complete, publication-ready MDX article strictly following the plan and the style guide. + +### Writing style + +Read `references/style-guide.md` in this skill's directory for the complete writing style and structural conventions. + +Key points: +- 10th grade reading level. Short sentences, plain words. +- Direct and dry, like a Dutch engineer. No filler, no fluff. +- Grounded in concrete examples, real numbers, and cited evidence. +- Do NOT use em dashes. +- Do NOT use tricolon structures (three-item lists for rhetorical effect, e.g. "It's fast, reliable, and cheap"). +- Do NOT use "X is not Y, but Z" constructions. + +### Structural conventions + +- Use `##` for H2 sections, `###` for H3 +- Aim for 800-1400 words +- Lead with a hook that frames the specific problem — NOT a generic intro paragraph +- Do NOT include a generic conclusion that restates what was said +- Place `` at roughly the 40-50% mark (after the first major section) +- End the article with `` + +### MDX frontmatter format + +``` +--- +title: 'Article Title Here' +subtitle: Published {month} {year} +description: >- + One or two sentence SEO description, 120-160 characters. +date: '{YYYY-MM-DD}T00:00:00.000Z' +author: Frances +tag: {tag} +section: Use Cases +hidden: false +--- +import BlogNewsletterCTA from '@components/site/BlogNewsletterCTA.astro'; +import BlogRequestDemo from '@components/site/BlogRequestDemo.astro'; + +[article body starts here] +``` + +Fill in the current date and month/year. For `tag`, choose the best fit from: `Technical`, `Opinion`, `Customer Stories`, `Life at Promptless`. + +## Step 5: Edit pass + +Do a final editorial pass specifically rewriting every instance of: +1. "X is not Y, but Z" constructions — rewrite as a direct statement +2. Tricolon structures — break up or cut to the most important item +3. Em dashes — replace with periods, commas, or parentheses +4. Optimize SEO: update title, subtitle, and description to match what users would search for +5. Add relevant links to other Promptless blog posts where appropriate (check `src/content/blog/` for existing posts) + +## Step 6: Save the file + +The article file goes at: +``` +src/content/blog/technical/{slug}.mdx +``` + +Generate the slug from the article title: lowercase, remove special characters, replace spaces with hyphens, truncate to 60 characters. + +## Step 7: Create a branch and PR + +Use a git worktree so the skill doesn't interfere with whatever branch the user currently has checked out. This is especially important in remote tasks where the working directory may have uncommitted changes. + +```bash +# Fetch latest main +git fetch origin main + +# Create a worktree on a new branch +BRANCH="articles/{date}-{slug}" +WORKTREE=".worktrees/$(echo $BRANCH | tr '/' '-')" +git worktree add -b "$BRANCH" "$WORKTREE" origin/main +``` + +Write the article MDX file into the worktree (at `{worktree}/src/content/blog/technical/{slug}.mdx`), then commit and push: + +```bash +cd "$WORKTREE" +git add src/content/blog/technical/{slug}.mdx +git commit -m "content: Add blog article -- {title} + +Keyword: {keyword} +Generated by edu campaign skill" +git push -u origin "$BRANCH" +``` + +Create the PR from the worktree: +```bash +gh pr create --title "content: {title}" --body "$(cat <<'EOF' +**Keyword:** `{keyword}` + +## Article plan + +{paste the plan summary here} + +## File + +`src/content/blog/technical/{slug}.mdx` + +This is an AI-generated draft and needs human review before publishing. +EOF +)" +``` + +Then clean up: +```bash +cd - +git worktree remove --force "$WORKTREE" +``` + +## Step 8: Notify Slack + +Post the PR link to the `#gtm` channel using agent-slack: + +```bash +agent-slack message send --workspace promptless "#gtm" "New blog article draft ready for review: {title} (keyword: {keyword}) -- {pr_url}" +``` + +## Important notes + +- Always set `hidden: false` in the frontmatter. +- If any step fails (e.g. git push, PR creation, Slack), report the error but continue with remaining steps. +- The article should be genuinely useful content, not thinly-veiled marketing. Promptless connections should feel natural. diff --git a/.agents/skills/generate-article/references/style-guide.md b/.agents/skills/generate-article/references/style-guide.md new file mode 100644 index 00000000..20f4af9f --- /dev/null +++ b/.agents/skills/generate-article/references/style-guide.md @@ -0,0 +1,79 @@ +# Promptless Blog Style Guide + +## Voice and tone + +Promptless's audience is technical writers, DevRel engineers, developer advocates, solutions engineers, and engineering managers at software companies with developer-facing products. + +### Reading level +10th grade. Short sentences, plain words. No jargon without explanation. + +### Personality +Direct and dry, like a Dutch engineer. Say what you mean. Cut anything that doesn't add information. No filler, no fluff. + +### Evidence +Ground every claim in concrete examples, real numbers, or cited evidence. Vague claims ("many companies struggle with...") are not acceptable without supporting data. + +## Banned patterns + +### Em dashes +Do not use em dashes (—) anywhere in the article. Use periods, commas, colons, or parentheses instead. + +### Tricolon structures +Do not use three-item lists for rhetorical effect. Examples of what NOT to do: +- "It's fast, reliable, and cheap" +- "Teams need clarity, consistency, and confidence" +- "This saves time, reduces errors, and improves quality" + +If you catch yourself writing three adjectives or three noun phrases in a row, cut to the most important one or restructure the sentence. + +### "X is not Y, but Z" constructions +Do not use this pattern. Examples of what NOT to do: +- "This isn't a tooling problem, it's a process problem" +- "Documentation isn't a one-time task, it's an ongoing commitment" +- "The issue isn't writing docs, it's keeping them current" + +Rewrite these as direct statements about what the thing IS, without the negation setup. + +## Structure + +### Headings +- `##` for H2 sections +- `###` for H3 subsections +- No H1 in the body (the title serves as H1) + +### Length +800-1400 words. Shorter is better if the topic doesn't require length. + +### Opening +Lead with a hook that frames the specific problem the article addresses. Do NOT write a generic intro paragraph like "In today's fast-paced world..." or "Documentation is important for every company..." + +Start with a concrete situation, a surprising data point, or a specific pain point that the target reader will immediately recognize. + +### Closing +Do NOT include a generic conclusion that restates what was said. The article should end with its final substantive point, followed by the `` component. + +### CTA placement +Place `` at roughly the 40-50% mark of the article, after the first major section. It should fall at a natural break point. + +End the article with `` on its own line. + +## MDX components + +The article must import and use these two components: + +```mdx +import BlogNewsletterCTA from '@components/site/BlogNewsletterCTA.astro'; +import BlogRequestDemo from '@components/site/BlogRequestDemo.astro'; +``` + +Place them on their own lines with blank lines above and below. + +## SEO + +- Title should be specific and contain the target keyword naturally +- Description should be 120-160 characters and accurately summarize the article +- Subtitle format: "Published {Month} {Year}" + +## Links + +Where relevant, link to other Promptless blog posts. Check `src/content/blog/` for existing articles. Also link to external sources cited in the research. diff --git a/.agents/skills/launch-post/SKILL.md b/.agents/skills/launch-post/SKILL.md new file mode 100644 index 00000000..d1c9feb3 --- /dev/null +++ b/.agents/skills/launch-post/SKILL.md @@ -0,0 +1,330 @@ +--- +name: launch-post +description: | + Write Promptless launch articles by triaging recent changelog entries and + writing a focused, single-feature post for each one that meets the bar: + significantly improves UX for a non-trivial segment of users, OR adds + substantial new capabilities to the Promptless product. + + Intended to run as a weekly scheduled task — it scans the last 7 days of + commits to `src/content/changelog/changelogs/` for newly added entries, + decides which (if any) warrant a post, researches the backing PRs in + `Promptless/promptless`, and opens one PR per qualifying feature. + + Use this skill when: + - The weekly cron fires ("/launch-post", "run the weekly launch post check") + - The user says "any launch posts worth writing this week?" + - The user pastes a single changelog entry and asks for a launch post + - The user points at a specific feature and asks for a focused post + - The user asks to backfill a month of launch posts ("backfill March 2026", + "generate launch posts for February") + Always triage first — most changelog entries do not warrant a launch post. + A typical week produces zero or one post; a typical month produces 2–4. +--- + +## Repository Context + +Use this skill from the `Promptless/promptless.ai` repository. If the current checkout is different, first locate the website/docs repo, usually `~/promptless/promptless.ai`, and run commands there. If that repo is unavailable, explain that this skill requires the Promptless website repo. + + +# Launch Post + +Triages recent Promptless changelog entries, picks out the ones that genuinely warrant a launch article, and writes one focused single-feature article per qualifier. + +## Overview + +0. Determine the scan window and collect candidate changelog entries +1. Triage each entry against the bar +1.5. Filter out duplicates (features that already have a launch post or a draft PR) +2. For each qualifier: research the backing PR(s), write a focused single-feature article, open a PR, notify Slack + +For Mode D (monthly backfill), insert a **preview + confirm** step between 1.5 and 2 — see the Mode D section in Step 0. + +Most weeks: 0–1 qualifiers. Occasionally 2. More than 2 in a week is rare — if you're tempted to pick 3+, recheck the bar. + +--- + +## Step 0: Collect candidate changelog entries + +There are four invocation modes. Pick the one that matches the user's request. + +### Mode A — Weekly scheduled run (default) + +Scan commits on the changelog directory from the last 7 days: + +```bash +cd ~/promptless/promptless.ai +git fetch origin main +git log --since="7 days ago" origin/main -- src/content/changelog/changelogs/ \ + --pretty=format:'%H%x09%ai%x09%s' +``` + +For each commit, read the diff to extract the newly added changelog entry: + +```bash +git show --format= "$SHA" -- src/content/changelog/changelogs/ +``` + +Each commit typically adds one entry. Collect the feature name, the short description, and any PR number referenced in the commit message or entry body. + +### Mode B — Single-entry request + +The user pasted one changelog entry and wants a post for it. Skip to Step 1 triage on just that entry. If it qualifies, write it. If it doesn't, say so and stop — don't write the post just because it was asked for. + +### Mode C — Named feature + +The user names a specific feature ("write a post for the AGENTS.md support"). Treat it as a single entry and assume the user has already decided it qualifies. Skip triage, go straight to research and writing. Still produce a single-feature post, not a roll-up. + +### Mode D — Monthly backfill + +The user asks to backfill a specific month ("backfill March 2026", "generate launch posts for February 2026"). Scan all commits that touched the changelog directory within that calendar month: + +```bash +cd ~/promptless/promptless.ai +git fetch origin main +# Example: March 2026 +git log --since="2026-03-01" --until="2026-04-01" origin/main \ + -- src/content/changelog/changelogs/ \ + --pretty=format:'%H%x09%ai%x09%s' +``` + +**Cap the range at one calendar month.** If the user asks for a longer period (a quarter, a year), ask them to run it one month at a time — longer ranges produce too many PRs to review in one batch. + +Collect entries the same way as Mode A (one per commit, usually). + +After Step 1 triage completes, **before firing Step 2**, preview the plan and get the user's confirmation. See "Preview + confirm (Mode D only)" below. + +--- + +## Step 1: Triage each entry + +**The bar:** + +A changelog entry warrants a launch post if it meets at least one of these: +- **Significantly improves UX for a non-trivial segment of users** — a real, describable group of users can now do something meaningfully better or avoid something meaningfully painful. +- **Adds substantial new capabilities to the Promptless product** — a new trigger, integration, workflow, or configuration that unlocks something previously impossible. + +**Clear YES:** +- New trigger types, new integrations, new platform support +- New configuration that unlocks a workflow (e.g. version branch targeting for teams with versioned docs) +- New first-class capabilities (e.g. AGENTS.md as a style guide source) + +**Clear NO:** +- Bug fixes, even important ones (goes on the changelog, not in a launch post) +- Internal refactors, dependency upgrades, logging changes +- Copy tweaks, minor UI polish +- Performance improvements with no visible UX change + +**Borderline — default to NO:** +- Performance improvements with visible impact (only yes if the delta changes how users work) +- UI reorganizations (only yes if they materially change a core workflow) +- Expanded configuration on an existing capability (only yes if it opens a new use case) + +For each entry, output a one-line triage decision: `QUALIFIES` or `SKIP — {reason}`. Be explicit about which criterion from the bar is met for qualifiers. + +If **zero** entries qualify, stop. Report: +- The entries you considered +- Why each was skipped +- What kind of change would meet the bar next week + +Don't write a post just to have something to show. Zero is a valid answer. + +--- + +## Step 1.5: Check for duplicates + +**Mandatory for every mode.** Never write a launch post for a feature that already has one (or has an open draft PR). Running a second post on the same feature is a hard failure — catch it here. + +For each qualifier from Step 1, do the following checks in order: + +### 1. Scan existing posts + +List every existing launch post and read its frontmatter to see what feature it covers: + +```bash +ls src/content/blog/product-updates/ +``` + +For each `.mdx` file, read the frontmatter `title` and `description` (and skim the opening paragraph if the title is ambiguous). Compare **semantically** against the qualifier, not just by slug. A different filename can still describe the same feature. + +Examples of matches to catch: +- Qualifier "AGENTS.md support" vs. existing post titled "Using AGENTS.md as a Style Guide Source" → **duplicate** +- Qualifier "First-approval trigger" vs. existing post "Wait for First Review Before Generating Docs" → **duplicate** +- Qualifier "Starlight support" vs. existing post "Starlight (Astro) Docs Platform Support" → **duplicate** + +### 2. Check open draft PRs + +A post might already be in flight from an earlier skill run. Check open PRs that add to the product-updates directory: + +```bash +gh pr list --repo Promptless/promptless.ai --state open \ + --search "path:src/content/blog/product-updates/" \ + --json number,title,headRefName,files --limit 20 +``` + +Read each result's title and changed files. If any open PR already covers the qualifier, it's a duplicate. + +### 3. Decide + +- **Exact match** (same feature already has a merged post or an open PR) → drop the qualifier, log the duplicate in the triage table, continue with the rest. +- **Borderline match** (similar-but-distinct — e.g. "first-approval trigger" vs. an older post about "draft PR skipping") → pause and ask the user. Don't write speculatively. +- **No match** → proceed to Step 2. + +If a qualifier is dropped as a duplicate, still include it in the PR's "Entries considered this run" table with `SKIP — duplicate of {existing post title or PR #}` so the reviewer can audit the decision. + +--- + +## Preview + confirm (Mode D only) + +After Step 1.5, **before firing Step 2**, print the backfill plan and wait for confirmation. Monthly backfills can produce several PRs at once, and the reviewer needs a chance to drop entries or cap the count before the skill starts opening PRs and pinging Slack. + +Print: + +``` +Backfill plan — {Month Year} + +{N} commits in window. {M} entries considered. {K} qualifiers after dedup: + +Qualifiers (will write posts for these): + 1. {feature title} — {one-line reason} + 2. {feature title} — {one-line reason} + ... + +Skipped ({S}): + - {feature title} — {reason} + - {feature title} — {reason} + ... + +Dropped as duplicates ({D}): + - {feature title} — duplicate of {existing post or open PR} + ... + +Proceed with all {K} qualifiers? Reply with one of: + - "yes" to write all {K} posts + - "drop {1,2}" to remove specific entries by number + - "cap 2" to write only the top N qualifiers + - "no" to abort +``` + +Wait for the user's reply before proceeding. Do **not** open any PRs until confirmed. For Modes A, B, and C, skip this step and go straight to Step 2. + +--- + +## Step 2: For each qualifier — research, write, ship + +### 2a. Research the backing PR(s) + +```bash +gh pr list --repo Promptless/promptless --state merged \ + --search "{feature keyword}" --limit 10 \ + --json number,title,body,mergedAt,url + +gh pr view {number} --repo Promptless/promptless \ + --json title,body,mergedAt,url,files +``` + +For each feature, extract: +- **The problem it solves** — what was breaking or painful before +- **How it works** — user-visible behavior (not code) +- **Who benefits most** — which team type or workflow +- **Any setup required** — configuration, opt-in, caveats + +If a PR description is thin, check linked issues: `gh issue view {number} --repo Promptless/promptless`. The local repo at `~/promptless/promptless` is available too, but `gh` on the remote is usually faster. + +Don't over-research. 2–3 concrete facts is enough. + +### 2b. Write the article + +Read `references/launch-article-guide.md` for voice, structure, MDX format, and the banned-pattern list. + +Core job of a launch article: answer three questions about **one** feature. +1. What changed? (one sentence) +2. How does it work now? (concrete, behavioral) +3. What does this mean for the user? (the implication — what they can do, avoid, or stop worrying about) + +One feature per article. No roll-ups. No "also in this release" sections — skipped entries stay skipped. + +### 2c. Save + open PR + +File location: +``` +src/content/blog/product-updates/{feature-slug}.mdx +``` + +Slug: lowercase, hyphens, derived from the feature name. Examples: `first-approval-trigger-mode.mdx`, `agents-md-support.mdx`, `version-branch-targeting.mdx`. **Do not** use month/quarter slugs. + +Branch + worktree (one per article): +```bash +git fetch origin main +BRANCH="articles/$(date +%Y-%m-%d)-{feature-slug}" +WORKTREE=".worktrees/$(echo $BRANCH | tr '/' '-')" +git worktree add -b "$BRANCH" "$WORKTREE" origin/main + +cd "$WORKTREE" +git add src/content/blog/product-updates/{feature-slug}.mdx +git commit -m "content: Launch post — {feature title} + +Generated by launch-post skill." +git push -u origin "$BRANCH" +``` + +PR: +```bash +gh pr create --title "content: Launch post — {feature title}" --body "$(cat <<'EOF' +## Feature +{one-sentence description} + +## Entries considered this run + +Window: {start date} → {end date}. + +| # | Entry | Decision | Reason | +|---|-------|----------|--------| +| 1 | **{feature name}** — {short description} | QUALIFIES | {which bar criterion was met} | +| 2 | **{feature name}** — {short description} | SKIP | {reason — bug fix / internal / polish / etc} | +| ... | ... | ... | ... | + +{N} commit(s) in window. {M} entries considered, {K} qualified. + +## Covered entry (full text) +> {quote the entry verbatim, including any markdown formatting} + +Source: commit [{short sha}]({commit url}). + +## PR(s) researched +- [Promptless/promptless#{num}]({url}) — {title} (merged {date}) + +## File +`src/content/blog/product-updates/{feature-slug}.mdx` + +AI-generated draft — needs human review before publishing. +EOF +)" +``` + +**Always include the full "Entries considered this run" table**, even when only one entry is in window or zero entries were skipped. The reviewer uses this to audit triage decisions — so they need to see every candidate, not just the ones that made it through. + +Cleanup: +```bash +cd - +git worktree remove --force "$WORKTREE" +``` + +### 2d. Notify Slack + +One message per PR: + +```bash +agent-slack message send --workspace promptless "#gtm" \ + "New launch post draft: {feature title} — {pr_url}" +``` + +--- + +## Notes + +- `hidden: false` in frontmatter always. +- If `gh`, `git`, or `agent-slack` fails, report the failure and continue with the remaining qualifiers. +- If no PR can be found for a feature, write what you can from the changelog entry. Don't block on research. +- Typical week: 0–1 posts. Typical month: 2–4. If your triage produces more, recheck the bar. +- The goal is a post a prospect could read to understand *one* thing Promptless shipped and why they should care. Not a press release. Not a monthly summary. diff --git a/.agents/skills/launch-post/references/launch-article-guide.md b/.agents/skills/launch-post/references/launch-article-guide.md new file mode 100644 index 00000000..a3d3b3eb --- /dev/null +++ b/.agents/skills/launch-post/references/launch-article-guide.md @@ -0,0 +1,189 @@ +# Launch Article Writing Guide + +Conventions, structure, and MDX format for a **single-feature** Promptless launch article. + +A launch article in this skill is always about **one** feature. If you're tempted to cover two, write two articles. + +--- + +## What a launch article is (and isn't) + +A launch article is a short news story about **one thing Promptless shipped**, written for two audiences at once: +- **Existing customers** who want to know what's new and what they can do with it +- **Prospects** evaluating Promptless who want to see concrete, useful shipping + +It's **not** a changelog. Changelogs list what changed. Launch articles explain what it means. + +A changelog says "First-Approval GitHub PR Trigger Mode." A launch article says "You can now tell Promptless to wait until code has been reviewed before generating documentation suggestions, so you're not processing drafts that might be rewritten." + +The test for every sentence: would a user reading this understand what they can do differently now? If not, rewrite it. + +--- + +## Voice and tone + +**Direct and dry, like a Dutch engineer.** Clear, concise, no flair. No filler, no fluff. State what shipped, why it matters, who it's for. Move on. + +- **Plain statements over conversational hooks.** No "If you've ever wished...", no "Here's the cool part...", no "that's now a single comment away". Just say what it does. +- **Concrete over abstract.** Name the actual workflow. "When a PR gets its first approval" beats "at a configurable trigger point." +- **Honest about tradeoffs.** If a feature fits certain teams better, say so. +- **Not salesy.** No "game-changing", "industry-leading", "best-in-class". Let the feature speak. +- **Humor is OK if it lands naturally.** Never force it. A dry aside works ("We use Starlight for our own docs, so adding support was overdue"). A try-hard one-liner doesn't. +- 10th-grade reading level. Short sentences. Plain words. + +**Banned patterns:** +- Em dashes (—). Use a period or comma. +- Tricolon structures ("fast, reliable, and scalable"). Cut to the most important one. +- "X is not Y, but Z." Rewrite as a direct statement. +- Vague adjectives: "powerful", "seamless", "robust", "streamlined", "intuitive". +- Hedging: "essentially", "basically", "in a sense". +- AI writing tells: "In today's fast-paced world...", "It's worth noting that..." +- Conversational hooks that try to create intimacy: "If you've ever wished...", "Picture this...", "Imagine you..." +- Cute asides and performative jokes. + +--- + +## Structure + +A single-feature launch article is ~500–900 words. Structure: + +### Opening (1–2 short paragraphs) + +Lead with the feature and why it matters. Name the specific workflow it touches. Don't preamble about documentation in general. + +Good: *"Promptless can now wait for a PR's first code review approval before generating documentation suggestions. If your team heavily rewrites PRs during review, your docs won't be based on code that's about to change."* + +Bad: *"Documentation is hard. Teams everywhere struggle with keeping their docs in sync with code..."* + +### The problem + +One paragraph naming the specific pain this feature addresses. Ground it in a real workflow scenario — a team doing X, hitting Y. + +### What it does now + +One or two paragraphs explaining the new behavior. Be concrete. Describe what the user sees, configures, or gets. Screenshot if available. + +### Who benefits most + +One paragraph naming the workflow or team type for whom this is most useful. Calling out the best fit is not a downside — it helps the right users self-identify. + +### How to use it + +If setup is required — a config option, a new setting, an opt-in — describe it briefly. A single code block or a short list is fine here. If no setup is needed, skip this section. + +### Closing (2–3 sentences) + +A brief "what's next" or a direct call to action. Don't restate what you just covered. + +**Do not** add an "also in this release" section. Other changelog entries belong in other articles or nowhere. + +--- + +## MDX frontmatter + +```mdx +--- +title: '{Feature name or outcome — concrete, searchable}' +subtitle: Published {Month} {Year} +description: >- + One or two sentence SEO description. 120–160 characters. Name the specific feature and the user it helps. +date: '{YYYY-MM-DD}T00:00:00.000Z' +author: Frances +tag: Product Updates +section: Featured +hidden: false +--- +import Frame from '@components/fern/Frame.astro'; +import BlogNewsletterCTA from '@components/site/BlogNewsletterCTA.astro'; +import BlogRequestDemo from '@components/site/BlogRequestDemo.astro'; +``` + +- `tag` must be `Product Updates`. +- `section` is typically `Featured`. +- Import `Frame` only if you have a screenshot to embed. +- Always import `BlogNewsletterCTA` and `BlogRequestDemo`. +- Always set `hidden: false`. + +**Title guidance:** +- Name the specific feature or outcome: "First-Approval Trigger Mode", "Version Branch Targeting for Versioned Docs", "AGENTS.md as a Style Guide Source". +- Avoid generic titles: "April 2026 Product Update", "What's New This Month", "A New Way to Work with Promptless". + +--- + +## Body layout + +The body is mostly prose. Use subheadings sparingly — a single-feature post rarely needs more than one or two `##` sections beyond the opening. + +Place `` once, after "What it does now" or "Who benefits most". End the article with ``. + +--- + +## Images + +If a screenshot exists, embed it near the "What it does now" section: + +```mdx + + {descriptive alt text} + +``` + +Screenshot URLs follow the pattern: +`https://promptless-customer-doc-assets.s3.amazonaws.com/docs-images/org_2lvkgU9erOFxYhtEVVC0ymPrPdF/{filename}` + +If you don't have a URL, skip. Don't add placeholders or broken links. + +--- + +## Length + +- **500–900 words** for a standard single-feature launch article. +- If the feature is genuinely substantial (major integration, new workflow with several distinct parts), you can go up to ~1,100 words. Rare. +- If you can't get to 500 words without padding, the feature probably doesn't clear the bar for a launch article. Go back to triage. + +--- + +## What to include vs. what to skip + +**Include:** +- The specific user pain this feature addresses, grounded in a real workflow +- What the user can do now that they couldn't before +- Any configuration or opt-in they need to know about +- Who benefits most (team type, workflow type) + +**Skip:** +- Other features shipped the same week (they get their own posts or none) +- Bug fixes shipped alongside the feature +- Internal implementation details +- Generic intros about the state of documentation, developer productivity, etc. + +--- + +## Example: good single-feature post (excerpt) + +``` +## The problem + +Teams that squash-merge or heavily rewrite PRs during review often found that +triggering on PR open meant Promptless generated suggestions for code that +changed significantly before merging. Reviewers would then see documentation +suggestions for commits that no longer existed on the final branch. + +## What changed + +You can now configure a trigger to fire when a PR gets its first approval +instead of when it opens. Documentation suggestions show up after the code +has been reviewed, not on every work-in-progress push. +``` + +## Example: bad (reads like a changelog) + +``` +## First-Approval GitHub PR Trigger Mode + +Configure Promptless to trigger when a pull request receives its first approval +instead of when it opens — useful for teams that want documentation suggestions +only after code has been reviewed. +``` + +The bad version tells you what the feature does but not why you'd use it or what it costs you if you don't. It also uses an em dash, which is banned. diff --git a/.claude/skills/generate-article b/.claude/skills/generate-article new file mode 120000 index 00000000..79518ecf --- /dev/null +++ b/.claude/skills/generate-article @@ -0,0 +1 @@ +../../.agents/skills/generate-article \ No newline at end of file diff --git a/.claude/skills/launch-post b/.claude/skills/launch-post new file mode 120000 index 00000000..3beed041 --- /dev/null +++ b/.claude/skills/launch-post @@ -0,0 +1 @@ +../../.agents/skills/launch-post \ No newline at end of file diff --git a/AGENTS.md b/AGENTS.md index d3746547..86c71294 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -65,12 +65,12 @@ npm run build:diagrams # compile src/diagrams/*.mmd → public/mermaid/*.svg ## Shared Agent Tools -- Use the shared `promptless-internal` plugin for Slack and reusable Promptless - workflows. -- Use `generate-article` and `launch-post` from the shared plugin for the edu - campaign/blog pipeline. -- Do not add repo-local skills unless they truly cannot live in the central - plugin. +- Use the shared `promptless-internal` plugin for Slack, PR review, and generic + Promptless workflows. +- Website-owned content skills live in `.agents/skills/` in this repo. Use + `generate-article` and `launch-post` for the edu campaign/blog pipeline. +- Keep new repo-local skills limited to workflows that depend on this checkout's + content collections, scripts, or file layout. ## Diagrams