פלייבוק חינמי · כלי ב-60 דקות

תוכנית לסוכן קוד שמאשרים מהטלפון ב-2 דקות

ההבטחה: בתוך שעה סוכן הקוד שלכם (Claude Code, Cursor או Codex) יכתוב לפני כל משימה עמוד HTML אחד: מה ישתנה, בשפה עסקית, עם דוגמה לכל תוצאה והחלטות פתוחות עם המלצה מסומנת. פותחים בטלפון, מסמנים, לוחצים "שלח תשובות", ומדביקים לסוכן. רק אז הוא כותב קוד.

  • אביעד גינדי

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

מה יהיה לכם בסוף השעה

  • סקיל אחד (קובץ הוראות) לסוכן, שאומר לו לכתוב תוכנית לפני קוד, ולעצור עד שתענו.
  • תבנית HTML אחת, קובץ יחיד בלי ספריות חיצוניות, שעובדת בעברית ובאנגלית ונראית טוב במסך של טלפון.
  • עמוד תוכנית ראשון למשימה אמיתית שלכם, עם:
    • סיכום של שניים-שלושה משפטים, בלי ז'רגון;
    • תוצאות כמשפטים שאפשר לבדוק, ולכל אחת דוגמה או סקיצה של המסך;
    • 3 עד 5 החלטות פתוחות, לכל אחת 2 או 3 אפשרויות והמלצה מסומנת;
    • סיכונים, ומה בכוונה לא נעשה;
    • כפתור "שלח תשובות" שמעתיק בלוק תשובות מוכן להדבקה.
  • הרגל של 2 דקות: קוראים, מסמנים, שולחים. "דלג" אומר לסוכן לעבוד לפי כל ההמלצות.

מה צריך

  • סוכן קוד אחד: Claude Code, Cursor או Codex, ופרויקט שהוא עובד בו.
  • טלפון עם דפדפן.
  • דרך להעביר קובץ לטלפון: מייל לעצמכם, וואטסאפ או טלגרם לעצמכם, או תיקייה בענן.

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


הערכה, צעד אחרי צעד (60 דקות)

צעד 1 (5 דקות): שמים את התבנית בפרויקט

מה עושים: בתיקיית הפרויקט יוצרים תיקייה plans, ובתוכה קובץ template.html עם התבנית מהנספח בסוף הערכה.

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

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

צעד 2 (10 דקות): מתקינים את הסקיל

מה עושים: מעתיקים את קובץ ההוראות (בנספח, באנגלית, כי ככה הסוכנים קוראים אותו הכי עקבי. התוכנית עצמה תצא בשפה שבה אתם כותבים לו).

איך, לפי הסוכן:

  • Claude Code: יוצרים בפרויקט את הקובץ .claude/skills/plan-page/SKILL.md ומדביקים לתוכו את ההוראות כמו שהן. השורות העליונות (name, description) הן מה שגורם ל-Claude Code להשתמש בסקיל לבד.
  • Cursor: יוצרים את הקובץ .cursor/rules/plan-page.mdc, מדביקים את ההוראות, ומשנים את השורות העליונות כך שיהיה בהן description ו-alwaysApply: false.
  • Codex: מוסיפים את ההוראות (בלי השורות העליונות) לקובץ AGENTS.md בתיקייה הראשית של הפרויקט.

צעד 3 (5 דקות): משימה ראשונה

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

1. הבקשה הראשונה לסוכן
לפני שאתה כותב קוד, תכין תוכנית לפי הסקיל plan-page.
המשימה: [תיאור במשפט או שניים, כמו שהייתם מסבירים לעובד חדש].
מי משתמש בזה: [לקוחות / צוות / אני].
מה חשוב לי: [למשל: לא להוסיף עלות חודשית, שיעבוד גם בטלפון].
כתוב את התוכנית בעברית, ועצור עד שאחזיר תשובות.

הסוכן קורא את מה שהוא צריך בפרויקט, כותב קובץ כמו plans/2026-10-04-class-reminders.html, ואומר לכם איפה הוא.

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

