כתיבה

מפעל לאפליקציות: 41 סקילים שלוקחים אפליקציה מקוד ועד App Store ו-Google Play

הדרך המקובלת להנחות סוכן קוד היא קובץ הוראות אחד וגדול: "ככה משחררים לאפל", "ככה כותבים בדיקות", "ככה מעדכנים מחירון".

אצלי זה החזיק בערך עד האפליקציה השלישית. הקובץ גדל מעבר למה שנשאר בפוקוס, כללים התחילו לסתור זה את זה, והסוכן הציע לי דיפלוי לאנדרואיד בזמן שערכתי אתר אינטרנט.

הכשל הוא לא שהסוכן שוכח. הכשל הוא שסיפרו לו הכול בבת אחת, בין אם זה רלוונטי ובין אם לא.

זה החלק השני בסדרה על סוכני AI בסביבה של כמה מאגרים. כאן אתאר את מה שבא במקום הקובץ הענק: App Factory, ספריית סקילים בקוד פתוח שעובדת כפס ייצור. היא לוקחת ריפו עם קוד של אפליקציה ומביאה אותו עד לאפליקציה חיה ב-App Store וב-Google Play.


סקיל: הוראות, וגם הכלי

ב-Claude Code, סקיל הוא מסמך הוראות שהסוכן טוען רק כשהתיאור שלו מתאים למה שביקשת. 41 סקילים זמינים עולים בערך 8,100 טוקנים של תיאורים, וגם את זה משלמים רק בריפו שבו הפעלת אותם.

אבל ההבדל העיקרי הוא אחר. קובץ הוראות אומר לסוכן מה לעשות. סקיל במפעל מביא איתו גם סקריפט שעושה את זה ובודק את התוצאה. הסוכן מתזמר, והסקריפטים מבצעים.


פס הייצור: סקילים ליצירת אפליקציה מ-0 ועד הפצה

0  זהות          שם, תכונות, תיק אחד שכולם מצטטים
1  הבינארי       מפתחות → חתימה → תאימות → תרגום → אייקון → בנייה
2  מדיה          צילום מהאפליקציה הרצה
3  טקסט          לכל חנות, בכל שפה
4  רישום בחנות   משיכה והשוואה לפני כל העלאה
5  שחרור         הבינארי לחנות
6  אחרי          ביקורות, מחירים, מילות מפתח

0. זהות. app-identity קורא את הקוד ומחליט איתך על שם. הסקריפט שלו, check_name.py, שואל את ה-iTunes Search API על המתחרים בקטגוריה. לכל יתרון שאתה רוצה למכור הוא סופר כמה מתחרים כבר טוענים אותו. יתרון שמופיע אצל יותר מארבעה מקבל את התווית TABLE STAKES — do not sell on this.

אחר כך app-profile כותב תיק אחד, profile.md, מתוך הקוד, מחקר מתחרים עם מקורות, וריאיון איתך. כל טקסט בהמשך מצטט ממנו, וכלל הברזל הוא ששום סקיל לא ממציא טענה.

1. הבינארי. המפתחות יושבים מחוץ לכל ריפו ונקראים במקום, בלי שיועתקו או יודפסו. לפני הבנייה רצה סריקת תאימות, כי הממצאים שלה משנים את הפרויקט. היא מחפשת סיבות ידועות לדחייה:

  • API שדורש מחרוזת הרשאה ואין לו.
  • SDK בלי privacy manifest.
  • התחברות חברתית בלי Sign in with Apple.
  • ב-Android: target API level והרשאות.

תיקון שרק מוסיף נעשה אוטומטית. תיקון שמסיר משהו מחכה לך.

2. מדיה. appstore-media לא מבקש ממך צילומי מסך. הוא מעלה סימולטור עם פס סטטוס נקי (9:41, סוללה מלאה), מריץ בדיקת XCUITest לכל שפה, ומלביש על התמונות מסגרת מכשיר וכיתוב. לכיתוב בעברית הוא מסרב לרוץ אם הספרייה לא תומכת בכתיבה מימין לשמאל, כי עדיף לא לייצר מאשר לייצר טקסט הפוך. ב-Android, play-store-media מצלם עם adb.

3. טקסט. כל חנות מקבלת את השדות שלה במגבלות שלה. כל טקסט שלקוח יקרא עובר דרך copy-edit, ובו הבדיקה שהכי אהבתי: הוא סופר טענות בכל שפה. אם בעברית יש טענה אחת פחות מאשר באנגלית, משהו נפל בתרגום. בשפה שאף אחד בצוות לא קורא, זו הבדיקה היחידה שיש.

4. רישום בחנות. שתי החנויות מחליפות את כל סט התמונות שהן מקבלות, והעלאה עיוורת דורסת עריכות שנעשו ב-Console. לכן כל העלאה מתחילה ב-pull_and_diff, שמושך את הרישום החי ומשווה אותו למה שיש אצלך. אם בשני הצדדים יש טקסט שונה, הוא עוצר.

גם הרכישות בתוך האפליקציה מוקמות אוטומטית בשתי החנויות. ב-Play זה נעשה דרך ה-API החדש, כי הישן עונה היום 403 לכל אפליקציה. גיליתי את זה כשהוא נשבר לי באמצע שחרור.

