optikit 1.2.4 → 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 +79 -46
  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 -241
  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 +96 -82
  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 -362
  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 +124 -85
  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 -388
  104. package/VERSION_MANAGEMENT.md +0 -438
package/dist/cli.js CHANGED
@@ -1,251 +1,85 @@
1
1
  #!/usr/bin/env node
2
+ // MCP mode: redirect all console output to stderr before any imports
3
+ const isMcpMode = process.argv.includes("mcp");
4
+ if (isMcpMode) {
5
+ const origLog = console.log;
6
+ console.log = (...args) => console.error(...args);
7
+ // Restore for JSON-RPC (SDK uses process.stdout directly)
8
+ void origLog;
9
+ }
2
10
  import chalk from "chalk";
3
11
  import boxen from "boxen";
4
12
  import yargs from "yargs/yargs";
5
13
  import { hideBin } from "yargs/helpers";
6
- import { generateModule } from "./commands/project/generate.js";
7
- import { cleanProject } from "./commands/clean/flutter.js";
8
- import { cleanIosProject } from "./commands/clean/ios.js";
9
- import { updateFlutterVersion } from "./commands/version/update.js";
10
- import { buildFlutterApk, buildFlutterBundle, buildFlutterIos, buildFlutterIpa, } from "./commands/build/releases.js";
11
- import { boxenOptions } from "./styles.js";
12
- import { openIos, openAndroid, openIpaOutput, openBundleOutput, openApkOutput } from "./commands/project/open.js";
13
14
  import { createRequire } from "module";
14
- import { createVscodeSettings } from "./commands/project/setup.js";
15
- import { initializeProject } from "./commands/config/init.js";
16
- import { rollbackFiles, rollbackRestore } from "./commands/config/rollback.js";
17
- import { bumpVersion, bumpIosBuildOnly, bumpAndroidBuildOnly, showCurrentVersion } from "./commands/version/bump.js";
18
- import { listDevices, runApp, runAppInteractive } from "./commands/project/devices.js";
15
+ import { bannerBoxOptions, createBanner } from "./styles.js";
16
+ import { loadConfig } from "./utils/services/config.js";
17
+ import { setDryRunMode } from "./utils/helpers/dryRun.js";
18
+ // Command modules
19
+ import { buildCommands } from "./commands/build/commands.js";
20
+ import { cleanCommands } from "./commands/clean/commands.js";
21
+ import { versionCommands } from "./commands/version/commands.js";
22
+ import { projectCommands } from "./commands/project/commands.js";
23
+ import { configCommands } from "./commands/config/commands.js";
24
+ import { mcpCommands } from "./commands/mcp/commands.js";
19
25
  const require = createRequire(import.meta.url);
20
26
  const packageInfo = require("../package.json");
