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

  1. Define checks in checks.yaml — one file at your project root declares all cmd and prompt checks, optional file-scope filters, and conditions.
  2. Run holdpoint init — Holdpoint installs the unified default checks and generates engine files that hook into the agent's completion mechanism.
  3. 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:

AgentMechanismGenerated files
GitHub Copilot CLISDK 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 CodePer-hook context injection (SessionStart, UserPromptSubmit), a PreToolUse before_tool gate, Live lifecycle hooks, and TaskCompleted / Stop exit-2 gates.claude/settings.json
OpenAI CodexPer-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)
CursorNative 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 project hooks run in trusted workspaces. Local Cursor sessions use .cursor/hooks.json for runtime enforcement and Live telemetry, while .cursor/rules/holdpoint.md keeps the workflow visible as native Cursor rules context.
Codex note: Project-level hooks require trust approval before they run. Use /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 Copilot CLI local use: Holdpoint's Copilot engine lives in .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:

  1. Detects your package manager, writes checks.yaml from the unified default template, and adjusts commands for npm/pnpm/yarn.
  2. Generates engine files for all four agents (Copilot, Claude Code, Cursor, Codex).
  3. Creates repo-local guidance docs such as HOLDPOINT_PREREQUISITES.md and MASTER_PROMPT.md / HOLDPOINT_REFERENCE.md without overwriting an existing file. The default config injects MASTER_PROMPT.md into agents that support session context.
  4. Installs holdpoint as a devDependency so hooks resolve via node_modules/.bin/holdpoint without 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:

checks.yaml
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.

checks.yaml
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"
OperatorRequired fieldsDescription
file_existspathTrue if the file exists on disk
file_containspath, containsTrue if the file exists and contains the substring
env_var_setvariableTrue if the environment variable is non-empty
shell_returns_0commandTrue 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.

FieldTypeRequiredDescription
idstringyesUnique identifier for the check
labelstringyesHuman-readable display name
cmdstringcmd checksShell command — exits non-zero to block task completion
promptstringprompt checksInstruction the agent must act on before finishing
injectobjectinject checksSeed context: text, files (repo-relative), and/or datetime
whenstringnoFile filter: named scope or regex — see below
conditionIdstringnoGate this check behind a condition
onstringnoLifecycle hook: session_start, message_submit, before_tool, after_tool, session_end, or before_done (default)
checks.yaml
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:FiresEngine support
session_startA fresh session or resume — ideal for seeding contextClaude, Codex, Cursor, Copilot
message_submitEvery user prompt — datetime, reminders, light contextClaude, Codex, Copilot
before_toolBefore each tool call — a failing cmd blocks the toolClaude, Codex, Cursor, Copilot
before_doneThe completion gate — blocks finishing on failureAll 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.yaml
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"
Engines honor what their host supports and skip the rest. Cursor can't inject context per message (its 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.

ScopeFires 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/**
structuralpackage.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"
Named scopes use glob matching (minimatch). Plain strings that are not a named scope are treated as JavaScript regexes. An invalid regex will throw at runtime.

For project-specific paths, define named aliases in a top-level patterns: map so checks stay readable:

checks.yaml
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"
Pattern values are JavaScript regex strings matched against changed file paths. Built-in scope names (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:

Experimental mode required for local Copilot use: the extension depends on the Copilot CLI 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_start and message_submit checks (text, files, datetime) plus session_context_files, as additionalContext, with path-containment and truncation safeguards. Per-message injection is something CLI hooks can't do — only the extension can.
  • onPreToolUse — runs the before_done gate by intercepting the task_complete tool, and a before_tool gate for any tool when a before_tool cmd check exists. On failure it returns { permissionDecision: "deny" } and Copilot loops back to fix the issues, emitting Live stop_pass / stop_block events.
  • 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_start and message_submit checks (plus session_context_files and the datetime) as Claude additionalContext.
  • PreToolUse — runs a before_tool gate when a before_tool cmd check exists (exit 2 blocks 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 2 on failure so Claude stays in the loop and fixes the issues before finishing.
.claude/settings.json
{
  "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_start and message_submit checks (plus session_context_files and the datetime), bounded and returned as hookSpecificOutput.additionalContext per the Codex spec.
  • PreToolUse — runs a before_tool gate (exit 2 denies 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 2 with 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:

package.json
{
  "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.yaml and 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:

CommandWhat it opens
make dev-liveThe real Holdpoint Live daemon + browser UI (what users see)
make dev-webLanding page / docs site only
make devWeb site + the legacy standalone builder (contributor convenience)
Contributor note: 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.
Conflict behavior today: Holdpoint Live surfaces same-file conflicts in the UI when two sessions in the same project target the same path. It does not hard-stop both agents automatically in this phase; the conflict is made visible immediately so you can intervene before the edits diverge further.

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.yaml on disk, behind a diff-confirm step. Export / Copyare still there if you'd rather paste it yourself.
  • The History tab shows recent holdpoint check reports for the project, including pass/fail/skip results, changed files, and the HEAD SHA.

CLI reference

CommandDescription
holdpointPrint 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 startStart or connect to the singleton Holdpoint Live daemon
holdpoint daemon statusShow daemon pid, port, uptime, and session count
holdpoint daemon stopStop 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 eventInternal: ingest live event JSON from stdin
holdpoint validateValidate checks.yaml against the schema and print errors
holdpoint updateRegenerate engine files from the current checks.yaml
holdpoint builderOpen 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.

GateExamplesEffect
Project marker filespackage.json, pyproject.toml, go.mod, Cargo.tomlLanguage-specific checks are skipped unless the matching manifest exists
Tooling markersplaywright.config.ts, openapi.yaml, DockerfileOptional checks appear only for projects with that tool or artifact
File scopesfrontend, backend, python, go, rust, database, infra, structuralChanged 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 ↗