@daemux/store-automator 0.10.62 → 0.10.63

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.
@@ -5,14 +5,14 @@
5
5
  },
6
6
  "metadata": {
7
7
  "description": "App Store & Google Play automation for Flutter apps",
8
- "version": "0.10.62"
8
+ "version": "0.10.63"
9
9
  },
10
10
  "plugins": [
11
11
  {
12
12
  "name": "store-automator",
13
13
  "source": "./plugins/store-automator",
14
14
  "description": "3 agents for app store publishing: reviewer, meta-creator, media-designer",
15
- "version": "0.10.62",
15
+ "version": "0.10.63",
16
16
  "keywords": [
17
17
  "flutter",
18
18
  "app-store",
package/README.md CHANGED
@@ -20,30 +20,46 @@ Plus CI/CD templates for GitHub Actions, Fastlane, web pages, and scripts.
20
20
 
21
21
  ## Installation
22
22
 
23
+ **Project (default):** Full install into your Flutter project -- agents, CI/CD templates, MCP servers, and interactive setup:
24
+
23
25
  ```bash
24
26
  cd your-flutter-project
25
- npm install @daemux/store-automator@latest
27
+ npx --yes @daemux/store-automator
26
28
  ```
27
29
 
28
- The postinstall script runs an interactive setup with five sections:
30
+ The installer runs an interactive setup with five sections:
29
31
 
30
32
  1. **App Identity** -- App name, bundle ID, package name, SKU, Apple ID
31
- 2. **Credentials** -- Guided steps for App Store Connect API key, Google Play service account, Android keystore, and Match code signing
33
+ 2. **Credentials** -- App Store Connect API key, Google Play service account, Android keystore, Match code signing
32
34
  3. **Store Settings** -- iOS categories/pricing, Android track/rollout, metadata languages
33
35
  4. **Web Settings** -- Domain, colors, company info, legal jurisdiction
34
- 5. **MCP Tokens** -- Stitch and Cloudflare API keys (optional, press Enter to skip)
35
-
36
- All values are written to `ci.config.yaml`. The installer also:
36
+ 5. **MCP Tokens** -- Stitch and Cloudflare API keys (optional)
37
37
 
38
- - Configures `.mcp.json` with MCP servers (Playwright, mobile-mcp, Stitch, Cloudflare)
39
- - Installs `.claude/CLAUDE.md` with your app name and agent configurations
40
- - Copies CI/CD templates (Fastlane, scripts, web pages, GitHub Actions)
41
- - Configures `.claude/settings.json` with required env vars
42
- - Runs post-install guides for GitHub repo setup, secrets, and Firebase
38
+ All values are written to `ci.config.yaml`. The installer also configures `.mcp.json`, installs `.claude/CLAUDE.md`, copies CI/CD templates, and configures `.claude/settings.json`.
43
39
 
44
40
  Re-running the installer reads existing `ci.config.yaml` values as defaults, so you can update individual fields without re-entering everything.
45
41
 
46
- ## After Installation
42
+ **Global (agents only):** Install the plugin into `~/.claude` for use across all projects, without touching the current directory:
43
+
44
+ ```bash
45
+ npx --yes @daemux/store-automator --global
46
+ ```
47
+
48
+ Global install:
49
+
50
+ - Registers the marketplace and installs the plugin at user scope
51
+ - Writes `~/.claude/CLAUDE.md` with agent configurations
52
+ - Adds required env vars and statusLine to `~/.claude/settings.json`
53
+
54
+ Global install does NOT:
55
+
56
+ - Create `ci.config.yaml`, `fastlane/`, `scripts/`, `web/`, `Gemfile`, `.github/workflows/`, or any other project files
57
+ - Write or modify `.mcp.json` in the current directory
58
+ - Run any interactive prompts or post-install guides
59
+
60
+ Use project install for any Flutter app you are actually publishing. Global install is for agent-only access from arbitrary directories.
61
+
62
+ ## After Installation _(project scope only)_
47
63
 
48
64
  1. Add any credential files not configured during the guided setup:
49
65
  - `creds/AuthKey.p8` -- Apple App Store Connect API key
@@ -52,7 +68,7 @@ Re-running the installer reads existing `ci.config.yaml` values as defaults, so
52
68
  2. Verify `ci.config.yaml` has all required values filled in
53
69
  3. Start Claude Code and use the agents
54
70
 
55
- ## Manual Setup
71
+ ## Manual Setup _(project scope only)_
56
72
 
57
73
  If postinstall was skipped (CI environment), run manually:
58
74
 
@@ -240,19 +256,18 @@ web:
240
256
  google_play_url: "" # Filled after first Android publish
241
257
  ```
242
258
 
243
- ## Usage
259
+ ## Uninstall
244
260
 
245
- ### Global Install
261
+ **Project uninstall:** Removes CI templates, `ci.config.yaml`, `.mcp.json` entries, `.claude/CLAUDE.md`, and settings from the current project. Marketplace files remain in `~/.claude/plugins/`.
246
262
 
247
263
  ```bash
248
- npx @daemux/store-automator -g
264
+ npx --yes @daemux/store-automator --uninstall
249
265
  ```
250
266
 
251
- ### Uninstall
267
+ **Global uninstall:** Removes the plugin from `~/.claude` and clears the marketplace. Does NOT touch the current project's `ci.config.yaml`, CI templates, or `.mcp.json`.
252
268
 
253
269
  ```bash
254
- npx @daemux/store-automator -u # project scope
255
- npx @daemux/store-automator -g -u # global scope
270
+ npx --yes @daemux/store-automator --global --uninstall
256
271
  ```
257
272
 
258
273
  ## Agents
@@ -269,7 +284,7 @@ Designs complete app UI screens, creates ASO-optimized store screenshots for all
269
284
 
270
285
  Reviews all metadata, screenshots, privacy policy, and IAP configuration against Apple and Google guidelines. Returns APPROVED or REJECTED with specific issues.
271
286
 
272
- ## CI/CD Templates
287
+ ## CI/CD Templates _(project scope only)_
273
288
 
274
289
  The package installs these templates to your project:
275
290
 
@@ -282,7 +297,7 @@ The package installs these templates to your project:
282
297
  | `web/` | Marketing, privacy, terms, and support page templates |
283
298
  | `Gemfile` | Ruby gems for Fastlane |
284
299
 
285
- ## Workflow
300
+ ## Workflow _(project scope only)_
286
301
 
287
302
  1. Install the package (interactive setup fills `ci.config.yaml`)
288
303
  2. Add any remaining credential files
@@ -291,7 +306,7 @@ The package installs these templates to your project:
291
306
  5. Use `appstore-reviewer` to verify compliance
292
307
  6. Push to GitHub -- GitHub Actions builds and publishes automatically
293
308
 
294
- ## MCP Servers
309
+ ## MCP Servers _(project scope only)_
295
310
 
296
311
  The package configures these MCP servers in `.mcp.json`:
297
312
 
@@ -302,7 +317,7 @@ The package configures these MCP servers in `.mcp.json`:
302
317
  | stitch | AI design tool for screenshot generation | `STITCH_API_KEY` |
303
318
  | cloudflare | Cloudflare Pages deployment | `CLOUDFLARE_API_TOKEN` + Account ID |
304
319
 
305
- ## Idempotency
320
+ ## Idempotency _(project scope only)_
306
321
 
307
322
  The installer is idempotent. Re-running it reads existing values from `ci.config.yaml` as defaults for each prompt. This means you can:
308
323
 
package/bin/cli.mjs CHANGED
@@ -9,8 +9,6 @@ const __filename = fileURLToPath(import.meta.url);
9
9
  const __dirname = dirname(__filename);
10
10
  const pkg = JSON.parse(readFileSync(join(__dirname, '..', 'package.json'), 'utf8'));
11
11
 
12
- const notifier = updateNotifier({ pkg });
13
-
14
12
  const args = process.argv.slice(2);
15
13
  let scope = 'project';
16
14
  let action = 'install';
@@ -146,6 +144,13 @@ Examples:
146
144
  }
147
145
  }
148
146
 
147
+ if (scope === 'user' && cliTokens.githubActions) {
148
+ console.error('Error: --github-actions and --global are mutually exclusive.');
149
+ console.error(' --global targets ~/.claude (no CI templates).');
150
+ console.error(' --github-actions targets a project repo. Pick one.');
151
+ process.exit(1);
152
+ }
153
+
149
154
  if (cliTokens.githubActions) {
150
155
  const missing = [];
151
156
  if (!cliTokens.matchDeployKey) missing.push('--match-deploy-key');
@@ -163,7 +168,9 @@ if (cliTokens.githubActions) {
163
168
  }
164
169
  }
165
170
 
166
- notifier.notify();
171
+ if (!isPostinstall) {
172
+ updateNotifier({ pkg }).notify();
173
+ }
167
174
 
168
175
  try {
169
176
  if (action === 'uninstall') {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@daemux/store-automator",
3
- "version": "0.10.62",
3
+ "version": "0.10.63",
4
4
  "description": "Full App Store & Google Play automation for Flutter apps with Claude Code agents",
5
5
  "type": "module",
6
6
  "bin": {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "store-automator",
3
- "version": "0.10.62",
3
+ "version": "0.10.63",
4
4
  "description": "App Store & Google Play automation agents for Flutter app publishing",
5
5
  "author": {
6
6
  "name": "Daemux"
package/src/install.mjs CHANGED
@@ -152,7 +152,39 @@ function printNextSteps(prompted) {
152
152
  }
153
153
  }
154
154
 
155
+ async function withReadline(fn) {
156
+ const { createInterface } = await import('node:readline');
157
+ const rl = createInterface({ input: process.stdin, output: process.stdout });
158
+ try {
159
+ return await fn(rl);
160
+ } finally {
161
+ rl.close();
162
+ }
163
+ }
164
+
165
+ function configureScopedSettings(baseDir, packageDir, appName, scopeLabel) {
166
+ ensureDir(baseDir);
167
+ installClaudeMd(join(baseDir, 'CLAUDE.md'), packageDir, appName);
168
+ console.log(`Configuring ${scopeLabel} settings...`);
169
+ const settingsPath = join(baseDir, 'settings.json');
170
+ injectEnvVars(settingsPath);
171
+ injectStatusLine(settingsPath);
172
+ }
173
+
174
+ function printGlobalNote() {
175
+ console.log('');
176
+ console.log('Note: Global install registers the marketplace and installs ~/.claude/CLAUDE.md only.');
177
+ console.log('To generate CI/CD templates, MCP config, and ci.config.yaml, run `npx @daemux/store-automator` inside a project directory.');
178
+ }
179
+
155
180
  export async function runInstall(scope, isPostinstall = false, cliTokens = {}) {
181
+ const isGlobal = scope === 'user';
182
+
183
+ if (isPostinstall && isGlobal) {
184
+ console.log('Skipping postinstall in global scope.');
185
+ return;
186
+ }
187
+
156
188
  checkClaudeCli();
157
189
 
158
190
  console.log('Installing/updating Daemux Store Automator...');
@@ -160,102 +192,71 @@ export async function runInstall(scope, isPostinstall = false, cliTokens = {}) {
160
192
  const isGitHubActions = Boolean(cliTokens.githubActions);
161
193
  const nonInteractive = Boolean(process.env.npm_config_yes) || process.argv.includes('--postinstall');
162
194
  const projectDir = process.cwd();
163
- const oldVersion = readMarketplaceVersion();
164
195
  const packageDir = getPackageDir();
196
+ const oldVersion = readMarketplaceVersion();
165
197
 
166
- // 1. Copy plugin files + register marketplace
167
198
  copyPluginFiles(packageDir);
168
199
  clearCache();
169
200
  registerMarketplace();
170
201
  runClaudeInstall(scope);
171
-
172
202
  const newVersion = readMarketplaceVersion('unknown');
173
203
 
174
- // 2. Install CI templates (creates ci.config.yaml if missing)
175
- installCiTemplates(projectDir, packageDir);
176
- installFirebaseTemplates(projectDir, packageDir);
177
-
178
- // 3. Read current ci.config.yaml values
179
- const currentConfig = readCiConfig(projectDir);
204
+ let prompted = {};
205
+ if (!isGlobal) {
206
+ installCiTemplates(projectDir, packageDir);
207
+ installFirebaseTemplates(projectDir, packageDir);
208
+ const currentConfig = readCiConfig(projectDir);
180
209
 
181
- // 4. Run interactive prompts (or use CLI flags / skip in non-interactive)
182
- let prompted;
183
- if (isGitHubActions) {
184
- prompted = {
185
- bundleId: cliTokens.bundleId ?? '',
186
- matchDeployKeyPath: cliTokens.matchDeployKey,
187
- matchGitUrl: cliTokens.matchGitUrl,
188
- };
189
- } else if (nonInteractive) {
190
- prompted = { ...cliTokens };
191
- } else {
192
- const { createInterface } = await import('node:readline');
193
- const rl = createInterface({ input: process.stdin, output: process.stdout });
194
- try {
195
- prompted = await promptAll(rl, cliTokens, currentConfig, projectDir);
196
- } finally {
197
- rl.close();
210
+ if (isGitHubActions) {
211
+ prompted = {
212
+ bundleId: cliTokens.bundleId ?? '',
213
+ matchDeployKeyPath: cliTokens.matchDeployKey,
214
+ matchGitUrl: cliTokens.matchGitUrl,
215
+ };
216
+ } else if (nonInteractive) {
217
+ prompted = { ...cliTokens };
218
+ } else {
219
+ prompted = await withReadline((rl) => promptAll(rl, cliTokens, currentConfig, projectDir));
198
220
  }
199
- }
200
-
201
- // 5. Write all prompted values to ci.config.yaml
202
- const ciFields = mapPromptsToCiFields(prompted);
203
- const wrote = writeCiFields(projectDir, ciFields);
204
- if (wrote) console.log('Configuration written to ci.config.yaml');
205
-
206
- if (prompted.matchDeployKeyPath || prompted.matchGitUrl) {
207
- const wroteMatch = writeMatchConfig(projectDir, {
208
- deployKeyPath: prompted.matchDeployKeyPath,
209
- gitUrl: prompted.matchGitUrl,
210
- });
211
- if (wroteMatch) console.log('Match credentials written to ci.config.yaml');
212
- }
213
221
 
214
- // 6. Handle languages separately
215
- if (prompted.languages) {
216
- const langStr = Array.isArray(prompted.languages)
217
- ? prompted.languages.join(',')
218
- : prompted.languages;
219
- if (writeCiLanguages(projectDir, langStr)) {
220
- console.log('Languages updated in ci.config.yaml');
222
+ if (writeCiFields(projectDir, mapPromptsToCiFields(prompted))) {
223
+ console.log('Configuration written to ci.config.yaml');
221
224
  }
222
- }
223
-
224
- // 7. Configure MCP, CLAUDE.md, settings
225
- if (!isGitHubActions) {
226
- const servers = getMcpServers(prompted);
227
- writeMcpJson(projectDir, servers);
228
- }
229
225
 
230
- const baseDir = scope === 'user'
231
- ? join(homedir(), '.claude')
232
- : join(process.cwd(), '.claude');
233
-
234
- ensureDir(baseDir);
235
-
236
- installClaudeMd(join(baseDir, 'CLAUDE.md'), packageDir, prompted.appName);
226
+ if (prompted.matchDeployKeyPath || prompted.matchGitUrl) {
227
+ const wroteMatch = writeMatchConfig(projectDir, {
228
+ deployKeyPath: prompted.matchDeployKeyPath,
229
+ gitUrl: prompted.matchGitUrl,
230
+ });
231
+ if (wroteMatch) console.log('Match credentials written to ci.config.yaml');
232
+ }
237
233
 
238
- installGitHubActionsPath(projectDir, packageDir, prompted);
234
+ if (prompted.languages) {
235
+ const langStr = Array.isArray(prompted.languages) ? prompted.languages.join(',') : prompted.languages;
236
+ if (writeCiLanguages(projectDir, langStr)) {
237
+ console.log('Languages updated in ci.config.yaml');
238
+ }
239
+ }
239
240
 
240
- const scopeLabel = scope === 'user' ? 'global' : 'project';
241
- console.log(`Configuring ${scopeLabel} settings...`);
242
- const settingsPath = join(baseDir, 'settings.json');
243
- injectEnvVars(settingsPath);
244
- injectStatusLine(settingsPath);
241
+ if (!isGitHubActions) writeMcpJson(projectDir, getMcpServers(prompted));
242
+ installGitHubActionsPath(projectDir, packageDir, prompted);
245
243
 
246
- // 8. Run post-install guides (interactive only)
247
- if (!isGitHubActions && !nonInteractive) {
248
- const { createInterface } = await import('node:readline');
249
- const guideRl = createInterface({ input: process.stdin, output: process.stdout });
250
- try {
251
- const { runPostInstallGuides } = await import('./prompts/store-settings.mjs');
252
- await runPostInstallGuides(guideRl, currentConfig);
253
- } finally {
254
- guideRl.close();
244
+ if (!isGitHubActions && !nonInteractive) {
245
+ await withReadline(async (rl) => {
246
+ const { runPostInstallGuides } = await import('./prompts/store-settings.mjs');
247
+ await runPostInstallGuides(rl, currentConfig);
248
+ });
255
249
  }
256
250
  }
257
251
 
258
- // 9. Summary + dynamic next steps
252
+ const baseDir = isGlobal ? join(homedir(), '.claude') : join(projectDir, '.claude');
253
+ const appName = prompted.appName || (isGlobal ? 'your app' : undefined);
254
+ configureScopedSettings(baseDir, packageDir, appName, isGlobal ? 'global' : 'project');
255
+
259
256
  printSummary(scope, oldVersion, newVersion);
260
- printNextSteps(prompted);
257
+ if (isGlobal) {
258
+ printGlobalNote();
259
+ } else {
260
+ printNextSteps(prompted);
261
+ }
261
262
  }
package/src/templates.mjs CHANGED
@@ -55,7 +55,7 @@ export function installClaudeMd(targetPath, packageDir, appName) {
55
55
 
56
56
  let content = readFileSync(template, 'utf8');
57
57
  if (appName) {
58
- content = content.replace(/\{APP_NAME\}/g, appName);
58
+ content = content.split('{APP_NAME}').join(appName);
59
59
  }
60
60
  writeFileSync(targetPath, content, 'utf8');
61
61
  }
package/src/uninstall.mjs CHANGED
@@ -86,11 +86,15 @@ export async function runUninstall(scope) {
86
86
 
87
87
  runClaudeUninstall(scope);
88
88
 
89
+ let doneMessage;
89
90
  if (scope === 'user') {
90
91
  console.log('Removing marketplace...');
91
92
  rmSync(MARKETPLACE_DIR, { recursive: true, force: true });
92
93
  rmSync(CACHE_DIR, { recursive: true, force: true });
93
94
  unregisterMarketplace();
95
+ doneMessage = '\nDone! store-automator uninstalled globally.';
96
+ } else {
97
+ doneMessage = '\nDone! store-automator uninstalled from this project.\n\nNote: Marketplace files remain under ~/.claude/plugins/marketplaces.\nRun with --global --uninstall to remove marketplace completely.';
94
98
  }
95
99
 
96
100
  const isGlobal = scope === 'user';
@@ -98,15 +102,15 @@ export async function runUninstall(scope) {
98
102
  const scopeLabel = isGlobal ? 'global' : 'project';
99
103
 
100
104
  removeFileIfExists(join(baseDir, 'CLAUDE.md'), `${scopeLabel} CLAUDE.md`);
101
- removeCiTemplates(process.cwd());
102
- removeMcpServers(process.cwd());
105
+
106
+ if (!isGlobal) {
107
+ removeCiTemplates(process.cwd());
108
+ removeMcpServers(process.cwd());
109
+ }
103
110
 
104
111
  console.log(`Cleaning ${scopeLabel} settings...`);
105
112
  removeEnvVars(join(baseDir, 'settings.json'));
106
113
  removeStatusLine(join(baseDir, 'settings.json'));
107
114
 
108
- console.log(isGlobal
109
- ? '\nDone! store-automator uninstalled globally.'
110
- : `\nDone! store-automator uninstalled from this project.\n\nNote: Marketplace files remain in ${MARKETPLACE_DIR}\nRun with --global --uninstall to remove marketplace completely.`
111
- );
115
+ console.log(doneMessage);
112
116
  }