pabal-web-mcp 1.4.3 → 1.4.4

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.
@@ -517,6 +517,19 @@ function convertToMultilingual(data, locale) {
517
517
  // src/tools/public-to-aso.ts
518
518
  import fs4 from "fs";
519
519
  var FIELD_LIMITS_DOC_PATH = "docs/aso/ASO_FIELD_LIMITS.md";
520
+ var APP_STORE_LIMITS = {
521
+ name: 30,
522
+ subtitle: 30,
523
+ keywords: 100,
524
+ promotionalText: 170,
525
+ description: 4e3,
526
+ whatsNew: 4e3
527
+ };
528
+ var GOOGLE_PLAY_LIMITS = {
529
+ title: 50,
530
+ shortDescription: 80,
531
+ fullDescription: 4e3
532
+ };
520
533
  var toJsonSchema2 = zodToJsonSchema2;
521
534
  var publicToAsoInputSchema = z2.object({
522
535
  slug: z2.string().describe("Product slug"),
@@ -527,6 +540,51 @@ var jsonSchema2 = toJsonSchema2(publicToAsoInputSchema, {
527
540
  $refStrategy: "none"
528
541
  });
529
542
  var inputSchema2 = jsonSchema2.definitions?.PublicToAsoInput || jsonSchema2;
543
+ function validateFieldLimits(configData) {
544
+ const issues = [];
545
+ if (configData.appStore) {
546
+ const appStoreData = configData.appStore;
547
+ const locales = isAppStoreMultilingual(appStoreData) ? appStoreData.locales : { [appStoreData.locale || DEFAULT_LOCALE]: appStoreData };
548
+ for (const [locale, data] of Object.entries(locales)) {
549
+ const { name, subtitle, keywords, promotionalText, description, whatsNew } = data;
550
+ const push = (field, value, limit) => {
551
+ if (typeof value === "string" && value.length > limit) {
552
+ issues.push(
553
+ `App Store [${locale}].${field}: ${value.length}/${limit}`
554
+ );
555
+ }
556
+ };
557
+ push("name", name, APP_STORE_LIMITS.name);
558
+ push("subtitle", subtitle, APP_STORE_LIMITS.subtitle);
559
+ push("keywords", keywords, APP_STORE_LIMITS.keywords);
560
+ push("promotionalText", promotionalText, APP_STORE_LIMITS.promotionalText);
561
+ push("description", description, APP_STORE_LIMITS.description);
562
+ push("whatsNew", whatsNew, APP_STORE_LIMITS.whatsNew);
563
+ }
564
+ }
565
+ if (configData.googlePlay) {
566
+ const googlePlayData = configData.googlePlay;
567
+ const locales = isGooglePlayMultilingual(googlePlayData) ? googlePlayData.locales : { [googlePlayData.defaultLanguage || DEFAULT_LOCALE]: googlePlayData };
568
+ for (const [locale, data] of Object.entries(locales)) {
569
+ const { title, shortDescription, fullDescription } = data;
570
+ const push = (field, value, limit) => {
571
+ if (typeof value === "string" && value.length > limit) {
572
+ issues.push(
573
+ `Google Play [${locale}].${field}: ${value.length}/${limit}`
574
+ );
575
+ }
576
+ };
577
+ push("title", title, GOOGLE_PLAY_LIMITS.title);
578
+ push(
579
+ "shortDescription",
580
+ shortDescription,
581
+ GOOGLE_PLAY_LIMITS.shortDescription
582
+ );
583
+ push("fullDescription", fullDescription, GOOGLE_PLAY_LIMITS.fullDescription);
584
+ }
585
+ }
586
+ return issues;
587
+ }
530
588
  async function downloadScreenshotsToAsoDir(slug, asoData) {
531
589
  const rootDir = getPushDataDir();
532
590
  const productStoreRoot = path4.join(rootDir, "products", slug, "store");
@@ -672,6 +730,7 @@ This tool:
672
730
  2. Converts to store-compatible format (removes screenshots from metadata, sets contactWebsite/marketingUrl)
673
731
  3. Saves metadata to .aso/pushData/products/[slug]/store/ (path from ~/.config/pabal-mcp/config.json dataDir)
674
732
  4. Copies/downloads screenshots to .aso/pushData/products/[slug]/store/screenshots/
733
+ 5. Validates text field lengths against ${FIELD_LIMITS_DOC_PATH} (fails if over limits)
675
734
 
676
735
  Before running, review ${FIELD_LIMITS_DOC_PATH} for per-store limits. This prepares data for pushing to stores without actually uploading.`,
677
736
  inputSchema: inputSchema2
@@ -761,6 +820,11 @@ Possible causes:
761
820
  );
762
821
  }
763
822
  const storeData = prepareAsoDataForPush(slug, configData);
823
+ const validationIssues = validateFieldLimits(configData);
824
+ const validationMessage = validationIssues.length > 0 ? `\u26A0\uFE0F Field limit issues (see ${FIELD_LIMITS_DOC_PATH}):
825
+ - ${validationIssues.join(
826
+ "\n- "
827
+ )}` : `Field limits OK (checked against ${FIELD_LIMITS_DOC_PATH})`;
764
828
  const pushDataRoot = getPushDataDir();
765
829
  if (dryRun) {
766
830
  return {
@@ -773,11 +837,19 @@ ${JSON.stringify(
773
837
  storeData,
774
838
  null,
775
839
  2
776
- )}`
840
+ )}
841
+
842
+ ${validationMessage}`
777
843
  }
778
844
  ]
779
845
  };
780
846
  }
847
+ if (validationIssues.length > 0) {
848
+ throw new Error(
849
+ `Field limit violations detected. Fix before pushing.
850
+ ${validationMessage}`
851
+ );
852
+ }
781
853
  saveRawAsoData(slug, storeData);
782
854
  await downloadScreenshotsToAsoDir(slug, configData);
783
855
  const localeCounts = {};
@@ -1314,10 +1386,10 @@ ${researchSections.join("\n")}
1314
1386
  prompt += `**CRITICAL**: You MUST include the complete \`landing\` object in your optimized JSON output.
1315
1387
 
1316
1388
  `;
1317
- prompt += `## Step 3: Validate
1389
+ prompt += `## Step 3: Validate (after applying all keywords)
1318
1390
 
1319
1391
  `;
1320
- prompt += `Check all limits: title \u226430, subtitle \u226430, shortDescription \u226480, keywords \u2264100, intro \u2264300, outro \u2264200
1392
+ prompt += `Check all limits using ${FIELD_LIMITS_DOC_PATH2}: title \u226430, subtitle \u226430, shortDescription \u226480, keywords \u2264100, intro \u2264300, outro \u2264200
1321
1393
  `;
1322
1394
  prompt += `- Remove keyword duplicates (unique list; avoid repeating title/subtitle terms verbatim)
1323
1395
  `;
@@ -1436,7 +1508,7 @@ function generateKeywordLocalizationPrompt(args) {
1436
1508
  `;
1437
1509
  prompt += `2. **Replace ONLY keywords with optimized keywords** - keep ALL existing content, structure, tone, and context unchanged. Only swap keywords for better ASO keywords.
1438
1510
  `;
1439
- prompt += `3. Validate character limits + store rules (${FIELD_LIMITS_DOC_PATH2}) + keyword duplication
1511
+ prompt += `3. After all keywords are applied, validate character limits + store rules (${FIELD_LIMITS_DOC_PATH2}) + keyword duplication
1440
1512
  `;
1441
1513
  prompt += `4. **SAVE the updated JSON to file** using the save-locale-file tool (only if file exists)
1442
1514
 
@@ -1633,7 +1705,7 @@ ${researchSections.join(
1633
1705
  `;
1634
1706
  prompt += `3. **CRITICAL**: Ensure ALL landing fields are translated (not English)
1635
1707
  `;
1636
- prompt += `4. Validate limits + store rules (${FIELD_LIMITS_DOC_PATH2}) + keyword duplication (unique list; avoid repeating title/subtitle terms verbatim)
1708
+ prompt += `4. After swapping keywords, validate limits + store rules (${FIELD_LIMITS_DOC_PATH2}) + keyword duplication (unique list; avoid repeating title/subtitle terms verbatim)
1637
1709
  `;
1638
1710
  prompt += `5. **SAVE the updated JSON to file** using save-locale-file tool
1639
1711
  `;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pabal-web-mcp",
3
- "version": "1.4.3",
3
+ "version": "1.4.4",
4
4
  "type": "module",
5
5
  "description": "MCP server for ASO data management with shared types and utilities",
6
6
  "author": "skyu",