> ## Documentation Index
> Fetch the complete documentation index at: https://docs.bepurple.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Plan & Build

> p0's pipeline for turning an idea into a product spec and a product spec into a pull request.

export const Mock = props => {
  const {kind, ...rest} = props;
  const iconBase = {
    xmlns: "http://www.w3.org/2000/svg",
    viewBox: "0 0 24 24",
    fill: "none",
    stroke: "currentColor",
    strokeWidth: "2",
    strokeLinecap: "round",
    strokeLinejoin: "round",
    width: "14",
    height: "14",
    "aria-hidden": "true"
  };
  const renderShapes = shapes => shapes.map((shape, idx) => {
    if (shape.tag === "circle") return <circle key={idx} {...shape.attrs} />;
    if (shape.tag === "rect") return <rect key={idx} {...shape.attrs} />;
    if (shape.tag === "line") return <line key={idx} {...shape.attrs} />;
    if (shape.tag === "polyline") return <polyline key={idx} {...shape.attrs} />;
    if (shape.tag === "polygon") return <polygon key={idx} {...shape.attrs} />;
    return <path key={idx} {...shape.attrs} />;
  });
  if (kind === "appShell") {
    const {sidebar, children} = rest;
    return <div className="p0-app-shell" role="img" aria-label="p0 application window mock">
        <div className="p0-app-shell__titlebar" aria-hidden="true">
          <span className="p0-app-shell__dot p0-app-shell__dot--close" />
          <span className="p0-app-shell__dot p0-app-shell__dot--minimize" />
          <span className="p0-app-shell__dot p0-app-shell__dot--maximize" />
        </div>
        <div className="p0-app-shell__body">
          {sidebar ? <aside className="p0-app-shell__sidebar">{sidebar}</aside> : null}
          <main className="p0-app-shell__main">{children}</main>
        </div>
      </div>;
  }
  if (kind === "sidebar") {
    const {sessions = [], repos = []} = rest;
    return <div className="p0-sidebar" role="navigation" aria-label="p0 sidebar mock">
        <section className="p0-sidebar__section">
          <header className="p0-sidebar__section-header">
            <span className="p0-sidebar__section-title">Sessions</span>
            <button type="button" className="p0-sidebar__section-action" aria-label="New session" tabIndex={-1}>
              <svg {...iconBase}>
                <path d="M5 12h14" />
                <path d="M12 5v14" />
              </svg>
            </button>
          </header>
          <ul className="p0-sidebar__list">
            {sessions.map(s => <li key={s.id} className={s.active ? "p0-sidebar__item p0-sidebar__item--active" : "p0-sidebar__item"}>
                <svg {...iconBase} className="p0-sidebar__item-icon">
                  <path d="M22 17a2 2 0 0 1-2 2H6.828a2 2 0 0 0-1.414.586l-2.202 2.202A.71.71 0 0 1 2 21.286V5a2 2 0 0 1 2-2h16a2 2 0 0 1 2 2z" />
                </svg>
                <span className="p0-sidebar__item-label">{s.title}</span>
              </li>)}
          </ul>
        </section>

        <section className="p0-sidebar__section">
          <header className="p0-sidebar__section-header">
            <span className="p0-sidebar__section-title">Repositories</span>
          </header>
          <ul className="p0-sidebar__list">
            {repos.map(r => <li key={r.name} className="p0-sidebar__item">
                <svg {...iconBase} className="p0-sidebar__item-icon">
                  <path d="M18 19a5 5 0 0 1-5-5v8" />
                  <path d="M9 20H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h3.9a2 2 0 0 1 1.69.9l.81 1.2a2 2 0 0 0 1.67.9H20a2 2 0 0 1 2 2v5" />
                  <circle cx="13" cy="12" r="2" />
                  <circle cx="20" cy="19" r="2" />
                </svg>
                <span className="p0-sidebar__item-label">{r.name}</span>
              </li>)}
          </ul>
        </section>
      </div>;
  }
  if (kind === "chatInput") {
    const {placeholder = "Message p0…", slashOpen = false} = rest;
    const SLASH_PREVIEW = [{
      command: "/plan",
      description: "Switch to Plan mode"
    }, {
      command: "/build-from-spec",
      description: "End-to-end spec → PR pipeline"
    }, {
      command: "/refine",
      description: "Iterate on a built feature"
    }];
    const ciIconBase = {
      ...iconBase,
      width: "16",
      height: "16"
    };
    const slashIconBase = {
      ...ciIconBase,
      width: "12",
      height: "12"
    };
    return <div className="p0-chat-input-wrapper">
        {slashOpen ? <div className="p0-chat-input__dropdown-slot">
            <div className="p0-slash-dropdown" role="listbox" aria-label="Slash command suggestions">
              <div className="p0-slash-dropdown__header">
                <svg {...slashIconBase}>
                  <path d="M22 2 2 22" />
                </svg>
                <span>Commands matching /</span>
              </div>
              <ul className="p0-slash-dropdown__list">
                {SLASH_PREVIEW.map((c, i) => <li key={c.command} className={i === 0 ? "p0-slash-dropdown__item p0-slash-dropdown__item--active" : "p0-slash-dropdown__item"} role="option" aria-selected={i === 0}>
                    <code className="p0-slash-dropdown__command">{c.command}</code>
                    <span className="p0-slash-dropdown__description">{c.description}</span>
                  </li>)}
              </ul>
            </div>
          </div> : null}
        <div className="p0-chat-input" role="group" aria-label="p0 chat input mock">
          <button type="button" className="p0-chat-input__icon-btn" aria-label="Attach file" tabIndex={-1}>
            <svg {...ciIconBase}>
              <path d="m16 6-8.414 8.586a2 2 0 0 0 2.829 2.829l8.414-8.586a4 4 0 1 0-5.657-5.657l-8.379 8.551a6 6 0 1 0 8.485 8.485l8.379-8.551" />
            </svg>
          </button>
          <span className="p0-chat-input__field" aria-hidden="true">
            {placeholder}
          </span>
          <button type="button" className="p0-chat-input__send" aria-label="Send message" tabIndex={-1}>
            <svg {...ciIconBase}>
              <path d="m5 12 7-7 7 7" />
              <path d="M12 19V5" />
            </svg>
          </button>
        </div>
      </div>;
  }
  if (kind === "slashDropdown") {
    const {query = "/"} = rest;
    const SLASH_COMMANDS = [{
      command: "/agent",
      description: "Switch to Agent mode (applies changes directly)"
    }, {
      command: "/clear",
      description: "Start a new conversation (creates new sub-chat)"
    }, {
      command: "/compact",
      description: "Compact conversation context to reduce token usage"
    }, {
      command: "/config",
      description: "View and modify configuration settings"
    }, {
      command: "/cost",
      description: "Show token usage and cost for the session"
    }, {
      command: "/doctor",
      description: "Check the health of your p0 installation"
    }, {
      command: "/help",
      description: "Show available commands and help"
    }, {
      command: "/mcp",
      description: "View and manage MCP server connections"
    }, {
      command: "/memory",
      description: "View and edit CLAUDE.md memory files"
    }, {
      command: "/model",
      description: "Switch AI model"
    }, {
      command: "/permissions",
      description: "View and manage tool permissions"
    }, {
      command: "/plan",
      description: "Switch to Plan mode (creates plan before making changes)"
    }, {
      command: "/pr-comments",
      description: "Ask the agent to generate PR review comments"
    }, {
      command: "/release-notes",
      description: "Ask the agent to generate release notes"
    }, {
      command: "/review",
      description: "Ask the agent to review your code"
    }, {
      command: "/security-review",
      description: "Ask the agent to perform a security audit"
    }, {
      command: "/skills",
      description: "Manage your skills"
    }, {
      command: "/status",
      description: "Show current session status and context"
    }, {
      command: "/build-from-spec",
      description: "Run the end-to-end spec → build → QA pipeline"
    }, {
      command: "/create-standards",
      description: "Generate engineering standards for your workspace"
    }, {
      command: "/refine",
      description: "Make incremental changes to a recently built feature"
    }, {
      command: "/import-spec",
      description: "Import a product spec and ask clarifying questions"
    }];
    const sdIconBase = {
      ...iconBase,
      width: "12",
      height: "12"
    };
    const needle = (query || "/").toLowerCase();
    const matches = SLASH_COMMANDS.filter(c => c.command.toLowerCase().startsWith(needle));
    return <div className="p0-slash-dropdown" role="listbox" aria-label="Slash command suggestions">
        <div className="p0-slash-dropdown__header">
          <svg {...sdIconBase}>
            <path d="M22 2 2 22" />
          </svg>
          <span>Commands matching {query}</span>
        </div>
        {matches.length === 0 ? <div className="p0-slash-dropdown__empty">No commands match.</div> : <ul className="p0-slash-dropdown__list">
            {matches.map((c, i) => <li key={c.command} className={i === 0 ? "p0-slash-dropdown__item p0-slash-dropdown__item--active" : "p0-slash-dropdown__item"} role="option" aria-selected={i === 0}>
                <code className="p0-slash-dropdown__command">{c.command}</code>
                <span className="p0-slash-dropdown__description">{c.description}</span>
              </li>)}
          </ul>}
      </div>;
  }
  if (kind === "ticketTree") {
    const {phases = []} = rest;
    const STATUS_META = {
      todo: {
        label: "Todo",
        modifier: "todo",
        shapes: [{
          tag: "circle",
          attrs: {
            cx: "12",
            cy: "12",
            r: "10"
          }
        }]
      },
      in_progress: {
        label: "In progress",
        modifier: "in-progress",
        shapes: [{
          tag: "path",
          attrs: {
            d: "M21 12a9 9 0 1 1-6.219-8.56"
          }
        }]
      },
      completed: {
        label: "Completed",
        modifier: "completed",
        shapes: [{
          tag: "circle",
          attrs: {
            cx: "12",
            cy: "12",
            r: "10"
          }
        }, {
          tag: "path",
          attrs: {
            d: "m9 12 2 2 4-4"
          }
        }]
      },
      failed: {
        label: "Failed",
        modifier: "failed",
        shapes: [{
          tag: "circle",
          attrs: {
            cx: "12",
            cy: "12",
            r: "10"
          }
        }, {
          tag: "path",
          attrs: {
            d: "m15 9-6 6"
          }
        }, {
          tag: "path",
          attrs: {
            d: "m9 9 6 6"
          }
        }]
      }
    };
    return <div className="p0-ticket-tree" role="tree" aria-label="Build progress">
        <ul className="p0-ticket-tree__phases">
          {phases.map(phase => <li key={phase.name} className="p0-ticket-tree__phase">
              <div className="p0-ticket-tree__phase-name">{phase.name}</div>
              <ul className="p0-ticket-tree__tickets">
                {phase.tickets.map(ticket => {
      const meta = STATUS_META[ticket.status] ?? STATUS_META.todo;
      return <li key={ticket.id} className="p0-ticket-tree__ticket" data-status={ticket.status}>
                      <span className={`p0-ticket-tree__dot p0-ticket-tree__dot--${meta.modifier}`} role="img" aria-label={meta.label}>
                        <svg {...iconBase} aria-hidden="true">
                          {renderShapes(meta.shapes)}
                        </svg>
                      </span>
                      <code className="p0-ticket-tree__ticket-id">{ticket.id}</code>
                      <span className="p0-ticket-tree__ticket-name">{ticket.name}</span>
                    </li>;
    })}
              </ul>
            </li>)}
        </ul>
      </div>;
  }
  if (kind === "diffView") {
    const {mode = "unified", file, before = "", after = ""} = rest;
    const computeUnifiedLines = (b, a) => {
      const beforeLines = b.split("\n");
      const afterLines = a.split("\n");
      const lines = [];
      const max = Math.max(beforeLines.length, afterLines.length);
      for (let i = 0; i < max; i++) {
        const bl = beforeLines[i];
        const al = afterLines[i];
        if (bl === al) {
          if (bl !== undefined) lines.push({
            kind: "context",
            text: bl
          });
        } else {
          if (bl !== undefined) lines.push({
            kind: "remove",
            text: bl
          });
          if (al !== undefined) lines.push({
            kind: "add",
            text: al
          });
        }
      }
      return lines;
    };
    const PREFIX = {
      add: "+",
      remove: "-",
      context: " "
    };
    const MODIFIER = {
      add: "add",
      remove: "remove",
      context: "context"
    };
    const computeSplitRows = (b, a) => {
      const beforeLines = b.split("\n");
      const afterLines = a.split("\n");
      const max = Math.max(beforeLines.length, afterLines.length);
      const rows = [];
      for (let i = 0; i < max; i++) {
        const bl = beforeLines[i];
        const al = afterLines[i];
        const sameRow = bl === al;
        rows.push({
          before: bl === undefined ? {
            kind: "empty",
            text: ""
          } : sameRow ? {
            kind: "context",
            text: bl
          } : {
            kind: "remove",
            text: bl
          },
          after: al === undefined ? {
            kind: "empty",
            text: ""
          } : sameRow ? {
            kind: "context",
            text: al
          } : {
            kind: "add",
            text: al
          }
        });
      }
      return rows;
    };
    const unifiedLines = mode === "unified" ? computeUnifiedLines(before, after) : [];
    const splitRows = mode === "split" ? computeSplitRows(before, after) : [];
    return <div className={`p0-diff-view p0-diff-view--${mode}`} role="figure" aria-label={`Diff for ${file}`}>
        <div className="p0-diff-view__header">
          <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" width="14" height="14" aria-hidden="true">
            <path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z" />
            <path d="M14 2v4a2 2 0 0 0 2 2h4" />
            <path d="M9 10h6" />
            <path d="M12 13v-6" />
            <path d="M9 17h6" />
          </svg>
          <code className="p0-diff-view__file">{file}</code>
        </div>

        {mode === "split" ? <div className="p0-diff-view__body p0-diff-view__body--split">
            <div className="p0-diff-view__column" aria-label="Before">
              {splitRows.map((row, i) => row.before.kind === "empty" ? <div key={`b-${i}`} className="p0-diff-view__line p0-diff-view__line--empty" aria-hidden="true">
                    <span className="p0-diff-view__text">{" "}</span>
                  </div> : <div key={`b-${i}`} className={`p0-diff-view__line p0-diff-view__line--${MODIFIER[row.before.kind]}`} data-kind={row.before.kind}>
                    <span className="p0-diff-view__prefix" aria-hidden="true">
                      {PREFIX[row.before.kind]}
                    </span>
                    <span className="p0-diff-view__text">{row.before.text || " "}</span>
                  </div>)}
            </div>
            <div className="p0-diff-view__column" aria-label="After">
              {splitRows.map((row, i) => row.after.kind === "empty" ? <div key={`a-${i}`} className="p0-diff-view__line p0-diff-view__line--empty" aria-hidden="true">
                    <span className="p0-diff-view__text">{" "}</span>
                  </div> : <div key={`a-${i}`} className={`p0-diff-view__line p0-diff-view__line--${MODIFIER[row.after.kind]}`} data-kind={row.after.kind}>
                    <span className="p0-diff-view__prefix" aria-hidden="true">
                      {PREFIX[row.after.kind]}
                    </span>
                    <span className="p0-diff-view__text">{row.after.text || " "}</span>
                  </div>)}
            </div>
          </div> : <div className="p0-diff-view__body">
            {unifiedLines.map((line, i) => <div key={i} className={`p0-diff-view__line p0-diff-view__line--${MODIFIER[line.kind]}`} data-kind={line.kind}>
                <span className="p0-diff-view__prefix" aria-hidden="true">
                  {PREFIX[line.kind]}
                </span>
                <span className="p0-diff-view__text">{line.text || " "}</span>
              </div>)}
          </div>}
      </div>;
  }
  if (kind === "terminalPane") {
    const {prompt = "$", command, output} = rest;
    return <div className="p0-terminal-pane" role="figure" aria-label="Terminal output">
        <div className="p0-terminal-pane__chrome" aria-hidden="true">
          <span className="p0-terminal-pane__dot p0-terminal-pane__dot--red" />
          <span className="p0-terminal-pane__dot p0-terminal-pane__dot--yellow" />
          <span className="p0-terminal-pane__dot p0-terminal-pane__dot--green" />
          <span className="p0-terminal-pane__title">
            <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" width="12" height="12" aria-hidden="true">
              <path d="m7 11 2-2-2-2" />
              <path d="M11 13h4" />
              <rect width="18" height="18" x="3" y="3" rx="2" ry="2" />
            </svg>
            {" "}terminal
          </span>
        </div>
        <pre className="p0-terminal-pane__body">
          <code>
            <span className="p0-terminal-pane__line">
              <span className="p0-terminal-pane__prompt">{prompt}</span>
              <span className="p0-terminal-pane__command">{command}</span>
            </span>
            {output ? <span className="p0-terminal-pane__output">{"\n" + output}</span> : null}
          </code>
        </pre>
      </div>;
  }
  if (kind === "devToolsPanel") {
    const {defaultTab = "terminal", terminal, changes, run} = rest;
    const SECTIONS = [{
      id: "terminal",
      label: "Terminal",
      shapes: [{
        tag: "path",
        attrs: {
          d: "m7 11 2-2-2-2"
        }
      }, {
        tag: "path",
        attrs: {
          d: "M11 13h4"
        }
      }, {
        tag: "rect",
        attrs: {
          width: "18",
          height: "18",
          x: "3",
          y: "3",
          rx: "2",
          ry: "2"
        }
      }]
    }, {
      id: "changes",
      label: "Changes",
      shapes: [{
        tag: "path",
        attrs: {
          d: "M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"
        }
      }, {
        tag: "path",
        attrs: {
          d: "M14 2v4a2 2 0 0 0 2 2h4"
        }
      }, {
        tag: "path",
        attrs: {
          d: "M9 10h6"
        }
      }, {
        tag: "path",
        attrs: {
          d: "M12 13v-6"
        }
      }, {
        tag: "path",
        attrs: {
          d: "M9 17h6"
        }
      }]
    }, {
      id: "run",
      label: "Run",
      shapes: [{
        tag: "polygon",
        attrs: {
          points: "6 3 20 12 6 21 6 3"
        }
      }]
    }];
    const panes = {
      terminal,
      changes,
      run
    };
    return <div className="p0-devtools-panel" role="region" aria-label="DevTools panel">
        {SECTIONS.map(section => {
      const isEmphasized = section.id === defaultTab;
      return <section key={section.id} id={`p0-devtools-panel__section--${section.id}`} className={"p0-devtools-panel__section" + (isEmphasized ? " p0-devtools-panel__section--active" : "")} aria-label={section.label}>
              <h4 className="p0-devtools-panel__section-heading">
                <svg {...iconBase} aria-hidden="true">
                  {renderShapes(section.shapes)}
                </svg>
                <span>{section.label}</span>
              </h4>
              <div className="p0-devtools-panel__section-body">
                {panes[section.id]}
              </div>
            </section>;
    })}
      </div>;
  }
  if (kind === "howToP0Dialog") {
    const htpIconBase = {
      ...iconBase,
      width: "16",
      height: "16"
    };
    const ICON_SHAPES = {
      archive: [{
        tag: "rect",
        attrs: {
          width: "20",
          height: "5",
          x: "2",
          y: "3",
          rx: "1"
        }
      }, {
        tag: "path",
        attrs: {
          d: "M4 8v11a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8"
        }
      }, {
        tag: "path",
        attrs: {
          d: "M10 12h4"
        }
      }],
      book: [{
        tag: "path",
        attrs: {
          d: "M4 19.5v-15A2.5 2.5 0 0 1 6.5 2H19a1 1 0 0 1 1 1v17a1 1 0 0 1-1 1H6.5a1 1 0 0 1 0-5H20"
        }
      }],
      calendarDays: [{
        tag: "path",
        attrs: {
          d: "M8 2v4"
        }
      }, {
        tag: "path",
        attrs: {
          d: "M16 2v4"
        }
      }, {
        tag: "rect",
        attrs: {
          width: "18",
          height: "18",
          x: "3",
          y: "4",
          rx: "2"
        }
      }, {
        tag: "path",
        attrs: {
          d: "M3 10h18"
        }
      }, {
        tag: "path",
        attrs: {
          d: "M8 14h.01"
        }
      }, {
        tag: "path",
        attrs: {
          d: "M12 14h.01"
        }
      }, {
        tag: "path",
        attrs: {
          d: "M16 14h.01"
        }
      }, {
        tag: "path",
        attrs: {
          d: "M8 18h.01"
        }
      }, {
        tag: "path",
        attrs: {
          d: "M12 18h.01"
        }
      }, {
        tag: "path",
        attrs: {
          d: "M16 18h.01"
        }
      }],
      code: [{
        tag: "polyline",
        attrs: {
          points: "16 18 22 12 16 6"
        }
      }, {
        tag: "polyline",
        attrs: {
          points: "8 6 2 12 8 18"
        }
      }],
      gitBranch: [{
        tag: "line",
        attrs: {
          x1: "6",
          x2: "6",
          y1: "3",
          y2: "15"
        }
      }, {
        tag: "circle",
        attrs: {
          cx: "18",
          cy: "6",
          r: "3"
        }
      }, {
        tag: "circle",
        attrs: {
          cx: "6",
          cy: "18",
          r: "3"
        }
      }, {
        tag: "path",
        attrs: {
          d: "M18 9a9 9 0 0 1-9 9"
        }
      }],
      gitCompare: [{
        tag: "circle",
        attrs: {
          cx: "5",
          cy: "6",
          r: "3"
        }
      }, {
        tag: "path",
        attrs: {
          d: "M12 6h5a2 2 0 0 1 2 2v7"
        }
      }, {
        tag: "circle",
        attrs: {
          cx: "19",
          cy: "18",
          r: "3"
        }
      }, {
        tag: "path",
        attrs: {
          d: "M12 18H7a2 2 0 0 1-2-2V9"
        }
      }],
      gitPullRequestArrow: [{
        tag: "circle",
        attrs: {
          cx: "5",
          cy: "6",
          r: "3"
        }
      }, {
        tag: "path",
        attrs: {
          d: "M5 9v12"
        }
      }, {
        tag: "circle",
        attrs: {
          cx: "19",
          cy: "18",
          r: "3"
        }
      }, {
        tag: "path",
        attrs: {
          d: "m15 9-3-3 3-3"
        }
      }, {
        tag: "path",
        attrs: {
          d: "M12 6h5a2 2 0 0 1 2 2v7"
        }
      }],
      keyRound: [{
        tag: "path",
        attrs: {
          d: "M2.586 17.414A2 2 0 0 0 2 18.828V21a1 1 0 0 0 1 1h3a1 1 0 0 0 1-1v-1a1 1 0 0 1 1-1h1a1 1 0 0 0 1-1v-1a1 1 0 0 1 1-1h.172a2 2 0 0 0 1.414-.586l.814-.814a6.5 6.5 0 1 0-4-4z"
        }
      }, {
        tag: "circle",
        attrs: {
          cx: "16.5",
          cy: "7.5",
          r: ".5",
          fill: "currentColor"
        }
      }],
      lightbulb: [{
        tag: "path",
        attrs: {
          d: "M15 14c.2-1 .7-1.7 1.5-2.5 1-.9 1.5-2.2 1.5-3.5A6 6 0 0 0 6 8c0 1 .2 2.2 1.5 3.5.7.7 1.3 1.5 1.5 2.5"
        }
      }, {
        tag: "path",
        attrs: {
          d: "M9 18h6"
        }
      }, {
        tag: "path",
        attrs: {
          d: "M10 22h4"
        }
      }],
      messageCircle: [{
        tag: "path",
        attrs: {
          d: "M7.9 20A9 9 0 1 0 4 16.1L2 22z"
        }
      }],
      messageSquare: [{
        tag: "path",
        attrs: {
          d: "M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"
        }
      }],
      notebookText: [{
        tag: "path",
        attrs: {
          d: "M2 6h4"
        }
      }, {
        tag: "path",
        attrs: {
          d: "M2 10h4"
        }
      }, {
        tag: "path",
        attrs: {
          d: "M2 14h4"
        }
      }, {
        tag: "path",
        attrs: {
          d: "M2 18h4"
        }
      }, {
        tag: "rect",
        attrs: {
          width: "16",
          height: "20",
          x: "4",
          y: "2",
          rx: "2"
        }
      }, {
        tag: "path",
        attrs: {
          d: "M9.5 8h5"
        }
      }, {
        tag: "path",
        attrs: {
          d: "M9.5 12H16"
        }
      }, {
        tag: "path",
        attrs: {
          d: "M9.5 16H14"
        }
      }],
      play: [{
        tag: "polygon",
        attrs: {
          points: "6 3 20 12 6 21 6 3"
        }
      }],
      rocket: [{
        tag: "path",
        attrs: {
          d: "M4.5 16.5c-1.5 1.26-2 5-2 5s3.74-.5 5-2c.71-.84.7-2.13-.09-2.91a2.18 2.18 0 0 0-2.91-.09z"
        }
      }, {
        tag: "path",
        attrs: {
          d: "m12 15-3-3a22 22 0 0 1 2-3.95A12.88 12.88 0 0 1 22 2c0 2.72-.78 7.5-6 11a22.35 22.35 0 0 1-4 2z"
        }
      }, {
        tag: "path",
        attrs: {
          d: "M9 12H4s.55-3.03 2-4c1.62-1.08 5 0 5 0"
        }
      }, {
        tag: "path",
        attrs: {
          d: "M12 15v5s3.03-.55 4-2c1.08-1.62 0-5 0-5"
        }
      }],
      terminal: [{
        tag: "polyline",
        attrs: {
          points: "4 17 10 11 4 5"
        }
      }, {
        tag: "line",
        attrs: {
          x1: "12",
          x2: "20",
          y1: "19",
          y2: "19"
        }
      }],
      testTube2: [{
        tag: "path",
        attrs: {
          d: "M21 7 6.82 21.18a2.83 2.83 0 0 1-3.99-.01a2.83 2.83 0 0 1 0-4L17 3"
        }
      }, {
        tag: "path",
        attrs: {
          d: "m16 2 6 6"
        }
      }, {
        tag: "path",
        attrs: {
          d: "M12 16H4"
        }
      }]
    };
    const SECTIONS = [{
      id: "modes-of-working",
      label: "Modes of Working",
      description: "p0 has different modes for different tasks. Pick the right one to get the best results.",
      tips: [{
        id: "brainstorm-mode",
        title: "Plan & Build Mode",
        description: "Use Plan & Build to flesh out an idea into a product spec, then let p0 build and QA it autonomously.",
        icon: "lightbulb"
      }, {
        id: "quick-changes",
        title: "Quick Changes (Chat)",
        description: "Ask p0 to fix bugs, refactor code, or make quick changes — just describe what you need.",
        icon: "messageSquare"
      }]
    }, {
      id: "project-setup",
      label: "Project Setup",
      description: "Open the Repositories manager from your sidebar to configure your project for p0.",
      tips: [{
        id: "engineering-standards",
        title: "Create Engineering Standards",
        description: "Define your coding conventions so p0 follows your team's patterns and preferences.",
        icon: "notebookText"
      }, {
        id: "qa-loops",
        title: "Create QA Loops",
        description: "Teach agents how to verify their work in your environment — URLs, credentials, and testing workflows.",
        icon: "testTube2"
      }, {
        id: "env-variables",
        title: "Set Up Environment Variables",
        description: "Configure .env files so p0 can run your project with the right secrets and config.",
        icon: "keyRound"
      }, {
        id: "run-commands",
        title: "Configure Run Commands",
        description: "Define how to start, build, or test your project so p0 can run it for you.",
        icon: "play"
      }, {
        id: "default-branch",
        title: "Set Default Branch",
        description: "Choose which branch p0 branches off from and creates PRs into for each repo.",
        icon: "gitPullRequestArrow"
      }]
    }, {
      id: "toolbar-actions",
      label: "Toolbar Actions",
      description: "On the top right of each session you'll find these tools to run, edit, and review your code.",
      tips: [{
        id: "open-terminal",
        title: "Open Terminal",
        description: "Launch a terminal inside your session's worktree to run commands without switching apps.",
        icon: "terminal"
      }, {
        id: "run-project",
        title: "Run Your Project",
        description: "Start your dev server or build process right from the session's worktree.",
        icon: "rocket"
      }, {
        id: "open-in-editor",
        title: "Open in Code Editor",
        description: "Open the session's worktree in your preferred code editor (VS Code, Cursor, etc.).",
        icon: "code"
      }, {
        id: "review-changes",
        title: "Review Changes",
        description: "See a diff of all changes p0 has made in the current session before committing.",
        icon: "gitCompare"
      }]
    }, {
      id: "advanced",
      label: "Advanced",
      description: "Power-user features that give you more control over how p0 works with your codebase.",
      tips: [{
        id: "worktrees",
        title: "Worktrees",
        description: "Each session branches off your repo's default branch into an isolated worktree — your main branch stays clean.",
        icon: "gitBranch"
      }, {
        id: "archive-sessions",
        title: "Archive & Unarchive Sessions",
        description: "Keep your sidebar clean by archiving old sessions. Right-click any session to archive or restore it.",
        icon: "archive"
      }]
    }];
    return <div className="p0-howto-dialog" role="img" aria-label="How to p0 dialog mock">
        <div className="p0-howto-dialog__header">
          <h3 className="p0-howto-dialog__title">How to p0</h3>
          <p className="p0-howto-dialog__subtitle">
            Learn everything p0 can do for you.
          </p>
        </div>

        {SECTIONS.map(section => <div key={section.id} className="p0-howto-dialog__section">
            <h4 className="p0-howto-dialog__section-heading">{section.label}</h4>
            <p className="p0-howto-dialog__section-desc">{section.description}</p>
            <ul className="p0-howto-dialog__tip-list">
              {section.tips.map(tip => <li key={tip.id} className="p0-howto-dialog__tip">
                  <span className="p0-howto-dialog__tip-check" aria-hidden="true" />
                  <span className="p0-howto-dialog__tip-body">
                    <span className="p0-howto-dialog__tip-title-row">
                      <svg {...htpIconBase} className="p0-howto-dialog__tip-icon">
                        {renderShapes(ICON_SHAPES[tip.icon] || [])}
                      </svg>
                      <span className="p0-howto-dialog__tip-title">{tip.title}</span>
                    </span>
                    <span className="p0-howto-dialog__tip-desc">{tip.description}</span>
                  </span>
                </li>)}
            </ul>
          </div>)}

        <div className="p0-howto-dialog__footer">
          <span className="p0-howto-dialog__footer-link">
            <svg {...htpIconBase} className="p0-howto-dialog__footer-icon">
              {renderShapes(ICON_SHAPES.book)}
            </svg>
            Open Documentation
          </span>
          <span className="p0-howto-dialog__footer-link">
            <svg {...htpIconBase} className="p0-howto-dialog__footer-icon">
              {renderShapes(ICON_SHAPES.messageCircle)}
            </svg>
            Join Community
          </span>
          <span className="p0-howto-dialog__footer-link">
            <svg {...htpIconBase} className="p0-howto-dialog__footer-icon">
              {renderShapes(ICON_SHAPES.calendarDays)}
            </svg>
            Book a Call
          </span>
        </div>
      </div>;
  }
  if (kind === "modelPicker") {
    const {current = "claude"} = rest;
    const mpIconBase = {
      ...iconBase,
      width: "16",
      height: "16"
    };
    const CHEVRON_SHAPES = [{
      tag: "path",
      attrs: {
        d: "m6 9 6 6 6-6"
      }
    }];
    const SPARKLES_SHAPES = [{
      tag: "path",
      attrs: {
        d: "M9.937 15.5A2 2 0 0 0 8.5 14.063l-6.135-1.582a.5.5 0 0 1 0-.962L8.5 9.936A2 2 0 0 0 9.937 8.5l1.582-6.135a.5.5 0 0 1 .963 0L14.063 8.5A2 2 0 0 0 15.5 9.937l6.135 1.581a.5.5 0 0 1 0 .964L15.5 14.063a2 2 0 0 0-1.437 1.437l-1.582 6.135a.5.5 0 0 1-.963 0z"
      }
    }, {
      tag: "path",
      attrs: {
        d: "M20 3v4"
      }
    }, {
      tag: "path",
      attrs: {
        d: "M22 5h-4"
      }
    }, {
      tag: "path",
      attrs: {
        d: "M4 17v2"
      }
    }, {
      tag: "path",
      attrs: {
        d: "M5 18H3"
      }
    }];
    const CPU_SHAPES = [{
      tag: "rect",
      attrs: {
        width: "16",
        height: "16",
        x: "4",
        y: "4",
        rx: "2"
      }
    }, {
      tag: "rect",
      attrs: {
        width: "6",
        height: "6",
        x: "9",
        y: "9",
        rx: "1"
      }
    }, {
      tag: "path",
      attrs: {
        d: "M15 2v2"
      }
    }, {
      tag: "path",
      attrs: {
        d: "M15 20v2"
      }
    }, {
      tag: "path",
      attrs: {
        d: "M2 15h2"
      }
    }, {
      tag: "path",
      attrs: {
        d: "M2 9h2"
      }
    }, {
      tag: "path",
      attrs: {
        d: "M20 15h2"
      }
    }, {
      tag: "path",
      attrs: {
        d: "M20 9h2"
      }
    }, {
      tag: "path",
      attrs: {
        d: "M9 2v2"
      }
    }, {
      tag: "path",
      attrs: {
        d: "M9 20v2"
      }
    }];
    const MODEL_META = {
      claude: {
        label: "Claude",
        shapes: SPARKLES_SHAPES
      },
      codex: {
        label: "Codex",
        shapes: CPU_SHAPES
      }
    };
    const active = MODEL_META[current] ?? MODEL_META.claude;
    const alternateId = current === "claude" ? "codex" : "claude";
    const alternate = MODEL_META[alternateId];
    return <span className="p0-model-picker" role="img" aria-label={`Model picker mock, current selection ${active.label}`}>
        <button type="button" className="p0-model-picker__chip" aria-haspopup="listbox" aria-expanded="false">
          <svg {...mpIconBase} className="p0-model-picker__icon">
            {renderShapes(active.shapes)}
          </svg>
          <span className="p0-model-picker__label">{active.label}</span>
          <svg {...mpIconBase} className="p0-model-picker__chevron">
            {renderShapes(CHEVRON_SHAPES)}
          </svg>
        </button>
        <span className="p0-model-picker__menu" role="listbox" aria-hidden="true">
          <span className="p0-model-picker__option p0-model-picker__option--active">
            <svg {...mpIconBase} className="p0-model-picker__icon">
              {renderShapes(active.shapes)}
            </svg>
            <span className="p0-model-picker__label">{active.label}</span>
          </span>
          <span className="p0-model-picker__option">
            <svg {...mpIconBase} className="p0-model-picker__icon">
              {renderShapes(alternate.shapes)}
            </svg>
            <span className="p0-model-picker__label">{alternate.label}</span>
          </span>
        </span>
      </span>;
  }
  if (kind === "prCelebration") {
    const {prNumber, tokens, costCents} = rest;
    const formatCostCents = cents => {
      const safe = Number.isFinite(cents) ? Math.max(0, cents) : 0;
      return `$${(safe / 100).toFixed(2)}`;
    };
    const formatTokens = t => {
      const safe = Number.isFinite(t) ? Math.max(0, Math.floor(t)) : 0;
      return safe.toLocaleString("en-US");
    };
    const costLabel = formatCostCents(costCents);
    const tokenLabel = formatTokens(tokens);
    return <div className="p0-pr-celebration" role="img" aria-label={`PR shipped celebration overlay mock for PR #${prNumber}`}>
        <div className="p0-pr-celebration__backdrop" aria-hidden="true" />
        <div className="p0-pr-celebration__panel">
          <div className="p0-pr-celebration__icon" aria-hidden="true">
            <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" width="16" height="16" className="p0-pr-celebration__icon-glyph" aria-hidden="true">
              <path d="M5.8 11.3 2 22l10.7-3.79" />
              <path d="M4 3h.01" />
              <path d="M22 8h.01" />
              <path d="M15 2h.01" />
              <path d="M22 20h.01" />
              <path d="m22 2-2.24.75a2.9 2.9 0 0 0-1.96 3.12c.1.86-.57 1.63-1.45 1.63h-.38c-.86 0-1.6.6-1.76 1.44L14 10" />
              <path d="m22 13-1.7.3c-.87.16-1.46.94-1.42 1.81v.5c.04.87-.55 1.65-1.42 1.81L16 18" />
              <path d="M3 7v4c0 1.1.9 2 2 2h4" />
              <path d="m7.5 11 4.55-4.55c.36-.36.85-.55 1.36-.45L17 7" />
            </svg>
          </div>
          <h3 className="p0-pr-celebration__title">Pull Request Created!</h3>
          <p className="p0-pr-celebration__pr">PR #{prNumber}</p>
          <p className="p0-pr-celebration__stats">
            <span className="p0-pr-celebration__stat-value">{tokenLabel}</span> tokens
            {" · "}
            <span className="p0-pr-celebration__stat-value">{costLabel}</span>
          </p>
          <div className="p0-pr-celebration__actions">
            <span className="p0-pr-celebration__btn p0-pr-celebration__btn--secondary">
              Continue
            </span>
            <span className="p0-pr-celebration__btn p0-pr-celebration__btn--primary">
              View PR
            </span>
          </div>
        </div>
      </div>;
  }
  if (kind === "contributionHeatmap") {
    const {weeks, seed} = rest;
    const ROWS = 7;
    const DEFAULT_WEEKS = 52;
    const DEFAULT_SEED = 42;
    const LEVELS = 5;
    const resolvedWeeks = weeks === undefined ? DEFAULT_WEEKS : weeks;
    const resolvedSeed = seed === undefined ? DEFAULT_SEED : seed;
    const makeRng = s => {
      let state = s >>> 0 || 1;
      return () => {
        state = Math.imul(state, 1664525) + 1013904223 >>> 0;
        return state;
      };
    };
    const pickLevel = rng => {
      const r = rng() >>> 16;
      if (r < 0.3 * 65536) return 0;
      if (r < 0.55 * 65536) return 1;
      if (r < 0.75 * 65536) return 2;
      if (r < 0.9 * 65536) return 3;
      return 4;
    };
    const rng = makeRng(resolvedSeed);
    const cells = [];
    for (let col = 0; col < resolvedWeeks; col++) {
      const column = [];
      for (let row = 0; row < ROWS; row++) {
        column.push(pickLevel(rng));
      }
      cells.push(column);
    }
    return <div className="p0-heatmap" role="img" aria-label={`Contribution heatmap mock — ${resolvedWeeks} weeks of activity`}>
        <div className="p0-heatmap__grid" aria-hidden="true">
          {cells.map((column, colIdx) => <div className="p0-heatmap__col" key={`col-${colIdx}`}>
              {column.map((level, rowIdx) => <span className={`p0-heatmap__cell p0-heatmap__cell--level-${level}`} key={`cell-${colIdx}-${rowIdx}`} />)}
            </div>)}
        </div>
        <div className="p0-heatmap__legend" aria-hidden="true">
          <span className="p0-heatmap__legend-label">Less</span>
          {Array.from({
      length: LEVELS
    }).map((_, i) => <span className={`p0-heatmap__cell p0-heatmap__cell--level-${i}`} key={`legend-${i}`} />)}
          <span className="p0-heatmap__legend-label">More</span>
        </div>
      </div>;
  }
  if (kind === "shareSheet") {
    const {link, teammates = [], expiry} = rest;
    const getInitials = name => {
      if (!name) return "?";
      const parts = name.trim().split(/\s+/);
      if (parts.length === 1) return parts[0].charAt(0).toUpperCase();
      return parts[0].charAt(0).toUpperCase() + parts[parts.length - 1].charAt(0).toUpperCase();
    };
    return <div className="p0-share-sheet" role="dialog" aria-label="Share session">
        <div className="p0-share-sheet__header">
          <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" width="16" height="16" className="p0-share-sheet__icon" aria-hidden="true">
            <path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" />
            <path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" />
          </svg>
          <span className="p0-share-sheet__title">Share session</span>
        </div>

        <div className="p0-share-sheet__link-row">
          <input className="p0-share-sheet__link-input" type="text" value={link} readOnly aria-label="Share link" />
          <button className="p0-share-sheet__copy" type="button" aria-label="Copy link" onClick={event => event.preventDefault()}>
            <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" width="14" height="14" aria-hidden="true">
              <rect x="9" y="9" width="13" height="13" rx="2" ry="2" />
              <path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1" />
            </svg>
            <span>Copy</span>
          </button>
        </div>

        {teammates.length > 0 ? <div className="p0-share-sheet__teammates">
            <span className="p0-share-sheet__label">Shared with</span>
            <div className="p0-share-sheet__avatars">
              {teammates.map((member, idx) => member.avatar ? <img key={`${member.name}-${idx}`} className="p0-share-sheet__avatar p0-share-sheet__avatar--img" src={member.avatar} alt={member.name} /> : <span key={`${member.name}-${idx}`} className="p0-share-sheet__avatar p0-share-sheet__avatar--initials" aria-label={member.name} title={member.name}>
                    {getInitials(member.name)}
                  </span>)}
            </div>
          </div> : null}

        {expiry ? <div className="p0-share-sheet__expiry">
            <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" width="12" height="12" aria-hidden="true">
              <circle cx="12" cy="12" r="10" />
              <polyline points="12 6 12 12 16 14" />
            </svg>
            <span>{expiry}</span>
          </div> : null}
      </div>;
  }
  if (kind === "reviewsBell") {
    const {unread} = rest;
    const BADGE_CAP = 99;
    const count = Number.isFinite(unread) ? unread : 0;
    const showBadge = count > 0;
    const label = count > BADGE_CAP ? `${BADGE_CAP}+` : String(count);
    return <span className={`p0-reviews-bell${showBadge ? " p0-reviews-bell--has-unread" : ""}`} role="status" aria-label={showBadge ? `Reviews — ${count} unread` : "Reviews — no unread feedback"}>
        <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" width="18" height="18" className="p0-reviews-bell__icon" aria-hidden="true">
          <path d="M6 8a6 6 0 0 1 12 0c0 7 3 9 3 9H3s3-2 3-9" />
          <path d="M10.3 21a1.94 1.94 0 0 0 3.4 0" />
        </svg>
        {showBadge ? <span className="p0-reviews-bell__badge" aria-hidden="true">
            {label}
          </span> : null}
      </span>;
  }
  if (kind === "remoteControlQR") {
    const {code = "PURPLE"} = rest;
    const SIZE = 21;
    const FINDER = 7;
    const djb2 = input => {
      let hash = 5381;
      for (let i = 0; i < input.length; i++) {
        hash = (hash << 5) + hash + input.charCodeAt(i) | 0;
      }
      return hash >>> 0;
    };
    const makeRng = seed => {
      let state = seed >>> 0 || 1;
      return () => {
        state = Math.imul(state, 1664525) + 1013904223 >>> 0;
        return state;
      };
    };
    const isFinderCell = (row, col) => {
      const inTopLeft = row < FINDER && col < FINDER;
      const inTopRight = row < FINDER && col >= SIZE - FINDER;
      const inBottomLeft = row >= SIZE - FINDER && col < FINDER;
      return inTopLeft || inTopRight || inBottomLeft;
    };
    const finderFill = (row, col) => {
      let r = row;
      let c = col;
      if (col >= SIZE - FINDER) c = col - (SIZE - FINDER);
      if (row >= SIZE - FINDER) r = row - (SIZE - FINDER);
      const onOuterRing = r === 0 || r === 6 || c === 0 || c === 6;
      const inInnerSquare = r >= 2 && r <= 4 && c >= 2 && c <= 4;
      return onOuterRing || inInnerSquare;
    };
    const rng = makeRng(djb2(String(code)));
    const cells = [];
    for (let row = 0; row < SIZE; row++) {
      for (let col = 0; col < SIZE; col++) {
        let fill;
        if (isFinderCell(row, col)) {
          fill = finderFill(row, col);
        } else {
          fill = rng() >>> 31 === 1;
        }
        cells.push(fill);
      }
    }
    return <div className="p0-remote-qr" role="img" aria-label={`Illustrative QR mock for pairing code ${code}`}>
        <div className="p0-remote-qr__grid" style={{
      gridTemplateColumns: `repeat(${SIZE}, 1fr)`,
      gridTemplateRows: `repeat(${SIZE}, 1fr)`
    }} aria-hidden="true">
          {cells.map((fill, idx) => <span key={`qr-${idx}`} className={`p0-remote-qr__cell${fill ? " p0-remote-qr__cell--on" : ""}`} />)}
        </div>
        <div className="p0-remote-qr__caption">
          <span className="p0-remote-qr__illustrative">Illustrative</span>
          <span className="p0-remote-qr__code">{code}</span>
        </div>
        <div className="p0-remote-qr__status" role="status">
          <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" width="14" height="14" className="p0-remote-qr__status-icon" aria-hidden="true">
            <rect x="5" y="2" width="14" height="20" rx="2" ry="2" />
            <path d="M12 18h.01" />
          </svg>
          <span>Waiting for phone…</span>
        </div>
      </div>;
  }
  if (kind === "kbdRow") {
    const {keys = [], description} = rest;
    return <span className="p0-kbd-row" role="group" aria-label={description}>
        <span className="p0-kbd-row__chips" aria-hidden="true">
          {keys.map((key, idx) => <span className="p0-kbd-row__chip" key={`kbd-${idx}-${key}`}>
              {idx > 0 ? <span className="p0-kbd-row__plus">+</span> : null}
              <kbd className="p0-kbd">{key}</kbd>
            </span>)}
        </span>
        <span className="p0-kbd-row__description">{description}</span>
      </span>;
  }
  if (kind === "renamedCallout") {
    const {from, to, version} = rest;
    return <span className="p0-renamed-callout" role="note">
        Renamed in v{version}:{" "}
        <span className="p0-renamed-callout__from">{from}</span>
        {" → "}
        <span className="p0-renamed-callout__to">{to}</span>
      </span>;
  }
  if (kind === "slashCommandsTable") {
    const SLASH_COMMANDS = [{
      command: "/agent",
      description: "Switch to Agent mode (applies changes directly)"
    }, {
      command: "/clear",
      description: "Start a new conversation (creates new sub-chat)"
    }, {
      command: "/compact",
      description: "Compact conversation context to reduce token usage"
    }, {
      command: "/config",
      description: "View and modify configuration settings"
    }, {
      command: "/cost",
      description: "Show token usage and cost for the session"
    }, {
      command: "/doctor",
      description: "Check the health of your p0 installation"
    }, {
      command: "/help",
      description: "Show available commands and help"
    }, {
      command: "/mcp",
      description: "View and manage MCP server connections"
    }, {
      command: "/memory",
      description: "View and edit CLAUDE.md memory files"
    }, {
      command: "/model",
      description: "Switch AI model"
    }, {
      command: "/permissions",
      description: "View and manage tool permissions"
    }, {
      command: "/plan",
      description: "Switch to Plan mode (creates plan before making changes)"
    }, {
      command: "/pr-comments",
      description: "Ask the agent to generate PR review comments"
    }, {
      command: "/release-notes",
      description: "Ask the agent to generate release notes"
    }, {
      command: "/review",
      description: "Ask the agent to review your code"
    }, {
      command: "/security-review",
      description: "Ask the agent to perform a security audit"
    }, {
      command: "/skills",
      description: "Manage your skills"
    }, {
      command: "/status",
      description: "Show current session status and context"
    }, {
      command: "/build-from-spec",
      description: "Run the end-to-end spec → build → QA pipeline"
    }, {
      command: "/create-standards",
      description: "Generate engineering standards for your workspace"
    }, {
      command: "/refine",
      description: "Make incremental changes to a recently built feature"
    }, {
      command: "/import-spec",
      description: "Import a product spec and ask clarifying questions"
    }];
    return <table>
        <thead>
          <tr>
            <th>Command</th>
            <th>What it does</th>
            <th>Where to find it</th>
          </tr>
        </thead>
        <tbody>
          {SLASH_COMMANDS.map(row => <tr key={row.command}>
              <td><code>{row.command}</code></td>
              <td>{row.description}</td>
              <td>Chat input · slash menu</td>
            </tr>)}
        </tbody>
      </table>;
  }
  return null;
};

