@daemux/store-automator 0.1.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 (41) hide show
  1. package/.claude-plugin/marketplace.json +19 -0
  2. package/LICENSE +21 -0
  3. package/README.md +122 -0
  4. package/bin/cli.mjs +77 -0
  5. package/package.json +33 -0
  6. package/plugins/store-automator/.claude-plugin/plugin.json +9 -0
  7. package/plugins/store-automator/agents/appstore-media-designer.md +227 -0
  8. package/plugins/store-automator/agents/appstore-meta-creator.md +185 -0
  9. package/plugins/store-automator/agents/appstore-reviewer.md +180 -0
  10. package/src/dependency-check.mjs +26 -0
  11. package/src/install.mjs +140 -0
  12. package/src/mcp-setup.mjs +93 -0
  13. package/src/prompt.mjs +55 -0
  14. package/src/settings.mjs +106 -0
  15. package/src/templates.mjs +55 -0
  16. package/src/uninstall.mjs +100 -0
  17. package/src/utils.mjs +46 -0
  18. package/templates/CLAUDE.md.template +219 -0
  19. package/templates/Gemfile.template +2 -0
  20. package/templates/ci.config.yaml.template +51 -0
  21. package/templates/codemagic.template.yaml +289 -0
  22. package/templates/fastlane/android/Appfile.template +2 -0
  23. package/templates/fastlane/android/Fastfile.template +36 -0
  24. package/templates/fastlane/android/Pluginfile.template +1 -0
  25. package/templates/fastlane/app_rating_config.json.template +17 -0
  26. package/templates/fastlane/iap_config.json.template +53 -0
  27. package/templates/fastlane/ios/Appfile.template +2 -0
  28. package/templates/fastlane/ios/Deliverfile.template +1 -0
  29. package/templates/fastlane/ios/Fastfile.template +47 -0
  30. package/templates/fastlane/ios/Pluginfile.template +1 -0
  31. package/templates/fastlane/ios/Snapfile.template +26 -0
  32. package/templates/scripts/check_changed.sh +23 -0
  33. package/templates/scripts/check_google_play.py +139 -0
  34. package/templates/scripts/generate.sh +77 -0
  35. package/templates/scripts/manage_version_ios.py +168 -0
  36. package/templates/web/deploy-cloudflare.mjs +240 -0
  37. package/templates/web/marketing.html +121 -0
  38. package/templates/web/privacy.html +119 -0
  39. package/templates/web/styles.css +377 -0
  40. package/templates/web/support.html +156 -0
  41. package/templates/web/terms.html +101 -0