צעד 4 (5 דקות): מעבירים לטלפון

מה עושים: שולחים את הקובץ לעצמכם ופותחים אותו בדפדפן של הטלפון.

איך: מייל לעצמכם עם הקובץ מצורף, הודעה לעצמכם בוואטסאפ או בטלגרם, או תיקייה בענן. חלק מהאפליקציות מציגות קובץ HTML בתצוגה מקדימה שלא מריצה כפתורים. אם הכפתורים לא מגיבים, בחרו "פתח בדפדפן" (Chrome או Safari).

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

צעד 5 (2 דקות): עונים

מה עושים: עוברים על הדף מלמעלה למטה.

איך:

  1. הסיכום: האם זה מה שביקשתם? אם לא, עדיף לכתוב לסוכן במילים שלכם ולבקש תוכנית חדשה.
  2. כל תוצאה: "מאשר", או "לשנות" וכותבים מה. תוצאה שלא סימנתם נחשבת מאושרת.
  3. כל החלטה: ההמלצה כבר מסומנת. משנים רק אם אתם לא מסכימים.
  4. "שלח תשובות": הבלוק מועתק. מדביקים אותו בשיחה עם הסוכן.

מסכימים עם הכל? כותבים לסוכן מילה אחת: דלג. הוא יעבוד לפי התוכנית ולפי כל ההמלצות.

צעד 6 (15 דקות): הסוכן בונה ומדווח לפי התוצאות

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

2. דוח סיום לפי התוכנית
סיימת? תן דוח לפי התוכנית: לכל תוצאה (O1, O2...) כתוב אם היא בוצעה, ואיך בדקת (בדיקה אוטומטית, צילום מסך או פקודה שהרצת).
אחר כך רשימה של כל מה שעשית אחרת מהתוכנית, ולמה. אם יש תוצאה שלא בוצעה, אל תסמן אותה כבוצעה.

צעד 7 (10 דקות): מתאימים את הסקיל אליכם

מה עושים: אחרי תוכנית או שתיים, מוסיפים לסקיל כללים משלכם. כל כלל בשורה אחת, בסוף סעיף 2.

דוגמאות לכללים שכדאי להוסיף:

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

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

3. פרומפט חד-פעמי, בלי להתקין סקיל
לפני שאתה כותב קוד, כתוב תוכנית כעמוד HTML. הנה התבנית: [הדביקו את התבנית, או כתבו: plans/template.html].
החלף רק את בלוק ה-JSON שבתוך <script id="plan">. בתוכו: lang ("he"), title, summary (2-3 משפטים בשפה עסקית), outcomes (2-6 משפטים שאפשר לבדוק, לכל אחד example מסוג mockup, text או code), decisions (3-5, לכל אחת 2-3 אפשרויות ו-recommended), risks, out_of_scope.
אל תשאל מה שאפשר להחליט מהקוד. שמור את הקובץ ב-plans/, תגיד לי איפה, ועצור עד שאחזיר בלוק תשובות או "דלג".

דוגמה מלאה: סטודיו גל לפילאטיס (עסק בדוי)

כל מה שבדוגמה בדוי: הסטודיו, הלקוחות, המדריכות והשיעורים.

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

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

התוצאות, ומה מופיע מתחת לכל אחת:

התוצאה הדוגמה בעמוד
O1 כל לקוח שרשום לשיעור מקבל תזכורת אחת לפני השיעור, עם שם השיעור, השעה והמדריכה. סקיצה של ההודעה: "היי מאיה, תזכורת: מחר 18:00 פילאטיס מכשירים עם רוני. לא מגיעה? לביטול"
O2 לקוח שלוחץ על קישור הביטול מבטל את המקום שלו בלחיצה אחת, בלי להתחבר. סקיצה של מסך הביטול עם כפתור "כן, לבטל"
O3 כשמקום מתפנה, הראשון ברשימת ההמתנה מקבל הודעה, ויש לו שעתיים לאשר לפני שהמקום עובר לבא בתור. נוסח ההודעה לרשימת ההמתנה
O4 בעלת הסטודיו רואה ביומן מי קיבל תזכורת, מי ביטל ומי נכנס מרשימת ההמתנה. שורה לדוגמה מיומן השיעור

