---
name: store-listing-copy
description: >
  Generate polished, platform-validated App Store / Google Play / Connect IQ store listing copy
  (title, subtitle, description, "what's new", keywords) from a git changelog or release notes.
  Use when preparing a new release submission, when the user says "write the store listing",
  "update the App Store description", "draft the what's new", or "prepare my store copy" for iOS,
  Android, or Garmin apps. Chains naturally after changelog-generator and before apple-store-submit
  or garmin-watchface's store publishing workflow.
license: MIT
allowed-tools: Bash, Read, Write, Edit, WebSearch
compatibility: Codex, Claude Code, Cursor, GitHub Copilot, Windsurf, Kiro, and other Agent Skills compatible tools. Requires git history access in the target repository.
metadata:
  targets: [_source-only]
  author: Oleg Koval
  tags:
    - app-store
    - google-play
    - connect-iq
    - garmin
    - store
    - listing
    - copy
    - release-notes
    - ios
    - android
source: weekly-pattern-learner
source_reason: "garmin-watchface got a full store publishing workflow this week and apple-store-submit handles rejections, but neither skill generates the listing copy itself: the human still writes descriptions and keywords manually"
source_date: "2026-07-31"
---

> 🤖 *Auto-generated by **weekly-pattern-learner** · garmin-watchface got a full store publishing workflow this week and apple-store-submit handles rejections, but neither skill generates the listing copy itself: the human still writes descriptions and keywords manually*

# Store Listing Copy

Generate platform-ready store listing text from your git history and release notes.
The human pastes the output; this skill never submits directly.

## Inputs

- **Platform** (required): `ios`, `android`, `garmin`, or `all`
- **Version** (optional): target release version; defaults to the latest tag
- **Locale** (optional, default: `en`): language code for the copy
- **App name** (optional): if not already in the repo's metadata
- **Positioning statement** (optional): one sentence describing what the app does and for whom

## Step 1: Gather context

### App metadata

```bash
# iOS: read from Info.plist or fastlane/metadata/
cat ios/*/Info.plist 2>/dev/null | grep -A1 "CFBundleDisplayName\|CFBundleShortVersionString" | head -10
ls fastlane/metadata/en-US/ 2>/dev/null

# Android: read from build.gradle or fastlane
cat android/app/build.gradle 2>/dev/null | grep "versionName\|applicationId" | head -5
ls fastlane/metadata/android/en-US/ 2>/dev/null

# Garmin: read from manifest.xml
cat manifest.xml 2>/dev/null | grep "entry\|version" | head -10
```

### Recent changelog

Read the relevant changelog. Prefer `CHANGELOG.md` or `CHANGELOG.json`. If unavailable, generate from git:

```bash
# Since last release tag
git log $(git describe --tags --abbrev=0)..HEAD --oneline --no-merges | head -40

# Or between two specific tags
git log <prev-tag>..<new-tag> --oneline --no-merges
```

Also read any existing store copy so you can evolve rather than replace:

```bash
# iOS fastlane
cat fastlane/metadata/en-US/description.txt 2>/dev/null
cat fastlane/metadata/en-US/release_notes.txt 2>/dev/null

# Android fastlane
cat fastlane/metadata/android/en-US/full_description.txt 2>/dev/null
cat fastlane/metadata/android/en-US/changelogs/<version-code>.txt 2>/dev/null

# Garmin: check references/store.md in the garmin-watchface skill for field locations
```

## Step 2: Classify changes

From the changelog/commits, classify each change:

| Category | Examples |
|----------|---------|
| **New features** | Added, New, Introduced, Support for |
| **Improvements** | Faster, Better, Improved, Now supports |
| **Bug fixes** | Fixed, Resolved, Corrected |
| **Breaking / major** | Removed, Deprecated, Requires, Now requires |

Keep only user-facing changes. Strip: refactor commits, test additions, CI changes,
dependency bumps, internal logging, code cleanup without behavior change.

## Step 3: Draft per platform

### iOS App Store

**Character limits** (hard limits, Apple rejects submissions that exceed them):

| Field | Limit |
|-------|-------|
| App Name | 30 chars |
| Subtitle | 30 chars |
| Promotional Text | 170 chars (not indexed) |
| Description | 4000 chars |
| What's New | 4000 chars |
| Keywords | 100 chars total (comma-separated, no spaces after commas) |

**What's New**: user-facing bullet list. Lead with the most impactful change:

```
• [Biggest new feature in one sentence]
• [Second improvement]
• Bug fixes and performance improvements
```

**Keywords**: pack the 100 chars with distinct terms not already in the title/subtitle:

```
productivity,focus,timer,tasks,habit,streak,pomodoro,deep work
```

Never repeat a word that is already in the App Name or Subtitle: Apple's index counts
them once, and repeating wastes keyword space.

**Description**: three-act structure:
1. Hook (1–2 sentences): what problem does this app solve, for whom?
2. Feature list (3–5 bullets): concrete benefits, not marketing claims.
3. Social proof / closing (1 sentence): rating, user count, or simple CTA.

### Google Play

**Character limits:**

| Field | Limit |
|-------|-------|
| Title | 30 chars |
| Short Description | 80 chars |
| Full Description | 4000 chars |
| Release Notes ("What's new") | 500 chars |

Google Play indexes the full description body: keyword placement matters.
Naturally include 3–5 relevant terms in the first 250 chars of the full description.

Release notes must fit 500 chars. Use plain language, no markdown (it renders literally).

### Garmin Connect IQ

**Character limits and validator rules** (from `garmin-watchface/references/store.md`):

| Field | Limit / Rule |
|-------|-------------|
| App name | 25 chars |
| Description | 4000 chars |
| What's New | 1500 chars |
| Screenshots | 1280×720 (landscape) or 480×480 for square; 7 max |

**Validator rejects**: `<` and `>` characters (use `->` or `→`), and U+2699 GEAR emoji
(write "tap the settings option" instead). The description validator only surfaces these
errors after the whole form is submitted: validate before pasting.

**Settings discoverability note** (required if the app has settings):

> Settings are configured in the Garmin Connect app: Device → Connect IQ Apps →
> Watch Faces → [App Name] → gear icon.

Include this in the description or it reads as a missing feature.

## Step 4: Validate before delivering

Run these checks on every generated field:

- [ ] All fields within character limits (count with `echo -n "..." | wc -c`)
- [ ] No `<`, `>` in Garmin copy
- [ ] No repeated keywords between Title, Subtitle, and Keywords (iOS)
- [ ] What's New / Release Notes written in past tense, not "we have added"
- [ ] No first-person pronouns (`I`, `we`, `our`) in App Store descriptions
- [ ] No competitor names or false comparative claims
- [ ] No mention of pricing, discounts, or "free" in App Store descriptions (Apple rejects this)
- [ ] Garmin descriptions use `→` not `->` for directional arrows (aesthetic; validator accepts both)

## Step 5: Deliver

Write output to a scratch file alongside the repo:

```
store-copy-v<version>-<YYYY-MM-DD>.md
```

Include for each platform:

1. Each field name, character count, and the drafted text
2. A copy-paste ready block for each field (no surrounding quotes)
3. Validation result (pass/fail per field)
4. One line per change category summarising what was included and what was cut

Do not commit or submit. The human pastes.

## Chaining

| Before this skill | After this skill |
|---|---|
| `olko:changelog-generator` | `olko:apple-store-submit` (for metadata update) |
| `olko:semantic-release-beta` (cut a release) | `garmin-watchface` store publishing workflow |
| Manual release notes | Paste into App Store Connect / Google Play Console / Connect IQ portal |
