@appeeky/spec 0.0.0-stage → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (111) hide show
  1. package/.github/assets/cover.gif +0 -0
  2. package/LICENSE +110 -0
  3. package/README.md +636 -2
  4. package/agents/appeeky-ads.md +50 -0
  5. package/agents/appeeky-aso.md +58 -0
  6. package/agents/appeeky-releases.md +47 -0
  7. package/agents/appeeky-reviews.md +53 -0
  8. package/agents/appeeky-social.md +54 -0
  9. package/agents/appeeky-ugc.md +56 -0
  10. package/agents/cofounder.md +36 -0
  11. package/dist/ads.d.ts +42 -0
  12. package/dist/ads.js +71 -0
  13. package/dist/agents.d.ts +47 -0
  14. package/dist/agents.js +88 -0
  15. package/dist/appeeky-api.d.ts +62 -0
  16. package/dist/appeeky-api.js +240 -0
  17. package/dist/attribution-setup.d.ts +28 -0
  18. package/dist/attribution-setup.js +65 -0
  19. package/dist/attribution.d.ts +121 -0
  20. package/dist/attribution.js +170 -0
  21. package/dist/bin.d.ts +2 -0
  22. package/dist/bin.js +4 -0
  23. package/dist/category.d.ts +71 -0
  24. package/dist/category.js +198 -0
  25. package/dist/cli.d.ts +3 -0
  26. package/dist/cli.js +1960 -0
  27. package/dist/competitors.d.ts +74 -0
  28. package/dist/competitors.js +143 -0
  29. package/dist/control.d.ts +21 -0
  30. package/dist/control.js +53 -0
  31. package/dist/core.d.ts +15 -0
  32. package/dist/core.js +15 -0
  33. package/dist/coverage.d.ts +46 -0
  34. package/dist/coverage.js +115 -0
  35. package/dist/data.d.ts +48 -0
  36. package/dist/data.js +298 -0
  37. package/dist/dataforseo.d.ts +139 -0
  38. package/dist/dataforseo.js +354 -0
  39. package/dist/diff.d.ts +11 -0
  40. package/dist/diff.js +33 -0
  41. package/dist/experiments.d.ts +115 -0
  42. package/dist/experiments.js +85 -0
  43. package/dist/files.d.ts +27 -0
  44. package/dist/files.js +129 -0
  45. package/dist/history.d.ts +43 -0
  46. package/dist/history.js +55 -0
  47. package/dist/index.d.ts +24 -0
  48. package/dist/index.js +24 -0
  49. package/dist/limits.d.ts +15 -0
  50. package/dist/limits.js +30 -0
  51. package/dist/lint.d.ts +16 -0
  52. package/dist/lint.js +159 -0
  53. package/dist/loops.d.ts +37 -0
  54. package/dist/loops.js +197 -0
  55. package/dist/measure.d.ts +40 -0
  56. package/dist/measure.js +159 -0
  57. package/dist/metrics.d.ts +290 -0
  58. package/dist/metrics.js +254 -0
  59. package/dist/output.d.ts +21 -0
  60. package/dist/output.js +79 -0
  61. package/dist/project.d.ts +19 -0
  62. package/dist/project.js +35 -0
  63. package/dist/report.d.ts +10 -0
  64. package/dist/report.js +113 -0
  65. package/dist/reviews.d.ts +130 -0
  66. package/dist/reviews.js +287 -0
  67. package/dist/schema.d.ts +612 -0
  68. package/dist/schema.js +253 -0
  69. package/dist/screenshots.d.ts +23 -0
  70. package/dist/screenshots.js +116 -0
  71. package/dist/search-ads.d.ts +137 -0
  72. package/dist/search-ads.js +354 -0
  73. package/dist/seo.d.ts +87 -0
  74. package/dist/seo.js +391 -0
  75. package/dist/setup.d.ts +45 -0
  76. package/dist/setup.js +259 -0
  77. package/dist/social.d.ts +129 -0
  78. package/dist/social.js +189 -0
  79. package/dist/storefronts.d.ts +15 -0
  80. package/dist/storefronts.js +89 -0
  81. package/dist/stores/app-store.d.ts +33 -0
  82. package/dist/stores/app-store.js +159 -0
  83. package/dist/stores/google-play.d.ts +13 -0
  84. package/dist/stores/google-play.js +61 -0
  85. package/dist/stores/types.d.ts +34 -0
  86. package/dist/stores/types.js +2 -0
  87. package/dist/suggest.d.ts +73 -0
  88. package/dist/suggest.js +135 -0
  89. package/dist/text.d.ts +18 -0
  90. package/dist/text.js +55 -0
  91. package/dist/transport.d.ts +99 -0
  92. package/dist/transport.js +170 -0
  93. package/dist/ugc-core.js +1 -0
  94. package/dist/ugc-local.d.ts +116 -0
  95. package/dist/ugc-local.js +174 -0
  96. package/dist/ugc.d.ts +149 -0
  97. package/dist/ugc.js +111 -0
  98. package/dist/verify.d.ts +70 -0
  99. package/dist/verify.js +134 -0
  100. package/dist/visibility-core.js +96 -0
  101. package/dist/visibility-local.d.ts +159 -0
  102. package/dist/visibility-local.js +246 -0
  103. package/dist/visibility.d.ts +152 -0
  104. package/dist/visibility.js +239 -0
  105. package/package.json +80 -4
  106. package/skills/appeeky-spec/SKILL.md +142 -0
  107. package/skills/appeeky-spec-experiments/SKILL.md +93 -0
  108. package/skills/appeeky-spec-localize/SKILL.md +52 -0
  109. package/skills/appeeky-spec-review/SKILL.md +75 -0
  110. package/skills/appeeky-spec-targets/SKILL.md +66 -0
  111. package/skills/appeeky-spec-visibility/SKILL.md +106 -0