ההחלטות הפתוחות:

השאלה האפשרויות ההמלצה
D1 באיזה ערוץ לשלוח? מייל (חינם, נקרא פחות) / SMS (נקרא כמעט תמיד, עלות לכל הודעה) / מייל, ו-SMS רק למי שבחר מייל, ו-SMS רק למי שבחר
D2 מתי לשלוח? 24 שעות לפני (יש זמן למלא מקום) / 3 שעות לפני (טרי, מעט זמן למלא) 24 שעות לפני
D3 מה קורה כשמישהו מבטל? הודעה לראשון ברשימה, שעתיים לאשר / הודעה לכל הרשימה, הראשון שמאשר מקבל הודעה לראשון ברשימה

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

מחוץ לתוכנית: דמי ביטול או חיוב על אי הגעה. שינוי מערכת ההרשמה או לוח השיעורים. תזכורות בוואטסאפ.

מה הבעלים ענתה, מהטלפון, בפחות משתי דקות: אישרה את O1, ביקשה שינוי ב-O2, שינתה את D2 ל-3 שעות לפני, ולחצה "שלח תשובות". זה הבלוק שהודבק לסוכן:

להעתקה
תשובות לתוכנית: תזכורת ללקוחות לפני שיעור
O1: מאשר
O2: לשנות: גם הודעה לבעלת הסטודיו כשמישהו מבטל
O3: לא סומן
O4: לא סומן
D1: מייל, ו-SMS רק למי שבחר בזה (ההמלצה)
D2: 3 שעות לפני
D3: הודעה לראשון ברשימה, שעתיים לאשר (ההמלצה)

מה הסוכן עשה עם זה: הוסיף ל-O2 הודעה לבעלת הסטודיו על כל ביטול. בגלל D2 (3 שעות לפני) וזמן אישור של שעתיים ב-D3, הוא כתב שמקום שמתפנה מהתזכורת יישאר לרשימת ההמתנה רק שעה לפני השיעור, ושאל שאלה אחת: לקצר את זמן האישור לשעה? זו בדיוק השאלה שעדיף לשאול לפני הקוד.


צ'קליסט

  • plans/template.html בפרויקט, ונפתח בדפדפן במחשב
  • הסקיל מותקן בסוכן שלכם (Claude Code, Cursor או Codex)
  • תוכנית ראשונה למשימה אמיתית נכתבה ב-plans/
  • הקובץ נפתח בדפדפן של הטלפון, והכפתורים מגיבים
  • התשובות הודבקו לסוכן (או "דלג")
  • הסוכן נתן דוח סיום לפי O1, O2...
  • לפחות כלל אחד שלכם נוסף לסקיל

נספח א: הסקיל (SKILL.md)

SKILL.md: להעתקה מלאה
---
name: plan-page
description: Before writing code for a feature or any change bigger than a small fix, write the plan as one self-contained HTML page that the owner approves from a phone, then stop and wait for the answers. Use when asked to build a feature, change several files, or "plan first".
---

# Plan page

The owner will not read a long plan. They open one page on a phone, check the statements, pick options, and paste the answers back to you. Write that page, then stop.

## 1. Write the page (before any code)
- Read only what you need to understand the task.
- Copy `plans/template.html` to `plans/<yyyy-mm-dd>-<short-name>.html`.
- Replace only the JSON inside `<script type="application/json" id="plan">`. Do not change anything else in the file.
- Never write `</script` anywhere inside the JSON.

