@daemux/store-automator 0.5.2 → 0.6.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.
@@ -5,14 +5,14 @@
5
5
  },
6
6
  "metadata": {
7
7
  "description": "App Store & Google Play automation for Flutter apps",
8
- "version": "0.5.2"
8
+ "version": "0.6.0"
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.5.2",
15
+ "version": "0.6.0",
16
16
  "keywords": ["flutter", "app-store", "google-play", "fastlane", "codemagic"]
17
17
  }
18
18
  ]
package/README.md CHANGED
@@ -27,8 +27,8 @@ npm install @daemux/store-automator
27
27
 
28
28
  The postinstall script will:
29
29
 
30
- 1. Prompt for MCP server tokens (Stitch, Cloudflare)
31
- 2. Configure `.mcp.json` with MCP servers (Playwright, mobile-mcp, Stitch, Cloudflare)
30
+ 1. Prompt for MCP server tokens (Stitch, Cloudflare, Codemagic)
31
+ 2. Configure `.mcp.json` with MCP servers (Playwright, mobile-mcp, Stitch, Cloudflare, Codemagic)
32
32
  3. Install the plugin marketplace and register agents
33
33
  4. Copy `CLAUDE.md` template to `.claude/CLAUDE.md`
34
34
  5. Copy CI/CD templates (Fastlane, scripts, web pages, ci.config.yaml)
@@ -112,8 +112,54 @@ The package configures these MCP servers in `.mcp.json`:
112
112
  |--------|---------|---------------|
113
113
  | playwright | Browser automation for testing web pages | None |
114
114
  | mobile-mcp | Mobile device automation | None |
115
- | stitch | AI design tool for screenshot generation | Google API Key |
116
- | cloudflare | Cloudflare Pages deployment | API Token + Account ID |
115
+ | stitch | AI design tool for screenshot generation | `STITCH_API_KEY` |
116
+ | cloudflare | Cloudflare Pages deployment | `CLOUDFLARE_API_TOKEN` + Account ID |
117
+ | codemagic | Codemagic CI/CD build management (27 tools) | `CODEMAGIC_API_TOKEN` |
118
+
119
+ ## Non-interactive Install
120
+
121
+ For CI/CD environments or scripted setups, pass tokens as CLI flags to skip interactive prompts:
122
+
123
+ ```bash
124
+ npx @daemux/store-automator \
125
+ --codemagic-token=YOUR_CM_TOKEN \
126
+ --stitch-key=YOUR_STITCH_KEY \
127
+ --cloudflare-token=YOUR_CF_TOKEN \
128
+ --cloudflare-account-id=YOUR_CF_ACCOUNT_ID
129
+ ```
130
+
131
+ Any tokens provided via flags will skip the corresponding interactive prompt. If all four tokens are provided, the entire interactive session is skipped.
132
+
133
+ ## CLI Options
134
+
135
+ ```
136
+ Usage: npx @daemux/store-automator [options]
137
+
138
+ Options:
139
+ -g, --global Install globally (~/.claude) instead of project scope
140
+ -u, --uninstall Uninstall plugin and remove files
141
+ --postinstall Run as postinstall hook (auto-detected)
142
+ -v, --version Show version number
143
+ -h, --help Show help
144
+
145
+ MCP Token Flags (skip interactive prompts):
146
+ --codemagic-token=TOKEN Codemagic API token
147
+ --stitch-key=KEY Stitch MCP API key
148
+ --cloudflare-token=TOKEN Cloudflare API token
149
+ --cloudflare-account-id=ID Cloudflare account ID
150
+
151
+ Codemagic:
152
+ --codemagic-setup Register repo and optionally trigger build
153
+ --token=TOKEN API token (or set CM_API_TOKEN env var)
154
+ --branch=BRANCH Branch to build (default: main)
155
+ --workflow=ID Workflow ID (default: default)
156
+ --trigger Trigger build after setup
157
+ --wait Wait for build completion (implies --trigger)
158
+
159
+ GitHub Actions:
160
+ --github-setup Set CM_API_TOKEN secret for GitHub Actions
161
+ --token=TOKEN API token (or set CM_API_TOKEN env var)
162
+ ```
117
163
 
118
164
  ## License
119
165
 
package/bin/cli.mjs CHANGED
@@ -21,19 +21,34 @@ let cmWorkflowId = 'default';
21
21
  let cmTrigger = false;
