@holdpoint/cli 0.1.0-alpha.2 → 0.1.0-alpha.20

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,23 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/lib/prompt.ts
4
+ import readline from "readline/promises";
5
+ import { stdin, stdout } from "process";
6
+ async function promptYesNo(question, defaultYes = true) {
7
+ if (!stdout.isTTY || !stdin.isTTY) {
8
+ return defaultYes;
9
+ }
10
+ const rl = readline.createInterface({ input: stdin, output: stdout });
11
+ try {
12
+ const suffix = defaultYes ? " [Y/n] " : " [y/N] ";
13
+ const answer = (await rl.question(question + suffix)).trim().toLowerCase();
14
+ if (answer === "") return defaultYes;
15
+ return answer === "y" || answer === "yes";
16
+ } finally {
17
+ rl.close();
18
+ }
19
+ }
20
+ export {
21
+ promptYesNo
22
+ };
23
+ //# sourceMappingURL=prompt-EQ5IFADN.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/lib/prompt.ts"],"sourcesContent":["import readline from \"node:readline/promises\";\nimport { stdin, stdout } from \"node:process\";\n\n/**\n * Ask a yes/no question on the terminal and resolve to true/false.\n *\n * Defaults to true on empty input (`Y/n` style). Returns `defaultYes` and\n * skips the prompt entirely when stdout isn't a TTY (CI, piped invocation,\n * agent hook) so we never block on input nobody can provide. Callers that\n * need different non-TTY behaviour should check `stdout.isTTY` first.\n */\nexport async function promptYesNo(question: string, defaultYes = true): Promise<boolean> {\n if (!stdout.isTTY || !stdin.isTTY) {\n return defaultYes;\n }\n const rl = readline.createInterface({ input: stdin, output: stdout });\n try {\n const suffix = defaultYes ? \" [Y/n] \" : \" [y/N] \";\n const answer = (await rl.question(question + suffix)).trim().toLowerCase();\n if (answer === \"\") return defaultYes;\n return answer === \"y\" || answer === \"yes\";\n } finally {\n rl.close();\n }\n}\n"],"mappings":";;;AAAA,OAAO,cAAc;AACrB,SAAS,OAAO,cAAc;AAU9B,eAAsB,YAAY,UAAkB,aAAa,MAAwB;AACvF,MAAI,CAAC,OAAO,SAAS,CAAC,MAAM,OAAO;AACjC,WAAO;AAAA,EACT;AACA,QAAM,KAAK,SAAS,gBAAgB,EAAE,OAAO,OAAO,QAAQ,OAAO,CAAC;AACpE,MAAI;AACF,UAAM,SAAS,aAAa,YAAY;AACxC,UAAM,UAAU,MAAM,GAAG,SAAS,WAAW,MAAM,GAAG,KAAK,EAAE,YAAY;AACzE,QAAI,WAAW,GAAI,QAAO;AAC1B,WAAO,WAAW,OAAO,WAAW;AAAA,EACtC,UAAE;AACA,OAAG,MAAM;AAAA,EACX;AACF;","names":[]}
@@ -0,0 +1,10 @@
1
+ # Holdpoint prerequisites
2
+
3
+ Holdpoint installed repo-local adapters for one or more AI coding agents. Before relying on them locally, review these setup notes:
4
+
5
+ - **GitHub Copilot CLI** — Holdpoint's `.github/extensions/holdpoint/extension.mjs` uses the Copilot CLI **EXTENSIONS** feature. Today that feature is gated behind experimental mode. In Copilot CLI, run `/experimental on` so **EXTENSIONS** appears in the enabled feature set before using Holdpoint locally.
6
+ - **Cursor** — project-level hooks run in trusted workspaces. After opening the repo in Cursor, confirm the workspace is trusted and review Settings → Hooks if hooks do not fire.
7
+ - **OpenAI Codex** — project-level hooks require trust approval. Run `codex trust` in the Codex TUI or review the hook with `/hooks`.
8
+ - **General** — Holdpoint expects Node.js 18+ and a git repository so `holdpoint init`, `holdpoint update`, and `holdpoint check` can run normally.
9
+
10
+ Docs: https://holdpoint.dev/docs
@@ -0,0 +1,372 @@
1
+ # Holdpoint — Eval Checkpoints
2
+
3
+ This project uses [Holdpoint](https://github.com/holdpoint-dev/holdpoint) to enforce
4
+ eval checkpoints. Before marking any task done, all checks must pass.
5
+
6
+ ---
7
+
8
+ ## The Rule
9
+
10
+ Before marking **any** task complete:
11
+
12
+ 1. Run `holdpoint check` — all tasks must exit 0.
13
+ 2. `holdpoint check` also prints every **prompt** check whose `when` matches the
14
+ files you changed. Read and act on each listed instruction before finishing.
15
+
16
+ ---
17
+
18
+ ## The Suggest Loop
19
+
20
+ `checks.yaml` is not static — it grows alongside the project automatically.
21
+
22
+ **`holdpoint-suggest` is a deterministic check** in `checks.yaml` that fires whenever you change a structural file (`package.json`, `pyproject.toml`, `go.mod`, `Dockerfile`, `tsconfig.json`, `vitest.config.*`, etc.). When it fires, `holdpoint suggest` runs and **exits 1 if `checks.yaml` is out of sync** — blocking task completion until you apply the proposals.
23
+
24
+ When blocked by `holdpoint-suggest`, run:
25
+
26
+ ```
27
+ holdpoint suggest --apply # scan, apply proposals, regenerate engine files
28
+ ```
29
+
30
+ Then commit:
31
+
32
+ ```
33
+ git add checks.yaml .github/holdpoint/generated/
34
+ git commit -m "chore: suggest holdpoint checks"
35
+ ```
36
+
37
+ `holdpoint suggest --apply` is idempotent — safe to re-run at any time. It only adds checks for tools/patterns detected in the project and wraps stale checks (whose `when:` pattern no longer matches any file) with `conditionId: file_exists` so they auto-skip instead of failing.
38
+
39
+ **What triggers evolution:**
40
+
41
+ - New dependency in `package.json` / `pyproject.toml` / `go.mod` / `Cargo.toml`
42
+ - New `Dockerfile`, `docker-compose.yml`, `*.tf`, `openapi.yaml`
43
+ - New test runner config (`vitest.config.*`, `jest.config.*`, `playwright.config.*`)
44
+ - New CI workflow in `.github/workflows/`
45
+ - New TypeScript setup (`tsconfig.json`)
46
+
47
+ **What does NOT trigger it:** `.ts` / `.py` / `.go` source files, docs, styles, tests — minor work proceeds without interruption.
48
+
49
+ ---
50
+
51
+ ## Git workflow best practices
52
+
53
+ Prefer the least-disruptive git workflow that still satisfies the task:
54
+
55
+ - Use a branch + PR when the user requests it, when work targets protected
56
+ `main`, or when remote CI/review is part of the task.
57
+ - For small local fixes, commit directly on the current branch and do not open a
58
+ PR unless the user asks.
59
+ - If already on a feature branch, keep committing there instead of creating
60
+ another branch.
61
+ - After committing, decide whether to push: push when a PR, remote review, CI
62
+ run, or handoff needs it; otherwise leave the commit local and report the
63
+ branch/commit.
64
+
65
+ ---
66
+
67
+ ## checks.yaml — Full Reference
68
+
69
+ `checks.yaml` at the project root is the single source of truth. Edit it to add,
70
+ remove, or change checkpoints.
71
+
72
+ After every edit, regenerate the engine files and commit everything together:
73
+
74
+ ```
75
+ holdpoint update
76
+ git add checks.yaml .github/holdpoint/generated/ .github/hooks/
77
+ git commit -m "chore: update holdpoint checks"
78
+ ```
79
+
80
+ ### Top-level structure
81
+
82
+ ```yaml
83
+ version: 1
84
+
85
+ context:
86
+ guides: # project notes shown when `holdpoint check` runs
87
+ setup: >
88
+ Use pnpm, not npm. Node 20+ required.
89
+
90
+ session_context_files:
91
+ - MASTER_PROMPT.md # injected into Copilot/Codex sessions when supported
92
+
93
+ conditions: # gate checks on file/env state
94
+ - id: dist-built
95
+ operator: file_exists
96
+ path: dist/index.js
97
+
98
+ checks: # list of all checks — each has on/when + cmd (task) or prompt
99
+ - ...
100
+ ```
101
+
102
+ ---
103
+
104
+ ### Deterministic check
105
+
106
+ ```yaml
107
+ - id: lint # unique slug, kebab-case
108
+ label: "ESLint — all packages" # human-readable label shown in output
109
+ # on: before_done # lifecycle hook (default; only value today)
110
+ # when: frontend # file filter — omit to run on every task
111
+ cmd: "pnpm turbo lint" # shell command; must exit 0 to pass
112
+ conditionId: dist-built # optional: skip if condition is not met
113
+ ```
114
+
115
+ ### Prompt check
116
+
117
+ ```yaml
118
+ - id: migration-review
119
+ label: "Review DB migration"
120
+ when: "^prisma/migrations/" # only fires when migration files change
121
+ prompt: >
122
+ Open the new migration file. Confirm it is backward-compatible
123
+ and does not drop or truncate data without a fallback.
124
+ ```
125
+
126
+ ---
127
+
128
+ ### `on` — lifecycle hooks
129
+
130
+ `on` specifies _when in the agent lifecycle_ a check fires. Omit it to use the default.
131
+
132
+ | Value | Fires |
133
+ | ------------- | ---------------------------------- |
134
+ | `before_done` | Before the agent marks a task done |
135
+
136
+ ---
137
+
138
+ ### `when` — file filters
139
+
140
+ `when` is an optional file filter. If omitted the check runs on every task.
141
+
142
+ | Value | Fires when changed files match |
143
+ | ----------- | -------------------------------------------------------------------------------------------------- |
144
+ | _(absent)_ | Every task — no file filter applied |
145
+ | `frontend` | `**/*.tsx`, `**/*.jsx`, `**/*.css`, `**/*.scss`, `**/tailwind.config.*`, `apps/**` |
146
+ | `backend` | `**/api/**`, `**/server/**`, `**/routes/**`, `**/controllers/**`, `packages/*/src/**` |
147
+ | `socket` | `**/socket/**`, `**/ws/**`, `**/websocket/**` |
148
+ | `visual` | `**/*.stories.{ts,tsx}`, `**/__screenshots__/**`, `**/*.snap` |
149
+ | `python` | `**/*.py`, `**/*.pyi`, `**/requirements*.txt`, `**/pyproject.toml`, `**/setup.py`, `**/pytest.ini` |
150
+ | `go` | `**/*.go`, `**/go.mod`, `**/go.sum` |
151
+ | `rust` | `**/*.rs`, `**/Cargo.toml`, `**/Cargo.lock` |
152
+ | `java` | `**/*.java`, `**/*.kt`, `**/*.gradle`, `**/*.gradle.kts`, `**/pom.xml` |
153
+ | `ruby` | `**/*.rb`, `**/Gemfile`, `**/Gemfile.lock`, `**/Rakefile` |
154
+ | `database` | `**/*.sql`, `**/migrations/**`, `**/db/**`, `**/database/**`, `**/prisma/**`, `**/*.prisma` |
155
+ | `prisma` | `**/prisma/**`, `**/*.prisma` — focused subset of `database` for Prisma-specific checks |
156
+ | `testing` | `**/*.test.*`, `**/*.spec.*`, `**/__tests__/**`, `**/test/**`, `**/tests/**`, `**/spec/**` |
157
+ | `infra` | `**/Dockerfile*`, `**/docker-compose.*`, `**/*.tf`, `**/*.tfvars`, `**/k8s/**`, `**/kubernetes/**` |
158
+ | `ci` | `**/.github/workflows/**`, `**/.circleci/**`, `**/Jenkinsfile`, `**/.gitlab-ci.yml` |
159
+ | `docs` | `**/*.mdx`, `**/*.rst`, `**/docs/**`, `**/documentation/**` |
160
+ | `"^src/.*"` | Any JavaScript regex tested against each changed file path |
161
+
162
+ Regex example — fires only when files under `src/api/` change:
163
+
164
+ ```yaml
165
+ when: "^src/api/" # new RegExp(when).test(filePath)
166
+ ```
167
+
168
+ > **Note:** Named scopes use glob matching; plain strings are treated as JavaScript regexes.
169
+
170
+ ---
171
+
172
+ ### Conditions
173
+
174
+ Conditions let you skip a check when a prerequisite is not yet met (e.g. a build
175
+ artefact doesn't exist yet).
176
+
177
+ | Operator | What it checks |
178
+ | ----------------- | ------------------------------------------------- |
179
+ | `file_exists` | A file or directory exists at `path` |
180
+ | `file_contains` | The file at `path` contains the substring `value` |
181
+ | `env_var_set` | The environment variable named `value` is set |
182
+ | `shell_returns_0` | The shell command in `cmd` exits with code 0 |
183
+
184
+ ```yaml
185
+ conditions:
186
+ - id: packages-built
187
+ operator: file_exists
188
+ path: packages/yaml-core/dist/index.js
189
+
190
+ checks:
191
+ - id: validate-templates
192
+ label: "Templates parse against schema"
193
+ conditionId: packages-built # skipped (◌) when dist is absent
194
+ cmd: "node dist/validate.js templates/"
195
+ ```
196
+
197
+ ---
198
+
199
+ ### Context guides
200
+
201
+ `context.guides` is a freeform key → multiline-string map. Guides are printed
202
+ at the start of `holdpoint check` output as project-level reminders to whoever
203
+ (or whatever) is running the checks.
204
+
205
+ ```yaml
206
+ context:
207
+ guides:
208
+ setup: >
209
+ This project requires Node 20 and pnpm 9+.
210
+ Run `pnpm install` from the repo root before any other command.
211
+ architecture: >
212
+ API routes live in src/api/. Models live in src/models/.
213
+ Client code must never import from server modules.
214
+ ```
215
+
216
+ ---
217
+
218
+ ## Adding a New Check
219
+
220
+ 1. Open `checks.yaml`.
221
+ 2. Add your entry under `checks:`.
222
+ 3. Run `holdpoint update`.
223
+ 4. Commit `checks.yaml` and the generated files.
224
+
225
+ **Add a task check (runs a shell command automatically):**
226
+
227
+ ```yaml
228
+ checks:
229
+ - id: vitest
230
+ label: "Vitest — unit tests"
231
+ cmd: "pnpm vitest run"
232
+ ```
233
+
234
+ **Add a scoped task (fires only on matching file changes):**
235
+
236
+ ```yaml
237
+ checks:
238
+ - id: openapi-sync
239
+ label: "OpenAPI types are up to date"
240
+ when: "^src/api/"
241
+ cmd: "pnpm generate:types && git diff --exit-code src/generated/"
242
+ ```
243
+
244
+ **Add an agent prompt checkpoint:**
245
+
246
+ ```yaml
247
+ checks:
248
+ - id: jsdoc
249
+ label: "JSDoc on changed public functions"
250
+ prompt: >
251
+ For every public function or export you modified, ensure there is an
252
+ accurate JSDoc comment: description, @param, and @returns.
253
+ ```
254
+
255
+ **Enforce changelog and git commit on every task (recommended):**
256
+
257
+ ```yaml
258
+ checks:
259
+ - id: changelog-update
260
+ label: "Add a CHANGELOG.md entry for this session"
261
+ prompt: >
262
+ Before committing, add an entry to CHANGELOG.md describing what was done.
263
+ Use Keep a Changelog format — add under ## [Unreleased] (create the file
264
+ and that section if absent). Group entries as Added, Changed, Fixed, or Removed.
265
+ Be concise but specific. The entry text will serve as the commit message.
266
+
267
+ - id: readme-sync
268
+ label: "Update README.md if user-facing changes were made"
269
+ prompt: >
270
+ If you added, changed, or removed user-facing functionality — CLI commands,
271
+ configuration options, public APIs, or significant new features — update
272
+ README.md to reflect those changes.
273
+
274
+ - id: git-workflow
275
+ label: "Use the right git workflow"
276
+ prompt: >
277
+ Choose the least-disruptive git workflow: use branch + PR for requested
278
+ feature branches or protected-main work; for small local fixes, commit on
279
+ the current branch without opening a PR unless asked; if already on a
280
+ feature branch, keep committing there instead of creating another branch.
281
+ Push only when a PR, remote review, CI run, or handoff needs it; otherwise
282
+ leave the commit local and report the branch/commit.
283
+
284
+ - id: git-commit
285
+ label: "Commit all changes before finishing"
286
+ cmd: 'git rev-parse --is-inside-work-tree >/dev/null 2>&1 || exit 0; [ -z "$(git status --porcelain)" ] && exit 0; git status --short; exit 1'
287
+ ```
288
+
289
+ When the `git-commit` check fails (uncommitted changes remain), the agent will also see
290
+ the `changelog-update` and `readme-sync` prompt reminders inline — ensuring it updates
291
+ the changelog, syncs docs, _then_ commits before it can mark the task done.
292
+
293
+ ---
294
+
295
+ ## `session_context_files`
296
+
297
+ `session_context_files` is an optional list of project files that Holdpoint injects
298
+ as context at the start of every Copilot session. Use it for files the agent should
299
+ always read before starting work.
300
+
301
+ ```yaml
302
+ session_context_files:
303
+ - MASTER_PROMPT.md
304
+ - AGENT_CONTEXT.md
305
+ ```
306
+
307
+ Files are resolved relative to the repo root and must stay inside it (traversal
308
+ paths like `../../etc/passwd` are rejected). If a file doesn't exist it is silently
309
+ skipped.
310
+
311
+ ---
312
+
313
+ ## `inject_datetime`
314
+
315
+ Holdpoint can inject the current date and time into every prompt the agent receives.
316
+ This fixes a common failure mode where models anchor their sense of "now" to their
317
+ training cutoff and make stale assumptions (e.g. treating months-old information as
318
+ current, or not knowing what year it is).
319
+
320
+ The feature is **on by default** — no configuration needed. To opt out:
321
+
322
+ ```yaml
323
+ inject_datetime: false
324
+ ```
325
+
326
+ When enabled, each prompt submission includes:
327
+
328
+ ```
329
+ Current date and time: 2026-05-29T14:23:45.123Z (UTC)
330
+ Provided by Holdpoint — use this to avoid knowledge-cutoff confusion.
331
+ ```
332
+
333
+ **Agent support:**
334
+
335
+ | Agent | Hook | Notes |
336
+ | ------- | ----------------------- | ---------------------------------------------------------------- |
337
+ | Claude | `UserPromptSubmit` | Fires on every prompt via `additionalContext` |
338
+ | Cursor | `beforeSubmitPrompt` | Fires on every prompt via `additional_context` |
339
+ | Codex | `UserPromptSubmit` | Fires on every prompt via `hookSpecificOutput.additionalContext` |
340
+ | Copilot | `onUserPromptSubmitted` | Fires on every prompt via `additionalContext` |
341
+
342
+ ---
343
+
344
+ ## Commands
345
+
346
+ | Command | What it does |
347
+ | ----------------------------- | ------------------------------------------------------- |
348
+ | `holdpoint check` | Run checks against all files changed vs HEAD |
349
+ | `holdpoint check --staged` | Run checks against staged files only |
350
+ | `holdpoint suggest` | Scan project and show proposed additions to checks.yaml |
351
+ | `holdpoint suggest --apply` | Apply proposals and regenerate engine files |
352
+ | `holdpoint require-changeset` | Require `.changeset/*.md` for package changes |
353
+ | `holdpoint update` | Regenerate engine files from the current `checks.yaml` |
354
+ | `holdpoint validate` | Validate `checks.yaml` schema (no commands run) |
355
+ | `holdpoint builder` | Open the daemon-served visual builder UI |
356
+
357
+ ---
358
+
359
+ ## Generated files (do not edit directly)
360
+
361
+ | File | Agent |
362
+ | --------------------------------------------------- | ------- |
363
+ | `.github/holdpoint/generated/checks.immutable.json` | all |
364
+ | `.github/hooks/holdpoint.json` | Copilot |
365
+ | `.github/hooks/holdpoint-check.mjs` | Copilot |
366
+ | `.claude/settings.json` | Claude |
367
+ | `.cursor/hooks.json` | Cursor |
368
+ | `.cursor/holdpoint-hook.mjs` | Cursor |
369
+ | `.cursorrules` (Holdpoint section) | Cursor |
370
+
371
+ All generated files are overwritten by `holdpoint update`. Edit `checks.yaml`,
372
+ then run `update` — never edit the generated files directly.