optikit 1.2.5 → 1.3.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 (104) hide show
  1. package/.claude/commands/build.md +57 -0
  2. package/.claude/commands/clean.md +45 -0
  3. package/.claude/commands/generate.md +64 -0
  4. package/.claude/commands/rollback.md +67 -0
  5. package/.claude/commands/run.md +63 -0
  6. package/.claude/commands/setup.md +69 -0
  7. package/.claude/commands/sync-docs.md +148 -0
  8. package/.claude/commands/version.md +63 -0
  9. package/.claude/settings.local.json +28 -0
  10. package/.claude-plugin/marketplace.json +36 -0
  11. package/.claude-plugin/plugin.json +25 -0
  12. package/.history/README_20260325211923.md +268 -0
  13. package/.history/README_20260325212116.md +268 -0
  14. package/.history/README_20260325212234.md +266 -0
  15. package/.history/README_20260325221838.md +321 -0
  16. package/.history/README_20260325221920.md +327 -0
  17. package/.history/README_20260325222245.md +328 -0
  18. package/.history/README_20260325222247.md +328 -0
  19. package/.mcp.json +8 -0
  20. package/CHANGELOG.md +67 -98
  21. package/CLAUDE.md +57 -206
  22. package/OPTIKIT_AGENT.md +398 -0
  23. package/README.md +293 -60
  24. package/dist/cli.js +75 -244
  25. package/dist/commands/build/commands.js +146 -0
  26. package/dist/commands/build/testflight.js +14 -0
  27. package/dist/commands/clean/commands.js +41 -0
  28. package/dist/commands/clean/flutter.js +8 -14
  29. package/dist/commands/clean/ios.js +12 -15
  30. package/dist/commands/config/aliases.js +122 -0
  31. package/dist/commands/config/commands.js +49 -0
  32. package/dist/commands/config/initApp.js +191 -0
  33. package/dist/commands/config/rollback.js +15 -4
  34. package/dist/commands/config/upgrade.js +36 -0
  35. package/dist/commands/mcp/commands.js +21 -0
  36. package/dist/commands/mcp/server.js +27 -0
  37. package/dist/commands/mcp/setup.js +62 -0
  38. package/dist/commands/mcp/tools.js +359 -0
  39. package/dist/commands/project/commands.js +132 -0
  40. package/dist/commands/project/devices.js +10 -26
  41. package/dist/commands/project/doctor.js +58 -0
  42. package/dist/commands/project/generate.js +183 -30
  43. package/dist/commands/project/setup.js +10 -28
  44. package/dist/commands/project/status.js +65 -0
  45. package/dist/commands/version/bump.js +75 -101
  46. package/dist/commands/version/commands.js +63 -0
  47. package/dist/commands/version/update.js +36 -24
  48. package/dist/constants.js +6 -1
  49. package/dist/styles.js +42 -5
  50. package/dist/utils/helpers/error.js +14 -0
  51. package/dist/utils/helpers/file.js +1 -1
  52. package/dist/utils/helpers/version.js +2 -1
  53. package/dist/utils/services/backup.js +12 -1
  54. package/dist/utils/services/command.js +1 -34
  55. package/dist/utils/services/exec.js +76 -101
  56. package/dist/utils/services/logger.js +10 -4
  57. package/dist/utils/validators/validation.js +24 -12
  58. package/docs/INSTALLATION.md +72 -0
  59. package/docs/TROUBLESHOOT.md +140 -0
  60. package/docs/USAGE.md +185 -0
  61. package/docs/VERSION_MANAGEMENT.md +177 -0
  62. package/package.json +7 -11
  63. package/src/cli.ts +82 -371
  64. package/src/commands/build/commands.ts +169 -0
  65. package/src/commands/build/testflight.ts +18 -0
  66. package/src/commands/clean/commands.ts +43 -0
  67. package/src/commands/clean/flutter.ts +9 -13
  68. package/src/commands/clean/ios.ts +13 -13
  69. package/src/commands/config/aliases.ts +150 -0
  70. package/src/commands/config/commands.ts +50 -0
  71. package/src/commands/config/initApp.ts +213 -0
  72. package/src/commands/config/rollback.ts +16 -4
  73. package/src/commands/config/upgrade.ts +40 -0
  74. package/src/commands/mcp/commands.ts +23 -0
  75. package/src/commands/mcp/server.ts +35 -0
  76. package/src/commands/mcp/setup.ts +69 -0
  77. package/src/commands/mcp/tools.ts +365 -0
  78. package/src/commands/project/commands.ts +132 -0
  79. package/src/commands/project/devices.ts +11 -24
  80. package/src/commands/project/doctor.ts +81 -0
  81. package/src/commands/project/generate.ts +211 -32
  82. package/src/commands/project/setup.ts +13 -30
  83. package/src/commands/project/status.ts +72 -0
  84. package/src/commands/version/bump.ts +98 -110
  85. package/src/commands/version/commands.ts +76 -0
  86. package/src/commands/version/update.ts +86 -75
  87. package/src/constants.ts +7 -1
  88. package/src/styles.ts +49 -7
  89. package/src/utils/helpers/error.ts +16 -0
  90. package/src/utils/helpers/file.ts +1 -1
  91. package/src/utils/helpers/version.ts +2 -1
  92. package/src/utils/services/backup.ts +17 -1
  93. package/src/utils/services/command.ts +1 -58
  94. package/src/utils/services/exec.ts +92 -117
  95. package/src/utils/services/logger.ts +12 -4
  96. package/src/utils/validators/validation.ts +24 -12
  97. package/CODE_QUALITY.md +0 -398
  98. package/ENHANCEMENTS.md +0 -310
  99. package/FEATURE_ENHANCEMENTS.md +0 -435
  100. package/INSTALLATION.md +0 -118
  101. package/SAFETY_FEATURES.md +0 -396
  102. package/TROUBLESHOOT.md +0 -60
  103. package/USAGE.md +0 -412
  104. package/VERSION_MANAGEMENT.md +0 -438
