> ## Documentation Index
> Fetch the complete documentation index at: https://felimet-hub.jmcores.com/llms.txt
> Use this file to discover all available pages before exploring further.

# 02-6 Configuration Across Other Tools

> Using Claude as the baseline, map OpenAI Codex, Google Antigravity, GitHub Copilot CLI, and Cursor onto the 02-1 layer model: where personalization lives, what privacy opt-out is called, which file holds project rules, how automation works, how memory is stored, and where MCP connects. Switch tools by looking up the table, not by relearning everything.

export const ToolCompare = ({lang = "zh", tools = [], dimensions = [], defaultSelected, notes = []}) => {
  const UI = lang === "en" ? {
    allTools: "All",
    recommend: "Recommended",
    noSupport: "N/A",
    minOneToolW: "Select at least one tool",
    mobileLabel: "Tool",
    dimensionLbl: "Dimension",
    notes: "Notes"
  } : {
    allTools: "全選",
    recommend: "推薦",
    noSupport: "無對應",
    minOneToolW: "至少選一個工具",
    mobileLabel: "工具",
    dimensionLbl: "維度",
    notes: "注意事項"
  };
  const ACCENT_L = "#bf7551";
  const ACCENT_D = "#cf8a68";
  const REC_BDR_L = "rgba(191,117,81,0.45)";
  const REC_BG_L = "rgba(191,117,81,0.06)";
  const REC_BDR_D = "rgba(207,138,104,0.45)";
  const REC_BG_D = "rgba(207,138,104,0.08)";
  const looksLikePath = val => typeof val === "string" && (/[/\\.*:]/).test(val);
  const safeTools = Array.isArray(tools) ? tools : [];
  const safeDimensions = Array.isArray(dimensions) ? dimensions : [];
  if (safeTools.length === 0 || safeDimensions.length === 0) return null;
  const allIds = safeTools.map(t => t.id);
  const initSelected = Array.isArray(defaultSelected) && defaultSelected.length > 0 ? defaultSelected.filter(id => allIds.includes(id)) : allIds;
  const [selectedIds, setSelectedIds] = useState(initSelected.length > 0 ? initSelected : allIds);
  const [expandedCell, setExpandedCell] = useState(null);
  const [mobileTool, setMobileTool] = useState(initSelected[0] || allIds[0]);
  const [isDark, setIsDark] = useState(false);
  useEffect(() => {
    const detect = () => setIsDark(document.documentElement.classList.contains("dark"));
    detect();
    const obs = new MutationObserver(detect);
    obs.observe(document.documentElement, {
      attributes: true,
      attributeFilter: ["class"]
    });
    return () => obs.disconnect();
  }, []);
  const toggleTool = id => {
    setSelectedIds(prev => {
      const next = prev.includes(id) ? prev.filter(x => x !== id) : [...prev, id];
      if (next.length === 0) return prev;
      if (expandedCell) {
        const [, cellToolId] = expandedCell.split(":");
        if (!next.includes(cellToolId)) setExpandedCell(null);
      }
      return next;
    });
  };
  const isAllSelected = selectedIds.length === allIds.length;
  const toggleAll = () => {
    if (isAllSelected) {
      setSelectedIds([allIds[0]]);
    } else {
      setSelectedIds(allIds);
    }
    setExpandedCell(null);
  };
  const visibleTools = safeTools.filter(t => selectedIds.includes(t.id));
  const cellKey = (dimId, toolId) => dimId + ":" + toolId;
  const toggleExpand = (dimId, toolId) => {
    const key = cellKey(dimId, toolId);
    setExpandedCell(prev => prev === key ? null : key);
  };
  const renderValue = cell => {
    if (cell.na) {
      return <span className="tc-na">{UI.noSupport}</span>;
    }
    const useMono = cell.mono === true || cell.mono !== false && looksLikePath(cell.value);
    return useMono ? <code className="tc-code">{cell.value}</code> : <span>{cell.value}</span>;
  };
  const css = `
  /* ── 根容器 ── */
  .tc-root {
    --tc-bg:           #FAF8F3;
    --tc-surface:      rgba(0,0,0,0.022);
    --tc-stripe:       rgba(0,0,0,0.028);
    --tc-border:       rgba(0,0,0,0.09);
    --tc-text:         #2b2722;
    --tc-dim:          #6f6a62;
    --tc-faint:        #8a8378;
    --tc-accent:       ${ACCENT_L};
    --tc-accent-bg:    ${REC_BG_L};
    --tc-accent-bdr:   ${REC_BDR_L};
    --tc-expand-bg:    rgba(0,0,0,0.016);
    --tc-code-bg:      rgba(0,0,0,0.055);
    --tc-na-color:     #9a9490;
    --tc-pill-on-bg:   rgba(191,117,81,0.10);
    --tc-pill-on-bdr:  rgba(191,117,81,0.35);
    --tc-pill-on-txt:  #a05c38;
    border: 1px solid var(--tc-border);
    border-radius: 14px;
    background: var(--tc-bg);
    color: var(--tc-text);
    overflow: hidden;
    font-size: 14px;
  }
  .dark .tc-root {
    --tc-bg:           #1b1a18;
    --tc-surface:      rgba(255,255,255,0.03);
    --tc-stripe:       rgba(255,255,255,0.025);
    --tc-border:       rgba(255,255,255,0.08);
    --tc-text:         #e7e3da;
    --tc-dim:          #a8a299;
    --tc-faint:        #706b64;
    --tc-accent:       ${ACCENT_D};
    --tc-accent-bg:    ${REC_BG_D};
    --tc-accent-bdr:   ${REC_BDR_D};
    --tc-expand-bg:    rgba(255,255,255,0.022);
    --tc-code-bg:      rgba(255,255,255,0.08);
    --tc-na-color:     #6e6b65;
    --tc-pill-on-bg:   rgba(207,138,104,0.12);
    --tc-pill-on-bdr:  rgba(207,138,104,0.35);
    --tc-pill-on-txt:  ${ACCENT_D};
  }

  /* ── 精簡篩選列 ── */
  .tc-filter-bar {
    display: flex;
    align-items: center;
    gap: 0;
    padding: 0 14px;
    border-bottom: 1px solid var(--tc-border);
    background: var(--tc-surface);
    overflow-x: auto;
    overflow-y: hidden;
    scrollbar-width: none;
    -webkit-overflow-scrolling: touch;
    /* 單行、不折行，高度由內容決定（約 36px） */
    flex-wrap: nowrap;
    white-space: nowrap;
    min-height: 36px;
  }
  .tc-filter-bar::-webkit-scrollbar { display: none; }

  /* 分隔竿（All 後面） */
  .tc-filter-sep {
    flex-shrink: 0;
    width: 1px;
    height: 14px;
    background: var(--tc-border);
    margin: 0 10px 0 6px;
    align-self: center;
  }

  /* pill 基底 — 極輕量文字標籤 */
  .tc-pill {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    padding: 5px 9px;
    border-radius: 5px;
    border: 1px solid transparent;
    background: transparent;
    color: var(--tc-faint);
    font: inherit;
    font-size: 12px;
    font-weight: 500;
    letter-spacing: 0.01em;
    cursor: pointer;
    transition: color 0.12s, background 0.12s, border-color 0.12s;
    flex-shrink: 0;
    white-space: nowrap;
    /* 行高對齊 filter-bar */
    margin: 5px 1px;
  }
  .tc-pill:hover {
    color: var(--tc-text);
    background: rgba(0,0,0,0.04);
  }
  .dark .tc-pill:hover {
    background: rgba(255,255,255,0.05);
  }
  /* 選中態：細框 + 淡底 + 文字加深（非按鈕感，保持輕量） */
  .tc-pill-on {
    color: var(--tc-pill-on-txt);
    background: var(--tc-pill-on-bg);
    border-color: var(--tc-pill-on-bdr);
    font-weight: 600;
  }
  /* 全選 pill — 略小一點字型 */
  .tc-pill-all {
    font-size: 11px;
    font-weight: 600;
    letter-spacing: 0.03em;
    text-transform: uppercase;
    padding: 4px 8px;
    color: var(--tc-dim);
  }
  .tc-pill-all.tc-pill-on {
    color: var(--tc-pill-on-txt);
  }
  /* 選中小圓點 */
  .tc-pill-dot {
    width: 5px;
    height: 5px;
    border-radius: 50%;
    background: var(--tc-accent);
    flex-shrink: 0;
  }

  /* ── 桌面表格 ── */
  .tc-table-wrap {
    overflow: hidden;
  }
  /* Mintlify 的 MDX 渲染器會自動把 <table> 包進 data-table-wrapper：
   * 加 -mx-[var(--page-padding)] 負邊距 + w-[calc(100%+padding*2)] 全寬 + py-[1em]，
   * 讓表格往外溢出，撐破 tc-root 的圓角容器（2026-06-12 線上實證跑版）。
   * 中和它：邊距歸零、寬度收回 100%、把橫向捲動容器設在這層（sticky 左欄靠它）。 */
  .tc-root [data-table-wrapper] {
    margin: 0 !important;
    width: 100% !important;
    max-width: 100% !important;
    padding: 0 !important;
    overflow-x: auto;
    contain: none !important;
  }
  .tc-root [data-table-wrapper] > div {
    padding: 0 !important;
    margin: 0 !important;
  }
  .tc-root [data-table-wrapper]::-webkit-scrollbar { height: 4px; }
  .tc-root [data-table-wrapper]::-webkit-scrollbar-thumb { background: var(--tc-border); border-radius: 2px; }
  .tc-table {
    width: 100%;
    border-collapse: collapse;
    margin: 0 !important;
  }

  /* 表頭 */
  .tc-thead th {
    padding: 9px 15px;
    text-align: left;
    font-size: 11px;
    font-weight: 700;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--tc-faint);
    border-bottom: 2px solid var(--tc-border);
    background: var(--tc-surface);
    white-space: nowrap;
  }
  /* 維度欄（sticky 左欄） */
  .tc-thead th:first-child,
  .tc-td-dim {
    position: sticky;
    left: 0;
    z-index: 1;
  }
  .tc-thead th:first-child {
    width: 160px;
    min-width: 130px;
    background: var(--tc-surface);
    border-right: 1px solid var(--tc-border);
    z-index: 2;
  }

  /* 維度標籤欄 */
  .tc-td-dim {
    padding: 13px 15px;
    font-size: 12.5px;
    font-weight: 700;
    color: var(--tc-dim);
    vertical-align: middle;
    white-space: nowrap;
    border-bottom: 1px solid var(--tc-border);
    border-right: 1px solid var(--tc-border);
    background: var(--tc-surface);
  }

  /* 斑馬紋：奇數維度列 */
  .tc-row-even .tc-td-dim,
  .tc-row-even .tc-td {
    background-color: var(--tc-stripe);
  }
  .tc-row-even .tc-td-dim {
    background: color-mix(in srgb, var(--tc-surface) 70%, var(--tc-stripe) 30%);
  }

  /* 資料儲存格 */
  .tc-td {
    padding: 0;
    border-bottom: 1px solid var(--tc-border);
    border-left: 1px solid var(--tc-border);
    vertical-align: top;
    min-width: 150px;
  }
  .tc-td-inner {
    display: flex;
    flex-direction: column;
  }

  /* 主值行（可點擊） */
  .tc-cell-btn {
    display: flex;
    align-items: flex-start;
    gap: 6px;
    width: 100%;
    text-align: left;
    background: transparent;
    border: none;
    padding: 13px 15px;
    color: inherit;
    font: inherit;
    font-size: 13px;
    line-height: 1.5;
    cursor: pointer;
    transition: background 0.11s;
    -webkit-tap-highlight-color: transparent;
  }
  .tc-cell-btn:hover {
    background: rgba(0,0,0,0.03);
  }
  .dark .tc-cell-btn:hover {
    background: rgba(255,255,255,0.03);
  }
  .tc-cell-btn-on {
    background: var(--tc-expand-bg);
  }

  /* 推薦標記：左 3px accent 邊框 + 淡底 */
  .tc-td-recommend {
    border-left: 3px solid var(--tc-accent-bdr);
    background: var(--tc-accent-bg);
  }
  .tc-td-recommend .tc-cell-btn:hover {
    background: rgba(191,117,81,0.05);
  }

  /* recommend 角標：超小 badge */
  .tc-rec-badge {
    flex-shrink: 0;
    margin-top: 1px;
    font-size: 9px;
    font-weight: 700;
    letter-spacing: 0.05em;
    text-transform: uppercase;
    color: var(--tc-accent);
    border: 1px solid var(--tc-accent-bdr);
    border-radius: 3px;
    padding: 1px 4px;
    white-space: nowrap;
    opacity: 0.85;
  }

  /* na 樣式 */
  .tc-na {
    font-style: italic;
    color: var(--tc-na-color);
    font-size: 12.5px;
    opacity: 0.7;
  }

  /* 等寬值 */
  .tc-code {
    font-family: "JetBrains Mono", "Fira Code", "Cascadia Code", monospace;
    font-size: 12px;
    background: var(--tc-code-bg);
    border-radius: 3px;
    padding: 1px 4px;
    word-break: break-all;
  }

  /* 展開詳情列 */
  .tc-detail-row td {
    padding: 0;
    border-bottom: 1px solid var(--tc-border);
  }
  .tc-detail-cell {
    padding: 10px 15px 12px;
    font-size: 12.5px;
    line-height: 1.65;
    color: var(--tc-dim);
    background: var(--tc-expand-bg);
    border-top: 1px dashed var(--tc-border);
    border-left: 1px solid var(--tc-border);
  }
  .tc-detail-cell:first-child {
    border-left: none;
    font-weight: 700;
    color: var(--tc-faint);
    font-size: 11px;
    text-transform: uppercase;
    letter-spacing: 0.05em;
    vertical-align: top;
    padding-top: 12px;
    background: var(--tc-surface);
    white-space: nowrap;
    width: 160px;
  }
  .tc-detail-recommend {
    border-left: 3px solid var(--tc-accent-bdr) !important;
    background: var(--tc-accent-bg) !important;
  }

  /* 展開箭頭 */
  .tc-chevron {
    flex-shrink: 0;
    margin-top: 3px;
    opacity: 0.35;
    transition: transform 0.14s, opacity 0.14s;
  }
  .tc-chevron-open {
    transform: rotate(90deg);
    opacity: 0.65;
  }

  /* ── 注腳 ── */
  .tc-notes {
    border-top: 1px solid var(--tc-border);
    padding: 10px 16px 12px;
    display: flex;
    flex-direction: column;
    gap: 4px;
  }
  .tc-notes-label {
    font-size: 10.5px;
    font-weight: 700;
    letter-spacing: 0.07em;
    text-transform: uppercase;
    color: var(--tc-faint);
    margin-bottom: 3px;
  }
  .tc-note-item {
    font-size: 12.5px;
    color: var(--tc-dim);
    line-height: 1.55;
    padding-left: 14px;
    position: relative;
  }
  .tc-note-item::before {
    content: "*";
    position: absolute;
    left: 2px;
    color: var(--tc-faint);
  }

  /* ── 手機模式 ─────────────────────────────────────────── */
  .tc-mobile { display: none; }
  @media (max-width: 700px) {
    .tc-filter-bar { display: none; }
    .tc-table-wrap  { display: none; }
    .tc-mobile      { display: block; }

    .tc-mob-tabs {
      display: flex;
      overflow-x: auto;
      border-bottom: 1px solid var(--tc-border);
      -webkit-overflow-scrolling: touch;
      scrollbar-width: none;
    }
    .tc-mob-tabs::-webkit-scrollbar { display: none; }
    .tc-mob-tab {
      flex: 1 0 auto;
      padding: 10px 16px;
      background: transparent;
      border: none;
      border-bottom: 2px solid transparent;
      color: var(--tc-dim);
      font: inherit;
      font-size: 13px;
      font-weight: 500;
      cursor: pointer;
      white-space: nowrap;
      transition: color 0.12s, border-color 0.12s;
    }
    .tc-mob-tab-on {
      color: var(--tc-accent);
      border-bottom-color: var(--tc-accent);
      font-weight: 700;
    }
    .tc-mob-cards {
      padding: 12px 0 4px;
    }
    .tc-mob-card {
      border-bottom: 1px solid var(--tc-border);
      padding: 12px 16px;
    }
    .tc-mob-card:last-child { border-bottom: none; }
    .tc-mob-dim {
      font-size: 11px;
      font-weight: 700;
      letter-spacing: 0.05em;
      text-transform: uppercase;
      color: var(--tc-faint);
      margin-bottom: 6px;
    }
    .tc-mob-val {
      font-size: 13.5px;
      line-height: 1.55;
    }
    .tc-mob-rec {
      display: inline-block;
      margin-top: 6px;
      font-size: 9.5px;
      font-weight: 700;
      letter-spacing: 0.05em;
      text-transform: uppercase;
      color: var(--tc-accent);
      border: 1px solid var(--tc-accent-bdr);
      border-radius: 3px;
      padding: 1px 5px;
    }
    .tc-mob-detail {
      margin-top: 7px;
      font-size: 12.5px;
      line-height: 1.65;
      color: var(--tc-dim);
    }
    .tc-notes { padding: 11px 16px 13px; }
  }
  `;
  const renderDesktopTable = () => <div className="tc-table-wrap">
      <table className="tc-table">
        <thead className="tc-thead">
          <tr>
            <th>{UI.dimensionLbl}</th>
            {visibleTools.map(tool => <th key={tool.id}>{tool.label}</th>)}
          </tr>
        </thead>
        <tbody>
          {}
          {safeDimensions.flatMap((dim, dimIdx) => {
    const isAnyExpanded = visibleTools.some(t => expandedCell === cellKey(dim.id, t.id));
    const stripeClass = dimIdx % 2 === 1 ? " tc-row-even" : "";
    const rows = [];
    rows.push(<tr key={dim.id} className={"tc-row" + stripeClass}>
                <td className="tc-td-dim">{dim.label}</td>
                {visibleTools.map(tool => {
      const cell = (dim.cells || ({}))[tool.id] || ({});
      const key = cellKey(dim.id, tool.id);
      const isOpen = expandedCell === key;
      const hasDetail = !!cell.detail;
      const isRec = !!cell.recommend;
      return <td key={tool.id} className={"tc-td" + (isRec ? " tc-td-recommend" : "")}>
                      <div className="tc-td-inner">
                        <button type="button" className={"tc-cell-btn" + (isOpen ? " tc-cell-btn-on" : "")} onClick={hasDetail ? () => toggleExpand(dim.id, tool.id) : undefined} style={hasDetail ? {} : {
        cursor: "default"
      }} aria-expanded={hasDetail ? String(isOpen) : undefined}>
                          <span style={{
        flex: "1 1 0",
        minWidth: 0
      }}>
                            {renderValue(cell)}
                          </span>
                          {isRec && <span className="tc-rec-badge">{UI.recommend}</span>}
                          {hasDetail && <svg className={"tc-chevron" + (isOpen ? " tc-chevron-open" : "")} width="10" height="10" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round">
                              <polyline points="9 18 15 12 9 6" />
                            </svg>}
                        </button>
                      </div>
                    </td>;
    })}
              </tr>);
    if (isAnyExpanded) {
      const openToolId = visibleTools.find(t => expandedCell === cellKey(dim.id, t.id))?.id;
      const openCell = openToolId ? (dim.cells || ({}))[openToolId] || ({}) : {};
      if (openCell.detail) {
        const openTool = safeTools.find(t => t.id === openToolId);
        const isRec = !!openCell.recommend;
        rows.push(<tr key={dim.id + "-detail"} className="tc-detail-row">
                    <td className="tc-detail-cell">
                      {openTool ? openTool.label : ""}
                    </td>
                    <td colSpan={visibleTools.length} className={"tc-detail-cell" + (isRec ? " tc-detail-recommend" : "")}>
                      {openCell.detail}
                    </td>
                  </tr>);
      }
    }
    return rows;
  })}
        </tbody>
      </table>
    </div>;
  const renderMobile = () => {
    const activeTool = safeTools.find(t => t.id === mobileTool) || safeTools[0];
    return <div className="tc-mobile">
        <div className="tc-mob-tabs" role="tablist">
          {safeTools.map(tool => <button key={tool.id} type="button" role="tab" aria-selected={tool.id === mobileTool ? "true" : "false"} className={"tc-mob-tab" + (tool.id === mobileTool ? " tc-mob-tab-on" : "")} onClick={() => setMobileTool(tool.id)}>
              {tool.label}
            </button>)}
        </div>

        <div className="tc-mob-cards">
          {safeDimensions.map(dim => {
      const cell = (dim.cells || ({}))[activeTool.id] || ({});
      const isRec = !!cell.recommend;
      return <div key={dim.id} className="tc-mob-card">
                <div className="tc-mob-dim">{dim.label}</div>
                <div className="tc-mob-val">{renderValue(cell)}</div>
                {isRec && <div className="tc-mob-rec">{UI.recommend}</div>}
                {cell.detail && <div className="tc-mob-detail">{cell.detail}</div>}
              </div>;
    })}
        </div>
      </div>;
  };
  return <div className="tc-root">
      <style>{css}</style>

      {}
      <div className="tc-filter-bar">
        <button type="button" className={"tc-pill tc-pill-all" + (isAllSelected ? " tc-pill-on" : "")} onClick={toggleAll} aria-pressed={String(isAllSelected)}>
          {UI.allTools}
        </button>

        {}
        <span className="tc-filter-sep" aria-hidden="true" />

        {safeTools.map(tool => {
    const isOn = selectedIds.includes(tool.id);
    return <button key={tool.id} type="button" className={"tc-pill" + (isOn ? " tc-pill-on" : "")} onClick={() => toggleTool(tool.id)} aria-pressed={String(isOn)}>
              {isOn && <span className="tc-pill-dot" aria-hidden="true" />}
              {tool.label}
            </button>;
  })}
      </div>

      {}
      {visibleTools.length > 0 && renderDesktopTable()}

      {}
      {renderMobile()}

      {}
      {notes.length > 0 && <div className="tc-notes">
          <div className="tc-notes-label">{UI.notes}</div>
          {notes.map((note, i) => <div key={i} className="tc-note-item">{note}</div>)}
        </div>}
    </div>;
};

