halfcycle 0.3.39 → 0.4.0

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.
@@ -19,8 +19,8 @@
19
19
  * would reorder keys. The schema parse decides only whether it is written at all.
20
20
  *
21
21
  * INV-001: Node builtins, the global `fetch`, `@halfcycle/events` and
22
- * `@halfcycle/core` only (the latter for the one shared shell-quoting helper,
23
- * `quoteForShell`, used by `refetchCommand` below).
22
+ * `@halfcycle/core` only (the latter for the shared command prefix and shell-quoting
23
+ * helper, used by `refetchCommand` below).
24
24
  * SHIPS TO CLIENT DISK inside the CLI: every string here is a client-facing
25
25
  * sentence, and no comment sits beside an object member.
26
26
  */
@@ -32,11 +32,13 @@ import { type RecordPairIo } from './write.js';
32
32
  * relative to the repository). `exists`: a file this CLI will not replace is in the
33
33
  * way; nothing was written. `withheld` and `refused`: the service's answer, with its
34
34
  * own sentence. `unavailable`: the record could not be obtained, and `why` says so.
35
+ * `headline` is the latest close's headline as the .md states it, in plain words.
35
36
  */
36
37
  export type ObtainOutcome = {
37
38
  kind: 'written';
38
39
  jsonPath: string;
39
40
  mdPath: string;
41
+ headline: string;
40
42
  } | {
41
43
  kind: 'exists';
42
44
  path: string;
@@ -1 +1 @@
1
- {"version":3,"file":"obtain.d.ts","sourceRoot":"","sources":["../../src/build-record/obtain.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAGH,OAAO,EAGL,KAAK,wBAAwB,EAC9B,MAAM,mBAAmB,CAAC;AAE3B,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AACvD,OAAO,EAAwC,KAAK,YAAY,EAAE,MAAM,YAAY,CAAC;AAErF;;;;;GAKG;AACH,MAAM,MAAM,aAAa,GACrB;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GACrD;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GAChC;IAAE,IAAI,EAAE,UAAU,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,GACrC;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,MAAM,EAAE,wBAAwB,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,GACtE;IAAE,IAAI,EAAE,aAAa,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,CAAC;AAEzC;;;;;GAKG;AACH,eAAO,MAAM,iBAAiB;;;;;;CAM+B,CAAC;AAE9D,8EAA8E;AAC9E,eAAO,MAAM,eAAe,EAAE,SAAS,wBAAwB,EAA2C,CAAC;AAE3G,oFAAoF;AACpF,wBAAgB,cAAc,CAAC,UAAU,EAAE,cAAc,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAKhF;AASD;;;;;GAKG;AACH,wBAAsB,iBAAiB,CACrC,UAAU,EAAE,cAAc,EAC1B,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,MAAM,EACb,EAAE,GAAE,YAAkC,GACrC,OAAO,CAAC,aAAa,CAAC,CA0ExB;AAED;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAEpD"}
1
+ {"version":3,"file":"obtain.d.ts","sourceRoot":"","sources":["../../src/build-record/obtain.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAGH,OAAO,EAGL,KAAK,wBAAwB,EAC9B,MAAM,mBAAmB,CAAC;AAE3B,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AACvD,OAAO,EAAwC,KAAK,YAAY,EAAE,MAAM,YAAY,CAAC;AAGrF;;;;;;GAMG;AACH,MAAM,MAAM,aAAa,GACrB;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,GACvE;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GAChC;IAAE,IAAI,EAAE,UAAU,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,GACrC;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,MAAM,EAAE,wBAAwB,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,GACtE;IAAE,IAAI,EAAE,aAAa,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,CAAC;AAEzC;;;;;GAKG;AACH,eAAO,MAAM,iBAAiB;;;;;;CAM+B,CAAC;AAE9D,8EAA8E;AAC9E,eAAO,MAAM,eAAe,EAAE,SAAS,wBAAwB,EAA2C,CAAC;AAE3G,oFAAoF;AACpF,wBAAgB,cAAc,CAAC,UAAU,EAAE,cAAc,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAKhF;AASD;;;;;GAKG;AACH,wBAAsB,iBAAiB,CACrC,UAAU,EAAE,cAAc,EAC1B,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,MAAM,EACb,EAAE,GAAE,YAAkC,GACrC,OAAO,CAAC,aAAa,CAAC,CA2ExB;AAED;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAEpD"}
@@ -38,6 +38,14 @@ import type { BuildRecordV2 } from '@halfcycle/events';
38
38
  * so a slot is never silently empty.
39
39
  */
40
40
  export declare function printRecordText(text: string): string;
41
+ /**
42
+ * The latest close's headline in plain words — the same title and sentence the .md
43
+ * opens that close with, without the bold. The CLI prints it after a close (cp4f
44
+ * T-20, acceptance walk F9), so the terminal and the record say one thing about
45
+ * how the phase closed: the terminal said "closed as clean" beside a record that
46
+ * said "this is not a clean result".
47
+ */
48
+ export declare function latestCloseHeadline(record: BuildRecordV2): string;
41
49
  /**
42
50
  * Render a parsed v2 Build Record as markdown.
43
51
  *
@@ -1 +1 @@
1
- {"version":3,"file":"render-v2.d.ts","sourceRoot":"","sources":["../../src/build-record/render-v2.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AA4JvD;;;;;;;;GAQG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAepD;AAuUD;;;;;GAKG;AACH,wBAAgB,2BAA2B,CAAC,MAAM,EAAE,aAAa,GAAG,MAAM,CA2DzE"}
1
+ {"version":3,"file":"render-v2.d.ts","sourceRoot":"","sources":["../../src/build-record/render-v2.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AA4JvD;;;;;;;;GAQG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAepD;AAsLD;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,aAAa,GAAG,MAAM,CAOjE;AA+JD;;;;;GAKG;AACH,wBAAgB,2BAA2B,CAAC,MAAM,EAAE,aAAa,GAAG,MAAM,CA2DzE"}
@@ -124,7 +124,9 @@ export declare function requirementMessage(verb: CliVerb, flag: string): string;
124
124
  *
125
125
  * RENDERED, not typed out. A remedy string built by hand is the defect this file
126
126
  * was opened for; one built here cannot omit a flag, because the flags come from
127
- * the same table the binary refuses on.
127
+ * the same table the binary refuses on. It starts with `HALFCYCLE_COMMAND`
128
+ * (`packages/core`), the one home of how a printed command reaches this CLI: a
129
+ * bare `halfcycle` is not on a client's PATH (cp4f T-20).
128
130
  *
129
131
  * `phase` is quoted for the shell (`quoteForShell`, `packages/core`) — a phase
130
132
  * name has no closed vocabulary and a space or `#` in it would otherwise paste
@@ -134,4 +136,25 @@ export declare function requirementMessage(verb: CliVerb, flag: string): string;
134
136
  * argument parser, which is a wider change than this function and is not done.
135
137
  */
136
138
  export declare function runnableInvocation(verb: CliVerb, phase: string): string;
139
+ /**
140
+ * The `--reason` each verb's override remedy prints. Angle-bracketed so it reads
141
+ * as something to replace, and so `placeholderRefusal` refuses it if it is pasted
142
+ * unchanged — an ellipsis was pasted and recorded as the reason (cp4f T-20).
143
+ */
144
+ export declare const REASON_PLACEHOLDER: {
145
+ readonly 'open-phase': "<why you are opening it anyway>";
146
+ readonly 'close-phase': "<why you are closing over this>";
147
+ };
148
+ /**
149
+ * The override remedy a refused close prints: the close the developer asked for,
150
+ * every value they typed quoted for the shell, plus `--override` and a reason
151
+ * still to be written.
152
+ */
153
+ export declare function closeOverrideInvocation(phase: string, verdict: string, actor: string): string;
154
+ /**
155
+ * The refusal for the first recorded flag whose value is still a placeholder, or
156
+ * `null`. Checked before anything is sent: the value would otherwise be recorded
157
+ * as written, in a record that is never rewritten.
158
+ */
159
+ export declare function placeholderRefusal(verb: CliVerb, tokens: readonly string[]): string | null;
137
160
  //# sourceMappingURL=cli-contract.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"cli-contract.d.ts","sourceRoot":"","sources":["../src/cli-contract.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8DG;AAIH,iFAAiF;AACjF,eAAO,MAAM,SAAS,+FAAgG,CAAC;AAEvH,MAAM,MAAM,OAAO,GAAG,CAAC,OAAO,SAAS,CAAC,CAAC,MAAM,CAAC,CAAC;AAEjD,sDAAsD;AACtD,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB;;;;OAIG;IACH,QAAQ,CAAC,OAAO,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACrC,2DAA2D;IAC3D,QAAQ,CAAC,UAAU,EAAE,OAAO,CAAC;IAC7B,+EAA+E;IAC/E,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;CACjC;AAED,iDAAiD;AACjD,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,UAAU,EAAE,OAAO,CAAC;IAC7B,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;CACjC;AAED,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC;IACvB,QAAQ,CAAC,QAAQ,EAAE,SAAS,YAAY,EAAE,CAAC;IAC3C,QAAQ,CAAC,WAAW,EAAE,SAAS,eAAe,EAAE,CAAC;CAClD;AAsFD,6EAA6E;AAC7E,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,YAAY,GAAG,SAAS,CAElE;AAED,iDAAiD;AACjD,MAAM,WAAW,SAAS;IACxB,kEAAkE;IAClE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,oCAAoC;IACpC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CAC1B;AAmBD;;;;;;;;;;;GAWG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,MAAM,EAAE,GAAG,SAAS,EAAE,CAyBxF;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAMtE;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAMvE"}
1
+ {"version":3,"file":"cli-contract.d.ts","sourceRoot":"","sources":["../src/cli-contract.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8DG;AAIH,iFAAiF;AACjF,eAAO,MAAM,SAAS,+FAAgG,CAAC;AAEvH,MAAM,MAAM,OAAO,GAAG,CAAC,OAAO,SAAS,CAAC,CAAC,MAAM,CAAC,CAAC;AAEjD,sDAAsD;AACtD,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB;;;;OAIG;IACH,QAAQ,CAAC,OAAO,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACrC,2DAA2D;IAC3D,QAAQ,CAAC,UAAU,EAAE,OAAO,CAAC;IAC7B,+EAA+E;IAC/E,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;CACjC;AAED,iDAAiD;AACjD,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,UAAU,EAAE,OAAO,CAAC;IAC7B,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;CACjC;AAED,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC;IACvB,QAAQ,CAAC,QAAQ,EAAE,SAAS,YAAY,EAAE,CAAC;IAC3C,QAAQ,CAAC,WAAW,EAAE,SAAS,eAAe,EAAE,CAAC;CAClD;AAsFD,6EAA6E;AAC7E,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,YAAY,GAAG,SAAS,CAElE;AAED,iDAAiD;AACjD,MAAM,WAAW,SAAS;IACxB,kEAAkE;IAClE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,oCAAoC;IACpC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CAC1B;AAmBD;;;;;;;;;;;GAWG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,MAAM,EAAE,GAAG,SAAS,EAAE,CAyBxF;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAMtE;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAMvE;AASD;;;;GAIG;AACH,eAAO,MAAM,kBAAkB;;;CAGrB,CAAC;AAEX;;;;GAIG;AACH,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAK7F;AAmBD;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,OAAO,EAAE,MAAM,EAAE,SAAS,MAAM,EAAE,GAAG,MAAM,GAAG,IAAI,CAa1F"}
package/dist/index.js CHANGED
@@ -213,6 +213,7 @@ var MCP_SERVER_KEY = "halfcycle";
213
213
  function quoteForShell(value) {
214
214
  return `'${value.replace(/'/g, `'\\''`)}'`;
215
215
  }
216
+ var HALFCYCLE_COMMAND = "npx halfcycle@latest";
216
217
 
217
218
  // ../events/dist/result.js
218
219
  import { z as z2 } from "zod";
@@ -3547,31 +3548,44 @@ function renderCouldNotRun(entries, p) {
3547
3548
  }
3548
3549
  return lines.join("\n");
3549
3550
  }
3550
- function headline(observed, attested, checks) {
3551
+ function headlineText(observed, attested, checks) {
3551
3552
  const met = attested.decision === "done-when-met";
3552
3553
  const problemsReported = attested.walk !== null && attested.walk.verdict === "defects";
3553
3554
  switch (observed.crossed.kind) {
3554
3555
  case "none":
3555
3556
  if (!met) {
3556
- return "**Closed by override.** Halfcycle saw nothing outstanding, and the person closing the phase chose to override rather than state its finish conditions were met.";
3557
+ return ["Closed by override.", "Halfcycle saw nothing outstanding, and the person closing the phase chose to override rather than state its finish conditions were met."];
3557
3558
  }
3558
3559
  if (problemsReported) {
3559
- return "**Closed with problems reported.** Halfcycle saw nothing outstanding, and the person closing the phase reported problems found in its acceptance walk.";
3560
+ return ["Closed with problems reported.", "Halfcycle saw nothing outstanding, and the person closing the phase reported problems found in its acceptance walk."];
3560
3561
  }
3561
- return checks === "not-observed" ? "**Closed with nothing outstanding, but no check on record.** Halfcycle saw nothing outstanding, and the person closing the phase stated its finish conditions were met, but Halfcycle has no record of any check running during this phase, so this is not a clean result." : "**Closed cleanly.** Halfcycle saw nothing outstanding, and the person closing the phase stated its finish conditions were met.";
3562
+ return checks === "not-observed" ? ["Closed with nothing outstanding, but no check on record.", "Halfcycle saw nothing outstanding, and the person closing the phase stated its finish conditions were met, but Halfcycle has no record of any check running during this phase, so this is not a clean result."] : ["Closed cleanly.", "Halfcycle saw nothing outstanding, and the person closing the phase stated its finish conditions were met."];
3562
3563
  case "unfinished-steps":
3563
3564
  case "clean-over-findings":
3564
3565
  if (!met) {
3565
- return "**Closed over outstanding work.** Halfcycle saw work still outstanding, and the person closing the phase chose to override it.";
3566
+ return ["Closed over outstanding work.", "Halfcycle saw work still outstanding, and the person closing the phase chose to override it."];
3566
3567
  }
3567
- return problemsReported ? "**Closed over outstanding work.** Halfcycle saw work still outstanding, and the person closing the phase closed it as it stood, reporting problems found in its acceptance walk." : "**Closed over outstanding work.** Halfcycle saw work still outstanding, and the person closing the phase stated its finish conditions were met anyway.";
3568
+ return problemsReported ? ["Closed over outstanding work.", "Halfcycle saw work still outstanding, and the person closing the phase closed it as it stood, reporting problems found in its acceptance walk."] : ["Closed over outstanding work.", "Halfcycle saw work still outstanding, and the person closing the phase stated its finish conditions were met anyway."];
3568
3569
  case "not-recorded":
3569
3570
  if (!met) {
3570
- return "**Outstanding work not recorded.** Halfcycle has no record of what was outstanding at this close. The person closing the phase chose to override.";
3571
+ return ["Outstanding work not recorded.", "Halfcycle has no record of what was outstanding at this close. The person closing the phase chose to override."];
3571
3572
  }
3572
- return problemsReported ? "**Outstanding work not recorded.** Halfcycle has no record of what was outstanding at this close. The person closing the phase reported problems found in its acceptance walk." : "**Outstanding work not recorded.** Halfcycle has no record of what was outstanding at this close. The person closing the phase stated its finish conditions were met.";
3573
+ return problemsReported ? ["Outstanding work not recorded.", "Halfcycle has no record of what was outstanding at this close. The person closing the phase reported problems found in its acceptance walk."] : ["Outstanding work not recorded.", "Halfcycle has no record of what was outstanding at this close. The person closing the phase stated its finish conditions were met."];
3573
3574
  }
3574
3575
  }
3576
+ function headline(observed, attested, checks) {
3577
+ const [title, sentence] = headlineText(observed, attested, checks);
3578
+ return `**${title}** ${sentence}`;
3579
+ }
3580
+ function latestCloseHeadline(record2) {
3581
+ const attested = new Map(record2.attested.closes.map((c) => [c.sequence, c]));
3582
+ const ordered = [...record2.observed.closes].sort((a, b) => a.sequence - b.sequence);
3583
+ const latest = ordered[ordered.length - 1];
3584
+ const stated = latest === void 0 ? void 0 : attested.get(latest.sequence);
3585
+ if (latest === void 0 || stated === void 0)
3586
+ throw new Error(MISMATCHED_HALVES);
3587
+ return headlineText(latest, stated, record2.observed.checks.state).join(" ");
3588
+ }
3575
3589
  function closedAtText(closedAt, p) {
3576
3590
  const m = /^(\d{4}-\d{2}-\d{2})T(\d{2}:\d{2}:\d{2})(?:\.\d+)?Z$/.exec(closedAt);
3577
3591
  return m === null ? p.text(closedAt) : `${m[1]} ${m[2]} UTC`;
@@ -3624,9 +3638,9 @@ function machines(coverage, checks) {
3624
3638
  function walkLines(walk, p) {
3625
3639
  const label = "> - Acceptance walk (the finished work tried as a user would, by someone other than its builder): ";
3626
3640
  if (walk === null)
3627
- return [`${label}none reported.`];
3641
+ return [`${label}the person closing the phase reported none.`];
3628
3642
  const lines = [
3629
- `${label}${walk.verdict === "clean" ? "reported as finding no problems." : "reported as finding problems."}`
3643
+ `${label}${walk.verdict === "clean" ? "the person closing the phase said it found no problems." : "the person closing the phase said it found problems."}`
3630
3644
  ];
3631
3645
  walk.findings.forEach((finding, i) => {
3632
3646
  lines.push(`> - Finding ${String(i + 1)}: ${p.text(finding)}`);
@@ -3919,7 +3933,8 @@ async function obtainBuildRecord(credential, repoRoot, phase, io = NODE_RECORD_P
3919
3933
  return {
3920
3934
  kind: "written",
3921
3935
  jsonPath: relative3(repoRoot, result.jsonPath),
3922
- mdPath: relative3(repoRoot, result.mdPath)
3936
+ mdPath: relative3(repoRoot, result.mdPath),
3937
+ headline: latestCloseHeadline(answer.record)
3923
3938
  };
3924
3939
  }
3925
3940
  if (isMissingRoute(res.status, raw)) {
@@ -3938,7 +3953,7 @@ async function obtainBuildRecord(credential, repoRoot, phase, io = NODE_RECORD_P
3938
3953
  };
3939
3954
  }
3940
3955
  function refetchCommand(phase) {
3941
- return `npx halfcycle@latest build-record ${quoteForShell(phase)}`;
3956
+ return `${HALFCYCLE_COMMAND} build-record ${quoteForShell(phase)}`;
3942
3957
  }
3943
3958
  export {
3944
3959
  BUILD_RECORD_DIR,