Skip to content

Product Configuration

Products are defined as YAML files in products/. Each file represents one product that Cryyer tracks and sends updates for.

id: my-app
name: My App
repo: owner/repo
emailSubjectTemplate: "My App — Week of {{weekOf}}"
voice: |
You are writing a product update email for My App users.
Be concise, friendly, and focus on what matters to users.
FieldRequiredDescription
idYesUnique identifier, also used as GitHub issue label
nameYesDisplay name
voiceYes*LLM voice/tone instructions (injected into the draft prompt)
repoYesowner/repo for GitHub activity gathering
emailSubjectTemplateYes*Subject line template — use {{weekOf}} for the date
audiencesNoList of audience-specific overrides (see Audiences)
taglineNoProduct tagline
from_nameNoOverride sender name for this product
from_emailNoOverride sender email for this product
reply_toNoReply-to address

* Required when audiences is not set. When using audiences, voice and emailSubjectTemplate are set per-audience instead.

The voice field is injected directly into the LLM prompt and controls the tone of generated emails. Use a multi-line string to give detailed instructions about your product’s communication style.

voice: |
You are writing a product update for Acme Cloud users.
Tone: professional but approachable. Use "we" not "I".
Focus on user-facing changes. Skip internal refactors.
Keep paragraphs short — these are busy developers.

If you need different email voices for different subscriber groups (e.g. beta testers vs enterprise customers), use the audiences field instead of top-level voice and emailSubjectTemplate:

id: my-app
name: My App
repo: owner/repo
audiences:
- id: beta
voice: |
You are writing a casual update for My App beta testers.
Be enthusiastic and highlight experimental features.
emailSubjectTemplate: "My App Beta — Week of {{weekOf}}"
- id: enterprise
voice: |
You are writing a professional update for My App enterprise customers.
Focus on stability, security, and compliance improvements.
emailSubjectTemplate: "My App Enterprise Update — {{weekOf}}"
from_name: My App Enterprise
from_email: enterprise@myapp.com

Each audience gets its own draft and email send. Subscribers are scoped per audience — a subscriber to my-app:beta won’t receive my-app:enterprise emails.

FieldRequiredDescription
idYesUnique identifier for this audience
voiceYesLLM voice/tone instructions for this audience
emailSubjectTemplateYesSubject template for this audience
from_nameNoOverride sender name (inherits from product)
from_emailNoOverride sender email (inherits from product)
reply_toNoOverride reply-to address (inherits from product)

When using cryyer draft-file or cryyer send-file with a multi-audience product, pass --audience to target a specific audience:

Terminal window
cryyer draft-file --product my-app --audience beta --output drafts/v1.0-beta.md
cryyer send-file drafts/v1.0-beta.md --product my-app --audience beta

The MCP server subscriber tools (list_subscribers, add_subscriber, remove_subscriber) accept an optional audience_id parameter. The composite GitHub Actions (draft-file, send-file) accept an optional audience input.

If multiple products share the same repo (a monorepo), you can add a filter block so each product only gets its own activity:

id: admin-portal
name: Admin Portal
repo: myorg/monorepo
emailSubjectTemplate: "Admin Portal — Week of {{weekOf}}"
voice: "..."
filter:
labels: ["admin-portal"]
paths: ["apps/admin/"]
tag_prefix: "admin/"

All three filter fields are optional — use any combination. Without a filter block, Cryyer gathers all activity from the repo (the default).

FieldWhat it filtersHow it works
pathsPRs + commitsIncludes only PRs that touch files under these path prefixes (checks changed files per PR). Also scopes fallback commits to these paths. This is the easiest option — no labeling or tagging workflow needed.
labelsPRsUses the GitHub Search API to fetch only merged PRs with these labels. A single API call, but requires your PRs to be labeled.
tag_prefixReleasesOnly includes releases whose tag starts with this prefix (e.g. "admin/" matches "admin/v1.2.0" but not "ios/v3.0.0").

The simplest setup — just use paths:

products/admin-portal.yaml
id: admin-portal
repo: myorg/monorepo
filter:
paths: ["apps/admin/"]
tag_prefix: "admin/" # optional, if you use tag prefixes for releases
# ...
# products/ios-app.yaml
id: ios-app
repo: myorg/monorepo
filter:
paths: ["apps/ios/"]
tag_prefix: "ios/"
# ...
# products/api.yaml
id: api
repo: myorg/monorepo
filter:
paths: ["packages/api/", "libs/shared/"]
# ...

paths checks each PR’s changed files — no setup required, but makes one API call per PR. For most weekly digests (5–20 PRs) this is fine.

labels uses the GitHub Search API — a single API call no matter how many PRs exist, but requires your team to label PRs. If you already label PRs by area, this is more efficient.

If both labels and paths are set, labels is used for PRs (more efficient) and paths still applies to fallback commits.

The emailSubjectTemplate supports these placeholders:

  • {{weekOf}} — replaced with the current week’s date (for weekly pipeline)
  • {{version}} — replaced with the release version (for release pipeline)
# Weekly pipeline
emailSubjectTemplate: "My App — Week of {{weekOf}}"
# → "My App — Week of January 6, 2025"
# Release pipeline
emailSubjectTemplate: "My App v{{version}} Release Notes"
# → "My App v1.2.0 Release Notes"