export const DirExplorer = ({lang = "zh", scopes = []}) => {
  const UI = lang === "en" ? {
    expandAll: "Expand all",
    collapseAll: "Collapse all",
    whenLoads: "WHEN IT LOADS",
    tips: "TIPS",
    copy: "Copy",
    copied: "Copied",
    mapsToLabel: "Maps to Claude",
    badgeLabels: {
      committed: "Committed to version control",
      gitignored: "Git-ignored (local only)",
      local: "Local override",
      autogen: "Auto-generated"
    },
    badgeTexts: {
      committed: "COMMITTED",
      gitignored: "GITIGNORED",
      local: "LOCAL",
      autogen: "AUTOGEN"
    }
  } : {
    expandAll: "全部展開",
    collapseAll: "全部收合",
    whenLoads: "載入時機",
    tips: "提示",
    copy: "複製",
    copied: "已複製",
    mapsToLabel: "對照 Claude",
    badgeLabels: {
      committed: "已提交至版本控制",
      gitignored: "Git 忽略（僅本機）",
      local: "本機覆寫",
      autogen: "自動產生"
    },
    badgeTexts: {
      committed: "COMMITTED",
      gitignored: "GITIGNORED",
      local: "LOCAL",
      autogen: "AUTOGEN"
    }
  };
  const ACCENT_LIGHT = "#bf7551";
  const ACCENT_DARK = "#cf8a68";
  const DIR_COLORS = {
    blue: "#5b6c8f",
    purple: "#8a6f8e",
    yellow: "#c9a35b",
    green: "#8a9a7b",
    teal: "#6f9290",
    rose: "#ab7269",
    amber: "#b9835c",
    gray: "#8a8378",
    default: "#8a8378"
  };
  const BADGE_COLORS = {
    committed: {
      dot: "#6dab7d",
      title: "committed"
    },
    gitignored: {
      dot: "#d4844a",
      title: "gitignored"
    },
    local: {
      dot: "#8a8378",
      title: "local"
    },
    autogen: {
      dot: "#c8a240",
      title: "autogen"
    }
  };
  const safeScopes = Array.isArray(scopes) && scopes.length > 0 ? scopes : [];
  if (safeScopes.length === 0) return null;
  const [activeScopeId, setActiveScopeId] = useState(safeScopes[0].id);
  const [expandedIds, setExpandedIds] = useState({});
  const [selectedNode, setSelectedNode] = useState(null);
  const [copiedFlag, setCopiedFlag] = useState(false);
  const [isDark, setIsDark] = useState(false);
  useEffect(() => {
    const detectDark = () => setIsDark(document.documentElement.classList.contains("dark"));
    detectDark();
    const obs = new MutationObserver(detectDark);
    obs.observe(document.documentElement, {
      attributes: true,
      attributeFilter: ["class"]
    });
    const firstScope = safeScopes[0];
    if (firstScope && Array.isArray(firstScope.tree)) {
      const initExpanded = {};
      firstScope.tree.forEach((node, i) => {
        if (node.kind === "dir") initExpanded[firstScope.id + ":" + i] = true;
      });
      setExpandedIds(initExpanded);
    }
    return () => obs.disconnect();
  }, []);
  const switchScope = id => {
    setActiveScopeId(id);
    setSelectedNode(null);
    const scope = safeScopes.find(s => s.id === id);
    if (!scope) return;
    const initExpanded = {};
    (scope.tree || []).forEach((node, i) => {
      if (node.kind === "dir") initExpanded[id + ":" + i] = true;
    });
    setExpandedIds(initExpanded);
  };
  const activeScope = safeScopes.find(s => s.id === activeScopeId) || safeScopes[0];
  const tree = Array.isArray(activeScope.tree) ? activeScope.tree : [];
  const collectAllDirKeys = (nodes, scopeId, prefix) => {
    const keys = [];
    (nodes || []).forEach((node, i) => {
      const key = prefix + i;
      if (node.kind === "dir") {
        keys.push(scopeId + ":" + key);
        const childKeys = collectAllDirKeys(node.children, scopeId, key + "-");
        childKeys.forEach(k => keys.push(k));
      }
    });
    return keys;
  };
  const handleExpandAll = () => {
    const keys = collectAllDirKeys(tree, activeScopeId, "");
    const next = {};
    keys.forEach(k => {
      next[k] = true;
    });
    setExpandedIds(next);
  };
  const handleCollapseAll = () => {
    setExpandedIds({});
  };
  const isAllExpanded = () => {
    const keys = collectAllDirKeys(tree, activeScopeId, "");
    return keys.length > 0 && keys.every(k => expandedIds[k]);
  };
  const handleCopy = code => {
    if (!code) return;
    try {
      navigator.clipboard.writeText(code).then(() => {
        setCopiedFlag(true);
        setTimeout(() => setCopiedFlag(false), 1600);
      });
    } catch (_) {
      setCopiedFlag(false);
    }
  };
  const FolderIcon = ({color}) => <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke={color || DIR_COLORS.default} strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" style={{
    flexShrink: 0
  }}>
      <path d="M22 19a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h5l2 3h9a2 2 0 0 1 2 2z" />
    </svg>;
  const FileDocIcon = ({color}) => <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke={color || DIR_COLORS.default} strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" style={{
    flexShrink: 0
  }}>
      <path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z" />
      <polyline points="14 2 14 8 20 8" />
      <line x1="16" y1="13" x2="8" y2="13" />
      <line x1="16" y1="17" x2="8" y2="17" />
      <polyline points="10 9 9 9 8 9" />
    </svg>;
  const FileJsonIcon = ({color}) => <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke={color || DIR_COLORS.default} strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" style={{
    flexShrink: 0
  }}>
      <path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z" />
      <polyline points="14 2 14 8 20 8" />
      <text x="7" y="19" fontSize="7" fontWeight="700" fill={color || DIR_COLORS.default} stroke="none" fontFamily="monospace">{"{}"}</text>
    </svg>;
  const ChevronRight = () => <svg width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round" style={{
    flexShrink: 0,
    transition: "transform 0.15s"
  }}>
      <polyline points="9 18 15 12 9 6" />
    </svg>;
  const getFileIcon = node => {
    const c = DIR_COLORS[node.color] || DIR_COLORS.default;
    if (node.kind === "dir") return <FolderIcon color={c} />;
    if (node.name && (node.name.endsWith(".json") || node.name.endsWith(".jsonc"))) return <FileJsonIcon color={c} />;
    return <FileDocIcon color={c} />;
  };
  const renderNodes = (nodes, scopeId, depth, keyPrefix) => {
    return (nodes || []).map((node, i) => {
      const nodeKey = keyPrefix + i;
      const stateKey = scopeId + ":" + nodeKey;
      const isExpanded = !!expandedIds[stateKey];
      const isSelected = selectedNode && selectedNode._key === stateKey;
      const badge = node.badge ? BADGE_COLORS[node.badge] : null;
      const hasDetail = node.detail && typeof node.detail === "object";
      const isDir = node.kind === "dir";
      const handleClick = () => {
        if (isDir) {
          setExpandedIds(prev => ({
            ...prev,
            [stateKey]: !prev[stateKey]
          }));
        }
        if (hasDetail) {
          setSelectedNode(isSelected ? null : {
            ...node,
            _key: stateKey
          });
        } else if (isDir && !hasDetail) {}
      };
      return <div key={stateKey} className="dx-node-wrap">
          <button type="button" className={"dx-node" + (isSelected ? " dx-node-selected" : "")} style={{
        paddingLeft: depth * 14 + 8 + "px"
      }} onClick={handleClick} aria-pressed={isSelected ? "true" : "false"}>
            {isDir ? <span className="dx-chevron" style={{
        transform: isExpanded ? "rotate(90deg)" : "rotate(0deg)"
      }}>
                <ChevronRight />
              </span> : <span className="dx-chevron-spacer" />}
            {getFileIcon(node)}
            <span className="dx-node-name">{node.name}</span>
            {badge && <span className="dx-badge-dot" style={{
        background: badge.dot
      }} title={UI.badgeLabels[node.badge] || node.badge} />}
          </button>
          {isDir && isExpanded && node.children && node.children.length > 0 && <div className="dx-children">
              {renderNodes(node.children, scopeId, depth + 1, nodeKey + "-")}
            </div>}
        </div>;
    });
  };
  const renderDetail = () => {
    if (!selectedNode || !selectedNode.detail) {
      return <div className="dx-detail-empty">
          <svg width="32" height="32" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" style={{
        opacity: 0.25,
        marginBottom: "10px"
      }}>
            <path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z" />
            <polyline points="14 2 14 8 20 8" />
            <line x1="16" y1="13" x2="8" y2="13" />
            <line x1="16" y1="17" x2="8" y2="17" />
          </svg>
          <span>{lang === "en" ? "Select a file or folder" : "點選左側項目查看說明"}</span>
        </div>;
    }
    const d = selectedNode.detail;
    const c = DIR_COLORS[selectedNode.color] || DIR_COLORS.default;
    const badge = selectedNode.badge ? BADGE_COLORS[selectedNode.badge] : null;
    return <div className="dx-detail-inner">
        {d.breadcrumb && <div className="dx-breadcrumb">{d.breadcrumb}</div>}

        <div className="dx-detail-title-row">
          {getFileIcon(selectedNode)}
          <span className="dx-detail-title">{d.title || selectedNode.name}</span>
          {badge && <span className="dx-pill" style={{
      color: badge.dot,
      borderColor: badge.dot + "55",
      background: badge.dot + "18"
    }}>
              {UI.badgeTexts[selectedNode.badge]}
            </span>}
        </div>

        {d.tagline && <div className="dx-tagline">{d.tagline}</div>}

        {d.whenItLoads && <div className="dx-when-card">
            <div className="dx-when-label">{UI.whenLoads}</div>
            <div className="dx-when-body">{d.whenItLoads}</div>
          </div>}

        {d.description && <div className="dx-description">{d.description}</div>}

        {d.tips && d.tips.length > 0 && <div className="dx-tips-card">
            <div className="dx-tips-label">{UI.tips}</div>
            <ul className="dx-tips-list">
              {d.tips.map((tip, i) => <li key={i} className="dx-tips-item">{tip}</li>)}
            </ul>
          </div>}

        {d.example && d.example.code && <div className="dx-code-block">
            <div className="dx-code-header">
              <span className="dx-code-filename">{d.example.filename || ""}</span>
              <button type="button" className="dx-copy-btn" onClick={() => handleCopy(d.example.code)}>
                {copiedFlag ? UI.copied : UI.copy}
              </button>
            </div>
            <pre className="dx-code-pre"><code>{d.example.code}</code></pre>
          </div>}

        {d.mapsTo && <div className="dx-maps-card">
            <div className="dx-maps-label">{UI.mapsToLabel}</div>
            <div className="dx-maps-body">{d.mapsTo}</div>
          </div>}
      </div>;
  };
  const accentColor = isDark ? ACCENT_DARK : ACCENT_LIGHT;
  const css = `
  .dx-root {
    --dx-bg: #FAF8F3;
    --dx-surface: rgba(0,0,0,0.025);
    --dx-border: rgba(0,0,0,0.09);
    --dx-text: #2b2722;
    --dx-dim: #6f6a62;
    --dx-faint: #8a8378;
    --dx-accent: #bf7551;
    --dx-accent-bg: rgba(191,117,81,0.08);
    --dx-accent-border: rgba(191,117,81,0.30);
    --dx-when-bg: rgba(91,108,143,0.07);
    --dx-when-border: rgba(91,108,143,0.35);
    --dx-when-label: #46587a;
    --dx-tips-bg: rgba(191,117,81,0.06);
    --dx-code-bg: #1A1918;
    --dx-code-text: #e2ddd7;
    --dx-code-header: #2a2826;
    --dx-maps-bg: rgba(138,111,142,0.09);
    --dx-maps-border: rgba(138,111,142,0.35);
    --dx-maps-label: #6f5673;
    border: 1px solid var(--dx-border);
    border-radius: 12px;
    background: var(--dx-bg);
    color: var(--dx-text);
    overflow: hidden;
    display: flex;
    min-height: 560px;
  }
  .dark .dx-root {
    --dx-bg: #1b1a18;
    --dx-surface: rgba(255,255,255,0.03);
    --dx-border: rgba(255,255,255,0.08);
    --dx-text: #e7e3da;
    --dx-dim: #a8a299;
    --dx-faint: #8a8378;
    --dx-accent: #cf8a68;
    --dx-accent-bg: rgba(207,138,104,0.09);
    --dx-accent-border: rgba(207,138,104,0.30);
    --dx-when-bg: rgba(91,108,143,0.14);
    --dx-when-border: rgba(91,108,143,0.42);
    --dx-when-label: #9fb0d4;
    --dx-tips-bg: rgba(207,138,104,0.07);
    --dx-code-bg: #121110;
    --dx-code-text: #d8d4ce;
    --dx-code-header: #1e1c1a;
    --dx-maps-bg: rgba(138,111,142,0.14);
    --dx-maps-border: rgba(138,111,142,0.42);
    --dx-maps-label: #c4a8c8;
  }

  /* ── 左面板 ── */
  .dx-left {
    width: min(264px, 38%);
    min-width: 196px;
    flex-shrink: 0;
    border-right: 1px solid var(--dx-border);
    display: flex;
    flex-direction: column;
    overflow: hidden;
  }
  /* scope bar 強制單列：scope 按鈕收縮溢出省略，展開鈕固定不擠掉 */
  .dx-scope-bar {
    display: flex;
    align-items: center;
    gap: 4px;
    padding: 7px 8px 6px;
    border-bottom: 1px solid var(--dx-border);
    flex-wrap: nowrap;
  }
  .dx-scope-btn {
    flex: 1 1 0;
    min-width: 0;
    font-size: 11px;
    font-weight: 600;
    padding: 4px 7px;
    border-radius: 6px;
    border: 1px solid transparent;
    background: transparent;
    color: var(--dx-dim);
    cursor: pointer;
    transition: background 0.13s, color 0.13s, border-color 0.13s;
    font: inherit;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
  }
  .dx-scope-btn:hover {
    background: var(--dx-surface);
    color: var(--dx-text);
  }
  .dx-scope-btn.dx-scope-active {
    background: var(--dx-accent-bg);
    border-color: var(--dx-accent-border);
    color: var(--dx-accent);
  }
  .dx-expand-btn {
    flex-shrink: 0;
    font-size: 10.5px;
    padding: 4px 7px;
    border-radius: 6px;
    border: 1px solid var(--dx-border);
    background: transparent;
    color: var(--dx-faint);
    cursor: pointer;
    transition: color 0.13s, background 0.13s;
    font: inherit;
    white-space: nowrap;
    letter-spacing: 0.01em;
  }
  .dx-expand-btn:hover {
    background: var(--dx-surface);
    color: var(--dx-text);
  }
  .dx-tree {
    flex: 1 1 0;
    overflow-y: auto;
    padding: 6px 0 10px;
  }
  .dx-tree::-webkit-scrollbar { width: 5px; }
  .dx-tree::-webkit-scrollbar-thumb { background: var(--dx-border); border-radius: 4px; }
  .dx-node-wrap { display: flex; flex-direction: column; }
  .dx-node {
    display: flex;
    align-items: center;
    gap: 5px;
    width: 100%;
    text-align: left;
    background: transparent;
    border: none;
    border-left: 2px solid transparent;
    cursor: pointer;
    padding-top: 4px;
    padding-bottom: 4px;
    padding-right: 10px;
    color: var(--dx-text);
    font: inherit;
    font-size: 13px;
    line-height: 1.4;
    transition: background 0.12s, border-color 0.12s;
    -webkit-tap-highlight-color: transparent;
  }
  .dx-node:hover {
    background: var(--dx-surface);
  }
  .dx-node-selected {
    border-left-color: var(--dx-accent);
    background: var(--dx-accent-bg);
    color: var(--dx-text);
  }
  .dx-chevron {
    display: flex;
    align-items: center;
    flex-shrink: 0;
    color: var(--dx-faint);
    width: 13px;
  }
  .dx-chevron-spacer { width: 13px; flex-shrink: 0; }
  .dx-node-name {
    flex: 1 1 0;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }
  /* 狀態 badge 改小圓角方塊（非外部文件站的圓點），保持自家識別 */
  .dx-badge-dot {
    width: 5.5px;
    height: 5.5px;
    border-radius: 1.5px;
    transform: rotate(45deg);
    flex-shrink: 0;
    margin-left: 2px;
    opacity: 0.85;
  }

  /* ── 右詳情面板 ── */
  .dx-right {
    flex: 1 1 0;
    min-width: 0;
    overflow-y: auto;
    padding: 20px 24px;
    display: flex;
    flex-direction: column;
  }
  .dx-right::-webkit-scrollbar { width: 5px; }
  .dx-right::-webkit-scrollbar-thumb { background: var(--dx-border); border-radius: 4px; }
  .dx-detail-empty {
    flex: 1 1 0;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    color: var(--dx-faint);
    font-size: 13px;
    gap: 4px;
    text-align: center;
  }
  .dx-detail-inner {
    display: flex;
    flex-direction: column;
    gap: 14px;
  }
  .dx-breadcrumb {
    font-size: 11.5px;
    color: var(--dx-faint);
    font-family: monospace;
    letter-spacing: 0.01em;
  }
  .dx-detail-title-row {
    display: flex;
    align-items: center;
    gap: 8px;
    flex-wrap: wrap;
  }
  .dx-detail-title {
    font-size: 22px;
    font-weight: 700;
    line-height: 1.25;
    color: var(--dx-text);
  }
  .dx-pill {
    font-size: 10.5px;
    font-weight: 700;
    letter-spacing: 0.05em;
    text-transform: uppercase;
    padding: 2px 8px;
    border-radius: 20px;
    border: 1px solid;
    flex-shrink: 0;
  }
  .dx-tagline {
    font-size: 13.5px;
    color: var(--dx-dim);
    line-height: 1.5;
    margin-top: -4px;
  }
  /* 載入時機卡走自家樣式：左緣色條 + 暖中性底（非外部文件站的藍框卡） */
  .dx-when-card {
    background: var(--dx-when-bg);
    border: none;
    border-left: 3px solid var(--dx-when-border);
    border-radius: 4px 10px 10px 4px;
    padding: 11px 14px;
  }
  .dx-when-label {
    font-size: 10.5px;
    font-weight: 700;
    letter-spacing: 0.07em;
    text-transform: uppercase;
    color: var(--dx-when-label);
    margin-bottom: 5px;
  }
  .dx-when-body {
    font-size: 13.5px;
    color: var(--dx-text);
    line-height: 1.55;
  }
  .dx-description {
    font-size: 15px;
    line-height: 1.65;
    color: var(--dx-dim);
  }
  .dx-tips-card {
    background: var(--dx-tips-bg);
    border-radius: 8px;
    padding: 11px 14px 12px;
  }
  .dx-tips-label {
    font-size: 10.5px;
    font-weight: 700;
    letter-spacing: 0.07em;
    text-transform: uppercase;
    color: var(--dx-accent);
    margin-bottom: 7px;
  }
  .dx-tips-list {
    margin: 0;
    padding-left: 18px;
    display: flex;
    flex-direction: column;
    gap: 5px;
  }
  .dx-tips-item {
    font-size: 13.5px;
    color: var(--dx-dim);
    line-height: 1.55;
  }
  .dx-code-block {
    border-radius: 9px;
    overflow: hidden;
    border: 1px solid rgba(255,255,255,0.06);
  }
  .dx-code-header {
    background: var(--dx-code-header);
    padding: 7px 12px;
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 8px;
  }
  .dx-code-filename {
    font-size: 11.5px;
    font-family: monospace;
    color: var(--dx-faint);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
  }
  .dx-copy-btn {
    flex-shrink: 0;
    font-size: 11px;
    font-weight: 600;
    padding: 3px 9px;
    border-radius: 5px;
    border: 1px solid rgba(255,255,255,0.12);
    background: rgba(255,255,255,0.06);
    color: #c0bbb4;
    cursor: pointer;
    transition: background 0.13s, color 0.13s;
    font: inherit;
    font-size: 11px;
    font-weight: 600;
  }
  .dx-copy-btn:hover {
    background: rgba(255,255,255,0.12);
    color: #e2ddd7;
  }
  .dx-code-pre {
    margin: 0;
    background: var(--dx-code-bg);
    padding: 14px 16px;
    overflow-x: auto;
    font-size: 12.5px;
    line-height: 1.6;
    color: var(--dx-code-text);
    font-family: "JetBrains Mono", "Fira Code", "Cascadia Code", monospace;
    tab-size: 2;
  }
  .dx-code-pre::-webkit-scrollbar { height: 5px; }
  .dx-code-pre::-webkit-scrollbar-thumb { background: rgba(255,255,255,0.12); border-radius: 3px; }
  .dx-maps-card {
    background: var(--dx-maps-bg);
    border: 1px solid var(--dx-maps-border);
    border-radius: 8px;
    padding: 11px 14px;
  }
  .dx-maps-label {
    font-size: 10.5px;
    font-weight: 700;
    letter-spacing: 0.07em;
    text-transform: uppercase;
    color: var(--dx-maps-label);
    margin-bottom: 5px;
  }
  .dx-maps-body {
    font-size: 13.5px;
    color: var(--dx-dim);
    line-height: 1.55;
  }

  /* ── 手機堆疊 ── */
  @media (max-width: 700px) {
    .dx-root { flex-direction: column; min-height: unset; }
    .dx-left {
      width: 100%;
      min-width: unset;
      border-right: none;
      border-bottom: 1px solid var(--dx-border);
      max-height: 260px;
    }
    .dx-right { padding: 16px; }
    .dx-detail-title { font-size: 18px; }
  }
  `;
  return <div className="dx-root">
      <style>{css}</style>

      {}
      <div className="dx-left">
        <div className="dx-scope-bar">
          {safeScopes.map(scope => <button key={scope.id} type="button" className={"dx-scope-btn" + (scope.id === activeScopeId ? " dx-scope-active" : "")} onClick={() => switchScope(scope.id)}>
              {scope.label}
            </button>)}
          <button type="button" className="dx-expand-btn" onClick={isAllExpanded() ? handleCollapseAll : handleExpandAll} title={isAllExpanded() ? UI.collapseAll : UI.expandAll}>
            {isAllExpanded() ? "⊟" : "⊞"}
          </button>
        </div>

        <div className="dx-tree" role="tree">
          {renderNodes(tree, activeScopeId, 0, "")}
        </div>
      </div>

      {}
      <div className="dx-right">
        {renderDetail()}
      </div>
    </div>;
};

