@tmhs/mobile-mcp 0.7.0 → 0.9.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/dist/index.js +15 -1
- package/dist/index.js.map +1 -1
- package/dist/tools/addMap.d.ts +3 -0
- package/dist/tools/addMap.d.ts.map +1 -0
- package/dist/tools/addMap.js +223 -0
- package/dist/tools/addMap.js.map +1 -0
- package/dist/tools/generateForm.d.ts +3 -0
- package/dist/tools/generateForm.d.ts.map +1 -0
- package/dist/tools/generateForm.js +337 -0
- package/dist/tools/generateForm.js.map +1 -0
- package/dist/tools/generateTestFile.d.ts +3 -0
- package/dist/tools/generateTestFile.d.ts.map +1 -0
- package/dist/tools/generateTestFile.js +184 -0
- package/dist/tools/generateTestFile.js.map +1 -0
- package/dist/tools/runTests.d.ts +3 -0
- package/dist/tools/runTests.d.ts.map +1 -0
- package/dist/tools/runTests.js +165 -0
- package/dist/tools/runTests.js.map +1 -0
- package/dist/tools/setupCI.d.ts +3 -0
- package/dist/tools/setupCI.d.ts.map +1 -0
- package/dist/tools/setupCI.js +202 -0
- package/dist/tools/setupCI.js.map +1 -0
- package/dist/tools/setupI18n.d.ts +3 -0
- package/dist/tools/setupI18n.d.ts.map +1 -0
- package/dist/tools/setupI18n.js +155 -0
- package/dist/tools/setupI18n.js.map +1 -0
- package/dist/tools/setupRealtime.d.ts +3 -0
- package/dist/tools/setupRealtime.d.ts.map +1 -0
- package/dist/tools/setupRealtime.js +311 -0
- package/dist/tools/setupRealtime.js.map +1 -0
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -20,9 +20,16 @@ import { register as registerSubmitToPlayStore } from "./tools/submitToPlayStore
|
|
|
20
20
|
import { register as registerGenerateScreenshots } from "./tools/generateScreenshots.js";
|
|
21
21
|
import { register as registerAnalyzeBundle } from "./tools/analyzeBundle.js";
|
|
22
22
|
import { register as registerConfigureOTA } from "./tools/configureOTA.js";
|
|
23
|
+
import { register as registerRunTests } from "./tools/runTests.js";
|
|
24
|
+
import { register as registerSetupCI } from "./tools/setupCI.js";
|
|
25
|
+
import { register as registerGenerateTestFile } from "./tools/generateTestFile.js";
|
|
26
|
+
import { register as registerSetupI18n } from "./tools/setupI18n.js";
|
|
27
|
+
import { register as registerAddMap } from "./tools/addMap.js";
|
|
28
|
+
import { register as registerGenerateForm } from "./tools/generateForm.js";
|
|
29
|
+
import { register as registerSetupRealtime } from "./tools/setupRealtime.js";
|
|
23
30
|
const server = new McpServer({
|
|
24
31
|
name: "mobile-mcp",
|
|
25
|
-
version: "0.
|
|
32
|
+
version: "0.9.0",
|
|
26
33
|
});
|
|
27
34
|
registerCheckDevEnvironment(server);
|
|
28
35
|
registerScaffoldProject(server);
|
|
@@ -43,6 +50,13 @@ registerSubmitToPlayStore(server);
|
|
|
43
50
|
registerGenerateScreenshots(server);
|
|
44
51
|
registerAnalyzeBundle(server);
|
|
45
52
|
registerConfigureOTA(server);
|
|
53
|
+
registerRunTests(server);
|
|
54
|
+
registerSetupCI(server);
|
|
55
|
+
registerGenerateTestFile(server);
|
|
56
|
+
registerSetupI18n(server);
|
|
57
|
+
registerAddMap(server);
|
|
58
|
+
registerGenerateForm(server);
|
|
59
|
+
registerSetupRealtime(server);
|
|
46
60
|
async function main() {
|
|
47
61
|
const transport = new StdioServerTransport();
|
|
48
62
|
await server.connect(transport);
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAEA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACpE,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AAEjF,OAAO,EAAE,QAAQ,IAAI,2BAA2B,EAAE,MAAM,gCAAgC,CAAC;AACzF,OAAO,EAAE,QAAQ,IAAI,uBAAuB,EAAE,MAAM,4BAA4B,CAAC;AACjF,OAAO,EAAE,QAAQ,IAAI,mBAAmB,EAAE,MAAM,wBAAwB,CAAC;AACzE,OAAO,EAAE,QAAQ,IAAI,sBAAsB,EAAE,MAAM,2BAA2B,CAAC;AAC/E,OAAO,EAAE,QAAQ,IAAI,yBAAyB,EAAE,MAAM,8BAA8B,CAAC;AACrF,OAAO,EAAE,QAAQ,IAAI,yBAAyB,EAAE,MAAM,8BAA8B,CAAC;AACrF,OAAO,EAAE,QAAQ,IAAI,qBAAqB,EAAE,MAAM,0BAA0B,CAAC;AAC7E,OAAO,EAAE,QAAQ,IAAI,mBAAmB,EAAE,MAAM,wBAAwB,CAAC;AACzE,OAAO,EAAE,QAAQ,IAAI,wBAAwB,EAAE,MAAM,6BAA6B,CAAC;AACnF,OAAO,EAAE,QAAQ,IAAI,4BAA4B,EAAE,MAAM,iCAAiC,CAAC;AAC3F,OAAO,EAAE,QAAQ,IAAI,0BAA0B,EAAE,MAAM,+BAA+B,CAAC;AACvF,OAAO,EAAE,QAAQ,IAAI,2BAA2B,EAAE,MAAM,gCAAgC,CAAC;AACzF,OAAO,EAAE,QAAQ,IAAI,qBAAqB,EAAE,MAAM,0BAA0B,CAAC;AAC7E,OAAO,EAAE,QAAQ,IAAI,6BAA6B,EAAE,MAAM,kCAAkC,CAAC;AAC7F,OAAO,EAAE,QAAQ,IAAI,wBAAwB,EAAE,MAAM,6BAA6B,CAAC;AACnF,OAAO,EAAE,QAAQ,IAAI,yBAAyB,EAAE,MAAM,8BAA8B,CAAC;AACrF,OAAO,EAAE,QAAQ,IAAI,2BAA2B,EAAE,MAAM,gCAAgC,CAAC;AACzF,OAAO,EAAE,QAAQ,IAAI,qBAAqB,EAAE,MAAM,0BAA0B,CAAC;AAC7E,OAAO,EAAE,QAAQ,IAAI,oBAAoB,EAAE,MAAM,yBAAyB,CAAC;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"addMap.d.ts","sourceRoot":"","sources":["../../src/tools/addMap.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AA0IzE,wBAAgB,QAAQ,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI,CAkHhD"}
|
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { writeFileSync, mkdirSync, existsSync, readFileSync } from "node:fs";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
import { textResponse, errorResponse } from "../types.js";
|
|
5
|
+
const inputSchema = {
|
|
6
|
+
project_path: z
|
|
7
|
+
.string()
|
|
8
|
+
.optional()
|
|
9
|
+
.describe("Absolute path to the project root. Defaults to cwd."),
|
|
10
|
+
framework: z
|
|
11
|
+
.enum(["expo", "flutter"])
|
|
12
|
+
.optional()
|
|
13
|
+
.default("expo")
|
|
14
|
+
.describe("Project framework (default: expo)."),
|
|
15
|
+
provider: z
|
|
16
|
+
.enum(["google", "apple"])
|
|
17
|
+
.optional()
|
|
18
|
+
.default("google")
|
|
19
|
+
.describe("Map provider (default: google). Apple Maps only available on iOS."),
|
|
20
|
+
screen_name: z
|
|
21
|
+
.string()
|
|
22
|
+
.optional()
|
|
23
|
+
.default("MapScreen")
|
|
24
|
+
.describe("Name for the generated map screen component (default: MapScreen)."),
|
|
25
|
+
};
|
|
26
|
+
function generateExpoMapScreen(name) {
|
|
27
|
+
return `import { useState } from "react";
|
|
28
|
+
import { StyleSheet, View } from "react-native";
|
|
29
|
+
import MapView, { Marker, type Region } from "react-native-maps";
|
|
30
|
+
|
|
31
|
+
const INITIAL_REGION: Region = {
|
|
32
|
+
latitude: 37.7749,
|
|
33
|
+
longitude: -122.4194,
|
|
34
|
+
latitudeDelta: 0.0922,
|
|
35
|
+
longitudeDelta: 0.0421,
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
interface MapMarker {
|
|
39
|
+
id: string;
|
|
40
|
+
coordinate: { latitude: number; longitude: number };
|
|
41
|
+
title: string;
|
|
42
|
+
description?: string;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export default function ${name}() {
|
|
46
|
+
const [markers] = useState<MapMarker[]>([
|
|
47
|
+
{
|
|
48
|
+
id: "1",
|
|
49
|
+
coordinate: { latitude: 37.7749, longitude: -122.4194 },
|
|
50
|
+
title: "San Francisco",
|
|
51
|
+
description: "Starting point",
|
|
52
|
+
},
|
|
53
|
+
]);
|
|
54
|
+
|
|
55
|
+
return (
|
|
56
|
+
<View style={styles.container}>
|
|
57
|
+
<MapView
|
|
58
|
+
style={styles.map}
|
|
59
|
+
initialRegion={INITIAL_REGION}
|
|
60
|
+
showsUserLocation
|
|
61
|
+
showsMyLocationButton
|
|
62
|
+
>
|
|
63
|
+
{markers.map((marker) => (
|
|
64
|
+
<Marker
|
|
65
|
+
key={marker.id}
|
|
66
|
+
coordinate={marker.coordinate}
|
|
67
|
+
title={marker.title}
|
|
68
|
+
description={marker.description}
|
|
69
|
+
/>
|
|
70
|
+
))}
|
|
71
|
+
</MapView>
|
|
72
|
+
</View>
|
|
73
|
+
);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const styles = StyleSheet.create({
|
|
77
|
+
container: {
|
|
78
|
+
flex: 1,
|
|
79
|
+
},
|
|
80
|
+
map: {
|
|
81
|
+
width: "100%",
|
|
82
|
+
height: "100%",
|
|
83
|
+
},
|
|
84
|
+
});
|
|
85
|
+
`;
|
|
86
|
+
}
|
|
87
|
+
function generateFlutterMapScreen(name) {
|
|
88
|
+
const className = name.charAt(0).toUpperCase() + name.slice(1);
|
|
89
|
+
return `import 'package:flutter/material.dart';
|
|
90
|
+
import 'package:google_maps_flutter/google_maps_flutter.dart';
|
|
91
|
+
|
|
92
|
+
class ${className} extends StatefulWidget {
|
|
93
|
+
const ${className}({super.key});
|
|
94
|
+
|
|
95
|
+
@override
|
|
96
|
+
State<${className}> createState() => _${className}State();
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
class _${className}State extends State<${className}> {
|
|
100
|
+
static const _initialPosition = CameraPosition(
|
|
101
|
+
target: LatLng(37.7749, -122.4194),
|
|
102
|
+
zoom: 14,
|
|
103
|
+
);
|
|
104
|
+
|
|
105
|
+
final Set<Marker> _markers = {
|
|
106
|
+
const Marker(
|
|
107
|
+
markerId: MarkerId('san_francisco'),
|
|
108
|
+
position: LatLng(37.7749, -122.4194),
|
|
109
|
+
infoWindow: InfoWindow(title: 'San Francisco'),
|
|
110
|
+
),
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
GoogleMapController? _controller;
|
|
114
|
+
|
|
115
|
+
@override
|
|
116
|
+
Widget build(BuildContext context) {
|
|
117
|
+
return Scaffold(
|
|
118
|
+
appBar: AppBar(title: const Text('Map')),
|
|
119
|
+
body: GoogleMap(
|
|
120
|
+
initialCameraPosition: _initialPosition,
|
|
121
|
+
markers: _markers,
|
|
122
|
+
myLocationEnabled: true,
|
|
123
|
+
myLocationButtonEnabled: true,
|
|
124
|
+
onMapCreated: (controller) => _controller = controller,
|
|
125
|
+
),
|
|
126
|
+
);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
@override
|
|
130
|
+
void dispose() {
|
|
131
|
+
_controller?.dispose();
|
|
132
|
+
super.dispose();
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
`;
|
|
136
|
+
}
|
|
137
|
+
export function register(server) {
|
|
138
|
+
server.tool("mobile_addMap", "Add a map view with provider config, location permissions, and marker support. Generates a starter map screen component.", inputSchema, async (args) => {
|
|
139
|
+
try {
|
|
140
|
+
const root = args.project_path || process.cwd();
|
|
141
|
+
const filesCreated = [];
|
|
142
|
+
const configChanges = [];
|
|
143
|
+
if (args.framework === "flutter") {
|
|
144
|
+
const screenDir = join(root, "lib", "screens");
|
|
145
|
+
mkdirSync(screenDir, { recursive: true });
|
|
146
|
+
const fileName = args.screen_name
|
|
147
|
+
.replace(/([A-Z])/g, "_$1")
|
|
148
|
+
.toLowerCase()
|
|
149
|
+
.replace(/^_/, "");
|
|
150
|
+
const screenPath = join(screenDir, `${fileName}.dart`);
|
|
151
|
+
if (existsSync(screenPath)) {
|
|
152
|
+
return errorResponse(new Error(`File already exists: ${screenPath}`));
|
|
153
|
+
}
|
|
154
|
+
writeFileSync(screenPath, generateFlutterMapScreen(args.screen_name), "utf-8");
|
|
155
|
+
filesCreated.push(screenPath);
|
|
156
|
+
return textResponse(JSON.stringify({
|
|
157
|
+
success: true,
|
|
158
|
+
framework: "flutter",
|
|
159
|
+
provider: args.provider,
|
|
160
|
+
files_created: filesCreated,
|
|
161
|
+
next_steps: [
|
|
162
|
+
"Add google_maps_flutter to pubspec.yaml: flutter pub add google_maps_flutter",
|
|
163
|
+
"Add your API key to android/app/src/main/AndroidManifest.xml:",
|
|
164
|
+
' <meta-data android:name="com.google.android.geo.API_KEY" android:value="YOUR_KEY"/>',
|
|
165
|
+
"Add your API key to ios/Runner/AppDelegate.swift:",
|
|
166
|
+
' GMSServices.provideAPIKey("YOUR_KEY")',
|
|
167
|
+
"Add location permissions to AndroidManifest.xml and Info.plist",
|
|
168
|
+
"Get a Google Maps API key from console.cloud.google.com",
|
|
169
|
+
],
|
|
170
|
+
}, null, 2));
|
|
171
|
+
}
|
|
172
|
+
const appJsonPath = join(root, "app.json");
|
|
173
|
+
if (existsSync(appJsonPath)) {
|
|
174
|
+
const appJson = JSON.parse(readFileSync(appJsonPath, "utf-8"));
|
|
175
|
+
const expo = appJson.expo || {};
|
|
176
|
+
if (!expo.plugins)
|
|
177
|
+
expo.plugins = [];
|
|
178
|
+
const hasMapPlugin = expo.plugins.some((p) => (typeof p === "string" && p === "react-native-maps") ||
|
|
179
|
+
(Array.isArray(p) && p[0] === "react-native-maps"));
|
|
180
|
+
if (!hasMapPlugin) {
|
|
181
|
+
const mapConfig = [
|
|
182
|
+
"react-native-maps",
|
|
183
|
+
{
|
|
184
|
+
googleMapsApiKey: "GOOGLE_MAPS_API_KEY_HERE",
|
|
185
|
+
},
|
|
186
|
+
];
|
|
187
|
+
expo.plugins.push(mapConfig);
|
|
188
|
+
appJson.expo = expo;
|
|
189
|
+
writeFileSync(appJsonPath, JSON.stringify(appJson, null, 2) + "\n", "utf-8");
|
|
190
|
+
configChanges.push("Added react-native-maps plugin to app.json");
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
const screenDir = join(root, "app");
|
|
194
|
+
if (!existsSync(screenDir)) {
|
|
195
|
+
mkdirSync(screenDir, { recursive: true });
|
|
196
|
+
}
|
|
197
|
+
const screenPath = join(screenDir, `${args.screen_name.charAt(0).toLowerCase() + args.screen_name.slice(1)}.tsx`);
|
|
198
|
+
if (!existsSync(screenPath)) {
|
|
199
|
+
writeFileSync(screenPath, generateExpoMapScreen(args.screen_name), "utf-8");
|
|
200
|
+
filesCreated.push(screenPath);
|
|
201
|
+
}
|
|
202
|
+
return textResponse(JSON.stringify({
|
|
203
|
+
success: true,
|
|
204
|
+
framework: "expo",
|
|
205
|
+
provider: args.provider,
|
|
206
|
+
files_created: filesCreated,
|
|
207
|
+
config_changes: configChanges,
|
|
208
|
+
next_steps: [
|
|
209
|
+
"Install react-native-maps: npx expo install react-native-maps",
|
|
210
|
+
"Install expo-location for user location: npx expo install expo-location",
|
|
211
|
+
"Replace GOOGLE_MAPS_API_KEY_HERE in app.json with your actual key",
|
|
212
|
+
"Get a Google Maps API key from console.cloud.google.com",
|
|
213
|
+
"Add location permission with mobile_addPermission",
|
|
214
|
+
"This requires a dev build (not Expo Go) for native map rendering",
|
|
215
|
+
],
|
|
216
|
+
}, null, 2));
|
|
217
|
+
}
|
|
218
|
+
catch (err) {
|
|
219
|
+
return errorResponse(err);
|
|
220
|
+
}
|
|
221
|
+
});
|
|
222
|
+
}
|
|
223
|
+
//# sourceMappingURL=addMap.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"addMap.js","sourceRoot":"","sources":["../../src/tools/addMap.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,aAAa,EAAE,SAAS,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAC7E,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEjC,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAE1D,MAAM,WAAW,GAAG;IAClB,YAAY,EAAE,CAAC;SACZ,MAAM,EAAE;SACR,QAAQ,EAAE;SACV,QAAQ,CAAC,qDAAqD,CAAC;IAClE,SAAS,EAAE,CAAC;SACT,IAAI,CAAC,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;SACzB,QAAQ,EAAE;SACV,OAAO,CAAC,MAAM,CAAC;SACf,QAAQ,CAAC,oCAAoC,CAAC;IACjD,QAAQ,EAAE,CAAC;SACR,IAAI,CAAC,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;SACzB,QAAQ,EAAE;SACV,OAAO,CAAC,QAAQ,CAAC;SACjB,QAAQ,CAAC,mEAAmE,CAAC;IAChF,WAAW,EAAE,CAAC;SACX,MAAM,EAAE;SACR,QAAQ,EAAE;SACV,OAAO,CAAC,WAAW,CAAC;SACpB,QAAQ,CAAC,mEAAmE,CAAC;CACjF,CAAC;AAEF,SAAS,qBAAqB,CAAC,IAAY;IACzC,OAAO;;;;;;;;;;;;;;;;;;0BAkBiB,IAAI;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAwC7B,CAAC;AACF,CAAC;AAED,SAAS,wBAAwB,CAAC,IAAY;IAC5C,MAAM,SAAS,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;IAC/D,OAAO;;;QAGD,SAAS;UACP,SAAS;;;UAGT,SAAS,uBAAuB,SAAS;;;SAG1C,SAAS,uBAAuB,SAAS;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAoCjD,CAAC;AACF,CAAC;AAED,MAAM,UAAU,QAAQ,CAAC,MAAiB;IACxC,MAAM,CAAC,IAAI,CACT,eAAe,EACf,0HAA0H,EAC1H,WAAW,EACX,KAAK,EAAE,IAAI,EAAE,EAAE;QACb,IAAI,CAAC;YACH,MAAM,IAAI,GAAG,IAAI,CAAC,YAAY,IAAI,OAAO,CAAC,GAAG,EAAE,CAAC;YAChD,MAAM,YAAY,GAAa,EAAE,CAAC;YAClC,MAAM,aAAa,GAAa,EAAE,CAAC;YAEnC,IAAI,IAAI,CAAC,SAAS,KAAK,SAAS,EAAE,CAAC;gBACjC,MAAM,SAAS,GAAG,IAAI,CAAC,IAAI,EAAE,KAAK,EAAE,SAAS,CAAC,CAAC;gBAC/C,SAAS,CAAC,SAAS,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;gBAC1C,MAAM,QAAQ,GAAG,IAAI,CAAC,WAAW;qBAC9B,OAAO,CAAC,UAAU,EAAE,KAAK,CAAC;qBAC1B,WAAW,EAAE;qBACb,OAAO,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;gBACrB,MAAM,UAAU,GAAG,IAAI,CAAC,SAAS,EAAE,GAAG,QAAQ,OAAO,CAAC,CAAC;gBAEvD,IAAI,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;oBAC3B,OAAO,aAAa,CAAC,IAAI,KAAK,CAAC,wBAAwB,UAAU,EAAE,CAAC,CAAC,CAAC;gBACxE,CAAC;gBAED,aAAa,CAAC,UAAU,EAAE,wBAAwB,CAAC,IAAI,CAAC,WAAW,CAAC,EAAE,OAAO,CAAC,CAAC;gBAC/E,YAAY,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;gBAE9B,OAAO,YAAY,CACjB,IAAI,CAAC,SAAS,CACZ;oBACE,OAAO,EAAE,IAAI;oBACb,SAAS,EAAE,SAAS;oBACpB,QAAQ,EAAE,IAAI,CAAC,QAAQ;oBACvB,aAAa,EAAE,YAAY;oBAC3B,UAAU,EAAE;wBACV,8EAA8E;wBAC9E,+DAA+D;wBAC/D,uFAAuF;wBACvF,mDAAmD;wBACnD,yCAAyC;wBACzC,gEAAgE;wBAChE,yDAAyD;qBAC1D;iBACF,EACD,IAAI,EACJ,CAAC,CACF,CACF,CAAC;YACJ,CAAC;YAED,MAAM,WAAW,GAAG,IAAI,CAAC,IAAI,EAAE,UAAU,CAAC,CAAC;YAC3C,IAAI,UAAU,CAAC,WAAW,CAAC,EAAE,CAAC;gBAC5B,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,WAAW,EAAE,OAAO,CAAC,CAAC,CAAC;gBAC/D,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,IAAI,EAAE,CAAC;gBAEhC,IAAI,CAAC,IAAI,CAAC,OAAO;oBAAE,IAAI,CAAC,OAAO,GAAG,EAAE,CAAC;gBAErC,MAAM,YAAY,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,CACpC,CAAC,CAAU,EAAE,EAAE,CACb,CAAC,OAAO,CAAC,KAAK,QAAQ,IAAI,CAAC,KAAK,mBAAmB,CAAC;oBACpD,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,KAAK,mBAAmB,CAAC,CACrD,CAAC;gBAEF,IAAI,CAAC,YAAY,EAAE,CAAC;oBAClB,MAAM,SAAS,GAAsC;wBACnD,mBAAmB;wBACnB;4BACE,gBAAgB,EAAE,0BAA0B;yBAC7C;qBACF,CAAC;oBACF,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;oBAC7B,OAAO,CAAC,IAAI,GAAG,IAAI,CAAC;oBACpB,aAAa,CAAC,WAAW,EAAE,IAAI,CAAC,SAAS,CAAC,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC,GAAG,IAAI,EAAE,OAAO,CAAC,CAAC;oBAC7E,aAAa,CAAC,IAAI,CAAC,4CAA4C,CAAC,CAAC;gBACnE,CAAC;YACH,CAAC;YAED,MAAM,SAAS,GAAG,IAAI,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;YACpC,IAAI,CAAC,UAAU,CAAC,SAAS,CAAC,EAAE,CAAC;gBAC3B,SAAS,CAAC,SAAS,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;YAC5C,CAAC;YAED,MAAM,UAAU,GAAG,IAAI,CAAC,SAAS,EAAE,GAAG,IAAI,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,GAAG,IAAI,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC;YAClH,IAAI,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;gBAC5B,aAAa,CAAC,UAAU,EAAE,qBAAqB,CAAC,IAAI,CAAC,WAAW,CAAC,EAAE,OAAO,CAAC,CAAC;gBAC5E,YAAY,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;YAChC,CAAC;YAED,OAAO,YAAY,CACjB,IAAI,CAAC,SAAS,CACZ;gBACE,OAAO,EAAE,IAAI;gBACb,SAAS,EAAE,MAAM;gBACjB,QAAQ,EAAE,IAAI,CAAC,QAAQ;gBACvB,aAAa,EAAE,YAAY;gBAC3B,cAAc,EAAE,aAAa;gBAC7B,UAAU,EAAE;oBACV,+DAA+D;oBAC/D,yEAAyE;oBACzE,mEAAmE;oBACnE,yDAAyD;oBACzD,mDAAmD;oBACnD,kEAAkE;iBACnE;aACF,EACD,IAAI,EACJ,CAAC,CACF,CACF,CAAC;QACJ,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,OAAO,aAAa,CAAC,GAAG,CAAC,CAAC;QAC5B,CAAC;IACH,CAAC,CACF,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"generateForm.d.ts","sourceRoot":"","sources":["../../src/tools/generateForm.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AA6RzE,wBAAgB,QAAQ,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI,CAwFhD"}
|
|
@@ -0,0 +1,337 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { writeFileSync, mkdirSync, existsSync } from "node:fs";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
import { textResponse, errorResponse } from "../types.js";
|
|
5
|
+
const fieldSchema = z.object({
|
|
6
|
+
name: z.string().describe("Field name in camelCase (e.g. 'email', 'firstName')."),
|
|
7
|
+
type: z
|
|
8
|
+
.enum(["text", "email", "password", "number", "phone", "textarea", "select"])
|
|
9
|
+
.describe("Field input type."),
|
|
10
|
+
label: z.string().describe("Display label for the field."),
|
|
11
|
+
required: z.boolean().optional().default(true).describe("Whether the field is required."),
|
|
12
|
+
placeholder: z.string().optional().describe("Placeholder text."),
|
|
13
|
+
});
|
|
14
|
+
const inputSchema = {
|
|
15
|
+
form_name: z
|
|
16
|
+
.string()
|
|
17
|
+
.describe("Form component name in PascalCase (e.g. 'LoginForm', 'ContactForm')."),
|
|
18
|
+
fields: z
|
|
19
|
+
.array(fieldSchema)
|
|
20
|
+
.min(1)
|
|
21
|
+
.describe("Array of field definitions."),
|
|
22
|
+
project_path: z
|
|
23
|
+
.string()
|
|
24
|
+
.optional()
|
|
25
|
+
.describe("Absolute path to the project root. Defaults to cwd."),
|
|
26
|
+
framework: z
|
|
27
|
+
.enum(["expo", "flutter"])
|
|
28
|
+
.optional()
|
|
29
|
+
.default("expo")
|
|
30
|
+
.describe("Project framework (default: expo)."),
|
|
31
|
+
output_directory: z
|
|
32
|
+
.string()
|
|
33
|
+
.optional()
|
|
34
|
+
.default("components")
|
|
35
|
+
.describe("Output directory relative to project root (default: components)."),
|
|
36
|
+
};
|
|
37
|
+
function zodValidation(field) {
|
|
38
|
+
let chain = "z.string()";
|
|
39
|
+
if (field.required)
|
|
40
|
+
chain += '.min(1, "Required")';
|
|
41
|
+
if (field.type === "email")
|
|
42
|
+
chain += '.email("Invalid email")';
|
|
43
|
+
if (field.type === "number")
|
|
44
|
+
chain = "z.coerce.number()";
|
|
45
|
+
if (field.type === "phone")
|
|
46
|
+
chain += '.regex(/^\\+?[0-9\\s-()]+$/, "Invalid phone number")';
|
|
47
|
+
if (field.type === "password")
|
|
48
|
+
chain += '.min(8, "At least 8 characters")';
|
|
49
|
+
return chain;
|
|
50
|
+
}
|
|
51
|
+
function rnInputType(type) {
|
|
52
|
+
switch (type) {
|
|
53
|
+
case "email": return "email-address";
|
|
54
|
+
case "number": return "numeric";
|
|
55
|
+
case "phone": return "phone-pad";
|
|
56
|
+
default: return "default";
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
function generateExpoForm(name, fields) {
|
|
60
|
+
const schemaFields = fields
|
|
61
|
+
.map((f) => ` ${f.name}: ${zodValidation(f)},`)
|
|
62
|
+
.join("\n");
|
|
63
|
+
const formFields = fields
|
|
64
|
+
.map((f) => {
|
|
65
|
+
const secureEntry = f.type === "password" ? "\n secureTextEntry" : "";
|
|
66
|
+
const multiline = f.type === "textarea" ? "\n multiline\n numberOfLines={4}" : "";
|
|
67
|
+
const keyboard = f.type !== "text" && f.type !== "password" && f.type !== "textarea" && f.type !== "select"
|
|
68
|
+
? `\n keyboardType="${rnInputType(f.type)}"`
|
|
69
|
+
: "";
|
|
70
|
+
return ` <View style={styles.field}>
|
|
71
|
+
<Text style={styles.label}>${f.label}</Text>
|
|
72
|
+
<Controller
|
|
73
|
+
control={control}
|
|
74
|
+
name="${f.name}"
|
|
75
|
+
render={({ field: { onChange, onBlur, value } }) => (
|
|
76
|
+
<TextInput
|
|
77
|
+
style={[styles.input, errors.${f.name} && styles.inputError]}
|
|
78
|
+
onBlur={onBlur}
|
|
79
|
+
onChangeText={onChange}
|
|
80
|
+
value={value}
|
|
81
|
+
placeholder="${f.placeholder || f.label}"${secureEntry}${multiline}${keyboard}
|
|
82
|
+
/>
|
|
83
|
+
)}
|
|
84
|
+
/>
|
|
85
|
+
{errors.${f.name} && (
|
|
86
|
+
<Text style={styles.error}>{errors.${f.name}?.message}</Text>
|
|
87
|
+
)}
|
|
88
|
+
</View>`;
|
|
89
|
+
})
|
|
90
|
+
.join("\n\n");
|
|
91
|
+
const defaultValues = fields
|
|
92
|
+
.map((f) => ` ${f.name}: "",`)
|
|
93
|
+
.join("\n");
|
|
94
|
+
return `import { View, Text, TextInput, Pressable, StyleSheet } from "react-native";
|
|
95
|
+
import { useForm, Controller } from "react-hook-form";
|
|
96
|
+
import { zodResolver } from "@hookform/resolvers/zod";
|
|
97
|
+
import { z } from "zod";
|
|
98
|
+
|
|
99
|
+
const schema = z.object({
|
|
100
|
+
${schemaFields}
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
type FormData = z.infer<typeof schema>;
|
|
104
|
+
|
|
105
|
+
interface ${name}Props {
|
|
106
|
+
onSubmit: (data: FormData) => void;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
export function ${name}({ onSubmit }: ${name}Props) {
|
|
110
|
+
const {
|
|
111
|
+
control,
|
|
112
|
+
handleSubmit,
|
|
113
|
+
formState: { errors, isSubmitting },
|
|
114
|
+
} = useForm<FormData>({
|
|
115
|
+
resolver: zodResolver(schema),
|
|
116
|
+
defaultValues: {
|
|
117
|
+
${defaultValues}
|
|
118
|
+
},
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
return (
|
|
122
|
+
<View style={styles.container}>
|
|
123
|
+
${formFields}
|
|
124
|
+
|
|
125
|
+
<Pressable
|
|
126
|
+
style={[styles.button, isSubmitting && styles.buttonDisabled]}
|
|
127
|
+
onPress={handleSubmit(onSubmit)}
|
|
128
|
+
disabled={isSubmitting}
|
|
129
|
+
>
|
|
130
|
+
<Text style={styles.buttonText}>
|
|
131
|
+
{isSubmitting ? "Submitting..." : "Submit"}
|
|
132
|
+
</Text>
|
|
133
|
+
</Pressable>
|
|
134
|
+
</View>
|
|
135
|
+
);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
const styles = StyleSheet.create({
|
|
139
|
+
container: {
|
|
140
|
+
padding: 16,
|
|
141
|
+
gap: 12,
|
|
142
|
+
},
|
|
143
|
+
field: {
|
|
144
|
+
gap: 4,
|
|
145
|
+
},
|
|
146
|
+
label: {
|
|
147
|
+
fontSize: 14,
|
|
148
|
+
fontWeight: "600",
|
|
149
|
+
},
|
|
150
|
+
input: {
|
|
151
|
+
borderWidth: 1,
|
|
152
|
+
borderColor: "#ccc",
|
|
153
|
+
borderRadius: 8,
|
|
154
|
+
padding: 12,
|
|
155
|
+
fontSize: 16,
|
|
156
|
+
},
|
|
157
|
+
inputError: {
|
|
158
|
+
borderColor: "#dc2626",
|
|
159
|
+
},
|
|
160
|
+
error: {
|
|
161
|
+
color: "#dc2626",
|
|
162
|
+
fontSize: 12,
|
|
163
|
+
},
|
|
164
|
+
button: {
|
|
165
|
+
backgroundColor: "#0A84FF",
|
|
166
|
+
padding: 16,
|
|
167
|
+
borderRadius: 8,
|
|
168
|
+
alignItems: "center",
|
|
169
|
+
marginTop: 8,
|
|
170
|
+
},
|
|
171
|
+
buttonDisabled: {
|
|
172
|
+
opacity: 0.6,
|
|
173
|
+
},
|
|
174
|
+
buttonText: {
|
|
175
|
+
color: "#fff",
|
|
176
|
+
fontSize: 16,
|
|
177
|
+
fontWeight: "600",
|
|
178
|
+
},
|
|
179
|
+
});
|
|
180
|
+
`;
|
|
181
|
+
}
|
|
182
|
+
function generateFlutterForm(name, fields) {
|
|
183
|
+
const className = name.charAt(0).toUpperCase() + name.slice(1);
|
|
184
|
+
const controllers = fields
|
|
185
|
+
.map((f) => ` final _${f.name}Controller = TextEditingController();`)
|
|
186
|
+
.join("\n");
|
|
187
|
+
const disposeControllers = fields
|
|
188
|
+
.map((f) => ` _${f.name}Controller.dispose();`)
|
|
189
|
+
.join("\n");
|
|
190
|
+
const formFields = fields
|
|
191
|
+
.map((f) => {
|
|
192
|
+
const obscure = f.type === "password" ? "\n obscureText: true," : "";
|
|
193
|
+
const maxLines = f.type === "textarea" ? "\n maxLines: 4," : "";
|
|
194
|
+
const keyboard = f.type === "email"
|
|
195
|
+
? "\n keyboardType: TextInputType.emailAddress,"
|
|
196
|
+
: f.type === "number"
|
|
197
|
+
? "\n keyboardType: TextInputType.number,"
|
|
198
|
+
: f.type === "phone"
|
|
199
|
+
? "\n keyboardType: TextInputType.phone,"
|
|
200
|
+
: "";
|
|
201
|
+
const validator = f.required
|
|
202
|
+
? `\n validator: (value) {\n if (value == null || value.isEmpty) return '${f.label} is required';\n return null;\n },`
|
|
203
|
+
: "";
|
|
204
|
+
return ` TextFormField(
|
|
205
|
+
controller: _${f.name}Controller,
|
|
206
|
+
decoration: const InputDecoration(
|
|
207
|
+
labelText: '${f.label}',
|
|
208
|
+
hintText: '${f.placeholder || f.label}',
|
|
209
|
+
),${obscure}${maxLines}${keyboard}${validator}
|
|
210
|
+
),
|
|
211
|
+
const SizedBox(height: 16),`;
|
|
212
|
+
})
|
|
213
|
+
.join("\n");
|
|
214
|
+
return `import 'package:flutter/material.dart';
|
|
215
|
+
|
|
216
|
+
class ${className} extends StatefulWidget {
|
|
217
|
+
final void Function(Map<String, String> data) onSubmit;
|
|
218
|
+
|
|
219
|
+
const ${className}({super.key, required this.onSubmit});
|
|
220
|
+
|
|
221
|
+
@override
|
|
222
|
+
State<${className}> createState() => _${className}State();
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
class _${className}State extends State<${className}> {
|
|
226
|
+
final _formKey = GlobalKey<FormState>();
|
|
227
|
+
bool _isSubmitting = false;
|
|
228
|
+
|
|
229
|
+
${controllers}
|
|
230
|
+
|
|
231
|
+
@override
|
|
232
|
+
void dispose() {
|
|
233
|
+
${disposeControllers}
|
|
234
|
+
super.dispose();
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
Future<void> _handleSubmit() async {
|
|
238
|
+
if (!_formKey.currentState!.validate()) return;
|
|
239
|
+
|
|
240
|
+
setState(() => _isSubmitting = true);
|
|
241
|
+
try {
|
|
242
|
+
widget.onSubmit({
|
|
243
|
+
${fields.map((f) => ` '${f.name}': _${f.name}Controller.text,`).join("\n")}
|
|
244
|
+
});
|
|
245
|
+
} finally {
|
|
246
|
+
if (mounted) setState(() => _isSubmitting = false);
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
@override
|
|
251
|
+
Widget build(BuildContext context) {
|
|
252
|
+
return Form(
|
|
253
|
+
key: _formKey,
|
|
254
|
+
child: Padding(
|
|
255
|
+
padding: const EdgeInsets.all(16),
|
|
256
|
+
child: Column(
|
|
257
|
+
crossAxisAlignment: CrossAxisAlignment.stretch,
|
|
258
|
+
children: [
|
|
259
|
+
${formFields}
|
|
260
|
+
ElevatedButton(
|
|
261
|
+
onPressed: _isSubmitting ? null : _handleSubmit,
|
|
262
|
+
child: Text(_isSubmitting ? 'Submitting...' : 'Submit'),
|
|
263
|
+
),
|
|
264
|
+
],
|
|
265
|
+
),
|
|
266
|
+
),
|
|
267
|
+
);
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
`;
|
|
271
|
+
}
|
|
272
|
+
export function register(server) {
|
|
273
|
+
server.tool("mobile_generateForm", "Scaffold a validated form component with typed fields, validation rules, and error handling. Uses React Hook Form + Zod for Expo or Form + TextFormField for Flutter.", inputSchema, async (args) => {
|
|
274
|
+
try {
|
|
275
|
+
const root = args.project_path || process.cwd();
|
|
276
|
+
const outDir = join(root, args.output_directory);
|
|
277
|
+
mkdirSync(outDir, { recursive: true });
|
|
278
|
+
const fields = args.fields.map((f) => ({
|
|
279
|
+
name: f.name,
|
|
280
|
+
type: f.type,
|
|
281
|
+
label: f.label,
|
|
282
|
+
required: f.required,
|
|
283
|
+
placeholder: f.placeholder,
|
|
284
|
+
}));
|
|
285
|
+
if (args.framework === "flutter") {
|
|
286
|
+
const fileName = args.form_name
|
|
287
|
+
.replace(/([A-Z])/g, "_$1")
|
|
288
|
+
.toLowerCase()
|
|
289
|
+
.replace(/^_/, "");
|
|
290
|
+
const filePath = join(outDir, `${fileName}.dart`);
|
|
291
|
+
if (existsSync(filePath)) {
|
|
292
|
+
return errorResponse(new Error(`File already exists: ${filePath}`));
|
|
293
|
+
}
|
|
294
|
+
writeFileSync(filePath, generateFlutterForm(args.form_name, fields), "utf-8");
|
|
295
|
+
return textResponse(JSON.stringify({
|
|
296
|
+
success: true,
|
|
297
|
+
framework: "flutter",
|
|
298
|
+
file_created: filePath,
|
|
299
|
+
form_name: args.form_name,
|
|
300
|
+
fields: fields.map((f) => f.name),
|
|
301
|
+
next_steps: [
|
|
302
|
+
"Import and use the form widget in your screen",
|
|
303
|
+
"Add email/phone validators if needed",
|
|
304
|
+
"Add keyboard avoidance with SingleChildScrollView",
|
|
305
|
+
],
|
|
306
|
+
}, null, 2));
|
|
307
|
+
}
|
|
308
|
+
const filePath = join(outDir, `${args.form_name}.tsx`);
|
|
309
|
+
if (existsSync(filePath)) {
|
|
310
|
+
return errorResponse(new Error(`File already exists: ${filePath}`));
|
|
311
|
+
}
|
|
312
|
+
writeFileSync(filePath, generateExpoForm(args.form_name, fields), "utf-8");
|
|
313
|
+
return textResponse(JSON.stringify({
|
|
314
|
+
success: true,
|
|
315
|
+
framework: "expo",
|
|
316
|
+
file_created: filePath,
|
|
317
|
+
form_name: args.form_name,
|
|
318
|
+
fields: fields.map((f) => f.name),
|
|
319
|
+
dependencies_needed: [
|
|
320
|
+
"react-hook-form",
|
|
321
|
+
"@hookform/resolvers",
|
|
322
|
+
"zod",
|
|
323
|
+
],
|
|
324
|
+
next_steps: [
|
|
325
|
+
"Install dependencies: npx expo install react-hook-form @hookform/resolvers zod",
|
|
326
|
+
"Import and render the form in your screen",
|
|
327
|
+
"Implement the onSubmit handler with your API call",
|
|
328
|
+
"Add KeyboardAvoidingView wrapper if fields are below the fold",
|
|
329
|
+
],
|
|
330
|
+
}, null, 2));
|
|
331
|
+
}
|
|
332
|
+
catch (err) {
|
|
333
|
+
return errorResponse(err);
|
|
334
|
+
}
|
|
335
|
+
});
|
|
336
|
+
}
|
|
337
|
+
//# sourceMappingURL=generateForm.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"generateForm.js","sourceRoot":"","sources":["../../src/tools/generateForm.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"generateTestFile.d.ts","sourceRoot":"","sources":["../../src/tools/generateTestFile.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAwHzE,wBAAgB,QAAQ,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI,CAyGhD"}
|