> ## 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 其他工具設定對照

> 以 Claude 為基準，把 OpenAI Codex、Google Antigravity、GitHub Copilot CLI、Cursor 的設定面對位到 02-1 的層級模型：個人化、隱私退出、專案規則、自動化、記憶、MCP 各在哪設，換工具查表，不必重學。

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="zh"
  items={[
"各家 Memory 機制完全不同，套舊心智模型就誤判隱私邊界",
"同時維護五份規則檔，三個月後沒有一份是對的",
"消費端預設都拿你的對話訓練，沒主動關就是預設開",
"AGENTS.md 是基線，不是把所有工具差異一鍋煮",
"你把 .cursorignore 和 .cursorindexingignore 混淆，機密外洩",
"Claude Code 不讀 AGENTS.md，要 @AGENTS.md import 進來",
"照抄三方部落格的設定路徑，版本一變就全錯",
]}
/>

<Info>
  **這個單元解決什麼問題**

  你不是在學四套工具，你在學同一組概念換四套名字。以 [02-2 的 Claude](/code-agent/configuration/anthropic-claude-setup) 為基準，把 OpenAI Codex、Google Antigravity、GitHub Copilot 的設定面對位到 [02-1 的層級模型](/code-agent/configuration/config-layer-model)：個人化在哪設、隱私退出叫什麼、專案規則檔是哪一個、自動化機制有沒有、記憶怎麼存、MCP 接在哪。Cursor 作為第三方 IDE，僅在末段短提一節作對照。對齊完，換工具就是查表，不必重學。

  **本頁與 [04-0 .claude 目錄完整參考](/code-agent/customization/claude-directory-reference) 的分工**：04-0 是 Claude 自家 `.claude/` 的完整參考；本頁以 Claude 為基準，展示跨工具的設定目錄對照。
</Info>

## 學習目標

* [ ] 能把任一工具的個人化、隱私、專案規則、自動化、記憶、MCP 對應到 [02-1 的層級模型](/code-agent/configuration/config-layer-model)。
* [ ] 能說出每個工具的專案級規則檔名與放置位置，並判斷它套用的範圍與優先序。
* [ ] 能找到每個工具的隱私 / 訓練退出開關，並知道預設是開還是關。
* [ ] 能用 `AGENTS.md` 讓一份專案規則被多個工具共讀，並說出它與各家專屬檔的取捨。

<Warning>
  **時效聲明**

  以下設定名稱、檔名與機制查證截至 2026-06，來源見文末。各工具改版頻繁，標「需以官方文件再核」者尤須在動手前重查。設定檔名與資料夾這類事實會隨版本變動，本單元給的是對位框架，不是可永久照抄的清單。
</Warning>

***

## 1. 先有框架，再看四套主軸工具

每套工具的設定面都繞著同六個概念轉。把它們先釘住，後面就是逐欄填空：

| 概念   | 它回答的問題         | 對應 02-1 層級  |
| ---- | -------------- | ----------- |
| 個人化  | 模型該怎麼跟「你」說話    | 使用者全域層      |
| 專案規則 | 在這個 repo 裡該守什麼 | 專案層         |
| 記憶   | 跨對話要記住什麼       | 使用者層（多為帳號側） |
| 隱私退出 | 你的對話會不會拿去訓練    | 帳號 / 組織層    |
| 自動化  | 可重用的指令、技能、觸發   | 專案層為主       |
| MCP  | 外部工具與資料怎麼接進來   | 專案 / 使用者層   |

判準很簡單：拿到一個新工具，先問這六格各在哪設、預設值是什麼。填得出來就掌握了它的設定面；填不出來的格子就是你還沒讀到的文件。下面四節按這六格逐一對位，差異處標出來；第五節短提 Cursor 作為對照。

## 2. OpenAI：ChatGPT（消費端）與 Codex（命令列）

OpenAI 的設定面分兩條線：ChatGPT 是帳號設定 + GUI，Codex 是檔案 + CLI，兩者概念對得上但介面完全不同。

**ChatGPT 的個人化**走 Settings → Personalization → Custom Instructions，兩個欄位（「關於你」與「希望如何回應」），需開啟 Enable customization 才生效。語氣另有 Base style and tone 下拉（Default / Professional / Friendly / Candid / Quirky / Efficient / Nerdy / Cynical），對應 Claude 的 response style 概念。

**記憶**分兩塊且可分別關閉：Saved Memories（可逐條檢視刪除）與 Reference chat history（跨對話引用歷史），路徑 Settings → Personalization → Manage memories。這是與 Claude 一個明顯差異點：ChatGPT 的記憶是帳號側自動累積，行為與 Claude 的記憶機制不同，別假設兩者語意一致。

**專案**（Projects）給專案級指令與檔案，可設 project-only memory（僅在建立專案時可選），把該專案的記憶與全域隔開。**Custom GPTs** 則用 Configure 分頁的 Instructions / Knowledge / Capabilities / Actions 四欄組裝可分享的客製助手。

**隱私退出**：Settings → Data Controls 的「Improve the model for everyone」開關；單次對話可用 Temporary Chat 不入歷史；Team / Enterprise 方案預設不訓練、可談 ZDR（zero data retention）。消費端預設是「開」（會用於改進模型），要退出得自己關。

**Codex** 把所有設定寫在兩種檔案：`AGENTS.md` 系列放專案指令，`config.toml` 放行為配置（TOML 格式）。怎麼讀、怎麼疊加：