## 2. What goes in the JSON
- `lang`: `"he"` or `"en"`, the language the owner writes to you in. Write every text field in that language.
- `title`: up to 8 words.
- `summary`: 2-3 sentences in plain business language. What changes for the people who use it, and what stays the same. No file names, no jargon.
- `outcomes` (2-6): what the owner will have at the end. Each `statement` is one checkable sentence a non-developer can disagree with. Good: "A client gets one reminder 24 hours before class." Bad: "Reminder improvements." Ids `O1`, `O2`, ...
- Each outcome has an `example`:
  - `{"kind": "mockup", "caption": "...", "html": "..."}`: a small phone-width HTML fragment with inline styles. No scripts, images, links or `url()`; the page removes them anyway.
  - `{"kind": "text", "caption": "...", "text": "..."}`: the real message or text people will see.
  - `{"kind": "code", "caption": "...", "code": "..."}`: only when the outcome is technical.
- `decisions` (3-5): only choices that change what gets built and that you cannot decide from the code or the request. Each has a `question`, an optional one-line `why`, 2-3 `options` (`label` + one-line `detail` with the cost or effect), and `recommended` (the index of your recommendation). Put choices about money, privacy or data first. Ids `D1`, `D2`, ...
- `risks`: 1-4 lines. `out_of_scope`: what you deliberately will not do.
- Plain short sentences. One idea per sentence. No superlatives.

## 3. Check and hand over
- Check that the JSON parses, ids are unique, and every `recommended` is a valid index.
- Tell the owner the file path and one line: "Open it on your phone, answer, press Send answers, and paste the block here. Or reply skip to follow all recommendations."
- Stop. Do not write code until the answers arrive.

## 4. Read the answers
The answers arrive as a block that starts with "Answers to the plan:" or "תשובות לתוכנית:".
- `approve` / `מאשר`: build that outcome as written.
- `change: ...` / `לשנות: ...`: adjust that outcome. If the change is large, write an updated plan page and wait again.
- `not marked` / `לא סומן`: treat as approved.
- A decision line names the chosen option. "(recommended)" / "(ההמלצה)" means the owner kept your recommendation.
- `skip` / `דלג` on its own: approve everything and follow every recommendation.
- The answers override the plan.

## 5. When you finish
Report per outcome: the id, done or not, and how you checked it (a test, a screenshot, a command). List anything you changed from the plan and why.

נספח ב: התבנית (plans/template.html)

