@holdpoint/cli 0.1.0-alpha.18 → 0.1.0-alpha.19

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.
package/dist/index.js CHANGED
@@ -1,495 +1,24 @@
1
1
  #!/usr/bin/env node
2
+ import {
3
+ detectInstalledAgents,
4
+ ensureBundledFile,
5
+ initCommand,
6
+ mergeClaudeSettings,
7
+ mergeCursorHooks,
8
+ spliceBreadcrumb
9
+ } from "./chunk-COPLLMYJ.js";
2
10
 
3
11
  // src/index.ts
4
12
  import { Command } from "commander";
5
13
 
6
- // src/commands/init.ts
7
- import { execSync as execSync2 } from "child_process";
8
- import { existsSync as existsSync4, readFileSync as readFileSync3, writeFileSync as writeFileSync3, mkdirSync as mkdirSync2 } from "fs";
9
- import { join as join2, dirname as dirname3 } from "path";
10
- import { fileURLToPath as fileURLToPath2 } from "url";
11
- import chalk2 from "chalk";
12
- import ora from "ora";
13
- import { buildConfigJson, buildEngine } from "@holdpoint/engine-copilot";
14
- import { buildEngineJson as buildClaudeEngineJson } from "@holdpoint/engine-claude";
15
- import {
16
- buildCheckScript as buildCursorCheckScript,
17
- buildEngine as buildCursorEngine,
18
- buildHooksJson as buildCursorHooksJson
19
- } from "@holdpoint/engine-cursor";
20
- import {
21
- buildConfigToml as buildCodexConfigToml,
22
- buildHooksJson as buildCodexHooksJson,
23
- buildCheckScript as buildCodexCheckScript
24
- } from "@holdpoint/engine-codex";
25
- import { parseHoldpointYaml } from "@holdpoint/yaml-core";
26
-
27
- // src/detect.ts
28
- import { existsSync, readFileSync } from "fs";
29
- function detectPackageManager() {
30
- if (existsSync("pnpm-lock.yaml")) return "pnpm";
31
- if (existsSync("yarn.lock")) return "yarn";
32
- return "npm";
33
- }
34
- function detectInstalledAgents() {
35
- const agents = [];
36
- if (existsSync(".github/extensions/holdpoint/extension.mjs")) agents.push("copilot");
37
- if (existsSync(".claude/settings.json")) agents.push("claude");
38
- if (existsSync(".cursor/hooks.json")) {
39
- try {
40
- if (readFileSync(".cursor/hooks.json", "utf8").includes("HOLDPOINT_MANAGED=cursor")) {
41
- agents.push("cursor");
42
- }
43
- } catch {
44
- }
45
- }
46
- if (existsSync(".cursorrules")) {
47
- try {
48
- if (!agents.includes("cursor") && readFileSync(".cursorrules", "utf8").includes("Holdpoint Rules")) {
49
- agents.push("cursor");
50
- }
51
- } catch {
52
- }
53
- }
54
- if (existsSync(".codex/holdpoint-check.mjs")) agents.push("codex");
55
- return agents;
56
- }
57
-
58
- // src/templates.ts
59
- import { copyFileSync, existsSync as existsSync2, writeFileSync } from "fs";
60
- import { join, dirname } from "path";
61
- import { fileURLToPath } from "url";
62
- var __dirname = dirname(fileURLToPath(import.meta.url));
63
- function getBundledTemplatePath(filename) {
64
- const candidates = [
65
- join(__dirname, "templates", filename),
66
- // dist/templates/ (published package)
67
- join(__dirname, "../../../templates", filename),
68
- // monorepo dev fallback
69
- join(process.cwd(), "templates", filename)
70
- // cwd fallback
71
- ];
72
- for (const candidate of candidates) {
73
- if (existsSync2(candidate)) return candidate;
74
- }
75
- return "";
76
- }
77
- function ensureBundledFile(outputPath, templateFilename, fallbackContent) {
78
- if (existsSync2(outputPath)) {
79
- return false;
80
- }
81
- const templatePath = getBundledTemplatePath(templateFilename);
82
- if (templatePath) {
83
- copyFileSync(templatePath, outputPath);
84
- } else {
85
- writeFileSync(outputPath, fallbackContent, "utf8");
86
- }
87
- return true;
88
- }
89
-
90
- // src/lib/preflight.ts
91
- import { execSync } from "child_process";
92
- import chalk from "chalk";
93
- function silentExec(cmd) {
94
- try {
95
- const stdout = execSync(cmd, { stdio: ["ignore", "pipe", "ignore"] }).toString();
96
- return { ok: true, stdout };
97
- } catch {
98
- return { ok: false, stdout: "" };
99
- }
100
- }
101
- function checkCopilot() {
102
- const gh = silentExec("gh --version");
103
- if (!gh.ok) {
104
- return {
105
- agent: "copilot",
106
- status: "action_required",
107
- message: "GitHub CLI not found on PATH",
108
- command: "brew install gh # or: https://cli.github.com/"
109
- };
110
- }
111
- const copilot = silentExec("gh copilot --version");
112
- if (!copilot.ok) {
113
- return {
114
- agent: "copilot",
115
- status: "action_required",
116
- message: "Copilot CLI extension not installed",
117
- command: "gh extension install github/gh-copilot"
118
- };
119
- }
120
- return {
121
- agent: "copilot",
122
- status: "action_required",
123
- message: "Copilot CLI detected \u2014 experimental mode required for EXTENSIONS",
124
- command: "Inside Copilot CLI, run: /experimental on"
125
- };
126
- }
127
- function checkClaude() {
128
- const claude = silentExec("claude --version");
129
- if (!claude.ok) {
130
- return {
131
- agent: "claude",
132
- status: "unknown",
133
- message: "Claude Code binary not on PATH (hooks still written for when it is)"
134
- };
135
- }
136
- return {
137
- agent: "claude",
138
- status: "ok",
139
- message: "Claude Code detected \u2014 hooks installed at .claude/settings.json"
140
- };
141
- }
142
- function checkCursor() {
143
- return {
144
- agent: "cursor",
145
- status: "ok",
146
- message: "Cursor \u2014 .cursor/hooks.json gate + .cursor/rules breadcrumb installed",
147
- docs: "https://holdpoint.dev/docs#cursor"
148
- };
149
- }
150
- function checkCodex() {
151
- const codex = silentExec("codex --version");
152
- if (!codex.ok) {
153
- return {
154
- agent: "codex",
155
- status: "action_required",
156
- message: "Codex CLI not found on PATH",
157
- command: "Install Codex: https://github.com/openai/codex"
158
- };
159
- }
160
- return {
161
- agent: "codex",
162
- status: "action_required",
163
- message: "Codex detected \u2014 project-level hooks require trust approval",
164
- command: "In the Codex TUI: codex trust (or /hooks to review)"
165
- };
166
- }
167
- var CHECKS = {
168
- copilot: checkCopilot,
169
- claude: checkClaude,
170
- cursor: checkCursor,
171
- codex: checkCodex
172
- };
173
- function runPreflight(agents) {
174
- return agents.flatMap((agent) => {
175
- const check = CHECKS[agent];
176
- return check ? [check()] : [];
177
- });
178
- }
179
- function printPreflight(results) {
180
- if (results.length === 0) return;
181
- const ok = results.filter((r) => r.status === "ok");
182
- const unknown = results.filter((r) => r.status === "unknown");
183
- const action = results.filter((r) => r.status === "action_required");
184
- console.log("");
185
- console.log(chalk.bold("Agent preflight:"));
186
- for (const r of ok) {
187
- console.log(` ${chalk.green("\u2713")} ${r.agent.padEnd(7)} ${chalk.dim(r.message)}`);
188
- }
189
- for (const r of unknown) {
190
- console.log(` ${chalk.dim("?")} ${r.agent.padEnd(7)} ${chalk.dim(r.message)}`);
191
- }
192
- for (const r of action) {
193
- console.log(` ${chalk.yellow("\u2192")} ${chalk.bold(r.agent.padEnd(7))} ${r.message}`);
194
- if (r.command) console.log(` ${chalk.cyan(r.command)}`);
195
- }
196
- }
197
-
198
- // src/claude-settings.ts
199
- var HOLDPOINT_CLAUDE_HOOK_MARKER = "HOLDPOINT_MANAGED=claude";
200
- function isObject(value) {
201
- return value != null && typeof value === "object" && !Array.isArray(value);
202
- }
203
- function asHookArray(value) {
204
- return Array.isArray(value) ? value : [];
205
- }
206
- function isManagedHookCommand(value) {
207
- return isObject(value) && typeof value.command === "string" && value.command.includes(HOLDPOINT_CLAUDE_HOOK_MARKER);
208
- }
209
- function isLegacyManagedHookCommand(value) {
210
- if (!isObject(value) || typeof value.command !== "string") return false;
211
- return value.command === "node_modules/.bin/holdpoint event --engine claude --from-hook || true" || value.command === "node_modules/.bin/holdpoint check --staged";
212
- }
213
- function isManagedHookEntry(value) {
214
- if (!isObject(value)) return false;
215
- const hooks = asHookArray(value.hooks);
216
- return hooks.length > 0 && (hooks.every(isManagedHookCommand) || hooks.every(isLegacyManagedHookCommand));
217
- }
218
- function mergeClaudeSettings(existing, generated) {
219
- const existingHooks = isObject(existing.hooks) ? existing.hooks : {};
220
- const generatedHooks = isObject(generated.hooks) ? generated.hooks : {};
221
- const mergedHooks = {};
222
- for (const eventName of /* @__PURE__ */ new Set([
223
- ...Object.keys(existingHooks),
224
- ...Object.keys(generatedHooks)
225
- ])) {
226
- const preserved = asHookArray(existingHooks[eventName]).filter(
227
- (entry) => !isManagedHookEntry(entry)
228
- );
229
- const next = asHookArray(generatedHooks[eventName]);
230
- if (preserved.length > 0 || next.length > 0) {
231
- mergedHooks[eventName] = [...preserved, ...next];
232
- }
233
- }
234
- return { ...existing, ...generated, hooks: mergedHooks };
235
- }
236
-
237
- // src/cursor-hooks.ts
238
- var HOLDPOINT_CURSOR_HOOK_MARKER = "HOLDPOINT_MANAGED=cursor";
239
- function isObject2(value) {
240
- return value != null && typeof value === "object" && !Array.isArray(value);
241
- }
242
- function asHookArray2(value) {
243
- return Array.isArray(value) ? value : [];
244
- }
245
- function isManagedCursorHook(value) {
246
- return isObject2(value) && typeof value.command === "string" && (value.command.includes(HOLDPOINT_CURSOR_HOOK_MARKER) || value.command.includes(".cursor/holdpoint-hook.mjs"));
247
- }
248
- function mergeCursorHooks(existing, generated) {
249
- const existingHooks = isObject2(existing.hooks) ? existing.hooks : {};
250
- const generatedHooks = isObject2(generated.hooks) ? generated.hooks : {};
251
- const mergedHooks = {};
252
- for (const eventName of /* @__PURE__ */ new Set([
253
- ...Object.keys(existingHooks),
254
- ...Object.keys(generatedHooks)
255
- ])) {
256
- const preserved = asHookArray2(existingHooks[eventName]).filter(
257
- (entry) => !isManagedCursorHook(entry)
258
- );
259
- const next = asHookArray2(generatedHooks[eventName]);
260
- if (preserved.length > 0 || next.length > 0) {
261
- mergedHooks[eventName] = [...preserved, ...next];
262
- }
263
- }
264
- return { ...existing, ...generated, hooks: mergedHooks };
265
- }
266
-
267
- // src/lib/instructions-breadcrumb.ts
268
- import { existsSync as existsSync3, readFileSync as readFileSync2, writeFileSync as writeFileSync2, mkdirSync } from "fs";
269
- import { dirname as dirname2 } from "path";
270
- var START_MARKER = "<!-- HOLDPOINT_MANAGED \u2014 content between these markers is auto-generated by holdpoint init / holdpoint update -->";
271
- var END_MARKER = "<!-- /HOLDPOINT_MANAGED -->";
272
- var BREADCRUMB_BODY = `## Holdpoint workflow
273
-
274
- This repo uses [Holdpoint](https://holdpoint.dev) to gate task completion on deterministic checks.
275
-
276
- Before marking any task done:
277
-
278
- 1. Run \`holdpoint check\` (or it will run automatically via Stop / TaskCompleted hooks).
279
- 2. Fix any failures. Re-run until exit 0.
280
- 3. Never bypass with \`--no-verify\` or by skipping the agent's stop hook.
281
-
282
- Full workflow reference: [\`MASTER_PROMPT.md\`](./MASTER_PROMPT.md) (always injected at session start).
283
- Deep reference: [\`HOLDPOINT_REFERENCE.md\`](./HOLDPOINT_REFERENCE.md) (read on demand).
284
- Active checks: [\`checks.yaml\`](./checks.yaml).`;
285
- function spliceBreadcrumb(filePath, body = BREADCRUMB_BODY, createIfMissing = true) {
286
- const block = `${START_MARKER}
287
-
288
- ${body}
289
-
290
- ${END_MARKER}`;
291
- if (!existsSync3(filePath)) {
292
- if (!createIfMissing) return;
293
- mkdirSync(dirname2(filePath), { recursive: true });
294
- writeFileSync2(filePath, block + "\n", "utf8");
295
- return;
296
- }
297
- const existing = readFileSync2(filePath, "utf8");
298
- const startIdx = existing.indexOf(START_MARKER);
299
- const endIdx = existing.indexOf(END_MARKER);
300
- if (startIdx !== -1 && endIdx !== -1 && endIdx > startIdx) {
301
- const before = existing.slice(0, startIdx);
302
- const after = existing.slice(endIdx + END_MARKER.length);
303
- writeFileSync2(filePath, before + block + after, "utf8");
304
- return;
305
- }
306
- const sep2 = existing.endsWith("\n") ? "\n" : "\n\n";
307
- writeFileSync2(filePath, existing + sep2 + block + "\n", "utf8");
308
- }
309
-
310
- // src/commands/init.ts
311
- var __dirname2 = dirname3(fileURLToPath2(import.meta.url));
312
- function getDefaultTemplatePath() {
313
- const candidates = [
314
- join2(__dirname2, "templates", "default.yaml"),
315
- // dist/templates/ (published package)
316
- join2(__dirname2, "../../../templates", "default.yaml"),
317
- // monorepo dev fallback
318
- join2(process.cwd(), "templates", "default.yaml")
319
- // cwd fallback
320
- ];
321
- for (const p of candidates) {
322
- if (existsSync4(p)) return p;
323
- }
324
- return "";
325
- }
326
- var MINIMAL_CHECKS_YAML = `version: 1
327
- context:
328
- guides: {}
329
- conditions: []
330
- checks:
331
- - id: lint
332
- label: "Lint codebase"
333
- cmd: "echo 'Add your lint command here'"
334
-
335
- - id: jsdoc
336
- label: "JSDoc on changed public functions"
337
- prompt: "Ensure all changed public functions and exports have JSDoc comments."
338
- `;
339
- var MINIMAL_MASTER_PROMPT = `# Holdpoint
340
-
341
- Run \`holdpoint check\` before marking any task complete.
342
- See \`checks.yaml\` for the full list of checks.
343
- `;
344
- var MINIMAL_HOLDPOINT_REFERENCE = `# Holdpoint reference
345
-
346
- Read \`MASTER_PROMPT.md\` first for the mandatory workflow, then use this file for deeper project-specific Holdpoint notes.
347
- `;
348
- var MINIMAL_PREREQUISITES = `# Holdpoint prerequisites
349
-
350
- Holdpoint installed repo-local engine integrations for one or more AI coding agents. Before relying on them locally, review these setup notes:
351
-
352
- - **GitHub Copilot CLI** \u2014 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.
353
- - **Cursor** \u2014 project-level hooks run in trusted workspaces. After opening the repo in Cursor, confirm the workspace is trusted and review Settings \u2192 Hooks if hooks do not fire.
354
- - **OpenAI Codex** \u2014 project-level hooks require trust approval. Run \`codex trust\` in the Codex TUI or review the hook with \`/hooks\`.
355
- - **General** \u2014 Holdpoint expects Node.js 18+ and a git repository so \`holdpoint init\`, \`holdpoint update\`, and \`holdpoint check\` can run normally.
356
-
357
- Docs: https://holdpoint.dev/docs
358
- `;
359
- async function initCommand(options) {
360
- const spinner = ora("Initialising Holdpoint\u2026").start();
361
- const agentOpt = options.agent;
362
- const agents = !agentOpt || agentOpt === "all" ? ["copilot", "claude", "cursor", "codex"] : [agentOpt];
363
- spinner.text = `Installing for: ${chalk2.cyan(agents.join(", "))}`;
364
- const pm = detectPackageManager();
365
- let yamlContent = MINIMAL_CHECKS_YAML;
366
- if (!existsSync4("checks.yaml")) {
367
- const templatePath = getDefaultTemplatePath();
368
- if (templatePath) {
369
- yamlContent = readFileSync3(templatePath, "utf8");
370
- }
371
- if (pm !== "pnpm") {
372
- yamlContent = yamlContent.replace(/\bpnpm\b/g, pm);
373
- }
374
- writeFileSync3("checks.yaml", yamlContent, "utf8");
375
- } else {
376
- yamlContent = readFileSync3("checks.yaml", "utf8");
377
- }
378
- const config = parseHoldpointYaml(yamlContent);
379
- const generatedDir = ".github/holdpoint/generated";
380
- mkdirSync2(generatedDir, { recursive: true });
381
- writeFileSync3(`${generatedDir}/checks.immutable.json`, buildConfigJson(config), "utf8");
382
- if (agents.includes("copilot")) {
383
- const extDir = ".github/extensions/holdpoint";
384
- mkdirSync2(extDir, { recursive: true });
385
- writeFileSync3(join2(extDir, "extension.mjs"), buildEngine(config), "utf8");
386
- spliceBreadcrumb(".github/copilot-instructions.md");
387
- }
388
- if (agents.includes("claude")) {
389
- mkdirSync2(".claude", { recursive: true });
390
- const settingsPath = ".claude/settings.json";
391
- let existing = {};
392
- if (existsSync4(settingsPath)) {
393
- try {
394
- existing = JSON.parse(readFileSync3(settingsPath, "utf8"));
395
- } catch {
396
- }
397
- }
398
- const holdpointHooks = JSON.parse(buildClaudeEngineJson(config));
399
- writeFileSync3(
400
- settingsPath,
401
- JSON.stringify(mergeClaudeSettings(existing, holdpointHooks), null, 2),
402
- "utf8"
403
- );
404
- spliceBreadcrumb("CLAUDE.md");
405
- }
406
- if (agents.includes("cursor")) {
407
- mkdirSync2(".cursor", { recursive: true });
408
- const cursorHooksPath = ".cursor/hooks.json";
409
- let existingHooks = {};
410
- if (existsSync4(cursorHooksPath)) {
411
- try {
412
- existingHooks = JSON.parse(readFileSync3(cursorHooksPath, "utf8"));
413
- } catch {
414
- }
415
- }
416
- const cursorHooks = JSON.parse(buildCursorHooksJson(config));
417
- writeFileSync3(
418
- cursorHooksPath,
419
- JSON.stringify(mergeCursorHooks(existingHooks, cursorHooks), null, 2) + "\n",
420
- "utf8"
421
- );
422
- writeFileSync3(".cursor/holdpoint-hook.mjs", buildCursorCheckScript(), "utf8");
423
- const cursorRules = buildCursorEngine(config);
424
- const cursorPath = ".cursorrules";
425
- if (existsSync4(cursorPath)) {
426
- const existing = readFileSync3(cursorPath, "utf8");
427
- if (!existing.includes("Holdpoint Rules")) {
428
- writeFileSync3(cursorPath, `${existing.trimEnd()}
429
-
430
- ${cursorRules}`, "utf8");
431
- }
432
- } else {
433
- writeFileSync3(cursorPath, cursorRules, "utf8");
434
- }
435
- spliceBreadcrumb(".cursor/rules/holdpoint.md");
436
- }
437
- if (agents.includes("codex")) {
438
- mkdirSync2(".codex", { recursive: true });
439
- writeFileSync3(".codex/hooks.json", buildCodexHooksJson(config), "utf8");
440
- writeFileSync3(".codex/holdpoint-check.mjs", buildCodexCheckScript(config), "utf8");
441
- writeFileSync3(".codex/config.toml", buildCodexConfigToml(), "utf8");
442
- spliceBreadcrumb("AGENTS.md");
443
- }
444
- ensureBundledFile("MASTER_PROMPT.md", "MASTER_PROMPT.md", MINIMAL_MASTER_PROMPT);
445
- ensureBundledFile(
446
- "HOLDPOINT_REFERENCE.md",
447
- "HOLDPOINT_REFERENCE.md",
448
- MINIMAL_HOLDPOINT_REFERENCE
449
- );
450
- ensureBundledFile(
451
- "HOLDPOINT_PREREQUISITES.md",
452
- "HOLDPOINT_PREREQUISITES.md",
453
- MINIMAL_PREREQUISITES
454
- );
455
- spinner.text = "Installing holdpoint as a devDependency\u2026";
456
- const installCmds = {
457
- pnpm: "pnpm add -D holdpoint",
458
- yarn: "yarn add --dev holdpoint",
459
- npm: "npm install --save-dev holdpoint"
460
- };
461
- const installCmd = installCmds[pm];
462
- try {
463
- execSync2(installCmd, { stdio: "pipe" });
464
- spinner.succeed(chalk2.bold.green("Holdpoint initialised!"));
465
- } catch {
466
- spinner.warn(
467
- chalk2.yellow(`Holdpoint initialised, but could not install the package automatically.`) + `
468
- Run manually: ${chalk2.yellow(installCmd)}`
469
- );
470
- }
471
- const preflight = runPreflight(agents);
472
- printPreflight(preflight);
473
- console.log(`
474
- ${chalk2.cyan("Next steps:")}
475
- 1. Edit ${chalk2.yellow("checks.yaml")} to customise your eval checkpoints
476
- 2. Address any ${chalk2.yellow("\u2192")} items above (full notes in ${chalk2.yellow("HOLDPOINT_PREREQUISITES.md")})
477
- 3. Commit ${chalk2.yellow("checks.yaml")}, ${chalk2.yellow("HOLDPOINT_PREREQUISITES.md")}, and the generated engine files
478
- 4. Run ${chalk2.yellow("holdpoint check")} at any time to validate
479
-
480
- Visual builder: ${chalk2.yellow("holdpoint builder")} (opens the daemon at /builder)
481
- Agents: ${chalk2.cyan(agents.join(", "))}
482
- `);
483
- }
484
-
485
14
  // src/commands/check.ts