* **專案指令（`AGENTS.md`）**：放在專案根目錄。Codex 從專案根（通常是 git root）向下掃到當前工作目錄，把沿途每一層的 `AGENTS.md` 串接進來，越靠近當前目錄的因為排在後面而覆寫前面的（同一層先讀 `AGENTS.override.md` 再讀 `AGENTS.md`，可用 `project_doc_fallback_filenames` 指定備用檔名）。也有全域一份在 `~/.codex/AGENTS.md`，套用到你所有專案。
* **行為配置（`config.toml`）**：誰能蓋誰，由高到低是 IT 管的 `requirements.toml`（企業強制約束，使用者改不動，例如禁止把 `approval_policy` 設成 `never`）→ CLI 帶的旗標 → 專案內 `.codex/config.toml`（**只在信任該專案時才生效**；clone 來的不明專案這層不會被讀）→ profile（具名設定組，以 `--profile <name>` 切換）→ `~/.codex/config.toml` 套所有專案。整個 `~/.codex` 目錄可用 `CODEX_HOME` 環境變數搬到別處。
* **幾個最常用的鍵**：
  * `model`：預設用哪個模型
  * `approval_policy`：何時停下來問你（`untrusted` 處處問、`on-request` 該問才問、`never` 都不問；想更細可用 `granular` 物件）
  * `sandbox_mode`：能動到哪裡（`read-only` 唯讀、`workspace-write` 專案內可寫、`danger-full-access` 全開）
  * `[mcp_servers.<id>]`：接 MCP 伺服器
  * `[hooks]`（stable，預設啟用；舊的 `codex_hooks` 開關名已棄用，要關改在 `[features]` 設 `hooks = false`）：生命週期掛鉤
* **計費**：登入 ChatGPT 帳號（含每 5 小時的用量時窗）或自備 API key（按 token 計）兩種。

### Codex 設定目錄（`~/.codex/`）

<DirExplorer
  lang="zh"
  scopes={[
{
  id: "global",
  label: "全域 ~/.codex",
  tree: [
    {
      name: "~/.codex/",
      kind: "dir",
      color: "default",
      children: [
        {
          name: "config.toml",
          kind: "file",
          color: "gray",
          badge: "local",
          detail: {
            breadcrumb: "~/.codex/ > config.toml",
            title: "全域行為配置",
            tagline: "所有專案的預設值，優先序最低",
            whenItLoads: "每次 Codex 啟動時載入，可被 profile、專案 .codex/config.toml 與 CLI 旗標覆寫",
            description: "TOML 格式。常用鍵：model（預設模型）、approval_policy（untrusted / on-request / never / granular）、sandbox_mode（read-only / workspace-write / danger-full-access）、[mcp_servers.<id>]、[hooks]（stable）。企業端在此層之上還有 IT 管控的 requirements.toml（使用者改不動）。",
            mapsTo: "對照 .claude/：~/.claude/settings.json（全域行為）"
          }
        },
        {
          name: "AGENTS.md",
          kind: "file",
          color: "blue",
          badge: "local",
          detail: {
            breadcrumb: "~/.codex/ > AGENTS.md",
            title: "全域專案指令",
            tagline: "套用到你所有專案的基線規則",
            whenItLoads: "每次 Codex session 啟動時最先讀，優先序最低（被專案層覆寫）。同目錄若存在 AGENTS.override.md 則以 override 優先。",
            description: "純 Markdown，無強制 schema。放「對我所有專案都成立」的指令：編碼偏好、語言、不得做的事。專案層的 AGENTS.md 越靠近工作目錄越優先，最後拼接成完整指令串。",
            mapsTo: "對照 .claude/：~/.claude/CLAUDE.md（全域個人化）"
          }
        },
        {
          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: "自訂提示（已棄用）",
                tagline: "以 /prompts:draft-pr 叫用；官方已建議改用 skills",
                whenItLoads: "輸入 /prompts:<檔名> 時展開，$1/$ARGUMENTS 等佔位符替換後送入 session",
                description: "YAML frontmatter 含 description（選單顯示）與 argument-hint（參數說明）。官方 2026 版文件已標記 deprecated，建議改用 skills（~/.codex/skills/ 或 .codex/skills/）。Codex 只掃頂層 .md 檔，子目錄忽略。",
                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: "對照 .claude/：.claude/skills/<name>/SKILL.md（可叫用技能）"
              }
            },
            {
              name: "*.md",
              kind: "file",
              color: "yellow",
              detail: {
                breadcrumb: "~/.codex/prompts/ > *.md",
                title: "其他自訂提示檔",
                tagline: "每個 .md 對應一個 /prompts:<name> 斜線指令",
                whenItLoads: "依叫用觸發",
                description: "每個 .md 在斜線選單獨立一項。deprecated 後官方推薦用 skills 取代，但現有 prompts 仍可運作。",
                mapsTo: "對照 .claude/：.claude/skills/<name>/SKILL.md"
              }
            }
          ]
        },
        {
          name: "<profile>.config.toml",
          kind: "file",
          color: "gray",
          badge: "local",
          detail: {
            breadcrumb: "~/.codex/ > <profile>.config.toml",
            title: "具名 profile 設定",
            tagline: "以 --profile <name> 切換，覆寫全域 config.toml",
            whenItLoads: "CLI 帶 --profile <name> 旗標時載入，優先序高於 ~/.codex/config.toml、低於專案 .codex/config.toml",
            description: "用於在不同場景（個人 / 工作 / 實驗）切換不同 model、approval_policy、sandbox_mode 設定，不需改動全域預設值。",
            mapsTo: "對照 .claude/：.claude/settings.json 無直接對應（Claude 以 settings.local.json 做個人覆寫）"
          }
        },
        {
          name: "threads/",
          kind: "dir",
          color: "gray",
          badge: "autogen",
          detail: {
            breadcrumb: "~/.codex/ > threads/",
            title: "對話歷史快取",
            tagline: "Codex 自動管理，勿手動修改",
            whenItLoads: "自動寫入，--no-history 旗標可停用",
            description: "每個 chat session 的 thread 記錄存於此。路徑可透過 config.toml 的 history.log_dir 指定別處。",
            mapsTo: "對照 .claude/：無直接對應（Claude 無本地歷史快取）"
          }
        }
      ]
    }
  ]
},
{
  id: "project",
  label: "專案 .codex",
  tree: [
    {
      name: ".codex/",
      kind: "dir",
      color: "default",
      children: [
        {
          name: "config.toml",
          kind: "file",
          color: "gray",
          badge: "committed",
          detail: {
            breadcrumb: ".codex/ > config.toml",
            title: "專案行為配置",
            tagline: "只在 Codex 信任此專案時才載入",
            whenItLoads: "進入 trusted 專案時載入，優先序高於全域 config.toml 與 profile，低於 CLI 旗標。clone 來的不明專案此層不被讀取。",
            description: "與全域 config.toml 格式相同，用於鎖定此 repo 的 model、sandbox_mode、mcp_servers 等。適合團隊共享 — 進版控讓所有成員使用相同設定。",
            mapsTo: "對照 .claude/：.claude/settings.json（專案共享設定）"
          }
        }
      ]
    },
    {
      name: "AGENTS.md",
      kind: "file",
      color: "blue",
      badge: "committed",
      detail: {
        breadcrumb: "（repo root） > AGENTS.md",
        title: "專案規則入口（跨工具基線）",
        tagline: "放 repo 根目錄，Codex / Cursor / Copilot / Gemini CLI 原生共讀",
        whenItLoads: "Codex 從 git root 掃到 cwd，每層至多一份串接。同層 AGENTS.override.md 優先於 AGENTS.md。越靠近 cwd 的越後讀、越優先。",
        description: "跨工具共通基線。Claude Code 不原生讀取，需在 CLAUDE.md 裡 @AGENTS.md 顯式匯入。放「在這個 repo 裡對所有工具都成立」的規則；工具差異化規則另在各家專屬檔（如 .codex/config.toml）補充。",
        mapsTo: "對照 .claude/：CLAUDE.md（需以 @AGENTS.md 主動匯入）"
      }
    },
    {
      name: "AGENTS.override.md",
      kind: "file",
      color: "purple",
      badge: "committed",
      detail: {
        breadcrumb: "（repo root） > AGENTS.override.md",
        title: "強制覆寫規則",
        tagline: "同層優先於 AGENTS.md，優先序最高",
        whenItLoads: "Codex 在每個目錄層先找 AGENTS.override.md，找到就用它、跳過同層的 AGENTS.md",
        description: "用於特殊情境（如 CI 環境、特定功能目錄）需要強制覆蓋全域規則時。多數 repo 不需要，有需求才建。",
        mapsTo: "對照 .claude/：.claude/rules/*.md（path-scoped frontmatter 可限制生效範圍）"
      }
    }
  ]
}
]}
/>