package/src/cli.ts CHANGED
@@ -1,387 +1,98 @@
1
1
  #!/usr/bin/env node
2
2
 
3
+ // MCP mode: redirect all console output to stderr before any imports
4
+ const isMcpMode = process.argv.includes("mcp");
5
+ if (isMcpMode) {
6
+ const origLog = console.log;
7
+ console.log = (...args: unknown[]) => console.error(...args);
8
+ // Restore for JSON-RPC (SDK uses process.stdout directly)
9
+ void origLog;
10
+ }
11
+
3
12
  import chalk from "chalk";
4
13
  import boxen from "boxen";
5
14
  import yargs from "yargs/yargs";
6
15
  import { hideBin } from "yargs/helpers";
7
- import { generateModule } from "./commands/project/generate.js";
8
- import { cleanProject } from "./commands/clean/flutter.js";
9
- import { cleanIosProject } from "./commands/clean/ios.js";
10
- import { updateFlutterVersion } from "./commands/version/update.js";
11
- import {
12
- buildFlutterApk,
13
- buildFlutterBundle,
14
- buildFlutterIos,
15
- buildFlutterIpa,
16
- } from "./commands/build/releases.js";
17
- import { boxenOptions } from "./styles.js";
18
- import { openIos, openAndroid, openIpaOutput, openBundleOutput, openApkOutput } from "./commands/project/open.js";
16
+ import type { CommandModule } from "yargs";
19
17
  import { createRequire } from "module";
20
- import { createVscodeSettings } from "./commands/project/setup.js";
21
- import { initializeProject } from "./commands/config/init.js";
22
- import { rollbackFiles, rollbackRestore } from "./commands/config/rollback.js";
23
- import {
24
- bumpVersion,
25
- bumpIosBuildOnly,
26
- bumpAndroidBuildOnly,
27
- bumpBothBuilds,
28
- showCurrentVersion
29
- } from "./commands/version/bump.js";
30
- import { listDevices, runApp, runAppInteractive } from "./commands/project/devices.js";
18
+ import { bannerBoxOptions, createBanner } from "./styles.js";
19
+ import { loadConfig } from "./utils/services/config.js";
20
+ import { setDryRunMode } from "./utils/helpers/dryRun.js";
21
+
22
+ // Command modules
23
+ import { buildCommands } from "./commands/build/commands.js";
24
+ import { cleanCommands } from "./commands/clean/commands.js";
25
+ import { versionCommands } from "./commands/version/commands.js";
26
+ import { projectCommands } from "./commands/project/commands.js";
27
+ import { configCommands } from "./commands/config/commands.js";
28
+ import { mcpCommands } from "./commands/mcp/commands.js";
29
+
31
30
  const require = createRequire(import.meta.url);
