halfcycle 0.11.1 → 0.12.0-rc.1

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.
Files changed (57) hide show
  1. package/.claude-plugin/plugin.json +1 -1
  2. package/bin/bin.bundle.mjs +525 -144
  3. package/bin/pre-tool-use-filter.sh +1 -0
  4. package/dist/banner-facts.d.ts +7 -2
  5. package/dist/banner-facts.d.ts.map +1 -1
  6. package/dist/bin.js +0 -0
  7. package/dist/build-record/obtain.d.ts.map +1 -1
  8. package/dist/change.d.ts.map +1 -1
  9. package/dist/check-criteria.d.ts +1 -1
  10. package/dist/check-criteria.d.ts.map +1 -1
  11. package/dist/check-report.d.ts +1 -1
  12. package/dist/check-report.d.ts.map +1 -1
  13. package/dist/check-tasks.d.ts +10 -1
  14. package/dist/check-tasks.d.ts.map +1 -1
  15. package/dist/cli-contract.d.ts +1 -1
  16. package/dist/cli-contract.d.ts.map +1 -1
  17. package/dist/cli.js +989 -697
  18. package/dist/cli.js.map +3 -3
  19. package/dist/crew.d.ts +37 -0
  20. package/dist/crew.d.ts.map +1 -0
  21. package/dist/draw/index.d.ts +1 -1
  22. package/dist/draw/index.d.ts.map +1 -1
  23. package/dist/engagement-credential.d.ts +4 -0
  24. package/dist/engagement-credential.d.ts.map +1 -1
  25. package/dist/failure-line.d.ts +3 -2
  26. package/dist/failure-line.d.ts.map +1 -1
  27. package/dist/folder-refusals.d.ts.map +1 -1
  28. package/dist/index.js +271 -206
  29. package/dist/index.js.map +3 -3
  30. package/dist/install.d.ts +20 -7
  31. package/dist/install.d.ts.map +1 -1
  32. package/dist/local-allow.d.ts +10 -3
  33. package/dist/local-allow.d.ts.map +1 -1
  34. package/dist/open-phase.d.ts +1 -1
  35. package/dist/open-phase.d.ts.map +1 -1
  36. package/dist/own-version.d.ts +42 -0
  37. package/dist/own-version.d.ts.map +1 -0
  38. package/dist/pace.d.ts +2 -2
  39. package/dist/pace.d.ts.map +1 -1
  40. package/dist/plan.d.ts +1 -1
  41. package/dist/plan.d.ts.map +1 -1
  42. package/dist/pre-create-checks.d.ts.map +1 -1
  43. package/dist/ready.d.ts +1 -1
  44. package/dist/ready.d.ts.map +1 -1
  45. package/dist/regression.d.ts +48 -0
  46. package/dist/regression.d.ts.map +1 -0
  47. package/dist/request-shapes-verb.d.ts +1 -1
  48. package/dist/request-shapes-verb.d.ts.map +1 -1
  49. package/dist/restore.d.ts +1 -1
  50. package/dist/restore.d.ts.map +1 -1
  51. package/dist/review.d.ts +2 -2
  52. package/dist/review.d.ts.map +1 -1
  53. package/dist/route.d.ts +1 -1
  54. package/dist/route.d.ts.map +1 -1
  55. package/dist/size.d.ts.map +1 -1
  56. package/dist/uninstall.d.ts.map +1 -1
  57. package/package.json +11 -11
