Capsula
[!WARNING] This project is in early development. The CLI interface and configuration format may change in future releases.
A powerful CLI tool for running hooks and capturing their output before and after your command executions. Capsula automatically records the state of your project environment before and after running commands, making your workflows reproducible and auditable.
[!NOTE] The Python version of Capsula is deprecated and can be found at the
pythonbranch of this repository.
Features
- 📸 Context Capture: Automatically capture git state, file contents, environment variables, and more
- 🔄 Reproducible Runs: Complete record of execution hook for debugging and auditing
- 🛡️ Safety Checks: Prevent execution on dirty repositories or other unsafe conditions
- 📊 Structured Output: JSON-formatted capture data for easy processing
- 🔧 Extensible: Multiple built-in hooks with clean error handling
Installation / Update
Rust 1.90.0 or later is required.
To install Capsula CLI or update to the latest version, use one of the following methods:
Install from crates.io (recommended)
Install from the GitHub repository
Quick Start
- Create a configuration file (
capsula.toml) in your project root:
[]
= "my-project"
[[]]
= "capture-git-repo"
= "repo-name"
= "."
[[]]
= "capture-cwd"
[[]]
= "capture-file"
= "config.json"
= "copy"
= "sha256"
- Run a command with hooks:
Configuration
Basic Structure
The capsula.toml configuration file defines:
- Vault: Where to store captured data
- Phases: Pre-run and post-run hooks
[]
= "project-name" # Vault identifier
= ".capsula" # Storage path (optional, defaults to .capsula/{name})
[] # Pre-execution hooks
[[]]
= "capture-git-repo"
# ... hook configuration
[] # Post-execution hooks
[[]]
= "capture-file"
# ... hook configuration
Available Hook Types
Git Hook
Captures git repository state including commit hash and cleanliness check.
[[]]
= "capture-git-repo"
= "repo-name" # Hook name
= "." # Repository path
= false # Allow uncommitted changes (default: false)
Output Example:
Current Working Directory
Captures the current working directory path.
[[]]
= "capture-cwd"
Output Example:
File Hook
Captures file contents and/or metadata.
[[]]
= "capture-file"
= "config.json" # File pattern to capture
= "copy" # Capture mode ("copy", "move", or "none". default: "copy")
= "sha256" # Calculate file hash ("sha256" or "none". default: "sha256")
Output Example:
Environment Variables Hook
Captures specified environment variables.
[[]]
= "capture-env"
= "HOME" # Variable name to capture
Output Example:
Command Hook
Captures output of shell commands.
[[]]
= "capture-command"
= ["uname", "-a"]
= false # Abort if command fails (default: false)
Output Example:
Machine Hook
Captures system information like CPU, memory, and OS details.
[[]]
= "capture-machine"
Output Example:
Slack Notification Hook
Sends notifications to a Slack channel when a run starts (pre-run phase) or completes (post-run phase).
# Send to a channel
[[]]
= "notify-slack"
= "#general" # Slack channel name (or channel ID like "C01234567")
= "xoxb-..." # Slack bot token (optional, can use SLACK_BOT_TOKEN env var)
# Send as a DM to a user
[[]]
= "notify-slack"
= "U01234ABCD" # Slack user ID for DMs (not @username)
# Token will be read from SLACK_BOT_TOKEN env var
[!TIP] It's recommended to set the Slack bot token via the
SLACK_BOT_TOKENenvironment variable instead of storing it in the configuration file. If thetokenfield is omitted from the configuration, Capsula will automatically read it from the environment variable.
Pre-run notification message:
Run `chubby-back` (ID: `01K8WSYC91YAE21R7CWHQ4KYN2`) is starting.
Post-run notification message:
Run `chubby-back` (ID: `01K8WSYC91YAE21R7CWHQ4KYN2`) has completed.
Output Example:
Setup Requirements:
To use the Slack notification hook, you need to:
- Create a Slack app at https://api.slack.com/apps
- Add the
chat:writebot token scope to your app - Install the app to your workspace
- Copy the bot token (starts with
xoxb-) - Set the
SLACK_BOT_TOKENenvironment variable or add it to your config - For channel notifications: Invite the bot to the channel you want to post to
- For DM notifications: Find the user ID by clicking the user's profile → "More" → "Copy member ID" (format:
U01234ABCD)
CLI Usage
Commands
capsula run <command>
Execute a command with full hook capture.
# Run with default config
# Run with custom config
# Run with arguments
Behavior:
- Runs pre-run hooks and saves their outputs to vault
- Checks for abort conditions (e.g., dirty git repo)
- Executes the command if safe, aborts otherwise
- Runs post-run hooks and saves their outputs to vault
Environment Variables:
When executing a command with capsula run, the following environment variables are automatically set and available to your command:
| Variable | Description | Example |
|---|---|---|
CAPSULA_RUN_ID |
Unique ULID identifier for this run | 01K8WSYC91YAE21R7CWHQ4KYN2 |
CAPSULA_RUN_NAME |
Human-readable generated name | chubby-back |
CAPSULA_RUN_DIRECTORY |
Absolute path to the run directory in the vault | /path/to/.capsula/vault-name/2025-10-31/093525-chubby-back |
CAPSULA_RUN_TIMESTAMP |
ISO 8601 timestamp of when the run started | 2025-10-31T09:35:25.473+00:00 |
CAPSULA_RUN_COMMAND |
Shell-quoted string of the executed command | python train.py --epochs 100 |
CAPSULA_PRE_RUN_OUTPUT_PATH |
Path to the pre-run output JSON file | /path/to/.capsula/vault-name/.../pre-run.json |
[!CAUTION] While
CAPSULA_RUN_DIRECTORYis available, it is not recommended to write files directly to this directory. Instead, output files to your project root and capture them using thecapture-filehook in the post-run phase. This approach ensures files are properly tracked and managed by Capsula's file handling system.
These variables can be used within your scripts to access run metadata:
# Example: Embed run name in matplotlib figures for traceability
=
# Create your plot
# Add run name to the figure - useful when copying plots to presentations
# Save to project root, then capture with post-run hook
# Configure a post-run hook to capture the output file
[[]]
= "capture-file"
= "results.png"
= "move" # Move the file to the vault
capsula list
List all captured runs in the vault.
# List runs with default config
# List runs with custom config
Example Output:
TIMESTAMP (UTC) NAME COMMAND
---------------------------------------------------------------------------------------------
2025-10-31 09:35:29 kind-year echo hello
2025-10-31 09:35:28 smelly-apparel echo hello
2025-10-31 09:35:26 clear-waste echo hello
2025-10-31 09:30:15 cheap-trip echo this is a very long command with many argu...
The output shows:
- Timestamp: UTC time when the command was executed
- Name: Human-readable generated name for the run
- Command: The command that was executed (truncated if too long)
Output Structure
Metadata
Every hook output includes metadata for traceability:
Vault Structure
Captured data is organized in the vault:
.capsula/
└── vault-name/
└── 2025-10-31/ # Date-based directory (YYYY-MM-DD, UTC)
└── 093525-chubby-back/ # Unique run directory (HHMMSS-run-name)
├── _capsula/ # Capsula metadata directory
│ ├── metadata.json # Run metadata (ID, name, command, timestamp)
│ ├── pre-run.json # Pre-phase hook outputs (array)
│ ├── command.json # Command execution results
│ └── post-run.json # Post-phase hook outputs (array)
└── [captured files] # Files copied by file hooks
metadata.json contains run information:
command.json contains command execution results:
License
This project is licensed under either of the MIT license or the Apache License 2.0 at your option.