Emulsify CLI can run project setup hooks and caches cloned system repositories locally.
During emulsify init, the CLI checks the cloned starter for:
.cli/init.js
If the file exists, it is executed with the same Node.js binary running the CLI.
Execution timing:
- Starter repository is cloned.
project.emulsify.jsonis written.- Project dependencies are installed.
.cli/init.jsruns if present.- The starter
.gitdirectory is removed.
The hook working directory is the hook file directory, so relative file operations are resolved from .cli/.
Example starter hook:
import { writeFile } from 'node:fs/promises';
import { resolve } from 'node:path';
await writeFile(resolve('..', '.env.example'), 'STORYBOOK_PORT=6006\n');During emulsify system install, the CLI checks the current project root for:
.cli/systemInstall.js
The project root is the directory containing project.emulsify.json. If the hook exists, the CLI executes it with Node.js after required components and general assets are installed.
Use this hook for setup that must happen after a system has populated project files. Keep it idempotent because system installs may be repeated in local development or test projects.
System repositories are cloned into the Emulsify cache directory:
~/.emulsify/cache
The cache path includes:
| Input | Why It Matters |
|---|---|
| Cache bucket | Systems currently use the systems bucket. |
| Project config path | Different Emulsify projects get separate cache locations. |
| Repository | Different repository URLs get separate cache locations. |
| Checkout | Different tags, branches, or commits get separate cache locations. |
| System name | The parsed repository name becomes the final cache segment. |
The project path, normalized repository URL, and checkout are hashed, so the full path is intentionally not human-friendly. This prevents same-named repositories at the same checkout from sharing an entry.
After a successful clone, the CLI writes .emulsify-cache.json inside the cache entry. The sidecar records the repository, requested checkout, resolved Git ref, clone time, and a completion marker. It is written only after cloning and ref resolution succeed.
Before reusing an entry, the CLI validates the sidecar, repository, checkout, local origin fetch URL, and local HEAD. Missing, malformed, incomplete, or mismatched entries are removed and cloned again. Routine component commands perform only these local checks, so an installed system remains usable offline.
system install checks remote freshness when reusing a cache entry. Component commands do so only when passed --refresh. The bounded lookup compares a named checkout such as main (or the default remote HEAD) with the recorded resolved ref and re-clones when it has advanced. If the remote check times out or is otherwise unavailable, a locally valid clone remains usable.
To inspect how much would be removed without changing files, run:
emulsify cache clear --dry-runTo remove every cache bucket and entry under ~/.emulsify/cache, run:
emulsify cache clearBoth commands report bucket and entry counts. Clearing an already empty cache succeeds without an error.
Component and asset copies come from the cache into the current project.
| Command | Copy Source | Destination |
|---|---|---|
system install |
Required or all system components, plus variant files and directories. | Project paths from the selected variant. |
component install |
One component and its dependencies, or all components. | Structure implementation directories in project.emulsify.json. |
component create |
Built-in templates or .cli/templates overrides. |
Structure implementation directory selected by --directory or prompt. |
Install commands use safe path resolution so component and asset destinations stay inside the Emulsify project root.