5. שחרור.

  • Apple: ship-apple-app מוודא שכל החלקים קיימים ומחזיר כל חוסר לסקיל שאחראי עליו. אחר כך הוא בונה, מאמת ומעלה.
  • Google Play: publish_aab.py מעלה את ה-AAB, ואז קורא בחזרה מה נחת בפועל. הוא תומך ב-rollout חלקי, ב---dry-run שמבטל בסוף, וב---draft לאפליקציה שעוד לא פורסמה, שבלעדיו Play מחזירה HTTP 400.

6. אחרי. תגובות לביקורות בשפה של הכותב, אף פעם לא בלי "כן" מפורש. price-sync משווה את המחירים באפליקציה ובאתר מול מקור אמת אחד, ונכשל על כל אי-התאמה.

מעל השלבים יושבים שני מתזמרים. launch-app מוביל השקה ראשונה ומנהל קובץ מצב, כך שהשקה של שבועיים לא מתחילה מחדש בכל פעם. prepare-app-release מוביל כל גרסה שאחריה.

הפס ממשיך גם מחוץ לחנויות:

  • הפצה ישירה של אפליקציית Mac: target נפרד עם Sparkle לעדכונים, notarization וחתימה.
  • הפצה ישירה ל-Windows: מתקין NSIS שמעדכן את עצמו דרך WinSparkle, חתום באותו מפתח של גרסת ה-Mac ורשום בפיד משלו. אין לו חתימת קוד, ולכן Windows מזהיר בהפעלה הראשונה.
  • מלכודות WebView שנמדדו על מכשירים, לאפליקציות Capacitor.
  • ביקורת SEO לאתר של האפליקציה.

איפה האדם נשאר

הפס עוצר בכוונה בכמה נקודות, וזה כתוב בקובץ משלו, TRUST.md:

  • שום סקריפט לא שולח לביקורת. ההעלאה נוחתת כטיוטה, ואת Submit לוחץ אדם.
  • מחיר לא משתנה אוטומטית אף פעם.
  • שום דבר לא נדחף לגיט, אלא אם ביקשת באותו רגע.

והוא אומר גם מה הוא לא עושה: תעודות חתימה נוצרות ב-Xcode, טפסי ה-Console של Play הם עבודה ידנית, ול-Windows אין מסלול חנות: הוא מקבל הורדה ישירה שמעדכנת את עצמה, בלי תעודת חתימת קוד.


מה נטען איפה

הסקילים מחולקים לשמונה מסלולים, וכל מסלול הוא פלאגין נפרד:

מסלול סקילים על מה
apple-track 16 App Store, Mac App Store והפצה ישירה
android-track 9 Google Play, והעלאת בילד למכשיר
shared-track 7 מה שזהה בשתי החנויות: זהות, טקסט, סדר שחרור, מחירים
web-track 4 מבנה אתר, ביקורת עליו, ו-SEO
capacitor-track 2 אפליקציות web-in-a-shell
design-track 1 עיצוב לכל ממשק HTML
windows-track 1 מתקין Windows שמעדכן את עצמו, מחוץ לכל חנות
factory-setup 1 אומר מתי ריפו נראה כמו אפליקציה ואין בו מסלול פעיל

שום דבר לא גלובלי. מפעילים מסלול לריפו מסוים:

claude plugin marketplace add yybd/app-factory
cd ~/code/my-app && python3 ~/.claude/plugins/marketplaces/app-factory/factory/enable.py

הסקריפט מזהה מה יש בריפו (Xcode, Gradle, Capacitor, אתר) ומציע את המסלולים המתאימים. סשן שנפתח בתיקיית האתר לא מכיר את play-store-ship, כי הסקיל פשוט לא נטען שם.


שלושה דברים שלמדתי בדרך הקשה

1. קומיט הוא לא פריסה. פלאגין ב-Claude Code הוא עותק בתוך ~/.claude/plugins/cache. תיקנתי סקיל, עשיתי קומיט, דיווחתי "תוקן", והמחשב המשיך להריץ את הגרסה השבורה.

2. claude plugin update משווה גרסאות, לא תוכן. שינוי בלי העלאת מספר גרסה לא מגיע לאף אחד. מדדתי: שישה מסלולים ענו "already at the latest version", בזמן שהשוואה בית-בבית הראתה שהעותקים שלהם ישנים.

3. בדיקה שרצה רק במחשב שבו נכתבה לא נבדקה. ה-CI נכשל בפעם הראשונה שדחפתי לגיטהאב, בגלל ספרייה אחת שהייתה מותקנת אצלי ולא ב-runner.


סיכום

אין כאן קסם, וגם לא הבטחה שהסוכן לא יטעה. יש פס ייצור: כל שלב יודע מה הוא צריך מהשלב שלפניו, כל סקריפט בודק לפני שהוא כותב, וכל צעד שאין ממנו חזרה נשאר בידיים שלך. וכל הוראה נטענת רק במקום שבו היא רלוונטית.

הפרויקט בקוד פתוח (MIT): github.com/yybd/app-factory. הוא בנוי ל-Claude Code ונבדק רק בו. QUICKSTART.md עובר על כל התהליך ברבע שעה, על אפליקציה אחת.

בחלק הראשון: מה קורה כשסוכן שעומד בריפו אחד כותב לתוך ריפו אחר, ולמה הפתרון היה הוקים ולא עוד שורה ב-CLAUDE.md. זה RepoGuards.