22
22
  let cmWait = false;
23
23
 
24
+ const cliTokens = {};
25
+
26
+ function flagValue(arg, prefix) {
27
+ return arg.startsWith(prefix) ? arg.slice(prefix.length) : undefined;
28
+ }
29
+
30
+ const tokenFlags = {
31
+ '--codemagic-token=': 'codemagicToken',
32
+ '--stitch-key=': 'stitchApiKey',
33
+ '--cloudflare-token=': 'cloudflareToken',
34
+ '--cloudflare-account-id=': 'cloudflareAccountId',
35
+ };
36
+
24
37
  for (const arg of args) {
25
- if (arg.startsWith('--token=')) {
26
- cmTokenArg = arg.slice(8);
27
- continue;
28
- }
29
- if (arg.startsWith('--branch=')) {
30
- cmBranch = arg.slice(9);
31
- continue;
32
- }
33
- if (arg.startsWith('--workflow=')) {
34
- cmWorkflowId = arg.slice(11);
35
- continue;
38
+ let v;
39
+ if ((v = flagValue(arg, '--token=')) !== undefined) { cmTokenArg = v; continue; }
40
+ if ((v = flagValue(arg, '--branch=')) !== undefined) { cmBranch = v; continue; }
41
+ if ((v = flagValue(arg, '--workflow=')) !== undefined) { cmWorkflowId = v; continue; }
42
+
43
+ let matched = false;
44
+ for (const [prefix, key] of Object.entries(tokenFlags)) {
45
+ if ((v = flagValue(arg, prefix)) !== undefined) {
46
+ cliTokens[key] = v;
47
+ matched = true;
48
+ break;
49
+ }
36
50
  }
51
+ if (matched) continue;
37
52
 
38
53
  switch (arg) {
39
54
  case '-g':
@@ -64,23 +79,29 @@ for (const arg of args) {
64
79
  console.log(`Usage: npx @daemux/store-automator [options]
65
80
 
66
81
  Options:
67
- -g, --global Install globally (~/.claude) instead of project scope
68
- -u, --uninstall Uninstall plugin and remove files
69
- --postinstall Run as postinstall hook (auto-detected)
70
- -v, --version Show version number
71
- -h, --help Show help
82
+ -g, --global Install globally (~/.claude) instead of project scope
83
+ -u, --uninstall Uninstall plugin and remove files
84
+ --postinstall Run as postinstall hook (auto-detected)
85
+ -v, --version Show version number
86
+ -h, --help Show help
87
+
88
+ MCP Token Flags (skip interactive prompts):
89
+ --codemagic-token=TOKEN Codemagic API token
90
+ --stitch-key=KEY Stitch MCP API key
91
+ --cloudflare-token=TOKEN Cloudflare API token
92
+ --cloudflare-account-id=ID Cloudflare account ID
72
93
 
73
94
  Codemagic:
74
- --codemagic-setup Register repo and optionally trigger build
75
- --token=TOKEN API token (or set CM_API_TOKEN env var)
76
- --branch=BRANCH Branch to build (default: main)
77
- --workflow=ID Workflow ID (default: default)
78
- --trigger Trigger build after setup
79
- --wait Wait for build completion (implies --trigger)
95
+ --codemagic-setup Register repo and optionally trigger build
96
+ --token=TOKEN API token (or set CM_API_TOKEN env var)
97
+ --branch=BRANCH Branch to build (default: main)
98
+ --workflow=ID Workflow ID (default: default)
99
+ --trigger Trigger build after setup
100
+ --wait Wait for build completion (implies --trigger)
80
101
 
81
102
  GitHub Actions (auto-configured during install if gh CLI available):
82
- --github-setup Set CM_API_TOKEN secret for GitHub Actions
83
- --token=TOKEN API token (or set CM_API_TOKEN env var)
103
+ --github-setup Set CM_API_TOKEN secret for GitHub Actions
104
+ --token=TOKEN API token (or set CM_API_TOKEN env var)
84
105
 
85
106
  Examples:
86
107
  npx @daemux/store-automator Install for project
@@ -89,7 +110,11 @@ Examples:
89
110
  npx @daemux/store-automator -g -u Uninstall globally
90
111
  npx @daemux/store-automator --codemagic-setup Register with Codemagic
91
112
  npx @daemux/store-automator --codemagic-setup --trigger --wait Trigger and wait
92
- npx @daemux/store-automator --github-setup Configure GitHub Actions`);
113
+ npx @daemux/store-automator --github-setup Configure GitHub Actions
114
+
115
+ Non-interactive install (CI/CD):
116
+ npx @daemux/store-automator --codemagic-token=TOKEN --stitch-key=KEY
117
+ npx @daemux/store-automator --cloudflare-token=TOKEN --cloudflare-account-id=ID`);
93
118
  process.exit(0);
94
119
  break; // eslint: no-fallthrough
95
120
  case '-v':
@@ -122,7 +147,7 @@ try {
122
147
  await runUninstall(scope);
123
148
  } else {
124
149
  const { runInstall } = await import('../src/install.mjs');
125
- await runInstall(scope, isPostinstall);
150
+ await runInstall(scope, isPostinstall, cliTokens);
126
151
  }
127
152
  } catch (err) {
128
153
  if (isPostinstall) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@daemux/store-automator",
3
- "version": "0.5.2",
3
+ "version": "0.6.0",
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.5.0",
3
+ "version": "0.6.0",
4
4
  "description": "App Store & Google Play automation agents for Flutter app publishing",
5
5
  "author": {
6
6
  "name": "Daemux"
@@ -42,14 +42,9 @@ The Codemagic pipeline runs two parallel workflows on push to `main`:
42
42
  **iOS Release**: Flutter analyze -> Flutter test -> Build IPA -> Code signing -> Upload metadata + screenshots -> Deploy to App Store Connect -> Sync IAP
43
43
  **Android Release**: Flutter analyze -> Flutter test -> Build AAB -> Keystore signing -> Check Google Play readiness -> Deploy to Google Play -> Sync IAP
44
44
 
45
- ### Token Resolution
45
+ ### Token Configuration
46
46
 
47
- The Codemagic API token is stored in `ci.config.yaml` under `codemagic.api_token`. Read it from there NEVER ask the user for it. If the field is empty, warn the user to fill `codemagic.api_token` in `ci.config.yaml` and stop.
48
-
49
- ```bash
50
- # Extract token from ci.config.yaml
51
- CM_API_TOKEN=$(grep 'api_token:' ci.config.yaml | head -1 | sed 's/.*api_token: *"\{0,1\}\([^"]*\)"\{0,1\}/\1/' | tr -d ' ')
52
- ```
47
+ The Codemagic API token is auto-configured via the codemagic MCP server in `.mcp.json` (set during install). You do not need to resolve the token manually -- all MCP tool calls authenticate automatically. If the codemagic MCP server is missing from `.mcp.json`, instruct the user to re-run `npx @daemux/store-automator` or add `--codemagic-token=TOKEN` to configure it.
53
48
 
54
49
  ### Triggering Builds
55
50
 
@@ -59,29 +54,50 @@ git push origin main
59
54
  ```
60
55
  Codemagic triggers both iOS and Android workflows automatically on push to `main`.
61
56
 
62
- **Option 2: Codemagic REST API** (preferred for full automation allows status polling and log reading)
63
- ```bash
64
- CM_API_TOKEN=$(grep 'api_token:' ci.config.yaml | head -1 | sed 's/.*api_token: *"\{0,1\}\([^"]*\)"\{0,1\}/\1/' | tr -d ' ')
65
- node -e "
66
- import {startBuild} from './src/codemagic-api.mjs';
67
- const r = await startBuild('$CM_API_TOKEN', '<appId>', '<workflowId>', 'main');
68
- console.log(JSON.stringify(r, null, 2));
69
- "
70
- ```
57
+ **Option 2: Codemagic MCP** (preferred for full automation -- allows status polling and log reading)
58
+ Use the `start_build` MCP tool:
59
+ - `appId`: from `codemagic.app_id` in `ci.config.yaml`
60
+ - `workflowId`: from `codemagic.workflows` in `ci.config.yaml`
61
+ - `branch`: `main` (or as needed)
71
62
 
72
63
  ### Monitoring Build Status
73
64
 
74
- ```bash
75
- CM_API_TOKEN=$(grep 'api_token:' ci.config.yaml | head -1 | sed 's/.*api_token: *"\{0,1\}\([^"]*\)"\{0,1\}/\1/' | tr -d ' ')
76
- node -e "
77
- import {getBuildStatus} from './src/codemagic-api.mjs';
78
- const r = await getBuildStatus('$CM_API_TOKEN', '<buildId>');
79
- console.log(JSON.stringify(r, null, 2));
80
- "
81
- ```
65
+ Use the `get_build` MCP tool with the `buildId` returned from `start_build`. Poll every 60 seconds until the build reaches a terminal state.
82
66
 
83
67
  Build states: `queued` -> `preparing` -> `building` -> `testing` -> `publishing` -> `finished` | `failed` | `canceled`
84
68
 
69
+ ### Available Codemagic MCP Tools
70
+
71
+ | Tool | Description |
72
+ |------|-------------|
73
+ | `list_apps` | List all Codemagic applications |
74
+ | `get_app` | Get details of a specific application |
75
+ | `add_app` | Add a new application to Codemagic |
76
+ | `start_build` | Start a new build |
77
+ | `get_build` | Get details of a specific build |
78
+ | `cancel_build` | Cancel a running build |
79
+ | `list_builds` | List builds for a team (V3 API) |
80
+ | `get_artifact_url` | Get the download URL for a build artifact |
81
+ | `create_public_artifact_url` | Create a time-limited public URL for an artifact |
82
+ | `list_caches` | List build caches for an application |
83
+ | `delete_caches` | Delete build caches for an application |
84
+ | `setup_asc_credentials` | Create variable group with App Store Connect credentials |
85
+ | `setup_code_signing` | Create variable group with iOS code signing cert and profile |
86
+ | `get_user` | Get the current authenticated user info |
87
+ | `list_teams` | List all teams the user belongs to |
88
+ | `get_team` | Get details of a specific team |
89
+ | `list_team_members` | List members of a specific team |
90
+ | `list_variable_groups` | List variable groups for a team or application |
91
+ | `get_variable_group` | Get details of a specific variable group |
92
+ | `create_variable_group` | Create a new variable group |
93
+ | `update_variable_group` | Update a variable group name or security setting |
94
+ | `delete_variable_group` | Delete a variable group |
95
+ | `list_variables` | List variables in a variable group |
96
+ | `get_variable` | Get a specific variable from a variable group |
97
+ | `update_variable` | Update a variable in a variable group |
98
+ | `delete_variable` | Delete a variable from a variable group |
99
+ | `bulk_import_variables` | Import multiple variables into a variable group |
100
+
85
101
  ### Build Failure Analysis
86
102
 
87
103
  When a build fails:
@@ -115,24 +131,9 @@ Versions are auto-incremented:
115
131
  - Build number = latest store build number + 1
116
132
  - Both platforms share the same version name (e.g., 1.0.0), synced from iOS
117
133
 
118
- ### Health Check Process
119
-
120
- After triggering a build:
121
- 1. Poll build status every 60 seconds until completion
122
- 2. Check both iOS and Android workflows independently
123
- 3. If one passes and one fails, report partial success and investigate the failure
124
- 4. Verify artifacts exist (IPA for iOS, AAB for Android)
125
- 5. Check store submission status after successful upload
126
-
127
- ### Log Analysis
134
+ ### Health Check and Log Analysis
128
135
 
129
- When analyzing Codemagic build logs:
130
- 1. Look for `ERROR`, `FAILURE`, `EXCEPTION`, `BUILD FAILED` patterns
131
- 2. Check Flutter analyze output for lint warnings/errors
132
- 3. Check test results for failures and skipped tests
133
- 4. Check code signing logs for certificate/profile issues
134
- 5. Check Fastlane output for store upload errors
135
- 6. Note which pipeline step failed and its exit code
136
+ After triggering a build, poll status every 60 seconds. Check both iOS and Android independently. If one passes and one fails, report partial success and investigate. Verify artifacts (IPA/AAB) and store submission status after success. In logs, look for `ERROR`, `FAILURE`, `EXCEPTION`, `BUILD FAILED`, check Flutter analyze, test results, code signing, and Fastlane output.
136
137
 
137
138
  ### Output Format
138
139
 
@@ -142,18 +143,7 @@ PLATFORM: iOS | Android | Both
142
143
  BUILD ID: [id]
143
144
  STATUS: queued | building | testing | publishing | finished | failed
144
145
  VERSION: [version]+[build_number]
145
-
146
- For failures:
147
- - Failed step: [step name]
148
- - Error: [summary]
149
- - Root cause: [analysis]
150
- - Fix: [action taken or recommended]
151
-
152
- For success:
153
- - iOS: [App Store submission status]
154
- - Android: [Google Play upload status]
155
- - Artifacts: [list]
156
-
146
+ RESULT: [success details or failure analysis with root cause and fix]
157
147
  RECOMMENDATION: [next action if needed]
158
148
  ```
159
149
 
@@ -383,7 +373,7 @@ Analyze Firestore usage patterns and optimize queries, indexes, and security rul
383
373
  | `scripts/check_changed.sh` | Detects changed files for conditional uploads |
384
374
  | `scripts/manage_version_ios.py` | iOS version auto-increment |
385
375
  | `scripts/check_google_play.py` | Google Play readiness checker |
386
- | `src/codemagic-api.mjs` | Codemagic REST API client |
376
+ | `.mcp.json (codemagic)` | Codemagic MCP server config and API token |
387
377
  | `fastlane/metadata/` | Store listing metadata (iOS + Android) |
388
378
  | `fastlane/iap_config.json` | In-app purchase configuration |
389
379
  | `web/deploy-cloudflare.mjs` | Cloudflare Pages deployment script |
@@ -0,0 +1,23 @@
1
+ import { readFileSync, writeFileSync, existsSync } from 'node:fs';
2
+ import { join } from 'node:path';
3
+
4
+ const CI_CONFIG_FILE = 'ci.config.yaml';
5
+ const APP_ID_PATTERN = /^(\s*app_id:\s*)"[^"]*"/m;
6
+
7
+ export function writeCiAppId(projectDir, appId) {
8
+ const configPath = join(projectDir, CI_CONFIG_FILE);
9
+ if (!existsSync(configPath)) return false;
10
+
11
+ try {
12
+ const content = readFileSync(configPath, 'utf8');
13
+ if (!APP_ID_PATTERN.test(content)) return false;
14
+
15
+ const updated = content.replace(APP_ID_PATTERN, `$1"${appId}"`);
16
+ if (updated === content) return false;
17
+
18
+ writeFileSync(configPath, updated, 'utf8');
19
+ return true;
20
+ } catch {
21
+ return false;
22
+ }
23
+ }
@@ -1,20 +1,13 @@
1
1
  import { findAppByRepo, addApp, startBuild, getBuildStatus, normalizeRepoUrl } from './codemagic-api.mjs';
2
- import { exec } from './utils.mjs';
2
+ import { exec, resolveToken } from './utils.mjs';
3
3
  import { execFileSync } from 'child_process';
4
+ import { writeCiAppId } from './ci-config.mjs';
5
+ import { updateMcpAppId } from './mcp-setup.mjs';
4
6
 
5
7
  const POLL_INTERVAL_MS = 30_000;
6
8
  const POLL_TIMEOUT_MS = 15 * 60 * 1000;
7
9
  const TERMINAL_STATUSES = new Set(['finished', 'failed', 'canceled']);
8
10
 
9
- function resolveToken(tokenArg) {
10
- const token = process.env.CM_API_TOKEN || tokenArg;
11
- if (!token) {
12
- console.error('Codemagic API token required. Set CM_API_TOKEN or pass --token=...');
13
- process.exit(1);
14
- }
15
- return token;
16
- }
17
-
18
11
  function resolveRepoUrl() {
19
12
  const url = exec('git remote get-url origin');
20
13
  if (!url) {
@@ -130,12 +123,18 @@ export async function runCodemagicSetup(options) {
130
123
  }
131
124
 
132
125
  const appId = app._id;
126
+ const appIdWritten = writeCiAppId(process.cwd(), appId);
127
+ updateMcpAppId(process.cwd(), appId);
133
128
 
134
129
  if (!trigger) {
135
130
  console.log('\nSetup complete. Use --trigger to start a build.\n');
131
+ if (appIdWritten) {
132
+ console.log('codemagic.app_id written to ci.config.yaml.');
133
+ } else {
134
+ console.log('Fill codemagic.app_id in ci.config.yaml manually.');
135
+ }
136
136
  console.log('To enable GitHub Actions auto-trigger:');
137
- console.log(' 1. Fill codemagic.app_id in ci.config.yaml');
138
- console.log(' 2. Run: npx @daemux/store-automator --github-setup');
137
+ console.log(' Run: npx @daemux/store-automator --github-setup');
139
138
  return { appId, buildId: null, status: null };
140
139
  }
141
140
 
@@ -1,15 +1,6 @@
1
- import { exec } from './utils.mjs';
1
+ import { exec, resolveToken } from './utils.mjs';
2
2
  import { execFileSync } from 'child_process';
3
3
 
4
- function resolveToken(tokenArg) {
5
- const token = process.env.CM_API_TOKEN || tokenArg;
6
- if (!token) {
7
- console.error('Codemagic API token required. Set CM_API_TOKEN or pass --token=...');
8
- process.exit(1);
9
- }
10
- return token;
11
- }
12
-
13
4
  function checkGhCli() {
14
5
  const ghPath = exec('which gh');
15
6
  if (!ghPath) {
@@ -47,6 +38,6 @@ export async function runGitHubSetup(options) {
47
38
  setGitHubSecret('CM_API_TOKEN', token);
48
39
 
49
40
  console.log('\nGitHub Actions setup complete.');
50
- console.log('Next: Fill codemagic.app_id in ci.config.yaml.');
41
+ console.log('Next: Verify codemagic.app_id in ci.config.yaml (auto-configured during install).');
51
42
  console.log('GitHub Actions will trigger configured Codemagic workflows on push to main.');
52
43
  }
package/src/install.mjs CHANGED
@@ -9,8 +9,10 @@ import {
9
9
  } from './utils.mjs';
10
10
  import { injectEnvVars, injectStatusLine } from './settings.mjs';
11
11
  import { promptForTokens } from './prompt.mjs';
12
- import { getMcpServers, writeMcpJson } from './mcp-setup.mjs';
12
+ import { getMcpServers, writeMcpJson, updateMcpAppId } from './mcp-setup.mjs';
13
13
  import { installClaudeMd, installCiTemplates, installFirebaseTemplates } from './templates.mjs';
14
+ import { findAppByRepo, addApp, normalizeRepoUrl } from './codemagic-api.mjs';
15
+ import { writeCiAppId } from './ci-config.mjs';
14
16
 
15
17
  function checkClaudeCli() {
16
18
  const result = exec('command -v claude') || exec('which claude');
@@ -110,12 +112,47 @@ function setupGitHubActions(codemagicToken) {
110
112
  }
111
113
  }
112
114
 
113
- export async function runInstall(scope, isPostinstall = false) {
115
+ async function setupCodemagicApp(projectDir, codemagicToken) {
116
+ if (!codemagicToken) return;
117
+
118
+ let repoUrl;
119
+ try {
120
+ const raw = execSync('git remote get-url origin', {
121
+ encoding: 'utf8',
122
+ stdio: ['pipe', 'pipe', 'pipe'],
123
+ }).trim();
124
+ if (!raw) return;
125
+ repoUrl = normalizeRepoUrl(raw);
126
+ } catch {
127
+ return;
128
+ }
129
+
130
+ try {
131
+ let app = await findAppByRepo(codemagicToken, repoUrl);
132
+ if (!app) {
133
+ app = await addApp(codemagicToken, repoUrl);
134
+ console.log(`Codemagic app created: ${app.appName || app._id}`);
135
+ } else {
136
+ console.log(`Codemagic app found: ${app.appName || app._id}`);
137
+ }
138
+
139
+ const written = writeCiAppId(projectDir, app._id);
140
+ if (written) {
141
+ console.log(`Codemagic app_id written to ci.config.yaml`);
142
+ }
143
+
144
+ updateMcpAppId(projectDir, app._id);
145
+ } catch (err) {
146
+ console.log(`Codemagic auto-setup skipped: ${err.message || err}`);
147
+ }
148
+ }
149
+
150
+ export async function runInstall(scope, isPostinstall = false, cliTokens = {}) {
114
151
  checkClaudeCli();
115
152
 
116
153
  console.log('Installing/updating Daemux Store Automator...');
117
154
 
118
- const tokens = await promptForTokens();
155
+ const tokens = await promptForTokens(cliTokens);
119
156
 
120
157
  const projectDir = process.cwd();
121
158
  const servers = getMcpServers(tokens);
@@ -141,6 +178,8 @@ export async function runInstall(scope, isPostinstall = false) {
141
178
  installCiTemplates(projectDir, packageDir);
142
179
  installFirebaseTemplates(projectDir, packageDir);
143
180
 
181
+ await setupCodemagicApp(projectDir, tokens.codemagicToken);
182
+
144
183
  const scopeLabel = scope === 'user' ? 'global' : 'project';
145
184
  console.log(`Configuring ${scopeLabel} settings...`);
146
185
  const settingsPath = join(baseDir, 'settings.json');
@@ -157,7 +196,7 @@ export async function runInstall(scope, isPostinstall = false) {
157
196
  printSummary(scope, oldVersion, newVersion);
158
197
  console.log('');
159
198
  console.log('Next steps:');
160
- console.log(' 1. Fill ci.config.yaml (including codemagic.app_id)');
199
+ console.log(' 1. Fill ci.config.yaml (codemagic.app_id is auto-configured if token was provided)');
161
200
  console.log(' 2. Add creds/AuthKey.p8 and creds/play-service-account.json');
162
201
  console.log(' 3. Start Claude Code');
163
202
  if (!ghConfigured) {
package/src/mcp-setup.mjs CHANGED
@@ -32,6 +32,16 @@ export function getMcpServers(tokens) {
32
32
  };
33
33
  }
34
34
 
35
+ if (tokens.codemagicToken) {
36
+ const codemagicEnv = { CODEMAGIC_API_TOKEN: tokens.codemagicToken };
37
+ if (tokens.codemagicAppId) codemagicEnv.CODEMAGIC_APP_ID = tokens.codemagicAppId;
38
+ servers.codemagic = {
39
+ command: 'npx',
40
+ args: ['-y', '@daemux/codemagic-mcp@latest'],
41
+ env: codemagicEnv,
42
+ };
43
+ }
44
+
35
45
  return servers;
36
46
  }
37
47
 
@@ -67,6 +77,21 @@ export function writeMcpJson(projectDir, servers) {
67
77
  }
68
78
  }
69
79
 
80
+ export function updateMcpAppId(projectDir, appId) {
81
+ const mcpPath = join(projectDir, '.mcp.json');
82
+ if (!existsSync(mcpPath)) return false;
83
+
84
+ try {
85
+ const data = readJson(mcpPath);
86
+ if (!data.mcpServers?.codemagic?.env) return false;
87
+ data.mcpServers.codemagic.env.CODEMAGIC_APP_ID = appId;
88
+ writeJson(mcpPath, data);
89
+ return true;
90
+ } catch {
91
+ return false;
92
+ }
93
+ }
94
+
70
95
  export function removeMcpServers(projectDir) {
71
96
  const mcpPath = join(projectDir, '.mcp.json');
72
97
  if (!existsSync(mcpPath)) return;
@@ -75,7 +100,7 @@ export function removeMcpServers(projectDir) {
75
100
  const data = readJson(mcpPath);
76
101
  if (!data.mcpServers) return;
77
102
 
78
- const toRemove = ['playwright', 'mobile-mcp', 'stitch', 'cloudflare'];
103
+ const toRemove = ['playwright', 'mobile-mcp', 'stitch', 'cloudflare', 'codemagic'];
79
104
  for (const name of toRemove) {
80
105
  delete data.mcpServers[name];
81
106
  }
package/src/prompt.mjs CHANGED
@@ -12,11 +12,32 @@ function ask(rl, question) {
12
12
  });
13
13
  }
14
14
 
15
- export async function promptForTokens() {
15
+ function allTokensProvided(cliTokens) {
16
+ return (
17
+ cliTokens.stitchApiKey !== undefined &&
18
+ cliTokens.cloudflareToken !== undefined &&
19
+ cliTokens.cloudflareAccountId !== undefined &&
20
+ cliTokens.codemagicToken !== undefined
21
+ );
22
+ }
23
+
24
+ export async function promptForTokens(cliTokens = {}) {
25
+ const result = {
26
+ stitchApiKey: cliTokens.stitchApiKey ?? '',
27
+ cloudflareToken: cliTokens.cloudflareToken ?? '',
28
+ cloudflareAccountId: cliTokens.cloudflareAccountId ?? '',
29
+ codemagicToken: cliTokens.codemagicToken ?? '',
30
+ };
31
+
32
+ if (allTokensProvided(cliTokens)) {
33
+ console.log('All MCP tokens provided via CLI flags, skipping prompts.');
34
+ return result;
35
+ }
36
+
16
37
  if (!isInteractive()) {
17
38
  console.log('Non-interactive terminal detected, skipping token prompts.');
18
39
  console.log('Run "npx store-automator" manually to configure MCP tokens.');
19
- return { stitchApiKey: '', cloudflareToken: '', cloudflareAccountId: '', codemagicToken: '' };
40
+ return result;
20
41
  }
21
42
 
22
43
  const rl = createInterface({
@@ -30,30 +51,35 @@ export async function promptForTokens() {
30
51
  console.log('');
31
52
 
32
53
  try {
33
- const stitchApiKey = await ask(
34
- rl,
35
- 'Stitch MCP API Key (STITCH_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(
54
+ if (cliTokens.stitchApiKey === undefined) {
55
+ result.stitchApiKey = await ask(
56
+ rl,
57
+ 'Stitch MCP API Key (STITCH_API_KEY value): '
58
+ );
59
+ }
60
+
61
+ if (cliTokens.cloudflareToken === undefined) {
62
+ result.cloudflareToken = await ask(
63
+ rl,
64
+ 'Cloudflare API Token: '
65
+ );
66
+ }
67
+
68
+ if (result.cloudflareToken && cliTokens.cloudflareAccountId === undefined) {
69
+ result.cloudflareAccountId = await ask(
46
70
  rl,
47
71
  'Cloudflare Account ID: '
48
72
  );
49
73
  }
50
74
 
51
- const codemagicToken = await ask(
52
- rl,
53
- 'Codemagic API Token (CM_API_TOKEN for CI/CD builds): '
54
- );
75
+ if (cliTokens.codemagicToken === undefined) {
76
+ result.codemagicToken = await ask(
77
+ rl,
78
+ 'Codemagic API Token (CM_API_TOKEN for CI/CD builds): '
79
+ );
80
+ }
55
81
 
56
- return { stitchApiKey, cloudflareToken, cloudflareAccountId, codemagicToken };
82
+ return result;
57
83
  } finally {
58
84
  rl.close();
59
85
  }
package/src/utils.mjs CHANGED
@@ -44,3 +44,12 @@ export function readJson(filePath) {
44
44
  export function writeJson(filePath, data) {
45
45
  writeFileSync(filePath, JSON.stringify(data, null, 2) + '\n', 'utf8');
46
46
  }
47
+
48
+ export function resolveToken(tokenArg) {
49
+ const token = process.env.CM_API_TOKEN || tokenArg;
50
+ if (!token) {
51
+ console.error('Codemagic API token required. Set CM_API_TOKEN or pass --token=...');
52
+ process.exit(1);
53
+ }
54
+ return token;
55
+ }
@@ -38,7 +38,7 @@ Prefer using the devops agent over direct Bash/SSH for structured operations.
38
38
  ## Configuration Files
39
39
 
40
40
  ### ci.config.yaml
41
- Single source of truth: credentials, app identity, store settings, web domain, Codemagic CI/CD (app_id, api_token for build triggering/monitoring, workflow IDs).
41
+ Single source of truth: credentials, app identity, store settings, web domain, Codemagic CI/CD (app_id for GitHub Actions auto-trigger, workflow IDs). The Codemagic API token is managed by the codemagic MCP server in `.mcp.json`.
42
42
 
43
43
  ### fastlane/metadata/ Structure
44
44
  All store listing texts are stored in fastlane directories and managed by the appstore-meta-creator agent:
@@ -57,10 +57,9 @@ web:
57
57
  # === CODEMAGIC CI/CD ===
58
58
  # Fill after creating app in Codemagic (https://codemagic.io/apps)
59
59
  # Find app_id in your Codemagic dashboard URL: codemagic.io/app/{app_id}
60
- # Generate API token: Codemagic dashboard Settings Integrations Codemagic API → Show
60
+ # API token is stored in .mcp.json (codemagic MCP server, set during install)
61
61
  codemagic:
62
62
  app_id: ""
63
- api_token: ""
64
63
  workflows:
65
64
  - ios-release
66
65
  - android-release