From 5809fe32490c15d6935dcb7da824d50ee9f5d574 Mon Sep 17 00:00:00 2001 From: Simon <10131203+gaomeng1900@users.noreply.github.com> Date: Thu, 16 Apr 2026 17:04:23 +0800 Subject: [PATCH] refactor(setup): consolidate prettier config and streamline CI - Replace scattered .prettierignore files with a single root config - Add scripts/ci.js to orchestrate lint, format, typecheck, commitlint, and build - Simplify ci.yml to use ci.js and npm ci - Apply prettier formatting to docs, locales, and HTML files --- .agents/skills/pre-impl-discussion/SKILL.md | 22 ++- .github/workflows/ci.yml | 20 +-- .prettierignore | 8 + .vscode/settings.json | 3 +- docs/CODE_OF_CONDUCT.md | 46 ++--- docs/README-zh.md | 1 - docs/developer-guide.md | 2 +- package.json | 1 + packages/core/src/prompts/.prettierignore | 1 - packages/extension/.prettierignore | 2 - packages/extension/docs/extension_api.md | 163 ++++++++---------- .../public/_locales/en/messages.json | 18 +- .../public/_locales/zh_CN/messages.json | 18 +- packages/extension/src/agent/.prettierignore | 1 - packages/mcp/README.md | 8 +- packages/mcp/src/launcher.html | 36 ++-- packages/website/index.html | 32 +++- scripts/ci.js | 55 ++++++ 18 files changed, 255 insertions(+), 182 deletions(-) create mode 100644 .prettierignore delete mode 100644 packages/core/src/prompts/.prettierignore delete mode 100644 packages/extension/.prettierignore delete mode 100644 packages/extension/src/agent/.prettierignore create mode 100644 scripts/ci.js diff --git a/.agents/skills/pre-impl-discussion/SKILL.md b/.agents/skills/pre-impl-discussion/SKILL.md index 70d29f9..8445dac 100644 --- a/.agents/skills/pre-impl-discussion/SKILL.md +++ b/.agents/skills/pre-impl-discussion/SKILL.md @@ -1,7 +1,7 @@ --- 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" +argument-hint: 'Describe the change to evaluate' --- # Pre-Implementation Discussion @@ -40,6 +40,7 @@ Do NOT skip this step. Do NOT rely on assumptions about what "most projects" do. - **Search in parallel** to save time — batch independent queries Common pitfalls: + - Assuming compatibility without checking actual version constraints - Confusing roadmap/aspirations with actual released state - Missing transitive constraints (a dependency of a dependency) @@ -53,6 +54,7 @@ Share a **brief** assessment. Tables work well for comparisons. Highlight **bloc Surface the decisions the user needs to make. Present them as clear choices with trade-offs, not as a recommendation monologue. For each decision point: + - What are the options? (2-3 max) - What does each option cost or give up? - What's your lean and why? (one sentence) @@ -66,6 +68,7 @@ The user will ask follow-up questions, raise concerns, or challenge assumptions. - **Update your mental model** based on user feedback Common mistakes in this phase: + - Repeating the full plan after every small clarification - Answering a narrow question with a broad redesign - Treating user questions as confirmation to proceed @@ -84,17 +87,18 @@ Keep it terse. Tables over paragraphs. No explanations the user already heard du ### 7. Wait for Confirmation After presenting the final plan, **stop and wait**. The user will either: + - Confirm → then (and only then) proceed to implementation - Ask more questions → go back to step 5 - Modify scope → update the plan and re-present ## Anti-Patterns -| Don't | Do Instead | -|-------|------------| -| Start implementation "to test" without confirmation | Present findings and wait | -| Repeat the full plan in every response | Answer the specific question asked | -| Say "X should work" without checking | Say "I need to verify X" and research it | -| Assume project structure or constraints | Read the actual files | -| Present one recommendation as the only option | Present 2-3 options with trade-offs | -| Write long paragraphs explaining trade-offs | Use tables and bullet points | +| Don't | Do Instead | +| --------------------------------------------------- | ---------------------------------------- | +| Start implementation "to test" without confirmation | Present findings and wait | +| Repeat the full plan in every response | Answer the specific question asked | +| Say "X should work" without checking | Say "I need to verify X" and research it | +| Assume project structure or constraints | Read the actual files | +| Present one recommendation as the only option | Present 2-3 options with trade-offs | +| Write long paragraphs explaining trade-offs | Use tables and bullet points | diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9423693..2212352 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,6 +1,6 @@ +name: CI permissions: contents: read -name: CI on: push: @@ -17,6 +17,8 @@ jobs: steps: - uses: actions/checkout@v6 + with: + fetch-depth: 0 - name: Setup Node.js ${{ matrix.node-version }} uses: actions/setup-node@v6 @@ -24,18 +26,8 @@ jobs: node-version: ${{ matrix.node-version }} cache: 'npm' - # test on default version of npm - # - 9.6~10.8 on node@20 - # - 11.3~11.6 on node@24 - - - name: Node and NPM version - run: node --version && npm --version - - name: Install dependencies - run: npm install + run: npm ci - - name: Lint - run: npx eslint . && npx prettier --check **/*.ts - - - name: Build - run: npm run build + - name: CI checks + run: node scripts/ci.js diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 0000000..00f576d --- /dev/null +++ b/.prettierignore @@ -0,0 +1,8 @@ +# Prompt templates (formatted manually for LLM readability) +*prompt.md + +# Generated +packages/extension/.wxt + +# Vendored +**/components/ui diff --git a/.vscode/settings.json b/.vscode/settings.json index b57a9cb..a2c1a48 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -22,7 +22,7 @@ "packages/*/node_modules": true }, "markdownlint.config": { - // "comment": "Relaxed rules", + // Relaxed rules "default": true, "whitespace": false, "line_length": false, @@ -36,6 +36,7 @@ "ol-prefix": false, "no-duplicate-heading": false }, + "editor.defaultFormatter": "esbenp.prettier-vscode", "js/ts.tsdk.path": "node_modules/typescript/lib", "typescript.tsdk": "node_modules/typescript/lib", "typescript.enablePromptUseWorkspaceTsdk": true diff --git a/docs/CODE_OF_CONDUCT.md b/docs/CODE_OF_CONDUCT.md index 0ad856e..5bb84c0 100644 --- a/docs/CODE_OF_CONDUCT.md +++ b/docs/CODE_OF_CONDUCT.md @@ -16,22 +16,22 @@ appearance, race, religion, or sexual identity and orientation. Examples of behavior that contributes to creating a positive environment include: -* Using welcoming and inclusive language -* Being respectful of differing viewpoints and experiences -* Gracefully accepting constructive criticism -* Focusing on what is best for the community -* Showing empathy towards other community members +- Using welcoming and inclusive language +- Being respectful of differing viewpoints and experiences +- Gracefully accepting constructive criticism +- Focusing on what is best for the community +- Showing empathy towards other community members Examples of unacceptable behavior by participants include: -* The use of sexualized language or imagery and unwelcome sexual attention or - advances -* Trolling, insulting/derogatory comments, and personal or political attacks -* Public or private harassment -* Publishing others' private information, such as a physical or electronic - address, without explicit permission -* Other conduct which could reasonably be considered inappropriate in a - professional setting +- The use of sexualized language or imagery and unwelcome sexual attention or + advances +- Trolling, insulting/derogatory comments, and personal or political attacks +- Public or private harassment +- Publishing others' private information, such as a physical or electronic + address, without explicit permission +- Other conduct which could reasonably be considered inappropriate in a + professional setting ## Our Responsibilities @@ -85,19 +85,19 @@ available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.ht 有助于创造正面环境的行为包括但不限于: -* 使用友好和包容性语言 -* 尊重不同的观点和经历 -* 耐心地接受建设性批评 -* 关注对社区最有利的事情 -* 友善对待其他社区成员 +- 使用友好和包容性语言 +- 尊重不同的观点和经历 +- 耐心地接受建设性批评 +- 关注对社区最有利的事情 +- 友善对待其他社区成员 身为参与者不能接受的行为包括但不限于: -* 使用与性有关的言语或是图像,以及不受欢迎的性骚扰 -* 捣乱/煽动/造谣的行为或进行侮辱/贬损的评论,人身攻击及政治攻击 -* 公开或私下的骚扰 -* 未经许可地发布他人的个人资料,例如住址或是电子地址 -* 其他可以被合理地认定为不恰当或者违反职业操守的行为 +- 使用与性有关的言语或是图像,以及不受欢迎的性骚扰 +- 捣乱/煽动/造谣的行为或进行侮辱/贬损的评论,人身攻击及政治攻击 +- 公开或私下的骚扰 +- 未经许可地发布他人的个人资料,例如住址或是电子地址 +- 其他可以被合理地认定为不恰当或者违反职业操守的行为 ## 我们的责任 diff --git a/docs/README-zh.md b/docs/README-zh.md index 44d7524..c6b0dc8 100644 --- a/docs/README-zh.md +++ b/docs/README-zh.md @@ -106,4 +106,3 @@ this project possible. --- **⭐ 如果觉得 PageAgent 有用或有趣,请给项目点个星!** - diff --git a/docs/developer-guide.md b/docs/developer-guide.md index cd5e317..60dc5ff 100644 --- a/docs/developer-guide.md +++ b/docs/developer-guide.md @@ -24,7 +24,7 @@ For contribution rules and expectations, see [../CONTRIBUTING.md](../CONTRIBUTIN ## 📦 Project Structure -This is a **monorepo** with npm workspaces. +This is a **monorepo** with npm workspaces. Published packages: diff --git a/package.json b/package.json index 4b437ce..9b3b552 100644 --- a/package.json +++ b/package.json @@ -36,6 +36,7 @@ "postpublish": "npm run postpublish --workspaces --if-present", "typecheck": "tsc --noEmit -p tsconfig.typecheck.json && tsc --noEmit -p packages/extension/tsconfig.json", "lint": "eslint .", + "ci": "node scripts/ci.js", "cleanup": "rm -rf packages/*/dist && rm -rf packages/*/.output", "prepare": "husky || true" }, diff --git a/packages/core/src/prompts/.prettierignore b/packages/core/src/prompts/.prettierignore deleted file mode 100644 index 7b801dc..0000000 --- a/packages/core/src/prompts/.prettierignore +++ /dev/null @@ -1 +0,0 @@ -system_prompt.md \ No newline at end of file diff --git a/packages/extension/.prettierignore b/packages/extension/.prettierignore deleted file mode 100644 index 3744489..0000000 --- a/packages/extension/.prettierignore +++ /dev/null @@ -1,2 +0,0 @@ -.wxt -src/components/ui \ No newline at end of file diff --git a/packages/extension/docs/extension_api.md b/packages/extension/docs/extension_api.md index 1586660..ab661d2 100644 --- a/packages/extension/docs/extension_api.md +++ b/packages/extension/docs/extension_api.md @@ -42,33 +42,28 @@ localStorage.setItem('PageAgentExtUserAuthToken', 'your-token') ## Quick Start ```typescript -import type { - AgentActivity, - AgentStatus, - ExecutionResult, - HistoricalEvent, -} from '@page-agent/core' +import type { AgentActivity, AgentStatus, ExecutionResult, HistoricalEvent } from '@page-agent/core' // Wait for extension injection (up to 1 second) async function waitForExtension(timeout = 1000): Promise { - const start = Date.now() - while (Date.now() - start < timeout) { - if (window.PAGE_AGENT_EXT) return true - await new Promise((r) => setTimeout(r, 100)) - } - return false + const start = Date.now() + while (Date.now() - start < timeout) { + if (window.PAGE_AGENT_EXT) return true + await new Promise((r) => setTimeout(r, 100)) + } + return false } // Usage if (await waitForExtension()) { - const result = await window.PAGE_AGENT_EXT!.execute('Click the login button', { - baseURL: 'https://api.openai.com/v1', - apiKey: 'your-api-key', - model: 'gpt-5.2', - onStatusChange: (status) => console.log('Status:', status), - onActivity: (activity) => console.log('Activity:', activity), - }) - console.log('Result:', result) + const result = await window.PAGE_AGENT_EXT!.execute('Click the login button', { + baseURL: 'https://api.openai.com/v1', + apiKey: 'your-api-key', + model: 'gpt-5.2', + onStatusChange: (status) => console.log('Status:', status), + onActivity: (activity) => console.log('Activity:', activity), + }) + console.log('Result:', result) } ``` @@ -90,10 +85,10 @@ Execute one agent task. Parameters: -| Name | Type | Required | Description | -| ---- | ---- | -------- | ----------- | -| `task` | `string` | Yes | Task description | -| `config` | `ExecuteConfig` | Yes | LLM settings, options, and callbacks | +| Name | Type | Required | Description | +| -------- | --------------- | -------- | ------------------------------------ | +| `task` | `string` | Yes | Task description | +| `config` | `ExecuteConfig` | Yes | LLM settings, options, and callbacks | Returns: `Promise` @@ -106,33 +101,28 @@ Stop the current task. Install `@page-agent/core` for complete types: ```typescript -import type { - AgentActivity, - AgentStatus, - ExecutionResult, - HistoricalEvent, -} from '@page-agent/core' +import type { AgentActivity, AgentStatus, ExecutionResult, HistoricalEvent } from '@page-agent/core' export interface ExecuteConfig { - baseURL: string - model: string - apiKey?: string + baseURL: string + model: string + apiKey?: string - // Global system-level instructions for the agent. - // Equivalent to AgentConfig.instructions.system. - systemInstruction?: string + // Global system-level instructions for the agent. + // Equivalent to AgentConfig.instructions.system. + systemInstruction?: string - // Include the initial tab where page JS starts. Default: true. - includeInitialTab?: boolean + // Include the initial tab where page JS starts. Default: true. + includeInitialTab?: boolean - // Control all unpinned tabs in the window instead of only the tab group. - // When enabled, agent sees and can switch to every non-pinned tab. - // Default: false. Experimental. - experimentalIncludeAllTabs?: boolean + // Control all unpinned tabs in the window instead of only the tab group. + // When enabled, agent sees and can switch to every non-pinned tab. + // Default: false. Experimental. + experimentalIncludeAllTabs?: boolean - onStatusChange?: (status: AgentStatus) => void - onActivity?: (activity: AgentActivity) => void - onHistoryUpdate?: (history: HistoricalEvent[]) => void + onStatusChange?: (status: AgentStatus) => void + onActivity?: (activity: AgentActivity) => void + onHistoryUpdate?: (history: HistoricalEvent[]) => void } export type Execute = (task: string, config: ExecuteConfig) => Promise @@ -148,31 +138,31 @@ type AgentStatus = 'idle' | 'running' | 'completed' | 'error' ```typescript type AgentActivity = - | { type: 'thinking' } - | { type: 'executing'; tool: string; input: unknown } - | { type: 'executed'; tool: string; input: unknown; output: string; duration: number } - | { type: 'retrying'; attempt: number; maxAttempts: number } - | { type: 'error'; message: string } + | { type: 'thinking' } + | { type: 'executing'; tool: string; input: unknown } + | { type: 'executed'; tool: string; input: unknown; output: string; duration: number } + | { type: 'retrying'; attempt: number; maxAttempts: number } + | { type: 'error'; message: string } ``` `HistoricalEvent` ```typescript type HistoricalEvent = - | { type: 'step'; stepIndex: number; reflection: AgentReflection; action: Action } - | { type: 'observation'; content: string } - | { type: 'user_takeover' } - | { type: 'retry'; message: string; attempt: number; maxAttempts: number } - | { type: 'error'; message: string; rawResponse?: unknown } + | { type: 'step'; stepIndex: number; reflection: AgentReflection; action: Action } + | { type: 'observation'; content: string } + | { type: 'user_takeover' } + | { type: 'retry'; message: string; attempt: number; maxAttempts: number } + | { type: 'error'; message: string; rawResponse?: unknown } ``` `ExecutionResult` ```typescript interface ExecutionResult { - success: boolean - data: string - history: HistoricalEvent[] + success: boolean + data: string + history: HistoricalEvent[] } ``` @@ -182,15 +172,15 @@ interface ExecutionResult { ```typescript const result = await window.PAGE_AGENT_EXT!.execute( - 'Fill in the email field with test@example.com and click Submit', - { - baseURL: 'https://api.openai.com/v1', - apiKey: process.env.OPENAI_API_KEY!, - model: 'gpt-5.2', - includeInitialTab: false, // Optional: exclude current tab - onStatusChange: (status) => console.log(status), - onActivity: (activity) => console.log(activity), - } + 'Fill in the email field with test@example.com and click Submit', + { + baseURL: 'https://api.openai.com/v1', + apiKey: process.env.OPENAI_API_KEY!, + model: 'gpt-5.2', + includeInitialTab: false, // Optional: exclude current tab + onStatusChange: (status) => console.log(status), + onActivity: (activity) => console.log(activity), + } ) ``` @@ -205,35 +195,30 @@ window.PAGE_AGENT_EXT!.stop() If you are not importing `@page-agent/core`, add: ```typescript -import type { - AgentActivity, - AgentStatus, - ExecutionResult, - HistoricalEvent, -} from '@page-agent/core' +import type { AgentActivity, AgentStatus, ExecutionResult, HistoricalEvent } from '@page-agent/core' interface ExecuteConfig { - baseURL: string - model: string - apiKey?: string + baseURL: string + model: string + apiKey?: string - systemInstruction?: string + systemInstruction?: string - includeInitialTab?: boolean - experimentalIncludeAllTabs?: boolean - onStatusChange?: (status: AgentStatus) => void - onActivity?: (activity: AgentActivity) => void - onHistoryUpdate?: (history: HistoricalEvent[]) => void + includeInitialTab?: boolean + experimentalIncludeAllTabs?: boolean + onStatusChange?: (status: AgentStatus) => void + onActivity?: (activity: AgentActivity) => void + onHistoryUpdate?: (history: HistoricalEvent[]) => void } declare global { - interface Window { - PAGE_AGENT_EXT_VERSION?: string - PAGE_AGENT_EXT?: { - version: string - execute: Execute - stop: () => void + interface Window { + PAGE_AGENT_EXT_VERSION?: string + PAGE_AGENT_EXT?: { + version: string + execute: Execute + stop: () => void + } } - } } ``` diff --git a/packages/extension/public/_locales/en/messages.json b/packages/extension/public/_locales/en/messages.json index 6a30abd..ee92be9 100644 --- a/packages/extension/public/_locales/en/messages.json +++ b/packages/extension/public/_locales/en/messages.json @@ -1,11 +1,11 @@ { - "extName": { - "message": "Page Agent Ext" - }, - "extDescription": { - "message": "AI-powered browser automation assistant. Control web pages with natural language." - }, - "extActionTitle": { - "message": "Open Page Agent" - } + "extName": { + "message": "Page Agent Ext" + }, + "extDescription": { + "message": "AI-powered browser automation assistant. Control web pages with natural language." + }, + "extActionTitle": { + "message": "Open Page Agent" + } } diff --git a/packages/extension/public/_locales/zh_CN/messages.json b/packages/extension/public/_locales/zh_CN/messages.json index d25db95..d449835 100644 --- a/packages/extension/public/_locales/zh_CN/messages.json +++ b/packages/extension/public/_locales/zh_CN/messages.json @@ -1,11 +1,11 @@ { - "extName": { - "message": "Page Agent Ext" - }, - "extDescription": { - "message": "AI 驱动的浏览器自动化助手,用自然语言控制网页。" - }, - "extActionTitle": { - "message": "打开 Page Agent" - } + "extName": { + "message": "Page Agent Ext" + }, + "extDescription": { + "message": "AI 驱动的浏览器自动化助手,用自然语言控制网页。" + }, + "extActionTitle": { + "message": "打开 Page Agent" + } } diff --git a/packages/extension/src/agent/.prettierignore b/packages/extension/src/agent/.prettierignore deleted file mode 100644 index 7b801dc..0000000 --- a/packages/extension/src/agent/.prettierignore +++ /dev/null @@ -1 +0,0 @@ -system_prompt.md \ No newline at end of file diff --git a/packages/mcp/README.md b/packages/mcp/README.md index dd76f2d..94ff096 100644 --- a/packages/mcp/README.md +++ b/packages/mcp/README.md @@ -36,11 +36,11 @@ Same format — add the config to the MCP settings of your client. ## MCP Tools -| Tool | Input | Description | -| -------------- | ------------------ | ---------------------------------------------------- | +| Tool | Input | Description | +| -------------- | ------------------ | ----------------------------------------------------- | | `execute_task` | `{ task: string }` | Execute a browser task in natural language. Blocking. | -| `get_status` | — | Returns `{ connected, busy }` | -| `stop_task` | — | Stop the currently running task. | +| `get_status` | — | Returns `{ connected, busy }` | +| `stop_task` | — | Stop the currently running task. | ## Environment Variables diff --git a/packages/mcp/src/launcher.html b/packages/mcp/src/launcher.html index a4bdd89..27ad3b0 100644 --- a/packages/mcp/src/launcher.html +++ b/packages/mcp/src/launcher.html @@ -3,7 +3,10 @@ - + Page Agent MCP Launcher diff --git a/scripts/ci.js b/scripts/ci.js new file mode 100644 index 0000000..54c46c9 --- /dev/null +++ b/scripts/ci.js @@ -0,0 +1,55 @@ +#!/usr/bin/env node +/** + * CI check script. Run locally before commit or in GitHub Actions. + * + * Usage: + * node scripts/ci.js # run all checks + * node scripts/ci.js --no-build # skip build step + */ +import chalk from 'chalk' +import { execSync } from 'child_process' + +import { parallelTask } from './parallel-task.js' + +const args = new Set(process.argv.slice(2)) +const skipBuild = args.has('--no-build') + +function run(label, command) { + console.log(chalk.bgBlue.white.bold(` ▸ ${label} `)) + execSync(command, { stdio: 'inherit' }) +} + +function isMainBranch() { + if (process.env.GITHUB_REF) return process.env.GITHUB_REF === 'refs/heads/main' + try { + return execSync('git rev-parse --abbrev-ref HEAD', { encoding: 'utf-8' }).trim() === 'main' + } catch { + return true + } +} + +// 1. Commitlint — skip on main +if (isMainBranch()) { + console.log(chalk.dim(' ▸ commitlint (skipped on main)')) +} else { + const from = execSync('git merge-base origin/main HEAD', { encoding: 'utf-8' }).trim() + run('commitlint', `npx commitlint --from ${from} --to HEAD`) +} + +// 2. Lint + Format + Typecheck in parallel +console.log(chalk.bgBlue.white.bold(' ▸ lint + format + typecheck ')) +await parallelTask( + [ + { label: 'lint', command: 'npm run lint' }, + { label: 'format', command: 'npx prettier --check .' }, + { label: 'typecheck', command: 'npm run typecheck' }, + ], + { timeoutMs: 120_000 } +) + +// 3. Build +if (skipBuild) { + console.log(chalk.dim(' ▸ build (skipped)')) +} else { + run('build', 'npm run build') +}