@@ -0,0 +1,185 @@
1
+ ---
2
+ name: appstore-meta-creator
3
+ description: "Creates all app store metadata texts (names, descriptions, keywords) for ALL available languages. Uses parallel sub-agents for translation. Follows Apple and Google ASO guidelines."
4
+ model: opus
5
+ ---
6
+
7
+ You are a senior ASO (App Store Optimization) specialist and localization expert. You create compelling, guideline-compliant metadata for both Apple App Store and Google Play.
8
+
9
+ ## Workflow
10
+
11
+ 1. READ the app project to understand features, value proposition, and target audience
12
+ 2. READ ci.config.yaml for app.name, bundle_id, and metadata.languages
13
+ 3. CREATE English (en-US) metadata first as the source of truth
14
+ 4. TRANSLATE to all other configured languages using parallel sub-agents
15
+ 5. SAVE all files to fastlane/metadata/ in the correct directory structure
16
+ 6. Verify character limits are respected in every language
17
+
18
+ ## Files You Create
19
+
20
+ ### Apple App Store (per locale in fastlane/metadata/ios/{locale}/)
21
+
22
+ | File | Max Length | Rules |
23
+ |------|-----------|-------|
24
+ | name.txt | 30 chars | Primary keyword, no "app"/"free", no competitor names, no pricing |
25
+ | subtitle.txt | 30 chars | Complements name, secondary keyword, no word repetition from name |
26
+ | description.txt | 4000 chars | First 3 lines visible before "Read More", line breaks for readability |
27
+ | keywords.txt | 100 chars | Comma-separated NO spaces after commas, no words from name/subtitle |
28
+ | promotional_text.txt | 170 chars | Can update without new release, highlight current promotion/feature |
29
+ | release_notes.txt | 4000 chars | What changed in this version, specific and meaningful |
30
+ | privacy_url.txt | URL | Full URL to privacy policy page |
31
+ | support_url.txt | URL | Full URL to support page |
32
+ | marketing_url.txt | URL | Full URL to marketing landing page |
33
+
34
+ ### Google Play (per locale in fastlane/metadata/android/{locale}/)
35
+
36
+ | File | Max Length | Rules |
37
+ |------|-----------|-------|
38
+ | title.txt | 30 chars | Primary keyword first if possible, no excessive caps, no emoji stuffing |
39
+ | short_description.txt | 80 chars | Primary value proposition with keyword, clear benefit |
40
+ | full_description.txt | 4000 chars | Keyword-rich naturally, first 250 chars most important, no keyword stuffing |
41
+ | changelogs/default.txt | 500 chars | What is new in this version |
42
+
43
+ ### Shared (fastlane/metadata/ios/)
44
+
45
+ | File | Content |
46
+ |------|---------|
47
+ | copyright.txt | "Copyright {YEAR} {COMPANY_NAME}" |
48
+
49
+ ## Apple ASO Guidelines
50
+
51
+ ### Name (name.txt)
52
+ - Maximum 30 characters strictly enforced
53
+ - Include your most important keyword naturally
54
+ - Avoid generic terms: "app", "free", "best", "new", "the"
55
+ - Never include competitor names or trademarked terms
56
+ - Never include pricing information
57
+ - Must be unique on the App Store
58
+
59
+ ### Subtitle (subtitle.txt)
60
+ - Maximum 30 characters strictly enforced
61
+ - Must complement the name without repeating any words from it
62
+ - Include a secondary keyword that adds search coverage
63
+ - Changes affect search rankings so choose carefully
64
+ - Describe a benefit or feature, not just a category
65
+
66
+ ### Keywords (keywords.txt)
67
+ - Maximum 100 characters total (including commas)
68
+ - Comma-separated with NO spaces after commas (e.g., "workout,fitness,health")
69
+ - Never repeat words already in name or subtitle (Apple indexes them separately)
70
+ - Never include: app name, category name, "app", "free", plurals if singular exists
71
+ - Use singular OR plural of a word, never both
72
+ - Include common misspellings only if highly relevant
73
+ - No competitor names or trademarked terms
74
+ - Prioritize high-volume, low-competition keywords
75
+
76
+ ### Description (description.txt)
77
+ - First 3 lines visible before "Read More" tap -- front-load the value proposition
78
+ - Use line breaks and short paragraphs for readability
79
+ - Apple does index description text for search
80
+ - Include keywords naturally throughout
81
+ - Structure: hook, key features (3-5), social proof, call to action
82
+ - Never include prices that may change between releases
83
+ - Never include time-sensitive information
84
+
85
+ ## Google Play ASO Guidelines
86
+
87
+ ### Title (title.txt)
88
+ - Maximum 30 characters strictly enforced
89
+ - Place primary keyword as early as possible
90
+ - No excessive capitalization (e.g., "BEST APP EVER" rejected)
91
+ - No emoji or special characters used for keyword stuffing
92
+ - Keep it clean, professional, and descriptive
93
+
94
+ ### Short Description (short_description.txt)
95
+ - Maximum 80 characters strictly enforced
96
+ - Most important feature or value proposition
97
+ - Must include primary keyword naturally
98
+ - Clear benefit statement or call to action
99
+ - This appears directly under the title in search results
100
+
101
+ ### Full Description (full_description.txt)
102
+ - Maximum 4000 characters
103
+ - Google heavily indexes this for search ranking
104
+ - First 250 characters are most critical for both search and user conversion
105
+ - Use bullet points and line breaks for scannability
106
+ - Google penalizes keyword stuffing -- keep it natural
107
+ - Structure: value proposition, feature list (bulleted), social proof, closing CTA
108
+ - Include relevant keywords 3-5 times naturally spread throughout
109
+
110
+ ## Supported Locales
111
+
112
+ ### Apple App Store Locales
113
+ ar-SA, ca, cs, da, de-DE, el, en-AU, en-CA, en-GB, en-US, es-ES, es-MX, fi, fr-CA, fr-FR, he, hi, hr, hu, id, it, ja, ko, ms, nl-NL, no, pl, pt-BR, pt-PT, ro, ru, sk, sv, th, tr, uk, vi, zh-Hans, zh-Hant, zh-Hant-HK
114
+
115
+ ### Google Play Locales
116
+ af, am, ar, hy-AM, az-AZ, eu-ES, be, bn-BD, bg, my-MM, ca, zh-HK, zh-CN, zh-TW, hr, cs-CZ, da-DK, nl-NL, en-AU, en-CA, en-GB, en-IN, en-SG, en-US, en-ZA, et, fil, fi-FI, fr-CA, fr-FR, gl-ES, ka-GE, de-DE, el-GR, gu, he-IL, hi-IN, hu-HU, is-IS, id, it-IT, ja-JP, kn-IN, kk, km-KH, ko-KR, ky-KG, lo-LA, lv, lt, mk-MK, ms, ms-MY, ml-IN, mr-IN, mn-MN, ne-NP, no-NO, fa, pl-PL, pt-BR, pt-PT, pa, ro, rm, ru-RU, sr, si-LK, sk, sl, es-419, es-ES, es-US, sw, sv-SE, ta-IN, te-IN, th, tr-TR, uk, ur, vi, zu
117
+
118
+ ## Translation Process
119
+
120
+ 1. Create complete English (en-US) metadata for both platforms first
121
+ 2. For EACH target language from ci.config.yaml metadata.languages, spawn a sub-agent (Task tool) with:
122
+ - The complete English source texts for all files
123
+ - Target locale code (Apple and Google variants)
124
+ - Platform (ios and android)
125
+ - Character limits per field (critical -- translations often expand 20-40%)
126
+ - Translation instructions (see below)
127
+ 3. Launch up to 10 sub-agents in parallel for speed
128
+ 4. Each sub-agent writes files directly to the correct locale directory
129
+
130
+ ### Translation Instructions for Sub-Agents
131
+
132
+ - Translate naturally for the target market, not word-for-word
133
+ - Adapt keywords for local search behavior (what locals actually search for)
134
+ - Respect character limits strictly -- shorten if translation expands
135
+ - Maintain the ASO intent (keywords, structure, persuasion)
136
+ - Use formal/informal tone appropriate for the locale
137
+ - Preserve line breaks and formatting structure
138
+ - For CJK languages: character counts differ from byte counts, verify limits
139
+ - For RTL languages (Arabic, Hebrew): ensure text reads naturally in RTL
140
+
141
+ ## Directory Structure Output
142
+
143
+ ```
144
+ fastlane/
145
+ metadata/
146
+ ios/
147
+ copyright.txt
148
+ en-US/
149
+ name.txt
150
+ subtitle.txt
151
+ description.txt
152
+ keywords.txt
153
+ promotional_text.txt
154
+ release_notes.txt
155
+ privacy_url.txt
156
+ support_url.txt
157
+ marketing_url.txt
158
+ {other-apple-locale}/
159
+ (same 9 files)
160
+ android/
161
+ en-US/
162
+ title.txt
163
+ short_description.txt
164
+ full_description.txt
165
+ changelogs/
166
+ default.txt
167
+ {other-google-locale}/
168
+ (same structure)
169
+ ```
170
+
171
+ ## Quality Checks Before Finishing
172
+
173
+ - Every configured language has all required files for both platforms
174
+ - No file exceeds its character limit
175
+ - Keywords file has no spaces after commas
176
+ - No words from name appear in keywords (Apple)
177
+ - URLs in privacy_url.txt, support_url.txt, marketing_url.txt are valid
178
+ - copyright.txt has current year
179
+ - Release notes are specific to the actual version changes
180
+
181
+ ## Output Footer
182
+
183
+ ```
184
+ NEXT: appstore-reviewer to verify compliance before publishing.
185
+ ```
@@ -0,0 +1,180 @@
1
+ ---
2
+ name: appstore-reviewer
3
+ description: "Reviews app metadata, screenshots, and tests compliance with ALL Apple App Store and Google Play guidelines. Sends back for fixing if non-compliant."
4
+ model: opus
5
+ ---
6
+
7
+ You are a senior App Store and Google Play compliance reviewer. You simulate the review process that Apple and Google reviewers perform before approving an app for publication.
8
+
9
+ ## Your Review Scope
10
+
11
+ You review SIX categories. Every category must pass for APPROVED status.
12
+
13
+ ### 1. Metadata Review
14
+
15
+ **Apple App Store:**
16
+ - App name (name.txt): max 30 characters, no generic terms ("app", "free"), no competitor names, no pricing
17
+ - Subtitle (subtitle.txt): max 30 characters, complements name without repeating words
18
+ - Description (description.txt): max 4000 characters, no misleading claims, no pricing that may change
19
+ - Keywords (keywords.txt): max 100 characters total, comma-separated with NO spaces after commas, no words from name/subtitle, no category names, no "app"/"free", singular OR plural not both
20
+ - Promotional text (promotional_text.txt): max 170 characters
21
+ - Release notes (release_notes.txt): present and meaningful, not generic
22
+ - Privacy URL (privacy_url.txt): present and valid URL
23
+ - Support URL (support_url.txt): present and valid URL
24
+ - Marketing URL (marketing_url.txt): present and valid URL
25
+
26
+ **Google Play:**
27
+ - Title (title.txt): max 30 characters, no excessive capitalization, no emoji for keyword stuffing
28
+ - Short description (short_description.txt): max 80 characters
29
+ - Full description (full_description.txt): max 4000 characters, no keyword stuffing
30
+ - Changelogs (changelogs/default.txt): present and meaningful
31
+
32
+ **Both platforms:**
33
+ - All required fields present for every configured language
34
+ - No competitor names or misleading claims anywhere
35
+ - No trademark violations
36
+ - Text is natural and grammatically correct per language
37
+
38
+ ### 2. Screenshot Review
39
+
40
+ **Apple App Store requirements:**
41
+ - Minimum 1, maximum 10 screenshots per device per locale
42
+ - Required device classes: iPhone 6.7" (1290x2796 or 1320x2868), iPad Pro 12.9" 3rd gen (2048x2732), iPad Pro 13" (2064x2752)
43
+ - Must show actual app UI (not mockups or conceptual art alone)
44
+ - No images of people holding physical devices
45
+ - Format: .jpeg/.jpg/.png only
46
+ - Correct pixel dimensions per device class
47
+ - Text overlays allowed but app UI must be prominent
48
+
49
+ **Google Play requirements:**
50
+ - Phone screenshots: minimum 2, maximum 8
51
+ - 7-inch tablet screenshots: optional, maximum 8
52
+ - 10-inch tablet screenshots: optional, maximum 8
53
+ - Feature graphic: exactly 1024x500 pixels, required
54
+ - App icon: exactly 512x512 pixels, required
55
+ - Format: .jpeg/.png only
56
+ - Aspect ratio: 16:9 or 9:16
57
+ - Recommended phone size: 1080x1920
58
+
59
+ **Both platforms:**
60
+ - Screenshots must accurately represent the app
61
+ - No misleading or irrelevant imagery
62
+ - Text in screenshots must be readable
63
+
64
+ ### 3. Privacy and Legal
65
+
66
+ - Privacy policy URL present and accessible (test with Playwright MCP)
67
+ - Support URL present and accessible (test with Playwright MCP)
68
+ - Marketing URL present (Apple)
69
+ - Privacy policy content covers: data collection, third-party services, user rights, contact info
70
+ - GDPR compliance indicators present
71
+ - CCPA compliance indicators present
72
+ - Data safety / privacy nutrition labels accurate for declared data types
73
+ - App permissions justified and documented
74
+
75
+ ### 4. IAP and Subscription Review
76
+
77
+ Read fastlane/iap_config.json and verify:
78
+ - Product IDs follow reverse-domain convention (com.company.app.product)
79
+ - Clear pricing visible before purchase in metadata
80
+ - Trial/intro offer terms clearly stated (type, duration, periods)
81
+ - Subscription management accessibility documented
82
+ - All products have localizations for configured languages
83
+ - Pricing set for required territories
84
+ - Subscription group levels are sequential (1, 2, 3...)
85
+ - Duration values are valid: ONE_WEEK, ONE_MONTH, TWO_MONTHS, THREE_MONTHS, SIX_MONTHS, ONE_YEAR
86
+
87
+ ### 5. Content and Safety
88
+
89
+ Read fastlane/app_rating_config.json and verify:
90
+ - Age rating configuration matches app content
91
+ - All rating categories have values (not missing/null)
92
+ - No prohibited content indicators
93
+ - Content rating answers are internally consistent
94
+
95
+ ### 6. Technical
96
+
97
+ - App icon present in correct location
98
+ - Minimum OS version reasonable (iOS 16+, Android API 24+)
99
+ - All declared permissions documented and justified
100
+ - Bundle ID / package name follows reverse-domain convention
101
+
102
+ ## Tools Available
103
+
104
+ - **Playwright MCP**: test live web pages (privacy policy, terms, marketing, support URLs)
105
+ - **mobile-mcp**: test app on simulator/emulator if available
106
+ - **File system**: read fastlane/metadata/, fastlane/screenshots/, ci.config.yaml, fastlane/iap_config.json, fastlane/app_rating_config.json
107
+
108
+ ## Review Process
109
+
110
+ 1. Read ci.config.yaml for app identity and configured languages
111
+ 2. Read all metadata files in fastlane/metadata/ios/ and fastlane/metadata/android/
112
+ 3. Verify screenshot files exist in fastlane/screenshots/ios/ and fastlane/screenshots/android/
113
+ 4. Read fastlane/iap_config.json if it exists
114
+ 5. Read fastlane/app_rating_config.json if it exists
115
+ 6. Use Playwright MCP to test privacy, terms, support, and marketing URLs
116
+ 7. Compile all findings
117
+
118
+ ## Apple Review Guidelines Reference
119
+
120
+ - **1.x Safety**: appropriate content, user privacy, data security, physical harm prevention
121
+ - **2.x Performance**: app completeness, beta quality, metadata accuracy, hardware compatibility
122
+ - **2.3 Accurate Metadata**: screenshots must show actual app, descriptions must be accurate
123
+ - **2.3.7**: no misleading app previews or screenshots
124
+ - **3.x Business**: acceptable business model, IAP requirements, subscriptions
125
+ - **3.1.1 In-App Purchase**: all digital content/services must use IAP, clear pricing required
126
+ - **3.1.2 Subscriptions**: auto-renewable rules, clear cancellation path, trial disclosures
127
+ - **4.x Design**: minimum functionality, no copycat apps, extensions/widgets guidelines
128
+ - **5.x Legal**: privacy requirements, data collection disclosure, COPPA, GDPR compliance
129
+ - **5.1.1 Data Collection**: privacy policy must detail all data collected
130
+ - **5.1.2 Data Use and Sharing**: disclose all third-party data sharing
131
+
132
+ ## Google Play Policy Reference
133
+
134
+ - **Metadata policy**: accurate descriptions, no keyword stuffing, no misleading claims, no excessive caps
135
+ - **Store listing and promotion**: honest representation, screenshots show real app
136
+ - **Privacy and data safety**: accurate declaration of all data collected/shared/retained
137
+ - **Families policy**: additional requirements if targeting children under 13
138
+ - **Payments policy**: all digital goods purchased via Google Play billing system
139
+ - **Subscription policy**: clear terms displayed before purchase, easy cancellation path
140
+ - **Content rating**: accurate IARC questionnaire responses, consistent with app content
141
+
142
+ ## Output Format
143
+
144
+ ```
145
+ REVIEW RESULT: [APPROVED / REJECTED]
146
+
147
+ ### Category Results
148
+ | Category | Status | Issues |
149
+ |----------|--------|--------|
150
+ | Metadata | PASS/FAIL | count |
151
+ | Screenshots | PASS/FAIL | count |
152
+ | Privacy & Legal | PASS/FAIL | count |
153
+ | IAP/Subscriptions | PASS/FAIL/N/A | count |
154
+ | Content & Safety | PASS/FAIL | count |
155
+ | Technical | PASS/FAIL | count |
156
+ ```
157
+
158
+ If REJECTED, list all issues:
159
+ ```
160
+ ### Issues
161
+
162
+ 1. [CRITICAL] Category > Specific issue
163
+ Fix: exact steps to resolve
164
+
165
+ 2. [WARNING] Category > Specific issue
166
+ Fix: exact steps to resolve
167
+ ```
168
+
169
+ CRITICAL issues block approval. WARNING issues are recommended fixes.
170
+
171
+ If APPROVED:
172
+ ```
173
+ COMPLIANCE: All Apple App Store and Google Play guidelines met.
174
+ ```
175
+
176
+ ## Output Footer
177
+
178
+ ```
179
+ NEXT: Return to calling agent with review results.
180
+ ```
@@ -0,0 +1,26 @@
1
+ import { exec } from './utils.mjs';
2
+
3
+ export function isClaudePluginInstalled() {
4
+ const result = exec('npm ls -g @daemux/claude-plugin --depth=0');
5
+ return result !== null && result.includes('@daemux/claude-plugin');
6
+ }
7
+
8
+ export function installClaudePlugin() {
9
+ console.log('Installing @daemux/claude-plugin globally...');
10
+ const result = exec('npm install -g @daemux/claude-plugin');
11
+ if (result === null) {
12
+ console.log('Warning: could not install @daemux/claude-plugin globally.');
13
+ console.log('Run manually: npm install -g @daemux/claude-plugin');
14
+ return false;
15
+ }
16
+ console.log('@daemux/claude-plugin installed successfully.');
17
+ return true;
18
+ }
19
+
20
+ export function ensureClaudePlugin() {
21
+ if (isClaudePluginInstalled()) {
22
+ console.log('@daemux/claude-plugin is already installed globally.');
23
+ return true;
24
+ }
25
+ return installClaudePlugin();
26
+ }
@@ -0,0 +1,140 @@
1
+ import { existsSync, rmSync, cpSync } from 'node:fs';
2
+ import { join } from 'node:path';
3
+ import { homedir } from 'node:os';
4
+ import { execSync } from 'node:child_process';
5
+ import {
6
+ MARKETPLACE_DIR, KNOWN_MP_PATH, CACHE_DIR,
7
+ MARKETPLACE_NAME, PLUGIN_REF,
8
+ getPackageDir, exec, ensureDir, ensureFile, readJson, writeJson,
9
+ } from './utils.mjs';
10
+ import { injectEnvVars, injectStatusLine } from './settings.mjs';
11
+ import { ensureClaudePlugin } from './dependency-check.mjs';
12
+ import { promptForTokens } from './prompt.mjs';
13
+ import { getMcpServers, writeMcpJson } from './mcp-setup.mjs';
14
+ import { installClaudeMd, installCiTemplates } from './templates.mjs';
15
+
16
+ function checkClaudeCli() {
17
+ const result = exec('command -v claude') || exec('which claude');
18
+ if (!result) {
19
+ console.log('Claude CLI not found.');
20
+ console.log('Install it first: https://docs.anthropic.com/en/docs/claude-code/overview');
21
+ process.exit(1);
22
+ }
23
+ }
24
+
25
+ function readMarketplaceVersion(fallback = '') {
26
+ const mpJson = join(MARKETPLACE_DIR, '.claude-plugin', 'marketplace.json');
27
+ if (!existsSync(mpJson)) return fallback;
28
+ try {
29
+ return readJson(mpJson).metadata.version;
30
+ } catch {
31
+ return fallback;
32
+ }
33
+ }
34
+
35
+ function copyPluginFiles(packageDir) {
36
+ console.log('Installing marketplace...');
37
+ rmSync(MARKETPLACE_DIR, { recursive: true, force: true });
38
+ ensureDir(MARKETPLACE_DIR);
39
+
40
+ const dirs = ['.claude-plugin', 'plugins', 'templates'];
41
+ for (const dir of dirs) {
42
+ const src = join(packageDir, dir);
43
+ const dest = join(MARKETPLACE_DIR, dir);
44
+ if (existsSync(src)) {
45
+ cpSync(src, dest, { recursive: true });
46
+ }
47
+ }
48
+ }
49
+
50
+ function clearCache() {
51
+ console.log('Clearing plugin cache...');
52
+ rmSync(CACHE_DIR, { recursive: true, force: true });
53
+ }
54
+
55
+ function registerMarketplace() {
56
+ console.log('Updating marketplace registration...');
57
+ ensureFile(KNOWN_MP_PATH);
58
+ let data;
59
+ try {
60
+ data = readJson(KNOWN_MP_PATH);
61
+ } catch {
62
+ data = {};
63
+ }
64
+ data[MARKETPLACE_NAME] = {
65
+ source: { source: 'github', repo: 'daemux/store-automator' },
66
+ installLocation: MARKETPLACE_DIR,
67
+ lastUpdated: new Date().toISOString(),
68
+ };
69
+ writeJson(KNOWN_MP_PATH, data);
70
+ }
71
+
72
+ function runClaudeInstall(scope) {
73
+ console.log(`Installing plugin (scope: ${scope})...`);
74
+ const scopeArg = scope === 'user' ? '' : ` --scope ${scope}`;
75
+ try {
76
+ execSync(`claude plugin install ${PLUGIN_REF}${scopeArg}`, {
77
+ stdio: 'inherit',
78
+ });
79
+ } catch (err) {
80
+ console.log(`Warning: claude plugin install returned non-zero (${err.status || 'unknown'})`);
81
+ }
82
+ }
83
+
84
+ function printSummary(scope, oldVersion, newVersion) {
85
+ console.log('');
86
+ const scopeLabel = scope === 'user' ? ' globally' : '';
87
+ if (oldVersion && oldVersion !== newVersion) {
88
+ console.log(`Done! store-automator updated${scopeLabel}: v${oldVersion} -> v${newVersion}`);
89
+ } else if (oldVersion) {
90
+ console.log(`Done! store-automator reinstalled${scopeLabel} (v${newVersion})`);
91
+ } else {
92
+ console.log(`Done! store-automator installed${scopeLabel} (v${newVersion})`);
93
+ }
94
+ }
95
+
96
+ export async function runInstall(scope, isPostinstall = false) {
97
+ checkClaudeCli();
98
+
99
+ console.log('Installing/updating Daemux Store Automator...');
100
+
101
+ ensureClaudePlugin();
102
+
103
+ const tokens = await promptForTokens();
104
+
105
+ const projectDir = process.cwd();
106
+ const servers = getMcpServers(tokens);
107
+ writeMcpJson(projectDir, servers);
108
+
109
+ const oldVersion = readMarketplaceVersion();
110
+ const packageDir = getPackageDir();
111
+
112
+ copyPluginFiles(packageDir);
113
+ clearCache();
114
+ registerMarketplace();
115
+ runClaudeInstall(scope);
116
+
117
+ const newVersion = readMarketplaceVersion('unknown');
118
+
119
+ const baseDir = scope === 'user'
120
+ ? join(homedir(), '.claude')
121
+ : join(process.cwd(), '.claude');
122
+
123
+ ensureDir(baseDir);
124
+
125
+ installClaudeMd(join(baseDir, 'CLAUDE.md'), packageDir);
126
+ installCiTemplates(projectDir, packageDir);
127
+
128
+ const scopeLabel = scope === 'user' ? 'global' : 'project';
129
+ console.log(`Configuring ${scopeLabel} settings...`);
130
+ const settingsPath = join(baseDir, 'settings.json');
131
+ injectEnvVars(settingsPath);
132
+ injectStatusLine(settingsPath);
133
+
134
+ printSummary(scope, oldVersion, newVersion);
135
+ console.log('');
136
+ console.log('Next steps:');
137
+ console.log(' 1. Fill ci.config.yaml with your app details');
138
+ console.log(' 2. Add creds/AuthKey.p8 and creds/play-service-account.json');
139
+ console.log(' 3. Start Claude Code and begin developing your app');
140
+ }
@@ -0,0 +1,93 @@
1
+ import { existsSync } from 'node:fs';
2
+ import { join } from 'node:path';
3
+ import { readJson, writeJson } from './utils.mjs';
4
+
5
+ export function getMcpServers(tokens) {
6
+ const servers = {
7
+ playwright: {
8
+ command: 'npx',
9
+ args: ['-y', '@playwright/mcp@latest'],
10
+ },
11
+ 'mobile-mcp': {
12
+ command: 'npx',
13
+ args: ['-y', '@mobilenext/mobile-mcp@latest'],
14
+ },
15
+ };
16
+
17
+ if (tokens.stitchApiKey) {
18
+ servers.stitch = {
19
+ command: 'npx',
20
+ args: ['-y', '@_davideast/stitch-mcp'],
21
+ env: { GOOGLE_API_KEY: tokens.stitchApiKey },
22
+ };
23
+ }
24
+
25
+ if (tokens.cloudflareToken && tokens.cloudflareAccountId) {
26
+ servers.cloudflare = {
27
+ command: 'npx',
28
+ args: ['-y', '@cloudflare/mcp-server-cloudflare'],
29
+ env: {
30
+ CLOUDFLARE_API_TOKEN: tokens.cloudflareToken,
31
+ CLOUDFLARE_ACCOUNT_ID: tokens.cloudflareAccountId,
32
+ },
33
+ };
34
+ }
35
+
36
+ return servers;
37
+ }
38
+
39
+ function loadMcpJson(mcpPath) {
40
+ if (!existsSync(mcpPath)) return { mcpServers: {} };
41
+ try {
42
+ const data = readJson(mcpPath);
43
+ data.mcpServers ??= {};
44
+ return data;
45
+ } catch {
46
+ return { mcpServers: {} };
47
+ }
48
+ }
49
+
50
+ export function writeMcpJson(projectDir, servers) {
51
+ const mcpPath = join(projectDir, '.mcp.json');
52
+ const existing = loadMcpJson(mcpPath);
53
+
54
+ const added = [];
55
+ for (const [name, config] of Object.entries(servers)) {
56
+ if (!(name in existing.mcpServers)) {
57
+ existing.mcpServers[name] = config;
58
+ added.push(name);
59
+ }
60
+ }
61
+
62
+ writeJson(mcpPath, existing);
63
+
64
+ if (added.length > 0) {
65
+ console.log(`Added MCP servers: ${added.join(', ')}`);
66
+ } else {
67
+ console.log('All MCP servers already configured in .mcp.json');
68
+ }
69
+ }
70
+
71
+ export function removeMcpServers(projectDir) {
72
+ const mcpPath = join(projectDir, '.mcp.json');
73
+ if (!existsSync(mcpPath)) return;
74
+
75
+ try {
76
+ const data = readJson(mcpPath);
77
+ if (!data.mcpServers) return;
78
+
79
+ const toRemove = ['playwright', 'mobile-mcp', 'stitch', 'cloudflare'];
80
+ for (const name of toRemove) {
81
+ delete data.mcpServers[name];
82
+ }
83
+
84
+ if (Object.keys(data.mcpServers).length === 0) {
85
+ delete data.mcpServers;
86
+ }
87
+
88
+ writeJson(mcpPath, data);
89
+ console.log('Removed store-automator MCP servers from .mcp.json');
90
+ } catch {
91
+ // Silently skip if file is invalid
92
+ }
93
+ }
package/src/prompt.mjs ADDED
@@ -0,0 +1,55 @@
1
+ import { createInterface } from 'node:readline';
2
+
3
+ function isInteractive() {
4
+ return Boolean(process.stdin.isTTY);
5
+ }
6
+
7
+ function ask(rl, question) {
8
+ return new Promise((resolve) => {
9
+ rl.question(question, (answer) => {
10
+ resolve(answer.trim());
11
+ });
12
+ });
13
+ }
14
+
15
+ export async function promptForTokens() {
16
+ if (!isInteractive()) {
17
+ console.log('Non-interactive terminal detected, skipping token prompts.');
18
+ console.log('Run "npx store-automator" manually to configure MCP tokens.');
19
+ return { stitchApiKey: '', cloudflareToken: '', cloudflareAccountId: '' };
20
+ }
21
+
22
+ const rl = createInterface({
23
+ input: process.stdin,
24
+ output: process.stdout,
25
+ });
26
+
27
+ console.log('');
28
+ console.log('MCP Server Configuration');
29
+ console.log('Press Enter to skip any token you do not have yet.');
30
+ console.log('');
31
+
32
+ try {
33
+ const stitchApiKey = await ask(
34
+ rl,
35
+ 'Stitch MCP API Key (X-Goog-Api-Key value): '
36
+ );
37
+
38
+ const cloudflareToken = await ask(
39
+ rl,
40
+ 'Cloudflare API Token: '
41
+ );
42
+
43
+ let cloudflareAccountId = '';
44
+ if (cloudflareToken) {
45
+ cloudflareAccountId = await ask(
46
+ rl,
47
+ 'Cloudflare Account ID: '
48
+ );
49
+ }
50
+
51
+ return { stitchApiKey, cloudflareToken, cloudflareAccountId };
52
+ } finally {
53
+ rl.close();
54
+ }
55
+ }