32
31
  const packageInfo: { version: string } = require("../package.json");
33
32
 
34
- const version = packageInfo.version;
33
+ // Greeting banner (skip in MCP mode — stdout is reserved for JSON-RPC)
34
+ if (!isMcpMode) {
35
+ console.log(boxen(createBanner(packageInfo.version), bannerBoxOptions));
36
+ }
35
37
 
36
- const greeting = chalk.white.bold("Hello ^_^");
37
- console.log(boxen(greeting, boxenOptions));
38
+ // Load config once
39
+ const config = loadConfig();
38
40
 
39
- const options = yargs(hideBin(process.argv))
40
- .command(
41
- "generate module <moduleName>",
42
- "Generate a module with structure",
43
- (yargs) => {
44
- return yargs.positional("moduleName", {
45
- describe: "The name of the module to generate",
46
- type: "string",
47
- });
48
- },
49
- (argv) => {
50
- const moduleName = argv.moduleName as string;
51
- generateModule(moduleName);
52
- }
53
- )
54
- .command(
55
- "clean-flutter",
56
- "Clean the Flutter project",
57
- (yargs) => {
58
- return yargs.option("disable-fvm", {
59
- type: "boolean",
60
- default: false,
61
- description: "Run without FVM (use --disable-fvm to enable)",
62
- });
63
- },
64
- (argv) => {
65
- const noFvm = argv.disableFvm as boolean;
66
- cleanProject(noFvm);
67
- }
68
- )
69
- .command(
70
- "clean-ios",
71
- "Clean the iOS project",
72
- (yargs) => {
73
- return yargs
74
- .option("clean-cache", {
75
- type: "boolean",
76
- default: false,
77
- description:
78
- "Run with CocoaPods cache cleaning (use --clean-cache to enable)",
79
- })
80
- .option("repo-update", {
81
- type: "boolean",
82
- default: false,
83
- description:
84
- "Run pod install with repository update (use --repo-update to enable)",
85
- });
86
- },
87
- (argv) => {
88
- const cleanCache = argv.cleanCache as boolean;
89
- const repoUpdate = argv.repoUpdate as boolean;
90
- cleanIosProject(cleanCache, repoUpdate);
91
- }
92
- )
93
- .command(
94
- "flutter-build-apk",
95
- "Build the Flutter APK with release configuration, obfuscation, and split debug info",
96
- (yargs) => {
97
- return yargs.option("disable-fvm", {
98
- type: "boolean",
99
- default: false,
100
- description: "Run without FVM (use --disable-fvm to enable)",
101
- });
102
- },
103
- async (argv) => {
104
- const noFvm = argv.disableFvm as boolean;
105
- await buildFlutterApk(noFvm);
106
- }
107
- )
108
- .command(
109
- "flutter-build-bundle",
110
- "Build the Flutter Bundle with release configuration, obfuscation, and split debug info",
111
- (yargs) => {
112
- return yargs.option("disable-fvm", {
113
- type: "boolean",
114
- default: false,
115
- description: "Run without FVM (use --disable-fvm to enable)",
116
- });
117
- },
118
- async (argv) => {
119
- const noFvm = argv.disableFvm as boolean;
120
- await buildFlutterBundle(noFvm);
121
- }
122
- )
123
- .command(
124
- "flutter-build-ios",
125
- "Build the Flutter iOS app with release configuration and increment the build version",
126
- (yargs) => {
127
- return yargs.option("disable-fvm", {
128
- type: "boolean",
129
- default: false,
130
- description: "Run without FVM (use --disable-fvm to enable)",
131
- });
132
- },
133
- async (argv) => {
134
- const noFvm = argv.disableFvm as boolean;
135
- await buildFlutterIos(noFvm);
136
- }
137
- )
138
- .command(
139
- "flutter-build-ipa",
140
- "Create a release IPA with an updated build version number",
141
- (yargs) => {
142
- return yargs.option("disable-fvm", {
143
- type: "boolean",
144
- default: false,
145
- description: "Run without FVM (use --disable-fvm to enable)",
146
- });
147
- },
148
- async (argv) => {
149
- const noFvm = argv.disableFvm as boolean;
150
- await buildFlutterIpa(noFvm);
151
- }
152
- )
153
- .command(
154
- "flutter-update-version",
155
- "Update version and build numbers for both Android and iOS",
156
- (yargs) => {
157
- return yargs
158
- .option("app-version", {
159
- type: "string",
160
- description: "The version number to set for both Android and iOS",
161
- demandOption: false,
162
- default: "",
163
- })
164
- .option("android-build", {
165
- type: "string",
166
- description: "The Android build number to set in pubspec.yaml",
167
- demandOption: false,
168
- default: "",
169
- })
170
- .option("ios-build", {
171
- type: "string",
172
- description:
173
- "The iOS build number to set using agv-tool and Info.plist",
174
- demandOption: false,
175
- default: "",
176
- });
177
- },
178
- async (argv) => {
179
- const version = argv["app-version"];
180
- const androidBuildNumber = argv["android-build"];
181
- const iosBuildNumber = argv["ios-build"];
41
+ // Collect all commands
42
+ const allCommands: CommandModule[] = [
43
+ ...buildCommands,
44
+ ...cleanCommands,
45
+ ...versionCommands,
46
+ ...projectCommands,
47
+ ...configCommands,
48
+ ...mcpCommands,
49
+ ];
182
50
 
183
- await updateFlutterVersion(version, androidBuildNumber, iosBuildNumber);
184
- }
185
- )
186
- .command("open-ios", "Open the iOS project in Xcode", {}, async () => {
187
- await openIos();
51
+ // Build CLI
52
+ const cli = yargs(hideBin(process.argv))
53
+ .scriptName("optikit")
54
+ .option("dry-run", {
55
+ type: "boolean",
56
+ default: false,
57
+ description: "Preview operations without executing them",
58
+ global: true,
188
59
  })
189
- .command(
190
- "open-android",
191
- "Open the Android project in Android Studio",
192
- {},
193
- async () => {
194
- await openAndroid();
60
+ .middleware((argv) => {
61
+ if (argv.dryRun) {
62
+ setDryRunMode(true);
195
63
  }
196
- )
197
- .command(
198
- "open-ipa",
199
- "Open the IPA build output directory",
200
- {},
201
- async () => {
202
- await openIpaOutput();
203
- }
204
- )
205
- .command(
206
- "open-apk",
207
- "Open the APK build output directory",
208
- {},
209
- async () => {
210
- await openApkOutput();
64
+ if (config.useFvmByDefault === false && !process.argv.includes("--disable-fvm")) {
65
+ argv.disableFvm = true;
211
66
  }
212
- )
213
- .command(
214
- "open-bundle",
215
- "Open the Android Bundle build output directory",
216
- {},
217
- async () => {
218
- await openBundleOutput();
219
- }
220
- )
221
- .command(
222
- "setup-vscode",
223
- "Create a .vscode folder with recommended Flutter settings",
224
- () => {},
225
- async () => {
226
- await createVscodeSettings();
227
- }
228
- )
229
- .command(
230
- "init",
231
- "Initialize OptiKit configuration in the current project",
232
- () => {},
233
- async () => {
234
- await initializeProject();
235
- }
236
- )
237
- .command(
238
- "rollback",
239
- "List and restore files from OptiKit backups",
240
- (yargs) => {
241
- return yargs.option("restore", {
242
- type: "number",
243
- description: "Restore backup by index number",
244
- demandOption: false,
245
- });
246
- },
247
- async (argv) => {
248
- const restoreIndex = argv.restore as number | undefined;
249
- if (restoreIndex !== undefined) {
250
- await rollbackRestore(restoreIndex);
251
- } else {
252
- await rollbackFiles();
253
- }
254
- }
255
- )
256
- .command(
257
- "bump <type>",
258
- "Bump version (major, minor, or patch)",
259
- (yargs) => {
260
- return yargs.positional("type", {
261
- describe: "Version bump type (major, minor, patch)",
262
- type: "string",
263
- choices: ["major", "minor", "patch"],
264
- });
265
- },
266
- async (argv) => {
267
- const type = argv.type as 'major' | 'minor' | 'patch';
268
- await bumpVersion(type);
269
- }
270
- )
271
- .command(
272
- "bump-ios",
273
- "Increment iOS build number only (for TestFlight)",
274
- () => {},
275
- async () => {
276
- await bumpIosBuildOnly();
277
- }
278
- )
279
- .command(
280
- "bump-android",
281
- "Increment Android build number only",
282
- () => {},
283
- async () => {
284
- await bumpAndroidBuildOnly();
285
- }
286
- )
287
- .command(
288
- "bump-build",
289
- "Increment both Android and iOS build numbers",
290
- () => {},
291
- async () => {
292
- await bumpBothBuilds();
293
- }
294
- )
295
- .command(
296
- "version",
297
- "Show current version information",
298
- () => {},
299
- async () => {
300
- await showCurrentVersion();
301
- }
302
- )
303
- .command(
304
- "devices",
305
- "List all connected devices",
306
- (yargs) => {
307
- return yargs.option("disable-fvm", {
308
- type: "boolean",
309
- default: false,
310
- description: "Run without FVM (use --disable-fvm to enable)",
311
- });
312
- },
313
- async (argv) => {
314
- const useFvm = !argv.disableFvm;
315
- await listDevices(useFvm);
316
- }
317
- )
318
- .command(
319
- "run",
320
- "Run Flutter app on connected device",
321
- (yargs) => {
322
- return yargs
323
- .option("device", {
324
- alias: "d",
325
- type: "string",
326
- description: "Specific device ID to run on",
327
- })
328
- .option("release", {
329
- alias: "r",
330
- type: "boolean",
331
- default: false,
332
- description: "Run in release mode",
333
- })
334
- .option("flavor", {
335
- alias: "f",
336
- type: "string",
337
- description: "Build flavor to use",
338
- })
339
- .option("disable-fvm", {
340
- type: "boolean",
341
- default: false,
342
- description: "Run without FVM (use --disable-fvm to enable)",
343
- });
344
- },
345
- async (argv) => {
346
- const useFvm = !argv.disableFvm;
347
- await runApp({
348
- device: argv.device as string | undefined,
349
- release: argv.release as boolean,
350
- flavor: argv.flavor as string | undefined,
351
- useFvm,
352
- });
353
- }
354
- )
355
- .command(
356
- "run-select",
357
- "Interactive device selection and run",
358
- (yargs) => {
359
- return yargs
360
- .option("release", {
361
- alias: "r",
362
- type: "boolean",
363
- default: false,
364
- description: "Run in release mode",
365
- })
366
- .option("flavor", {
367
- alias: "f",
368
- type: "string",
369
- description: "Build flavor to use",
370
- })
371
- .option("disable-fvm", {
372
- type: "boolean",
373
- default: false,
374
- description: "Run without FVM (use --disable-fvm to enable)",
375
- });
376
- },
377
- async (argv) => {
378
- const useFvm = !argv.disableFvm;
379
- await runAppInteractive({
380
- release: argv.release as boolean,
381
- flavor: argv.flavor as string | undefined,
382
- useFvm,
383
- });
384
- }
385
- )
386
- .version(version)
387
- .help(true).argv;
67
+ });
68
+
69
+ // Register all commands
70
+ for (const command of allCommands) {
71
+ cli.command(command);
72
+ }
73
+
74
+ cli
75
+ .completion("completion", "Generate shell completion script")
76
+ .usage(chalk.white.bold("\nUsage:") + chalk.gray(" optikit <command> [options]"))
77
+ .epilogue(
78
+ chalk.cyan.bold("\nQuick Reference") + chalk.gray(" (use ok or optikit both work)\n") +
79
+ "\n" +
80
+ chalk.cyan(" Build ") + chalk.white.bold("apk aab ios ipa tf") + chalk.gray(" (testflight)\n") +
81
+ chalk.cyan(" Clean ") + chalk.white.bold("c") + chalk.gray(" (flutter) ") + chalk.white.bold("ci") + chalk.gray(" (iOS) ") + chalk.white.bold("c -a") + chalk.gray(" (all)\n") +
82
+ chalk.cyan(" Bump ") + chalk.white.bold("bump <type> bi ba bb\n") +
83
+ chalk.cyan(" Version ") + chalk.white.bold("v") + chalk.gray(" (show) ") + chalk.white.bold("vset") + chalk.gray(" (set manually)\n") +
84
+ chalk.cyan(" Generate ") + chalk.white.bold("gen module <name> gen repo <name> route <name>\n") +
85
+ chalk.cyan(" Run ") + chalk.white.bold("run rs") + chalk.gray(" (interactive) ") + chalk.white.bold("devs") + chalk.gray(" (list devices)\n") +
86
+ chalk.cyan(" Open ") + chalk.white.bold("xcode studio\n") +
87
+ chalk.cyan(" Config ") + chalk.white.bold("init vscode undo up\n") +
88
+ chalk.cyan(" Info ") + chalk.white.bold("info dr aliases\n") +
89
+ chalk.cyan(" MCP ") + chalk.white.bold("mcp setup-claude\n") +
90
+ "\n" +
91
+ chalk.cyan(" Combos ") + chalk.green("ok apk --clean -o") + chalk.gray(" | ") + chalk.green("ok ipa -b patch -o") + chalk.gray(" | ") + chalk.green("ok tf -o") +
92
+ "\n\n" +
93
+ chalk.gray(" Run ") + chalk.white("optikit <command> --help") + chalk.gray(" for details | ") +
94
+ chalk.white("optikit aliases") + chalk.gray(" for full reference")
95
+ )
96
+ .version(packageInfo.version)
97
+ .help(true)
98
+ .argv;
@@ -0,0 +1,169 @@
1
+ import type { CommandModule } from "yargs";
2
+ import {
3
+ buildFlutterApk,
4
+ buildFlutterBundle,
5
+ buildFlutterIos,
6
+ buildFlutterIpa,
7
+ } from "./releases.js";
8
+ import { cleanProject } from "../clean/flutter.js";
9
+ import { cleanIosProject } from "../clean/ios.js";
10
+ import { bumpVersion, bumpIosBuildOnly } from "../version/bump.js";
11
+ import { openApkOutput, openBundleOutput, openIpaOutput } from "../project/open.js";
12
+ import { LoggerHelpers } from "../../utils/services/logger.js";
13
+ import { runTestflight } from "./testflight.js";
14
+
15
+ const buildOptions = {
16
+ "disable-fvm": {
17
+ alias: "f",
18
+ type: "boolean" as const,
19
+ default: false,
20
+ description: "Run without FVM",
21
+ },
22
+ clean: {
23
+ type: "boolean" as const,
24
+ default: false,
25
+ description: "Clean before building",
26
+ },
27
+ open: {
28
+ alias: "o",
29
+ type: "boolean" as const,
30
+ default: false,
31
+ description: "Open output directory after build",
32
+ },
33
+ bump: {
34
+ alias: "b",
35
+ type: "string" as const,
36
+ choices: ["major", "minor", "patch"] as const,
37
+ description: "Bump version before building",
38
+ },
39
+ };
40
+
41
+ const ipaOptions = {
42
+ ...buildOptions,
43
+ "bump-ios": {
44
+ alias: "i",
45
+ type: "boolean" as const,
46
+ default: false,
47
+ description: "Bump iOS build number before building",
48
+ },
49
+ };
50
+
51
+ type BumpType = "major" | "minor" | "patch";
52
+
53
+ interface BuildFlags {
54
+ disableFvm: boolean;
55
+ clean: boolean;
56
+ open: boolean;
57
+ bump?: BumpType;
58
+ bumpIos?: boolean;
59
+ }
60
+
61
+ async function runPreBuild(flags: BuildFlags, isIosBuild: boolean): Promise<void> {
62
+ // 1. Bump version
63
+ if (flags.bump) {
64
+ LoggerHelpers.step(`Bumping ${flags.bump} version...`);
65
+ await bumpVersion(flags.bump, true);
66
+ }
67
+ if (flags.bumpIos) {
68
+ LoggerHelpers.step("Bumping iOS build number...");
69
+ await bumpIosBuildOnly(true);
70
+ }
71
+
72
+ // 2. Clean
73
+ if (flags.clean) {
74
+ LoggerHelpers.step("Cleaning project...");
75
+ await cleanProject(flags.disableFvm);
76
+ if (isIosBuild) {
77
+ await cleanIosProject(false, false);
78
+ }
79
+ }
80
+ }
81
+
82
+ async function runPostBuild(flags: BuildFlags, openFn: () => Promise<void>): Promise<void> {
83
+ // 4. Open output
84
+ if (flags.open) {
85
+ await openFn();
86
+ }
87
+ }
88
+
89
+ function parseFlags(argv: Record<string, unknown>): BuildFlags {
90
+ return {
91
+ disableFvm: argv.disableFvm as boolean,
92
+ clean: argv.clean as boolean,
93
+ open: argv.open as boolean,
94
+ bump: argv.bump as BumpType | undefined,
95
+ bumpIos: argv.bumpIos as boolean | undefined,
96
+ };
97
+ }
98
+
99
+ export const buildCommands: CommandModule[] = [
100
+ {
101
+ command: "flutter-build-apk",
102
+ aliases: ["apk"],
103
+ describe: "Build the Flutter APK with release configuration, obfuscation, and split debug info",
104
+ builder: buildOptions,
105
+ handler: async (argv) => {
106
+ const flags = parseFlags(argv);
107
+ await runPreBuild(flags, false);
108
+ await buildFlutterApk(flags.disableFvm);
109
+ await runPostBuild(flags, openApkOutput);
110
+ },
111
+ },
112
+ {
113
+ command: "flutter-build-bundle",
114
+ aliases: ["aab"],
115
+ describe: "Build the Flutter Bundle with release configuration, obfuscation, and split debug info",
116
+ builder: buildOptions,
117
+ handler: async (argv) => {
118
+ const flags = parseFlags(argv);
119
+ await runPreBuild(flags, false);
120
+ await buildFlutterBundle(flags.disableFvm);
121
+ await runPostBuild(flags, openBundleOutput);
122
+ },
123
+ },
124
+ {
125
+ command: "flutter-build-ios",
126
+ aliases: ["ios"],
127
+ describe: "Build the Flutter iOS app with release configuration and increment the build version",
128
+ builder: buildOptions,
129
+ handler: async (argv) => {
130
+ const flags = parseFlags(argv);
131
+ await runPreBuild(flags, true);
132
+ await buildFlutterIos(flags.disableFvm);
133
+ },
134
+ },
135
+ {
136
+ command: "flutter-build-ipa",
137
+ aliases: ["ipa"],
138
+ describe: "Create a release IPA with an updated build version number",
139
+ builder: ipaOptions,
140
+ handler: async (argv) => {
141
+ const flags = parseFlags(argv);
142
+ await runPreBuild(flags, true);
143
+ await buildFlutterIpa(flags.disableFvm);
144
+ await runPostBuild(flags, openIpaOutput);
145
+ },
146
+ },
147
+ {
148
+ command: "testflight",
149
+ aliases: ["tf"],
150
+ describe: "Bump iOS build number and build IPA (TestFlight workflow)",
151
+ builder: {
152
+ "disable-fvm": {
153
+ alias: "f",
154
+ type: "boolean" as const,
155
+ default: false,
156
+ description: "Run without FVM",
157
+ },
158
+ open: {
159
+ alias: "o",
160
+ type: "boolean" as const,
161
+ default: false,
162
+ description: "Open IPA output directory after build",
163
+ },
164
+ },
165
+ handler: async (argv) => {
166
+ await runTestflight(argv.disableFvm as boolean, argv.open as boolean);
167
+ },
168
+ },
169
+ ];
@@ -0,0 +1,18 @@
1
+ import { bumpIosBuildOnly } from "../version/bump.js";
2
+ import { buildFlutterIpa } from "./releases.js";
3
+ import { openIpaOutput } from "../project/open.js";
4
+ import { LoggerHelpers } from "../../utils/services/logger.js";
5
+
6
+ export { runTestflight };
7
+
8
+ async function runTestflight(disableFvm: boolean, open: boolean): Promise<void> {
9
+ LoggerHelpers.step("Bumping iOS build number...");
10
+ await bumpIosBuildOnly(true);
11
+
12
+ LoggerHelpers.step("Building IPA...");
13
+ await buildFlutterIpa(disableFvm);
14
+
15
+ if (open) {
16
+ await openIpaOutput();
17
+ }
18
+ }