## 3. Google：Gemini（消費端）與 Antigravity（開發平台）

同樣兩條線。Gemini 是帳號 + GUI，Antigravity 是檔案 + IDE/CLI。

**Gemini 的個人化**用 Gems（自訂 persona，欄位 Name / Instructions / Knowledge，可接 Drive、NotebookLM）。帳號側的 Personal context（Settings）含三塊：Memory（從過往對話學習）、Instructions for Gemini（常駐指令，對應 Claude 的 Instructions for Claude）、Connected Apps（Gmail / Calendar 等）。

**隱私退出**：「Keep Activity」開關（18 歲以上預設為開），自動刪除週期 3 / 18（預設）/ 36 個月可選；關閉後不用於訓練。Workspace 帳號由管理員控制，個人開關可能被組織政策覆寫。

**Antigravity** 是 Google 的 agentic 開發平台，同一品牌涵蓋四個 surface：桌面 app（Antigravity 2.0）、IDE、CLI（`agy`）、SDK，各自設定面不同；基礎模型為 Gemini 3.5 Flash（截至 2026-06，\[9]）。它與 Gemini CLI 是不同產品，但全域規則共用 `~/.gemini/GEMINI.md`（衝突見本節末警示）。規則分兩層：

1. **全域規則**：`~/.gemini/GEMINI.md`，套用所有 workspace。
2. **工作區規則**：專案內 `.agents/rules/*.md`（注意是複數 `.agents/`，為官方現行預設；舊版單數 `.agent/rules/` 仍向後相容）；工作區的 `GEMINI.md` 與 `AGENTS.md` 也會被讀取。規則為純 Markdown，有 Manual / Always On / Model Decision / Glob 四種啟動模式。`GEMINI.md` 與 `AGENTS.md` 的嚴格優先序官方頁未宣告。

放置位置依 surface 不同（\[9]，以官方 `antigravity.google/docs` 為準）：

* **Skills**：全工具共用 `~/.gemini/skills/<name>/SKILL.md`；CLI 專屬全域 `~/.gemini/antigravity-cli/skills/`；工作區一律 `.agents/skills/`（向後相容 `.agent/skills/`）。
* **MCP**：官方設定檔 `~/.gemini/config/mcp_config.json`（IDE 與 CLI 全域共用）；CLI 工作區可放 `.agents/mcp_config.json`。
* **Hooks**：`hooks.json` 放 `.agents/`（工作區）或 `~/.gemini/config/`（全域），事件含 `PreToolUse` / `PostToolUse` / `PreInvocation` / `PostInvocation` / `Stop`。
* **CLI（`agy`）設定**：`~/.gemini/antigravity-cli/settings.json` 與 `keybindings.json`。
* **Workflows**：以 `/workflow-name` 叫用，經 IDE Customizations 面板建立；確切 on-disk 路徑以 Antigravity 官方文件為準。

**Antigravity 的隱私退出**：遙測開關是 Enable Telemetry（Settings → Account），與上面 Gemini 消費端的「Keep Activity」是不同產品的不同開關，別混用。

<Warning>
  **Antigravity 規則目錄已改複數，仍須核對你的版本**

  官方現行預設為複數 `.agents/`（rules / skills / mcp\_config.json 等都在此），舊版單數 `.agent/` 仍向後相容但非預設；早期三方文章與本知識庫舊版曾誤記為「官方用單數」，現以官方頁為準改正。`~/.gemini/GEMINI.md` 同時被 Gemini CLI 與 Antigravity 寫入已知會衝突（GitHub issue #16058，closed as not planned）。Antigravity 官方站 `antigravity.google/docs` 為前端渲染 SPA，一般抓取拿不到內文；本節事實以 playwright 渲染取得的官方頁一手內容為準（截至 2026-06）。仍以你安裝版本的官方文件為最終依據。
