Write and update release notes in whatsNew.ts with context-aware content generation
Write and update release notes in src/ts/docs/help/whatsNew.ts. This skill can generate release note content from conversation context, git branch changes, or Linear issues.
The file src/ts/docs/help/whatsNew.ts exports a function that returns an array of block elements. Each release is a "page" with this structure:
// Helper definitions at top of file (lines 1-24)
const hl = (t: string) => `<span class="highlight">${t}</span>`;
const text = (t: string) => block(I.TextStyle.Paragraph, t);
const h1 = (t: string) => block(I.TextStyle.Header1, t);
const h2 = (t: string) => block(I.TextStyle.Header2, t);
const bullet = (t: string) => block(I.TextStyle.Bulleted, t);
const img = (src: string) => text(`<img src="..." />`);
const link = (url: string, t: string) => `<a href="${url}">${t}</a>`;
const div = () => ({ type: I.BlockType.Div, style: I.DivStyle.Dot });
// Release pages start after helpers (~line 25)
return [
{ type: I.BlockType.IconPage, icon: '...' },
title(`Catchy Release Title`), // PAGE START
h4(`<span>Release 0.XX.0</span>`),
text(''),
text('Intro paragraph...'),
text(''),
// Big Feature Sections (h2)
h2(`Feature Name`),
text(`Feature description...`),
img(`XX/1.png`),
text(``),
// Quality of Life Improvements
h2(`Quality of Life Improvements`),
text(``),
text(`<b>Improvement Name</b>`),
text(`Description...`),
text(``),
// Bug Fixes
h2(`Bug Fixes`),
text(``),
text(`<b>Category Name</b>`),
bullet(`Fix description. Thanks to @${link('url', 'user')}!`),
text(``),
div(), // PAGE END
// --------------------------------------------//
// Next release page...
];
A release may be split across multiple Linear root issues (parts), e.g.:
JS-8571 "Release 18 | 0.54.0 | Desktop | Part 1" — contains Features, Design, TechJS-8672 "Release 18 | 0.54.0 | Desktop | Part 2" — contains Bugs, QualityCommands that accept issue IDs (all, from-parent, docx) support multiple IDs. When multiple roots are provided, fetch each one and merge their children by type (features, QoL, bugs). Each part's items keep their relative Linear ordering; Part 1 items come before Part 2.
When processing issues from Linear (via from-parent, all, docx), skip any issue that matches:
Analytics-only — labels contain only 📈 analytics (no bug/quality/feature labels)
Internal — labels contain only 🖌️ design or ⚙️ tech (no user-facing labels)
Alpha-version references — the issue title or description contains a version string with an -alpha suffix (e.g., 0.53.28-alpha, 0.50.29-alpha). These issues were reported against pre-release builds and were already fixed before the public release; including them would confuse users who never saw the bug.
Detection: match the regex \d+\.\d+\.\d+-alpha in title or description fields.
Parse the user's command to determine the action:
/release-notes feature "Title" [context]Add a big feature (h2 section) to the current release.
Insertion point: Before h2(\Quality of Life Improvements`)`
Format:
h2(`Feature Title`),
text(`Description paragraph 1.`),
text(`Description paragraph 2 if needed.`),
text(``),
/release-notes qol "Title" [context]Add a Quality of Life improvement.
Insertion point: After h2(\Quality of Life Improvements`), before h2(`Bug Fixes`)`
Format:
text(`<b>Improvement Title</b>`),
text(`Description. Can mention shortcuts like ${hl('Cmd+V')}.`),
text(``),
Ordering: When adding multiple QoL items, sort them by impact — largest first, smallest last:
Community-credited items can appear at any tier based on their impact — don't group them separately.
/release-notes fix "Category" [context]Add a bug fix under the specified category.
Insertion point: Under h2(\Bug Fixes`)`, find or create the category
Format:
text(`<b>Category Name</b>`), // Only if category doesn't exist
bullet(`Fix description. Thanks to @${link('https://community.anytype.io/t/XXXXX', 'username')}!`),
Existing categories:
/release-notes from-parent JS-XXXXBatch create entries from a Linear parent issue and its sub-issues.
bug label → fixfeature label → featureimprovement label → qol/release-notes new "0.XX.0" "Title"Create a new release page at the top of the file.
Insert after: The IconPage block and before the first title() call
Format:
title(`Catchy Title`),
h4(`<span>Release 0.XX.0</span>`),
text(''),
text('Intro paragraph describing the release highlights.'),
text(''),
h2(`Quality of Life Improvements`),
text(``),
h2(`Bug Fixes`),
text(``),
div(),
// --------------------------------------------//
/release-notes all JS-XXXX [JS-YYYY ...]Generate a complete release notes page from one or more root Linear issues (typically sprint/milestone issues). When a release is split across multiple parts, pass all root issue IDs — their children are merged by type.
How it works:
curl -s -X POST "https://api.linear.app/graphql" \
--header "Content-Type: application/json" \
--header "Authorization: $(printenv LINEAR_API_KEY)" \
--data '{"query":"query{issue(id:\"JS-XXXX\"){title description labels{nodes{name}}children(first:250){nodes{identifier title description state{name}labels{nodes{name}}children(first:250){nodes{identifier title description state{name}labels{nodes{name}}}}}}}}"}' | jq .
| Child labels | Classification | Action |
|---|---|---|
📁 folder + 🐛 bug | Bug folder | Process like from-parent: fetch grandchildren, categorize into bug fix categories, write bullet entries |
📁 folder + 👌 quality | QoL folder | Process like from-parent: fetch grandchildren, write QoL entries. Items with 💫 feature label become h2 features instead |
💫 feature (no folder) | Standalone feature | Write as h2 feature section |
📈 analytics only | Analytics (internal) | Skip entirely — not user-facing |
Create the release page structure (if not already present):
new page with version and titleh2('Quality of Life Improvements')h2('Bug Fixes') organized by categoryintroFor grandchildren (sub-issues of folder issues), apply the same label-based rules:
🐛 bug → bug fix bullet👌 quality → QoL entry💫 feature → h2 feature or QoL depending on scope📈 analytics only → skip👨💻 feedback → community-reported, extract username and link from descriptionOrder of operations:
Community credits: When a sub-issue has 👨💻 feedback label, look for community links in the description (https://community.anytype.io/t/XXXXX) and extract the reporter's name. Add Thanks to @${link('url', 'name')}! to the entry.
Example:
User: /release-notes all JS-8500
Root issue "Sprint 18" has children:
JS-8574 "Bugs | 18" [📁 folder, 🐛 bug] → process 81 bug sub-issues
JS-8573 "Quality | 18" [📁 folder, 👌 quality] → process 25 QoL sub-issues
JS-292 "[epic] Tabs" [💫 feature] → h2 feature
JS-4551 "[epic] Filters" [💫 feature] → h2 feature
JS-8725 "Chat Search" [💫 feature] → h2 feature
JS-8703 "Transfer Ownership" [💫 feature] → h2 feature
Result: Complete release page with features, QoL, bug fixes, and intro.
/release-notes docx JS-XXXX [JS-YYYY ...]Export the current release notes as a .docx file with hyperlinks to Linear issues. Items follow Linear's ordering (the order children appear under their parent issue), not the category-based grouping from whatsNew.ts.
Accepts one or more root issue IDs. When a release is split across multiple Linear issues ("parts"), pass all of them — their children are merged by type (features, QoL, bugs) and each part's items keep their relative order within the merged section.
How it works:
first:250):curl -s -X POST "https://api.linear.app/graphql" \
--header "Content-Type: application/json" \
--header "Authorization: $(printenv LINEAR_API_KEY)" \
--data '{"query":"query{issue(id:\"JS-XXXX\"){title description labels{nodes{name}}children(first:250){nodes{identifier title description state{name}labels{nodes{name}}children(first:250){nodes{identifier title description state{name}labels{nodes{name}}}}}}}}"}' | jq .
| Child labels | Classification | Action |
|---|---|---|
💫 feature (with or without 📁 folder) | Features | If folder: use grandchildren as individual features. If standalone: single feature entry |
📁 folder + 👌 quality | QoL folder | Grandchildren → QoL items. Items with 💫 feature label become features instead |
🐛 bug (with or without 📁 folder) | Bug folder | Grandchildren → Bug Fixes |
📈 analytics only | Analytics | Skip |
🖌️ design / ⚙️ tech only | Internal | Skip — not user-facing |
When merging across parts, append Part 2's items after Part 1's items within each section (preserving each part's internal ordering).
Read the current release page from whatsNew.ts to get the user-facing note text for each entry.
Match each Linear issue to its whatsNew.ts entry. Matching strategy:
h2(\Tabs`)`)text(\<b>Toggle Headings</b>`)`) and collect the description paragraph(s) that followcommunity.anytype.io/t/XXXXX), by keywords from the issue title, or by position within the same category**Title** – <Linear title>.Build a JSON data file with this structure, preserving Linear's child ordering within each section:
{
"version": "0.54.0",
"title": "Focus & Flow",
"sections": [
{
"heading": "Features",
"items": [
{"id": "JS-292", "text": "Note text from whatsNew.ts (cleaned of HTML)."},
{"id": "JS-4551", "text": "..."}
]
},
{
"heading": "Quality of Life Improvements",
"items": [
{"id": "JS-XXXX", "text": "**Toggle Headings** – H1, H2, H3 can now be collapsible toggles..."}
]
},
{
"heading": "Bug Fixes",
"items": [
{"id": "JS-8845", "text": "Action icons now look consistent across light and dark modes."}
]
}
]
}
Text cleanup rules when extracting from whatsNew.ts:
<span class="highlight">X</span> → X, <b>X</b> → **X**, <i>X</i> → X<a href="...">text</a> → text${hl('Cmd+F')} → Cmd+F, ${link('url', 'name')} → name**Title** – Description text.Write the JSON to a temp file, then run the generator script:
python3 .claude/skills/release-notes/generate_docx.py /tmp/release_data.json <output_path>
Output location: Save the .docx in the project root as release-<version>.docx (e.g., release-0.54.0.docx).
Item ordering: Within each section, items MUST appear in the same order as they are listed under their parent issue in Linear. This is the order returned by the Linear API's children field. Do NOT reorder by category, alphabet, or any other criterion. When multiple parts are merged, Part 1 items come first, then Part 2, etc.
Example (multi-part):
User: /release-notes docx JS-8571 JS-8672
1. Fetch JS-8571 (Part 1) children:
- JS-8575 [💫 + 📁] → Features folder → 4 feature grandchildren
- JS-8630 [🖌️ + 📁] → Design folder → skip (internal)
- JS-8594 [⚙️ + 📁] → Tech folder → skip (internal)
2. Fetch JS-8672 (Part 2) children:
- JS-8573 [👌 + 📁] → QoL folder → 33 grandchildren → QoL items
- JS-8574 [🐛] → Bug folder → 82 grandchildren → Bug Fixes
3. Merge: Features from Part 1, QoL from Part 2, Bugs from Part 2
4. Read whatsNew.ts, match each issue to its note text
5. Write JSON with items in Linear order
6. Run generate_docx.py → release-0.54.0.docx
7. Report: "Generated release-0.54.0.docx with 4 features, 33 QoL items, 82 bug fixes."
/release-notes introWrite or update the intro paragraph for the current release.
How it works:
text('') lines between h4(...) and the first h2(...)Insertion point: The text('') lines between the h4(<span>Release ...</span>) and the first content section
Format:
text('Intro sentence one. Intro sentence two.'),
text('Intro sentence three if needed.'),
Style:
/release-notes showDisplay the current release notes (first page in the file).
When user provides a context hint instead of explicit text:
today or sessionbranchgit log main..HEAD --oneline to see commitsgit diff main --stat to see changed filesfeature/JS-8826-advanced-filters)JS-XXXX (Linear issue)curl -s -X POST "https://api.linear.app/graphql" \
--header "Content-Type: application/json" \
--header "Authorization: $(printenv LINEAR_API_KEY)" \
--data '{"query":"query{issue(id:\"JS-XXXX\"){title description state{name}priority labels{nodes{name}}}}"}' | jq .
Use the provided text directly, but ensure it follows the style guidelines.
Action-oriented, clear names:
${hl('Cmd+V')} syntax<b>bold</b> for emphasis within textWhen fixing user-reported issues:
bullet(`Fix description. Thanks to @${link('https://community.anytype.io/t/12345', 'username')}!`)
User: /release-notes feature "Advanced Filters" today
Action:
1. Review conversation - user worked on advanced filters with AND/OR logic
2. Find insertion point before h2(`Quality of Life Improvements`)
3. Insert:
h2(`Advanced Filters`),
text(`Need more control over what shows in your Views? You can now create Advanced Filters that combine multiple conditions using AND or OR logic. Group related rules together to build precise queries – like finding all tasks that are either high priority or due this week.`),
text(``),
User: /release-notes qol "Filter Bar Redesign" branch
Action:
1. Analyze git changes on current branch
2. Find insertion point after h2(`Quality of Life Improvements`)
3. Insert:
text(`<b>Filter Bar Redesign</b>`),
text(`Filters and Sorts in Queries and Collections got a fresh look. Active filters now appear in a dedicated bar above your View, showing the property name, condition, and value at a glance.`),
text(``),
User: /release-notes fix "Objects & Views" "Filter items now show correct active state"
Action:
1. Find h2(`Bug Fixes`)
2. Find text(`<b>Objects & Views</b>`) category (or create if missing)
3. Insert bullet after category header:
bullet(`Filter items now show correct active state.`),
User: /release-notes intro
Action:
1. Read whatsNew.ts — find h2 feature sections: "Tabs", "Advanced Filters", "Chat Search", "Transfer Channel Ownership"
2. Write 2–3 sentences covering the highlights
3. Replace empty text('') lines after h4(`<span>Release 0.54.0</span>`):
text('This release brings Tabs to Anytype — open multiple Objects side by side and pin the ones you use most. Advanced Filters let you combine conditions with AND/OR logic, Chat Search helps you find any message instantly, and Channel Owners can now transfer ownership to another member.'),
User: /release-notes all JS-8500
Action:
1. Fetch root issue JS-8500 — "Sprint 18" with 6 children
2. Classify children:
- JS-8574 [📁 + 🐛] → bug folder, fetch 81 grandchildren → bug fixes
- JS-8573 [📁 + 👌] → QoL folder, fetch 28 grandchildren → QoL entries + features
- JS-292 [💫] → standalone feature "Tabs"
- JS-4551 [💫] → standalone feature "Advanced Filters"
- JS-8725 [💫] → standalone feature "Chat Search"
- JS-8703 [💫] → standalone feature "Transfer Ownership"
3. Create new release page with `new "0.54.0" "Focus & Flow"`
4. Insert 4 feature h2 sections
5. Insert 25 QoL entries
6. Insert 80 bug fixes across categories
7. Write intro paragraph