Plan & Build is the card on the new-session page that takes you from an idea to a merged pull request without leaving p0. You bring a description; p0 turns it into a product spec, breaks it into tickets, runs an engineer team that builds and QAs the work, and opens a PR at the end.

<Mock kind="renamedCallout" from="Brainstorm" to="Plan & Build" version="1.0.48" />

## How to start one

On the new-session page, pick the **Plan & Build** card. It's the default. The panel's own subtitle gives the pitch in one line.

> Bring an idea, plan it out with an agent that understands your codebase, then let p0 build it autonomously, QA its own work, and hand you the PRs.

Power users can also type `/build-from-spec` directly in any chat input. That is the underlying slash command Plan & Build wraps. Pointing the command at an existing product spec (and optionally a technical spec) skips past idea-gathering and into the engineering phase.

## The phases

Plan & Build moves through five phases. The first four are visible in the breadcrumb at the top of the session. **Post-PR** is rendered as a full-screen takeover when PRs are ready. The exact labels come from `PHASE_LABELS` in `features/spec-mode/atoms/index.ts`.

| #  | Label    | What it produces                                              |
| -- | -------- | ------------------------------------------------------------- |
| 1. | Import.  | A product spec (and optional technical spec) in the editor.   |
| 2. | Improve. | A refined spec that is ready to be expanded into tickets.     |
| 3. | Watch.   | An implementation, ticket by ticket, in an isolated worktree. |
| 4. | Done.    | A reviewed build with a manual-setup checklist.               |
| 5. | Post-PR. | One or more pull requests, surfaced in a celebration screen.  |