</Warning>

### Antigravity 設定目錄（`~/.gemini/`）

<DirExplorer
  lang="zh"
  scopes={[
{
  id: "global",
  label: "全域 ~/.gemini",
  tree: [
    {
      name: "~/.gemini/",
      kind: "dir",
      color: "default",
      children: [
        {
          name: "GEMINI.md",
          kind: "file",
          color: "blue",
          badge: "local",
          detail: {
            breadcrumb: "~/.gemini/ > GEMINI.md",
            title: "全域規則（Antigravity + Gemini CLI 共用）",
            tagline: "套用所有 workspace，但與 Gemini CLI 共寫同一檔有衝突風險",
            whenItLoads: "每次 Antigravity 或 Gemini CLI session 啟動時載入。已知 Antigravity 與 Gemini CLI 會相互覆寫此檔（GitHub issue #16058，closed as not planned）。",
            description: "純 Markdown，放對所有專案成立的 AI 行為指引。因兩個工具共用，兩者規則可能互相干擾；建議內容保持保守（不含工具專屬指令），差異化規則下沉至工作區層。",
            mapsTo: "對照 .claude/：~/.claude/CLAUDE.md（全域個人化）"
          }
        },
        {
          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: "全域 MCP 設定（IDE + CLI 共用）",
                tagline: "跨所有 Antigravity surface 共用的 MCP 伺服器清單",
                whenItLoads: "所有 Antigravity surface（IDE、CLI、桌面 app）啟動時讀取。工作區的 .agents/mcp_config.json 可追加或覆寫個別伺服器。",
                description: "JSON 格式，頂層 mcpServers 物件，每個 key 為伺服器 id，值含 command、args、env。是跨所有 Antigravity 工具的 MCP 設定單一入口，官方建議統一放這裡而非各工具子目錄。",
                mapsTo: "對照 .claude/：~/.claude.json（local scope MCP）或 .mcp.json（專案 MCP）"
              }
            },
            {
              name: "plugins/",
              kind: "dir",
              color: "gray",
              badge: "autogen",
              detail: {
                breadcrumb: "~/.gemini/config/ > plugins/",
                title: "全域 plugin 設定",
                tagline: "IDE plugin 的全域設定目錄",
                whenItLoads: "IDE 啟動時載入",
                description: "存放 IDE plugin 相關設定。確切格式以 Antigravity 官方文件為準（截至 2026-06）。",
                mapsTo: "對照 .claude/：無直接對應"
              }
            }
          ]
        },
        {
          name: "skills/",
          kind: "dir",
          color: "yellow",
          children: [
            {
              name: "<skill-name>/",
              kind: "dir",
              color: "yellow",
              detail: {
                breadcrumb: "~/.gemini/skills/ > <skill-name>/",
                title: "全工具共用 skill（所有 Antigravity surface）",
                tagline: "IDE、CLI、桌面 app 皆可叫用",
                whenItLoads: "任何 Antigravity surface 啟動時掃描，比 antigravity-cli/skills/ 範圍更廣",
                description: "每個 skill 一個子目錄，內含 SKILL.md。這是最廣的全域 skill 位置，IDE 與 CLI 都讀得到。若只需要 CLI 專屬 skill，改放 antigravity-cli/skills/。",
                mapsTo: "對照 .claude/：~/.claude/skills/<name>/SKILL.md（全域 skills）"
              }
            },
            {
              name: "SKILL.md（範例）",
              kind: "file",
              color: "yellow",
              detail: {
                breadcrumb: "~/.gemini/skills/<skill-name>/ > SKILL.md",
                title: "Skill 定義檔",
                tagline: "以 /skill-name 叫用",
                whenItLoads: "依叫用觸發，或 Always On 模式下自動注入",
                description: "純 Markdown，定義 skill 的說明與指令內容。支援 Manual / Always On / Model Decision / Glob 四種啟動模式（在 SKILL.md frontmatter 設定）。",
                mapsTo: "對照 .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）全域設定",
                tagline: "僅 agy CLI 生效，不影響 IDE",
                whenItLoads: "agy CLI 啟動時載入",
                description: "JSON 格式，控制 CLI 行為：default model、telemetry、output 格式等。與 IDE 的設定分開管理，避免 CLI 與 IDE 相互干擾。",
                mapsTo: "對照 .claude/：~/.claude/settings.json（全域設定）"
              }
            },
            {
              name: "skills/",
              kind: "dir",
              color: "yellow",
              children: [
                {
                  name: "<skill-name>/",
                  kind: "dir",
                  color: "yellow",
                  detail: {
                    breadcrumb: "~/.gemini/antigravity-cli/skills/ > <skill-name>/",
                    title: "CLI 專屬全域 skill",
                    tagline: "僅 agy CLI 可叫用，不被 IDE 讀取",
                    whenItLoads: "agy CLI 啟動時掃描",
                    description: "範圍比 ~/.gemini/skills/ 窄，只有 CLI session 看得到。用於 CLI 專屬的 automation 或不想暴露給 IDE 的 skill。",
                    mapsTo: "對照 .claude/：~/.claude/skills/<name>/SKILL.md"
                  }
                }
              ]
            }
          ]
        }
      ]
    }
  ]
},
{
  id: "workspace",
  label: "工作區（.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: "工作區規則檔（主要入口）",
                tagline: "純 Markdown，支援 Manual / Always On / Model Decision / Glob 四種啟動模式",
                whenItLoads: "依 frontmatter 啟動模式決定：Always On 每次 session 自動注入；Manual 需叫用；Model Decision 由模型判斷；Glob 依匹配路徑觸發",
                description: "官方現行預設目錄為複數 .agents/（舊版單數 .agent/ 仍向後相容）。每個 .md 為一條獨立規則，frontmatter 設定啟動模式。這是 Antigravity 比多數工具更細膩的地方：規則可精準綁定觸發條件。",
                mapsTo: "對照 .claude/：.claude/rules/*.md（path-scoped frontmatter 控制範圍）"
              }
            }
          ]
        },
        {
          name: "GEMINI.md",
          kind: "file",
          color: "blue",
          badge: "committed",
          detail: {
            breadcrumb: ".agents/ > GEMINI.md",
            title: "工作區 Antigravity 專屬規則",
            tagline: "補充或覆寫全域 ~/.gemini/GEMINI.md",
            whenItLoads: "進入此 workspace 時載入，優先序高於全域 GEMINI.md",
            description: "工作區層的 Antigravity 差異化規則，放「此 repo 專屬、非全域通用」的指令。與 AGENTS.md 的嚴格優先序官方頁未宣告，兩者避免矛盾。",
            mapsTo: "對照 .claude/：CLAUDE.md（專案層）"
          }
        },
        {
          name: "AGENTS.md",
          kind: "file",
          color: "blue",
          badge: "committed",
          detail: {
            breadcrumb: ".agents/ > AGENTS.md（或 repo root）",
            title: "跨工具基線規則",
            tagline: "Antigravity / Codex / Cursor / Copilot 共讀",
            whenItLoads: "Antigravity 讀取 .agents/ 下的 AGENTS.md 及 repo root 的 AGENTS.md",
            description: "開放標準跨工具基線。Claude Code 不原生支援，需在 CLAUDE.md 以 @AGENTS.md 匯入。",
            mapsTo: "對照 .claude/：CLAUDE.md（需 @AGENTS.md 匯入）"
          }
        },
        {
          name: "mcp_config.json",
          kind: "file",
          color: "purple",
          badge: "committed",
          detail: {
            breadcrumb: ".agents/ > mcp_config.json",
            title: "工作區 MCP 設定（CLI）",
            tagline: "補充全域 ~/.gemini/config/mcp_config.json，僅 CLI 工作區生效",
            whenItLoads: "進入此 workspace 的 agy CLI session 時載入",
            description: "JSON 格式同全域 mcp_config.json（mcpServers 物件）。工作區層可追加或覆寫特定伺服器，不影響全域設定。",
            mapsTo: "對照 .claude/：.mcp.json（專案 MCP）"
          }
        },
        {
          name: "hooks.json",
          kind: "file",
          color: "rose",
          badge: "committed",
          detail: {
            breadcrumb: ".agents/ > hooks.json",
            title: "工作區生命週期 hooks",
            tagline: "PreToolUse / PostToolUse / PreInvocation / PostInvocation / Stop",
            whenItLoads: "進入此 workspace 的 Antigravity session 時載入",
            description: "JSON 格式，每個 hook 事件對應要執行的命令。支援五種事件：PreToolUse、PostToolUse、PreInvocation、PostInvocation、Stop。全域 hooks 放 ~/.gemini/config/hooks.json。",
            mapsTo: "對照 .claude/：.claude/settings.json 的 hooks 區塊"
          }
        },
        {
          name: "skills/",
          kind: "dir",
          color: "yellow",
          children: [
            {
              name: "<skill-name>/",
              kind: "dir",
              color: "yellow",
              detail: {
                breadcrumb: ".agents/skills/ > <skill-name>/",
                title: "工作區 skill",
                tagline: "僅此 workspace 可叫用，向後相容 .agent/skills/",
                whenItLoads: "進入此 workspace 的 Antigravity session 時掃描",
                description: "工作區層 skill，範圍最窄、優先序最高。與全域 skill 同名時以工作區為準。進版控讓團隊共享。",
                mapsTo: "對照 .claude/：.claude/skills/<name>/SKILL.md（專案 skills）"
              }
            }
          ]
        }
      ]
    }
  ]
}
]}
/>