export const LearnerPrimer = ({items = [], lang = "zh"}) => {
  const t = lang === "en" ? {
    title: "Before this unit, be honest with yourself",
    sub: "If you can't answer these, that gap is exactly what this unit closes."
  } : {
    title: "讀這個單元前，先誠實面對",
    sub: "這幾題答不出來，正是這個單元要替你補的洞。"
  };
  const css = `
  .lp-root{--lp-bg:#FAF7F1;--lp-surface:rgba(191,117,81,0.05);--lp-border:rgba(0,0,0,0.09);--lp-edge:rgba(191,117,81,0.42);--lp-text:#2b2722;--lp-dim:#6f6a62;--lp-accent:#bf7551;border:1px solid var(--lp-border);border-left:3px solid var(--lp-edge);border-radius:13px;background:var(--lp-bg);color:var(--lp-text);overflow:hidden;margin:1.25rem 0;}
  .dark .lp-root{--lp-bg:#1b1a18;--lp-surface:rgba(207,138,104,0.07);--lp-border:rgba(255,255,255,0.08);--lp-edge:rgba(207,138,104,0.5);--lp-text:#e7e3da;--lp-dim:#a8a299;--lp-accent:#cf8a68;}
  .lp-head{display:flex;align-items:center;gap:9px;padding:13px 18px 11px;border-bottom:1px solid var(--lp-border);background:var(--lp-surface);}
  .lp-ic{color:var(--lp-accent);flex-shrink:0;}
  .lp-htx{display:flex;flex-direction:column;gap:1px;min-width:0;}
  .lp-title{font-size:14px;font-weight:650;line-height:1.3;letter-spacing:.01em;}
  .lp-sub{font-size:12px;color:var(--lp-dim);line-height:1.4;}
  .lp-list{list-style:none;margin:0;padding:10px 18px 14px;display:flex;flex-direction:column;gap:0;}
  .lp-item{display:flex;align-items:baseline;gap:11px;padding:7px 0;font-size:14px;line-height:1.6;border-top:1px solid var(--lp-border);}
  .lp-item:first-child{border-top:none;}
  .lp-mark{flex-shrink:0;color:var(--lp-accent);font-size:13px;font-weight:700;line-height:1.55;font-variant-numeric:tabular-nums;opacity:.85;}
  .lp-q{flex:1 1 0;min-width:0;color:var(--lp-text);}
  @media (max-width:620px){.lp-head{padding:12px 14px 10px;}.lp-list{padding:8px 14px 12px;}}
  `;
  return <div className="lp-root">
      <style>{css}</style>
      <div className="lp-head">
        <svg className="lp-ic" xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><circle cx="12" cy="12" r="10" /><circle cx="12" cy="12" r="6" /><circle cx="12" cy="12" r="2" /></svg>
        <span className="lp-htx">
          <span className="lp-title">{t.title}</span>
          <span className="lp-sub">{t.sub}</span>
        </span>
      </div>
      <ul className="lp-list">
        {items.map((q, i) => <li className="lp-item" key={i}>
            <span className="lp-mark">{String(i + 1).padStart(2, "0")}</span>
            <span className="lp-q">{q}</span>
          </li>)}
      </ul>
    </div>;
};

