Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions Architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,16 @@ steplock writes to two channels that don't touch the hook stdin/stdout protocol:

When two checklist directories both match the same event, steplock processes them in alphabetical order (sorted by directory name). The first incomplete checklist blocks. Once it reaches `[*]`, the next checklist's first state blocks on the subsequent invocation.

### Global checklists

steplock evaluates two steplock directories: the project `.steplock/` and the global directory (`$STEPLOCK_GLOBAL_DIR`, else `$XDG_CONFIG_HOME/steplock`, else `~/.config/steplock`). Both use the same layout.

- **Order** — all project checklists first, then global checklists. The first incomplete match blocks.
- **Override** — a global checklist is skipped when the project has a checklist directory with the same name. This lets a project replace a global gate, or turn it off with an empty directory.
- **State** — each checklist stores sessions and audit events in the directory it came from. Global session state lives in the global directory, so no files are written into projects that have no `.steplock/`.
- **Same directory** — when the global directory resolves to the project `.steplock/` (for example, `STEPLOCK_GLOBAL_DIR=~/.steplock` and the project is `~`), it is evaluated once.
- **`session:stop`** — cleans the session directory in both locations.

### Idempotent ack

If `ack.sh` runs when the session is already complete or `current_state` is null (e.g. agent ran it twice), it exits 0 with a message and makes no writes:
Expand Down
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased]

### Added
- Global checklists: `$STEPLOCK_GLOBAL_DIR`, `$XDG_CONFIG_HOME/steplock` or `~/.config/steplock` holds checklists that apply to every project; project checklists with the same name override them
- `steplock init --global` and `steplock clean --global`; `steplock validate` also checks global checklists
- `run_with_global` and `global_steplock_dir` library API
- `steplock init` command creates `.steplock/checklists/` and `.gitignore` skeleton
- `session:stop` event cleans up the session directory so the checklist resets
- `allow_preview_request` config option generates a `preview.sh` showing checklist progress
Expand Down
20 changes: 18 additions & 2 deletions Installation.md
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,8 @@ In your project's `.claude/settings.json`:
}
```

To gate every project, put the same block in `~/.claude/settings.json` and add your checklists to the global steplock directory (see [Global checklists](#global-checklists)).

### Cursor / Windsurf / Cline / Amp

Follow your tool's hook registration docs and point the hook command at `steplock`. polyhook normalises the event format — no per-tool changes needed.
Expand All @@ -174,11 +176,25 @@ cat .steplock/audit.log

---

## Global checklists

Global checklists apply to every project. Create the global steplock directory with a sample checklist:

```sh
steplock init --global
# steplock: initialized /Users/you/.config/steplock/checklists
```

The directory is `$STEPLOCK_GLOBAL_DIR`, else `$XDG_CONFIG_HOME/steplock`, else `~/.config/steplock`. Project checklists run first. A project checklist with the same name replaces the global one. Set `STEPLOCK_GLOBAL_DIR=""` to turn global checklists off.

---

## Other commands

```sh
steplock validate # check every config.toml / flow.mmd under .steplock/checklists/ for errors
steplock clean # remove all session state under .steplock/sessions/ (checklists restart fresh)
steplock validate # check every config.toml / flow.mmd in .steplock/checklists/ and the global checklists/
steplock clean # remove all session state under .steplock/sessions/ (checklists restart fresh)
steplock clean --global # remove all session state in the global steplock directory
```

---
Expand Down
28 changes: 28 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,34 @@ stateDiagram-v2

See [`examples/git-push-quality-gate/`](examples/git-push-quality-gate/) for a complete working example.

### Global checklists

A global checklist applies to every project, including projects with no `.steplock/`. Use it for gates you want everywhere, such as a check before every `git push`.

Global checklists live in the global steplock directory, which uses the same layout as `.steplock/`:

```
~/.config/steplock/
└── checklists/
└── git-push-quality-gate/
├── config.toml
└── flow.mmd
```

steplock finds the global directory in this order:

1. `$STEPLOCK_GLOBAL_DIR`. Set it to an empty string to turn global checklists off.
2. `$XDG_CONFIG_HOME/steplock`, when `XDG_CONFIG_HOME` is an absolute path.
3. `~/.config/steplock`, where `~` is your home directory (`%USERPROFILE%` on Windows).

Rules:

- Project checklists run first. Global checklists run after them, in alphabetical order.
- A project checklist with the same directory name replaces the global one. An empty `.steplock/checklists/<name>/` directory turns that global checklist off for the project.
- Session state and the audit log for global checklists are written to the global directory, not to the project.

Run `steplock init --global` to create the directory with a sample checklist. Register the hook once in your user-level tool settings (for Claude Code, `~/.claude/settings.json`) so it runs in every project.

---

## Editor support
Expand Down
66 changes: 66 additions & 0 deletions core/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -82,11 +82,14 @@ unwrap_used = "deny"
wildcard_imports = "deny"
enum_glob_use = "deny"
single_match_else = "deny"
manual_let_else = "deny"

[dependencies]
cel-interpreter = "0.10"
chrono = { version = "0.4", features = ["serde"] }
dirs = "6"
polyhook = "0.1.5"
same-file = "1"
serde = { version = "1", features = ["derive"] }
serde_json = "1"
thiserror = "2"
Expand Down
Loading
Loading