## 4. GitHub Copilot CLI（新版獨立 `copilot` 二進位）

<Note>
  **身份釐清**

  本節只談本機新版獨立 `copilot` CLI（npm `@github/copilot`），與舊版 `gh copilot` 擴充（僅 suggest / explain）為兩條產品線。VS Code 內的 Copilot Chat 與本機 CLI **不是同一套設定機制**：CLI 讀 `~/.copilot/`，VS Code Copilot Chat 讀 `chat.*` 工作區設定（如 `chat.instructionsFilesLocations`），檔案路徑與載入行為不同。本節不涵蓋 VS Code Copilot Chat。
</Note>

Copilot CLI 的設定面幾乎全是檔案，且按範圍切得很細（\[3]）。預設根目錄 `~/.copilot/`，可被 `COPILOT_HOME` 或 `--config-dir` 覆寫；`COPILOT_CACHE_HOME` 單獨管快取：

* **本機指令（local）**：`~/.copilot/copilot-instructions.md`（單檔，位於 `~/.copilot/` 根），該機器所有 session 生效。
* **倉庫級指令**（repository-wide）：`.github/copilot-instructions.md`，整個 repo 套用。
* **路徑級指令**（path-specific）：`.github/instructions/NAME.instructions.md`，frontmatter 用 `applyTo` 以 glob 控制套用範圍，選用 `excludeAgent`。這是 Copilot 比多數工具細的地方：規則能精準綁到特定路徑。
* **Agent 指令層**：root `AGENTS.md`（primary）、cwd 或 `COPILOT_CUSTOM_INSTRUCTIONS_DIRS` 各目錄的 `AGENTS.md`（additional）、root `CLAUDE.md` / `GEMINI.md` 作為 `AGENTS.md` 替代。**所有命中的指令檔皆併用，無嚴格優先序；衝突時 Copilot 的取捨為非確定性**，避免互相矛盾。
* **自動化**：
  * 個人 / 專案 agent：使用者層 `~/.copilot/agents/<name>.agent.md`；專案層 `.github/agents/<name>.agent.md`（同名覆寫個人層）。
  * 個人 / 專案 skill：使用者層 `~/.copilot/skills/<n>/SKILL.md`；專案層 `.github/skills/<n>/SKILL.md`（同名覆寫個人層）。
  * Hook：使用者層 `~/.copilot/hooks/` 或 `config.json` 內 inline；專案層 `.github/hooks/`；兩者**併用**（非覆寫）。