template.html: להעתקה מלאה
<!doctype html>
<html lang="he" dir="rtl">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>תוכנית לאישור</title>
<style>
:root{--ink:#16202e;--muted:#5a6475;--brand:#0f4c81;--accent:#e8743b;--ok:#1f7a4d;--soft:#f3f6fa;--line:#dfe5ee;--card:#fff;--bg:#f7f8fb}
*{box-sizing:border-box}
html{-webkit-text-size-adjust:100%}
body{margin:0;background:var(--bg);color:var(--ink);font:16px/1.6 system-ui,-apple-system,"Segoe UI",Arial,sans-serif}
main{max-width:720px;margin:0 auto;padding:16px 16px 120px}
header.top{background:var(--brand);color:#fff;border-radius:14px;padding:18px 18px 16px;margin-bottom:16px}
header.top small{opacity:.8;font-weight:600}
h1{font-size:22px;line-height:1.3;margin:4px 0 8px}
header.top p{margin:0;color:#dbe7f5}
h2{font-size:17px;margin:24px 0 10px;color:var(--brand)}
.card{background:var(--card);border:1px solid var(--line);border-radius:12px;padding:14px;margin-bottom:12px;overflow-wrap:anywhere}
.stmt{font-weight:600;margin:0 0 10px}
.id{display:inline-block;font-size:12px;font-weight:700;color:var(--brand);background:var(--soft);border-radius:6px;padding:0 6px;margin-inline-end:6px}
.ex{border:1px dashed var(--line);border-radius:10px;padding:10px;background:var(--soft);margin:0 0 10px;overflow-x:auto}
.ex .cap{display:block;font-size:12px;color:var(--muted);margin-bottom:6px}
.ex pre{margin:0;white-space:pre-wrap;direction:ltr;text-align:left;font:13px/1.5 ui-monospace,Consolas,monospace}
.seg{display:flex;gap:8px;flex-wrap:wrap}
.seg button,.opt{font:inherit;font-size:15px;border:1px solid var(--line);background:#fff;color:var(--ink);border-radius:10px;padding:9px 12px;cursor:pointer;min-height:44px}
.seg button[aria-pressed=true].ok{background:var(--ok);border-color:var(--ok);color:#fff}
.seg button[aria-pressed=true].chg{background:var(--accent);border-color:var(--accent);color:#fff}
textarea{width:100%;font:inherit;font-size:16px;border:1px solid var(--line);border-radius:10px;padding:8px 10px;margin-top:8px;min-height:64px}
textarea[hidden]{display:none}
.why{color:var(--muted);font-size:14px;margin:-4px 0 10px}
.opts{display:grid;gap:8px}
.opt{display:block;width:100%;text-align:start}
.opt[aria-pressed=true]{border-color:var(--brand);box-shadow:inset 0 0 0 2px var(--brand)}
.opt b{display:block}
.opt span{display:block;color:var(--muted);font-size:14px}
.rec,.opt span.rec{display:inline-block;font-size:12px;font-weight:700;color:#fff;background:var(--ok);border-radius:6px;padding:0 6px;margin-inline-start:6px}
ul.plain{margin:0;padding-inline-start:20px}
.bar{position:fixed;inset-inline:0;bottom:0;background:rgba(255,255,255,.96);border-top:1px solid var(--line);padding:10px 16px calc(10px + env(safe-area-inset-bottom))}
.bar .in{max-width:720px;margin:0 auto;display:flex;gap:10px;align-items:center}
.bar small{color:var(--muted);flex:1;line-height:1.3}
#send{font:inherit;font-weight:700;font-size:16px;background:var(--accent);color:#fff;border:0;border-radius:12px;padding:12px 18px;min-height:48px;cursor:pointer}
#out{display:none}
#out.show{display:block}
#out textarea{direction:auto;unicode-bidi:plaintext;min-height:180px;font-size:14px}
.status{font-weight:700;color:var(--ok);margin:6px 0 0}
</style>
</head>
<body>
<main id="app"></main>
<div class="bar"><div class="in"><small id="hint"></small><button id="send" type="button"></button></div></div>

<!-- The agent replaces only the JSON below. Everything else stays as is. -->
<script type="application/json" id="plan">
{
  "lang": "he",
  "title": "כותרת קצרה של המשימה",
  "summary": "שניים-שלושה משפטים בשפה עסקית: מה ישתנה אחרי העבודה, ומה לא ישתנה.",
  "outcomes": [
    {
      "id": "O1",
      "statement": "משפט שאפשר לבדוק ולא להסכים איתו, למשל: הלקוח מקבל תזכורת יום לפני השיעור.",
      "example": { "kind": "mockup", "caption": "איך זה ייראה", "html": "<div style=\"border:1px solid #ccc;border-radius:8px;padding:8px\">דוגמה</div>" }
    }
  ],
  "decisions": [
    {
      "id": "D1",
      "question": "שאלה שמשנה את מה שנבנה",
      "why": "שורה אחת: למה זה חשוב",
      "options": [
        { "label": "אפשרות א", "detail": "מה זה עולה ומה זה נותן" },
        { "label": "אפשרות ב", "detail": "מה זה עולה ומה זה נותן" }
      ],
      "recommended": 0
    }
  ],
  "risks": ["סיכון אחד בשורה"],
  "out_of_scope": ["מה לא נעשה בכוונה"]
}
</script>

<script>
(function () {
  var UI = {
    he: { dir: 'rtl', kicker: 'תוכנית לאישור לפני כתיבת קוד', outcomes: 'מה יהיה בסוף', decisions: 'החלטות פתוחות', risks: 'סיכונים', scope: 'מחוץ לתוכנית', ok: 'מאשר', chg: 'לשנות', note: 'מה לשנות?', rec: 'המלצה', send: 'שלח תשובות',
      hint: 'החלטה שלא בחרתם תישלח כהמלצה. רוצים לאשר הכל כמו שהוא? כתבו לסוכן: דלג', head: 'תשובות לתוכנית', approve: 'מאשר', change: 'לשנות', noAnswer: 'לא סומן', recTag: '(ההמלצה)', general: 'הערות כלליות', copied: 'הועתק. הדביקו את זה בשיחה עם הסוכן.', manual: 'סמנו את הטקסט, העתיקו והדביקו בשיחה עם הסוכן.', notes: 'הערות כלליות (לא חובה)' },
    en: { dir: 'ltr', kicker: 'Plan to approve before any code', outcomes: 'What you will have', decisions: 'Open decisions', risks: 'Risks', scope: 'Out of scope', ok: 'Approve', chg: 'Change', note: 'What should change?', rec: 'Recommended', send: 'Send answers',
      hint: 'A decision you skip is sent as the recommendation. Approve everything as is? Tell the agent: skip', head: 'Answers to the plan', approve: 'approve', change: 'change', noAnswer: 'not marked', recTag: '(recommended)', general: 'General notes', copied: 'Copied. Paste it into the chat with the agent.', manual: 'Select the text, copy it and paste it into the chat with the agent.', notes: 'General notes (optional)' }
  };
  var plan = JSON.parse(document.getElementById('plan').textContent);
  var t = UI[plan.lang] || UI.he;
  document.documentElement.lang = plan.lang === 'en' ? 'en' : 'he';
  document.documentElement.dir = t.dir;
  document.title = plan.title;
  var state = { outcomes: {}, notes: {}, decisions: {} };

  function el(tag, attrs, kids) {
    var e = document.createElement(tag);
    Object.keys(attrs || {}).forEach(function (k) { if (k === 'text') e.textContent = attrs[k]; else e.setAttribute(k, attrs[k]); });
    (kids || []).forEach(function (c) { if (c) e.appendChild(c); });
    return e;
  }
  // Mockups are the agent's own HTML: keep the markup, drop anything that can run or load
  function safeHtml(html) {
    var doc = new DOMParser().parseFromString('<div>' + html + '</div>', 'text/html');
    doc.querySelectorAll('script,iframe,object,embed,link,meta,base,form,img,video,audio').forEach(function (n) { n.remove(); });
    doc.querySelectorAll('*').forEach(function (n) {
      [].slice.call(n.attributes).forEach(function (a) {
        if (/^on/i.test(a.name) || /^(href|src|action|xlink:href)$/i.test(a.name) || /url\(/i.test(a.value)) n.removeAttribute(a.name);
      });
    });
    return doc.body.firstChild;
  }
  function example(ex) {
    if (!ex || ex.kind === 'none') return null;
    var box = el('div', { class: 'ex' }, [ex.caption ? el('span', { class: 'cap', text: ex.caption }) : null]);
    if (ex.kind === 'mockup') box.appendChild(safeHtml(ex.html || ''));
    else if (ex.kind === 'code') box.appendChild(el('pre', { text: ex.code || '' }));
    else box.appendChild(el('div', { text: ex.text || '' }));
    return box;
  }
  function list(title, items) {
    if (!items || !items.length) return [];
    return [el('h2', { text: title }), el('div', { class: 'card' }, [el('ul', { class: 'plain' }, items.map(function (x) { return el('li', { text: x }); }))])];
  }

  var app = document.getElementById('app');
  app.appendChild(el('header', { class: 'top' }, [el('small', { text: t.kicker }), el('h1', { text: plan.title }), el('p', { text: plan.summary })]));

  app.appendChild(el('h2', { text: t.outcomes }));
  plan.outcomes.forEach(function (o) {
    var note = el('textarea', { 'aria-label': t.note + ' ' + o.id, placeholder: t.note, hidden: '' });
    note.addEventListener('input', function () { state.notes[o.id] = note.value; });
    var ok = el('button', { type: 'button', class: 'ok', 'aria-pressed': 'false', text: t.ok });
    var chg = el('button', { type: 'button', class: 'chg', 'aria-pressed': 'false', text: t.chg });
    function pick(v) {
      state.outcomes[o.id] = v;
      ok.setAttribute('aria-pressed', String(v === 'ok'));
      chg.setAttribute('aria-pressed', String(v === 'chg'));
      if (v === 'chg') { note.removeAttribute('hidden'); note.focus(); } else note.setAttribute('hidden', '');
    }
    ok.addEventListener('click', function () { pick('ok'); });
    chg.addEventListener('click', function () { pick('chg'); });
    app.appendChild(el('section', { class: 'card', id: o.id }, [
      el('p', { class: 'stmt' }, [el('span', { class: 'id', text: o.id }), document.createTextNode(o.statement)]),
      example(o.example),
      el('div', { class: 'seg' }, [ok, chg]),
      note,
    ]));
  });

  if (plan.decisions && plan.decisions.length) app.appendChild(el('h2', { text: t.decisions }));
  (plan.decisions || []).forEach(function (d) {
    var btns = d.options.map(function (op, i) {
      var b = el('button', { type: 'button', class: 'opt', 'aria-pressed': String(i === d.recommended) }, [
        el('b', { text: op.label }, [i === d.recommended ? el('span', { class: 'rec', text: t.rec }) : null]),
        op.detail ? el('span', { text: op.detail }) : null,
      ]);
      b.addEventListener('click', function () {
        state.decisions[d.id] = i;
        btns.forEach(function (x, j) { x.setAttribute('aria-pressed', String(j === i)); });
      });
      return b;
    });
    app.appendChild(el('section', { class: 'card', id: d.id }, [
      el('p', { class: 'stmt' }, [el('span', { class: 'id', text: d.id }), document.createTextNode(d.question)]),
      d.why ? el('p', { class: 'why', text: d.why }) : null,
      el('div', { class: 'opts' }, btns),
    ]));
  });

  list(t.risks, plan.risks).forEach(function (n) { app.appendChild(n); });
  list(t.scope, plan.out_of_scope).forEach(function (n) { app.appendChild(n); });

  var general = el('textarea', { 'aria-label': t.notes, placeholder: t.notes });
  app.appendChild(el('div', { class: 'card' }, [general]));
  var outText = el('textarea', { 'aria-label': t.send, readonly: '' });
  var status = el('p', { class: 'status', role: 'status' });
  var out = el('div', { class: 'card', id: 'out' }, [outText, status]);
  app.appendChild(out);

  function answers() {
    var lines = [t.head + ': ' + plan.title];
    plan.outcomes.forEach(function (o) {
      var v = state.outcomes[o.id];
      var n = (state.notes[o.id] || '').trim();
      lines.push(o.id + ': ' + (v === 'ok' ? t.approve : v === 'chg' ? t.change + (n ? ': ' + n.replace(/\s*\n\s*/g, ' ') : '') : t.noAnswer));
    });
    (plan.decisions || []).forEach(function (d) {
      var i = d.id in state.decisions ? state.decisions[d.id] : d.recommended;
      lines.push(d.id + ': ' + d.options[i].label + (i === d.recommended ? ' ' + t.recTag : ''));
    });
    var g = general.value.trim();
    if (g) lines.push(t.general + ': ' + g.replace(/\s*\n\s*/g, ' '));
    return lines.join('\n');
  }

  document.getElementById('hint').textContent = t.hint;
  var send = document.getElementById('send');
  send.textContent = t.send;
  send.addEventListener('click', function () {
    var text = answers();
    outText.value = text;
    out.classList.add('show');
    function manual() { outText.focus(); outText.select(); status.textContent = t.manual; }
    function done() { status.textContent = t.copied; }
    if (navigator.clipboard && window.isSecureContext) navigator.clipboard.writeText(text).then(done, function () {
      outText.select(); try { document.execCommand('copy') ? done() : manual(); } catch (e) { manual(); }
    });
    else { outText.select(); try { document.execCommand('copy') ? done() : manual(); } catch (e) { manual(); } }
    out.scrollIntoView({ block: 'center' });
  });
  window.planAnswers = answers; // handy for tests
})();
</script>
</body>
</html>

הבהרה

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


ומה הלאה

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

רוצים שזה ירוץ אצלכם אוטומטית? aviad@gindiinnovation.com · gindiinnovation.com