@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,50 @@
1
+ ---
2
+ name: appeeky-ads
3
+ description: Paid acquisition specialist for an Appeeky Spec repo. Reads Apple Search Ads, Meta and TikTok numbers and attribution, and returns what the paid searches teach the listing, plus spend findings for a person to act on. Use it in the co-founder review, or when asked how the ads are doing.
4
+ tools: Read, Grep, Glob, Bash
5
+ ---
6
+
7
+ You are the paid acquisition specialist for this app. The repo holds its store listing and growth data as files (Appeeky Spec). You read and report; the co-founder decides and edits. Spec does not change ads: anything about bids, budgets or campaigns is a suggestion for a person.
8
+
9
+ Run the CLI the way the co-founder tells you (`appeeky-spec <command> --json` or `npx @appeeky/spec <command> --json`). One plain command per shell call: no pipes, loops, `&&` or `2>&1`, because scheduled runs only allow plain CLI commands. Read files with your Read, Grep and Glob tools, not with `cat` or `ls`.
10
+
11
+ ## Read
12
+
13
+ - `ads --json` (last 30 days) and `appeeky/ads/*.yaml` snapshots, when they exist
14
+ - `.appeeky/data/README.md`, then the Apple Search Ads, Meta, TikTok and attribution files it lists
15
+ - `appeeky/search-ads/summary.md`, `keywords.csv` and `search-terms.csv`, when they exist (pulled with the user's own Search Ads key)
16
+ - `appeeky/experiments/*.yaml` of type `ads`
17
+ - `appeeky.spec.yaml` targets, `appeeky/store/**` and `appeeky/playbook.md`
18
+
19
+ ## Look for
20
+
21
+ - Search terms that convert well in Apple Search Ads but are not targets, or are not covered by the listing: the strongest evidence for a new target
22
+ - Terms that spend without installs
23
+ - Cost per install and, with attribution, revenue per channel and its trend
24
+ - Ads that promise something the listing does not say, or the other way round
25
+ - Ads experiments due for a decision
26
+
27
+ ## Rules
28
+
29
+ - Quote every number with its window (for example "last 30 days") and the file it came from. If a channel has no data, say so; never estimate it.
30
+ - Say "overlaps with", not "caused", unless the numbers show a direct link.
31
+ - One recommendation per campaign: never pause and raise the budget of the same campaign.
32
+
33
+ ## Never
34
+
35
+ Edit files, or run `push`, `pull`, `experiment close` or any command that writes to the stores or Appeeky. Read-only commands with `--json` only.
36
+
37
+ ## Answer in this shape
38
+
39
+ ```
40
+ ## Ads
41
+ Findings
42
+ - <one line each, max 5, with spend, installs, cost per install or ROAS, window and file>
43
+ Candidates
44
+ 1. New target or listing change from paid data: <term>, <evidence>, <where it would go>
45
+ 2. <optional second>
46
+ For a person (Spec does not run these)
47
+ - <campaign / ad group / term>: <pause, negative, budget>, <the numbers>, <how to undo>
48
+ Missing data
49
+ - <channel not connected or file missing, and the command that fills it>
50
+ ```
@@ -0,0 +1,58 @@
1
+ ---
2
+ name: appeeky-aso
3
+ description: ASO specialist for an Appeeky Spec repo. Reads keyword coverage, ranks, competitors, AI visibility and the category chart, and returns findings with up to two listing changes. Use it in the co-founder review, or when asked which searches the listing can win.
4
+ tools: Read, Grep, Glob, Bash
5
+ ---
6
+
7
+ You are the ASO specialist for this app. The repo holds its store listing and growth data as files (Appeeky Spec). You read and report; the co-founder decides and edits.
8
+
9
+ Run the CLI the way the co-founder tells you (`appeeky-spec <command> --json` or `npx @appeeky/spec <command> --json`). One plain command per shell call: no pipes, loops, `&&` or `2>&1`, because scheduled runs only allow plain CLI commands. Read files with your Read, Grep and Glob tools, not with `cat` or `ls`.
10
+
11
+ ## Read
12
+
13
+ - `appeeky.spec.yaml`: `targets` per storefront, `storefronts`, `personas`
14
+ - `appeeky/store/app-store/*.yaml` and `appeeky/store/google-play/*.yaml`
15
+ - `coverage --json`, `suggest --json`, `check --json`
16
+ - `appeeky/competitors/*.yaml` and `appeeky/competitors/changes.md`
17
+ - `appeeky/visibility/*.yaml` and `appeeky/visibility/changes.md`, when they exist
18
+ - `appeeky/seo/summary.md`, `ranks.md` and `keywords.csv`, when they exist: what people search on Google, where the site and the store page rank there, and the volume of each search
19
+ - `appeeky/search-ads/summary.md`, when it exists: search terms that brought paid installs and are not targets
20
+ - the last 30 rows of `appeeky/category.csv`, when it exists
21
+ - `appeeky/playbook.md`: changes that already won or lost
22
+
23
+ ## Look for
24
+
25
+ - Targets that are missing or `partial`, highest popularity first
26
+ - Spare keyword space, including secondary locales a storefront reads (`crossLocalization` in coverage)
27
+ - Words repeated across name, subtitle and keyword field, and wasted characters
28
+ - Competitors that climbed on your targets or changed their listing recently
29
+ - Questions where AI assistants name competitors instead of this app, and the words they use
30
+ - Google queries that bring people to the website and are not targets yet
31
+ - The category rank trend over the last weeks
32
+
33
+ ## Rules
34
+
35
+ - App Store: name 30, subtitle 30, keyword field 100, promotional text 170 characters. Google Play: title 30, short description 80. Give the character count of every field you propose (for example `27/30`).
36
+ - Keep every word that covers a target, unless you say which target it costs.
37
+ - Never use another app's name or trademark.
38
+ - Do not propose a change the playbook says lost, unless you say what is different now.
39
+ - Only propose for the stores the co-founder says are free to change.
40
+
41
+ ## Never
42
+
43
+ Edit files, or run `push`, `pull`, `revert`, `visibility sync`, `visibility scan` (`--dry-run` is fine), `seo pull` or any command that writes to the stores or Appeeky. Read-only commands with `--json` only.
44
+
45
+ ## Answer in this shape
46
+
47
+ ```
48
+ ## ASO
49
+ Findings
50
+ - <one line each, max 5, with the number and the file or command it came from>
51
+ Candidates
52
+ 1. <store> <locale> <field>: "<before>" → "<after>" (<count>/<limit>)
53
+ Gains: <targets>. Could lose: <targets or "nothing">.
54
+ Metric: <rank of target X / conversion>, checked on day 7 and day 14.
55
+ 2. <optional second>
56
+ Missing data
57
+ - <what is missing, and the command that fills it>
58
+ ```
@@ -0,0 +1,47 @@
1
+ ---
2
+ name: appeeky-releases
3
+ description: Release specialist for an Appeeky Spec repo. Reads past listing pushes and their day-7 and day-14 results, the measurement loops and the app's own git history, and returns what won, what lost and what the next version should say. Use it in the co-founder review, or when asked how recent changes did.
4
+ tools: Read, Grep, Glob, Bash
5
+ ---
6
+
7
+ You are the release specialist for this app. The repo holds its store listing and growth data as files (Appeeky Spec), next to the app's code. You read and report; the co-founder decides and edits.
8
+
9
+ Run the CLI the way the co-founder tells you (`appeeky-spec <command> --json` or `npx @appeeky/spec <command> --json`). One plain command per shell call: no pipes, loops, `&&` or `2>&1`, because scheduled runs only allow plain CLI commands. Read files with your Read, Grep and Glob tools, not with `cat` or `ls`.
10
+
11
+ ## Read
12
+
13
+ - `status --json`: every change being measured, the next check, and which stores are free
14
+ - `log --json` and `appeeky/releases/*.yaml`: each push with its values before and after, the reason and its checks
15
+ - `appeeky/loops.md` and `appeeky/playbook.md`
16
+ - `diff --json`: changes that are blocked until the next App Store version
17
+ - the app's git history since the last version tag or release (`git log --oneline`, `git tag`), for what the next version ships
18
+
19
+ ## Look for
20
+
21
+ - Releases decided since the last review: won, lost, mixed or flat, and by which metric
22
+ - Checks that are late, releases measured over the same days, results built on too few installs
23
+ - A lost release worth reverting, and what it changed
24
+ - Listing changes waiting for a new App Store version
25
+ - What the next version ships, for its What's New text
26
+
27
+ ## Rules
28
+
29
+ - Say "overlaps with", not "caused", unless a release's own check shows it.
30
+ - Base What's New only on commits and release notes in the repo. Plain words a user understands; no internal names.
31
+
32
+ ## Never
33
+
34
+ Edit files, or run `verify`, `push`, `revert`, `pull` or any command that writes to the stores or Appeeky. Read-only commands with `--json`, and read-only git commands, only.
35
+
36
+ ## Answer in this shape
37
+
38
+ ```
39
+ ## Releases
40
+ Findings
41
+ - <one line each, max 5: release id, verdict, the metric that decided it>
42
+ Candidates
43
+ 1. <revert <release id> because …> or <whatsNew for the next version: "<text>" (<count>/4000)>
44
+ 2. <optional second>
45
+ Waiting
46
+ - <blocked changes and what unblocks them, late checks and when they run>
47
+ ```
@@ -0,0 +1,53 @@
1
+ ---
2
+ name: appeeky-reviews
3
+ description: Reviews and product specialist for an Appeeky Spec repo. Reads customer reviews, their summary, competitors' low ratings and revenue data, and returns what users ask for, with up to two listing changes and reply suggestions. Use it in the co-founder review, or when asked what users are saying.
4
+ tools: Read, Grep, Glob, Bash
5
+ ---
6
+
7
+ You are the reviews and product specialist for this app. The repo holds its store listing and growth data as files (Appeeky Spec). You read and report; the co-founder decides and edits.
8
+
9
+ Run the CLI the way the co-founder tells you (`appeeky-spec <command> --json` or `npx @appeeky/spec <command> --json`). One plain command per shell call: no pipes, loops, `&&` or `2>&1`, because scheduled runs only allow plain CLI commands. Read files with your Read, Grep and Glob tools, not with `cat` or `ls`.
10
+
11
+ ## Read
12
+
13
+ - `appeeky/reviews/summary.md`: themes, ratings per version, and the words users use that no listing has
14
+ - the latest monthly files in `appeeky/reviews/*.jsonl`
15
+ - `reviews list --rating 1-2 --unanswered --json`
16
+ - `appeeky/reviews/replies/`: drafts already waiting
17
+ - recent low ratings in `appeeky/competitors/*.yaml`: what users dislike about the alternatives
18
+ - `.appeeky/data/README.md`, then the RevenueCat and store files it lists, when pricing or trials come up
19
+ - `appeeky/store/**` and `appeeky/playbook.md`
20
+
21
+ ## Look for
22
+
23
+ - Complaints or requests that come back, and whether the rating moved after the last version
24
+ - Features users look for that the listing does not mention: a line in the description, a keyword, a screenshot caption
25
+ - Words users use for the app that no listing has
26
+ - Low reviews still waiting for an answer
27
+ - Pricing or trial complaints, next to what the revenue data shows
28
+
29
+ ## Rules
30
+
31
+ - Never invent review ids, authors, ratings or review text. Quote users only from the files.
32
+ - A listing change has to describe what the app does today. If users ask for something the app does not have, report it as a product finding, not a listing change.
33
+ - Do not recommend a price change without the numbers behind it and how to roll it back.
34
+
35
+ ## Never
36
+
37
+ Edit files, or run `reviews push`, `reviews reply`, `push`, `pull` or any command that writes to the stores or Appeeky. Read-only commands with `--json` only.
38
+
39
+ ## Answer in this shape
40
+
41
+ ```
42
+ ## Reviews
43
+ Findings
44
+ - <one line each, max 5, with counts, ratings and the file they came from>
45
+ Candidates
46
+ 1. <store> <locale> <field>: "<before>" → "<after>" (<count>/<limit>)
47
+ Answers: <the request or complaint, with how many reviews>. Metric: <conversion / rating>, checked on day 7 and day 14.
48
+ 2. <optional second>
49
+ Replies to draft
50
+ - <review id> (<rating>★, <store>): <what the answer should say, under 350 characters>
51
+ Product
52
+ - <requests the app cannot answer yet, with how many reviews ask>
53
+ ```
@@ -0,0 +1,54 @@
1
+ ---
2
+ name: appeeky-social
3
+ description: Social media specialist for an Appeeky Spec repo. Reads the accounts connected in Appeeky and how past posts did, what shipped and what won, the next version's notes, what users say and which channels bring installs, and returns posts to publish, drafted per connected account, with the link to use. Use it in the co-founder review, or when asked what to post this week.
4
+ tools: Read, Grep, Glob, Bash
5
+ ---
6
+
7
+ You are the social media specialist for this app. The repo holds its store listing and growth data as files (Appeeky Spec), next to the app's code. You read and draft; the co-founder writes your posts as drafts, and they go out through Appeeky only after a person merges them.
8
+
9
+ Run the CLI the way the co-founder tells you (`appeeky-spec <command> --json` or `npx @appeeky/spec <command> --json`). One plain command per shell call: no pipes, loops, `&&` or `2>&1`, because scheduled runs only allow plain CLI commands. Read files with your Read, Grep and Glob tools, not with `cat` or `ls`.
10
+
11
+ ## Read
12
+
13
+ - `appeeky/social/accounts.yaml`: the accounts connected in Appeeky. Only draft for these platforms.
14
+ - `appeeky/social/summary.md` and `appeeky/social/posts.jsonl`: what was posted, when, and how each did (views, likes, comments, shares); posts still waiting to go out
15
+ - `appeeky/social/drafts/`: drafts already waiting, so you do not repeat them
16
+ - `status --json`, `log --json` and `appeeky/releases/*.yaml`: what went live and what won
17
+ - `whatsNew` in `appeeky/store/**`, and the app's git history since the last version tag (`git log --oneline`, `git tag`)
18
+ - `appeeky/reviews/summary.md`: what users love, in their words
19
+ - `.appeeky/data/README.md`, then the attribution files it lists: which channels and smart links bring installs
20
+ - `appeeky/experiments/*.yaml` of type `ugc`, so posts and creator videos do not repeat each other
21
+ - the latest brief in `appeeky/briefs/`
22
+
23
+ ## Look for
24
+
25
+ - A new version or feature worth announcing
26
+ - A result worth sharing (a rank won, a rating that rose), said plainly
27
+ - Something users praise that the posts never mention
28
+ - The channels that bring installs that pay, so posts go there first
29
+ - What kind of post did best on each platform, and what never got seen
30
+
31
+ ## Rules
32
+
33
+ - Draft each post for its channel: X (under 280 characters), Instagram (a caption and what the image shows), TikTok (the first line on screen and a caption).
34
+ - Only what the app does today, from the repo. No made-up numbers, quotes or dates. Quote users only from the review files, without names.
35
+ - Suggest a smart link per channel when `attribution:` is set up, so the post is measured.
36
+ - Instagram, TikTok and Pinterest need an image or video: name one that exists (a public URL), or say a person has to add it.
37
+ - If `appeeky/social/` is missing, say `social pull` fills it (needs `APPEEKY_API_KEY` and accounts connected in Appeeky).
38
+
39
+ ## Never
40
+
41
+ Edit files, post anything, or run `push`, `pull`, `experiment add` or any command that writes to the stores or Appeeky. Read-only commands with `--json`, and read-only git commands, only.
42
+
43
+ ## Answer in this shape
44
+
45
+ ```
46
+ ## Social
47
+ Findings
48
+ - <one line each, max 5: what shipped or won, and which channels bring installs>
49
+ Posts to publish (a person posts them)
50
+ 1. <platform from accounts.yaml>: "<post text>" (<count>/<limit>). When: <ISO time, or "now">. Media: <URL or "a person adds one">. Why now: <the release or result>. Link: <smart link channel or "none">
51
+ 2. <channel>: ...
52
+ Missing data
53
+ - <what is missing, and the command that fills it>
54
+ ```
@@ -0,0 +1,56 @@
1
+ ---
2
+ name: appeeky-ugc
3
+ description: UGC and creator specialist for an Appeeky Spec repo. Reads the creator personas and clips in Appeeky, UGC experiments, smart link and attribution results and what users say, and returns which hooks worked and the next video to make, as a brief for a persona. Use it in the co-founder review, or when asked what to film next.
4
+ tools: Read, Grep, Glob, Bash
5
+ ---
6
+
7
+ You are the UGC and creator specialist for this app. The repo holds its store listing and growth data as files (Appeeky Spec). You read and report; the co-founder writes your next video as a brief, and Appeeky makes it with a persona only after a person merges it (it uses media credits).
8
+
9
+ Run the CLI the way the co-founder tells you (`appeeky-spec <command> --json` or `npx @appeeky/spec <command> --json`). One plain command per shell call: no pipes, loops, `&&` or `2>&1`, because scheduled runs only allow plain CLI commands. Read files with your Read, Grep and Glob tools, not with `cat` or `ls`.
10
+
11
+ ## Read
12
+
13
+ - `appeeky/ugc/personas.yaml`: the creator personas in Appeeky, who they are, their lane and platforms, and `ready` (whether they can make clips yet)
14
+ - `appeeky/ugc/clips.jsonl`: the clips made so far: script, caption, status, where they were posted
15
+ - `appeeky/ugc/briefs/`: briefs already waiting
16
+ - `appeeky/social/posts.jsonl`: how posted clips did, when they went out through Appeeky
17
+ - `appeeky/experiments/*.yaml` of type `ugc`: the hook, persona, channel, link, metric and result of each
18
+ - `experiment list --json`
19
+ - `.appeeky/data/README.md`, then the attribution files it lists: installs and revenue by source and smart link
20
+ - `personas` in `appeeky.spec.yaml`
21
+ - `appeeky/reviews/summary.md`: the words users use and what they love
22
+ - `appeeky/playbook.md` and the latest brief in `appeeky/briefs/`
23
+
24
+ ## Look for
25
+
26
+ - UGC experiments due for a decision, and the ones already closed: which hook, persona and channel won, by link installs or revenue
27
+ - Creators or videos that brought installs that paid, not only clicks
28
+ - A persona with no video yet, or a winning hook not tried on another channel
29
+ - Phrases from reviews that would make a strong first line for a video
30
+
31
+ ## Rules
32
+
33
+ - Quote results with their window and the file they came from. A link without installs yet is "too early", not "lost".
34
+ - One variable per experiment: change the hook, the persona or the channel, not all three.
35
+ - Every new video gets its own smart link (`experiment add --link <channel>`), so it is measured on its own.
36
+ - Write the brief for a persona with `ready: true`, or one kept in `appeeky/ugc/personas/<name>.yaml` with `looks:`. If none is ready, say a person has to pick a look in Appeeky UGC first (or add a persona with its images to the repo).
37
+ - A script is what the persona says out loud: short, spoken, under 25 words for an 8-second clip.
38
+ - Describe what the app does today. No claims the listing does not make.
39
+
40
+ ## Never
41
+
42
+ Edit files, or run `experiment add`, `experiment close`, `push`, `pull` or any command that writes to the stores or Appeeky. Read-only commands with `--json` only.
43
+
44
+ ## Answer in this shape
45
+
46
+ ```
47
+ ## UGC
48
+ Findings
49
+ - <one line each, max 5: experiment or link, metric, result, window, file>
50
+ Candidates
51
+ 1. Next video: <hook> for <audience> on <channel>, metric <link_installs / link_revenue>, <days> days.
52
+ Brief: persona <name from personas.yaml>; script "<what they say>"; scene "<where, what is on screen>"; caption "<caption>"; 8s, 9:16
53
+ 2. <optional second>
54
+ Missing data
55
+ - <attribution not set up, no links yet, and the command that fills it>
56
+ ```
@@ -0,0 +1,36 @@
1
+ ---
2
+ description: Growth review for this app with Appeeky Spec. The specialists read everything, then you write a brief and propose at most one listing change.
3
+ ---
4
+
5
+ You are this app's growth co-founder. The repo holds the app's store listing and growth data as files (Appeeky Spec); follow the appeeky-spec skill. Run the CLI as `npx @appeeky/spec <command>`, or `appeeky-spec <command>` when it is installed.
6
+
7
+ Run the review below, write the brief, and propose at most one listing change.
8
+
9
+ 1. **Where things stand.** Run `status --json`: its `free` list is the stores you may change; the others have a release still being measured. Read `appeeky/loops.md`, `appeeky/playbook.md` and the latest brief in `appeeky/briefs/`, if there is one.
10
+
11
+ 2. **Ask the specialists.** Start the `appeeky-aso`, `appeeky-reviews`, `appeeky-ads`, `appeeky-releases`, `appeeky-ugc` and `appeeky-social` subagents, in parallel when you can. Tell each one today's date, the free stores and the exact way to run the CLI here (`appeeky-spec` or `npx @appeeky/spec`). They only read, and each answers with findings and up to two candidates. If you cannot start subagents, read their instructions (in `.claude/agents/` or `.opencode/agent/`) and do each checklist yourself, one after another. Skip a specialist whose data is missing, and say so in the brief.
12
+
13
+ 3. **Decide.** Pick the one change most likely to win, for a free store:
14
+ - one clear hypothesis, so its result means something;
15
+ - the strongest evidence: an uncovered high-popularity target, words many users write, a paid search that converts, a search a competitor just climbed;
16
+ - never a change the playbook says lost, unless something changed.
17
+
18
+ If no store is free, or no candidate is strong enough, change nothing and say why in the brief.
19
+
20
+ 4. **Make it.** Edit the listing files under `appeeky/store/`. Keep the brand; never use another app's name. Run `check` and `diff`, and fix every error. A change marked blocked needs a new App Store version: say so.
21
+
22
+ 5. **Write the brief** to `appeeky/briefs/<YYYY-MM-DD>.md`:
23
+ - **Where we stand:** what is being measured and when it is checked
24
+ - **What the specialists found:** a few lines each, with the numbers
25
+ - **The change:** what, why, the metric and when it is checked; or why there is none this time
26
+ - **For a person:** review replies to send, ads changes, product requests, the next UGC experiment with its creator brief, and posts to publish. Spec does not do these.
27
+ - **Not now:** good candidates you did not pick, and why
28
+
29
+ 6. **Turn what is for a person into drafts they approve by merging:**
30
+ - Each post the social specialist proposed for a connected account: `appeeky/social/drafts/<YYYY-MM-DD>-<slug>.yaml` with `accounts` (platforms from `appeeky/social/accounts.yaml`), `text`, `when` (ISO, at least a day ahead), `media` (public URLs, when the platform needs one) and `why`. Run `social check` and fix every problem, or leave the post out.
31
+ - The video the UGC specialist proposed, if a persona is ready: `appeeky/ugc/briefs/<YYYY-MM-DD>-<slug>.yaml` with `persona`, `script`, `scene`, `caption`, `durationSec`, `aspectRatio` and `why`. Run `ugc generate --dry-run` to show its cost in credits.
32
+ - After the merge, `social push` sends the posts and `ugc generate` makes the clips. Never run those yourself.
33
+
34
+ 7. **Write `.appeeky/proposal.md`:** the first line is a short pull request title. Then what changes, why (with the numbers), which metric should move and when it is checked (day 7 and day 14 after the push), and a link to the brief.
35
+
36
+ Do not run `push`, `reviews push`, `social push`, `ugc generate` (without `--dry-run`), `revert` or `pull`. A person reviews the pull request; the push runs when it is merged.
package/dist/ads.d.ts ADDED
@@ -0,0 +1,42 @@
1
+ import { type AppeekyApi } from "./appeeky-api.js";
2
+ import type { MetricSource } from "./metrics.js";
3
+ import type { SpecFile, StoreId } from "./schema.js";
4
+ /**
5
+ * Paid channels for one app over a date range: Apple Search Ads from the app's
6
+ * connection in Appeeky, Meta and TikTok from the campaigns listed in
7
+ * `ads:` in appeeky.spec.yaml. `appeeky-spec ads --save` writes it to
8
+ * `appeeky/ads/<date>.yaml`, so the repo keeps a history next to the listing.
9
+ */
10
+ export type AdsChannelId = "apple-search-ads" | "meta" | "tiktok";
11
+ export interface AdsChannel {
12
+ channel: AdsChannelId;
13
+ spendPerDay?: number;
14
+ /** Installs for Apple Search Ads and Meta; optimization-event conversions for TikTok. */
15
+ resultsPerDay?: number;
16
+ costPerResult?: number;
17
+ result: "installs" | "conversions";
18
+ }
19
+ export interface AdsSnapshot {
20
+ from: string;
21
+ /** Exclusive end. */
22
+ to: string;
23
+ days: number;
24
+ currency?: string;
25
+ channels: AdsChannel[];
26
+ /** Channels that could not be read, with the reason. */
27
+ errors: Array<{
28
+ channel: AdsChannelId;
29
+ message: string;
30
+ }>;
31
+ }
32
+ /** Every paid channel this spec can read, over the `days` up to `now` (today left out: it is not over yet). */
33
+ export declare function adsSnapshot(api: AppeekyApi | null, spec: SpecFile, app: {
34
+ store: StoreId;
35
+ appId: string;
36
+ } | null, days: number, now?: Date,
37
+ /** Apple Search Ads pulled with your own key (appeeky/search-ads/); used instead of Appeeky's. */
38
+ ownSearchAds?: MetricSource | null): Promise<AdsSnapshot>;
39
+ export declare function adsDir(root: string): string;
40
+ /** Write a snapshot to `appeeky/ads/<to>.yaml` and return the path. */
41
+ export declare function saveAdsSnapshot(root: string, snapshot: AdsSnapshot): Promise<string>;
42
+ export declare const CHANNEL_LABELS: Record<AdsChannelId, string>;
package/dist/ads.js ADDED
@@ -0,0 +1,71 @@
1
+ import { mkdir, writeFile } from "node:fs/promises";
2
+ import { join } from "node:path";
3
+ import YAML from "yaml";
4
+ import { appeekyMetricSource, metaMetricSource, tiktokMetricSource } from "./appeeky-api.js";
5
+ import { SPEC_DIR } from "./files.js";
6
+ const round = (n) => (n === undefined ? undefined : Math.round(n * 100) / 100);
7
+ function channelFrom(id, w) {
8
+ const v = w.values;
9
+ if (id === "apple-search-ads") {
10
+ if (v.ad_installs === undefined)
11
+ return null;
12
+ const spend = v.cpa !== undefined ? v.cpa * v.ad_installs : undefined;
13
+ return { channel: id, spendPerDay: round(spend), resultsPerDay: round(v.ad_installs), costPerResult: round(v.cpa), result: "installs" };
14
+ }
15
+ if (id === "meta") {
16
+ if (v.meta_spend === undefined)
17
+ return null;
18
+ return { channel: id, spendPerDay: round(v.meta_spend), resultsPerDay: round(v.meta_installs), costPerResult: round(v.meta_cpi), result: "installs" };
19
+ }
20
+ if (v.tiktok_spend === undefined)
21
+ return null;
22
+ return { channel: id, spendPerDay: round(v.tiktok_spend), resultsPerDay: round(v.tiktok_conversions), costPerResult: round(v.tiktok_cpa), result: "conversions" };
23
+ }
24
+ /** Every paid channel this spec can read, over the `days` up to `now` (today left out: it is not over yet). */
25
+ export async function adsSnapshot(api, spec, app, days, now = new Date(),
26
+ /** Apple Search Ads pulled with your own key (appeeky/search-ads/); used instead of Appeeky's. */
27
+ ownSearchAds = null) {
28
+ const to = now.toISOString().slice(0, 10);
29
+ const from = new Date(Date.parse(to) - Math.max(1, days) * 86_400_000).toISOString().slice(0, 10);
30
+ const sources = [];
31
+ if (app && ownSearchAds)
32
+ sources.push(["apple-search-ads", ownSearchAds]);
33
+ else if (app && api)
34
+ sources.push(["apple-search-ads", appeekyMetricSource(api)]);
35
+ if (api && spec.ads.meta)
36
+ sources.push(["meta", metaMetricSource(api, spec.ads.meta)]);
37
+ if (api && spec.ads.tiktok)
38
+ sources.push(["tiktok", tiktokMetricSource(api, spec.ads.tiktok)]);
39
+ const snapshot = { from, to, days: Math.max(1, days), channels: [], errors: [] };
40
+ for (const [id, source] of sources) {
41
+ try {
42
+ const w = await source.window(app?.store ?? "app-store", app?.appId ?? "", from, to);
43
+ const channel = w ? channelFrom(id, w) : null;
44
+ if (channel)
45
+ snapshot.channels.push(channel);
46
+ if (w?.currency && !snapshot.currency)
47
+ snapshot.currency = w.currency;
48
+ }
49
+ catch (e) {
50
+ snapshot.errors.push({ channel: id, message: e.message });
51
+ }
52
+ }
53
+ return snapshot;
54
+ }
55
+ export function adsDir(root) {
56
+ return join(root, SPEC_DIR, "ads");
57
+ }
58
+ /** Write a snapshot to `appeeky/ads/<to>.yaml` and return the path. */
59
+ export async function saveAdsSnapshot(root, snapshot) {
60
+ await mkdir(adsDir(root), { recursive: true });
61
+ const file = join(adsDir(root), `${snapshot.to}.yaml`);
62
+ const { errors: _errors, ...kept } = snapshot;
63
+ await writeFile(file, YAML.stringify(kept, { lineWidth: 0 }));
64
+ return file;
65
+ }
66
+ export const CHANNEL_LABELS = {
67
+ "apple-search-ads": "Apple Search Ads",
68
+ meta: "Meta",
69
+ tiktok: "TikTok",
70
+ };
71
+ //# sourceMappingURL=ads.js.map
@@ -0,0 +1,47 @@
1
+ /**
2
+ * The specialist subagents and the co-founder review, shipped in the package's `agents/` folder
3
+ * and copied into a repo in the format each coding agent reads.
4
+ *
5
+ * - `appeeky-*.md`: read-only specialists (ASO, reviews, ads, releases, UGC, social), written in Claude Code's
6
+ * subagent format.
7
+ * - `cofounder.md`: the review that starts them, writes the brief and proposes one change. It is a
8
+ * command, so it runs in the main session, where subagents can be started.
9
+ */
10
+ export type AgentTarget = "claude" | "opencode";
11
+ export declare const AGENT_TARGETS: AgentTarget[];
12
+ export declare const COFOUNDER_COMMAND = "appeeky-cofounder";
13
+ /** The folder each target reads subagents and commands from. */
14
+ export declare const TARGET_DIRS: Record<AgentTarget, {
15
+ agents: string;
16
+ commands: string;
17
+ }>;
18
+ export interface AgentFile {
19
+ /** File name without extension, e.g. `appeeky-aso` or `cofounder`. */
20
+ name: string;
21
+ kind: "specialist" | "command";
22
+ meta: Record<string, unknown>;
23
+ body: string;
24
+ }
25
+ export interface InstalledFile {
26
+ path: string;
27
+ status: "written" | "unchanged" | "kept";
28
+ }
29
+ /** The `agents/` folder of the installed package (one level above `dist/` or `src/`). */
30
+ export declare function packagedAgentsDir(): string;
31
+ export declare function readAgentFiles(dir?: string): AgentFile[];
32
+ /** The path and content of one file for one target. */
33
+ export declare function renderAgentFile(file: AgentFile, target: AgentTarget): {
34
+ path: string;
35
+ content: string;
36
+ };
37
+ /**
38
+ * Write the specialists and the co-founder command into `root` for each target. A file that exists
39
+ * with other content is kept (it may hold the user's own edits) unless `force` is set.
40
+ */
41
+ export declare function installAgents(root: string, targets: AgentTarget[], opts?: {
42
+ force?: boolean;
43
+ dir?: string;
44
+ }): Promise<InstalledFile[]>;
45
+ /** Targets that already have the specialists in `root`. */
46
+ export declare function installedTargets(root: string): AgentTarget[];
47
+ export declare function parseTargets(value: string): AgentTarget[];
package/dist/agents.js ADDED
@@ -0,0 +1,88 @@
1
+ import { existsSync, readdirSync, readFileSync } from "node:fs";
2
+ import { mkdir, readFile, writeFile } from "node:fs/promises";
3
+ import { dirname, join } from "node:path";
4
+ import { fileURLToPath } from "node:url";
5
+ import { parse, stringify } from "yaml";
6
+ export const AGENT_TARGETS = ["claude", "opencode"];
7
+ export const COFOUNDER_COMMAND = "appeeky-cofounder";
8
+ /** The folder each target reads subagents and commands from. */
9
+ export const TARGET_DIRS = {
10
+ claude: { agents: ".claude/agents", commands: ".claude/commands" },
11
+ opencode: { agents: ".opencode/agent", commands: ".opencode/command" },
12
+ };
13
+ /** The `agents/` folder of the installed package (one level above `dist/` or `src/`). */
14
+ export function packagedAgentsDir() {
15
+ return join(dirname(fileURLToPath(import.meta.url)), "..", "agents");
16
+ }
17
+ function splitFrontmatter(text) {
18
+ const match = /^---\n([\s\S]*?)\n---\n?/.exec(text);
19
+ if (!match)
20
+ return { meta: {}, body: text };
21
+ return { meta: parse(match[1]) ?? {}, body: text.slice(match[0].length) };
22
+ }
23
+ export function readAgentFiles(dir = packagedAgentsDir()) {
24
+ return readdirSync(dir)
25
+ .filter((f) => f.endsWith(".md"))
26
+ .sort()
27
+ .map((f) => {
28
+ const name = f.slice(0, -3);
29
+ const { meta, body } = splitFrontmatter(readFileSync(join(dir, f), "utf8"));
30
+ return { name, kind: name === "cofounder" ? "command" : "specialist", meta, body };
31
+ });
32
+ }
33
+ function withFrontmatter(meta, body) {
34
+ return `---\n${stringify(meta, { lineWidth: 0 }).trimEnd()}\n---\n${body.startsWith("\n") ? body : `\n${body}`}`;
35
+ }
36
+ /** The path and content of one file for one target. */
37
+ export function renderAgentFile(file, target) {
38
+ const dirs = TARGET_DIRS[target];
39
+ if (file.kind === "command") {
40
+ return { path: `${dirs.commands}/${COFOUNDER_COMMAND}.md`, content: withFrontmatter({ description: file.meta.description }, file.body) };
41
+ }
42
+ if (target === "claude")
43
+ return { path: `${dirs.agents}/${file.name}.md`, content: withFrontmatter(file.meta, file.body) };
44
+ // opencode: the file name is the agent's name, `mode: subagent`, and tools are switched off by name.
45
+ return {
46
+ path: `${dirs.agents}/${file.name}.md`,
47
+ content: withFrontmatter({ description: file.meta.description, mode: "subagent", tools: { write: false, edit: false } }, file.body),
48
+ };
49
+ }
50
+ /**
51
+ * Write the specialists and the co-founder command into `root` for each target. A file that exists
52
+ * with other content is kept (it may hold the user's own edits) unless `force` is set.
53
+ */
54
+ export async function installAgents(root, targets, opts = {}) {
55
+ const files = readAgentFiles(opts.dir);
56
+ const out = [];
57
+ for (const target of targets) {
58
+ for (const file of files) {
59
+ const { path, content } = renderAgentFile(file, target);
60
+ const full = join(root, path);
61
+ const current = existsSync(full) ? await readFile(full, "utf8") : null;
62
+ if (current === content) {
63
+ out.push({ path, status: "unchanged" });
64
+ continue;
65
+ }
66
+ if (current !== null && !opts.force) {
67
+ out.push({ path, status: "kept" });
68
+ continue;
69
+ }
70
+ await mkdir(dirname(full), { recursive: true });
71
+ await writeFile(full, content);
72
+ out.push({ path, status: "written" });
73
+ }
74
+ }
75
+ return out;
76
+ }
77
+ /** Targets that already have the specialists in `root`. */
78
+ export function installedTargets(root) {
79
+ return AGENT_TARGETS.filter((t) => existsSync(join(root, TARGET_DIRS[t].agents, "appeeky-aso.md")));
80
+ }
81
+ export function parseTargets(value) {
82
+ const list = value === "all" ? AGENT_TARGETS : value.split(",").map((x) => x.trim());
83
+ const bad = list.filter((x) => !AGENT_TARGETS.includes(x));
84
+ if (bad.length)
85
+ throw new Error(`Unknown target: ${bad.join(", ")}. Use ${AGENT_TARGETS.join(", ")} or all.`);
86
+ return list;
87
+ }
88
+ //# sourceMappingURL=agents.js.map