@forvibe/cli 0.1.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/aso-generator-AZXT6ZCL.js +144 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +2215 -0
- package/dist/report-generator-NMGCGKPS.js +168 -0
- package/package.json +57 -0
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
// src/ai/report-generator.ts
|
|
2
|
+
import { GoogleGenerativeAI } from "@google/generative-ai";
|
|
3
|
+
var SYSTEM_PROMPT = `You are a senior mobile app analyst and App Store Optimization specialist. You will receive technical data about a mobile application project including its tech stack, dependencies, config files, README, project file tree, and source code excerpts.
|
|
4
|
+
|
|
5
|
+
Your job is to deeply analyze the project and produce a comprehensive structured JSON analysis report.
|
|
6
|
+
|
|
7
|
+
## ANALYSIS APPROACH \u2014 THINK LIKE A DETECTIVE:
|
|
8
|
+
1. Study the PROJECT FILE TREE carefully. Directory names like "screens/", "models/", "services/", "pages/" reveal the app's architecture. File names like "plant_detail.dart", "watering_schedule.dart", "shop_screen.dart" reveal features.
|
|
9
|
+
2. Read the SOURCE CODE thoroughly. Look for: screen/page names, navigation routes, data models, API calls, UI components, business logic, state management patterns.
|
|
10
|
+
3. Cross-reference the README (if available) with actual code to understand the app's purpose.
|
|
11
|
+
4. Even if the source code is minimal (e.g., a new project), infer the app's intent from naming conventions, file structure, bundle ID, and any available context.
|
|
12
|
+
|
|
13
|
+
## DESCRIPTION REQUIREMENTS \u2014 THIS IS CRITICAL:
|
|
14
|
+
The "description" field must be a DETAILED, RICH description of 4-8 sentences that:
|
|
15
|
+
- Explains what the app does and its core purpose
|
|
16
|
+
- Describes the main features and user experience
|
|
17
|
+
- Mentions the target use case (e.g., "helps plant lovers track watering schedules")
|
|
18
|
+
- Notes any notable technical aspects (e.g., "uses real-time syncing", "leverages AI")
|
|
19
|
+
- If code is minimal, infer from app name, bundle ID, file structure, and README. NEVER leave this empty.
|
|
20
|
+
- Write as if you're explaining the app to someone who has never seen it. Be specific, not generic.
|
|
21
|
+
|
|
22
|
+
## KEY FEATURES REQUIREMENTS:
|
|
23
|
+
- List 5-10 specific features you can identify from the code, file tree, and README
|
|
24
|
+
- Each feature should be a clear, descriptive phrase (e.g., "Plant watering schedule with customizable reminders" NOT just "Scheduling")
|
|
25
|
+
- Infer features from file names, screen names, model classes, and navigation structure
|
|
26
|
+
- If you see a "shop" screen, there's a shopping feature. If you see "notification", there's push notifications. Etc.
|
|
27
|
+
|
|
28
|
+
## UNIQUE SELLING POINTS:
|
|
29
|
+
- 3-5 points that make this app stand out
|
|
30
|
+
- Be specific based on actual features found in the code
|
|
31
|
+
|
|
32
|
+
## RESPONSE RULES:
|
|
33
|
+
- Respond ONLY with a valid JSON object, no markdown, no explanation
|
|
34
|
+
- All string values must be non-empty \u2014 NEVER return empty strings
|
|
35
|
+
- app_type must be one of: game, health, finance, ecommerce, education, media, utility, social, other
|
|
36
|
+
- advertising_type must be one of: personalized, non_personalized, none
|
|
37
|
+
- business_model.model must be one of: free, paid, freemium
|
|
38
|
+
- business_model.purchase_type must be one of: one_time, subscription, both, none
|
|
39
|
+
- app_category_suggestion should be an App Store category name (e.g., "Photo & Video", "Productivity", "Health & Fitness", "Lifestyle")
|
|
40
|
+
- target_audience should be a 2-3 sentence description of who would use this app and why`;
|
|
41
|
+
function buildUserPrompt(input) {
|
|
42
|
+
const parts = [];
|
|
43
|
+
parts.push(`## Tech Stack
|
|
44
|
+
- Framework: ${input.techStack.label}
|
|
45
|
+
- Platforms: ${input.techStack.platforms.join(", ")}
|
|
46
|
+
- Config files found: ${input.techStack.configFiles.join(", ")}`);
|
|
47
|
+
parts.push(`## Project Configuration
|
|
48
|
+
- App Name: ${input.config.app_name || "Unknown"}
|
|
49
|
+
- Bundle ID: ${input.config.bundle_id || "Unknown"}
|
|
50
|
+
- Version: ${input.config.version || "Unknown"}
|
|
51
|
+
- Min iOS: ${input.config.min_ios_version || "N/A"}
|
|
52
|
+
- Min Android SDK: ${input.config.min_android_sdk || "N/A"}
|
|
53
|
+
- Description from config: ${input.config.description || "N/A"}`);
|
|
54
|
+
if (input.projectTree) {
|
|
55
|
+
parts.push(`## Project File Tree (IMPORTANT \u2014 analyze directory and file names to understand the app's features and architecture)
|
|
56
|
+
${input.projectTree}`);
|
|
57
|
+
}
|
|
58
|
+
if (input.sdkScan.detected_sdks.length > 0) {
|
|
59
|
+
parts.push(`## Detected SDKs (${input.sdkScan.detected_sdks.length})
|
|
60
|
+
${input.sdkScan.detected_sdks.map((sdk) => `- ${sdk.name} (${sdk.category})`).join("\n")}
|
|
61
|
+
|
|
62
|
+
Data collected: ${input.sdkScan.data_collected.join(", ") || "none detected"}
|
|
63
|
+
Advertising type: ${input.sdkScan.advertising_type}
|
|
64
|
+
Has in-app purchases: ${input.sdkScan.has_iap}`);
|
|
65
|
+
} else {
|
|
66
|
+
parts.push(`## Detected SDKs
|
|
67
|
+
No known SDKs detected in dependencies. Determine data collection and business model from source code analysis.`);
|
|
68
|
+
}
|
|
69
|
+
if (input.readmeContent) {
|
|
70
|
+
parts.push(`## README Content (USE THIS to understand the app's purpose)
|
|
71
|
+
${input.readmeContent.substring(0, 8e3)}`);
|
|
72
|
+
}
|
|
73
|
+
if (input.sourceCode) {
|
|
74
|
+
parts.push(`## Source Code Excerpts (ANALYZE CAREFULLY \u2014 look for screens, routes, models, features, API calls)
|
|
75
|
+
${input.sourceCode.substring(0, 4e4)}`);
|
|
76
|
+
}
|
|
77
|
+
parts.push(`## Required JSON Response Format
|
|
78
|
+
{
|
|
79
|
+
"description": "4-8 sentence detailed description covering: what the app does, main features, target use case, and notable aspects. MUST be specific and non-empty.",
|
|
80
|
+
"app_type": "one of: game, health, finance, ecommerce, education, media, utility, social, other",
|
|
81
|
+
"is_for_children": false,
|
|
82
|
+
"app_category_suggestion": "App Store category name (e.g. Lifestyle, Health & Fitness, Productivity)",
|
|
83
|
+
"key_features": ["Detailed feature 1", "Detailed feature 2", "...up to 10 specific features found in code/tree"],
|
|
84
|
+
"target_audience": "2-3 sentences describing who would use this app and why",
|
|
85
|
+
"unique_selling_points": ["Specific USP 1", "Specific USP 2", "...3-5 items"],
|
|
86
|
+
"business_model": {
|
|
87
|
+
"model": "free | paid | freemium",
|
|
88
|
+
"purchase_type": "one_time | subscription | both | none",
|
|
89
|
+
"has_trial": false,
|
|
90
|
+
"has_auto_renewal": false
|
|
91
|
+
}
|
|
92
|
+
}`);
|
|
93
|
+
return parts.join("\n\n");
|
|
94
|
+
}
|
|
95
|
+
async function generateReport(input, apiKey) {
|
|
96
|
+
const genAI = new GoogleGenerativeAI(apiKey);
|
|
97
|
+
const model = genAI.getGenerativeModel({
|
|
98
|
+
model: "gemini-2.5-flash",
|
|
99
|
+
generationConfig: {
|
|
100
|
+
temperature: 0.4,
|
|
101
|
+
responseMimeType: "application/json"
|
|
102
|
+
}
|
|
103
|
+
});
|
|
104
|
+
const userPrompt = buildUserPrompt(input);
|
|
105
|
+
const result = await model.generateContent([
|
|
106
|
+
{ text: SYSTEM_PROMPT },
|
|
107
|
+
{ text: userPrompt }
|
|
108
|
+
]);
|
|
109
|
+
const responseText = result.response.text();
|
|
110
|
+
let aiAnalysis;
|
|
111
|
+
try {
|
|
112
|
+
aiAnalysis = JSON.parse(responseText);
|
|
113
|
+
} catch {
|
|
114
|
+
const jsonMatch = responseText.match(/\{[\s\S]*\}/);
|
|
115
|
+
const rawJson = jsonMatch ? jsonMatch[0] : responseText;
|
|
116
|
+
try {
|
|
117
|
+
const cleaned = rawJson.replace(/,\s*([}\]])/g, "$1").replace(
|
|
118
|
+
/[\x00-\x1F\x7F]/g,
|
|
119
|
+
(ch) => ch === "\n" || ch === "\r" || ch === " " ? ch : ""
|
|
120
|
+
);
|
|
121
|
+
aiAnalysis = JSON.parse(cleaned);
|
|
122
|
+
} catch {
|
|
123
|
+
throw new Error("Failed to parse AI response as JSON");
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
const report = {
|
|
127
|
+
// Basic identification
|
|
128
|
+
app_name: input.config.app_name || "Unknown App",
|
|
129
|
+
bundle_id: input.config.bundle_id || "com.unknown.app",
|
|
130
|
+
description: aiAnalysis.description || input.config.description || "",
|
|
131
|
+
platforms: input.techStack.platforms,
|
|
132
|
+
tech_stack: input.techStack.label,
|
|
133
|
+
min_os_versions: {
|
|
134
|
+
ios: input.config.min_ios_version || void 0,
|
|
135
|
+
android: input.config.min_android_sdk || void 0
|
|
136
|
+
},
|
|
137
|
+
version: input.config.version || "1.0.0",
|
|
138
|
+
// Legal/Privacy from SDK scan + AI
|
|
139
|
+
app_type: aiAnalysis.app_type || "utility",
|
|
140
|
+
is_for_children: aiAnalysis.is_for_children || false,
|
|
141
|
+
data_collected: input.sdkScan.data_collected,
|
|
142
|
+
advertising_type: input.sdkScan.advertising_type,
|
|
143
|
+
third_party_services: input.sdkScan.third_party_services,
|
|
144
|
+
business_model: {
|
|
145
|
+
model: aiAnalysis.business_model?.model || "free",
|
|
146
|
+
purchase_type: aiAnalysis.business_model?.purchase_type || (input.sdkScan.has_iap ? "subscription" : "none"),
|
|
147
|
+
has_trial: aiAnalysis.business_model?.has_trial,
|
|
148
|
+
has_auto_renewal: aiAnalysis.business_model?.has_auto_renewal
|
|
149
|
+
},
|
|
150
|
+
// ASO
|
|
151
|
+
app_category_suggestion: aiAnalysis.app_category_suggestion || "Utilities",
|
|
152
|
+
key_features: aiAnalysis.key_features || [],
|
|
153
|
+
target_audience: aiAnalysis.target_audience || "",
|
|
154
|
+
unique_selling_points: aiAnalysis.unique_selling_points || [],
|
|
155
|
+
// Branding
|
|
156
|
+
primary_color: input.branding.primary_color || "#007AFF",
|
|
157
|
+
secondary_color: input.branding.secondary_color || "#5856D6",
|
|
158
|
+
app_icon_base64: input.branding.app_icon_base64,
|
|
159
|
+
// Raw data
|
|
160
|
+
detected_sdks: input.sdkScan.detected_sdks,
|
|
161
|
+
readme_content: input.readmeContent,
|
|
162
|
+
config_files_found: input.techStack.configFiles
|
|
163
|
+
};
|
|
164
|
+
return report;
|
|
165
|
+
}
|
|
166
|
+
export {
|
|
167
|
+
generateReport
|
|
168
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@forvibe/cli",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Forvibe CLI - AI-powered project analyzer for App Store automation",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"forvibe": "./dist/index.js"
|
|
8
|
+
},
|
|
9
|
+
"scripts": {
|
|
10
|
+
"build": "tsup src/index.ts --format esm --dts --clean",
|
|
11
|
+
"dev": "tsup src/index.ts --format esm --watch",
|
|
12
|
+
"start": "node dist/index.js",
|
|
13
|
+
"typecheck": "tsc --noEmit",
|
|
14
|
+
"lint": "eslint src/",
|
|
15
|
+
"prepublishOnly": "npm run build",
|
|
16
|
+
"release": "npm run build && npm publish --access public",
|
|
17
|
+
"release:dry": "npm run build && npm publish --access public --dry-run"
|
|
18
|
+
},
|
|
19
|
+
"keywords": [
|
|
20
|
+
"forvibe",
|
|
21
|
+
"app-store",
|
|
22
|
+
"aso",
|
|
23
|
+
"cli",
|
|
24
|
+
"ai",
|
|
25
|
+
"automation"
|
|
26
|
+
],
|
|
27
|
+
"author": "Forvibe",
|
|
28
|
+
"license": "MIT",
|
|
29
|
+
"repository": {
|
|
30
|
+
"type": "git",
|
|
31
|
+
"url": "https://github.com/Forvibe/cli.git"
|
|
32
|
+
},
|
|
33
|
+
"homepage": "https://forvibe.com",
|
|
34
|
+
"bugs": {
|
|
35
|
+
"url": "https://github.com/Forvibe/cli/issues"
|
|
36
|
+
},
|
|
37
|
+
"dependencies": {
|
|
38
|
+
"@google/generative-ai": "^0.24.0",
|
|
39
|
+
"chalk": "^5.4.1",
|
|
40
|
+
"commander": "^13.1.0",
|
|
41
|
+
"ora": "^8.2.0",
|
|
42
|
+
"plist": "^3.1.0",
|
|
43
|
+
"yaml": "^2.7.0"
|
|
44
|
+
},
|
|
45
|
+
"devDependencies": {
|
|
46
|
+
"@types/node": "^22.13.0",
|
|
47
|
+
"@types/plist": "^3.0.5",
|
|
48
|
+
"tsup": "^8.4.0",
|
|
49
|
+
"typescript": "^5.7.0"
|
|
50
|
+
},
|
|
51
|
+
"engines": {
|
|
52
|
+
"node": ">=18"
|
|
53
|
+
},
|
|
54
|
+
"files": [
|
|
55
|
+
"dist"
|
|
56
|
+
]
|
|
57
|
+
}
|