<LearnerPrimer
  lang="en"
  items={[
"Each tool's Memory mechanism is completely different -- apply your old mental model and you'll misjudge the privacy boundary.",
"Maintaining five rule files simultaneously means none of them will be correct in three months.",
"Consumer tiers default to using your conversations for training. If you haven't actively turned it off, it's on.",
"AGENTS.md is a baseline, not a single pot for every tool's differences.",
"You confused .cursorignore with .cursorindexingignore and your secrets leaked.",
"Claude Code does not read AGENTS.md -- you need to @AGENTS.md import it explicitly.",
"Copy-pasting config paths from third-party blog posts means one version bump breaks everything.",
]}
/>

<Info>
  **What this unit solves**

  You are not learning four tools; you are learning the same set of concepts under four different names. Using [02-2 Claude](/en/code-agent/configuration/anthropic-claude-setup) as the baseline, map OpenAI Codex, Google Antigravity, and GitHub Copilot onto the [02-1 layer model](/en/code-agent/configuration/config-layer-model): where personalization is configured, what privacy opt-out is called, which file holds project rules, whether an automation mechanism exists, how memory is stored, and where MCP connects. Cursor, as a third-party IDE, appears only in a brief section at the end. Once the mapping is done, switching tools means consulting a table, not relearning from scratch.

  **How this page differs from [04-0 .claude directory reference](/en/code-agent/customization/claude-directory-reference)**: 04-0 is the complete reference for Claude's own `.claude/` directory. This page uses Claude as the baseline and shows configuration directory comparisons across tools.
