Skip to content
28 changes: 14 additions & 14 deletions .claude/skills/sync-tsdown-cli/SKILL.md
Original file line number Diff line number Diff line change
@@ -1,21 +1,21 @@
---
name: sync-tsdown-cli
description: Compare tsdown CLI options with vp pack and sync any new or removed options. Use when tsdown is upgraded or when you need to check for CLI option drift between tsdown and vp pack.
description: Sync tsdown runtime CLI options with vp pack after a tsdown upgrade. Use for option forwarding changes; use sync-upstream-cli-help for static help wording.
allowed-tools: Read, Grep, Glob, Edit, Bash
---

# Sync tsdown CLI Options with vp pack
# Sync tsdown CLI

Compare the upstream `tsdown` CLI options with `vp pack` (defined in `packages/cli/src/pack-bin.ts`) and sync any differences.
Runtime options live in `packages/cli/src/pack-bin.ts`; static help lives in
`packages/cli/src/help.ts`.

## Steps

1. Run `npx tsdown --help` from `packages/cli/` to get tsdown's current CLI options
2. Read `packages/cli/src/pack-bin.ts` to see vp pack's current options
3. Compare and add any new tsdown options to `pack-bin.ts` using the existing cac `.option()` pattern
4. If tsdown removed options, do NOT remove them from `pack-bin.ts` -- instead add a code comment like `// NOTE: removed from tsdown CLI in vX.Y.Z` above the option so reviewers can decide whether to follow up
5. Preserve intentional differences:
- `-c, --config` is intentionally commented out (vp pack uses vite.config.ts)
- `--env-prefix` has a different default (`['VITE_PACK_', 'TSDOWN_']`)
6. Verify with `pnpm --filter vite-plus build-ts` and `vp pack -h`
7. If new parameters were added, add a corresponding PTY snapshot case under `crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/` to verify the new option works correctly
1. Run `npx tsdown --help` from `packages/cli/` and compare it with `pack-bin.ts`.
2. Add new forwarded options using the existing cac `.option()` pattern. For removed
options, add `// NOTE: removed from tsdown CLI in vX.Y.Z` for reviewer follow-up.
3. Preserve runtime differences: `-c, --config` stays disabled because Vite+ uses
`vite.config.ts`, and `--env-prefix` keeps the `['VITE_PACK_', 'TSDOWN_']` default.
4. For static labels and descriptions, follow `sync-upstream-cli-help`; do not adapt
upstream wording to explain runtime differences.
5. Run `pnpm --filter vite-plus build-ts` and `vp pack -h`. Add a focused PTY snapshot
case under `crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/` when a new runtime
option is exposed.
49 changes: 49 additions & 0 deletions .claude/skills/sync-upstream-cli-help/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
---
name: sync-upstream-cli-help
description: Sync Vite+'s static CLI help with Vite, Vitest, Oxlint, Oxfmt, and tsdown while preserving intentional omissions. Use when an upstream dependency upgrade changes CLI help.
allowed-tools: Read, Grep, Glob, Edit, Bash
---

# Sync upstream CLI help

## Input and target

- Read the diff from `$CLI_HELP_DIFF_REPORT`; first run
`test -r "$CLI_HELP_DIFF_REPORT"`. Act only when `$CLI_HELP_DIFF_CHANGED` is
`true`.
- Treat the upgraded tool's `--help` output as the source of truth. The report locates
changes and versions; rerun the exact version when its diff is truncated.
- Edit `commandHelpDocs` in `packages/cli/src/help.ts`.
- Do not edit `packages/cli/src/utils/help.ts` for content drift. It owns terminal
wrapping, alignment, and the right margin.

| Upstream help | Document entry |
| --------------------- | -------------- |
| `vite --help` | `dev` |
| `vite build --help` | `build` |
| `vite preview --help` | `preview` |
| `vitest --help` | `test` |
| `oxlint --help` | `lint` |
| `oxfmt --help` | `fmt` |
| `tsdown --help` | `pack` |

## Change

- For items Vite+ exposes, copy upstream labels, descriptions, section titles, and
section guidance exactly. Preserve intentional lines and lists, but not terminal
padding, automatic wrapping, ANSI color, or version banners.
- Remove an upstream item only after confirming Vite+ no longer supports or
deliberately retains it.

## Do not change

- Keep Vite+-owned usage, summaries, examples, and documentation URLs.
- Keep config selectors/loaders hidden, including `--config`, `--configLoader`, and
`--disable-nested-config`.
- Do not add standalone modes such as `--init`, `--migrate`, or `--lsp`, top-level
`--version`, or options Vite+ does not forward.
- Do not change runtime forwarding or rewrite upstream wording for a Vite+-specific
runtime default. Use `sync-tsdown-cli` for tsdown runtime changes.

