name: google-zx-scripting description: "Writes and executes JavaScript-based shell scripts using Google's zx library. Use when writing shell scripts, automation, build tools, file processing, CLI tools, deployment scripts, data pipelines, or batch operations. Also covers piping, streams, parallel execution, retries, cross-platform scripting, built-in fs utilities, and minimist argument parsing."
Google ZX Scripting
Use this skill when writing scripts with Google's zx library — the tool that makes shell scripting with JavaScript/TypeScript productive and safe. Read only the reference file(s) needed for the task.
Quick Start
All zx scripts use the lite entry point for portability. Scripts are standalone .mjs files — no project setup required.
#!/usr/bin/env npx zx
import { $, fs, path, glob, chalk } from 'zx/core';
const files = await glob('src/**/*.ts');
await $`eslint ${files}`;
Run directly:
chmod +x script.mjs && ./script.mjs
# or
npx zx script.mjs
Critical Rules
- Always use
zx/core— never barezx. Avoids the heavy CLI wrapper. - Template literals auto-escape —
$`echo ${userInput}`is safe. Never manually quote interpolated variables. - Arrays expand correctly —
$`cmd ${arrayOfArgs}`expands each element as a separate quoted argument. - Non-zero exits throw — use
.nothrow()to suppress when you expect failures (e.g.,grepwith no matches). within()for isolation — creates an async scope with its own$.cwdand$.env. Essential for parallel tasks.- Pipe with
.pipe()— use$`cmd1`.pipe($`cmd2`)instead of shell|. - Prefer zx builtins —
glob()overfind,fsovercat/cp/mv,fetch()overcurl. cd()is global — usewithin()or$({cwd: '/path'})for scoped directory changes.- Scripts must be
.mjs— for top-levelawaitsupport without bundler config.
Reference Map
| Need | File |
|---|---|
$, ProcessPromise, ProcessOutput, configuration, piping, streams |
references/core-api.md |
| Ad-hoc scripts, CLI tools, build scripts, deployment, project scaffolding | references/scripting-patterns.md |
| File processing, data pipelines, batch ops, AI scripts, log analysis | references/processing-recipes.md |
Task Routing
- Writing a quick one-off command or shell automation ->
references/scripting-patterns.md - Building a build/deploy/CI script ->
references/scripting-patterns.md - Processing files, data, logs, or batch operations ->
references/processing-recipes.md - Need API details for
$, pipes, streams, config ->references/core-api.md - Combining multiple patterns (e.g., build + process) -> read both relevant files
Import Cheatsheet
// Core — always start here
import { $, fs, path, glob, chalk } from 'zx/core';
// Additional utilities (import individually as needed)
import { spinner, retry, question, echo, sleep, within,
stdin, tmpdir, tmpfile, which, ps, kill,
quote, YAML, argv, fetch } from 'zx/core';
Validation Checkpoints
Before executing destructive or bulk operations, always verify scope:
// Verify glob matches before bulk processing
const files = await glob('src/**/*.ts');
console.log(`Found ${files.length} files`);
if (files.length === 0) throw new Error('No files matched — check glob pattern');
if (files.length > 500) throw new Error(`Too many files (${files.length}) — narrow the glob`);
// Dry-run flag for destructive operations
const dryRun = argv['dry-run'] ?? false;
for (const file of files) {
if (dryRun) { console.log(`[dry-run] would delete ${file}`); continue; }
await fs.rm(file);
}
For parallel operations, use Promise.allSettled() to handle partial failures:
const results = await Promise.allSettled(
files.map(f => within(async () => $`process ${f}`))
);
const failed = results.filter(r => r.status === 'rejected');
if (failed.length > 0) {
console.error(chalk.red(`${failed.length}/${results.length} tasks failed`));
failed.forEach(r => console.error(r.reason));
process.exitCode = 1;
}
Conventions for Generated Scripts
- Shebang line: Always include
#!/usr/bin/env npx zxas the first line - Verbose off: Set
$.verbose = falsebefore main logic to suppress command echoing - Error handling: Use the
main().catch()pattern — wrap logic inasync function main(), then callmain().catch(err => { console.error(chalk.red(err.message)); process.exit(1); }) - Output colors: Use
chalk.green()for success,chalk.red()for errors,chalk.yellow()for warnings,chalk.dim()for secondary info - Progress: Use
spinner()for long-running operations in interactive contexts - Arguments: Use
argv(pre-parsedminimist) for CLI argument handling - Temp files: Use
tmpdir()andtmpfile()— they auto-clean on exit - Parallelism: Use
Promise.all()withwithin()for parallel operations that need isolation