</Info>

## Learning objectives

* [ ] Map any tool's personalization, privacy, project rules, automation, memory, and MCP to the [02-1 layer model](/en/code-agent/configuration/config-layer-model).
* [ ] State each tool's project-level rule file name and location, and judge the scope and priority order it applies.
* [ ] Find each tool's privacy / training opt-out switch and know whether the default is on or off.
* [ ] Use `AGENTS.md` to share one set of project rules across multiple tools, and explain the trade-offs between it and each tool's proprietary file.

<Warning>
  **Currency disclaimer**

  The setting names, file names, and mechanisms below were verified as of 2026-06; see sources at the end. All tools update frequently; recheck against official docs before you act. File names and directory paths are fast-moving facts -- this unit provides a mapping framework, not a list you can copy forever.
</Warning>

***

## 1. Framework first, then the four main tools

Every tool's configuration surface revolves around the same six concepts. Pin them down first; everything after is filling in the columns.

| Concept         | The question it answers                       | 02-1 layer                        |
| --------------- | --------------------------------------------- | --------------------------------- |
| Personalization | How should the model talk to "you"?           | User global layer                 |
| Project rules   | What constraints apply in this repo?          | Project layer                     |
| Memory          | What should persist across conversations?     | User layer (usually account-side) |
| Privacy opt-out | Will your conversations be used for training? | Account / org layer               |
| Automation      | Reusable commands, skills, triggers           | Primarily project layer           |
| MCP             | How do external tools and data connect?       | Project / user layer              |

The test is simple: when you pick up a new tool, ask what fills each of these six cells and what the defaults are. If you can fill the whole table you understand its configuration surface. Any cell you cannot fill is documentation you have not read yet. The next four sections map each tool to these six cells and call out the differences; Section 5 briefly covers Cursor as a contrast.

## 2. OpenAI: ChatGPT (consumer) and Codex (CLI)

OpenAI's configuration surface runs on two tracks: ChatGPT is account settings plus a GUI; Codex is files plus a CLI. The concepts align, but the interfaces are completely different.

**ChatGPT personalization** goes through Settings → Personalization → Custom Instructions, with two fields ("about you" and "how you'd like responses"), and requires enabling Enable customization to take effect. Tone is a separate dropdown under Base style and tone (Default / Professional / Friendly / Candid / Quirky / Efficient / Nerdy / Cynical), which corresponds to Claude's response style concept.

**Memory** splits into two independently toggleable parts: Saved Memories (individually viewable and deletable) and Reference chat history (drawing on past conversations), at Settings → Personalization → Manage memories. This is one notable difference from Claude: ChatGPT's memory accumulates automatically on the account side. Its behavior differs from Claude's memory mechanism -- do not assume the semantics are the same.

**Projects** provide project-level instructions and files, with a project-only memory option (selectable only when the project is first created) to isolate that project's memory from the global pool. **Custom GPTs** assemble shareable customized assistants through the Configure tab's Instructions / Knowledge / Capabilities / Actions fields.

**Privacy opt-out**: Settings → Data Controls → "Improve the model for everyone" toggle. Individual conversations can use Temporary Chat to stay out of history. Team / Enterprise plans default to no training and can negotiate ZDR (zero data retention). Consumer tier defaults to "on" (conversations may be used to improve the model) -- you have to turn it off yourself.

**Codex** stores all settings in two file types: the `AGENTS.md` family holds project instructions; `config.toml` holds behavioral configuration in TOML format. How they are read and layered:

* **Project instructions (`AGENTS.md`)**: placed in the project root. Codex scans from the project root (typically the git root) down to the current working directory, concatenating every `AGENTS.md` found along the way. Entries closer to the current directory appear later and therefore override earlier ones. (At the same level, `AGENTS.override.md` is read before `AGENTS.md`; `project_doc_fallback_filenames` can specify alternate names.) A global copy at `~/.codex/AGENTS.md` applies to all your projects.
* **Behavioral configuration (`config.toml`)**: precedence from high to low is IT-managed `requirements.toml` (enterprise-enforced constraints the user cannot override, such as prohibiting `approval_policy: never`) → CLI flags → project-local `.codex/config.toml` (**only loaded when the project is trusted**; an unknown cloned project does not get this layer read) → named profile (switched with `--profile <name>`) → `~/.codex/config.toml` applying to all projects. The entire `~/.codex` directory can be relocated with the `CODEX_HOME` environment variable.
* **Most commonly used keys**:
  * `model`: which model to use by default
  * `approval_policy`: when to pause and ask (`untrusted` asks at every step, `on-request` asks when appropriate, `never` never asks; a `granular` object allows finer control)
  * `sandbox_mode`: what the agent can touch (`read-only`, `workspace-write` for writes within the project, `danger-full-access` for unrestricted access)
  * `[mcp_servers.<id>]`: connect MCP servers
  * `[hooks]` (stable, enabled by default; the old `codex_hooks` toggle name is deprecated -- to disable, set `hooks = false` under `[features]`): lifecycle hooks
* **Billing**: either log in with a ChatGPT account (which includes a 5-hour usage window) or supply your own API key (billed per token).

### Codex configuration directory (`~/.codex/`)

<DirExplorer
  lang="en"
  scopes={[
{
  id: "global",
  label: "Global ~/.codex",
  tree: [
    {
      name: "~/.codex/",
      kind: "dir",
      color: "default",
      children: [
        {
          name: "config.toml",
          kind: "file",
          color: "gray",
          badge: "local",
          detail: {
            breadcrumb: "~/.codex/ > config.toml",
            title: "Global behavioral configuration",
            tagline: "Default values for all projects; lowest precedence",
            whenItLoads: "Loaded on every Codex startup. Can be overridden by a profile, a project .codex/config.toml, or CLI flags.",
            description: "TOML format. Common keys: model (default model), approval_policy (untrusted / on-request / never / granular), sandbox_mode (read-only / workspace-write / danger-full-access), [mcp_servers.<id>], [hooks] (stable). Enterprises add an IT-managed requirements.toml above this layer that users cannot modify.",
            mapsTo: "Maps to .claude/: ~/.claude/settings.json (global behavior)"
          }
        },
        {
          name: "AGENTS.md",
          kind: "file",
          color: "blue",
          badge: "local",
          detail: {
            breadcrumb: "~/.codex/ > AGENTS.md",
            title: "Global project instructions",
            tagline: "Baseline rules that apply to all your projects",
            whenItLoads: "Read first at every Codex session startup; lowest precedence (overridden by project layer). If AGENTS.override.md exists in the same directory it takes priority.",
            description: "Plain Markdown, no enforced schema. Put instructions that apply to every project: coding preferences, language, prohibited actions. Project-layer AGENTS.md files closer to the working directory take precedence; all files are concatenated into one instruction chain.",
            mapsTo: "Maps to .claude/: ~/.claude/CLAUDE.md (global personalization)"
          }
        },
        {
          name: "prompts/",
          kind: "dir",
          color: "yellow",
          badge: "local",
          children: [
            {
              name: "draft-pr.md",
              kind: "file",
              color: "yellow",
              detail: {
                breadcrumb: "~/.codex/prompts/ > draft-pr.md",
                title: "Custom prompt (deprecated)",
                tagline: "Invoked as /prompts:draft-pr; official docs recommend migrating to skills",
                whenItLoads: "Expanded when /prompts:<filename> is typed; placeholders such as $1 / $ARGUMENTS are substituted before the prompt is sent into the session.",
                description: "YAML frontmatter supports description (shown in slash menu) and argument-hint (parameter documentation). Marked deprecated in 2026 official docs; recommended replacement is skills (~/.codex/skills/ or .codex/skills/). Codex only scans top-level .md files; subdirectories are ignored.",
                example: {
                  filename: "draft-pr.md",
                  code: `---\ndescription: Draft a pull request description\nargument-hint: [TITLE=<title>]\n---\nWrite a PR description for $TITLE based on recent commits.`
                },
                mapsTo: "Maps to .claude/: .claude/skills/<name>/SKILL.md (invokable skills)"
              }
            },
            {
              name: "*.md",
              kind: "file",
              color: "yellow",
              detail: {
                breadcrumb: "~/.codex/prompts/ > *.md",
                title: "Additional custom prompt files",
                tagline: "Each .md file becomes a separate /prompts:<name> slash command",
                whenItLoads: "Triggered on invocation",
                description: "Each .md appears as a separate item in the slash command menu. Deprecated; official recommendation is to migrate to skills. Existing prompts continue to work.",
                mapsTo: "Maps to .claude/: .claude/skills/<name>/SKILL.md"
              }
            }
          ]
        },
        {
          name: "<profile>.config.toml",
          kind: "file",
          color: "gray",
          badge: "local",
          detail: {
            breadcrumb: "~/.codex/ > <profile>.config.toml",
            title: "Named profile configuration",
            tagline: "Selected with --profile <name>; overrides global config.toml",
            whenItLoads: "Loaded when --profile <name> is passed on the CLI. Precedence: higher than ~/.codex/config.toml, lower than project .codex/config.toml.",
            description: "Used to switch model, approval_policy, and sandbox_mode between contexts (personal / work / experiments) without touching the global defaults.",
            mapsTo: "Maps to .claude/: no direct equivalent (.claude/settings.local.json serves a similar personal-override purpose)"
          }
        },
        {
          name: "threads/",
          kind: "dir",
          color: "gray",
          badge: "autogen",
          detail: {
            breadcrumb: "~/.codex/ > threads/",
            title: "Conversation history cache",
            tagline: "Managed automatically by Codex; do not modify manually",
            whenItLoads: "Written automatically; can be disabled with --no-history",
            description: "Stores thread records for each chat session. The path can be changed via history.log_dir in config.toml.",
            mapsTo: "Maps to .claude/: no equivalent (Claude has no local history cache)"
          }
        }
      ]
    }
  ]
},
{
  id: "project",
  label: "Project .codex",
  tree: [
    {
      name: ".codex/",
      kind: "dir",
      color: "default",
      children: [
        {
          name: "config.toml",
          kind: "file",
          color: "gray",
          badge: "committed",
          detail: {
            breadcrumb: ".codex/ > config.toml",
            title: "Project behavioral configuration",
            tagline: "Only loaded when Codex trusts this project",
            whenItLoads: "Loaded when entering a trusted project. Precedence: higher than global config.toml and profiles, lower than CLI flags. Not read for unknown cloned repos.",
            description: "Same format as the global config.toml. Used to pin the repo's model, sandbox_mode, mcp_servers, etc. Committing it to version control lets all team members share the same settings.",
            mapsTo: "Maps to .claude/: .claude/settings.json (shared project settings)"
          }
        }
      ]
    },
    {
      name: "AGENTS.md",
      kind: "file",
      color: "blue",
      badge: "committed",
      detail: {
        breadcrumb: "(repo root) > AGENTS.md",
        title: "Project rules entry point (cross-tool baseline)",
        tagline: "Placed at the repo root; natively shared by Codex / Cursor / Copilot / Gemini CLI",
        whenItLoads: "Codex scans from git root down to cwd, concatenating at most one file per directory. AGENTS.override.md at the same level takes priority. Files closer to cwd are read later and take precedence.",
        description: "Cross-tool shared baseline. Claude Code does not read it natively; import it with @AGENTS.md inside CLAUDE.md. Put rules that apply to all tools in this repo; tool-specific differences go in each tool's proprietary file (e.g., .codex/config.toml).",
        mapsTo: "Maps to .claude/: CLAUDE.md (must import with @AGENTS.md)"
      }
    },
    {
      name: "AGENTS.override.md",
      kind: "file",
      color: "purple",
      badge: "committed",
      detail: {
        breadcrumb: "(repo root) > AGENTS.override.md",
        title: "Forced override rules",
        tagline: "Takes priority over AGENTS.md at the same level; highest precedence",
        whenItLoads: "Codex checks for AGENTS.override.md first at each directory level. If found it is used and the same-level AGENTS.md is skipped.",
        description: "For special contexts (CI environments, specific feature directories) that need to forcibly override global rules. Most repos do not need this; create it only when necessary.",
        mapsTo: "Maps to .claude/: .claude/rules/*.md (path-scoped frontmatter limits scope)"
      }
    }
  ]
}
]}
/>

