flutterflow-mcp 0.2.5 → 0.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/build/index.js +14 -2
- package/build/prompts/generate-page.js +6 -5
- package/build/prompts/modify-component.js +6 -5
- package/build/tools/get-app-settings.d.ts +2 -0
- package/build/tools/get-app-settings.js +169 -0
- package/build/tools/get-custom-code.js +99 -3
- package/build/tools/get-editing-guide.d.ts +7 -0
- package/build/tools/get-editing-guide.js +185 -0
- package/build/tools/get-general-settings.d.ts +2 -0
- package/build/tools/get-general-settings.js +116 -0
- package/build/tools/get-in-app-purchases.d.ts +2 -0
- package/build/tools/get-in-app-purchases.js +51 -0
- package/build/tools/get-integrations.d.ts +2 -0
- package/build/tools/get-integrations.js +137 -0
- package/build/tools/get-project-setup.d.ts +2 -0
- package/build/tools/get-project-setup.js +212 -0
- package/build/tools/get-yaml-docs.js +1 -111
- package/build/tools/list-files.js +22 -3
- package/build/tools/search-project-files.d.ts +2 -0
- package/build/tools/search-project-files.js +69 -0
- package/build/tools/update-yaml.js +1 -1
- package/build/tools/validate-yaml.js +19 -2
- package/build/utils/topic-map.d.ts +7 -0
- package/build/utils/topic-map.js +115 -0
- package/package.json +1 -1
- package/build/tools/get-project-config.d.ts +0 -2
- package/build/tools/get-project-config.js +0 -160
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import YAML from "yaml";
|
|
3
|
+
import { cacheRead, cacheMeta } from "../utils/cache.js";
|
|
4
|
+
async function resolvePageName(projectId, scaffoldId) {
|
|
5
|
+
const content = await cacheRead(projectId, `page/id-${scaffoldId}`);
|
|
6
|
+
if (!content)
|
|
7
|
+
return scaffoldId;
|
|
8
|
+
const nameMatch = content.match(/^name:\s*(.+)$/m);
|
|
9
|
+
return nameMatch ? nameMatch[1].trim() : scaffoldId;
|
|
10
|
+
}
|
|
11
|
+
export function registerGetGeneralSettingsTool(server) {
|
|
12
|
+
server.tool("get_general_settings", "Get General settings — App Details (name, package, initial page, routing), App Assets (icon, splash, error image), Nav Bar & App Bar. Mirrors the FlutterFlow 'General' settings section. Cache-based, no API calls. Run sync_project first.", {
|
|
13
|
+
projectId: z.string().describe("The FlutterFlow project ID"),
|
|
14
|
+
}, async ({ projectId }) => {
|
|
15
|
+
const meta = await cacheMeta(projectId);
|
|
16
|
+
if (!meta) {
|
|
17
|
+
return {
|
|
18
|
+
content: [
|
|
19
|
+
{
|
|
20
|
+
type: "text",
|
|
21
|
+
text: `No cache found for project "${projectId}". Run sync_project first.`,
|
|
22
|
+
},
|
|
23
|
+
],
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
const [appDetailsRaw, appAssetsRaw, navBarRaw] = await Promise.all([
|
|
27
|
+
cacheRead(projectId, "app-details"),
|
|
28
|
+
cacheRead(projectId, "app-assets"),
|
|
29
|
+
cacheRead(projectId, "nav-bar"),
|
|
30
|
+
]);
|
|
31
|
+
const sections = ["# General Settings"];
|
|
32
|
+
// --- App Details ---
|
|
33
|
+
if (appDetailsRaw) {
|
|
34
|
+
const appDetails = YAML.parse(appDetailsRaw);
|
|
35
|
+
const appName = appDetails.name || "Unknown";
|
|
36
|
+
// Package name: take first env's packageName
|
|
37
|
+
let packageName = "not set";
|
|
38
|
+
const allAppNames = appDetails.allAppNames;
|
|
39
|
+
const appNames = allAppNames?.appNames;
|
|
40
|
+
if (appNames) {
|
|
41
|
+
const firstEnvKey = Object.keys(appNames)[0];
|
|
42
|
+
if (firstEnvKey) {
|
|
43
|
+
packageName = appNames[firstEnvKey].packageName || "not set";
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
// Initial page
|
|
47
|
+
const initialPageKey = appDetails.initialPageKeyRef?.key;
|
|
48
|
+
let initialPageLine = "not set";
|
|
49
|
+
if (initialPageKey) {
|
|
50
|
+
const name = await resolvePageName(projectId, initialPageKey);
|
|
51
|
+
initialPageLine = `${name} (${initialPageKey})`;
|
|
52
|
+
}
|
|
53
|
+
// Routing
|
|
54
|
+
const routing = appDetails.routingSettings;
|
|
55
|
+
const routingEnabled = routing?.enableRouting === true;
|
|
56
|
+
const subroutes = routing?.pagesAreSubroutesOfRoot === true;
|
|
57
|
+
sections.push(`\n## App Details`, `Name: ${appName}`, `Package name: ${packageName}`, `Initial page: ${initialPageLine}`, `Routing: ${routingEnabled ? "enabled" : "disabled"}, pages are subroutes: ${subroutes ? "yes" : "no"}`);
|
|
58
|
+
}
|
|
59
|
+
else {
|
|
60
|
+
sections.push(`\n## App Details`, `(not configured)`);
|
|
61
|
+
}
|
|
62
|
+
// --- App Assets ---
|
|
63
|
+
if (appAssetsRaw) {
|
|
64
|
+
const appAssets = YAML.parse(appAssetsRaw);
|
|
65
|
+
const appIconPath = appAssets.appIconPath || "not set";
|
|
66
|
+
const splashImage = appAssets.splashImage;
|
|
67
|
+
const errorImagePath = appAssets.errorImagePath || "not set";
|
|
68
|
+
sections.push(`\n## App Assets`);
|
|
69
|
+
sections.push(`App icon: ${appIconPath}`);
|
|
70
|
+
if (splashImage) {
|
|
71
|
+
const splashPath = splashImage.path || "not set";
|
|
72
|
+
const splashFit = splashImage.fit || "not set";
|
|
73
|
+
const splashDuration = splashImage.minSplashScreenDuration;
|
|
74
|
+
sections.push(`Splash image: ${splashPath} (fit: ${splashFit})`);
|
|
75
|
+
if (splashDuration)
|
|
76
|
+
sections.push(`Splash duration: ${splashDuration}ms`);
|
|
77
|
+
}
|
|
78
|
+
else {
|
|
79
|
+
sections.push(`Splash image: not set`);
|
|
80
|
+
}
|
|
81
|
+
sections.push(`Error image: ${errorImagePath}`);
|
|
82
|
+
}
|
|
83
|
+
else {
|
|
84
|
+
sections.push(`\n## App Assets`, `(not configured)`);
|
|
85
|
+
}
|
|
86
|
+
// --- Nav Bar & App Bar ---
|
|
87
|
+
if (navBarRaw) {
|
|
88
|
+
const navBar = YAML.parse(navBarRaw);
|
|
89
|
+
const visible = navBar.show === true;
|
|
90
|
+
if (visible) {
|
|
91
|
+
const navType = navBar.navBarType || "UNKNOWN";
|
|
92
|
+
const labels = navBar.labels === true;
|
|
93
|
+
const pageRefs = navBar.pageKeyRefOrder;
|
|
94
|
+
sections.push(`\n## Nav Bar & App Bar`, `Visible: Yes`, `Type: ${navType}`, `Labels: ${labels ? "Yes" : "No"}`);
|
|
95
|
+
if (pageRefs && pageRefs.length > 0) {
|
|
96
|
+
const tabs = [];
|
|
97
|
+
for (let i = 0; i < pageRefs.length; i++) {
|
|
98
|
+
const scaffoldId = pageRefs[i].key;
|
|
99
|
+
const name = await resolvePageName(projectId, scaffoldId);
|
|
100
|
+
tabs.push(` ${i + 1}. ${name} (${scaffoldId})`);
|
|
101
|
+
}
|
|
102
|
+
sections.push(`Tabs:`, ...tabs);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
else {
|
|
106
|
+
sections.push(`\n## Nav Bar & App Bar`, `Visible: No`);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
else {
|
|
110
|
+
sections.push(`\n## Nav Bar & App Bar`, `(not configured)`);
|
|
111
|
+
}
|
|
112
|
+
return {
|
|
113
|
+
content: [{ type: "text", text: sections.join("\n") }],
|
|
114
|
+
};
|
|
115
|
+
});
|
|
116
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import YAML from "yaml";
|
|
3
|
+
import { cacheRead, cacheMeta } from "../utils/cache.js";
|
|
4
|
+
const PROVIDERS = [
|
|
5
|
+
{ key: "stripe", label: "Stripe" },
|
|
6
|
+
{ key: "braintree", label: "Braintree" },
|
|
7
|
+
{ key: "revenue-cat", label: "RevenueCat" },
|
|
8
|
+
{ key: "razorpay", label: "Razorpay" },
|
|
9
|
+
];
|
|
10
|
+
export function registerGetInAppPurchasesTool(server) {
|
|
11
|
+
server.tool("get_in_app_purchases", "Get In-App Purchases & Subscriptions settings — Stripe, Braintree, RevenueCat, Razorpay. Mirrors the FlutterFlow 'In App Purchases & Subscriptions' section. Cache-based, no API calls. Run sync_project first.", {
|
|
12
|
+
projectId: z.string().describe("The FlutterFlow project ID"),
|
|
13
|
+
}, async ({ projectId }) => {
|
|
14
|
+
const meta = await cacheMeta(projectId);
|
|
15
|
+
if (!meta) {
|
|
16
|
+
return {
|
|
17
|
+
content: [
|
|
18
|
+
{
|
|
19
|
+
type: "text",
|
|
20
|
+
text: `No cache found for project "${projectId}". Run sync_project first.`,
|
|
21
|
+
},
|
|
22
|
+
],
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
const sections = ["# In-App Purchases & Subscriptions"];
|
|
26
|
+
for (const provider of PROVIDERS) {
|
|
27
|
+
const raw = await cacheRead(projectId, provider.key);
|
|
28
|
+
sections.push(`\n## ${provider.label}`);
|
|
29
|
+
if (!raw) {
|
|
30
|
+
sections.push("(not configured)");
|
|
31
|
+
continue;
|
|
32
|
+
}
|
|
33
|
+
const doc = YAML.parse(raw);
|
|
34
|
+
const enabled = doc.enabled === true;
|
|
35
|
+
sections.push(`Enabled: ${enabled ? "Yes" : "No"}`);
|
|
36
|
+
// Show any additional top-level scalar fields (skip 'enabled' itself and complex objects)
|
|
37
|
+
for (const [key, value] of Object.entries(doc)) {
|
|
38
|
+
if (key === "enabled")
|
|
39
|
+
continue;
|
|
40
|
+
if (value === null || value === undefined)
|
|
41
|
+
continue;
|
|
42
|
+
if (typeof value === "object")
|
|
43
|
+
continue;
|
|
44
|
+
sections.push(`${key}: ${String(value)}`);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
return {
|
|
48
|
+
content: [{ type: "text", text: sections.join("\n") }],
|
|
49
|
+
};
|
|
50
|
+
});
|
|
51
|
+
}
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import YAML from "yaml";
|
|
3
|
+
import { cacheRead, cacheMeta } from "../utils/cache.js";
|
|
4
|
+
/**
|
|
5
|
+
* Fields that should never be output — API keys, secrets, tokens, etc.
|
|
6
|
+
* Only IDs and non-sensitive configuration are shown.
|
|
7
|
+
*/
|
|
8
|
+
const SENSITIVE_KEYS = new Set([
|
|
9
|
+
"searchApiKey",
|
|
10
|
+
"supabaseAnonKey",
|
|
11
|
+
"supabaseServiceRoleKey",
|
|
12
|
+
"apiKey",
|
|
13
|
+
"secretKey",
|
|
14
|
+
"secret",
|
|
15
|
+
"token",
|
|
16
|
+
"accessToken",
|
|
17
|
+
"refreshToken",
|
|
18
|
+
"privateKey",
|
|
19
|
+
]);
|
|
20
|
+
// ---------------------------------------------------------------------------
|
|
21
|
+
// Custom formatters for specific integrations
|
|
22
|
+
// ---------------------------------------------------------------------------
|
|
23
|
+
function formatAlgolia(doc) {
|
|
24
|
+
const lines = [];
|
|
25
|
+
const enabled = doc.enabled === true;
|
|
26
|
+
lines.push(`Enabled: ${enabled ? "Yes" : "No"}`);
|
|
27
|
+
const appId = doc.applicationId || "not set";
|
|
28
|
+
lines.push(`Application ID: ${appId}`);
|
|
29
|
+
const collections = doc.indexedCollections;
|
|
30
|
+
if (collections && collections.length > 0) {
|
|
31
|
+
lines.push(`Indexed collections:`);
|
|
32
|
+
for (const col of collections) {
|
|
33
|
+
lines.push(` - ${col.name || "unnamed"}`);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
else {
|
|
37
|
+
lines.push(`Indexed collections: none`);
|
|
38
|
+
}
|
|
39
|
+
return lines;
|
|
40
|
+
}
|
|
41
|
+
function formatGoogleMaps(doc) {
|
|
42
|
+
const androidKey = doc.androidKey || "not set";
|
|
43
|
+
const iosKey = doc.iosKey || "not set";
|
|
44
|
+
const webKey = doc.webKey || "not set";
|
|
45
|
+
return [
|
|
46
|
+
`Android key: ${androidKey}`,
|
|
47
|
+
`iOS key: ${iosKey}`,
|
|
48
|
+
`Web key: ${webKey}`,
|
|
49
|
+
];
|
|
50
|
+
}
|
|
51
|
+
function formatFirebaseAnalytics(doc) {
|
|
52
|
+
const lines = [];
|
|
53
|
+
const enabled = doc.enabled === true;
|
|
54
|
+
lines.push(`Enabled: ${enabled ? "Yes" : "No"}`);
|
|
55
|
+
const settings = doc.automaticEventSettings;
|
|
56
|
+
if (settings) {
|
|
57
|
+
lines.push(`Automatic event settings:`);
|
|
58
|
+
lines.push(` onPageLoad: ${settings.onPageLoad === true ? "Yes" : "No"}`);
|
|
59
|
+
lines.push(` onActionsStart: ${settings.onActionsStart === true ? "Yes" : "No"}`);
|
|
60
|
+
lines.push(` onAuth: ${settings.onAuth === true ? "Yes" : "No"}`);
|
|
61
|
+
}
|
|
62
|
+
return lines;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Generic formatter: shows `enabled` status and all non-sensitive
|
|
66
|
+
* top-level string/boolean fields.
|
|
67
|
+
*/
|
|
68
|
+
function formatGeneric(doc) {
|
|
69
|
+
const lines = [];
|
|
70
|
+
if ("enabled" in doc) {
|
|
71
|
+
lines.push(`Enabled: ${doc.enabled === true ? "Yes" : "No"}`);
|
|
72
|
+
}
|
|
73
|
+
for (const [key, value] of Object.entries(doc)) {
|
|
74
|
+
if (key === "enabled")
|
|
75
|
+
continue;
|
|
76
|
+
if (SENSITIVE_KEYS.has(key))
|
|
77
|
+
continue;
|
|
78
|
+
if (typeof value === "string" || typeof value === "boolean") {
|
|
79
|
+
lines.push(`${key}: ${typeof value === "boolean" ? (value ? "Yes" : "No") : value}`);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
return lines;
|
|
83
|
+
}
|
|
84
|
+
// ---------------------------------------------------------------------------
|
|
85
|
+
// Integration list — order matches the spec
|
|
86
|
+
// ---------------------------------------------------------------------------
|
|
87
|
+
const INTEGRATIONS = [
|
|
88
|
+
{ fileKey: "supabase", displayName: "Supabase" },
|
|
89
|
+
{ fileKey: "sqlite", displayName: "SQLite" },
|
|
90
|
+
{ fileKey: "github", displayName: "GitHub" },
|
|
91
|
+
{ fileKey: "algolia", displayName: "Algolia", format: formatAlgolia },
|
|
92
|
+
{ fileKey: "firebase-analytics", displayName: "Google Analytics", format: formatFirebaseAnalytics },
|
|
93
|
+
{ fileKey: "google-maps", displayName: "Google Maps", format: formatGoogleMaps },
|
|
94
|
+
{ fileKey: "admob", displayName: "AdMob" },
|
|
95
|
+
{ fileKey: "mux", displayName: "Mux Livestream" },
|
|
96
|
+
{ fileKey: "onesignal", displayName: "OneSignal" },
|
|
97
|
+
{ fileKey: "gemini", displayName: "Gemini" },
|
|
98
|
+
];
|
|
99
|
+
// ---------------------------------------------------------------------------
|
|
100
|
+
// Tool registration
|
|
101
|
+
// ---------------------------------------------------------------------------
|
|
102
|
+
export function registerGetIntegrationsTool(server) {
|
|
103
|
+
server.tool("get_integrations", "Get Integrations settings — Supabase, SQLite, GitHub, Algolia, Google Analytics, Google Maps, AdMob, Mux Livestream, OneSignal, Gemini. Mirrors the FlutterFlow 'Integrations' section. Cache-based, no API calls. Run sync_project first.", {
|
|
104
|
+
projectId: z.string().describe("The FlutterFlow project ID"),
|
|
105
|
+
}, async ({ projectId }) => {
|
|
106
|
+
const meta = await cacheMeta(projectId);
|
|
107
|
+
if (!meta) {
|
|
108
|
+
return {
|
|
109
|
+
content: [
|
|
110
|
+
{
|
|
111
|
+
type: "text",
|
|
112
|
+
text: `No cache found for project "${projectId}". Run sync_project first.`,
|
|
113
|
+
},
|
|
114
|
+
],
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
// Read all integration files in parallel
|
|
118
|
+
const rawResults = await Promise.all(INTEGRATIONS.map((def) => cacheRead(projectId, def.fileKey)));
|
|
119
|
+
const sections = ["# Integrations"];
|
|
120
|
+
for (let i = 0; i < INTEGRATIONS.length; i++) {
|
|
121
|
+
const def = INTEGRATIONS[i];
|
|
122
|
+
const raw = rawResults[i];
|
|
123
|
+
sections.push(`\n## ${def.displayName}`);
|
|
124
|
+
if (!raw) {
|
|
125
|
+
sections.push("(not configured)");
|
|
126
|
+
continue;
|
|
127
|
+
}
|
|
128
|
+
const doc = YAML.parse(raw);
|
|
129
|
+
const formatter = def.format || formatGeneric;
|
|
130
|
+
const lines = formatter(doc);
|
|
131
|
+
sections.push(lines.join("\n"));
|
|
132
|
+
}
|
|
133
|
+
return {
|
|
134
|
+
content: [{ type: "text", text: sections.join("\n") }],
|
|
135
|
+
};
|
|
136
|
+
});
|
|
137
|
+
}
|
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import YAML from "yaml";
|
|
3
|
+
import { cacheRead, cacheMeta } from "../utils/cache.js";
|
|
4
|
+
import { resolveDataType } from "../utils/resolve-data-type.js";
|
|
5
|
+
export function registerGetProjectSetupTool(server) {
|
|
6
|
+
server.tool("get_project_setup", "Get Project Setup settings — Firebase services, Languages, Platforms, Permissions, Project Dependencies, Dev Environments. Mirrors the FlutterFlow 'Project Setup' settings section. Cache-based, no API calls. Run sync_project first.", {
|
|
7
|
+
projectId: z.string().describe("The FlutterFlow project ID"),
|
|
8
|
+
}, async ({ projectId }) => {
|
|
9
|
+
const meta = await cacheMeta(projectId);
|
|
10
|
+
if (!meta) {
|
|
11
|
+
return {
|
|
12
|
+
content: [
|
|
13
|
+
{
|
|
14
|
+
type: "text",
|
|
15
|
+
text: `No cache found for project "${projectId}". Run sync_project first.`,
|
|
16
|
+
},
|
|
17
|
+
],
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
const [analyticsRaw, appCheckRaw, crashlyticsRaw, perfMonRaw, remoteConfigRaw, languagesRaw, platformsRaw, permissionsRaw, dependenciesRaw, customCodeDepsRaw, envSettingsRaw,] = await Promise.all([
|
|
21
|
+
cacheRead(projectId, "firebase-analytics"),
|
|
22
|
+
cacheRead(projectId, "firebase-app-check"),
|
|
23
|
+
cacheRead(projectId, "firebase-crashlytics"),
|
|
24
|
+
cacheRead(projectId, "firebase-performance-monitoring"),
|
|
25
|
+
cacheRead(projectId, "firebase-remote-config"),
|
|
26
|
+
cacheRead(projectId, "languages"),
|
|
27
|
+
cacheRead(projectId, "platforms"),
|
|
28
|
+
cacheRead(projectId, "permissions"),
|
|
29
|
+
cacheRead(projectId, "dependencies"),
|
|
30
|
+
cacheRead(projectId, "custom-code-dependencies"),
|
|
31
|
+
cacheRead(projectId, "environment-settings"),
|
|
32
|
+
]);
|
|
33
|
+
const sections = ["# Project Setup"];
|
|
34
|
+
// --- Firebase ---
|
|
35
|
+
sections.push(`\n## Firebase`);
|
|
36
|
+
const anyFirebase = analyticsRaw || appCheckRaw || crashlyticsRaw || perfMonRaw || remoteConfigRaw;
|
|
37
|
+
if (anyFirebase) {
|
|
38
|
+
// Analytics
|
|
39
|
+
if (analyticsRaw) {
|
|
40
|
+
const analytics = YAML.parse(analyticsRaw);
|
|
41
|
+
const enabled = analytics.enabled === true;
|
|
42
|
+
sections.push(`Analytics: ${enabled ? "enabled" : "disabled"}`);
|
|
43
|
+
if (enabled) {
|
|
44
|
+
const eventSettings = analytics.automaticEventSettings;
|
|
45
|
+
if (eventSettings) {
|
|
46
|
+
sections.push(` onPageLoad: ${eventSettings.onPageLoad === true ? "Yes" : "No"}`, ` onActionsStart: ${eventSettings.onActionsStart === true ? "Yes" : "No"}`, ` onAuth: ${eventSettings.onAuth === true ? "Yes" : "No"}`);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
// App Check
|
|
51
|
+
if (appCheckRaw) {
|
|
52
|
+
const appCheck = YAML.parse(appCheckRaw);
|
|
53
|
+
sections.push(`App Check: ${appCheck.enabled === true ? "enabled" : "disabled"}`);
|
|
54
|
+
}
|
|
55
|
+
// Crashlytics
|
|
56
|
+
if (crashlyticsRaw) {
|
|
57
|
+
const crashlytics = YAML.parse(crashlyticsRaw);
|
|
58
|
+
sections.push(`Crashlytics: ${crashlytics.enabled === true ? "enabled" : "disabled"}`);
|
|
59
|
+
}
|
|
60
|
+
// Performance Monitoring
|
|
61
|
+
if (perfMonRaw) {
|
|
62
|
+
const perfMon = YAML.parse(perfMonRaw);
|
|
63
|
+
sections.push(`Performance Monitoring: ${perfMon.enabled === true ? "enabled" : "disabled"}`);
|
|
64
|
+
}
|
|
65
|
+
// Remote Config
|
|
66
|
+
if (remoteConfigRaw) {
|
|
67
|
+
const remoteConfig = YAML.parse(remoteConfigRaw);
|
|
68
|
+
const enabled = remoteConfig.enabled === true;
|
|
69
|
+
sections.push(`Remote Config: ${enabled ? "enabled" : "disabled"}`);
|
|
70
|
+
if (enabled) {
|
|
71
|
+
const fields = remoteConfig.fields || [];
|
|
72
|
+
if (fields.length > 0) {
|
|
73
|
+
sections.push(` Fields:`);
|
|
74
|
+
for (const field of fields) {
|
|
75
|
+
const name = field.parameter?.identifier?.name || "unknown";
|
|
76
|
+
const dt = resolveDataType(field.parameter?.dataType || {});
|
|
77
|
+
const defaultVal = field.serializedDefaultValue ?? "";
|
|
78
|
+
sections.push(` - ${name}: ${dt} (default: "${defaultVal}")`);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
else {
|
|
85
|
+
sections.push(`(not configured)`);
|
|
86
|
+
}
|
|
87
|
+
// --- Languages ---
|
|
88
|
+
sections.push(`\n## Languages`);
|
|
89
|
+
if (languagesRaw) {
|
|
90
|
+
const langs = YAML.parse(languagesRaw);
|
|
91
|
+
const primaryLang = langs.primaryLanguage
|
|
92
|
+
?.language;
|
|
93
|
+
const displayLang = langs.displayLanguage
|
|
94
|
+
?.language;
|
|
95
|
+
const allLangs = langs.languages || [];
|
|
96
|
+
sections.push(`Primary: ${primaryLang || "not set"}`);
|
|
97
|
+
sections.push(`Display: ${displayLang || "not set"}`);
|
|
98
|
+
if (allLangs.length > 0) {
|
|
99
|
+
const langCodes = allLangs.map((l) => l.language).join(", ");
|
|
100
|
+
sections.push(`Available: ${langCodes}`);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
else {
|
|
104
|
+
sections.push(`(not configured)`);
|
|
105
|
+
}
|
|
106
|
+
// --- Platforms ---
|
|
107
|
+
sections.push(`\n## Platforms`);
|
|
108
|
+
if (platformsRaw) {
|
|
109
|
+
const platforms = YAML.parse(platformsRaw);
|
|
110
|
+
sections.push(`Web: ${platforms.enableWeb === true ? "enabled" : "disabled"}`);
|
|
111
|
+
}
|
|
112
|
+
else {
|
|
113
|
+
sections.push(`(not configured)`);
|
|
114
|
+
}
|
|
115
|
+
// --- Permissions ---
|
|
116
|
+
sections.push(`\n## Permissions`);
|
|
117
|
+
if (permissionsRaw) {
|
|
118
|
+
const permissions = YAML.parse(permissionsRaw);
|
|
119
|
+
const builtIn = permissions.permissionMessages;
|
|
120
|
+
const custom = permissions.userDefinedPermissions;
|
|
121
|
+
const hasBuiltIn = builtIn && builtIn.length > 0;
|
|
122
|
+
const hasCustom = custom && custom.length > 0;
|
|
123
|
+
if (hasBuiltIn || hasCustom) {
|
|
124
|
+
if (hasBuiltIn) {
|
|
125
|
+
sections.push(`Built-in:`);
|
|
126
|
+
for (const perm of builtIn) {
|
|
127
|
+
const permType = perm.permissionType || "UNKNOWN";
|
|
128
|
+
const msg = perm.message?.textValue?.inputValue;
|
|
129
|
+
sections.push(` - ${permType}: ${msg ? `"${msg}"` : "(no message)"}`);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
if (hasCustom) {
|
|
133
|
+
sections.push(`Custom:`);
|
|
134
|
+
for (const perm of custom) {
|
|
135
|
+
const names = perm.names;
|
|
136
|
+
const iosName = names?.iosName || "?";
|
|
137
|
+
const androidName = names?.androidName || "?";
|
|
138
|
+
const msg = perm.message?.textValue?.inputValue;
|
|
139
|
+
sections.push(` - ${iosName} / ${androidName}: ${msg ? `"${msg}"` : "(no message)"}`);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
else {
|
|
144
|
+
sections.push(`(not configured)`);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
else {
|
|
148
|
+
sections.push(`(not configured)`);
|
|
149
|
+
}
|
|
150
|
+
// --- Project Dependencies ---
|
|
151
|
+
sections.push(`\n## Project Dependencies`);
|
|
152
|
+
const hasDeps = dependenciesRaw || customCodeDepsRaw;
|
|
153
|
+
if (hasDeps) {
|
|
154
|
+
// FF library dependencies
|
|
155
|
+
if (dependenciesRaw) {
|
|
156
|
+
const deps = YAML.parse(dependenciesRaw);
|
|
157
|
+
const libDeps = deps.dependencies || [];
|
|
158
|
+
if (libDeps.length > 0) {
|
|
159
|
+
sections.push(`FF Libraries:`);
|
|
160
|
+
for (const dep of libDeps) {
|
|
161
|
+
sections.push(` - ${dep.name || "unnamed"} (${dep.projectId || "?"}, version: ${dep.version || "?"})`);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
// Pubspec dependencies
|
|
166
|
+
if (customCodeDepsRaw) {
|
|
167
|
+
const customDeps = YAML.parse(customCodeDepsRaw);
|
|
168
|
+
const pubspecDeps = customDeps.pubspecDependencies || [];
|
|
169
|
+
if (pubspecDeps.length > 0) {
|
|
170
|
+
sections.push(`Pubspec Dependencies:`);
|
|
171
|
+
for (const dep of pubspecDeps) {
|
|
172
|
+
sections.push(` - ${dep.name || "unnamed"}: ${dep.version || "any"}`);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
else {
|
|
178
|
+
sections.push(`(not configured)`);
|
|
179
|
+
}
|
|
180
|
+
// --- Dev Environments ---
|
|
181
|
+
sections.push(`\n## Dev Environments`);
|
|
182
|
+
if (envSettingsRaw) {
|
|
183
|
+
const doc = YAML.parse(envSettingsRaw);
|
|
184
|
+
const currentEnv = doc.currentEnvironment;
|
|
185
|
+
const envValues = doc.environmentValues || [];
|
|
186
|
+
if (currentEnv) {
|
|
187
|
+
sections.push(`Current: ${currentEnv.name || "unknown"} (${currentEnv.key || "?"})`);
|
|
188
|
+
}
|
|
189
|
+
const lines = [];
|
|
190
|
+
for (const ev of envValues) {
|
|
191
|
+
const name = ev.parameter?.identifier?.name || "unknown";
|
|
192
|
+
const dt = resolveDataType(ev.parameter?.dataType || {});
|
|
193
|
+
const privateTag = ev.isPrivate ? " (private)" : "";
|
|
194
|
+
lines.push(`- ${name}: ${dt}${privateTag}`);
|
|
195
|
+
const valuesMap = ev.valuesMap || {};
|
|
196
|
+
for (const [envKey, envVal] of Object.entries(valuesMap)) {
|
|
197
|
+
const display = ev.isPrivate ? "****" : (envVal.serializedValue ?? "");
|
|
198
|
+
lines.push(` ${envKey}: ${display}`);
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
if (lines.length > 0) {
|
|
202
|
+
sections.push(lines.join("\n"));
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
else {
|
|
206
|
+
sections.push(`(not configured)`);
|
|
207
|
+
}
|
|
208
|
+
return {
|
|
209
|
+
content: [{ type: "text", text: sections.join("\n") }],
|
|
210
|
+
};
|
|
211
|
+
});
|
|
212
|
+
}
|
|
@@ -1,115 +1,5 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
-
import
|
|
3
|
-
import path from "path";
|
|
4
|
-
import { fileURLToPath } from "url";
|
|
5
|
-
const __filename = fileURLToPath(import.meta.url);
|
|
6
|
-
const __dirname = path.dirname(__filename);
|
|
7
|
-
const DOCS_DIR = path.resolve(__dirname, "../../docs/ff-yaml");
|
|
8
|
-
/** Topic-to-file mapping for fuzzy search. */
|
|
9
|
-
const TOPIC_MAP = {
|
|
10
|
-
// Widgets
|
|
11
|
-
button: "04-widgets/button.md",
|
|
12
|
-
iconbutton: "04-widgets/button.md",
|
|
13
|
-
text: "04-widgets/text.md",
|
|
14
|
-
richtext: "04-widgets/text.md",
|
|
15
|
-
richtextspan: "04-widgets/text.md",
|
|
16
|
-
textfield: "04-widgets/text-field.md",
|
|
17
|
-
"text-field": "04-widgets/text-field.md",
|
|
18
|
-
input: "04-widgets/text-field.md",
|
|
19
|
-
container: "04-widgets/container.md",
|
|
20
|
-
boxdecoration: "04-widgets/container.md",
|
|
21
|
-
column: "04-widgets/layout.md",
|
|
22
|
-
row: "04-widgets/layout.md",
|
|
23
|
-
stack: "04-widgets/layout.md",
|
|
24
|
-
wrap: "04-widgets/layout.md",
|
|
25
|
-
layout: "04-widgets/layout.md",
|
|
26
|
-
image: "04-widgets/image.md",
|
|
27
|
-
form: "04-widgets/form.md",
|
|
28
|
-
validation: "04-widgets/form.md",
|
|
29
|
-
dropdown: "04-widgets/dropdown.md",
|
|
30
|
-
choicechips: "04-widgets/dropdown.md",
|
|
31
|
-
icon: "04-widgets/misc.md",
|
|
32
|
-
progressbar: "04-widgets/misc.md",
|
|
33
|
-
appbar: "04-widgets/misc.md",
|
|
34
|
-
conditionalbuilder: "04-widgets/misc.md",
|
|
35
|
-
widget: "04-widgets/README.md",
|
|
36
|
-
widgets: "04-widgets/README.md",
|
|
37
|
-
// Non-widget topics
|
|
38
|
-
actions: "05-actions.md",
|
|
39
|
-
action: "05-actions.md",
|
|
40
|
-
trigger: "05-actions.md",
|
|
41
|
-
navigate: "05-actions.md",
|
|
42
|
-
navigation: "05-actions.md",
|
|
43
|
-
ontap: "05-actions.md",
|
|
44
|
-
variables: "06-variables.md",
|
|
45
|
-
variable: "06-variables.md",
|
|
46
|
-
binding: "06-variables.md",
|
|
47
|
-
"data-binding": "06-variables.md",
|
|
48
|
-
data: "07-data.md",
|
|
49
|
-
collections: "07-data.md",
|
|
50
|
-
firestore: "07-data.md",
|
|
51
|
-
api: "07-data.md",
|
|
52
|
-
custom: "08-custom-code.md",
|
|
53
|
-
dart: "08-custom-code.md",
|
|
54
|
-
"custom-code": "08-custom-code.md",
|
|
55
|
-
theme: "09-theming.md",
|
|
56
|
-
theming: "09-theming.md",
|
|
57
|
-
color: "09-theming.md",
|
|
58
|
-
colors: "09-theming.md",
|
|
59
|
-
font: "09-theming.md",
|
|
60
|
-
typography: "09-theming.md",
|
|
61
|
-
editing: "10-editing-guide.md",
|
|
62
|
-
workflow: "10-editing-guide.md",
|
|
63
|
-
"editing-guide": "10-editing-guide.md",
|
|
64
|
-
push: "10-editing-guide.md",
|
|
65
|
-
overview: "00-overview.md",
|
|
66
|
-
structure: "00-overview.md",
|
|
67
|
-
"project-files": "01-project-files.md",
|
|
68
|
-
config: "01-project-files.md",
|
|
69
|
-
settings: "01-project-files.md",
|
|
70
|
-
pages: "02-pages.md",
|
|
71
|
-
page: "02-pages.md",
|
|
72
|
-
scaffold: "02-pages.md",
|
|
73
|
-
components: "03-components.md",
|
|
74
|
-
component: "03-components.md",
|
|
75
|
-
createcomponent: "03-components.md",
|
|
76
|
-
refactor: "03-components.md",
|
|
77
|
-
refactoring: "03-components.md",
|
|
78
|
-
isdummyroot: "03-components.md",
|
|
79
|
-
dummyroot: "03-components.md",
|
|
80
|
-
componentclasskeyref: "03-components.md",
|
|
81
|
-
parametervalues: "03-components.md",
|
|
82
|
-
callback: "03-components.md",
|
|
83
|
-
executecallbackaction: "03-components.md",
|
|
84
|
-
// Universal patterns
|
|
85
|
-
inputvalue: "README.md",
|
|
86
|
-
mostrecentinputvalue: "README.md",
|
|
87
|
-
padding: "README.md",
|
|
88
|
-
"border-radius": "README.md",
|
|
89
|
-
};
|
|
90
|
-
/** List all doc files recursively. */
|
|
91
|
-
function listDocFiles(dir, prefix = "") {
|
|
92
|
-
const results = [];
|
|
93
|
-
if (!fs.existsSync(dir))
|
|
94
|
-
return results;
|
|
95
|
-
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
|
|
96
|
-
const relPath = prefix ? `${prefix}/${entry.name}` : entry.name;
|
|
97
|
-
if (entry.isDirectory()) {
|
|
98
|
-
results.push(...listDocFiles(path.join(dir, entry.name), relPath));
|
|
99
|
-
}
|
|
100
|
-
else if (entry.name.endsWith(".md")) {
|
|
101
|
-
results.push(relPath);
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
return results;
|
|
105
|
-
}
|
|
106
|
-
/** Read a doc file. Returns null if not found. */
|
|
107
|
-
function readDoc(relPath) {
|
|
108
|
-
const filePath = path.join(DOCS_DIR, relPath);
|
|
109
|
-
if (!fs.existsSync(filePath))
|
|
110
|
-
return null;
|
|
111
|
-
return fs.readFileSync(filePath, "utf-8");
|
|
112
|
-
}
|
|
2
|
+
import { TOPIC_MAP, DOCS_DIR, listDocFiles, readDoc } from "../utils/topic-map.js";
|
|
113
3
|
export function registerGetYamlDocsTool(server) {
|
|
114
4
|
server.tool("get_yaml_docs", "Search and retrieve FlutterFlow YAML reference documentation. Use `topic` to search by keyword (e.g. 'Button', 'actions', 'theming') or `file` to fetch a specific doc file. Returns the full doc content.", {
|
|
115
5
|
topic: z
|