486
- import { existsSync as existsSync5, readFileSync as readFileSync4, writeFileSync as writeFileSync4, mkdirSync as mkdirSync3 } from "fs";
487
- import { join as join3 } from "path";
488
- import chalk3 from "chalk";
489
- import ora2 from "ora";
490
- import { parseHoldpointYaml as parseHoldpointYaml2, matchesWhen } from "@holdpoint/yaml-core";
15
+ import { existsSync, readFileSync, writeFileSync, mkdirSync } from "fs";
16
+ import { join } from "path";
17
+ import chalk from "chalk";
18
+ import ora from "ora";
19
+ import { parseHoldpointYaml, matchesWhen } from "@holdpoint/yaml-core";
491
20
  import { runDeterministicChecks } from "@holdpoint/yaml-core/runner";
492
- import { execSync as execSync3 } from "child_process";
21
+ import { execSync } from "child_process";
493
22
  import { randomUUID } from "crypto";
494
23
  import { identifyProject } from "@holdpoint/live-daemon";
495
24
  import { BridgeClient } from "@holdpoint/sdk";
@@ -499,7 +28,7 @@ var CHECK_REPORTS_PATH = ".holdpoint/check-reports.json";
499
28
  var CHECK_REPORTS_MAX = 50;
500
29
  function getStagedFiles() {
501
30
  try {
502
- const out = execSync3("git diff --cached --name-only", {
31
+ const out = execSync("git diff --cached --name-only", {
503
32
  encoding: "utf8",
504
33
  stdio: ["pipe", "pipe", "ignore"]
505
34
  });
@@ -510,7 +39,7 @@ function getStagedFiles() {
510
39
  }
511
40
  function getAllChangedFiles() {
512
41
  try {
513
- const out = execSync3("git diff --name-only HEAD", {
42
+ const out = execSync("git diff --name-only HEAD", {
514
43
  encoding: "utf8",
515
44
  stdio: ["pipe", "pipe", "ignore"]
516
45
  });
@@ -521,7 +50,7 @@ function getAllChangedFiles() {
521
50
  }
522
51
  function getLastCommitFiles() {
523
52
  try {
524
- const out = execSync3("git diff --name-only HEAD~1 HEAD", {
53
+ const out = execSync("git diff --name-only HEAD~1 HEAD", {
525
54
  encoding: "utf8",
526
55
  stdio: ["pipe", "pipe", "ignore"]
527
56
  });
@@ -532,7 +61,7 @@ function getLastCommitFiles() {
532
61
  }
533
62
  function getHeadSha() {
534
63
  try {
535
- return execSync3("git rev-parse HEAD", {
64
+ return execSync("git rev-parse HEAD", {
536
65
  encoding: "utf8",
537
66
  stdio: ["pipe", "pipe", "ignore"]
538
67
  }).trim();
@@ -542,7 +71,7 @@ function getHeadSha() {
542
71
  }
543
72
  function readCommitCache() {
544
73
  try {
545
- const raw = readFileSync4(COMMIT_CACHE_PATH, "utf8");
74
+ const raw = readFileSync(COMMIT_CACHE_PATH, "utf8");
546
75
  const parsed = JSON.parse(raw);
547
76
  return new Set(Array.isArray(parsed.verified) ? parsed.verified : []);
548
77
  } catch {
@@ -553,18 +82,18 @@ function recordCommitCache(sha) {
553
82
  try {
554
83
  const existing = readCommitCache();
555
84
  const updated = [sha, ...[...existing].filter((s) => s !== sha)].slice(0, COMMIT_CACHE_MAX);
556
- mkdirSync3(join3(COMMIT_CACHE_PATH, ".."), { recursive: true });
557
- writeFileSync4(COMMIT_CACHE_PATH, JSON.stringify({ verified: updated }, null, 2) + "\n", "utf8");
85
+ mkdirSync(join(COMMIT_CACHE_PATH, ".."), { recursive: true });
86
+ writeFileSync(COMMIT_CACHE_PATH, JSON.stringify({ verified: updated }, null, 2) + "\n", "utf8");
558
87
  } catch {
559
88
  }
560
89
  }
561
90
  function recordCheckReport(run) {
562
91
  try {
563
- mkdirSync3(join3(CHECK_REPORTS_PATH, ".."), { recursive: true });
92
+ mkdirSync(join(CHECK_REPORTS_PATH, ".."), { recursive: true });
564
93
  let existing = { runs: [] };
565
- if (existsSync5(CHECK_REPORTS_PATH)) {
94
+ if (existsSync(CHECK_REPORTS_PATH)) {
566
95
  try {
567
- existing = JSON.parse(readFileSync4(CHECK_REPORTS_PATH, "utf8"));
96
+ existing = JSON.parse(readFileSync(CHECK_REPORTS_PATH, "utf8"));
568
97
  if (!Array.isArray(existing.runs)) existing.runs = [];
569
98
  } catch {
570
99
  existing = { runs: [] };
@@ -573,21 +102,38 @@ function recordCheckReport(run) {
573
102
  const updated = {
574
103
  runs: [run, ...existing.runs].slice(0, CHECK_REPORTS_MAX)
575
104
  };
576
- writeFileSync4(CHECK_REPORTS_PATH, JSON.stringify(updated, null, 2) + "\n", "utf8");
105
+ writeFileSync(CHECK_REPORTS_PATH, JSON.stringify(updated, null, 2) + "\n", "utf8");
577
106
  } catch {
578
107
  }
579
108
  }
580
109
  async function checkCommand(options) {
581
- if (!existsSync5("checks.yaml")) {
582
- console.error(chalk3.red("No checks.yaml found. Run `holdpoint init` first."));
583
- process.exit(1);
110
+ if (!existsSync("checks.yaml")) {
111
+ if (options.staged || !process.stdout.isTTY || !process.stdin.isTTY) {
112
+ console.error(chalk.red("No checks.yaml found. Run `holdpoint init` first."));
113
+ process.exit(1);
114
+ }
115
+ const { promptYesNo } = await import("./prompt-EQ5IFADN.js");
116
+ console.log(
117
+ chalk.yellow("No checks.yaml in this directory.") + chalk.dim(" (") + process.cwd() + chalk.dim(")")
118
+ );
119
+ const shouldInit = await promptYesNo(chalk.bold("Initialise Holdpoint here?"), true);
120
+ if (!shouldInit) {
121
+ console.error(chalk.dim("Skipped. Run `holdpoint init` when you're ready."));
122
+ process.exit(1);
123
+ }
124
+ const { initCommand: initCommand2 } = await import("./init-FNQ5GQBD.js");
125
+ await initCommand2({});
126
+ console.log(
127
+ chalk.dim("\nReview ") + chalk.yellow("checks.yaml") + chalk.dim(" and run ") + chalk.yellow("holdpoint check") + chalk.dim(" again when you're ready.")
128
+ );
129
+ return;
584
130
  }
585
- const yamlContent = readFileSync4("checks.yaml", "utf8");
131
+ const yamlContent = readFileSync("checks.yaml", "utf8");
586
132
  let config;
587
133
  try {
588
- config = parseHoldpointYaml2(yamlContent);
134
+ config = parseHoldpointYaml(yamlContent);
589
135
  } catch (err) {
590
- console.error(chalk3.red("Invalid checks.yaml:"), err.message);
136
+ console.error(chalk.red("Invalid checks.yaml:"), err.message);
591
137
  process.exit(1);
592
138
  }
593
139
  const headSha = getHeadSha();
@@ -600,7 +146,7 @@ async function checkCommand(options) {
600
146
  } else {
601
147
  if (headSha && readCommitCache().has(headSha)) {
602
148
  console.log(
603
- chalk3.green(`\u2713 Commit ${headSha.slice(0, 8)} already verified \u2014 nothing to re-check.`)
149
+ chalk.green(`\u2713 Commit ${headSha.slice(0, 8)} already verified \u2014 nothing to re-check.`)
604
150
  );
605
151
  process.exit(0);
606
152
  }
@@ -609,10 +155,10 @@ async function checkCommand(options) {
609
155
  changedFiles = lastCommit;
610
156
  usedHeadShaForCache = true;
611
157
  console.log(
612
- chalk3.yellow("No staged files. Running checks scoped to the most recent commit's files.")
158
+ chalk.yellow("No staged files. Running checks scoped to the most recent commit's files.")
613
159
  );
614
160
  } else {
615
- console.log(chalk3.green("\u2713 No staged changes and no recent commit \u2014 nothing to check."));
161
+ console.log(chalk.green("\u2713 No staged changes and no recent commit \u2014 nothing to check."));
616
162
  process.exit(0);
617
163
  }
618
164
  }
@@ -620,10 +166,10 @@ async function checkCommand(options) {
620
166
  changedFiles = getAllChangedFiles();
621
167
  if (changedFiles.length === 0) {
622
168
  console.log(
623
- chalk3.yellow("No changed files detected. Running all checks with no file filter.")
169
+ chalk.yellow("No changed files detected. Running all checks with no file filter.")
624
170
  );
625
171
  console.log(
626
- chalk3.dim(
172
+ chalk.dim(
627
173
  " Tip: if you just ran `holdpoint init`, commit the generated files to clear the git-commit check."
628
174
  )
629
175
  );
@@ -631,14 +177,14 @@ async function checkCommand(options) {
631
177
  }
632
178
  const guides = Object.entries(config.context?.guides ?? {});
633
179
  if (guides.length > 0) {
634
- console.log(chalk3.cyan("\nProject guides:"));
180
+ console.log(chalk.cyan("\nProject guides:"));
635
181
  for (const [key, text] of guides) {
636
- console.log(chalk3.bold(` ${key}:`), chalk3.dim(String(text).trim()));
182
+ console.log(chalk.bold(` ${key}:`), chalk.dim(String(text).trim()));
637
183
  }
638
184
  console.log("");
639
185
  }
640
186
  const taskCount = config.checks.filter((c) => c.cmd !== void 0).length;
641
- const spinner = ora2(`Running ${taskCount} task(s)\u2026`).start();
187
+ const spinner = ora(`Running ${taskCount} task(s)\u2026`).start();
642
188
  const effectiveFiles = changedFiles.length > 0 ? changedFiles : ["__all__"];
643
189
  const results = runDeterministicChecks(config, effectiveFiles);
644
190
  const passed = results.filter((r) => r.status === "pass");
@@ -651,19 +197,29 @@ async function checkCommand(options) {
651
197
  console.log("");
652
198
  console.log(
653
199
  [
654
- chalk3.green(`\u2713 ${passed.length} passed`),
655
- failed.length > 0 ? chalk3.red(`\u2717 ${failed.length} failed`) : "",
656
- skipped.length > 0 ? chalk3.gray(`\u25CC ${skipped.length} skipped`) : ""
200
+ chalk.green(`\u2713 ${passed.length} passed`),
201
+ failed.length > 0 ? chalk.red(`\u2717 ${failed.length} failed`) : "",
202
+ skipped.length > 0 ? chalk.gray(`\u25CC ${skipped.length} skipped`) : ""
657
203
  ].filter(Boolean).join(" ")
658
204
  );
659
- const promptChecks = config.checks.filter(
660
- (c) => c.prompt !== void 0 && matchesWhen(c.when, changedFiles.length > 0 ? changedFiles : ["__all__"], config.patterns)
661
- );
205
+ const promptChecks = changedFiles.length > 0 ? config.checks.filter(
206
+ (c) => c.prompt !== void 0 && matchesWhen(c.when, changedFiles, config.patterns)
207
+ ) : [];
662
208
  if (promptChecks.length > 0) {
663
209
  console.log(`
664
- ${chalk3.cyan("Agent prompts to act on:")}`);
210
+ ${chalk.cyan("Agent prompts to act on:")}`);
665
211
  for (const c of promptChecks) {
666
- console.log(` ${chalk3.yellow("\u25A1")} [${c.label}] ${c.prompt ?? ""}`);
212
+ console.log(` ${chalk.yellow("\u25A1")} [${c.label}] ${c.prompt ?? ""}`);
213
+ }
214
+ } else if (changedFiles.length === 0) {
215
+ const totalPromptChecks = config.checks.filter((c) => c.prompt !== void 0).length;
216
+ if (totalPromptChecks > 0) {
217
+ console.log(
218
+ chalk.dim(
219
+ `
220
+ (${totalPromptChecks} prompt-style checks defined; they fire relative to changed files \u2014 none surfaced with no diff context)`
221
+ )
222
+ );
667
223
  }
668
224
  }
669
225
  const reportResults = [
@@ -729,70 +285,70 @@ ${chalk3.cyan("Agent prompts to act on:")}`);
729
285
  }
730
286
  }
731
287
  function printResult(result) {
732
- const icon = result.status === "pass" ? chalk3.green("\u2713") : result.status === "fail" ? chalk3.red("\u2717") : result.status === "skip" ? chalk3.gray("\u25CC") : chalk3.yellow("\u2026");
288
+ const icon = result.status === "pass" ? chalk.green("\u2713") : result.status === "fail" ? chalk.red("\u2717") : result.status === "skip" ? chalk.gray("\u25CC") : chalk.yellow("\u2026");
733
289
  const label = result.check.label;
734
290
  console.log(`${icon} ${label}`);
735
291
  if (result.status === "fail" && result.output) {
736
292
  const trimmed = result.output.trim().split("\n").slice(0, 10).join("\n");
737
- console.log(chalk3.dim(trimmed.replace(/^/gm, " ")));
293
+ console.log(chalk.dim(trimmed.replace(/^/gm, " ")));
738
294
  }
739
295
  if (result.status === "skip" && result.skipReason) {
740
- console.log(chalk3.dim(` ${result.skipReason}`));
296
+ console.log(chalk.dim(` ${result.skipReason}`));
741
297
  }
742
298
  }
743
299
 
744
300
  // src/commands/validate.ts
745
- import { existsSync as existsSync6, readFileSync as readFileSync5 } from "fs";
746
- import chalk4 from "chalk";
747
- import { parseHoldpointYaml as parseHoldpointYaml3, validateConfig } from "@holdpoint/yaml-core";
301
+ import { existsSync as existsSync2, readFileSync as readFileSync2 } from "fs";
302
+ import chalk2 from "chalk";
303
+ import { parseHoldpointYaml as parseHoldpointYaml2, validateConfig } from "@holdpoint/yaml-core";
748
304
  async function validateCommand() {
749
- if (!existsSync6("checks.yaml")) {
750
- console.error(chalk4.red("No checks.yaml found. Run `holdpoint init` first."));
305
+ if (!existsSync2("checks.yaml")) {
306
+ console.error(chalk2.red("No checks.yaml found. Run `holdpoint init` first."));
751
307
  process.exit(1);
752
308
  }
753
- const text = readFileSync5("checks.yaml", "utf8");
309
+ const text = readFileSync2("checks.yaml", "utf8");
754
310
  let config;
755
311
  try {
756
- config = parseHoldpointYaml3(text);
312
+ config = parseHoldpointYaml2(text);
757
313
  } catch (err) {
758
- console.error(chalk4.red("Parse error:"), err.message);
314
+ console.error(chalk2.red("Parse error:"), err.message);
759
315
  process.exit(1);
760
316
  }
761
317
  const result = validateConfig(config);
762
318
  if (result.valid) {
763
- console.log(chalk4.green("\u2713 checks.yaml is valid"));
319
+ console.log(chalk2.green("\u2713 checks.yaml is valid"));
764
320
  console.log(
765
- chalk4.dim(
321
+ chalk2.dim(
766
322
  ` ${config.checks.filter((c) => c.cmd !== void 0).length} tasks, ${config.checks.filter((c) => c.prompt !== void 0).length} prompts, ${config.conditions.length} conditions`
767
323
  )
768
324
  );
769
325
  } else {
770
- console.error(chalk4.red("\u2717 checks.yaml has errors:"));
326
+ console.error(chalk2.red("\u2717 checks.yaml has errors:"));
771
327
  for (const err of result.errors) {
772
- console.error(` ${chalk4.yellow(err.path)}: ${err.message}`);
328
+ console.error(` ${chalk2.yellow(err.path)}: ${err.message}`);
773
329
  }
774
330
  process.exit(1);
775
331
  }
776
332
  }
777
333
 
778
334
  // src/commands/update.ts
779
- import { existsSync as existsSync7, readFileSync as readFileSync6, writeFileSync as writeFileSync5, mkdirSync as mkdirSync4 } from "fs";
780
- import chalk5 from "chalk";
781
- import ora3 from "ora";
782
- import { parseHoldpointYaml as parseHoldpointYaml4 } from "@holdpoint/yaml-core";
783
- import { buildConfigJson as buildConfigJson2, buildEngine as buildEngine2 } from "@holdpoint/engine-copilot";
784
- import { buildEngineJson as buildClaudeEngineJson2 } from "@holdpoint/engine-claude";
335
+ import { existsSync as existsSync3, readFileSync as readFileSync3, writeFileSync as writeFileSync2, mkdirSync as mkdirSync2 } from "fs";
336
+ import chalk3 from "chalk";
337
+ import ora2 from "ora";
338
+ import { parseHoldpointYaml as parseHoldpointYaml3 } from "@holdpoint/yaml-core";
339
+ import { buildConfigJson, buildEngine } from "@holdpoint/engine-copilot";
340
+ import { buildEngineJson as buildClaudeEngineJson } from "@holdpoint/engine-claude";
785
341
  import {
786
- buildCheckScript as buildCursorCheckScript2,
787
- buildEngine as buildCursorEngine2,
788
- buildHooksJson as buildCursorHooksJson2
342
+ buildCheckScript as buildCursorCheckScript,
343
+ buildEngine as buildCursorEngine,
344
+ buildHooksJson as buildCursorHooksJson
789
345
  } from "@holdpoint/engine-cursor";
790
346
  import {
791
- buildConfigToml as buildCodexConfigToml2,
792
- buildHooksJson as buildCodexHooksJson2,
793
- buildCheckScript as buildCodexCheckScript2
347
+ buildConfigToml as buildCodexConfigToml,
348
+ buildHooksJson as buildCodexHooksJson,
349
+ buildCheckScript as buildCodexCheckScript
794
350
  } from "@holdpoint/engine-codex";
795
- var MINIMAL_PREREQUISITES2 = `# Holdpoint prerequisites
351
+ var MINIMAL_PREREQUISITES = `# Holdpoint prerequisites
796
352
 
797
353
  Holdpoint installed repo-local engine integrations for one or more AI coding agents. Before relying on them locally, review these setup notes:
798
354
 
@@ -803,66 +359,66 @@ Holdpoint installed repo-local engine integrations for one or more AI coding age
803
359
 
804
360
  Docs: https://holdpoint.dev/docs
805
361
  `;
806
- var MINIMAL_HOLDPOINT_REFERENCE2 = `# Holdpoint reference
362
+ var MINIMAL_HOLDPOINT_REFERENCE = `# Holdpoint reference
807
363
 
808
364
  Read \`MASTER_PROMPT.md\` first for the mandatory workflow, then use this file for deeper project-specific Holdpoint notes.
809
365
  `;
810
366
  async function updateCommand() {
811
- if (!existsSync7("checks.yaml")) {
812
- console.error(chalk5.red("No checks.yaml found. Run `holdpoint init` first."));
367
+ if (!existsSync3("checks.yaml")) {
368
+ console.error(chalk3.red("No checks.yaml found. Run `holdpoint init` first."));
813
369
  process.exit(1);
814
370
  }
815
- const spinner = ora3("Updating Holdpoint engine files\u2026").start();
816
- const config = parseHoldpointYaml4(readFileSync6("checks.yaml", "utf8"));
371
+ const spinner = ora2("Updating Holdpoint engine files\u2026").start();
372
+ const config = parseHoldpointYaml3(readFileSync3("checks.yaml", "utf8"));
817
373
  const detected = detectInstalledAgents();
818
374
  const agents = detected.length > 0 ? detected : ["copilot", "claude", "cursor", "codex"];
819
375
  const generatedDir = ".github/holdpoint/generated";
820
- mkdirSync4(generatedDir, { recursive: true });
821
- writeFileSync5(`${generatedDir}/checks.immutable.json`, buildConfigJson2(config), "utf8");
376
+ mkdirSync2(generatedDir, { recursive: true });
377
+ writeFileSync2(`${generatedDir}/checks.immutable.json`, buildConfigJson(config), "utf8");
822
378
  if (agents.includes("copilot")) {
823
379
  const extDir = ".github/extensions/holdpoint";
824
- mkdirSync4(extDir, { recursive: true });
825
- writeFileSync5(`${extDir}/extension.mjs`, buildEngine2(config), "utf8");
380
+ mkdirSync2(extDir, { recursive: true });
381
+ writeFileSync2(`${extDir}/extension.mjs`, buildEngine(config), "utf8");
826
382
  spliceBreadcrumb(".github/copilot-instructions.md");
827
383
  }
828
384
  if (agents.includes("claude")) {
829
- mkdirSync4(".claude", { recursive: true });
385
+ mkdirSync2(".claude", { recursive: true });
830
386
  const settingsPath = ".claude/settings.json";
831
387
  let existing = {};
832
- if (existsSync7(settingsPath)) {
388
+ if (existsSync3(settingsPath)) {
833
389
  try {
834
- existing = JSON.parse(readFileSync6(settingsPath, "utf8"));
390
+ existing = JSON.parse(readFileSync3(settingsPath, "utf8"));
835
391
  } catch {
836
392
  }
837
393
  }
838
- const hooks = JSON.parse(buildClaudeEngineJson2(config));
839
- writeFileSync5(
394
+ const hooks = JSON.parse(buildClaudeEngineJson(config));
395
+ writeFileSync2(
840
396
  settingsPath,
841
397
  JSON.stringify(mergeClaudeSettings(existing, hooks), null, 2) + "\n"
842
398
  );
843
399
  spliceBreadcrumb("CLAUDE.md");
844
400
  }
845
401
  if (agents.includes("cursor")) {
846
- mkdirSync4(".cursor", { recursive: true });
402
+ mkdirSync2(".cursor", { recursive: true });
847
403
  const cursorHooksPath = ".cursor/hooks.json";
848
404
  let existingHooks = {};
849
- if (existsSync7(cursorHooksPath)) {
405
+ if (existsSync3(cursorHooksPath)) {
850
406
  try {
851
- existingHooks = JSON.parse(readFileSync6(cursorHooksPath, "utf8"));
407
+ existingHooks = JSON.parse(readFileSync3(cursorHooksPath, "utf8"));
852
408
  } catch {
853
409
  }
854
410
  }
855
- const cursorHooks = JSON.parse(buildCursorHooksJson2(config));
856
- writeFileSync5(
411
+ const cursorHooks = JSON.parse(buildCursorHooksJson(config));
412
+ writeFileSync2(
857
413
  cursorHooksPath,
858
414
  JSON.stringify(mergeCursorHooks(existingHooks, cursorHooks), null, 2) + "\n",
859
415
  "utf8"
860
416
  );
861
- writeFileSync5(".cursor/holdpoint-hook.mjs", buildCursorCheckScript2(), "utf8");
862
- const cursorRules = buildCursorEngine2(config);
417
+ writeFileSync2(".cursor/holdpoint-hook.mjs", buildCursorCheckScript(), "utf8");
418
+ const cursorRules = buildCursorEngine(config);
863
419
  const cursorPath = ".cursorrules";
864
- if (existsSync7(cursorPath)) {
865
- const content = readFileSync6(cursorPath, "utf8");
420
+ if (existsSync3(cursorPath)) {
421
+ const content = readFileSync3(cursorPath, "utf8");
866
422
  const start = content.indexOf("# \u2500\u2500\u2500 Holdpoint Rules");
867
423
  const end = content.indexOf("# \u2500\u2500\u2500 End Holdpoint Rules \u2500\u2500\u2500");
868
424
  if (start !== -1 && end !== -1) {
@@ -873,28 +429,28 @@ async function updateCommand() {
873
429
 
874
430
  ` : "") + cursorRules + (suffix ? `
875
431
  ${suffix}` : "");
876
- writeFileSync5(cursorPath, updated);
432
+ writeFileSync2(cursorPath, updated);
877
433
  } else {
878
- writeFileSync5(cursorPath, `${content.trimEnd()}
434
+ writeFileSync2(cursorPath, `${content.trimEnd()}
879
435
 
880
436
  ${cursorRules}`);
881
437
  }
882
438
  } else {
883
- writeFileSync5(cursorPath, cursorRules);
439
+ writeFileSync2(cursorPath, cursorRules);
884
440
  }
885
441
  spliceBreadcrumb(".cursor/rules/holdpoint.md");
886
442
  }
887
443
  if (agents.includes("codex")) {
888
- mkdirSync4(".codex", { recursive: true });
889
- writeFileSync5(".codex/hooks.json", buildCodexHooksJson2(config), "utf8");
890
- writeFileSync5(".codex/holdpoint-check.mjs", buildCodexCheckScript2(config), "utf8");
444
+ mkdirSync2(".codex", { recursive: true });
445
+ writeFileSync2(".codex/hooks.json", buildCodexHooksJson(config), "utf8");
446
+ writeFileSync2(".codex/holdpoint-check.mjs", buildCodexCheckScript(config), "utf8");
891
447
  const configTomlPath = ".codex/config.toml";
892
- if (!existsSync7(configTomlPath)) {
893
- writeFileSync5(configTomlPath, buildCodexConfigToml2(), "utf8");
448
+ if (!existsSync3(configTomlPath)) {
449
+ writeFileSync2(configTomlPath, buildCodexConfigToml(), "utf8");
894
450
  } else {
895
- const existing = readFileSync6(configTomlPath, "utf8");
451
+ const existing = readFileSync3(configTomlPath, "utf8");
896
452
  if (!existing.includes("[features]")) {
897
- writeFileSync5(configTomlPath, existing.trimEnd() + "\n\n" + buildCodexConfigToml2(), "utf8");
453
+ writeFileSync2(configTomlPath, existing.trimEnd() + "\n\n" + buildCodexConfigToml(), "utf8");
898
454
  }
899
455
  }
900
456
  spliceBreadcrumb("AGENTS.md");
@@ -902,22 +458,22 @@ ${cursorRules}`);
902
458
  const wroteReference = ensureBundledFile(
903
459
  "HOLDPOINT_REFERENCE.md",
904
460
  "HOLDPOINT_REFERENCE.md",
905
- MINIMAL_HOLDPOINT_REFERENCE2
461
+ MINIMAL_HOLDPOINT_REFERENCE
906
462
  );
907
463
  const wrotePrerequisites = ensureBundledFile(
908
464
  "HOLDPOINT_PREREQUISITES.md",
909
465
  "HOLDPOINT_PREREQUISITES.md",
910
- MINIMAL_PREREQUISITES2
466
+ MINIMAL_PREREQUISITES
911
467
  );
912
- spinner.succeed(chalk5.green("Engine files updated from current checks.yaml"));
468
+ spinner.succeed(chalk3.green("Engine files updated from current checks.yaml"));
913
469
  if (wroteReference) {
914
470
  console.log(
915
- chalk5.cyan("Created HOLDPOINT_REFERENCE.md with the full Holdpoint workflow reference.")
471
+ chalk3.cyan("Created HOLDPOINT_REFERENCE.md with the full Holdpoint workflow reference.")
916
472
  );
917
473
  }
918
474
  if (wrotePrerequisites) {
919
475
  console.log(
920
- chalk5.cyan(
476
+ chalk3.cyan(
921
477
  "Created HOLDPOINT_PREREQUISITES.md with Copilot experimental-mode and other agent setup notes."
922
478
  )
923
479
  );
@@ -925,7 +481,7 @@ ${cursorRules}`);
925
481
  }
926
482
 
927
483
  // src/commands/build.ts
928
- import chalk6 from "chalk";
484
+ import chalk4 from "chalk";
929
485
 
930
486
  // src/lib/ensure-daemon.ts
931
487
  import { spawn } from "child_process";
@@ -960,27 +516,27 @@ async function ensureDaemon(timeoutMs = 5e3) {
960
516
  }
961
517
 
962
518
  // src/lib/open-browser.ts
963
- import { execSync as execSync4 } from "child_process";
519
+ import { execSync as execSync2 } from "child_process";
964
520
  function openBrowser(url) {
965
521
  const openCmd = process.platform === "darwin" ? "open" : process.platform === "win32" ? "start" : "xdg-open";
966
522
  try {
967
- execSync4(`${openCmd} ${JSON.stringify(url)}`, { stdio: "ignore" });
523
+ execSync2(`${openCmd} ${JSON.stringify(url)}`, { stdio: "ignore" });
968
524
  } catch {
969
525
  }
970
526
  }
971
527
 
972
528
  // src/lib/project.ts
973
- import { existsSync as existsSync8 } from "fs";
974
- import { dirname as dirname4, join as join4 } from "path";
529
+ import { existsSync as existsSync4 } from "fs";
530
+ import { dirname, join as join2 } from "path";
975
531
  import { identifyProject as identifyProject2 } from "@holdpoint/live-daemon";
976
532
  function findChecksYaml(startDir) {
977
533
  let current = startDir;
978
534
  for (; ; ) {
979
- const candidate = join4(current, "checks.yaml");
980
- if (existsSync8(candidate)) {
535
+ const candidate = join2(current, "checks.yaml");
536
+ if (existsSync4(candidate)) {
981
537
  return candidate;
982
538
  }
983
- const parent = dirname4(current);
539
+ const parent = dirname(current);
984
540
  if (parent === current) {
985
541
  return null;
986
542
  }
@@ -990,7 +546,7 @@ function findChecksYaml(startDir) {
990
546
  function tryResolveCurrentProject() {
991
547
  const checksYaml = findChecksYaml(process.cwd());
992
548
  if (checksYaml) {
993
- return identifyProject2(dirname4(checksYaml));
549
+ return identifyProject2(dirname(checksYaml));
994
550
  }
995
551
  try {
996
552
  return identifyProject2(process.cwd());
@@ -1016,50 +572,50 @@ async function buildCommand() {
1016
572
  appendProjectAuthParams(url, tryResolveCurrentProject());
1017
573
  openBrowser(url.toString());
1018
574
  console.log(
1019
- chalk6.green(
575
+ chalk4.green(
1020
576
  started ? "\u2713 Started Holdpoint Live and opened the builder" : "\u2713 Opened Holdpoint builder"
1021
577
  )
1022
578
  );
1023
- console.log(` url: ${chalk6.cyan(url.toString())}`);
579
+ console.log(` url: ${chalk4.cyan(url.toString())}`);
1024
580
  }
1025
581
 
1026
582
  // src/commands/evolve.ts
1027
- import { existsSync as existsSync11, readFileSync as readFileSync8, writeFileSync as writeFileSync6 } from "fs";
1028
- import { execSync as execSync7 } from "child_process";
1029
- import chalk7 from "chalk";
1030
- import ora4 from "ora";
1031
- import { parseHoldpointYaml as parseHoldpointYaml5, generateYaml } from "@holdpoint/yaml-core";
583
+ import { existsSync as existsSync7, readFileSync as readFileSync5, writeFileSync as writeFileSync3 } from "fs";
584
+ import { execSync as execSync5 } from "child_process";
585
+ import chalk5 from "chalk";
586
+ import ora3 from "ora";
587
+ import { parseHoldpointYaml as parseHoldpointYaml4, generateYaml } from "@holdpoint/yaml-core";
1032
588
 
1033
589
  // src/evolve/scanner.ts
1034
- import { existsSync as existsSync9, readFileSync as readFileSync7, readdirSync } from "fs";
1035
- import { join as join5 } from "path";
1036
- import { execSync as execSync5 } from "child_process";
590
+ import { existsSync as existsSync5, readFileSync as readFileSync4, readdirSync } from "fs";
591
+ import { join as join3 } from "path";
592
+ import { execSync as execSync3 } from "child_process";
1037
593
  function tryReadJson(path) {
1038
594
  try {
1039
- return JSON.parse(readFileSync7(path, "utf8"));
595
+ return JSON.parse(readFileSync4(path, "utf8"));
1040
596
  } catch {
1041
597
  return null;
1042
598
  }
1043
599
  }
1044
600
  function tryReadText(path) {
1045
601
  try {
1046
- return readFileSync7(path, "utf8");
602
+ return readFileSync4(path, "utf8");
1047
603
  } catch {
1048
604
  return "";
1049
605
  }
1050
606
  }
1051
607
  function scanProject(cwd = process.cwd()) {
1052
- const exists = (p) => existsSync9(join5(cwd, p));
608
+ const exists = (p) => existsSync5(join3(cwd, p));
1053
609
  const packageManager = exists("pnpm-lock.yaml") ? "pnpm" : exists("yarn.lock") ? "yarn" : exists("bun.lockb") ? "bun" : "npm";
1054
- const pkg = tryReadJson(join5(cwd, "package.json"));
610
+ const pkg = tryReadJson(join3(cwd, "package.json"));
1055
611
  const scripts = pkg?.scripts ?? {};
1056
612
  const deps = /* @__PURE__ */ new Set([
1057
613
  ...Object.keys(pkg?.dependencies ?? {}),
1058
614
  ...Object.keys(pkg?.devDependencies ?? {})
1059
615
  ]);
1060
- const pyprojectText = tryReadText(join5(cwd, "pyproject.toml"));
1061
- const requirementsText = tryReadText(join5(cwd, "requirements.txt"));
1062
- const pipfileText = tryReadText(join5(cwd, "Pipfile"));
616
+ const pyprojectText = tryReadText(join3(cwd, "pyproject.toml"));
617
+ const requirementsText = tryReadText(join3(cwd, "requirements.txt"));
618
+ const pipfileText = tryReadText(join3(cwd, "Pipfile"));
1063
619
  const allPyText = pyprojectText + requirementsText + pipfileText;
1064
620
  const hasPytest = exists("pytest.ini") || exists("setup.cfg") || allPyText.includes("pytest") || allPyText.includes("[tool.pytest");
1065
621
  const hasRuff = allPyText.includes("ruff") || deps.has("ruff");
@@ -1104,6 +660,10 @@ function scanProject(cwd = process.cwd()) {
1104
660
  hasOpenApi: exists("openapi.yaml") || exists("openapi.yml") || exists("openapi.json") || exists("api/openapi.yaml"),
1105
661
  // CI
1106
662
  hasGithubActions: exists(".github/workflows"),
663
+ // Release tooling — gates the `changelog-update` suggest template,
664
+ // since projects using changesets get release notes from .changeset
665
+ // files automatically and don't want a manual-CHANGELOG-entry check.
666
+ hasChangesets: exists(".changeset/config.json"),
1107
667
  packageManager,
1108
668
  scripts,
1109
669
  deps
@@ -1111,9 +671,9 @@ function scanProject(cwd = process.cwd()) {
1111
671
  }
1112
672
 
1113
673
  // src/evolve/dead-checker.ts
1114
- import { execSync as execSync6 } from "child_process";
1115
- import { readdirSync as readdirSync2, existsSync as existsSync10 } from "fs";
1116
- import { join as join6 } from "path";
674
+ import { execSync as execSync4 } from "child_process";
675
+ import { readdirSync as readdirSync2, existsSync as existsSync6 } from "fs";
676
+ import { join as join4 } from "path";
1117
677
  var NAMED_SCOPES = /* @__PURE__ */ new Set([
1118
678
  "frontend",
1119
679
  "backend",
@@ -1158,7 +718,7 @@ function walkDir(dir, root, depth, maxDepth) {
1158
718
  const results = [];
1159
719
  for (const entry of entries) {
1160
720
  if (WALK_IGNORED.has(entry) || entry.startsWith(".")) continue;
1161
- const full = join6(dir, entry);
721
+ const full = join4(dir, entry);
1162
722
  const rel = full.slice(root.length + 1);
1163
723
  results.push(rel);
1164
724
  const children = walkDir(full, root, depth + 1, maxDepth);
@@ -1168,7 +728,7 @@ function walkDir(dir, root, depth, maxDepth) {
1168
728
  }
1169
729
  function getRepoFiles(cwd) {
1170
730
  try {
1171
- const out = execSync6("git ls-files", {
731
+ const out = execSync4("git ls-files", {
1172
732
  cwd,
1173
733
  encoding: "utf8",
1174
734
  stdio: ["pipe", "pipe", "ignore"]
@@ -1205,7 +765,7 @@ function detectStaleChecks(config, repoFiles) {
1205
765
  if (matches.length === 0) {
1206
766
  const label = patternAlias ? `Pattern '${patternAlias}' (= '${regexStr}')` : `Regex '${regexStr}'`;
1207
767
  const suggestedConditionPath = extractPathFromRegex(regexStr);
1208
- const pathGone = !suggestedConditionPath || !existsSync10(join6(process.cwd(), suggestedConditionPath));
768
+ const pathGone = !suggestedConditionPath || !existsSync6(join4(process.cwd(), suggestedConditionPath));
1209
769
  if (pathGone) {
1210
770
  stale.push({
1211
771
  check,
@@ -1240,7 +800,11 @@ function getTemplates(profile) {
1240
800
  id: "changelog-update",
1241
801
  label: "Add a CHANGELOG.md entry for this session",
1242
802
  prompt: "Before committing, add an entry to CHANGELOG.md describing what was done. Use Keep a Changelog format \u2014 add under ## [Unreleased] (create the file and that section if absent). Group entries as Added, Changed, Fixed, or Removed. Be concise but specific. The entry text will serve as the commit message.",
1243
- trigger: () => true
803
+ // Don't propose this for changesets-using projects — those get
804
+ // release notes from .changeset/*.md files automatically and the
805
+ // sibling `changelog-changeset` check is what they should use
806
+ // instead. Proposing both would be confusing and contradictory.
807
+ trigger: (p) => !p.hasChangesets
1244
808
  },
1245
809
  {
1246
810
  id: "readme-sync",
@@ -1409,20 +973,20 @@ function withHeader(header, newYaml) {
1409
973
  return header + "\n\n" + newYaml;
1410
974
  }
1411
975
  async function evolveCommand(options) {
1412
- if (!existsSync11("checks.yaml")) {
1413
- console.error(chalk7.red("No checks.yaml found. Run `holdpoint init` first."));
976
+ if (!existsSync7("checks.yaml")) {
977
+ console.error(chalk5.red("No checks.yaml found. Run `holdpoint init` first."));
1414
978
  process.exit(1);
1415
979
  }
1416
- const spinner = ora4("Scanning project profile\u2026").start();
980
+ const spinner = ora3("Scanning project profile\u2026").start();
1417
981
  const cwd = process.cwd();
1418
982
  const profile = scanProject(cwd);
1419
983
  const repoFiles = getRepoFiles(cwd);
1420
- const yamlContent = readFileSync8("checks.yaml", "utf8");
984
+ const yamlContent = readFileSync5("checks.yaml", "utf8");
1421
985
  let config;
1422
986
  try {
1423
- config = parseHoldpointYaml5(yamlContent);
987
+ config = parseHoldpointYaml4(yamlContent);
1424
988
  } catch (err) {
1425
- spinner.fail(chalk7.red("Invalid checks.yaml:") + " " + err.message);
989
+ spinner.fail(chalk5.red("Invalid checks.yaml:") + " " + err.message);
1426
990
  process.exit(1);
1427
991
  }
1428
992
  spinner.stop();
@@ -1431,7 +995,7 @@ async function evolveCommand(options) {
1431
995
  const allTemplates = getTemplates(profile);
1432
996
  const proposals = allTemplates.filter((t) => t.trigger(profile) && !existingIds.has(t.id));
1433
997
  const staleChecks = detectStaleChecks(config, repoFiles);
1434
- console.log(chalk7.bold("\n\u{1F4CB} Project profile:"));
998
+ console.log(chalk5.bold("\n\u{1F4CB} Project profile:"));
1435
999
  const traits = [
1436
1000
  ["TypeScript", profile.hasTypeScript, "tsconfig.json"],
1437
1001
  ["ESLint", profile.hasEslint, "eslint.config.*"],
@@ -1457,44 +1021,44 @@ async function evolveCommand(options) {
1457
1021
  ];
1458
1022
  const detected = traits.filter(([, yes]) => yes);
1459
1023
  if (detected.length === 0) {
1460
- console.log(chalk7.dim(" (empty project \u2014 only universal checks apply)"));
1024
+ console.log(chalk5.dim(" (empty project \u2014 only universal checks apply)"));
1461
1025
  } else {
1462
1026
  for (const [name, , hint] of detected) {
1463
- console.log(` ${chalk7.green("\u2713")} ${name.padEnd(18)} ${chalk7.dim(hint)}`);
1027
+ console.log(` ${chalk5.green("\u2713")} ${name.padEnd(18)} ${chalk5.dim(hint)}`);
1464
1028
  }
1465
1029
  }
1466
1030
  if (staleChecks.length > 0) {
1467
- console.log(chalk7.bold(`
1031
+ console.log(chalk5.bold(`
1468
1032
  \u26A0\uFE0F Stale checks (${staleChecks.length}):`));
1469
1033
  for (const { check, reason, suggestedConditionPath } of staleChecks) {
1470
- const fix = suggestedConditionPath ? chalk7.dim(` \u2192 will wrap with conditionId: file_exists: ${suggestedConditionPath}`) : chalk7.dim(" \u2192 no path inferred; review manually");
1471
- console.log(` ${chalk7.yellow("\u25CC")} ${chalk7.bold(check.id)} ${chalk7.dim(reason)}${fix}`);
1034
+ const fix = suggestedConditionPath ? chalk5.dim(` \u2192 will wrap with conditionId: file_exists: ${suggestedConditionPath}`) : chalk5.dim(" \u2192 no path inferred; review manually");
1035
+ console.log(` ${chalk5.yellow("\u25CC")} ${chalk5.bold(check.id)} ${chalk5.dim(reason)}${fix}`);
1472
1036
  }
1473
1037
  }
1474
1038
  if (proposals.length === 0 && staleChecks.length === 0) {
1475
- console.log(chalk7.green("\n\u2713 checks.yaml is fully in sync with the project profile."));
1039
+ console.log(chalk5.green("\n\u2713 checks.yaml is fully in sync with the project profile."));
1476
1040
  return;
1477
1041
  }
1478
1042
  if (proposals.length > 0) {
1479
- console.log(chalk7.bold(`
1043
+ console.log(chalk5.bold(`
1480
1044
  \u{1F4A1} Proposed additions (${proposals.length}):`));
1481
1045
  for (const t of proposals) {
1482
- const scope = t.when ? chalk7.cyan(` when: ${t.when}`) : "";
1483
- const type = t.cmd ? chalk7.dim("cmd") : chalk7.dim("prompt");
1484
- const preview = t.cmd ? chalk7.dim(` ${t.cmd.slice(0, 80)}${t.cmd.length > 80 ? "\u2026" : ""}`) : chalk7.dim(` ${(t.prompt ?? "").slice(0, 80)}${(t.prompt?.length ?? 0) > 80 ? "\u2026" : ""}`);
1485
- console.log(` ${chalk7.green("+")} ${chalk7.bold(t.id.padEnd(24))} [${type}]${scope}`);
1046
+ const scope = t.when ? chalk5.cyan(` when: ${t.when}`) : "";
1047
+ const type = t.cmd ? chalk5.dim("cmd") : chalk5.dim("prompt");
1048
+ const preview = t.cmd ? chalk5.dim(` ${t.cmd.slice(0, 80)}${t.cmd.length > 80 ? "\u2026" : ""}`) : chalk5.dim(` ${(t.prompt ?? "").slice(0, 80)}${(t.prompt?.length ?? 0) > 80 ? "\u2026" : ""}`);
1049
+ console.log(` ${chalk5.green("+")} ${chalk5.bold(t.id.padEnd(24))} [${type}]${scope}`);
1486
1050
  console.log(` ${preview}`);
1487
1051
  }
1488
1052
  }
1489
1053
  if (!options.apply) {
1490
1054
  console.log(
1491
- chalk7.red(`
1055
+ chalk5.red(`
1492
1056
  \u2717 checks.yaml is out of sync with the project profile.`) + `
1493
- Run ${chalk7.bold("npx @holdpoint/cli suggest --apply")} to apply these changes.`
1057
+ Run ${chalk5.bold("npx @holdpoint/cli suggest --apply")} to apply these changes.`
1494
1058
  );
1495
1059
  process.exit(1);
1496
1060
  }
1497
- const applySpinner = ora4("Applying changes to checks.yaml\u2026").start();
1061
+ const applySpinner = ora3("Applying changes to checks.yaml\u2026").start();
1498
1062
  const newConditions = [...config.conditions];
1499
1063
  for (const t of proposals) {
1500
1064
  if (t.condition && !existingConditionIds.has(t.condition.id)) {
@@ -1531,33 +1095,33 @@ async function evolveCommand(options) {
1531
1095
  };
1532
1096
  const header = extractHeader(yamlContent);
1533
1097
  const newYaml = withHeader(header, generateYaml(updatedConfig));
1534
- writeFileSync6("checks.yaml", newYaml, "utf8");
1098
+ writeFileSync3("checks.yaml", newYaml, "utf8");
1535
1099
  applySpinner.text = "Running holdpoint update\u2026";
1536
1100
  try {
1537
- execSync7("npx @holdpoint/cli update", { stdio: "pipe" });
1101
+ execSync5("npx @holdpoint/cli update", { stdio: "pipe" });
1538
1102
  } catch {
1539
1103
  applySpinner.warn(
1540
- chalk7.yellow("checks.yaml updated, but `holdpoint update` failed \u2014 run it manually.")
1104
+ chalk5.yellow("checks.yaml updated, but `holdpoint update` failed \u2014 run it manually.")
1541
1105
  );
1542
1106
  printAppliedSummary(proposals.length, staleChecks.length);
1543
1107
  return;
1544
1108
  }
1545
- applySpinner.succeed(chalk7.green("checks.yaml updated and engine files regenerated."));
1109
+ applySpinner.succeed(chalk5.green("checks.yaml updated and engine files regenerated."));
1546
1110
  printAppliedSummary(proposals.length, staleChecks.length);
1547
1111
  }
1548
1112
  function printAppliedSummary(added, wrapped) {
1549
1113
  const parts = [];
1550
- if (added > 0) parts.push(chalk7.green(`${added} check${added === 1 ? "" : "s"} added`));
1114
+ if (added > 0) parts.push(chalk5.green(`${added} check${added === 1 ? "" : "s"} added`));
1551
1115
  if (wrapped > 0)
1552
- parts.push(chalk7.yellow(`${wrapped} stale check${wrapped === 1 ? "" : "s"} wrapped`));
1116
+ parts.push(chalk5.yellow(`${wrapped} stale check${wrapped === 1 ? "" : "s"} wrapped`));
1553
1117
  if (parts.length > 0) console.log(" " + parts.join(" \xB7 "));
1554
1118
  console.log(
1555
- chalk7.dim("\n Review checks.yaml, then commit: ") + chalk7.yellow("git add checks.yaml && git commit -m 'chore: update holdpoint checks'")
1119
+ chalk5.dim("\n Review checks.yaml, then commit: ") + chalk5.yellow("git add checks.yaml && git commit -m 'chore: update holdpoint checks'")
1556
1120
  );
1557
1121
  }
1558
1122
 
1559
1123
  // src/commands/live.ts
1560
- import chalk8 from "chalk";
1124
+ import chalk6 from "chalk";
1561
1125
  async function liveCommand(options = {}) {
1562
1126
  const { info, started } = await ensureDaemon();
1563
1127
  const baseUrl = new URL(`/__holdpoint/live-auth`, `http://127.0.0.1:${info.port}`);
@@ -1571,15 +1135,15 @@ async function liveCommand(options = {}) {
1571
1135
  }
1572
1136
  openBrowser(baseUrl.toString());
1573
1137
  console.log(
1574
- chalk8.green(
1138
+ chalk6.green(
1575
1139
  started ? "\u2713 Started Holdpoint Live and opened the browser" : "\u2713 Opened Holdpoint Live"
1576
1140
  )
1577
1141
  );
1578
- console.log(` url: ${chalk8.cyan(baseUrl.toString())}`);
1142
+ console.log(` url: ${chalk6.cyan(baseUrl.toString())}`);
1579
1143
  }
1580
1144
 
1581
1145
  // src/commands/daemon.ts
1582
- import chalk9 from "chalk";
1146
+ import chalk7 from "chalk";
1583
1147
  import {
1584
1148
  DaemonAlreadyRunningError,
1585
1149
  isProcessAlive,
@@ -1620,38 +1184,38 @@ async function daemonStartCommand() {
1620
1184
  const { info, started } = await ensureDaemon();
1621
1185
  const sessionCount = await fetchSessionCount(info);
1622
1186
  const headline = started ? "Started Holdpoint Live daemon" : "Reused existing Holdpoint Live daemon";
1623
- console.log(chalk9.green(`\u2713 ${headline}`));
1624
- console.log(` pid: ${chalk9.cyan(String(info.pid))}`);
1625
- console.log(` port: ${chalk9.cyan(String(info.port))}`);
1626
- console.log(` uptime: ${chalk9.cyan(formatUptime(info))}`);
1187
+ console.log(chalk7.green(`\u2713 ${headline}`));
1188
+ console.log(` pid: ${chalk7.cyan(String(info.pid))}`);
1189
+ console.log(` port: ${chalk7.cyan(String(info.port))}`);
1190
+ console.log(` uptime: ${chalk7.cyan(formatUptime(info))}`);
1627
1191
  if (sessionCount !== null) {
1628
- console.log(` sessions: ${chalk9.cyan(String(sessionCount))}`);
1192
+ console.log(` sessions: ${chalk7.cyan(String(sessionCount))}`);
1629
1193
  }
1630
1194
  }
1631
1195
  async function daemonStatusCommand() {
1632
1196
  const lock = await readHealthyDaemonLock2();
1633
1197
  if (!lock) {
1634
- console.log(chalk9.yellow("Holdpoint Live daemon is not running."));
1198
+ console.log(chalk7.yellow("Holdpoint Live daemon is not running."));
1635
1199
  return;
1636
1200
  }
1637
1201
  const sessionCount = await fetchSessionCount(lock);
1638
- console.log(chalk9.green("\u2713 Holdpoint Live daemon is running"));
1639
- console.log(` pid: ${chalk9.cyan(String(lock.pid))}`);
1640
- console.log(` port: ${chalk9.cyan(String(lock.port))}`);
1641
- console.log(` uptime: ${chalk9.cyan(formatUptime(lock))}`);
1202
+ console.log(chalk7.green("\u2713 Holdpoint Live daemon is running"));
1203
+ console.log(` pid: ${chalk7.cyan(String(lock.pid))}`);
1204
+ console.log(` port: ${chalk7.cyan(String(lock.port))}`);
1205
+ console.log(` uptime: ${chalk7.cyan(formatUptime(lock))}`);
1642
1206
  if (sessionCount !== null) {
1643
- console.log(` sessions: ${chalk9.cyan(String(sessionCount))}`);
1207
+ console.log(` sessions: ${chalk7.cyan(String(sessionCount))}`);
1644
1208
  }
1645
1209
  }
1646
1210
  async function daemonStopCommand() {
1647
1211
  const lock = readDaemonLock();
1648
1212
  if (!lock) {
1649
- console.log(chalk9.yellow("Holdpoint Live daemon is not running."));
1213
+ console.log(chalk7.yellow("Holdpoint Live daemon is not running."));
1650
1214
  return;
1651
1215
  }
1652
1216
  if (!isProcessAlive(lock.pid)) {
1653
1217
  removeDaemonLock(void 0, lock.token);
1654
- console.log(chalk9.yellow("Removed stale Holdpoint Live lockfile."));
1218
+ console.log(chalk7.yellow("Removed stale Holdpoint Live lockfile."));
1655
1219
  return;
1656
1220
  }
1657
1221
  process.kill(lock.pid, "SIGTERM");
@@ -1659,7 +1223,7 @@ async function daemonStopCommand() {
1659
1223
  while (Date.now() < deadline) {
1660
1224
  if (!isProcessAlive(lock.pid)) {
1661
1225
  removeDaemonLock(void 0, lock.token);
1662
- console.log(chalk9.green(`\u2713 Stopped Holdpoint Live daemon (${lock.pid})`));
1226
+ console.log(chalk7.green(`\u2713 Stopped Holdpoint Live daemon (${lock.pid})`));
1663
1227
  return;
1664
1228
  }
1665
1229
  await sleep2(100);
@@ -1667,7 +1231,7 @@ async function daemonStopCommand() {
1667
1231
  process.kill(lock.pid, "SIGKILL");
1668
1232
  await sleep2(100);
1669
1233
  removeDaemonLock(void 0, lock.token);
1670
- console.log(chalk9.green(`\u2713 Force-stopped Holdpoint Live daemon (${lock.pid})`));
1234
+ console.log(chalk7.green(`\u2713 Force-stopped Holdpoint Live daemon (${lock.pid})`));
1671
1235
  }
1672
1236
  async function daemonServeCommand(options) {
1673
1237
  try {
@@ -1685,16 +1249,16 @@ async function daemonServeCommand(options) {
1685
1249
  }
1686
1250
 
1687
1251
  // src/commands/engines.ts
1688
- import chalk10 from "chalk";
1252
+ import chalk8 from "chalk";
1689
1253
 
1690
1254
  // src/engines.ts
1691
- import { existsSync as existsSync12, readFileSync as readFileSync9 } from "fs";
1692
- import { dirname as dirname5, join as join7, resolve, sep } from "path";
1255
+ import { existsSync as existsSync8, readFileSync as readFileSync6 } from "fs";
1256
+ import { dirname as dirname2, join as join5, resolve, sep } from "path";
1693
1257
  import { createRequire } from "module";
1694
- import { fileURLToPath as fileURLToPath3, pathToFileURL } from "url";
1258
+ import { fileURLToPath, pathToFileURL } from "url";
1695
1259
  import { parseEventV1 } from "@holdpoint/live-protocol";
1696
1260
  var require2 = createRequire(import.meta.url);
1697
- var CLI_SRC_DIR = dirname5(fileURLToPath3(import.meta.url));
1261
+ var CLI_SRC_DIR = dirname2(fileURLToPath(import.meta.url));
1698
1262
  var MONOREPO_ROOT = resolve(CLI_SRC_DIR, "../../..");
1699
1263
  var BUILTIN_LIVE_ENGINE_PACKAGES = [
1700
1264
  "@holdpoint/engine-claude",
@@ -1702,19 +1266,19 @@ var BUILTIN_LIVE_ENGINE_PACKAGES = [
1702
1266
  "@holdpoint/engine-cursor"
1703
1267
  ];
1704
1268
  var HOLDPOINT_ENGINE_KEYWORD = "holdpoint-engine";
1705
- function isObject3(value) {
1269
+ function isObject(value) {
1706
1270
  return value != null && typeof value === "object" && !Array.isArray(value);
1707
1271
  }
1708
1272
  function isExternalLiveEnginePackageName(packageName) {
1709
1273
  return /^holdpoint-engine-[a-z0-9-]+$/.test(packageName) || /^@[a-z0-9_.-]+\/holdpoint-engine-[a-z0-9-]+$/.test(packageName);
1710
1274
  }
1711
1275
  function readJsonFile(path) {
1712
- if (!existsSync12(path)) {
1276
+ if (!existsSync8(path)) {
1713
1277
  return null;
1714
1278
  }
1715
1279
  try {
1716
- const parsed = JSON.parse(readFileSync9(path, "utf8"));
1717
- return isObject3(parsed) ? parsed : null;
1280
+ const parsed = JSON.parse(readFileSync6(path, "utf8"));
1281
+ return isObject(parsed) ? parsed : null;
1718
1282
  } catch {
1719
1283
  return null;
1720
1284
  }
@@ -1722,10 +1286,10 @@ function readJsonFile(path) {
1722
1286
  function findNearestPackageRoot(startDir) {
1723
1287
  let current = resolve(startDir);
1724
1288
  while (true) {
1725
- if (existsSync12(join7(current, "package.json"))) {
1289
+ if (existsSync8(join5(current, "package.json"))) {
1726
1290
  return current;
1727
1291
  }
1728
- const parent = dirname5(current);
1292
+ const parent = dirname2(current);
1729
1293
  if (parent === current) {
1730
1294
  return resolve(startDir);
1731
1295
  }
@@ -1733,12 +1297,12 @@ function findNearestPackageRoot(startDir) {
1733
1297
  }
1734
1298
  }
1735
1299
  function findPackageRootFromFile(path) {
1736
- let current = dirname5(path);
1300
+ let current = dirname2(path);
1737
1301
  while (true) {
1738
- if (existsSync12(join7(current, "package.json"))) {
1302
+ if (existsSync8(join5(current, "package.json"))) {
1739
1303
  return current;
1740
1304
  }
1741
- const parent = dirname5(current);
1305
+ const parent = dirname2(current);
1742
1306
  if (parent === current) {
1743
1307
  return null;
1744
1308
  }
@@ -1746,14 +1310,14 @@ function findPackageRootFromFile(path) {
1746
1310
  }
1747
1311
  }
1748
1312
  function getDependencyEnginePackageNames(projectRoot) {
1749
- const packageJson = readJsonFile(join7(projectRoot, "package.json"));
1313
+ const packageJson = readJsonFile(join5(projectRoot, "package.json"));
1750
1314
  if (!packageJson) {
1751
1315
  return [];
1752
1316
  }
1753
1317
  const packageNames = /* @__PURE__ */ new Set();
1754
1318
  for (const field of ["dependencies", "devDependencies", "optionalDependencies"]) {
1755
1319
  const deps = packageJson[field];
1756
- if (!isObject3(deps)) {
1320
+ if (!isObject(deps)) {
1757
1321
  continue;
1758
1322
  }
1759
1323
  for (const packageName of Object.keys(deps)) {
@@ -1781,13 +1345,13 @@ function resolvePackageRoot(packageName, projectRoot) {
1781
1345
  const packageJsonPath = require2.resolve(`${packageName}/package.json`, {
1782
1346
  paths: [projectRoot, process.cwd()]
1783
1347
  });
1784
- return dirname5(packageJsonPath);
1348
+ return dirname2(packageJsonPath);
1785
1349
  } catch {
1786
1350
  if (packageName.startsWith("@holdpoint/")) {
1787
1351
  const scopedName = packageName.split("/")[1];
1788
1352
  if (scopedName) {
1789
1353
  const packageDir = resolve(MONOREPO_ROOT, "packages", scopedName);
1790
- if (existsSync12(join7(packageDir, "package.json"))) {
1354
+ if (existsSync8(join5(packageDir, "package.json"))) {
1791
1355
  return packageDir;
1792
1356
  }
1793
1357
  }
@@ -1799,7 +1363,7 @@ function formatImportError(error) {
1799
1363
  return error instanceof Error && error.message ? error.message : String(error);
1800
1364
  }
1801
1365
  function parseManifest(value) {
1802
- if (!isObject3(value)) {
1366
+ if (!isObject(value)) {
1803
1367
  return null;
1804
1368
  }
1805
1369
  if (value.manifestVersion !== 1) {
@@ -1818,7 +1382,7 @@ function parseManifest(value) {
1818
1382
  };
1819
1383
  }
1820
1384
  function parseLiveCapabilities(value) {
1821
- if (!isObject3(value)) {
1385
+ if (!isObject(value)) {
1822
1386
  return null;
1823
1387
  }
1824
1388
  const capabilities = {};
@@ -1841,7 +1405,7 @@ function parseLiveCapabilities(value) {
1841
1405
  return capabilities;
1842
1406
  }
1843
1407
  function parseLiveAdapter(value, manifest) {
1844
- if (!isObject3(value)) {
1408
+ if (!isObject(value)) {
1845
1409
  return null;
1846
1410
  }
1847
1411
  if (typeof value.id !== "string" || typeof value.displayName !== "string") {
@@ -1889,10 +1453,10 @@ function resolvePackageAssetPath(packageRoot, relativePath) {
1889
1453
  packageRoot,
1890
1454
  relativePath.replace(/^\.\/dist\//, "./src/").replace(/\.js$/, ".ts")
1891
1455
  );
1892
- if (packageRoot.startsWith(resolve(MONOREPO_ROOT, "packages") + sep) && existsSync12(sourceFallback)) {
1456
+ if (packageRoot.startsWith(resolve(MONOREPO_ROOT, "packages") + sep) && existsSync8(sourceFallback)) {
1893
1457
  return sourceFallback;
1894
1458
  }
1895
- if (existsSync12(declaredPath)) {
1459
+ if (existsSync8(declaredPath)) {
1896
1460
  return declaredPath;
1897
1461
  }
1898
1462
  return sourceFallback;
@@ -1907,7 +1471,7 @@ async function resolveCandidate(packageName, source, projectRoot) {
1907
1471
  reason: "package could not be resolved from this project"
1908
1472
  };
1909
1473
  }
1910
- const packageJson = readJsonFile(join7(packageRoot, "package.json"));
1474
+ const packageJson = readJsonFile(join5(packageRoot, "package.json"));
1911
1475
  if (!packageJson) {
1912
1476
  return {
1913
1477
  packageName,
@@ -1925,7 +1489,7 @@ async function resolveCandidate(packageName, source, projectRoot) {
1925
1489
  reason: `missing \`${HOLDPOINT_ENGINE_KEYWORD}\` keyword`
1926
1490
  };
1927
1491
  }
1928
- const metadata = isObject3(packageJson.holdpoint) ? packageJson.holdpoint : void 0;
1492
+ const metadata = isObject(packageJson.holdpoint) ? packageJson.holdpoint : void 0;
1929
1493
  if (!metadata?.manifest) {
1930
1494
  return {
1931
1495
  packageName,
@@ -1944,7 +1508,7 @@ async function resolveCandidate(packageName, source, projectRoot) {
1944
1508
  }
1945
1509
  const manifestPath = resolvePackageAssetPath(packageRoot, metadata.manifest);
1946
1510
  const adapterPath = resolvePackageAssetPath(packageRoot, metadata.adapter);
1947
- if (!existsSync12(manifestPath)) {
1511
+ if (!existsSync8(manifestPath)) {
1948
1512
  return {
1949
1513
  packageName,
1950
1514
  source,
@@ -1952,7 +1516,7 @@ async function resolveCandidate(packageName, source, projectRoot) {
1952
1516
  reason: "manifest file does not exist"
1953
1517
  };
1954
1518
  }
1955
- if (!existsSync12(adapterPath)) {
1519
+ if (!existsSync8(adapterPath)) {
1956
1520
  return {
1957
1521
  packageName,
1958
1522
  source,
@@ -2064,22 +1628,22 @@ async function enginesCommand(options = {}) {
2064
1628
  for (const engine of engines) {
2065
1629
  if (engine.status === "loaded" && engine.manifest) {
2066
1630
  console.log(
2067
- `${chalk10.green("loaded")} ${chalk10.cyan(engine.manifest.id)} (${engine.manifest.displayName}) from ${chalk10.yellow(engine.packageName)} [${engine.source}]`
1631
+ `${chalk8.green("loaded")} ${chalk8.cyan(engine.manifest.id)} (${engine.manifest.displayName}) from ${chalk8.yellow(engine.packageName)} [${engine.source}]`
2068
1632
  );
2069
1633
  continue;
2070
1634
  }
2071
1635
  console.log(
2072
- `${chalk10.yellow("ignored")} ${chalk10.yellow(engine.packageName)} [${engine.source}] \u2014 ${engine.reason ?? "unknown reason"}`
1636
+ `${chalk8.yellow("ignored")} ${chalk8.yellow(engine.packageName)} [${engine.source}] \u2014 ${engine.reason ?? "unknown reason"}`
2073
1637
  );
2074
1638
  }
2075
1639
  }
2076
1640
 
2077
1641
  // src/commands/event.ts
2078
- import { readFileSync as readFileSync10 } from "fs";
1642
+ import { readFileSync as readFileSync7 } from "fs";
2079
1643
  import { parseEventV1 as parseEventV12, parseEventsBatch } from "@holdpoint/live-protocol";
2080
1644
  import { BridgeClient as BridgeClient2 } from "@holdpoint/sdk";
2081
1645
  function readStdin() {
2082
- return readFileSync10(0, "utf8");
1646
+ return readFileSync7(0, "utf8");
2083
1647
  }
2084
1648
  async function eventCommand(options) {
2085
1649
  const stdin = readStdin().trim();
@@ -2129,10 +1693,10 @@ async function eventCommand(options) {
2129
1693
  }
2130
1694
 
2131
1695
  // src/commands/changeset.ts
2132
- import { execSync as execSync8 } from "child_process";
2133
- import { existsSync as existsSync13, readdirSync as readdirSync3, readFileSync as readFileSync11, statSync } from "fs";
2134
- import { join as join8, relative } from "path";
2135
- import chalk11 from "chalk";
1696
+ import { execSync as execSync6 } from "child_process";
1697
+ import { existsSync as existsSync9, readdirSync as readdirSync3, readFileSync as readFileSync8, statSync } from "fs";
1698
+ import { join as join6, relative } from "path";
1699
+ import chalk9 from "chalk";
2136
1700
  var IGNORED_DIRS = /* @__PURE__ */ new Set([
2137
1701
  ".git",
2138
1702
  ".next",
@@ -2144,7 +1708,7 @@ var IGNORED_DIRS = /* @__PURE__ */ new Set([
2144
1708
  ]);
2145
1709
  function runGit(command) {
2146
1710
  try {
2147
- const out = execSync8(command, {
1711
+ const out = execSync6(command, {
2148
1712
  encoding: "utf8",
2149
1713
  stdio: ["pipe", "pipe", "ignore"]
2150
1714
  });
@@ -2155,7 +1719,7 @@ function runGit(command) {
2155
1719
  }
2156
1720
  function readJson(path) {
2157
1721
  try {
2158
- return JSON.parse(readFileSync11(path, "utf8"));
1722
+ return JSON.parse(readFileSync8(path, "utf8"));
2159
1723
  } catch {
2160
1724
  return null;
2161
1725
  }
@@ -2198,22 +1762,22 @@ function getChangedFiles(options) {
2198
1762
  return runGit("git diff --name-only HEAD~1 HEAD");
2199
1763
  }
2200
1764
  function parsePnpmWorkspacePatterns() {
2201
- if (!existsSync13("pnpm-workspace.yaml")) return [];
2202
- const lines = readFileSync11("pnpm-workspace.yaml", "utf8").split(/\r?\n/);
1765
+ if (!existsSync9("pnpm-workspace.yaml")) return [];
1766
+ const lines = readFileSync8("pnpm-workspace.yaml", "utf8").split(/\r?\n/);
2203
1767
  return lines.map((line) => line.match(/^\s*-\s*['"]?([^'"]+)['"]?\s*$/)?.[1]).filter((line) => Boolean(line)).filter((line) => !line.startsWith("!"));
2204
1768
  }
2205
1769
  function expandOneLevelWorkspacePattern(pattern) {
2206
1770
  const normalized = normalizePath(pattern).replace(/\/package\.json$/, "");
2207
1771
  if (!normalized.includes("*")) {
2208
- return existsSync13(join8(normalized, "package.json")) ? [normalized] : [];
1772
+ return existsSync9(join6(normalized, "package.json")) ? [normalized] : [];
2209
1773
  }
2210
1774
  const starIndex = normalized.indexOf("*");
2211
1775
  const parent = normalized.slice(0, starIndex).replace(/\/$/, "");
2212
1776
  const suffix = normalized.slice(starIndex + 1).replace(/^\//, "");
2213
- if (!parent || suffix.includes("*") || !existsSync13(parent)) {
1777
+ if (!parent || suffix.includes("*") || !existsSync9(parent)) {
2214
1778
  return [];
2215
1779
  }
2216
- return readdirSync3(parent).map((entry) => join8(parent, entry, suffix)).map(normalizePath).filter((candidate) => existsSync13(join8(candidate, "package.json")));
1780
+ return readdirSync3(parent).map((entry) => join6(parent, entry, suffix)).map(normalizePath).filter((candidate) => existsSync9(join6(candidate, "package.json")));
2217
1781
  }
2218
1782
  function walkPackageRoots(start, roots) {
2219
1783
  let entries;
@@ -2222,13 +1786,13 @@ function walkPackageRoots(start, roots) {
2222
1786
  } catch {
2223
1787
  return;
2224
1788
  }
2225
- if (start !== "." && existsSync13(join8(start, "package.json"))) {
1789
+ if (start !== "." && existsSync9(join6(start, "package.json"))) {
2226
1790
  roots.push(normalizePath(start));
2227
1791
  return;
2228
1792
  }
2229
1793
  for (const entry of entries) {
2230
1794
  if (IGNORED_DIRS.has(entry)) continue;
2231
- const candidate = join8(start, entry);
1795
+ const candidate = join6(start, entry);
2232
1796
  let stats;
2233
1797
  try {
2234
1798
  stats = statSync(candidate);
@@ -2241,7 +1805,7 @@ function walkPackageRoots(start, roots) {
2241
1805
  }
2242
1806
  }
2243
1807
  function readPackageRoot(path) {
2244
- const pkg = readJson(join8(path, "package.json"));
1808
+ const pkg = readJson(join6(path, "package.json"));
2245
1809
  if (!pkg) return null;
2246
1810
  return {
2247
1811
  path: normalizePath(path === "." ? "" : path),
@@ -2269,7 +1833,7 @@ function discoverPackageRoots(includePatterns = []) {
2269
1833
  }
2270
1834
  const discovered = [];
2271
1835
  walkPackageRoots(".", discovered);
2272
- const roots = discovered.length > 0 ? discovered : existsSync13("package.json") ? ["."] : [];
1836
+ const roots = discovered.length > 0 ? discovered : existsSync9("package.json") ? ["."] : [];
2273
1837
  return uniquePackageRoots(
2274
1838
  roots.map(readPackageRoot).filter((pkg) => Boolean(pkg)).filter((pkg) => !pkg.private)
2275
1839
  );
@@ -2330,30 +1894,30 @@ function analyzeChangesetRequirement(input) {
2330
1894
  async function requireChangesetCommand(options) {
2331
1895
  const changedFiles = getChangedFiles(options);
2332
1896
  if (changedFiles.length === 0) {
2333
- console.log(chalk11.green("\u2713 No changed files detected \u2014 no changeset required."));
1897
+ console.log(chalk9.green("\u2713 No changed files detected \u2014 no changeset required."));
2334
1898
  return;
2335
1899
  }
2336
1900
  const packageRoots = discoverPackageRoots(options.include ?? []);
2337
1901
  if (packageRoots.length === 0) {
2338
- console.log(chalk11.green("\u2713 No package roots detected \u2014 no changeset required."));
1902
+ console.log(chalk9.green("\u2713 No package roots detected \u2014 no changeset required."));
2339
1903
  return;
2340
1904
  }
2341
- const hasChangesetSetup = existsSync13(".changeset");
1905
+ const hasChangesetSetup = existsSync9(".changeset");
2342
1906
  const { requiredFiles, hasChangeset } = analyzeChangesetRequirement({
2343
1907
  changedFiles,
2344
1908
  packageRoots
2345
1909
  });
2346
1910
  if (requiredFiles.length === 0) {
2347
- console.log(chalk11.green("\u2713 No release-affecting package files changed."));
1911
+ console.log(chalk9.green("\u2713 No release-affecting package files changed."));
2348
1912
  return;
2349
1913
  }
2350
1914
  if (hasChangeset) {
2351
- console.log(chalk11.green("\u2713 Package changes include a changeset."));
1915
+ console.log(chalk9.green("\u2713 Package changes include a changeset."));
2352
1916
  return;
2353
1917
  }
2354
- console.error(chalk11.red("\u2717 Package changes need a changeset."));
1918
+ console.error(chalk9.red("\u2717 Package changes need a changeset."));
2355
1919
  console.error("");
2356
- console.error(chalk11.bold("Changed package files:"));
1920
+ console.error(chalk9.bold("Changed package files:"));
2357
1921
  for (const item of requiredFiles.slice(0, 12)) {
2358
1922
  console.error(` - ${item.file} (${item.packageName})`);
2359
1923
  }
@@ -2365,12 +1929,12 @@ async function requireChangesetCommand(options) {
2365
1929
  console.error(
2366
1930
  "No .changeset directory was found. Create one and add a changeset before finishing:"
2367
1931
  );
2368
- console.error(chalk11.yellow(" mkdir -p .changeset"));
1932
+ console.error(chalk9.yellow(" mkdir -p .changeset"));
2369
1933
  } else {
2370
1934
  console.error("Add a changeset before finishing:");
2371
1935
  }
2372
- console.error(chalk11.yellow(" pnpm changeset"));
2373
- console.error(chalk11.dim(" or add a .changeset/<name>.md file manually"));
1936
+ console.error(chalk9.yellow(" pnpm changeset"));
1937
+ console.error(chalk9.dim(" or add a .changeset/<name>.md file manually"));
2374
1938
  process.exit(1);
2375
1939
  }
2376
1940