Guidance for agents working in this repository.
This repo is a TypeScript GitHub Action for comparing bundle sizes. The action runs from the committed dist/index.js bundle, not directly from src/.
Core behavior today:
- Accepts an npm
package-namebaseline. - Accepts newline-delimited
filesto compare. - Resolves local files under
path. - Resolves npm release tarballs for the latest release plus up to 10 previous stable releases.
- Resolves tarball files relative to the archive root, stripping a single shared top-level directory such as
package/. - Measures gzip-compressed byte size.
- Writes a JSON comparison report.
- Does not enforce budgets, thresholds, or target sizes.
Keep src/index.ts thin. It should remain the bootstrap/re-export surface, not the place where all logic accumulates.
Current modules:
src/action.ts: orchestrates the GitHub Action run and action outputs.src/config.ts: reads and validates@actions/coreinputs.src/npm.ts: resolves npm package metadata and release tarball baselines.src/paths.ts: normalizes configured paths and prevents path traversal.src/tarball.ts: downloads tarballs and extracts regular files from.tar.gzarchives.src/comparison.ts: computes gzip sizes and per-file/totals deltas.src/report.ts: writes the JSON comparison report.src/types.ts: shared data types.src/index.ts: invokesrun()when executed and re-exports testable helpers.
Prefer extending these focused modules over reintroducing a large single entrypoint.
Use PNPM. The package manager is pinned in package.json.
pnpm install --frozen-lockfile: install dependencies in CI or a fresh checkout.pnpm run lint: run Oxlint.pnpm run typecheck: runtsc --noEmit.pnpm test: run TypeScript tests with Vitest.pnpm run build: bundlesrc/index.tsintodist/index.jswith Vite.pnpm run clean: remove generated Vite output.
Always run pnpm test after behavior changes. Always run pnpm run build before finishing changes that affect runtime code, dependencies, action metadata, or generated output. Commit updated dist/index.js with source changes because GitHub Actions executes from dist/index.js.
When asked to commit changes in this repository, use Conventional Commit messages and split the work into focused commits by coherent scope. Do not squash unrelated changes into one large hunk; include all changes the user asked to commit unless a file appears unrelated, unsafe, or blocked, in which case call that out before committing.
- The action uses Node 24.
- Runtime dependencies should stay small. Prefer Node built-ins when they are clear and maintainable.
- Do not assume the caller has installed dependencies at action runtime.
- Do not build the target project inside the action; workflows should build artifacts before invoking this action.
- Treat missing configured local files and latest-release baseline files as errors. Historical previous-release missing files may be reported as incomplete context.
Configured file paths are user-facing paths, not archive-internal paths. For npm-style tarballs, callers should write paths like dist/axios.min.js, not package/dist/axios.min.js.
Path rules:
- Normalize backslashes to POSIX-style
/for configured paths and tar entries. - Reject absolute paths and
..traversal. - Resolve local files inside the configured
pathroot only. - When all tarball files share one top-level directory, expose both the original tar entry path and the stripped path in the lookup map.
Gzip size is the canonical metric for the current capability. Measure gzip from the file bytes; do not assume files are already compressed. Report raw gzip byte counts and calculate deltas from those counts.
The report shape should remain machine-readable JSON first. Markdown summaries, PR comments, artifact uploads, and threshold enforcement are separate future capabilities.
Tests live in tests/ as TypeScript files and run with Vitest against source modules through @/ aliases.
Every code change in this repo should include corresponding tests. Treat tests as part of the change, not optional follow-up work. If a change truly cannot be tested, explicitly explain why in the implementation notes or OpenSpec tasks.
Prefer module-aligned tests: source behavior in src/<module>.ts should be covered by tests/<module>.test.ts, except src/index.ts when it remains only a bootstrap/re-export surface. Interface-only files such as src/types.ts are covered by TypeScript compilation unless they gain runtime behavior.
Cover these behaviors when changing comparison logic:
- Input path parsing and rejection of unsafe paths.
- Tarball extraction and package-root stripping.
- Missing baseline/local file failures with useful error messages.
- Gzip size and delta report structure.
- Output path safety for generated reports.
Prefer small direct tests for individual modules over end-to-end tests that require network access.
This repo uses OpenSpec for scoped changes.
- Active changes live under
openspec/changes/<name>/. - Archived changes live under
openspec/changes/archive/YYYY-MM-DD-<name>/. - Main specs live under
openspec/specs/.
When implementing a spec-driven change, read proposal, design, specs, and tasks before editing. Mark tasks complete as they are implemented. If a change introduces or modifies requirements, sync the delta spec into openspec/specs/ before archiving.
The current tarball gzip behavior is captured in openspec/specs/tarball-gzip-comparison/spec.md. Npm release baseline behavior is captured in openspec/specs/npm-release-baselines/spec.md after the corresponding change is archived.
- Keep action code modular early. A single
index.tsbecomes difficult to review once config parsing, tarball handling, path safety, gzip measurement, reporting, and GitHub outputs are all mixed together. - Preserve
pathas the local project root. New inputs should not overload it. - Treat the npm package name as the release baseline contract. Do not add package-manager shorthand such as
npm:axios@latestunless a spec/proposal calls for it. - Build comparison files on PRs from already-built local artifacts. On GitHub pull requests, the checkout/build represents the merge result when using the default PR merge ref.
- Avoid target-size enforcement in the current capability. Reporting and enforcing are different product behaviors and should remain separate changes.
- Keep
dist/index.jssynchronized withsrc/; otherwise the committed action will not match the reviewed TypeScript.