* **MCP**：使用者層 `~/.copilot/mcp-config.json`；專案層 `.mcp.json` / `.github/mcp.json` / `.vscode/mcp.json`（同名時專案層覆寫使用者層）。
* **雲端 Coding agent**：以 Issue 或 `@copilot` 派任務，是另一條產品線，執行環境由 `.github/workflows/copilot-setup-steps.yml` 設定；本機 CLI 不走這條。

## 5. Cursor（短提）

Cursor 是第三方 IDE（Anysphere 開發，非模型廠商），規則格式為 `.cursor/rules/*.mdc`（注意：純 `.md` 會被忽略，必須 `.mdc`），並讀 `AGENTS.md` 作為簡化替代。舊 `.cursorrules` 已標記將棄用。詳細格式與 ignore 檔差異見 Cursor 官方文件 \[4]；本 Playbook 不深討。

## 6. AGENTS.md：跨工具的共通規則檔

`AGENTS.md` 是把上面各家專屬規則檔收斂成一份的開放標準。`AGENTS.md` 是專案規則層的標準，不是個人化層的標準。把它當成跨工具的「共通基線檔」最不容易誤用。

重點：

* 純 Markdown、無強制 schema，放倉庫根；monorepo 可多層，越靠近被編輯檔的越優先。
* 採用面：Codex、Cursor、Copilot、Gemini CLI、Jules、Devin、Amp、VS Code 等二十多個工具原生支援，已逾六萬個開源專案採用（截至 2026-06）\[5]。
* 治理：Linux Foundation 旗下 Agentic AI Foundation（AAIF）統籌，AGENTS.md 是 OpenAI 貢獻的創始專案之一（另兩個為 Anthropic 的 MCP、Block 的 goose）\[6]。標準站 `agents.md`。

各家如何讀 `AGENTS.md`：

* **原生支援**：Codex、Cursor、Copilot、Gemini CLI 等把 `AGENTS.md` 視為專案規則入口；各家另有自己的專屬檔（`GEMINI.md`、`.cursor/rules/`、`.github/copilot-instructions.md`）做差異化覆寫或擴充。
* **不原生支援**：Claude Code。`AGENTS.md` 不在讀取清單，要用同一份基線得在 `CLAUDE.md` 用 `@AGENTS.md` import 拉進來 \[8]。Claude 的「專屬檔」就是 `CLAUDE.md` 本身，是唯一讀得到的入口。

`AGENTS.md` 是基線，不是替代：差異化規則仍分散在各家專屬檔裡。具體怎麼抽基線、共讀驗證什麼，第 7 節動手做走一遍。

## 總對照表（截至 2026-06）

行 = 概念，欄 = 工具，Claude 欄置首。本表四套主軸工具以 CLI 為對照軸；OpenAI 欄以 Codex 為主、ChatGPT 為補；Google 欄以 Antigravity 為主、Gemini 為補。**Antigravity 主要為 IDE 介面（Electron / VS Code 衍生），CLI 對位僅作概念對照**，嚴格檔名與路徑見第 3 節。Cursor 為第三方 IDE，不主動展開細節，僅以「短提」欄列重點。

