Script execution
flog run <script> executes the matching package.json script in the current
package. Flog locates the nearest workspace root when invoked from a package or
one of its subdirectories.
flog run build
flog run build -- --watch
Matching pre<script> and post<script> hooks run before and after the main
script. Arguments after the script name are passed only to the main script and
are shell-quoted individually.
Package binaries#
flog x <binary> [arguments] executes a dependency-provided binary directly,
without a shell. It searches node_modules/.bin from the selected package up
through the workspace root, nearest first:
flog x primate build --target=node
flog x --cwd apps/website primate build --target=node
Only project-local binaries are eligible. Flog does not download a missing
package and does not fall back to an executable on the global PATH; run
flog install first. Arguments and child exit status are preserved. Use --
before a binary name beginning with a hyphen.
When no installed shim exists, a built local package is also eligible when the
current package directly declares it with a workspace: dependency. Flog reads
that package's bin field and executes the target without creating links or
installing anything. Ambiguous providers, missing build output, and targets
outside the package are errors.
Workspace selection#
Workspace execution is deliberately sequential for now. Selected packages are ordered deterministically with dependencies before dependents, output is streamed directly, and the first failure stops further scheduling.
# Every workspace package
flog run --recursive --if-present test
# Exact package name
flog run --workspace @primate/core build
# A source file selects its owning declared workspace
flog run --workspace ./packages/core/src/index.ts test
# Workspace path glob
flog run --workspace './packages/**' --if-present build
# Brace-expanded path alternatives
flog run --workspace './{packages,apps}/**' --if-present build
# A low-level script selection including local dependencies
flog run --workspace @app/frontend --include-dependencies --if-present build
# A package and all transitive dependents
flog run --workspace @primate/core --include-dependents test
--dependencies-only and --dependents-only exclude the packages matched by
the original workspace selectors. Exact selectors accept a package name,
workspace directory, or file owned by a workspace. Path selectors resolve from
the effective --cwd (or process directory). All repeated selectors must be
exact or all must be globs; mixing the two is an error. Every selector must
match, and selecting one workspace twice through repeated names, aliases,
files, or overlapping globs is an error rather than a silent deduplication.
Missing exact paths, unmatched or malformed globs, files outside declared
workspaces, and paths whose symlinks escape the workspace root are errors before
scripts run. --recursive remains the separate request for every workspace.
For normal source builds, prefer the dedicated graph-aware
flog build command. It includes the selected app, permits and
reports build-less intermediates without --if-present, supports the shared
production-workspace policy, and avoids package-local nested orchestration.
During command migration, script selection and flog x working-directory
resolution also accept the top-level packages block from an existing
pnpm-workspace.yaml when the root package.json has no workspaces field.
This fallback does not change flog's installation model: add the same patterns
to package.json before expecting workspace dependency installation and links.
In default fail-fast mode, missing requested scripts are errors and are
validated across the entire selection before any package runs. --if-present
is the explicit opt-in to skip selected packages that do not define the script.
Retaining all workspace results#
Default execution remains fail-fast. Validation and release evidence can opt into complete sequential traversal:
flog run --recursive --if-present --no-bail test
flog run --recursive --if-present --no-bail --json test > test-results.json
With --no-bail, a failed or signaled package does not prevent later
independent packages from running. A selected dependent is blocked when one
of its selected workspace dependencies failed, was signaled, was missing its
required script, was itself blocked, or was interrupted. A dependency-healthy
package skipped by --if-present does not block its dependents. If that package
has an unsuccessful selected dependency, it is blocked rather than skipped,
so the failure continues through the selected dependency chain. Scripts remain
sequential and in the same deterministic dependency-first order as default
execution.
Human output streams each script normally and ends with one ordered result per
package plus stable totals. The aggregate command exits 1 after a completed
traversal containing a failure, signal, required missing script, or blocked
package. Interruption stops the active process group, marks the active and all
unstarted selections interrupted, emits the report, and retains the normal
interrupted exit status.
--json requires --no-bail. It reserves stdout for one report and captures
each executed package's stdout and stderr in its result. The report has
schemaVersion: 2, the requested script, ordered results, summary totals,
and optional coordinatedWork evidence. Every result includes package name,
workspace path, script, and one of
success, reused, failure, signaled, missing, skipped, blocked, or
interrupted. Applicable results also include the failed lifecycle event, an
exitCode or terminating signal, a stable reason, blockedBy package
names, an optional diagnostic error, and captured stdout / stderr.
Parallel workspace execution#
All-results runs can schedule independent workspaces concurrently:
flog run -r --if-present --no-bail --jobs auto test
flog run --workspace='./apps/**' --if-present --no-bail --jobs 4 build
flog run -r --if-present --no-bail --jobs 1 test # explicit serial debugging
--jobs accepts a positive maximum or auto, which uses the available Go
process concurrency. It requires --no-bail; omitting --jobs keeps the
existing sequential behavior. Ready workspaces are started in deterministic
selection order. A selected dependent is not ready until every selected
dependency succeeded or was safely skipped. Failures promptly block forbidden
dependent work while other independent work continues. Result and JSON order
always follows deterministic dependency-first selection, never completion
timing.
Parallel package output is captured separately to prevent byte-level
interleaving. On redirected stderr or an unsuitable terminal, completed package
output is appended as labeled blocks. --stream explicitly requests those
blocks on an interactive terminal as well. JSON mode remains silent until its
single final stdout report and retains each package's stdout and stderr.
On a capable interactive stderr terminal, parallel human mode instead repaints
one bounded status table showing queued/running/final state, current lifecycle
event, and elapsed time. Successful output stays collapsed; complete output for
failed, signaled, blocked, or interrupted packages is printed after the table.
TERM=dumb disables repainting. The display emits no color, so redirected logs
and NO_COLOR environments do not receive color escapes.
SIGINT or SIGTERM cancels every active package process group, waits for cleanup, marks queued work interrupted, and then emits the deterministic final report.
Nested dependency builds#
Every top-level flog run starts a private authenticated loopback coordinator
and passes its endpoint only to child scripts. A nested Flog process joins that
single command session. Equivalent work that already succeeded is reported as
reused instead of executing again; all coordinator state disappears when the
top-level command exits, so a later command always performs fresh work.
Work identity includes the canonical workspace root and importer path, script,
pre/main/post command text, forwarded arguments, top-level invocation context,
caller PATH, npm configuration, and the remaining caller environment. Flog
also includes the package's effective selected workspace dependency subgraph,
so production and full builds reuse an unchanged dependency but never reuse an
app build whose newly selected development workspace changes its inputs. Flog
records the exact complete PATH value supplied to each lifecycle and its
normalized caller value. A descendant replaces only that exact generated value;
caller prepends, appends, replacements, and other changes remain significant.
Generated package/lifecycle, working-directory, and coordinator variables are
excluded. Changing a compiler path, npm configuration, argument, command,
invocation, or ordinary environment value therefore prevents reuse.
Only successful compatible work is reusable. A failure, signal, missing script,
blocked result, or interruption is retained as unsuccessful and causes an
equivalent waiter to fail rather than retrying or treating it as satisfied.
Wait relationships are tracked centrally; circular nested invocations fail with
an actionable nested run cycle diagnostic instead of deadlocking. Cancellation
closes active process groups and the top-level coordinator, so no persistent
cache, daemon, socket file, or cross-terminal state remains.
Nested coordination remains available for general scripts and legacy wrappers such as:
flog run --cwd ../.. --workspace @app/name --dependencies-only --if-present build
Migrate dependency-only prebuild orchestration to top-level flog build so
the app and its dependency closure are selected together. During one
multi-app top-level run, shared equivalent dependencies execute once and later
nested requests report reused. Coordination spans root scripts that invoke
another flog run, so ordinary commands such as flog run build:apps retain one
session through the workspace traversal and nested child scripts. Joined all-results
runs read an authenticated evidence snapshot from the owner; human and JSON
summaries therefore retain shared-work reuse counts even through wrappers. Human
output distinguishes selected-package reused results from the aggregate count
of nested requests satisfied by coordinated work.
Script environment#
Flog runs scripts from their package directory using /bin/sh on Linux and
macOS. It prepends node_modules/.bin directories from the package through the
workspace root to PATH, nearest first.
The environment includes:
INIT_CWDPWDFLOG_WORKSPACE_ROOTnpm_commandnpm_config_local_prefixnpm_config_user_agentnpm_execpathnpm_lifecycle_eventnpm_lifecycle_scriptnpm_package_jsonnpm_package_namenpm_package_version
flog x receives the common workspace, invocation, path, package-manager, and
working-directory values above, but not lifecycle- or package-specific values.
SIGINT and SIGTERM cancellation reaches the active process group. Flog waits briefly for graceful termination and then kills a process that does not exit. Child exit statuses are preserved by the flog CLI.