package/README.md CHANGED
@@ -1,3 +1,637 @@
1
- # Temporary Holding Version
1
+ # Appeeky Spec
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ <img src="https://cdn.jsdelivr.net/npm/@appeeky/spec/.github/assets/cover.gif" alt="Animated pixel-art night sky over a hilltop: a puffin in a scarf hops between Grok, Claude Code, Codex and Cursor on the left and Gemini CLI, opencode, T3 Code and Zed on the right. Above them: App growth, run by your coding agent, the features with their logos and the places it runs, from GitHub Actions and Docker to Hetzner, DigitalOcean, AWS, Google Cloud, Railway, Fly.io and Render." width="100%" />
4
+
5
+ **App growth, run by your coding agent.**
6
+
7
+ Appeeky Spec puts your app's whole growth loop in one folder of your repo: the store listing and the searches it should win, reviews and competitors, what AI assistants say about the app, ads, experiments, and what every past change did. Your coding agent (Claude Code, Codex, Cursor, Gemini CLI and others) reads it and proposes the next change as a pull request. You merge it. The change is checked against the store rules, pushed to App Store Connect and Google Play, and measured on day 7 and day 14, and the result goes into a playbook the next change starts from.
8
+
9
+ [![npm](https://img.shields.io/npm/v/@appeeky/spec.svg)](https://www.npmjs.com/package/@appeeky/spec) [![CI](https://github.com/appeeky/appeeky-spec/actions/workflows/ci.yml/badge.svg)](https://github.com/appeeky/appeeky-spec/actions/workflows/ci.yml) [![License: FSL-1.1-MIT](https://img.shields.io/badge/license-FSL--1.1--MIT-blue.svg)](LICENSE)
10
+
11
+ > **Status: 0.x.** Commands and file formats can still change between minor versions. See the [changelog](CHANGELOG.md).
12
+
13
+ ## Quick start
14
+
15
+ ```bash
16
+ npx @appeeky/spec init # finds your app, pulls the listing, suggests targets, adds CI
17
+ npx skills add appeeky/appeeky-spec # teaches your coding agent the workflow
18
+ ```
19
+
20
+ Then ask your agent something like *"we're launching in Germany next week"* or *"why did we drop for white noise?"*. It works with the same commands you can run yourself:
21
+
22
+ ```bash
23
+ npx @appeeky/spec doctor # what is set up, what is missing, how to fix it
24
+ npx @appeeky/spec coverage # which searches each storefront is found for
25
+ npx @appeeky/spec check # store limits and ASO rules
26
+ npx @appeeky/spec diff # the spec against the live store
27
+ npx @appeeky/spec push --note "Lead with white noise in the US"
28
+ npx @appeeky/spec verify # day 7 and day 14: did it work?
29
+ ```
30
+
31
+ `pnpm dlx`, `yarn dlx` and `bunx` work the same way. Node 20 or later.
32
+
33
+ `init` with no options asks for the app's name, finds it on the App Store, checks the Google Play package, pulls every store it can reach, suggests five targets from what the app already ranks for, offers the GitHub workflows (`--ci`) and the agent skills, and ends on the first coverage table. With options (`--name`, `--app-store`, `--google-play`, `--pull`) it does the same without questions, for scripts. It also adds `.appeeky/` to `.gitignore`.
34
+
35
+ ## What it does
36
+
37
+ | Feature | What you get |
38
+ | --- | --- |
39
+ | **Listing as code** | Name, subtitle, keywords, descriptions and what's new for every locale, as YAML in your repo. Changes go through pull requests like code. |
40
+ | **Checked before it ships** | Store limits, wasted keyword space, words repeated between name, subtitle and keyword field, Google Play title policy and screenshot sizes. |
41
+ | **Keyword coverage** | Which of your target searches each storefront indexes, weighted by popularity, with words to add and the spare space in secondary locales. |
42
+ | **Pushed to both stores** | App Store Connect and Google Play, with your own keys or through your Appeeky connection. Every push writes a release file; `revert` puts the old values back. |
43
+ | **Measured** | Day 7 and day 14: search ranks, conversion, downloads, revenue and ads, against storefronts the release did not change. The verdict goes into `appeeky/playbook.md`. |
44
+ | **What users and rivals say** | Reviews with a summary and drafted replies, competitors' listings and moves, and the category chart rank, one row a day. |
45
+ | **AI visibility** | Whether ChatGPT, Claude, Gemini and Perplexity name your app, who they name instead and the pages they cite. |
46
+ | **Ads and data** | Apple Search Ads, Meta and TikTok spend and installs, App Store Connect, Google Play and RevenueCat numbers as CSV your agent can read. |
47
+ | **Specialists and a co-founder** | Six read-only specialist subagents (ASO, reviews, ads, releases, UGC, social) and a co-founder review that starts them, writes a brief to `appeeky/briefs/` and opens a pull request with one listing change. Run it as `/appeeky-cofounder`, or once a week on your Claude plan. |
48
+
49
+ ## How a change goes live
50
+
51
+ ```mermaid
52
+ flowchart LR
53
+ A["Your agent edits<br/>appeeky/store/"] --> B["Pull request<br/>check + coverage comment"]
54
+ B --> C{"You merge"}
55
+ C --> D["push to App Store Connect<br/>and Google Play"]
56
+ D --> E["verify on day 7 and 14<br/>with a control group"]
57
+ E --> F["playbook.md<br/>what worked"]
58
+ F --> A
59
+ ```
60
+
61
+ Nothing reaches the stores until a person merges. The merge is the approval.
62
+
63
+ ## Where it runs
64
+
65
+ | Where | How |
66
+ | --- | --- |
67
+ | **GitHub Actions** | `init --ci` writes three workflows: a check on every pull request, a push on merge, and a daily run that measures and commits. See [In pull requests](#in-pull-requests). |
68
+ | **Your own server** | `docker compose up -d` on any VM: Hetzner, DigitalOcean, AWS, Google Cloud, Railway, Fly.io, Render or a machine in your office. See [Running without GitHub Actions](#running-without-github-actions). |
69
+ | **Your computer** | Every command runs locally with `npx`. `examples/weekly/` has a cron script for the weekly co-founder review. |
70
+ | **Other CI** | GitLab, Bitbucket or CircleCI: the same three triggers and commands. |
71
+
72
+ Powered by [Appeeky](https://appeeky.com). Works with your own store keys on your machine, or with your Appeeky connection when you'd rather not handle keys.
73
+
74
+ ## Layout
75
+
76
+ ```
77
+ appeeky.spec.yaml # app, stores, target keywords per storefront
78
+ appeeky/
79
+ store/
80
+ app-store/en-US.yaml # name, subtitle, keywords, promotionalText, description, whatsNew, URLs
81
+ google-play/en-US.yaml # title, shortDescription, fullDescription, video
82
+ releases/ # one file per push: what changed, where, why
83
+ reviews/ # customer reviews (JSONL per month), summary.md, replies/ drafted for review
84
+ competitors/ # one file per competitor: listing, ranks, recent reviews
85
+ visibility/<country>.yaml # what ChatGPT, Claude, Gemini and Perplexity answer: named or not, who instead, cited pages
86
+ seo/ # Google Search Console for the website: clicks per day, queries, pages, summary.md
87
+ search-ads/ # Apple Search Ads on your own key: spend and installs per day, keywords, search terms
88
+ category.csv # rank in the category chart, one row a day
89
+ playbook.md # what worked and what did not for this app
90
+ .appeeky/data/ # analytics, ads and revenue as CSV (gitignored; data.commit moves it to appeeky/data/)
91
+ ```
92
+
93
+ `appeeky.spec.yaml`:
94
+
95
+ ```yaml
96
+ version: 1
97
+ app:
98
+ name: Sleep Sounds
99
+ primaryLocale: en-US
100
+ stores:
101
+ appStore:
102
+ appId: "1234567890"
103
+ via: auto # auto | local | hosted
104
+ googlePlay:
105
+ packageName: com.example.sleep
106
+ via: auto
107
+ targets: # the searches this app should be found for, per storefront
108
+ us: [sleep sounds, white noise, rain sounds, brown noise]
109
+ tr: [uyku sesleri, beyaz gürültü]
110
+ storefronts: # optional: which App Store listing locales a storefront indexes
111
+ us: [en-US, es-MX]
112
+ ```
113
+
114
+ A field you leave out of a listing file is not managed by the spec: it is never diffed or pushed. You can adopt the spec one field or one locale at a time.
115
+
116
+ ## Commands
117
+
118
+ | Command | What it does |
119
+ | --- | --- |
120
+ | `init` | Create `appeeky.spec.yaml` and `appeeky/`. `--pull` fetches the live listings right away. |
121
+ | `pull` | Write the live listings into `appeeky/store/` (overwrites local files). |
122
+ | `check` | Store limits, ASO rules and screenshot sizes. Exits 1 on errors (`--strict`: on warnings too). |
123
+ | `coverage` | Which target keywords each storefront indexes, plus tips for storefronts that read a second locale (the US also reads Spanish (Mexico)). |
124
+ | `suggest` | Words to add to a keyword field: missing target words first, then Appeeky keyword opportunities. `--apply <words…>` adds them. |
125
+ | `diff` | Field-level differences between the spec and the live store, and what cannot be pushed yet. |
126
+ | `push` | Check, diff, confirm, record search ranks, push, and write `appeeky/releases/<id>.yaml`. `--note` stores why, `--create-version 2.5.0` opens the next App Store version first. |
127
+ | `verify` | On day 7 and day 14 after a push, compare ranks with the ones recorded before it and write the verdict into the release. With `APPEEKY_API_KEY`, also conversion, downloads, revenue and ads (Apple Search Ads, Meta, TikTok). `--playbook` adds a line to `appeeky/playbook.md`. |
128
+ | `ads` | Spend, installs and cost per install of your paid channels over the last `--days` (default 30): Apple Search Ads, plus the Meta and TikTok campaigns under `ads:`. `--save` writes them to `appeeky/ads/<date>.yaml`. Needs `APPEEKY_API_KEY`. |
129
+ | `reviews pull\|list\|reply\|push` | Customer reviews from both stores into `appeeky/reviews/`, with `summary.md`. `reply <id> --text` drafts an answer in `appeeky/reviews/replies/` for a pull request; `push` sends the drafts. |
130
+ | `competitors discover\|pull` | `discover` lists apps like yours (`--add <n>` puts them in the spec). `pull` writes each competitor's listing, the searches it ranks for and its recent reviews to `appeeky/competitors/`. |
131
+ | `visibility init\|sync\|pull\|scan` | AI visibility: whether ChatGPT, Claude, Gemini and Perplexity name your app. `init` writes the intents and prompts Appeeky tracks into the spec, `sync` sends your edits back, `pull` writes the answers to `appeeky/visibility/`, `scan` asks the assistants now. Needs `APPEEKY_API_KEY`, or with `init --local` / `scan --local` your own OpenAI, Anthropic, Gemini or Perplexity key. |
132
+ | `search-ads pull` | Apple Search Ads on your own API key: this app's spend, installs, keywords and search terms in `appeeky/search-ads/`, with the search terms that installed and are not targets yet. `ads` and `verify` read it. |
133
+ | `seo check\|llms\|pull\|ranks\|keywords\|domain` | The app's website in Google and in AI answers. `check` lists what is missing for search, for the app and for AI crawlers; `llms` writes llms.txt from the listing; `pull` writes Search Console clicks, queries and pages to `appeeky/seo/` (`GSC_CREDENTIALS`). On your own DataForSEO account: `ranks` (Google positions of the site and the store page, and AI Overview citations), `keywords` (volume, difficulty, ideas) and `domain` (what the site ranks for, competing sites). |
134
+ | `category pull` | Record today's rank in the category chart (top 100) in `appeeky/category.csv`. App Store needs no key; Google Play needs `APPEEKY_API_KEY` and `category.googlePlay`. |
135
+ | `data pull` | App Store Connect, Google Play, Apple Search Ads, RevenueCat, Meta and TikTok numbers per day, and attribution by source, keyword and smart link, as CSV files with an index (`--days`, default 90). |
136
+ | `status` | Every change being measured, in one list: releases and experiments, the metric that decides each, the next check and the result so far. Flags checks that are late, releases measured over the same days, and results built on too few installs. Rewrites `appeeky/loops.md`. |
137
+ | `log` | Releases pushed from this spec. `--markdown` for changelogs and PR descriptions. |
138
+ | `revert <release>` | Put the values from before a release back into the spec. Then `push`. |
139
+ | `experiment add\|list\|close` | Track growth experiments outside the listing (screenshots, UGC, ads, pricing) with one metric and a check date. |
140
+ | `screenshots pull\|check` | Download the live App Store screenshots into `appeeky/screenshots/` and check their sizes. |
141
+ | `report` | Markdown for a pull request: listing changes, check results and coverage against `--base`. |
142
+ | `social pull\|check\|push` | Organic social media through the accounts connected in Appeeky: `pull` writes the accounts, recent posts and how they did to `appeeky/social/`; `check` checks the drafts in `appeeky/social/drafts/`; `push` sends them. Needs `APPEEKY_API_KEY`. |
143
+ | `ugc pull\|generate` | UGC from your Appeeky creator personas: `pull` writes the personas and their clips to `appeeky/ugc/`; `generate` makes a clip from each brief in `appeeky/ugc/briefs/` (`--dry-run` shows the cost in media credits). Needs `APPEEKY_API_KEY`. |
144
+ | `agents install` | Write the specialist subagents and the `/appeeky-cofounder` review into `.claude/` (`--for opencode` or `all` for `.opencode/`). Files you changed are kept unless `--force`. |
145
+
146
+ Global options: `--json` for machine-readable output, `--cwd <dir>`, `--via auto|local|hosted`.
147
+
148
+ ## Connecting to the stores
149
+
150
+ Appeeky Spec talks to the stores through the Appeeky store CLIs, which expose the same operations locally and on Appeeky's hosted gateways:
151
+
152
+ | `via` | How | Needs |
153
+ | --- | --- | --- |
154
+ | `local` | Runs [`appstore`](https://github.com/appeeky/app-store-connect-cli) and [`gps`](https://github.com/appeeky/google-play-console-cli) on your machine with your own keys. Falls back to `npx` when they are not installed. | An App Store Connect API key (`appstore accounts add`) or a Google Play service account. |
155
+ | `hosted` | Calls `asc-api.appeeky.com` and `gps-api.appeeky.com`. Your store keys stay in Appeeky. | `APPEEKY_API_KEY` and a connected store in Appeeky. |
156
+ | `auto` | Local when the CLI is installed, hosted when `APPEEKY_API_KEY` is set, `npx` otherwise. | |
157
+
158
+ **Your own keys, saved once.** Both CLIs keep named accounts in your user config (`~/.config/appstore/`, `~/.config/gps/`), never in the repo. `init` offers to save them for you; by hand:
159
+
160
+ ```bash
161
+ npx -p @appeeky/app-store-connect-cli appstore accounts add my-team \
162
+ --key-id ABC123DEFG --issuer-id <issuer-id> --private-key-path ~/AuthKey_ABC123DEFG.p8 --default
163
+ npx -p @appeeky/google-play-store-cli gps accounts add my-co \
164
+ --credentials ~/play-service-account.json --default-package com.example.sleep --default
165
+ ```
166
+
167
+ With several teams or clients, pick the account per app in the spec: `stores.appStore.account` and `stores.googlePlay.account`. In CI, pass the same keys as environment variables instead (`ASC_KEY_ID`, `ASC_ISSUER_ID`, `ASC_PRIVATE_KEY`, `GOOGLE_APPLICATION_CREDENTIALS`). The CLIs also work on their own, and as MCP servers for your agent (`appstore-mcp`, `gps-mcp`).
168
+
169
+ ### What can change when
170
+
171
+ - **App Store:** name, subtitle, keywords, description, what's new and URLs change only with a new version. Create the version in App Store Connect (or `appstore versions create 2.5.0 --confirm`), then push. Promotional text changes on the live version at any time. `diff` marks changes that are blocked and says why.
172
+ - **Google Play:** listing changes go live without an app update. A new language needs a title, short description and full description.
173
+
174
+ ## Measuring a release
175
+
176
+ `push` records the search rank of every target right before it changes anything. `verify` checks them again on day 7 and day 14:
177
+
178
+ - **won**: more targets rose than fell
179
+ - **lost**: one top-10 target or two top-20 targets fell out of the top 20 by 10+ places (consider `revert`)
180
+ - **mixed** or **flat** otherwise
181
+
182
+ **A control group.** Storefronts whose listing the release did not change are measured over the same days. A push to en-US changes the US store; the German store reads de-DE and did not change, but its ranks still move with the store's algorithm, the season and competitors. When the changed storefronts gained less than 2 places per target beyond the unchanged ones, a "won" becomes **mixed**: the store moved, not the listing. The check records both moves and the net (`control`).
183
+
184
+ **Normal moves.** Before comparing conversion, installs or revenue, `verify` reads the four windows before the push and learns how much each metric moves on its own. A change inside that range is **flat**, and the output says so: `downloads/day 100 → 120 (+20%) (moves ±41% on its own)`.
185
+
186
+ When no target moved (or the release has no targets), the outcomes decide instead: organic installs and their revenue with `attribution:`, otherwise store conversion, otherwise downloads, otherwise the category rank from `appeeky/category.csv`. Better is **won**, worse is **lost**, and the check says so (`decidedBy`).
187
+
188
+ Ranks come from the Appeeky API when `APPEEKY_API_KEY` is set (both stores), or from Apple's public search for the App Store. Run `verify --playbook` on a schedule (CI or cron) and the playbook fills itself.
189
+
190
+ With `APPEEKY_API_KEY`, `verify` also compares the days before and after the push for the data you connected in Appeeky: conversion, downloads and proceeds from App Store Connect or Google Play, revenue and trials from RevenueCat, installs and cost per install from Apple Search Ads, spend, installs and cost per install of the Meta and TikTok campaigns listed under `ads:` (see below), and how often AI assistants name the app (`ai_visibility`, see below). Ranks tell you whether more people find the app; these tell you whether more of them install and pay. Experiments on these metrics (`experiment add --metric trial_conversion`) measure their own baseline and result.
191
+
192
+ ## Paid channels
193
+
194
+ Apple Search Ads is matched to the app in Appeeky, so it needs nothing in the spec. Meta and TikTok ad accounts usually promote more than one app, so list the campaigns that belong to this one:
195
+
196
+ ```yaml
197
+ ads:
198
+ meta:
199
+ adAccount: act_1234567890 # optional: defaults to the account picked in Appeeky
200
+ campaigns: ["120210000000001"]
201
+ tiktok:
202
+ advertiser: "7000000000000" # optional: defaults to the advertiser picked in Appeeky
203
+ campaigns: ["1790000000000"]
204
+ ```
205
+
206
+ Connect Meta and TikTok in Appeeky first. Then:
207
+
208
+ - `appeeky-spec ads` prints spend, installs (conversions for TikTok) and cost per result per channel. `--save` keeps a copy in `appeeky/ads/` so the repo has the history next to the listing.
209
+ - `verify` compares those numbers before and after every push, so a listing change and an ad change are judged on the same days.
210
+ - Experiments can use `meta_cpi`, `meta_installs`, `tiktok_cpa`, `tiktok_conversions`, `cpa` or `ad_installs` as their metric: `experiment add --type ads --metric meta_cpi` takes its baseline from the last days and `verify` closes it.
211
+
212
+ ## Reviews, competitors and data
213
+
214
+ Everything an agent needs to decide the next change, as plain files next to the listing. All three need `APPEEKY_API_KEY` and the stores connected in Appeeky.
215
+
216
+ **Reviews.** `reviews pull` writes every review to `appeeky/reviews/<store>/<YYYY-MM>.jsonl`, one JSON object per line (id, rating, title, body, territory or language, version, date, reply). Pulling again adds new reviews and replaces edited or answered ones. `summary.md` has the last 30 days against the 30 before, star counts, ratings per version, the low ratings still waiting for a reply, and the words users use that your listing does not: phrases that come up in three or more reviews and appear nowhere in any listing, with how many came from 1–3 star reviews. They are candidates for targets, or for the product. Replies go through review like the listing:
217
+
218
+ ```bash
219
+ appeeky-spec reviews list --rating 1-2 --unanswered
220
+ appeeky-spec reviews reply 1234567 --text "Sorry about that. 3.1 fixes the crash on launch."
221
+ git checkout -b reviews/replies && git add appeeky/reviews && git commit -m "Reply to crash reports"
222
+ # after the pull request is approved
223
+ appeeky-spec reviews push
224
+ ```
225
+
226
+ `verify` also compares the average rating and the share of 1–2 star reviews before and after each push, from the pulled files.
227
+
228
+ **Competitors.** List them in `appeeky.spec.yaml`, or find them with `competitors discover --add 3`:
229
+
230
+ ```yaml
231
+ competitors:
232
+ - name: Calm Night
233
+ appStore: "1234567891"
234
+ googlePlay: com.calmnight.app
235
+ ```
236
+
237
+ `competitors pull` writes `appeeky/competitors/calm-night.yaml`: title, rating, version and release notes, the first part of the description, the 25 searches it ranks best for in your first targets country, where it ranks for your own targets, and its recent low ratings. Each pull compares with the last one and adds what changed to `appeeky/competitors/changes.md`: a new title, a new version and its notes, a rewritten description, a climb into the top 10 for one of your targets. `status` warns a release being measured when a competitor changed something inside its window.
238
+
239
+ **A weekly co-founder review.** Once a week the specialist subagents read the repo, and an agent writes a brief and proposes one listing change as a pull request, for a store with no release still being measured: `status --json` lists those stores as `free`, so a new change never mixes with an open one. See [Specialists and the co-founder review](#specialists-and-the-co-founder-review). [`examples/weekly/`](examples/weekly) has a GitHub Actions workflow and the same thing as a script for cron on a machine of your own. Nothing reaches the stores until someone merges it.
240
+
241
+ **Data.** `data pull` writes CSV files per source, merged by date on every pull, with a `README.md` index:
242
+
243
+ | File | From |
244
+ | --- | --- |
245
+ | `app-store-analytics.csv`, `google-play-analytics.csv` | impressions, page views, downloads, conversion, proceeds, trials, subscriptions |
246
+ | `app-store-traffic-sources.csv` | where product page views came from |
247
+ | `app-store-sales.csv`, `app-store-countries.csv` | App Store Connect sales reports, by day and by country |
248
+ | `apple-search-ads.csv` | spend, impressions, taps, installs |
249
+ | `revenuecat.csv` | revenue, MRR, trials, new customers, trial conversion, churn, actives |
250
+ | `meta-ads.csv`, `tiktok-ads.csv` | the campaigns under `ads:`, per day |
251
+
252
+ They are your private business numbers, so they stay in `.appeeky/data/` (gitignored). Set `data: { commit: true }` to write them to `appeeky/data/` and version them with the spec, for example in a private repo.
253
+
254
+ No CI is needed: an agent runs these commands when it needs fresh numbers. To keep the files current on their own, run them on a schedule:
255
+
256
+ ```yaml
257
+ # .github/workflows/appeeky-spec-daily.yml
258
+ on:
259
+ schedule: [{ cron: "0 6 * * *" }]
260
+ jobs:
261
+ refresh:
262
+ runs-on: ubuntu-latest
263
+ permissions: { contents: write }
264
+ steps:
265
+ - uses: actions/checkout@v4
266
+ - run: npx @appeeky/spec category pull && npx @appeeky/spec visibility pull && npx @appeeky/spec reviews pull && npx @appeeky/spec competitors pull && npx @appeeky/spec verify --playbook
267
+ env: { APPEEKY_API_KEY: "${{ secrets.APPEEKY_API_KEY }}" }
268
+ - run: git add appeeky && git commit -m "Daily category, AI visibility, reviews, competitors and verify" && git push || true
269
+ ```
270
+
271
+ ## Social media and UGC
272
+
273
+ With the social accounts and creator personas you set up in Appeeky, posts and videos go through the repo like listing changes: drafted, reviewed in a pull request, sent after the merge.
274
+
275
+ ```
276
+ appeeky/social/accounts.yaml connected accounts (platform, name, id)
277
+ appeeky/social/posts.jsonl recent posts and each platform's result: views, likes, comments, shares
278
+ appeeky/social/summary.md the last 30 days per platform, and what did best
279
+ appeeky/social/drafts/<name>.yaml a post to send: accounts, text, when, media, why
280
+ appeeky/ugc/personas.yaml creator personas, and whether each can make clips yet
281
+ appeeky/ugc/clips.jsonl the clips made with them
282
+ appeeky/ugc/briefs/<name>.yaml a video to make: persona, script, scene, caption, why
283
+ appeeky/ugc/personas/<name>.yaml optional: a persona kept in the repo, with its look images, for --local
284
+ ```
285
+
286
+ ```yaml
287
+ # appeeky/social/drafts/2026-11-10-fan-sounds.yaml
288
+ accounts: [twitter, instagram] # platforms from accounts.yaml, or account ids
289
+ text: "Fan sounds are here. Pick a fan, set a timer, sleep."
290
+ when: "2026-11-12T16:00:00Z" # without it, the post goes out when it is sent
291
+ media: ["https://…/fan.mp4"] # Instagram, TikTok and Pinterest need one
292
+ why: "Users asked for fan sounds in 9 reviews; it shipped in 3.2."
293
+ ```
294
+
295
+ `social check` flags drafts that would fail: a platform with no connected account, text over the platform's limit, a missing image, a time in the past. `ugc generate --dry-run` shows what each brief costs in media credits without making anything. The push workflow written by `init --ci` (and the docker `push` routine) runs `social push --yes` and `ugc generate --yes` when a merged pull request adds drafts or briefs; each is marked `sent` or `generating` and committed, so it never goes out twice. Social media needs the Startup plan or above in Appeeky; a persona needs a look (reference images) before it can make clips.
296
+
297
+ ### Clips on your own fal key
298
+
299
+ `ugc generate --local` makes the clips where Spec runs, with your own [fal](https://fal.ai) key, instead of in Appeeky with media credits. It is the same pipeline as Appeeky UGC (a still of the persona in the scene, then a talking video with the script in their voice) and fal bills your account directly.
300
+
301
+ ```bash
302
+ export FAL_KEY=… # fal.ai → API keys
303
+ appeeky-spec ugc generate --local --dry-run # what each brief costs on fal, in USD
304
+ appeeky-spec ugc generate --local # asks first, then renders
305
+ ```
306
+
307
+ The persona is either one from Appeeky (its cover image is the look, needs `APPEEKY_API_KEY`) or one kept in the repo:
308
+
309
+ ```yaml
310
+ # appeeky/ugc/personas/mia.yaml
311
+ name: Mia
312
+ appearance: early 20s, dyed pink bob, oversized hoodie
313
+ personality: playful, talks fast
314
+ lane: dance trends and sound edits
315
+ looks: [mia/front.png, mia/side.png] # images next to this file, or URLs
316
+ ```
317
+
318
+ The clip is saved to `.appeeky/media/<brief>.mp4` (gitignored) and the brief records `clipFile`, fal's `videoUrl` (use it in a social draft's `media:`) and `costUsd`. With `FAL_KEY` set as a secret, the push workflow and the docker `push` routine render locally too.
319
+
320
+ ## AI visibility
321
+
322
+ People ask ChatGPT, Claude, Gemini and Perplexity for apps, not only the store. Appeeky AI Visibility asks them on a schedule, per storefront, and records whether they name your app. The questions live in the spec: an **intent** is what someone wants, its **prompts** are how they ask.
323
+
324
+ ```yaml
325
+ aiVisibility:
326
+ countries: [us] # default: the first storefront under targets
327
+ intents:
328
+ us:
329
+ - label: fall asleep to rain sounds
330
+ prompts:
331
+ - what app plays rain sounds to help me sleep
332
+ - best free rain sounds app for iPhone
333
+ ```
334
+
335
+ - `visibility init` writes the intents Appeeky already tracks into the spec. With none yet, Appeeky writes them from your listing; run `init` again a few minutes later.
336
+ - `visibility sync` sends the spec's intents and prompts to Appeeky. Ones only in Appeeky are listed, and archived only with `--prune`. `--dry-run` shows the plan.
337
+ - `visibility pull` writes `appeeky/visibility/us.yaml`. It shows each intent as `winning`, `weak` or `missing`, the share of answers that name you per assistant, who they name instead, the pages they cite (`yours: true` for your own site), and the fixes Appeeky suggests. What moved since the last pull goes to `appeeky/visibility/changes.md`.
338
+ - `visibility scan` asks the assistants now instead of waiting for the schedule. It uses AI Visibility credits.
339
+
340
+ `verify` reads the daily series as `ai_visibility`, the share of answers that name the app, before and after a push. A description that says what the app is for in the words people use can move it, and so can the pages the assistants cite.
341
+
342
+ ### On your own keys
343
+
344
+ The same scan runs where Spec runs, on your own model accounts, with no Appeeky account. Set any of `OPENAI_API_KEY` (ChatGPT), `ANTHROPIC_API_KEY` (Claude), `GEMINI_API_KEY` (Gemini) and `PERPLEXITY_API_KEY` (Perplexity); each key adds that assistant.
345
+
346
+ ```bash
347
+ npx @appeeky/spec visibility init --local # intents and prompts from your listing, written into the spec
348
+ npx @appeeky/spec visibility scan --local --dry-run
349
+ # US 6 intents · 24 prompts × chatgpt, claude, gemini, perplexity · about $2.45
350
+ npx @appeeky/spec visibility scan --local
351
+ ```
352
+
353
+ Each prompt goes to each assistant with web search on, the way the apps answer people, and a second cheap call lists the apps each answer names. The result is the same `appeeky/visibility/<country>.yaml`, plus `history.csv` (one row per scan, intent and assistant, read by `verify`) and `answers/<country>.jsonl` with every answer. Add `aliases:` under `aiVisibility` for other names the assistants use for the app.
354
+
355
+ On a schedule: set `VISIBILITY_EVERY` (days, e.g. `7`) as a repository variable or in the docker `.env`, with the keys as secrets, and the daily routine scans when it is due.
356
+
357
+ ## Apple Search Ads on your own key
358
+
359
+ Without an Appeeky account, read Search Ads straight from Apple. In Apple Search Ads → Account Settings → API, invite an API user with a read role and upload its public key. Then save the key on the same App Store Connect CLI account that holds your App Store Connect key, so both live in one place and the private key never leaves the CLI:
360
+
361
+ ```bash
362
+ appstore accounts search-ads my-team --client-id SEARCHADS.… --team-id SEARCHADS.… --key-id … --private-key-path ./searchads-private-key.pem
363
+ npx @appeeky/spec search-ads pull
364
+ # ✓ appeeky/search-ads/ US Exact, Discovery
365
+ # 30 days: 412.80 EUR · 236 installs · 1.75 EUR each · 980 taps
366
+ ```
367
+
368
+ ```yaml
369
+ stores:
370
+ appStore:
371
+ appId: "1234567890"
372
+ searchAds:
373
+ orgId: "1234567" # only when the key reaches more than one organization
374
+ campaigns: ["987654"] # optional: only these campaigns
375
+ ```
376
+
377
+ The spec uses `stores.appStore.account` (or the CLI's default account). In CI, where there is no CLI account, set `ASA_CLIENT_ID`, `ASA_TEAM_ID`, `ASA_KEY_ID` and `ASA_PRIVATE_KEY` (the PEM or its path) instead; they win over the account.
378
+
379
+ Only the campaigns for `stores.appStore.appId` are read. It writes `daily.csv` (spend, installs, taps and impressions per day, kept across pulls), `keywords.csv`, `search-terms.csv` and `summary.md`. The summary lists the search terms that brought installs and are not targets yet, which you can rank for organically for free, and the keywords that spent and brought nothing. `ads` and `verify` read `ad_installs` and `cpa` from it, so no Appeeky connection is needed. The daily routines pull it when `ASA_CLIENT_ID` is set.
380
+
381
+ ## Website: SEO and AI readiness
382
+
383
+ People also find an app through its website, in Google and in the pages AI assistants cite. Point the spec at it:
384
+
385
+ ```yaml
386
+ web:
387
+ url: https://sleepsounds.app/
388
+ searchConsole: sc-domain:sleepsounds.app # optional, this is the default
389
+ ```
390
+
391
+ - `seo check` reads the site the way a crawler does, with no key. **Search**: HTTPS, title, description, canonical, language, one h1, Open Graph, noindex, robots.txt and the sitemap. **The app**: store links, the Smart App Banner, `MobileApplication` structured data, the app's name in the text. **AI assistants**: whether robots.txt lets GPTBot, OAI-SearchBot, ChatGPT-User, ClaudeBot, Claude-SearchBot, PerplexityBot, Google-Extended and Applebot-Extended in, llms.txt, how many words are readable without JavaScript, the target keywords on the page and an FAQ. Each finding comes with the fix; it exits 1 when something fails.
392
+ - `seo llms --out public/llms.txt` writes llms.txt from the listing: what the app is, where to get it, what it is good for and the description.
393
+ - `seo pull` reads Google Search Console with your own service account. Create one in Google Cloud, enable the Search Console API, add its email as a user of the property, and set `GSC_CREDENTIALS` to the JSON key (or its path). On your own machine, a `gcloud auth application-default login` with the `webmasters.readonly` scope works too. It writes `appeeky/seo/daily.csv` (clicks and impressions per day, kept across pulls), `queries.csv`, `pages.csv` and `summary.md` with the trend, the top queries, the queries close to the top (position 4–15) and the ones with your store targets.
394
+
395
+ `verify` reads `search_clicks` and `search_impressions` from `daily.csv`, so a listing change that also changes the website's copy is measured in Google too. The daily workflow runs `seo pull` when the `GSC_CREDENTIALS` secret is set.
396
+
397
+ ### Google ranks and keyword data on DataForSEO
398
+
399
+ With your own [DataForSEO](https://dataforseo.com) account (`DATAFORSEO_LOGIN`, `DATAFORSEO_PASSWORD` from API access), the spec reads Google itself. The keywords are `web.keywords.<country>`, or the store targets of the first storefront:
400
+
401
+ ```yaml
402
+ web:
403
+ url: https://sleepsounds.app/
404
+ keywords:
405
+ us: [rain sounds, white noise app, sleep sounds for baby]
406
+ device: mobile # default; desktop reads the desktop results page
407
+ ```
408
+
409
+ - `seo ranks` reads the results page for each keyword (about $0.02 each). It records where the site ranks, where the app's App Store or Google Play page ranks on Google, and whether Google's AI Overview shows and cites the site, to `appeeky/seo/ranks.csv` (one row per keyword per check) and `ranks.md`. Keywords that enter or leave the top 10, and AI Overview citations gained or lost, go to `appeeky/seo/changes.md`.
410
+ - `seo keywords` writes search volume, difficulty, CPC and intent for the keywords, plus related ideas, to `appeeky/seo/keywords.csv`.
411
+ - `seo domain` writes what the site ranks for, its estimated visits and the sites that rank for the same searches to `appeeky/seo/domain.md` and `ranked-keywords.csv`.
412
+
413
+ Each one quotes with `--dry-run` and asks before spending (`--yes` skips the question). `verify` reads `web_rank` (the site's average position, 101 when not ranked) and `ai_overview_citations` from `ranks.csv`. The daily routines run `seo ranks --every 7` when `DATAFORSEO_LOGIN` is set (`SEO_RANKS_EVERY` changes the days); the weekly agent cannot run the paid ones.
414
+
415
+ ## Category rank
416
+
417
+ `category pull` records the app's rank in its category chart for each storefront under `targets`, one row a day in `appeeky/category.csv`. The rank is public data, so the file is committed with the spec. On the App Store the genre and chart come from the app (a paid app is read from top paid), straight from Apple's charts and with no key. On Google Play, set the category:
418
+
419
+ ```yaml
420
+ category:
421
+ googlePlay: HEALTH_AND_FITNESS # appStore: "6013" to override the genre
422
+ chart: top-free # top-free | top-paid | top-grossing
423
+ countries: [us, gb] # default: the storefronts under targets
424
+ ```
425
+
426
+ Charts list the top 100; below that is an empty rank. `verify` reads the file as `category_rank` (lower is better). When no target moved and there is no analytics data, the category rank decides the release. Run it daily (see above) so there is a history before each push.
427
+
428
+ ## Attribution
429
+
430
+ With [Appeeky Analytics](https://appeeky.com/attribution) in the app, the spec knows where each install came from (organic store search, an Apple Search Ads keyword, a smart link, the Google Play referrer) and what it paid. Run the server yourself on Cloudflare, or use Appeeky's:
431
+
432
+ ```yaml
433
+ attribution:
434
+ via: self # your own server: set ATTRIBUTION_TOKEN to its ADMIN_TOKEN
435
+ url: https://analytics.sleepsounds.workers.dev
436
+ app: sleep-sounds # the app's id in Appeeky Analytics
437
+ # or
438
+ attribution:
439
+ via: hosted # Appeeky's service, with APPEEKY_API_KEY
440
+ app: "1234567890"
441
+ ```
442
+
443
+ `attribution init` sets this up: hosted, it turns attribution on for each store's app in Appeeky; with `--self <url>`, it finds or creates the apps on your server (with `ATTRIBUTION_TOKEN`). It writes `attribution:` and prints the SDK key and the RevenueCat webhook to add.
444
+
445
+ An app in Appeeky Analytics is one platform. For an app on both stores, give one id per store; each release is measured with its own store's app, and `data pull` writes one set of files per store:
446
+
447
+ ```yaml
448
+ attribution:
449
+ via: self
450
+ url: https://analytics.sleepsounds.workers.dev
451
+ app:
452
+ app-store: app_ios123
453
+ google-play: app_and456
454
+ ```
455
+
456
+ - `verify` measures what a listing change is for: **organic installs per day, the revenue they brought and the share that paid**, before and after each push. The server counts installs first seen in a window, so revenue covers what those installs have paid so far; older installs have had longer to pay, which makes a rise after a push a conservative reading.
457
+ - `data pull` adds `attribution-sources.csv` (installs, trials, payers and revenue by source and campaign), `attribution-keywords.csv` (Search Ads keyword ids with the revenue they led to; Apple reports ids, the words are in your Search Ads account), `attribution-campaigns.csv`, `attribution-countries.csv` and `smart-links.csv`. It works with attribution alone, without an Appeeky key.
458
+ - `experiment add --link [channel]` creates a smart link for the experiment: one URL that sends each visitor to the right store, with its installs and revenue counted to it. Use `--metric link_installs`, `link_revenue` or `link_clicks` and `experiment close` fills the value in. On iOS the App Store passes nothing from a click to a new install, so link installs are, in practice, Google Play installs (the SDK reads the link only on the app's first launch); for an App Store only app, measure `link_clicks` (or `organic_installs`).
459
+
460
+ ```bash
461
+ appeeky-spec experiment add --type ugc --link tiktok --hypothesis "Before/after hooks get installs" --metric link_installs --target 50
462
+ # Started 2026-11-04-before-after-hooks-get-installs
463
+ # link https://analytics.sleepsounds.workers.dev/l/ab12xy
464
+ ```
465
+
466
+ ## Experiments and personas
467
+
468
+ Listing pushes are experiments already. Everything else goes in `appeeky/experiments/`:
469
+
470
+ ```bash
471
+ appeeky-spec experiment add --type ugc --persona sleepless-parent \
472
+ --hypothesis "Before/after hooks get more installs" --metric install_per_1k_views --baseline 1.8 --target 2.5
473
+ appeeky-spec experiment close 2026-11-01-before-after --value 2.7 --playbook
474
+ ```
475
+
476
+ Personas live in `appeeky.spec.yaml` so screenshots, UGC and ads speak to the same people:
477
+
478
+ ```yaml
479
+ personas:
480
+ - id: sleepless-parent
481
+ name: Sleepless parent
482
+ searches: [baby sleep sounds, white noise for baby]
483
+ ```
484
+
485
+ ## In pull requests
486
+
487
+ ```yaml
488
+ # .github/workflows/appeeky-spec.yml
489
+ on: pull_request
490
+ permissions:
491
+ contents: read
492
+ pull-requests: write
493
+ jobs:
494
+ spec:
495
+ runs-on: ubuntu-latest
496
+ steps:
497
+ - uses: actions/checkout@v4
498
+ with:
499
+ fetch-depth: 0
500
+ - uses: appeeky/appeeky-spec@v0
501
+ ```
502
+
503
+ The action comments the listing changes, check results and coverage before → after, and fails when `check` finds errors.
504
+
505
+ ## Running without GitHub Actions
506
+
507
+ Everything the workflows do lives in the repo, so any machine that can run Docker can do it: a server of your own, Railway, Fly, Render. Nothing listens on a port; the container only calls out to GitHub, Appeeky and the stores.
508
+
509
+ **On your own server.** Put [`docker/docker-compose.yml`](docker/docker-compose.yml) and a `.env` filled from [`docker/.env.example`](docker/.env.example) in one folder:
510
+
511
+ ```bash
512
+ docker compose up -d # runs the routines on a schedule, and again after a reboot
513
+ docker compose logs -f
514
+ ```
515
+
516
+ It runs `daily` at 06:00 UTC and checks for merged listing changes every 15 minutes. `DAILY_AT`, `PUSH_EVERY` and `WEEKLY_AT` change the schedule. Set `HEALTHCHECK_URL` (for example from healthchecks.io) to get an email when a daily run fails or does not happen. For more than one app, copy the service and give each its own env file.
517
+
518
+ **With a scheduler you already have** (cron, Cloud Run Jobs, Railway cron), run one routine per job:
519
+
520
+ ```bash
521
+ docker run --rm --env-file app.env ghcr.io/appeeky/appeeky-spec daily
522
+ ```
523
+
524
+ | Routine | Run it | Does |
525
+ | --- | --- | --- |
526
+ | `daily` | once a day | `category pull`, `visibility pull`, `verify --playbook`, `reviews pull`, `competitors pull`, then commits |
527
+ | `push` | every 10–15 minutes | pushes listing changes merged to `main` and commits the release. It remembers the last commit it handled as the `appeeky-spec/pushed` tag. The first run only sets the tag, so it never pushes what was on `main` before. A run that fails leaves the tag, so the next run tries again. |
528
+ | `weekly` | once a week | the co-founder review: the specialists read the repo, an agent writes a brief and opens a pull request with one listing change. Use the `latest-agent` image (adds Claude Code and `gh`), set `CLAUDE_CODE_OAUTH_TOKEN` (run `claude setup-token` on your computer: it uses your Claude Pro or Max plan) or `ANTHROPIC_API_KEY`, and commit the files `agents install` writes (or your own `.github/appeeky-propose.md`). |
529
+
530
+ `PUSH_DRY_RUN=1` shows what `push` would change without changing anything: a safe first run. `GITHUB_TOKEN` needs contents write on the repo (and pull requests write for `weekly`); it is passed to git through a credential helper, never in a URL. Pull request comments still need GitHub: keep the action above for those. To build the image yourself: `docker build -f docker/Dockerfile -t appeeky-spec .` (`--build-arg AGENT=1` for the agent).
531
+
532
+ ## Keyword popularity
533
+
534
+ `coverage` ranks targets by search popularity and difficulty from the [Appeeky API](https://appeeky.com) when `APPEEKY_API_KEY` is set. Results are cached for 7 days in `.appeeky/cache/` (add it to `.gitignore`). Without a key, coverage still works; it just is not weighted.
535
+
536
+ ## Using it with coding agents
537
+
538
+ Every command takes `--json`. A typical agent loop:
539
+
540
+ 1. `appeeky-spec coverage --json` and `appeeky-spec check --json` to find gaps.
541
+ 2. Edit the listing files and explain the change.
542
+ 3. `appeeky-spec diff --json` to confirm what will go live.
543
+ 4. A human runs `appeeky-spec push --note "<hypothesis>"`, or the agent runs it with `--yes` when you allow it.
544
+
545
+ Install the bundled skills so your agent knows the workflow:
546
+
547
+ ```bash
548
+ npx skills add appeeky/appeeky-spec
549
+ ```
550
+
551
+ | Skill | Use it for |
552
+ | --- | --- |
553
+ | `appeeky-spec` | Set up the spec, `doctor`, the workflows or other automation, the weekly routine, find gaps, edit, check and push listing changes, screenshots |
554
+ | `appeeky-spec-targets` | Personas, keyword research and which searches to target, competitor moves |
555
+ | `appeeky-spec-localize` | A new language or country, and spare keyword space in secondary locales |
556
+ | `appeeky-spec-review` | Day-7/14 results, keep or revert, playbook, closing experiments |
557
+ | `appeeky-spec-visibility` | Whether AI assistants name your app, which questions to track and how to get named; the category chart rank |
558
+ | `appeeky-spec-experiments` | Screenshot, UGC, ad and pricing experiments, attribution and smart links, ads and data for analysis |
559
+
560
+ Full documentation: [docs.appeeky.com/docs/spec](https://docs.appeeky.com/docs/spec).
561
+
562
+ ## Specialists and the co-founder review
563
+
564
+ Appeeky Spec ships six specialist subagents and a review that runs them, so your coding agent can work like a small growth team:
565
+
566
+ | Specialist | Reads | Returns |
567
+ | --- | --- | --- |
568
+ | `appeeky-aso` | coverage, ranks, competitors, AI visibility, the category chart | missing targets, spare keyword space, up to two listing changes with character counts |
569
+ | `appeeky-reviews` | reviews and their summary, competitors' low ratings, revenue data | what users ask for, a listing change that answers it, replies to draft |
570
+ | `appeeky-ads` | Apple Search Ads, Meta, TikTok and attribution data | paid searches worth targeting, and spend findings for a person |
571
+ | `appeeky-releases` | releases and their day-7 and day-14 checks, `loops.md`, the app's git history | what won and lost, what waits for a new version, What's New text |
572
+ | `appeeky-ugc` | UGC experiments, smart link and attribution results, personas, review words | which creators and hooks worked, the next UGC experiment with a brief for the creator |
573
+ | `appeeky-social` | what shipped and won, What's New, review words, the channels that bring installs | posts to publish, drafted per channel, with the link to use |
574
+
575
+ The specialists only read. The **co-founder review** (`/appeeky-cofounder`) is the one that acts: it checks which stores are free, starts the six specialists in parallel, picks the single change with the strongest evidence, edits and checks it, and writes a brief to `appeeky/briefs/<date>.md` with what each specialist found, the change, what is for a person (review replies, ads, product requests, the next UGC experiment, posts to publish) and what it did not pick. It turns the social specialist's posts into drafts in `appeeky/social/drafts/` and the UGC specialist's next video into a brief in `appeeky/ugc/briefs/`, so they go out after the same merge. Then it writes the pull request. It never pushes, posts or spends credits itself: the weekly routines deny those commands to the agent.
576
+
577
+ ```bash
578
+ npx @appeeky/spec agents install # .claude/agents/appeeky-*.md and .claude/commands/appeeky-cofounder.md
579
+ npx @appeeky/spec agents install --for opencode # .opencode/agent/ and .opencode/command/
580
+ ```
581
+
582
+ Commit them. In Claude Code, run `/appeeky-cofounder`; in agents without subagents, the review does the specialists' checklists itself. The weekly routines (GitHub Actions, cron, docker `WEEKLY_AT`) run the same review on a schedule. Edit the files to fit your app: `agents install` keeps your changes.
583
+
584
+ ## Library
585
+
586
+ The same logic is available as a library. `@appeeky/spec/core` has no Node dependencies and runs in the browser (editors, previews):
587
+
588
+ ```ts
589
+ import { lintAppStore, appStoreCoverage } from "@appeeky/spec/core";
590
+ import { loadProject, planStore, storeHandle } from "@appeeky/spec";
591
+
592
+ const project = await loadProject();
593
+ const plan = await planStore(project, storeHandle(project, "app-store"));
594
+ ```
595
+
596
+ ## Development
597
+
598
+ ```bash
599
+ git clone https://github.com/appeeky/appeeky-spec.git
600
+ cd appeeky-spec
601
+ npm install
602
+ npm test
603
+ npm run build
604
+ node dist/bin.js --help
605
+ ```
606
+
607
+ The UGC and AI visibility pipelines ship as minified bundles in `vendor/`. `npm run vendor:ugc` and `npm run vendor:visibility` rebuild them from the Appeeky repo next to this one; commit the result.
608
+
609
+ ### Releases
610
+
611
+ Merging to `main` releases automatically. After the tests pass, CI reads the [Conventional Commits](https://www.conventionalcommits.org) since the last `v*` tag and bumps the version:
612
+
613
+ | Commits since the last release | Release |
614
+ | --- | --- |
615
+ | only `docs:`, `chore:`, `test:`, `ci:` | none |
616
+ | `fix:` or `perf:` | patch, `0.1.0` → `0.1.1` |
617
+ | `feat:` | minor, `0.1.0` → `0.2.0` |
618
+ | a breaking change (`feat!:` or `BREAKING CHANGE:`) | minor while at `0.x`, then major |
619
+
620
+ It then:
621
+ - publishes `@appeeky/spec` to npm,
622
+ - turns `## Unreleased` in the changelog into the release section (or lists the commits when nothing was written there),
623
+ - tags `v<version>` and moves `v0`, the tag the GitHub Action is used by,
624
+ - creates the GitHub release,
625
+ - publishes the `ghcr.io/appeeky/appeeky-spec` images.
626
+
627
+ `npm run release:dry` shows what the next release would be, and `npm run pack:check` what the package would ship. CI needs the `NPM_TOKEN` secret, an npm automation token with publish rights on `@appeeky`.
628
+
629
+ ## License
630
+
631
+ Appeeky Spec is licensed under the [Functional Source License, Version 1.1, MIT Future License](./LICENSE) (FSL-1.1-MIT).
632
+
633
+ You can use, change and run it for your own apps and inside your company, including commercially. What it does not allow is offering it, or a product built from it, as a competing product or service.
634
+
635
+ ---
636
+
637
+ Appeeky Spec is an independent tool and is not affiliated with, endorsed by, or sponsored by Apple Inc. or Google LLC. App Store and App Store Connect are trademarks of Apple Inc. Google Play is a trademark of Google LLC.