### 1. Import

The Import panel offers three sources: **Files** (local `.md` / `.txt` / `.pdf` / `.docx`), **Linear**, and **JIRA**. The Files mode separates a required **Product Spec** from an optional **Technical Spec** and accepts arbitrary attachments (images, PDFs, reference docs) for context.

The phase ends when a spec is loaded. The `handleSpecImported` callback in `spec-driven-content.tsx` persists the spec content, renames the session after the imported file, and advances the session's `phase` column to 2.

### 2. Improve

The Improve panel is a split view: spec editor on the left (with **Product** / **Technical** / **Attachments** tabs), a chat with p0 on the right, and a readiness meter in the footer. The meter colors itself based on product-spec length — **red** under 100 chars, **orange** at 100–499, **yellow** at 500–1499, **green** at 1500+.

The phase ends when you click the **Go!** button in the readiness footer of the Improve panel (`improve-spec-panel.tsx` L1014). The split-button next to it has a clock caret that opens a schedule popover if you want to defer the build — see [Scheduled sessions](/scheduled-sessions). Behind the scenes, Go! saves the spec to disk and writes phase `3` to the DB.

### 3. Watch

Watch is another split view: the Claude stream on the left (`WatchModeStream`) and the live ticket tree on the right (`TicketTreePanel`). This is where `/build-from-spec`'s middle three phases play out. The engineering architect breaks the spec into an `implementation-spec-{slug}.md`. The orchestrator spawns an agent team named `impl-{slug}`. QA teammates verify the work against the engineers who wrote it (max three QA attempts, per the command file).

