{"owner":"alibaba","repo":"page-agent","hasSkills":true,"totalSkillsCount":10,"totalTokensCount":10183,"categories":["anthropic-skill","root-instruction","claude-rule","plugin-manifest"],"hasMcp":false,"mcpConfig":null,"found":[".agents/skills/git-cleanup/SKILL.md",".agents/skills/maintain-model-list/SKILL.md",".agents/skills/pre-impl-discussion/SKILL.md",".agents/skills/submit-pr-from-current-changes/SKILL.md",".agents/skills/update-changelog/SKILL.md","AGENTS.md","CLAUDE.md","packages/extension/docs/extension_api.md","packages/mcp/README.md","packages/website/AGENTS.md"],"skills":{".agents/skills/git-cleanup/SKILL.md":"---\nname: git-cleanup\ndescription: Clean up local git branches and remotes accumulated from PR reviews. Use when the user asks to clean branches, remove stale remotes, or tidy up the local git state.\n---\n\n# Git Cleanup\n\nClean local branches (except `main` and current branch) and non-origin remotes.\nUseful after reviewing multiple PRs that leave behind tracking branches and contributor remotes.\n\n## Workflow\n\n### 1. Survey\n\nRun these in parallel:\n\n```bash\ngit branch\ngit remote\ngit status --short\ngit stash list\n```\n\nPresent a summary of how many branches and remotes will be removed.\n\n### 2. Safety Checks — Ask Before Proceeding\n\n**Stop and ask for confirmation if any of these are true:**\n\n- Current branch is NOT `main`\n- Working tree has uncommitted changes or stashes\n- Any branch is ahead of its upstream\n\nList unmerged branches separately and let the user decide.\n\n### 3. Delete Branches\n\n```bash\n# Safe delete first (fails on unmerged branches)\ngit branch | grep -v '^\\*' | grep -v '^\\s*main$' | xargs git branch -d 2>&1\n\n# Force-delete only with explicit user approval\ngit branch -D <branch>\n```\n\n### 4. Remove Remotes\n\n```bash\ngit remote | grep -v '^origin$' | xargs -I{} git remote remove {}\n```\n\n### 5. Confirm\n\n```bash\ngit branch && echo \"---\" && git remote\n```\n\nReport what was cleaned up.\n\n## Key Rules\n\n- **NEVER** delete `main` or the current checked-out branch\n- **NEVER** force-delete unmerged branches without user confirmation\n- If in doubt, ask\n",".agents/skills/maintain-model-list/SKILL.md":"---\nname: maintain-model-list\ndescription: 'Maintain the supported LLM model list: add a new model, or run routine maintenance to verify availability and discover new models worth adding. Use when the user asks to add/support a model, update the model list, or check model availability.'\nargument-hint: 'A model name to add (e.g. \"gpt-5.7\"), or empty for routine maintenance'\n---\n\n# Maintain Model List\n\nThe supported model list lives in three places that must stay in sync:\n\n| File                                                       | What it holds                                                                                                   |\n| ---------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------- |\n| `packages/website/src/pages/docs/features/models/page.tsx` | `MODEL_GROUPS` (public docs, source of truth for display names) and `BASELINE` (recommended models)             |\n| `packages/llms/src/models.live.test.ts`                    | Mirrored `MODEL_GROUPS`, plus `OPENROUTER_ID_OVERRIDES` / `ALIYUN_ID_OVERRIDES` for provider-specific model ids |\n| `packages/llms/src/utils.ts`                               | `modelPatch` — per-model-family request parameter fixes (thinking/reasoning flags, `tool_choice` quirks, etc.)  |\n\n## Before Touching Anything\n\n1. Run `git status`. If the working tree has uncommitted changes that look like unrelated in-progress work, do NOT edit files — report the situation and ask the user for permission first.\n2. Run the full live test suite as a baseline: `npm run test:live -w @page-agent/llms`. API keys come from the repo-root `.env` (`TESTING_OPENROUTER_KEY`, `TESTING_ALIYUN_KEY`, `TESTING_DEEPSEEK_KEY`); tests skip silently when a key is missing, so check which providers actually ran. Record which models pass/fail before making changes, so new failures are attributable.\n\n## Workflow A: A Specific Model Was Given\n\n1. **Research the model** (web search + provider docs):\n    - Is it served on OpenRouter? Fetch `https://openrouter.ai/api/v1/models` and find the exact id (`<vendor-slug>/<model-id>`, watch for `-preview`, dated snapshots, dots vs hyphens).\n    - Which other channels serve it (vendor native API, Aliyun DashScope, etc.) and what are their native model ids?\n    - API differences: can thinking/reasoning be disabled or minimized? Any `tool_choice`, `parallel_tool_calls`, or parameter-schema quirks? Does the vendor's OpenAI-compatible endpoint differ from OpenRouter's behavior?\n    - Agent suitability: tool call support is mandatory; note context window, latency, and cost.\n2. **Update `modelPatch`** in `packages/llms/src/utils.ts` if the model needs new parameter handling. Model names are matched after `normalizeModelName` (lowercased, `/`-prefix, `.` and `_` stripped) — check whether an existing family branch already covers it.\n3. **Add the model to both `MODEL_GROUPS` lists** (website page and live test), newest first within its brand group. Add id overrides if the OpenRouter/Aliyun id differs from the display name.\n4. **Test availability carefully.** Run the live suite and confirm the new model passes on every provider that serves it:\n\n```bash\nnpm run test:live -w @page-agent/llms\n```\n\nA failure means the request/response shape is wrong for that model — fix `modelPatch` or the id override, don't shrug it off. If the model fails consistently on a provider, exclude it from that provider (see the `deepseek-3.2` precedent in the test file) and document why in a comment. 5. **Decide on `BASELINE`** only if the model is a fast, cheap, strong-tool-call option; otherwise leave it out. 6. Run `npm run typecheck` and `npm test`.\n\n## Workflow B: No Model Given (Routine Maintenance)\n\n1. **Verify the existing list**: run the live suite (see above). Investigate any model that newly fails — deprecated? renamed? provider dropped it?\n2. **Check for drift**: re-verify `OPENROUTER_ID_OVERRIDES` against the current `https://openrouter.ai/api/v1/models` output; ids change when vendors promote previews to stable.\n3. **Scan for new models** worth adding (web search for recent releases from the brands in `MODEL_GROUPS`, plus notable newcomers). A candidate must support tool calls via an OpenAI-compatible API.\n4. **Act by significance**:\n    - Minor updates (a preview id went stable, a small point-release replaces its predecessor): apply the change yourself following Workflow A steps 2–6, then present it to the user for judgment.\n    - New models or removals: report findings and recommendations, let the user decide before editing.\n\n## Hand Off to the User\n\nAfter your own tests pass, always remind the user to verify manually:\n\n1. Tell them which line to put in the repo-root `.env` — give the exact model id per channel, including the OpenRouter id, e.g.:\n\n```bash\nLLM_BASE_URL=\"https://openrouter.ai/api/v1\"\nLLM_API_KEY=\"...\"\nLLM_MODEL_NAME=\"<exact-openrouter-id>\"\n```\n\n2. Tell them to run the demo themselves: `npm run dev:demo` (serves on port 5174) and exercise the agent against a real page.\n\nThe live test only proves a single forced tool call round-trips; it is not an agent-quality eval. The user's manual run is the real acceptance test.\n",".agents/skills/pre-impl-discussion/SKILL.md":"---\nname: pre-impl-discussion\ndescription: \"Conduct a thorough pre-implementation discussion before making significant changes. Use when the user wants to discuss, plan, or evaluate a change before implementing it — especially when they say words like 'discuss', 'evaluate', 'plan', or 'let's talk about'.\"\nargument-hint: 'Describe the change to evaluate'\n---\n\n# Pre-Implementation Discussion\n\nFacilitate a structured, collaborative discussion to evaluate a proposed change **before any implementation begins**.\n\n## Golden Rule\n\n**Do NOT implement anything until the user explicitly confirms the final plan and says to proceed.** Not even \"let me try on a branch\" — that's implementation. The user will tell you when discussion is over.\n\n## Interaction Style\n\n- **Concise during discussion.** Each intermediate response should be short and focused on the current question. Do NOT repeat the full plan in every response.\n- **Complete when finalizing.** Once the plan is mature and the user asks for it, present a single comprehensive (but terse) summary for final review.\n- **Ask, don't assume.** When uncertain about project context, constraints, or preferences — ask. The user prefers collaborative discussion over receiving a pre-baked answer.\n- **Challenge the premise.** Question whether the proposed change is the right one. Suggest simpler alternatives if they exist.\n- **Match the user's language.** Reply in the same language the user writes in.\n\n## Procedure\n\n### 1. Understand the Current State\n\nBefore forming any opinion:\n\n- **Read the relevant source files** — configs, code, docs that relate to the change\n- **Understand the project structure** — what's published vs private, what environments things target, existing constraints\n- **Map the impact surface** — which files, packages, or systems would be affected\n- **Estimate implementation cost** — how many files to touch, how much config to rewrite, what could break\n\nDo NOT skip this step. Do NOT rely on assumptions about what \"most projects\" do.\n\n### 2. Research Thoroughly\n\n- **Consult official sources** — docs, changelogs, migration guides, GitHub issues\n- **Verify every claim with evidence.** Don't say \"X supports Y\" without a source. If unsure, say so.\n- **Search in parallel** to save time — batch independent queries\n\nCommon pitfalls:\n\n- Assuming compatibility without checking actual version constraints\n- Confusing roadmap/aspirations with actual released state\n- Missing transitive constraints (a dependency of a dependency)\n\n### 3. Present Findings (Concise)\n\nShare a **brief** assessment. Tables work well for comparisons. Highlight **blockers** and **unknowns** prominently — don't bury them in paragraphs.\n\n### 4. Identify Key Decisions\n\nSurface the decisions the user needs to make. Present them as clear choices with trade-offs, not as a recommendation monologue.\n\nFor each decision point:\n\n- What are the options? (2-3 max)\n- What does each option cost or give up?\n- What's your lean and why? (one sentence)\n\n### 5. Iterate\n\nThe user will ask follow-up questions, raise concerns, or challenge assumptions. For each round:\n\n- **Research if needed** — don't answer from instinct when facts are available\n- **Answer only what was asked** — don't re-present the entire plan\n- **Update your mental model** based on user feedback\n\nCommon mistakes in this phase:\n\n- Repeating the full plan after every small clarification\n- Answering a narrow question with a broad redesign\n- Treating user questions as confirmation to proceed\n\n### 6. Finalize the Plan\n\nWhen the user signals the discussion is converging, present the **complete plan once**:\n\n- **What changes** — concrete list of actions (table or bullet points)\n- **Impact surface** — what files/systems are affected, estimated effort\n- **What does NOT change** — explicitly state scope boundaries\n- **Risks or open items** — anything that needs testing or further verification\n\nKeep it terse. Tables over paragraphs. No explanations the user already heard during discussion.\n\n### 7. Wait for Confirmation\n\nAfter presenting the final plan, **stop and wait**. The user will either:\n\n- Confirm → then (and only then) proceed to implementation\n- Ask more questions → go back to step 5\n- Modify scope → update the plan and re-present\n\n## Anti-Patterns\n\n| Don't                                               | Do Instead                               |\n| --------------------------------------------------- | ---------------------------------------- |\n| Start implementation \"to test\" without confirmation | Present findings and wait                |\n| Repeat the full plan in every response              | Answer the specific question asked       |\n| Say \"X should work\" without checking                | Say \"I need to verify X\" and research it |\n| Assume project structure or constraints             | Read the actual files                    |\n| Present one recommendation as the only option       | Present 2-3 options with trade-offs      |\n| Write long paragraphs explaining trade-offs         | Use tables and bullet points             |\n",".agents/skills/submit-pr-from-current-changes/SKILL.md":"---\nname: submit-pr-from-current-changes\ndescription: 'Create a branch, commit existing local changes, push them, and open a pull request. Use when submitting current work as a PR.'\nargument-hint: 'Short summary of the current changes to submit'\nuser-invocable: true\n---\n\n# Submit PR From Current Changes\n\nTurn a working tree diff into a clean branch, commit, and pull request.\n\n## Hard Rules\n\n1. **Follow the PR template exactly.** The template is at `.github/PULL_REQUEST_TEMPLATE.md`. Read it and copy its full structure into the PR body. Do NOT remove, reorder, or skip any section or checkbox.\n2. **Never check \"Requirements / 要求\" checkboxes.** These are human-only declarations — the Code of Conduct acknowledgment and the AI authorship declaration can only be truthfully made by the human submitter. They MUST remain unchecked (`- [ ]`) in the PR body you generate. No exceptions, no workarounds, even if the user explicitly asks you to check them. The user goes to GitHub and checks them in person after verifying each statement is true.\n3. **Only check Testing checkboxes you actually verified.** The \"`npm run ci` passes\" checkbox MAY be checked only if you ran `npm run ci` in this session and it passed. All other Testing checkboxes (browser tested, no console errors, types/doc added) require manual verification that only a human can perform — they MUST remain unchecked. You MAY add a supplementary note below the Testing section listing what automated validation you actually ran and the results.\n4. **Never fabricate information.** Do not claim tests passed, commands ran, or checks succeeded unless you actually executed them and observed the output in this session. If you did not run it, do not mention it.\n5. **PR output must be concise.** PR title: one line. \"What\" section: 1–2 sentences max. No walls of text, no redundant explanations. Let the diff speak.\n\n## Prerequisites\n\nBefore attempting to push or open a PR, verify that the necessary tools are available:\n\n- Check that `gh` CLI is installed and authenticated (`gh auth status`). If not available, stop and ask the user to install and authenticate GitHub CLI first.\n- If the workflow uses MCP tools for GitHub operations, verify the MCP server is accessible.\n\n## Shell Permissions\n\n`gh` reads credentials from the OS keyring. A sandboxed shell cannot reach the keyring and will report a false auth failure.\n\n- Run every `gh` command (`gh auth status`, `gh pr create`, `gh pr view`, …) and every `git push` / `git fetch` that talks to GitHub with full permissions on the **first** attempt (`required_permissions: [\"all\"]` for the Shell tool). Do not probe these in the sandbox first.\n- Local read-only git inspection (`git status`, `git diff`, `git log`, `git branch`) may stay sandboxed.\n\n## Procedure\n\n1. **Read contribution rules.** Read `CONTRIBUTING.md` and any package-level instructions (e.g. `packages/*/AGENTS.md`) relevant to the changed files.\n2. **Inspect the diff.** Review changed files, scope, and impact. Understand what the change does before writing anything.\n3. **Inspect repo conventions.** Check recent branch names (`git branch -r --sort=-committerdate | head -20`), recent commit messages (`git log --oneline -20`), and the PR template.\n4. **Pre-submission health checks** (run all before creating the PR):\n    - **Unified theme**: All changes should serve one purpose. If unrelated changes are mixed in, ask the user to split or confirm.\n    - **Commit hygiene**: Every new commit must follow the repo's conventional commit style. Squash or reword if needed.\n    - **Author identity**: Verify `git config user.name` and `git config user.email` are set and the email looks real (not empty, not `noreply` unless intentional). Warn the user if not.\n    - **No leftover artifacts**: Check for debug logs, `.only` in tests, conflict markers, or temp files in the diff.\n    - **CI**: Run `npm run ci` and make sure it passes. Record results honestly.\n5. **Warn if the PR looks too hasty.** If any of these are true, pause and warn the user before proceeding:\n    - Large diff with no description of intent provided\n    - Changes touch core lib or extension (where vibe coding is prohibited per `CONTRIBUTING.md`)\n    - Multiple unrelated concerns in one diff\n    - No validation has been run at all\n    - Remind the user: _\"This project does not accept low-quality or AI-generated PRs without meaningful human review. Please review your changes carefully.\"_\n6. **Branch.** Check the current branch first:\n    - If already on a non-main feature branch with a valid name (matching `type/topic` convention), reuse it.\n    - If the branch name does not follow repo conventions (e.g. missing prefix, unclear topic), ask the user whether to rename or create a new one.\n    - If on `main` or a default branch, create a new branch from it with a short kebab-case name: prefix (`fix/`, `feat/`, `docs/`, `refactor/`, `chore/`) + concrete topic words.\n7. **Stage and commit.** Stage only the intended files. Use `type(scope): subject` format. Keep the subject specific and compact.\n8. **Push.** Push with `-u origin HEAD`.\n9. **Open PR.** Use `gh pr create` with the full PR template structure. Fill in \"What\" and \"Type\" sections based on the actual diff. Check \"`npm run ci` passes\" only if it actually passed in this session; leave all other \"Testing\" and all \"Requirements\" checkboxes unchecked.\n10. **Report results.** Tell the user: branch name, commit hash, PR link, and what validation actually ran (with pass/fail).\n\n## Post-Submission Reminder\n\nAfter successfully opening the PR, ALWAYS give a brief reminder in the user's language. Keep it concise and natural, but make sure it clearly tells the user:\n\n1. They need to test the changes themselves in the browser.\n2. They need to go to the PR page and check the Testing and Requirements checkboxes only after verifying each item.\n3. The PR will not enter review until those checkboxes are checked.\n4. The project does not accept autonomously AI-generated PRs, so they should only check the AI declaration if it is truthful.\n\n## Branch Naming\n\n- Prefer short kebab-case names with a repo-consistent prefix such as `fix/`, `feat/`, `docs/`, `refactor/`, or `chore/`.\n- Match the change type first, then the smallest useful topic.\n- Prefer concrete topic words over issue text dumps.\n\n## Commit Style\n\n- Use the repository's prevailing commit style (conventional commits).\n- Use `type(scope): subject` when scope is clear from the changed area.\n- Keep the subject specific and compact.\n- If multiple commits exist on the branch, each one must independently follow conventions.\n\n## Validation Strategy\n\n- Default to enough validation to defend the PR, not the absolute minimum.\n- Always run `npm run ci` (build, lint, typecheck, tests) before opening the PR. It must pass.\n- Escalate to broader validation when the diff crosses packages, changes shared code, or affects release behavior.\n- Never claim checks that did not actually run.\n- You MAY note what you ran and the results below the Testing section in the PR body, but do NOT check the template checkboxes.\n\n## Decision Points\n\n- No local changes → stop, say so.\n- `gh` CLI not available → stop, ask user to install it.\n- Unrelated files mixed → stage only the intended subset or ask whether the work should be split.\n- If the repo has area-specific instructions, read them before naming the branch or writing the PR.\n- Change is small but high-risk → prefer broader validation.\n- Change is narrowly scoped and low-risk → run the most relevant checks, not arbitrary unrelated ones.\n- Template has declarations you cannot truthfully check → leave unchecked, tell the user why.\n\n## Completion Checks\n\n- The branch exists remotely and tracks upstream.\n- The commit message(s) match repo style.\n- All commit authors have proper name and email configured.\n- The PR title matches the commit intent.\n- The PR body follows the template structure completely.\n- All \"Requirements\" checkboxes and all human-only \"Testing\" checkboxes are unchecked (`- [ ]`) in the PR body. The only checkbox you may check is \"`npm run ci` passes\", and only if it truly passed.\n- The reported validation is accurate — nothing fabricated.\n- The post-submission reminder was delivered in the user's language, concisely and accurately.\n",".agents/skills/update-changelog/SKILL.md":"---\nname: update-changelog\ndescription: 'Update docs/CHANGELOG.md from git history, GitHub releases, and code diffs. Use when: writing release notes, syncing the latest changelog entry, summarizing a new tag, or keeping changelog wording concise and consistent.'\nargument-hint: 'Describe the target, for example: latest version only'\n---\n\n# Update Changelog\n\nUpdate `docs/CHANGELOG.md` from repository evidence instead of guesswork.\n\n## When to Use\n\n- Add the newest release entry to `docs/CHANGELOG.md`\n- Sync changelog text with GitHub Releases\n- Summarize the latest tag from git history or code diffs\n\n## Defaults\n\n- Keep the wording brief\n- Only add the latest missing version unless the user explicitly asks to backfill older releases\n- Prefer GitHub sources first, especially Releases and tag metadata\n- Skip `Features` / `Improvements` / `Bug Fixes` headings when the release only has a few clear items\n\n## Procedure\n\n### 1. Read the local style\n\n- Open `docs/CHANGELOG.md`\n- Match the existing tone, bullet style, section ordering, and date format\n\n### 2. Determine the target release\n\n- Read the root `package.json` version and compare it with the top changelog entry\n- Find the previous tag for the target version\n- If the latest version is already documented, stop and report that no changelog update is needed\n\n### 3. Gather evidence\n\nPrefer these sources in order:\n\n1. GitHub release notes\n\n```bash\nGH_PAGER=cat gh release view v<version> --repo <owner>/<repo> --json tagName,name,publishedAt,body\n```\n\n2. Tag date\n\n```bash\ngit log -1 --format=%cs v<version>\n```\n\n3. Commit history between tags\n\n```bash\ngit --no-pager log --format='%h %s' --no-merges v<previous>..v<version>\n```\n\n4. Diff scope when commit subjects are vague\n\n```bash\ngit --no-pager diff --name-only v<previous>..v<version>\ngit --no-pager diff --stat v<previous>..v<version>\n```\n\n5. Read touched files directly only when the user-visible change is still unclear\n\n### 4. Distill what belongs in the changelog\n\nInclude:\n\n- User-visible features\n- Important behavior changes\n- Bug fixes that improve reliability, compatibility, or developer experience\n- Small docs updates only when they materially change supported setups or onboarding\n\nExclude unless explicitly requested:\n\n- Pure version bumps\n- Routine dependency updates\n- Internal refactors with no visible impact\n- Mechanical formatting noise\n\n### 5. Choose the structure\n\n- If the release has 1 to 3 clear points, write flat bullets directly under the version heading\n- If the release has several distinct items, use short headings such as `### Features`, `### Improvements`, and `### Bug Fixes`\n- Do not force categories when they make the entry longer or noisier\n\n### 6. Write the entry\n\n- Add or update only the requested release entry\n- Keep bullets short and concrete\n- Reuse project terminology from nearby entries\n- Avoid marketing language\n- Do not copy noisy GitHub release text verbatim\n\n### 7. Verify\n\n- Check dates, version numbers, and tag boundaries\n- Ensure Markdown structure matches nearby entries\n- Confirm no intermediate versions were added unless requested\n\n## Completion Checks\n\n- The newest requested version is documented\n- The wording is concise and consistent with the surrounding changelog\n- The entry is backed by GitHub releases, git history, or code diff evidence\n- Low-signal internal changes were left out\n","AGENTS.md":"# Instructions for Coding Assistants\n\n## Project Overview\n\nThis is a **monorepo** with npm workspaces:\n\n- **Page Agent** (`packages/page-agent/`) - Main entry with built-in UI Panel, published as `page-agent` on npm\n- **Extension** (`packages/extension/`) - Browser extension (WXT + React)\n- **Website** (`packages/website/`) - React docs and landing page. **When working on website, follow `packages/website/AGENTS.md`**\n\nInternal packages:\n\n- **Core** (`packages/core/`) - PageAgentCore without UI (npm: `@page-agent/core`)\n- **LLMs** (`packages/llms/`) - LLM client with reflection-before-action mental model\n- **Page Controller** (`packages/page-controller/`) - DOM operations and visual feedback (SimulatorMask), independent of LLM\n- **UI** (`packages/ui/`) - Panel and i18n. Decoupled from PageAgent\n\n## Development Commands\n\n```bash\nnpm start                      # Start website dev server\nnpm run build                  # Build all packages\nnpm run build:libs             # Build all libraries\nnpm run build:ext              # Build and zip the extension package\nnpm run typecheck              # Typecheck all packages\nnpm run test                   # Run unit tests across all workspaces\nnpm run lint                   # ESLint\n```\n\n## Architecture\n\n### Monorepo Structure\n\nSource-first monorepo: library `package.json` exports point to `src/*.ts` during development. At publish time, `scripts/pre-publish.js` promotes `publishConfig` fields to top-level (swapping to `dist/`), and `scripts/post-publish.js` restores the originals.\n\n```\npackages/\n├── core/                    # npm: \"@page-agent/core\" ⭐ Core agent logic (headless)\n├── page-agent/              # npm: \"page-agent\" entry class (with UI + controller + demo builds)\n├── website/                 # @page-agent/website (private)\n├── llms/                    # @page-agent/llms\n├── extension/               # Browser extension\n├── page-controller/         # @page-agent/page-controller\n└── ui/                      # @page-agent/ui\n```\n\n`workspaces` in `package.json` must be in topological order.\n\n### Module Boundaries\n\n- **Page Agent**: Main entry with UI. Extends PageAgentCore and adds Panel. Imports from `@page-agent/core`, `@page-agent/ui`\n- **Core**: PageAgentCore without UI. Imports from `@page-agent/llms`, `@page-agent/page-controller`\n- **LLMs**: LLM client with MacroToolInput contract. No dependency on page-agent\n- **UI**: Panel and i18n. Decoupled from PageAgent via PanelAgentAdapter interface\n- **Page Controller**: DOM operations with optional visual feedback (SimulatorMask). No LLM dependency. Enable mask via `enableMask: true` config\n\n### PageController ↔ PageAgent Communication\n\nAll communication is async and isolated:\n\n```typescript\n// PageAgent delegates DOM operations to PageController\nawait this.pageController.updateTree()\nawait this.pageController.clickElement(index)\nawait this.pageController.inputText(index, text)\nawait this.pageController.scroll({ down: true, numPages: 1 })\n\n// PageController exposes state via async methods\nconst simplifiedHTML = await this.pageController.getSimplifiedHTML()\nconst pageInfo = await this.pageController.getPageInfo()\n```\n\n### DOM Pipeline\n\n1. **DOM Extraction**: Live DOM → `FlatDomTree` via `page-controller/src/dom/dom_tree/`\n2. **Dehydration**: DOM tree → simplified text for LLM\n3. **LLM Processing**: AI returns action plans (page-agent)\n4. **Indexed Operations**: PageAgent calls PageController by element index\n\n## Key Files Reference\n\n### Page Agent (`packages/page-agent/`)\n\n| File               | Description                                  |\n| ------------------ | -------------------------------------------- |\n| `src/PageAgent.ts` | ⭐ Main class with UI, extends PageAgentCore |\n| `src/demo.ts`      | IIFE demo entry (auto-init with demo API)    |\n\n### Core (`packages/core/`)\n\n| File                   | Description                             |\n| ---------------------- | --------------------------------------- |\n| `src/PageAgentCore.ts` | ⭐ Core agent class without UI          |\n| `src/tools/`           | Tool definitions calling PageController |\n| `src/config/`          | Configuration types and constants       |\n| `src/prompts/`         | System prompt templates                 |\n\n### LLMs (`packages/llms/`)\n\n| File                  | Description                           |\n| --------------------- | ------------------------------------- |\n| `src/index.ts`        | ⭐ LLM class with retry logic         |\n| `src/types.ts`        | MacroToolInput, AgentBrain, LLMConfig |\n| `src/OpenAIClient.ts` | OpenAI-compatible client              |\n\n### Page Controller (`packages/page-controller/`)\n\n| File                        | Description                                                |\n| --------------------------- | ---------------------------------------------------------- |\n| `src/PageController.ts`     | ⭐ Main controller class with optional mask support        |\n| `src/SimulatorMask.ts`      | Visual overlay blocking user interaction during automation |\n| `src/actions.ts`            | Element interactions (click, input, scroll)                |\n| `src/dom/dom_tree/index.js` | Core DOM extraction engine                                 |\n\n## Adding New Features\n\n### New Agent Tool\n\n1. Implement in `packages/core/src/tools/index.ts`\n2. If tool needs DOM ops, add method to PageController first\n3. Tool calls `this.pageController.methodName()` for DOM interactions\n\n### New PageController Action\n\n1. Add implementation in `packages/page-controller/src/actions.ts`\n2. Expose via async method in `PageController.ts`\n3. Export from `packages/page-controller/src/index.ts`\n\n## Testing\n\n- **Test value**: Do not add redundant tests or directly translate implementation code into assertions. Test meaningful behavior and realistic failure modes.\n- **Framework**: Vitest (unit tests only for now; future E2E goes to `packages/e2e/` with Playwright)\n- **Location**: co-located, `src/foo.test.ts` next to `src/foo.ts`\n- **Coverage today**: `packages/llms` only — other packages will follow incrementally\n- **Adding tests to a new package**: create `vitest.config.ts` in the package and add a `\"test\": \"vitest run\"` script. Root `npm test` and `node scripts/ci.js` pick it up through npm workspaces.\n- **Live tests** (hit real external APIs, slow/costly): name them `*.live.test.ts`, exclude them from the package's `test` script, and expose them via a `test:live` script. Root `npm run test:live` runs all of them; they never run in `npm test` or CI. Template: `packages/llms`.\n- **Template**: See @page-agent/llms\n\n```bash\nnpm test                            # all packages with a test script\nnpm test -w @page-agent/llms        # single package\ncd packages/llms && npx vitest      # watch mode in one package\n```\n\n## Code Standards\n\n- Explicit typing for exported/public APIs\n- ESLint relaxes some unsafe rules for rapid iteration\n- Every change you make should not only implement the desired functionality but also improve the quality of the codebase\n- All code and comments must be in English.\n- Do not try to hide errors or risks. They are valuable feedbacks for developers and users. Make them visible and actionable.\n- Traceability and predictability is more important than success rate.\n","CLAUDE.md":"@AGENTS.md\n","packages/extension/docs/extension_api.md":"# Page Agent Extension API\n\nIntegrate the Page Agent extension into your web app and trigger multi-page browser tasks from page JavaScript.\n\n## Installation\n\n### 1. Install the browser extension\n\nPrimary channel:\n\n- Chrome Web Store: https://chromewebstore.google.com/detail/page-agent-ext/akldabonmimlicnjlflnapfeklbfemhj\n\nLatest updates are often published earlier on:\n\n- GitHub Releases: https://github.com/alibaba/page-agent/releases\n\n### 2. Install type definitions (recommended)\n\n```bash\nnpm install @page-agent/core --save-dev\n```\n\n### 3. Authorization (Token)\n\nThe token allows your page JS to call the extension API (`window.PAGE_AGENT_EXT`) and execute multi-page tasks.\n\nWhy token-based access is required:\n\n- The extension has broad browser permissions (page access, navigation, multi-tab control).\n- If abused, it can harm user privacy and security.\n- Users must explicitly provide the token only to applications they trust.\n\nSetup:\n\n1. Open the extension side panel and copy your auth token.\n2. Set the token in your page:\n\n```typescript\nlocalStorage.setItem('PageAgentExtUserAuthToken', 'your-token')\n```\n\n## Quick Start\n\n```typescript\nimport type { AgentActivity, AgentStatus, ExecutionResult, HistoricalEvent } from '@page-agent/core'\n\n// Wait for extension injection (up to 1 second)\nasync function waitForExtension(timeout = 1000): Promise<boolean> {\n    const start = Date.now()\n    while (Date.now() - start < timeout) {\n        if (window.PAGE_AGENT_EXT) return true\n        await new Promise((r) => setTimeout(r, 100))\n    }\n    return false\n}\n\n// Usage\nif (await waitForExtension()) {\n    const result = await window.PAGE_AGENT_EXT!.execute('Click the login button', {\n        baseURL: 'https://api.openai.com/v1',\n        apiKey: 'your-api-key',\n        model: 'gpt-5.2',\n        onStatusChange: (status) => console.log('Status:', status),\n        onActivity: (activity) => console.log('Activity:', activity),\n    })\n    console.log('Result:', result)\n}\n```\n\n## Global API\n\nAfter token match, the extension injects APIs into `window`.\n\n### `window.PAGE_AGENT_EXT_VERSION`\n\nExtension version string (for capability checks before using the main API).\n\n### `window.PAGE_AGENT_EXT`\n\nMain namespace object.\n\n#### `PAGE_AGENT_EXT.execute(task, config)`\n\nExecute one agent task.\n\nParameters:\n\n| Name     | Type            | Required | Description                          |\n| -------- | --------------- | -------- | ------------------------------------ |\n| `task`   | `string`        | Yes      | Task description                     |\n| `config` | `ExecuteConfig` | Yes      | LLM settings, options, and callbacks |\n\nReturns: `Promise<ExecutionResult>`\n\n#### `PAGE_AGENT_EXT.stop()`\n\nStop the current task.\n\n## Limitations\n\n- **Normal browser windows only.** This extension relies on tab group API which does not work for pop-up window or PWA App window.\n\n## Types\n\nInstall `@page-agent/core` for complete types:\n\n```typescript\nimport type { AgentActivity, AgentStatus, ExecutionResult, HistoricalEvent } from '@page-agent/core'\n\nexport interface ExecuteConfig {\n    baseURL: string\n    model: string\n    apiKey?: string\n\n    // Global system-level instructions for the agent.\n    // Equivalent to AgentConfig.instructions.system.\n    systemInstruction?: string\n\n    // Include the initial tab where page JS starts. Default: true.\n    includeInitialTab?: boolean\n\n    // Control all unpinned tabs in the window instead of only the tab group.\n    // When enabled, agent sees and can switch to every non-pinned tab.\n    // Default: false. Experimental.\n    experimentalIncludeAllTabs?: boolean\n\n    onStatusChange?: (status: AgentStatus) => void\n    onActivity?: (activity: AgentActivity) => void\n    onHistoryUpdate?: (history: HistoricalEvent[]) => void\n}\n\nexport type Execute = (task: string, config: ExecuteConfig) => Promise<ExecutionResult>\n```\n\n`AgentStatus`\n\n```typescript\ntype AgentStatus = 'idle' | 'running' | 'completed' | 'error' | 'stopped'\n```\n\n`AgentActivity`\n\n```typescript\ntype AgentActivity =\n    | { type: 'thinking' }\n    | { type: 'executing'; tool: string; input: unknown }\n    | { type: 'executed'; tool: string; input: unknown; output: string; duration: number }\n    | { type: 'retrying'; attempt: number; maxAttempts: number }\n    | { type: 'error'; message: string }\n```\n\n`HistoricalEvent`\n\n```typescript\ntype HistoricalEvent =\n    | { type: 'step'; stepIndex: number; reflection: AgentReflection; action: Action }\n    | { type: 'observation'; content: string }\n    | { type: 'user_takeover' }\n    | { type: 'retry'; message: string; attempt: number; maxAttempts: number }\n    | { type: 'error'; message: string; rawResponse?: unknown }\n```\n\n`ExecutionResult`\n\n```typescript\ninterface ExecutionResult {\n    success: boolean\n    data: string\n    history: HistoricalEvent[]\n}\n```\n\n## Usage Examples\n\n### Basic Execution\n\n```typescript\nconst result = await window.PAGE_AGENT_EXT!.execute(\n    'Fill in the email field with test@example.com and click Submit',\n    {\n        baseURL: 'https://api.openai.com/v1',\n        apiKey: process.env.OPENAI_API_KEY!,\n        model: 'gpt-5.2',\n        includeInitialTab: false, // Optional: exclude current tab\n        onStatusChange: (status) => console.log(status),\n        onActivity: (activity) => console.log(activity),\n    }\n)\n```\n\n### Stop the Current Task\n\n```typescript\nwindow.PAGE_AGENT_EXT!.stop()\n```\n\n## Window Type Declaration\n\nIf you are not importing `@page-agent/core`, add:\n\n```typescript\nimport type { AgentActivity, AgentStatus, ExecutionResult, HistoricalEvent } from '@page-agent/core'\n\ninterface ExecuteConfig {\n    baseURL: string\n    model: string\n    apiKey?: string\n\n    systemInstruction?: string\n\n    includeInitialTab?: boolean\n    experimentalIncludeAllTabs?: boolean\n    onStatusChange?: (status: AgentStatus) => void\n    onActivity?: (activity: AgentActivity) => void\n    onHistoryUpdate?: (history: HistoricalEvent[]) => void\n}\n\ndeclare global {\n    interface Window {\n        PAGE_AGENT_EXT_VERSION?: string\n        PAGE_AGENT_EXT?: {\n            version: string\n            execute: Execute\n            stop: () => void\n        }\n    }\n}\n```\n","packages/mcp/README.md":"# @page-agent/mcp\n\nMCP server that lets AI agent clients (Claude Desktop, Copilot, etc.) control your browser through the [Page Agent](https://github.com/alibaba/page-agent) extension.\n\n## Prerequisites\n\n- Node.js >= 20\n- [Page Agent Extension](https://chromewebstore.google.com/detail/page-agent-ext/akldabonmimlicnjlflnapfeklbfemhj) installed in Chrome\n- An LLM API key (OpenAI-compatible)\n\n## Installation\n\n### Claude Desktop\n\nAdd to `~/Library/Application Support/Claude/claude_desktop_config.json`:\n\n```json\n{\n    \"mcpServers\": {\n        \"page-agent\": {\n            \"command\": \"npx\",\n            \"args\": [\"-y\", \"@page-agent/mcp\"],\n            \"env\": {\n                \"LLM_BASE_URL\": \"https://dashscope.aliyuncs.com/compatible-mode/v1\",\n                \"LLM_API_KEY\": \"sk-xxx\",\n                \"LLM_MODEL_NAME\": \"qwen3.5-plus\"\n            }\n        }\n    }\n}\n```\n\n### Cursor / Copilot\n\nSame format — add the config to the MCP settings of your client.\n\n## MCP Tools\n\n| Tool           | Input              | Description                                           |\n| -------------- | ------------------ | ----------------------------------------------------- |\n| `execute_task` | `{ task: string }` | Execute a browser task in natural language. Blocking. |\n| `get_status`   | —                  | Returns `{ connected, busy }`                         |\n| `stop_task`    | —                  | Stop the currently running task.                      |\n\n## Environment Variables\n\n| Variable         | Default | Description           |\n| ---------------- | ------- | --------------------- |\n| `LLM_BASE_URL`   | —       | LLM API base URL      |\n| `LLM_API_KEY`    | —       | LLM API key           |\n| `LLM_MODEL_NAME` | —       | Model name            |\n| `PORT`           | `38401` | HTTP + WebSocket port |\n\n## How It Works\n\n```\n┌──────────────┐  stdio   ┌──────────────────┐  WebSocket   ┌──────────────┐\n│ Claude /     │◄────────►│ @page-agent/mcp  │◄────────────►│ Hub tab      │\n│ Copilot      │  (MCP)   │ (Node.js)        │  (localhost) │ (extension)  │\n└──────────────┘          └──────────────────┘              └──────┬───────┘\n                                   │                               │\n                                   │ HTTP                          │ useAgent\n                                   ▼                               ▼\n                          ┌──────────────────┐              ┌──────────────┐\n                          │ Launcher page    │              │ MultiPage    │\n                          │ (localhost:PORT) │              │ Agent        │\n                          └──────────────────┘              └──────────────┘\n```\n\n1. Agent client starts the MCP server via stdio (`npx @page-agent/mcp`).\n2. Server starts HTTP + WS on `localhost:PORT`, opens the launcher page in browser.\n3. Launcher page triggers the extension to open a **hub tab** (`hub.html?ws=PORT`).\n4. Hub connects to the WS server. MCP tools now proxy tasks to the hub.\n\nThe hub tab speaks a generic WebSocket protocol (defined in `hub-ws.ts` in the extension package) and has no knowledge of MCP. See the hub's protocol docs for message format details.\n\n## Architecture\n\nPure JS ESM, no build step. Source files are the published artifacts.\n\n```\nsrc/\n├── index.js        # CLI entry: MCP server (stdio) + opens launcher\n├── hub-bridge.js   # HTTP server + WebSocket bridge to hub tab\n└── launcher.html   # Bootstrap page: detects extension, triggers hub open\n```\n\n## Dev\n\n```bash\nnpm run dev:ext\nnpx @modelcontextprotocol/inspector node packages/mcp/src/index.js\n```\n","packages/website/AGENTS.md":"# Website Package - Instructions for Coding Assistants\n\n## Tech Stack\n\n- **React** with TypeScript\n- **Vite** for dev server and build\n- **Tailwind CSS** for styling\n- **shadcn/ui** (new-york style) for UI components — **do NOT hand-edit `src/components/ui/` files**\n- **Magic UI** for animations and effects\n- **wouter** with browser routing (`base: \"/page-agent\"`)\n- **lucide-react** for icons\n\n## Component Guidelines\n\n### Use shadcn/ui Components First\n\n**ALWAYS prefer shadcn/ui components over custom implementations.**\n\nBefore creating any UI component, check if shadcn already provides it:\n\n```bash\n# IMPORTANT: Run from packages/website/, NOT from repo root\ncd packages/website\n\n# Add a new shadcn component\nnpx shadcn@latest add <component-name>\n\n# Add a Magic UI component\nnpx shadcn@latest add \"@magicui/<component-name>\"\n```\n\nAvailable shadcn components: https://ui.shadcn.com/docs/components\nAvailable Magic UI components: https://magicui.design/docs/components\n\n### Current UI Components\n\nLocated in `src/components/ui/`:\n\n**From shadcn/ui:**\n\n- `alert`, `badge`, `button`, `separator`, `sonner`, `switch`, `tooltip`\n\n**From Magic UI:**\n\n- `animated-gradient-text`, `animated-shiny-text`, `aurora-text`\n- `hyper-text`, `magic-card`, `neon-gradient-card`, `particles`\n- `sparkles-text`, `text-animate`, `typing-animation`\n\n**Custom:**\n\n- `highlighter`, `kbd`, `spinner`\n\n### Styling Rules\n\n1. **Prefer Tailwind classes** over custom CSS\n2. Support dark mode via `dark:` classes\n3. Use CSS variables from `src/index.css` for theme colors\n\n## Project Structure\n\n```\nsrc/\n├── pages/\n│   ├── home/\n│   │   ├── index.tsx  # Homepage\n│   │   └── ...Section.tsx\n│   └── docs/\n│       ├── index.tsx    # Docs route switch\n│       ├── Layout.tsx   # Sidebar navigation\n│       └── [section]/[topic]/page.tsx\n├── components/\n│   ├── ui/              # shadcn/ui + Magic UI (DO NOT hand-edit)\n│   ├── Heading.tsx      # Anchor heading for doc pages\n│   ├── Header.tsx       # Site header\n│   └── Footer.tsx       # Site footer\n├── i18n/                # Internationalization\n├── router.tsx           # Root layout + routing\n└── main.tsx             # App entry\n```\n\n## Routing\n\nUses wouter browser routing with base path for GitHub Pages deployment at `https://alibaba.github.io/page-agent/`.\n\n```tsx\n// main.tsx\n<Router base=\"/page-agent\">\n    <PagesRouter />\n</Router>\n```\n\n**Key rules:**\n\n- Header and Footer live in `router.tsx` **outside** `<Switch>`, so they always see the root router context (`base=\"/page-agent\"`)\n- Docs pages are nested via `<Route path=\"/docs\" nest>`, which creates a child context (`base=\"/page-agent/docs\"`)\n- Inside the docs nest, Link hrefs are relative to `/docs` (e.g. `href=\"/features/models\"`, NOT `href=\"/docs/features/models\"`)\n- **Never use `~` prefix** in Link hrefs — it bypasses the base path entirely\n- Doc page headings use `<Heading id=\"slug\" level={2}>` for anchor links\n\n### SPA on GitHub Pages\n\nInstead of `404.html` redirects, the build copies `index.html` into every route directory. Add new routes to the `SPA_ROUTES` array in `vite.config.js`.\n\n## Adding New Pages\n\n### Documentation Page\n\n1. Create `src/pages/docs/<section>/<slug>/page.tsx`\n2. Add route in `src/pages/docs/index.tsx`\n3. Add navigation item in `src/pages/docs/Layout.tsx`\n4. Add path to `SPA_ROUTES` in `vite.config.js`\n5. Keep `<slug>` consistent across the folder name, import path, route path, sidebar link, and any related cross-links. If the public route changes, rename the folder too.\n\n## Configuration Files\n\n| File              | Purpose                 |\n| ----------------- | ----------------------- |\n| `components.json` | shadcn/ui configuration |\n| `vite.config.js`  | Vite build + SPA routes |\n| `tsconfig.json`   | TypeScript config       |\n\n## Commands\n\n```bash\nnpm start            # Dev server (from root)\nnpm run build:website    # Build website (from root)\n```\n"},"files":{".agents/skills/git-cleanup/SKILL.md":"---\nname: git-cleanup\ndescription: Clean up local git branches and remotes accumulated from PR reviews. Use when the user asks to clean branches, remove stale remotes, or tidy up the local git state.\n---\n\n# Git Cleanup\n\nClean local branches (except `main` and current branch) and non-origin remotes.\nUseful after reviewing multiple PRs that leave behind tracking branches and contributor remotes.\n\n## Workflow\n\n### 1. Survey\n\nRun these in parallel:\n\n```bash\ngit branch\ngit remote\ngit status --short\ngit stash list\n```\n\nPresent a summary of how many branches and remotes will be removed.\n\n### 2. Safety Checks — Ask Before Proceeding\n\n**Stop and ask for confirmation if any of these are true:**\n\n- Current branch is NOT `main`\n- Working tree has uncommitted changes or stashes\n- Any branch is ahead of its upstream\n\nList unmerged branches separately and let the user decide.\n\n### 3. Delete Branches\n\n```bash\n# Safe delete first (fails on unmerged branches)\ngit branch | grep -v '^\\*' | grep -v '^\\s*main$' | xargs git branch -d 2>&1\n\n# Force-delete only with explicit user approval\ngit branch -D <branch>\n```\n\n### 4. Remove Remotes\n\n```bash\ngit remote | grep -v '^origin$' | xargs -I{} git remote remove {}\n```\n\n### 5. Confirm\n\n```bash\ngit branch && echo \"---\" && git remote\n```\n\nReport what was cleaned up.\n\n## Key Rules\n\n- **NEVER** delete `main` or the current checked-out branch\n- **NEVER** force-delete unmerged branches without user confirmation\n- If in doubt, ask\n",".agents/skills/maintain-model-list/SKILL.md":"---\nname: maintain-model-list\ndescription: 'Maintain the supported LLM model list: add a new model, or run routine maintenance to verify availability and discover new models worth adding. Use when the user asks to add/support a model, update the model list, or check model availability.'\nargument-hint: 'A model name to add (e.g. \"gpt-5.7\"), or empty for routine maintenance'\n---\n\n# Maintain Model List\n\nThe supported model list lives in three places that must stay in sync:\n\n| File                                                       | What it holds                                                                                                   |\n| ---------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------- |\n| `packages/website/src/pages/docs/features/models/page.tsx` | `MODEL_GROUPS` (public docs, source of truth for display names) and `BASELINE` (recommended models)             |\n| `packages/llms/src/models.live.test.ts`                    | Mirrored `MODEL_GROUPS`, plus `OPENROUTER_ID_OVERRIDES` / `ALIYUN_ID_OVERRIDES` for provider-specific model ids |\n| `packages/llms/src/utils.ts`                               | `modelPatch` — per-model-family request parameter fixes (thinking/reasoning flags, `tool_choice` quirks, etc.)  |\n\n## Before Touching Anything\n\n1. Run `git status`. If the working tree has uncommitted changes that look like unrelated in-progress work, do NOT edit files — report the situation and ask the user for permission first.\n2. Run the full live test suite as a baseline: `npm run test:live -w @page-agent/llms`. API keys come from the repo-root `.env` (`TESTING_OPENROUTER_KEY`, `TESTING_ALIYUN_KEY`, `TESTING_DEEPSEEK_KEY`); tests skip silently when a key is missing, so check which providers actually ran. Record which models pass/fail before making changes, so new failures are attributable.\n\n## Workflow A: A Specific Model Was Given\n\n1. **Research the model** (web search + provider docs):\n    - Is it served on OpenRouter? Fetch `https://openrouter.ai/api/v1/models` and find the exact id (`<vendor-slug>/<model-id>`, watch for `-preview`, dated snapshots, dots vs hyphens).\n    - Which other channels serve it (vendor native API, Aliyun DashScope, etc.) and what are their native model ids?\n    - API differences: can thinking/reasoning be disabled or minimized? Any `tool_choice`, `parallel_tool_calls`, or parameter-schema quirks? Does the vendor's OpenAI-compatible endpoint differ from OpenRouter's behavior?\n    - Agent suitability: tool call support is mandatory; note context window, latency, and cost.\n2. **Update `modelPatch`** in `packages/llms/src/utils.ts` if the model needs new parameter handling. Model names are matched after `normalizeModelName` (lowercased, `/`-prefix, `.` and `_` stripped) — check whether an existing family branch already covers it.\n3. **Add the model to both `MODEL_GROUPS` lists** (website page and live test), newest first within its brand group. Add id overrides if the OpenRouter/Aliyun id differs from the display name.\n4. **Test availability carefully.** Run the live suite and confirm the new model passes on every provider that serves it:\n\n```bash\nnpm run test:live -w @page-agent/llms\n```\n\nA failure means the request/response shape is wrong for that model — fix `modelPatch` or the id override, don't shrug it off. If the model fails consistently on a provider, exclude it from that provider (see the `deepseek-3.2` precedent in the test file) and document why in a comment. 5. **Decide on `BASELINE`** only if the model is a fast, cheap, strong-tool-call option; otherwise leave it out. 6. Run `npm run typecheck` and `npm test`.\n\n## Workflow B: No Model Given (Routine Maintenance)\n\n1. **Verify the existing list**: run the live suite (see above). Investigate any model that newly fails — deprecated? renamed? provider dropped it?\n2. **Check for drift**: re-verify `OPENROUTER_ID_OVERRIDES` against the current `https://openrouter.ai/api/v1/models` output; ids change when vendors promote previews to stable.\n3. **Scan for new models** worth adding (web search for recent releases from the brands in `MODEL_GROUPS`, plus notable newcomers). A candidate must support tool calls via an OpenAI-compatible API.\n4. **Act by significance**:\n    - Minor updates (a preview id went stable, a small point-release replaces its predecessor): apply the change yourself following Workflow A steps 2–6, then present it to the user for judgment.\n    - New models or removals: report findings and recommendations, let the user decide before editing.\n\n## Hand Off to the User\n\nAfter your own tests pass, always remind the user to verify manually:\n\n1. Tell them which line to put in the repo-root `.env` — give the exact model id per channel, including the OpenRouter id, e.g.:\n\n```bash\nLLM_BASE_URL=\"https://openrouter.ai/api/v1\"\nLLM_API_KEY=\"...\"\nLLM_MODEL_NAME=\"<exact-openrouter-id>\"\n```\n\n2. Tell them to run the demo themselves: `npm run dev:demo` (serves on port 5174) and exercise the agent against a real page.\n\nThe live test only proves a single forced tool call round-trips; it is not an agent-quality eval. The user's manual run is the real acceptance test.\n",".agents/skills/pre-impl-discussion/SKILL.md":"---\nname: pre-impl-discussion\ndescription: \"Conduct a thorough pre-implementation discussion before making significant changes. Use when the user wants to discuss, plan, or evaluate a change before implementing it — especially when they say words like 'discuss', 'evaluate', 'plan', or 'let's talk about'.\"\nargument-hint: 'Describe the change to evaluate'\n---\n\n# Pre-Implementation Discussion\n\nFacilitate a structured, collaborative discussion to evaluate a proposed change **before any implementation begins**.\n\n## Golden Rule\n\n**Do NOT implement anything until the user explicitly confirms the final plan and says to proceed.** Not even \"let me try on a branch\" — that's implementation. The user will tell you when discussion is over.\n\n## Interaction Style\n\n- **Concise during discussion.** Each intermediate response should be short and focused on the current question. Do NOT repeat the full plan in every response.\n- **Complete when finalizing.** Once the plan is mature and the user asks for it, present a single comprehensive (but terse) summary for final review.\n- **Ask, don't assume.** When uncertain about project context, constraints, or preferences — ask. The user prefers collaborative discussion over receiving a pre-baked answer.\n- **Challenge the premise.** Question whether the proposed change is the right one. Suggest simpler alternatives if they exist.\n- **Match the user's language.** Reply in the same language the user writes in.\n\n## Procedure\n\n### 1. Understand the Current State\n\nBefore forming any opinion:\n\n- **Read the relevant source files** — configs, code, docs that relate to the change\n- **Understand the project structure** — what's published vs private, what environments things target, existing constraints\n- **Map the impact surface** — which files, packages, or systems would be affected\n- **Estimate implementation cost** — how many files to touch, how much config to rewrite, what could break\n\nDo NOT skip this step. Do NOT rely on assumptions about what \"most projects\" do.\n\n### 2. Research Thoroughly\n\n- **Consult official sources** — docs, changelogs, migration guides, GitHub issues\n- **Verify every claim with evidence.** Don't say \"X supports Y\" without a source. If unsure, say so.\n- **Search in parallel** to save time — batch independent queries\n\nCommon pitfalls:\n\n- Assuming compatibility without checking actual version constraints\n- Confusing roadmap/aspirations with actual released state\n- Missing transitive constraints (a dependency of a dependency)\n\n### 3. Present Findings (Concise)\n\nShare a **brief** assessment. Tables work well for comparisons. Highlight **blockers** and **unknowns** prominently — don't bury them in paragraphs.\n\n### 4. Identify Key Decisions\n\nSurface the decisions the user needs to make. Present them as clear choices with trade-offs, not as a recommendation monologue.\n\nFor each decision point:\n\n- What are the options? (2-3 max)\n- What does each option cost or give up?\n- What's your lean and why? (one sentence)\n\n### 5. Iterate\n\nThe user will ask follow-up questions, raise concerns, or challenge assumptions. For each round:\n\n- **Research if needed** — don't answer from instinct when facts are available\n- **Answer only what was asked** — don't re-present the entire plan\n- **Update your mental model** based on user feedback\n\nCommon mistakes in this phase:\n\n- Repeating the full plan after every small clarification\n- Answering a narrow question with a broad redesign\n- Treating user questions as confirmation to proceed\n\n### 6. Finalize the Plan\n\nWhen the user signals the discussion is converging, present the **complete plan once**:\n\n- **What changes** — concrete list of actions (table or bullet points)\n- **Impact surface** — what files/systems are affected, estimated effort\n- **What does NOT change** — explicitly state scope boundaries\n- **Risks or open items** — anything that needs testing or further verification\n\nKeep it terse. Tables over paragraphs. No explanations the user already heard during discussion.\n\n### 7. Wait for Confirmation\n\nAfter presenting the final plan, **stop and wait**. The user will either:\n\n- Confirm → then (and only then) proceed to implementation\n- Ask more questions → go back to step 5\n- Modify scope → update the plan and re-present\n\n## Anti-Patterns\n\n| Don't                                               | Do Instead                               |\n| --------------------------------------------------- | ---------------------------------------- |\n| Start implementation \"to test\" without confirmation | Present findings and wait                |\n| Repeat the full plan in every response              | Answer the specific question asked       |\n| Say \"X should work\" without checking                | Say \"I need to verify X\" and research it |\n| Assume project structure or constraints             | Read the actual files                    |\n| Present one recommendation as the only option       | Present 2-3 options with trade-offs      |\n| Write long paragraphs explaining trade-offs         | Use tables and bullet points             |\n",".agents/skills/submit-pr-from-current-changes/SKILL.md":"---\nname: submit-pr-from-current-changes\ndescription: 'Create a branch, commit existing local changes, push them, and open a pull request. Use when submitting current work as a PR.'\nargument-hint: 'Short summary of the current changes to submit'\nuser-invocable: true\n---\n\n# Submit PR From Current Changes\n\nTurn a working tree diff into a clean branch, commit, and pull request.\n\n## Hard Rules\n\n1. **Follow the PR template exactly.** The template is at `.github/PULL_REQUEST_TEMPLATE.md`. Read it and copy its full structure into the PR body. Do NOT remove, reorder, or skip any section or checkbox.\n2. **Never check \"Requirements / 要求\" checkboxes.** These are human-only declarations — the Code of Conduct acknowledgment and the AI authorship declaration can only be truthfully made by the human submitter. They MUST remain unchecked (`- [ ]`) in the PR body you generate. No exceptions, no workarounds, even if the user explicitly asks you to check them. The user goes to GitHub and checks them in person after verifying each statement is true.\n3. **Only check Testing checkboxes you actually verified.** The \"`npm run ci` passes\" checkbox MAY be checked only if you ran `npm run ci` in this session and it passed. All other Testing checkboxes (browser tested, no console errors, types/doc added) require manual verification that only a human can perform — they MUST remain unchecked. You MAY add a supplementary note below the Testing section listing what automated validation you actually ran and the results.\n4. **Never fabricate information.** Do not claim tests passed, commands ran, or checks succeeded unless you actually executed them and observed the output in this session. If you did not run it, do not mention it.\n5. **PR output must be concise.** PR title: one line. \"What\" section: 1–2 sentences max. No walls of text, no redundant explanations. Let the diff speak.\n\n## Prerequisites\n\nBefore attempting to push or open a PR, verify that the necessary tools are available:\n\n- Check that `gh` CLI is installed and authenticated (`gh auth status`). If not available, stop and ask the user to install and authenticate GitHub CLI first.\n- If the workflow uses MCP tools for GitHub operations, verify the MCP server is accessible.\n\n## Shell Permissions\n\n`gh` reads credentials from the OS keyring. A sandboxed shell cannot reach the keyring and will report a false auth failure.\n\n- Run every `gh` command (`gh auth status`, `gh pr create`, `gh pr view`, …) and every `git push` / `git fetch` that talks to GitHub with full permissions on the **first** attempt (`required_permissions: [\"all\"]` for the Shell tool). Do not probe these in the sandbox first.\n- Local read-only git inspection (`git status`, `git diff`, `git log`, `git branch`) may stay sandboxed.\n\n## Procedure\n\n1. **Read contribution rules.** Read `CONTRIBUTING.md` and any package-level instructions (e.g. `packages/*/AGENTS.md`) relevant to the changed files.\n2. **Inspect the diff.** Review changed files, scope, and impact. Understand what the change does before writing anything.\n3. **Inspect repo conventions.** Check recent branch names (`git branch -r --sort=-committerdate | head -20`), recent commit messages (`git log --oneline -20`), and the PR template.\n4. **Pre-submission health checks** (run all before creating the PR):\n    - **Unified theme**: All changes should serve one purpose. If unrelated changes are mixed in, ask the user to split or confirm.\n    - **Commit hygiene**: Every new commit must follow the repo's conventional commit style. Squash or reword if needed.\n    - **Author identity**: Verify `git config user.name` and `git config user.email` are set and the email looks real (not empty, not `noreply` unless intentional). Warn the user if not.\n    - **No leftover artifacts**: Check for debug logs, `.only` in tests, conflict markers, or temp files in the diff.\n    - **CI**: Run `npm run ci` and make sure it passes. Record results honestly.\n5. **Warn if the PR looks too hasty.** If any of these are true, pause and warn the user before proceeding:\n    - Large diff with no description of intent provided\n    - Changes touch core lib or extension (where vibe coding is prohibited per `CONTRIBUTING.md`)\n    - Multiple unrelated concerns in one diff\n    - No validation has been run at all\n    - Remind the user: _\"This project does not accept low-quality or AI-generated PRs without meaningful human review. Please review your changes carefully.\"_\n6. **Branch.** Check the current branch first:\n    - If already on a non-main feature branch with a valid name (matching `type/topic` convention), reuse it.\n    - If the branch name does not follow repo conventions (e.g. missing prefix, unclear topic), ask the user whether to rename or create a new one.\n    - If on `main` or a default branch, create a new branch from it with a short kebab-case name: prefix (`fix/`, `feat/`, `docs/`, `refactor/`, `chore/`) + concrete topic words.\n7. **Stage and commit.** Stage only the intended files. Use `type(scope): subject` format. Keep the subject specific and compact.\n8. **Push.** Push with `-u origin HEAD`.\n9. **Open PR.** Use `gh pr create` with the full PR template structure. Fill in \"What\" and \"Type\" sections based on the actual diff. Check \"`npm run ci` passes\" only if it actually passed in this session; leave all other \"Testing\" and all \"Requirements\" checkboxes unchecked.\n10. **Report results.** Tell the user: branch name, commit hash, PR link, and what validation actually ran (with pass/fail).\n\n## Post-Submission Reminder\n\nAfter successfully opening the PR, ALWAYS give a brief reminder in the user's language. Keep it concise and natural, but make sure it clearly tells the user:\n\n1. They need to test the changes themselves in the browser.\n2. They need to go to the PR page and check the Testing and Requirements checkboxes only after verifying each item.\n3. The PR will not enter review until those checkboxes are checked.\n4. The project does not accept autonomously AI-generated PRs, so they should only check the AI declaration if it is truthful.\n\n## Branch Naming\n\n- Prefer short kebab-case names with a repo-consistent prefix such as `fix/`, `feat/`, `docs/`, `refactor/`, or `chore/`.\n- Match the change type first, then the smallest useful topic.\n- Prefer concrete topic words over issue text dumps.\n\n## Commit Style\n\n- Use the repository's prevailing commit style (conventional commits).\n- Use `type(scope): subject` when scope is clear from the changed area.\n- Keep the subject specific and compact.\n- If multiple commits exist on the branch, each one must independently follow conventions.\n\n## Validation Strategy\n\n- Default to enough validation to defend the PR, not the absolute minimum.\n- Always run `npm run ci` (build, lint, typecheck, tests) before opening the PR. It must pass.\n- Escalate to broader validation when the diff crosses packages, changes shared code, or affects release behavior.\n- Never claim checks that did not actually run.\n- You MAY note what you ran and the results below the Testing section in the PR body, but do NOT check the template checkboxes.\n\n## Decision Points\n\n- No local changes → stop, say so.\n- `gh` CLI not available → stop, ask user to install it.\n- Unrelated files mixed → stage only the intended subset or ask whether the work should be split.\n- If the repo has area-specific instructions, read them before naming the branch or writing the PR.\n- Change is small but high-risk → prefer broader validation.\n- Change is narrowly scoped and low-risk → run the most relevant checks, not arbitrary unrelated ones.\n- Template has declarations you cannot truthfully check → leave unchecked, tell the user why.\n\n## Completion Checks\n\n- The branch exists remotely and tracks upstream.\n- The commit message(s) match repo style.\n- All commit authors have proper name and email configured.\n- The PR title matches the commit intent.\n- The PR body follows the template structure completely.\n- All \"Requirements\" checkboxes and all human-only \"Testing\" checkboxes are unchecked (`- [ ]`) in the PR body. The only checkbox you may check is \"`npm run ci` passes\", and only if it truly passed.\n- The reported validation is accurate — nothing fabricated.\n- The post-submission reminder was delivered in the user's language, concisely and accurately.\n",".agents/skills/update-changelog/SKILL.md":"---\nname: update-changelog\ndescription: 'Update docs/CHANGELOG.md from git history, GitHub releases, and code diffs. Use when: writing release notes, syncing the latest changelog entry, summarizing a new tag, or keeping changelog wording concise and consistent.'\nargument-hint: 'Describe the target, for example: latest version only'\n---\n\n# Update Changelog\n\nUpdate `docs/CHANGELOG.md` from repository evidence instead of guesswork.\n\n## When to Use\n\n- Add the newest release entry to `docs/CHANGELOG.md`\n- Sync changelog text with GitHub Releases\n- Summarize the latest tag from git history or code diffs\n\n## Defaults\n\n- Keep the wording brief\n- Only add the latest missing version unless the user explicitly asks to backfill older releases\n- Prefer GitHub sources first, especially Releases and tag metadata\n- Skip `Features` / `Improvements` / `Bug Fixes` headings when the release only has a few clear items\n\n## Procedure\n\n### 1. Read the local style\n\n- Open `docs/CHANGELOG.md`\n- Match the existing tone, bullet style, section ordering, and date format\n\n### 2. Determine the target release\n\n- Read the root `package.json` version and compare it with the top changelog entry\n- Find the previous tag for the target version\n- If the latest version is already documented, stop and report that no changelog update is needed\n\n### 3. Gather evidence\n\nPrefer these sources in order:\n\n1. GitHub release notes\n\n```bash\nGH_PAGER=cat gh release view v<version> --repo <owner>/<repo> --json tagName,name,publishedAt,body\n```\n\n2. Tag date\n\n```bash\ngit log -1 --format=%cs v<version>\n```\n\n3. Commit history between tags\n\n```bash\ngit --no-pager log --format='%h %s' --no-merges v<previous>..v<version>\n```\n\n4. Diff scope when commit subjects are vague\n\n```bash\ngit --no-pager diff --name-only v<previous>..v<version>\ngit --no-pager diff --stat v<previous>..v<version>\n```\n\n5. Read touched files directly only when the user-visible change is still unclear\n\n### 4. Distill what belongs in the changelog\n\nInclude:\n\n- User-visible features\n- Important behavior changes\n- Bug fixes that improve reliability, compatibility, or developer experience\n- Small docs updates only when they materially change supported setups or onboarding\n\nExclude unless explicitly requested:\n\n- Pure version bumps\n- Routine dependency updates\n- Internal refactors with no visible impact\n- Mechanical formatting noise\n\n### 5. Choose the structure\n\n- If the release has 1 to 3 clear points, write flat bullets directly under the version heading\n- If the release has several distinct items, use short headings such as `### Features`, `### Improvements`, and `### Bug Fixes`\n- Do not force categories when they make the entry longer or noisier\n\n### 6. Write the entry\n\n- Add or update only the requested release entry\n- Keep bullets short and concrete\n- Reuse project terminology from nearby entries\n- Avoid marketing language\n- Do not copy noisy GitHub release text verbatim\n\n### 7. Verify\n\n- Check dates, version numbers, and tag boundaries\n- Ensure Markdown structure matches nearby entries\n- Confirm no intermediate versions were added unless requested\n\n## Completion Checks\n\n- The newest requested version is documented\n- The wording is concise and consistent with the surrounding changelog\n- The entry is backed by GitHub releases, git history, or code diff evidence\n- Low-signal internal changes were left out\n","AGENTS.md":"# Instructions for Coding Assistants\n\n## Project Overview\n\nThis is a **monorepo** with npm workspaces:\n\n- **Page Agent** (`packages/page-agent/`) - Main entry with built-in UI Panel, published as `page-agent` on npm\n- **Extension** (`packages/extension/`) - Browser extension (WXT + React)\n- **Website** (`packages/website/`) - React docs and landing page. **When working on website, follow `packages/website/AGENTS.md`**\n\nInternal packages:\n\n- **Core** (`packages/core/`) - PageAgentCore without UI (npm: `@page-agent/core`)\n- **LLMs** (`packages/llms/`) - LLM client with reflection-before-action mental model\n- **Page Controller** (`packages/page-controller/`) - DOM operations and visual feedback (SimulatorMask), independent of LLM\n- **UI** (`packages/ui/`) - Panel and i18n. Decoupled from PageAgent\n\n## Development Commands\n\n```bash\nnpm start                      # Start website dev server\nnpm run build                  # Build all packages\nnpm run build:libs             # Build all libraries\nnpm run build:ext              # Build and zip the extension package\nnpm run typecheck              # Typecheck all packages\nnpm run test                   # Run unit tests across all workspaces\nnpm run lint                   # ESLint\n```\n\n## Architecture\n\n### Monorepo Structure\n\nSource-first monorepo: library `package.json` exports point to `src/*.ts` during development. At publish time, `scripts/pre-publish.js` promotes `publishConfig` fields to top-level (swapping to `dist/`), and `scripts/post-publish.js` restores the originals.\n\n```\npackages/\n├── core/                    # npm: \"@page-agent/core\" ⭐ Core agent logic (headless)\n├── page-agent/              # npm: \"page-agent\" entry class (with UI + controller + demo builds)\n├── website/                 # @page-agent/website (private)\n├── llms/                    # @page-agent/llms\n├── extension/               # Browser extension\n├── page-controller/         # @page-agent/page-controller\n└── ui/                      # @page-agent/ui\n```\n\n`workspaces` in `package.json` must be in topological order.\n\n### Module Boundaries\n\n- **Page Agent**: Main entry with UI. Extends PageAgentCore and adds Panel. Imports from `@page-agent/core`, `@page-agent/ui`\n- **Core**: PageAgentCore without UI. Imports from `@page-agent/llms`, `@page-agent/page-controller`\n- **LLMs**: LLM client with MacroToolInput contract. No dependency on page-agent\n- **UI**: Panel and i18n. Decoupled from PageAgent via PanelAgentAdapter interface\n- **Page Controller**: DOM operations with optional visual feedback (SimulatorMask). No LLM dependency. Enable mask via `enableMask: true` config\n\n### PageController ↔ PageAgent Communication\n\nAll communication is async and isolated:\n\n```typescript\n// PageAgent delegates DOM operations to PageController\nawait this.pageController.updateTree()\nawait this.pageController.clickElement(index)\nawait this.pageController.inputText(index, text)\nawait this.pageController.scroll({ down: true, numPages: 1 })\n\n// PageController exposes state via async methods\nconst simplifiedHTML = await this.pageController.getSimplifiedHTML()\nconst pageInfo = await this.pageController.getPageInfo()\n```\n\n### DOM Pipeline\n\n1. **DOM Extraction**: Live DOM → `FlatDomTree` via `page-controller/src/dom/dom_tree/`\n2. **Dehydration**: DOM tree → simplified text for LLM\n3. **LLM Processing**: AI returns action plans (page-agent)\n4. **Indexed Operations**: PageAgent calls PageController by element index\n\n## Key Files Reference\n\n### Page Agent (`packages/page-agent/`)\n\n| File               | Description                                  |\n| ------------------ | -------------------------------------------- |\n| `src/PageAgent.ts` | ⭐ Main class with UI, extends PageAgentCore |\n| `src/demo.ts`      | IIFE demo entry (auto-init with demo API)    |\n\n### Core (`packages/core/`)\n\n| File                   | Description                             |\n| ---------------------- | --------------------------------------- |\n| `src/PageAgentCore.ts` | ⭐ Core agent class without UI          |\n| `src/tools/`           | Tool definitions calling PageController |\n| `src/config/`          | Configuration types and constants       |\n| `src/prompts/`         | System prompt templates                 |\n\n### LLMs (`packages/llms/`)\n\n| File                  | Description                           |\n| --------------------- | ------------------------------------- |\n| `src/index.ts`        | ⭐ LLM class with retry logic         |\n| `src/types.ts`        | MacroToolInput, AgentBrain, LLMConfig |\n| `src/OpenAIClient.ts` | OpenAI-compatible client              |\n\n### Page Controller (`packages/page-controller/`)\n\n| File                        | Description                                                |\n| --------------------------- | ---------------------------------------------------------- |\n| `src/PageController.ts`     | ⭐ Main controller class with optional mask support        |\n| `src/SimulatorMask.ts`      | Visual overlay blocking user interaction during automation |\n| `src/actions.ts`            | Element interactions (click, input, scroll)                |\n| `src/dom/dom_tree/index.js` | Core DOM extraction engine                                 |\n\n## Adding New Features\n\n### New Agent Tool\n\n1. Implement in `packages/core/src/tools/index.ts`\n2. If tool needs DOM ops, add method to PageController first\n3. Tool calls `this.pageController.methodName()` for DOM interactions\n\n### New PageController Action\n\n1. Add implementation in `packages/page-controller/src/actions.ts`\n2. Expose via async method in `PageController.ts`\n3. Export from `packages/page-controller/src/index.ts`\n\n## Testing\n\n- **Test value**: Do not add redundant tests or directly translate implementation code into assertions. Test meaningful behavior and realistic failure modes.\n- **Framework**: Vitest (unit tests only for now; future E2E goes to `packages/e2e/` with Playwright)\n- **Location**: co-located, `src/foo.test.ts` next to `src/foo.ts`\n- **Coverage today**: `packages/llms` only — other packages will follow incrementally\n- **Adding tests to a new package**: create `vitest.config.ts` in the package and add a `\"test\": \"vitest run\"` script. Root `npm test` and `node scripts/ci.js` pick it up through npm workspaces.\n- **Live tests** (hit real external APIs, slow/costly): name them `*.live.test.ts`, exclude them from the package's `test` script, and expose them via a `test:live` script. Root `npm run test:live` runs all of them; they never run in `npm test` or CI. Template: `packages/llms`.\n- **Template**: See @page-agent/llms\n\n```bash\nnpm test                            # all packages with a test script\nnpm test -w @page-agent/llms        # single package\ncd packages/llms && npx vitest      # watch mode in one package\n```\n\n## Code Standards\n\n- Explicit typing for exported/public APIs\n- ESLint relaxes some unsafe rules for rapid iteration\n- Every change you make should not only implement the desired functionality but also improve the quality of the codebase\n- All code and comments must be in English.\n- Do not try to hide errors or risks. They are valuable feedbacks for developers and users. Make them visible and actionable.\n- Traceability and predictability is more important than success rate.\n","CLAUDE.md":"@AGENTS.md\n","packages/extension/docs/extension_api.md":"# Page Agent Extension API\n\nIntegrate the Page Agent extension into your web app and trigger multi-page browser tasks from page JavaScript.\n\n## Installation\n\n### 1. Install the browser extension\n\nPrimary channel:\n\n- Chrome Web Store: https://chromewebstore.google.com/detail/page-agent-ext/akldabonmimlicnjlflnapfeklbfemhj\n\nLatest updates are often published earlier on:\n\n- GitHub Releases: https://github.com/alibaba/page-agent/releases\n\n### 2. Install type definitions (recommended)\n\n```bash\nnpm install @page-agent/core --save-dev\n```\n\n### 3. Authorization (Token)\n\nThe token allows your page JS to call the extension API (`window.PAGE_AGENT_EXT`) and execute multi-page tasks.\n\nWhy token-based access is required:\n\n- The extension has broad browser permissions (page access, navigation, multi-tab control).\n- If abused, it can harm user privacy and security.\n- Users must explicitly provide the token only to applications they trust.\n\nSetup:\n\n1. Open the extension side panel and copy your auth token.\n2. Set the token in your page:\n\n```typescript\nlocalStorage.setItem('PageAgentExtUserAuthToken', 'your-token')\n```\n\n## Quick Start\n\n```typescript\nimport type { AgentActivity, AgentStatus, ExecutionResult, HistoricalEvent } from '@page-agent/core'\n\n// Wait for extension injection (up to 1 second)\nasync function waitForExtension(timeout = 1000): Promise<boolean> {\n    const start = Date.now()\n    while (Date.now() - start < timeout) {\n        if (window.PAGE_AGENT_EXT) return true\n        await new Promise((r) => setTimeout(r, 100))\n    }\n    return false\n}\n\n// Usage\nif (await waitForExtension()) {\n    const result = await window.PAGE_AGENT_EXT!.execute('Click the login button', {\n        baseURL: 'https://api.openai.com/v1',\n        apiKey: 'your-api-key',\n        model: 'gpt-5.2',\n        onStatusChange: (status) => console.log('Status:', status),\n        onActivity: (activity) => console.log('Activity:', activity),\n    })\n    console.log('Result:', result)\n}\n```\n\n## Global API\n\nAfter token match, the extension injects APIs into `window`.\n\n### `window.PAGE_AGENT_EXT_VERSION`\n\nExtension version string (for capability checks before using the main API).\n\n### `window.PAGE_AGENT_EXT`\n\nMain namespace object.\n\n#### `PAGE_AGENT_EXT.execute(task, config)`\n\nExecute one agent task.\n\nParameters:\n\n| Name     | Type            | Required | Description                          |\n| -------- | --------------- | -------- | ------------------------------------ |\n| `task`   | `string`        | Yes      | Task description                     |\n| `config` | `ExecuteConfig` | Yes      | LLM settings, options, and callbacks |\n\nReturns: `Promise<ExecutionResult>`\n\n#### `PAGE_AGENT_EXT.stop()`\n\nStop the current task.\n\n## Limitations\n\n- **Normal browser windows only.** This extension relies on tab group API which does not work for pop-up window or PWA App window.\n\n## Types\n\nInstall `@page-agent/core` for complete types:\n\n```typescript\nimport type { AgentActivity, AgentStatus, ExecutionResult, HistoricalEvent } from '@page-agent/core'\n\nexport interface ExecuteConfig {\n    baseURL: string\n    model: string\n    apiKey?: string\n\n    // Global system-level instructions for the agent.\n    // Equivalent to AgentConfig.instructions.system.\n    systemInstruction?: string\n\n    // Include the initial tab where page JS starts. Default: true.\n    includeInitialTab?: boolean\n\n    // Control all unpinned tabs in the window instead of only the tab group.\n    // When enabled, agent sees and can switch to every non-pinned tab.\n    // Default: false. Experimental.\n    experimentalIncludeAllTabs?: boolean\n\n    onStatusChange?: (status: AgentStatus) => void\n    onActivity?: (activity: AgentActivity) => void\n    onHistoryUpdate?: (history: HistoricalEvent[]) => void\n}\n\nexport type Execute = (task: string, config: ExecuteConfig) => Promise<ExecutionResult>\n```\n\n`AgentStatus`\n\n```typescript\ntype AgentStatus = 'idle' | 'running' | 'completed' | 'error' | 'stopped'\n```\n\n`AgentActivity`\n\n```typescript\ntype AgentActivity =\n    | { type: 'thinking' }\n    | { type: 'executing'; tool: string; input: unknown }\n    | { type: 'executed'; tool: string; input: unknown; output: string; duration: number }\n    | { type: 'retrying'; attempt: number; maxAttempts: number }\n    | { type: 'error'; message: string }\n```\n\n`HistoricalEvent`\n\n```typescript\ntype HistoricalEvent =\n    | { type: 'step'; stepIndex: number; reflection: AgentReflection; action: Action }\n    | { type: 'observation'; content: string }\n    | { type: 'user_takeover' }\n    | { type: 'retry'; message: string; attempt: number; maxAttempts: number }\n    | { type: 'error'; message: string; rawResponse?: unknown }\n```\n\n`ExecutionResult`\n\n```typescript\ninterface ExecutionResult {\n    success: boolean\n    data: string\n    history: HistoricalEvent[]\n}\n```\n\n## Usage Examples\n\n### Basic Execution\n\n```typescript\nconst result = await window.PAGE_AGENT_EXT!.execute(\n    'Fill in the email field with test@example.com and click Submit',\n    {\n        baseURL: 'https://api.openai.com/v1',\n        apiKey: process.env.OPENAI_API_KEY!,\n        model: 'gpt-5.2',\n        includeInitialTab: false, // Optional: exclude current tab\n        onStatusChange: (status) => console.log(status),\n        onActivity: (activity) => console.log(activity),\n    }\n)\n```\n\n### Stop the Current Task\n\n```typescript\nwindow.PAGE_AGENT_EXT!.stop()\n```\n\n## Window Type Declaration\n\nIf you are not importing `@page-agent/core`, add:\n\n```typescript\nimport type { AgentActivity, AgentStatus, ExecutionResult, HistoricalEvent } from '@page-agent/core'\n\ninterface ExecuteConfig {\n    baseURL: string\n    model: string\n    apiKey?: string\n\n    systemInstruction?: string\n\n    includeInitialTab?: boolean\n    experimentalIncludeAllTabs?: boolean\n    onStatusChange?: (status: AgentStatus) => void\n    onActivity?: (activity: AgentActivity) => void\n    onHistoryUpdate?: (history: HistoricalEvent[]) => void\n}\n\ndeclare global {\n    interface Window {\n        PAGE_AGENT_EXT_VERSION?: string\n        PAGE_AGENT_EXT?: {\n            version: string\n            execute: Execute\n            stop: () => void\n        }\n    }\n}\n```\n","packages/mcp/README.md":"# @page-agent/mcp\n\nMCP server that lets AI agent clients (Claude Desktop, Copilot, etc.) control your browser through the [Page Agent](https://github.com/alibaba/page-agent) extension.\n\n## Prerequisites\n\n- Node.js >= 20\n- [Page Agent Extension](https://chromewebstore.google.com/detail/page-agent-ext/akldabonmimlicnjlflnapfeklbfemhj) installed in Chrome\n- An LLM API key (OpenAI-compatible)\n\n## Installation\n\n### Claude Desktop\n\nAdd to `~/Library/Application Support/Claude/claude_desktop_config.json`:\n\n```json\n{\n    \"mcpServers\": {\n        \"page-agent\": {\n            \"command\": \"npx\",\n            \"args\": [\"-y\", \"@page-agent/mcp\"],\n            \"env\": {\n                \"LLM_BASE_URL\": \"https://dashscope.aliyuncs.com/compatible-mode/v1\",\n                \"LLM_API_KEY\": \"sk-xxx\",\n                \"LLM_MODEL_NAME\": \"qwen3.5-plus\"\n            }\n        }\n    }\n}\n```\n\n### Cursor / Copilot\n\nSame format — add the config to the MCP settings of your client.\n\n## MCP Tools\n\n| Tool           | Input              | Description                                           |\n| -------------- | ------------------ | ----------------------------------------------------- |\n| `execute_task` | `{ task: string }` | Execute a browser task in natural language. Blocking. |\n| `get_status`   | —                  | Returns `{ connected, busy }`                         |\n| `stop_task`    | —                  | Stop the currently running task.                      |\n\n## Environment Variables\n\n| Variable         | Default | Description           |\n| ---------------- | ------- | --------------------- |\n| `LLM_BASE_URL`   | —       | LLM API base URL      |\n| `LLM_API_KEY`    | —       | LLM API key           |\n| `LLM_MODEL_NAME` | —       | Model name            |\n| `PORT`           | `38401` | HTTP + WebSocket port |\n\n## How It Works\n\n```\n┌──────────────┐  stdio   ┌──────────────────┐  WebSocket   ┌──────────────┐\n│ Claude /     │◄────────►│ @page-agent/mcp  │◄────────────►│ Hub tab      │\n│ Copilot      │  (MCP)   │ (Node.js)        │  (localhost) │ (extension)  │\n└──────────────┘          └──────────────────┘              └──────┬───────┘\n                                   │                               │\n                                   │ HTTP                          │ useAgent\n                                   ▼                               ▼\n                          ┌──────────────────┐              ┌──────────────┐\n                          │ Launcher page    │              │ MultiPage    │\n                          │ (localhost:PORT) │              │ Agent        │\n                          └──────────────────┘              └──────────────┘\n```\n\n1. Agent client starts the MCP server via stdio (`npx @page-agent/mcp`).\n2. Server starts HTTP + WS on `localhost:PORT`, opens the launcher page in browser.\n3. Launcher page triggers the extension to open a **hub tab** (`hub.html?ws=PORT`).\n4. Hub connects to the WS server. MCP tools now proxy tasks to the hub.\n\nThe hub tab speaks a generic WebSocket protocol (defined in `hub-ws.ts` in the extension package) and has no knowledge of MCP. See the hub's protocol docs for message format details.\n\n## Architecture\n\nPure JS ESM, no build step. Source files are the published artifacts.\n\n```\nsrc/\n├── index.js        # CLI entry: MCP server (stdio) + opens launcher\n├── hub-bridge.js   # HTTP server + WebSocket bridge to hub tab\n└── launcher.html   # Bootstrap page: detects extension, triggers hub open\n```\n\n## Dev\n\n```bash\nnpm run dev:ext\nnpx @modelcontextprotocol/inspector node packages/mcp/src/index.js\n```\n","packages/website/AGENTS.md":"# Website Package - Instructions for Coding Assistants\n\n## Tech Stack\n\n- **React** with TypeScript\n- **Vite** for dev server and build\n- **Tailwind CSS** for styling\n- **shadcn/ui** (new-york style) for UI components — **do NOT hand-edit `src/components/ui/` files**\n- **Magic UI** for animations and effects\n- **wouter** with browser routing (`base: \"/page-agent\"`)\n- **lucide-react** for icons\n\n## Component Guidelines\n\n### Use shadcn/ui Components First\n\n**ALWAYS prefer shadcn/ui components over custom implementations.**\n\nBefore creating any UI component, check if shadcn already provides it:\n\n```bash\n# IMPORTANT: Run from packages/website/, NOT from repo root\ncd packages/website\n\n# Add a new shadcn component\nnpx shadcn@latest add <component-name>\n\n# Add a Magic UI component\nnpx shadcn@latest add \"@magicui/<component-name>\"\n```\n\nAvailable shadcn components: https://ui.shadcn.com/docs/components\nAvailable Magic UI components: https://magicui.design/docs/components\n\n### Current UI Components\n\nLocated in `src/components/ui/`:\n\n**From shadcn/ui:**\n\n- `alert`, `badge`, `button`, `separator`, `sonner`, `switch`, `tooltip`\n\n**From Magic UI:**\n\n- `animated-gradient-text`, `animated-shiny-text`, `aurora-text`\n- `hyper-text`, `magic-card`, `neon-gradient-card`, `particles`\n- `sparkles-text`, `text-animate`, `typing-animation`\n\n**Custom:**\n\n- `highlighter`, `kbd`, `spinner`\n\n### Styling Rules\n\n1. **Prefer Tailwind classes** over custom CSS\n2. Support dark mode via `dark:` classes\n3. Use CSS variables from `src/index.css` for theme colors\n\n## Project Structure\n\n```\nsrc/\n├── pages/\n│   ├── home/\n│   │   ├── index.tsx  # Homepage\n│   │   └── ...Section.tsx\n│   └── docs/\n│       ├── index.tsx    # Docs route switch\n│       ├── Layout.tsx   # Sidebar navigation\n│       └── [section]/[topic]/page.tsx\n├── components/\n│   ├── ui/              # shadcn/ui + Magic UI (DO NOT hand-edit)\n│   ├── Heading.tsx      # Anchor heading for doc pages\n│   ├── Header.tsx       # Site header\n│   └── Footer.tsx       # Site footer\n├── i18n/                # Internationalization\n├── router.tsx           # Root layout + routing\n└── main.tsx             # App entry\n```\n\n## Routing\n\nUses wouter browser routing with base path for GitHub Pages deployment at `https://alibaba.github.io/page-agent/`.\n\n```tsx\n// main.tsx\n<Router base=\"/page-agent\">\n    <PagesRouter />\n</Router>\n```\n\n**Key rules:**\n\n- Header and Footer live in `router.tsx` **outside** `<Switch>`, so they always see the root router context (`base=\"/page-agent\"`)\n- Docs pages are nested via `<Route path=\"/docs\" nest>`, which creates a child context (`base=\"/page-agent/docs\"`)\n- Inside the docs nest, Link hrefs are relative to `/docs` (e.g. `href=\"/features/models\"`, NOT `href=\"/docs/features/models\"`)\n- **Never use `~` prefix** in Link hrefs — it bypasses the base path entirely\n- Doc page headings use `<Heading id=\"slug\" level={2}>` for anchor links\n\n### SPA on GitHub Pages\n\nInstead of `404.html` redirects, the build copies `index.html` into every route directory. Add new routes to the `SPA_ROUTES` array in `vite.config.js`.\n\n## Adding New Pages\n\n### Documentation Page\n\n1. Create `src/pages/docs/<section>/<slug>/page.tsx`\n2. Add route in `src/pages/docs/index.tsx`\n3. Add navigation item in `src/pages/docs/Layout.tsx`\n4. Add path to `SPA_ROUTES` in `vite.config.js`\n5. Keep `<slug>` consistent across the folder name, import path, route path, sidebar link, and any related cross-links. If the public route changes, rename the folder too.\n\n## Configuration Files\n\n| File              | Purpose                 |\n| ----------------- | ----------------------- |\n| `components.json` | shadcn/ui configuration |\n| `vite.config.js`  | Vite build + SPA routes |\n| `tsconfig.json`   | TypeScript config       |\n\n## Commands\n\n```bash\nnpm start            # Dev server (from root)\nnpm run build:website    # Build website (from root)\n```\n"},"items":[{"name":"AGENTS.md","path":"AGENTS.md","rawUrl":"https://raw.githubusercontent.com/alibaba/page-agent/HEAD/AGENTS.md","title":"AI Agent Protocol & Instructions","category":"root-instruction","format":"markdown","content":"# Instructions for Coding Assistants\n\n## Project Overview\n\nThis is a **monorepo** with npm workspaces:\n\n- **Page Agent** (`packages/page-agent/`) - Main entry with built-in UI Panel, published as `page-agent` on npm\n- **Extension** (`packages/extension/`) - Browser extension (WXT + React)\n- **Website** (`packages/website/`) - React docs and landing page. **When working on website, follow `packages/website/AGENTS.md`**\n\nInternal packages:\n\n- **Core** (`packages/core/`) - PageAgentCore without UI (npm: `@page-agent/core`)\n- **LLMs** (`packages/llms/`) - LLM client with reflection-before-action mental model\n- **Page Controller** (`packages/page-controller/`) - DOM operations and visual feedback (SimulatorMask), independent of LLM\n- **UI** (`packages/ui/`) - Panel and i18n. Decoupled from PageAgent\n\n## Development Commands\n\n```bash\nnpm start                      # Start website dev server\nnpm run build                  # Build all packages\nnpm run build:libs             # Build all libraries\nnpm run build:ext              # Build and zip the extension package\nnpm run typecheck              # Typecheck all packages\nnpm run test                   # Run unit tests across all workspaces\nnpm run lint                   # ESLint\n```\n\n## Architecture\n\n### Monorepo Structure\n\nSource-first monorepo: library `package.json` exports point to `src/*.ts` during development. At publish time, `scripts/pre-publish.js` promotes `publishConfig` fields to top-level (swapping to `dist/`), and `scripts/post-publish.js` restores the originals.\n\n```\npackages/\n├── core/                    # npm: \"@page-agent/core\" ⭐ Core agent logic (headless)\n├── page-agent/              # npm: \"page-agent\" entry class (with UI + controller + demo builds)\n├── website/                 # @page-agent/website (private)\n├── llms/                    # @page-agent/llms\n├── extension/               # Browser extension\n├── page-controller/         # @page-agent/page-controller\n└── ui/                      # @page-agent/ui\n```\n\n`workspaces` in `package.json` must be in topological order.\n\n### Module Boundaries\n\n- **Page Agent**: Main entry with UI. Extends PageAgentCore and adds Panel. Imports from `@page-agent/core`, `@page-agent/ui`\n- **Core**: PageAgentCore without UI. Imports from `@page-agent/llms`, `@page-agent/page-controller`\n- **LLMs**: LLM client with MacroToolInput contract. No dependency on page-agent\n- **UI**: Panel and i18n. Decoupled from PageAgent via PanelAgentAdapter interface\n- **Page Controller**: DOM operations with optional visual feedback (SimulatorMask). No LLM dependency. Enable mask via `enableMask: true` config\n\n### PageController ↔ PageAgent Communication\n\nAll communication is async and isolated:\n\n```typescript\n// PageAgent delegates DOM operations to PageController\nawait this.pageController.updateTree()\nawait this.pageController.clickElement(index)\nawait this.pageController.inputText(index, text)\nawait this.pageController.scroll({ down: true, numPages: 1 })\n\n// PageController exposes state via async methods\nconst simplifiedHTML = await this.pageController.getSimplifiedHTML()\nconst pageInfo = await this.pageController.getPageInfo()\n```\n\n### DOM Pipeline\n\n1. **DOM Extraction**: Live DOM → `FlatDomTree` via `page-controller/src/dom/dom_tree/`\n2. **Dehydration**: DOM tree → simplified text for LLM\n3. **LLM Processing**: AI returns action plans (page-agent)\n4. **Indexed Operations**: PageAgent calls PageController by element index\n\n## Key Files Reference\n\n### Page Agent (`packages/page-agent/`)\n\n| File               | Description                                  |\n| ------------------ | -------------------------------------------- |\n| `src/PageAgent.ts` | ⭐ Main class with UI, extends PageAgentCore |\n| `src/demo.ts`      | IIFE demo entry (auto-init with demo API)    |\n\n### Core (`packages/core/`)\n\n| File                   | Description                             |\n| ---------------------- | --------------------------------------- |\n| `src/PageAgentCore.ts` | ⭐ Core agent class without UI          |\n| `src/tools/`           | Tool definitions calling PageController |\n| `src/config/`          | Configuration types and constants       |\n| `src/prompts/`         | System prompt templates                 |\n\n### LLMs (`packages/llms/`)\n\n| File                  | Description                           |\n| --------------------- | ------------------------------------- |\n| `src/index.ts`        | ⭐ LLM class with retry logic         |\n| `src/types.ts`        | MacroToolInput, AgentBrain, LLMConfig |\n| `src/OpenAIClient.ts` | OpenAI-compatible client              |\n\n### Page Controller (`packages/page-controller/`)\n\n| File                        | Description                                                |\n| --------------------------- | ---------------------------------------------------------- |\n| `src/PageController.ts`     | ⭐ Main controller class with optional mask support        |\n| `src/SimulatorMask.ts`      | Visual overlay blocking user interaction during automation |\n| `src/actions.ts`            | Element interactions (click, input, scroll)                |\n| `src/dom/dom_tree/index.js` | Core DOM extraction engine                                 |\n\n## Adding New Features\n\n### New Agent Tool\n\n1. Implement in `packages/core/src/tools/index.ts`\n2. If tool needs DOM ops, add method to PageController first\n3. Tool calls `this.pageController.methodName()` for DOM interactions\n\n### New PageController Action\n\n1. Add implementation in `packages/page-controller/src/actions.ts`\n2. Expose via async method in `PageController.ts`\n3. Export from `packages/page-controller/src/index.ts`\n\n## Testing\n\n- **Test value**: Do not add redundant tests or directly translate implementation code into assertions. Test meaningful behavior and realistic failure modes.\n- **Framework**: Vitest (unit tests only for now; future E2E goes to `packages/e2e/` with Playwright)\n- **Location**: co-located, `src/foo.test.ts` next to `src/foo.ts`\n- **Coverage today**: `packages/llms` only — other packages will follow incrementally\n- **Adding tests to a new package**: create `vitest.config.ts` in the package and add a `\"test\": \"vitest run\"` script. Root `npm test` and `node scripts/ci.js` pick it up through npm workspaces.\n- **Live tests** (hit real external APIs, slow/costly): name them `*.live.test.ts`, exclude them from the package's `test` script, and expose them via a `test:live` script. Root `npm run test:live` runs all of them; they never run in `npm test` or CI. Template: `packages/llms`.\n- **Template**: See @page-agent/llms\n\n```bash\nnpm test                            # all packages with a test script\nnpm test -w @page-agent/llms        # single package\ncd packages/llms && npx vitest      # watch mode in one package\n```\n\n## Code Standards\n\n- Explicit typing for exported/public APIs\n- ESLint relaxes some unsafe rules for rapid iteration\n- Every change you make should not only implement the desired functionality but also improve the quality of the codebase\n- All code and comments must be in English.\n- Do not try to hide errors or risks. They are valuable feedbacks for developers and users. Make them visible and actionable.\n- Traceability and predictability is more important than success rate.\n","isInternal":false,"tokens":1621,"sizeBytes":7327},{"name":"CLAUDE.md","path":"CLAUDE.md","rawUrl":"https://raw.githubusercontent.com/alibaba/page-agent/HEAD/CLAUDE.md","title":"Claude Agent Guidelines & System Prompt","category":"claude-rule","format":"markdown","content":"@AGENTS.md\n","isInternal":false,"tokens":5,"sizeBytes":11},{"name":"SKILL.md","path":".agents/skills/git-cleanup/SKILL.md","rawUrl":"https://raw.githubusercontent.com/alibaba/page-agent/HEAD/.agents/skills/git-cleanup/SKILL.md","title":"git-cleanup","category":"anthropic-skill","format":"markdown","content":"---\nname: git-cleanup\ndescription: Clean up local git branches and remotes accumulated from PR reviews. Use when the user asks to clean branches, remove stale remotes, or tidy up the local git state.\n---\n\n# Git Cleanup\n\nClean local branches (except `main` and current branch) and non-origin remotes.\nUseful after reviewing multiple PRs that leave behind tracking branches and contributor remotes.\n\n## Workflow\n\n### 1. Survey\n\nRun these in parallel:\n\n```bash\ngit branch\ngit remote\ngit status --short\ngit stash list\n```\n\nPresent a summary of how many branches and remotes will be removed.\n\n### 2. Safety Checks — Ask Before Proceeding\n\n**Stop and ask for confirmation if any of these are true:**\n\n- Current branch is NOT `main`\n- Working tree has uncommitted changes or stashes\n- Any branch is ahead of its upstream\n\nList unmerged branches separately and let the user decide.\n\n### 3. Delete Branches\n\n```bash\n# Safe delete first (fails on unmerged branches)\ngit branch | grep -v '^\\*' | grep -v '^\\s*main$' | xargs git branch -d 2>&1\n\n# Force-delete only with explicit user approval\ngit branch -D <branch>\n```\n\n### 4. Remove Remotes\n\n```bash\ngit remote | grep -v '^origin$' | xargs -I{} git remote remove {}\n```\n\n### 5. Confirm\n\n```bash\ngit branch && echo \"---\" && git remote\n```\n\nReport what was cleaned up.\n\n## Key Rules\n\n- **NEVER** delete `main` or the current checked-out branch\n- **NEVER** force-delete unmerged branches without user confirmation\n- If in doubt, ask\n","frontmatter":{"name":"git-cleanup","description":"Clean up local git branches and remotes accumulated from PR reviews. Use when the user asks to clean branches, remove stale remotes, or tidy up the local git state."},"isInternal":false,"tokens":370,"sizeBytes":1472},{"name":"SKILL.md","path":".agents/skills/maintain-model-list/SKILL.md","rawUrl":"https://raw.githubusercontent.com/alibaba/page-agent/HEAD/.agents/skills/maintain-model-list/SKILL.md","title":"maintain-model-list","category":"anthropic-skill","format":"markdown","content":"---\nname: maintain-model-list\ndescription: 'Maintain the supported LLM model list: add a new model, or run routine maintenance to verify availability and discover new models worth adding. Use when the user asks to add/support a model, update the model list, or check model availability.'\nargument-hint: 'A model name to add (e.g. \"gpt-5.7\"), or empty for routine maintenance'\n---\n\n# Maintain Model List\n\nThe supported model list lives in three places that must stay in sync:\n\n| File                                                       | What it holds                                                                                                   |\n| ---------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------- |\n| `packages/website/src/pages/docs/features/models/page.tsx` | `MODEL_GROUPS` (public docs, source of truth for display names) and `BASELINE` (recommended models)             |\n| `packages/llms/src/models.live.test.ts`                    | Mirrored `MODEL_GROUPS`, plus `OPENROUTER_ID_OVERRIDES` / `ALIYUN_ID_OVERRIDES` for provider-specific model ids |\n| `packages/llms/src/utils.ts`                               | `modelPatch` — per-model-family request parameter fixes (thinking/reasoning flags, `tool_choice` quirks, etc.)  |\n\n## Before Touching Anything\n\n1. Run `git status`. If the working tree has uncommitted changes that look like unrelated in-progress work, do NOT edit files — report the situation and ask the user for permission first.\n2. Run the full live test suite as a baseline: `npm run test:live -w @page-agent/llms`. API keys come from the repo-root `.env` (`TESTING_OPENROUTER_KEY`, `TESTING_ALIYUN_KEY`, `TESTING_DEEPSEEK_KEY`); tests skip silently when a key is missing, so check which providers actually ran. Record which models pass/fail before making changes, so new failures are attributable.\n\n## Workflow A: A Specific Model Was Given\n\n1. **Research the model** (web search + provider docs):\n    - Is it served on OpenRouter? Fetch `https://openrouter.ai/api/v1/models` and find the exact id (`<vendor-slug>/<model-id>`, watch for `-preview`, dated snapshots, dots vs hyphens).\n    - Which other channels serve it (vendor native API, Aliyun DashScope, etc.) and what are their native model ids?\n    - API differences: can thinking/reasoning be disabled or minimized? Any `tool_choice`, `parallel_tool_calls`, or parameter-schema quirks? Does the vendor's OpenAI-compatible endpoint differ from OpenRouter's behavior?\n    - Agent suitability: tool call support is mandatory; note context window, latency, and cost.\n2. **Update `modelPatch`** in `packages/llms/src/utils.ts` if the model needs new parameter handling. Model names are matched after `normalizeModelName` (lowercased, `/`-prefix, `.` and `_` stripped) — check whether an existing family branch already covers it.\n3. **Add the model to both `MODEL_GROUPS` lists** (website page and live test), newest first within its brand group. Add id overrides if the OpenRouter/Aliyun id differs from the display name.\n4. **Test availability carefully.** Run the live suite and confirm the new model passes on every provider that serves it:\n\n```bash\nnpm run test:live -w @page-agent/llms\n```\n\nA failure means the request/response shape is wrong for that model — fix `modelPatch` or the id override, don't shrug it off. If the model fails consistently on a provider, exclude it from that provider (see the `deepseek-3.2` precedent in the test file) and document why in a comment. 5. **Decide on `BASELINE`** only if the model is a fast, cheap, strong-tool-call option; otherwise leave it out. 6. Run `npm run typecheck` and `npm test`.\n\n## Workflow B: No Model Given (Routine Maintenance)\n\n1. **Verify the existing list**: run the live suite (see above). Investigate any model that newly fails — deprecated? renamed? provider dropped it?\n2. **Check for drift**: re-verify `OPENROUTER_ID_OVERRIDES` against the current `https://openrouter.ai/api/v1/models` output; ids change when vendors promote previews to stable.\n3. **Scan for new models** worth adding (web search for recent releases from the brands in `MODEL_GROUPS`, plus notable newcomers). A candidate must support tool calls via an OpenAI-compatible API.\n4. **Act by significance**:\n    - Minor updates (a preview id went stable, a small point-release replaces its predecessor): apply the change yourself following Workflow A steps 2–6, then present it to the user for judgment.\n    - New models or removals: report findings and recommendations, let the user decide before editing.\n\n## Hand Off to the User\n\nAfter your own tests pass, always remind the user to verify manually:\n\n1. Tell them which line to put in the repo-root `.env` — give the exact model id per channel, including the OpenRouter id, e.g.:\n\n```bash\nLLM_BASE_URL=\"https://openrouter.ai/api/v1\"\nLLM_API_KEY=\"...\"\nLLM_MODEL_NAME=\"<exact-openrouter-id>\"\n```\n\n2. Tell them to run the demo themselves: `npm run dev:demo` (serves on port 5174) and exercise the agent against a real page.\n\nThe live test only proves a single forced tool call round-trips; it is not an agent-quality eval. The user's manual run is the real acceptance test.\n","frontmatter":{"name":"maintain-model-list","description":"Maintain the supported LLM model list: add a new model, or run routine maintenance to verify availability and discover new models worth adding. Use when the user asks to add/support a model, update the model list, or check model availability.","argument-hint":"A model name to add (e.g. \"gpt-5.7\"), or empty for routine maintenance"},"isInternal":false,"tokens":1184,"sizeBytes":5278},{"name":"SKILL.md","path":".agents/skills/pre-impl-discussion/SKILL.md","rawUrl":"https://raw.githubusercontent.com/alibaba/page-agent/HEAD/.agents/skills/pre-impl-discussion/SKILL.md","title":"pre-impl-discussion","category":"anthropic-skill","format":"markdown","content":"---\nname: pre-impl-discussion\ndescription: \"Conduct a thorough pre-implementation discussion before making significant changes. Use when the user wants to discuss, plan, or evaluate a change before implementing it — especially when they say words like 'discuss', 'evaluate', 'plan', or 'let's talk about'.\"\nargument-hint: 'Describe the change to evaluate'\n---\n\n# Pre-Implementation Discussion\n\nFacilitate a structured, collaborative discussion to evaluate a proposed change **before any implementation begins**.\n\n## Golden Rule\n\n**Do NOT implement anything until the user explicitly confirms the final plan and says to proceed.** Not even \"let me try on a branch\" — that's implementation. The user will tell you when discussion is over.\n\n## Interaction Style\n\n- **Concise during discussion.** Each intermediate response should be short and focused on the current question. Do NOT repeat the full plan in every response.\n- **Complete when finalizing.** Once the plan is mature and the user asks for it, present a single comprehensive (but terse) summary for final review.\n- **Ask, don't assume.** When uncertain about project context, constraints, or preferences — ask. The user prefers collaborative discussion over receiving a pre-baked answer.\n- **Challenge the premise.** Question whether the proposed change is the right one. Suggest simpler alternatives if they exist.\n- **Match the user's language.** Reply in the same language the user writes in.\n\n## Procedure\n\n### 1. Understand the Current State\n\nBefore forming any opinion:\n\n- **Read the relevant source files** — configs, code, docs that relate to the change\n- **Understand the project structure** — what's published vs private, what environments things target, existing constraints\n- **Map the impact surface** — which files, packages, or systems would be affected\n- **Estimate implementation cost** — how many files to touch, how much config to rewrite, what could break\n\nDo NOT skip this step. Do NOT rely on assumptions about what \"most projects\" do.\n\n### 2. Research Thoroughly\n\n- **Consult official sources** — docs, changelogs, migration guides, GitHub issues\n- **Verify every claim with evidence.** Don't say \"X supports Y\" without a source. If unsure, say so.\n- **Search in parallel** to save time — batch independent queries\n\nCommon pitfalls:\n\n- Assuming compatibility without checking actual version constraints\n- Confusing roadmap/aspirations with actual released state\n- Missing transitive constraints (a dependency of a dependency)\n\n### 3. Present Findings (Concise)\n\nShare a **brief** assessment. Tables work well for comparisons. Highlight **blockers** and **unknowns** prominently — don't bury them in paragraphs.\n\n### 4. Identify Key Decisions\n\nSurface the decisions the user needs to make. Present them as clear choices with trade-offs, not as a recommendation monologue.\n\nFor each decision point:\n\n- What are the options? (2-3 max)\n- What does each option cost or give up?\n- What's your lean and why? (one sentence)\n\n### 5. Iterate\n\nThe user will ask follow-up questions, raise concerns, or challenge assumptions. For each round:\n\n- **Research if needed** — don't answer from instinct when facts are available\n- **Answer only what was asked** — don't re-present the entire plan\n- **Update your mental model** based on user feedback\n\nCommon mistakes in this phase:\n\n- Repeating the full plan after every small clarification\n- Answering a narrow question with a broad redesign\n- Treating user questions as confirmation to proceed\n\n### 6. Finalize the Plan\n\nWhen the user signals the discussion is converging, present the **complete plan once**:\n\n- **What changes** — concrete list of actions (table or bullet points)\n- **Impact surface** — what files/systems are affected, estimated effort\n- **What does NOT change** — explicitly state scope boundaries\n- **Risks or open items** — anything that needs testing or further verification\n\nKeep it terse. Tables over paragraphs. No explanations the user already heard during discussion.\n\n### 7. Wait for Confirmation\n\nAfter presenting the final plan, **stop and wait**. The user will either:\n\n- Confirm → then (and only then) proceed to implementation\n- Ask more questions → go back to step 5\n- Modify scope → update the plan and re-present\n\n## Anti-Patterns\n\n| Don't                                               | Do Instead                               |\n| --------------------------------------------------- | ---------------------------------------- |\n| Start implementation \"to test\" without confirmation | Present findings and wait                |\n| Repeat the full plan in every response              | Answer the specific question asked       |\n| Say \"X should work\" without checking                | Say \"I need to verify X\" and research it |\n| Assume project structure or constraints             | Read the actual files                    |\n| Present one recommendation as the only option       | Present 2-3 options with trade-offs      |\n| Write long paragraphs explaining trade-offs         | Use tables and bullet points             |\n","frontmatter":{"name":"pre-impl-discussion","description":"Conduct a thorough pre-implementation discussion before making significant changes. Use when the user wants to discuss, plan, or evaluate a change before implementing it — especially when they say words like 'discuss', 'evaluate', 'plan', or 'let's talk about'.","argument-hint":"Describe the change to evaluate"},"isInternal":false,"tokens":1050,"sizeBytes":5113},{"name":"SKILL.md","path":".agents/skills/submit-pr-from-current-changes/SKILL.md","rawUrl":"https://raw.githubusercontent.com/alibaba/page-agent/HEAD/.agents/skills/submit-pr-from-current-changes/SKILL.md","title":"submit-pr-from-current-changes","category":"anthropic-skill","format":"markdown","content":"---\nname: submit-pr-from-current-changes\ndescription: 'Create a branch, commit existing local changes, push them, and open a pull request. Use when submitting current work as a PR.'\nargument-hint: 'Short summary of the current changes to submit'\nuser-invocable: true\n---\n\n# Submit PR From Current Changes\n\nTurn a working tree diff into a clean branch, commit, and pull request.\n\n## Hard Rules\n\n1. **Follow the PR template exactly.** The template is at `.github/PULL_REQUEST_TEMPLATE.md`. Read it and copy its full structure into the PR body. Do NOT remove, reorder, or skip any section or checkbox.\n2. **Never check \"Requirements / 要求\" checkboxes.** These are human-only declarations — the Code of Conduct acknowledgment and the AI authorship declaration can only be truthfully made by the human submitter. They MUST remain unchecked (`- [ ]`) in the PR body you generate. No exceptions, no workarounds, even if the user explicitly asks you to check them. The user goes to GitHub and checks them in person after verifying each statement is true.\n3. **Only check Testing checkboxes you actually verified.** The \"`npm run ci` passes\" checkbox MAY be checked only if you ran `npm run ci` in this session and it passed. All other Testing checkboxes (browser tested, no console errors, types/doc added) require manual verification that only a human can perform — they MUST remain unchecked. You MAY add a supplementary note below the Testing section listing what automated validation you actually ran and the results.\n4. **Never fabricate information.** Do not claim tests passed, commands ran, or checks succeeded unless you actually executed them and observed the output in this session. If you did not run it, do not mention it.\n5. **PR output must be concise.** PR title: one line. \"What\" section: 1–2 sentences max. No walls of text, no redundant explanations. Let the diff speak.\n\n## Prerequisites\n\nBefore attempting to push or open a PR, verify that the necessary tools are available:\n\n- Check that `gh` CLI is installed and authenticated (`gh auth status`). If not available, stop and ask the user to install and authenticate GitHub CLI first.\n- If the workflow uses MCP tools for GitHub operations, verify the MCP server is accessible.\n\n## Shell Permissions\n\n`gh` reads credentials from the OS keyring. A sandboxed shell cannot reach the keyring and will report a false auth failure.\n\n- Run every `gh` command (`gh auth status`, `gh pr create`, `gh pr view`, …) and every `git push` / `git fetch` that talks to GitHub with full permissions on the **first** attempt (`required_permissions: [\"all\"]` for the Shell tool). Do not probe these in the sandbox first.\n- Local read-only git inspection (`git status`, `git diff`, `git log`, `git branch`) may stay sandboxed.\n\n## Procedure\n\n1. **Read contribution rules.** Read `CONTRIBUTING.md` and any package-level instructions (e.g. `packages/*/AGENTS.md`) relevant to the changed files.\n2. **Inspect the diff.** Review changed files, scope, and impact. Understand what the change does before writing anything.\n3. **Inspect repo conventions.** Check recent branch names (`git branch -r --sort=-committerdate | head -20`), recent commit messages (`git log --oneline -20`), and the PR template.\n4. **Pre-submission health checks** (run all before creating the PR):\n    - **Unified theme**: All changes should serve one purpose. If unrelated changes are mixed in, ask the user to split or confirm.\n    - **Commit hygiene**: Every new commit must follow the repo's conventional commit style. Squash or reword if needed.\n    - **Author identity**: Verify `git config user.name` and `git config user.email` are set and the email looks real (not empty, not `noreply` unless intentional). Warn the user if not.\n    - **No leftover artifacts**: Check for debug logs, `.only` in tests, conflict markers, or temp files in the diff.\n    - **CI**: Run `npm run ci` and make sure it passes. Record results honestly.\n5. **Warn if the PR looks too hasty.** If any of these are true, pause and warn the user before proceeding:\n    - Large diff with no description of intent provided\n    - Changes touch core lib or extension (where vibe coding is prohibited per `CONTRIBUTING.md`)\n    - Multiple unrelated concerns in one diff\n    - No validation has been run at all\n    - Remind the user: _\"This project does not accept low-quality or AI-generated PRs without meaningful human review. Please review your changes carefully.\"_\n6. **Branch.** Check the current branch first:\n    - If already on a non-main feature branch with a valid name (matching `type/topic` convention), reuse it.\n    - If the branch name does not follow repo conventions (e.g. missing prefix, unclear topic), ask the user whether to rename or create a new one.\n    - If on `main` or a default branch, create a new branch from it with a short kebab-case name: prefix (`fix/`, `feat/`, `docs/`, `refactor/`, `chore/`) + concrete topic words.\n7. **Stage and commit.** Stage only the intended files. Use `type(scope): subject` format. Keep the subject specific and compact.\n8. **Push.** Push with `-u origin HEAD`.\n9. **Open PR.** Use `gh pr create` with the full PR template structure. Fill in \"What\" and \"Type\" sections based on the actual diff. Check \"`npm run ci` passes\" only if it actually passed in this session; leave all other \"Testing\" and all \"Requirements\" checkboxes unchecked.\n10. **Report results.** Tell the user: branch name, commit hash, PR link, and what validation actually ran (with pass/fail).\n\n## Post-Submission Reminder\n\nAfter successfully opening the PR, ALWAYS give a brief reminder in the user's language. Keep it concise and natural, but make sure it clearly tells the user:\n\n1. They need to test the changes themselves in the browser.\n2. They need to go to the PR page and check the Testing and Requirements checkboxes only after verifying each item.\n3. The PR will not enter review until those checkboxes are checked.\n4. The project does not accept autonomously AI-generated PRs, so they should only check the AI declaration if it is truthful.\n\n## Branch Naming\n\n- Prefer short kebab-case names with a repo-consistent prefix such as `fix/`, `feat/`, `docs/`, `refactor/`, or `chore/`.\n- Match the change type first, then the smallest useful topic.\n- Prefer concrete topic words over issue text dumps.\n\n## Commit Style\n\n- Use the repository's prevailing commit style (conventional commits).\n- Use `type(scope): subject` when scope is clear from the changed area.\n- Keep the subject specific and compact.\n- If multiple commits exist on the branch, each one must independently follow conventions.\n\n## Validation Strategy\n\n- Default to enough validation to defend the PR, not the absolute minimum.\n- Always run `npm run ci` (build, lint, typecheck, tests) before opening the PR. It must pass.\n- Escalate to broader validation when the diff crosses packages, changes shared code, or affects release behavior.\n- Never claim checks that did not actually run.\n- You MAY note what you ran and the results below the Testing section in the PR body, but do NOT check the template checkboxes.\n\n## Decision Points\n\n- No local changes → stop, say so.\n- `gh` CLI not available → stop, ask user to install it.\n- Unrelated files mixed → stage only the intended subset or ask whether the work should be split.\n- If the repo has area-specific instructions, read them before naming the branch or writing the PR.\n- Change is small but high-risk → prefer broader validation.\n- Change is narrowly scoped and low-risk → run the most relevant checks, not arbitrary unrelated ones.\n- Template has declarations you cannot truthfully check → leave unchecked, tell the user why.\n\n## Completion Checks\n\n- The branch exists remotely and tracks upstream.\n- The commit message(s) match repo style.\n- All commit authors have proper name and email configured.\n- The PR title matches the commit intent.\n- The PR body follows the template structure completely.\n- All \"Requirements\" checkboxes and all human-only \"Testing\" checkboxes are unchecked (`- [ ]`) in the PR body. The only checkbox you may check is \"`npm run ci` passes\", and only if it truly passed.\n- The reported validation is accurate — nothing fabricated.\n- The post-submission reminder was delivered in the user's language, concisely and accurately.\n","frontmatter":{"name":"submit-pr-from-current-changes","description":"Create a branch, commit existing local changes, push them, and open a pull request. Use when submitting current work as a PR.","argument-hint":"Short summary of the current changes to submit","user-invocable":true},"isInternal":false,"tokens":1864,"sizeBytes":8330},{"name":"SKILL.md","path":".agents/skills/update-changelog/SKILL.md","rawUrl":"https://raw.githubusercontent.com/alibaba/page-agent/HEAD/.agents/skills/update-changelog/SKILL.md","title":"update-changelog","category":"anthropic-skill","format":"markdown","content":"---\nname: update-changelog\ndescription: 'Update docs/CHANGELOG.md from git history, GitHub releases, and code diffs. Use when: writing release notes, syncing the latest changelog entry, summarizing a new tag, or keeping changelog wording concise and consistent.'\nargument-hint: 'Describe the target, for example: latest version only'\n---\n\n# Update Changelog\n\nUpdate `docs/CHANGELOG.md` from repository evidence instead of guesswork.\n\n## When to Use\n\n- Add the newest release entry to `docs/CHANGELOG.md`\n- Sync changelog text with GitHub Releases\n- Summarize the latest tag from git history or code diffs\n\n## Defaults\n\n- Keep the wording brief\n- Only add the latest missing version unless the user explicitly asks to backfill older releases\n- Prefer GitHub sources first, especially Releases and tag metadata\n- Skip `Features` / `Improvements` / `Bug Fixes` headings when the release only has a few clear items\n\n## Procedure\n\n### 1. Read the local style\n\n- Open `docs/CHANGELOG.md`\n- Match the existing tone, bullet style, section ordering, and date format\n\n### 2. Determine the target release\n\n- Read the root `package.json` version and compare it with the top changelog entry\n- Find the previous tag for the target version\n- If the latest version is already documented, stop and report that no changelog update is needed\n\n### 3. Gather evidence\n\nPrefer these sources in order:\n\n1. GitHub release notes\n\n```bash\nGH_PAGER=cat gh release view v<version> --repo <owner>/<repo> --json tagName,name,publishedAt,body\n```\n\n2. Tag date\n\n```bash\ngit log -1 --format=%cs v<version>\n```\n\n3. Commit history between tags\n\n```bash\ngit --no-pager log --format='%h %s' --no-merges v<previous>..v<version>\n```\n\n4. Diff scope when commit subjects are vague\n\n```bash\ngit --no-pager diff --name-only v<previous>..v<version>\ngit --no-pager diff --stat v<previous>..v<version>\n```\n\n5. Read touched files directly only when the user-visible change is still unclear\n\n### 4. Distill what belongs in the changelog\n\nInclude:\n\n- User-visible features\n- Important behavior changes\n- Bug fixes that improve reliability, compatibility, or developer experience\n- Small docs updates only when they materially change supported setups or onboarding\n\nExclude unless explicitly requested:\n\n- Pure version bumps\n- Routine dependency updates\n- Internal refactors with no visible impact\n- Mechanical formatting noise\n\n### 5. Choose the structure\n\n- If the release has 1 to 3 clear points, write flat bullets directly under the version heading\n- If the release has several distinct items, use short headings such as `### Features`, `### Improvements`, and `### Bug Fixes`\n- Do not force categories when they make the entry longer or noisier\n\n### 6. Write the entry\n\n- Add or update only the requested release entry\n- Keep bullets short and concrete\n- Reuse project terminology from nearby entries\n- Avoid marketing language\n- Do not copy noisy GitHub release text verbatim\n\n### 7. Verify\n\n- Check dates, version numbers, and tag boundaries\n- Ensure Markdown structure matches nearby entries\n- Confirm no intermediate versions were added unless requested\n\n## Completion Checks\n\n- The newest requested version is documented\n- The wording is concise and consistent with the surrounding changelog\n- The entry is backed by GitHub releases, git history, or code diff evidence\n- Low-signal internal changes were left out\n","frontmatter":{"name":"update-changelog","description":"Update docs/CHANGELOG.md from git history, GitHub releases, and code diffs. Use when: writing release notes, syncing the latest changelog entry, summarizing a new tag, or keeping changelog wording concise and consistent.","argument-hint":"Describe the target, for example: latest version only"},"isInternal":false,"tokens":770,"sizeBytes":3374},{"name":"extension_api.md","path":"packages/extension/docs/extension_api.md","rawUrl":"https://raw.githubusercontent.com/alibaba/page-agent/HEAD/packages/extension/docs/extension_api.md","title":"Docs Skill","category":"anthropic-skill","format":"markdown","content":"# Page Agent Extension API\n\nIntegrate the Page Agent extension into your web app and trigger multi-page browser tasks from page JavaScript.\n\n## Installation\n\n### 1. Install the browser extension\n\nPrimary channel:\n\n- Chrome Web Store: https://chromewebstore.google.com/detail/page-agent-ext/akldabonmimlicnjlflnapfeklbfemhj\n\nLatest updates are often published earlier on:\n\n- GitHub Releases: https://github.com/alibaba/page-agent/releases\n\n### 2. Install type definitions (recommended)\n\n```bash\nnpm install @page-agent/core --save-dev\n```\n\n### 3. Authorization (Token)\n\nThe token allows your page JS to call the extension API (`window.PAGE_AGENT_EXT`) and execute multi-page tasks.\n\nWhy token-based access is required:\n\n- The extension has broad browser permissions (page access, navigation, multi-tab control).\n- If abused, it can harm user privacy and security.\n- Users must explicitly provide the token only to applications they trust.\n\nSetup:\n\n1. Open the extension side panel and copy your auth token.\n2. Set the token in your page:\n\n```typescript\nlocalStorage.setItem('PageAgentExtUserAuthToken', 'your-token')\n```\n\n## Quick Start\n\n```typescript\nimport type { AgentActivity, AgentStatus, ExecutionResult, HistoricalEvent } from '@page-agent/core'\n\n// Wait for extension injection (up to 1 second)\nasync function waitForExtension(timeout = 1000): Promise<boolean> {\n    const start = Date.now()\n    while (Date.now() - start < timeout) {\n        if (window.PAGE_AGENT_EXT) return true\n        await new Promise((r) => setTimeout(r, 100))\n    }\n    return false\n}\n\n// Usage\nif (await waitForExtension()) {\n    const result = await window.PAGE_AGENT_EXT!.execute('Click the login button', {\n        baseURL: 'https://api.openai.com/v1',\n        apiKey: 'your-api-key',\n        model: 'gpt-5.2',\n        onStatusChange: (status) => console.log('Status:', status),\n        onActivity: (activity) => console.log('Activity:', activity),\n    })\n    console.log('Result:', result)\n}\n```\n\n## Global API\n\nAfter token match, the extension injects APIs into `window`.\n\n### `window.PAGE_AGENT_EXT_VERSION`\n\nExtension version string (for capability checks before using the main API).\n\n### `window.PAGE_AGENT_EXT`\n\nMain namespace object.\n\n#### `PAGE_AGENT_EXT.execute(task, config)`\n\nExecute one agent task.\n\nParameters:\n\n| Name     | Type            | Required | Description                          |\n| -------- | --------------- | -------- | ------------------------------------ |\n| `task`   | `string`        | Yes      | Task description                     |\n| `config` | `ExecuteConfig` | Yes      | LLM settings, options, and callbacks |\n\nReturns: `Promise<ExecutionResult>`\n\n#### `PAGE_AGENT_EXT.stop()`\n\nStop the current task.\n\n## Limitations\n\n- **Normal browser windows only.** This extension relies on tab group API which does not work for pop-up window or PWA App window.\n\n## Types\n\nInstall `@page-agent/core` for complete types:\n\n```typescript\nimport type { AgentActivity, AgentStatus, ExecutionResult, HistoricalEvent } from '@page-agent/core'\n\nexport interface ExecuteConfig {\n    baseURL: string\n    model: string\n    apiKey?: string\n\n    // Global system-level instructions for the agent.\n    // Equivalent to AgentConfig.instructions.system.\n    systemInstruction?: string\n\n    // Include the initial tab where page JS starts. Default: true.\n    includeInitialTab?: boolean\n\n    // Control all unpinned tabs in the window instead of only the tab group.\n    // When enabled, agent sees and can switch to every non-pinned tab.\n    // Default: false. Experimental.\n    experimentalIncludeAllTabs?: boolean\n\n    onStatusChange?: (status: AgentStatus) => void\n    onActivity?: (activity: AgentActivity) => void\n    onHistoryUpdate?: (history: HistoricalEvent[]) => void\n}\n\nexport type Execute = (task: string, config: ExecuteConfig) => Promise<ExecutionResult>\n```\n\n`AgentStatus`\n\n```typescript\ntype AgentStatus = 'idle' | 'running' | 'completed' | 'error' | 'stopped'\n```\n\n`AgentActivity`\n\n```typescript\ntype AgentActivity =\n    | { type: 'thinking' }\n    | { type: 'executing'; tool: string; input: unknown }\n    | { type: 'executed'; tool: string; input: unknown; output: string; duration: number }\n    | { type: 'retrying'; attempt: number; maxAttempts: number }\n    | { type: 'error'; message: string }\n```\n\n`HistoricalEvent`\n\n```typescript\ntype HistoricalEvent =\n    | { type: 'step'; stepIndex: number; reflection: AgentReflection; action: Action }\n    | { type: 'observation'; content: string }\n    | { type: 'user_takeover' }\n    | { type: 'retry'; message: string; attempt: number; maxAttempts: number }\n    | { type: 'error'; message: string; rawResponse?: unknown }\n```\n\n`ExecutionResult`\n\n```typescript\ninterface ExecutionResult {\n    success: boolean\n    data: string\n    history: HistoricalEvent[]\n}\n```\n\n## Usage Examples\n\n### Basic Execution\n\n```typescript\nconst result = await window.PAGE_AGENT_EXT!.execute(\n    'Fill in the email field with test@example.com and click Submit',\n    {\n        baseURL: 'https://api.openai.com/v1',\n        apiKey: process.env.OPENAI_API_KEY!,\n        model: 'gpt-5.2',\n        includeInitialTab: false, // Optional: exclude current tab\n        onStatusChange: (status) => console.log(status),\n        onActivity: (activity) => console.log(activity),\n    }\n)\n```\n\n### Stop the Current Task\n\n```typescript\nwindow.PAGE_AGENT_EXT!.stop()\n```\n\n## Window Type Declaration\n\nIf you are not importing `@page-agent/core`, add:\n\n```typescript\nimport type { AgentActivity, AgentStatus, ExecutionResult, HistoricalEvent } from '@page-agent/core'\n\ninterface ExecuteConfig {\n    baseURL: string\n    model: string\n    apiKey?: string\n\n    systemInstruction?: string\n\n    includeInitialTab?: boolean\n    experimentalIncludeAllTabs?: boolean\n    onStatusChange?: (status: AgentStatus) => void\n    onActivity?: (activity: AgentActivity) => void\n    onHistoryUpdate?: (history: HistoricalEvent[]) => void\n}\n\ndeclare global {\n    interface Window {\n        PAGE_AGENT_EXT_VERSION?: string\n        PAGE_AGENT_EXT?: {\n            version: string\n            execute: Execute\n            stop: () => void\n        }\n    }\n}\n```\n","isInternal":false,"tokens":1424,"sizeBytes":6157},{"name":"AGENTS.md","path":"packages/website/AGENTS.md","rawUrl":"https://raw.githubusercontent.com/alibaba/page-agent/HEAD/packages/website/AGENTS.md","title":"Website Skill","category":"anthropic-skill","format":"markdown","content":"# Website Package - Instructions for Coding Assistants\n\n## Tech Stack\n\n- **React** with TypeScript\n- **Vite** for dev server and build\n- **Tailwind CSS** for styling\n- **shadcn/ui** (new-york style) for UI components — **do NOT hand-edit `src/components/ui/` files**\n- **Magic UI** for animations and effects\n- **wouter** with browser routing (`base: \"/page-agent\"`)\n- **lucide-react** for icons\n\n## Component Guidelines\n\n### Use shadcn/ui Components First\n\n**ALWAYS prefer shadcn/ui components over custom implementations.**\n\nBefore creating any UI component, check if shadcn already provides it:\n\n```bash\n# IMPORTANT: Run from packages/website/, NOT from repo root\ncd packages/website\n\n# Add a new shadcn component\nnpx shadcn@latest add <component-name>\n\n# Add a Magic UI component\nnpx shadcn@latest add \"@magicui/<component-name>\"\n```\n\nAvailable shadcn components: https://ui.shadcn.com/docs/components\nAvailable Magic UI components: https://magicui.design/docs/components\n\n### Current UI Components\n\nLocated in `src/components/ui/`:\n\n**From shadcn/ui:**\n\n- `alert`, `badge`, `button`, `separator`, `sonner`, `switch`, `tooltip`\n\n**From Magic UI:**\n\n- `animated-gradient-text`, `animated-shiny-text`, `aurora-text`\n- `hyper-text`, `magic-card`, `neon-gradient-card`, `particles`\n- `sparkles-text`, `text-animate`, `typing-animation`\n\n**Custom:**\n\n- `highlighter`, `kbd`, `spinner`\n\n### Styling Rules\n\n1. **Prefer Tailwind classes** over custom CSS\n2. Support dark mode via `dark:` classes\n3. Use CSS variables from `src/index.css` for theme colors\n\n## Project Structure\n\n```\nsrc/\n├── pages/\n│   ├── home/\n│   │   ├── index.tsx  # Homepage\n│   │   └── ...Section.tsx\n│   └── docs/\n│       ├── index.tsx    # Docs route switch\n│       ├── Layout.tsx   # Sidebar navigation\n│       └── [section]/[topic]/page.tsx\n├── components/\n│   ├── ui/              # shadcn/ui + Magic UI (DO NOT hand-edit)\n│   ├── Heading.tsx      # Anchor heading for doc pages\n│   ├── Header.tsx       # Site header\n│   └── Footer.tsx       # Site footer\n├── i18n/                # Internationalization\n├── router.tsx           # Root layout + routing\n└── main.tsx             # App entry\n```\n\n## Routing\n\nUses wouter browser routing with base path for GitHub Pages deployment at `https://alibaba.github.io/page-agent/`.\n\n```tsx\n// main.tsx\n<Router base=\"/page-agent\">\n    <PagesRouter />\n</Router>\n```\n\n**Key rules:**\n\n- Header and Footer live in `router.tsx` **outside** `<Switch>`, so they always see the root router context (`base=\"/page-agent\"`)\n- Docs pages are nested via `<Route path=\"/docs\" nest>`, which creates a child context (`base=\"/page-agent/docs\"`)\n- Inside the docs nest, Link hrefs are relative to `/docs` (e.g. `href=\"/features/models\"`, NOT `href=\"/docs/features/models\"`)\n- **Never use `~` prefix** in Link hrefs — it bypasses the base path entirely\n- Doc page headings use `<Heading id=\"slug\" level={2}>` for anchor links\n\n### SPA on GitHub Pages\n\nInstead of `404.html` redirects, the build copies `index.html` into every route directory. Add new routes to the `SPA_ROUTES` array in `vite.config.js`.\n\n## Adding New Pages\n\n### Documentation Page\n\n1. Create `src/pages/docs/<section>/<slug>/page.tsx`\n2. Add route in `src/pages/docs/index.tsx`\n3. Add navigation item in `src/pages/docs/Layout.tsx`\n4. Add path to `SPA_ROUTES` in `vite.config.js`\n5. Keep `<slug>` consistent across the folder name, import path, route path, sidebar link, and any related cross-links. If the public route changes, rename the folder too.\n\n## Configuration Files\n\n| File              | Purpose                 |\n| ----------------- | ----------------------- |\n| `components.json` | shadcn/ui configuration |\n| `vite.config.js`  | Vite build + SPA routes |\n| `tsconfig.json`   | TypeScript config       |\n\n## Commands\n\n```bash\nnpm start            # Dev server (from root)\nnpm run build:website    # Build website (from root)\n```\n","isInternal":false,"tokens":1027,"sizeBytes":4031},{"name":"README.md","path":"packages/mcp/README.md","rawUrl":"https://raw.githubusercontent.com/alibaba/page-agent/HEAD/packages/mcp/README.md","title":"mcp Documentation","category":"plugin-manifest","format":"markdown","content":"# @page-agent/mcp\n\nMCP server that lets AI agent clients (Claude Desktop, Copilot, etc.) control your browser through the [Page Agent](https://github.com/alibaba/page-agent) extension.\n\n## Prerequisites\n\n- Node.js >= 20\n- [Page Agent Extension](https://chromewebstore.google.com/detail/page-agent-ext/akldabonmimlicnjlflnapfeklbfemhj) installed in Chrome\n- An LLM API key (OpenAI-compatible)\n\n## Installation\n\n### Claude Desktop\n\nAdd to `~/Library/Application Support/Claude/claude_desktop_config.json`:\n\n```json\n{\n    \"mcpServers\": {\n        \"page-agent\": {\n            \"command\": \"npx\",\n            \"args\": [\"-y\", \"@page-agent/mcp\"],\n            \"env\": {\n                \"LLM_BASE_URL\": \"https://dashscope.aliyuncs.com/compatible-mode/v1\",\n                \"LLM_API_KEY\": \"sk-xxx\",\n                \"LLM_MODEL_NAME\": \"qwen3.5-plus\"\n            }\n        }\n    }\n}\n```\n\n### Cursor / Copilot\n\nSame format — add the config to the MCP settings of your client.\n\n## MCP Tools\n\n| Tool           | Input              | Description                                           |\n| -------------- | ------------------ | ----------------------------------------------------- |\n| `execute_task` | `{ task: string }` | Execute a browser task in natural language. Blocking. |\n| `get_status`   | —                  | Returns `{ connected, busy }`                         |\n| `stop_task`    | —                  | Stop the currently running task.                      |\n\n## Environment Variables\n\n| Variable         | Default | Description           |\n| ---------------- | ------- | --------------------- |\n| `LLM_BASE_URL`   | —       | LLM API base URL      |\n| `LLM_API_KEY`    | —       | LLM API key           |\n| `LLM_MODEL_NAME` | —       | Model name            |\n| `PORT`           | `38401` | HTTP + WebSocket port |\n\n## How It Works\n\n```\n┌──────────────┐  stdio   ┌──────────────────┐  WebSocket   ┌──────────────┐\n│ Claude /     │◄────────►│ @page-agent/mcp  │◄────────────►│ Hub tab      │\n│ Copilot      │  (MCP)   │ (Node.js)        │  (localhost) │ (extension)  │\n└──────────────┘          └──────────────────┘              └──────┬───────┘\n                                   │                               │\n                                   │ HTTP                          │ useAgent\n                                   ▼                               ▼\n                          ┌──────────────────┐              ┌──────────────┐\n                          │ Launcher page    │              │ MultiPage    │\n                          │ (localhost:PORT) │              │ Agent        │\n                          └──────────────────┘              └──────────────┘\n```\n\n1. Agent client starts the MCP server via stdio (`npx @page-agent/mcp`).\n2. Server starts HTTP + WS on `localhost:PORT`, opens the launcher page in browser.\n3. Launcher page triggers the extension to open a **hub tab** (`hub.html?ws=PORT`).\n4. Hub connects to the WS server. MCP tools now proxy tasks to the hub.\n\nThe hub tab speaks a generic WebSocket protocol (defined in `hub-ws.ts` in the extension package) and has no knowledge of MCP. See the hub's protocol docs for message format details.\n\n## Architecture\n\nPure JS ESM, no build step. Source files are the published artifacts.\n\n```\nsrc/\n├── index.js        # CLI entry: MCP server (stdio) + opens launcher\n├── hub-bridge.js   # HTTP server + WebSocket bridge to hub tab\n└── launcher.html   # Bootstrap page: detects extension, triggers hub open\n```\n\n## Dev\n\n```bash\nnpm run dev:ext\nnpx @modelcontextprotocol/inspector node packages/mcp/src/index.js\n```\n","isInternal":false,"tokens":868,"sizeBytes":4060}],"systemPromptSnippet":"<yakaai_skills repo=\"alibaba/page-agent\">\n<!-- File: AGENTS.md (Tokens: ~1621 | Category: root-instruction) -->\n# Instructions for Coding Assistants\n\n## Project Overview\n\nThis is a **monorepo** with npm workspaces:\n\n- **Page Agent** (`packages/page-agent/`) - Main entry with built-in UI Panel, published as `page-agent` on npm\n- **Extension** (`packages/extension/`) - Browser extension (WXT + React)\n- **Website** (`packages/website/`) - React docs and landing page. **When working on website, follow `packages/website/AGENTS.md`**\n\nInternal packages:\n\n- **Core** (`packages/core/`) - PageAgentCore without UI (npm: `@page-agent/core`)\n- **LLMs** (`packages/llms/`) - LLM client with reflection-before-action mental model\n- **Page Controller** (`packages/page-controller/`) - DOM operations and visual feedback (SimulatorMask), independent of LLM\n- **UI** (`packages/ui/`) - Panel and i18n. Decoupled from PageAgent\n\n## Development Commands\n\n```bash\nnpm start                      # Start website dev server\nnpm run build                  # Build all packages\nnpm run build:libs             # Build all libraries\nnpm run build:ext              # Build and zip the extension package\nnpm run typecheck              # Typecheck all packages\nnpm run test                   # Run unit tests across all workspaces\nnpm run lint                   # ESLint\n```\n\n## Architecture\n\n### Monorepo Structure\n\nSource-first monorepo: library `package.json` exports point to `src/*.ts` during development. At publish time, `scripts/pre-publish.js` promotes `publishConfig` fields to top-level (swapping to `dist/`), and `scripts/post-publish.js` restores the originals.\n\n```\npackages/\n├── core/                    # npm: \"@page-agent/core\" ⭐ Core agent logic (headless)\n├── page-agent/              # npm: \"page-agent\" entry class (with UI + controller + demo builds)\n├── website/                 # @page-agent/website (private)\n├── llms/                    # @page-agent/llms\n├── extension/               # Browser extension\n├── page-controller/         # @page-agent/page-controller\n└── ui/                      # @page-agent/ui\n```\n\n`workspaces` in `package.json` must be in topological order.\n\n### Module Boundaries\n\n- **Page Agent**: Main entry with UI. Extends PageAgentCore and adds Panel. Imports from `@page-agent/core`, `@page-agent/ui`\n- **Core**: PageAgentCore without UI. Imports from `@page-agent/llms`, `@page-agent/page-controller`\n- **LLMs**: LLM client with MacroToolInput contract. No dependency on page-agent\n- **UI**: Panel and i18n. Decoupled from PageAgent via PanelAgentAdapter interface\n- **Page Controller**: DOM operations with optional visual feedback (SimulatorMask). No LLM dependency. Enable mask via `enableMask: true` config\n\n### PageController ↔ PageAgent Communication\n\nAll communication is async and isolated:\n\n```typescript\n// PageAgent delegates DOM operations to PageController\nawait this.pageController.updateTree()\nawait this.pageController.clickElement(index)\nawait this.pageController.inputText(index, text)\nawait this.pageController.scroll({ down: true, numPages: 1 })\n\n// PageController exposes state via async methods\nconst simplifiedHTML = await this.pageController.getSimplifiedHTML()\nconst pageInfo = await this.pageController.getPageInfo()\n```\n\n### DOM Pipeline\n\n1. **DOM Extraction**: Live DOM → `FlatDomTree` via `page-controller/src/dom/dom_tree/`\n2. **Dehydration**: DOM tree → simplified text for LLM\n3. **LLM Processing**: AI returns action plans (page-agent)\n4. **Indexed Operations**: PageAgent calls PageController by element index\n\n## Key Files Reference\n\n### Page Agent (`packages/page-agent/`)\n\n| File               | Description                                  |\n| ------------------ | -------------------------------------------- |\n| `src/PageAgent.ts` | ⭐ Main class with UI, extends PageAgentCore |\n| `src/demo.ts`      | IIFE demo entry (auto-init with demo API)    |\n\n### Core (`packages/core/`)\n\n| File                   | Description                             |\n| ---------------------- | --------------------------------------- |\n| `src/PageAgentCore.ts` | ⭐ Core agent class without UI          |\n| `src/tools/`           | Tool definitions calling PageController |\n| `src/config/`          | Configuration types and constants       |\n| `src/prompts/`         | System prompt templates                 |\n\n### LLMs (`packages/llms/`)\n\n| File                  | Description                           |\n| --------------------- | ------------------------------------- |\n| `src/index.ts`        | ⭐ LLM class with retry logic         |\n| `src/types.ts`        | MacroToolInput, AgentBrain, LLMConfig |\n| `src/OpenAIClient.ts` | OpenAI-compatible client              |\n\n### Page Controller (`packages/page-controller/`)\n\n| File                        | Description                                                |\n| --------------------------- | ---------------------------------------------------------- |\n| `src/PageController.ts`     | ⭐ Main controller class with optional mask support        |\n| `src/SimulatorMask.ts`      | Visual overlay blocking user interaction during automation |\n| `src/actions.ts`            | Element interactions (click, input, scroll)                |\n| `src/dom/dom_tree/index.js` | Core DOM extraction engine                                 |\n\n## Adding New Features\n\n### New Agent Tool\n\n1. Implement in `packages/core/src/tools/index.ts`\n2. If tool needs DOM ops, add method to PageController first\n3. Tool calls `this.pageController.methodName()` for DOM interactions\n\n### New PageController Action\n\n1. Add implementation in `packages/page-controller/src/actions.ts`\n2. Expose via async method in `PageController.ts`\n3. Export from `packages/page-controller/src/index.ts`\n\n## Testing\n\n- **Test value**: Do not add redundant tests or directly translate implementation code into assertions. Test meaningful behavior and realistic failure modes.\n- **Framework**: Vitest (unit tests only for now; future E2E goes to `packages/e2e/` with Playwright)\n- **Location**: co-located, `src/foo.test.ts` next to `src/foo.ts`\n- **Coverage today**: `packages/llms` only — other packages will follow incrementally\n- **Adding tests to a new package**: create `vitest.config.ts` in the package and add a `\"test\": \"vitest run\"` script. Root `npm test` and `node scripts/ci.js` pick it up through npm workspaces.\n- **Live tests** (hit real external APIs, slow/costly): name them `*.live.test.ts`, exclude them from the package's `test` script, and expose them via a `test:live` script. Root `npm run test:live` runs all of them; they never run in `npm test` or CI. Template: `packages/llms`.\n- **Template**: See @page-agent/llms\n\n```bash\nnpm test                            # all packages with a test script\nnpm test -w @page-agent/llms        # single package\ncd packages/llms && npx vitest      # watch mode in one package\n```\n\n## Code Standards\n\n- Explicit typing for exported/public APIs\n- ESLint relaxes some unsafe rules for rapid iteration\n- Every change you make should not only implement the desired functionality but also improve the quality of the codebase\n- All code and comments must be in English.\n- Do not try to hide errors or risks. They are valuable feedbacks for developers and users. Make them visible and actionable.\n- Traceability and predictability is more important than success rate.\n\n<!-- File: CLAUDE.md (Tokens: ~5 | Category: claude-rule) -->\n@AGENTS.md\n\n<!-- File: .agents/skills/git-cleanup/SKILL.md (Tokens: ~370 | Category: anthropic-skill) -->\n---\nname: git-cleanup\ndescription: Clean up local git branches and remotes accumulated from PR reviews. Use when the user asks to clean branches, remove stale remotes, or tidy up the local git state.\n---\n\n# Git Cleanup\n\nClean local branches (except `main` and current branch) and non-origin remotes.\nUseful after reviewing multiple PRs that leave behind tracking branches and contributor remotes.\n\n## Workflow\n\n### 1. Survey\n\nRun these in parallel:\n\n```bash\ngit branch\ngit remote\ngit status --short\ngit stash list\n```\n\nPresent a summary of how many branches and remotes will be removed.\n\n### 2. Safety Checks — Ask Before Proceeding\n\n**Stop and ask for confirmation if any of these are true:**\n\n- Current branch is NOT `main`\n- Working tree has uncommitted changes or stashes\n- Any branch is ahead of its upstream\n\nList unmerged branches separately and let the user decide.\n\n### 3. Delete Branches\n\n```bash\n# Safe delete first (fails on unmerged branches)\ngit branch | grep -v '^\\*' | grep -v '^\\s*main$' | xargs git branch -d 2>&1\n\n# Force-delete only with explicit user approval\ngit branch -D <branch>\n```\n\n### 4. Remove Remotes\n\n```bash\ngit remote | grep -v '^origin$' | xargs -I{} git remote remove {}\n```\n\n### 5. Confirm\n\n```bash\ngit branch && echo \"---\" && git remote\n```\n\nReport what was cleaned up.\n\n## Key Rules\n\n- **NEVER** delete `main` or the current checked-out branch\n- **NEVER** force-delete unmerged branches without user confirmation\n- If in doubt, ask\n\n<!-- File: .agents/skills/maintain-model-list/SKILL.md (Tokens: ~1184 | Category: anthropic-skill) -->\n---\nname: maintain-model-list\ndescription: 'Maintain the supported LLM model list: add a new model, or run routine maintenance to verify availability and discover new models worth adding. Use when the user asks to add/support a model, update the model list, or check model availability.'\nargument-hint: 'A model name to add (e.g. \"gpt-5.7\"), or empty for routine maintenance'\n---\n\n# Maintain Model List\n\nThe supported model list lives in three places that must stay in sync:\n\n| File                                                       | What it holds                                                                                                   |\n| ---------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------- |\n| `packages/website/src/pages/docs/features/models/page.tsx` | `MODEL_GROUPS` (public docs, source of truth for display names) and `BASELINE` (recommended models)             |\n| `packages/llms/src/models.live.test.ts`                    | Mirrored `MODEL_GROUPS`, plus `OPENROUTER_ID_OVERRIDES` / `ALIYUN_ID_OVERRIDES` for provider-specific model ids |\n| `packages/llms/src/utils.ts`                               | `modelPatch` — per-model-family request parameter fixes (thinking/reasoning flags, `tool_choice` quirks, etc.)  |\n\n## Before Touching Anything\n\n1. Run `git status`. If the working tree has uncommitted changes that look like unrelated in-progress work, do NOT edit files — report the situation and ask the user for permission first.\n2. Run the full live test suite as a baseline: `npm run test:live -w @page-agent/llms`. API keys come from the repo-root `.env` (`TESTING_OPENROUTER_KEY`, `TESTING_ALIYUN_KEY`, `TESTING_DEEPSEEK_KEY`); tests skip silently when a key is missing, so check which providers actually ran. Record which models pass/fail before making changes, so new failures are attributable.\n\n## Workflow A: A Specific Model Was Given\n\n1. **Research the model** (web search + provider docs):\n    - Is it served on OpenRouter? Fetch `https://openrouter.ai/api/v1/models` and find the exact id (`<vendor-slug>/<model-id>`, watch for `-preview`, dated snapshots, dots vs hyphens).\n    - Which other channels serve it (vendor native API, Aliyun DashScope, etc.) and what are their native model ids?\n    - API differences: can thinking/reasoning be disabled or minimized? Any `tool_choice`, `parallel_tool_calls`, or parameter-schema quirks? Does the vendor's OpenAI-compatible endpoint differ from OpenRouter's behavior?\n    - Agent suitability: tool call support is mandatory; note context window, latency, and cost.\n2. **Update `modelPatch`** in `packages/llms/src/utils.ts` if the model needs new parameter handling. Model names are matched after `normalizeModelName` (lowercased, `/`-prefix, `.` and `_` stripped) — check whether an existing family branch already covers it.\n3. **Add the model to both `MODEL_GROUPS` lists** (website page and live test), newest first within its brand group. Add id overrides if the OpenRouter/Aliyun id differs from the display name.\n4. **Test availability carefully.** Run the live suite and confirm the new model passes on every provider that serves it:\n\n```bash\nnpm run test:live -w @page-agent/llms\n```\n\nA failure means the request/response shape is wrong for that model — fix `modelPatch` or the id override, don't shrug it off. If the model fails consistently on a provider, exclude it from that provider (see the `deepseek-3.2` precedent in the test file) and document why in a comment. 5. **Decide on `BASELINE`** only if the model is a fast, cheap, strong-tool-call option; otherwise leave it out. 6. Run `npm run typecheck` and `npm test`.\n\n## Workflow B: No Model Given (Routine Maintenance)\n\n1. **Verify the existing list**: run the live suite (see above). Investigate any model that newly fails — deprecated? renamed? provider dropped it?\n2. **Check for drift**: re-verify `OPENROUTER_ID_OVERRIDES` against the current `https://openrouter.ai/api/v1/models` output; ids change when vendors promote previews to stable.\n3. **Scan for new models** worth adding (web search for recent releases from the brands in `MODEL_GROUPS`, plus notable newcomers). A candidate must support tool calls via an OpenAI-compatible API.\n4. **Act by significance**:\n    - Minor updates (a preview id went stable, a small point-release replaces its predecessor): apply the change yourself following Workflow A steps 2–6, then present it to the user for judgment.\n    - New models or removals: report findings and recommendations, let the user decide before editing.\n\n## Hand Off to the User\n\nAfter your own tests pass, always remind the user to verify manually:\n\n1. Tell them which line to put in the repo-root `.env` — give the exact model id per channel, including the OpenRouter id, e.g.:\n\n```bash\nLLM_BASE_URL=\"https://openrouter.ai/api/v1\"\nLLM_API_KEY=\"...\"\nLLM_MODEL_NAME=\"<exact-openrouter-id>\"\n```\n\n2. Tell them to run the demo themselves: `npm run dev:demo` (serves on port 5174) and exercise the agent against a real page.\n\nThe live test only proves a single forced tool call round-trips; it is not an agent-quality eval. The user's manual run is the real acceptance test.\n\n<!-- File: .agents/skills/pre-impl-discussion/SKILL.md (Tokens: ~1050 | Category: anthropic-skill) -->\n---\nname: pre-impl-discussion\ndescription: \"Conduct a thorough pre-implementation discussion before making significant changes. Use when the user wants to discuss, plan, or evaluate a change before implementing it — especially when they say words like 'discuss', 'evaluate', 'plan', or 'let's talk about'.\"\nargument-hint: 'Describe the change to evaluate'\n---\n\n# Pre-Implementation Discussion\n\nFacilitate a structured, collaborative discussion to evaluate a proposed change **before any implementation begins**.\n\n## Golden Rule\n\n**Do NOT implement anything until the user explicitly confirms the final plan and says to proceed.** Not even \"let me try on a branch\" — that's implementation. The user will tell you when discussion is over.\n\n## Interaction Style\n\n- **Concise during discussion.** Each intermediate response should be short and focused on the current question. Do NOT repeat the full plan in every response.\n- **Complete when finalizing.** Once the plan is mature and the user asks for it, present a single comprehensive (but terse) summary for final review.\n- **Ask, don't assume.** When uncertain about project context, constraints, or preferences — ask. The user prefers collaborative discussion over receiving a pre-baked answer.\n- **Challenge the premise.** Question whether the proposed change is the right one. Suggest simpler alternatives if they exist.\n- **Match the user's language.** Reply in the same language the user writes in.\n\n## Procedure\n\n### 1. Understand the Current State\n\nBefore forming any opinion:\n\n- **Read the relevant source files** — configs, code, docs that relate to the change\n- **Understand the project structure** — what's published vs private, what environments things target, existing constraints\n- **Map the impact surface** — which files, packages, or systems would be affected\n- **Estimate implementation cost** — how many files to touch, how much config to rewrite, what could break\n\nDo NOT skip this step. Do NOT rely on assumptions about what \"most projects\" do.\n\n### 2. Research Thoroughly\n\n- **Consult official sources** — docs, changelogs, migration guides, GitHub issues\n- **Verify every claim with evidence.** Don't say \"X supports Y\" without a source. If unsure, say so.\n- **Search in parallel** to save time — batch independent queries\n\nCommon pitfalls:\n\n- Assuming compatibility without checking actual version constraints\n- Confusing roadmap/aspirations with actual released state\n- Missing transitive constraints (a dependency of a dependency)\n\n### 3. Present Findings (Concise)\n\nShare a **brief** assessment. Tables work well for comparisons. Highlight **blockers** and **unknowns** prominently — don't bury them in paragraphs.\n\n### 4. Identify Key Decisions\n\nSurface the decisions the user needs to make. Present them as clear choices with trade-offs, not as a recommendation monologue.\n\nFor each decision point:\n\n- What are the options? (2-3 max)\n- What does each option cost or give up?\n- What's your lean and why? (one sentence)\n\n### 5. Iterate\n\nThe user will ask follow-up questions, raise concerns, or challenge assumptions. For each round:\n\n- **Research if needed** — don't answer from instinct when facts are available\n- **Answer only what was asked** — don't re-present the entire plan\n- **Update your mental model** based on user feedback\n\nCommon mistakes in this phase:\n\n- Repeating the full plan after every small clarification\n- Answering a narrow question with a broad redesign\n- Treating user questions as confirmation to proceed\n\n### 6. Finalize the Plan\n\nWhen the user signals the discussion is converging, present the **complete plan once**:\n\n- **What changes** — concrete list of actions (table or bullet points)\n- **Impact surface** — what files/systems are affected, estimated effort\n- **What does NOT change** — explicitly state scope boundaries\n- **Risks or open items** — anything that needs testing or further verification\n\nKeep it terse. Tables over paragraphs. No explanations the user already heard during discussion.\n\n### 7. Wait for Confirmation\n\nAfter presenting the final plan, **stop and wait**. The user will either:\n\n- Confirm → then (and only then) proceed to implementation\n- Ask more questions → go back to step 5\n- Modify scope → update the plan and re-present\n\n## Anti-Patterns\n\n| Don't                                               | Do Instead                               |\n| --------------------------------------------------- | ---------------------------------------- |\n| Start implementation \"to test\" without confirmation | Present findings and wait                |\n| Repeat the full plan in every response              | Answer the specific question asked       |\n| Say \"X should work\" without checking                | Say \"I need to verify X\" and research it |\n| Assume project structure or constraints             | Read the actual files                    |\n| Present one recommendation as the only option       | Present 2-3 options with trade-offs      |\n| Write long paragraphs explaining trade-offs         | Use tables and bullet points             |\n\n<!-- File: .agents/skills/submit-pr-from-current-changes/SKILL.md (Tokens: ~1864 | Category: anthropic-skill) -->\n---\nname: submit-pr-from-current-changes\ndescription: 'Create a branch, commit existing local changes, push them, and open a pull request. Use when submitting current work as a PR.'\nargument-hint: 'Short summary of the current changes to submit'\nuser-invocable: true\n---\n\n# Submit PR From Current Changes\n\nTurn a working tree diff into a clean branch, commit, and pull request.\n\n## Hard Rules\n\n1. **Follow the PR template exactly.** The template is at `.github/PULL_REQUEST_TEMPLATE.md`. Read it and copy its full structure into the PR body. Do NOT remove, reorder, or skip any section or checkbox.\n2. **Never check \"Requirements / 要求\" checkboxes.** These are human-only declarations — the Code of Conduct acknowledgment and the AI authorship declaration can only be truthfully made by the human submitter. They MUST remain unchecked (`- [ ]`) in the PR body you generate. No exceptions, no workarounds, even if the user explicitly asks you to check them. The user goes to GitHub and checks them in person after verifying each statement is true.\n3. **Only check Testing checkboxes you actually verified.** The \"`npm run ci` passes\" checkbox MAY be checked only if you ran `npm run ci` in this session and it passed. All other Testing checkboxes (browser tested, no console errors, types/doc added) require manual verification that only a human can perform — they MUST remain unchecked. You MAY add a supplementary note below the Testing section listing what automated validation you actually ran and the results.\n4. **Never fabricate information.** Do not claim tests passed, commands ran, or checks succeeded unless you actually executed them and observed the output in this session. If you did not run it, do not mention it.\n5. **PR output must be concise.** PR title: one line. \"What\" section: 1–2 sentences max. No walls of text, no redundant explanations. Let the diff speak.\n\n## Prerequisites\n\nBefore attempting to push or open a PR, verify that the necessary tools are available:\n\n- Check that `gh` CLI is installed and authenticated (`gh auth status`). If not available, stop and ask the user to install and authenticate GitHub CLI first.\n- If the workflow uses MCP tools for GitHub operations, verify the MCP server is accessible.\n\n## Shell Permissions\n\n`gh` reads credentials from the OS keyring. A sandboxed shell cannot reach the keyring and will report a false auth failure.\n\n- Run every `gh` command (`gh auth status`, `gh pr create`, `gh pr view`, …) and every `git push` / `git fetch` that talks to GitHub with full permissions on the **first** attempt (`required_permissions: [\"all\"]` for the Shell tool). Do not probe these in the sandbox first.\n- Local read-only git inspection (`git status`, `git diff`, `git log`, `git branch`) may stay sandboxed.\n\n## Procedure\n\n1. **Read contribution rules.** Read `CONTRIBUTING.md` and any package-level instructions (e.g. `packages/*/AGENTS.md`) relevant to the changed files.\n2. **Inspect the diff.** Review changed files, scope, and impact. Understand what the change does before writing anything.\n3. **Inspect repo conventions.** Check recent branch names (`git branch -r --sort=-committerdate | head -20`), recent commit messages (`git log --oneline -20`), and the PR template.\n4. **Pre-submission health checks** (run all before creating the PR):\n    - **Unified theme**: All changes should serve one purpose. If unrelated changes are mixed in, ask the user to split or confirm.\n    - **Commit hygiene**: Every new commit must follow the repo's conventional commit style. Squash or reword if needed.\n    - **Author identity**: Verify `git config user.name` and `git config user.email` are set and the email looks real (not empty, not `noreply` unless intentional). Warn the user if not.\n    - **No leftover artifacts**: Check for debug logs, `.only` in tests, conflict markers, or temp files in the diff.\n    - **CI**: Run `npm run ci` and make sure it passes. Record results honestly.\n5. **Warn if the PR looks too hasty.** If any of these are true, pause and warn the user before proceeding:\n    - Large diff with no description of intent provided\n    - Changes touch core lib or extension (where vibe coding is prohibited per `CONTRIBUTING.md`)\n    - Multiple unrelated concerns in one diff\n    - No validation has been run at all\n    - Remind the user: _\"This project does not accept low-quality or AI-generated PRs without meaningful human review. Please review your changes carefully.\"_\n6. **Branch.** Check the current branch first:\n    - If already on a non-main feature branch with a valid name (matching `type/topic` convention), reuse it.\n    - If the branch name does not follow repo conventions (e.g. missing prefix, unclear topic), ask the user whether to rename or create a new one.\n    - If on `main` or a default branch, create a new branch from it with a short kebab-case name: prefix (`fix/`, `feat/`, `docs/`, `refactor/`, `chore/`) + concrete topic words.\n7. **Stage and commit.** Stage only the intended files. Use `type(scope): subject` format. Keep the subject specific and compact.\n8. **Push.** Push with `-u origin HEAD`.\n9. **Open PR.** Use `gh pr create` with the full PR template structure. Fill in \"What\" and \"Type\" sections based on the actual diff. Check \"`npm run ci` passes\" only if it actually passed in this session; leave all other \"Testing\" and all \"Requirements\" checkboxes unchecked.\n10. **Report results.** Tell the user: branch name, commit hash, PR link, and what validation actually ran (with pass/fail).\n\n## Post-Submission Reminder\n\nAfter successfully opening the PR, ALWAYS give a brief reminder in the user's language. Keep it concise and natural, but make sure it clearly tells the user:\n\n1. They need to test the changes themselves in the browser.\n2. They need to go to the PR page and check the Testing and Requirements checkboxes only after verifying each item.\n3. The PR will not enter review until those checkboxes are checked.\n4. The project does not accept autonomously AI-generated PRs, so they should only check the AI declaration if it is truthful.\n\n## Branch Naming\n\n- Prefer short kebab-case names with a repo-consistent prefix such as `fix/`, `feat/`, `docs/`, `refactor/`, or `chore/`.\n- Match the change type first, then the smallest useful topic.\n- Prefer concrete topic words over issue text dumps.\n\n## Commit Style\n\n- Use the repository's prevailing commit style (conventional commits).\n- Use `type(scope): subject` when scope is clear from the changed area.\n- Keep the subject specific and compact.\n- If multiple commits exist on the branch, each one must independently follow conventions.\n\n## Validation Strategy\n\n- Default to enough validation to defend the PR, not the absolute minimum.\n- Always run `npm run ci` (build, lint, typecheck, tests) before opening the PR. It must pass.\n- Escalate to broader validation when the diff crosses packages, changes shared code, or affects release behavior.\n- Never claim checks that did not actually run.\n- You MAY note what you ran and the results below the Testing section in the PR body, but do NOT check the template checkboxes.\n\n## Decision Points\n\n- No local changes → stop, say so.\n- `gh` CLI not available → stop, ask user to install it.\n- Unrelated files mixed → stage only the intended subset or ask whether the work should be split.\n- If the repo has area-specific instructions, read them before naming the branch or writing the PR.\n- Change is small but high-risk → prefer broader validation.\n- Change is narrowly scoped and low-risk → run the most relevant checks, not arbitrary unrelated ones.\n- Template has declarations you cannot truthfully check → leave unchecked, tell the user why.\n\n## Completion Checks\n\n- The branch exists remotely and tracks upstream.\n- The commit message(s) match repo style.\n- All commit authors have proper name and email configured.\n- The PR title matches the commit intent.\n- The PR body follows the template structure completely.\n- All \"Requirements\" checkboxes and all human-only \"Testing\" checkboxes are unchecked (`- [ ]`) in the PR body. The only checkbox you may check is \"`npm run ci` passes\", and only if it truly passed.\n- The reported validation is accurate — nothing fabricated.\n- The post-submission reminder was delivered in the user's language, concisely and accurately.\n\n<!-- File: .agents/skills/update-changelog/SKILL.md (Tokens: ~770 | Category: anthropic-skill) -->\n---\nname: update-changelog\ndescription: 'Update docs/CHANGELOG.md from git history, GitHub releases, and code diffs. Use when: writing release notes, syncing the latest changelog entry, summarizing a new tag, or keeping changelog wording concise and consistent.'\nargument-hint: 'Describe the target, for example: latest version only'\n---\n\n# Update Changelog\n\nUpdate `docs/CHANGELOG.md` from repository evidence instead of guesswork.\n\n## When to Use\n\n- Add the newest release entry to `docs/CHANGELOG.md`\n- Sync changelog text with GitHub Releases\n- Summarize the latest tag from git history or code diffs\n\n## Defaults\n\n- Keep the wording brief\n- Only add the latest missing version unless the user explicitly asks to backfill older releases\n- Prefer GitHub sources first, especially Releases and tag metadata\n- Skip `Features` / `Improvements` / `Bug Fixes` headings when the release only has a few clear items\n\n## Procedure\n\n### 1. Read the local style\n\n- Open `docs/CHANGELOG.md`\n- Match the existing tone, bullet style, section ordering, and date format\n\n### 2. Determine the target release\n\n- Read the root `package.json` version and compare it with the top changelog entry\n- Find the previous tag for the target version\n- If the latest version is already documented, stop and report that no changelog update is needed\n\n### 3. Gather evidence\n\nPrefer these sources in order:\n\n1. GitHub release notes\n\n```bash\nGH_PAGER=cat gh release view v<version> --repo <owner>/<repo> --json tagName,name,publishedAt,body\n```\n\n2. Tag date\n\n```bash\ngit log -1 --format=%cs v<version>\n```\n\n3. Commit history between tags\n\n```bash\ngit --no-pager log --format='%h %s' --no-merges v<previous>..v<version>\n```\n\n4. Diff scope when commit subjects are vague\n\n```bash\ngit --no-pager diff --name-only v<previous>..v<version>\ngit --no-pager diff --stat v<previous>..v<version>\n```\n\n5. Read touched files directly only when the user-visible change is still unclear\n\n### 4. Distill what belongs in the changelog\n\nInclude:\n\n- User-visible features\n- Important behavior changes\n- Bug fixes that improve reliability, compatibility, or developer experience\n- Small docs updates only when they materially change supported setups or onboarding\n\nExclude unless explicitly requested:\n\n- Pure version bumps\n- Routine dependency updates\n- Internal refactors with no visible impact\n- Mechanical formatting noise\n\n### 5. Choose the structure\n\n- If the release has 1 to 3 clear points, write flat bullets directly under the version heading\n- If the release has several distinct items, use short headings such as `### Features`, `### Improvements`, and `### Bug Fixes`\n- Do not force categories when they make the entry longer or noisier\n\n### 6. Write the entry\n\n- Add or update only the requested release entry\n- Keep bullets short and concrete\n- Reuse project terminology from nearby entries\n- Avoid marketing language\n- Do not copy noisy GitHub release text verbatim\n\n### 7. Verify\n\n- Check dates, version numbers, and tag boundaries\n- Ensure Markdown structure matches nearby entries\n- Confirm no intermediate versions were added unless requested\n\n## Completion Checks\n\n- The newest requested version is documented\n- The wording is concise and consistent with the surrounding changelog\n- The entry is backed by GitHub releases, git history, or code diff evidence\n- Low-signal internal changes were left out\n\n<!-- File: packages/extension/docs/extension_api.md (Tokens: ~1424 | Category: anthropic-skill) -->\n# Page Agent Extension API\n\nIntegrate the Page Agent extension into your web app and trigger multi-page browser tasks from page JavaScript.\n\n## Installation\n\n### 1. Install the browser extension\n\nPrimary channel:\n\n- Chrome Web Store: https://chromewebstore.google.com/detail/page-agent-ext/akldabonmimlicnjlflnapfeklbfemhj\n\nLatest updates are often published earlier on:\n\n- GitHub Releases: https://github.com/alibaba/page-agent/releases\n\n### 2. Install type definitions (recommended)\n\n```bash\nnpm install @page-agent/core --save-dev\n```\n\n### 3. Authorization (Token)\n\nThe token allows your page JS to call the extension API (`window.PAGE_AGENT_EXT`) and execute multi-page tasks.\n\nWhy token-based access is required:\n\n- The extension has broad browser permissions (page access, navigation, multi-tab control).\n- If abused, it can harm user privacy and security.\n- Users must explicitly provide the token only to applications they trust.\n\nSetup:\n\n1. Open the extension side panel and copy your auth token.\n2. Set the token in your page:\n\n```typescript\nlocalStorage.setItem('PageAgentExtUserAuthToken', 'your-token')\n```\n\n## Quick Start\n\n```typescript\nimport type { AgentActivity, AgentStatus, ExecutionResult, HistoricalEvent } from '@page-agent/core'\n\n// Wait for extension injection (up to 1 second)\nasync function waitForExtension(timeout = 1000): Promise<boolean> {\n    const start = Date.now()\n    while (Date.now() - start < timeout) {\n        if (window.PAGE_AGENT_EXT) return true\n        await new Promise((r) => setTimeout(r, 100))\n    }\n    return false\n}\n\n// Usage\nif (await waitForExtension()) {\n    const result = await window.PAGE_AGENT_EXT!.execute('Click the login button', {\n        baseURL: 'https://api.openai.com/v1',\n        apiKey: 'your-api-key',\n        model: 'gpt-5.2',\n        onStatusChange: (status) => console.log('Status:', status),\n        onActivity: (activity) => console.log('Activity:', activity),\n    })\n    console.log('Result:', result)\n}\n```\n\n## Global API\n\nAfter token match, the extension injects APIs into `window`.\n\n### `window.PAGE_AGENT_EXT_VERSION`\n\nExtension version string (for capability checks before using the main API).\n\n### `window.PAGE_AGENT_EXT`\n\nMain namespace object.\n\n#### `PAGE_AGENT_EXT.execute(task, config)`\n\nExecute one agent task.\n\nParameters:\n\n| Name     | Type            | Required | Description                          |\n| -------- | --------------- | -------- | ------------------------------------ |\n| `task`   | `string`        | Yes      | Task description                     |\n| `config` | `ExecuteConfig` | Yes      | LLM settings, options, and callbacks |\n\nReturns: `Promise<ExecutionResult>`\n\n#### `PAGE_AGENT_EXT.stop()`\n\nStop the current task.\n\n## Limitations\n\n- **Normal browser windows only.** This extension relies on tab group API which does not work for pop-up window or PWA App window.\n\n## Types\n\nInstall `@page-agent/core` for complete types:\n\n```typescript\nimport type { AgentActivity, AgentStatus, ExecutionResult, HistoricalEvent } from '@page-agent/core'\n\nexport interface ExecuteConfig {\n    baseURL: string\n    model: string\n    apiKey?: string\n\n    // Global system-level instructions for the agent.\n    // Equivalent to AgentConfig.instructions.system.\n    systemInstruction?: string\n\n    // Include the initial tab where page JS starts. Default: true.\n    includeInitialTab?: boolean\n\n    // Control all unpinned tabs in the window instead of only the tab group.\n    // When enabled, agent sees and can switch to every non-pinned tab.\n    // Default: false. Experimental.\n    experimentalIncludeAllTabs?: boolean\n\n    onStatusChange?: (status: AgentStatus) => void\n    onActivity?: (activity: AgentActivity) => void\n    onHistoryUpdate?: (history: HistoricalEvent[]) => void\n}\n\nexport type Execute = (task: string, config: ExecuteConfig) => Promise<ExecutionResult>\n```\n\n`AgentStatus`\n\n```typescript\ntype AgentStatus = 'idle' | 'running' | 'completed' | 'error' | 'stopped'\n```\n\n`AgentActivity`\n\n```typescript\ntype AgentActivity =\n    | { type: 'thinking' }\n    | { type: 'executing'; tool: string; input: unknown }\n    | { type: 'executed'; tool: string; input: unknown; output: string; duration: number }\n    | { type: 'retrying'; attempt: number; maxAttempts: number }\n    | { type: 'error'; message: string }\n```\n\n`HistoricalEvent`\n\n```typescript\ntype HistoricalEvent =\n    | { type: 'step'; stepIndex: number; reflection: AgentReflection; action: Action }\n    | { type: 'observation'; content: string }\n    | { type: 'user_takeover' }\n    | { type: 'retry'; message: string; attempt: number; maxAttempts: number }\n    | { type: 'error'; message: string; rawResponse?: unknown }\n```\n\n`ExecutionResult`\n\n```typescript\ninterface ExecutionResult {\n    success: boolean\n    data: string\n    history: HistoricalEvent[]\n}\n```\n\n## Usage Examples\n\n### Basic Execution\n\n```typescript\nconst result = await window.PAGE_AGENT_EXT!.execute(\n    'Fill in the email field with test@example.com and click Submit',\n    {\n        baseURL: 'https://api.openai.com/v1',\n        apiKey: process.env.OPENAI_API_KEY!,\n        model: 'gpt-5.2',\n        includeInitialTab: false, // Optional: exclude current tab\n        onStatusChange: (status) => console.log(status),\n        onActivity: (activity) => console.log(activity),\n    }\n)\n```\n\n### Stop the Current Task\n\n```typescript\nwindow.PAGE_AGENT_EXT!.stop()\n```\n\n## Window Type Declaration\n\nIf you are not importing `@page-agent/core`, add:\n\n```typescript\nimport type { AgentActivity, AgentStatus, ExecutionResult, HistoricalEvent } from '@page-agent/core'\n\ninterface ExecuteConfig {\n    baseURL: string\n    model: string\n    apiKey?: string\n\n    systemInstruction?: string\n\n    includeInitialTab?: boolean\n    experimentalIncludeAllTabs?: boolean\n    onStatusChange?: (status: AgentStatus) => void\n    onActivity?: (activity: AgentActivity) => void\n    onHistoryUpdate?: (history: HistoricalEvent[]) => void\n}\n\ndeclare global {\n    interface Window {\n        PAGE_AGENT_EXT_VERSION?: string\n        PAGE_AGENT_EXT?: {\n            version: string\n            execute: Execute\n            stop: () => void\n        }\n    }\n}\n```\n\n<!-- File: packages/website/AGENTS.md (Tokens: ~1027 | Category: anthropic-skill) -->\n# Website Package - Instructions for Coding Assistants\n\n## Tech Stack\n\n- **React** with TypeScript\n- **Vite** for dev server and build\n- **Tailwind CSS** for styling\n- **shadcn/ui** (new-york style) for UI components — **do NOT hand-edit `src/components/ui/` files**\n- **Magic UI** for animations and effects\n- **wouter** with browser routing (`base: \"/page-agent\"`)\n- **lucide-react** for icons\n\n## Component Guidelines\n\n### Use shadcn/ui Components First\n\n**ALWAYS prefer shadcn/ui components over custom implementations.**\n\nBefore creating any UI component, check if shadcn already provides it:\n\n```bash\n# IMPORTANT: Run from packages/website/, NOT from repo root\ncd packages/website\n\n# Add a new shadcn component\nnpx shadcn@latest add <component-name>\n\n# Add a Magic UI component\nnpx shadcn@latest add \"@magicui/<component-name>\"\n```\n\nAvailable shadcn components: https://ui.shadcn.com/docs/components\nAvailable Magic UI components: https://magicui.design/docs/components\n\n### Current UI Components\n\nLocated in `src/components/ui/`:\n\n**From shadcn/ui:**\n\n- `alert`, `badge`, `button`, `separator`, `sonner`, `switch`, `tooltip`\n\n**From Magic UI:**\n\n- `animated-gradient-text`, `animated-shiny-text`, `aurora-text`\n- `hyper-text`, `magic-card`, `neon-gradient-card`, `particles`\n- `sparkles-text`, `text-animate`, `typing-animation`\n\n**Custom:**\n\n- `highlighter`, `kbd`, `spinner`\n\n### Styling Rules\n\n1. **Prefer Tailwind classes** over custom CSS\n2. Support dark mode via `dark:` classes\n3. Use CSS variables from `src/index.css` for theme colors\n\n## Project Structure\n\n```\nsrc/\n├── pages/\n│   ├── home/\n│   │   ├── index.tsx  # Homepage\n│   │   └── ...Section.tsx\n│   └── docs/\n│       ├── index.tsx    # Docs route switch\n│       ├── Layout.tsx   # Sidebar navigation\n│       └── [section]/[topic]/page.tsx\n├── components/\n│   ├── ui/              # shadcn/ui + Magic UI (DO NOT hand-edit)\n│   ├── Heading.tsx      # Anchor heading for doc pages\n│   ├── Header.tsx       # Site header\n│   └── Footer.tsx       # Site footer\n├── i18n/                # Internationalization\n├── router.tsx           # Root layout + routing\n└── main.tsx             # App entry\n```\n\n## Routing\n\nUses wouter browser routing with base path for GitHub Pages deployment at `https://alibaba.github.io/page-agent/`.\n\n```tsx\n// main.tsx\n<Router base=\"/page-agent\">\n    <PagesRouter />\n</Router>\n```\n\n**Key rules:**\n\n- Header and Footer live in `router.tsx` **outside** `<Switch>`, so they always see the root router context (`base=\"/page-agent\"`)\n- Docs pages are nested via `<Route path=\"/docs\" nest>`, which creates a child context (`base=\"/page-agent/docs\"`)\n- Inside the docs nest, Link hrefs are relative to `/docs` (e.g. `href=\"/features/models\"`, NOT `href=\"/docs/features/models\"`)\n- **Never use `~` prefix** in Link hrefs — it bypasses the base path entirely\n- Doc page headings use `<Heading id=\"slug\" level={2}>` for anchor links\n\n### SPA on GitHub Pages\n\nInstead of `404.html` redirects, the build copies `index.html` into every route directory. Add new routes to the `SPA_ROUTES` array in `vite.config.js`.\n\n## Adding New Pages\n\n### Documentation Page\n\n1. Create `src/pages/docs/<section>/<slug>/page.tsx`\n2. Add route in `src/pages/docs/index.tsx`\n3. Add navigation item in `src/pages/docs/Layout.tsx`\n4. Add path to `SPA_ROUTES` in `vite.config.js`\n5. Keep `<slug>` consistent across the folder name, import path, route path, sidebar link, and any related cross-links. If the public route changes, rename the folder too.\n\n## Configuration Files\n\n| File              | Purpose                 |\n| ----------------- | ----------------------- |\n| `components.json` | shadcn/ui configuration |\n| `vite.config.js`  | Vite build + SPA routes |\n| `tsconfig.json`   | TypeScript config       |\n\n## Commands\n\n```bash\nnpm start            # Dev server (from root)\nnpm run build:website    # Build website (from root)\n```\n\n<!-- File: packages/mcp/README.md (Tokens: ~868 | Category: plugin-manifest) -->\n# @page-agent/mcp\n\nMCP server that lets AI agent clients (Claude Desktop, Copilot, etc.) control your browser through the [Page Agent](https://github.com/alibaba/page-agent) extension.\n\n## Prerequisites\n\n- Node.js >= 20\n- [Page Agent Extension](https://chromewebstore.google.com/detail/page-agent-ext/akldabonmimlicnjlflnapfeklbfemhj) installed in Chrome\n- An LLM API key (OpenAI-compatible)\n\n## Installation\n\n### Claude Desktop\n\nAdd to `~/Library/Application Support/Claude/claude_desktop_config.json`:\n\n```json\n{\n    \"mcpServers\": {\n        \"page-agent\": {\n            \"command\": \"npx\",\n            \"args\": [\"-y\", \"@page-agent/mcp\"],\n            \"env\": {\n                \"LLM_BASE_URL\": \"https://dashscope.aliyuncs.com/compatible-mode/v1\",\n                \"LLM_API_KEY\": \"sk-xxx\",\n                \"LLM_MODEL_NAME\": \"qwen3.5-plus\"\n            }\n        }\n    }\n}\n```\n\n### Cursor / Copilot\n\nSame format — add the config to the MCP settings of your client.\n\n## MCP Tools\n\n| Tool           | Input              | Description                                           |\n| -------------- | ------------------ | ----------------------------------------------------- |\n| `execute_task` | `{ task: string }` | Execute a browser task in natural language. Blocking. |\n| `get_status`   | —                  | Returns `{ connected, busy }`                         |\n| `stop_task`    | —                  | Stop the currently running task.                      |\n\n## Environment Variables\n\n| Variable         | Default | Description           |\n| ---------------- | ------- | --------------------- |\n| `LLM_BASE_URL`   | —       | LLM API base URL      |\n| `LLM_API_KEY`    | —       | LLM API key           |\n| `LLM_MODEL_NAME` | —       | Model name            |\n| `PORT`           | `38401` | HTTP + WebSocket port |\n\n## How It Works\n\n```\n┌──────────────┐  stdio   ┌──────────────────┐  WebSocket   ┌──────────────┐\n│ Claude /     │◄────────►│ @page-agent/mcp  │◄────────────►│ Hub tab      │\n│ Copilot      │  (MCP)   │ (Node.js)        │  (localhost) │ (extension)  │\n└──────────────┘          └──────────────────┘              └──────┬───────┘\n                                   │                               │\n                                   │ HTTP                          │ useAgent\n                                   ▼                               ▼\n                          ┌──────────────────┐              ┌──────────────┐\n                          │ Launcher page    │              │ MultiPage    │\n                          │ (localhost:PORT) │              │ Agent        │\n                          └──────────────────┘              └──────────────┘\n```\n\n1. Agent client starts the MCP server via stdio (`npx @page-agent/mcp`).\n2. Server starts HTTP + WS on `localhost:PORT`, opens the launcher page in browser.\n3. Launcher page triggers the extension to open a **hub tab** (`hub.html?ws=PORT`).\n4. Hub connects to the WS server. MCP tools now proxy tasks to the hub.\n\nThe hub tab speaks a generic WebSocket protocol (defined in `hub-ws.ts` in the extension package) and has no knowledge of MCP. See the hub's protocol docs for message format details.\n\n## Architecture\n\nPure JS ESM, no build step. Source files are the published artifacts.\n\n```\nsrc/\n├── index.js        # CLI entry: MCP server (stdio) + opens launcher\n├── hub-bridge.js   # HTTP server + WebSocket bridge to hub tab\n└── launcher.html   # Bootstrap page: detects extension, triggers hub open\n```\n\n## Dev\n\n```bash\nnpm run dev:ext\nnpx @modelcontextprotocol/inspector node packages/mcp/src/index.js\n```\n</yakaai_skills>"}