21
- const version = packageInfo.version;
22
- const greeting = chalk.white.bold("Hello ^_^");
23
- console.log(boxen(greeting, boxenOptions));
24
- const options = yargs(hideBin(process.argv))
25
- .command("generate module <moduleName>", "Generate a module with structure", (yargs) => {
26
- return yargs.positional("moduleName", {
27
- describe: "The name of the module to generate",
28
- type: "string",
29
- });
30
- }, (argv) => {
31
- const moduleName = argv.moduleName;
32
- generateModule(moduleName);
33
- })
34
- .command("clean-flutter", "Clean the Flutter project", (yargs) => {
35
- return yargs.option("disable-fvm", {
36
- type: "boolean",
37
- default: false,
38
- description: "Run without FVM (use --disable-fvm to enable)",
39
- });
40
- }, (argv) => {
41
- const noFvm = argv.disableFvm;
42
- cleanProject(noFvm);
43
- })
44
- .command("clean-ios", "Clean the iOS project", (yargs) => {
45
- return yargs
46
- .option("clean-cache", {
47
- type: "boolean",
48
- default: false,
49
- description: "Run with CocoaPods cache cleaning (use --clean-cache to enable)",
50
- })
51
- .option("repo-update", {
52
- type: "boolean",
53
- default: false,
54
- description: "Run pod install with repository update (use --repo-update to enable)",
55
- });
56
- }, (argv) => {
57
- const cleanCache = argv.cleanCache;
58
- const repoUpdate = argv.repoUpdate;
59
- cleanIosProject(cleanCache, repoUpdate);
60
- })
61
- .command("flutter-build-apk", "Build the Flutter APK with release configuration, obfuscation, and split debug info", (yargs) => {
62
- return yargs.option("disable-fvm", {
63
- type: "boolean",
64
- default: false,
65
- description: "Run without FVM (use --disable-fvm to enable)",
66
- });
67
- }, async (argv) => {
68
- const noFvm = argv.disableFvm;
69
- await buildFlutterApk(noFvm);
70
- })
71
- .command("flutter-build-bundle", "Build the Flutter Bundle with release configuration, obfuscation, and split debug info", (yargs) => {
72
- return yargs.option("disable-fvm", {
73
- type: "boolean",
74
- default: false,
75
- description: "Run without FVM (use --disable-fvm to enable)",
76
- });
77
- }, async (argv) => {
78
- const noFvm = argv.disableFvm;
79
- await buildFlutterBundle(noFvm);
80
- })
81
- .command("flutter-build-ios", "Build the Flutter iOS app with release configuration and increment the build version", (yargs) => {
82
- return yargs.option("disable-fvm", {
83
- type: "boolean",
84
- default: false,
85
- description: "Run without FVM (use --disable-fvm to enable)",
86
- });
87
- }, async (argv) => {
88
- const noFvm = argv.disableFvm;
89
- await buildFlutterIos(noFvm);
90
- })
91
- .command("flutter-build-ipa", "Create a release IPA with an updated build version number", (yargs) => {
92
- return yargs.option("disable-fvm", {
93
- type: "boolean",
94
- default: false,
95
- description: "Run without FVM (use --disable-fvm to enable)",
96
- });
97
- }, async (argv) => {
98
- const noFvm = argv.disableFvm;
99
- await buildFlutterIpa(noFvm);
100
- })
101
- .command("flutter-update-version", "Update version and build numbers for both Android and iOS", (yargs) => {
102
- return yargs
103
- .option("app-version", {
104
- type: "string",
105
- description: "The version number to set for both Android and iOS",
106
- demandOption: false,
107
- default: "",
108
- })
109
- .option("android-build", {
110
- type: "string",
111
- description: "The Android build number to set in pubspec.yaml",
112
- demandOption: false,
113
- default: "",
114
- })
115
- .option("ios-build", {
116
- type: "string",
117
- description: "The iOS build number to set using agv-tool and Info.plist",
118
- demandOption: false,
119
- default: "",
120
- });
121
- }, async (argv) => {
122
- const version = argv["app-version"];
123
- const androidBuildNumber = argv["android-build"];
124
- const iosBuildNumber = argv["ios-build"];
125
- await updateFlutterVersion(version, androidBuildNumber, iosBuildNumber);
126
- })
127
- .command("open-ios", "Open the iOS project in Xcode", {}, async () => {
128
- await openIos();
129
- })
130
- .command("open-android", "Open the Android project in Android Studio", {}, async () => {
131
- await openAndroid();
132
- })
133
- .command("open-ipa", "Open the IPA build output directory", {}, async () => {
134
- await openIpaOutput();
135
- })
136
- .command("open-apk", "Open the APK build output directory", {}, async () => {
137
- await openApkOutput();
138
- })
139
- .command("open-bundle", "Open the Android Bundle build output directory", {}, async () => {
140
- await openBundleOutput();
141
- })
142
- .command("setup-vscode", "Create a .vscode folder with recommended Flutter settings", () => { }, async () => {
143
- await createVscodeSettings();
144
- })
145
- .command("init", "Initialize OptiKit configuration in the current project", () => { }, async () => {
146
- await initializeProject();
147
- })
148
- .command("rollback", "List and restore files from OptiKit backups", (yargs) => {
149
- return yargs.option("restore", {
150
- type: "number",
151
- description: "Restore backup by index number",
152
- demandOption: false,
153
- });
154
- }, async (argv) => {
155
- const restoreIndex = argv.restore;
156
- if (restoreIndex !== undefined) {
157
- await rollbackRestore(restoreIndex);
27
+ // Greeting banner (skip in MCP mode — stdout is reserved for JSON-RPC)
28
+ if (!isMcpMode) {
29
+ console.log(boxen(createBanner(packageInfo.version), bannerBoxOptions));
30
+ }
31
+ // Load config once
32
+ const config = loadConfig();
33
+ // Collect all commands
34
+ const allCommands = [
35
+ ...buildCommands,
36
+ ...cleanCommands,
37
+ ...versionCommands,
38
+ ...projectCommands,
39
+ ...configCommands,
40
+ ...mcpCommands,
41
+ ];
42
+ // Build CLI
43
+ const cli = yargs(hideBin(process.argv))
44
+ .scriptName("optikit")
45
+ .option("dry-run", {
46
+ type: "boolean",
47
+ default: false,
48
+ description: "Preview operations without executing them",
49
+ global: true,
50
+ })
51
+ .middleware((argv) => {
52
+ if (argv.dryRun) {
53
+ setDryRunMode(true);
158
54
  }
159
- else {
160
- await rollbackFiles();
55
+ if (config.useFvmByDefault === false && !process.argv.includes("--disable-fvm")) {
56
+ argv.disableFvm = true;
161
57
  }
162
- })
163
- .command("bump <type>", "Bump version (major, minor, or patch)", (yargs) => {
164
- return yargs.positional("type", {
165
- describe: "Version bump type (major, minor, patch)",
166
- type: "string",
167
- choices: ["major", "minor", "patch"],
168
- });
169
- }, async (argv) => {
170
- const type = argv.type;
171
- await bumpVersion(type);
172
- })
173
- .command("bump-ios", "Increment iOS build number only (for TestFlight)", () => { }, async () => {
174
- await bumpIosBuildOnly();
175
- })
176
- .command("bump-android", "Increment Android build number only", () => { }, async () => {
177
- await bumpAndroidBuildOnly();
178
- })
179
- .command("version", "Show current version information", () => { }, async () => {
180
- await showCurrentVersion();
181
- })
182
- .command("devices", "List all connected devices", (yargs) => {
183
- return yargs.option("disable-fvm", {
184
- type: "boolean",
185
- default: false,
186
- description: "Run without FVM (use --disable-fvm to enable)",
187
- });
188
- }, async (argv) => {
189
- const useFvm = !argv.disableFvm;
190
- await listDevices(useFvm);
191
- })
192
- .command("run", "Run Flutter app on connected device", (yargs) => {
193
- return yargs
194
- .option("device", {
195
- alias: "d",
196
- type: "string",
197
- description: "Specific device ID to run on",
198
- })
199
- .option("release", {
200
- alias: "r",
201
- type: "boolean",
202
- default: false,
203
- description: "Run in release mode",
204
- })
205
- .option("flavor", {
206
- alias: "f",
207
- type: "string",
208
- description: "Build flavor to use",
209
- })
210
- .option("disable-fvm", {
211
- type: "boolean",
212
- default: false,
213
- description: "Run without FVM (use --disable-fvm to enable)",
214
- });
215
- }, async (argv) => {
216
- const useFvm = !argv.disableFvm;
217
- await runApp({
218
- device: argv.device,
219
- release: argv.release,
220
- flavor: argv.flavor,
221
- useFvm,
222
- });
223
- })
224
- .command("run-select", "Interactive device selection and run", (yargs) => {
225
- return yargs
226
- .option("release", {
227
- alias: "r",
228
- type: "boolean",
229
- default: false,
230
- description: "Run in release mode",
231
- })
232
- .option("flavor", {
233
- alias: "f",
234
- type: "string",
235
- description: "Build flavor to use",
236
- })
237
- .option("disable-fvm", {
238
- type: "boolean",
239
- default: false,
240
- description: "Run without FVM (use --disable-fvm to enable)",
241
- });
242
- }, async (argv) => {
243
- const useFvm = !argv.disableFvm;
244
- await runAppInteractive({
245
- release: argv.release,
246
- flavor: argv.flavor,
247
- useFvm,
248
- });
249
- })
250
- .version(version)
251
- .help(true).argv;
58
+ });
59
+ // Register all commands
60
+ for (const command of allCommands) {
61
+ cli.command(command);
62
+ }
63
+ cli
64
+ .completion("completion", "Generate shell completion script")
65
+ .usage(chalk.white.bold("\nUsage:") + chalk.gray(" optikit <command> [options]"))
66
+ .epilogue(chalk.cyan.bold("\nQuick Reference") + chalk.gray(" (use ok or optikit — both work)\n") +
67
+ "\n" +
68
+ chalk.cyan(" Build ") + chalk.white.bold("apk aab ios ipa tf") + chalk.gray(" (testflight)\n") +
69
+ 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") +
70
+ chalk.cyan(" Bump ") + chalk.white.bold("bump <type> bi ba bb\n") +
71
+ chalk.cyan(" Version ") + chalk.white.bold("v") + chalk.gray(" (show) ") + chalk.white.bold("vset") + chalk.gray(" (set manually)\n") +
72
+ chalk.cyan(" Generate ") + chalk.white.bold("gen module <name> gen repo <name> route <name>\n") +
73
+ chalk.cyan(" Run ") + chalk.white.bold("run rs") + chalk.gray(" (interactive) ") + chalk.white.bold("devs") + chalk.gray(" (list devices)\n") +
74
+ chalk.cyan(" Open ") + chalk.white.bold("xcode studio\n") +
75
+ chalk.cyan(" Config ") + chalk.white.bold("init vscode undo up\n") +
76
+ chalk.cyan(" Info ") + chalk.white.bold("info dr aliases\n") +
77
+ chalk.cyan(" MCP ") + chalk.white.bold("mcp setup-claude\n") +
78
+ "\n" +
79
+ 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") +
80
+ "\n\n" +
81
+ chalk.gray(" Run ") + chalk.white("optikit <command> --help") + chalk.gray(" for details | ") +
82
+ chalk.white("optikit aliases") + chalk.gray(" for full reference"))
83
+ .version(packageInfo.version)
84
+ .help(true)
85
+ .argv;
@@ -0,0 +1,146 @@
1
+ import { buildFlutterApk, buildFlutterBundle, buildFlutterIos, buildFlutterIpa, } from "./releases.js";
2
+ import { cleanProject } from "../clean/flutter.js";
3
+ import { cleanIosProject } from "../clean/ios.js";
4
+ import { bumpVersion, bumpIosBuildOnly } from "../version/bump.js";
5
+ import { openApkOutput, openBundleOutput, openIpaOutput } from "../project/open.js";
6
+ import { LoggerHelpers } from "../../utils/services/logger.js";
7
+ import { runTestflight } from "./testflight.js";
8
+ const buildOptions = {
9
+ "disable-fvm": {
10
+ alias: "f",
11
+ type: "boolean",
12
+ default: false,
13
+ description: "Run without FVM",
14
+ },
15
+ clean: {
16
+ type: "boolean",
17
+ default: false,
18
+ description: "Clean before building",
19
+ },
20
+ open: {
21
+ alias: "o",
22
+ type: "boolean",
23
+ default: false,
24
+ description: "Open output directory after build",
25
+ },
26
+ bump: {
27
+ alias: "b",
28
+ type: "string",
29
+ choices: ["major", "minor", "patch"],
30
+ description: "Bump version before building",
31
+ },
32
+ };
33
+ const ipaOptions = {
34
+ ...buildOptions,
35
+ "bump-ios": {
36
+ alias: "i",
37
+ type: "boolean",
38
+ default: false,
39
+ description: "Bump iOS build number before building",
40
+ },
41
+ };
42
+ async function runPreBuild(flags, isIosBuild) {
43
+ // 1. Bump version
44
+ if (flags.bump) {
45
+ LoggerHelpers.step(`Bumping ${flags.bump} version...`);
46
+ await bumpVersion(flags.bump, true);
47
+ }
48
+ if (flags.bumpIos) {
49
+ LoggerHelpers.step("Bumping iOS build number...");
50
+ await bumpIosBuildOnly(true);
51
+ }
52
+ // 2. Clean
53
+ if (flags.clean) {
54
+ LoggerHelpers.step("Cleaning project...");
55
+ await cleanProject(flags.disableFvm);
56
+ if (isIosBuild) {
57
+ await cleanIosProject(false, false);
58
+ }
59
+ }
60
+ }
61
+ async function runPostBuild(flags, openFn) {
62
+ // 4. Open output
63
+ if (flags.open) {
64
+ await openFn();
65
+ }
66
+ }
67
+ function parseFlags(argv) {
68
+ return {
69
+ disableFvm: argv.disableFvm,
70
+ clean: argv.clean,
71
+ open: argv.open,
72
+ bump: argv.bump,
73
+ bumpIos: argv.bumpIos,
74
+ };
75
+ }
76
+ export const buildCommands = [
77
+ {
78
+ command: "flutter-build-apk",
79
+ aliases: ["apk"],
80
+ describe: "Build the Flutter APK with release configuration, obfuscation, and split debug info",
81
+ builder: buildOptions,
82
+ handler: async (argv) => {
83
+ const flags = parseFlags(argv);
84
+ await runPreBuild(flags, false);
85
+ await buildFlutterApk(flags.disableFvm);
86
+ await runPostBuild(flags, openApkOutput);
87
+ },
88
+ },
89
+ {
90
+ command: "flutter-build-bundle",
91
+ aliases: ["aab"],
92
+ describe: "Build the Flutter Bundle with release configuration, obfuscation, and split debug info",
93
+ builder: buildOptions,
94
+ handler: async (argv) => {
95
+ const flags = parseFlags(argv);
96
+ await runPreBuild(flags, false);
97
+ await buildFlutterBundle(flags.disableFvm);
98
+ await runPostBuild(flags, openBundleOutput);
99
+ },
100
+ },
101
+ {
102
+ command: "flutter-build-ios",
103
+ aliases: ["ios"],
104
+ describe: "Build the Flutter iOS app with release configuration and increment the build version",
105
+ builder: buildOptions,
106
+ handler: async (argv) => {
107
+ const flags = parseFlags(argv);
108
+ await runPreBuild(flags, true);
109
+ await buildFlutterIos(flags.disableFvm);
110
+ },
111
+ },
112
+ {
113
+ command: "flutter-build-ipa",
114
+ aliases: ["ipa"],
115
+ describe: "Create a release IPA with an updated build version number",
116
+ builder: ipaOptions,
117
+ handler: async (argv) => {
118
+ const flags = parseFlags(argv);
119
+ await runPreBuild(flags, true);
120
+ await buildFlutterIpa(flags.disableFvm);
121
+ await runPostBuild(flags, openIpaOutput);
122
+ },
123
+ },
124
+ {
125
+ command: "testflight",
126
+ aliases: ["tf"],
127
+ describe: "Bump iOS build number and build IPA (TestFlight workflow)",
128
+ builder: {
129
+ "disable-fvm": {
130
+ alias: "f",
131
+ type: "boolean",
132
+ default: false,
133
+ description: "Run without FVM",
134
+ },
135
+ open: {
136
+ alias: "o",
137
+ type: "boolean",
138
+ default: false,
139
+ description: "Open IPA output directory after build",
140
+ },
141
+ },
142
+ handler: async (argv) => {
143
+ await runTestflight(argv.disableFvm, argv.open);
144
+ },
145
+ },
146
+ ];
@@ -0,0 +1,14 @@
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
+ export { runTestflight };
6
+ async function runTestflight(disableFvm, open) {
7
+ LoggerHelpers.step("Bumping iOS build number...");
8
+ await bumpIosBuildOnly(true);
9
+ LoggerHelpers.step("Building IPA...");
10
+ await buildFlutterIpa(disableFvm);
11
+ if (open) {
12
+ await openIpaOutput();
13
+ }
14
+ }
@@ -0,0 +1,41 @@
1
+ import { cleanProject } from "./flutter.js";
2
+ import { cleanIosProject } from "./ios.js";
3
+ export const cleanCommands = [
4
+ {
5
+ command: "clean",
6
+ aliases: ["c"],
7
+ describe: "Clean Flutter project (add --ios or --all for iOS)",
8
+ builder: {
9
+ ios: { alias: "i", type: "boolean", default: false, description: "Also clean iOS project (pods)" },
10
+ all: { alias: "a", type: "boolean", default: false, description: "Clean both Flutter and iOS" },
11
+ "disable-fvm": { alias: "f", type: "boolean", default: false, description: "Run without FVM" },
12
+ "clean-cache": { alias: "c", type: "boolean", default: false, description: "Clean CocoaPods cache (iOS only)" },
13
+ "repo-update": { alias: "u", type: "boolean", default: false, description: "Update CocoaPods repo (iOS only)" },
14
+ },
15
+ handler: async (argv) => {
16
+ await cleanProject(argv.disableFvm);
17
+ if (argv.ios || argv.all) {
18
+ await cleanIosProject(argv.cleanCache, argv.repoUpdate);
19
+ }
20
+ },
21
+ },
22
+ {
23
+ command: "clean-flutter",
24
+ aliases: ["cf"],
25
+ describe: "Clean the Flutter project",
26
+ builder: {
27
+ "disable-fvm": { alias: "f", type: "boolean", default: false, description: "Run without FVM" },
28
+ },
29
+ handler: (argv) => { cleanProject(argv.disableFvm); },
30
+ },
31
+ {
32
+ command: "clean-ios",
33
+ aliases: ["ci"],
34
+ describe: "Clean the iOS project",
35
+ builder: {
36
+ "clean-cache": { alias: "c", type: "boolean", default: false, description: "Clean CocoaPods cache" },
37
+ "repo-update": { alias: "u", type: "boolean", default: false, description: "Update CocoaPods repo" },
38
+ },
39
+ handler: (argv) => { cleanIosProject(argv.cleanCache, argv.repoUpdate); },
40
+ },
41
+ ];
@@ -3,7 +3,9 @@ import path from "path";
3
3
  import { execCommand } from "../../utils/services/exec.js";
4
4
  import { LoggerHelpers } from "../../utils/services/logger.js";
5
5
  import { validateFlutterProject, validateFlutterSdk } from "../../utils/validators/validation.js";
6
- import { createBackup } from "../../utils/services/backup.js";
6
+ import { createBackupWithCleanup } from "../../utils/services/backup.js";
7
+ import { handleCommandError } from "../../utils/helpers/error.js";
8
+ import { getFlutterCommand } from "../../utils/services/command.js";
7
9
  export { cleanProject };
8
10
  async function cleanProject(noFvm) {
9
11
  LoggerHelpers.info(noFvm ? "Running clean without FVM..." : "Running clean with FVM...");
@@ -15,17 +17,16 @@ async function cleanProject(noFvm) {
15
17
  process.exit(1);
16
18
  }
17
19
  try {
20
+ const useFvm = !noFvm;
18
21
  // Step 1: Run flutter clean
19
- const flutterCommand = noFvm ? "flutter clean" : "fvm flutter clean";
20
22
  LoggerHelpers.info("Running Flutter clean...");
21
- await execCommand(flutterCommand);
23
+ await execCommand(getFlutterCommand("flutter clean", useFvm));
22
24
  LoggerHelpers.success("Flutter clean completed.");
23
25
  // Step 2: Remove pubspec.lock using Node.js fs (cross-platform)
24
26
  const pubspecLockPath = path.join(process.cwd(), "pubspec.lock");
25
27
  if (fs.existsSync(pubspecLockPath)) {
26
28
  LoggerHelpers.info("Removing pubspec.lock...");
27
- // Create backup before deletion
28
- createBackup(pubspecLockPath);
29
+ createBackupWithCleanup(pubspecLockPath);
29
30
  fs.unlinkSync(pubspecLockPath);
30
31
  LoggerHelpers.success("pubspec.lock removed.");
31
32
  }
@@ -33,19 +34,12 @@ async function cleanProject(noFvm) {
33
34
  LoggerHelpers.info("pubspec.lock does not exist, skipping removal.");
34
35
  }
35
36
  // Step 3: Run flutter pub get
36
- const pubGetCommand = noFvm ? "flutter pub get" : "fvm flutter pub get";
37
37
  LoggerHelpers.info("Running Flutter pub get...");
38
- await execCommand(pubGetCommand);
38
+ await execCommand(getFlutterCommand("flutter pub get", useFvm));
39
39
  LoggerHelpers.success("Flutter pub get completed.");
40
40
  LoggerHelpers.success("Project cleaned successfully.");
41
41
  }
42
42
  catch (error) {
43
- if (error instanceof Error) {
44
- LoggerHelpers.error(`Error during clean: ${error.message}`);
45
- }
46
- else {
47
- LoggerHelpers.error(`Error during clean: ${error}`);
48
- }
49
- process.exit(1);
43
+ handleCommandError(error, "Error during clean");
50
44
  }
51
45
  }
@@ -3,6 +3,8 @@ import * as path from "path";
3
3
  import { LoggerHelpers } from "../../utils/services/logger.js";
4
4
  import { execInIos, execCommand, iosDirectory, execInIosWithRetry } from "../../utils/services/exec.js";
5
5
  import { validateFlutterProject, validateIosProject } from "../../utils/validators/validation.js";
6
+ import { FLUTTER_COMMANDS, FVM_COMMANDS, RETRY_CONFIG } from "../../constants.js";
7
+ import { handleCommandError } from "../../utils/helpers/error.js";
6
8
  export { cleanIosProject };
7
9
  async function getFlutterSdkPath() {
8
10
  const fvmDir = path.join(process.cwd(), ".fvm", "flutter_sdk");
@@ -29,9 +31,13 @@ async function ensureFlutterArtifactsExist() {
29
31
  const flutterXcframeworkPath = await getFlutterXcframeworkPath();
30
32
  if (!fs.existsSync(flutterXcframeworkPath)) {
31
33
  LoggerHelpers.warning("Flutter.xcframework not found.");
34
+ const useFvm = fs.existsSync(path.join(process.cwd(), ".fvm", "flutter_sdk"));
35
+ const precacheCommand = useFvm
36
+ ? FVM_COMMANDS.PRECACHE_IOS
37
+ : FLUTTER_COMMANDS.PRECACHE_IOS;
32
38
  try {
33
39
  LoggerHelpers.info("Downloading Flutter.xcframework...");
34
- await execCommand("fvm flutter precache --ios");
40
+ await execCommand(precacheCommand);
35
41
  LoggerHelpers.success("Flutter.xcframework has been downloaded successfully.");
36
42
  }
37
43
  catch (error) {
@@ -39,7 +45,7 @@ async function ensureFlutterArtifactsExist() {
39
45
  }
40
46
  }
41
47
  else {
42
- LoggerHelpers.success("Flutter.xcframework exists. No need to run 'fvm flutter precache --ios'.");
48
+ LoggerHelpers.success("Flutter.xcframework exists. No need to run precache.");
43
49
  }
44
50
  }
45
51
  async function cleanIosProject(cleanCache, repoUpdate) {
@@ -73,7 +79,7 @@ async function cleanIosProject(cleanCache, repoUpdate) {
73
79
  if (repoUpdate) {
74
80
  LoggerHelpers.info("Updating CocoaPods repositories...");
75
81
  try {
76
- await execInIosWithRetry("pod repo update", 3, 10000);
82
+ await execInIosWithRetry("pod repo update", RETRY_CONFIG.DEFAULT_ATTEMPTS, RETRY_CONFIG.DEFAULT_DELAY_MS);
77
83
  LoggerHelpers.success("Updated CocoaPods repositories.");
78
84
  }
79
85
  catch (error) {
@@ -81,7 +87,7 @@ async function cleanIosProject(cleanCache, repoUpdate) {
81
87
  }
82
88
  LoggerHelpers.info("Installing pods with repo update...");
83
89
  try {
84
- await execInIosWithRetry("pod update", 3, 10000);
90
+ await execInIosWithRetry("pod update", RETRY_CONFIG.DEFAULT_ATTEMPTS, RETRY_CONFIG.DEFAULT_DELAY_MS);
85
91
  LoggerHelpers.success("Installed pods with repo update.");
86
92
  }
87
93
  catch (error) {
@@ -90,20 +96,11 @@ async function cleanIosProject(cleanCache, repoUpdate) {
90
96
  }
91
97
  else {
92
98
  LoggerHelpers.info("Installing pods without repo update...");
93
- await execInIosWithRetry("pod install", 3, 10000);
99
+ await execInIosWithRetry("pod install", RETRY_CONFIG.DEFAULT_ATTEMPTS, RETRY_CONFIG.DEFAULT_DELAY_MS);
94
100
  LoggerHelpers.success("Installed pods without repo update.");
95
101
  }
96
102
  }
97
103
  catch (error) {
98
- if (error instanceof Error) {
99
- LoggerHelpers.error(`Error: ${error.message}`);
100
- }
101
- else if (typeof error === "string") {
102
- LoggerHelpers.error(`Error: ${error}`);
103
- }
104
- else {
105
- LoggerHelpers.error(`Unknown error: ${JSON.stringify(error)}`);
106
- }
107
- process.exit(1);
104
+ handleCommandError(error, "Error cleaning iOS project");
108
105
  }
109
106
  }