<ToolCompare
  lang="zh"
  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: "個人化",
  cells: {
    claude:  { value: "Instructions for Claude / ~/.claude/CLAUDE.md", detail: "GUI 端走 claude.ai Settings → Instructions for Claude；CLI 端讀 ~/.claude/CLAUDE.md，套用所有專案。", recommend: true },
    codex:   { value: "~/.codex/AGENTS.md", detail: "語意為「全域專案指令」而非帳號側個人化；沒有獨立的帳號個人化欄位，個人偏好寫進全域 AGENTS.md。" },
    agy:     { value: "~/.gemini/GEMINI.md", detail: "全域規則檔，同時被 Antigravity 與 Gemini CLI 讀取（共用同一份，已知有衝突風險）。Gemini 消費端的個人化走 Settings → Personal context → Instructions for Gemini，是另一個產品的另一個入口。" },
    copilot: { value: "~/.copilot/copilot-instructions.md", detail: "單檔，放在 ~/.copilot/ 根目錄，對該機器所有 session 生效。可被 COPILOT_HOME 或 --config-dir 搬路徑。" },
    cursor:  { value: "User Rules（GUI）", detail: "Cursor Settings → Rules for AI → User Rules，純 GUI 設定，無對應本機設定檔。" }
  }
},
{
  id: "project_rules",
  label: "專案規則",
  cells: {
    claude:  { value: "CLAUDE.md + .claude/rules/", detail: "repo 根的 CLAUDE.md 為主入口；.claude/rules/*.md 以 path-scoped frontmatter 限制生效範圍。只有 CLAUDE.md（及其 @import）在讀取清單，不原生讀 AGENTS.md。", recommend: true },
    codex:   { value: "AGENTS.md + .codex/config.toml", detail: "AGENTS.md 為規則主入口（git root 向下串接到 cwd，AGENTS.override.md 同層優先）；.codex/config.toml 為行為配置（trusted 專案才載入）。" },
    agy:     { value: ".agents/rules/*.md + GEMINI.md + AGENTS.md", detail: "主入口是 .agents/rules/*.md（四種啟動模式）；工作區 GEMINI.md 與 AGENTS.md 也被讀取。官方現行預設為複數 .agents/（舊版 .agent/ 向後相容）。" },
    copilot: { value: ".github/copilot-instructions.md + *.instructions.md", detail: "repo-wide 規則走 .github/copilot-instructions.md；path-specific 走 .github/instructions/NAME.instructions.md（frontmatter applyTo glob）。root AGENTS.md / CLAUDE.md / GEMINI.md 也讀，但優先序為非確定性。" },
    cursor:  { value: ".cursor/rules/*.mdc", detail: "注意副檔名必須是 .mdc（純 .md 被忽略）。讀 repo root AGENTS.md 作為簡化替代。舊版 .cursorrules 已標記棄用。" }
  }
},
{
  id: "memory",
  label: "記憶",
  cells: {
    claude:  { value: "自動記憶 / MEMORY.md", detail: "帳號側自動記憶（可在 Settings 管理）；CLI 端以 MEMORY.md 或 @import 做跨 session 持久記憶。", recommend: true },
    codex:   { value: "（無獨立記憶）", detail: "無專屬記憶機制；以全域 ~/.codex/AGENTS.md 或專案 AGENTS.md 作為常駐指令替代。若需 session 間持久記憶，靠外部 MCP 工具。" },
    agy:     { value: "Memory（帳號側）", detail: "Gemini 消費端有 Settings → Personalization → Memory（從對話自動學習，可逐條管理）。Antigravity 開發平台的記憶機制以官方文件為準；與 Gemini 消費端記憶是否共用尚未官方確認。" },
    copilot: { value: "（無專屬記憶）", detail: "無獨立記憶機制；以指令檔（~/.copilot/copilot-instructions.md 等）作為持久指令替代。" },
    cursor:  { value: "（以規則檔替代）", detail: "無獨立記憶機制；以 .cursor/rules/*.mdc 作為持久規則替代。" }
  }
},
{
  id: "privacy",
  label: "隱私退出",
  cells: {
    claude:  { value: "Help Improve Claude", detail: "Settings → Privacy → Help improve Claude；關閉即退出訓練資料使用。Claude.ai 消費端與 API / Claude Code 是不同設定入口，分別確認。", recommend: true },
    codex:   { value: "Data Controls 訓練開關", detail: "ChatGPT Settings → Data Controls → Improve the model for everyone；消費端預設開，要退出得自己關。Team / Enterprise 方案預設不訓練，可談 ZDR（zero data retention）。" },
    agy:     { value: "Enable Telemetry（Settings → Account）", detail: "Antigravity 開發平台的遙測開關，與 Gemini 消費端的 Keep Activity 是不同產品的不同開關，別混用。Gemini Keep Activity 預設開（18 歲以上），自動刪除週期可選 3 / 18 / 36 個月。" },
    copilot: { value: "帳號 / 企業設定", detail: "github.com 帳號 Settings → Copilot → Policies；組織層由管理員統一設定。企業方案可設 no-retention 與 no-training 政策。" },
    cursor:  { value: "帳號設定", detail: "cursor.com 帳號 Settings → Privacy；企業方案有 zero data retention 選項。詳見 Cursor 官方隱私政策。" }
  }
},
{
  id: "automation",
  label: "自動化",
  cells: {
    claude:  { value: "Skills / Hooks / Commands", detail: "Skills：.claude/skills/<name>/SKILL.md（專案）或 ~/.claude/skills/（全域）；Hooks：.claude/settings.json 的 hooks 區塊（PreToolUse / PostToolUse / Stop）；Commands：/ 開頭的斜線指令。", recommend: true },
    codex:   { value: "config.toml [hooks] + prompts/*.md（deprecated）", detail: "[hooks] stable，放 config.toml（全域或 .codex/config.toml）；prompts/*.md 已標 deprecated（官方建議改 skills）；skills 機制見官方文件（2026-06）。" },
    agy:     { value: "Skills / Workflows / Hooks", detail: "Skills 分三層：全工具共用 ~/.gemini/skills/、CLI 專屬 ~/.gemini/antigravity-cli/skills/、工作區 .agents/skills/；Workflows 以 /workflow-name 叫用；Hooks 走 hooks.json（五種事件）。" },
    copilot: { value: "Skills + Agents + Hooks（疊加載入）", detail: "Skills：~/.copilot/skills/<n>/SKILL.md（個人）、.github/skills/<n>/SKILL.md（專案）；Agents：~/.copilot/agents/<name>.agent.md / .github/agents/；Hooks：~/.copilot/hooks/ 與 .github/hooks/ 兩層疊加（非覆寫）。" },
    cursor:  { value: ".mdc rules", detail: ".cursor/rules/*.mdc 既是規則也是自動化的唯一機制；無獨立 skill / hook 系統。" }
  }
},
{
  id: "mcp",
  label: "MCP",
  cells: {
    claude:  { value: ".mcp.json（專案）/ ~/.claude.json（全域）", detail: "專案 MCP 走 .mcp.json（進版控）；全域 MCP 走 ~/.claude.json 的 local scope mcpServers 欄位。兩者皆以 mcpServers 物件格式，key = server id。", recommend: true },
    codex:   { value: "config.toml [mcp_servers.<id>]", detail: "MCP 設定內嵌在 config.toml（全域 ~/.codex/config.toml 或專案 .codex/config.toml），無獨立 JSON 檔。key 格式 [mcp_servers.my-server]，支援 command / args / env。" },
    agy:     { value: "~/.gemini/config/mcp_config.json（全域）+ .agents/mcp_config.json（工作區）", detail: "官方建議全域 MCP 統一放 ~/.gemini/config/mcp_config.json（跨所有 Antigravity surface 共用）；工作區補充走 .agents/mcp_config.json（CLI）。JSON 格式，頂層 mcpServers 物件。" },
    copilot: { value: "~/.copilot/mcp-config.json + 多個專案層路徑", detail: "使用者層 ~/.copilot/mcp-config.json；專案層支援三個路徑：.mcp.json / .github/mcp.json / .vscode/mcp.json（同名時專案層覆寫使用者層）。" },
    cursor:  { value: ".cursor/mcp.json", detail: "專案層 .cursor/mcp.json；全域設定走 Cursor Settings → MCP（GUI）。JSON 格式，mcpServers 物件同其他工具。" }
  }
}
]}
  notes={[
"本表以 CLI 為對照軸。OpenAI 欄以 Codex 為主、ChatGPT 為補；Google 欄以 Antigravity 為主、Gemini 消費端為補。",
"Antigravity 主要為 IDE 介面，CLI（agy）對位僅作概念對照，嚴格路徑見第 3 節及設定目錄瀏覽器。",
"Cursor 為第三方 IDE（非模型廠商），不展開細節，僅列要點。",
"「記憶」欄各工具語意不同：Claude 的自動記憶、ChatGPT 的 Saved Memories、Gemini 的 Memory 存什麼、何時觸發、能否關閉三者皆不同，不可假設語意一致。",
"所有路徑截至 2026-06；Antigravity 路徑尤其快變動，動手前以安裝版本官方文件為準。"
]}
/>

