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.
- package/.claude/commands/build.md +57 -0
- package/.claude/commands/clean.md +45 -0
- package/.claude/commands/generate.md +64 -0
- package/.claude/commands/rollback.md +67 -0
- package/.claude/commands/run.md +63 -0
- package/.claude/commands/setup.md +69 -0
- package/.claude/commands/sync-docs.md +148 -0
- package/.claude/commands/version.md +63 -0
- package/.claude/settings.local.json +28 -0
- package/.claude-plugin/marketplace.json +36 -0
- package/.claude-plugin/plugin.json +25 -0
- package/.history/README_20260325211923.md +268 -0
- package/.history/README_20260325212116.md +268 -0
- package/.history/README_20260325212234.md +266 -0
- package/.history/README_20260325221838.md +321 -0
- package/.history/README_20260325221920.md +327 -0
- package/.history/README_20260325222245.md +328 -0
- package/.history/README_20260325222247.md +328 -0
- package/.mcp.json +8 -0
- package/CHANGELOG.md +79 -46
- package/CLAUDE.md +57 -206
- package/OPTIKIT_AGENT.md +398 -0
- package/README.md +293 -60
- package/dist/cli.js +75 -241
- package/dist/commands/build/commands.js +146 -0
- package/dist/commands/build/testflight.js +14 -0
- package/dist/commands/clean/commands.js +41 -0
- package/dist/commands/clean/flutter.js +8 -14
- package/dist/commands/clean/ios.js +12 -15
- package/dist/commands/config/aliases.js +122 -0
- package/dist/commands/config/commands.js +49 -0
- package/dist/commands/config/initApp.js +191 -0
- package/dist/commands/config/rollback.js +15 -4
- package/dist/commands/config/upgrade.js +36 -0
- package/dist/commands/mcp/commands.js +21 -0
- package/dist/commands/mcp/server.js +27 -0
- package/dist/commands/mcp/setup.js +62 -0
- package/dist/commands/mcp/tools.js +359 -0
- package/dist/commands/project/commands.js +132 -0
- package/dist/commands/project/devices.js +10 -26
- package/dist/commands/project/doctor.js +58 -0
- package/dist/commands/project/generate.js +183 -30
- package/dist/commands/project/setup.js +10 -28
- package/dist/commands/project/status.js +65 -0
- package/dist/commands/version/bump.js +96 -82
- package/dist/commands/version/commands.js +63 -0
- package/dist/commands/version/update.js +36 -24
- package/dist/constants.js +6 -1
- package/dist/styles.js +42 -5
- package/dist/utils/helpers/error.js +14 -0
- package/dist/utils/helpers/file.js +1 -1
- package/dist/utils/helpers/version.js +2 -1
- package/dist/utils/services/backup.js +12 -1
- package/dist/utils/services/command.js +1 -34
- package/dist/utils/services/exec.js +76 -101
- package/dist/utils/services/logger.js +10 -4
- package/dist/utils/validators/validation.js +24 -12
- package/docs/INSTALLATION.md +72 -0
- package/docs/TROUBLESHOOT.md +140 -0
- package/docs/USAGE.md +185 -0
- package/docs/VERSION_MANAGEMENT.md +177 -0
- package/package.json +7 -11
- package/src/cli.ts +82 -362
- package/src/commands/build/commands.ts +169 -0
- package/src/commands/build/testflight.ts +18 -0
- package/src/commands/clean/commands.ts +43 -0
- package/src/commands/clean/flutter.ts +9 -13
- package/src/commands/clean/ios.ts +13 -13
- package/src/commands/config/aliases.ts +150 -0
- package/src/commands/config/commands.ts +50 -0
- package/src/commands/config/initApp.ts +213 -0
- package/src/commands/config/rollback.ts +16 -4
- package/src/commands/config/upgrade.ts +40 -0
- package/src/commands/mcp/commands.ts +23 -0
- package/src/commands/mcp/server.ts +35 -0
- package/src/commands/mcp/setup.ts +69 -0
- package/src/commands/mcp/tools.ts +365 -0
- package/src/commands/project/commands.ts +132 -0
- package/src/commands/project/devices.ts +11 -24
- package/src/commands/project/doctor.ts +81 -0
- package/src/commands/project/generate.ts +211 -32
- package/src/commands/project/setup.ts +13 -30
- package/src/commands/project/status.ts +72 -0
- package/src/commands/version/bump.ts +124 -85
- package/src/commands/version/commands.ts +76 -0
- package/src/commands/version/update.ts +86 -75
- package/src/constants.ts +7 -1
- package/src/styles.ts +49 -7
- package/src/utils/helpers/error.ts +16 -0
- package/src/utils/helpers/file.ts +1 -1
- package/src/utils/helpers/version.ts +2 -1
- package/src/utils/services/backup.ts +17 -1
- package/src/utils/services/command.ts +1 -58
- package/src/utils/services/exec.ts +92 -117
- package/src/utils/services/logger.ts +12 -4
- package/src/utils/validators/validation.ts +24 -12
- package/CODE_QUALITY.md +0 -398
- package/ENHANCEMENTS.md +0 -310
- package/FEATURE_ENHANCEMENTS.md +0 -435
- package/INSTALLATION.md +0 -118
- package/SAFETY_FEATURES.md +0 -396
- package/TROUBLESHOOT.md +0 -60
- package/USAGE.md +0 -388
- package/VERSION_MANAGEMENT.md +0 -438
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { createInterface } from "readline";
|
|
1
2
|
import { validateFlutterProject } from "../../utils/validators/validation.js";
|
|
2
3
|
import { LoggerHelpers } from "../../utils/services/logger.js";
|
|
3
4
|
import {
|
|
@@ -7,6 +8,7 @@ import {
|
|
|
7
8
|
getNextBuildNumber,
|
|
8
9
|
getCurrentIosBuildNumber
|
|
9
10
|
} from "../../utils/helpers/version.js";
|
|
11
|
+
import { handleCommandError } from "../../utils/helpers/error.js";
|
|
10
12
|
import { updateFlutterVersion } from "./update.js";
|
|
11
13
|
import chalk from "chalk";
|
|
12
14
|
|
|
@@ -14,27 +16,70 @@ export {
|
|
|
14
16
|
bumpVersion,
|
|
15
17
|
bumpIosBuildOnly,
|
|
16
18
|
bumpAndroidBuildOnly,
|
|
19
|
+
bumpBothBuilds,
|
|
17
20
|
showCurrentVersion
|
|
18
21
|
};
|
|
19
22
|
|
|
20
23
|
/**
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
* iOS build number resets to 1 on version change
|
|
24
|
-
*
|
|
25
|
-
* @param type - Type of version bump (major, minor, patch)
|
|
24
|
+
* Prompts user for confirmation. Returns true if confirmed.
|
|
25
|
+
* Skips prompt and returns true if skipConfirm is true (--yes flag).
|
|
26
26
|
*/
|
|
27
|
+
function confirmAction(message: string, skipConfirm: boolean): Promise<boolean> {
|
|
28
|
+
if (skipConfirm) return Promise.resolve(true);
|
|
29
|
+
|
|
30
|
+
return new Promise((resolve) => {
|
|
31
|
+
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
32
|
+
rl.question(chalk.yellow(`${message} (y/n): `), (answer) => {
|
|
33
|
+
rl.close();
|
|
34
|
+
resolve(answer.toLowerCase() === "y");
|
|
35
|
+
});
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Shows a colored diff preview of what will change.
|
|
41
|
+
*/
|
|
42
|
+
function showDiffPreview(
|
|
43
|
+
oldVersion: string,
|
|
44
|
+
newVersion: string,
|
|
45
|
+
oldAndroidBuild: number,
|
|
46
|
+
newAndroidBuild: number | string,
|
|
47
|
+
oldIosBuild: number,
|
|
48
|
+
newIosBuild: number | string
|
|
49
|
+
) {
|
|
50
|
+
console.log(chalk.cyan("\nFile changes preview:"));
|
|
51
|
+
|
|
52
|
+
if (newAndroidBuild !== "") {
|
|
53
|
+
console.log(chalk.gray("\n pubspec.yaml:"));
|
|
54
|
+
console.log(chalk.red(` - version: ${oldVersion}+${oldAndroidBuild}`));
|
|
55
|
+
console.log(chalk.green(` + version: ${newVersion}+${newAndroidBuild}`));
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
if (newIosBuild !== "") {
|
|
59
|
+
console.log(chalk.gray("\n project.pbxproj:"));
|
|
60
|
+
console.log(chalk.red(` - MARKETING_VERSION = ${oldVersion};`));
|
|
61
|
+
console.log(chalk.green(` + MARKETING_VERSION = ${newVersion};`));
|
|
62
|
+
console.log(chalk.red(` - CURRENT_PROJECT_VERSION = ${oldIosBuild};`));
|
|
63
|
+
console.log(chalk.green(` + CURRENT_PROJECT_VERSION = ${newIosBuild};`));
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
console.log();
|
|
67
|
+
}
|
|
68
|
+
|
|
27
69
|
async function bumpVersion(
|
|
28
|
-
type: 'major' | 'minor' | 'patch'
|
|
70
|
+
type: 'major' | 'minor' | 'patch',
|
|
71
|
+
skipConfirm: boolean = false
|
|
29
72
|
): Promise<void> {
|
|
30
|
-
// Pre-flight validation
|
|
31
73
|
if (!validateFlutterProject()) {
|
|
32
74
|
process.exit(1);
|
|
33
75
|
}
|
|
34
76
|
|
|
35
77
|
try {
|
|
36
78
|
const current = getCurrentVersion();
|
|
79
|
+
const currentIosBuild = getCurrentIosBuildNumber();
|
|
37
80
|
const newVersion = incrementVersion(current, type);
|
|
81
|
+
const currentVersionString = `${current.major}.${current.minor}.${current.patch}`;
|
|
82
|
+
const newVersionString = `${newVersion.major}.${newVersion.minor}.${newVersion.patch}`;
|
|
38
83
|
|
|
39
84
|
LoggerHelpers.info(`Current version: ${formatVersion(current)}`);
|
|
40
85
|
LoggerHelpers.info(`Bumping ${type} version...`);
|
|
@@ -45,16 +90,20 @@ async function bumpVersion(
|
|
|
45
90
|
|
|
46
91
|
console.log(chalk.cyan("\nBuild number strategy:"));
|
|
47
92
|
console.log(chalk.gray(" Android:"), chalk.white(`${current.buildNumber} → ${newVersion.buildNumber} (incremented)`));
|
|
48
|
-
console.log(chalk.gray(" iOS:"), chalk.white(`${
|
|
49
|
-
|
|
93
|
+
console.log(chalk.gray(" iOS:"), chalk.white(`${currentIosBuild} → 1 (reset for new version)`));
|
|
94
|
+
|
|
95
|
+
showDiffPreview(currentVersionString, newVersionString, current.buildNumber, newVersion.buildNumber, currentIosBuild, 1);
|
|
96
|
+
|
|
97
|
+
const confirmed = await confirmAction("Proceed with version bump?", skipConfirm);
|
|
98
|
+
if (!confirmed) {
|
|
99
|
+
LoggerHelpers.info("Bump cancelled.");
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
50
102
|
|
|
51
|
-
// Update with new version
|
|
52
|
-
// Android uses the incremented build number from version
|
|
53
|
-
// iOS gets reset to 1 for new version releases
|
|
54
103
|
await updateFlutterVersion(
|
|
55
|
-
|
|
104
|
+
newVersionString,
|
|
56
105
|
newVersion.buildNumber.toString(),
|
|
57
|
-
"1"
|
|
106
|
+
"1"
|
|
58
107
|
);
|
|
59
108
|
|
|
60
109
|
LoggerHelpers.success(`Version bumped to ${formatVersion(newVersion)}`);
|
|
@@ -62,24 +111,11 @@ async function bumpVersion(
|
|
|
62
111
|
console.log(chalk.gray("iOS build:"), chalk.white("1"));
|
|
63
112
|
|
|
64
113
|
} catch (error) {
|
|
65
|
-
|
|
66
|
-
LoggerHelpers.error(`Error bumping version: ${error.message}`);
|
|
67
|
-
} else {
|
|
68
|
-
LoggerHelpers.error(`Error bumping version: ${error}`);
|
|
69
|
-
}
|
|
70
|
-
process.exit(1);
|
|
114
|
+
handleCommandError(error, "Error bumping version");
|
|
71
115
|
}
|
|
72
116
|
}
|
|
73
117
|
|
|
74
|
-
|
|
75
|
-
* Increments ONLY iOS build number (for TestFlight builds)
|
|
76
|
-
* Keeps version and Android build number unchanged
|
|
77
|
-
* Perfect for uploading new iOS builds without changing app version
|
|
78
|
-
*
|
|
79
|
-
* Example: 1.0.2+45 (iOS: 45) → 1.0.2+45 (iOS: 46)
|
|
80
|
-
*/
|
|
81
|
-
async function bumpIosBuildOnly(): Promise<void> {
|
|
82
|
-
// Pre-flight validation
|
|
118
|
+
async function bumpIosBuildOnly(skipConfirm: boolean = false): Promise<void> {
|
|
83
119
|
if (!validateFlutterProject()) {
|
|
84
120
|
process.exit(1);
|
|
85
121
|
}
|
|
@@ -87,8 +123,6 @@ async function bumpIosBuildOnly(): Promise<void> {
|
|
|
87
123
|
try {
|
|
88
124
|
const current = getCurrentVersion();
|
|
89
125
|
const currentVersionString = `${current.major}.${current.minor}.${current.patch}`;
|
|
90
|
-
|
|
91
|
-
// Read actual iOS build number from project.pbxproj
|
|
92
126
|
const currentIosBuild = getCurrentIosBuildNumber();
|
|
93
127
|
const nextIosBuild = currentIosBuild + 1;
|
|
94
128
|
|
|
@@ -99,35 +133,25 @@ async function bumpIosBuildOnly(): Promise<void> {
|
|
|
99
133
|
console.log(chalk.gray(" Version:"), chalk.white(`${currentVersionString} (unchanged)`));
|
|
100
134
|
console.log(chalk.gray(" Android:"), chalk.white(`${current.buildNumber} (unchanged)`));
|
|
101
135
|
console.log(chalk.gray(" iOS:"), chalk.white(`${currentIosBuild} → ${nextIosBuild}`), chalk.green("(incremented)"));
|
|
102
|
-
console.log();
|
|
103
136
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
137
|
+
showDiffPreview(currentVersionString, currentVersionString, current.buildNumber, "", currentIosBuild, nextIosBuild);
|
|
138
|
+
|
|
139
|
+
const confirmed = await confirmAction("Proceed?", skipConfirm);
|
|
140
|
+
if (!confirmed) {
|
|
141
|
+
LoggerHelpers.info("Bump cancelled.");
|
|
142
|
+
return;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
await updateFlutterVersion(currentVersionString, "", nextIosBuild.toString());
|
|
110
146
|
|
|
111
147
|
LoggerHelpers.success(`iOS build number incremented to ${nextIosBuild}`);
|
|
112
|
-
console.log(chalk.gray("\nResult:"), chalk.white(`${currentVersionString}+${current.buildNumber} (iOS: ${nextIosBuild})`));
|
|
113
|
-
console.log(chalk.gray("Use this for:"), chalk.white("TestFlight uploads without version changes"));
|
|
114
148
|
|
|
115
149
|
} catch (error) {
|
|
116
|
-
|
|
117
|
-
LoggerHelpers.error(`Error incrementing iOS build: ${error.message}`);
|
|
118
|
-
} else {
|
|
119
|
-
LoggerHelpers.error(`Error incrementing iOS build: ${error}`);
|
|
120
|
-
}
|
|
121
|
-
process.exit(1);
|
|
150
|
+
handleCommandError(error, "Error incrementing iOS build");
|
|
122
151
|
}
|
|
123
152
|
}
|
|
124
153
|
|
|
125
|
-
|
|
126
|
-
* Increments ONLY Android build number
|
|
127
|
-
* Keeps version and iOS build number unchanged
|
|
128
|
-
*/
|
|
129
|
-
async function bumpAndroidBuildOnly(): Promise<void> {
|
|
130
|
-
// Pre-flight validation
|
|
154
|
+
async function bumpAndroidBuildOnly(skipConfirm: boolean = false): Promise<void> {
|
|
131
155
|
if (!validateFlutterProject()) {
|
|
132
156
|
process.exit(1);
|
|
133
157
|
}
|
|
@@ -135,6 +159,7 @@ async function bumpAndroidBuildOnly(): Promise<void> {
|
|
|
135
159
|
try {
|
|
136
160
|
const current = getCurrentVersion();
|
|
137
161
|
const currentVersionString = `${current.major}.${current.minor}.${current.patch}`;
|
|
162
|
+
const currentIosBuild = getCurrentIosBuildNumber();
|
|
138
163
|
const nextAndroidBuild = current.buildNumber + 1;
|
|
139
164
|
|
|
140
165
|
LoggerHelpers.info(`Current version: ${formatVersion(current)}`);
|
|
@@ -144,30 +169,61 @@ async function bumpAndroidBuildOnly(): Promise<void> {
|
|
|
144
169
|
console.log(chalk.gray(" Version:"), chalk.white(`${currentVersionString} (unchanged)`));
|
|
145
170
|
console.log(chalk.gray(" Android:"), chalk.white(`${current.buildNumber} → ${nextAndroidBuild}`), chalk.green("(incremented)"));
|
|
146
171
|
console.log(chalk.gray(" iOS:"), chalk.white("(unchanged)"));
|
|
147
|
-
console.log();
|
|
148
172
|
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
""
|
|
154
|
-
|
|
173
|
+
showDiffPreview(currentVersionString, currentVersionString, current.buildNumber, nextAndroidBuild, currentIosBuild, "");
|
|
174
|
+
|
|
175
|
+
const confirmed = await confirmAction("Proceed?", skipConfirm);
|
|
176
|
+
if (!confirmed) {
|
|
177
|
+
LoggerHelpers.info("Bump cancelled.");
|
|
178
|
+
return;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
await updateFlutterVersion(currentVersionString, nextAndroidBuild.toString(), "");
|
|
155
182
|
|
|
156
183
|
LoggerHelpers.success(`Android build number incremented to ${nextAndroidBuild}`);
|
|
157
184
|
|
|
158
185
|
} catch (error) {
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
186
|
+
handleCommandError(error, "Error incrementing Android build");
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
async function bumpBothBuilds(skipConfirm: boolean = false): Promise<void> {
|
|
191
|
+
if (!validateFlutterProject()) {
|
|
164
192
|
process.exit(1);
|
|
165
193
|
}
|
|
194
|
+
|
|
195
|
+
try {
|
|
196
|
+
const current = getCurrentVersion();
|
|
197
|
+
const currentVersionString = `${current.major}.${current.minor}.${current.patch}`;
|
|
198
|
+
const currentIosBuild = getCurrentIosBuildNumber();
|
|
199
|
+
const nextAndroidBuild = current.buildNumber + 1;
|
|
200
|
+
const nextIosBuild = currentIosBuild + 1;
|
|
201
|
+
|
|
202
|
+
LoggerHelpers.info(`Current version: ${formatVersion(current)}`);
|
|
203
|
+
LoggerHelpers.info("Incrementing both Android and iOS build numbers...");
|
|
204
|
+
|
|
205
|
+
console.log(chalk.cyan("\nBuild number changes:"));
|
|
206
|
+
console.log(chalk.gray(" Version:"), chalk.white(`${currentVersionString} (unchanged)`));
|
|
207
|
+
console.log(chalk.gray(" Android:"), chalk.white(`${current.buildNumber} → ${nextAndroidBuild}`), chalk.green("(incremented)"));
|
|
208
|
+
console.log(chalk.gray(" iOS:"), chalk.white(`${currentIosBuild} → ${nextIosBuild}`), chalk.green("(incremented)"));
|
|
209
|
+
|
|
210
|
+
showDiffPreview(currentVersionString, currentVersionString, current.buildNumber, nextAndroidBuild, currentIosBuild, nextIosBuild);
|
|
211
|
+
|
|
212
|
+
const confirmed = await confirmAction("Proceed?", skipConfirm);
|
|
213
|
+
if (!confirmed) {
|
|
214
|
+
LoggerHelpers.info("Bump cancelled.");
|
|
215
|
+
return;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
await updateFlutterVersion(currentVersionString, nextAndroidBuild.toString(), nextIosBuild.toString());
|
|
219
|
+
|
|
220
|
+
LoggerHelpers.success(`Both build numbers incremented successfully`);
|
|
221
|
+
|
|
222
|
+
} catch (error) {
|
|
223
|
+
handleCommandError(error, "Error incrementing build numbers");
|
|
224
|
+
}
|
|
166
225
|
}
|
|
167
226
|
|
|
168
|
-
/**
|
|
169
|
-
* Shows current version information
|
|
170
|
-
*/
|
|
171
227
|
async function showCurrentVersion(): Promise<void> {
|
|
172
228
|
try {
|
|
173
229
|
const current = getCurrentVersion();
|
|
@@ -184,23 +240,6 @@ async function showCurrentVersion(): Promise<void> {
|
|
|
184
240
|
console.log();
|
|
185
241
|
|
|
186
242
|
} catch (error) {
|
|
187
|
-
|
|
188
|
-
LoggerHelpers.error(`Error reading version: ${error.message}`);
|
|
189
|
-
} else {
|
|
190
|
-
LoggerHelpers.error(`Error reading version: ${error}`);
|
|
191
|
-
}
|
|
192
|
-
process.exit(1);
|
|
243
|
+
handleCommandError(error, "Error reading version");
|
|
193
244
|
}
|
|
194
245
|
}
|
|
195
|
-
|
|
196
|
-
/**
|
|
197
|
-
* Helper to get next iOS build number
|
|
198
|
-
* In the future, this could read from Info.plist or project.pbxproj
|
|
199
|
-
* For now, we'll use a simple increment
|
|
200
|
-
*/
|
|
201
|
-
async function getNextIosBuildNumber(): Promise<number> {
|
|
202
|
-
// TODO: Read actual iOS build number from Info.plist or project.pbxproj
|
|
203
|
-
// For now, we'll just increment based on timestamp or simple counter
|
|
204
|
-
const current = getCurrentVersion();
|
|
205
|
-
return current.buildNumber + 1;
|
|
206
|
-
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import type { CommandModule } from "yargs";
|
|
2
|
+
import {
|
|
3
|
+
bumpVersion,
|
|
4
|
+
bumpIosBuildOnly,
|
|
5
|
+
bumpAndroidBuildOnly,
|
|
6
|
+
bumpBothBuilds,
|
|
7
|
+
showCurrentVersion,
|
|
8
|
+
} from "./bump.js";
|
|
9
|
+
import { updateFlutterVersion } from "./update.js";
|
|
10
|
+
|
|
11
|
+
const yesOption = {
|
|
12
|
+
yes: { alias: "y", type: "boolean" as const, default: false, description: "Skip confirmation prompt" },
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
export const versionCommands: CommandModule[] = [
|
|
16
|
+
{
|
|
17
|
+
command: "bump <type>",
|
|
18
|
+
describe: "Bump version (major, minor, or patch)",
|
|
19
|
+
builder: (yargs) => {
|
|
20
|
+
return yargs
|
|
21
|
+
.positional("type", {
|
|
22
|
+
describe: "Version bump type (major, minor, patch)",
|
|
23
|
+
type: "string" as const,
|
|
24
|
+
choices: ["major", "minor", "patch"] as const,
|
|
25
|
+
})
|
|
26
|
+
.option("yes", yesOption.yes);
|
|
27
|
+
},
|
|
28
|
+
handler: async (argv) => {
|
|
29
|
+
await bumpVersion(argv.type as "major" | "minor" | "patch", argv.yes as boolean);
|
|
30
|
+
},
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
command: "bump-ios",
|
|
34
|
+
aliases: ["bi"],
|
|
35
|
+
describe: "Increment iOS build number only (for TestFlight)",
|
|
36
|
+
builder: yesOption,
|
|
37
|
+
handler: async (argv) => { await bumpIosBuildOnly(argv.yes as boolean); },
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
command: "bump-android",
|
|
41
|
+
aliases: ["ba"],
|
|
42
|
+
describe: "Increment Android build number only",
|
|
43
|
+
builder: yesOption,
|
|
44
|
+
handler: async (argv) => { await bumpAndroidBuildOnly(argv.yes as boolean); },
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
command: "bump-build",
|
|
48
|
+
aliases: ["bb"],
|
|
49
|
+
describe: "Increment both Android and iOS build numbers",
|
|
50
|
+
builder: yesOption,
|
|
51
|
+
handler: async (argv) => { await bumpBothBuilds(argv.yes as boolean); },
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
command: "version",
|
|
55
|
+
aliases: ["v"],
|
|
56
|
+
describe: "Show current version information",
|
|
57
|
+
handler: async () => { await showCurrentVersion(); },
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
command: "flutter-update-version",
|
|
61
|
+
aliases: ["vset"],
|
|
62
|
+
describe: "Update version and build numbers for both Android and iOS",
|
|
63
|
+
builder: {
|
|
64
|
+
"app-version": { type: "string", description: "The version number to set", default: "" },
|
|
65
|
+
"android-build": { type: "string", description: "The Android build number", default: "" },
|
|
66
|
+
"ios-build": { type: "string", description: "The iOS build number", default: "" },
|
|
67
|
+
},
|
|
68
|
+
handler: async (argv) => {
|
|
69
|
+
await updateFlutterVersion(
|
|
70
|
+
argv["app-version"] as string,
|
|
71
|
+
argv["android-build"] as string,
|
|
72
|
+
argv["ios-build"] as string
|
|
73
|
+
);
|
|
74
|
+
},
|
|
75
|
+
},
|
|
76
|
+
];
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import fs from "fs";
|
|
2
2
|
import path from "path";
|
|
3
|
-
import { execInIos } from "../../utils/services/exec.js";
|
|
4
3
|
import { LoggerHelpers } from "../../utils/services/logger.js";
|
|
5
4
|
import { validateFlutterProject } from "../../utils/validators/validation.js";
|
|
6
|
-
import {
|
|
5
|
+
import { createBackupWithCleanup } from "../../utils/services/backup.js";
|
|
6
|
+
import { handleCommandError } from "../../utils/helpers/error.js";
|
|
7
|
+
import { isDryRunMode, DryRunManager } from "../../utils/helpers/dryRun.js";
|
|
8
|
+
import { PROJECT_PATHS } from "../../constants.js";
|
|
7
9
|
|
|
8
10
|
const currentDir = process.cwd();
|
|
9
11
|
|
|
@@ -14,59 +16,66 @@ async function updateFlutterVersion(
|
|
|
14
16
|
build: string,
|
|
15
17
|
iosBuild: string
|
|
16
18
|
) {
|
|
17
|
-
// Pre-flight validation
|
|
18
19
|
if (!validateFlutterProject()) {
|
|
19
20
|
process.exit(1);
|
|
20
21
|
}
|
|
21
22
|
|
|
22
23
|
try {
|
|
23
|
-
// Always update the version since it is required.
|
|
24
24
|
LoggerHelpers.info(`Starting update for version ${version}...`);
|
|
25
25
|
|
|
26
|
-
//
|
|
26
|
+
// Single dry-run manager for all operations
|
|
27
|
+
const dryRun = isDryRunMode() ? new DryRunManager() : null;
|
|
28
|
+
|
|
27
29
|
if (build.trim().length > 0) {
|
|
28
30
|
LoggerHelpers.info("Updating build number in pubspec.yaml...");
|
|
29
|
-
await updatePubspecVersionAndBuild(version, build);
|
|
31
|
+
await updatePubspecVersionAndBuild(version, build, dryRun);
|
|
30
32
|
} else {
|
|
31
33
|
LoggerHelpers.info("Android build number not provided. Skipping pubspec.yaml update.");
|
|
32
34
|
}
|
|
33
35
|
|
|
34
|
-
// Update iOS settings only if an iOS build number is provided.
|
|
35
36
|
if (iosBuild.trim().length > 0) {
|
|
36
37
|
LoggerHelpers.info("Updating iOS version and build number...");
|
|
37
|
-
await updateIosVersionAndBuild(version, iosBuild);
|
|
38
|
+
await updateIosVersionAndBuild(version, iosBuild, dryRun);
|
|
38
39
|
} else {
|
|
39
40
|
LoggerHelpers.info("iOS build number not provided. Skipping iOS update.");
|
|
40
41
|
}
|
|
41
42
|
|
|
43
|
+
// Show dry-run summary once at the end
|
|
44
|
+
if (dryRun) {
|
|
45
|
+
dryRun.displaySummary();
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
|
|
42
49
|
LoggerHelpers.success(
|
|
43
50
|
`Update complete. Version set to ${version}` +
|
|
44
51
|
(build.trim().length > 0 ? `, Android build set to ${build}` : "") +
|
|
45
52
|
(iosBuild.trim().length > 0 ? `, and iOS build set to ${iosBuild}` : "")
|
|
46
53
|
);
|
|
47
54
|
} catch (error: unknown) {
|
|
48
|
-
|
|
49
|
-
`Error while updating Flutter version and build: ${
|
|
50
|
-
error instanceof Error ? error.message : String(error)
|
|
51
|
-
}`
|
|
52
|
-
);
|
|
53
|
-
process.exit(1);
|
|
55
|
+
handleCommandError(error, "Error while updating Flutter version and build");
|
|
54
56
|
}
|
|
55
57
|
}
|
|
56
58
|
|
|
57
|
-
async function updatePubspecVersionAndBuild(
|
|
59
|
+
async function updatePubspecVersionAndBuild(
|
|
60
|
+
version: string,
|
|
61
|
+
build: string,
|
|
62
|
+
dryRun: DryRunManager | null
|
|
63
|
+
) {
|
|
58
64
|
try {
|
|
59
|
-
const pubspecPath = path.join(currentDir,
|
|
65
|
+
const pubspecPath = path.join(currentDir, PROJECT_PATHS.PUBSPEC);
|
|
60
66
|
|
|
61
67
|
if (!fs.existsSync(pubspecPath)) {
|
|
62
68
|
throw new Error(`pubspec.yaml not found at ${pubspecPath}`);
|
|
63
69
|
}
|
|
64
70
|
|
|
65
|
-
|
|
66
|
-
|
|
71
|
+
if (dryRun) {
|
|
72
|
+
dryRun.logFileOperation("Update pubspec.yaml", pubspecPath, `version: ${version}+${build}`);
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
67
75
|
|
|
68
|
-
|
|
76
|
+
createBackupWithCleanup(pubspecPath);
|
|
69
77
|
|
|
78
|
+
const pubspecContent = fs.readFileSync(pubspecPath, "utf8");
|
|
70
79
|
const updatedPubspec = pubspecContent.replace(
|
|
71
80
|
/version: \d+\.\d+\.\d+\+\d+/g,
|
|
72
81
|
`version: ${version}+${build}`
|
|
@@ -78,74 +87,76 @@ async function updatePubspecVersionAndBuild(version: string, build: string) {
|
|
|
78
87
|
);
|
|
79
88
|
} catch (error) {
|
|
80
89
|
LoggerHelpers.error(
|
|
81
|
-
`Error while updating pubspec.yaml
|
|
90
|
+
`Error while updating pubspec.yaml: ${
|
|
82
91
|
error instanceof Error ? error.message : String(error)
|
|
83
92
|
}`
|
|
84
93
|
);
|
|
85
|
-
|
|
86
94
|
throw error;
|
|
87
95
|
}
|
|
88
96
|
}
|
|
89
97
|
|
|
90
|
-
async function updateIosVersionAndBuild(
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
// Create backup before modification
|
|
101
|
-
createBackup(projectPbxProjPath);
|
|
102
|
-
|
|
103
|
-
let projectContent = fs.readFileSync(projectPbxProjPath, 'utf8');
|
|
104
|
-
|
|
105
|
-
projectContent = projectContent
|
|
106
|
-
.replace(
|
|
107
|
-
/MARKETING_VERSION\s*=\s*[^;]+;/g,
|
|
108
|
-
`MARKETING_VERSION = ${version};`
|
|
109
|
-
)
|
|
110
|
-
.replace(
|
|
111
|
-
/CURRENT_PROJECT_VERSION\s*=\s*[^;]+;/g,
|
|
112
|
-
`CURRENT_PROJECT_VERSION = ${iosBuild};`
|
|
113
|
-
);
|
|
114
|
-
|
|
115
|
-
fs.writeFileSync(projectPbxProjPath, projectContent);
|
|
116
|
-
|
|
117
|
-
LoggerHelpers.success(`Updated MARKETING_VERSION to ${version} and CURRENT_PROJECT_VERSION to ${iosBuild}`);
|
|
118
|
-
|
|
119
|
-
const infoPlistPath = path.join(currentDir, "ios/Runner/Info.plist");
|
|
98
|
+
async function updateIosVersionAndBuild(
|
|
99
|
+
version: string,
|
|
100
|
+
iosBuild: string,
|
|
101
|
+
dryRun: DryRunManager | null
|
|
102
|
+
) {
|
|
103
|
+
try {
|
|
104
|
+
const projectPbxProjPath = path.join(currentDir, PROJECT_PATHS.IOS_PROJECT_PBXPROJ);
|
|
105
|
+
const infoPlistPath = path.join(currentDir, PROJECT_PATHS.IOS_INFO_PLIST);
|
|
120
106
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
107
|
+
if (!fs.existsSync(projectPbxProjPath)) {
|
|
108
|
+
throw new Error(`project.pbxproj not found at ${projectPbxProjPath}`);
|
|
109
|
+
}
|
|
124
110
|
|
|
125
|
-
|
|
126
|
-
|
|
111
|
+
if (dryRun) {
|
|
112
|
+
dryRun.logFileOperation("Update project.pbxproj", projectPbxProjPath, `MARKETING_VERSION = ${version}, CURRENT_PROJECT_VERSION = ${iosBuild}`);
|
|
113
|
+
dryRun.logFileOperation("Update Info.plist", infoPlistPath, `CFBundleShortVersionString = ${version}, CFBundleVersion = ${iosBuild}`);
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
127
116
|
|
|
128
|
-
|
|
117
|
+
// Update project.pbxproj
|
|
118
|
+
createBackupWithCleanup(projectPbxProjPath);
|
|
119
|
+
|
|
120
|
+
let projectContent = fs.readFileSync(projectPbxProjPath, 'utf8');
|
|
121
|
+
projectContent = projectContent
|
|
122
|
+
.replace(
|
|
123
|
+
/MARKETING_VERSION\s*=\s*[^;]+;/g,
|
|
124
|
+
`MARKETING_VERSION = ${version};`
|
|
125
|
+
)
|
|
126
|
+
.replace(
|
|
127
|
+
/CURRENT_PROJECT_VERSION\s*=\s*[^;]+;/g,
|
|
128
|
+
`CURRENT_PROJECT_VERSION = ${iosBuild};`
|
|
129
|
+
);
|
|
129
130
|
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
/<key>CFBundleShortVersionString<\/key>\s*<string>\$\{MARKETING_VERSION\}<\/string>/,
|
|
133
|
-
`<key>CFBundleShortVersionString</key><string>${version}</string>`
|
|
134
|
-
)
|
|
135
|
-
.replace(
|
|
136
|
-
/<key>CFBundleVersion<\/key>\s*<string>\$\{CURRENT_PROJECT_VERSION\}<\/string>/,
|
|
137
|
-
`<key>CFBundleVersion</key><string>${iosBuild}</string>`
|
|
138
|
-
);
|
|
131
|
+
fs.writeFileSync(projectPbxProjPath, projectContent);
|
|
132
|
+
LoggerHelpers.success(`Updated MARKETING_VERSION to ${version} and CURRENT_PROJECT_VERSION to ${iosBuild}`);
|
|
139
133
|
|
|
140
|
-
|
|
141
|
-
|
|
134
|
+
// Update Info.plist
|
|
135
|
+
if (!fs.existsSync(infoPlistPath)) {
|
|
136
|
+
throw new Error(`Info.plist not found at ${infoPlistPath}`);
|
|
137
|
+
}
|
|
142
138
|
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
139
|
+
createBackupWithCleanup(infoPlistPath);
|
|
140
|
+
|
|
141
|
+
const infoPlistContent = fs.readFileSync(infoPlistPath, "utf8");
|
|
142
|
+
const updatedPlist = infoPlistContent
|
|
143
|
+
.replace(
|
|
144
|
+
/<key>CFBundleShortVersionString<\/key>\s*<string>[^<]+<\/string>/,
|
|
145
|
+
`<key>CFBundleShortVersionString</key>\n\t<string>${version}</string>`
|
|
146
|
+
)
|
|
147
|
+
.replace(
|
|
148
|
+
/<key>CFBundleVersion<\/key>\s*<string>[^<]+<\/string>/,
|
|
149
|
+
`<key>CFBundleVersion</key>\n\t<string>${iosBuild}</string>`
|
|
148
150
|
);
|
|
149
|
-
|
|
150
|
-
|
|
151
|
+
|
|
152
|
+
fs.writeFileSync(infoPlistPath, updatedPlist);
|
|
153
|
+
LoggerHelpers.success("Updated Info.plist with the new version and iOS build.");
|
|
154
|
+
} catch (error) {
|
|
155
|
+
LoggerHelpers.error(
|
|
156
|
+
`Error while updating iOS version and build: ${
|
|
157
|
+
error instanceof Error ? error.message : String(error)
|
|
158
|
+
}`
|
|
159
|
+
);
|
|
160
|
+
throw error;
|
|
151
161
|
}
|
|
162
|
+
}
|
package/src/constants.ts
CHANGED
|
@@ -48,7 +48,7 @@ export const BACKUP_CONFIG = {
|
|
|
48
48
|
|
|
49
49
|
// Module generation
|
|
50
50
|
export const MODULE_STRUCTURE = {
|
|
51
|
-
DIRECTORIES: ["bloc", "event", "state", "screen", "import", "factory"],
|
|
51
|
+
DIRECTORIES: ["bloc", "event", "state", "screen", "import", "factory", "repo"],
|
|
52
52
|
NAME_PATTERN: /^[a-z0-9_]+$/,
|
|
53
53
|
} as const;
|
|
54
54
|
|
|
@@ -135,6 +135,12 @@ export const ERROR_MESSAGES = {
|
|
|
135
135
|
MODULE_NAME_INVALID: "Module name must contain only lowercase letters, numbers, and underscores.",
|
|
136
136
|
} as const;
|
|
137
137
|
|
|
138
|
+
// MCP server configuration
|
|
139
|
+
export const MCP_CONFIG = {
|
|
140
|
+
SERVER_NAME: "optikit",
|
|
141
|
+
CLAUDE_SETTINGS_FILE: ".claude.json",
|
|
142
|
+
} as const;
|
|
143
|
+
|
|
138
144
|
// Info messages
|
|
139
145
|
export const INFO_MESSAGES = {
|
|
140
146
|
RUN_FROM_PROJECT_ROOT: "Please run this command from the root of a Flutter project.",
|