<Mock
  kind="ticketTree"
  phases={[
{
  name: "Phase 1: Scaffold",
  tickets: [
    { id: "AUTH-1.1", name: "Add /login route", status: "completed" },
    { id: "AUTH-1.2", name: "Wire JWT middleware", status: "in_progress" },
    { id: "AUTH-1.3", name: "Persist refresh tokens", status: "todo" },
  ],
},
{
  name: "Phase 2: Tests",
  tickets: [
    { id: "AUTH-2.1", name: "Unit tests for token refresh", status: "todo" },
  ],
},
]}
/>

You do not click anything to leave Watch. The transition to **Done** fires automatically when either the agent team sends a `buildComplete` MCP signal (the preferred path) or `manual-setup.md` lands in the feature folder (the polling fallback). Both are wired through `usePhaseDetection`.

### 4. Done

Done preserves the Phase 3 chat history on the left (`WatchModeStream` in read-only mode). The right pane shows a **Manual Setup** checklist — the contents of the `manual-setup.md` file the build wrote. Examples: env vars, indexes, third-party config, anything the code itself could not do.

The footer has one primary action: **Create PR/s** (it relabels to **Update PR/s** if PRs have already been opened from this session). Clicking it injects the PR-creation instruction into the agent, which pushes the worktree branch and opens a pull request per repo.

### 5. Post-PR

The final phase is a full-screen takeover with a celebration animation. It lists one row per repo with the PR number, an **External link** to the PR/MR, a state badge ("open" / "merged" / "closed"), and a **Check status** action. Failed repos get a friendly error card with the branch name and a manual-alternative path (open the worktree, copy the branch, etc.). The terminology adapts to your provider — GitHub repos say "Pull Request"; GitLab repos say "Merge Request".

## The DevTools panel

Throughout Watch and Done, the bottom **DevTools panel** keeps three tools alongside the chat. **Terminal** opens an xterm session rooted in the worktree. **Changes** shows a live diff of every file p0 has touched, split or unified. **Run** starts and tails the dev script you configured for each repo. See [Run scripts](/run-scripts) for how the Run tab gets wired.

## What happens after Done

Once **Create PR/s** finishes, the session writes the PR results to the DB and hands them to `PostPRSuccess`. The **Update Standards** button is gated on `standardsSuggestionAtom` being non-empty — it appears only when the agent sent one or more standards suggestions during the build. The label shows the single suggestion's description, or "N standards updates detected" for multiple. Clicking it opens the review modal so you can accept or dismiss each suggestion before closing the session.