## 3. Google: Gemini (consumer) and Antigravity (developer platform)

Same two-track structure. Gemini is account plus GUI; Antigravity is files plus IDE/CLI.

**Gemini personalization** uses Gems (custom personas with Name / Instructions / Knowledge fields, connectable to Drive and NotebookLM). The account-side Personal context (Settings) contains three parts: Memory (learns from past conversations), Instructions for Gemini (standing instructions, corresponding to Claude's Instructions for Claude), and Connected Apps (Gmail / Calendar, etc.).

**Privacy opt-out**: the "Keep Activity" toggle (defaulting to on for users 18 and over), with auto-delete periods of 3 / 18 (default) / 36 months available; turning it off stops use for training. Workspace accounts are admin-controlled and personal toggles may be overridden by organizational policy.

**Antigravity** is Google's agentic development platform covering four surfaces under one brand: desktop app (Antigravity 2.0), IDE, CLI (`agy`), and SDK, each with a different configuration surface. The base model is Gemini 3.5 Flash (as of 2026-06, \[9]). It is a different product from Gemini CLI but shares a global rule file at `~/.gemini/GEMINI.md` (see the conflict warning at the end of this section). Rules are divided into two layers:

1. **Global rules**: `~/.gemini/GEMINI.md`, applying to all workspaces.
2. **Workspace rules**: `.agents/rules/*.md` inside the project (note the plural `.agents/`, which is the current official default; the old singular `.agent/rules/` remains backward-compatible). The workspace `GEMINI.md` and `AGENTS.md` are also read. Rules are plain Markdown with four activation modes: Manual / Always On / Model Decision / Glob. The strict priority order between `GEMINI.md` and `AGENTS.md` is not declared on the official page.

Placement varies by surface (\[9], per `antigravity.google/docs`):

* **Skills**: shared across all tools at `~/.gemini/skills/<name>/SKILL.md`; CLI-only global at `~/.gemini/antigravity-cli/skills/`; workspace always at `.agents/skills/` (backward-compatible with `.agent/skills/`).
* **MCP**: official config at `~/.gemini/config/mcp_config.json` (shared across IDE and CLI globally); CLI workspace can use `.agents/mcp_config.json`.
* **Hooks**: `hooks.json` in `.agents/` (workspace) or `~/.gemini/config/` (global); events include `PreToolUse` / `PostToolUse` / `PreInvocation` / `PostInvocation` / `Stop`.
* **CLI (`agy`) settings**: `~/.gemini/antigravity-cli/settings.json` and `keybindings.json`.
* **Workflows**: invoked as `/workflow-name`, created through the IDE Customizations panel; the exact on-disk path is subject to Antigravity official documentation.

**Antigravity privacy opt-out**: the Enable Telemetry toggle (Settings → Account) is a different switch in a different product from the Gemini consumer "Keep Activity" toggle above -- do not conflate them.

<Warning>
  **Antigravity rule directory has been renamed to plural; verify against your version**

  The current official default is plural `.agents/` (rules / skills / mcp\_config.json all live there). The old singular `.agent/` is still backward-compatible but is not the default. Early third-party articles and older versions of this knowledge base incorrectly recorded "official uses singular" -- this has been corrected to follow the official page. `~/.gemini/GEMINI.md` is known to conflict when both Gemini CLI and Antigravity write to it (GitHub issue #16058, closed as not planned). The Antigravity official site `antigravity.google/docs` is a client-side-rendered SPA and its content cannot be retrieved by ordinary crawling. The facts in this section are based on first-hand official page content obtained via playwright rendering (as of 2026-06). Always treat the official docs for your installed version as the final authority.
</Warning>

### Antigravity configuration directory (`~/.gemini/`)

<DirExplorer
  lang="en"
  scopes={[
{
  id: "global",
  label: "Global ~/.gemini",
  tree: [
    {
      name: "~/.gemini/",
      kind: "dir",
      color: "default",
      children: [
        {
          name: "GEMINI.md",
          kind: "file",
          color: "blue",
          badge: "local",
          detail: {
            breadcrumb: "~/.gemini/ > GEMINI.md",
            title: "Global rules (shared by Antigravity and Gemini CLI)",
            tagline: "Applies to all workspaces; shared write access with Gemini CLI creates conflict risk",
            whenItLoads: "Loaded at every Antigravity or Gemini CLI session startup. Both tools are known to overwrite this file (GitHub issue #16058, closed as not planned).",
            description: "Plain Markdown; put AI behavior guidance that applies to all projects. Because both tools share it, mutual interference is possible. Keep content conservative (no tool-specific commands) and push tool-specific rules down to the workspace layer.",
            mapsTo: "Maps to .claude/: ~/.claude/CLAUDE.md (global personalization)"
          }
        },
        {
          name: "config/",
          kind: "dir",
          color: "gray",
          children: [
            {
              name: "mcp_config.json",
              kind: "file",
              color: "purple",
              badge: "local",
              detail: {
                breadcrumb: "~/.gemini/config/ > mcp_config.json",
                title: "Global MCP configuration (shared across IDE and CLI)",
                tagline: "Single MCP server list shared by all Antigravity surfaces",
                whenItLoads: "Read by all Antigravity surfaces (IDE, CLI, desktop app) at startup. Workspace .agents/mcp_config.json can add or override individual servers.",
                description: "JSON format, top-level mcpServers object, each key is a server id with command, args, and env. Official recommendation is to put all cross-tool MCP config here rather than in product-specific subdirectories.",
                mapsTo: "Maps to .claude/: ~/.claude.json (local scope MCP) or .mcp.json (project MCP)"
              }
            },
            {
              name: "plugins/",
              kind: "dir",
              color: "gray",
              badge: "autogen",
              detail: {
                breadcrumb: "~/.gemini/config/ > plugins/",
                title: "Global plugin configuration",
                tagline: "Global settings directory for IDE plugins",
                whenItLoads: "Loaded at IDE startup",
                description: "Stores IDE plugin-related configuration. Exact format follows Antigravity official documentation (as of 2026-06).",
                mapsTo: "Maps to .claude/: no direct equivalent"
              }
            }
          ]
        },
        {
          name: "skills/",
          kind: "dir",
          color: "yellow",
          children: [
            {
              name: "<skill-name>/",
              kind: "dir",
              color: "yellow",
              detail: {
                breadcrumb: "~/.gemini/skills/ > <skill-name>/",
                title: "Cross-tool global skill (all Antigravity surfaces)",
                tagline: "Available in IDE, CLI, and desktop app",
                whenItLoads: "Scanned by any Antigravity surface at startup; broader scope than antigravity-cli/skills/",
                description: "One subdirectory per skill, containing SKILL.md. This is the widest global skill location; both IDE and CLI can read it. Use antigravity-cli/skills/ for CLI-only skills.",
                mapsTo: "Maps to .claude/: ~/.claude/skills/<name>/SKILL.md (global skills)"
              }
            },
            {
              name: "SKILL.md (example)",
              kind: "file",
              color: "yellow",
              detail: {
                breadcrumb: "~/.gemini/skills/<skill-name>/ > SKILL.md",
                title: "Skill definition file",
                tagline: "Invoked as /skill-name",
                whenItLoads: "Triggered on invocation, or injected automatically in Always On mode",
                description: "Plain Markdown defining the skill's description and instruction content. Supports four activation modes (set in SKILL.md frontmatter): Manual / Always On / Model Decision / Glob.",
                mapsTo: "Maps to .claude/: .claude/skills/<name>/SKILL.md"
              }
            }
          ]
        },
        {
          name: "antigravity-cli/",
          kind: "dir",
          color: "gray",
          children: [
            {
              name: "settings.json",
              kind: "file",
              color: "gray",
              badge: "local",
              detail: {
                breadcrumb: "~/.gemini/antigravity-cli/ > settings.json",
                title: "CLI (agy) global settings",
                tagline: "Applies to agy CLI only; does not affect the IDE",
                whenItLoads: "Loaded when agy CLI starts",
                description: "JSON format; controls CLI behavior: default model, telemetry, output format, and similar settings. Kept separate from IDE settings to avoid mutual interference.",
                mapsTo: "Maps to .claude/: ~/.claude/settings.json (global settings)"
              }
            },
            {
              name: "skills/",
              kind: "dir",
              color: "yellow",
              children: [
                {
                  name: "<skill-name>/",
                  kind: "dir",
                  color: "yellow",
                  detail: {
                    breadcrumb: "~/.gemini/antigravity-cli/skills/ > <skill-name>/",
                    title: "CLI-only global skill",
                    tagline: "Invokable from agy CLI only; not read by the IDE",
                    whenItLoads: "Scanned when agy CLI starts",
                    description: "Narrower scope than ~/.gemini/skills/; only CLI sessions can see these. Use for CLI-specific automation or skills you do not want to expose to the IDE.",
                    mapsTo: "Maps to .claude/: ~/.claude/skills/<name>/SKILL.md"
                  }
                }
              ]
            }
          ]
        }
      ]
    }
  ]
},
{
  id: "workspace",
  label: "Workspace .agents",
  tree: [
    {
      name: ".agents/",
      kind: "dir",
      color: "default",
      children: [
        {
          name: "rules/",
          kind: "dir",
          color: "purple",
          children: [
            {
              name: "*.md",
              kind: "file",
              color: "purple",
              badge: "committed",
              detail: {
                breadcrumb: ".agents/rules/ > *.md",
                title: "Workspace rule files (primary entry point)",
                tagline: "Plain Markdown; four activation modes: Manual / Always On / Model Decision / Glob",
                whenItLoads: "Determined by frontmatter activation mode: Always On injects automatically each session; Manual requires explicit invocation; Model Decision is left to the model; Glob triggers on matching file paths.",
                description: "Current official default directory is plural .agents/ (old singular .agent/ remains backward-compatible). Each .md is a separate rule; frontmatter sets the activation mode. This finer-grained triggering is one of Antigravity's distinguishing features.",
                mapsTo: "Maps to .claude/: .claude/rules/*.md (path-scoped frontmatter controls scope)"
              }
            }
          ]
        },
        {
          name: "GEMINI.md",
          kind: "file",
          color: "blue",
          badge: "committed",
          detail: {
            breadcrumb: ".agents/ > GEMINI.md",
            title: "Workspace Antigravity-specific rules",
            tagline: "Supplements or overrides global ~/.gemini/GEMINI.md",
            whenItLoads: "Loaded when entering this workspace; higher precedence than global GEMINI.md",
            description: "Workspace-layer Antigravity rules for instructions that are repo-specific rather than globally applicable. Strict priority ordering against AGENTS.md is not declared by the official page; avoid contradictions between them.",
            mapsTo: "Maps to .claude/: CLAUDE.md (project layer)"
          }
        },
        {
          name: "AGENTS.md",
          kind: "file",
          color: "blue",
          badge: "committed",
          detail: {
            breadcrumb: ".agents/ > AGENTS.md (or repo root)",
            title: "Cross-tool baseline rules",
            tagline: "Shared natively by Antigravity / Codex / Cursor / Copilot",
            whenItLoads: "Antigravity reads AGENTS.md under .agents/ and at the repo root",
            description: "Open-standard cross-tool baseline. Claude Code does not support it natively; import it with @AGENTS.md inside CLAUDE.md.",
            mapsTo: "Maps to .claude/: CLAUDE.md (must import with @AGENTS.md)"
          }
        },
        {
          name: "mcp_config.json",
          kind: "file",
          color: "purple",
          badge: "committed",
          detail: {
            breadcrumb: ".agents/ > mcp_config.json",
            title: "Workspace MCP configuration (CLI)",
            tagline: "Supplements global ~/.gemini/config/mcp_config.json; applies to CLI workspace only",
            whenItLoads: "Loaded when entering an agy CLI session in this workspace",
            description: "JSON format identical to the global mcp_config.json (mcpServers object). The workspace layer can add or override specific servers without affecting global settings.",
            mapsTo: "Maps to .claude/: .mcp.json (project MCP)"
          }
        },
        {
          name: "hooks.json",
          kind: "file",
          color: "rose",
          badge: "committed",
          detail: {
            breadcrumb: ".agents/ > hooks.json",
            title: "Workspace lifecycle hooks",
            tagline: "PreToolUse / PostToolUse / PreInvocation / PostInvocation / Stop",
            whenItLoads: "Loaded when entering an Antigravity session in this workspace",
            description: "JSON format; each hook event maps to a command to execute. Supports five events: PreToolUse, PostToolUse, PreInvocation, PostInvocation, Stop. Global hooks go in ~/.gemini/config/hooks.json.",
            mapsTo: "Maps to .claude/: .claude/settings.json hooks block"
          }
        },
        {
          name: "skills/",
          kind: "dir",
          color: "yellow",
          children: [
            {
              name: "<skill-name>/",
              kind: "dir",
              color: "yellow",
              detail: {
                breadcrumb: ".agents/skills/ > <skill-name>/",
                title: "Workspace skill",
                tagline: "Available in this workspace only; backward-compatible with .agent/skills/",
                whenItLoads: "Scanned when entering an Antigravity session in this workspace",
                description: "Narrowest scope, highest precedence among skills. A workspace skill with the same name as a global skill overrides it. Commit to version control for team sharing.",
                mapsTo: "Maps to .claude/: .claude/skills/<name>/SKILL.md (project skills)"
              }
            }
          ]
        }
      ]
    }
  ]
}
]}
/>

## 4. GitHub Copilot CLI (the new standalone `copilot` binary)

<Note>
  **Identity clarification**

  This section covers only the new standalone local `copilot` CLI (npm `@github/copilot`), which is a separate product line from the older `gh copilot` extension (which only handles suggest / explain). VS Code's Copilot Chat and the local CLI **do not share a configuration mechanism**: the CLI reads `~/.copilot/`, while VS Code Copilot Chat reads `chat.*` workspace settings (such as `chat.instructionsFilesLocations`), with different file paths and loading behavior. VS Code Copilot Chat is not covered in this section.
</Note>

Copilot CLI's configuration surface is almost entirely file-based and is carved up by scope with unusually fine granularity (\[3]). The default root is `~/.copilot/`, overridable with `COPILOT_HOME` or `--config-dir`; `COPILOT_CACHE_HOME` controls the cache separately.

* **Local instructions**: `~/.copilot/copilot-instructions.md` (single file at the `~/.copilot/` root), effective for all sessions on that machine.
* **Repository-wide instructions**: `.github/copilot-instructions.md`, applied across the whole repo.
* **Path-specific instructions**: `.github/instructions/NAME.instructions.md`, with frontmatter using `applyTo` as a glob to control scope, and optional `excludeAgent`. This is where Copilot is more granular than most tools: rules can be bound precisely to specific paths.
* **Agent instruction layer**: root `AGENTS.md` (primary), `AGENTS.md` files in cwd or in directories listed in `COPILOT_CUSTOM_INSTRUCTIONS_DIRS` (additional), and root `CLAUDE.md` / `GEMINI.md` as `AGENTS.md` alternatives. **All matching instruction files are used together with no strict priority order; Copilot's resolution of conflicts is non-deterministic** -- avoid contradictions.
* **Automation**:
  * Personal / project agents: user layer `~/.copilot/agents/<name>.agent.md`; project layer `.github/agents/<name>.agent.md` (same name at project level overrides user level).
  * Personal / project skills: user layer `~/.copilot/skills/<n>/SKILL.md`; project layer `.github/skills/<n>/SKILL.md` (same name at project level overrides user level).
  * Hooks: user layer `~/.copilot/hooks/` or inline in `config.json`; project layer `.github/hooks/`; both layers are **merged** (not overriding).
* **MCP**: user layer `~/.copilot/mcp-config.json`; project layer `.mcp.json` / `.github/mcp.json` / `.vscode/mcp.json` (project layer overrides user layer when names match).
* **Cloud Coding agent**: tasks dispatched via Issue or `@copilot` are a separate product line whose execution environment is configured by `.github/workflows/copilot-setup-steps.yml`. The local CLI does not use this path.

## 5. Cursor (brief mention)

Cursor is a third-party IDE (developed by Anysphere, not a model vendor). Rule format is `.cursor/rules/*.mdc` (note: plain `.md` files are ignored -- it must be `.mdc`). It also reads `AGENTS.md` as a simplified alternative. The older `.cursorrules` has been marked for deprecation. For detailed format and ignore-file differences see Cursor's official documentation \[4]; this Playbook does not go deeper.

## 6. AGENTS.md: the shared rule file across tools

`AGENTS.md` is the open standard for converging each tool's proprietary rule files into one. It is a standard for the project rules layer, not the personalization layer. Treating it as the "shared baseline file" across tools is the framing least likely to cause misuse.

Key points:

* Plain Markdown with no enforced schema; placed at the repository root. Monorepos can have multiple levels: files closer to the file being edited take precedence.
* Adoption: Codex, Cursor, Copilot, Gemini CLI, Jules, Devin, Amp, VS Code, and more than twenty other tools support it natively; over 60,000 open-source projects have adopted it (as of 2026-06) \[5].
* Governance: coordinated by the Agentic AI Foundation (AAIF) under the Linux Foundation. AGENTS.md is one of three founding projects -- contributed by OpenAI; the other two are Anthropic's MCP and Block's goose \[6]. Standard site: `agents.md`.

How each tool reads `AGENTS.md`:

* **Native support**: Codex, Cursor, Copilot, Gemini CLI, and others treat `AGENTS.md` as the project rules entry point. Each also has its own proprietary file (`GEMINI.md`, `.cursor/rules/`, `.github/copilot-instructions.md`) for tool-specific overrides or additions.
* **No native support**: Claude Code. `AGENTS.md` is not on its read list. To use the same baseline, add `@AGENTS.md` inside `CLAUDE.md` to import it \[8]. Claude's "proprietary file" is `CLAUDE.md` itself -- it is the only entry point Claude Code reads.

`AGENTS.md` is the baseline, not the replacement: tool-specific rules still live in each tool's proprietary file. For a concrete walkthrough of extracting a baseline and validating shared reads, see Section 7.

## Cross-tool comparison table (as of 2026-06)

Rows are concepts; columns are tools; Claude column is first. All four main tools use CLI as the comparison axis. The OpenAI column uses Codex as primary with ChatGPT as supplementary; the Google column uses Antigravity as primary with Gemini as supplementary. **Antigravity is primarily an IDE interface (Electron / VS Code-derived); CLI pairings are concept-level only** -- for exact file names and paths see Section 3. Cursor is a third-party IDE listed with brief notes only.

<ToolCompare
  lang="en"
  tools={[
{ id: "claude", label: "Claude Code" },
{ id: "codex",  label: "OpenAI Codex" },
{ id: "agy",    label: "Google Antigravity" },
{ id: "copilot",label: "GitHub Copilot CLI" },
{ id: "cursor", label: "Cursor" }
]}
  dimensions={[
{
  id: "personalization",
  label: "Personalization",
  cells: {
    claude:  { value: "Instructions for Claude / ~/.claude/CLAUDE.md", detail: "GUI side: claude.ai Settings -> Instructions for Claude. CLI side: reads ~/.claude/CLAUDE.md, applies to all projects.", recommend: true },
    codex:   { value: "~/.codex/AGENTS.md", detail: "Semantically 'global project instructions' rather than account-side personalization. There is no separate account personalization field; personal preferences go into the global AGENTS.md." },
    agy:     { value: "~/.gemini/GEMINI.md", detail: "Global rule file read by both Antigravity and Gemini CLI (shared file; known conflict risk). Gemini consumer personalization uses Settings -> Personal context -> Instructions for Gemini, a different product's separate entry point." },
    copilot: { value: "~/.copilot/copilot-instructions.md", detail: "Single file at the ~/.copilot/ root; applies to all sessions on that machine. Relocatable via COPILOT_HOME or --config-dir." },
    cursor:  { value: "User Rules (GUI)", detail: "Cursor Settings -> Rules for AI -> User Rules; pure GUI setting with no corresponding local config file." }
  }
},
{
  id: "project_rules",
  label: "Project rules",
  cells: {
    claude:  { value: "CLAUDE.md + .claude/rules/", detail: "Repo-root CLAUDE.md is the main entry point. .claude/rules/*.md uses path-scoped frontmatter to limit scope. Only CLAUDE.md (and its @imports) is on the read list; AGENTS.md is not read natively.", recommend: true },
    codex:   { value: "AGENTS.md + .codex/config.toml", detail: "AGENTS.md is the rule entry point (concatenated from git root down to cwd; AGENTS.override.md at the same level takes priority). .codex/config.toml is behavioral config (loaded only for trusted projects)." },
    agy:     { value: ".agents/rules/*.md + GEMINI.md + AGENTS.md", detail: "Primary entry point is .agents/rules/*.md (four activation modes). Workspace GEMINI.md and AGENTS.md are also read. Current official default is plural .agents/ (old .agent/ is backward-compatible)." },
    copilot: { value: ".github/copilot-instructions.md + *.instructions.md", detail: "Repo-wide rules: .github/copilot-instructions.md. Path-specific: .github/instructions/NAME.instructions.md (frontmatter applyTo glob). Root AGENTS.md / CLAUDE.md / GEMINI.md are also read, but priority order is non-deterministic." },
    cursor:  { value: ".cursor/rules/*.mdc", detail: "File extension must be .mdc (plain .md is ignored). Reads repo root AGENTS.md as a simplified alternative. Older .cursorrules is marked for deprecation." }
  }
},
{
  id: "memory",
  label: "Memory",
  cells: {
    claude:  { value: "Auto memory / MEMORY.md", detail: "Account-side auto memory (manageable in Settings). CLI side uses MEMORY.md or @imports for cross-session persistence.", recommend: true },
    codex:   { value: "(no dedicated memory)", detail: "No built-in memory mechanism. Global ~/.codex/AGENTS.md or project AGENTS.md acts as persistent standing instructions. Cross-session persistence requires an external MCP tool." },
    agy:     { value: "Memory (account-side)", detail: "Gemini consumer has Settings -> Personalization -> Memory (auto-learns from conversations; individually manageable). Whether Antigravity developer platform shares this memory is not officially confirmed." },
    copilot: { value: "(no dedicated memory)", detail: "No dedicated memory mechanism. Instruction files (e.g., ~/.copilot/copilot-instructions.md) serve as persistent standing instructions." },
    cursor:  { value: "(rule files substitute)", detail: "No dedicated memory mechanism. .cursor/rules/*.mdc files serve as persistent rules." }
  }
},
{
  id: "privacy",
  label: "Privacy opt-out",
  cells: {
    claude:  { value: "Help Improve Claude", detail: "Settings -> Privacy -> Help improve Claude; turning it off opts out of training data use. The claude.ai consumer entry point and the API / Claude Code entry point are separate -- verify both.", recommend: true },
    codex:   { value: "Data Controls training toggle", detail: "ChatGPT Settings -> Data Controls -> Improve the model for everyone. Consumer tier defaults to on; you must turn it off yourself. Team / Enterprise plans default to no training and can negotiate ZDR (zero data retention)." },
    agy:     { value: "Enable Telemetry (Settings -> Account)", detail: "Antigravity developer platform telemetry toggle. Different product, different switch from Gemini consumer 'Keep Activity' -- do not conflate them. Gemini Keep Activity defaults to on (18+); auto-delete period selectable: 3 / 18 / 36 months." },
    copilot: { value: "Account / enterprise settings", detail: "github.com account Settings -> Copilot -> Policies. Organization-level policy set by admin. Enterprise plans can enforce no-retention and no-training policies." },
    cursor:  { value: "Account settings", detail: "cursor.com account Settings -> Privacy. Enterprise plans offer zero data retention. See Cursor official privacy policy for details." }
  }
},
{
  id: "automation",
  label: "Automation",
  cells: {
    claude:  { value: "Skills / Hooks / Commands", detail: "Skills: .claude/skills/<name>/SKILL.md (project) or ~/.claude/skills/ (global). Hooks: .claude/settings.json hooks block (PreToolUse / PostToolUse / Stop). Commands: /-prefixed slash commands.", recommend: true },
    codex:   { value: "config.toml [hooks] + prompts/*.md (deprecated)", detail: "[hooks] stable, placed in config.toml (global or .codex/config.toml). prompts/*.md is marked deprecated (official recommendation: migrate to skills). Skills mechanism see official docs (2026-06)." },
    agy:     { value: "Skills / Workflows / Hooks", detail: "Skills in three layers: cross-tool global ~/.gemini/skills/, CLI-only global ~/.gemini/antigravity-cli/skills/, workspace .agents/skills/. Workflows invoked as /workflow-name. Hooks via hooks.json (five events)." },
    copilot: { value: "Skills + Agents + Hooks (merged layers)", detail: "Skills: ~/.copilot/skills/<n>/SKILL.md (personal), .github/skills/<n>/SKILL.md (project). Agents: ~/.copilot/agents/<name>.agent.md / .github/agents/. Hooks: ~/.copilot/hooks/ and .github/hooks/ both merged (not overriding)." },
    cursor:  { value: ".mdc rules", detail: ".cursor/rules/*.mdc serves as both rules and the only automation mechanism. No separate skill / hook system." }
  }
},
{
  id: "mcp",
  label: "MCP",
  cells: {
    claude:  { value: ".mcp.json (project) / ~/.claude.json (global)", detail: "Project MCP: .mcp.json (committed to version control). Global MCP: ~/.claude.json local scope mcpServers field. Both use the mcpServers object format, key = server id.", recommend: true },
    codex:   { value: "config.toml [mcp_servers.<id>]", detail: "MCP config is embedded in config.toml (global ~/.codex/config.toml or project .codex/config.toml); no separate JSON file. Key format: [mcp_servers.my-server], supporting command / args / env." },
    agy:     { value: "~/.gemini/config/mcp_config.json (global) + .agents/mcp_config.json (workspace)", detail: "Official recommendation: put all global MCP config in ~/.gemini/config/mcp_config.json (shared across all Antigravity surfaces). Workspace additions go in .agents/mcp_config.json (CLI). JSON format, top-level mcpServers object." },
    copilot: { value: "~/.copilot/mcp-config.json + multiple project paths", detail: "User layer: ~/.copilot/mcp-config.json. Project layer supports three paths: .mcp.json / .github/mcp.json / .vscode/mcp.json (project layer overrides user layer when names match)." },
    cursor:  { value: ".cursor/mcp.json", detail: "Project layer: .cursor/mcp.json. Global config via Cursor Settings -> MCP (GUI). JSON format; mcpServers object same as other tools." }
  }
}
]}
  notes={[
"This table uses CLI as the comparison axis. The OpenAI column uses Codex as primary with ChatGPT as supplementary; the Google column uses Antigravity as primary with Gemini consumer as supplementary.",
"Antigravity is primarily an IDE interface; CLI (agy) pairings are concept-level only. For exact paths see Section 3 and the directory explorer above.",
"Cursor is a third-party IDE (not a model vendor); only key points are listed.",
"The 'Memory' row has different semantics per tool: Claude's auto memory, ChatGPT's Saved Memories, and Gemini's Memory differ in what they store, what triggers them, and whether they can be disabled. Do not assume semantic equivalence.",
"All paths as of 2026-06. Antigravity paths in particular change frequently; verify against your installed version's official docs before acting."
]}
/>

## Hands-on: fill in the six concepts for Claude, then map to another tool

Scenario: you use Claude and want to evaluate migrating to another tool, or running both in parallel. The approach has three steps: fill in Claude's six concept cells with concrete values as a source template; map each cell to the target tool; then extract the overlapping project rules into `AGENTS.md` so the other tool can read them too.

Step one: fill in the six cells from Section 1 with Claude's real settings. This becomes your mapping template.

<Note>
  **Claude six-concept fill-in: using Claude Code as the example**

  | Concept         | Claude's concrete location                                                      |
  | --------------- | ------------------------------------------------------------------------------- |
  | Personalization | Claude.ai's Instructions for Claude; CLI side `~/.claude/CLAUDE.md`             |
  | Project rules   | `./CLAUDE.md` + `.claude/rules/*.md`                                            |
  | Memory          | Auto memory / project `MEMORY.md`                                               |
  | Privacy opt-out | Settings -> Privacy -> Help improve Claude; turning it off opts out of training |
  | Automation      | `.claude/skills/`, hooks in `.claude/settings.json`, `/`-prefixed commands      |
  | MCP             | `.mcp.json` (project), `~/.claude.json` local scope                             |
</Note>

Step two: take that template and map each cell to the target tool. Using a migration to Codex as an example -- the concepts stay the same, only the file names and locations change.

<Note>
  **Claude to Codex cell-by-cell mapping**

  | Concept         | Claude                          | Codex                                                                                              |
  | --------------- | ------------------------------- | -------------------------------------------------------------------------------------------------- |
  | Personalization | `~/.claude/CLAUDE.md`           | `~/.codex/AGENTS.md` (semantically: global project instructions, not account-side personalization) |
  | Project rules   | `./CLAUDE.md`, `.claude/rules/` | `./AGENTS.md`, `.codex/config.toml` (trusted projects only)                                        |
  | Privacy opt-out | Help improve Claude             | Data Controls training toggle                                                                      |
  | Automation      | hooks / skills / commands       | `config.toml` `[hooks]` (stable), `prompts/*.md` (deprecated; migrate to skills), `[skills]`       |
  | MCP             | `.mcp.json`                     | `config.toml` `[mcp_servers.<id>]`                                                                 |

  After completing the mapping you will find only two things differ: Codex centralizes configuration in `config.toml`, while Claude uses `settings.json`.
</Note>

Stop there. The Claude entry point remains `CLAUDE.md` at all times -- you do not need to restructure it to enable cross-tool shared reads.

## Common pitfalls

* **Assuming same-named features behave identically.** Each tool's Memory is the biggest trap: Claude's memory, ChatGPT's Saved Memories, and Gemini's Memory differ in what they store, what triggers them, and whether they can be disabled. Seeing a familiar word and applying your old mental model will cause privacy and behavioral problems. Mapping the concept is fine; assuming the semantics are identical is not.
* **Maintaining multiple proprietary files on a tool that supports `AGENTS.md`.** Duplicate content drifts. Every copy ends up half-right and half-wrong. Decide which file is the baseline (usually `AGENTS.md`) and have the others point to it.
* **Confusing Cursor's two ignore files.** `.cursorignore` completely blocks AI access. `.cursorindexingignore` only excludes indexing -- files can still be read. Putting secrets in the wrong file means you think you've blocked access when you haven't.
* **Assuming consumer tiers default to no training.** ChatGPT and Gemini's consumer plans typically default to using conversations to improve the model. You have to opt out yourself. Do not apply enterprise plan defaults to personal accounts.
* **Copying fast-moving file names and paths verbatim.** Things like Antigravity's `.agent/` vs `.agents/`, or the location of Codex's `config.toml`, change with versions. This unit provides a mapping framework -- verify against your version's official docs before acting.

## Self-check

<Check>
  **Can you do the mapping?**

  1. Take the non-Claude tool you use most often and fill in all six concept cells from Section 1: where are personalization, project rules, memory, privacy opt-out, automation, and MCP configured? Which cell can you not fill?
  2. Is your tool's privacy opt-out switch on or off by default? What is the current state of your account?
  3. If you wanted Claude and that tool to share the same project rules, where would you put the baseline, and what would you keep in each tool's proprietary file?
  4. Does your repo contain two rule files with overlapping content? Are they consistent right now?
</Check>

## Sources and further reading

Factual claims are grounded in official documentation; fast-changing items are annotated as of 2026-06.

<div className="references">
  * \[1] OpenAI, "Config basics," "AGENTS.md guide," "Custom prompts," and Codex / ChatGPT docs, OpenAI. Accessed: 2026-06. \[Online]. Available: [https://developers.openai.com/codex/config-basic](https://developers.openai.com/codex/config-basic) , [https://developers.openai.com/codex/guides/agents-md](https://developers.openai.com/codex/guides/agents-md) , [https://developers.openai.com/codex/custom-prompts](https://developers.openai.com/codex/custom-prompts) , [https://developers.openai.com/codex/config-reference](https://developers.openai.com/codex/config-reference) , [https://developers.openai.com/codex](https://developers.openai.com/codex) , [https://help.openai.com](https://help.openai.com) , [https://openai.com/policies](https://openai.com/policies) . Codex's AGENTS.md search direction "git root down to cwd", \[hooks], and subagents confirmed stable, reverified 2026-06. prompts/\*.md confirmed deprecated (migrate to skills), also reverified 2026-06.

  * \[2] Google, "Gemini Apps Help & Antigravity / Gemini API docs," Google. Accessed: 2026-06. \[Online]. Available: [https://support.google.com/gemini](https://support.google.com/gemini) , [https://developers.googleblog.com](https://developers.googleblog.com) , [https://ai.google.dev](https://ai.google.dev)

  * \[3] GitHub, "Copilot docs & VS Code Copilot customization," GitHub / Microsoft. Accessed: 2026-06. \[Online]. Available: [https://docs.github.com/copilot](https://docs.github.com/copilot) , [https://code.visualstudio.com/docs/copilot](https://code.visualstudio.com/docs/copilot)

  * \[4] Anysphere, "Cursor docs (Rules, Ignore files, MCP)," Cursor. Accessed: 2026-06. \[Online]. Available: [https://cursor.com/docs](https://cursor.com/docs)

  * \[5] "AGENTS.md: open standard for agent instructions," agents.md. Accessed: 2026-06. \[Online]. Available: [https://agents.md](https://agents.md)

  * \[6] Linux Foundation, "Linux Foundation Announces the Formation of the Agentic AI Foundation (AAIF), Anchored by MCP, goose and AGENTS.md," 2026. \[Online]. Available: [https://www.linuxfoundation.org/press/linux-foundation-announces-the-formation-of-the-agentic-ai-foundation](https://www.linuxfoundation.org/press/linux-foundation-announces-the-formation-of-the-agentic-ai-foundation)

  * \[7] "Antigravity Global Rules and Gemini CLI Global Context Both Write to `~/.gemini/GEMINI.md` Causing Configuration Conflicts," GitHub issue #16058, google-gemini/gemini-cli, 2026. \[Online]. Available: [https://github.com/google-gemini/gemini-cli/issues/16058](https://github.com/google-gemini/gemini-cli/issues/16058) (Antigravity rule directory and global file conflict details; refer to current official documentation)

  * \[8] Anthropic, "How Claude remembers your project" (CLAUDE.md imports other files with @path syntax; AGENTS.md shared-read example is @AGENTS.md), Claude Code Docs. Accessed: 2026-06. \[Online]. Available: [https://code.claude.com/docs/en/memory](https://code.claude.com/docs/en/memory)

  * \[9] Google, "Antigravity Documentation" (Settings, Rules & Workflows, Skills, MCP, Hooks, CLI Settings, Gemini CLI Migration pages; content obtained via playwright rendering of first-hand official pages; this knowledge base no longer uses prior raw-compiled files), Antigravity Docs. Accessed: 2026-06. \[Online]. Available: [https://antigravity.google/docs/rules-workflows](https://antigravity.google/docs/rules-workflows) , [https://antigravity.google/docs/skills](https://antigravity.google/docs/skills) , [https://antigravity.google/docs/mcp](https://antigravity.google/docs/mcp) , [https://antigravity.google/docs/hooks](https://antigravity.google/docs/hooks) , [https://antigravity.google/docs/settings](https://antigravity.google/docs/settings) , [https://antigravity.google/docs/cli-settings](https://antigravity.google/docs/cli-settings) , [https://antigravity.google/docs/gcli-migration](https://antigravity.google/docs/gcli-migration) ; supplemented by Google Codelabs [https://codelabs.developers.google.com/getting-started-google-antigravity](https://codelabs.developers.google.com/getting-started-google-antigravity)

  * \[10] D. Lester, "Configuring MCP Servers and Skills for Antigravity CLI and IDE," Google Cloud Community / Medium. Accessed: 2026-06. \[Online]. Available: [https://medium.com/google-cloud/configuring-mcp-servers-and-skills-for-antigravity-cli-and-ide-a938c7eebb78](https://medium.com/google-cloud/configuring-mcp-servers-and-skills-for-antigravity-cli-and-ide-a938c7eebb78) (\~/.gemini/ directory hands-on structure, including skills/ cross-tool vs antigravity-cli/skills/ CLI-only distinction)
</div>

* Related: [02-2 Anthropic Claude configuration: Chat, Cowork, Code](/en/code-agent/configuration/anthropic-claude-setup); layer model at [02-1 The Configuration Layer Model](/en/code-agent/configuration/config-layer-model).