Re-record the affected CLI help snapshots and inspect their diffs. Do not modify help
documents when the report contains no actionable exposed change.
111 changes: 111 additions & 0 deletions .github/scripts/__tests__/cli-help-diff.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
/// <reference types="node" />

import { execFileSync } from 'node:child_process';
import { mkdtempSync, readFileSync, rmSync, writeFileSync } from 'node:fs';
import { tmpdir } from 'node:os';
import { join, resolve } from 'node:path';

import { afterEach, expect, test } from 'vitest';

const SCRIPT_PATH = resolve(import.meta.dirname, '../cli-help-diff.ts');
const tempDirs: string[] = [];

afterEach(() => {
for (const dir of tempDirs.splice(0)) {
rmSync(dir, { force: true, recursive: true });
}
});

test('reports changed, unchanged, and not-updated CLI help in one comment', () => {
const tempDir = mkdtempSync(join(tmpdir(), 'vite-plus-cli-help-test-'));
tempDirs.push(tempDir);
const beforePath = join(tempDir, 'before.json');
const afterPath = join(tempDir, 'after.json');
const githubOutputPath = join(tempDir, 'github-output.txt');
const reportPath = join(tempDir, 'report.md');
const before = {
tools: {
vite: { help: 'vite/1.0.0\n--old-option', version: '1.0.0' },
vitest: { help: 'vitest/1.0.0\n--watch', version: '1.0.0' },
oxlint: { help: 'oxlint 1.0.0\n--fix', version: '1.0.0' },
oxfmt: { help: 'oxfmt\n--write', version: '1.0.0' },
tsdown: { help: 'tsdown 1.0.0\n--old-option', version: '1.0.0' },
},
};
const after = {
tools: {
vite: { help: 'vite/2.0.0\n--new-option', version: '2.0.0' },
vitest: { help: 'vitest/1.0.0\n--watch', version: '1.0.0' },
oxlint: { help: 'oxlint 2.0.0\n--fix', version: '2.0.0' },
oxfmt: { help: 'oxfmt\n--write', version: '1.0.0' },
tsdown: { help: 'tsdown 2.0.0\n--new-option', version: '2.0.0' },
},
};
writeFileSync(beforePath, JSON.stringify(before));
writeFileSync(afterPath, JSON.stringify(after));

execFileSync(
process.execPath,
[
SCRIPT_PATH,
'report',
'--before',
beforePath,
'--after',
afterPath,
'--output',
reportPath,
'--github-output',
githubOutputPath,
],
{ cwd: resolve(import.meta.dirname, '../../..') },
);

const report = readFileSync(reportPath, 'utf8');
expect(report).toContain('## ⚠️ Upstream CLI help changes detected');
expect(report).toContain('<strong>⚠️ Vite: CLI help changed (1.0.0 → 2.0.0)</strong>');
expect(report).toContain('<strong>✅ Oxlint: no CLI help changes (1.0.0 → 2.0.0)</strong>');
expect(report).toContain('<strong>➖ Vitest: no version update (1.0.0)</strong>');
expect(report).toContain('```diff\n--- vite@1.0.0\n+++ vite@2.0.0');
expect(report).toContain('---old-option');
expect(report).toContain('+--new-option');
expect(report).not.toContain('-vite/1.0.0');
expect(report).not.toContain('+vite/2.0.0');
expect(readFileSync(githubOutputPath, 'utf8')).toBe('has-changes=true\n');
});

test('reports no machine-readable changes when help is unchanged', () => {
const tempDir = mkdtempSync(join(tmpdir(), 'vite-plus-cli-help-test-'));
tempDirs.push(tempDir);
const snapshotPath = join(tempDir, 'snapshot.json');
const githubOutputPath = join(tempDir, 'github-output.txt');
const reportPath = join(tempDir, 'report.md');
const snapshot = {
tools: Object.fromEntries(
['vite', 'vitest', 'oxlint', 'oxfmt', 'tsdown'].map((tool) => [
tool,
{ help: `${tool}/1.0.0\n--help`, version: '1.0.0' },
]),
),
};
writeFileSync(snapshotPath, JSON.stringify(snapshot));

execFileSync(
process.execPath,
[
SCRIPT_PATH,
'report',
'--before',
snapshotPath,
'--after',
snapshotPath,
'--output',
reportPath,
'--github-output',
githubOutputPath,
],
{ cwd: resolve(import.meta.dirname, '../../..') },
);

expect(readFileSync(githubOutputPath, 'utf8')).toBe('has-changes=false\n');
});
Loading
Loading