diff --git a/.github/actions/configure-windows-runner/action.yml b/.github/actions/configure-windows-runner/action.yml index 3acfef9b0b..e383adca95 100644 --- a/.github/actions/configure-windows-runner/action.yml +++ b/.github/actions/configure-windows-runner/action.yml @@ -1,5 +1,5 @@ name: 'Configure Windows self-hosted runner' -description: 'Tunes a self-hosted Windows runner for the test gates: exports the Linux gates'' C.UTF-8 locale env (inert on Windows, where Node collates through ICU), redirects TEMP/TMP to RUNNER_TEMP, and puts Git Bash on PATH for bash-shell steps. Runs after actions/checkout because repository-local actions resolve from the workspace; callers turn off autocrlf before the checkout itself. Shared by the Windows merge-queue gate and the runner smoke workflow so both always run an identical configuration.' +description: "Tunes a self-hosted Windows runner for the test gates: exports the Linux gates' C.UTF-8 locale env (inert on Windows, where Node collates through ICU), redirects TEMP/TMP to RUNNER_TEMP, and puts Git Bash on PATH for bash-shell steps. Runs after actions/checkout because repository-local actions resolve from the workspace; callers turn off autocrlf before the checkout itself. Shared by the Windows merge-queue gate and the runner smoke workflow so both always run an identical configuration." runs: using: 'composite' diff --git a/.github/scripts/ci/classify-profile.test.mjs b/.github/scripts/ci/classify-profile.test.mjs index d3cedfe1fe..d16eb4b0f1 100644 --- a/.github/scripts/ci/classify-profile.test.mjs +++ b/.github/scripts/ci/classify-profile.test.mjs @@ -30,7 +30,10 @@ test('MDX is executable content, never docs_only', () => { assert.equal(classifyChangedFiles(['docs/guide.mdx']), 'full'); assert.equal(classifyChangedFiles(['docs/guide.MDX']), 'full'); assert.equal(classifyChangedFiles(['README.mdx']), 'full'); - assert.equal(classifyChangedFiles(['docs/usage.md', 'docs/guide.mdx']), 'full'); + assert.equal( + classifyChangedFiles(['docs/usage.md', 'docs/guide.mdx']), + 'full', + ); }); test('falls back to full for root docs names used as directories', () => { @@ -56,7 +59,9 @@ test('keeps yaml-dependent helper suites on the full profile', () => { // dependency-free github_ci_only lane can never execute it; downgrading a // PR that only edits that suite would green-light it without running it. assert.equal( - classifyChangedFiles(['.github/scripts/web-shell-visuals-publish.test.mjs']), + classifyChangedFiles([ + '.github/scripts/web-shell-visuals-publish.test.mjs', + ]), 'full', ); }); diff --git a/.github/scripts/ci/main-failure-signature.test.mjs b/.github/scripts/ci/main-failure-signature.test.mjs index dd9a3b8302..a55945ccba 100644 --- a/.github/scripts/ci/main-failure-signature.test.mjs +++ b/.github/scripts/ci/main-failure-signature.test.mjs @@ -191,9 +191,8 @@ test('the body stays bounded on a total-suite failure', () => { `body is ${body.length} chars, must stay under GitHub's 65,536 limit`, ); assert.ok(body.includes(`- …and ${400 - MAX_BODY_TESTS} more`)); - const markerCount = ( - body.match(new RegExp(TEST_MARKER_PREFIX, 'g')) ?? [] - ).length; + const markerCount = (body.match(new RegExp(TEST_MARKER_PREFIX, 'g')) ?? []) + .length; assert.ok( markerCount <= MAX_SEARCH_MARKERS, `body carries ${markerCount} markers, at most ${MAX_SEARCH_MARKERS}`, diff --git a/.github/scripts/classify-release-notes.test.mjs b/.github/scripts/classify-release-notes.test.mjs index a8e659d4fd..9c0ea4aa82 100644 --- a/.github/scripts/classify-release-notes.test.mjs +++ b/.github/scripts/classify-release-notes.test.mjs @@ -240,7 +240,7 @@ describe('release note classification', () => { // labels — its projectCards lookup fails on affected gh builds). "if (args[0] === 'api' && args[1] === '-X' && (args[2] === 'POST' || args[2] === 'DELETE') && /\\/issues\\/\\d+\\/labels/.test(args[3])) {", " const action = args[2] === 'DELETE' ? 'remove' : 'add';", - " const number = args[3].match(/\\/issues\\/(\\d+)\\/labels/)[1];", + ' const number = args[3].match(/\\/issues\\/(\\d+)\\/labels/)[1];', ` process.getBuiltinModule('node:fs').appendFileSync(${JSON.stringify(updates)}, number + ' ' + action + '\\n');`, ' process.exit(0);', '}', diff --git a/.github/scripts/create-electron-bridge-manifest.mjs b/.github/scripts/create-electron-bridge-manifest.mjs index 8d5944975c..18067dbb86 100644 --- a/.github/scripts/create-electron-bridge-manifest.mjs +++ b/.github/scripts/create-electron-bridge-manifest.mjs @@ -7,12 +7,7 @@ import path from 'node:path'; const options = parseArguments(process.argv.slice(2)); const assets = fs.readdirSync(options.assets).sort(); const patterns = { - macos: [ - /-arm64\.zip$/i, - /-x64\.zip$/i, - /-arm64\.dmg$/i, - /-x64\.dmg$/i, - ], + macos: [/-arm64\.zip$/i, /-x64\.zip$/i, /-arm64\.dmg$/i, /-x64\.dmg$/i], windows: [/-setup\.exe$/i], linux: [/\.AppImage$/i], }; @@ -20,7 +15,9 @@ const selectedPatterns = patterns[options.platform]; if (!selectedPatterns) { throw new Error(`Invalid --platform: ${options.platform}`); } -const names = selectedPatterns.map((pattern) => selectArtifact(assets, pattern)); +const names = selectedPatterns.map((pattern) => + selectArtifact(assets, pattern), +); const artifacts = names.map((name) => readArtifact(assets, name)); const primary = artifacts[0]; diff --git a/.github/scripts/dsw-swe-verified/make-terminal-bench-manifest.test.mjs b/.github/scripts/dsw-swe-verified/make-terminal-bench-manifest.test.mjs index 1db0c9a08d..77f5ac3896 100644 --- a/.github/scripts/dsw-swe-verified/make-terminal-bench-manifest.test.mjs +++ b/.github/scripts/dsw-swe-verified/make-terminal-bench-manifest.test.mjs @@ -1,13 +1,22 @@ import assert from 'node:assert/strict'; import { spawnSync } from 'node:child_process'; -import { mkdtempSync, mkdirSync, readFileSync, rmSync, writeFileSync } from 'node:fs'; +import { + mkdtempSync, + mkdirSync, + readFileSync, + rmSync, + writeFileSync, +} from 'node:fs'; import { tmpdir } from 'node:os'; import { dirname, join } from 'node:path'; import { after, before, describe, it } from 'node:test'; import { fileURLToPath } from 'node:url'; const root = mkdtempSync(join(tmpdir(), 'dsw-tb-manifest-')); -const script = join(dirname(fileURLToPath(import.meta.url)), 'make-terminal-bench-manifest.py'); +const script = join( + dirname(fileURLToPath(import.meta.url)), + 'make-terminal-bench-manifest.py', +); let archive; before(() => { @@ -23,18 +32,30 @@ before(() => { writeFileSync(join(task, 'instruction.md'), 'test\n'); } archive = join(root, 'tasks.tar.gz'); - const result = spawnSync('tar', ['-czf', archive, '-C', root, 'tasks'], { encoding: 'utf8' }); + const result = spawnSync('tar', ['-czf', archive, '-C', root, 'tasks'], { + encoding: 'utf8', + }); assert.equal(result.status, 0, result.stderr); }); after(() => rmSync(root, { recursive: true, force: true })); -const run = (...args) => spawnSync('python3', [script, '--archive', archive, ...args], { encoding: 'utf8' }); +const run = (...args) => + spawnSync('python3', [script, '--archive', archive, ...args], { + encoding: 'utf8', + }); describe('make-terminal-bench-manifest', () => { it('selects one exact task for an end-to-end smoke', () => { const output = join(root, 'one.json'); - const result = run('--limit', '1', '--instance-id', 'task-42', '--output', output); + const result = run( + '--limit', + '1', + '--instance-id', + 'task-42', + '--output', + output, + ); assert.equal(result.status, 0, result.stderr); const manifest = JSON.parse(readFileSync(output, 'utf8')); assert.equal(manifest.expected_instances, 1); @@ -51,7 +72,14 @@ describe('make-terminal-bench-manifest', () => { }); it('rejects an unknown exact task', () => { - const result = run('--limit', '1', '--instance-id', 'missing', '--output', join(root, 'bad.json')); + const result = run( + '--limit', + '1', + '--instance-id', + 'missing', + '--output', + join(root, 'bad.json'), + ); assert.equal(result.status, 1); assert.match(result.stderr, /Unknown Terminal-Bench/); }); diff --git a/.github/scripts/update-ecs-runner-qwen-workflow.test.mjs b/.github/scripts/update-ecs-runner-qwen-workflow.test.mjs index 2bcc5f0e6d..e41f26ad00 100644 --- a/.github/scripts/update-ecs-runner-qwen-workflow.test.mjs +++ b/.github/scripts/update-ecs-runner-qwen-workflow.test.mjs @@ -61,9 +61,9 @@ const updateJobName = workflow.match(/\n {2}update:\n {4}name: '([^']*)'/)?.[1] ?? ''; const poolPrefix = updateJobName.replace(/\$\{\{.*\}\}$/, ''); -// Both layouts, because CI runs `prettier --write .` before this suite and -// prettier reflows the inline matrix array onto its own lines. A parser that -// reads only the checked-in layout passes locally and finds zero pools there. +// Both layouts, because the checked-in workflow carries the reflowed form +// prettier produces for an inline matrix array, and older branches still +// carry the single-line one. A parser that reads only one finds zero pools. function parsePools(text) { return (text.match(/\n\s+runner:\s*\[([^\]]*)\]/)?.[1] ?? '') .split(',') diff --git a/.github/workflows/audio-capture-prebuilds.yml b/.github/workflows/audio-capture-prebuilds.yml index 4716ca541d..5556d3dd6c 100644 --- a/.github/workflows/audio-capture-prebuilds.yml +++ b/.github/workflows/audio-capture-prebuilds.yml @@ -50,7 +50,7 @@ jobs: runner: 'windows-2022' arch: 'x64' steps: - - uses: 'actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10' # v6.0.3 + - uses: 'actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10' # v6.0.3 - uses: 'actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e' # v6.4.0 with: node-version: '22' diff --git a/.github/workflows/build-and-publish-image.yml b/.github/workflows/build-and-publish-image.yml index 96fa1b7cf3..514178c750 100644 --- a/.github/workflows/build-and-publish-image.yml +++ b/.github/workflows/build-and-publish-image.yml @@ -46,7 +46,7 @@ jobs: run: 'echo "push_image=${PUSH_IMAGE}" >> "$GITHUB_OUTPUT"' - name: 'Checkout repository' - uses: 'actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10' # v6.0.3 + uses: 'actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10' # v6.0.3 with: ref: '${{ github.ref }}' @@ -189,7 +189,7 @@ jobs: issues: 'write' steps: - name: 'Checkout repository' - uses: 'actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10' # v6.0.3 + uses: 'actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10' # v6.0.3 with: persist-credentials: false diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 6b4c33c8fb..07139f6439 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -30,7 +30,7 @@ jobs: timeout-minutes: 30 steps: - name: 'Checkout' - uses: 'actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10' # v6.0.3 + uses: 'actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10' # v6.0.3 - name: 'Initialize CodeQL' uses: 'github/codeql-action/init@df559355d593797519d70b90fc8edd5db049e7a2' # ratchet:github/codeql-action/init@v3 diff --git a/.github/workflows/docs-page-action.yml b/.github/workflows/docs-page-action.yml index 288b1eac04..c6f36f64af 100644 --- a/.github/workflows/docs-page-action.yml +++ b/.github/workflows/docs-page-action.yml @@ -24,7 +24,7 @@ jobs: runs-on: 'ubuntu-latest' steps: - name: 'Checkout' - uses: 'actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10' # v6.0.3 + uses: 'actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10' # v6.0.3 - name: 'Setup Pages' uses: 'actions/configure-pages@45bfe0192ca1faeb007ade9deae92b16b8254a0d' # ratchet:actions/configure-pages@v6 diff --git a/.github/workflows/dsw-swe-verified-release.yml b/.github/workflows/dsw-swe-verified-release.yml index 91f14aec17..42b2cd8036 100644 --- a/.github/workflows/dsw-swe-verified-release.yml +++ b/.github/workflows/dsw-swe-verified-release.yml @@ -74,7 +74,7 @@ jobs: id: 'gate' env: EVENT_NAME: '${{ github.event_name }}' - RELEASE_TAG: '${{ github.event.release.tag_name || '''' }}' + RELEASE_TAG: "${{ github.event.release.tag_name || '' }}" RELEASE_PRERELEASE: '${{ github.event.release.prerelease || false }}' INPUT_EXECUTION_BACKEND: '${{ inputs.execution_backend }}' INPUT_INSTANCE_LIMIT: '${{ inputs.instance_limit }}' @@ -136,12 +136,12 @@ jobs: benchmark: name: 'Dispatch SWE-bench and Terminal-Bench to DSW' needs: 'release_gate' - if: '${{ needs.release_gate.outputs.should_run == ''true'' }}' + if: "${{ needs.release_gate.outputs.should_run == 'true' }}" runs-on: ['self-hosted', 'Linux', 'X64', 'qwen-benchmark-dsw-hk-eas'] timeout-minutes: 15 env: - RELEASE_TAG: '${{ github.event_name == ''release'' && github.event.release.tag_name || inputs.release_tag }}' - QWEN_REF: '${{ github.event_name == ''release'' && github.event.release.tag_name || inputs.qwen_release_tag || inputs.release_tag }}' + RELEASE_TAG: "${{ github.event_name == 'release' && github.event.release.tag_name || inputs.release_tag }}" + QWEN_REF: "${{ github.event_name == 'release' && github.event.release.tag_name || inputs.qwen_release_tag || inputs.release_tag }}" INSTANCE_LIMIT: '${{ needs.release_gate.outputs.instance_limit }}' BENCHMARK_INSTANCE_ID: '${{ needs.release_gate.outputs.instance_id }}' TERMINAL_BENCH_LIMIT: '${{ needs.release_gate.outputs.terminal_bench_limit }}' diff --git a/.github/workflows/qwen-code-pr-review.yml b/.github/workflows/qwen-code-pr-review.yml index c72cacadca..366d367604 100644 --- a/.github/workflows/qwen-code-pr-review.yml +++ b/.github/workflows/qwen-code-pr-review.yml @@ -3809,7 +3809,6 @@ jobs: gh pr comment "$PR_NUMBER" --repo "$REPO" --body-file "${WORKDIR}/report.md" || echo "::warning::Resolve was skipped, but posting the skip-reason comment failed." - # Publishes what the agent job produced — verification, push and the result # comment — on a runner that never executed the agent. It starts from a # fresh checkout, fetches the base and head refs from GitHub itself, and diff --git a/.github/workflows/qwen-pr-safety-precheck.yml b/.github/workflows/qwen-pr-safety-precheck.yml index 53222af08c..c5ff3c24f3 100644 --- a/.github/workflows/qwen-pr-safety-precheck.yml +++ b/.github/workflows/qwen-pr-safety-precheck.yml @@ -25,7 +25,7 @@ jobs: decision: '${{ steps.assess.outputs.decision }}' steps: - name: 'Checkout trusted precheck script' - uses: 'actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10' # v6.0.3 + uses: 'actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10' # v6.0.3 with: ref: '${{ github.event.repository.default_branch }}' sparse-checkout: '.github/scripts/pr-safety-precheck.mjs' diff --git a/.github/workflows/release-sdk-python.yml b/.github/workflows/release-sdk-python.yml index 27a343515d..dfe14fee61 100644 --- a/.github/workflows/release-sdk-python.yml +++ b/.github/workflows/release-sdk-python.yml @@ -34,7 +34,7 @@ on: default: false concurrency: - group: '${{ github.workflow }}-${{ github.event.inputs.create_nightly_release == ''true'' && ''nightly'' || github.event.inputs.create_preview_release == ''true'' && ''preview'' || ''stable'' }}' + group: "${{ github.workflow }}-${{ github.event.inputs.create_nightly_release == 'true' && 'nightly' || github.event.inputs.create_preview_release == 'true' && 'preview' || 'stable' }}" cancel-in-progress: false jobs: @@ -100,7 +100,7 @@ jobs: fi - name: 'Checkout' - uses: 'actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10' # v6.0.3 + uses: 'actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10' # v6.0.3 with: ref: '${{ github.event.inputs.ref || github.sha }}' fetch-depth: 0 @@ -136,7 +136,7 @@ jobs: echo "sha=$(git rev-parse HEAD)" >> "${GITHUB_OUTPUT}" - name: 'Setup Node.js' - uses: 'actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e' # v6.4.0 + uses: 'actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e' # v6.4.0 with: node-version-file: '.nvmrc' cache: 'npm' @@ -203,7 +203,7 @@ jobs: MANUAL_VERSION: '${{ inputs.version }}' - name: 'Setup Python' - uses: 'actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405' # v6.2.0 + uses: 'actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405' # v6.2.0 with: # Keep in sync with packages/sdk-python/pyproject.toml [project] requires-python. python-version: '3.11' diff --git a/.github/workflows/release-vscode-companion.yml b/.github/workflows/release-vscode-companion.yml index 4ae6a6ff52..af7d8d3487 100644 --- a/.github/workflows/release-vscode-companion.yml +++ b/.github/workflows/release-vscode-companion.yml @@ -64,7 +64,7 @@ jobs: steps: - name: 'Checkout' - uses: 'actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10' # v6.0.3 + uses: 'actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10' # v6.0.3 with: ref: '${{ github.event.release.tag_name || github.event.inputs.ref || github.sha }}' fetch-depth: 0 @@ -208,7 +208,7 @@ jobs: steps: - name: 'Checkout' - uses: 'actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10' # v6.0.3 + uses: 'actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10' # v6.0.3 with: ref: '${{ github.event.release.tag_name || github.event.inputs.ref || github.sha }}' fetch-depth: 0 @@ -295,7 +295,7 @@ jobs: steps: - name: 'Checkout' - uses: 'actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10' # v6.0.3 + uses: 'actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10' # v6.0.3 with: ref: '${{ github.event.release.tag_name || github.event.inputs.ref || github.sha }}' diff --git a/.github/workflows/sdk-python.yml b/.github/workflows/sdk-python.yml index 41b7710e85..f6cdce04a1 100644 --- a/.github/workflows/sdk-python.yml +++ b/.github/workflows/sdk-python.yml @@ -77,10 +77,10 @@ jobs: python-version: ['3.10', '3.11', '3.12'] steps: - name: 'Checkout' - uses: 'actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10' # v6.0.3 + uses: 'actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10' # v6.0.3 - name: 'Set up Python' - uses: 'actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405' # v6.2.0 + uses: 'actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405' # v6.2.0 with: python-version: '${{ matrix.python-version }}' diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml index 1aa816c4a1..2044143c5a 100644 --- a/.github/workflows/stale.yml +++ b/.github/workflows/stale.yml @@ -24,7 +24,7 @@ jobs: group: '${{ github.workflow }}-stale' cancel-in-progress: true steps: - - uses: 'actions/stale@eb5cf3af3ac0a1aa4c9c45633dd1ae542a27a899' # v10.3.0 + - uses: 'actions/stale@eb5cf3af3ac0a1aa4c9c45633dd1ae542a27a899' # v10.3.0 with: repo-token: '${{ secrets.GITHUB_TOKEN }}' # Issues are intentionally disabled here; a separate policy will diff --git a/.github/workflows/update-ecs-runner-qwen.yml b/.github/workflows/update-ecs-runner-qwen.yml index 650354ad51..c578b0c36a 100644 --- a/.github/workflows/update-ecs-runner-qwen.yml +++ b/.github/workflows/update-ecs-runner-qwen.yml @@ -75,7 +75,14 @@ jobs: if: "${{ github.repository == 'QwenLM/qwen-code' }}" strategy: matrix: - runner: ['ecs-update-hk-1', 'ecs-update-hk-2', 'ecs-update-hk-3', 'ecs-update-hk-4', 'ecs-update-hk-5'] + runner: + [ + 'ecs-update-hk-1', + 'ecs-update-hk-2', + 'ecs-update-hk-3', + 'ecs-update-hk-4', + 'ecs-update-hk-5', + ] fail-fast: false runs-on: ['self-hosted', 'linux', 'x64', '${{ matrix.runner }}'] concurrency: @@ -150,7 +157,7 @@ jobs: issues: 'write' steps: - name: 'Checkout' - uses: 'actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10' # v6.0.3 + uses: 'actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10' # v6.0.3 with: persist-credentials: false diff --git a/.prettierignore b/.prettierignore index bb330c5579..df999081a0 100644 --- a/.prettierignore +++ b/.prettierignore @@ -23,3 +23,15 @@ Thumbs.db packages/vscode-ide-companion/schemas/settings.schema.json packages/cli/src/services/insight/templates/insightTemplate.ts packages/cua-driver/ +# The rest of eslint.config.js's global ignores, for the same reasons it +# gives. scripts/codemod/fixtures is the load-bearing one: those files are +# "intentionally non-idiomatic ink input/output", so normalising them to +# house style would corrupt the very data the codemod tests assert on. +scripts/codemod/fixtures/ +packages/live-host/ +packages/desktop-shell/runtime/ +# Vendored from mobile-next/mobile-mcp and carries its own eslint config, so +# reformatting it to house style only widens the diff against upstream on the +# next sync. eslint.config.js already ignores packages/mobile-mcp/** for the +# same reason; .prettierignore had cua-driver but not its sibling. +packages/mobile-mcp/ diff --git a/.qwen/skills/find-simplifications/SKILL.md b/.qwen/skills/find-simplifications/SKILL.md index cc481a5aca..38033aab5d 100644 --- a/.qwen/skills/find-simplifications/SKILL.md +++ b/.qwen/skills/find-simplifications/SKILL.md @@ -78,26 +78,26 @@ path this repo ships or loads. A path named by more than one row takes the most restrictive outcome: Never-a-target beats Report-only, which beats Landable. -| Territory | Outcome | -| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `packages/cli/src` — the whole package (`generated/` stays under the Never-a-target row below; `**/*.sb` stays under the Report-only row below; `i18n/locales/**` and `commands/extensions/examples/**` stay under the Report-only row below; `**/*.test.ts(x)`, `**/*.spec.ts(x)`, `**/__snapshots__/**` are never targets, always searched as consumers) | Landable | -| `scripts/`, `esbuild.config.js`, `eslint.legacy-filenames.mjs`, root manifests | Landable | +| Territory | Outcome | +| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `packages/cli/src` — the whole package (`generated/` stays under the Never-a-target row below; `**/*.sb` stays under the Report-only row below; `i18n/locales/**` and `commands/extensions/examples/**` stay under the Report-only row below; `**/*.test.ts(x)`, `**/*.spec.ts(x)`, `**/__snapshots__/**` are never targets, always searched as consumers) | Landable | +| `scripts/`, `esbuild.config.js`, `eslint.legacy-filenames.mjs`, root manifests | Landable | | Whole files or directories nothing consumes by any mechanism named above — no import and no runtime read, loader, manifest, or tool config — anywhere outside `packages/core/src`, `packages/audio-capture`, `packages/channels`, `packages/sdk-*`, `packages/acp-bridge`, `packages/vscode-ide-companion`, `packages/chrome-extension`, `packages/zed-extension`, `packages/web-shell`, `packages/core/vendor`, `.github`, and the Never-a-target row below | Landable | -| `docs/users/**`, `docs/developers/**`, `docs/index.md`, `docs/_meta.ts`, `packages/cli/src/i18n/locales/**`, `packages/cli/src/commands/extensions/examples/**` — as whole files or directories | **Report-only** — copied into the published tarball (`scripts/prepare-package.js` copies the locales and extension examples; `scripts/copy_bundle_assets.js` copies `docs/users/` for qc-helper) and consumed by the published docs site (`docs-site/scripts/link-public-docs.mjs` symlinks `docs/users/` and `docs/developers/` into the Nextra build per `PUBLIC_DOC_ROOTS` and copies `docs/index.md` and `docs/_meta.ts`; the site discovers pages by walking that tree); consumers are runtime reads — qc-helper's doc paths, the i18n loader's segment-assembled `import()`, `/extensions new` scaffolds — never imports. Individual orphan locale keys stay class-4 candidates: their proof greps the literal key, naming its mechanism | -| `docs-site/` | **Report-only** — standalone published-site app, not a workspace member; route files are consumed by Next.js filesystem routing and an out-of-repo deploy, never imports, and no in-repo CI builds it | -| Tracked `.qwen/skills/**`, `.qwen/agents/**`, `.qwen/e2e-tests/**`, `docs/design/**`, `docs/plans/**` | **Report-only** — consumed by the skill loader, agent definitions, and process readers (including `AGENTS.md` itself), never imports | -| `AGENTS.md`, `CLAUDE.md`, `SECURITY.md`, `CONTRIBUTING.md`, `.prettierrc.json`, `.prettierignore`, `.editorconfig`, `.nvmrc`, `.npmrc`, `.yamllint.yml` | **Report-only** — consumed by external tooling through filename convention (agent harnesses, GitHub's security-policy UI, prettier and yamllint config auto-discovery, nvm, editors); never imports, and an in-repo grep for them measures only prose | -| Anything under `packages/core/src` | **Report-only** — published | -| `packages/audio-capture`, `packages/channels` | **Report-only** — npm-published (`--access public`) | -| `packages/core/vendor/**`, `packages/web-shell` | **Report-only** — shipped inside the published `@qwen-code/qwen-code` tarball / served to browsers by `qwen serve`; consumers are bundled or browser-side, never imports | -| `packages/cli/src/utils/**/*.sb` | **Report-only** — copied into the published bundle by extension glob (`scripts/copy_bundle_assets.js` copies `packages/**/*.sb`, `scripts/prepare-package.js` lists `'*.sb'`) and read at runtime through a segment-assembled path (`resolveSeatbeltProfileFile()` builds `sandbox-macos-${profile}.sb`); consumers are never imports, and a basename grep measures zero | -| Any key in `packages/cli/src/config/settingsSchema.ts` | **Report-only** — see below | -| `packages/sdk-*`, `packages/acp-bridge`, protocol/wire shapes | **Report-only** — out-of-repo consumers | -| `packages/vscode-ide-companion`, `packages/chrome-extension`, `packages/zed-extension` | **Report-only** — shipped as store manifests; the store is the consumer | -| `.github/` — workflows, actions, CODEOWNERS | **Report-only** — consumed GitHub-side: triggers, required checks, cross-repo `uses:` | -| `package.json` dependencies | **Report-only** — bundlers and postinstall hide consumers | -| Comments, JSDoc, commented-out code | **Out of scope** — `AGENTS.md` says do not delete existing comments as cleanup | -| `packages/desktop-shell`, `packages/cua-driver`, `packages/mobile-mcp`, `**/generated/**`, `**/*.test.ts(x)`, `**/*.spec.ts(x)`, `**/__snapshots__/**` | Never a target; always searched as consumers — vitest discovers tests by filename glob, nothing imports them, so an import-based orphan detector matches every live test vacuously | +| `docs/users/**`, `docs/developers/**`, `docs/index.md`, `docs/_meta.ts`, `packages/cli/src/i18n/locales/**`, `packages/cli/src/commands/extensions/examples/**` — as whole files or directories | **Report-only** — copied into the published tarball (`scripts/prepare-package.js` copies the locales and extension examples; `scripts/copy_bundle_assets.js` copies `docs/users/` for qc-helper) and consumed by the published docs site (`docs-site/scripts/link-public-docs.mjs` symlinks `docs/users/` and `docs/developers/` into the Nextra build per `PUBLIC_DOC_ROOTS` and copies `docs/index.md` and `docs/_meta.ts`; the site discovers pages by walking that tree); consumers are runtime reads — qc-helper's doc paths, the i18n loader's segment-assembled `import()`, `/extensions new` scaffolds — never imports. Individual orphan locale keys stay class-4 candidates: their proof greps the literal key, naming its mechanism | +| `docs-site/` | **Report-only** — standalone published-site app, not a workspace member; route files are consumed by Next.js filesystem routing and an out-of-repo deploy, never imports, and no in-repo CI builds it | +| Tracked `.qwen/skills/**`, `.qwen/agents/**`, `.qwen/e2e-tests/**`, `docs/design/**`, `docs/plans/**` | **Report-only** — consumed by the skill loader, agent definitions, and process readers (including `AGENTS.md` itself), never imports | +| `AGENTS.md`, `CLAUDE.md`, `SECURITY.md`, `CONTRIBUTING.md`, `.prettierrc.json`, `.prettierignore`, `.editorconfig`, `.nvmrc`, `.npmrc`, `.yamllint.yml` | **Report-only** — consumed by external tooling through filename convention (agent harnesses, GitHub's security-policy UI, prettier and yamllint config auto-discovery, nvm, editors); never imports, and an in-repo grep for them measures only prose | +| Anything under `packages/core/src` | **Report-only** — published | +| `packages/audio-capture`, `packages/channels` | **Report-only** — npm-published (`--access public`) | +| `packages/core/vendor/**`, `packages/web-shell` | **Report-only** — shipped inside the published `@qwen-code/qwen-code` tarball / served to browsers by `qwen serve`; consumers are bundled or browser-side, never imports | +| `packages/cli/src/utils/**/*.sb` | **Report-only** — copied into the published bundle by extension glob (`scripts/copy_bundle_assets.js` copies `packages/**/*.sb`, `scripts/prepare-package.js` lists `'*.sb'`) and read at runtime through a segment-assembled path (`resolveSeatbeltProfileFile()` builds `sandbox-macos-${profile}.sb`); consumers are never imports, and a basename grep measures zero | +| Any key in `packages/cli/src/config/settingsSchema.ts` | **Report-only** — see below | +| `packages/sdk-*`, `packages/acp-bridge`, protocol/wire shapes | **Report-only** — out-of-repo consumers | +| `packages/vscode-ide-companion`, `packages/chrome-extension`, `packages/zed-extension` | **Report-only** — shipped as store manifests; the store is the consumer | +| `.github/` — workflows, actions, CODEOWNERS | **Report-only** — consumed GitHub-side: triggers, required checks, cross-repo `uses:` | +| `package.json` dependencies | **Report-only** — bundlers and postinstall hide consumers | +| Comments, JSDoc, commented-out code | **Out of scope** — `AGENTS.md` says do not delete existing comments as cleanup | +| `packages/desktop-shell`, `packages/cua-driver`, `packages/mobile-mcp`, `**/generated/**`, `**/*.test.ts(x)`, `**/*.spec.ts(x)`, `**/__snapshots__/**` | Never a target; always searched as consumers — vitest discovers tests by filename glob, nothing imports them, so an import-based orphan detector matches every live test vacuously | A settings key with zero read sites is still not cleanup. The key is likely documented under `docs/users/` and completed from the generated schema; @@ -331,9 +331,9 @@ easy surface is gone — the remaining work is design, not sweeping). delete a colleague's. Removing the doc comment attached to a symbol you are deleting in the same commit is not comment deletion; anything else is out of scope. -- No formatting sweeps, no dependency bumps, no drive-by renames. CI's - Prettier step runs `--write`, not `--check`, so a formatting diff carries - zero signal. +- No formatting sweeps, no dependency bumps, no drive-by renames. But CI's + Prettier step runs `--check` and exits non-zero, so any deletion that + changes line shape must be accompanied by `npm run format`. - Never file a candidate whose evidence is a line count, a complexity score, or "this looks complex." It names no consumer and proves no deletion. diff --git a/.qwen/skills/find-simplifications/references/land.md b/.qwen/skills/find-simplifications/references/land.md index afc56a8991..1d2da4591a 100644 --- a/.qwen/skills/find-simplifications/references/land.md +++ b/.qwen/skills/find-simplifications/references/land.md @@ -139,9 +139,8 @@ Green CI is weaker evidence than it looks for a deletion: skips most steps. Deleting docs alongside code can quietly buy you less CI than you think. - The Prettier CI step (the `Run Prettier` step in `ci.yml` → `runPrettier()` - in `scripts/lint.js`) runs `prettier --write .`, not `--check`, so - formatting never fails a build — and a formatting-only diff carries zero - signal. + in `scripts/lint.js`) runs `prettier --check .`, so unformatted code fails + the build. Run `npm run format` before pushing. - The pre-commit hook runs formatters over staged files. Re-read the diff after committing; if the hook reformatted lines you did not otherwise touch, restore them in a follow-up commit (history is additive here — never amend). diff --git a/.qwen/skills/verify-pr/SKILL.md b/.qwen/skills/verify-pr/SKILL.md index 8438825776..8363ed1deb 100644 --- a/.qwen/skills/verify-pr/SKILL.md +++ b/.qwen/skills/verify-pr/SKILL.md @@ -743,10 +743,10 @@ since the merge-base, say so and re-measure there. `bash -n` and `shellcheck` on extracted `run:` blocks always work; the repo's wrapper only lints when the pinned binaries are present, so install them with `node scripts/lint.js --setup` and then invoke the - individual non-mutating checks (`--actionlint`, `--yamllint`, `--eslint`). - **Never run `node scripts/lint.js` with no arguments** — the no-arg form - also runs `prettier --write .`, which rewrites the PR working tree - underneath your A/B and replay harnesses. If the tools cannot be installed + individual non-mutating checks (`--actionlint`, `--yamllint`, `--eslint`, + `--prettier`). **Never run `node scripts/lint.js` with no arguments** — the + no-arg form calls `setupLinters()`, which wipes the linter temp dir and + re-downloads three pinned binaries before running anything. If the tools cannot be installed in-container, say which gate you could not run rather than implying it passed. For a new automated trigger, do the day-one cost math — arrival rate against the job's drain rate. Event history needs the API, diff --git a/package.json b/package.json index f1b3e81ea3..05bd1adac6 100644 --- a/package.json +++ b/package.json @@ -190,7 +190,7 @@ "prettier --write", "eslint --fix --max-warnings 0 --no-warn-ignored" ], - "*.{json,md}": [ + "*.{mjs,cjs,json,md,yml,yaml,css,html}": [ "prettier --write" ] } diff --git a/packages/cli/src/commands/extensions/list.ts b/packages/cli/src/commands/extensions/list.ts index 8a244e4435..97a9ac9cb2 100644 --- a/packages/cli/src/commands/extensions/list.ts +++ b/packages/cli/src/commands/extensions/list.ts @@ -8,11 +8,7 @@ import type { CommandModule } from 'yargs'; import { getErrorMessage } from '../../utils/errors.js'; import { writeStdoutLine, writeStderrLine } from '../../utils/stdioHelpers.js'; import { extensionToOutputString, getExtensionManager } from './utils.js'; -import { - t, - initializeI18n, - resolveLanguageSetting, -} from '../../i18n/index.js'; +import { t, initializeI18n, resolveLanguageSetting } from '../../i18n/index.js'; import { loadSettings } from '../../config/settings.js'; export async function handleList() { diff --git a/packages/cli/src/i18n/index.ts b/packages/cli/src/i18n/index.ts index 3332e484aa..7bfaca79f8 100644 --- a/packages/cli/src/i18n/index.ts +++ b/packages/cli/src/i18n/index.ts @@ -311,7 +311,7 @@ export async function initializeI18n( export function resolveLanguageSetting( settingsLanguage?: string, ): SupportedLanguage | 'auto' { - return ( - process.env['QWEN_CODE_LANG'] || settingsLanguage || 'auto' - ) as SupportedLanguage | 'auto'; + return (process.env['QWEN_CODE_LANG'] || settingsLanguage || 'auto') as + | SupportedLanguage + | 'auto'; } diff --git a/packages/cli/src/ui/components/extensions/steps/ScopeSelectStep.tsx b/packages/cli/src/ui/components/extensions/steps/ScopeSelectStep.tsx index 07e24bc2ee..606a2a1f92 100644 --- a/packages/cli/src/ui/components/extensions/steps/ScopeSelectStep.tsx +++ b/packages/cli/src/ui/components/extensions/steps/ScopeSelectStep.tsx @@ -54,10 +54,16 @@ export function ScopeSelectStep({ const title = mode === 'disable' ? t('Disable "{{name}}" - Select Scope', { - name: getExtensionDisplayName(selectedExtension, getCurrentLanguage()), + name: getExtensionDisplayName( + selectedExtension, + getCurrentLanguage(), + ), }) : t('Enable "{{name}}" - Select Scope', { - name: getExtensionDisplayName(selectedExtension, getCurrentLanguage()), + name: getExtensionDisplayName( + selectedExtension, + getCurrentLanguage(), + ), }); return ( diff --git a/packages/cli/src/ui/components/extensions/steps/UninstallConfirmStep.tsx b/packages/cli/src/ui/components/extensions/steps/UninstallConfirmStep.tsx index 8376a217fb..a3622b9f9a 100644 --- a/packages/cli/src/ui/components/extensions/steps/UninstallConfirmStep.tsx +++ b/packages/cli/src/ui/components/extensions/steps/UninstallConfirmStep.tsx @@ -60,7 +60,10 @@ export function UninstallConfirmStep({ {t('Are you sure you want to uninstall extension "{{name}}"?', { - name: getExtensionDisplayName(selectedExtension, getCurrentLanguage()), + name: getExtensionDisplayName( + selectedExtension, + getCurrentLanguage(), + ), })} diff --git a/packages/core/src/core/openaiContentGenerator/converter.test.ts b/packages/core/src/core/openaiContentGenerator/converter.test.ts index 1a59786b5b..27f47a64fb 100644 --- a/packages/core/src/core/openaiContentGenerator/converter.test.ts +++ b/packages/core/src/core/openaiContentGenerator/converter.test.ts @@ -6045,9 +6045,7 @@ describe('OpenAIContentConverter', () => { }; const tools = [ { - functionDeclarations: [ - { name: 'stable', parametersJsonSchema }, - ], + functionDeclarations: [{ name: 'stable', parametersJsonSchema }], }, ] as Tool[]; const compileStrict = vi.spyOn(SchemaValidator, 'compileStrict'); diff --git a/packages/core/src/tools/client-mcp-registrar.test.ts b/packages/core/src/tools/client-mcp-registrar.test.ts index ff623928ef..96a22b6cde 100644 --- a/packages/core/src/tools/client-mcp-registrar.test.ts +++ b/packages/core/src/tools/client-mcp-registrar.test.ts @@ -53,9 +53,10 @@ class InMemoryServerTransport { } /** Build a canned client-hosted MCP server exposing one echo tool. */ -function buildCannedServer( - sink: (message: JSONRPCMessage) => void, -): { transport: InMemoryServerTransport; ready: Promise } { +function buildCannedServer(sink: (message: JSONRPCMessage) => void): { + transport: InMemoryServerTransport; + ready: Promise; +} { const server = new McpServer({ name: 'chrome-tools', version: '0.0.1', diff --git a/packages/node-repl/scripts/lifecycle-smoke.mjs b/packages/node-repl/scripts/lifecycle-smoke.mjs index 8b7cc8cf71..c61c24dc00 100644 --- a/packages/node-repl/scripts/lifecycle-smoke.mjs +++ b/packages/node-repl/scripts/lifecycle-smoke.mjs @@ -32,7 +32,9 @@ const kernelChildren = () => { let failures = 0; const check = (label, cond, detail) => { - console.log(`${cond ? 'PASS' : 'FAIL'} ${label}${detail ? ` — ${detail}` : ''}`); + console.log( + `${cond ? 'PASS' : 'FAIL'} ${label}${detail ? ` — ${detail}` : ''}`, + ); if (!cond) failures++; }; @@ -86,8 +88,14 @@ const stillAlive = before.filter((pid) => { return false; } }); -check('kernel child reaped', stillAlive.length === 0, `alive=${stillAlive.join(',') || 'none'}`); +check( + 'kernel child reaped', + stillAlive.length === 0, + `alive=${stillAlive.join(',') || 'none'}`, +); if (!exited) server.kill('SIGKILL'); -console.log(failures === 0 ? '\nLIFECYCLE SMOKE PASSED' : `\n${failures} FAILED`); +console.log( + failures === 0 ? '\nLIFECYCLE SMOKE PASSED' : `\n${failures} FAILED`, +); process.exit(failures === 0 ? 0 : 1); diff --git a/packages/node-repl/scripts/smoke.mjs b/packages/node-repl/scripts/smoke.mjs index 109a2cc503..1160174933 100644 --- a/packages/node-repl/scripts/smoke.mjs +++ b/packages/node-repl/scripts/smoke.mjs @@ -29,7 +29,9 @@ const run = async (code) => { let failures = 0; const check = (label, cond, detail) => { - console.log(`${cond ? 'PASS' : 'FAIL'} ${label}${detail ? ` — ${detail}` : ''}`); + console.log( + `${cond ? 'PASS' : 'FAIL'} ${label}${detail ? ` — ${detail}` : ''}`, + ); if (!cond) failures++; }; @@ -46,7 +48,11 @@ try { ); const r3 = await run('nodeRepl.write(typeof host);'); const text3 = r3.mcp.content.find((b) => b.type === 'text')?.text ?? ''; - check('dynamic import binding persists', text3.includes('string'), text3.trim()); + check( + 'dynamic import binding persists', + text3.includes('string'), + text3.trim(), + ); // 3. Image output (1x1 PNG). const png = @@ -75,5 +81,9 @@ try { manager.dispose(); } -console.log(failures === 0 ? '\nALL SMOKE CHECKS PASSED' : `\n${failures} CHECK(S) FAILED`); +console.log( + failures === 0 + ? '\nALL SMOKE CHECKS PASSED' + : `\n${failures} CHECK(S) FAILED`, +); process.exit(failures === 0 ? 0 : 1); diff --git a/packages/node-repl/src/runtime/kernel.mjs b/packages/node-repl/src/runtime/kernel.mjs index 0d195fc8b3..72dbbae98e 100644 --- a/packages/node-repl/src/runtime/kernel.mjs +++ b/packages/node-repl/src/runtime/kernel.mjs @@ -839,10 +839,7 @@ function readSuccessfulBindings(module, bindingExports) { async function handleExec(message) { if (!config || !loader) throw new Error('kernel is not initialized'); if (activeExec) throw new Error('kernel received overlapping executions'); - if ( - pendingCancelExecId !== null && - pendingCancelExecId !== message.execId - ) { + if (pendingCancelExecId !== null && pendingCancelExecId !== message.execId) { pendingCancelExecId = null; } const expected = sortedBindingDescriptors(); diff --git a/packages/qwen-live/test-fixtures/fake-acp-agent.mjs b/packages/qwen-live/test-fixtures/fake-acp-agent.mjs index 930a46eb4b..cf4dca03ff 100644 --- a/packages/qwen-live/test-fixtures/fake-acp-agent.mjs +++ b/packages/qwen-live/test-fixtures/fake-acp-agent.mjs @@ -49,7 +49,11 @@ new AgentSideConnection( authMethods: [{ id: 'openai', name: 'Use OpenAI API key' }], agentCapabilities: { loadSession: false, - promptCapabilities: { image: true, audio: false, embeddedContext: false }, + promptCapabilities: { + image: true, + audio: false, + embeddedContext: false, + }, }, }; }, @@ -85,7 +89,11 @@ new AgentSideConnection( title: text.slice(0, 80), }, options: [ - { optionId: 'proceed_once', name: 'Allow once', kind: 'allow_once' }, + { + optionId: 'proceed_once', + name: 'Allow once', + kind: 'allow_once', + }, { optionId: 'cancel', name: 'Cancel', kind: 'reject_once' }, ], }); diff --git a/packages/web-shell/client/components/ChatEditor.module.css b/packages/web-shell/client/components/ChatEditor.module.css index 7ccb1f59ac..b05ddc68c2 100644 --- a/packages/web-shell/client/components/ChatEditor.module.css +++ b/packages/web-shell/client/components/ChatEditor.module.css @@ -250,18 +250,14 @@ overflow: hidden; } -:global( - [data-web-shell-toolbar-popover][data-web-shell-compact-overlay] - ) { +:global([data-web-shell-toolbar-popover][data-web-shell-compact-overlay]) { width: min(360px, calc(100vw - 16px)); max-width: calc(100vw - 16px); box-sizing: border-box; padding: 6px; } -:global( - [data-web-shell-toolbar-popover][data-web-shell-compact-overlay] - ) +:global([data-web-shell-toolbar-popover][data-web-shell-compact-overlay]) .dropdownItemDesc { display: -webkit-box; overflow-wrap: anywhere; @@ -1329,10 +1325,7 @@ border-radius: 6px; } -.editorShell[data-web-shell-compact-composer] - .toolbarRight - button - svg { +.editorShell[data-web-shell-compact-composer] .toolbarRight button svg { width: 16px; height: 16px; } diff --git a/packages/web-shell/client/components/GitModePopover.module.css b/packages/web-shell/client/components/GitModePopover.module.css index 8105245bf8..dad42349d8 100644 --- a/packages/web-shell/client/components/GitModePopover.module.css +++ b/packages/web-shell/client/components/GitModePopover.module.css @@ -15,8 +15,7 @@ padding: 3px 9px; border-radius: 20px; cursor: pointer; - border: 1px solid - color-mix(in srgb, var(--git-mode-current) 20%, transparent); + border: 1px solid color-mix(in srgb, var(--git-mode-current) 20%, transparent); background: color-mix(in srgb, var(--git-mode-current) 10%, transparent); color: var(--git-mode-current); transition: diff --git a/packages/web-shell/client/components/dialogs/GitDialog.module.css b/packages/web-shell/client/components/dialogs/GitDialog.module.css index 45f41eacdd..a49a06f5e6 100644 --- a/packages/web-shell/client/components/dialogs/GitDialog.module.css +++ b/packages/web-shell/client/components/dialogs/GitDialog.module.css @@ -262,11 +262,6 @@ gap: 8px; } - - - - - /* ── branch select (searchable) ─────────────── */ .branchSelectTrigger { display: inline-flex; diff --git a/packages/web-shell/client/index.html b/packages/web-shell/client/index.html index d67fa77e2a..a7c764eed8 100644 --- a/packages/web-shell/client/index.html +++ b/packages/web-shell/client/index.html @@ -24,8 +24,11 @@ background matches the app theme on mobile. Without this, the iOS Safari rubber-band overscroll and the status-bar / home-indicator safe areas briefly show the default white canvas. Storage key mirrors - THEME_STORAGE_KEY in main.tsx. + THEME_STORAGE_KEY in main.tsx. Kept on one line behind the guard below: + this comment ships verbatim in the built index.html, so the rationale + stays short. --> + diff --git a/scripts/benchmark-api-latency.mjs b/scripts/benchmark-api-latency.mjs index 3ed38c4c34..42dca1275c 100644 --- a/scripts/benchmark-api-latency.mjs +++ b/scripts/benchmark-api-latency.mjs @@ -26,16 +26,25 @@ const require = createRequire(import.meta.url); const { Agent } = require('../packages/core/node_modules/undici/index.js'); const ITERATIONS = parseInt(process.env['ITERATIONS'] ?? '3', 10); -const REQUEST_TIMEOUT_MS = parseInt(process.env['REQUEST_TIMEOUT_MS'] ?? '5000', 10); +const REQUEST_TIMEOUT_MS = parseInt( + process.env['REQUEST_TIMEOUT_MS'] ?? '5000', + 10, +); const DEFAULT_ENDPOINTS = [ - { url: 'https://api.openai.com', label: 'OpenAI' }, - { url: 'https://api.anthropic.com', label: 'Anthropic' }, - { url: 'https://dashscope.aliyuncs.com/compatible-mode/v1', label: 'DashScope (openai-compatible)' }, + { url: 'https://api.openai.com', label: 'OpenAI' }, + { url: 'https://api.anthropic.com', label: 'Anthropic' }, + { + url: 'https://dashscope.aliyuncs.com/compatible-mode/v1', + label: 'DashScope (openai-compatible)', + }, ]; const extraUrls = process.env['BENCHMARK_URLS'] - ? process.env['BENCHMARK_URLS'].split(' ').filter(Boolean).map((url) => ({ url, label: url })) + ? process.env['BENCHMARK_URLS'] + .split(' ') + .filter(Boolean) + .map((url) => ({ url, label: url })) : []; const ENDPOINTS = [...DEFAULT_ENDPOINTS, ...extraUrls]; @@ -146,10 +155,10 @@ for (const endpoint of ENDPOINTS) { console.log('\n\n=== Results ===\n'); console.log( 'Endpoint'.padEnd(36) + - 'Cold (avg)'.padStart(12) + - 'Warm (avg)'.padStart(12) + - 'Saved'.padStart(10) + - 'Improvement'.padStart(13), + 'Cold (avg)'.padStart(12) + + 'Warm (avg)'.padStart(12) + + 'Saved'.padStart(10) + + 'Improvement'.padStart(13), ); console.log('─'.repeat(83)); @@ -157,10 +166,10 @@ for (const r of results) { const status = r.pct >= 30 ? '✓' : r.pct >= 10 ? '~' : '✗'; console.log( r.label.slice(0, 35).padEnd(36) + - fmt(r.avgCold).padStart(12) + - fmt(r.avgWarm).padStart(12) + - fmt(r.saved).padStart(10) + - `${r.pct.toFixed(1)}% ${status}`.padStart(13), + fmt(r.avgCold).padStart(12) + + fmt(r.avgWarm).padStart(12) + + fmt(r.saved).padStart(10) + + `${r.pct.toFixed(1)}% ${status}`.padStart(13), ); } diff --git a/scripts/lint.js b/scripts/lint.js index db5fd6d31d..6bd885e0eb 100644 --- a/scripts/lint.js +++ b/scripts/lint.js @@ -296,9 +296,17 @@ export function runYamllint() { } } +// `--check`, not `--write`. `--write` reformats in place and exits 0 +// whether or not anything changed, and no caller inspects the tree +// afterwards, so for the life of this script the Prettier lane reported a +// pass on unformatted code and CI discarded the rewrites when the job +// ended. ESLint does not cover the gap either: eslint.config.js loads +// eslint-config-prettier, which turns formatting rules off rather than +// enforcing them. `npm run format` remains the way to fix what this +// reports. export function runPrettier() { console.log('\nRunning Prettier...'); - if (!runCommand('prettier --write .')) { + if (!runCommand('prettier --experimental-cli --check .')) { process.exit(1); } } diff --git a/scripts/measure-flicker.mjs b/scripts/measure-flicker.mjs index bff6722df6..dc053b9cd9 100755 --- a/scripts/measure-flicker.mjs +++ b/scripts/measure-flicker.mjs @@ -110,7 +110,9 @@ function renderDelta(current, baseline) { const b = baseline.counts[p.name]; const d = c - b; const arrow = d < 0 ? '↓' : d > 0 ? '↑' : '·'; - stdout.write(` ${p.name.padEnd(18)} ${d > 0 ? '+' : ''}${d} ${arrow}\n`); + stdout.write( + ` ${p.name.padEnd(18)} ${d > 0 ? '+' : ''}${d} ${arrow}\n`, + ); } stdout.write( '\ntip: lower clearTerminalPair (and lower clearScreen) on "current" wins.\n', diff --git a/scripts/tests/lint.test.js b/scripts/tests/lint.test.js index 39973f30c4..41f31bc66c 100644 --- a/scripts/tests/lint.test.js +++ b/scripts/tests/lint.test.js @@ -17,6 +17,7 @@ import { } from 'node:fs'; import { tmpdir } from 'node:os'; import path from 'node:path'; +import { fileURLToPath } from 'node:url'; import { afterEach, beforeEach, describe, expect, it } from 'vitest'; // getLinterTempDir joins with the platform separator; compare normalized @@ -223,3 +224,43 @@ describe('linter directories', () => { 15_000, ); }); + +// The --write to --check flip in runPrettier() is the whole point of the +// Prettier lane: --write reformats in place and exits 0 whether or not +// anything changed, so the lane reported a pass on unformatted code for as +// long as it was there (#11109). Nothing else pins the flag — +// ci-platform-lanes.test.js asserts the step exists and is gated, not what it +// runs — so a refactor or a well-meant "make lint auto-fix again" could +// restore the silent pass with every suite green. +describe('prettier lane', () => { + const repoRoot = path.resolve( + path.dirname(fileURLToPath(import.meta.url)), + '..', + '..', + ); + const lintSource = readFileSync( + path.join(repoRoot, 'scripts', 'lint.js'), + 'utf8', + ); + + it('checks formatting rather than rewriting it', () => { + expect(lintSource).toContain('prettier --experimental-cli --check .'); + expect(lintSource).not.toMatch(/prettier[^\n'"]*--write/); + }); + + // The gate and the remedy it points people at must resolve the same file + // set. The classic CLI reads only the repo-root .gitignore while the + // experimental one also honours nested ones, and this repo has 23 of those, + // so a mismatch lets the lane go red on files `npm run format` cannot reach. + it('uses the same Prettier CLI as npm run format', () => { + const pkg = JSON.parse( + readFileSync(path.join(repoRoot, 'package.json'), 'utf8'), + ); + const formatIsExperimental = + pkg.scripts.format.includes('--experimental-cli'); + const gateIsExperimental = lintSource.includes( + 'prettier --experimental-cli --check', + ); + expect(gateIsExperimental).toBe(formatIsExperimental); + }); +});