-
Notifications
You must be signed in to change notification settings - Fork 0
Expose global file() function in the functional API #7
Description
Summary
Currently, the only way to configure file logging is via the class API:
import { createScript } from '@axhxrx/script';
const script = createScript();
await script.file({ path: logPath, output: 'full', timestamps: true });
script.add('echo hello').description('Say hello');
await script.execute();This forces scripts that need file logging to use createScript() even when they don't need any other class-level features. It would be cleaner to expose a global file() function that delegates to Script.default.file(...), matching the pattern of the existing global functions (add, banner, validate, execute):
import { add, banner, execute, file, validate } from '@axhxrx/script';
await file({ path: logPath, output: 'full', timestamps: true });
validate('Running on Linux', isLinux);
banner('Do stuff');
add('echo hello').description('Say hello');
await execute();This would keep all scripts on the functional API and eliminate the createScript() ceremony when file logging is the only reason for using the class API.
Claude skill for @axhxrx/script
We built a Claude skill that documents the conventions and API patterns for writing scripts with this library. It covers file structure, style rules (named functions for validations/skipIf, Node.js APIs over Deno-specific ones, etc.), and a quick API reference. Attaching it here for reference — it could be useful as a starting point for an official skill to ship with the library.
axhxrx-script SKILL.md
---
name: axhxrx-script
description: Use the @axhxrx/script library to write readable, maintainable TypeScript scripts instead of shell scripts.
---
# @axhxrx/script — TypeScript Setup Scripts
Write setup and automation scripts using `@axhxrx/script`, a TypeScript DSL that replaces shell scripts. Use this skill when writing new install/setup scripts or refactoring existing ones.
## Why this library exists
Shell scripts are hard to debug, hard to test, and their syntax is a nightmare. This library replaces them with TypeScript:
- Steps are queued with `add()`, shown as a plan, then executed sequentially
- Pre-flight validations catch problems before anything runs
- `--dry-run` and `--yes` flags come free
- File logging with timestamps and auto-redaction of secrets
- `.skipIf()` for idempotent steps, `.or()` / `.and()` for conditional chaining
- Function steps let you use TypeScript (fetch, fs, etc.) instead of `curl | python3` hacks
Scripts are published to JSR and runnable on any machine with Deno — no install step needed. They can also be used with Bun, and in most cases also Node.js 24+
## Script file structure
Order matters. Scripts should read top-to-bottom as "what this does", with implementation details at the bottom.
1. Shebang: #!/usr/bin/env -S deno run -A
2. JSDoc: What the script does and how to run it
3. Imports: @axhxrx/script, node:fs, node:process, etc.
4. Constants: Paths, target values, config
5. Async init: Top-level await for data that steps depend on
6. Validations: validate() calls referencing named functions
7. Steps: banner() + add() chains — the core of the script
8. Execute: await execute() or await script.execute()
9. Final message: Post-execution output
10. Utility functions: All named functions used above
Functions are hoisted, so utility functions defined at the bottom are available throughout the file. This keeps the "what" at the top and the "how" at the bottom.
## Style rules
### Use named functions for validations and skipIf conditions
Named functions are used in user-facing output (function names appear in logs and will be used for skip reasons in future versions). They also read better in the step declarations.
CORRECT:
validate('Running on Linux', isLinux);
add('git clone ...').description('Clone repo').skipIf(isRepoAlreadyCloned);
INCORRECT:
validate('Running on Linux', () => process.platform === 'linux' || 'Not Linux');
add('git clone ...').description('Clone repo').skipIf(() => existsSync(path));
### Use .skipIf() instead of conditional add()
Let every step appear in the plan. Use .skipIf() so the skip is logged at execution time.
### Blank lines between step chains
Each add() chain gets a blank line before and after it.
### Use Node.js APIs, not Deno-specific APIs
Scripts should be runtime-agnostic. Use node: imports for everything that has a Node.js equivalent (process.env, process.platform, readFileSync, etc.).
### JSDoc format
No leading asterisks on body lines. Paragraphs separated by blank lines.(Filed by Claude Opus 4.6 (1M context), running as a VS Code extension agent, per verbal request of @masonmark)