@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
@@ -0,0 +1,93 @@
1
+ ---
2
+ name: appeeky-spec-experiments
3
+ description: Run and measure growth experiments outside the store listing in an Appeeky Spec repo, such as screenshots, UGC and creator videos, ads and pricing, with install attribution, smart links and the app's numbers. Use when asked to test screenshots, a TikTok or creator campaign, an ad creative or a price, to set up attribution, to see where installs and revenue come from, or to pull analytics, ads and RevenueCat data for analysis.
4
+ metadata:
5
+ version: 1.0.0
6
+ ---
7
+
8
+ # Experiments, attribution and data with Appeeky Spec
9
+
10
+ A listing push is already an experiment: `verify` measures it. Everything else you try to grow the app, like a new screenshot set, a creator video, an ad or a price, is tracked the same way in `appeeky/experiments/<id>.yaml`. Each one has one hypothesis, one metric, a baseline, a date to check and a result.
11
+
12
+ Run commands as `npx @appeeky/spec <command> --json`.
13
+
14
+ ## 1. Know what can be measured
15
+
16
+ `experiment add --metric` takes one metric. Prefer one the spec can measure on its own. It then takes the baseline from the last days and `verify` closes the experiment when it is due:
17
+
18
+ | Needs | Metrics |
19
+ | --- | --- |
20
+ | `APPEEKY_API_KEY` with the store connected in Appeeky | `conversion`, `page_views`, `downloads`, `proceeds`, `trials` |
21
+ | RevenueCat connected in Appeeky (app-scoped project) | `revenue`, `rc_trials`, `trial_conversion` |
22
+ | Apple Search Ads connected in Appeeky, or `search-ads pull` | `ad_installs`, `cpa` |
23
+ | Meta / TikTok campaigns under `ads:` | `meta_spend`, `meta_installs`, `meta_cpi`, `tiktok_spend`, `tiktok_conversions`, `tiktok_cpa` |
24
+ | `attribution:` in the spec | `organic_installs`, `organic_revenue`, `organic_paid`, and with `--link`: `link_clicks`, `link_installs`, `link_revenue` |
25
+ | reviews pulled | `rating`, `low_ratings` |
26
+ | AI Visibility / `category pull` | `ai_visibility`, `category_rank` (see appeeky-spec-visibility) |
27
+
28
+ Without any of these, the user gives the value at the end (`experiment close --value`). Say so up front.
29
+
30
+ ## 2. Attribution: where installs come from
31
+
32
+ With Appeeky Analytics in the app, the spec knows each install's source: organic store search, an Apple Search Ads keyword, a smart link, the Google Play referrer. It also knows what that install paid. Set it up once:
33
+
34
+ - `attribution init` uses Appeeky's hosted service (`APPEEKY_API_KEY`). `attribution init --self <url>` uses the user's own server on Cloudflare (`ATTRIBUTION_TOKEN`).
35
+ - It writes `attribution:` to the spec and prints the SDK key and the RevenueCat webhook. Adding the SDK to the app and the webhook to RevenueCat is a code and settings change: propose it, do not do it silently.
36
+ - An app on both stores has one id per store under `attribution.app`.
37
+
38
+ Then listing releases are judged on organic installs and revenue first, not only ranks.
39
+
40
+ ## 3. Start an experiment
41
+
42
+ ```bash
43
+ npx @appeeky/spec experiment add --type ugc --persona new-parents \
44
+ --hypothesis "A 'my baby slept in 5 minutes' hook gets more installs per view than the product demo" \
45
+ --metric link_installs --days 14 --link tiktok
46
+ ```
47
+
48
+ - `--type`: `screenshots`, `ugc`, `ads`, `pricing`, `other`.
49
+ - `--persona`: an id from `personas` in the spec. Write the hypothesis for that person.
50
+ - `--link [channel]` creates a smart link for the experiment (needs `attribution:`), so its installs and revenue are counted exactly. Channels: `tiktok`, `instagram`, `creator`, `tiktok_ads`, `meta_ads`, and others. Give each creator or ad its own experiment and link.
51
+ - **App Store caveat:** link installs can only be counted on Google Play. For an App Store-only app, measure `link_clicks`.
52
+ - `--asset` records what ran: file paths, video links, ad ids.
53
+ - `--baseline` and `--target` when the user knows them. Otherwise the baseline is measured.
54
+
55
+ Rules:
56
+
57
+ - One change per experiment. A new video with a new price measures neither.
58
+ - Do not start a screenshot or pricing experiment on a store that has a listing release still being measured. Check `status` first; `free` stores are safe. Both would move conversion.
59
+ - Screenshots are not indexed for search. They change conversion, so measure `conversion` (or `organic_installs` with attribution).
60
+
61
+ ## 4. Close it
62
+
63
+ `verify` closes due experiments on measured metrics (`closedExperiments` in its JSON) and lists the ones that still need a value (`dueExperiments`). For those:
64
+
65
+ ```bash
66
+ npx @appeeky/spec experiment close <id> --value 3.1 --note "Hook B: 3.1 installs per 1k views vs 1.8" --playbook
67
+ ```
68
+
69
+ `--playbook` adds the result to `appeeky/playbook.md`. A lost experiment is a result too: write it so it is not tried again.
70
+
71
+ ## 5. Ads
72
+
73
+ - `ads --json` gives spend, installs and cost per install for the last `--days` (default 30). It covers Apple Search Ads and the Meta and TikTok campaigns listed under `ads:` in the spec.
74
+ - With the user's own Search Ads key, `search-ads pull` writes `appeeky/search-ads/`: `daily.csv`, `keywords.csv`, `search-terms.csv` and `summary.md`. The key is saved on the App Store Connect CLI account (`appstore accounts search-ads <account> --client-id … --team-id … --key-id … --private-key-path …`, the account in `stores.appStore.account`), or set as `ASA_*` env in CI. It reads only the campaigns for the App Store app (`stores.appStore.searchAds` sets the org and the campaigns). The summary lists search terms that installed and are not targets yet: propose them as targets, since ranking for them organically is free. `ads` and `verify` then read Search Ads from these files, without Appeeky.
75
+ - `--save` keeps a copy in `appeeky/ads/<date>.yaml`.
76
+ - Campaigns are listed by id. Ask the user which campaigns promote this app; never guess from names.
77
+
78
+ ## 6. Social posts and UGC videos
79
+
80
+ With accounts and creator personas set up in Appeeky (`APPEEKY_API_KEY`):
81
+
82
+ - `social pull` writes the connected accounts, recent posts and how each did to `appeeky/social/` (read `summary.md`). `ugc pull` writes the personas (`ready` when they can make clips) and their clips to `appeeky/ugc/`.
83
+ - To post: write `appeeky/social/drafts/<date>-<slug>.yaml` (`accounts`, `text`, `when`, `media`, `why`) and run `social check`. To make a video: write `appeeky/ugc/briefs/<date>-<slug>.yaml` (`persona`, `script`, `scene`, `caption`, `durationSec`, `aspectRatio`, `why`) and run `ugc generate --dry-run` for its cost.
84
+ - Open a pull request. `social push` and `ugc generate` run after the merge (the push workflow does it). Never run them, or `ugc generate --yes`, unless the user asked: posts are public and clips use media credits.
85
+ - Give each post or video its own smart link (`experiment add --link <channel>`) so `experiment close` can say what it brought.
86
+
87
+ ## 7. Data for analysis
88
+
89
+ `data pull --json` writes CSV files the agent can read: store analytics, traffic sources, sales, Apple Search Ads, RevenueCat, Meta and TikTok per day. It also writes attribution by source, keyword and smart link. `--days` defaults to 90, and `README.md` in the folder says what each file holds.
90
+
91
+ - They are private business numbers. They go to `.appeeky/data/` (gitignored) unless the spec says `data: { commit: true }`. Never move them into the repo or paste them into a public pull request without asking.
92
+ - Pulling again merges by date, so files grow; there is no need to delete them first.
93
+ - Use them to find the next experiment: a country with many page views and low conversion, a channel with rising cost per install, a keyword that brings installs that never pay.
@@ -0,0 +1,52 @@
1
+ ---
2
+ name: appeeky-spec-localize
3
+ description: Add or improve a store listing for a new language or country in an Appeeky Spec repo (appeeky.spec.yaml + appeeky/store). Use when asked to localize App Store or Google Play metadata, launch in a new country, add a locale, translate keywords, or use spare keyword space in secondary locales such as Spanish (Mexico) for the US.
4
+ metadata:
5
+ version: 1.0.0
6
+ ---
7
+
8
+ # Localize a listing with Appeeky Spec
9
+
10
+ Localizing is keyword research per market, not translation. People in each country search with their own words, and a literal translation of an English keyword is often a term nobody types.
11
+
12
+ Run commands as `npx @appeeky/spec <command> --json`.
13
+
14
+ ## 1. Pick the storefront and the locale
15
+
16
+ - Storefronts are countries (`us`, `de`, `tr`). Listing locales are languages (`en-US`, `de-DE`, `tr`). One storefront can read several locales.
17
+ - Run `coverage --json`: `crossLocalization` shows extra locales a storefront already reads and how many keyword characters they leave unused. Filling those is the cheapest win (for the US App Store: the `es-MX` keyword field).
18
+ - For a new market, ask the user which countries matter (downloads, revenue, plans) before writing many locales.
19
+
20
+ ## 2. Targets first
21
+
22
+ Add the market's targets to `appeeky.spec.yaml` before writing copy:
23
+
24
+ ```yaml
25
+ targets:
26
+ de: [einschlafgeräusche, weißes rauschen, regengeräusche]
27
+ ```
28
+
29
+ Find the terms locally: what competitors in that country use in their titles and subtitles, store search suggestions, and keyword popularity from the Appeeky MCP server or `suggest --country <cc> --json` when `APPEEKY_API_KEY` is set. Mark terms you are unsure about and ask a native speaker when one is available.
30
+
31
+ ## 3. Write the listing file
32
+
33
+ Create `appeeky/store/app-store/<locale>.yaml` or `appeeky/store/google-play/<language>.yaml` (Google Play uses codes like `de-DE`, `tr-TR`).
34
+
35
+ - Only the fields you write are managed. Start with name, subtitle and keywords (App Store) or title and short description (Google Play); leave descriptions out until they are written properly.
36
+ - Keep the brand in the name the same in every locale.
37
+ - App Store keyword field: 100 characters, commas without spaces, no words already in the name or subtitle, no plurals of present words. Characters, not bytes.
38
+ - Google Play: a new language needs title, short description and full description together.
39
+ - For a secondary locale used only for another storefront's keywords (es-MX for the US), fill the keyword field with that storefront's target words that the primary locale does not cover. Name and subtitle should still read correctly in the locale's language.
40
+
41
+ ## 4. Check and ship
42
+
43
+ 1. `check --json` (no errors) and `coverage --country <cc> --json`: the new targets should be covered or partial.
44
+ 2. `diff --json`: a new locale shows as `newLocale`. On the App Store a new locale needs a version in Prepare for Submission.
45
+ 3. Push only when the user asks, with a note naming the market and the hypothesis: `push --note "Launch de-DE: einschlafgeräusche, weißes rauschen"`.
46
+
47
+ ## Mistakes to avoid
48
+
49
+ - Machine-translating the English keyword field.
50
+ - Copying the English subtitle into every locale.
51
+ - Adding ten locales at once with no targets: nothing can be measured.
52
+ - Using another app's brand name as a keyword.
@@ -0,0 +1,75 @@
1
+ ---
2
+ name: appeeky-spec-review
3
+ description: Review what past store listing changes did in an Appeeky Spec repo, decide whether to keep or revert them, update the playbook and plan the next change. Use when asked "did the last listing change work?", "should we revert?", for a weekly ASO review, to close experiments, or to summarize listing releases for a team update or PR.
4
+ metadata:
5
+ version: 1.2.0
6
+ ---
7
+
8
+ # Review listing releases with Appeeky Spec
9
+
10
+ Each push in `appeeky/releases/` holds the before/after values, the hypothesis (`note`) and the search ranks from right before it. `verify` measures again on day 7 and day 14.
11
+
12
+ Run commands as `npx @appeeky/spec <command> --json`.
13
+
14
+ ## 1. Measure
15
+
16
+ ```bash
17
+ npx @appeeky/spec verify --playbook --json
18
+ ```
19
+
20
+ This only measures releases that reached day 7 or day 14 and have not been measured for that day. Use `verify --now <release-id>` only when the user wants an early look, and say that early ranks are noisy. With `APPEEKY_API_KEY` set, each check also has `metrics`: conversion, downloads, revenue, trials and ads (Apple Search Ads, plus the Meta and TikTok campaigns under `ads:`) before and after the push, each marked `better`, `worse` or `flat`, and due experiments on those metrics are closed for you (`closedExperiments`). The JSON lists experiments that still need a value (`dueExperiments`).
21
+
22
+ ## 2. Read the verdict
23
+
24
+ | Verdict | Meaning | Usual action |
25
+ | --- | --- | --- |
26
+ | `won` | More targets rose (3+ places) than fell | Keep. Note what worked in the playbook |
27
+ | `lost` | One top-10 target, or two top-20 targets, fell out of the top 20 by 10+ places | Propose `revert` |
28
+ | `mixed` | Some rose, as many or more fell | Look at which targets moved and their popularity before deciding |
29
+ | `flat` | Nothing moved 3+ places | Wait for day 14; then treat as no effect |
30
+
31
+ When the check has `control`, read `net`: the places the changed storefronts gained beyond the unchanged ones. A "won" with a small net was turned into "mixed" (see `note`): the whole store moved. A metric change marked flat with a `noise` larger than the change is within its normal week-to-week range; do not credit or blame the release for it.
32
+
33
+ When no rank moved and the check has `decidedBy`, the verdict came from those outcome metrics, in this order:
34
+
35
+ 1. organic installs and revenue,
36
+ 2. else conversion,
37
+ 3. else downloads,
38
+ 4. else the category rank.
39
+
40
+ Say so: "won on organic installs, ranks unchanged".
41
+
42
+ A verdict decided by `category_rank` is the weakest kind. The chart moves with ads, featuring and season too, so say that and prefer waiting for day 14.
43
+
44
+ Two more metrics can be in `metrics.changes` (see appeeky-spec-visibility):
45
+
46
+ - `ai_visibility`: the share of AI assistant answers that name the app. Assistants update slowly, so read it on day 14.
47
+ - `category_rank`: lower is better, so a negative change is good.
48
+
49
+ Before recommending, check:
50
+
51
+ - **Popularity.** One popular term lost can outweigh three tiny ones gained.
52
+ - **Conversion and revenue.** When a check has `metrics` (or `conversion`), more ranks with lower conversion or revenue is not a win, and flat ranks with better conversion can be. Lead with the outcome the user cares about: revenue and trials before downloads, downloads before ranks.
53
+ - **Other causes.** A new app version, a featuring, a competitor's change (`appeeky/competitors/changes.md`), an ad campaign starting (`ads --json`) or seasonality in the same window. Say when the result could be noise.
54
+ - **Day 7 vs day 14.** App Store ranking changes settle over one to two weeks; prefer the day-14 check when it exists.
55
+
56
+ ## 3. Revert when it lost
57
+
58
+ ```bash
59
+ npx @appeeky/spec revert <release-id>
60
+ npx @appeeky/spec diff --json
61
+ ```
62
+
63
+ `revert` only edits the files. Push only when the user agrees, with a note: `push --note "Revert <id>: lost '<keyword>' (<before> → <after>)"`. App Store fields other than promotional text need a new version.
64
+
65
+ ## 4. Close experiments
66
+
67
+ `verify` closes due experiments on `conversion`, `downloads`, `revenue`, `trial_conversion`, `cpa`, `meta_cpi`, `tiktok_cpa` and the other measured metrics. With `attribution:` in the spec, checks also carry `organic_installs`, `organic_revenue` and `organic_paid`: whether more people found the app in search and paid. For UGC, creator or ad experiments, start them with `--link` so their installs are counted exactly. `appeeky-spec ads --json` shows spend and cost per install per paid channel; `--save` keeps a copy in `appeeky/ads/`. For the rest (installs per 1,000 views, tap-through rate), ask the user for the value or read it from the data they point to, then `experiment close <id> --value <n> --note "..." --playbook`. When starting one, prefer a measured metric: `experiment add --metric trial_conversion` takes its baseline from the last days.
68
+
69
+ ## 5. Update the playbook and plan
70
+
71
+ `appeeky/playbook.md` gets one line per result from `--playbook`. Add a short "why" in your own words when the cause is clear (for example: "subtitle words beat keyword-field words for 'white noise'"). Then propose the next single change, with a hypothesis that does not repeat a losing one.
72
+
73
+ ## Summaries
74
+
75
+ `log --markdown -n <count>` prints releases as Markdown tables with the note and the latest verdict, ready for a PR description or a team update.
@@ -0,0 +1,66 @@
1
+ ---
2
+ name: appeeky-spec-targets
3
+ description: Choose and prioritize the searches an app should be found for (the `targets` in appeeky.spec.yaml), from personas, competitors and keyword popularity. Use when asked which keywords to target, to do keyword research, to fill or clean up targets, to define personas, or to react to a competitor changing its title or subtitle.
4
+ metadata:
5
+ version: 1.0.0
6
+ ---
7
+
8
+ # Choose targets with Appeeky Spec
9
+
10
+ `targets` in `appeeky.spec.yaml` are the searches the app should be found for, per storefront. Coverage, suggestions, rank baselines and release verdicts are all measured against them, so wrong targets make every later step wrong. Pick them before editing any listing.
11
+
12
+ Run commands as `npx @appeeky/spec <command> --json`.
13
+
14
+ ## 1. Start from people, not words
15
+
16
+ Write down who the app is for as `personas`, with how each one searches:
17
+
18
+ ```yaml
19
+ personas:
20
+ - id: new-parents
21
+ name: New parents
22
+ description: Need their baby to fall asleep, often at night with one hand free.
23
+ searches: [baby sleep sounds, white noise baby, lullaby]
24
+ ```
25
+
26
+ Ask the user when you cannot tell from the repo, website or current listing. `searches` are seeds, not final targets. Experiments refer to personas by id.
27
+
28
+ The same personas are the source of the AI visibility intents (`aiVisibility.intents`, see appeeky-spec-visibility). A persona's need ("fall asleep to rain sounds") is an intent. How they would ask an assistant is a prompt; it is longer and more conversational than a store search.
29
+
30
+ ## 2. Collect candidates
31
+
32
+ From, in this order:
33
+
34
+ 1. The personas' `searches`.
35
+ 2. `suggest --json`: missing target words and Appeeky keyword ideas (with `APPEEKY_API_KEY`).
36
+ 3. Competitors: `competitors pull` writes the searches each competitor ranks best for to `appeeky/competitors/`, and what the top apps for the seed searches put in their titles and subtitles. With the Appeeky MCP server connected, use its keyword and competitor tools for popularity, difficulty, rankings and recent metadata changes.
37
+ 4. Store search suggestions in the storefront's language.
38
+
39
+ Drop other apps' brand names and trademarks, generic one-word terms the category giants own ("music", "photo"), and searches the app does not actually serve.
40
+
41
+ ## 3. Prioritize
42
+
43
+ For each candidate, look at popularity (how many search it), difficulty (how strong the apps ranking for it are) and relevance (would someone searching this install this app?).
44
+
45
+ - Relevance first: an irrelevant search that ranks still does not convert.
46
+ - Prefer specific phrases with decent popularity over head terms a small app cannot reach ("screenshot organizer" over "screenshot").
47
+ - Keep about 5 to 15 targets per storefront. More cannot be covered by a 30 + 30 + 100 character listing and cannot be measured clearly.
48
+ - Put the most important target first; it should go in the name or the title.
49
+
50
+ ## 4. Write them
51
+
52
+ ```yaml
53
+ targets:
54
+ us: [screenshot organizer, screenshot search, screenshot manager]
55
+ de: [screenshots organisieren, bildschirmfotos suchen]
56
+ ```
57
+
58
+ Then `coverage --json`: anything `missing` is the next listing change (see appeeky-spec). Each language market needs its own research, never translated targets (see appeeky-spec-localize).
59
+
60
+ ## Competitor moves
61
+
62
+ When a competitor drops a word from its title or subtitle, the search can open up; when it adds one of your targets, expect pressure on that rank. Check whether the word is one of your targets and how your rank moves, and propose a listing change only for a target that matters. Desktop lists these under ASO → Competitor moves.
63
+
64
+ ## Changing targets later
65
+
66
+ Targets are the yardstick of every release. Change them deliberately, say why in the commit, and do not swap targets in the same release as a listing change you want to measure.
@@ -0,0 +1,106 @@
1
+ ---
2
+ name: appeeky-spec-visibility
3
+ description: Get an app named when people ask ChatGPT, Claude, Gemini or Perplexity for an app like it, get its website found in Google, and track its category chart rank, in an Appeeky Spec repo. Use when asked about AI visibility, AI search, LLM or GEO/AEO recommendations, "does ChatGPT recommend us", which questions to track, why a competitor is named instead, SEO for the app's website, Search Console, llms.txt, or how the app ranks in its category.
4
+ metadata:
5
+ version: 1.0.0
6
+ ---
7
+
8
+ # AI visibility and category rank with Appeeky Spec
9
+
10
+ Three ways people find an app outside a store search: they ask an AI assistant, they search Google and land on the app's website, or they browse the category chart. The spec records all three and `verify` measures them around every push.
11
+
12
+ Run commands as `npx @appeeky/spec <command> --json`.
13
+
14
+ ## AI visibility
15
+
16
+ ### The model
17
+
18
+ - An **intent** is what someone wants: "fall asleep to rain sounds". Labels need 8+ characters.
19
+ - A **prompt** is one way to ask for it: "what app plays rain sounds to help me sleep". Prompts need 15+ characters.
20
+ - Both live in `appeeky.spec.yaml` under `aiVisibility.intents.<country>`. Appeeky AI Visibility asks each assistant every prompt on a schedule. It records whether the answer names the app, in what position, who else is named and which pages are cited.
21
+
22
+ Two ways to ask the assistants, writing the same files:
23
+
24
+ - **Appeeky AI Visibility** (`APPEEKY_API_KEY` on a plan with AI Visibility): Appeeky asks on a schedule; `visibility pull` writes the results.
25
+ - **Your own keys** (`--local`): `visibility scan --local` asks from where Spec runs, with `OPENAI_API_KEY` (ChatGPT), `ANTHROPIC_API_KEY` (Claude), `GEMINI_API_KEY` (Gemini) and `PERPLEXITY_API_KEY` (Perplexity), any of them. Each key adds that assistant. The providers bill the user; no Appeeky account is needed. It also writes `history.csv` (one row per scan, intent and assistant) and `answers/<country>.jsonl` (every answer, for reading why an assistant named someone else).
26
+
27
+ Use one of them per repo: both write `appeeky/visibility/<country>.yaml`.
28
+
29
+ ### Set up
30
+
31
+ 1. `visibility init`. If Appeeky already tracks intents, they are written into the spec. If not, Appeeky writes them from the store listing in the background; run `init` again a few minutes later. With your own keys, `visibility init --local` writes them from the listing right away, on the cheapest model of the key that is set (a fraction of a cent).
32
+ 2. Read the intents with the user. Keep the ones a real person of a persona would ask (see `personas` in the spec). Drop vague ones.
33
+ 3. With Appeeky: after the user approves the edits (in a pull request), run `visibility sync`. It only adds. It lists what exists only in Appeeky and archives it only with `--prune`, which you run only when the user asks.
34
+
35
+ Good prompts:
36
+
37
+ - Sound like a person typing: "best white noise app for a newborn", not "white noise app".
38
+ - Never contain the app's own name (that measures nothing) or a competitor's name.
39
+ - Each intent gets 2 to 4 prompts phrased differently: a question, a request for a list ("top 5…"), a comparison need ("an app like X but free" is fine without naming X).
40
+ - Use the storefront's language. A `de` intent gets German prompts written for German speakers, not translations.
41
+
42
+ ### Read the results
43
+
44
+ `visibility pull` writes `appeeky/visibility/<country>.yaml`:
45
+
46
+ | Field | Meaning |
47
+ | --- | --- |
48
+ | `summary` | intents by status: `winning` (named in 25%+ of answers), `weak`, `missing`, `noData` (not scanned yet) |
49
+ | `intents[].you` | share of answers that name the app, averaged over the assistants, 0–100 |
50
+ | `intents[].byModel` | the same per assistant: one assistant can name you while another never does |
51
+ | `intents[].namedInstead` | who the assistants name instead, with their share |
52
+ | `sources` | pages the assistants cite (mostly Perplexity), most cited first; `yours: true` is the app's own site |
53
+ | `fixes` | open suggestions from Appeeky for an intent |
54
+
55
+ `appeeky/visibility/changes.md` keeps what moved between pulls: an intent that went from `missing` to `winning`, or the app's site starting to be cited.
56
+
57
+ ### Improve it
58
+
59
+ Assistants repeat what the web says about an app. In order of what you can change:
60
+
61
+ 1. **The listing.** The first lines of the description and the subtitle should say what the app does in the intent's words ("rain sounds for sleep", not "an immersive audio experience"). This is a normal listing change: propose it in a pull request and measure it like any release (see appeeky-spec).
62
+ 2. **The app's own website.** A page per main use case, in plain words, with the app's name and store links. When `sources` shows the assistants cite pages like it from competitors, the same kind of page is the gap.
63
+ 3. **Cited third-party pages.** `sources` lists them: review sites, Reddit threads, "best apps for…" articles. Tell the user which ones name the competitors in `namedInstead` but not the app. Getting listed there is the user's outreach, not something to fake.
64
+ 4. **`fixes`.** Read each one. Propose the ones that are listing or website edits; summarize the rest for the user.
65
+
66
+ Never write fake reviews, posts or comments, and never make claims the app does not live up to. Assistants also read those.
67
+
68
+ ### Measure it
69
+
70
+ `verify` reads `ai_visibility`, the 0–1 share of answers that name the app, from Appeeky's daily series or from `appeeky/visibility/history.csv`. It compares the days before a push with the days after. Assistant answers change slowly and scans may be weekly, so a day-7 move is a hint and day 14 counts.
71
+
72
+ `visibility scan` costs money: AI Visibility credits, or with `--local` the user's own provider accounts. Run it only when the user asks. `visibility scan --local --dry-run` shows the prompts, the assistants and the cost and asks nothing; that one is always fine. On a schedule, the daily routine runs `visibility scan --local --every <days>` when the user set `VISIBILITY_EVERY`.
73
+
74
+ ## The app's website: Google and AI assistants
75
+
76
+ Set `web.url` in `appeeky.spec.yaml` (and `web.searchConsole` when the Search Console property is not the domain).
77
+
78
+ - `seo check` reads the site the way a crawler does, with no key: title, description, canonical, sitemap and noindex for Google; store links, the Smart App Banner and app structured data for the app; and for AI assistants whether robots.txt lets their crawlers in (GPTBot, OAI-SearchBot, ClaudeBot, PerplexityBot, Google-Extended and others), whether there is an llms.txt, how much text is readable without JavaScript, and how many target keywords the page uses. Each finding has a fix.
79
+ - `seo llms --out public/llms.txt` writes llms.txt from the listing. Propose it in the website's repo when the check says it is missing.
80
+ - `seo pull` reads Google Search Console with the user's own service account (`GSC_CREDENTIALS`: the JSON key or its path; the service account's email must be a user of the property). It writes `appeeky/seo/daily.csv` (clicks and impressions per day, kept), `queries.csv` and `pages.csv` (last 28 days), and `summary.md`: the trend, top queries, queries close to the top (position 4–15) and queries with the store target keywords.
81
+
82
+ `verify` reads `search_clicks` and `search_impressions` per day from `daily.csv`. Search Console lags two days.
83
+
84
+ On the user's own DataForSEO account (`DATAFORSEO_LOGIN`, `DATAFORSEO_PASSWORD`), for `web.keywords.<country>` or the store targets:
85
+
86
+ - `seo ranks` → `appeeky/seo/ranks.csv` and `ranks.md`: the site's Google position, where the app's store page ranks on Google, and whether the AI Overview shows and cites the site, per keyword. `changes.md` gets keywords entering or leaving the top 10. `verify` reads `web_rank` (lower is better) and `ai_overview_citations`.
87
+ - `seo keywords` → `appeeky/seo/keywords.csv`: volume, difficulty, CPC, intent, plus related ideas (`tracked: no`). High-volume, low-difficulty ideas are candidates for `web.keywords` and, when people would search the store for them too, for store `targets`.
88
+ - `seo domain` → `appeeky/seo/domain.md` and `ranked-keywords.csv`: what the site ranks for and which sites compete for the same searches.
89
+
90
+ They cost money. Run them with `--dry-run` freely, and without it only when the user asks; the daily routine checks ranks on its own.
91
+
92
+ Read them together: a query people search on Google is a phrase they may also ask an assistant, and a good intent or target. A page that ranks in Google is a page the assistants can cite. When `visibility` shows the assistants cite competitors' pages for an intent and `seo check` shows the site is thin or blocks AI crawlers, the website is the fix, and it is the user's website change, proposed as a list or in that repo, not a listing push.
93
+
94
+ ## Category rank
95
+
96
+ `category pull` adds today's rank in the category chart (top 100) for each storefront to `appeeky/category.csv`.
97
+
98
+ - **App Store:** reads Apple's chart, no key. The genre comes from the app, and a paid app is read from top-paid.
99
+ - **Google Play:** needs `APPEEKY_API_KEY` and `category.googlePlay` in the spec (e.g. `HEALTH_AND_FITNESS`). Ask the user for the category if it is not set.
100
+ - An empty rank means not in the top 100.
101
+
102
+ The rank mostly follows downloads, so it moves with everything: listing changes, ads, featuring, seasonality. Read it as a sign, not a cause:
103
+
104
+ - `verify` reads it as `category_rank`, where lower is better. When no target search moved and there is no analytics data, it decides the release (`decidedBy: ["category_rank"]`). Say so.
105
+ - A jump on the day an ad campaign started is the ad, not the listing. Check `appeeky ads` and `loops.md` first.
106
+ - It needs history: run it daily (the daily workflow does) so there are days before each push.