package/dist/cli.js CHANGED
@@ -285,12 +285,71 @@ function looksLikeSecret(text) {
285
285
  var MCP_REGISTRATION_REL = ".mcp.json";
286
286
  var MCP_SERVER_KEY = "halfcycle";
287
287
 
288
+ // ../core/dist/release-version.js
289
+ var RELEASE_VERSION_PATTERN = /^\d+\.\d+\.\d+$/;
290
+ function isReleaseVersion(value) {
291
+ return typeof value === "string" && RELEASE_VERSION_PATTERN.test(value);
292
+ }
293
+ function significant(part) {
294
+ const trimmed = part.replace(/^0+/, "");
295
+ return trimmed === "" ? "0" : trimmed;
296
+ }
297
+ function compareReleaseVersions(a, b) {
298
+ if (!isReleaseVersion(a) || !isReleaseVersion(b)) {
299
+ throw new TypeError("compareReleaseVersions takes two MAJOR.MINOR.PATCH versions");
300
+ }
301
+ const pa = a.split(".").map(significant);
302
+ const pb = b.split(".").map(significant);
303
+ for (let i = 0; i < 3; i++) {
304
+ const x = pa[i];
305
+ const y = pb[i];
306
+ if (x.length !== y.length)
307
+ return x.length - y.length;
308
+ if (x !== y)
309
+ return x < y ? -1 : 1;
310
+ }
311
+ return 0;
312
+ }
313
+
314
+ // ../core/dist/release-tag.js
315
+ var PRE_RELEASE_VERSION_PATTERN = /^(\d+\.\d+\.\d+)-rc\.\d+$/;
316
+ var PACKAGE_COMMAND = "npx halfcycle";
317
+ var COMMAND_FORMS = /npx halfcycle(?:@latest|(?![@\p{L}\p{N}-]))/gu;
318
+ function isPreReleaseVersion(value) {
319
+ return typeof value === "string" && PRE_RELEASE_VERSION_PATTERN.test(value);
320
+ }
321
+ function releaseOfVersion(value) {
322
+ if (typeof value !== "string")
323
+ return void 0;
324
+ if (isReleaseVersion(value))
325
+ return value;
326
+ const match = PRE_RELEASE_VERSION_PATTERN.exec(value);
327
+ return match === null ? void 0 : match[1];
328
+ }
329
+ function releaseTagOfVersion(version) {
330
+ return isPreReleaseVersion(version) ? "next" : "latest";
331
+ }
332
+ function halfcycleCommand(version) {
333
+ return `${PACKAGE_COMMAND}@${releaseTagOfVersion(version)}`;
334
+ }
335
+ function bareHalfcycleCommand(version) {
336
+ return releaseTagOfVersion(version) === "next" ? `${PACKAGE_COMMAND}@next` : PACKAGE_COMMAND;
337
+ }
338
+ function rewriteHalfcycleCommands(text, tag) {
339
+ if (tag === "latest")
340
+ return text;
341
+ return text.replace(COMMAND_FORMS, `${PACKAGE_COMMAND}@${tag}`);
342
+ }
343
+
288
344
  // ../core/dist/shell-quote.js
289
345
  function quoteForShell(value) {
290
346
  return `'${value.replace(/'/g, `'\\''`)}'`;
291
347
  }
292
- var HALFCYCLE_COMMAND = "npx halfcycle@latest";
293
- var DOCTOR_LINE = "Run `npx halfcycle doctor` to check set up, sign-in, connection, checks and version.";
348
+ var HALFCYCLE_COMMAND = halfcycleCommand();
349
+ function doctorLine(version) {
350
+ return `Run \`${bareHalfcycleCommand(version)} doctor\` to check set up, sign-in, connection, checks and version.`;
351
+ }
352
+ var DOCTOR_LINE = doctorLine();
294
353
 
295
354
  // ../core/dist/folder-trust.js
296
355
  var CLAUDE_STATE_FILENAME = ".claude.json";
@@ -561,32 +620,6 @@ function typedPhaseReference(phase, number) {
561
620
  return phase.includes(":") && number !== null ? String(number) : phase;
562
621
  }
563
622
 
564
- // ../core/dist/release-version.js
565
- var RELEASE_VERSION_PATTERN = /^\d+\.\d+\.\d+$/;
566
- function isReleaseVersion(value) {
567
- return typeof value === "string" && RELEASE_VERSION_PATTERN.test(value);
568
- }
569
- function significant(part) {
570
- const trimmed = part.replace(/^0+/, "");
571
- return trimmed === "" ? "0" : trimmed;
572
- }
573
- function compareReleaseVersions(a, b) {
574
- if (!isReleaseVersion(a) || !isReleaseVersion(b)) {
575
- throw new TypeError("compareReleaseVersions takes two MAJOR.MINOR.PATCH versions");
576
- }
577
- const pa = a.split(".").map(significant);
578
- const pb = b.split(".").map(significant);
579
- for (let i = 0; i < 3; i++) {
580
- const x = pa[i];
581
- const y = pb[i];
582
- if (x.length !== y.length)
583
- return x.length - y.length;
584
- if (x !== y)
585
- return x < y ? -1 : 1;
586
- }
587
- return 0;
588
- }
589
-
590
623
  // ../core/dist/github-address.js
591
624
  var GITHUB_REPOSITORY_ADDRESS = /^https:\/\/github\.com\/[A-Za-z0-9][A-Za-z0-9-]{0,38}\/(?!\.{1,2}$)[A-Za-z0-9._-]{1,100}$/;
592
625
  var HOST = "[Gg][Ii][Tt][Hh][Uu][Bb]\\.[Cc][Oo][Mm]";
@@ -603,8 +636,14 @@ var REMOTE_FORMS = [
603
636
  ];
604
637
 
605
638
  // ../core/dist/sign-in.js
606
- var SIGN_IN_COMMAND = '"npx halfcycle"';
607
- var HOOK_REMEDY = `Run ${SIGN_IN_COMMAND} in the project folder to sign in.`;
639
+ function signInCommand(version) {
640
+ return `"${bareHalfcycleCommand(version)}"`;
641
+ }
642
+ var SIGN_IN_COMMAND = signInCommand();
643
+ function hookRemedy(version) {
644
+ return `Run ${signInCommand(version)} in the project folder to sign in.`;
645
+ }
646
+ var HOOK_REMEDY = hookRemedy();
608
647
 
609
648
  // ../core/dist/task-plan.js
610
649
  import { z as z2 } from "zod";
@@ -1977,7 +2016,7 @@ function codexHookCommand(script, args2) {
1977
2016
  const invocation = `cd "$(git rev-parse --show-toplevel)" && bash ./${script}`;
1978
2017
  return args2 === void 0 ? invocation : `${invocation} ${args2}`;
1979
2018
  }
1980
- var CODEX_WINDOWS_NOT_SET_UP = "Halfcycle checks did not run because Halfcycle is not set up in this folder or any folder above it; run npx halfcycle in the project folder.";
2019
+ var CODEX_WINDOWS_NOT_SET_UP = `Halfcycle checks did not run because Halfcycle is not set up in this folder or any folder above it; run ${bareHalfcycleCommand()} in the project folder.`;
1981
2020
  var CODEX_WINDOWS_NO_GIT_BASH = "Halfcycle checks did not run because Git for Windows was not found; install Git for Windows, then open a new session.";
1982
2021
  function codexHookCommandWindows(script, args2) {
1983
2022
  const tick = (text) => "`" + text + "`";
@@ -2487,15 +2526,14 @@ if (switchedOn) {
2487
2526
  }
2488
2527
 
2489
2528
  // dist/bin.js
2490
- import { existsSync as existsSync16, statSync as statSync2, writeSync as writeSync4 } from "node:fs";
2529
+ import { existsSync as existsSync17, statSync as statSync2, writeSync as writeSync4 } from "node:fs";
2491
2530
 
2492
2531
  // dist/install.js
2493
2532
  import { execFileSync as execFileSync2 } from "node:child_process";
2494
2533
  import { createHash as createHash2 } from "node:crypto";
2495
- import { existsSync as existsSync5, lstatSync, mkdirSync as mkdirSync5, readFileSync as readFileSync6, rmSync as rmSync2, writeFileSync as writeFileSync5 } from "node:fs";
2534
+ import { existsSync as existsSync6, lstatSync, mkdirSync as mkdirSync5, readFileSync as readFileSync7, rmSync as rmSync2, writeFileSync as writeFileSync5 } from "node:fs";
2496
2535
  import { homedir as homedir3 } from "node:os";
2497
- import { dirname as dirname3, isAbsolute as isAbsolute2, join as join7, posix as posix2, relative, resolve } from "node:path";
2498
- import { fileURLToPath } from "node:url";
2536
+ import { dirname as dirname4, isAbsolute as isAbsolute2, join as join8, posix as posix2, relative, resolve } from "node:path";
2499
2537
 
2500
2538
  // ../events/dist/result.js
2501
2539
  import { z as z3 } from "zod";
@@ -4141,8 +4179,8 @@ var TIME_MEASURED_SENTENCE = "Measured by Halfcycle from what happened in your s
4141
4179
  function timePhaseElapsedSentenceOf(elapsedSeconds) {
4142
4180
  return `${timeDurationLabelOf(elapsedSeconds)} since this phase opened.`;
4143
4181
  }
4144
- function timeUsageTotalTokensOf(usage3) {
4145
- return usage3.inputTokens + usage3.outputTokens + usage3.cacheWriteTokens + usage3.cacheReadTokens;
4182
+ function timeUsageTotalTokensOf(usage5) {
4183
+ return usage5.inputTokens + usage5.outputTokens + usage5.cacheWriteTokens + usage5.cacheReadTokens;
4146
4184
  }
4147
4185
  var TIME_NO_RECORD_SENTENCE = "Halfcycle has not recorded any time for this phase yet. It records at the end of each reply in a session on this project.";
4148
4186
  function timeSummaryIsEmpty(summary) {
@@ -4176,25 +4214,25 @@ var TIME_PERMISSION_PROMPT_LABEL = "Waiting for you to allow an action";
4176
4214
  function timePermissionPromptLineOf(seconds) {
4177
4215
  return seconds === 0 ? void 0 : `${TIME_PERMISSION_PROMPT_LABEL} ${timeDurationLabelOf(seconds)}`;
4178
4216
  }
4179
- function timeUsageNotCachedTokensOf(usage3) {
4180
- return usage3.inputTokens + usage3.outputTokens + usage3.cacheWriteTokens;
4217
+ function timeUsageNotCachedTokensOf(usage5) {
4218
+ return usage5.inputTokens + usage5.outputTokens + usage5.cacheWriteTokens;
4181
4219
  }
4182
- function timeUsageLeadSentenceOf(usage3) {
4183
- if (usage3.state === "not-available") {
4220
+ function timeUsageLeadSentenceOf(usage5) {
4221
+ if (usage5.state === "not-available") {
4184
4222
  return "Usage not available: your coding agent keeps no usage record Halfcycle can read.";
4185
4223
  }
4186
- const n = timeCountLabelOf(timeUsageNotCachedTokensOf(usage3));
4187
- if (usage3.state === "partly-recorded") {
4224
+ const n = timeCountLabelOf(timeUsageNotCachedTokensOf(usage5));
4225
+ if (usage5.state === "partly-recorded") {
4188
4226
  return `At least ${n} tokens not read from the cache, as your coding agent recorded them. Some sessions' usage could not be read.`;
4189
4227
  }
4190
4228
  return `${n} tokens not read from the cache, as your coding agent recorded them.`;
4191
4229
  }
4192
- function timeUsageInAllSentenceOf(usage3) {
4193
- if (usage3.state === "not-available")
4230
+ function timeUsageInAllSentenceOf(usage5) {
4231
+ if (usage5.state === "not-available")
4194
4232
  return void 0;
4195
- const all = timeCountLabelOf(timeUsageTotalTokensOf(usage3));
4196
- const cached = timeCountLabelOf(usage3.cacheReadTokens);
4197
- const lead = usage3.state === "partly-recorded" ? `At least ${all}` : all;
4233
+ const all = timeCountLabelOf(timeUsageTotalTokensOf(usage5));
4234
+ const cached = timeCountLabelOf(usage5.cacheReadTokens);
4235
+ const lead = usage5.state === "partly-recorded" ? `At least ${all}` : all;
4198
4236
  return `${lead} in all; ${cached} of them were read back from the cache.`;
4199
4237
  }
4200
4238
  function timeReviewsSentenceOf(reviews) {
@@ -5487,7 +5525,9 @@ var openPhaseRequestSchema = z18.object({
5487
5525
  entry: phaseEntryDecisionSchema,
5488
5526
  startCommit: closeCommitSchema.optional(),
5489
5527
  sortingId: sortingIdSchema.optional(),
5490
- sizeId: projectSizeIdSchema.optional()
5528
+ sizeId: projectSizeIdSchema.optional(),
5529
+ // Present, and true, only when this phase is opened as a regression phase.
5530
+ regression: z18.literal(true).optional()
5491
5531
  }).strict().refine((v) => v.phase !== null || v.startCommit === void 0, {
5492
5532
  message: "is sent only when a phase is opened",
5493
5533
  path: ["startCommit"]
@@ -5497,6 +5537,9 @@ var openPhaseRequestSchema = z18.object({
5497
5537
  }).refine((v) => v.phase !== null || v.sizeId === void 0, {
5498
5538
  message: "is sent only when a phase is opened",
5499
5539
  path: ["sizeId"]
5540
+ }).refine((v) => v.phase !== null || v.regression === void 0, {
5541
+ message: "is sent only when a phase is opened",
5542
+ path: ["regression"]
5500
5543
  });
5501
5544
  var phaseEntryDecisionRecordSchema = z18.object({
5502
5545
  decisionId: z18.string(),
@@ -5799,6 +5842,11 @@ var SMALL_CREW = Object.freeze([
5799
5842
  function isSmallCrewCallsign(callsign) {
5800
5843
  return SMALL_CREW.includes(callsign);
5801
5844
  }
5845
+ function crewOfProjectSize(size) {
5846
+ if (size !== "small")
5847
+ return CREW_ROSTER;
5848
+ return CREW_ROSTER.filter((member) => isSmallCrewCallsign(member.callsign));
5849
+ }
5802
5850
  var crewLayerSchema = methodLayerSchema.extract(["l0", "l1", "l2", "l3", "l4", "l5"]);
5803
5851
  var crewRoleLayersSchema = z19.object({
5804
5852
  leads: z19.array(crewLayerSchema),
@@ -6502,9 +6550,8 @@ function paceClockOf(at, now) {
6502
6550
  }
6503
6551
  return `${day} ${clock}`;
6504
6552
  }
6505
- function paceP1Of(n, minutes) {
6506
- const first = `Halfcycle suggests running ${paceAgentsOf(n)} at once.`;
6507
- return minutes === null ? first : `${first} The work should take about ${timeDurationLabelOf(minutes * 60)}.`;
6553
+ function paceP1Of(n, _minutes) {
6554
+ return `Halfcycle suggests running ${paceAgentsOf(n)} at once.`;
6508
6555
  }
6509
6556
  var PACE_NO_BUILD_PLAN = "Halfcycle has no build plan for this phase yet, so it cannot say how long the work will take.";
6510
6557
  var PACE_P2 = "This is a careful start, so your allowance lasts. It leaves room for your own work.";
@@ -6517,27 +6564,30 @@ function paceP6Of(k) {
6517
6564
  return `It will probably pause ${k === 1 ? "once" : `${String(k)} times`} while your usage window resets.`;
6518
6565
  }
6519
6566
  var PACE_P7 = 'Or say how much of your allowance to keep free, like "keep 30% free", or how much this build may use, like "use up to 70%".';
6520
- var PACE_P8 = 'For these to work, run "npx halfcycle" once more in this project.';
6521
- function paceStartLinesOf(s, promptScriptBehind2 = false) {
6567
+ function paceP8Of(version) {
6568
+ return `For these to work, run "${bareHalfcycleCommand(version)}" once more in this project.`;
6569
+ }
6570
+ var PACE_P8 = paceP8Of();
6571
+ function paceStartLinesOf(s, promptScriptBehind2 = false, version) {
6522
6572
  const limit = s.limitTokens !== void 0 && s.expectedTokens !== void 0 ? { expected: s.expectedTokens, limit: s.limitTokens, multiple: s.multiple ?? null, setBy: s.limitSetBy ?? "default" } : null;
6523
- const usage3 = buildStartUsageLinesOf({ workMinutes: s.workMinutes, limit, weekCeilingTokens: s.weekCeilingTokens });
6524
- const lines = [paceP1Of(s.number, s.minutes), ...usage3.work];
6573
+ const usage5 = buildStartUsageLinesOf({ workMinutes: s.workMinutes, limit, weekCeilingTokens: s.weekCeilingTokens });
6574
+ const lines = [paceP1Of(s.number, s.minutes), ...usage5.work];
6525
6575
  if (s.minutes === null)
6526
6576
  lines.push(PACE_NO_BUILD_PLAN);
6527
6577
  if (s.keepFreePercent === 0)
6528
6578
  lines.push(PACE_P5);
6529
6579
  else
6530
6580
  lines.push(s.knownWindow !== null ? paceP3Of(s.keepFreePercent) : PACE_P2);
6531
- if (usage3.limit.length > 0) {
6532
- lines.push(...usage3.limit);
6581
+ if (usage5.limit.length > 0) {
6582
+ lines.push(...usage5.limit);
6533
6583
  if (s.firstGuess === true)
6534
6584
  lines.push(LIMIT_L1B);
6535
6585
  }
6536
6586
  if (s.pauses > 0)
6537
6587
  lines.push(paceP6Of(s.pauses));
6538
- lines.push(PACE_P4, PACE_P7, ...usage3.change);
6588
+ lines.push(PACE_P4, PACE_P7, ...usage5.change);
6539
6589
  if (promptScriptBehind2)
6540
- lines.push(PACE_P8);
6590
+ lines.push(paceP8Of(version));
6541
6591
  return lines;
6542
6592
  }
6543
6593
  function paceS1Of(window, k, until, now, keepFreePercent) {
@@ -6809,19 +6859,43 @@ function limitUsedShareTextOf(used, limit, weekCeilingTokens) {
6809
6859
  return figureThenCaveat(`This phase has used ${ofYourWeek(used, weekCeilingTokens)}, out of a limit of ${aboutShareOf(weekPercentOf(limit, weekCeilingTokens))}.`);
6810
6860
  }
6811
6861
 
6862
+ // ../events/dist/regression-line.js
6863
+ import { z as z26 } from "zod";
6864
+ var REGRESSION_EVERY_MIN = 1;
6865
+ var REGRESSION_EVERY_MAX = 20;
6866
+ var REGRESSION_EVERY_RANGE_REFUSAL = "The number of phases between regressions must be from 1 to 20. Nothing was changed.";
6867
+ var regressionEverySchema = z26.number().int({ message: REGRESSION_EVERY_RANGE_REFUSAL }).min(REGRESSION_EVERY_MIN, { message: REGRESSION_EVERY_RANGE_REFUSAL }).max(REGRESSION_EVERY_MAX, { message: REGRESSION_EVERY_RANGE_REFUSAL });
6868
+ var regressionClosedSinceSchema = z26.number().int().min(0);
6869
+ var REGRESSION_LINE_MAX = 200;
6870
+ var regressionLineSchema = z26.string().min(1).max(REGRESSION_LINE_MAX);
6871
+ var regressionEveryRequestSchema = z26.object({
6872
+ // How many phases may close before a regression phase is due, 1 to 20.
6873
+ every: regressionEverySchema
6874
+ }).strict();
6875
+ var regressionEveryResponseSchema = z26.object({
6876
+ // The number now stored.
6877
+ every: regressionEverySchema,
6878
+ // The line as it reads now, or null when there is none to show.
6879
+ line: regressionLineSchema.nullable()
6880
+ }).strict();
6881
+
6812
6882
  // ../events/dist/client-version.js
6813
6883
  var CHAT_APPROVAL_CLIENT_VERSION = "0.4.7";
6814
6884
  function clientMeetsChatApprovalFloor(version) {
6815
- return isReleaseVersion(version) && compareReleaseVersions(version, CHAT_APPROVAL_CLIENT_VERSION) >= 0;
6885
+ const release = releaseOfVersion(version);
6886
+ return release !== void 0 && compareReleaseVersions(release, CHAT_APPROVAL_CLIENT_VERSION) >= 0;
6816
6887
  }
6817
6888
  var TYPED_APPROVAL_OFF_SENTENCE = "Approving by typing is switched off for now.";
6818
6889
  var HEADLESS_CANNOT_APPROVE = "(a headless session cannot approve, by design)";
6819
6890
  var TYPED_REVIEW_OFF_SENTENCE = "Reviewing by typing is switched off for now.";
6820
- var CLIENT_UPDATE_SENTENCE = `To approve by typing in a session, Halfcycle needs updating: run \`${HALFCYCLE_COMMAND}\` in this project's folder, then reopen your session and ask again.`;
6891
+ function clientUpdateSentence(version) {
6892
+ return `To approve by typing in a session, Halfcycle needs updating: run \`${halfcycleCommand(version)}\` in this project's folder, then reopen your session and ask again.`;
6893
+ }
6894
+ var CLIENT_UPDATE_SENTENCE = clientUpdateSentence();
6821
6895
 
6822
6896
  // ../events/dist/device-auth.js
6823
- import { z as z26 } from "zod";
6824
- var utcIso86013 = z26.string().regex(/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d+)?Z$/, "must be a UTC ISO-8601 timestamp ending in Z");
6897
+ import { z as z27 } from "zod";
6898
+ var utcIso86013 = z27.string().regex(/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d+)?Z$/, "must be a UTC ISO-8601 timestamp ending in Z");
6825
6899
  var DEVICE_AUTH_STATUS = {
6826
6900
  /** The poll: minted, nobody has acted yet. Keep polling. 200. */
6827
6901
  PENDING: "pending",
@@ -6862,25 +6936,25 @@ var DEVICE_POLL_REFUSALS = [
6862
6936
  DEVICE_AUTH_STATUS.EXPIRED,
6863
6937
  DEVICE_AUTH_STATUS.UNKNOWN
6864
6938
  ];
6865
- var deviceAuthStartResponseSchema = z26.object({
6866
- deviceCode: z26.string().min(1),
6867
- userCode: z26.string().min(1),
6868
- verificationUrl: z26.string().min(1),
6939
+ var deviceAuthStartResponseSchema = z27.object({
6940
+ deviceCode: z27.string().min(1),
6941
+ userCode: z27.string().min(1),
6942
+ verificationUrl: z27.string().min(1),
6869
6943
  expiresAt: utcIso86013,
6870
- pollIntervalMs: z26.number().int().positive()
6944
+ pollIntervalMs: z27.number().int().positive()
6871
6945
  }).strict();
6872
- var devicePollPendingSchema = z26.object({
6873
- status: z26.literal(DEVICE_AUTH_STATUS.PENDING),
6874
- pollIntervalMs: z26.number().int().positive()
6946
+ var devicePollPendingSchema = z27.object({
6947
+ status: z27.literal(DEVICE_AUTH_STATUS.PENDING),
6948
+ pollIntervalMs: z27.number().int().positive()
6875
6949
  }).strict();
6876
- var devicePollApprovedSchema = z26.object({
6877
- status: z26.literal(DEVICE_AUTH_STATUS.APPROVED),
6878
- accountId: z26.string().min(1),
6879
- credential: z26.string().min(1),
6950
+ var devicePollApprovedSchema = z27.object({
6951
+ status: z27.literal(DEVICE_AUTH_STATUS.APPROVED),
6952
+ accountId: z27.string().min(1),
6953
+ credential: z27.string().min(1),
6880
6954
  expiresAt: utcIso86013
6881
6955
  }).strict();
6882
- var deviceAuthRefusalSchema = z26.object({
6883
- status: z26.enum([
6956
+ var deviceAuthRefusalSchema = z27.object({
6957
+ status: z27.enum([
6884
6958
  DEVICE_AUTH_STATUS.DECLINED,
6885
6959
  DEVICE_AUTH_STATUS.EXPIRED,
6886
6960
  DEVICE_AUTH_STATUS.UNKNOWN,
@@ -6891,25 +6965,25 @@ var deviceAuthRefusalSchema = z26.object({
6891
6965
  DEVICE_AUTH_STATUS.ALREADY_DECIDED,
6892
6966
  DEVICE_AUTH_STATUS.TERMS_REQUIRED
6893
6967
  ]),
6894
- message: z26.string().min(1)
6968
+ message: z27.string().min(1)
6895
6969
  }).strict();
6896
- var devicePollResponseSchema = z26.union([
6970
+ var devicePollResponseSchema = z27.union([
6897
6971
  devicePollPendingSchema,
6898
6972
  devicePollApprovedSchema,
6899
6973
  deviceAuthRefusalSchema
6900
6974
  ]);
6901
- var deviceConfirmRequestSchema = z26.object({
6902
- userCode: z26.string().min(1),
6903
- decision: z26.enum(["approve", "decline"]),
6904
- externalAuthId: z26.string().min(1).optional(),
6905
- email: z26.string().optional(),
6906
- acceptTerms: z26.boolean().optional()
6975
+ var deviceConfirmRequestSchema = z27.object({
6976
+ userCode: z27.string().min(1),
6977
+ decision: z27.enum(["approve", "decline"]),
6978
+ externalAuthId: z27.string().min(1).optional(),
6979
+ email: z27.string().optional(),
6980
+ acceptTerms: z27.boolean().optional()
6907
6981
  }).strict();
6908
- var deviceConfirmRecordedSchema = z26.object({
6909
- status: z26.literal(DEVICE_AUTH_STATUS.RECORDED),
6910
- accountId: z26.string().min(1).optional()
6982
+ var deviceConfirmRecordedSchema = z27.object({
6983
+ status: z27.literal(DEVICE_AUTH_STATUS.RECORDED),
6984
+ accountId: z27.string().min(1).optional()
6911
6985
  }).strict();
6912
- var deviceConfirmResponseSchema = z26.union([
6986
+ var deviceConfirmResponseSchema = z27.union([
6913
6987
  deviceConfirmRecordedSchema,
6914
6988
  deviceAuthRefusalSchema
6915
6989
  ]);
@@ -6953,52 +7027,58 @@ function requestAccessSentence(engagementId) {
6953
7027
  }
6954
7028
 
6955
7029
  // ../events/dist/join-refusal.js
6956
- import { z as z27 } from "zod";
7030
+ import { z as z28 } from "zod";
6957
7031
  var NOT_THIS_ACCOUNT_REASON = "not-this-account";
6958
7032
  var JOIN_REFUSAL_STATUS = 403;
6959
- var notThisAccountRefusalSchema = z27.object({
6960
- statusCode: z27.literal(JOIN_REFUSAL_STATUS),
6961
- error: z27.string(),
6962
- message: z27.string(),
6963
- reason: z27.literal(NOT_THIS_ACCOUNT_REASON)
7033
+ var notThisAccountRefusalSchema = z28.object({
7034
+ statusCode: z28.literal(JOIN_REFUSAL_STATUS),
7035
+ error: z28.string(),
7036
+ message: z28.string(),
7037
+ reason: z28.literal(NOT_THIS_ACCOUNT_REASON)
6964
7038
  }).strict();
6965
- var joinRefusalSchema = z27.union([notThisAccountRefusalSchema, wireErrorSchema]);
7039
+ var joinRefusalSchema = z28.union([notThisAccountRefusalSchema, wireErrorSchema]);
6966
7040
  function isNotThisAccountRefusal(body) {
6967
7041
  return typeof body === "object" && body !== null && body.reason === NOT_THIS_ACCOUNT_REASON;
6968
7042
  }
6969
7043
 
6970
7044
  // ../events/dist/account-identity.js
6971
- import { z as z28 } from "zod";
6972
- var accountIdentitySchema = z28.object({
6973
- accountId: z28.string().min(1),
6974
- email: z28.string().min(1).optional()
7045
+ import { z as z29 } from "zod";
7046
+ var accountIdentitySchema = z29.object({
7047
+ accountId: z29.string().min(1),
7048
+ email: z29.string().min(1).optional()
6975
7049
  }).strict();
6976
7050
  function safeParseAccountIdentity(payload) {
6977
7051
  return accountIdentitySchema.safeParse(payload);
6978
7052
  }
6979
7053
 
6980
7054
  // ../events/dist/credential-wire.js
6981
- import { z as z29 } from "zod";
6982
- var engagementCredentialResponseSchema = z29.object({
6983
- engagementId: z29.string(),
6984
- sessionToken: z29.string(),
6985
- mcpUrl: z29.string(),
6986
- guardUrl: z29.string(),
6987
- controlTelemetryUrl: z29.string(),
6988
- methodVersion: z29.string().optional(),
6989
- consoleUrl: z29.string().optional()
7055
+ import { z as z30 } from "zod";
7056
+ var engagementCredentialResponseSchema = z30.object({
7057
+ engagementId: z30.string(),
7058
+ sessionToken: z30.string(),
7059
+ mcpUrl: z30.string(),
7060
+ guardUrl: z30.string(),
7061
+ controlTelemetryUrl: z30.string(),
7062
+ methodVersion: z30.string().optional(),
7063
+ consoleUrl: z30.string().optional()
6990
7064
  }).strict();
6991
- var ciTokenExchangeResponseSchema = z29.object({
6992
- token: z29.string(),
7065
+ var ciTokenExchangeResponseSchema = z30.object({
7066
+ token: z30.string(),
6993
7067
  expiresAt: utcIso8601,
6994
- engagementId: z29.string()
7068
+ engagementId: z30.string()
6995
7069
  }).strict();
6996
7070
  var NO_CREDENTIAL_PLACEHOLDER = "halfcycle:no-credential";
6997
7071
  var NOT_THIS_ACCOUNT_PLACEHOLDER = "halfcycle:not-this-account";
6998
7072
  var UPDATE_CLAUDE_CODE_PLACEHOLDER = "halfcycle:update-claude-code";
6999
7073
  var SWITCH_ACCOUNT_ANSWER = 'answer "s" when asked';
7000
- var NO_CREDENTIAL_SENTENCE = 'Halfcycle is not connected: this machine has no working Halfcycle credential for this project. Run "npx halfcycle" in the project folder to sign in again.';
7001
- var NOT_THIS_ACCOUNT_SENTENCE = `Halfcycle is not connected: this project belongs to another Halfcycle account. To switch, run "npx halfcycle" in this project and ${SWITCH_ACCOUNT_ANSWER}.`;
7074
+ function noCredentialSentence(version) {
7075
+ return `Halfcycle is not connected: this machine has no working Halfcycle credential for this project. Run "${bareHalfcycleCommand(version)}" in the project folder to sign in again.`;
7076
+ }
7077
+ var NO_CREDENTIAL_SENTENCE = noCredentialSentence();
7078
+ function notThisAccountSentence(version) {
7079
+ return `Halfcycle is not connected: this project belongs to another Halfcycle account. To switch, run "${bareHalfcycleCommand(version)}" in this project and ${SWITCH_ACCOUNT_ANSWER}.`;
7080
+ }
7081
+ var NOT_THIS_ACCOUNT_SENTENCE = notThisAccountSentence();
7002
7082
  var UPDATE_CLAUDE_CODE_SENTENCE = "Halfcycle is not connected: this Claude Code version does not name the server it connects to, so no credential was sent. Update Claude Code, then reopen.";
7003
7083
  var NO_NODE_SENTENCE = "Halfcycle is not connected: Node is not installed, or not on the path this session uses, so nothing here is checked. Install Node, then reopen.";
7004
7084
  var FOLDER_NOT_TRUSTED_SENTENCE = "Folder not yet trusted. Open this folder once in interactive Claude Code and trust it; then headless runs work.";
@@ -7009,19 +7089,19 @@ var PLACEHOLDER_SENTENCES = Object.freeze({
7009
7089
  });
7010
7090
 
7011
7091
  // ../events/dist/project-engagement.js
7012
- import { z as z30 } from "zod";
7013
- var projectIdSchema = z30.string().uuid();
7014
- var projectEngagementRequestSchema = z30.object({
7015
- derivedName: z30.string()
7092
+ import { z as z31 } from "zod";
7093
+ var projectIdSchema = z31.string().uuid();
7094
+ var projectEngagementRequestSchema = z31.object({
7095
+ derivedName: z31.string()
7016
7096
  }).strict();
7017
7097
 
7018
7098
  // ../events/dist/phase-record.js
7019
- import { z as z32 } from "zod";
7099
+ import { z as z33 } from "zod";
7020
7100
 
7021
7101
  // ../events/dist/build-record-v4.js
7022
- import { z as z31 } from "zod";
7102
+ import { z as z32 } from "zod";
7023
7103
  var BUILD_RECORD_V4_FORMAT = "halfcycle-build-record/v4";
7024
- var recordReviewClaimSchema = z31.object({
7104
+ var recordReviewClaimSchema = z32.object({
7025
7105
  ...reviewClaimAnswerSchema.shape,
7026
7106
  note: reviewNoteSchema.nullable()
7027
7107
  }).strict().superRefine((c, ctx) => {
@@ -7029,19 +7109,19 @@ var recordReviewClaimSchema = z31.object({
7029
7109
  ctx.addIssue({ code: "custom", path: ["note"], message: "carries the owner's note exactly on a reject" });
7030
7110
  }
7031
7111
  });
7032
- var recordCallSchema = z31.object(workCallSchema.shape).omit({ at: true }).strict().superRefine((c, ctx) => checkReadCallHow(c, ctx));
7033
- var recordReviewV4Schema = z31.object({
7112
+ var recordCallSchema = z32.object(workCallSchema.shape).omit({ at: true }).strict().superRefine((c, ctx) => checkReadCallHow(c, ctx));
7113
+ var recordReviewV4Schema = z32.object({
7034
7114
  ...recordReviewSchema.shape,
7035
- claims: z31.array(recordReviewClaimSchema).max(REVIEW_CLAIMS_MAX),
7036
- calls: z31.array(recordCallSchema)
7115
+ claims: z32.array(recordReviewClaimSchema).max(REVIEW_CLAIMS_MAX),
7116
+ calls: z32.array(recordCallSchema)
7037
7117
  }).strict().superRefine((v, ctx) => checkRecordReview(v, ctx));
7038
- var buildRecordV4Schema = z31.object({
7118
+ var buildRecordV4Schema = z32.object({
7039
7119
  ...buildRecordV3Schema.shape,
7040
- format: z31.literal(BUILD_RECORD_V4_FORMAT),
7120
+ format: z32.literal(BUILD_RECORD_V4_FORMAT),
7041
7121
  path: phasePathSchema,
7042
7122
  review: recordReviewV4Schema
7043
7123
  }).strict().superRefine((record2, ctx) => checkReviewedRecord(record2, ctx));
7044
- var buildRecordSchema = z31.discriminatedUnion("format", [
7124
+ var buildRecordSchema = z32.discriminatedUnion("format", [
7045
7125
  buildRecordV2Schema,
7046
7126
  buildRecordV3Schema,
7047
7127
  buildRecordV4Schema
@@ -7073,81 +7153,81 @@ function removalsOf(record2) {
7073
7153
  }
7074
7154
 
7075
7155
  // ../events/dist/phase-record.js
7076
- var phaseRecordRefusalReasonSchema = z32.enum([
7156
+ var phaseRecordRefusalReasonSchema = z33.enum([
7077
7157
  "still-open",
7078
7158
  "reopened",
7079
7159
  "never-opened",
7080
7160
  "never-closed",
7081
7161
  "closed-before-records"
7082
7162
  ]);
7083
- var phaseRecordRecordSchema = z32.object({
7084
- outcome: z32.literal("record"),
7163
+ var phaseRecordRecordSchema = z33.object({
7164
+ outcome: z33.literal("record"),
7085
7165
  phase: phaseNameSchema,
7086
7166
  record: buildRecordSchema
7087
7167
  }).strict().refine((body) => body.record.phase === body.phase, {
7088
7168
  message: "the record is for a different phase than the one asked for",
7089
7169
  path: ["record", "phase"]
7090
7170
  });
7091
- var phaseRecordWithheldSchema = z32.object({
7092
- outcome: z32.literal("withheld"),
7171
+ var phaseRecordWithheldSchema = z33.object({
7172
+ outcome: z33.literal("withheld"),
7093
7173
  phase: phaseNameSchema,
7094
- sequence: z32.number().int().min(1),
7174
+ sequence: z33.number().int().min(1),
7095
7175
  closedAt: utcIso8601,
7096
- message: z32.string()
7176
+ message: z33.string()
7097
7177
  }).strict();
7098
7178
  var phaseRecordRefusedSchema = wireErrorSchema.extend({
7099
- outcome: z32.literal("refused"),
7179
+ outcome: z33.literal("refused"),
7100
7180
  phase: phaseNameSchema,
7101
- statusCode: z32.literal(409),
7102
- error: z32.literal("PhaseRecordRefused"),
7181
+ statusCode: z33.literal(409),
7182
+ error: z33.literal("PhaseRecordRefused"),
7103
7183
  reason: phaseRecordRefusalReasonSchema
7104
7184
  }).strict();
7105
- var phaseRecordResponseSchema = z32.discriminatedUnion("outcome", [
7185
+ var phaseRecordResponseSchema = z33.discriminatedUnion("outcome", [
7106
7186
  phaseRecordRecordSchema,
7107
7187
  phaseRecordWithheldSchema,
7108
7188
  phaseRecordRefusedSchema
7109
7189
  ]);
7110
7190
 
7111
7191
  // ../events/dist/scope-chat.js
7112
- import { z as z33 } from "zod";
7113
- var chatScopeApproveRequestSchema = z33.object({
7114
- act: z33.literal("approve"),
7192
+ import { z as z34 } from "zod";
7193
+ var chatScopeApproveRequestSchema = z34.object({
7194
+ act: z34.literal("approve"),
7115
7195
  reference: phaseIdentitySchema,
7116
7196
  planVersion: planVersionSchema
7117
7197
  }).strict();
7118
- var chatScopeSendBackRequestSchema = z33.object({
7119
- act: z33.literal("send-back"),
7198
+ var chatScopeSendBackRequestSchema = z34.object({
7199
+ act: z34.literal("send-back"),
7120
7200
  reference: phaseIdentitySchema,
7121
7201
  planVersion: planVersionSchema,
7122
7202
  note: scopeNoteWireSchema.optional()
7123
7203
  }).strict();
7124
- var chatScopeRequestSchema = z33.discriminatedUnion("act", [
7204
+ var chatScopeRequestSchema = z34.discriminatedUnion("act", [
7125
7205
  chatScopeApproveRequestSchema,
7126
7206
  chatScopeSendBackRequestSchema
7127
7207
  ]);
7128
7208
  var CHAT_SCOPE_ACTS = SCOPE_ACTS;
7129
- var chatScopeRecordedSchema = z33.object({
7130
- outcome: z33.literal("recorded"),
7131
- act: z33.enum(CHAT_SCOPE_ACTS),
7209
+ var chatScopeRecordedSchema = z34.object({
7210
+ outcome: z34.literal("recorded"),
7211
+ act: z34.enum(CHAT_SCOPE_ACTS),
7132
7212
  phase: scopePhaseSchema,
7133
7213
  planVersion: planVersionSchema
7134
7214
  }).strict();
7135
- var chatScopeAlreadyApprovedSchema = z33.object({
7136
- outcome: z33.literal("already-approved"),
7137
- replanNeeded: z33.boolean()
7215
+ var chatScopeAlreadyApprovedSchema = z34.object({
7216
+ outcome: z34.literal("already-approved"),
7217
+ replanNeeded: z34.boolean()
7138
7218
  }).strict();
7139
- var chatScopeRefusedSchema = z33.object({
7140
- outcome: z33.literal("refused"),
7141
- problems: z33.array(systemDescriptionProblemSchema).min(1)
7219
+ var chatScopeRefusedSchema = z34.object({
7220
+ outcome: z34.literal("refused"),
7221
+ problems: z34.array(systemDescriptionProblemSchema).min(1)
7142
7222
  }).strict();
7143
- var chatAmbiguousSchema = z33.object({
7144
- outcome: z33.literal("ambiguous"),
7145
- phases: z33.array(scopePhaseSchema).min(2)
7223
+ var chatAmbiguousSchema = z34.object({
7224
+ outcome: z34.literal("ambiguous"),
7225
+ phases: z34.array(scopePhaseSchema).min(2)
7146
7226
  }).strict();
7147
7227
  function bareOutcome(outcome) {
7148
- return z33.object({ outcome: z33.literal(outcome) }).strict();
7228
+ return z34.object({ outcome: z34.literal(outcome) }).strict();
7149
7229
  }
7150
- var chatScopeResultSchema = z33.discriminatedUnion("outcome", [
7230
+ var chatScopeResultSchema = z34.discriminatedUnion("outcome", [
7151
7231
  chatScopeRecordedSchema,
7152
7232
  chatScopeAlreadyApprovedSchema,
7153
7233
  chatScopeRefusedSchema,
@@ -7162,19 +7242,19 @@ var chatScopeResultSchema = z33.discriminatedUnion("outcome", [
7162
7242
  ]);
7163
7243
 
7164
7244
  // ../events/dist/review-chat.js
7165
- import { z as z34 } from "zod";
7166
- var chatReviewAcceptRequestSchema = z34.object({
7167
- act: z34.literal("accept"),
7245
+ import { z as z35 } from "zod";
7246
+ var chatReviewAcceptRequestSchema = z35.object({
7247
+ act: z35.literal("accept"),
7168
7248
  reference: phaseIdentitySchema,
7169
- reviewVersion: z34.number().int().min(0),
7170
- parts: z34.array(systemPartIdSchema).max(REVIEW_PARTS_MAX),
7171
- waitingClaims: z34.literal(true).optional()
7249
+ reviewVersion: z35.number().int().min(0),
7250
+ parts: z35.array(systemPartIdSchema).max(REVIEW_PARTS_MAX),
7251
+ waitingClaims: z35.literal(true).optional()
7172
7252
  }).strict();
7173
7253
  function notedRequest(act) {
7174
- return z34.object({
7175
- act: z34.literal(act),
7254
+ return z35.object({
7255
+ act: z35.literal(act),
7176
7256
  reference: phaseIdentitySchema,
7177
- reviewVersion: z34.number().int().min(0),
7257
+ reviewVersion: z35.number().int().min(0),
7178
7258
  part: systemPartIdSchema,
7179
7259
  note: reviewNoteWireSchema
7180
7260
  }).strict();
@@ -7183,14 +7263,14 @@ var chatReviewQuestionRequestSchema = notedRequest("question");
7183
7263
  var chatReviewSendBackRequestSchema = notedRequest("send-back");
7184
7264
  var chatReviewCarryRequestSchema = notedRequest("carry");
7185
7265
  var chatReviewDropRequestSchema = notedRequest("drop");
7186
- var chatReviewRejectRequestSchema = z34.object({
7187
- act: z34.literal("reject"),
7266
+ var chatReviewRejectRequestSchema = z35.object({
7267
+ act: z35.literal("reject"),
7188
7268
  reference: phaseIdentitySchema,
7189
- reviewVersion: z34.number().int().min(0),
7269
+ reviewVersion: z35.number().int().min(0),
7190
7270
  task: buildTaskIdSchema,
7191
7271
  note: reviewNoteWireSchema
7192
7272
  }).strict();
7193
- var chatReviewRequestSchema = z34.discriminatedUnion("act", [
7273
+ var chatReviewRequestSchema = z35.discriminatedUnion("act", [
7194
7274
  chatReviewAcceptRequestSchema,
7195
7275
  chatReviewQuestionRequestSchema,
7196
7276
  chatReviewSendBackRequestSchema,
@@ -7199,12 +7279,12 @@ var chatReviewRequestSchema = z34.discriminatedUnion("act", [
7199
7279
  chatReviewRejectRequestSchema
7200
7280
  ]);
7201
7281
  function bareOutcome2(outcome) {
7202
- return z34.object({ outcome: z34.literal(outcome) }).strict();
7282
+ return z35.object({ outcome: z35.literal(outcome) }).strict();
7203
7283
  }
7204
7284
  function notTheConsoleNotDecidable(option) {
7205
7285
  return option !== reviewNotDecidableSchema;
7206
7286
  }
7207
- var chatReviewResultSchema = z34.discriminatedUnion("outcome", [
7287
+ var chatReviewResultSchema = z35.discriminatedUnion("outcome", [
7208
7288
  reviewPartsNotDecidableSchema,
7209
7289
  ...reviewDecisionResultSchema.options.filter(notTheConsoleNotDecidable),
7210
7290
  bareOutcome2("show-first"),
@@ -7212,15 +7292,15 @@ var chatReviewResultSchema = z34.discriminatedUnion("outcome", [
7212
7292
  chatAmbiguousSchema,
7213
7293
  bareOutcome2("nothing-waiting")
7214
7294
  ]);
7215
- var reviewAnswerRequestSchema = z34.object({
7295
+ var reviewAnswerRequestSchema = z35.object({
7216
7296
  part: systemPartIdSchema,
7217
7297
  answer: statementText(REVIEW_ANSWER_MAX)
7218
7298
  }).strict();
7219
- var reviewAnswerRecordedSchema = z34.object({
7220
- outcome: z34.literal("recorded"),
7299
+ var reviewAnswerRecordedSchema = z35.object({
7300
+ outcome: z35.literal("recorded"),
7221
7301
  name: systemPartSchema.shape.name
7222
7302
  }).strict();
7223
- var reviewAnswerResultSchema = z34.discriminatedUnion("outcome", [
7303
+ var reviewAnswerResultSchema = z35.discriminatedUnion("outcome", [
7224
7304
  reviewAnswerRecordedSchema,
7225
7305
  bareOutcome2("no-question"),
7226
7306
  reviewRefusedSchema,
@@ -7228,27 +7308,27 @@ var reviewAnswerResultSchema = z34.discriminatedUnion("outcome", [
7228
7308
  ]);
7229
7309
 
7230
7310
  // ../events/dist/owner-waits.js
7231
- import { z as z36 } from "zod";
7311
+ import { z as z37 } from "zod";
7232
7312
 
7233
7313
  // ../events/dist/owner-wait-answer.js
7234
- import { z as z35 } from "zod";
7235
- var ownerWaitShortIdSchema = z35.string().regex(/^[0-9a-f]{8}$/);
7314
+ import { z as z36 } from "zod";
7315
+ var ownerWaitShortIdSchema = z36.string().regex(/^[0-9a-f]{8}$/);
7236
7316
  var OWNER_WAIT_ANSWER_ACTS = ["approve", "decline"];
7237
- var ownerWaitAnswerActSchema = z35.enum(OWNER_WAIT_ANSWER_ACTS);
7238
- var ownerWaitChatRequestSchema = z35.discriminatedUnion("act", [
7239
- z35.object({ act: z35.literal("approve"), wait: ownerWaitShortIdSchema }).strict(),
7240
- z35.object({ act: z35.literal("decline"), wait: ownerWaitShortIdSchema, note: scopeNoteWireSchema }).strict()
7317
+ var ownerWaitAnswerActSchema = z36.enum(OWNER_WAIT_ANSWER_ACTS);
7318
+ var ownerWaitChatRequestSchema = z36.discriminatedUnion("act", [
7319
+ z36.object({ act: z36.literal("approve"), wait: ownerWaitShortIdSchema }).strict(),
7320
+ z36.object({ act: z36.literal("decline"), wait: ownerWaitShortIdSchema, note: scopeNoteWireSchema }).strict()
7241
7321
  ]);
7242
7322
  function bareOutcome3(outcome) {
7243
- return z35.object({ outcome: z35.literal(outcome) }).strict();
7323
+ return z36.object({ outcome: z36.literal(outcome) }).strict();
7244
7324
  }
7245
- var ownerWaitChatResultSchema = z35.discriminatedUnion("outcome", [
7246
- z35.object({
7247
- outcome: z35.literal("recorded"),
7325
+ var ownerWaitChatResultSchema = z36.discriminatedUnion("outcome", [
7326
+ z36.object({
7327
+ outcome: z36.literal("recorded"),
7248
7328
  act: ownerWaitAnswerActSchema,
7249
- question: z35.string().min(1)
7329
+ question: z36.string().min(1)
7250
7330
  }).strict(),
7251
- z35.object({ outcome: z35.literal("refused"), problems: z35.array(systemDescriptionProblemSchema).min(1) }).strict(),
7331
+ z36.object({ outcome: z36.literal("refused"), problems: z36.array(systemDescriptionProblemSchema).min(1) }).strict(),
7252
7332
  bareOutcome3("not-owner"),
7253
7333
  bareOutcome3("no-owner"),
7254
7334
  bareOutcome3("not-found"),
@@ -7270,7 +7350,7 @@ var OWNER_WAIT_ITEMS = Object.freeze([
7270
7350
  Object.freeze({ id: "change-standing-rule", words: "Changing a standing rule." })
7271
7351
  ]);
7272
7352
  var OWNER_WAIT_ITEM_IDS = Object.freeze(OWNER_WAIT_ITEMS.map((item) => item.id));
7273
- var ownerWaitItemIdSchema = z36.enum(OWNER_WAIT_ITEM_IDS);
7353
+ var ownerWaitItemIdSchema = z37.enum(OWNER_WAIT_ITEM_IDS);
7274
7354
  var OWNER_WAIT_FIXED_COUNT = OWNER_WAIT_ITEMS.length;
7275
7355
  var OWNER_WAIT_ADDITIONS_MAX = 20;
7276
7356
  var OWNER_WAIT_TEXT_MAX = 240;
@@ -7280,23 +7360,23 @@ function ownerWaitItemOf(id) {
7280
7360
  throw new Error(`no owner-wait item ${id}`);
7281
7361
  return item;
7282
7362
  }
7283
- var ownerWaitItemRefSchema = z36.union([
7284
- z36.number().int().min(1),
7285
- z36.string().uuid({ message: "must be a number from 1 to 6, or the id shown beside an added item" })
7363
+ var ownerWaitItemRefSchema = z37.union([
7364
+ z37.number().int().min(1),
7365
+ z37.string().uuid({ message: "must be a number from 1 to 6, or the id shown beside an added item" })
7286
7366
  ]);
7287
7367
  var ownerWaitText = descriptionText(OWNER_WAIT_TEXT_MAX);
7288
- var ownerWaitAddRequestSchema = z36.object({
7289
- engagementId: z36.string().min(1),
7368
+ var ownerWaitAddRequestSchema = z37.object({
7369
+ engagementId: z37.string().min(1),
7290
7370
  text: ownerWaitText,
7291
7371
  reason: ownerWaitText
7292
7372
  }).strict();
7293
- var ownerWaitAddResultSchema = z36.discriminatedUnion("outcome", [
7294
- z36.object({ outcome: z36.literal("added"), id: z36.string().uuid(), addedAt: utcIso8601 }).strict(),
7295
- z36.object({ outcome: z36.literal("refused"), reason: z36.literal("too-many") }).strict()
7373
+ var ownerWaitAddResultSchema = z37.discriminatedUnion("outcome", [
7374
+ z37.object({ outcome: z37.literal("added"), id: z37.string().uuid(), addedAt: utcIso8601 }).strict(),
7375
+ z37.object({ outcome: z37.literal("refused"), reason: z37.literal("too-many") }).strict()
7296
7376
  ]);
7297
- var ownerWaitUndoableSchema = z36.enum(["yes", "no"]);
7298
- var ownerWaitDeclareRequestSchema = z36.object({
7299
- engagementId: z36.string().min(1),
7377
+ var ownerWaitUndoableSchema = z37.enum(["yes", "no"]);
7378
+ var ownerWaitDeclareRequestSchema = z37.object({
7379
+ engagementId: z37.string().min(1),
7300
7380
  item: ownerWaitItemRefSchema,
7301
7381
  question: ownerWaitText,
7302
7382
  recommendation: ownerWaitText,
@@ -7309,47 +7389,47 @@ var ownerWaitDeclareRequestSchema = z36.object({
7309
7389
  path: ["how"]
7310
7390
  });
7311
7391
  var OWNER_WAIT_DECLARE_REFUSALS = ["no-open-phase", "unknown-item", "say-if-undoable"];
7312
- var ownerWaitDeclareResultSchema = z36.discriminatedUnion("outcome", [
7313
- z36.object({ outcome: z36.literal("recorded"), id: z36.string().uuid(), say: z36.string().min(1) }).strict(),
7314
- z36.object({
7315
- outcome: z36.literal("go-ahead"),
7316
- id: z36.string().uuid(),
7317
- callId: z36.number().int().positive(),
7318
- say: z36.string().min(1)
7392
+ var ownerWaitDeclareResultSchema = z37.discriminatedUnion("outcome", [
7393
+ z37.object({ outcome: z37.literal("recorded"), id: z37.string().uuid(), say: z37.string().min(1) }).strict(),
7394
+ z37.object({
7395
+ outcome: z37.literal("go-ahead"),
7396
+ id: z37.string().uuid(),
7397
+ callId: z37.number().int().positive(),
7398
+ say: z37.string().min(1)
7319
7399
  }).strict(),
7320
- z36.object({ outcome: z36.literal("refused"), reason: z36.enum(OWNER_WAIT_DECLARE_REFUSALS) }).strict()
7400
+ z37.object({ outcome: z37.literal("refused"), reason: z37.enum(OWNER_WAIT_DECLARE_REFUSALS) }).strict()
7321
7401
  ]);
7322
- var ownerWaitAddedItemSchema = z36.object({
7323
- id: z36.string().uuid(),
7324
- words: z36.string().min(1),
7325
- reason: z36.string().min(1),
7402
+ var ownerWaitAddedItemSchema = z37.object({
7403
+ id: z37.string().uuid(),
7404
+ words: z37.string().min(1),
7405
+ reason: z37.string().min(1),
7326
7406
  addedAt: utcIso8601
7327
7407
  }).strict();
7328
- var ownerWaitListSchema = z36.object({
7329
- fixed: z36.array(z36.object({ id: ownerWaitItemIdSchema, words: z36.string().min(1) }).strict()).length(OWNER_WAIT_FIXED_COUNT),
7330
- added: z36.array(ownerWaitAddedItemSchema).max(OWNER_WAIT_ADDITIONS_MAX)
7408
+ var ownerWaitListSchema = z37.object({
7409
+ fixed: z37.array(z37.object({ id: ownerWaitItemIdSchema, words: z37.string().min(1) }).strict()).length(OWNER_WAIT_FIXED_COUNT),
7410
+ added: z37.array(ownerWaitAddedItemSchema).max(OWNER_WAIT_ADDITIONS_MAX)
7331
7411
  }).strict();
7332
- var ownerWaitDeclarationSchema = z36.object({
7333
- id: z36.string().uuid(),
7412
+ var ownerWaitDeclarationSchema = z37.object({
7413
+ id: z37.string().uuid(),
7334
7414
  fixedItem: ownerWaitItemIdSchema.nullable(),
7335
- addition: z36.object({ id: z36.string().uuid(), words: z36.string().min(1) }).strict().nullable(),
7336
- question: z36.string().min(1),
7337
- recommendation: z36.string().min(1),
7415
+ addition: z37.object({ id: z37.string().uuid(), words: z37.string().min(1) }).strict().nullable(),
7416
+ question: z37.string().min(1),
7417
+ recommendation: z37.string().min(1),
7338
7418
  task: buildTaskIdSchema.nullable(),
7339
7419
  declaredAt: utcIso8601
7340
7420
  }).strict().refine((d) => d.fixedItem === null !== (d.addition === null), {
7341
7421
  message: "names exactly one item: a fixed one or an added one",
7342
7422
  path: ["fixedItem"]
7343
7423
  });
7344
- var ownerWaitDeclarationsSchema = z36.object({ declarations: z36.array(ownerWaitDeclarationSchema) }).strict();
7345
- var ownerWaitOpenSchema = z36.object({
7424
+ var ownerWaitDeclarationsSchema = z37.object({ declarations: z37.array(ownerWaitDeclarationSchema) }).strict();
7425
+ var ownerWaitOpenSchema = z37.object({
7346
7426
  wait: ownerWaitShortIdSchema,
7347
7427
  task: buildTaskIdSchema.nullable(),
7348
- question: z36.string().min(1),
7349
- recommendation: z36.string().min(1),
7428
+ question: z37.string().min(1),
7429
+ recommendation: z37.string().min(1),
7350
7430
  declaredAt: utcIso8601
7351
7431
  }).strict();
7352
- var ownerWaitsOpenSchema = z36.object({ waits: z36.array(ownerWaitOpenSchema) }).strict();
7432
+ var ownerWaitsOpenSchema = z37.object({ waits: z37.array(ownerWaitOpenSchema) }).strict();
7353
7433
  function ownerWaitItemPhraseOf(item) {
7354
7434
  const cut = item.words.split(", such as ")[0] ?? item.words;
7355
7435
  const bare = cut.endsWith(".") ? cut.slice(0, -1) : cut;
@@ -7368,15 +7448,15 @@ function ownerWaitClosedLineOf(item, question2) {
7368
7448
  var OWNER_WAIT_DECLARATIONS_READ_FAILED = "Halfcycle could not read the decisions that waited for you.";
7369
7449
 
7370
7450
  // ../events/dist/autonomy.js
7371
- import { z as z37 } from "zod";
7451
+ import { z as z38 } from "zod";
7372
7452
  var AUTONOMY_CHANGED_IN = ["console", "chat"];
7373
- var autonomyChangedInSchema = z37.enum(AUTONOMY_CHANGED_IN);
7374
- var autonomySettingsSchema = z37.object({
7375
- on: z37.boolean(),
7453
+ var autonomyChangedInSchema = z38.enum(AUTONOMY_CHANGED_IN);
7454
+ var autonomySettingsSchema = z38.object({
7455
+ on: z38.boolean(),
7376
7456
  // whether builds run on their own
7377
- mayPublish: z37.boolean(),
7457
+ mayPublish: z38.boolean(),
7378
7458
  // whether builds may publish or release without waiting
7379
- maySpend: z37.boolean(),
7459
+ maySpend: z38.boolean(),
7380
7460
  // whether builds may spend money or sign up without waiting
7381
7461
  changedAt: utcIso8601.nullable(),
7382
7462
  // when the setting last changed; null if never
@@ -7386,37 +7466,37 @@ var autonomySettingsSchema = z37.object({
7386
7466
  message: "says when and where it changed, or neither",
7387
7467
  path: ["changedIn"]
7388
7468
  });
7389
- var autonomyChangeRequestSchema = z37.object({
7390
- on: z37.boolean(),
7469
+ var autonomyChangeRequestSchema = z38.object({
7470
+ on: z38.boolean(),
7391
7471
  // the switch
7392
- mayPublish: z37.boolean(),
7472
+ mayPublish: z38.boolean(),
7393
7473
  // the publish box
7394
- maySpend: z37.boolean()
7474
+ maySpend: z38.boolean()
7395
7475
  // the spend box
7396
7476
  }).strict();
7397
7477
  var AUTONOMY_CHAT_ACTS = ["on", "off"];
7398
- var autonomyChatActSchema = z37.enum(AUTONOMY_CHAT_ACTS);
7399
- var autonomyChatRequestSchema = z37.object({
7478
+ var autonomyChatActSchema = z38.enum(AUTONOMY_CHAT_ACTS);
7479
+ var autonomyChatRequestSchema = z38.object({
7400
7480
  act: autonomyChatActSchema
7401
7481
  // what the owner typed
7402
7482
  }).strict();
7403
- var autonomyChatResultSchema = z37.discriminatedUnion("outcome", [
7404
- z37.object({
7405
- outcome: z37.literal("recorded"),
7406
- on: z37.boolean()
7483
+ var autonomyChatResultSchema = z38.discriminatedUnion("outcome", [
7484
+ z38.object({
7485
+ outcome: z38.literal("recorded"),
7486
+ on: z38.boolean()
7407
7487
  // whether builds now run on their own
7408
7488
  }).strict(),
7409
- z37.object({ outcome: z37.literal("not-owner") }).strict(),
7410
- z37.object({ outcome: z37.literal("no-owner") }).strict(),
7411
- z37.object({ outcome: z37.literal("not-offered") }).strict(),
7412
- z37.object({ outcome: z37.literal("unavailable") }).strict()
7489
+ z38.object({ outcome: z38.literal("not-owner") }).strict(),
7490
+ z38.object({ outcome: z38.literal("no-owner") }).strict(),
7491
+ z38.object({ outcome: z38.literal("not-offered") }).strict(),
7492
+ z38.object({ outcome: z38.literal("unavailable") }).strict()
7413
7493
  ]);
7414
- var autonomyStateSchema = z37.object({
7415
- on: z37.boolean(),
7494
+ var autonomyStateSchema = z38.object({
7495
+ on: z38.boolean(),
7416
7496
  // whether builds run on their own
7417
- mayPublish: z37.boolean(),
7497
+ mayPublish: z38.boolean(),
7418
7498
  // whether builds may publish or release without waiting
7419
- maySpend: z37.boolean()
7499
+ maySpend: z38.boolean()
7420
7500
  // whether builds may spend money or sign up without waiting
7421
7501
  }).strict();
7422
7502
  var autonomyStateFileSchema = autonomyStateSchema.extend({
@@ -7425,8 +7505,8 @@ var autonomyStateFileSchema = autonomyStateSchema.extend({
7425
7505
  }).strict();
7426
7506
  var AUTONOMY_STATE_FILE_MAX_AGE_MS = 24 * 60 * 60 * 1e3;
7427
7507
  var AUTONOMY_ACTIVITY_KINDS = ["went-ahead", "waiting", "approved", "closed", "waiting-on-owner"];
7428
- var autonomyActivityKindSchema = z37.enum(AUTONOMY_ACTIVITY_KINDS);
7429
- var autonomyActivityRowSchema = z37.object({
7508
+ var autonomyActivityKindSchema = z38.enum(AUTONOMY_ACTIVITY_KINDS);
7509
+ var autonomyActivityRowSchema = z38.object({
7430
7510
  kind: autonomyActivityKindSchema,
7431
7511
  // what happened
7432
7512
  phase: scopePhaseSchema,
@@ -7477,11 +7557,11 @@ var autonomyActivityRowSchema = z37.object({
7477
7557
  ctx.addIssue({ code: "custom", message: "says how to undo it exactly when it can be undone", path: ["how"] });
7478
7558
  }
7479
7559
  });
7480
- var autonomyActivitySchema = z37.object({
7481
- rows: z37.array(autonomyActivityRowSchema)
7560
+ var autonomyActivitySchema = z38.object({
7561
+ rows: z38.array(autonomyActivityRowSchema)
7482
7562
  // newest first
7483
7563
  }).strict();
7484
- var savedIdSchema = z37.string().min(1).max(64).regex(/^[a-z0-9]+(?:-[a-z0-9]+)*$/, "must be lower-case letters, digits and single hyphens");
7564
+ var savedIdSchema = z38.string().min(1).max(64).regex(/^[a-z0-9]+(?:-[a-z0-9]+)*$/, "must be lower-case letters, digits and single hyphens");
7485
7565
  var SAVED_REF_PREFIX = "refs/halfcycle/saved/";
7486
7566
  function savedRefOf(id) {
7487
7567
  return `${SAVED_REF_PREFIX}${id}`;
@@ -7520,33 +7600,33 @@ function isGitFolderSegment(segment) {
7520
7600
  return t === ".git" || /^git~\d+$/.test(t);
7521
7601
  }
7522
7602
  var SAVED_PATH_KEPT_IN = ["commit", "copy"];
7523
- var savedPathSchema = z37.object({
7524
- path: z37.string().max(4096).refine(isProjectRelativePath, { message: "must be a path inside the project" }),
7603
+ var savedPathSchema = z38.object({
7604
+ path: z38.string().max(4096).refine(isProjectRelativePath, { message: "must be a path inside the project" }),
7525
7605
  // the file, relative to the project
7526
- keptIn: z37.enum(SAVED_PATH_KEPT_IN)
7606
+ keptIn: z38.enum(SAVED_PATH_KEPT_IN)
7527
7607
  // in the save's commit, or copied beside this file
7528
7608
  }).strict();
7529
- var savedCommitSchema = z37.string().regex(/^(?:[0-9a-f]{40}|[0-9a-f]{64})$/, "must be a full commit hash");
7530
- var savedBranchSchema = z37.object({
7531
- name: z37.string().min(1).max(255).refine((n) => !hasControlCharacter(n) && !n.startsWith("-") && !n.includes("..") && n !== "HEAD", {
7609
+ var savedCommitSchema = z38.string().regex(/^(?:[0-9a-f]{40}|[0-9a-f]{64})$/, "must be a full commit hash");
7610
+ var savedBranchSchema = z38.object({
7611
+ name: z38.string().min(1).max(255).refine((n) => !hasControlCharacter(n) && !n.startsWith("-") && !n.includes("..") && n !== "HEAD", {
7532
7612
  message: "must be a branch name"
7533
7613
  }),
7534
7614
  // the branch's name
7535
7615
  commit: savedCommitSchema
7536
7616
  // the commit it pointed at
7537
7617
  }).strict();
7538
- var savedSchema = z37.object({
7618
+ var savedSchema = z38.object({
7539
7619
  id: savedIdSchema,
7540
7620
  // what restore is typed with
7541
7621
  savedAt: utcIso8601,
7542
7622
  // when the copy was made
7543
- ref: z37.string().min(1),
7623
+ ref: z38.string().min(1),
7544
7624
  // where the saved commit is kept
7545
- what: z37.string().min(1).max(2e3),
7625
+ what: z38.string().min(1).max(2e3),
7546
7626
  // what the action would have changed, in words
7547
- paths: z37.array(savedPathSchema),
7627
+ paths: z38.array(savedPathSchema),
7548
7628
  // every saved file
7549
- branches: z37.array(savedBranchSchema)
7629
+ branches: z38.array(savedBranchSchema)
7550
7630
  // every saved line of work
7551
7631
  }).strict().refine((s) => s.ref === savedRefOf(s.id), { message: "must be the ref of this save", path: ["ref"] });
7552
7632
  function autonomyA13(accepted, carried) {
@@ -7600,23 +7680,23 @@ function recordedClaimLine(claim) {
7600
7680
  var NO_UNCHECKED_CLAIMS_LINE = "No agent said a check passes that Halfcycle never ran.";
7601
7681
 
7602
7682
  // ../events/dist/relayed-choices.js
7603
- import { z as z38 } from "zod";
7683
+ import { z as z39 } from "zod";
7604
7684
  var RELAYED_CHOICES_FILENAME = "relayed.json";
7605
7685
  var RELAYED_SAID_MAX = 80;
7606
- var relayedChoiceSchema = z38.object({
7686
+ var relayedChoiceSchema = z39.object({
7607
7687
  at: utcIso8601,
7608
- said: z38.string().min(1).max(RELAYED_SAID_MAX)
7688
+ said: z39.string().min(1).max(RELAYED_SAID_MAX)
7609
7689
  }).strict();
7610
- var relayedChoicesFileSchema = z38.object({
7690
+ var relayedChoicesFileSchema = z39.object({
7611
7691
  phase: phaseNameSchema,
7612
- entries: z38.array(relayedChoiceSchema)
7692
+ entries: z39.array(relayedChoiceSchema)
7613
7693
  }).strict();
7614
7694
  function relayedChoiceLineOf(entry) {
7615
7695
  return `On ${paceWhenOf(new Date(entry.at))} "${entry.said}" reached Halfcycle through Codex, which cannot show who typed it. Halfcycle could not confirm that you typed it, and it let this phase use more of your allowance.`;
7616
7696
  }
7617
7697
 
7618
7698
  // ../events/dist/tier-figures.js
7619
- import { z as z39 } from "zod";
7699
+ import { z as z40 } from "zod";
7620
7700
  var TIER_ORDER = ["fast", "standard", "deep"];
7621
7701
  function tierRankOf(tier) {
7622
7702
  return TIER_ORDER.indexOf(tier);
@@ -7649,15 +7729,15 @@ function routeNoSuchTaskLineOf(task) {
7649
7729
  return `"${task}" is not a task in the plan. Check the task id, then run this again.`;
7650
7730
  }
7651
7731
  var ROUTING_FILENAME = "routing.json";
7652
- var routingRecordSchema = z39.object({
7653
- phase: z39.string().min(1).max(200),
7654
- cheapStarted: z39.array(z39.string().min(1).max(200)).max(2e3)
7732
+ var routingRecordSchema = z40.object({
7733
+ phase: z40.string().min(1).max(200),
7734
+ cheapStarted: z40.array(z40.string().min(1).max(200)).max(2e3)
7655
7735
  }).strict();
7656
7736
  var PHASE_MODEL_NAME_MAX = 64;
7657
- var phaseModelsFileSchema = z39.object({
7658
- phase: z39.string().min(1).max(200),
7659
- openedAt: z39.string().min(1).max(64),
7660
- tasks: z39.record(z39.string().min(1).max(200), z39.record(z39.string().min(1).max(PHASE_MODEL_NAME_MAX), z39.number().int().min(0)))
7737
+ var phaseModelsFileSchema = z40.object({
7738
+ phase: z40.string().min(1).max(200),
7739
+ openedAt: z40.string().min(1).max(64),
7740
+ tasks: z40.record(z40.string().min(1).max(200), z40.record(z40.string().min(1).max(PHASE_MODEL_NAME_MAX), z40.number().int().min(0)))
7661
7741
  }).strict();
7662
7742
  function inAttemptOrder(attempts) {
7663
7743
  return [...attempts].sort((a, b) => a.attempt - b.attempt);
@@ -7715,9 +7795,9 @@ var CHECKS_IF_STILL_UNREPORTED = "If still unreported: Codex, approve in /hooks;
7715
7795
  var CHECKS_GONE_QUIET = "Halfcycle's checks have not reported from this machine lately.";
7716
7796
 
7717
7797
  // dist/engagement-credential.js
7718
- import { chmodSync as chmodSync2, mkdirSync as mkdirSync2, readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "node:fs";
7798
+ import { chmodSync as chmodSync2, mkdirSync as mkdirSync2, readFileSync as readFileSync3, writeFileSync as writeFileSync2 } from "node:fs";
7719
7799
  import { platform as platform2 } from "node:os";
7720
- import { join as join3 } from "node:path";
7800
+ import { join as join4 } from "node:path";
7721
7801
 
7722
7802
  // dist/account-credential.js
7723
7803
  import { chmodSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
@@ -7776,6 +7856,46 @@ function forgetStoredCredential(serviceUrl, home) {
7776
7856
  return true;
7777
7857
  }
7778
7858
 
7859
+ // dist/own-version.js
7860
+ import { existsSync as existsSync2, readFileSync as readFileSync2 } from "node:fs";
7861
+ import { dirname as dirname2, join as join3 } from "node:path";
7862
+ import { fileURLToPath } from "node:url";
7863
+ var moduleDir = dirname2(fileURLToPath(import.meta.url));
7864
+ function findBundleRoot(startDir) {
7865
+ let candidate = startDir;
7866
+ for (let i = 0; i < 4; i++) {
7867
+ const manifest = join3(candidate, ".claude-plugin", "plugin.json");
7868
+ try {
7869
+ if (existsSync2(manifest))
7870
+ return candidate;
7871
+ } catch {
7872
+ }
7873
+ candidate = dirname2(candidate);
7874
+ }
7875
+ return join3(startDir, "..");
7876
+ }
7877
+ var BUNDLE_ROOT = findBundleRoot(moduleDir);
7878
+ function bundleVersion() {
7879
+ try {
7880
+ const pkg = JSON.parse(readFileSync2(join3(BUNDLE_ROOT, "package.json"), "utf-8"));
7881
+ return typeof pkg.version === "string" ? pkg.version : void 0;
7882
+ } catch {
7883
+ return void 0;
7884
+ }
7885
+ }
7886
+ var cachedVersion;
7887
+ function ownVersion() {
7888
+ if (cachedVersion === void 0)
7889
+ cachedVersion = { value: bundleVersion() };
7890
+ return cachedVersion.value;
7891
+ }
7892
+ function ownCommand() {
7893
+ return halfcycleCommand(ownVersion());
7894
+ }
7895
+ function ownBareCommand() {
7896
+ return bareHalfcycleCommand(ownVersion());
7897
+ }
7898
+
7779
7899
  // dist/engagement-credential.js
7780
7900
  var ENGAGEMENTS_DIR = ENGAGEMENTS_DIR_NAME;
7781
7901
  var ENV_FILENAME = ENGAGEMENT_ENV_FILENAME;
@@ -7783,7 +7903,7 @@ var HALFCYCLE_DIR = HALFCYCLE_DIR_NAME;
7783
7903
  var InvalidEngagementIdError = class extends Error {
7784
7904
  constructor(engagementId) {
7785
7905
  const shown2 = JSON.stringify(engagementId.length > 80 ? `${engagementId.slice(0, 80)}\u2026` : engagementId);
7786
- super(`${shown2} is not a Halfcycle project id, so it cannot name a folder on this machine and nothing was written. A project id looks like 3f2a1c4e-8b7d-4e2f-9a61-0c5d7e8f9b10. If it is the "${PIN_ENGAGEMENT_ID_FIELD}" in .halfcycle/bundle.json, delete that file and run "npx halfcycle" again: this repository is then set up as a new Halfcycle project.`);
7906
+ super(`${shown2} is not a Halfcycle project id, so it cannot name a folder on this machine and nothing was written. A project id looks like 3f2a1c4e-8b7d-4e2f-9a61-0c5d7e8f9b10. If it is the "${PIN_ENGAGEMENT_ID_FIELD}" in .halfcycle/bundle.json, delete that file and run "${ownBareCommand()}" again: this repository is then set up as a new Halfcycle project.`);
7787
7907
  this.name = "InvalidEngagementIdError";
7788
7908
  }
7789
7909
  };
@@ -7793,13 +7913,13 @@ function assertEngagementId(engagementId) {
7793
7913
  }
7794
7914
  function engagementStateDir(engagementId, home) {
7795
7915
  assertEngagementId(engagementId);
7796
- return join3(halfcycleHome(home), ENGAGEMENTS_DIR, engagementId);
7916
+ return join4(halfcycleHome(home), ENGAGEMENTS_DIR, engagementId);
7797
7917
  }
7798
7918
  function engagementEnvPath(engagementId, home) {
7799
- return join3(engagementStateDir(engagementId, home), ENV_FILENAME);
7919
+ return join4(engagementStateDir(engagementId, home), ENV_FILENAME);
7800
7920
  }
7801
7921
  function notThisAccountMarkerPath(engagementId, home) {
7802
- return join3(engagementStateDir(engagementId, home), NOT_THIS_ACCOUNT_MARKER_FILENAME);
7922
+ return join4(engagementStateDir(engagementId, home), NOT_THIS_ACCOUNT_MARKER_FILENAME);
7803
7923
  }
7804
7924
  function writeNotThisAccountMarker(engagementId, home) {
7805
7925
  const dir = engagementStateDir(engagementId, home);
@@ -7829,7 +7949,7 @@ function reconcileEnvText2(existing, values, keys = ENGAGEMENT_ENV_KEYS) {
7829
7949
  }
7830
7950
  function readEngagementEnv(engagementId, home) {
7831
7951
  try {
7832
- return parseEnvText(readFileSync2(engagementEnvPath(engagementId, home), "utf-8"));
7952
+ return parseEnvText(readFileSync3(engagementEnvPath(engagementId, home), "utf-8"));
7833
7953
  } catch {
7834
7954
  return null;
7835
7955
  }
@@ -7839,7 +7959,7 @@ function writeEngagementEnv(engagementId, values, home, keys = ENGAGEMENT_ENV_KE
7839
7959
  const dir = engagementStateDir(engagementId, home);
7840
7960
  let existing;
7841
7961
  try {
7842
- existing = readFileSync2(path, "utf-8");
7962
+ existing = readFileSync3(path, "utf-8");
7843
7963
  } catch {
7844
7964
  existing = null;
7845
7965
  }
@@ -7870,7 +7990,7 @@ function engagementResolutionShell() {
7870
7990
  return `# --- Halfcycle credential resolution (generated; do not edit) ----------------
7871
7991
  # THE CREDENTIAL IS NOT IN THIS REPOSITORY. It lives at
7872
7992
  # $HOME/${HALFCYCLE_DIR}/${ENGAGEMENTS_DIR}/<engagement-id>/${ENV_FILENAME}
7873
- # written owner-only by \`npx halfcycle\`. What this repository holds is the
7993
+ # written owner-only by \`${bareHalfcycleCommand()}\`. What this repository holds is the
7874
7994
  # engagement id, in the committed .halfcycle/bundle.json. So: read the id out of
7875
7995
  # the pin, then name the file. There is no jq here and node is not dependable on a
7876
7996
  # desktop session's PATH, so the id is cut out with sed \u2014 after flattening the
@@ -7924,8 +8044,8 @@ halfcycle_env_file() {
7924
8044
 
7925
8045
  // dist/identity.js
7926
8046
  import { execFileSync } from "node:child_process";
7927
- import { existsSync as existsSync2, linkSync, mkdirSync as mkdirSync3, readFileSync as readFileSync3, renameSync, rmSync, writeFileSync as writeFileSync3 } from "node:fs";
7928
- import { dirname as dirname2, isAbsolute, join as join4 } from "node:path";
8047
+ import { existsSync as existsSync3, linkSync, mkdirSync as mkdirSync3, readFileSync as readFileSync4, renameSync, rmSync, writeFileSync as writeFileSync3 } from "node:fs";
8048
+ import { dirname as dirname3, isAbsolute, join as join5 } from "node:path";
7929
8049
  import { randomUUID } from "node:crypto";
7930
8050
  var PROJECT_IDENTITY_NOTE = 'projectId is NOT the engagement id \u2014 it identifies this repository/worktree to Halfcycle, not an engagement. The engagement id lives in .halfcycle/bundle.json, in its "engagementId" field.';
7931
8051
  var PROJECT_IDENTITY_REL = ".halfcycle/project.json";
@@ -8056,7 +8176,7 @@ function readIgnoredPaths(targetRepoRoot, paths) {
8056
8176
  const toplevel = git(targetRepoRoot, ["rev-parse", "--show-toplevel"]);
8057
8177
  if (toplevel === null)
8058
8178
  return null;
8059
- return ignored.map((entry) => isAbsolute(entry.source) ? entry : { ...entry, source: join4(toplevel, entry.source) });
8179
+ return ignored.map((entry) => isAbsolute(entry.source) ? entry : { ...entry, source: join5(toplevel, entry.source) });
8060
8180
  }
8061
8181
  function mintIdentity(targetRepoRoot) {
8062
8182
  return {
@@ -8067,10 +8187,10 @@ function mintIdentity(targetRepoRoot) {
8067
8187
  };
8068
8188
  }
8069
8189
  function plannedRemoteRepair(targetRepoRoot) {
8070
- const path = join4(targetRepoRoot, PROJECT_IDENTITY_REL);
8190
+ const path = join5(targetRepoRoot, PROJECT_IDENTITY_REL);
8071
8191
  let onDisk;
8072
8192
  try {
8073
- const parsed = JSON.parse(readFileSync3(path, "utf-8"));
8193
+ const parsed = JSON.parse(readFileSync4(path, "utf-8"));
8074
8194
  if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed))
8075
8195
  return null;
8076
8196
  onDisk = parsed;
@@ -8089,7 +8209,7 @@ function plannedRemoteRepair(targetRepoRoot) {
8089
8209
  });
8090
8210
  }
8091
8211
  function replaceProjectIdentityFile(targetRepoRoot, text) {
8092
- const path = join4(targetRepoRoot, PROJECT_IDENTITY_REL);
8212
+ const path = join5(targetRepoRoot, PROJECT_IDENTITY_REL);
8093
8213
  const temporary = `${path}.${process.pid}.${randomUUID()}.tmp`;
8094
8214
  try {
8095
8215
  writeFileSync3(temporary, text, { encoding: "utf-8", flag: "wx" });
@@ -8100,8 +8220,8 @@ function replaceProjectIdentityFile(targetRepoRoot, text) {
8100
8220
  }
8101
8221
  }
8102
8222
  function planProjectIdentity(targetRepoRoot) {
8103
- const path = join4(targetRepoRoot, PROJECT_IDENTITY_REL);
8104
- if (!existsSync2(path)) {
8223
+ const path = join5(targetRepoRoot, PROJECT_IDENTITY_REL);
8224
+ if (!existsSync3(path)) {
8105
8225
  return { action: "add", after: serializeProjectIdentity(mintIdentity(targetRepoRoot)) };
8106
8226
  }
8107
8227
  const repaired = plannedRemoteRepair(targetRepoRoot);
@@ -8109,7 +8229,7 @@ function planProjectIdentity(targetRepoRoot) {
8109
8229
  return { action: "change", after: repaired };
8110
8230
  let current;
8111
8231
  try {
8112
- current = readFileSync3(path, "utf-8");
8232
+ current = readFileSync4(path, "utf-8");
8113
8233
  } catch {
8114
8234
  current = null;
8115
8235
  }
@@ -8119,8 +8239,8 @@ function serializeProjectIdentity(identity) {
8119
8239
  return JSON.stringify(identity, null, 2) + "\n";
8120
8240
  }
8121
8241
  function createProjectIdentityFile(targetRepoRoot, text) {
8122
- const path = join4(targetRepoRoot, PROJECT_IDENTITY_REL);
8123
- mkdirSync3(dirname2(path), { recursive: true });
8242
+ const path = join5(targetRepoRoot, PROJECT_IDENTITY_REL);
8243
+ mkdirSync3(dirname3(path), { recursive: true });
8124
8244
  const temporary = `${path}.${process.pid}.${randomUUID()}.tmp`;
8125
8245
  try {
8126
8246
  writeFileSync3(temporary, text ?? serializeProjectIdentity(mintIdentity(targetRepoRoot)), {
@@ -8139,8 +8259,8 @@ function createProjectIdentityFile(targetRepoRoot, text) {
8139
8259
  }
8140
8260
 
8141
8261
  // dist/install-plan.js
8142
- import { existsSync as existsSync4, readFileSync as readFileSync5 } from "node:fs";
8143
- import { join as join6, posix } from "node:path";
8262
+ import { existsSync as existsSync5, readFileSync as readFileSync6 } from "node:fs";
8263
+ import { join as join7, posix } from "node:path";
8144
8264
 
8145
8265
  // dist/install-manifest.js
8146
8266
  import { Buffer as Buffer2 } from "node:buffer";
@@ -8491,8 +8611,8 @@ function union(a, b) {
8491
8611
  }
8492
8612
 
8493
8613
  // dist/scan.js
8494
- import { existsSync as existsSync3, mkdirSync as mkdirSync4, readFileSync as readFileSync4, writeFileSync as writeFileSync4 } from "node:fs";
8495
- import { join as join5 } from "node:path";
8614
+ import { existsSync as existsSync4, mkdirSync as mkdirSync4, readFileSync as readFileSync5, writeFileSync as writeFileSync4 } from "node:fs";
8615
+ import { join as join6 } from "node:path";
8496
8616
  var HALFCYCLE_STATE_FORMAT = "halfcycle-state-file/v1";
8497
8617
  var HALFCYCLE_STATE_NOTE = [
8498
8618
  "This file is a local cache written ONCE when Halfcycle was installed. It is not a",
@@ -8515,7 +8635,7 @@ var LAYER_ARTEFACT_PATHS = {
8515
8635
  function scanLayers(targetRepo, scannedAt = (/* @__PURE__ */ new Date()).toISOString()) {
8516
8636
  const layers = {};
8517
8637
  for (const [layer, paths] of Object.entries(LAYER_ARTEFACT_PATHS)) {
8518
- const found = paths.filter((p) => existsSync3(join5(targetRepo, p)));
8638
+ const found = paths.filter((p) => existsSync4(join6(targetRepo, p)));
8519
8639
  if (found.length > 0) {
8520
8640
  layers[layer] = {
8521
8641
  value: "asserted",
@@ -8529,17 +8649,17 @@ function scanLayers(targetRepo, scannedAt = (/* @__PURE__ */ new Date()).toISOSt
8529
8649
  }
8530
8650
  var BOOTSTRAP_STATE_REL = ".halfcycle/state.json";
8531
8651
  function planBootstrapScan(targetRepo) {
8532
- const statePath = join5(targetRepo, BOOTSTRAP_STATE_REL);
8533
- if (existsSync3(statePath)) {
8534
- const existing = JSON.parse(readFileSync4(statePath, "utf-8"));
8652
+ const statePath = join6(targetRepo, BOOTSTRAP_STATE_REL);
8653
+ if (existsSync4(statePath)) {
8654
+ const existing = JSON.parse(readFileSync5(statePath, "utf-8"));
8535
8655
  return { state: existing, text: null };
8536
8656
  }
8537
8657
  const state = scanLayers(targetRepo);
8538
8658
  return { state, text: JSON.stringify(state, null, 2) + "\n" };
8539
8659
  }
8540
8660
  function writeBootstrapState(targetRepo, text) {
8541
- mkdirSync4(join5(targetRepo, ".halfcycle"), { recursive: true });
8542
- writeFileSync4(join5(targetRepo, BOOTSTRAP_STATE_REL), text, "utf-8");
8661
+ mkdirSync4(join6(targetRepo, ".halfcycle"), { recursive: true });
8662
+ writeFileSync4(join6(targetRepo, BOOTSTRAP_STATE_REL), text, "utf-8");
8543
8663
  }
8544
8664
 
8545
8665
  // dist/install-plan.js
@@ -8689,12 +8809,12 @@ function planInstall(options) {
8689
8809
  throw new Error("[halfcycle] a plan for a project needs its kind as well as its id.");
8690
8810
  }
8691
8811
  }
8692
- if (!existsSync4(targetRepo)) {
8812
+ if (!existsSync5(targetRepo)) {
8693
8813
  throw new Error(`[halfcycle] Target repo does not exist: ${targetRepo}`);
8694
8814
  }
8695
8815
  const writeSet = closedWriteSet();
8696
8816
  const previous = readPreviousManifest(targetRepo);
8697
- const dirsBefore = new Set([...writeSet.dirs].filter((dir) => existsSync4(join6(targetRepo, dir))));
8817
+ const dirsBefore = new Set([...writeSet.dirs].filter((dir) => existsSync5(join7(targetRepo, dir))));
8698
8818
  const { version, commands } = planCommandStubs(targetRepo, previous, writeSet);
8699
8819
  const codexSkills = planCodexSkills(targetRepo, previous, writeSet);
8700
8820
  const vendored = planVendoredBinary(targetRepo);
@@ -8716,7 +8836,7 @@ function planInstall(options) {
8716
8836
  const gitattributes = planGitattributes(targetRepo);
8717
8837
  const identity = planProjectIdentity(targetRepo);
8718
8838
  if (identity.after === null)
8719
- readFileSync5(join6(targetRepo, PROJECT_IDENTITY_REL));
8839
+ readFileSync6(join7(targetRepo, PROJECT_IDENTITY_REL));
8720
8840
  const credentialPlan = !preview && credential !== void 0 ? planEngagementCredential(credential, engagementId, home) : null;
8721
8841
  const credentialWrite = credentialPlan === null ? null : { outcome: credentialPlan.outcome, existed: credentialPlan.existed, path: engagementEnvPath(engagementId, home) };
8722
8842
  const storedAfter = credentialPlan?.storedAfter ?? (preview ? Object.fromEntries(ENGAGEMENT_ENV_KEYS.map((key) => [key, "supplied at sign-in"])) : readEngagementEnv(engagementId, home) ?? {});
@@ -8849,7 +8969,7 @@ function entriesOf(steps, record2) {
8849
8969
  }
8850
8970
  };
8851
8971
  const pending = (rel) => {
8852
- const there = existsSync4(join6(targetRepo, rel));
8972
+ const there = existsSync5(join7(targetRepo, rel));
8853
8973
  entries.push({ path: rel, action: there ? "change" : "add", kind: kindOf(rel), bytes: PENDING });
8854
8974
  };
8855
8975
  steps.commands.forEach(fromFile);
@@ -8897,7 +9017,7 @@ function entriesOf(steps, record2) {
8897
9017
  action: "leave",
8898
9018
  kind: "codex-server",
8899
9019
  bytes: null,
8900
- ...existsSync4(join6(targetRepo, CODEX_CONFIG_REL2)) ? { yours: true } : {}
9020
+ ...existsSync5(join7(targetRepo, CODEX_CONFIG_REL2)) ? { yours: true } : {}
8901
9021
  });
8902
9022
  break;
8903
9023
  }
@@ -8976,10 +9096,10 @@ function applyInstallPlan(plan) {
8976
9096
  putPlannedCodexHooks(targetRepo, steps.codexHooks);
8977
9097
  putPlannedCodexConfig(targetRepo, steps.codexConfig);
8978
9098
  const gitignore = putPlannedGitignore(targetRepo, steps.gitignore);
8979
- const gitignorePath = join6(targetRepo, GITIGNORE_REL);
8980
- const gitignoreAfter = existsSync4(gitignorePath) ? readFileSync5(gitignorePath, "utf-8") : null;
9099
+ const gitignorePath = join7(targetRepo, GITIGNORE_REL);
9100
+ const gitignoreAfter = existsSync5(gitignorePath) ? readFileSync6(gitignorePath, "utf-8") : null;
8981
9101
  const gitattributes = putPlannedGitattributes(targetRepo, steps.gitattributes);
8982
- const gitattributesAfter = readTextOrNull(join6(targetRepo, GITATTRIBUTES_REL));
9102
+ const gitattributesAfter = readTextOrNull(join7(targetRepo, GITATTRIBUTES_REL));
8983
9103
  writeProjectIdentity(targetRepo, steps.identity);
8984
9104
  const credential = steps.credential;
8985
9105
  const credentialOutcome = steps.credentialWrite !== null && credential !== void 0 ? writeEngagementCredential(credential, engagementId, steps.home) : null;
@@ -9000,9 +9120,9 @@ function applyInstallPlan(plan) {
9000
9120
  gitattributesAfter,
9001
9121
  credential: credentialOutcome,
9002
9122
  legacy,
9003
- bytesAfter: (rel) => readFileSync5(join6(targetRepo, rel)),
9004
- dirExistsAfter: (dir) => existsSync4(join6(targetRepo, dir)),
9005
- stateExistsAfter: existsSync4(join6(targetRepo, BOOTSTRAP_STATE_REL))
9123
+ bytesAfter: (rel) => readFileSync6(join7(targetRepo, rel)),
9124
+ dirExistsAfter: (dir) => existsSync5(join7(targetRepo, dir)),
9125
+ stateExistsAfter: existsSync5(join7(targetRepo, BOOTSTRAP_STATE_REL))
9006
9126
  });
9007
9127
  const record2 = planInstallRecord(targetRepo, manifestText);
9008
9128
  putPlannedFile(targetRepo, record2);
@@ -9360,8 +9480,8 @@ function crewProfileRel(agent, callsign) {
9360
9480
  }
9361
9481
  function crewProfileTarget(targetRepo, rel) {
9362
9482
  const folder2 = rel.startsWith(`${CLAUDE_AGENTS_DIR}/`) ? CLAUDE_AGENTS_DIR : CODEX_AGENTS_DIR;
9363
- const target = resolve(join7(targetRepo, rel));
9364
- if (dirname3(target) !== resolve(targetRepo, folder2)) {
9483
+ const target = resolve(join8(targetRepo, rel));
9484
+ if (dirname4(target) !== resolve(targetRepo, folder2)) {
9365
9485
  throw new Error(`[halfcycle] crew profile "${rel}" does not sit directly in ${folder2}.`);
9366
9486
  }
9367
9487
  return target;
@@ -9444,41 +9564,25 @@ function renderCrewProfiles(roster = CREW_ROSTER) {
9444
9564
  ])
9445
9565
  ];
9446
9566
  }
9447
- var __filename = fileURLToPath(import.meta.url);
9448
- var __dirname = dirname3(__filename);
9449
- function findBundleRoot(startDir) {
9450
- let candidate = startDir;
9451
- for (let i = 0; i < 4; i++) {
9452
- const manifest = join7(candidate, ".claude-plugin", "plugin.json");
9453
- try {
9454
- if (existsSync5(manifest))
9455
- return candidate;
9456
- } catch {
9457
- }
9458
- candidate = dirname3(candidate);
9459
- }
9460
- return join7(startDir, "..");
9461
- }
9462
- var BUNDLE_ROOT = findBundleRoot(__dirname);
9463
9567
  function resolveVendoredBinary() {
9464
9568
  const candidates = [
9465
- join7(BUNDLE_ROOT, "bin", "bin.bundle.mjs"),
9466
- join7(BUNDLE_ROOT, "..", "runner", "dist", "bin.bundle.mjs")
9569
+ join8(BUNDLE_ROOT, "bin", "bin.bundle.mjs"),
9570
+ join8(BUNDLE_ROOT, "..", "runner", "dist", "bin.bundle.mjs")
9467
9571
  ];
9468
9572
  for (const candidate of candidates) {
9469
- if (existsSync5(candidate))
9573
+ if (existsSync6(candidate))
9470
9574
  return candidate;
9471
9575
  }
9472
9576
  throw new Error(`[halfcycle] Cannot find the self-contained guard binary bin.bundle.mjs. This copy of the halfcycle package is incomplete \u2014 reinstall it, or rebuild it if you are working on it. Looked in: ${candidates.join(", ")}.`);
9473
9577
  }
9474
9578
  function resolvePreFilterShell() {
9475
9579
  const candidates = [
9476
- join7(BUNDLE_ROOT, "bin", PRE_FILTER_SHELL_FILENAME),
9477
- join7(BUNDLE_ROOT, "..", "runner", "dist", PRE_FILTER_SHELL_FILENAME)
9580
+ join8(BUNDLE_ROOT, "bin", PRE_FILTER_SHELL_FILENAME),
9581
+ join8(BUNDLE_ROOT, "..", "runner", "dist", PRE_FILTER_SHELL_FILENAME)
9478
9582
  ];
9479
9583
  for (const candidate of candidates) {
9480
- if (existsSync5(candidate))
9481
- return readFileSync6(candidate, "utf-8");
9584
+ if (existsSync6(candidate))
9585
+ return readFileSync7(candidate, "utf-8");
9482
9586
  }
9483
9587
  throw new Error(`[halfcycle] Cannot find ${PRE_FILTER_SHELL_FILENAME}. This copy of the halfcycle package is incomplete \u2014 reinstall it, or rebuild it if you are working on it. Looked in: ${candidates.join(", ")}.`);
9484
9588
  }
@@ -9517,7 +9621,7 @@ function isAllowlisted(targetRelPath) {
9517
9621
  var CREW_ROSTER_REL = ".halfcycle/crew.json";
9518
9622
  var CAPTURED_INDEX_REL = "test/fixtures/captured/manifest.json";
9519
9623
  function capturedIndexScaffold() {
9520
- return join7(BUNDLE_ROOT, "scaffolding", "test", "fixtures", "captured", "manifest.json");
9624
+ return join8(BUNDLE_ROOT, "scaffolding", "test", "fixtures", "captured", "manifest.json");
9521
9625
  }
9522
9626
  var GITIGNORE_REL = ".gitignore";
9523
9627
  var GITATTRIBUTES_REL = ".gitattributes";
@@ -9592,14 +9696,15 @@ function closedWriteSet() {
9592
9696
  function sha256OfWhatThisVersionWrites(commands) {
9593
9697
  const out = /* @__PURE__ */ new Map();
9594
9698
  const fromFile = (rel, src) => {
9595
- if (existsSync5(src))
9596
- out.set(rel, fileSha256(readFileSync6(src)));
9699
+ if (existsSync6(src))
9700
+ out.set(rel, fileSha256(readFileSync7(src)));
9597
9701
  };
9598
9702
  for (const cmd of commands) {
9599
- const srcFile = join7(BUNDLE_ROOT, ".claude-plugin", cmd.path);
9600
- fromFile(commandStubRel(cmd.name), srcFile);
9601
- if (existsSync5(srcFile)) {
9602
- out.set(skillRel(cmd.name), fileSha256(Buffer.from(codexSkillContent(cmd.name, readFileSync6(srcFile, "utf-8")), "utf-8")));
9703
+ const srcFile = join8(BUNDLE_ROOT, ".claude-plugin", cmd.path);
9704
+ if (existsSync6(srcFile)) {
9705
+ const text = commandFileText(readFileSync7(srcFile, "utf-8"), ownVersion());
9706
+ out.set(commandStubRel(cmd.name), fileSha256(Buffer.from(text, "utf-8")));
9707
+ out.set(skillRel(cmd.name), fileSha256(Buffer.from(codexSkillContent(cmd.name, text), "utf-8")));
9603
9708
  }
9604
9709
  }
9605
9710
  for (const [rel, content] of hookScriptsThisVersionWrites())
@@ -9616,7 +9721,7 @@ function bytesAreOurs(path, bytes, recorded, writeSet) {
9616
9721
  }
9617
9722
  function readPreviousManifest(targetRepo) {
9618
9723
  try {
9619
- return parseManifest(readFileSync6(join7(targetRepo, INSTALL_MANIFEST_REL), "utf-8"));
9724
+ return parseManifest(readFileSync7(join8(targetRepo, INSTALL_MANIFEST_REL), "utf-8"));
9620
9725
  } catch {
9621
9726
  return null;
9622
9727
  }
@@ -9720,8 +9825,8 @@ function findNotShared(targetRepo, manifest, collidedPaths) {
9720
9825
  for (let dir = posix2.dirname(rel); dir !== "."; dir = posix2.dirname(dir)) {
9721
9826
  let plain = dirVerdicts.get(dir);
9722
9827
  if (plain === void 0) {
9723
- const abs = join7(targetRepo, dir);
9724
- plain = !lstatOrNull(abs)?.isSymbolicLink() && lstatOrNull(join7(abs, ".git")) === null;
9828
+ const abs = join8(targetRepo, dir);
9829
+ plain = !lstatOrNull(abs)?.isSymbolicLink() && lstatOrNull(join8(abs, ".git")) === null;
9725
9830
  dirVerdicts.set(dir, plain);
9726
9831
  }
9727
9832
  if (!plain)
@@ -9735,7 +9840,7 @@ function findNotShared(targetRepo, manifest, collidedPaths) {
9735
9840
  ...merged,
9736
9841
  INSTALL_MANIFEST_REL
9737
9842
  ])
9738
- ].filter((rel) => !collided.has(rel)).filter((rel) => lstatOrNull(join7(targetRepo, rel)) !== null).filter(travelsThroughPlainDirs).sort();
9843
+ ].filter((rel) => !collided.has(rel)).filter((rel) => lstatOrNull(join8(targetRepo, rel)) !== null).filter(travelsThroughPlainDirs).sort();
9739
9844
  const ignored = readIgnoredPaths(targetRepo, paths);
9740
9845
  if (ignored === null)
9741
9846
  return null;
@@ -9758,7 +9863,7 @@ function lstatOrNull(path) {
9758
9863
  }
9759
9864
  function readFileOrNull(path) {
9760
9865
  try {
9761
- return readFileSync6(path, "utf-8");
9866
+ return readFileSync7(path, "utf-8");
9762
9867
  } catch {
9763
9868
  return null;
9764
9869
  }
@@ -9767,8 +9872,8 @@ function isPlainObject3(value) {
9767
9872
  return value !== null && typeof value === "object" && !Array.isArray(value);
9768
9873
  }
9769
9874
  function readPluginManifest() {
9770
- const manifestPath = join7(BUNDLE_ROOT, ".claude-plugin", "plugin.json");
9771
- const raw = readFileSync6(manifestPath, "utf-8");
9875
+ const manifestPath = join8(BUNDLE_ROOT, ".claude-plugin", "plugin.json");
9876
+ const raw = readFileSync7(manifestPath, "utf-8");
9772
9877
  return JSON.parse(raw);
9773
9878
  }
9774
9879
  function parseCommandStub(raw, name) {
@@ -9811,6 +9916,9 @@ function codexSkillBody(name, sourceBody) {
9811
9916
  }
9812
9917
  return transformed;
9813
9918
  }
9919
+ function commandFileText(source, version) {
9920
+ return rewriteHalfcycleCommands(source, releaseTagOfVersion(version));
9921
+ }
9814
9922
  function codexSkillContent(name, sourceRaw) {
9815
9923
  const { description, body } = parseCommandStub(sourceRaw, name);
9816
9924
  const transformed = codexSkillBody(name, body);
@@ -9823,14 +9931,14 @@ ${transformed}`;
9823
9931
  function planManifestCommands(manifest, targetRepo, previous, writeSet) {
9824
9932
  const recordedFiles = new Map((previous?.files ?? []).filter((file) => writeSet.files.has(file.path)).map((file) => [file.path, file]));
9825
9933
  return (manifest.commands ?? []).map((cmd) => {
9826
- const srcFile = join7(BUNDLE_ROOT, ".claude-plugin", cmd.path);
9827
- if (!existsSync5(srcFile)) {
9934
+ const srcFile = join8(BUNDLE_ROOT, ".claude-plugin", cmd.path);
9935
+ if (!existsSync6(srcFile)) {
9828
9936
  throw new Error(`[halfcycle] plugin.json declares command "${cmd.name}" at "${cmd.path}", but no file exists there (${srcFile}). The manifest and commands/ must agree.`);
9829
9937
  }
9830
9938
  const rel = commandStubRel(cmd.name);
9831
- const content = readFileSync6(srcFile, "utf-8");
9939
+ const content = commandFileText(readFileSync7(srcFile, "utf-8"), ownVersion());
9832
9940
  const recorded = recordedFiles.get(rel);
9833
- const planned = decideCollisionSafe(join7(targetRepo, rel), targetRepo, content, (current) => bytesAreOurs(rel, current, recorded, writeSet));
9941
+ const planned = decideCollisionSafe(join8(targetRepo, rel), targetRepo, content, (current) => bytesAreOurs(rel, current, recorded, writeSet));
9834
9942
  return { ...planned, editedCollided: planned.outcome === "collided" && recorded !== void 0 };
9835
9943
  });
9836
9944
  }
@@ -9840,8 +9948,8 @@ function putPlannedFile(targetRepoRoot, planned) {
9840
9948
  if (!isAllowlisted(planned.rel)) {
9841
9949
  throw new Error(`[halfcycle] Write-allowlist violation: attempted to write "${planned.rel}". Only the method surface and scaffolding paths are writable.`);
9842
9950
  }
9843
- const target = join7(targetRepoRoot, planned.rel);
9844
- mkdirSync5(dirname3(target), { recursive: true });
9951
+ const target = join8(targetRepoRoot, planned.rel);
9952
+ mkdirSync5(dirname4(target), { recursive: true });
9845
9953
  writeFileSync5(target, planned.text, "utf-8");
9846
9954
  }
9847
9955
  function writeAllowlisted(targetAbsPath, targetRepoRoot, content, writtenPaths) {
@@ -9851,7 +9959,7 @@ function writeAllowlisted(targetAbsPath, targetRepoRoot, content, writtenPaths)
9851
9959
  }
9852
9960
  if (readFileOrNull(targetAbsPath) === content)
9853
9961
  return;
9854
- mkdirSync5(dirname3(targetAbsPath), { recursive: true });
9962
+ mkdirSync5(dirname4(targetAbsPath), { recursive: true });
9855
9963
  writeFileSync5(targetAbsPath, content, "utf-8");
9856
9964
  writtenPaths.push(rel);
9857
9965
  }
@@ -9860,7 +9968,7 @@ function decideCollisionSafe(targetAbsPath, targetRepoRoot, content, isOurs) {
9860
9968
  if (!isAllowlisted(rel)) {
9861
9969
  throw new Error(`[halfcycle] Write-allowlist violation: attempted to write "${rel}". Only the method surface and scaffolding paths are writable.`);
9862
9970
  }
9863
- const current = existsSync5(targetAbsPath) ? readFileSync6(targetAbsPath) : null;
9971
+ const current = existsSync6(targetAbsPath) ? readFileSync7(targetAbsPath) : null;
9864
9972
  const text = inLineEndingOf(current?.toString("utf-8") ?? null, content);
9865
9973
  if (current === null)
9866
9974
  return { rel, text, outcome: "written", replacedExisting: false };
@@ -9888,7 +9996,7 @@ function decideOwned(targetAbsPath, targetRepoRoot, content) {
9888
9996
  const text = inLineEndingOf(current, content);
9889
9997
  if (current === text)
9890
9998
  return { rel, text, outcome: "skipped", replacedExisting: false };
9891
- return { rel, text, outcome: "written", replacedExisting: existsSync5(targetAbsPath) };
9999
+ return { rel, text, outcome: "written", replacedExisting: existsSync6(targetAbsPath) };
9892
10000
  }
9893
10001
  var GENERATED_HEADER_LINE_INDEX = 1;
9894
10002
  function decideOwnedGenerated(targetAbsPath, targetRepoRoot, content, generatedMarker) {
@@ -9896,9 +10004,9 @@ function decideOwnedGenerated(targetAbsPath, targetRepoRoot, content, generatedM
9896
10004
  if (!isAllowlisted(rel)) {
9897
10005
  throw new Error(`[halfcycle] Write-allowlist violation: attempted to write "${rel}". Only the method surface and scaffolding paths are writable.`);
9898
10006
  }
9899
- if (!existsSync5(targetAbsPath))
10007
+ if (!existsSync6(targetAbsPath))
9900
10008
  return { rel, text: content, outcome: "written", replacedExisting: false };
9901
- const current = readFileSync6(targetAbsPath, "utf-8");
10009
+ const current = readFileSync7(targetAbsPath, "utf-8");
9902
10010
  if (current === content)
9903
10011
  return { rel, text: content, outcome: "skipped", replacedExisting: false };
9904
10012
  const headerLine = current.split("\n")[GENERATED_HEADER_LINE_INDEX] ?? "";
@@ -9914,7 +10022,7 @@ function recordOwnedGenerated(report, planned) {
9914
10022
  return planned.outcome;
9915
10023
  }
9916
10024
  function recordOwned(report, targetAbsPath, targetRepoRoot, content, rel) {
9917
- const existedBefore = existsSync5(targetAbsPath);
10025
+ const existedBefore = existsSync6(targetAbsPath);
9918
10026
  const outcome = writeOwned(targetAbsPath, targetRepoRoot, content);
9919
10027
  record(report, outcome, rel);
9920
10028
  if (outcome === "written" && existedBefore)
@@ -9936,8 +10044,8 @@ var DENY_PATTERNS = [
9936
10044
  ];
9937
10045
  var USER_PROMPT_REMINDER_SENTENCE = "Where this project stands, and what order to work in, is recorded in the Halfcycle service and nowhere in this repository \u2014 call halfcycle_resolve before you answer or plan, because the files here will look complete and current and still be wrong about position.";
9938
10046
  function readExistingSettings(targetRepo) {
9939
- const settingsPath = join7(targetRepo, SETTINGS_REL);
9940
- return existsSync5(settingsPath) ? JSON.parse(readFileSync6(settingsPath, "utf-8")) : void 0;
10047
+ const settingsPath = join8(targetRepo, SETTINGS_REL);
10048
+ return existsSync6(settingsPath) ? JSON.parse(readFileSync7(settingsPath, "utf-8")) : void 0;
9941
10049
  }
9942
10050
  var SettingsParseRefused = class extends Error {
9943
10051
  constructor(message) {
@@ -9946,14 +10054,14 @@ var SettingsParseRefused = class extends Error {
9946
10054
  }
9947
10055
  };
9948
10056
  function settingsParseRefusal(targetRepo, err) {
9949
- const file = join7(targetRepo, SETTINGS_REL);
10057
+ const file = join8(targetRepo, SETTINGS_REL);
9950
10058
  const offset = /position (\d+)/.exec(err instanceof Error ? err.message : "")?.[1];
9951
10059
  let where = "";
9952
10060
  if (offset !== void 0) {
9953
- const before = readFileSync6(file, "utf-8").slice(0, Number(offset)).split("\n");
10061
+ const before = readFileSync7(file, "utf-8").slice(0, Number(offset)).split("\n");
9954
10062
  where = ` (line ${before.length}, column ${before[before.length - 1].length + 1})`;
9955
10063
  }
9956
- return `${file} could not be read as JSON${where} \u2014 fix it and run "npx halfcycle" again.`;
10064
+ return `${file} could not be read as JSON${where} \u2014 fix it and run "${ownBareCommand()}" again.`;
9957
10065
  }
9958
10066
  var SETTINGS_SCHEMA = "https://json.schemastore.org/claude-code-settings.json";
9959
10067
  var HALFCYCLE_STATUS_LINE = Object.freeze({
@@ -9961,18 +10069,18 @@ var HALFCYCLE_STATUS_LINE = Object.freeze({
9961
10069
  command: 'bash "$CLAUDE_PROJECT_DIR"/.claude/hooks/guard-runner.sh statusline'
9962
10070
  });
9963
10071
  function statusLineSetElsewhere(targetRepo, home = homedir3(), env = process.env) {
9964
- const places = [join7(targetRepo, ".claude", "settings.local.json"), join7(home, ".claude", "settings.json")];
10072
+ const places = [join8(targetRepo, ".claude", "settings.local.json"), join8(home, ".claude", "settings.json")];
9965
10073
  const configDir = env["CLAUDE_CONFIG_DIR"];
9966
10074
  if (configDir !== void 0 && configDir !== "") {
9967
- const expanded = configDir === "~" ? home : configDir.startsWith("~/") ? join7(home, configDir.slice(2)) : configDir;
9968
- places.push(join7(isAbsolute2(expanded) ? expanded : resolve(expanded), "settings.json"));
10075
+ const expanded = configDir === "~" ? home : configDir.startsWith("~/") ? join8(home, configDir.slice(2)) : configDir;
10076
+ places.push(join8(isAbsolute2(expanded) ? expanded : resolve(expanded), "settings.json"));
9969
10077
  }
9970
10078
  return places.some((path) => {
9971
- if (!existsSync5(path))
10079
+ if (!existsSync6(path))
9972
10080
  return false;
9973
10081
  let value;
9974
10082
  try {
9975
- value = JSON.parse(readFileSync6(path, "utf-8"));
10083
+ value = JSON.parse(readFileSync7(path, "utf-8"));
9976
10084
  } catch {
9977
10085
  return true;
9978
10086
  }
@@ -10446,12 +10554,12 @@ function removeRetiredGeneratedFile(targetRepo, rel, headerLineIndex, knownHeade
10446
10554
  return decided === "removed" ? removePlannedRetiredFile(targetRepo, rel) : decided;
10447
10555
  }
10448
10556
  function decideRetiredRemoval(targetRepo, rel, headerLineIndex, knownHeaderHashes) {
10449
- const path = join7(targetRepo, rel);
10450
- if (!existsSync5(path))
10557
+ const path = join8(targetRepo, rel);
10558
+ if (!existsSync6(path))
10451
10559
  return "absent";
10452
10560
  let raw;
10453
10561
  try {
10454
- raw = readFileSync6(path, "utf-8");
10562
+ raw = readFileSync7(path, "utf-8");
10455
10563
  } catch {
10456
10564
  return "failed";
10457
10565
  }
@@ -10465,7 +10573,7 @@ function decideRetiredRemoval(targetRepo, rel, headerLineIndex, knownHeaderHashe
10465
10573
  }
10466
10574
  function removePlannedRetiredFile(targetRepo, rel) {
10467
10575
  try {
10468
- rmSync2(join7(targetRepo, rel));
10576
+ rmSync2(join8(targetRepo, rel));
10469
10577
  } catch {
10470
10578
  return "failed";
10471
10579
  }
@@ -10486,7 +10594,7 @@ var MCP_HEADERS_HELPER_COMMAND = `node -e "${nodeFloorProgram([
10486
10594
  `const u=p.dirname(d);`,
10487
10595
  `if(u===d)break;`,
10488
10596
  `d=u}`,
10489
- `console.error('halfcycle: no ${VENDORED_BIN_REL} at or above '+process.cwd()+'; run npx halfcycle in the project folder');`,
10597
+ `console.error('halfcycle: no ${VENDORED_BIN_REL} at or above '+process.cwd()+'; run ${ownBareCommand()} in the project folder');`,
10490
10598
  `process.exit(1)`
10491
10599
  ].join(""))}"`;
10492
10600
  var CODEX_ADDRESS_PATTERN = /^https?:\/\/[A-Za-z0-9.-]+(:[0-9]{1,5})?(\/[A-Za-z0-9._-]+)*\/mcp$/;
@@ -10510,7 +10618,7 @@ function codexMcpHeadersHelperCommand(address) {
10510
10618
  `if(u===d)break;`,
10511
10619
  `d=u}`,
10512
10620
  `if(b===''){`,
10513
- `console.error('halfcycle: no ${VENDORED_BIN_REL} at or above '+process.cwd()+'; run npx halfcycle in the project folder');`,
10621
+ `console.error('halfcycle: no ${VENDORED_BIN_REL} at or above '+process.cwd()+'; run ${ownBareCommand()} in the project folder');`,
10514
10622
  `n()}else{`,
10515
10623
  `const r=c.spawnSync(process.execPath,[b,'mcp-headers','--url','${address}'],{stdio:['ignore','pipe','inherit']});`,
10516
10624
  `if(r.status===0&&r.stdout&&r.stdout.length){o=1;process.stdout.write(r.stdout)}else{n()}}`,
@@ -10848,9 +10956,9 @@ function gitignoreCovers(content) {
10848
10956
  return REQUIRED_GITIGNORE_ENTRIES.every((entry) => lines.has(entry));
10849
10957
  }
10850
10958
  function planGitignore(targetRepo) {
10851
- const gitignorePath = join7(targetRepo, GITIGNORE_REL);
10959
+ const gitignorePath = join8(targetRepo, GITIGNORE_REL);
10852
10960
  const header = GITIGNORE_HEADER;
10853
- const before = existsSync5(gitignorePath) ? readFileSync6(gitignorePath, "utf-8") : null;
10961
+ const before = existsSync6(gitignorePath) ? readFileSync7(gitignorePath, "utf-8") : null;
10854
10962
  if (before === null) {
10855
10963
  return { before, after: [header, ...REQUIRED_GITIGNORE_ENTRIES].join("\n") + "\n", outcome: "written" };
10856
10964
  }
@@ -10866,7 +10974,7 @@ function putPlannedGitignore(targetRepo, planned) {
10866
10974
  if (planned.outcome !== "written" || planned.after === null)
10867
10975
  return planned.outcome;
10868
10976
  try {
10869
- writeFileSync5(join7(targetRepo, GITIGNORE_REL), planned.after, "utf-8");
10977
+ writeFileSync5(join8(targetRepo, GITIGNORE_REL), planned.after, "utf-8");
10870
10978
  return "written";
10871
10979
  } catch {
10872
10980
  return "failed";
@@ -10875,7 +10983,7 @@ function putPlannedGitignore(targetRepo, planned) {
10875
10983
  var GITATTRIBUTES_HEADER = "# Halfcycle \u2014 generated scripts keep LF line endings";
10876
10984
  function readTextOrNull(path) {
10877
10985
  try {
10878
- return readFileSync6(path, "utf-8");
10986
+ return readFileSync7(path, "utf-8");
10879
10987
  } catch {
10880
10988
  return null;
10881
10989
  }
@@ -10885,12 +10993,12 @@ function lfRules(paths) {
10885
10993
  }
10886
10994
  var REQUIRED_GITATTRIBUTES_RULES = lfRules(Object.keys(OWNED_GENERATED_HEADERS));
10887
10995
  function planGitattributes(targetRepo) {
10888
- const path = join7(targetRepo, GITATTRIBUTES_REL);
10996
+ const path = join8(targetRepo, GITATTRIBUTES_REL);
10889
10997
  const rules = REQUIRED_GITATTRIBUTES_RULES;
10890
10998
  const before = readTextOrNull(path);
10891
10999
  if (lstatOrNull(path)?.isSymbolicLink() === true)
10892
11000
  return { before, after: before, outcome: "failed" };
10893
- if (!existsSync5(path)) {
11001
+ if (!existsSync6(path)) {
10894
11002
  return { before, after: [GITATTRIBUTES_HEADER, ...rules].join("\n") + "\n", outcome: "written" };
10895
11003
  }
10896
11004
  if (before === null)
@@ -10908,7 +11016,7 @@ function putPlannedGitattributes(targetRepo, planned) {
10908
11016
  if (planned.outcome !== "written" || planned.after === null)
10909
11017
  return planned.outcome;
10910
11018
  try {
10911
- writeAllowlisted(join7(targetRepo, GITATTRIBUTES_REL), targetRepo, planned.after, []);
11019
+ writeAllowlisted(join8(targetRepo, GITATTRIBUTES_REL), targetRepo, planned.after, []);
10912
11020
  return "written";
10913
11021
  } catch {
10914
11022
  return "failed";
@@ -10942,7 +11050,7 @@ function planBundlePin(targetRepoRoot, version, engagementId, engagementType, cr
10942
11050
  ...carriedMethodVersion !== void 0 ? { methodVersion: carriedMethodVersion } : {},
10943
11051
  ...projectPage !== void 0 ? { projectPage } : {}
10944
11052
  };
10945
- const current = readFileOrNull(join7(targetRepoRoot, BUNDLE_PIN_REL));
11053
+ const current = readFileOrNull(join8(targetRepoRoot, BUNDLE_PIN_REL));
10946
11054
  const text = inLineEndingOf(current, JSON.stringify(pin, null, 2) + "\n");
10947
11055
  return {
10948
11056
  rel: BUNDLE_PIN_REL,
@@ -10973,7 +11081,7 @@ function planBundlePinPreview(targetRepoRoot, version, home) {
10973
11081
  return planBundlePin(targetRepoRoot, version, existing.engagementId, existing.engagementType, void 0, void 0);
10974
11082
  }
10975
11083
  function writeBundlePin(targetRepoRoot, planned, writtenPaths) {
10976
- const pinPath = join7(targetRepoRoot, BUNDLE_PIN_REL);
11084
+ const pinPath = join8(targetRepoRoot, BUNDLE_PIN_REL);
10977
11085
  writeAllowlisted(pinPath, targetRepoRoot, planned.text, writtenPaths);
10978
11086
  }
10979
11087
  function updatePinnedMethodVersion(repoRoot, engagementId, version) {
@@ -10985,7 +11093,7 @@ function updatePinnedMethodVersion(repoRoot, engagementId, version) {
10985
11093
  }
10986
11094
  if (pin === null || pin.engagementId !== engagementId)
10987
11095
  return;
10988
- const pinPath = join7(repoRoot, BUNDLE_PIN_REL);
11096
+ const pinPath = join8(repoRoot, BUNDLE_PIN_REL);
10989
11097
  const updated = { ...pin, methodVersion: version };
10990
11098
  const pinText = inLineEndingOf(readFileOrNull(pinPath), JSON.stringify(updated, null, 2) + "\n");
10991
11099
  try {
@@ -11005,17 +11113,17 @@ function renderCrewRoster() {
11005
11113
  }
11006
11114
  function writeCrewRoster(targetRepoRoot, report) {
11007
11115
  const rendered = renderCrewRoster();
11008
- const crewPath = join7(targetRepoRoot, CREW_ROSTER_REL);
11116
+ const crewPath = join8(targetRepoRoot, CREW_ROSTER_REL);
11009
11117
  return recordOwned(report, crewPath, targetRepoRoot, rendered, CREW_ROSTER_REL);
11010
11118
  }
11011
11119
  function pinCreatedByAccountId(pin) {
11012
11120
  return nonEmpty(pin?.createdByAccountId) ?? nonEmpty(pin?.accountId);
11013
11121
  }
11014
11122
  function readBundlePin(targetRepoRoot) {
11015
- const pinPath = join7(targetRepoRoot, BUNDLE_PIN_REL);
11016
- if (!existsSync5(pinPath))
11123
+ const pinPath = join8(targetRepoRoot, BUNDLE_PIN_REL);
11124
+ if (!existsSync6(pinPath))
11017
11125
  return null;
11018
- return JSON.parse(readFileSync6(pinPath, "utf-8"));
11126
+ return JSON.parse(readFileSync7(pinPath, "utf-8"));
11019
11127
  }
11020
11128
  function readPinnedEngagement(targetRepoRoot, home) {
11021
11129
  const pin = readBundlePin(targetRepoRoot);
@@ -11038,7 +11146,7 @@ function readPinnedEngagement(targetRepoRoot, home) {
11038
11146
  }
11039
11147
  function readLegacyEnvLocal(targetRepoRoot) {
11040
11148
  try {
11041
- return parseEnvText(readFileSync6(join7(targetRepoRoot, LEGACY_ENV_LOCAL_REL), "utf-8"));
11149
+ return parseEnvText(readFileSync7(join8(targetRepoRoot, LEGACY_ENV_LOCAL_REL), "utf-8"));
11042
11150
  } catch {
11043
11151
  return null;
11044
11152
  }
@@ -11090,7 +11198,7 @@ function writeEngagementCredential(credential, engagementId, home) {
11090
11198
  function planEngagementCredential(credential, engagementId, home) {
11091
11199
  let existing;
11092
11200
  try {
11093
- existing = readFileSync6(engagementEnvPath(engagementId, home), "utf-8");
11201
+ existing = readFileSync7(engagementEnvPath(engagementId, home), "utf-8");
11094
11202
  } catch {
11095
11203
  existing = null;
11096
11204
  }
@@ -11133,12 +11241,12 @@ function engagementCredentialValues(credential, engagementId) {
11133
11241
  }
11134
11242
  var LEGACY_ENV_LOCAL_REL = ".env.local";
11135
11243
  function migrateLegacyEnvLocal(targetRepo, stored) {
11136
- const path = join7(targetRepo, LEGACY_ENV_LOCAL_REL);
11137
- if (!existsSync5(path))
11244
+ const path = join8(targetRepo, LEGACY_ENV_LOCAL_REL);
11245
+ if (!existsSync6(path))
11138
11246
  return { outcome: "absent", unbacked: [], missingForAdoption: [] };
11139
11247
  let raw;
11140
11248
  try {
11141
- raw = readFileSync6(path, "utf-8");
11249
+ raw = readFileSync7(path, "utf-8");
11142
11250
  } catch {
11143
11251
  return { outcome: "failed", unbacked: [], missingForAdoption: [] };
11144
11252
  }
@@ -11164,7 +11272,7 @@ function migrateLegacyEnvLocal(targetRepo, stored) {
11164
11272
  return { outcome: "stripped", unbacked: [], missingForAdoption: [], keptText: kept.join("\n") };
11165
11273
  }
11166
11274
  function applyLegacyMigration(targetRepo, decided) {
11167
- const path = join7(targetRepo, LEGACY_ENV_LOCAL_REL);
11275
+ const path = join8(targetRepo, LEGACY_ENV_LOCAL_REL);
11168
11276
  try {
11169
11277
  if (decided.outcome === "removed") {
11170
11278
  rmSync2(path);
@@ -11190,23 +11298,23 @@ function planCodexSkills(targetRepo, previous, writeSet) {
11190
11298
  const manifest = readPluginManifest();
11191
11299
  const recordedFiles = new Map((previous?.files ?? []).filter((file) => writeSet.files.has(file.path)).map((file) => [file.path, file]));
11192
11300
  return (manifest.commands ?? []).map((cmd) => {
11193
- const srcFile = join7(BUNDLE_ROOT, ".claude-plugin", cmd.path);
11194
- if (!existsSync5(srcFile)) {
11301
+ const srcFile = join8(BUNDLE_ROOT, ".claude-plugin", cmd.path);
11302
+ if (!existsSync6(srcFile)) {
11195
11303
  throw new Error(`[halfcycle] plugin.json declares command "${cmd.name}" at "${cmd.path}", but no file exists there (${srcFile}). The manifest and commands/ must agree.`);
11196
11304
  }
11197
11305
  const rel = skillRel(cmd.name);
11198
- const content = codexSkillContent(cmd.name, readFileSync6(srcFile, "utf-8"));
11306
+ const content = codexSkillContent(cmd.name, commandFileText(readFileSync7(srcFile, "utf-8"), ownVersion()));
11199
11307
  const recorded = recordedFiles.get(rel);
11200
- const planned = decideCollisionSafe(join7(targetRepo, rel), targetRepo, content, (current) => bytesAreOurs(rel, current, recorded, writeSet));
11308
+ const planned = decideCollisionSafe(join8(targetRepo, rel), targetRepo, content, (current) => bytesAreOurs(rel, current, recorded, writeSet));
11201
11309
  return { ...planned, editedCollided: planned.outcome === "collided" && recorded !== void 0 };
11202
11310
  });
11203
11311
  }
11204
11312
  function planVendoredBinary(targetRepo) {
11205
- return decideOwned(join7(targetRepo, ".halfcycle", "bin", "bin.bundle.mjs"), targetRepo, readFileSync6(resolveVendoredBinary(), "utf-8"));
11313
+ return decideOwned(join8(targetRepo, ".halfcycle", "bin", "bin.bundle.mjs"), targetRepo, readFileSync7(resolveVendoredBinary(), "utf-8"));
11206
11314
  }
11207
11315
  function planSettings(targetRepo, previous, home, env) {
11208
- const settingsPath = join7(targetRepo, SETTINGS_REL);
11209
- const preexisted = existsSync5(settingsPath);
11316
+ const settingsPath = join8(targetRepo, SETTINGS_REL);
11317
+ const preexisted = existsSync6(settingsPath);
11210
11318
  const generated = JSON.parse(generateSettingsJson());
11211
11319
  let existing;
11212
11320
  try {
@@ -11230,11 +11338,11 @@ function planSettings(targetRepo, previous, home, env) {
11230
11338
  return { file, preexisted, existing, generated, merged, retiredOurs };
11231
11339
  }
11232
11340
  function planHookScripts(targetRepo) {
11233
- return hookScriptsThisVersionWrites().map(([rel, content]) => decideOwnedGenerated(join7(targetRepo, rel), targetRepo, content, OWNED_GENERATED_HEADERS[rel]));
11341
+ return hookScriptsThisVersionWrites().map(([rel, content]) => decideOwnedGenerated(join8(targetRepo, rel), targetRepo, content, OWNED_GENERATED_HEADERS[rel]));
11234
11342
  }
11235
11343
  function planMcpRegistration(targetRepo, credential) {
11236
- const mcpPath = join7(targetRepo, MCP_REGISTRATION_REL);
11237
- const existing = existsSync5(mcpPath) ? readFileSync6(mcpPath, "utf-8") : null;
11344
+ const mcpPath = join8(targetRepo, MCP_REGISTRATION_REL);
11345
+ const existing = existsSync6(mcpPath) ? readFileSync7(mcpPath, "utf-8") : null;
11238
11346
  if (credential === void 0)
11239
11347
  return { kind: "none", existing };
11240
11348
  if (credential === "pending")
@@ -11246,11 +11354,11 @@ function planMcpRegistration(targetRepo, credential) {
11246
11354
  function putPlannedMcp(targetRepo, planned) {
11247
11355
  if (planned.kind !== "write" || planned.outcome === "skipped")
11248
11356
  return;
11249
- writeAllowlisted(join7(targetRepo, MCP_REGISTRATION_REL), targetRepo, planned.text, []);
11357
+ writeAllowlisted(join8(targetRepo, MCP_REGISTRATION_REL), targetRepo, planned.text, []);
11250
11358
  }
11251
11359
  function fileInFolderPlace(root, rel) {
11252
11360
  for (let dir = posix2.dirname(rel); dir !== "."; dir = posix2.dirname(dir)) {
11253
- const found = lstatOrNull(join7(root, dir));
11361
+ const found = lstatOrNull(join8(root, dir));
11254
11362
  if (found !== null && !found.isDirectory())
11255
11363
  return true;
11256
11364
  }
@@ -11259,7 +11367,7 @@ function fileInFolderPlace(root, rel) {
11259
11367
  function symlinkOnPath(root, rel) {
11260
11368
  let current = root;
11261
11369
  for (const segment of rel.split("/")) {
11262
- current = join7(current, segment);
11370
+ current = join8(current, segment);
11263
11371
  try {
11264
11372
  if (lstatSync(current).isSymbolicLink())
11265
11373
  return true;
@@ -11280,14 +11388,14 @@ function planCodexHooks(targetRepo) {
11280
11388
  });
11281
11389
  if (symlinkOnPath(targetRepo, CODEX_HOOKS_REL2))
11282
11390
  return leave("link", null);
11283
- const folder2 = lstatOrNull(join7(targetRepo, posix2.dirname(CODEX_HOOKS_REL2)));
11391
+ const folder2 = lstatOrNull(join8(targetRepo, posix2.dirname(CODEX_HOOKS_REL2)));
11284
11392
  if (folder2 !== null && !folder2.isDirectory())
11285
11393
  return leave("unreadable", null);
11286
- const path = join7(targetRepo, CODEX_HOOKS_REL2);
11394
+ const path = join8(targetRepo, CODEX_HOOKS_REL2);
11287
11395
  let before = null;
11288
11396
  if (lstatOrNull(path) !== null) {
11289
11397
  try {
11290
- before = readFileSync6(path, "utf-8");
11398
+ before = readFileSync7(path, "utf-8");
11291
11399
  } catch {
11292
11400
  return leave("unreadable", null);
11293
11401
  }
@@ -11321,13 +11429,13 @@ function planCodexHooks(targetRepo) {
11321
11429
  function putPlannedCodexHooks(targetRepo, planned) {
11322
11430
  if (planned.outcome !== "written" || planned.text === null)
11323
11431
  return;
11324
- writeAllowlisted(join7(targetRepo, CODEX_HOOKS_REL2), targetRepo, planned.text, []);
11432
+ writeAllowlisted(join8(targetRepo, CODEX_HOOKS_REL2), targetRepo, planned.text, []);
11325
11433
  }
11326
11434
  function readCodexConfig(targetRepo) {
11327
- const folder2 = lstatOrNull(join7(targetRepo, posix2.dirname(CODEX_CONFIG_REL2)));
11435
+ const folder2 = lstatOrNull(join8(targetRepo, posix2.dirname(CODEX_CONFIG_REL2)));
11328
11436
  if (folder2 !== null && !folder2.isDirectory())
11329
11437
  return "unreadable";
11330
- const path = join7(targetRepo, CODEX_CONFIG_REL2);
11438
+ const path = join8(targetRepo, CODEX_CONFIG_REL2);
11331
11439
  const stat = lstatOrNull(path);
11332
11440
  if (stat === null)
11333
11441
  return { text: null };
@@ -11335,7 +11443,7 @@ function readCodexConfig(targetRepo) {
11335
11443
  return "unreadable";
11336
11444
  let bytes;
11337
11445
  try {
11338
- bytes = readFileSync6(path);
11446
+ bytes = readFileSync7(path);
11339
11447
  } catch {
11340
11448
  return "unreadable";
11341
11449
  }
@@ -11381,10 +11489,10 @@ function planCodexConfig(targetRepo, credential) {
11381
11489
  function putPlannedCodexConfig(targetRepo, planned) {
11382
11490
  if (planned.outcome !== "written" || planned.text === null)
11383
11491
  return;
11384
- writeAllowlisted(join7(targetRepo, CODEX_CONFIG_REL2), targetRepo, planned.text, []);
11492
+ writeAllowlisted(join8(targetRepo, CODEX_CONFIG_REL2), targetRepo, planned.text, []);
11385
11493
  }
11386
11494
  function planCrewRoster(targetRepo) {
11387
- return decideOwned(join7(targetRepo, CREW_ROSTER_REL), targetRepo, renderCrewRoster());
11495
+ return decideOwned(join8(targetRepo, CREW_ROSTER_REL), targetRepo, renderCrewRoster());
11388
11496
  }
11389
11497
  function planCrewProfiles(targetRepo, previous, writeSet) {
11390
11498
  const recordedFiles = new Map((previous?.files ?? []).filter((file) => writeSet.files.has(file.path)).map((file) => [file.path, file]));
@@ -11399,7 +11507,7 @@ function planCrewProfiles(targetRepo, previous, writeSet) {
11399
11507
  });
11400
11508
  }
11401
11509
  function planInstallRecord(targetRepo, text) {
11402
- return decideOwned(join7(targetRepo, INSTALL_MANIFEST_REL), targetRepo, text);
11510
+ return decideOwned(join8(targetRepo, INSTALL_MANIFEST_REL), targetRepo, text);
11403
11511
  }
11404
11512
  function accountInstall(steps, actual) {
11405
11513
  const { targetRepo, writeSet, pin } = steps;
@@ -11627,7 +11735,7 @@ function mcpHasHalfcycleEntry(text) {
11627
11735
  function writeMissingSelfBuildCrewProfiles(targetRepoRoot, report) {
11628
11736
  for (const { rel, text } of renderCrewProfiles()) {
11629
11737
  const target = crewProfileTarget(targetRepoRoot, rel);
11630
- if (existsSync5(target))
11738
+ if (existsSync6(target))
11631
11739
  continue;
11632
11740
  writeAllowlisted(target, targetRepoRoot, text, report.writtenPaths);
11633
11741
  }
@@ -11635,7 +11743,7 @@ function writeMissingSelfBuildCrewProfiles(targetRepoRoot, report) {
11635
11743
  function installSelfBuild(options) {
11636
11744
  const { targetRepo, engagementId, credential, freshlyMinted, home } = options;
11637
11745
  assertEngagementId(engagementId);
11638
- if (!existsSync5(targetRepo)) {
11746
+ if (!existsSync6(targetRepo)) {
11639
11747
  throw new Error(`[halfcycle] Target repo does not exist: ${targetRepo}`);
11640
11748
  }
11641
11749
  const report = {
@@ -11646,7 +11754,7 @@ function installSelfBuild(options) {
11646
11754
  editedCollidedPaths: [],
11647
11755
  replacedPaths: []
11648
11756
  };
11649
- const at = (rel) => join7(targetRepo, rel);
11757
+ const at = (rel) => join8(targetRepo, rel);
11650
11758
  const version = readPluginManifest().version;
11651
11759
  const existingMcp = readFileOrNull(at(MCP_REGISTRATION_REL));
11652
11760
  const mcpText = existingMcp === null || !mcpHasHalfcycleEntry(existingMcp) ? inLineEndingOf(existingMcp, generateMcpRegistration(existingMcp, credential.mcpUrl)) : null;
@@ -11657,17 +11765,17 @@ function installSelfBuild(options) {
11657
11765
  if (mcpText !== null) {
11658
11766
  writeAllowlisted(at(MCP_REGISTRATION_REL), targetRepo, mcpText, report.writtenPaths);
11659
11767
  }
11660
- if (!existsSync5(at(VENDORED_BIN_REL))) {
11661
- recordOwned(report, at(VENDORED_BIN_REL), targetRepo, readFileSync6(resolveVendoredBinary(), "utf-8"), VENDORED_BIN_REL);
11768
+ if (!existsSync6(at(VENDORED_BIN_REL))) {
11769
+ recordOwned(report, at(VENDORED_BIN_REL), targetRepo, readFileSync7(resolveVendoredBinary(), "utf-8"), VENDORED_BIN_REL);
11662
11770
  }
11663
- if (!existsSync5(at(SELF_BUILD_GITIGNORE_REL))) {
11771
+ if (!existsSync6(at(SELF_BUILD_GITIGNORE_REL))) {
11664
11772
  writeAllowlisted(at(SELF_BUILD_GITIGNORE_REL), targetRepo, SELF_BUILD_GITIGNORE_TEXT, report.writtenPaths);
11665
11773
  }
11666
- if (!existsSync5(at(CREW_ROSTER_REL))) {
11774
+ if (!existsSync6(at(CREW_ROSTER_REL))) {
11667
11775
  writeCrewRoster(targetRepo, report);
11668
11776
  }
11669
11777
  writeMissingSelfBuildCrewProfiles(targetRepo, report);
11670
- if (!existsSync5(at(BUNDLE_PIN_REL))) {
11778
+ if (!existsSync6(at(BUNDLE_PIN_REL))) {
11671
11779
  writeBundlePin(targetRepo, planBundlePin(targetRepo, version, engagementId, "self-build", void 0, credential.methodVersion), report.writtenPaths);
11672
11780
  }
11673
11781
  return { writtenPaths: report.writtenPaths, credentialWritten };
@@ -11841,12 +11949,12 @@ function closeServer(server) {
11841
11949
  }
11842
11950
 
11843
11951
  // dist/sign-in-attempt.js
11844
- import { mkdirSync as mkdirSync6, readFileSync as readFileSync7, realpathSync, rmSync as rmSync3, writeFileSync as writeFileSync6 } from "node:fs";
11952
+ import { mkdirSync as mkdirSync6, readFileSync as readFileSync8, realpathSync, rmSync as rmSync3, writeFileSync as writeFileSync6 } from "node:fs";
11845
11953
  import { homedir as homedir4 } from "node:os";
11846
- import { join as join8 } from "node:path";
11954
+ import { join as join9 } from "node:path";
11847
11955
  var ATTEMPT_FILENAME = "sign-in-attempt.json";
11848
11956
  function attemptPath(home) {
11849
- return join8(home ?? homedir4(), HALFCYCLE_DIR_NAME, ATTEMPT_FILENAME);
11957
+ return join9(home ?? homedir4(), HALFCYCLE_DIR_NAME, ATTEMPT_FILENAME);
11850
11958
  }
11851
11959
  function realFolder(folder2) {
11852
11960
  try {
@@ -11858,7 +11966,7 @@ function realFolder(folder2) {
11858
11966
  function recordSignInAttempt(folder2, expiresAt, home) {
11859
11967
  try {
11860
11968
  const path = attemptPath(home);
11861
- mkdirSync6(join8(home ?? homedir4(), HALFCYCLE_DIR_NAME), { recursive: true, mode: 448 });
11969
+ mkdirSync6(join9(home ?? homedir4(), HALFCYCLE_DIR_NAME), { recursive: true, mode: 448 });
11862
11970
  const record2 = { folder: realFolder(folder2), expiresAt };
11863
11971
  writeFileSync6(path, `${JSON.stringify(record2)}
11864
11972
  `, { mode: 384 });
@@ -11875,7 +11983,7 @@ function clearSignInAttempt(home) {
11875
11983
  }
11876
11984
  function readSignInAttempt(home) {
11877
11985
  try {
11878
- const parsed = JSON.parse(readFileSync7(attemptPath(home), "utf-8"));
11986
+ const parsed = JSON.parse(readFileSync8(attemptPath(home), "utf-8"));
11879
11987
  if (typeof parsed !== "object" || parsed === null)
11880
11988
  return null;
11881
11989
  const { folder: folder2, expiresAt } = parsed;
@@ -12716,7 +12824,7 @@ function installFailureLine() {
12716
12824
  if (code === 0 || verdict)
12717
12825
  return;
12718
12826
  try {
12719
- writeSync(2, `${DOCTOR_LINE}
12827
+ writeSync(2, `${doctorLine(ownVersion())}
12720
12828
  `);
12721
12829
  } catch {
12722
12830
  }
@@ -13169,9 +13277,9 @@ async function joinPinnedEngagement(serviceUrl, engagementId, targetRepo, deps =
13169
13277
 
13170
13278
  // dist/pre-create-checks.js
13171
13279
  import { spawnSync } from "node:child_process";
13172
- import { existsSync as existsSync6, readFileSync as readFileSync8, realpathSync as realpathSync2 } from "node:fs";
13173
- import { delimiter, dirname as dirname4, join as join9 } from "node:path";
13174
- var KEPT_FOLDER_ID_LINE = 'Nothing on the Halfcycle service needs undoing, and this folder keeps its project id, so running "npx halfcycle" again picks up where it left off.';
13280
+ import { existsSync as existsSync7, readFileSync as readFileSync9, realpathSync as realpathSync2 } from "node:fs";
13281
+ import { delimiter, dirname as dirname5, join as join10 } from "node:path";
13282
+ var KEPT_FOLDER_ID_LINE = `Nothing on the Halfcycle service needs undoing, and this folder keeps its project id, so running "${ownBareCommand()}" again picks up where it left off.`;
13175
13283
  var PreCreateRefused = class extends Error {
13176
13284
  constructor(message) {
13177
13285
  super(message);
@@ -13207,14 +13315,14 @@ function refuseSystemFolder(targetRepo, platform6 = process.platform) {
13207
13315
  };
13208
13316
  const target = key(targetRepo);
13209
13317
  if (SYSTEM_FOLDERS.some((folder2) => key(folder2) === target)) {
13210
- throw new PreCreateRefused(`${targetRepo} is a system folder \u2014 run "npx halfcycle" inside a project folder.`);
13318
+ throw new PreCreateRefused(`${targetRepo} is a system folder \u2014 run "${ownBareCommand()}" inside a project folder.`);
13211
13319
  }
13212
13320
  }
13213
13321
  function refuseInsideProject(targetRepo) {
13214
13322
  let folder2 = realOrLiteral(targetRepo);
13215
- for (let parent = dirname4(folder2); parent !== folder2; folder2 = parent, parent = dirname4(parent)) {
13216
- if (existsSync6(join9(parent, BUNDLE_PIN_REL))) {
13217
- throw new PreCreateRefused(`${targetRepo} is inside the Halfcycle project at ${parent} \u2014 open that folder in your coding agent instead, or run "npx halfcycle" in a folder outside it.`);
13323
+ for (let parent = dirname5(folder2); parent !== folder2; folder2 = parent, parent = dirname5(parent)) {
13324
+ if (existsSync7(join10(parent, BUNDLE_PIN_REL))) {
13325
+ throw new PreCreateRefused(`${targetRepo} is inside the Halfcycle project at ${parent} \u2014 open that folder in your coding agent instead, or run "${ownBareCommand()}" in a folder outside it.`);
13218
13326
  }
13219
13327
  }
13220
13328
  }
@@ -13234,16 +13342,16 @@ async function offerGitForWindows() {
13234
13342
  const yes = isInteractiveRun() && /^\s*y(es)?\s*$/i.test(await askLine("Git for Windows is not installed, and Claude Code needs it to run Halfcycle. Install it now with winget? [y/N] "));
13235
13343
  if (yes && runWingetInstall())
13236
13344
  return true;
13237
- throw new PreCreateRefused(`Git for Windows is needed and is not installed \u2014 install it with "${GIT_INSTALL_COMMAND}" or from ${GIT_DOWNLOAD_PAGE}, then open a new terminal and run "npx halfcycle" again.`);
13345
+ throw new PreCreateRefused(`Git for Windows is needed and is not installed \u2014 install it with "${GIT_INSTALL_COMMAND}" or from ${GIT_DOWNLOAD_PAGE}, then open a new terminal and run "${ownBareCommand()}" again.`);
13238
13346
  }
13239
13347
  function gitBashPath(env) {
13240
- const onPath = (env["PATH"] ?? "").split(delimiter).filter((dir) => dir !== "" && existsSync6(join9(dir, "git.exe"))).map((dir) => join9(dir, "..", "bin", "bash.exe"));
13348
+ const onPath = (env["PATH"] ?? "").split(delimiter).filter((dir) => dir !== "" && existsSync7(join10(dir, "git.exe"))).map((dir) => join10(dir, "..", "bin", "bash.exe"));
13241
13349
  const candidates = [
13242
13350
  env["CLAUDE_CODE_GIT_BASH_PATH"],
13243
13351
  ...onPath,
13244
- env["ProgramFiles"] === void 0 ? void 0 : join9(env["ProgramFiles"], "Git", "bin", "bash.exe")
13352
+ env["ProgramFiles"] === void 0 ? void 0 : join10(env["ProgramFiles"], "Git", "bin", "bash.exe")
13245
13353
  ];
13246
- return candidates.find((file) => file !== void 0 && file !== "" && existsSync6(file));
13354
+ return candidates.find((file) => file !== void 0 && file !== "" && existsSync7(file));
13247
13355
  }
13248
13356
  function hasGitBash(env) {
13249
13357
  return gitBashPath(env) !== void 0;
@@ -13252,10 +13360,10 @@ function runWingetInstall() {
13252
13360
  return spawnSync("winget", [...GIT_INSTALL_ARGS], { stdio: "inherit" }).status === 0;
13253
13361
  }
13254
13362
  function unreadableProjectRefusal(targetRepo) {
13255
- return `${join9(targetRepo, PROJECT_IDENTITY_REL)} could not be read as a Halfcycle project file \u2014 delete the .halfcycle folder in ${targetRepo} and run "npx halfcycle" again to start a new project here.`;
13363
+ return `${join10(targetRepo, PROJECT_IDENTITY_REL)} could not be read as a Halfcycle project file \u2014 delete the .halfcycle folder in ${targetRepo} and run "${ownBareCommand()}" again to start a new project here.`;
13256
13364
  }
13257
13365
  function cannotWriteRefusal(targetRepo) {
13258
- return `cannot write to ${targetRepo} \u2014 run "npx halfcycle" in a folder you can write to.`;
13366
+ return `cannot write to ${targetRepo} \u2014 run "${ownBareCommand()}" in a folder you can write to.`;
13259
13367
  }
13260
13368
  function claimFolderId(targetRepo) {
13261
13369
  const planned = planProjectIdentity(targetRepo);
@@ -13272,7 +13380,7 @@ function claimFolderId(targetRepo) {
13272
13380
  function readFolderId(targetRepo) {
13273
13381
  let folderId;
13274
13382
  try {
13275
- const text = readFileSync8(join9(targetRepo, PROJECT_IDENTITY_REL), "utf-8");
13383
+ const text = readFileSync9(join10(targetRepo, PROJECT_IDENTITY_REL), "utf-8");
13276
13384
  folderId = JSON.parse(text)?.projectId;
13277
13385
  } catch {
13278
13386
  return void 0;
@@ -13295,7 +13403,7 @@ function folderMissingRefusal(targetRepo) {
13295
13403
  return `halfcycle: no such directory: ${targetRepo}`;
13296
13404
  }
13297
13405
  function folderIsFileRefusal(targetRepo) {
13298
- return `halfcycle: ${targetRepo} is a file \u2014 run "npx halfcycle" with a project folder instead.`;
13406
+ return `halfcycle: ${targetRepo} is a file \u2014 run "${ownBareCommand()}" with a project folder instead.`;
13299
13407
  }
13300
13408
  function monorepoRefusal(targetRepo) {
13301
13409
  return `halfcycle: refusing to install into the Halfcycle monorepo itself (${targetRepo}).
@@ -13305,7 +13413,7 @@ function monorepoRefusal(targetRepo) {
13305
13413
  To install into a real project: halfcycle <path-to-that-repo>`;
13306
13414
  }
13307
13415
  function homeFolderRefusal(targetRepo) {
13308
- return `halfcycle: refusing to set up in your home folder (${targetRepo}). Run "npx halfcycle" inside a project folder.`;
13416
+ return `halfcycle: refusing to set up in your home folder (${targetRepo}). Run "${ownBareCommand()}" inside a project folder.`;
13309
13417
  }
13310
13418
  function isHomeFolder(dir) {
13311
13419
  try {
@@ -13318,16 +13426,16 @@ function isHomeFolder(dir) {
13318
13426
  }
13319
13427
 
13320
13428
  // dist/preview.js
13321
- import { accessSync, constants, existsSync as existsSync7, readFileSync as readFileSync10, statSync } from "node:fs";
13322
- import { join as join11 } from "node:path";
13429
+ import { accessSync, constants, existsSync as existsSync8, readFileSync as readFileSync11, statSync } from "node:fs";
13430
+ import { join as join12 } from "node:path";
13323
13431
 
13324
13432
  // dist/halfcycle-monorepo.js
13325
- import { readFileSync as readFileSync9 } from "node:fs";
13326
- import { join as join10 } from "node:path";
13433
+ import { readFileSync as readFileSync10 } from "node:fs";
13434
+ import { join as join11 } from "node:path";
13327
13435
  var MONOREPO_PACKAGE_NAME = "halfcycle-monorepo";
13328
13436
  function isHalfcycleMonorepo(dir) {
13329
13437
  try {
13330
- const pkg = JSON.parse(readFileSync9(join10(dir, "package.json"), "utf-8"));
13438
+ const pkg = JSON.parse(readFileSync10(join11(dir, "package.json"), "utf-8"));
13331
13439
  return pkg.name === MONOREPO_PACKAGE_NAME;
13332
13440
  } catch {
13333
13441
  return false;
@@ -13349,7 +13457,7 @@ var WORDS = {
13349
13457
  newProject: " A new project on Halfcycle's service for this folder, made when you sign in.",
13350
13458
  gitForWindows: " Git for Windows, which Claude Code needs: setting up will ask before installing it with winget.",
13351
13459
  firstSession: "Your first session will then write your project's own files as you answer its questions, starting with AGENTS.md and CLAUDE.md, which hold your project's own instructions.",
13352
- toSetUp: `To set up for real, run: ${HALFCYCLE_COMMAND}`,
13460
+ toSetUp: `To set up for real, run: ${ownCommand()}`,
13353
13461
  planFailed: "halfcycle: setting up this folder would stop on a file that could not be read. Nothing has been written; fix that file, then run the preview again."
13354
13462
  };
13355
13463
  function runPreview(targetRepo, options) {
@@ -13369,19 +13477,19 @@ function runPreview(targetRepo, options) {
13369
13477
  };
13370
13478
  }
13371
13479
  function claudeMdLine(targetRepo) {
13372
- const path = join11(targetRepo, CLAUDE_MD_REL);
13373
- if (!existsSync7(path))
13480
+ const path = join12(targetRepo, CLAUDE_MD_REL);
13481
+ if (!existsSync8(path))
13374
13482
  return void 0;
13375
13483
  let text;
13376
13484
  try {
13377
- text = readFileSync10(path, "utf-8");
13485
+ text = readFileSync11(path, "utf-8");
13378
13486
  } catch {
13379
13487
  return WORDS.claudeMdWillAdd;
13380
13488
  }
13381
13489
  return text.includes(AGENTS_IMPORT_LINE) ? WORDS.claudeMdAlreadyPoints : WORDS.claudeMdWillAdd;
13382
13490
  }
13383
13491
  function previewRefusal(targetRepo, options) {
13384
- if (!existsSync7(targetRepo))
13492
+ if (!existsSync8(targetRepo))
13385
13493
  return folderMissingRefusal(targetRepo);
13386
13494
  if (!statSync(targetRepo).isDirectory())
13387
13495
  return folderIsFileRefusal(targetRepo);
@@ -13402,7 +13510,7 @@ function previewRefusal(targetRepo, options) {
13402
13510
  } catch {
13403
13511
  return cannotWriteRefusal(targetRepo);
13404
13512
  }
13405
- if (existsSync7(join11(targetRepo, PROJECT_IDENTITY_REL)) && readFolderId(targetRepo) === void 0) {
13513
+ if (existsSync8(join12(targetRepo, PROJECT_IDENTITY_REL)) && readFolderId(targetRepo) === void 0) {
13406
13514
  return unreadableProjectRefusal(targetRepo);
13407
13515
  }
13408
13516
  try {
@@ -13460,8 +13568,8 @@ function renderPreview(plan, missingGitForWindows, claudeMdWords) {
13460
13568
 
13461
13569
  // dist/local-allow.js
13462
13570
  import { spawnSync as spawnSync2 } from "node:child_process";
13463
- import { existsSync as existsSync8, mkdirSync as mkdirSync7, readFileSync as readFileSync11, rmSync as rmSync4, writeFileSync as writeFileSync7 } from "node:fs";
13464
- import { dirname as dirname5, join as join12 } from "node:path";
13571
+ import { existsSync as existsSync9, mkdirSync as mkdirSync7, readFileSync as readFileSync12, rmSync as rmSync4, writeFileSync as writeFileSync7 } from "node:fs";
13572
+ import { dirname as dirname6, join as join13 } from "node:path";
13465
13573
  var LOCAL_SETTINGS_REL2 = ".claude/settings.local.json";
13466
13574
  var LOCAL_ALLOW_RECORD_REL = ".halfcycle/local-allow.json";
13467
13575
  var LOCAL_ALLOW_EDIT_MODE = "acceptEdits";
@@ -13469,6 +13577,7 @@ var SCRIPTS = ["test", "build", "lint", "typecheck"];
13469
13577
  var SCRIPT_RUNNERS = ["npm run", "pnpm", "pnpm run", "yarn", "bun run"];
13470
13578
  var HALFCYCLE_TOOLS_RULE = `mcp__${MCP_SERVER_KEY}`;
13471
13579
  var bash = (command) => `Bash(${command}:*)`;
13580
+ var INSTALLED_HOOK_SCRIPTS = Object.keys(OWNED_GENERATED_HEADERS).filter((path) => path.startsWith(".claude/hooks/"));
13472
13581
  var LOCAL_ALLOW_RULES = [
13473
13582
  ...SCRIPT_RUNNERS.flatMap((runner) => SCRIPTS.map((script) => bash(`${runner} ${script}`))),
13474
13583
  bash("npm test"),
@@ -13478,8 +13587,14 @@ var LOCAL_ALLOW_RULES = [
13478
13587
  bash("cargo test"),
13479
13588
  bash("cargo build"),
13480
13589
  bash("cargo check"),
13590
+ bash("cargo run"),
13591
+ bash("go run"),
13481
13592
  bash("pytest"),
13482
13593
  bash("python -m pytest"),
13594
+ bash("bun test"),
13595
+ bash("bun build"),
13596
+ bash("deno test"),
13597
+ bash("deno check"),
13483
13598
  bash("make test"),
13484
13599
  bash("make build"),
13485
13600
  bash("git status"),
@@ -13509,11 +13624,20 @@ var LOCAL_ALLOW_RULES = [
13509
13624
  bash("go mod tidy"),
13510
13625
  bash("go get"),
13511
13626
  bash("cargo add"),
13627
+ bash("pip install"),
13628
+ bash("pip3 install"),
13629
+ bash("bundle install"),
13512
13630
  bash("node"),
13513
13631
  bash("npx"),
13514
13632
  bash("pnpm exec"),
13515
13633
  bash("bunx"),
13634
+ bash("python3"),
13635
+ bash("python"),
13636
+ bash("ruby"),
13637
+ bash("deno run"),
13638
+ bash("bundle exec"),
13516
13639
  "Bash(node_modules/.bin/*)",
13640
+ ...INSTALLED_HOOK_SCRIPTS.flatMap((path) => [bash(`./${path}`), bash(`bash ${path}`)]),
13517
13641
  bash("mkdir"),
13518
13642
  bash("touch"),
13519
13643
  bash("cp"),
@@ -13537,10 +13661,10 @@ var LOCAL_ALLOW_RULES = [
13537
13661
  HALFCYCLE_TOOLS_RULE
13538
13662
  ];
13539
13663
  var LOCAL_ALLOW_QUESTION = "Let your coding agent work without asking on this computer? It would run your tests and builds, install packages, run local tools, make and move files, use git locally, run Halfcycle, and edit files in this project without a permission prompt. Pushing, publishing, deploying to production, deleting and changing anything on GitHub still ask. This is saved only on this computer, in .claude/settings.local.json, which is not committed.\nType yes to allow it, or press Enter to keep asking: ";
13540
- var LOCAL_ALLOW_ON_LINE = "Your coding agent can now work without asking on this computer. To undo it, run: npx halfcycle@latest allow --remove";
13541
- var LOCAL_ALLOW_OFF_LINE = "Your coding agent will ask before it runs commands. To stop most of those questions on this computer, run: npx halfcycle@latest allow";
13664
+ var LOCAL_ALLOW_ON_LINE = `Your coding agent can now work without asking on this computer. To undo it, run: ${ownCommand()} allow --remove`;
13665
+ var LOCAL_ALLOW_OFF_LINE = `Your coding agent will ask before it runs commands. To stop most of those questions on this computer, run: ${ownCommand()} allow`;
13542
13666
  var LOCAL_ALLOW_TRACKED_LINE = "Your .claude/settings.local.json is committed to git, so Halfcycle will not add rules to it. Remove it from git first if you want them.";
13543
- var LOCAL_ALLOW_UNREADABLE_LINE = "Halfcycle did not change .claude/settings.local.json because it is not a JSON object it can add to. Fix the file, then run: npx halfcycle@latest allow";
13667
+ var LOCAL_ALLOW_UNREADABLE_LINE = `Halfcycle did not change .claude/settings.local.json because it is not a JSON object it can add to. Fix the file, then run: ${ownCommand()} allow`;
13544
13668
  var LOCAL_ALLOW_NOTHING_TO_REMOVE_LINE = "Halfcycle has not added any rules on this computer, so there is nothing to remove.";
13545
13669
  var LOCAL_ALLOW_REMOVED_LINE = "Halfcycle took out the rules it added. Everything you had stays.";
13546
13670
  function isYes(answer) {
@@ -13558,11 +13682,11 @@ function trackedByGit(repo) {
13558
13682
  return run.status === 0;
13559
13683
  }
13560
13684
  function readRecord2(repo) {
13561
- const path = join12(repo, LOCAL_ALLOW_RECORD_REL);
13562
- if (!existsSync8(path))
13685
+ const path = join13(repo, LOCAL_ALLOW_RECORD_REL);
13686
+ if (!existsSync9(path))
13563
13687
  return null;
13564
13688
  try {
13565
- const raw = JSON.parse(readFileSync11(path, "utf8"));
13689
+ const raw = JSON.parse(readFileSync12(path, "utf8"));
13566
13690
  if (!isObject2(raw) || !Array.isArray(raw["rules"]))
13567
13691
  return null;
13568
13692
  return {
@@ -13575,12 +13699,12 @@ function readRecord2(repo) {
13575
13699
  }
13576
13700
  }
13577
13701
  function writeRecord(repo, record2) {
13578
- const path = join12(repo, LOCAL_ALLOW_RECORD_REL);
13579
- mkdirSync7(dirname5(path), { recursive: true });
13702
+ const path = join13(repo, LOCAL_ALLOW_RECORD_REL);
13703
+ mkdirSync7(dirname6(path), { recursive: true });
13580
13704
  writeFileSync7(path, JSON.stringify({ rules: record2.rules, defaultMode: record2.defaultMode, before: record2.before }, null, 2) + "\n");
13581
13705
  }
13582
13706
  function holdsLocalAllowRecord(repo) {
13583
- return existsSync8(join12(repo, LOCAL_ALLOW_RECORD_REL));
13707
+ return existsSync9(join13(repo, LOCAL_ALLOW_RECORD_REL));
13584
13708
  }
13585
13709
  function layoutOf(text) {
13586
13710
  const eol = text.includes("\r\n") ? "\r\n" : "\n";
@@ -13595,9 +13719,9 @@ function render(value, like) {
13595
13719
  function writeLocalAllow(repo) {
13596
13720
  if (trackedByGit(repo))
13597
13721
  return { kind: "tracked", line: LOCAL_ALLOW_TRACKED_LINE };
13598
- const path = join12(repo, LOCAL_SETTINGS_REL2);
13599
- const existed = existsSync8(path);
13600
- const text = existed ? readFileSync11(path, "utf8") : null;
13722
+ const path = join13(repo, LOCAL_SETTINGS_REL2);
13723
+ const existed = existsSync9(path);
13724
+ const text = existed ? readFileSync12(path, "utf8") : null;
13601
13725
  let settings = {};
13602
13726
  if (text !== null) {
13603
13727
  try {
@@ -13627,7 +13751,7 @@ function writeLocalAllow(repo) {
13627
13751
  if (setsMode)
13628
13752
  permissions["defaultMode"] = LOCAL_ALLOW_EDIT_MODE;
13629
13753
  const next = { ...settings, permissions };
13630
- mkdirSync7(dirname5(path), { recursive: true });
13754
+ mkdirSync7(dirname6(path), { recursive: true });
13631
13755
  writeFileSync7(path, render(next, text));
13632
13756
  const earlier = readRecord2(repo);
13633
13757
  writeRecord(repo, {
@@ -13639,15 +13763,15 @@ function writeLocalAllow(repo) {
13639
13763
  }
13640
13764
  function removeLocalAllow(repo) {
13641
13765
  const record2 = readRecord2(repo);
13642
- const recordPath = join12(repo, LOCAL_ALLOW_RECORD_REL);
13766
+ const recordPath = join13(repo, LOCAL_ALLOW_RECORD_REL);
13643
13767
  if (record2 === null) {
13644
- if (existsSync8(recordPath))
13768
+ if (existsSync9(recordPath))
13645
13769
  rmSync4(recordPath, { force: true });
13646
13770
  return { kind: "nothing", line: LOCAL_ALLOW_NOTHING_TO_REMOVE_LINE };
13647
13771
  }
13648
- const path = join12(repo, LOCAL_SETTINGS_REL2);
13649
- if (existsSync8(path)) {
13650
- const text = readFileSync11(path, "utf8");
13772
+ const path = join13(repo, LOCAL_SETTINGS_REL2);
13773
+ if (existsSync9(path)) {
13774
+ const text = readFileSync12(path, "utf8");
13651
13775
  let parsed = null;
13652
13776
  try {
13653
13777
  parsed = JSON.parse(text);
@@ -13714,10 +13838,10 @@ function canonical(value) {
13714
13838
  function shouldAskLocalAllow(repo, interactive) {
13715
13839
  if (!interactive || holdsLocalAllowRecord(repo))
13716
13840
  return false;
13717
- const path = join12(repo, LOCAL_SETTINGS_REL2);
13718
- if (!existsSync8(path))
13841
+ const path = join13(repo, LOCAL_SETTINGS_REL2);
13842
+ if (!existsSync9(path))
13719
13843
  return true;
13720
- const parsed = parseObject(readFileSync11(path, "utf8"));
13844
+ const parsed = parseObject(readFileSync12(path, "utf8"));
13721
13845
  const allow = isObject2(parsed?.["permissions"]) ? parsed["permissions"]["allow"] : void 0;
13722
13846
  if (!Array.isArray(allow))
13723
13847
  return true;
@@ -13736,9 +13860,9 @@ function localAllowClosingLine(repo, said) {
13736
13860
 
13737
13861
  // dist/doctor.js
13738
13862
  import { spawnSync as spawnSync3 } from "node:child_process";
13739
- import { existsSync as existsSync9, lstatSync as lstatSync2, readFileSync as readFileSync12, realpathSync as realpathSync4 } from "node:fs";
13863
+ import { existsSync as existsSync10, lstatSync as lstatSync2, readFileSync as readFileSync13, realpathSync as realpathSync4 } from "node:fs";
13740
13864
  import { homedir as homedir6 } from "node:os";
13741
- import { join as join13 } from "node:path";
13865
+ import { join as join14 } from "node:path";
13742
13866
  var GUARD_RUNNER_REL2 = ".claude/hooks/guard-runner.sh";
13743
13867
  var CONNECTION_VERB = "check-connection";
13744
13868
  var WRAPPER_TIMEOUT_MS = 3e4;
@@ -13777,13 +13901,13 @@ function runDoctor(root, options) {
13777
13901
  };
13778
13902
  }
13779
13903
  function setUpLine(root, upToDate) {
13780
- if (!existsSync9(join13(root, BUNDLE_PIN_REL))) {
13904
+ if (!existsSync10(join14(root, BUNDLE_PIN_REL))) {
13781
13905
  return {
13782
13906
  state: "no",
13783
13907
  hooksMissing: false,
13784
13908
  line: {
13785
13909
  ok: false,
13786
- text: `Set up: no. Halfcycle is not set up in this folder. To set it up, run ${HALFCYCLE_COMMAND} here.`
13910
+ text: `Set up: no. Halfcycle is not set up in this folder. To set it up, run ${ownCommand()} here.`
13787
13911
  }
13788
13912
  };
13789
13913
  }
@@ -13791,9 +13915,9 @@ function setUpLine(root, upToDate) {
13791
13915
  const missing = [];
13792
13916
  if (!pinNamesProject(root))
13793
13917
  missing.push("Halfcycle's project record");
13794
- if (!existsSync9(join13(root, INSTALL_MANIFEST_REL)))
13918
+ if (!existsSync10(join14(root, INSTALL_MANIFEST_REL)))
13795
13919
  missing.push("the install record");
13796
- if (!existsSync9(join13(root, VENDORED_BIN_REL)))
13920
+ if (!existsSync10(join14(root, VENDORED_BIN_REL)))
13797
13921
  missing.push("Halfcycle's check program");
13798
13922
  if (!mcpNamesServer(root))
13799
13923
  missing.push("Halfcycle's server entry");
@@ -13806,7 +13930,7 @@ function setUpLine(root, upToDate) {
13806
13930
  hooksMissing,
13807
13931
  line: {
13808
13932
  ok: false,
13809
- text: `Set up: not complete. Missing: ${naturalList(missing)}. To put it back, run ${HALFCYCLE_COMMAND} here; nothing of yours is changed.`
13933
+ text: `Set up: not complete. Missing: ${naturalList(missing)}. To put it back, run ${ownCommand()} here; nothing of yours is changed.`
13810
13934
  }
13811
13935
  };
13812
13936
  }
@@ -13819,14 +13943,14 @@ function pinNamesProject(root) {
13819
13943
  }
13820
13944
  }
13821
13945
  function mcpNamesServer(root) {
13822
- const parsed = readJson(join13(root, MCP_REGISTRATION_REL));
13946
+ const parsed = readJson(join14(root, MCP_REGISTRATION_REL));
13823
13947
  if (!isObject3(parsed) || !isObject3(parsed["mcpServers"]))
13824
13948
  return false;
13825
13949
  const entry = parsed["mcpServers"][MCP_SERVER_KEY];
13826
13950
  return isObject3(entry) && typeof entry["url"] === "string";
13827
13951
  }
13828
13952
  function settingsRegisterEveryCommand(root) {
13829
- const parsed = readJson(join13(root, SETTINGS_REL));
13953
+ const parsed = readJson(join14(root, SETTINGS_REL));
13830
13954
  if (!isObject3(parsed) || !isObject3(parsed["hooks"]))
13831
13955
  return false;
13832
13956
  const registered = /* @__PURE__ */ new Set();
@@ -13857,8 +13981,8 @@ function startWrapper(root, options) {
13857
13981
  }
13858
13982
  shell = gitBash;
13859
13983
  }
13860
- const wrapper = join13(root, GUARD_RUNNER_REL2);
13861
- if (!existsSync9(wrapper))
13984
+ const wrapper = join14(root, GUARD_RUNNER_REL2);
13985
+ if (!existsSync10(wrapper))
13862
13986
  return failed(repairSentence());
13863
13987
  const run = spawnSync3(shell, [wrapper, CONNECTION_VERB], {
13864
13988
  cwd: root,
@@ -13885,7 +14009,7 @@ function startWrapper(root, options) {
13885
14009
  }
13886
14010
  function settingsDisablingHooks(root) {
13887
14011
  for (const rel of [SETTINGS_REL, LOCAL_SETTINGS_REL]) {
13888
- const parsed = readJson(join13(root, rel));
14012
+ const parsed = readJson(join14(root, rel));
13889
14013
  if (isObject3(parsed) && parsed["disableAllHooks"] === true)
13890
14014
  return rel;
13891
14015
  }
@@ -13915,7 +14039,7 @@ function parseAnswer(stdout) {
13915
14039
  function usageOf(said) {
13916
14040
  if (!Array.isArray(said))
13917
14041
  return [];
13918
- const usage3 = [];
14042
+ const usage5 = [];
13919
14043
  for (const figure of said) {
13920
14044
  if (!isObject3(figure))
13921
14045
  continue;
@@ -13928,9 +14052,9 @@ function usageOf(said) {
13928
14052
  continue;
13929
14053
  if (typeof resetsAt !== "string" || Number.isNaN(Date.parse(resetsAt)))
13930
14054
  continue;
13931
- usage3.push({ agent, window, usedPercentage, resetsAt: new Date(resetsAt) });
14055
+ usage5.push({ agent, window, usedPercentage, resetsAt: new Date(resetsAt) });
13932
14056
  }
13933
- return usage3;
14057
+ return usage5;
13934
14058
  }
13935
14059
  function checksRunningLine(running, hooksMissing) {
13936
14060
  if (running.state === "not-run")
@@ -13947,7 +14071,7 @@ function failed(text) {
13947
14071
  return { state: "failed", line: { ok: false, text } };
13948
14072
  }
13949
14073
  function repairSentence() {
13950
- return `Checks running: no. Halfcycle's check could not start. To repair it, run ${HALFCYCLE_COMMAND} here.`;
14074
+ return `Checks running: no. Halfcycle's check could not start. To repair it, run ${ownCommand()} here.`;
13951
14075
  }
13952
14076
  function connectionLines(running) {
13953
14077
  if (running.state !== "answered") {
@@ -13975,8 +14099,8 @@ function folderTrustLine(root, options) {
13975
14099
  if (!mcpNamesServer(root))
13976
14100
  return void 0;
13977
14101
  const configFolder = options.env["CLAUDE_CONFIG_DIR"];
13978
- const stateFile = join13(configFolder !== void 0 && configFolder !== "" ? configFolder : options.home ?? homedir6(), CLAUDE_STATE_FILENAME);
13979
- const trust = trustOfWorkingFolder(root, stateFile, options.platform, { lstatSync: lstatSync2, readFileSync: readFileSync12, realpathSync: realpathSync4 });
14102
+ const stateFile = join14(configFolder !== void 0 && configFolder !== "" ? configFolder : options.home ?? homedir6(), CLAUDE_STATE_FILENAME);
14103
+ const trust = trustOfWorkingFolder(root, stateFile, options.platform, { lstatSync: lstatSync2, readFileSync: readFileSync13, realpathSync: realpathSync4 });
13980
14104
  return trust === "not-trusted" ? { ok: true, text: `Folder trust: ${FOLDER_NOT_TRUSTED_SENTENCE}` } : void 0;
13981
14105
  }
13982
14106
  function signInCodeLine(root, running, now, options) {
@@ -13986,7 +14110,7 @@ function signInCodeLine(root, running, now, options) {
13986
14110
  return void 0;
13987
14111
  return {
13988
14112
  ok: true,
13989
- text: `Sign-in code: it ran out before it was confirmed. To get a new one, run ${HALFCYCLE_COMMAND} here.`
14113
+ text: `Sign-in code: it ran out before it was confirmed. To get a new one, run ${ownCommand()} here.`
13990
14114
  };
13991
14115
  }
13992
14116
  var AGENT_NAMES = { "claude-code": "Claude Code", codex: "Codex" };
@@ -14035,9 +14159,9 @@ function installedVersion(root) {
14035
14159
  }
14036
14160
  function versionNoLine(installed, current) {
14037
14161
  if (installed === null) {
14038
- return `Up to date: no. This folder has no Halfcycle version recorded; the newest is ${current}. To set it up, run ${HALFCYCLE_COMMAND} here.`;
14162
+ return `Up to date: no. This folder has no Halfcycle version recorded; the newest is ${current}. To set it up, run ${ownCommand()} here.`;
14039
14163
  }
14040
- return `Up to date: no. This folder has Halfcycle ${installed}; the newest is ${current}. To update, run ${HALFCYCLE_COMMAND} here.`;
14164
+ return `Up to date: no. This folder has Halfcycle ${installed}; the newest is ${current}. To update, run ${ownCommand()} here.`;
14041
14165
  }
14042
14166
  var CODEX_EVENT_SNAKE = {
14043
14167
  SessionStart: "session_start",
@@ -14049,12 +14173,12 @@ var CODEX_EVENT_SNAKE = {
14049
14173
  SessionEnd: "session_end"
14050
14174
  };
14051
14175
  function codexLine(root, options) {
14052
- const hooksPath = join13(root, CODEX_HOOKS_REL2);
14053
- if (!existsSync9(hooksPath))
14176
+ const hooksPath = join14(root, CODEX_HOOKS_REL2);
14177
+ if (!existsSync10(hooksPath))
14054
14178
  return void 0;
14055
14179
  const positions = codexEntryPositions(hooksPath);
14056
14180
  if (positions === void 0) {
14057
- return { ok: false, text: `Codex: not complete. To repair it, run ${HALFCYCLE_COMMAND} here.` };
14181
+ return { ok: false, text: `Codex: not complete. To repair it, run ${ownCommand()} here.` };
14058
14182
  }
14059
14183
  return codexApprovalStatus(root, positions, options) === "approved" ? { ok: true, text: "Codex: set up, and its checks are approved." } : {
14060
14184
  ok: false,
@@ -14091,16 +14215,16 @@ function codexEntryPositions(hooksPath) {
14091
14215
  }
14092
14216
  function codexApprovalStatus(root, positions, options) {
14093
14217
  const codexHome = options.env["CODEX_HOME"];
14094
- const configPath = join13(codexHome !== void 0 && codexHome !== "" ? codexHome : join13(homedir6(), ".codex"), "config.toml");
14218
+ const configPath = join14(codexHome !== void 0 && codexHome !== "" ? codexHome : join14(homedir6(), ".codex"), "config.toml");
14095
14219
  let text;
14096
14220
  try {
14097
- text = readFileSync12(configPath, "utf-8");
14221
+ text = readFileSync13(configPath, "utf-8");
14098
14222
  } catch {
14099
14223
  return "not-approved";
14100
14224
  }
14101
14225
  let hooksPath;
14102
14226
  try {
14103
- hooksPath = join13(realpathSync4(root), CODEX_HOOKS_REL2);
14227
+ hooksPath = join14(realpathSync4(root), CODEX_HOOKS_REL2);
14104
14228
  } catch {
14105
14229
  return "not-approved";
14106
14230
  }
@@ -14130,10 +14254,10 @@ function naturalList(items) {
14130
14254
  return `${items.slice(0, -1).join(", ")} and ${items[items.length - 1]}`;
14131
14255
  }
14132
14256
  function readJson(path) {
14133
- if (!existsSync9(path))
14257
+ if (!existsSync10(path))
14134
14258
  return void 0;
14135
14259
  try {
14136
- return JSON.parse(readFileSync12(path, "utf-8"));
14260
+ return JSON.parse(readFileSync13(path, "utf-8"));
14137
14261
  } catch {
14138
14262
  return void 0;
14139
14263
  }
@@ -14144,8 +14268,8 @@ function isObject3(value) {
14144
14268
 
14145
14269
  // dist/claude-version-probe.js
14146
14270
  import { execFileSync as execFileSync3 } from "node:child_process";
14147
- import { existsSync as existsSync10, realpathSync as realpathSync5 } from "node:fs";
14148
- import { delimiter as delimiter2, join as join14 } from "node:path";
14271
+ import { existsSync as existsSync11, realpathSync as realpathSync5 } from "node:fs";
14272
+ import { delimiter as delimiter2, join as join15 } from "node:path";
14149
14273
  function execVersionCommand(command, timeoutMs, deps) {
14150
14274
  const platform6 = deps.platform ?? process.platform;
14151
14275
  const exec = deps.exec ?? execFileSync3;
@@ -14188,7 +14312,7 @@ var INSTALL_METHOD_RULES = [
14188
14312
  function probeClaudeCodeInstallMethod(deps = {}) {
14189
14313
  const env = deps.env ?? process.env;
14190
14314
  const platform6 = deps.platform ?? process.platform;
14191
- const exists = deps.exists ?? existsSync10;
14315
+ const exists = deps.exists ?? existsSync11;
14192
14316
  const realpath = deps.realpath ?? ((file) => {
14193
14317
  try {
14194
14318
  return realpathSync5(file);
@@ -14200,7 +14324,7 @@ function probeClaudeCodeInstallMethod(deps = {}) {
14200
14324
  const dirs = (env["PATH"] ?? "").split(delimiter2).filter((dir) => dir !== "");
14201
14325
  for (const dir of dirs) {
14202
14326
  for (const name of names) {
14203
- const file = join14(dir, name);
14327
+ const file = join15(dir, name);
14204
14328
  if (!exists(file))
14205
14329
  continue;
14206
14330
  const real = realpath(file);
@@ -14235,8 +14359,8 @@ function codexInstallLine(codex) {
14235
14359
  }
14236
14360
 
14237
14361
  // dist/build-record/write.js
14238
- import { existsSync as existsSync11, mkdirSync as mkdirSync8, rmSync as rmSync6 } from "node:fs";
14239
- import { basename as basename2, join as join15, resolve as resolve3, relative as relative2, isAbsolute as isAbsolute3 } from "node:path";
14362
+ import { existsSync as existsSync12, mkdirSync as mkdirSync8, rmSync as rmSync6 } from "node:fs";
14363
+ import { basename as basename2, join as join16, resolve as resolve3, relative as relative2, isAbsolute as isAbsolute3 } from "node:path";
14240
14364
 
14241
14365
  // dist/build-record/render-v2.js
14242
14366
  var HIDDEN = /[\p{Cc}\p{Zl}\p{Zp}\p{Default_Ignorable_Code_Point}]/u;
@@ -14827,7 +14951,7 @@ function renderBuildRecordV2Markdown(record2) {
14827
14951
  }
14828
14952
 
14829
14953
  // dist/build-record/pair-io.js
14830
- import { closeSync, openSync, readFileSync as readFileSync13, renameSync as renameSync2, rmSync as rmSync5, writeSync as writeSync2 } from "node:fs";
14954
+ import { closeSync, openSync, readFileSync as readFileSync14, renameSync as renameSync2, rmSync as rmSync5, writeSync as writeSync2 } from "node:fs";
14831
14955
  function createExclusiveOnDisk(path, data) {
14832
14956
  const bytes = typeof data === "string" ? Buffer.from(data, "utf-8") : data;
14833
14957
  const fd = openSync(path, "wx");
@@ -14858,7 +14982,7 @@ function isEexist(err) {
14858
14982
  }
14859
14983
  function readIfPresent(path) {
14860
14984
  try {
14861
- return readFileSync13(path, "utf-8");
14985
+ return readFileSync14(path, "utf-8");
14862
14986
  } catch {
14863
14987
  return null;
14864
14988
  }
@@ -14890,7 +15014,7 @@ function replaceOne(io, tmp, finalPath, text) {
14890
15014
  }
14891
15015
 
14892
15016
  // dist/build-record/write.js
14893
- var BUILD_RECORD_DIR = join15("docs", "build-records");
15017
+ var BUILD_RECORD_DIR = join16("docs", "build-records");
14894
15018
  function assertInsideOutDir(outDir, candidate, identity) {
14895
15019
  const rel = relative2(outDir, resolve3(candidate));
14896
15020
  const isPlainFilename = rel !== "" && rel !== ".." && !isAbsolute3(rel) && rel === basename2(rel);
@@ -14928,8 +15052,8 @@ function writeRecordPair(repoRoot, phase, rawRecord, record2, io = NODE_RECORD_P
14928
15052
  }
14929
15053
  const outDir = resolve3(repoRoot, BUILD_RECORD_DIR);
14930
15054
  const segment = renderPhaseSegment(phase);
14931
- const jsonPath = join15(outDir, `${segment}.json`);
14932
- const mdPath = join15(outDir, `${segment}.md`);
15055
+ const jsonPath = join16(outDir, `${segment}.json`);
15056
+ const mdPath = join16(outDir, `${segment}.md`);
14933
15057
  const jsonTmp = `${jsonPath}.tmp`;
14934
15058
  const mdTmp = `${mdPath}.tmp`;
14935
15059
  for (const candidate of [jsonPath, mdPath, jsonTmp, mdTmp]) {
@@ -14943,7 +15067,7 @@ function writeRecordPair(repoRoot, phase, rawRecord, record2, io = NODE_RECORD_P
14943
15067
  }
14944
15068
  const existingJson = readIfPresent(jsonPath);
14945
15069
  if (existingJson === null) {
14946
- return { kind: "in-the-way", path: existsSync11(jsonPath) ? jsonPath : mdPath };
15070
+ return { kind: "in-the-way", path: existsSync12(jsonPath) ? jsonPath : mdPath };
14947
15071
  }
14948
15072
  if (existingJson === jsonText) {
14949
15073
  replaceOne(io, mdTmp, mdPath, mdText);
@@ -14970,21 +15094,21 @@ function writeRecordPair(repoRoot, phase, rawRecord, record2, io = NODE_RECORD_P
14970
15094
  import { relative as relative5 } from "node:path";
14971
15095
 
14972
15096
  // dist/open-phase.js
14973
- import { readFileSync as readFileSync19 } from "node:fs";
14974
- import { join as join23 } from "node:path";
15097
+ import { readFileSync as readFileSync20 } from "node:fs";
15098
+ import { join as join24 } from "node:path";
14975
15099
 
14976
15100
  // dist/close-phase.js
14977
15101
  import { execFile, execFileSync as execFileSync5 } from "node:child_process";
14978
- import { readFileSync as readFileSync18 } from "node:fs";
14979
- import { join as join22 } from "node:path";
15102
+ import { readFileSync as readFileSync19 } from "node:fs";
15103
+ import { join as join23 } from "node:path";
14980
15104
 
14981
15105
  // dist/plan.js
14982
- import { existsSync as existsSync13, readFileSync as readFileSync17 } from "node:fs";
14983
- import { join as join21 } from "node:path";
15106
+ import { existsSync as existsSync14, readFileSync as readFileSync18 } from "node:fs";
15107
+ import { join as join22 } from "node:path";
14984
15108
 
14985
15109
  // dist/draw/write.js
14986
- import { closeSync as closeSync2, lstatSync as lstatSync3, mkdirSync as mkdirSync9, openSync as openSync2, readFileSync as readFileSync14, readSync, realpathSync as realpathSync6, rmSync as rmSync7 } from "node:fs";
14987
- import { dirname as dirname6, isAbsolute as isAbsolute4, join as join16, relative as relative3, resolve as resolve4, sep } from "node:path";
15110
+ import { closeSync as closeSync2, lstatSync as lstatSync3, mkdirSync as mkdirSync9, openSync as openSync2, readFileSync as readFileSync15, readSync, realpathSync as realpathSync6, rmSync as rmSync7 } from "node:fs";
15111
+ import { dirname as dirname7, isAbsolute as isAbsolute4, join as join17, relative as relative3, resolve as resolve4, sep } from "node:path";
14988
15112
  var DOCS_DIR = "docs";
14989
15113
  var DIAGRAMS_DIR = "diagrams";
14990
15114
  var DIAGRAM_DIR_REL = `${DOCS_DIR}/${DIAGRAMS_DIR}`;
@@ -15040,9 +15164,9 @@ function embedImageLink(base, doc) {
15040
15164
  }
15041
15165
  function diagramPaths(repoRoot, base) {
15042
15166
  const root = resolve4(repoRoot);
15043
- const dir = join16(root, DOCS_DIR, DIAGRAMS_DIR);
15167
+ const dir = join17(root, DOCS_DIR, DIAGRAMS_DIR);
15044
15168
  const names = fileNames(base);
15045
- const file = Object.fromEntries(DIAGRAM_FILES.map((which) => [which, join16(dir, names[which])]));
15169
+ const file = Object.fromEntries(DIAGRAM_FILES.map((which) => [which, join17(dir, names[which])]));
15046
15170
  return { repoRoot: root, dir, file };
15047
15171
  }
15048
15172
  function diagramRel(base, which) {
@@ -15077,7 +15201,7 @@ function checkDiagramPlace(paths) {
15077
15201
  }
15078
15202
  let ancestor = paths.dir;
15079
15203
  while (lstatOrNull2(ancestor) === null) {
15080
- const parent = dirname6(ancestor);
15204
+ const parent = dirname7(ancestor);
15081
15205
  if (parent === ancestor)
15082
15206
  break;
15083
15207
  ancestor = parent;
@@ -15137,7 +15261,7 @@ function readExistingSet(paths) {
15137
15261
  if (!stat.isFile())
15138
15262
  return { ok: false, inTheWay: which, temp: false };
15139
15263
  try {
15140
- found[which] = readFileSync14(paths.file[which]);
15264
+ found[which] = readFileSync15(paths.file[which]);
15141
15265
  } catch {
15142
15266
  return { ok: false, inTheWay: which, temp: false };
15143
15267
  }
@@ -15315,10 +15439,10 @@ function isMissingRoute(method, status, body) {
15315
15439
  }
15316
15440
 
15317
15441
  // dist/change.js
15318
- import { join as join19 } from "node:path";
15442
+ import { join as join20 } from "node:path";
15319
15443
 
15320
15444
  // dist/cli-contract.js
15321
- var CLI_VERBS = ["install", "check-drift", "build-record", "open-phase", "close-phase", "system", "draw", "plan", "review", "check-tasks", "check-criteria", "check-report", "ready", "uninstall", "change", "pace", "route", "prove", "preview", "doctor", "restore", "request-shapes", "allow", "size"];
15445
+ var CLI_VERBS = ["install", "check-drift", "build-record", "open-phase", "close-phase", "system", "draw", "plan", "review", "check-tasks", "check-criteria", "check-report", "ready", "uninstall", "change", "pace", "route", "prove", "preview", "doctor", "restore", "request-shapes", "allow", "size", "regression", "crew"];
15322
15446
  var CONTRACTS = [
15323
15447
  { verb: "install", required: [], conditional: [] },
15324
15448
  { verb: "check-drift", required: [], conditional: [] },
@@ -15522,7 +15646,7 @@ function requirementMessage(verb, flag) {
15522
15646
  function runnableInvocation(verb, phase) {
15523
15647
  const contract = contractFor(verb);
15524
15648
  const flags = (contract?.required ?? []).map((req) => `${req.flag} ${PLACEHOLDERS[req.flag] ?? '"\u2026"'}`).join(" ");
15525
- return `${HALFCYCLE_COMMAND} ${verb} ${quoteForShell(phase)}${flags ? ` ${flags}` : ""}`;
15649
+ return `${ownCommand()} ${verb} ${quoteForShell(phase)}${flags ? ` ${flags}` : ""}`;
15526
15650
  }
15527
15651
  var PLACEHOLDERS = {
15528
15652
  "--verdict": "<clean|defects>",
@@ -15559,12 +15683,12 @@ function placeholderRefusal(verb, tokens) {
15559
15683
 
15560
15684
  // dist/system-report.js
15561
15685
  import { execFileSync as execFileSync4 } from "node:child_process";
15562
- import { readFileSync as readFileSync15 } from "node:fs";
15563
- import { join as join17 } from "node:path";
15686
+ import { readFileSync as readFileSync16 } from "node:fs";
15687
+ import { join as join18 } from "node:path";
15564
15688
  function readDescriptionFile(repoRoot) {
15565
15689
  let text;
15566
15690
  try {
15567
- text = readFileSync15(join17(repoRoot, SYSTEM_DESCRIPTION_REL), "utf-8");
15691
+ text = readFileSync16(join18(repoRoot, SYSTEM_DESCRIPTION_REL), "utf-8");
15568
15692
  } catch (err) {
15569
15693
  const code = err.code;
15570
15694
  return code === "ENOENT" || code === "ENOTDIR" ? { kind: "missing" } : { kind: "unreadable" };
@@ -15699,7 +15823,7 @@ function readCredentialOrLine(repoRoot, deps, verb) {
15699
15823
  } catch {
15700
15824
  return {
15701
15825
  ok: false,
15702
- line: `halfcycle ${verb}: this project is not connected to Halfcycle on this machine. Run ${HALFCYCLE_COMMAND} here first, then run this again. Nothing was sent.
15826
+ line: `halfcycle ${verb}: this project is not connected to Halfcycle on this machine. Run ${ownCommand()} here first, then run this again. Nothing was sent.
15703
15827
  `
15704
15828
  };
15705
15829
  }
@@ -15788,8 +15912,8 @@ async function runSystem(repoRoot, deps) {
15788
15912
  }
15789
15913
 
15790
15914
  // dist/build-record/write-change.js
15791
- import { existsSync as existsSync12, mkdirSync as mkdirSync10 } from "node:fs";
15792
- import { basename as basename3, isAbsolute as isAbsolute5, join as join18, relative as relative4, resolve as resolve5 } from "node:path";
15915
+ import { existsSync as existsSync13, mkdirSync as mkdirSync10 } from "node:fs";
15916
+ import { basename as basename3, isAbsolute as isAbsolute5, join as join19, relative as relative4, resolve as resolve5 } from "node:path";
15793
15917
 
15794
15918
  // dist/build-record/render-change.js
15795
15919
  function counted2(n, one, many) {
@@ -15934,7 +16058,7 @@ function renderChangeRecordMarkdown(record2) {
15934
16058
  }
15935
16059
 
15936
16060
  // dist/build-record/write-change.js
15937
- var CHANGE_RECORD_DIR = join18("docs", "build-records", "changes");
16061
+ var CHANGE_RECORD_DIR = join19("docs", "build-records", "changes");
15938
16062
  var InvalidChangeIdError = class extends Error {
15939
16063
  id;
15940
16064
  constructor(id) {
@@ -15954,8 +16078,8 @@ function writeChangeRecordPair(repoRoot, sortingId, rawRecord, record2, io = NOD
15954
16078
  if (!checked.success)
15955
16079
  throw new InvalidChangeIdError(sortingId);
15956
16080
  const outDir = resolve5(repoRoot, CHANGE_RECORD_DIR);
15957
- const jsonPath = join18(outDir, `${checked.data}.json`);
15958
- const mdPath = join18(outDir, `${checked.data}.md`);
16081
+ const jsonPath = join19(outDir, `${checked.data}.json`);
16082
+ const mdPath = join19(outDir, `${checked.data}.md`);
15959
16083
  const mdTmp = `${mdPath}.tmp`;
15960
16084
  for (const candidate of [jsonPath, mdPath, mdTmp]) {
15961
16085
  assertInsideChangeDir(outDir, candidate, sortingId);
@@ -15968,7 +16092,7 @@ function writeChangeRecordPair(repoRoot, sortingId, rawRecord, record2, io = NOD
15968
16092
  }
15969
16093
  const existingJson = readIfPresent(jsonPath);
15970
16094
  if (existingJson === null) {
15971
- return { kind: "in-the-way", path: existsSync12(jsonPath) ? jsonPath : mdPath };
16095
+ return { kind: "in-the-way", path: existsSync13(jsonPath) ? jsonPath : mdPath };
15972
16096
  }
15973
16097
  if (existingJson !== jsonText)
15974
16098
  return { kind: "in-the-way", path: jsonPath };
@@ -15977,9 +16101,9 @@ function writeChangeRecordPair(repoRoot, sortingId, rawRecord, record2, io = NOD
15977
16101
  }
15978
16102
 
15979
16103
  // dist/change.js
15980
- var CHANGE_USAGE = `${HALFCYCLE_COMMAND} change sort --request "<one line>" --touches <path>\u2026 [--contract <from>:<to>]\u2026 [--stored-data] [--standing-rule] [--new-part] [--repo <root>]
15981
- ${HALFCYCLE_COMMAND} change up <mini|full> (--change <id> | --phase <phase>) [--repo <root>]
15982
- ${HALFCYCLE_COMMAND} change done <id> [--repo <root>]`;
16104
+ var CHANGE_USAGE = `${ownCommand()} change sort --request "<one line>" --touches <path>\u2026 [--contract <from>:<to>]\u2026 [--stored-data] [--standing-rule] [--new-part] [--repo <root>]
16105
+ ${ownCommand()} change up <mini|full> (--change <id> | --phase <phase>) [--repo <root>]
16106
+ ${ownCommand()} change done <id> [--repo <root>]`;
15983
16107
  function usage(line) {
15984
16108
  return { ok: false, line: `${line}
15985
16109
  Usage:
@@ -16176,7 +16300,7 @@ ${nextSayLine(sorted.next.say)}
16176
16300
  `;
16177
16301
  }
16178
16302
  function changeDoneCommand(sortingId) {
16179
- return `${HALFCYCLE_COMMAND} change done ${quoteForShell(sortingId)}`;
16303
+ return `${ownCommand()} change done ${quoteForShell(sortingId)}`;
16180
16304
  }
16181
16305
  function recordOutcomeLines(record2) {
16182
16306
  switch (record2.outcome) {
@@ -16231,7 +16355,7 @@ function writePairLines(deps, repoRoot, sortingId, found) {
16231
16355
  };
16232
16356
  }
16233
16357
  return {
16234
- stdout: `[halfcycle] Its record is in ${join19(CHANGE_RECORD_DIR, `${sortingId}.md`)}.
16358
+ stdout: `[halfcycle] Its record is in ${join20(CHANGE_RECORD_DIR, `${sortingId}.md`)}.
16235
16359
  `,
16236
16360
  stderr: "",
16237
16361
  ok: true
@@ -16445,7 +16569,7 @@ async function runChangeDone(repoRoot, sortingId, deps) {
16445
16569
  case "not-configured":
16446
16570
  return {
16447
16571
  stdout: "",
16448
- stderr: `${p} this project is not connected to Halfcycle on this machine. Run ${HALFCYCLE_COMMAND} here first, then run this again. Nothing was sent.
16572
+ stderr: `${p} this project is not connected to Halfcycle on this machine. Run ${ownCommand()} here first, then run this again. Nothing was sent.
16449
16573
  `,
16450
16574
  exitCode: 1
16451
16575
  };
@@ -16470,24 +16594,24 @@ function runChange(repoRoot, args2, deps) {
16470
16594
  }
16471
16595
 
16472
16596
  // dist/shown-record.js
16473
- import { chmodSync as chmodSync3, mkdirSync as mkdirSync11, readFileSync as readFileSync16, renameSync as renameSync3, unlinkSync, writeFileSync as writeFileSync8 } from "node:fs";
16597
+ import { chmodSync as chmodSync3, mkdirSync as mkdirSync11, readFileSync as readFileSync17, renameSync as renameSync3, unlinkSync, writeFileSync as writeFileSync8 } from "node:fs";
16474
16598
  import { platform as platform4 } from "node:os";
16475
- import { join as join20 } from "node:path";
16599
+ import { join as join21 } from "node:path";
16476
16600
  function updateShownRecord(engagementId, update, home) {
16477
16601
  let temp = null;
16478
16602
  try {
16479
16603
  const dir = engagementStateDir(engagementId, home);
16480
- const path = join20(dir, SHOWN_PLANS_FILENAME);
16604
+ const path = join21(dir, SHOWN_PLANS_FILENAME);
16481
16605
  let current;
16482
16606
  try {
16483
- current = parseShownRecord(readFileSync16(path, "utf-8"));
16607
+ current = parseShownRecord(readFileSync17(path, "utf-8"));
16484
16608
  } catch {
16485
16609
  current = { entries: [], reviews: [] };
16486
16610
  }
16487
16611
  const next = update(current);
16488
16612
  const body = serialiseShownRecord(next);
16489
16613
  mkdirSync11(dir, { recursive: true, mode: 448 });
16490
- temp = join20(dir, `.${SHOWN_PLANS_FILENAME}.${process.pid}.tmp`);
16614
+ temp = join21(dir, `.${SHOWN_PLANS_FILENAME}.${process.pid}.tmp`);
16491
16615
  writeFileSync8(temp, body, { mode: 384 });
16492
16616
  if (platform4() !== "win32")
16493
16617
  chmodSync3(temp, 384);
@@ -16505,7 +16629,7 @@ function updateShownRecord(engagementId, update, home) {
16505
16629
  }
16506
16630
 
16507
16631
  // dist/plan.js
16508
- var PLAN_USAGE = `${HALFCYCLE_COMMAND} plan <phase> [--show] [--repo <root>]`;
16632
+ var PLAN_USAGE = `${ownCommand()} plan <phase> [--show] [--repo <root>]`;
16509
16633
  var PLAN_DIR_REL = "docs/phases";
16510
16634
  function planFileRel(phase) {
16511
16635
  return `${PLAN_DIR_REL}/${renderPhaseSegment(phase)}.plan.json`;
@@ -16564,7 +16688,7 @@ function readAndCheckPlan(repoRoot, phase, description, verb) {
16564
16688
  const fileRel = planFileRel(phase);
16565
16689
  let text;
16566
16690
  try {
16567
- text = readFileSync17(join21(repoRoot, fileRel), "utf-8");
16691
+ text = readFileSync18(join22(repoRoot, fileRel), "utf-8");
16568
16692
  } catch (err) {
16569
16693
  const code = err.code;
16570
16694
  if (code === "ENOENT" || code === "ENOTDIR") {
@@ -16714,19 +16838,19 @@ function notAPhaseNameLine(verb, phase) {
16714
16838
  `;
16715
16839
  }
16716
16840
  function removalOwedLines(verb, parts) {
16717
- return parts.map((part) => `halfcycle ${verb}: docs/system.json still holds "${part.name}", which phase "${part.phase}"'s review ${part.act}. Take it out, run ${HALFCYCLE_COMMAND} system, then run this again.
16841
+ return parts.map((part) => `halfcycle ${verb}: docs/system.json still holds "${part.name}", which phase "${part.phase}"'s review ${part.act}. Take it out, run ${ownCommand()} system, then run this again.
16718
16842
  `).join("");
16719
16843
  }
16720
16844
  function redrawLine(repoRoot, phase) {
16721
16845
  let exists;
16722
16846
  try {
16723
- exists = existsSync13(join21(repoRoot, DIAGRAM_DIR_REL, embedImageName(phaseDiagramBase(phase))));
16847
+ exists = existsSync14(join22(repoRoot, DIAGRAM_DIR_REL, embedImageName(phaseDiagramBase(phase))));
16724
16848
  } catch {
16725
16849
  return null;
16726
16850
  }
16727
16851
  if (!exists)
16728
16852
  return null;
16729
- return `This phase's picture in your docs may show an earlier plan. Draw it again with: ${HALFCYCLE_COMMAND} draw phase ${quoteForShell(phase)}
16853
+ return `This phase's picture in your docs may show an earlier plan. Draw it again with: ${ownCommand()} draw phase ${quoteForShell(phase)}
16730
16854
  `;
16731
16855
  }
16732
16856
  function movedUpLines(movedUp) {
@@ -16753,7 +16877,7 @@ function approvalLine2(phase, number, typedApprovalOffered, pinnedVersion, shown
16753
16877
  return then(TYPED_APPROVAL_OFF_SENTENCE, "The project's owner approves it in the System view:");
16754
16878
  }
16755
16879
  if (!clientMeetsChatApprovalFloor(pinnedVersion)) {
16756
- return then(CLIENT_UPDATE_SENTENCE, "Or the project's owner approves it in the System view now:");
16880
+ return then(clientUpdateSentence(ownVersion()), "Or the project's owner approves it in the System view now:");
16757
16881
  }
16758
16882
  return then(`To approve it, the project's owner types \`approve ${phase}\` in their own interactive session ${HEADLESS_CANNOT_APPROVE}. Only the owner can.`, "They can also approve it in the System view:");
16759
16883
  }
@@ -16842,7 +16966,7 @@ ${redraw}`;
16842
16966
  }
16843
16967
 
16844
16968
  // dist/review.js
16845
- var REVIEW_USAGE = `${HALFCYCLE_COMMAND} review <phase> (--show | --answer <part> "<answer>") [--repo <root>]`;
16969
+ var REVIEW_USAGE = `${ownCommand()} review <phase> (--show | --answer <part> "<answer>") [--repo <root>]`;
16846
16970
  function parseReviewArgs(argv) {
16847
16971
  const positionals = [];
16848
16972
  let repoRoot;
@@ -17061,7 +17185,7 @@ function rv8Line(view) {
17061
17185
  }
17062
17186
  return `halfcycle review: phase "${view.phase}" has no review open. Its review opens when its build is finished and the close step runs.`;
17063
17187
  }
17064
- var RV9_LINE = `halfcycle review: this project is not signed in on this computer. ${HOOK_REMEDY}`;
17188
+ var RV9_LINE = `halfcycle review: this project is not signed in on this computer. ${hookRemedy(ownVersion())}`;
17065
17189
  var RV10_LINE = "halfcycle review: Halfcycle could not be reached just now. Check this computer's connection, then run this again.";
17066
17190
  var RV11_LINE = "halfcycle review: the list just shown could not be recorded on this computer, so a typed decision cannot use it. Run this again.";
17067
17191
  function an1Line(name) {
@@ -17680,7 +17804,7 @@ var PHASE_MODELS_FILENAME = "phase-models.json";
17680
17804
  var PACING_FOLDER = "pacing";
17681
17805
  function pacingFile(engagementId, name, home) {
17682
17806
  try {
17683
- return join22(engagementStateDir(engagementId, home), PACING_FOLDER, name);
17807
+ return join23(engagementStateDir(engagementId, home), PACING_FOLDER, name);
17684
17808
  } catch {
17685
17809
  return null;
17686
17810
  }
@@ -17691,7 +17815,7 @@ function readPhaseLimitFile(engagementId, phase, home) {
17691
17815
  return null;
17692
17816
  let text;
17693
17817
  try {
17694
- text = readFileSync18(path, "utf-8");
17818
+ text = readFileSync19(path, "utf-8");
17695
17819
  } catch (err) {
17696
17820
  return err.code === "ENOENT" ? "missing" : null;
17697
17821
  }
@@ -17732,7 +17856,7 @@ function relayedChoiceLines(engagementId, phase, home) {
17732
17856
  if (path === null)
17733
17857
  return [];
17734
17858
  try {
17735
- const parsed = relayedChoicesFileSchema.safeParse(JSON.parse(readFileSync18(path, "utf-8")));
17859
+ const parsed = relayedChoicesFileSchema.safeParse(JSON.parse(readFileSync19(path, "utf-8")));
17736
17860
  if (!parsed.success || parsed.data.phase !== phase)
17737
17861
  return [];
17738
17862
  return parsed.data.entries.map((entry) => relayedChoiceLineOf(entry));
@@ -17745,7 +17869,7 @@ function readPhaseModels(engagementId, phase, openedAt, home) {
17745
17869
  if (path === null)
17746
17870
  return null;
17747
17871
  try {
17748
- const parsed = phaseModelsFileSchema.safeParse(JSON.parse(readFileSync18(path, "utf-8")));
17872
+ const parsed = phaseModelsFileSchema.safeParse(JSON.parse(readFileSync19(path, "utf-8")));
17749
17873
  if (!parsed.success || parsed.data.phase !== phase)
17750
17874
  return null;
17751
17875
  if (openedAt !== null && parsed.data.openedAt !== openedAt)
@@ -17755,8 +17879,8 @@ function readPhaseModels(engagementId, phase, openedAt, home) {
17755
17879
  return null;
17756
17880
  }
17757
17881
  }
17758
- function usageTokensOf(usage3) {
17759
- return usage3.inputTokens + usage3.outputTokens + usage3.cacheWriteTokens + usage3.cacheReadTokens;
17882
+ function usageTokensOf(usage5) {
17883
+ return usage5.inputTokens + usage5.outputTokens + usage5.cacheWriteTokens + usage5.cacheReadTokens;
17760
17884
  }
17761
17885
  function tierFigureLines(work, expected, summary, models, details = false) {
17762
17886
  const lines = [];
@@ -17848,12 +17972,12 @@ function readRepoCredential(repoRoot, env = process.env, home) {
17848
17972
  if (pinned !== null)
17849
17973
  assertEngagementId(pinned);
17850
17974
  const fromStore = pinned === null ? null : readEngagementEnv(pinned, home);
17851
- const looked = pinned === null ? `${join23(repoRoot, ".halfcycle", "bundle.json")} (which names no engagement)` : engagementEnvPath(pinned, home);
17975
+ const looked = pinned === null ? `${join24(repoRoot, ".halfcycle", "bundle.json")} (which names no engagement)` : engagementEnvPath(pinned, home);
17852
17976
  const values = {};
17853
17977
  for (const key of OPEN_PHASE_ENV_KEYS) {
17854
17978
  const value = (env[key] ?? fromStore?.[key] ?? "").trim();
17855
17979
  if (value === "") {
17856
- throw new Error(`[halfcycle] ${key} is not set \u2014 it is neither in this shell's environment nor in ${looked}. That file is written by \`npx halfcycle\`; if this repo has no Halfcycle install yet, or this machine has never installed this engagement, run the installer here first.`);
17980
+ throw new Error(`[halfcycle] ${key} is not set \u2014 it is neither in this shell's environment nor in ${looked}. That file is written by \`${ownBareCommand()}\`; if this repo has no Halfcycle install yet, or this machine has never installed this engagement, run the installer here first.`);
17857
17981
  }
17858
17982
  values[key] = value;
17859
17983
  }
@@ -17865,7 +17989,7 @@ function readRepoCredential(repoRoot, env = process.env, home) {
17865
17989
  }
17866
17990
  function readPinnedEngagementId(repoRoot) {
17867
17991
  try {
17868
- const pin = JSON.parse(readFileSync19(join23(repoRoot, ".halfcycle", "bundle.json"), "utf-8"));
17992
+ const pin = JSON.parse(readFileSync20(join24(repoRoot, ".halfcycle", "bundle.json"), "utf-8"));
17869
17993
  const id = pin[PIN_ENGAGEMENT_ID_FIELD];
17870
17994
  return typeof id === "string" && id !== "" ? id : null;
17871
17995
  } catch {
@@ -17887,7 +18011,7 @@ function writePhaseStamp(engagementId, phase, home) {
17887
18011
  return { path, reason: err instanceof Error ? err.message : String(err) };
17888
18012
  }
17889
18013
  }
17890
- async function openPhase(credential, phase, entry, home, repoRoot, sortingId, sizeId) {
18014
+ async function openPhase(credential, phase, entry, home, repoRoot, sortingId, sizeId, regression) {
17891
18015
  const parsed = phaseEntryDecisionSchema.safeParse(entry);
17892
18016
  if (!parsed.success) {
17893
18017
  throw new Error(`[halfcycle] The entry decision is not a decision this platform accepts: ${parsed.error.issues.map((i) => i.message).join("; ")}.`);
@@ -17909,6 +18033,9 @@ async function openPhase(credential, phase, entry, home, repoRoot, sortingId, si
17909
18033
  if (phase !== null && sizeId !== void 0) {
17910
18034
  requestBody.sizeId = sizeId;
17911
18035
  }
18036
+ if (phase !== null && regression === true) {
18037
+ requestBody.regression = true;
18038
+ }
17912
18039
  const url = `${credential.serviceUrl}/engagements/${encodeURIComponent(credential.engagementId)}/phase`;
17913
18040
  let res;
17914
18041
  try {
@@ -18038,7 +18165,7 @@ async function obtainBuildRecord(credential, repoRoot, phase, io = NODE_RECORD_P
18038
18165
  };
18039
18166
  }
18040
18167
  function refetchCommand(phase) {
18041
- return `${HALFCYCLE_COMMAND} build-record ${quoteForShell(phase)}`;
18168
+ return `${ownCommand()} build-record ${quoteForShell(phase)}`;
18042
18169
  }
18043
18170
 
18044
18171
  // dist/phase-work.js
@@ -18073,7 +18200,7 @@ async function readPhaseWork(credential, phase) {
18073
18200
  }
18074
18201
 
18075
18202
  // dist/draw/index.js
18076
- var DRAW_USAGE = `${HALFCYCLE_COMMAND} draw (architecture | phase <phase>) [--repo <root>]`;
18203
+ var DRAW_USAGE = `${ownCommand()} draw (architecture | phase <phase>) [--repo <root>]`;
18077
18204
  function parseDrawArgs(argv) {
18078
18205
  const positionals = [];
18079
18206
  let repoRoot;
@@ -18349,9 +18476,9 @@ async function writeDrawOutcome(repoRoot, deps, outcome, target, warnings, canno
18349
18476
  }
18350
18477
 
18351
18478
  // dist/check-tasks.js
18352
- import { readFileSync as readFileSync20 } from "node:fs";
18353
- import { join as join24, resolve as resolve6 } from "node:path";
18354
- var CHECK_TASKS_USAGE = `${HALFCYCLE_COMMAND} check-tasks <phase> [--with-task-list] [--repo <root>]`;
18479
+ import { readFileSync as readFileSync21 } from "node:fs";
18480
+ import { join as join25, resolve as resolve6 } from "node:path";
18481
+ var CHECK_TASKS_USAGE = `${ownCommand()} check-tasks <phase> [--with-task-list] [--repo <root>]`;
18355
18482
  function parseCheckTasksArgs(argv) {
18356
18483
  const positionals = [];
18357
18484
  let repoRoot;
@@ -18391,7 +18518,7 @@ function issuePlace(rel, path) {
18391
18518
  function readJsonFile(repoRoot, rel, parse) {
18392
18519
  let text;
18393
18520
  try {
18394
- text = readFileSync20(resolve6(repoRoot, rel), "utf-8");
18521
+ text = readFileSync21(resolve6(repoRoot, rel), "utf-8");
18395
18522
  } catch (err) {
18396
18523
  const code = err.code;
18397
18524
  if (code === "ENOENT" || code === "ENOTDIR")
@@ -18449,7 +18576,7 @@ function readPhasePlanParts(repoRoot, phase, description) {
18449
18576
  const file = planFileRel(phase);
18450
18577
  let text;
18451
18578
  try {
18452
- text = readFileSync20(join24(repoRoot, file), "utf-8");
18579
+ text = readFileSync21(join25(repoRoot, file), "utf-8");
18453
18580
  } catch (err) {
18454
18581
  const code = err.code;
18455
18582
  if (code === "ENOENT" || code === "ENOTDIR")
@@ -18482,7 +18609,7 @@ function readPlanSpecFiles(repoRoot, plan) {
18482
18609
  const rel = `${SPEC_FOLDER}/${slug}.md`;
18483
18610
  let text;
18484
18611
  try {
18485
- text = readFileSync20(join24(repoRoot, rel), "utf-8");
18612
+ text = readFileSync21(join25(repoRoot, rel), "utf-8");
18486
18613
  } catch (err) {
18487
18614
  const code = err.code;
18488
18615
  if (code === "ENOENT" || code === "ENOTDIR") {
@@ -18622,7 +18749,7 @@ async function runCheckTasksSized(repoRoot, phase, withTaskList, deps) {
18622
18749
  }
18623
18750
 
18624
18751
  // dist/check-criteria.js
18625
- var CHECK_CRITERIA_USAGE = `${HALFCYCLE_COMMAND} check-criteria [<record file>] [--repo <root>]`;
18752
+ var CHECK_CRITERIA_USAGE = `${ownCommand()} check-criteria [<record file>] [--repo <root>]`;
18626
18753
  function parseCheckCriteriaArgs(argv) {
18627
18754
  const positionals = [];
18628
18755
  let repoRoot;
@@ -18673,8 +18800,8 @@ function runCheckCriteria(repoRoot, file) {
18673
18800
  }
18674
18801
 
18675
18802
  // dist/check-report.js
18676
- import { readFileSync as readFileSync21 } from "node:fs";
18677
- var CHECK_REPORT_USAGE = `${HALFCYCLE_COMMAND} check-report <file | -> --head <sha>`;
18803
+ import { readFileSync as readFileSync22 } from "node:fs";
18804
+ var CHECK_REPORT_USAGE = `${ownCommand()} check-report <file | -> --head <sha>`;
18678
18805
  function parseCheckReportArgs(argv) {
18679
18806
  const positionals = [];
18680
18807
  let head;
@@ -18709,7 +18836,7 @@ function runCheckReport(source, head, deps = {}) {
18709
18836
  }
18710
18837
  let body;
18711
18838
  try {
18712
- body = source === "-" ? (deps.readStdin ?? (() => readFileSync21(0, "utf-8")))() : readFileSync21(source, "utf-8");
18839
+ body = source === "-" ? (deps.readStdin ?? (() => readFileSync22(0, "utf-8")))() : readFileSync22(source, "utf-8");
18713
18840
  } catch (err) {
18714
18841
  const code = err.code;
18715
18842
  const what = source === "-" ? "The report could not be read from standard input" : `${source} could not be read`;
@@ -18727,7 +18854,7 @@ function runCheckReport(source, head, deps = {}) {
18727
18854
  }
18728
18855
 
18729
18856
  // dist/ready.js
18730
- var READY_USAGE = `${HALFCYCLE_COMMAND} ready <phase> [--cap <n>] [--repo <root>]`;
18857
+ var READY_USAGE = `${ownCommand()} ready <phase> [--cap <n>] [--repo <root>]`;
18731
18858
  var STATES_TEXT = buildTaskStateSchema.options.join(", ");
18732
18859
  var READY_LINES = {
18733
18860
  start: (id, title) => `Ready to start: ${named(id, title)}`,
@@ -18876,8 +19003,8 @@ function runReady(repoRoot, phase, cap) {
18876
19003
 
18877
19004
  // dist/prove.js
18878
19005
  import { spawn as spawn2 } from "node:child_process";
18879
- var PROVE_USAGE = `${HALFCYCLE_COMMAND} prove <phase> <task> --label "<label>" --before -- <test command>
18880
- ${HALFCYCLE_COMMAND} prove <phase> <task> --label "<label>" --after [-- <test command>]
19006
+ var PROVE_USAGE = `${ownCommand()} prove <phase> <task> --label "<label>" --before -- <test command>
19007
+ ${ownCommand()} prove <phase> <task> --label "<label>" --after [-- <test command>]
18881
19008
  (or --change <id> in place of <phase> <task>; add --reason "<why>" to say why) [--repo <root>]`;
18882
19009
  function splitProveInvocation(words) {
18883
19010
  const dash = words.indexOf("--");
@@ -18952,9 +19079,9 @@ function runProveChild(binary, credential, repoRoot, words, deps = {}) {
18952
19079
  }
18953
19080
 
18954
19081
  // dist/uninstall.js
18955
- import { existsSync as existsSync14, lstatSync as lstatSync4, readFileSync as readFileSync22, readdirSync, rmSync as rmSync8, rmdirSync, writeFileSync as writeFileSync9 } from "node:fs";
19082
+ import { existsSync as existsSync15, lstatSync as lstatSync4, readFileSync as readFileSync23, readdirSync, rmSync as rmSync8, rmdirSync, writeFileSync as writeFileSync9 } from "node:fs";
18956
19083
  import { homedir as homedir7 } from "node:os";
18957
- import { dirname as dirname7, join as join25, relative as relative6, sep as sep2 } from "node:path";
19084
+ import { dirname as dirname8, join as join26, relative as relative6, sep as sep2 } from "node:path";
18958
19085
  var REMOVE_CREDENTIAL_FLAG = "--remove-credential";
18959
19086
  function parseUninstallArgs(args2) {
18960
19087
  let removeCredential = false;
@@ -18989,13 +19116,13 @@ function readMergedJson(targetRepo, rule) {
18989
19116
  const { rel } = rule;
18990
19117
  if (symlinkOnPath(targetRepo, rel))
18991
19118
  return { state: "symlink" };
18992
- const path = join25(targetRepo, rel);
19119
+ const path = join26(targetRepo, rel);
18993
19120
  if (!lexists(path))
18994
19121
  return { state: "absent" };
18995
19122
  let text;
18996
19123
  let value;
18997
19124
  try {
18998
- text = readFileSync22(path, "utf-8");
19125
+ text = readFileSync23(path, "utf-8");
18999
19126
  } catch {
19000
19127
  text = "";
19001
19128
  }
@@ -19008,29 +19135,29 @@ function readMergedJson(targetRepo, rule) {
19008
19135
  return { state: "object", text, value };
19009
19136
  if (!rule.holdsOurs(text))
19010
19137
  return { state: "not-ours" };
19011
- throw new Refusal(`${rel} is not a JSON object, so Halfcycle cannot take its own entries out of it and nothing was removed. Fix it or delete it, then run "${HALFCYCLE_COMMAND} uninstall" again.`);
19138
+ throw new Refusal(`${rel} is not a JSON object, so Halfcycle cannot take its own entries out of it and nothing was removed. Fix it or delete it, then run "${ownCommand()} uninstall" again.`);
19012
19139
  }
19013
19140
  function validate(options, rules) {
19014
19141
  const { targetRepo, removeCredential, home } = options;
19015
- const manifestPath = join25(targetRepo, INSTALL_MANIFEST_REL);
19016
- const pinPath = join25(targetRepo, BUNDLE_PIN_REL);
19142
+ const manifestPath = join26(targetRepo, INSTALL_MANIFEST_REL);
19143
+ const pinPath = join26(targetRepo, BUNDLE_PIN_REL);
19017
19144
  if (symlinkOnPath(targetRepo, INSTALL_MANIFEST_REL) || symlinkOnPath(targetRepo, BUNDLE_PIN_REL)) {
19018
19145
  throw new Refusal(`.halfcycle, or a file Halfcycle keeps in it, is a symbolic link here, so nothing was followed and nothing was removed.`);
19019
19146
  }
19020
19147
  let manifest = null;
19021
19148
  if (lexists(manifestPath)) {
19022
19149
  try {
19023
- manifest = parseManifest(readFileSync22(manifestPath, "utf-8"));
19150
+ manifest = parseManifest(readFileSync23(manifestPath, "utf-8"));
19024
19151
  } catch (err) {
19025
19152
  const why = err instanceof UnreadableManifestError ? err.message : "it could not be read";
19026
- throw new Refusal(`${INSTALL_MANIFEST_REL} cannot be read (${why}), so nothing was removed. Restore it from git, or run "${HALFCYCLE_COMMAND}" once to rewrite it, then run "${HALFCYCLE_COMMAND} uninstall" again.`);
19153
+ throw new Refusal(`${INSTALL_MANIFEST_REL} cannot be read (${why}), so nothing was removed. Restore it from git, or run "${ownCommand()}" once to rewrite it, then run "${ownCommand()} uninstall" again.`);
19027
19154
  }
19028
19155
  }
19029
19156
  let engagementId;
19030
19157
  let pinReadable = true;
19031
19158
  if (lexists(pinPath)) {
19032
19159
  try {
19033
- const pin = JSON.parse(readFileSync22(pinPath, "utf-8"));
19160
+ const pin = JSON.parse(readFileSync23(pinPath, "utf-8"));
19034
19161
  const id = isPlainObject4(pin) ? pin[PIN_ENGAGEMENT_ID_FIELD] : void 0;
19035
19162
  engagementId = typeof id === "string" && id !== "" ? id : void 0;
19036
19163
  } catch {
@@ -19054,8 +19181,8 @@ function validate(options, rules) {
19054
19181
  throw new Refusal(`the project id in ${BUNDLE_PIN_REL} is not a valid Halfcycle project id, so it cannot name a folder on this machine. Nothing was removed, here or under ~/.halfcycle.`);
19055
19182
  }
19056
19183
  if (engagementId !== void 0) {
19057
- const expectedParent = join25(home ?? homedir7(), HALFCYCLE_DIR_NAME, ENGAGEMENTS_DIR_NAME);
19058
- if (dirname7(engagementStateDir(engagementId, home)) !== expectedParent) {
19184
+ const expectedParent = join26(home ?? homedir7(), HALFCYCLE_DIR_NAME, ENGAGEMENTS_DIR_NAME);
19185
+ if (dirname8(engagementStateDir(engagementId, home)) !== expectedParent) {
19059
19186
  throw new Refusal(`the project id in ${BUNDLE_PIN_REL} does not name a folder under ~/.halfcycle/engagements. Nothing was removed.`);
19060
19187
  }
19061
19188
  }
@@ -19080,7 +19207,7 @@ function mergedFileRules(writeSet) {
19080
19207
  function writeBack(targetRepo, rel, current, next, lists, name) {
19081
19208
  if (next === current)
19082
19209
  return;
19083
- const path = join25(targetRepo, rel);
19210
+ const path = join26(targetRepo, rel);
19084
19211
  try {
19085
19212
  if (next === null) {
19086
19213
  rmSync8(path);
@@ -19097,7 +19224,7 @@ function statusLineClaimedFromTree(targetRepo, writeSet) {
19097
19224
  try {
19098
19225
  if (symlinkOnPath(targetRepo, SETTINGS_REL))
19099
19226
  return {};
19100
- const value = JSON.parse(readFileSync22(join25(targetRepo, SETTINGS_REL), "utf-8"));
19227
+ const value = JSON.parse(readFileSync23(join26(targetRepo, SETTINGS_REL), "utf-8"));
19101
19228
  if (!isPlainObject4(value) || !("statusLine" in value))
19102
19229
  return {};
19103
19230
  const hash = canonicalSha256(value["statusLine"]);
@@ -19111,7 +19238,7 @@ function noRecord(targetRepo, writeSet) {
19111
19238
  const claim = (rel, header, lines) => {
19112
19239
  let text;
19113
19240
  try {
19114
- text = symlinkOnPath(targetRepo, rel) ? "" : readFileSync22(join25(targetRepo, rel), "utf-8");
19241
+ text = symlinkOnPath(targetRepo, rel) ? "" : readFileSync23(join26(targetRepo, rel), "utf-8");
19115
19242
  } catch {
19116
19243
  text = "";
19117
19244
  }
@@ -19352,7 +19479,7 @@ function lastOccurrence(text, block) {
19352
19479
  }
19353
19480
  function coveredPathExists(targetRepo, line) {
19354
19481
  const path = line.trim().replace(/^\//, "").replace(/\/$/, "");
19355
- return path !== "" && lexists(join25(targetRepo, path));
19482
+ return path !== "" && lexists(join26(targetRepo, path));
19356
19483
  }
19357
19484
  function selfBuildMergedFileRules() {
19358
19485
  return {
@@ -19366,7 +19493,7 @@ function removeSelfBuildOwnedFile(targetRepo, rel, lists, name) {
19366
19493
  name(lists.leftAlone, `${rel} (a symbolic link, or inside one \u2014 not followed)`, rel);
19367
19494
  return;
19368
19495
  }
19369
- const abs = join25(targetRepo, rel);
19496
+ const abs = join26(targetRepo, rel);
19370
19497
  let isFile;
19371
19498
  try {
19372
19499
  isFile = lstatSync4(abs).isFile();
@@ -19390,10 +19517,10 @@ function removeSelfBuildCrewRoster(targetRepo, lists, name) {
19390
19517
  name(lists.leftAlone, `${rel} (a symbolic link, or inside one \u2014 not followed)`, rel);
19391
19518
  return;
19392
19519
  }
19393
- const abs = join25(targetRepo, rel);
19520
+ const abs = join26(targetRepo, rel);
19394
19521
  let bytes;
19395
19522
  try {
19396
- bytes = readFileSync22(abs);
19523
+ bytes = readFileSync23(abs);
19397
19524
  } catch {
19398
19525
  return;
19399
19526
  }
@@ -19417,10 +19544,10 @@ function removeSelfBuildCrewProfile(targetRepo, rel, text, lists, name) {
19417
19544
  name(lists.leftAlone, `${rel} (a symbolic link, or inside one \u2014 not followed)`, rel);
19418
19545
  return;
19419
19546
  }
19420
- const abs = join25(targetRepo, rel);
19547
+ const abs = join26(targetRepo, rel);
19421
19548
  let bytes;
19422
19549
  try {
19423
- bytes = readFileSync22(abs);
19550
+ bytes = readFileSync23(abs);
19424
19551
  } catch {
19425
19552
  return;
19426
19553
  }
@@ -19442,7 +19569,7 @@ function removeSelfBuildCrewProfile(targetRepo, rel, text, lists, name) {
19442
19569
  function removeSelfBuildEmptyDir(targetRepo, dir, lists, name) {
19443
19570
  if (symlinkOnPath(targetRepo, dir))
19444
19571
  return;
19445
- const abs = join25(targetRepo, dir);
19572
+ const abs = join26(targetRepo, dir);
19446
19573
  try {
19447
19574
  if (!lstatSync4(abs).isDirectory() || readdirSync(abs).length > 0)
19448
19575
  return;
@@ -19476,7 +19603,7 @@ function uninstallSelfBuild(options) {
19476
19603
  throw err;
19477
19604
  }
19478
19605
  const { targetRepo } = options;
19479
- const pinExisted = lexists(join25(targetRepo, BUNDLE_PIN_REL));
19606
+ const pinExisted = lexists(join26(targetRepo, BUNDLE_PIN_REL));
19480
19607
  const name = (list2, text, _path) => {
19481
19608
  list2.push(text);
19482
19609
  };
@@ -19558,7 +19685,7 @@ function uninstall(options) {
19558
19685
  removeCodexConfigBlock(targetRepo, manifest.codexConfig, deleteCodexWhenEmpty(manifest.codexConfig), result, name);
19559
19686
  if (holdsLocalAllowRecord(targetRepo)) {
19560
19687
  removeLocalAllow(targetRepo);
19561
- if (existsSync14(join25(targetRepo, LOCAL_SETTINGS_REL2))) {
19688
+ if (existsSync15(join26(targetRepo, LOCAL_SETTINGS_REL2))) {
19562
19689
  name(result.restored, LOCAL_SETTINGS_REL2, LOCAL_SETTINGS_REL2);
19563
19690
  } else {
19564
19691
  name(result.removed, LOCAL_SETTINGS_REL2, LOCAL_SETTINGS_REL2);
@@ -19601,7 +19728,7 @@ function uninstall(options) {
19601
19728
  const gitattributes = manifest.gitattributes ?? { created: false, appended: [] };
19602
19729
  trimAppendedFile(targetRepo, gitattributes, deleteWhenEmpty(gitattributes.created), gitattributesRules(writeSet), result, name);
19603
19730
  const foundNothing = result.removed.length + result.restored.length + result.kept.length + result.failed.length === 0 && result.keptIgnoreLines.length === 0;
19604
- if (checked.manifest === null && !lexists(join25(targetRepo, BUNDLE_PIN_REL)) && foundNothing) {
19731
+ if (checked.manifest === null && !lexists(join26(targetRepo, BUNDLE_PIN_REL)) && foundNothing) {
19605
19732
  return { outcome: "not-installed", exitCode: 0, ...result, credential: nothingToSay };
19606
19733
  }
19607
19734
  const credential = handleCredential(options, checked);
@@ -19609,7 +19736,7 @@ function uninstall(options) {
19609
19736
  if (result.failed.length === 0 && !credentialFailed) {
19610
19737
  removeInstalledFile(targetRepo, BUNDLE_PIN_REL, recorded.get(BUNDLE_PIN_REL), writeSet, result, name);
19611
19738
  try {
19612
- rmSync8(join25(targetRepo, INSTALL_MANIFEST_REL), { force: true });
19739
+ rmSync8(join26(targetRepo, INSTALL_MANIFEST_REL), { force: true });
19613
19740
  named2.add(INSTALL_MANIFEST_REL);
19614
19741
  } catch {
19615
19742
  name(result.failed, INSTALL_MANIFEST_REL, INSTALL_MANIFEST_REL);
@@ -19644,13 +19771,13 @@ function applyMergedJson(targetRepo, rule, file, deleteWhenEmpty, lists, name, e
19644
19771
  return;
19645
19772
  const empty = Object.keys(value).length === 0 && deleteWhenEmpty;
19646
19773
  writeBack(targetRepo, rel, file.text, empty ? null : inLineEndingOf(file.text, serializeJson(value)), lists, name);
19647
- if (adopted && lexists(join25(targetRepo, rel))) {
19774
+ if (adopted && lexists(join26(targetRepo, rel))) {
19648
19775
  name(lists.leftAlone, `${rel} (Halfcycle's hooks were taken out. There is no record of this file from before the install, so a permission rule or "$schema" Halfcycle added may still be there \u2014 it cannot be told apart from your own)`, rel);
19649
19776
  }
19650
19777
  }
19651
19778
  function removeCodexConfigBlock(targetRepo, record2, deleteWhenEmpty, lists, name) {
19652
19779
  const rel = CODEX_CONFIG_REL2;
19653
- const path = join25(targetRepo, rel);
19780
+ const path = join26(targetRepo, rel);
19654
19781
  if (symlinkOnPath(targetRepo, rel)) {
19655
19782
  if (lexists(path))
19656
19783
  name(lists.leftAlone, `${rel} (a symbolic link, or inside one \u2014 not followed)`, rel);
@@ -19660,7 +19787,7 @@ function removeCodexConfigBlock(targetRepo, record2, deleteWhenEmpty, lists, nam
19660
19787
  try {
19661
19788
  if (!lstatSync4(path).isFile())
19662
19789
  return;
19663
- text = readFileSync22(path, "utf-8");
19790
+ text = readFileSync23(path, "utf-8");
19664
19791
  } catch {
19665
19792
  return;
19666
19793
  }
@@ -19695,7 +19822,7 @@ function removeInstalledFile(targetRepo, path, entry, writeSet, lists, name) {
19695
19822
  name(lists.leftAlone, `${path} (a symbolic link, or inside one \u2014 not followed)`, path);
19696
19823
  return;
19697
19824
  }
19698
- const abs = join25(targetRepo, path);
19825
+ const abs = join26(targetRepo, path);
19699
19826
  let isFile;
19700
19827
  try {
19701
19828
  isFile = lstatSync4(abs).isFile();
@@ -19709,7 +19836,7 @@ function removeInstalledFile(targetRepo, path, entry, writeSet, lists, name) {
19709
19836
  if (!writeSet.ownedDirs.has(path.slice(0, path.lastIndexOf("/")))) {
19710
19837
  let bytes;
19711
19838
  try {
19712
- bytes = readFileSync22(abs);
19839
+ bytes = readFileSync23(abs);
19713
19840
  } catch {
19714
19841
  name(lists.failed, path, path);
19715
19842
  return;
@@ -19729,7 +19856,7 @@ function removeInstalledFile(targetRepo, path, entry, writeSet, lists, name) {
19729
19856
  function removeEmptyDir(targetRepo, dir, writeSet, lists, name) {
19730
19857
  if (!writeSet.dirs.has(dir) || symlinkOnPath(targetRepo, dir))
19731
19858
  return;
19732
- const abs = join25(targetRepo, dir);
19859
+ const abs = join26(targetRepo, dir);
19733
19860
  try {
19734
19861
  if (!lstatSync4(abs).isDirectory() || readdirSync(abs).length > 0)
19735
19862
  return;
@@ -19748,10 +19875,10 @@ function trimAppendedFile(targetRepo, rec, deleteWhenEmpty, rules, lists, name)
19748
19875
  name(lists.leftAlone, `${rel} (a symbolic link \u2014 not followed)`, rel);
19749
19876
  return [];
19750
19877
  }
19751
- const path = join25(targetRepo, rel);
19878
+ const path = join26(targetRepo, rel);
19752
19879
  let before;
19753
19880
  try {
19754
- before = readFileSync22(path, "utf-8");
19881
+ before = readFileSync23(path, "utf-8");
19755
19882
  } catch {
19756
19883
  return [];
19757
19884
  }
@@ -19795,16 +19922,16 @@ function filesUnder(root, rel) {
19795
19922
  const walk = (dirRel) => {
19796
19923
  let names;
19797
19924
  try {
19798
- if (!lstatSync4(join25(root, dirRel)).isDirectory())
19925
+ if (!lstatSync4(join26(root, dirRel)).isDirectory())
19799
19926
  return;
19800
- names = readdirSync(join25(root, dirRel));
19927
+ names = readdirSync(join26(root, dirRel));
19801
19928
  } catch {
19802
19929
  return;
19803
19930
  }
19804
19931
  for (const entry of names.sort()) {
19805
19932
  const childRel = `${dirRel}/${entry}`;
19806
19933
  try {
19807
- if (lstatSync4(join25(root, childRel)).isDirectory())
19934
+ if (lstatSync4(join26(root, childRel)).isDirectory())
19808
19935
  walk(childRel);
19809
19936
  else
19810
19937
  out.push(childRel);
@@ -19843,13 +19970,13 @@ function renderUninstallReport(result, home) {
19843
19970
  section("Kept, because it changed after Halfcycle wrote it \u2014 delete it yourself if you do not want it:", result.kept);
19844
19971
  section('Kept in .gitignore, because the path each line covers still exists \u2014 without the line, "git add ." would commit it:', result.keptIgnoreLines);
19845
19972
  section("Left alone \u2014 Halfcycle did not write these, or cannot tell that it did:", result.leftAlone);
19846
- section(`Could not remove \u2014 fix the cause, then run "${HALFCYCLE_COMMAND} uninstall" again to finish:`, result.failed);
19973
+ section(`Could not remove \u2014 fix the cause, then run "${ownCommand()} uninstall" again to finish:`, result.failed);
19847
19974
  const c = result.credential;
19848
19975
  switch (c.kind) {
19849
19976
  case "still-held":
19850
19977
  out.push(`[halfcycle] This machine still holds this project's credential, at ${shownPath(c.dir, home)}.`);
19851
19978
  out.push(" Every checkout and worktree of this project on this machine uses it.");
19852
- out.push(` To remove it too: ${HALFCYCLE_COMMAND} uninstall ${REMOVE_CREDENTIAL_FLAG}`);
19979
+ out.push(` To remove it too: ${ownCommand()} uninstall ${REMOVE_CREDENTIAL_FLAG}`);
19853
19980
  break;
19854
19981
  case "invalid-id":
19855
19982
  out.push(`[halfcycle] This machine may still hold a credential for this project, but the project id in ${BUNDLE_PIN_REL} is not valid, so it is not shown as a path.`);
@@ -19857,7 +19984,7 @@ function renderUninstallReport(result, home) {
19857
19984
  case "removed":
19858
19985
  out.push(`[halfcycle] Removed this machine's credential for this project, at ${shownPath(c.dir, home)}.`);
19859
19986
  out.push(" Every other checkout and worktree of this project on this machine used it too. Each stops reaching");
19860
- out.push(` Halfcycle until \`${HALFCYCLE_COMMAND}\` is run there again.`);
19987
+ out.push(` Halfcycle until \`${ownCommand()}\` is run there again.`);
19861
19988
  break;
19862
19989
  case "not-held":
19863
19990
  out.push(`[halfcycle] This machine holds no credential for this project (nothing at ${shownPath(c.dir, home)}).`);
@@ -19866,7 +19993,7 @@ function renderUninstallReport(result, home) {
19866
19993
  out.push(`[halfcycle] No credential was removed: ${BUNDLE_PIN_REL} is missing, so there is no project id to find it by.`);
19867
19994
  break;
19868
19995
  case "failed":
19869
- out.push(`[halfcycle] Could not remove this machine's credential for this project, at ${shownPath(c.dir, home)}: ${c.reason}. Run "${HALFCYCLE_COMMAND} uninstall ${REMOVE_CREDENTIAL_FLAG}" again to finish.`);
19996
+ out.push(`[halfcycle] Could not remove this machine's credential for this project, at ${shownPath(c.dir, home)}: ${c.reason}. Run "${ownCommand()} uninstall ${REMOVE_CREDENTIAL_FLAG}" again to finish.`);
19870
19997
  break;
19871
19998
  case "nothing-to-say":
19872
19999
  break;
@@ -19876,8 +20003,7 @@ function renderUninstallReport(result, home) {
19876
20003
  }
19877
20004
 
19878
20005
  // dist/banner-facts.js
19879
- import { readFileSync as readFileSync23 } from "node:fs";
19880
- import { basename as basename4, join as join26, resolve as resolve7 } from "node:path";
20006
+ import { basename as basename4, resolve as resolve7 } from "node:path";
19881
20007
  var BRAND_URL = "halfcycle.ai";
19882
20008
  var TAGLINE = [
19883
20009
  "Fewer cycles.",
@@ -19888,14 +20014,6 @@ var TAGLINE_NARROW = [
19888
20014
  "The thinking moves earlier.",
19889
20015
  "So does the finish."
19890
20016
  ];
19891
- function bundleVersion() {
19892
- try {
19893
- const pkg = JSON.parse(readFileSync23(join26(BUNDLE_ROOT, "package.json"), "utf-8"));
19894
- return typeof pkg.version === "string" ? pkg.version : void 0;
19895
- } catch {
19896
- return void 0;
19897
- }
19898
- }
19899
20017
  function claudeCodeVersion(deps = {}) {
19900
20018
  return probeClaudeCodeVersion(3e3, deps);
19901
20019
  }
@@ -19984,8 +20102,8 @@ function safeBasename(dir) {
19984
20102
  }
19985
20103
 
19986
20104
  // dist/size.js
19987
- var SIZE_USAGE = `${HALFCYCLE_COMMAND} size sort --description "<one line>" [--existing-code] [--sign-in] [--shared-data] [--paid-services] [--more-than-three-parts] [--repo <root>]
19988
- ${HALFCYCLE_COMMAND} size up <medium|full> [--repo <root>]`;
20105
+ var SIZE_USAGE = `${ownCommand()} size sort --description "<one line>" [--existing-code] [--sign-in] [--shared-data] [--paid-services] [--more-than-three-parts] [--repo <root>]
20106
+ ${ownCommand()} size up <medium|full> [--repo <root>]`;
19989
20107
  function usage2(line) {
19990
20108
  return { ok: false, line: `${line}
19991
20109
  Usage:
@@ -20114,7 +20232,7 @@ async function runSizeSort(repoRoot, args2, deps) {
20114
20232
  } catch {
20115
20233
  return {
20116
20234
  stdout: "",
20117
- stderr: `halfcycle size: this project is not connected to Halfcycle on this machine. Run ${HALFCYCLE_COMMAND} here first, then run this again. Nothing was sent.
20235
+ stderr: `halfcycle size: this project is not connected to Halfcycle on this machine. Run ${ownCommand()} here first, then run this again. Nothing was sent.
20118
20236
  `,
20119
20237
  exitCode: 1
20120
20238
  };
@@ -20162,7 +20280,7 @@ async function runSizeUp(repoRoot, args2, deps) {
20162
20280
  } catch {
20163
20281
  return {
20164
20282
  stdout: "",
20165
- stderr: `halfcycle size: this project is not connected to Halfcycle on this machine. Run ${HALFCYCLE_COMMAND} here first, then run this again. Nothing was sent.
20283
+ stderr: `halfcycle size: this project is not connected to Halfcycle on this machine. Run ${ownCommand()} here first, then run this again. Nothing was sent.
20166
20284
  `,
20167
20285
  exitCode: 1
20168
20286
  };
@@ -20192,11 +20310,134 @@ function runSize(repoRoot, args2, deps) {
20192
20310
  return args2.form === "sort" ? runSizeSort(repoRoot, args2, deps) : runSizeUp(repoRoot, args2, deps);
20193
20311
  }
20194
20312
 
20313
+ // dist/regression.js
20314
+ var REGRESSION_USAGE = `${ownCommand()} regression --every <1 to 20> [--repo <root>]`;
20315
+ function usage3(line) {
20316
+ return { ok: false, line: `${line}
20317
+ Usage:
20318
+ ${REGRESSION_USAGE}
20319
+ `, exitCode: 2 };
20320
+ }
20321
+ function parseRegressionArgs(rest) {
20322
+ let every = null;
20323
+ let repoRoot;
20324
+ for (let i = 0; i < rest.length; i++) {
20325
+ const w = rest[i];
20326
+ if (w === "--every" || w === "--repo") {
20327
+ const v = rest[i + 1];
20328
+ if (v === void 0 || v.trim() === "" || w === "--repo" && v.startsWith("--")) {
20329
+ return usage3(`halfcycle regression: ${w} needs a value after it.`);
20330
+ }
20331
+ if (w === "--every")
20332
+ every = v;
20333
+ else
20334
+ repoRoot = v;
20335
+ i++;
20336
+ } else {
20337
+ return usage3(`halfcycle regression: "${w}" is not something this command takes.`);
20338
+ }
20339
+ }
20340
+ if (every === null)
20341
+ return usage3("halfcycle regression: say how many phases may close between regressions.");
20342
+ const number = /^-?\d+$/.test(every.trim()) ? Number(every) : Number.NaN;
20343
+ const parsed = regressionEveryRequestSchema.safeParse({ every: number });
20344
+ if (!parsed.success) {
20345
+ return { ok: false, line: `${REGRESSION_EVERY_RANGE_REFUSAL}
20346
+ `, exitCode: 2 };
20347
+ }
20348
+ return { ok: true, args: { request: parsed.data, ...repoRoot === void 0 ? {} : { repoRoot } } };
20349
+ }
20350
+ var COULD_NOT_REACH3 = "Halfcycle could not be reached. Nothing was changed; check this machine's connection, then run this again.";
20351
+ async function runRegression(repoRoot, args2, deps) {
20352
+ const p = "halfcycle regression:";
20353
+ let credential;
20354
+ try {
20355
+ credential = deps.readCredential(repoRoot);
20356
+ } catch {
20357
+ return {
20358
+ stdout: "",
20359
+ stderr: `${p} this project is not connected to Halfcycle on this machine. Run ${ownCommand()} here first, then run this again. Nothing was sent.
20360
+ `,
20361
+ exitCode: 1
20362
+ };
20363
+ }
20364
+ const url = `${credential.serviceUrl}/engagements/${encodeURIComponent(credential.engagementId)}/regression-every`;
20365
+ let status;
20366
+ let raw = null;
20367
+ try {
20368
+ const res = await deps.fetch(url, {
20369
+ method: "PUT",
20370
+ headers: { authorization: `Bearer ${credential.token}`, "content-type": "application/json" },
20371
+ body: JSON.stringify(args2.request)
20372
+ });
20373
+ status = res.status;
20374
+ const text = await res.text();
20375
+ try {
20376
+ raw = JSON.parse(text);
20377
+ } catch {
20378
+ raw = null;
20379
+ }
20380
+ } catch {
20381
+ return { stdout: "", stderr: `${p} ${COULD_NOT_REACH3}
20382
+ `, exitCode: 1 };
20383
+ }
20384
+ const answered2 = regressionEveryResponseSchema.safeParse(raw);
20385
+ if (status >= 200 && status < 300 && answered2.success) {
20386
+ const line = answered2.data.line === null ? "" : `[halfcycle] ${answered2.data.line}
20387
+ `;
20388
+ return {
20389
+ stdout: `[halfcycle] The number of phases between regressions is now ${answered2.data.every}.
20390
+ ${line}`,
20391
+ stderr: "",
20392
+ exitCode: 0
20393
+ };
20394
+ }
20395
+ const wire = wireErrorSchema.safeParse(raw);
20396
+ const said = wire.success ? wire.data.message : `Halfcycle answered ${status}. Nothing was changed; run this again later.`;
20397
+ return { stdout: "", stderr: `${p} ${said}
20398
+ `, exitCode: 1 };
20399
+ }
20400
+
20401
+ // dist/crew.js
20402
+ var CREW_USAGE = `${ownCommand()} crew [--repo <root>]`;
20403
+ function usage4(line) {
20404
+ return { ok: false, line: `${line}
20405
+ Usage:
20406
+ ${CREW_USAGE}
20407
+ `, exitCode: 2 };
20408
+ }
20409
+ function parseCrewArgs(rest) {
20410
+ let repoRoot;
20411
+ for (let i = 0; i < rest.length; i++) {
20412
+ const w = rest[i];
20413
+ if (w === "--repo") {
20414
+ const v = rest[i + 1];
20415
+ if (v === void 0 || v.trim() === "" || v.startsWith("--")) {
20416
+ return usage4("halfcycle crew: --repo needs a value after it.");
20417
+ }
20418
+ repoRoot = v;
20419
+ i++;
20420
+ } else {
20421
+ return usage4(`halfcycle crew: "${w}" is not something this command takes.`);
20422
+ }
20423
+ }
20424
+ return { ok: true, ...repoRoot === void 0 ? {} : { repoRoot } };
20425
+ }
20426
+ async function runCrew(repoRoot, deps) {
20427
+ const size = await readSizeOnce(repoRoot, deps);
20428
+ const members = crewOfProjectSize(size.kind === "small" ? "small" : void 0);
20429
+ const stdout = members.map((m) => `${m.callsign} ${m.role} ${m.tier}
20430
+ `).join("");
20431
+ const stderr = size.kind === "failed" ? `halfcycle crew: ${SIZE_READ_FAILED_LINE}
20432
+ ` : "";
20433
+ return { stdout, stderr, exitCode: 0 };
20434
+ }
20435
+
20195
20436
  // dist/pace.js
20196
20437
  import { spawn as spawn3 } from "node:child_process";
20197
20438
  import { readFileSync as readFileSync24 } from "node:fs";
20198
20439
  import { join as join27 } from "node:path";
20199
- var PACE_USAGE = `${HALFCYCLE_COMMAND} pace [--agent claude-code|codex] [--start <phase> [--agents <n>]] [--repo <root>]`;
20440
+ var PACE_USAGE = `${ownCommand()} pace [--agent claude-code|codex] [--start <phase> [--agents <n>]] [--repo <root>]`;
20200
20441
  function parsePaceArgs(argv) {
20201
20442
  let repoRoot;
20202
20443
  let phase;
@@ -20302,7 +20543,7 @@ function readPaceLine(stdout) {
20302
20543
  return null;
20303
20544
  }
20304
20545
  }
20305
- var PACE_NOT_CONNECTED_LINE = `halfcycle pace: this project is not signed in on this computer. ${HOOK_REMEDY}`;
20546
+ var PACE_NOT_CONNECTED_LINE = `halfcycle pace: this project is not signed in on this computer. ${hookRemedy(ownVersion())}`;
20306
20547
  function notPacedLine(reason) {
20307
20548
  return reason === "not-configured" ? `${PACE_NOT_CONNECTED_LINE}
20308
20549
  ` : "halfcycle pace: Halfcycle could not read this computer's usage records just now. Run this again in a minute.\n";
@@ -20344,7 +20585,7 @@ async function runPace(repoRoot, start, deps, agent = null) {
20344
20585
  if (result.suggestion === void 0)
20345
20586
  return { stdout: "", stderr: notPacedLine("unreadable"), exitCode: 0 };
20346
20587
  const behind = promptScriptBehind((deps.readPromptScript ?? readPromptScript)(repoRoot));
20347
- return { stdout: `${paceStartLinesOf(result.suggestion, behind).join("\n")}
20588
+ return { stdout: `${paceStartLinesOf(result.suggestion, behind, ownVersion()).join("\n")}
20348
20589
  `, stderr: "", exitCode: 0 };
20349
20590
  }
20350
20591
  const share = result.keepFreePercent ?? KEEP_FREE_DEFAULT;
@@ -20370,7 +20611,7 @@ async function runPace(repoRoot, start, deps, agent = null) {
20370
20611
  // dist/route.js
20371
20612
  import { readFileSync as readFileSync25 } from "node:fs";
20372
20613
  import { join as join28 } from "node:path";
20373
- var ROUTE_USAGE = `${HALFCYCLE_COMMAND} route <phase> <task> [--agent claude-code|codex] [--repo <root>]`;
20614
+ var ROUTE_USAGE = `${ownCommand()} route <phase> <task> [--agent claude-code|codex] [--repo <root>]`;
20374
20615
  var ROUTE_WORK_READ_DEADLINE_MS = 5e3;
20375
20616
  function parseRouteArgs(argv) {
20376
20617
  const positionals = [];
@@ -20506,7 +20747,7 @@ import { spawnSync as spawnSync4 } from "node:child_process";
20506
20747
  import { closeSync as closeSync3, fchmodSync, lstatSync as lstatSync5, mkdirSync as mkdirSync12, openSync as openSync3, readdirSync as readdirSync2, readFileSync as readFileSync26, readlinkSync, realpathSync as realpathSync7, rmSync as rmSync9, symlinkSync, writeSync as writeSync3 } from "node:fs";
20507
20748
  import { homedir as homedir8 } from "node:os";
20508
20749
  import { join as join29, resolve as resolve8 } from "node:path";
20509
- var RESTORE_USAGE = `${HALFCYCLE_COMMAND} restore [<id>]`;
20750
+ var RESTORE_USAGE = `${ownCommand()} restore [<id>]`;
20510
20751
  function parseRestoreArgs(rest) {
20511
20752
  if (rest.length > 1)
20512
20753
  return { ok: false };
@@ -20515,7 +20756,7 @@ function parseRestoreArgs(rest) {
20515
20756
  return { ok: false };
20516
20757
  return { ok: true, id: first };
20517
20758
  }
20518
- var LIST_COMMAND = `${HALFCYCLE_COMMAND} restore`;
20759
+ var LIST_COMMAND = `${ownCommand()} restore`;
20519
20760
  function runRestore(cwd, id, options = {}) {
20520
20761
  const now = options.now ?? Date.now;
20521
20762
  const top = git2(cwd, ["rev-parse", "--show-toplevel"]);
@@ -20525,7 +20766,7 @@ function runRestore(cwd, id, options = {}) {
20525
20766
  const root = realOrSelf(top.text.trim());
20526
20767
  const engagementId = pinnedEngagement(root);
20527
20768
  if (engagementId === null) {
20528
- return refused2(`Halfcycle is not set up in this project, so it has saved nothing here. To set it up, run: ${HALFCYCLE_COMMAND}`);
20769
+ return refused2(`Halfcycle is not set up in this project, so it has saved nothing here. To set it up, run: ${ownCommand()}`);
20529
20770
  }
20530
20771
  const folder2 = join29(options.home ?? homedir8(), HALFCYCLE_DIR_NAME, ENGAGEMENTS_DIR_NAME, engagementId, SAVED_DIR_NAME);
20531
20772
  const refs = savedRefs(root);
@@ -20920,10 +21161,10 @@ function realOrSelf(p) {
20920
21161
  }
20921
21162
 
20922
21163
  // dist/request-shapes-verb.js
20923
- import { chmodSync as chmodSync4, existsSync as existsSync15, mkdirSync as mkdirSync13, readFileSync as readFileSync27, rmSync as rmSync10, writeFileSync as writeFileSync10 } from "node:fs";
21164
+ import { chmodSync as chmodSync4, existsSync as existsSync16, mkdirSync as mkdirSync13, readFileSync as readFileSync27, rmSync as rmSync10, writeFileSync as writeFileSync10 } from "node:fs";
20924
21165
  import { homedir as homedir9, platform as platform5 } from "node:os";
20925
21166
  import { join as join30 } from "node:path";
20926
- var REQUEST_SHAPES_USAGE = `${HALFCYCLE_COMMAND} request-shapes on|off|show|clear`;
21167
+ var REQUEST_SHAPES_USAGE = `${ownCommand()} request-shapes on|off|show|clear`;
20927
21168
  var REQUEST_SHAPES_FORMS = ["on", "off", "show", "clear"];
20928
21169
  var REQUEST_SHAPES_ON_LINE = "Halfcycle will write down the shape of every request this computer sends to it, never what is in them, until you run request-shapes off.";
20929
21170
  var REQUEST_SHAPES_OFF_LINE = "Halfcycle has stopped writing down requests. What it wrote is still there: run request-shapes show to read it, or request-shapes clear to remove it.";
@@ -20967,7 +21208,7 @@ function formatLine(line) {
20967
21208
  function showLog(logPath2) {
20968
21209
  let text = "";
20969
21210
  try {
20970
- text = existsSync15(logPath2) ? readFileSync27(logPath2, "utf-8") : "";
21211
+ text = existsSync16(logPath2) ? readFileSync27(logPath2, "utf-8") : "";
20971
21212
  } catch {
20972
21213
  text = "";
20973
21214
  }
@@ -21004,7 +21245,7 @@ function runRequestShapes(form, home = homedir9()) {
21004
21245
  case "show":
21005
21246
  return { stdout: showLog(logPath2), stderr: "", exitCode: 0 };
21006
21247
  case "clear":
21007
- if (existsSync15(logPath2)) {
21248
+ if (existsSync16(logPath2)) {
21008
21249
  writeFileSync10(logPath2, "", { mode: 384 });
21009
21250
  ownerOnly(logPath2, 384);
21010
21251
  }
@@ -21075,19 +21316,23 @@ function reportCodexVersion(deps = {}) {
21075
21316
  function shortId(id) {
21076
21317
  return id.length > 10 ? `${id.slice(0, 8)}\u2026` : id;
21077
21318
  }
21078
- var USAGE = ` ${HALFCYCLE_COMMAND} [install] [target-repo] [engagement-id] [self-build|client]
21319
+ var USAGE = ` ${ownCommand()} [install] [target-repo] [engagement-id] [self-build|client]
21079
21320
  install into target (default: the current directory)
21080
21321
  --yes / -y: use this machine's saved sign-in without being asked
21081
- ${HALFCYCLE_COMMAND} check-drift <target-repo>
21082
- ${HALFCYCLE_COMMAND} build-record <phase> [--repo <root>]
21322
+ ${ownCommand()} check-drift <target-repo>
21323
+ ${ownCommand()} build-record <phase> [--repo <root>]
21083
21324
  fetch a closed phase's Build Record and write it into docs/build-records/
21084
- ${HALFCYCLE_COMMAND} open-phase <phase|--none> --actor "\u2026" (--evidence "\u2026" | --override --reason "\u2026" | --sorting <id> | --size <id>) [--repo <root>]
21325
+ ${ownCommand()} open-phase <phase|--none> --actor "\u2026" (--evidence "\u2026" | --override --reason "\u2026" | --sorting <id> | --size <id>) [--repo <root>]
21085
21326
  ${SIZE_USAGE}
21086
21327
  size a new project at its first sitting, or move its size up
21087
- ${HALFCYCLE_COMMAND} close-phase <phase> --verdict <clean|defects> --actor "\u2026" [--finding "\u2026"]\u2026 [--details] [--repo <root>]
21328
+ ${REGRESSION_USAGE}
21329
+ set how many phases may close between regression phases
21330
+ ${CREW_USAGE}
21331
+ list the crew this project may use
21332
+ ${ownCommand()} close-phase <phase> --verdict <clean|defects> --actor "\u2026" [--finding "\u2026"]\u2026 [--details] [--repo <root>]
21088
21333
  [--thesis "\u2026"] [--outcome "\u2026"] [--deviation "\u2026"]\u2026
21089
21334
  open the owner's review of the phase; it closes when the owner has decided every part
21090
- ${HALFCYCLE_COMMAND} system [--repo <root>]
21335
+ ${ownCommand()} system [--repo <root>]
21091
21336
  check docs/system.json, the description of your system, and send it to Halfcycle
21092
21337
  ${DRAW_USAGE}
21093
21338
  draw the architecture picture, or one phase's plan, from docs/system.json into docs/diagrams/
@@ -21113,9 +21358,9 @@ var USAGE = ` ${HALFCYCLE_COMMAND} [install] [target-repo] [engagement-id] [sel
21113
21358
  many may start right now
21114
21359
  ${ROUTE_USAGE}
21115
21360
  say how to start a task of a phase's plan: which crew member, and on which tier
21116
- ${HALFCYCLE_COMMAND} preview [folder]
21361
+ ${ownCommand()} preview [folder]
21117
21362
  show what setting up this folder would add, change, remove or leave alone; writes nothing
21118
- ${HALFCYCLE_COMMAND} doctor
21363
+ ${ownCommand()} doctor
21119
21364
  check this folder: set up, signed in, connected, checks running, up to date; says what to do for each one that is not; changes nothing
21120
21365
  ${REQUEST_SHAPES_USAGE}
21121
21366
  write down the shape of every request this computer sends to Halfcycle, never what is in
@@ -21123,11 +21368,11 @@ var USAGE = ` ${HALFCYCLE_COMMAND} [install] [target-repo] [engagement-id] [sel
21123
21368
  ${RESTORE_USAGE}
21124
21369
  list the copies Halfcycle saved before a change it let through, or bring one back; never
21125
21370
  replaces a file or a line of work that exists now
21126
- ${HALFCYCLE_COMMAND} allow [--remove]
21371
+ ${ownCommand()} allow [--remove]
21127
21372
  let your coding agent work without asking on this computer, for tests, builds, installs, local
21128
21373
  tools, files and local git; pushing, publishing, deploying, deleting and GitHub still ask;
21129
21374
  --remove takes out what this added and keeps everything of yours
21130
- ${HALFCYCLE_COMMAND} uninstall [${REMOVE_CREDENTIAL_FLAG}]
21375
+ ${ownCommand()} uninstall [${REMOVE_CREDENTIAL_FLAG}]
21131
21376
  take out what the installer added to this project, keeping anything you changed
21132
21377
  ${REMOVE_CREDENTIAL_FLAG}: also remove this machine's credential for the project, which every
21133
21378
  checkout and worktree of that project on this machine shares
@@ -21140,7 +21385,7 @@ var USAGE = ` ${HALFCYCLE_COMMAND} [install] [target-repo] [engagement-id] [sel
21140
21385
  var RETIRED_VERBS = /* @__PURE__ */ new Map([
21141
21386
  ["ci", '"ci" was removed \u2014 CI setup is part of your project, not Halfcycle']
21142
21387
  ]);
21143
- var PROJECT_RESET_LINE = 'To start your own project here, delete the .halfcycle folder and run "npx halfcycle" again.';
21388
+ var PROJECT_RESET_LINE = `To start your own project here, delete the .halfcycle folder and run "${ownBareCommand()}" again.`;
21144
21389
  function selfBuildNotOwnerLine(account) {
21145
21390
  return `Halfcycle's own project can only be joined by its owner. This computer is signed in as ${account}. Nothing was written.`;
21146
21391
  }
@@ -21293,7 +21538,7 @@ ${USAGE}`);
21293
21538
  if (cmd === "allow") {
21294
21539
  const extra = rest.filter((word, i) => word !== "--remove" && word !== "--repo" && rest[i - 1] !== "--repo");
21295
21540
  if (extra.length > 0) {
21296
- process.stderr.write(`halfcycle allow: usage: ${HALFCYCLE_COMMAND} allow [--remove]
21541
+ process.stderr.write(`halfcycle allow: usage: ${ownCommand()} allow [--remove]
21297
21542
  `);
21298
21543
  process.exitCode = 2;
21299
21544
  return;
@@ -21309,7 +21554,7 @@ ${USAGE}`);
21309
21554
  if (installArm) {
21310
21555
  const engagementIdArg = positionals[1];
21311
21556
  const engagementTypeRaw = positionals[2] ?? "client";
21312
- if (!existsSync16(targetRepo)) {
21557
+ if (!existsSync17(targetRepo)) {
21313
21558
  process.stderr.write(`${folderMissingRefusal(targetRepo)}
21314
21559
  `);
21315
21560
  process.exit(1);
@@ -21535,18 +21780,18 @@ ${PROJECT_RESET_LINE}
21535
21780
  `);
21536
21781
  }
21537
21782
  } else if (probe.slow === true) {
21538
- process.stdout.write(`${SLOW_SERVICE_LINE}${DOCTOR_LINE}
21783
+ process.stdout.write(`${SLOW_SERVICE_LINE}${doctorLine(ownVersion())}
21539
21784
  `);
21540
21785
  } else if (probe.credentialRefused) {
21541
21786
  process.stdout.write(`[halfcycle] Method delivery (MCP): ${credential.mcpUrl} \u2014 ${probe.problem}.
21542
- [halfcycle] The address is right, but Halfcycle commands will not work in this project until it accepts this machine's credential. Re-run "npx halfcycle" in this folder; if it says this again, the refusal is on Halfcycle's side, not in this project.
21543
- ${DOCTOR_LINE}
21787
+ [halfcycle] The address is right, but Halfcycle commands will not work in this project until it accepts this machine's credential. Re-run "${ownBareCommand()}" in this folder; if it says this again, the refusal is on Halfcycle's side, not in this project.
21788
+ ${doctorLine(ownVersion())}
21544
21789
  `);
21545
21790
  } else {
21546
21791
  const otherAddress = controlOriginConfirmed ? `The other address Halfcycle uses answered fine; this is the only one that did not. ` : `This run could not reach the Halfcycle service at all just now, so this may simply be offline. `;
21547
21792
  process.stdout.write(`[halfcycle] Method delivery (MCP): ${credential.mcpUrl} \u2014 NOT reachable: ${probe.problem}.
21548
21793
  [halfcycle] Halfcycle commands will not work in this project until that address answers. ${otherAddress}Run this installer again once the service is up.
21549
- ${DOCTOR_LINE}
21794
+ ${doctorLine(ownVersion())}
21550
21795
  `);
21551
21796
  }
21552
21797
  }
@@ -21636,7 +21881,7 @@ ${kept}`);
21636
21881
  }
21637
21882
  const fsCode = typeof err === "object" && err !== null && "code" in err ? String(err.code) : void 0;
21638
21883
  if (fsCode !== void 0) {
21639
- stop(`halfcycle: could not write to ${targetRepo} \u2014 check that you can write to this folder and run "npx halfcycle" again.`);
21884
+ stop(`halfcycle: could not write to ${targetRepo} \u2014 check that you can write to this folder and run "${ownBareCommand()}" again.`);
21640
21885
  return;
21641
21886
  }
21642
21887
  stop(`halfcycle install failed: ${err instanceof Error ? err.message : String(err)}`);
@@ -21973,6 +22218,42 @@ halfcycle build-record: until ${obtained.stampFailure.path} can be written, the
21973
22218
  process.exitCode = run.exitCode;
21974
22219
  return;
21975
22220
  }
22221
+ if (cmd === "regression") {
22222
+ const parsed = parseRegressionArgs(rest);
22223
+ if (!parsed.ok) {
22224
+ process.stderr.write(parsed.line);
22225
+ process.exitCode = parsed.exitCode;
22226
+ return;
22227
+ }
22228
+ const run = await runRegression(parsed.args.repoRoot ?? process.cwd(), parsed.args, {
22229
+ readCredential: (root) => readRepoCredential(root),
22230
+ fetch: (input, init) => fetch(input, init)
22231
+ });
22232
+ if (run.stdout !== "")
22233
+ process.stdout.write(run.stdout);
22234
+ if (run.stderr !== "")
22235
+ process.stderr.write(run.stderr);
22236
+ process.exitCode = run.exitCode;
22237
+ return;
22238
+ }
22239
+ if (cmd === "crew") {
22240
+ const parsed = parseCrewArgs(rest);
22241
+ if (!parsed.ok) {
22242
+ process.stderr.write(parsed.line);
22243
+ process.exitCode = parsed.exitCode;
22244
+ return;
22245
+ }
22246
+ const run = await runCrew(parsed.repoRoot ?? process.cwd(), {
22247
+ readCredential: (root) => readRepoCredential(root),
22248
+ fetch: (input, init) => fetch(input, init)
22249
+ });
22250
+ if (run.stdout !== "")
22251
+ process.stdout.write(run.stdout);
22252
+ if (run.stderr !== "")
22253
+ process.stderr.write(run.stderr);
22254
+ process.exitCode = run.exitCode;
22255
+ return;
22256
+ }
21976
22257
  if (cmd === "size") {
21977
22258
  const parsed = parseSizeArgs(rest);
21978
22259
  if (!parsed.ok) {
@@ -21997,8 +22278,8 @@ halfcycle build-record: until ${obtained.stampFailure.path} can be written, the
21997
22278
  const phaseArg = firstPositional(rest);
21998
22279
  if (!toProjectScope && !phaseArg) {
21999
22280
  process.stderr.write(`halfcycle open-phase: name the phase to open, or pass --none to return to project scope.
22000
- ${HALFCYCLE_COMMAND} open-phase <phase> --actor "you" --evidence "<what shows this phase is ready to start>"
22001
- ${HALFCYCLE_COMMAND} open-phase <phase> --actor "you" --override --reason "${REASON_PLACEHOLDER["open-phase"]}"
22281
+ ${ownCommand()} open-phase <phase> --actor "you" --evidence "<what shows this phase is ready to start>"
22282
+ ${ownCommand()} open-phase <phase> --actor "you" --override --reason "${REASON_PLACEHOLDER["open-phase"]}"
22002
22283
  `);
22003
22284
  process.exit(2);
22004
22285
  return;
@@ -22050,9 +22331,20 @@ halfcycle build-record: until ${obtained.stampFailure.path} can be written, the
22050
22331
  } else {
22051
22332
  entry = { decision: "entry-condition-met", actor, evidence: flagValue(rest, "--evidence") ?? "" };
22052
22333
  }
22334
+ const regressionArg = rest.includes("--regression");
22335
+ if (regressionArg && toProjectScope) {
22336
+ const refusal3 = openPhaseRequestSchema.safeParse({ phase: null, entry, regression: true });
22337
+ if (!refusal3.success) {
22338
+ const said = refusal3.error.issues.map((issue) => issue.message).join("; ");
22339
+ process.stderr.write(`halfcycle open-phase: --regression ${said}. Nothing was sent.
22340
+ `);
22341
+ process.exit(2);
22342
+ return;
22343
+ }
22344
+ }
22053
22345
  try {
22054
22346
  const credential = readRepoCredential(repoRoot);
22055
- const opened = await openPhase(credential, toProjectScope ? null : phaseArg, entry, void 0, repoRoot, sortingArg, sizeArg);
22347
+ const opened = await openPhase(credential, toProjectScope ? null : phaseArg, entry, void 0, repoRoot, sortingArg, sizeArg, regressionArg);
22056
22348
  process.stdout.write(`[halfcycle] ${describeOpen(opened)}
22057
22349
  `);
22058
22350
  const startCommitLine = describeStartCommit(opened.phase, opened.startCommitSent, opened.startCommit);
@@ -22118,8 +22410,8 @@ halfcycle open-phase: until ${opened.stampFailure.path} can be written, guard ev
22118
22410
  const phaseArg = firstPositional(rest);
22119
22411
  if (!phaseArg) {
22120
22412
  process.stderr.write(`halfcycle close-phase: name the phase whose review you are opening.
22121
- ${HALFCYCLE_COMMAND} close-phase <phase> --verdict clean --actor "you"
22122
- ${HALFCYCLE_COMMAND} close-phase <phase> --verdict defects --actor "you" --finding "<what is still wrong>"
22413
+ ${ownCommand()} close-phase <phase> --verdict clean --actor "you"
22414
+ ${ownCommand()} close-phase <phase> --verdict defects --actor "you" --finding "<what is still wrong>"
22123
22415
  `);
22124
22416
  process.exit(2);
22125
22417
  return;
@@ -22329,7 +22621,7 @@ function firstPositional(argv) {
22329
22621
  const arg = argv[i];
22330
22622
  if (arg.startsWith("--")) {
22331
22623
  const next = argv[i + 1];
22332
- if (next !== void 0 && !next.startsWith("--"))
22624
+ if (arg !== "--regression" && next !== void 0 && !next.startsWith("--"))
22333
22625
  i++;
22334
22626
  continue;
22335
22627
  }