qwen-code/scripts/copy_files.js
Shaojin Wen 10291e16f2
refactor(core): move review skill incident narratives to DESIGN.md (#8499)
* perf(review): issue independent setup calls in one response

Measured on a real small-PR run: the stretch from parse-args to the
first agent launch took 7 minutes of wall clock, one round-trip at a
time, on calls that never needed an order — pr-context, comment-status
and the Step 2 rules load are mutually independent reads. Step 1 now
tells the orchestrator to issue all three in a single response (the
same rule Step 3 already enforces for the agent fan-out) and to page
their outputs in shared responses too. comment-status loses its
wait-for-the-context-file guard in worktree mode: learning whether
inline comments exist cost a serial round-trip, while running it on a
commentless PR just writes an empty index. Step 6's two deterministic
gates (script-lint, test-plan) get the same one-response note.

The orderings that matter are kept explicit: fetch-pr before everything
(it creates the worktree and the plan), the roster after the rules load
(it bakes the rules into every brief).

* refactor(core): move review skill incident narratives to DESIGN.md

SKILL.md is injected wholesale into the review orchestrator's context on
every /review run and re-billed on each of its turns, and ~16KB of it was
incident narrative — accounts of past dogfood failures and measurements
that justify rules but are not themselves instructions.

Move 50 such narrative blocks into a new 'Measured incidents (moved from
SKILL.md)' section of DESIGN.md (47 anchors, not loaded at runtime),
leaving every rule in place with a short
'(measured; DESIGN.md — <anchor>)' pointer. Force-bearing figures stay
inline where the number is the argument (e.g. the ~161s cold npm ci, the
41% test-code median, the PR #6457 one-of-five checklist measurement).

No instruction, gate, format, flag, threshold, or ordering changed; the
YAML frontmatter and all 35 fenced code blocks are byte-identical, and
the MUST / Do not / never imperative counts are unchanged outside the
moved narrative text (verified by script).

SKILL.md: 237,847 -> 228,266 bytes; DESIGN.md: 106,708 -> 125,184 bytes.

* fix(review): keep DESIGN.md out of the runtime bundle and pin pointers

The slim refactor left DESIGN.md shipped beside SKILL.md in
dist/bundled/, so one curious read_file of the 125 KB maintainer
document would cost more context than the refactor saves. The bundle
copy now skips DESIGN.md, and SKILL.md gains a one-line guard telling
the orchestrator the pointers are for humans auditing a rule.

Also addresses review feedback: a test pins both directions of the
SKILL.md incident-pointer mapping, the transcribed-argument narrative
keeps its referent after the move, the incidents section title loses
its changelog suffix, and Step 2 no longer asks for a base fetch that
fetch-pr already performed.

* fix(review): gate setup batching by effort and consolidate incident blocks

Address round-1 review feedback on the skill-slim PR:

- Gate the ONE-response setup batch and the comment-status call to high
  and medium effort, matching Step 2's low-effort skip.
- Scope the Step 6 lint/test-plan batching to same-repo PR reviews.
- Merge same-run incident blocks (self-composed Approve into the
  paraphrased roster prompt; archive verdict into the narrated-away cap),
  cross-reference the roster-size and relocated-Critical tellings, and
  state the #8368 path in its block plus the pointer it was missing.
- Pointer-ize the last inline QQChannel narrative and fix the
  scripts-nobody-ran summary to match its block.
- Extend the DESIGN.md exclusion to copy_files.js so the transpiled
  dist/src build and the published core tarball stop shipping it.
- Pin the no-read_file guard and the batch ordering constraints in
  SKILL.test.ts, and fail loudly on pointers the regex cannot parse.

---------

Co-authored-by: verify <verify@local>
Co-authored-by: qwen-code-ci-bot <qwen-code-ci-bot@users.noreply.github.com>
2026-08-04 12:41:18 +00:00

112 lines
3.3 KiB
JavaScript

#!/usr/bin/env node
/**
* @license
* Copyright 2025 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
// Copyright 2025 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import fs from 'node:fs';
import path from 'node:path';
import { fileURLToPath } from 'node:url';
const extensionsToCopy = ['.md', '.json', '.sb'];
function isBundledSkillDesignDoc(normalizedPath) {
// DESIGN.md files are maintainer design narratives, not runtime inputs
// (see copy_bundle_assets.js); the transpiled build loads bundled skills
// from dist/src/, so they must stay out of it too.
return (
normalizedPath.startsWith('skills/bundled/') &&
path.basename(normalizedPath) === 'DESIGN.md'
);
}
function copyFilesRecursive(source, target, rootSourceDir) {
if (!fs.existsSync(target)) {
fs.mkdirSync(target, { recursive: true });
}
const items = fs.readdirSync(source, { withFileTypes: true });
for (const item of items) {
const sourcePath = path.join(source, item.name);
const targetPath = path.join(target, item.name);
if (item.isDirectory()) {
copyFilesRecursive(sourcePath, targetPath, rootSourceDir);
} else {
const ext = path.extname(item.name);
// Copy standard extensions, or .js files in i18n/locales directory
// Use path.relative for precise matching to avoid false positives
const relativePath = path.relative(rootSourceDir, sourcePath);
const normalizedPath = relativePath.replace(/\\/g, '/');
const isLocaleJs =
ext === '.js' && normalizedPath.startsWith('i18n/locales/');
if (
(extensionsToCopy.includes(ext) || isLocaleJs) &&
!isBundledSkillDesignDoc(normalizedPath)
) {
fs.copyFileSync(sourcePath, targetPath);
}
}
}
}
export function copyFiles({ root = process.cwd() } = {}) {
const sourceDir = path.join(root, 'src');
const targetDir = path.join(root, 'dist', 'src');
if (!fs.existsSync(sourceDir)) {
console.error(`Source directory ${sourceDir} not found.`);
process.exit(1);
}
copyFilesRecursive(sourceDir, targetDir, sourceDir);
// Copy example extensions into the bundle.
const packageName = path.basename(root);
if (packageName === 'cli') {
const examplesSource = path.join(
sourceDir,
'commands',
'extensions',
'examples',
);
const examplesTarget = path.join(
targetDir,
'commands',
'extensions',
'examples',
);
if (fs.existsSync(examplesSource)) {
fs.cpSync(examplesSource, examplesTarget, { recursive: true });
}
}
console.log('Successfully copied files.');
}
if (isDirectRun()) {
copyFiles();
}
function isDirectRun() {
return process.argv[1]
? fileURLToPath(import.meta.url) === path.resolve(process.argv[1])
: false;
}