@forvibe/cli 1.0.8 → 1.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.
@@ -26,7 +26,7 @@ Your task is to generate a complete, ASO-optimized store listing for a mobile ap
26
26
  6. Write in English (en-US locale)
27
27
 
28
28
  Respond ONLY with a valid JSON object, no markdown, no explanation.`;
29
- function buildASOPrompt(report) {
29
+ function buildASOPrompt(report, selectedStores) {
30
30
  const parts = [];
31
31
  parts.push(`## App Information
32
32
  - App Name: ${report.app_name}
@@ -50,23 +50,31 @@ ${report.unique_selling_points.map((u) => `- ${u}`).join("\n")}`);
50
50
  parts.push(`## README (for additional context)
51
51
  ${report.readme_content.substring(0, 3e3)}`);
52
52
  }
53
- const includePlayStore = report.platforms.includes("android");
54
- parts.push(`## Required JSON Response Format
55
- {
56
- "appstore": {
53
+ const includePlayStore = selectedStores.length > 0 ? selectedStores.includes("playstore") : report.platforms.includes("android");
54
+ const includeAppStore = selectedStores.length > 0 ? selectedStores.includes("appstore") : true;
55
+ const jsonParts = [];
56
+ if (includeAppStore) {
57
+ jsonParts.push(` "appstore": {
57
58
  "app_name": "max 30 chars, keyword-rich app name",
58
59
  "subtitle": "max 30 chars, complementary keywords",
59
60
  "description": "max 4000 chars, ASO-optimized with bullets and line breaks",
60
61
  "keywords": "keyword1,keyword2,keyword3 (max 100 chars, NO spaces after commas)",
61
62
  "promotional_text": "max 170 chars, engaging hook",
62
63
  "whats_new": "max 170 chars, launch highlights"
63
- }${includePlayStore ? `,
64
- "playstore": {
64
+ }`);
65
+ }
66
+ if (includePlayStore) {
67
+ jsonParts.push(` "playstore": {
65
68
  "title": "max 30 chars, Play Store optimized title",
66
69
  "short_description": "max 80 chars, concise value proposition",
67
70
  "description": "max 4000 chars, HTML formatting allowed",
68
71
  "whats_new": "max 500 chars, launch release notes"
69
- }` : ""}
72
+ }`);
73
+ }
74
+ parts.push(`## Required JSON Response Format
75
+ {
76
+ ${jsonParts.join(",\n")}
77
+ }
70
78
  }`);
71
79
  return parts.join("\n\n");
72
80
  }
@@ -82,16 +90,17 @@ function truncateKeywords(keywords, maxLength) {
82
90
  return result || keywords.substring(0, maxLength);
83
91
  }
84
92
  function enforceCharLimits(raw) {
85
- const result = {
86
- appstore: {
93
+ const result = {};
94
+ if (raw.appstore) {
95
+ result.appstore = {
87
96
  app_name: (raw.appstore.app_name || "").substring(0, 30),
88
97
  subtitle: (raw.appstore.subtitle || "").substring(0, 30),
89
98
  description: (raw.appstore.description || "").substring(0, 4e3),
90
99
  keywords: truncateKeywords(raw.appstore.keywords || "", 100),
91
100
  promotional_text: (raw.appstore.promotional_text || "").substring(0, 170),
92
101
  whats_new: (raw.appstore.whats_new || "").substring(0, 170)
93
- }
94
- };
102
+ };
103
+ }
95
104
  if (raw.playstore) {
96
105
  result.playstore = {
97
106
  title: (raw.playstore.title || "").substring(0, 30),
@@ -102,8 +111,8 @@ function enforceCharLimits(raw) {
102
111
  }
103
112
  return result;
104
113
  }
105
- async function generateASOContent(report, provider) {
106
- const userPrompt = buildASOPrompt(report);
114
+ async function generateASOContent(report, provider, selectedStores = []) {
115
+ const userPrompt = buildASOPrompt(report, selectedStores);
107
116
  const responseText = await provider.generateJSON(ASO_SYSTEM_PROMPT, userPrompt, 0.5);
108
117
  let parsed;
109
118
  try {
@@ -121,9 +130,14 @@ async function generateASOContent(report, provider) {
121
130
  throw new Error("Failed to parse ASO AI response as JSON");
122
131
  }
123
132
  }
124
- if (!parsed.appstore) {
133
+ const wantsAppStore = selectedStores.length === 0 || selectedStores.includes("appstore");
134
+ if (wantsAppStore && !parsed.appstore) {
125
135
  throw new Error("AI response missing appstore field");
126
136
  }
137
+ const wantsPlayStore = selectedStores.includes("playstore") || selectedStores.length === 0 && report.platforms.includes("android");
138
+ if (wantsPlayStore && !parsed.playstore) {
139
+ console.warn("AI response missing playstore field \u2014 will be generated server-side");
140
+ }
127
141
  return enforceCharLimits(parsed);
128
142
  }
129
143
  export {
package/dist/index.js CHANGED
@@ -2163,6 +2163,7 @@ var ForvibeClient = class {
2163
2163
  baseUrl;
2164
2164
  sessionToken = null;
2165
2165
  sessionId = null;
2166
+ storeConfig = null;
2166
2167
  constructor(baseUrl) {
2167
2168
  this.baseUrl = (baseUrl || process.env.FORVIBE_API_URL || DEFAULT_API_URL).replace(/\/$/, "");
2168
2169
  }
@@ -2192,6 +2193,7 @@ var ForvibeClient = class {
2192
2193
  const data = await response.json();
2193
2194
  this.sessionToken = data.session_token;
2194
2195
  this.sessionId = data.session_id;
2196
+ this.storeConfig = data.store_config || null;
2195
2197
  return data;
2196
2198
  }
2197
2199
  /**
@@ -2598,8 +2600,9 @@ async function analyzeCommand(options) {
2598
2600
  prefixText: " "
2599
2601
  }).start();
2600
2602
  try {
2601
- const { generateASOContent } = await import("./aso-generator-5I3OEF5J.js");
2602
- const asoContent = await generateASOContent(report, provider);
2603
+ const { generateASOContent } = await import("./aso-generator-PF4SDBTL.js");
2604
+ const stores = client.storeConfig?.stores || [];
2605
+ const asoContent = await generateASOContent(report, provider, stores);
2603
2606
  report.aso_content = asoContent;
2604
2607
  asoSpinner.succeed(chalk.green("Store listing content generated!"));
2605
2608
  } catch (error) {
@@ -2628,16 +2631,19 @@ async function analyzeCommand(options) {
2628
2631
  console.log(` Assets: ${report.app_assets?.length ? chalk.green(`${report.app_assets.length} found`) : chalk.gray("none")}`);
2629
2632
  console.log(chalk.gray(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
2630
2633
  if (report.aso_content) {
2631
- console.log();
2632
- console.log(chalk.bold(" \u{1F4F1} App Store Listing Preview"));
2633
- console.log(chalk.gray(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
2634
- const aso = report.aso_content.appstore;
2635
- console.log(` Title: ${chalk.white(aso.app_name)} ${chalk.gray(`(${aso.app_name.length}/30)`)}`);
2636
- console.log(` Subtitle: ${chalk.white(aso.subtitle)} ${chalk.gray(`(${aso.subtitle.length}/30)`)}`);
2637
- console.log(` Keywords: ${chalk.white(aso.keywords)} ${chalk.gray(`(${aso.keywords.length}/100)`)}`);
2638
- console.log(` Promo: ${chalk.white(aso.promotional_text)} ${chalk.gray(`(${aso.promotional_text.length}/170)`)}`);
2639
- const descPreview = aso.description.substring(0, 150).replace(/\n/g, " ");
2640
- console.log(` Description: ${chalk.white(descPreview)}${aso.description.length > 150 ? chalk.gray("...") : ""} ${chalk.gray(`(${aso.description.length}/4000)`)}`);
2634
+ if (report.aso_content.appstore) {
2635
+ console.log();
2636
+ console.log(chalk.bold(" \u{1F4F1} App Store Listing Preview"));
2637
+ console.log(chalk.gray(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
2638
+ const aso = report.aso_content.appstore;
2639
+ console.log(` Title: ${chalk.white(aso.app_name)} ${chalk.gray(`(${aso.app_name.length}/30)`)}`);
2640
+ console.log(` Subtitle: ${chalk.white(aso.subtitle)} ${chalk.gray(`(${aso.subtitle.length}/30)`)}`);
2641
+ console.log(` Keywords: ${chalk.white(aso.keywords)} ${chalk.gray(`(${aso.keywords.length}/100)`)}`);
2642
+ console.log(` Promo: ${chalk.white(aso.promotional_text)} ${chalk.gray(`(${aso.promotional_text.length}/170)`)}`);
2643
+ const descPreview = aso.description.substring(0, 150).replace(/\n/g, " ");
2644
+ console.log(` Description: ${chalk.white(descPreview)}${aso.description.length > 150 ? chalk.gray("...") : ""} ${chalk.gray(`(${aso.description.length}/4000)`)}`);
2645
+ console.log(chalk.gray(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
2646
+ }
2641
2647
  if (report.aso_content.playstore) {
2642
2648
  console.log();
2643
2649
  console.log(chalk.bold(" \u{1F916} Play Store Listing Preview"));
@@ -2647,56 +2653,200 @@ async function analyzeCommand(options) {
2647
2653
  console.log(` Short Desc: ${chalk.white(ps.short_description)} ${chalk.gray(`(${ps.short_description.length}/80)`)}`);
2648
2654
  const psDescPreview = ps.description.substring(0, 150).replace(/\n/g, " ");
2649
2655
  console.log(` Description: ${chalk.white(psDescPreview)}${ps.description.length > 150 ? chalk.gray("...") : ""} ${chalk.gray(`(${ps.description.length}/4000)`)}`);
2656
+ console.log(chalk.gray(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
2657
+ }
2658
+ }
2659
+ const selectedStores = client.storeConfig?.stores || [];
2660
+ const hasASC = selectedStores.includes("appstore") || selectedStores.length === 0;
2661
+ const hasPS = selectedStores.includes("playstore") || selectedStores.length === 0;
2662
+ if (hasASC) {
2663
+ console.log();
2664
+ console.log(chalk.bold(" \u{1F4DD} App Review Information"));
2665
+ console.log(chalk.gray(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
2666
+ console.log(chalk.gray(" Apple's review team may need to sign in to your app."));
2667
+ console.log(chalk.gray(" You can also provide this later in the Forvibe dashboard."));
2668
+ console.log();
2669
+ const hasAuthSDKs = (sdkScan.third_party_services.auth?.length ?? 0) > 0;
2670
+ if (hasAuthSDKs) {
2671
+ const signInAnswer = await askQuestion(
2672
+ chalk.cyan(" Does your app require sign-in to use? (y/n): ")
2673
+ );
2674
+ const signInRequired = signInAnswer.toLowerCase().startsWith("y");
2675
+ report.sign_in_required = signInRequired;
2676
+ if (signInRequired) {
2677
+ console.log();
2678
+ console.log(chalk.yellow(" \u26A0 Please create a test account in your app for Apple's review team."));
2679
+ console.log(chalk.gray(" This account should have full access to your app's features."));
2680
+ console.log();
2681
+ const testEmail = await askQuestion(chalk.cyan(" Test account email: "));
2682
+ const testPassword = await askQuestion(chalk.cyan(" Test account password: "));
2683
+ if (testEmail && testPassword) {
2684
+ report.test_account = { email: testEmail, password: testPassword };
2685
+ console.log(chalk.green(" \u2713 Test account saved"));
2686
+ }
2687
+ }
2688
+ } else {
2689
+ report.sign_in_required = false;
2690
+ }
2691
+ console.log();
2692
+ console.log(chalk.gray(" Contact information for Apple's review team (press Enter to skip):"));
2693
+ console.log();
2694
+ const firstName = await askQuestion(chalk.cyan(" First name: "));
2695
+ const lastName = await askQuestion(chalk.cyan(" Last name: "));
2696
+ const contactEmail = await askQuestion(chalk.cyan(" Email: "));
2697
+ const contactPhone = await askQuestion(chalk.cyan(" Phone (e.g. +1 555 123 4567): "));
2698
+ if (firstName && lastName && contactEmail) {
2699
+ report.review_contact = {
2700
+ first_name: firstName,
2701
+ last_name: lastName,
2702
+ email: contactEmail,
2703
+ phone: contactPhone || ""
2704
+ };
2705
+ console.log(chalk.green(" \u2713 Contact info saved"));
2706
+ } else {
2707
+ console.log(chalk.gray(" \u2139 Skipped \u2014 you can add this later in the dashboard."));
2650
2708
  }
2651
2709
  console.log(chalk.gray(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
2652
2710
  }
2653
- console.log();
2654
- console.log(chalk.bold(" \u{1F4DD} App Review Information"));
2655
- console.log(chalk.gray(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
2656
- console.log(chalk.gray(" Apple's review team may need to sign in to your app."));
2657
- console.log(chalk.gray(" You can also provide this later in the Forvibe dashboard."));
2658
- console.log();
2659
- const hasAuthSDKs = (sdkScan.third_party_services.auth?.length ?? 0) > 0;
2660
- if (hasAuthSDKs) {
2661
- const signInAnswer = await askQuestion(
2662
- chalk.cyan(" Does your app require sign-in to use? (y/n): ")
2711
+ if (hasPS) {
2712
+ console.log();
2713
+ console.log(chalk.bold(" \u{1F916} Play Store Declarations"));
2714
+ console.log(chalk.gray(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
2715
+ console.log(chalk.gray(" Google Play requires content declarations before publishing."));
2716
+ console.log(chalk.gray(" We'll auto-detect what we can and ask you to confirm."));
2717
+ console.log();
2718
+ const hasAdSDKs = (sdkScan.third_party_services.ads?.length ?? 0) > 0;
2719
+ const hasPaymentSDKs = (sdkScan.third_party_services.payment?.length ?? 0) > 0;
2720
+ const isHealthApp = report.app_type === "health";
2721
+ const isFinanceApp = report.app_type === "finance";
2722
+ const hasHealthSDKs = sdkScan.detected_sdks.some(
2723
+ (s) => s.name.toLowerCase().includes("health") || s.collects.includes("health_data")
2663
2724
  );
2664
- const signInRequired = signInAnswer.toLowerCase().startsWith("y");
2665
- report.sign_in_required = signInRequired;
2666
- if (signInRequired) {
2667
- console.log();
2668
- console.log(chalk.yellow(" \u26A0 Please create a test account in your app for Apple's review team."));
2669
- console.log(chalk.gray(" This account should have full access to your app's features."));
2725
+ const hasFinanceSDKs = hasPaymentSDKs || sdkScan.detected_sdks.some(
2726
+ (s) => s.name.toLowerCase().includes("billing") || s.name.toLowerCase().includes("stripe") || s.name.toLowerCase().includes("revenue")
2727
+ );
2728
+ let appAccess = "unrestricted";
2729
+ let appAccessInstructions;
2730
+ if (report.sign_in_required) {
2731
+ appAccess = "restricted_login";
2732
+ if (report.test_account) {
2733
+ appAccessInstructions = `Test account: ${report.test_account.email} / ${report.test_account.password}`;
2734
+ }
2735
+ console.log(chalk.white(" App Access: ") + chalk.yellow("Restricted (login required)"));
2736
+ } else {
2737
+ const accessAnswer = await askQuestion(
2738
+ chalk.cyan(" Does your app have restricted access (login, special permissions, etc.)? (y/n): ")
2739
+ );
2740
+ if (accessAnswer.toLowerCase().startsWith("y")) {
2741
+ appAccess = "restricted_login";
2742
+ const instrAnswer = await askQuestion(
2743
+ chalk.cyan(" How can a reviewer access all features? (describe or Enter to skip): ")
2744
+ );
2745
+ if (instrAnswer) appAccessInstructions = instrAnswer;
2746
+ } else {
2747
+ console.log(chalk.green(" \u2713 App is freely accessible"));
2748
+ }
2749
+ }
2750
+ let containsAds = hasAdSDKs || report.advertising_type !== "none";
2751
+ if (containsAds) {
2752
+ console.log(chalk.white(" Ads: ") + chalk.yellow(`Detected (${sdkScan.third_party_services.ads?.join(", ") || report.advertising_type})`));
2753
+ const adsConfirm = await askQuestion(
2754
+ chalk.cyan(" Does your app contain ads? (Y/n): ")
2755
+ );
2756
+ if (adsConfirm.toLowerCase() === "n") containsAds = false;
2757
+ } else {
2758
+ const adsAnswer = await askQuestion(
2759
+ chalk.cyan(" Does your app contain ads? (y/N): ")
2760
+ );
2761
+ containsAds = adsAnswer.toLowerCase().startsWith("y");
2762
+ }
2763
+ console.log(containsAds ? chalk.yellow(" \u2192 Contains ads") : chalk.green(" \u2713 No ads"));
2764
+ let targetAgeGroup = "all_ages";
2765
+ let isAppealToChildren = false;
2766
+ if (report.is_for_children) {
2767
+ console.log(chalk.yellow(" \u26A0 App appears to target children"));
2768
+ targetAgeGroup = "all_ages";
2769
+ isAppealToChildren = true;
2770
+ } else {
2670
2771
  console.log();
2671
- const testEmail = await askQuestion(chalk.cyan(" Test account email: "));
2672
- const testPassword = await askQuestion(chalk.cyan(" Test account password: "));
2673
- if (testEmail && testPassword) {
2674
- report.test_account = { email: testEmail, password: testPassword };
2675
- console.log(chalk.green(" \u2713 Test account saved"));
2772
+ console.log(chalk.gray(" Target age groups:"));
2773
+ console.log(chalk.gray(" 1. All ages (including children under 13)"));
2774
+ console.log(chalk.gray(" 2. Older users only (13+ or 18+)"));
2775
+ console.log(chalk.gray(" 3. Mixed (targets both children and older users)"));
2776
+ const ageAnswer = await askQuestion(chalk.cyan(" Choose target audience (1/2/3): "));
2777
+ if (ageAnswer === "2") {
2778
+ targetAgeGroup = "older_users_only";
2779
+ } else if (ageAnswer === "3") {
2780
+ targetAgeGroup = "mixed";
2781
+ isAppealToChildren = true;
2782
+ } else {
2783
+ targetAgeGroup = "all_ages";
2784
+ const appealAnswer = await askQuestion(
2785
+ chalk.cyan(" Could the app appeal to children even if not targeted at them? (y/N): ")
2786
+ );
2787
+ isAppealToChildren = appealAnswer.toLowerCase().startsWith("y");
2676
2788
  }
2677
2789
  }
2678
- } else {
2679
- report.sign_in_required = false;
2680
- }
2681
- console.log();
2682
- console.log(chalk.gray(" Contact information for Apple's review team (press Enter to skip):"));
2683
- console.log();
2684
- const firstName = await askQuestion(chalk.cyan(" First name: "));
2685
- const lastName = await askQuestion(chalk.cyan(" Last name: "));
2686
- const contactEmail = await askQuestion(chalk.cyan(" Email: "));
2687
- const contactPhone = await askQuestion(chalk.cyan(" Phone (e.g. +1 555 123 4567): "));
2688
- if (firstName && lastName && contactEmail) {
2689
- report.review_contact = {
2690
- first_name: firstName,
2691
- last_name: lastName,
2692
- email: contactEmail,
2693
- phone: contactPhone || ""
2790
+ const govAnswer = await askQuestion(
2791
+ chalk.cyan(" Is this a government app? (y/N): ")
2792
+ );
2793
+ const isGovernmentApp = govAnswer.toLowerCase().startsWith("y");
2794
+ let hasFinancialFeatures = isFinanceApp || hasFinanceSDKs;
2795
+ if (hasFinancialFeatures) {
2796
+ console.log(chalk.white(" Financial features: ") + chalk.yellow("Detected"));
2797
+ const finConfirm = await askQuestion(
2798
+ chalk.cyan(" Does your app offer financial features (payments, digital wallet, crypto, etc.)? (Y/n): ")
2799
+ );
2800
+ if (finConfirm.toLowerCase() === "n") hasFinancialFeatures = false;
2801
+ } else {
2802
+ const finAnswer = await askQuestion(
2803
+ chalk.cyan(" Does your app offer financial features (payments, digital wallet, crypto, etc.)? (y/N): ")
2804
+ );
2805
+ hasFinancialFeatures = finAnswer.toLowerCase().startsWith("y");
2806
+ }
2807
+ let financialDetails;
2808
+ if (hasFinancialFeatures) {
2809
+ const finDetailsAnswer = await askQuestion(
2810
+ chalk.cyan(" Describe financial features briefly (or Enter to skip): ")
2811
+ );
2812
+ if (finDetailsAnswer) financialDetails = finDetailsAnswer;
2813
+ }
2814
+ let isHealthAppDecl = isHealthApp || hasHealthSDKs;
2815
+ if (isHealthAppDecl) {
2816
+ console.log(chalk.white(" Health app: ") + chalk.yellow("Detected"));
2817
+ const healthConfirm = await askQuestion(
2818
+ chalk.cyan(" Is this a health or fitness app? (Y/n): ")
2819
+ );
2820
+ if (healthConfirm.toLowerCase() === "n") isHealthAppDecl = false;
2821
+ } else {
2822
+ const healthAnswer = await askQuestion(
2823
+ chalk.cyan(" Is this a health or fitness app? (y/N): ")
2824
+ );
2825
+ isHealthAppDecl = healthAnswer.toLowerCase().startsWith("y");
2826
+ }
2827
+ let healthDetails;
2828
+ if (isHealthAppDecl) {
2829
+ const healthDetailsAnswer = await askQuestion(
2830
+ chalk.cyan(" Describe health features briefly (or Enter to skip): ")
2831
+ );
2832
+ if (healthDetailsAnswer) healthDetails = healthDetailsAnswer;
2833
+ }
2834
+ report.play_store_declarations = {
2835
+ app_access: appAccess,
2836
+ app_access_instructions: appAccessInstructions,
2837
+ contains_ads: containsAds,
2838
+ target_age_group: targetAgeGroup,
2839
+ is_appeal_to_children: isAppealToChildren,
2840
+ is_government_app: isGovernmentApp,
2841
+ has_financial_features: hasFinancialFeatures,
2842
+ financial_features_details: financialDetails,
2843
+ is_health_app: isHealthAppDecl,
2844
+ health_app_details: healthDetails
2694
2845
  };
2695
- console.log(chalk.green(" \u2713 Contact info saved"));
2696
- } else {
2697
- console.log(chalk.gray(" \u2139 Skipped \u2014 you can add this later in the dashboard."));
2846
+ console.log();
2847
+ console.log(chalk.green(" \u2713 Play Store declarations collected"));
2848
+ console.log(chalk.gray(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
2698
2849
  }
2699
- console.log(chalk.gray(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
2700
2850
  console.log();
2701
2851
  const sendSpinner = ora({
2702
2852
  text: "Sending report to Forvibe...",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@forvibe/cli",
3
- "version": "1.0.8",
3
+ "version": "1.1.0",
4
4
  "description": "Forvibe CLI - AI-powered project analyzer for App Store automation",
5
5
  "type": "module",
6
6
  "bin": {