## 動手做：以 Claude 為示例，把六概念填滿再對位

情境：你是 Claude 使用者，要評估搬到另一套工具，或兩套並用。做法分三步：先把 Claude 的六概念格填出具體值當母版，再逐格對位到目標工具，最後把重疊的專案規則抽成 `AGENTS.md` 讓對方也讀到。

第一步，把第 1 節那六格用 Claude 的真實設定填滿。這份就是你的對位母版：

<Note>
  **Claude 六概念實填：以 Claude Code 為例**

  | 概念   | Claude 的具體位置                                                       |
  | ---- | ------------------------------------------------------------------ |
  | 個人化  | Claude.ai 的 Instructions for Claude；CLI 端 `~/.claude/CLAUDE.md`    |
  | 專案規則 | `./CLAUDE.md` + `.claude/rules/*.md`                               |
  | 記憶   | 自動記憶 / 專案 `MEMORY.md`                                              |
  | 隱私退出 | Settings → Privacy → Help improve Claude，關閉即退出訓練                   |
  | 自動化  | `.claude/skills/`、`.claude/settings.json` 的 hooks、`/` 開頭的 commands |
  | MCP  | `.mcp.json`（專案）、`~/.claude.json` 的 local scope                     |
</Note>

第二步，拿這份母版逐格對位到目標工具。以搬到 Codex 為例，概念不變，檔名與位置換掉：

<Note>
  **Claude → Codex 逐格對位**

  | 概念   | Claude                         | Codex                                                                             |
  | ---- | ------------------------------ | --------------------------------------------------------------------------------- |
  | 個人化  | `~/.claude/CLAUDE.md`          | `~/.codex/AGENTS.md`（語意為全域專案指令，非帳號側個人化）                                           |
  | 專案規則 | `./CLAUDE.md`、`.claude/rules/` | `./AGENTS.md`、`.codex/config.toml`（trusted 才載入）                                   |
  | 隱私退出 | Help improve Claude            | Data Controls 的訓練開關                                                               |
  | 自動化  | hooks / skills / commands      | `config.toml` 的 `[hooks]`（stable）、`prompts/*.md`（deprecated，改用 skills）、`[skills]` |
  | MCP  | `.mcp.json`                    | `config.toml` 的 `[mcp_servers.<id>]`                                              |

  對完你會發現只有兩件事：Codex 的設定集中在 `config.toml`，而 Claude 在 `settings.json`。
</Note>

對完即可停。Claude 端的入口始終是 `CLAUDE.md`，不需要為了跨工具共讀去改寫它的結構。

## 常見誤區

* **假設同名功能行為一致**。各家的 Memory 是最大陷阱：Claude 的記憶、ChatGPT 的 Saved Memories、Gemini 的 Memory，存什麼、何時觸發、能否關，三者都不同。看到熟悉的詞就套用舊心智模型，會在隱私與行為上出問題。對位概念可以，假設語意相同不行。
* **在採用 `AGENTS.md` 的工具上仍維護多份各家專屬檔**。內容重複就會漂移，最後每份都半對半錯。決定哪份是基線（多半是 `AGENTS.md`），其餘指回它。
* **混淆 Cursor 的兩個忽略檔**。`.cursorignore` 全面封鎖 AI 存取，`.cursorindexingignore` 只排除索引、檔案仍可被讀。把機密放錯檔，以為擋住了其實沒擋。
* **以為消費端預設不訓練**。ChatGPT、Gemini 的消費方案多半預設會用於改進模型，要退出得自己關；別把企業方案的預設套到個人帳號上。
* **照抄快變動的檔名與路徑**。Antigravity 的 `.agent/` vs `.agents/`、Codex 的 `config.toml` 位置這類，版本一變就錯。本單元給對位框架，動手前以你的版本官方文件核實。

## 自我檢核

<Check>
  **你能對位嗎**

  1. 拿出你現在最常用的非 Claude 工具，把第 1 節那六個概念欄全部填出來：個人化、專案規則、記憶、隱私退出、自動化、MCP 各在哪設？填不出哪一格？
  2. 你的工具的隱私退出開關，預設是開還是關？你現在這個帳號是哪個狀態？
  3. 如果要讓 Claude 與這個工具共讀同一份專案規則，你會把基線放哪、各家專屬檔留什麼？
  4. 你的 repo 裡有沒有兩份內容重疊的規則檔？它們現在一致嗎？
</Check>

## 來源與延伸閱讀

事實主張依官方文件，快變動項標註截至 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 的 `AGENTS.md` 搜尋方向「git root 向下走到 cwd」、`[hooks]` 與 subagents 為 stable，依前兩頁於 2026-06 複查；`prompts/*.md` 已標 deprecated（建議改 skills），同時複查確認。

  * \[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 規則目錄與全域檔衝突細節，以官方當前文件為準）

  * \[8] Anthropic, "How Claude remembers your project"（`CLAUDE.md` 以 `@path` 語法匯入其他檔，AGENTS.md 共讀範例為 `@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 等頁，經 playwright 渲染取得一手內容；本知識庫不再採用先前的 raw 整理檔），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) ；另佐 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/ 目錄實測結構，含 skills/ 全工具共用 vs antigravity-cli/skills/ CLI 專屬差異）
</div>

* 銜接：[02-2 Anthropic Claude 設定：Chat、Cowork、Code](/code-agent/configuration/anthropic-claude-setup)；層級模型見 [02-1 設定的層級模型](/code-agent/configuration/config-layer-model)。
