Introduction
Holdpoint is a universal eval-guard for AI coding agents. It enforces a set of checkpoints — shell commands and agent instructions — that must pass before any agent can mark a task as done. You define the rules once in a single checks.yaml file. Holdpoint generates the correct engine for whichever agent you use.
Holdpoint is not tied to any specific agent. It works with any AI coding tool that exposes a hook surface, completion event, or instruction-injection mechanism — including GitHub Copilot CLI, Claude Code, Cursor, OpenAI Codex, and others.
There are three kinds of checks:
- cmd checks — a shell command (e.g.
pnpm test) that Holdpoint runs automatically. If it exits non-zero, the agent is blocked at that hook (e.g. from completing the task). - prompt checks — an instruction that Holdpoint surfaces to the agent (e.g. "Update the OpenAPI spec"). The agent reads it and must act before marking the task done.
- inject checks— context (text, files, and/or the current date & time) Holdpoint seeds into the agent at a chosen lifecycle hook, such as the start of every session or every message.
Each check also declares when it runs via on: — a lifecycle hook like session_start, message_submit, before_tool, or before_done (the default completion gate).
How it works
- Define checks in
checks.yaml— one file at your project root declares all cmd and prompt checks, optional file-scope filters, and conditions. - Run
holdpoint init— Holdpoint installs the unified default checks and generates engine files that hook into the agent's completion mechanism. - The engine enforces checks at task completion — when the agent tries to finish a task, the engine runs all relevant checks. Failures block completion and surface the issue.
The engine mechanism varies by agent:
| Agent | Mechanism | Generated files |
|---|---|---|
| GitHub Copilot CLI | SDK extension — per-hook context injection (session_start, message_submit), before_tool + task_complete gates, Live events, and Copilot-only Live controls | .github/extensions/holdpoint/extension.mjs .github/holdpoint/generated/checks.immutable.json |
| Claude Code | Per-hook context injection (SessionStart, UserPromptSubmit), a PreToolUse before_tool gate, Live lifecycle hooks, and TaskCompleted / Stop exit-2 gates | .claude/settings.json |
| OpenAI Codex | Per-hook context injection (SessionStart, UserPromptSubmit), a PreToolUse before_tool gate, lifecycle/tool Live telemetry, and Stop/subagent exit-2 gates | .codex/hooks.json .codex/holdpoint-check.mjs .codex/config.toml AGENTS.md (breadcrumb) |
| Cursor | Native project hooks — sessionStart context, a preToolUse before_tool gate, stop/subagent follow-ups, Live telemetry, and a rules breadcrumb (beforeSubmitPrompt can't inject, so per-message context folds into sessionStart) | .cursor/hooks.json .cursor/holdpoint-hook.mjs .cursor/rules/holdpoint.md |
.cursor/hooks.json for runtime enforcement and Live telemetry, while .cursor/rules/holdpoint.md keeps the workflow visible as native Cursor rules context./hooks in the Codex TUI to review and trust the Holdpoint hook, or run codex trust in your project root.Installation
Run in your project root (git repo required):
macOS / Linux
curl -fsSL https://holdpoint.dev/install.sh | sh
Windows (PowerShell)
powershell -NoProfile -ExecutionPolicy Bypass -Command "irm https://holdpoint.dev/install.ps1 | iex"
Or run the CLI directly (cross-platform, including single-agent installs):
npx holdpoint init
.github/extensions/holdpoint/extension.mjs and relies on the Copilot CLI EXTENSIONS feature. Today that feature requires experimental mode, so run /experimental on in Copilot CLI before using Holdpoint locally. holdpoint init also creates HOLDPOINT_PREREQUISITES.md with this handoff note and the other agent setup caveats.holdpoint init does four things:
- Detects your package manager, writes
checks.yamlfrom the unified default template, and adjusts commands for npm/pnpm/yarn. - Generates engine files for all four agents (Copilot, Claude Code, Cursor, Codex).
- Creates repo-local guidance docs such as
HOLDPOINT_PREREQUISITES.mdandMASTER_PROMPT.md/HOLDPOINT_REFERENCE.mdwithout overwriting an existing file. The default config injectsMASTER_PROMPT.mdinto agents that support session context. - Installs
holdpointas adevDependencyso hooks resolve vianode_modules/.bin/holdpointwithout downloading on every fire.
You can also pass a flag to restrict the installed agent:
# Single-agent install npx holdpoint init --agent=copilot # Available agents: copilot, claude, cursor, codex
Requirements: Node.js 18+, an active git repository, and one of the supported agents installed. The PowerShell installer also appends .holdpoint/ to .gitignore so runtime check reports stay out of source control.
checks.yaml reference
The checks.yaml file lives at your project root and is the single source of truth for all Holdpoint checks. A minimal example:
version: 1
context:
guides: {}
conditions: []
checks:
- id: typecheck
label: "TypeScript type check"
cmd: "pnpm typecheck"
- id: docs-updated
label: "Update documentation"
when: docs
prompt: "Ensure all public APIs changed in this task are documented."version
Always 1. Required.
session_context_files
Optional list of file paths (relative to repo root) injected as agent context at session start. Useful for injecting project-specific guides or conventions. This is the simple top-level form; for per-hook control (e.g. seed different files on message_submit, add literal text, or include the datetime) use an inject check instead.
session_context_files: - MASTER_PROMPT.md
engines
Optional per-engine overrides. Each engine defaults to node_modules/.bin/holdpoint check --staged. Set overrides here when you need a different command — for example, if the project is the holdpoint repo and should use the local build instead of the installed package. Claude also supports a separate live_command override for the best-effort Live event emitter.
engines:
claude:
stop_command: "node_modules/.bin/holdpoint check --staged"
live_command: "node_modules/.bin/holdpoint event --engine claude --from-hook"
codex:
stop_command: "node_modules/.bin/holdpoint check --staged"
copilot:
check_command: "node_modules/.bin/holdpoint check --staged"The override survives holdpoint update re-runs because it lives in checks.yaml, not in the generated files.
context
Named guide text injected into every task. Use context.guides to add key/value pairs:
context:
guides:
architecture: >
This project uses a hexagonal architecture.
Always keep domain logic independent of infrastructure.conditions
Conditions gate checks — a check with a conditionId only runs when its condition evaluates to true.
conditions:
- id: has-openapi
operator: file_exists
path: openapi.yaml
- id: has-env-token
operator: env_var_set
variable: API_TOKEN
- id: schema-has-users
operator: file_contains
path: prisma/schema.prisma
contains: "model User"
- id: server-healthy
operator: shell_returns_0
command: "curl -sf http://localhost:3000/health"| Operator | Required fields | Description |
|---|---|---|
| file_exists | path | True if the file exists on disk |
| file_contains | path, contains | True if the file exists and contains the substring |
| env_var_set | variable | True if the environment variable is non-empty |
| shell_returns_0 | command | True if the shell command exits with code 0 |
checks
An array of check definitions. Each check sets exactly one behavior — a cmd check (cmd), a prompt check (prompt), or an inject check (inject) — plus an optional on: lifecycle hook.
| Field | Type | Required | Description |
|---|---|---|---|
| id | string | yes | Unique identifier for the check |
| label | string | yes | Human-readable display name |
| cmd | string | cmd checks | Shell command — exits non-zero to block task completion |
| prompt | string | prompt checks | Instruction the agent must act on before finishing |
| inject | object | inject checks | Seed context: text, files (repo-relative), and/or datetime |
| when | string | no | File filter: named scope or regex — see below |
| conditionId | string | no | Gate this check behind a condition |
| on | string | no | Lifecycle hook: session_start, message_submit, before_tool, after_tool, session_end, or before_done (default) |
checks:
# cmd check — runs a shell command
- id: lint
label: "ESLint"
cmd: "pnpm lint"
# cmd check with file filter — only runs when frontend files change
- id: typecheck-frontend
label: "TypeScript — frontend"
when: frontend
cmd: "pnpm tsc --noEmit"
# prompt check — instruction surfaced to the agent
- id: changelog
label: "Add CHANGELOG entry"
prompt: "Add an entry to CHANGELOG.md describing what changed in this task."
# prompt check with condition gate
- id: openapi-sync
label: "OpenAPI spec up to date"
when: backend
conditionId: has-openapi
prompt: "Update openapi.yaml to reflect any API route changes."Lifecycle hooks (on:) & inject checks
The on: field controls when in the agent loop a check fires. Omit it for before_done(the completion gate — today's default behavior). Other hooks let you run earlier — or seed context instead of gating.
| on: | Fires | Engine support |
|---|---|---|
| session_start | A fresh session or resume — ideal for seeding context | Claude, Codex, Cursor, Copilot |
| message_submit | Every user prompt — datetime, reminders, light context | Claude, Codex, Copilot |
| before_tool | Before each tool call — a failing cmd blocks the tool | Claude, Codex, Cursor, Copilot |
| before_done | The completion gate — blocks finishing on failure | All engines |
An inject check seeds context at its hook instead of running a command or surfacing an instruction. It accepts any of text (literal), files (repo-relative paths whose contents are injected), and datetime:
checks:
# Seed conventions at the start of every session
- id: seed-conventions
label: "Seed project conventions"
on: session_start
inject:
files: [AGENT_CONTEXT.md]
text: "Follow the conventions above."
# Keep the model's clock accurate on every message
- id: datetime
label: "Inject current date/time"
on: message_submit
inject:
datetime: true
# Block risky tool calls until a guard passes
- id: guard
label: "Pre-tool guard"
on: before_tool
cmd: "./scripts/guard.sh"beforeSubmitPrompt hook only gates submission), so its message_submit context folds into session_start. after_tool and session_end are accepted by the schema but advisory-only today.File filters (when:)
The when field narrows which checks activate based on which files changed. If omitted, the check always runs. Holdpoint ships with 16 named scopes covering the most common patterns across GitHub repos.
When no git-staged files are detected (e.g. running holdpoint check without staged changes), all checks run regardless of their when filter.
| Scope | Fires when changed files match |
|---|---|
| (absent) | Every task — no file filter applied |
| frontend | **/*.tsx, **/*.jsx, **/*.css, **/*.scss, **/tailwind.config.*, apps/** |
| backend | **/api/**, **/server/**, **/routes/**, **/controllers/**, packages/*/src/** |
| socket | **/socket/**, **/ws/**, **/websocket/** |
| visual | **/*.stories.{ts,tsx}, **/__screenshots__/**, **/*.snap |
| python | **/*.py, **/*.pyi, **/requirements*.txt, **/pyproject.toml |
| go | **/*.go, **/go.mod, **/go.sum |
| rust | **/*.rs, **/Cargo.toml, **/Cargo.lock |
| java | **/*.java, **/*.kt, **/*.gradle, **/pom.xml |
| ruby | **/*.rb, **/Gemfile, **/Rakefile |
| database | **/*.sql, **/migrations/**, **/db/**, **/prisma/**, **/*.prisma |
| prisma | **/prisma/**, **/*.prisma — focused subset for Prisma-specific checks |
| testing | **/*.test.*, **/*.spec.*, **/__tests__/**, **/tests/**, **/spec/** |
| infra | **/Dockerfile*, **/docker-compose.*, **/*.tf, **/k8s/** |
| ci | **/.github/workflows/**, **/.circleci/**, **/Jenkinsfile, **/.gitlab-ci.yml |
| docs | **/*.mdx, **/*.rst, **/docs/**, **/documentation/** |
| structural | package.json, tsconfig*, go.mod, Cargo.toml, Dockerfile*, docker-compose*, *.tf, openapi.*, .github/workflows/*.yml, vitest/jest/playwright configs, linter configs, and more — any file whose change signals the project's dependency graph or toolchain has shifted |
You can also use any JavaScript regex as the whenvalue. It is tested against each changed file's repo-relative path:
checks:
- id: e2e
label: "E2E tests for builder"
when: "^apps/builder/src/"
cmd: "pnpm test:e2e"For project-specific paths, define named aliases in a top-level patterns: map so checks stay readable:
patterns:
api-routes: "^src/api/"
openapi-spec: "openapi\\.(yaml|yml|json)$"
checks:
- id: openapi-lint
label: "Lint OpenAPI spec"
when: openapi-spec
cmd: "npx redocly lint openapi.yaml"frontend, structural, etc.) cannot be overridden in patterns.Supported agents
Holdpoint generates agent-specific engine files from your checks.yaml. Run holdpoint update after any change to regenerate them.
GitHub Copilot CLI
Holdpoint generates a local SDK extension at .github/extensions/holdpoint/extension.mjs. The extension runs as a persistent Node.js process alongside the CLI and handles three responsibilities:
EXTENSIONS feature. In the CLI, run /experimental on so EXTENSIONS shows up in the enabled feature flags before using Holdpoint locally. This note is also written to HOLDPOINT_PREREQUISITES.md during holdpoint init and holdpoint update.- onSessionStart / onUserPromptSubmitted — inject context for
session_startandmessage_submitchecks (text, files, datetime) plussession_context_files, asadditionalContext, with path-containment and truncation safeguards. Per-message injection is something CLI hooks can't do — only the extension can. - onPreToolUse — runs the
before_donegate by intercepting thetask_completetool, and abefore_toolgate for any tool when abefore_toolcmd check exists. On failure it returns{ permissionDecision: "deny" }and Copilot loops back to fix the issues, emitting Livestop_pass/stop_blockevents. - Holdpoint Live bridge — the extension keeps a session-scoped live connection to the daemon, streams tool + permission lifecycle events, and accepts Copilot-only control commands for approve/deny, queued context injection, and registered Holdpoint control tools.
The control surface is intentionally narrow: approvals are approve-once, injected context lands on the next eligible hook boundary, and triggerable tools are restricted to Holdpoint-owned extension tools such as holdpoint_dry_run.
Generated files:
- .github/extensions/holdpoint/extension.mjs — SDK extension (onSessionStart + gate)
- .github/holdpoint/generated/checks.immutable.json — pre-parsed config (JSON, no YAML parser needed in .mjs)
Claude Code
Holdpoint registers managed hooks in .claude/settings.json and preserves any user-defined hooks already present:
- SessionStart / UserPromptSubmit — inject context for
session_startandmessage_submitchecks (plussession_context_filesand the datetime) as ClaudeadditionalContext. - PreToolUse — runs a
before_toolgate when abefore_toolcmd check exists (exit2blocks the tool); otherwise emits best-effort Live events. Tool, permission, notification, subagent, compaction, and session hooks stay non-blocking so Live observability never becomes a hard gate. - TaskCompleted / Stop — run Holdpoint checks and exit
2on failure so Claude stays in the loop and fixes the issues before finishing.
{
"hooks": {
"SessionStart": [
{
"matcher": "startup|resume|clear|compact",
"hooks": [
{ "type": "command", "command": "node_modules/.bin/holdpoint event --engine claude --from-hook || true # HOLDPOINT_MANAGED=claude HOLDPOINT_HOOK=live", "async": true },
{ "type": "command", "command": "node -e '...inject session_context_files...' # HOLDPOINT_MANAGED=claude HOLDPOINT_HOOK=context" }
]
}
],
"PreToolUse": [
{
"hooks": [
{ "type": "command", "command": "node_modules/.bin/holdpoint event --engine claude --from-hook || true # HOLDPOINT_MANAGED=claude HOLDPOINT_HOOK=live", "async": true }
]
}
],
"TaskCompleted": [
{
"hooks": [
{ "type": "command", "command": "node_modules/.bin/holdpoint event --engine claude --from-hook || true # HOLDPOINT_MANAGED=claude HOLDPOINT_HOOK=live", "async": true },
{ "type": "command", "command": "node -e '...run holdpoint check; exit 2 on failure...' # HOLDPOINT_MANAGED=claude HOLDPOINT_HOOK=check" }
]
}
],
"Stop": [
{
"hooks": [
{ "type": "command", "command": "node_modules/.bin/holdpoint event --engine claude --from-hook || true # HOLDPOINT_MANAGED=claude HOLDPOINT_HOOK=live", "async": true },
{ "type": "command", "command": "node -e '...run holdpoint check; exit 2 on failure...' # HOLDPOINT_MANAGED=claude HOLDPOINT_HOOK=check" }
]
}
]
}
}OpenAI Codex
Holdpoint writes hooks to .codex/hooks.json and a single dispatcher script at .codex/holdpoint-check.mjs that handles the command-hook surface Codex exposes today:
- SessionStart / UserPromptSubmit — inject context for
session_startandmessage_submitchecks (plussession_context_filesand the datetime), bounded and returned ashookSpecificOutput.additionalContextper the Codex spec. - PreToolUse — runs a
before_toolgate (exit2denies the tool) when such a check exists; otherwise, like PostToolUse / PermissionRequest / PreCompact / PostCompact, streams best-effort Live telemetry without approving permissions or rewriting tool inputs. - Stop / SubagentStop — run Holdpoint checks after each turn. Failures exit
2with bounded stderr output, so Codex creates a continuation prompt and keeps working until checks pass.
Holdpoint also writes .codex/config.toml with [features] hooks = true to ensure hooks are active at the repo level, and splices a marker-bounded workflow breadcrumb into AGENTS.md as an instruction layer. Existing AGENTS.md content is preserved; the generated Holdpoint block is replaced in place on update.
Trust required: Codex only runs project-level hooks after trust approval. Use /hooks in the Codex TUI to review and approve, or run codex trust in your project root.
Cursor
Holdpoint writes native Cursor project hooks to .cursor/hooks.json and a generated .cursor/holdpoint-hook.mjs script. Local Cursor sessions get sessionStart context injection (for session_start checks and files), a preToolUse before_tool gate that returns { permission: "deny" } on failure, lifecycle telemetry, and completion checks on stop / subagentStop via follow-up messages. Cursor's beforeSubmitPrompt hook can only gate submission, not inject context, so per-message context folds into sessionStart. Holdpoint also splices a marker-bounded workflow breadcrumb into .cursor/rules/holdpoint.md.
External Live engines
Holdpoint also supports third-party Live engines without a Holdpoint repo PR. The current alpha contract is intentionally narrow: a package can register discovery metadata, provide a bridge command, and translate its native hook payloads into Holdpoint events for holdpoint event --engine <id> --from-hook.
Discovery looks for installed packages named holdpoint-engine-* or @scope/holdpoint-engine-* that declare the holdpoint-engine keyword plus this metadata. Engine authors should usually depend on @holdpoint/sdk for the LiveAdapter contract and on @holdpoint/live-protocol for the shared event schema:
{
"name": "holdpoint-engine-example",
"type": "module",
"keywords": ["holdpoint-engine"],
"holdpoint": {
"manifest": "./dist/manifest.js",
"adapter": "./dist/index.js"
}
}The manifest module exports manifest with manifestVersion, id, and displayName. For historical compatibility, the engine module exports adapter with generateBridgeCommand() and translateHookInput(). The generated bridge command normally shells out to holdpoint event, which is the CLI entrypoint that validates and ingests translated Live events. See examples/holdpoint-engine-template in the repo for a minimal skeleton.
Holdpoint Live UI
Holdpoint Live is the local browser surface for agent observability. It is project-first: the sidebar can list many repos, but the main panel always focuses one project at a time, with session cards, event filters, check runs, and conflict banners scoped to that project.
Within a project, the UI is explicitly multi-session: you can watch several agent sessions at once, compare their timelines, and spot overlapping work before it becomes a merge mess. It is one unified surface with tabs:
- Activity — a filterable, tone-coded timeline of every event in the project.
- Sessions — per-session cards with live controls (approve/deny, inject context) where the engine supports them.
- Conflicts — files two agents are touching at once, grouped by path.
- Health — gate-effectiveness metrics (pass rates, top failing checks).
- Checks & History — edit this project's
checks.yamland review past check runs (see below).
End users normally open it through the CLI:
holdpoint live
That command ensures the singleton daemon is running, bootstraps browser auth, and opens the daemon-served UI. For contributors working in this monorepo, the equivalent shortcut is:
make dev-live
The root Makefile now separates the local repo surfaces like this:
| Command | What it opens |
|---|---|
| make dev-live | The real Holdpoint Live daemon + browser UI (what users see) |
| make dev-web | Landing page / docs site only |
| make dev | Web site + the legacy standalone builder (contributor convenience) |
make dev-live opens the actual daemon-served Live UI — the shipped surface. The standalone apps/builderis the original editor, kept for reference but no longer bundled; its functionality moved into the Live UI's Checks tab.Editing checks (UI)
The Checks tab of the Live UI lets you create and edit checks.yaml without writing YAML by hand, scoped to the project selected in the sidebar. It is part of the same unified UI — there is no separate app. Jump straight to it with:
holdpoint builder # opens /live/?tab=checks
(The old /builder/ route redirects here.) The editor is a master-detail list grouped by lifecycle hook; selecting a check opens a panel that separates when it runs (hook), what it does (cmd / prompt / inject), and what triggers it (file filter + condition) as labeled dropdowns.
- Savewrites the validated YAML straight back to that repo's
checks.yamlon disk, behind a diff-confirm step. Export / Copyare still there if you'd rather paste it yourself. - The History tab shows recent
holdpoint checkreports for the project, including pass/fail/skip results, changed files, and the HEAD SHA.
CLI reference
| Command | Description |
|---|---|
| holdpoint | Print CLI help |
| holdpoint init [--agent] | Install Holdpoint with the unified default template |
| holdpoint check [--staged] | Run all deterministic checks; surface prompt checks |
| holdpoint live [--project] | Open Holdpoint Live, optionally focused to a project hash |
| holdpoint engines [--json] | List discovered Holdpoint Live engine packages and ignore reasons |
| holdpoint daemon start | Start or connect to the singleton Holdpoint Live daemon |
| holdpoint daemon status | Show daemon pid, port, uptime, and session count |
| holdpoint daemon stop | Stop the running Holdpoint Live daemon |
| holdpoint suggest [--apply] | Scan project and propose (or apply) new checks |
| holdpoint require-changeset [--staged] | Require .changeset/*.md for release-affecting package changes |
| holdpoint evolve [--apply] | Deprecated hidden alias for holdpoint suggest during alpha |
| holdpoint event | Internal: ingest live event JSON from stdin |
| holdpoint validate | Validate checks.yaml against the schema and print errors |
| holdpoint update | Regenerate engine files from the current checks.yaml |
| holdpoint builder | Open the visual builder via the daemon at /builder/ |
holdpoint / holdpoint live
holdpoint without arguments prints CLI help. Use holdpoint live to ensure the singleton daemon is running, bootstrap browser auth, and open the Holdpoint Live UI focused on the current project when possible. --project can force a specific project hash.
The UI is project-first: the sidebar can list many repos, but the main panel always shows exactly one project at a time. Within that project you still get a multi-session view: session cards, timelines, filters, and conflict banners are all scoped to the selected project only.
Active control buttons only appear for Copilot sessions whose extension bridge is currently connected. Claude, Codex, and Cursor stream hook telemetry but remain observe-only in this phase.
holdpoint engines
Lists the Holdpoint Live engine packages currently discoverable from the CLI. Built-in engines load first, then installed project packages named holdpoint-engine-* or @scope/holdpoint-engine-*.
Every row is either loaded or ignored with the reason attached, so engine authors can debug missing keywords, missing manifest metadata, bad exports, or id collisions quickly. Use --json for stable machine-readable output.
holdpoint check
Reads git-staged files to determine which checks to run (via when: filter matching). If no staged files are found, all checks run. Use --staged to always scope to staged files only.
cmd checks exit non-zero on failure and print the shell output. prompt checks are displayed as a list of instructions — they are not automatically enforced as commands.
Pass --hook <event> to run only the checks bound to a lifecycle hook (default before_done). Engines call this internally — e.g. holdpoint check --hook before_tool at a pre-tool gate.
holdpoint require-changeset
Discovers publishable package roots from workspace metadata or nearby package.json files, then blocks release-affecting package changes unless the diff includes a .changeset/*.md file. If a repo does not have .changeset/ yet, the failure output tells the agent to create it and run pnpm changeset.
The default template installs this as a normal Holdpoint cmd check, so package authors get the release-note gate automatically. Use --include to narrow enforcement to explicit package globs in unusual monorepos.
holdpoint update
Must be run after any change to checks.yaml. Regenerates all engine files. The holdpoint-sync check in the default configuration enforces this automatically when checks.yaml is staged.
holdpoint suggest
Scans the project filesystem, detects languages, frameworks, and tooling, then diffs the result against the current checks.yaml. In dry-run mode (default) it prints proposed new checks and any stale checks whose when: pattern matches zero files. Pass --apply to write all proposals to checks.yaml and regenerate engine files automatically.
The MASTER_PROMPT.md installed by holdpoint init instructs your AI agent to run holdpoint suggest --apply whenever the project structure changes — closing the zero-config evolution loop.
Default template
holdpoint init generates checks.yaml from one unified default template. Checks are pre-configured with when: file filters and optional conditionId: gates so they only fire when relevant.
| Gate | Examples | Effect |
|---|---|---|
| Project marker files | package.json, pyproject.toml, go.mod, Cargo.toml | Language-specific checks are skipped unless the matching manifest exists |
| Tooling markers | playwright.config.ts, openapi.yaml, Dockerfile | Optional checks appear only for projects with that tool or artifact |
| File scopes | frontend, backend, python, go, rust, database, infra, structural | Changed paths select the checks that apply to the current task |
Adding support for a new stack means adding a marker condition plus gated checks to the default template — not forking a separate template file.
Advanced
Conditions
Use conditionId to gate a check behind a runtime condition. A gated check is skipped entirely when its condition is false — it does not appear in the output at all. Useful for checks that only make sense in certain project setups (e.g. an OpenAPI check that only applies when an openapi.yaml exists).
conditions:
- id: has-openapi
operator: file_exists
path: openapi.yaml
checks:
- id: openapi-sync
label: "OpenAPI in sync"
conditionId: has-openapi # skipped if openapi.yaml does not exist
when: backend
prompt: "Update openapi.yaml for any changed API routes."Custom when: regex
Any string that is not a named scope is treated as a JavaScript regex and tested against each changed file path. Anchor with ^ to match from the repo root:
checks:
- id: builder-e2e
label: "Builder E2E tests"
when: "^apps/builder/src/" # only runs when builder source changes
cmd: "pnpm --filter @holdpoint/builder test:e2e"Multi-agent projects
holdpoint init installs for all four agents by default — Copilot, Claude Code, Cursor, and Codex. Pass --agent to restrict to one:
# Install for a single agent only npx holdpoint init --agent=claude
Run holdpoint update after any change to checks.yaml to regenerate all engine files. Holdpoint detects which agents are already installed and only regenerates their files.
session_context_files
Files listed under session_context_files are read at session start and injected as additional context into the agent via the sessionStart hook. Useful for injecting project conventions, architectural guides, or onboarding notes that the agent should know before starting any task. Holdpoint's default template includesMASTER_PROMPT.md so installed projects receive the default agent best practices automatically:
session_context_files: - MASTER_PROMPT.md # concise workflow rules injected at session start
Keeping generated files in sync
Holdpoint's own checks.yaml includes a holdpoint-sync check that runs npx holdpoint update whenever checks.yaml is staged. Add this to your project to enforce the same invariant:
checks:
- id: holdpoint-sync
label: "Regenerate engine files"
when: "^checks\.yaml$"
cmd: "node_modules/.bin/holdpoint update"Open source under the MIT license. GitHub ↗