CoderHelm Docs
Plugins

Build the marketing pluginComing soon

A full walkthrough, from an empty repo to a plugin that drafts launch posts from each release and publishes after an editor approves.

Coming soon

Plugins are being built and switched on team by team; they are not on main yet. This page follows the work in progress and will be updated when plugins ship.

In this walkthrough you build a plugin that:

  • reads each new release (and a weekly schedule, and briefs from people),
  • drafts a blog post in your CMS, three LinkedIn posts and an email, in your brand voice,
  • waits for an editor to approve a post, then publishes it to LinkedIn at the time they pick.

The finished plugin is in the docs repository at docs/examples/marketing. The files on this page are included from it, and the docs' tests validate it on every build, so what you read here is what passes.

Create the repo

mkdir marketing-plugin && cd marketing-plugin
git init
mkdir -p skills/brand-voice skills/launch-checklist agents

Write the brand voice skill

The skill holds what is true for every draft. It refers to the team's own guide, a setting, rather than containing any brand's text.

Brand voice

The team's own voice guide is in the brand_voice_doc setting. Follow it first. These rules apply when the guide says nothing:

  • Write to one reader, in the second person ("you").
  • Lead with what changed for the reader, then how it works.
  • Short sentences. One idea per paragraph.
  • Name the feature the way the product names it.
  • No superlatives you cannot prove. Never use a claim listed in the banned_claims setting.
  • Every claim about the product must come from the release notes or a merged pull request. If it is not there, leave it out.

Length

ChannelLength
LinkedIn40 to 120 words, one link
Blog300 to 700 words, one heading per change
EmailSubject under 60 characters; body under 150 words

Add a checklist skill

A second skill the agent runs over each draft before saving it.

Launch checklist

Before saving any draft, check that it:

  1. Names the release (the tag) and links to its notes.
  2. Says who the change is for.
  3. Has one clear next step for the reader, such as "Try it from Settings".
  4. Contains no claim from banned_claims and nothing the release notes do not support.
  5. Fits the length for its channel in the brand voice skill.

If a check fails, fix the draft. If it cannot be fixed from the sources, say so in the draft's note to the editor.

Write the agent

The agent's instructions say what it gets for each trigger, what to produce, and what it must never do. Its tools: list names only tools the manifest will grant.

You write launch content for the team's product.

What you get

  • On a release: read the release notes CoderHelm wrote, and the merged pull requests in it, with get_release_notes.
  • On the weekly schedule: the releases since last Monday. If there were none, stop and save nothing.
  • On a brief: the person's brief and the channels they picked.

What you do

  1. Read the brand-voice and launch-checklist skills.
  2. Look up earlier posts on the same feature with query_documents, so you do not repeat them.
  3. Write one draft per channel:
    • blog: save it in Sanity with create_draft, then save a blog draft that links to it.
    • social: three LinkedIn variants as separate social drafts.
    • email: one email draft with a subject line.
  4. Run the launch checklist on each draft and fix what fails.

What you never do

  • Never call publish_post yourself. Publishing happens only after an editor approves a draft, and CoderHelm makes that call with the approved text.
  • Never invent numbers, customers or quotes.

Write the manifest

Now declare everything the plugin needs.

plugin.yaml
# yaml-language-server: $schema=https://docs.coderhelm.com/schema/plugin-manifest.v1.json
name: marketing
version: 1.0.0
description: Drafts launch posts, emails and blog updates from each release, in your brand voice, for an editor to approve.

skills:
  - skills/*/SKILL.md
agents:
  - agents/*.md

settings:
  - key: brand_voice_doc
    type: string
  - key: banned_claims
    type: string
  - key: sanity
    type: connection
    provider: mcp
  - key: linkedin
    type: connection
    provider: oauth2
    scopes: [w_member_social]
    sensitive: true

tools:
  - name: get_release_notes
    kind: builtin
    description: Read the release notes CoderHelm wrote for a release, with its merged pull requests.
    risk: read
  - ref: mcp:sanity
    allow: [get_document, query_documents, create_draft]
    risk: write
  - name: publish_post
    kind: http
    description: Publish an approved post to the company's LinkedIn page.
    risk: consequential

triggers:
  - on: github.release
  - on: schedule
    cron: "0 14 * * MON"
  - on: manual
    form:
      brief: text
      channels: [linkedin, blog, email]

outputs:
  - type: draft
    kinds: [blog, social, email]
  - type: publish
    via: publish_post
    approval:
      role: marketing_editor
      min: 1

budget:
  monthly_tokens: 2000000

Section by section:

  • settings: the brand guide and banned claims are values the team types; the agent reads them. `sanity` and `linkedin` are accounts the team connects.
  • tools: `get_release_notes` is a built-in read tool. From Sanity, only three tools, at `write` risk because drafts there are not public. `publish_post` is the plugin's own tool, `consequential` because it reaches the public, and it signs in with the team's `linkedin` connection.
  • triggers: every release, Mondays at 14:00, and a brief form with a list of channels.
  • outputs: three kinds of draft, and publishing through `publish_post` once one approver says yes.
  • budget: at most 2 million tokens a month, unless the team changes it.

Validate

```bash curl -fsSLO https://docs.coderhelm.com/tools/validate-plugin.mjs node validate-plugin.mjs . ```

```txt ok marketing 1.0.0 ```

Try breaking it: change `publish_post`'s `risk` to `write` and the validator refuses, because a publish output must go through a consequential tool. Add `delete_document` to the agent's `tools:` and it refuses again, because the manifest doesn't grant it.

Push and install

Push the repo to GitHub. Then, in CoderHelm:

  1. An admin opens Plugins, adds the repository under Plugin sources (its public address, or `owner/name` if your GitHub App can read it), and picks the branch, tag or commit to follow.
  2. marketing appears under Plugins you can install. The admin installs it; with two or more admins, a second admin approves.
  3. On the plugin's page, the team fills in `brand_voice_doc` and `banned_claims`, connects Sanity (on Connections) and LinkedIn, and lists the editors under Who approves what goes out.

Run it

Start it by hand first: on the plugin's page, under Write a brief, ask for "Announce the new export button", pick channels and click Start the run. Or publish a release on a connected repository.

The run reads the release notes, checks earlier posts in Sanity, and saves the drafts. If the team turned on Slack or Teams notices, a card goes to the channel.

Approve and publish

An editor (not the person who asked) opens Plugins → Review, edits one LinkedIn variant (which saves a new version), and approves it for Tuesday at 10:00 with Approve for that time. On Tuesday, CoderHelm calls `publish_post` with exactly the approved text. Until then, anyone allowed can Cancel publish. The Audit log shows the trigger, the plugin version, each tool call and its risk, the editor's approval and the result.

Where to go next

  • Add a email publish tool for your email provider, with its own approval.
  • Add a read tool for post analytics from your connections, so next week's run can see what worked.
  • Put the plugin in your private marketplace for other teams.
Edit on GitHub

On this page