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/index.js CHANGED
@@ -1,10 +1,9 @@
1
1
  // dist/install.js
2
2
  import { execFileSync as execFileSync2 } from "node:child_process";
3
3
  import { createHash as createHash2 } from "node:crypto";
4
- import { existsSync as existsSync4, lstatSync, mkdirSync as mkdirSync4, readFileSync as readFileSync5, rmSync as rmSync2, writeFileSync as writeFileSync4 } from "node:fs";
4
+ import { existsSync as existsSync5, lstatSync, mkdirSync as mkdirSync4, readFileSync as readFileSync6, rmSync as rmSync2, writeFileSync as writeFileSync4 } from "node:fs";
5
5
  import { homedir as homedir2 } from "node:os";
6
- import { dirname as dirname3, isAbsolute as isAbsolute2, join as join6, posix as posix2, relative, resolve } from "node:path";
7
- import { fileURLToPath } from "node:url";
6
+ import { dirname as dirname4, isAbsolute as isAbsolute2, join as join7, posix as posix2, relative, resolve } from "node:path";
8
7
 
9
8
  // ../core/dist/guard-record.js
10
9
  import { z } from "zod";
@@ -268,14 +267,40 @@ function looksLikeSecret(text) {
268
267
  var MCP_REGISTRATION_REL = ".mcp.json";
269
268
  var MCP_SERVER_KEY = "halfcycle";
270
269
 
270
+ // ../core/dist/release-version.js
271
+ var RELEASE_VERSION_PATTERN = /^\d+\.\d+\.\d+$/;
272
+
273
+ // ../core/dist/release-tag.js
274
+ var PRE_RELEASE_VERSION_PATTERN = /^(\d+\.\d+\.\d+)-rc\.\d+$/;
275
+ var PACKAGE_COMMAND = "npx halfcycle";
276
+ var COMMAND_FORMS = /npx halfcycle(?:@latest|(?![@\p{L}\p{N}-]))/gu;
277
+ function isPreReleaseVersion(value) {
278
+ return typeof value === "string" && PRE_RELEASE_VERSION_PATTERN.test(value);
279
+ }
280
+ function releaseTagOfVersion(version) {
281
+ return isPreReleaseVersion(version) ? "next" : "latest";
282
+ }
283
+ function halfcycleCommand(version) {
284
+ return `${PACKAGE_COMMAND}@${releaseTagOfVersion(version)}`;
285
+ }
286
+ function bareHalfcycleCommand(version) {
287
+ return releaseTagOfVersion(version) === "next" ? `${PACKAGE_COMMAND}@next` : PACKAGE_COMMAND;
288
+ }
289
+ function rewriteHalfcycleCommands(text, tag) {
290
+ if (tag === "latest")
291
+ return text;
292
+ return text.replace(COMMAND_FORMS, `${PACKAGE_COMMAND}@${tag}`);
293
+ }
294
+
271
295
  // ../core/dist/shell-quote.js
272
296
  function quoteForShell(value) {
273
297
  return `'${value.replace(/'/g, `'\\''`)}'`;
274
298
  }
275
- var HALFCYCLE_COMMAND = "npx halfcycle@latest";
276
-
277
- // ../core/dist/release-version.js
278
- var RELEASE_VERSION_PATTERN = /^\d+\.\d+\.\d+$/;
299
+ var HALFCYCLE_COMMAND = halfcycleCommand();
300
+ function doctorLine(version) {
301
+ return `Run \`${bareHalfcycleCommand(version)} doctor\` to check set up, sign-in, connection, checks and version.`;
302
+ }
303
+ var DOCTOR_LINE = doctorLine();
279
304
 
280
305
  // ../core/dist/github-address.js
281
306
  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}$/;
@@ -293,8 +318,14 @@ var REMOTE_FORMS = [
293
318
  ];
294
319
 
295
320
  // ../core/dist/sign-in.js
296
- var SIGN_IN_COMMAND = '"npx halfcycle"';
297
- var HOOK_REMEDY = `Run ${SIGN_IN_COMMAND} in the project folder to sign in.`;
321
+ function signInCommand(version) {
322
+ return `"${bareHalfcycleCommand(version)}"`;
323
+ }
324
+ var SIGN_IN_COMMAND = signInCommand();
325
+ function hookRemedy(version) {
326
+ return `Run ${signInCommand(version)} in the project folder to sign in.`;
327
+ }
328
+ var HOOK_REMEDY = hookRemedy();
298
329
 
299
330
  // ../core/dist/task-plan.js
300
331
  import { z as z2 } from "zod";
@@ -408,7 +439,7 @@ function codexHookCommand(script, args) {
408
439
  const invocation = `cd "$(git rev-parse --show-toplevel)" && bash ./${script}`;
409
440
  return args === void 0 ? invocation : `${invocation} ${args}`;
410
441
  }
411
- 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.";
442
+ 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.`;
412
443
  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.";
413
444
  function codexHookCommandWindows(script, args) {
414
445
  const tick = (text) => "`" + text + "`";
@@ -3135,8 +3166,14 @@ var NO_CREDENTIAL_PLACEHOLDER = "halfcycle:no-credential";
3135
3166
  var NOT_THIS_ACCOUNT_PLACEHOLDER = "halfcycle:not-this-account";
3136
3167
  var UPDATE_CLAUDE_CODE_PLACEHOLDER = "halfcycle:update-claude-code";
3137
3168
  var SWITCH_ACCOUNT_ANSWER = 'answer "s" when asked';
3138
- 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.';
3139
- 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}.`;
3169
+ function noCredentialSentence(version) {
3170
+ 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.`;
3171
+ }
3172
+ var NO_CREDENTIAL_SENTENCE = noCredentialSentence();
3173
+ function notThisAccountSentence(version) {
3174
+ return `Halfcycle is not connected: this project belongs to another Halfcycle account. To switch, run "${bareHalfcycleCommand(version)}" in this project and ${SWITCH_ACCOUNT_ANSWER}.`;
3175
+ }
3176
+ var NOT_THIS_ACCOUNT_SENTENCE = notThisAccountSentence();
3140
3177
  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.";
3141
3178
  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.";
3142
3179
  var PLACEHOLDER_SENTENCES = Object.freeze({
@@ -3572,9 +3609,9 @@ var phaseModelsFileSchema = z26.object({
3572
3609
  }).strict();
3573
3610
 
3574
3611
  // dist/engagement-credential.js
3575
- import { chmodSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
3612
+ import { chmodSync, mkdirSync, readFileSync as readFileSync2, writeFileSync } from "node:fs";
3576
3613
  import { platform as platform2 } from "node:os";
3577
- import { join as join2 } from "node:path";
3614
+ import { join as join3 } from "node:path";
3578
3615
 
3579
3616
  // dist/account-credential.js
3580
3617
  import { homedir, platform } from "node:os";
@@ -3583,6 +3620,46 @@ function halfcycleHome(home) {
3583
3620
  return join(home ?? homedir(), HALFCYCLE_DIR_NAME);
3584
3621
  }
3585
3622
 
3623
+ // dist/own-version.js
3624
+ import { existsSync, readFileSync } from "node:fs";
3625
+ import { dirname as dirname2, join as join2 } from "node:path";
3626
+ import { fileURLToPath } from "node:url";
3627
+ var moduleDir = dirname2(fileURLToPath(import.meta.url));
3628
+ function findBundleRoot(startDir) {
3629
+ let candidate = startDir;
3630
+ for (let i = 0; i < 4; i++) {
3631
+ const manifest = join2(candidate, ".claude-plugin", "plugin.json");
3632
+ try {
3633
+ if (existsSync(manifest))
3634
+ return candidate;
3635
+ } catch {
3636
+ }
3637
+ candidate = dirname2(candidate);
3638
+ }
3639
+ return join2(startDir, "..");
3640
+ }
3641
+ var BUNDLE_ROOT = findBundleRoot(moduleDir);
3642
+ function bundleVersion() {
3643
+ try {
3644
+ const pkg = JSON.parse(readFileSync(join2(BUNDLE_ROOT, "package.json"), "utf-8"));
3645
+ return typeof pkg.version === "string" ? pkg.version : void 0;
3646
+ } catch {
3647
+ return void 0;
3648
+ }
3649
+ }
3650
+ var cachedVersion;
3651
+ function ownVersion() {
3652
+ if (cachedVersion === void 0)
3653
+ cachedVersion = { value: bundleVersion() };
3654
+ return cachedVersion.value;
3655
+ }
3656
+ function ownCommand() {
3657
+ return halfcycleCommand(ownVersion());
3658
+ }
3659
+ function ownBareCommand() {
3660
+ return bareHalfcycleCommand(ownVersion());
3661
+ }
3662
+
3586
3663
  // dist/engagement-credential.js
3587
3664
  var ENGAGEMENTS_DIR = ENGAGEMENTS_DIR_NAME;
3588
3665
  var ENV_FILENAME = ENGAGEMENT_ENV_FILENAME;
@@ -3590,7 +3667,7 @@ var HALFCYCLE_DIR = HALFCYCLE_DIR_NAME;
3590
3667
  var InvalidEngagementIdError = class extends Error {
3591
3668
  constructor(engagementId) {
3592
3669
  const shown = JSON.stringify(engagementId.length > 80 ? `${engagementId.slice(0, 80)}\u2026` : engagementId);
3593
- super(`${shown} 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.`);
3670
+ super(`${shown} 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.`);
3594
3671
  this.name = "InvalidEngagementIdError";
3595
3672
  }
3596
3673
  };
@@ -3600,10 +3677,10 @@ function assertEngagementId(engagementId) {
3600
3677
  }
3601
3678
  function engagementStateDir(engagementId, home) {
3602
3679
  assertEngagementId(engagementId);
3603
- return join2(halfcycleHome(home), ENGAGEMENTS_DIR, engagementId);
3680
+ return join3(halfcycleHome(home), ENGAGEMENTS_DIR, engagementId);
3604
3681
  }
3605
3682
  function engagementEnvPath(engagementId, home) {
3606
- return join2(engagementStateDir(engagementId, home), ENV_FILENAME);
3683
+ return join3(engagementStateDir(engagementId, home), ENV_FILENAME);
3607
3684
  }
3608
3685
  var GUARD_ENV_KEYS = [
3609
3686
  "GUARD_SERVICE_URL",
@@ -3625,7 +3702,7 @@ function reconcileEnvText2(existing, values, keys = ENGAGEMENT_ENV_KEYS) {
3625
3702
  }
3626
3703
  function readEngagementEnv(engagementId, home) {
3627
3704
  try {
3628
- return parseEnvText(readFileSync(engagementEnvPath(engagementId, home), "utf-8"));
3705
+ return parseEnvText(readFileSync2(engagementEnvPath(engagementId, home), "utf-8"));
3629
3706
  } catch {
3630
3707
  return null;
3631
3708
  }
@@ -3635,7 +3712,7 @@ function writeEngagementEnv(engagementId, values, home, keys = ENGAGEMENT_ENV_KE
3635
3712
  const dir = engagementStateDir(engagementId, home);
3636
3713
  let existing;
3637
3714
  try {
3638
- existing = readFileSync(path, "utf-8");
3715
+ existing = readFileSync2(path, "utf-8");
3639
3716
  } catch {
3640
3717
  existing = null;
3641
3718
  }
@@ -3666,7 +3743,7 @@ function engagementResolutionShell() {
3666
3743
  return `# --- Halfcycle credential resolution (generated; do not edit) ----------------
3667
3744
  # THE CREDENTIAL IS NOT IN THIS REPOSITORY. It lives at
3668
3745
  # $HOME/${HALFCYCLE_DIR}/${ENGAGEMENTS_DIR}/<engagement-id>/${ENV_FILENAME}
3669
- # written owner-only by \`npx halfcycle\`. What this repository holds is the
3746
+ # written owner-only by \`${bareHalfcycleCommand()}\`. What this repository holds is the
3670
3747
  # engagement id, in the committed .halfcycle/bundle.json. So: read the id out of
3671
3748
  # the pin, then name the file. There is no jq here and node is not dependable on a
3672
3749
  # desktop session's PATH, so the id is cut out with sed \u2014 after flattening the
@@ -3720,8 +3797,8 @@ halfcycle_env_file() {
3720
3797
 
3721
3798
  // dist/identity.js
3722
3799
  import { execFileSync } from "node:child_process";
3723
- import { existsSync, linkSync, mkdirSync as mkdirSync2, readFileSync as readFileSync2, renameSync, rmSync, writeFileSync as writeFileSync2 } from "node:fs";
3724
- import { dirname as dirname2, isAbsolute, join as join3 } from "node:path";
3800
+ import { existsSync as existsSync2, linkSync, mkdirSync as mkdirSync2, readFileSync as readFileSync3, renameSync, rmSync, writeFileSync as writeFileSync2 } from "node:fs";
3801
+ import { dirname as dirname3, isAbsolute, join as join4 } from "node:path";
3725
3802
  import { randomUUID } from "node:crypto";
3726
3803
  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.';
3727
3804
  var PROJECT_IDENTITY_REL = ".halfcycle/project.json";
@@ -3834,13 +3911,13 @@ function readIgnoredPaths(targetRepoRoot, paths) {
3834
3911
  const toplevel = git(targetRepoRoot, ["rev-parse", "--show-toplevel"]);
3835
3912
  if (toplevel === null)
3836
3913
  return null;
3837
- return ignored.map((entry) => isAbsolute(entry.source) ? entry : { ...entry, source: join3(toplevel, entry.source) });
3914
+ return ignored.map((entry) => isAbsolute(entry.source) ? entry : { ...entry, source: join4(toplevel, entry.source) });
3838
3915
  }
3839
3916
  function mintOrReadIdentity(targetRepoRoot) {
3840
- const path = join3(targetRepoRoot, ".halfcycle", "project.json");
3841
- if (existsSync(path)) {
3917
+ const path = join4(targetRepoRoot, ".halfcycle", "project.json");
3918
+ if (existsSync2(path)) {
3842
3919
  repairRemoteCredential(targetRepoRoot);
3843
- const onDisk = JSON.parse(readFileSync2(path, "utf-8"));
3920
+ const onDisk = JSON.parse(readFileSync3(path, "utf-8"));
3844
3921
  const identity = {
3845
3922
  projectId: onDisk.projectId ?? "",
3846
3923
  rootCommit: onDisk.rootCommit ?? null,
@@ -3865,10 +3942,10 @@ function repairRemoteCredential(targetRepoRoot) {
3865
3942
  replaceProjectIdentityFile(targetRepoRoot, repaired);
3866
3943
  }
3867
3944
  function plannedRemoteRepair(targetRepoRoot) {
3868
- const path = join3(targetRepoRoot, PROJECT_IDENTITY_REL);
3945
+ const path = join4(targetRepoRoot, PROJECT_IDENTITY_REL);
3869
3946
  let onDisk;
3870
3947
  try {
3871
- const parsed = JSON.parse(readFileSync2(path, "utf-8"));
3948
+ const parsed = JSON.parse(readFileSync3(path, "utf-8"));
3872
3949
  if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed))
3873
3950
  return null;
3874
3951
  onDisk = parsed;
@@ -3887,7 +3964,7 @@ function plannedRemoteRepair(targetRepoRoot) {
3887
3964
  });
3888
3965
  }
3889
3966
  function replaceProjectIdentityFile(targetRepoRoot, text) {
3890
- const path = join3(targetRepoRoot, PROJECT_IDENTITY_REL);
3967
+ const path = join4(targetRepoRoot, PROJECT_IDENTITY_REL);
3891
3968
  const temporary = `${path}.${process.pid}.${randomUUID()}.tmp`;
3892
3969
  try {
3893
3970
  writeFileSync2(temporary, text, { encoding: "utf-8", flag: "wx" });
@@ -3898,8 +3975,8 @@ function replaceProjectIdentityFile(targetRepoRoot, text) {
3898
3975
  }
3899
3976
  }
3900
3977
  function planProjectIdentity(targetRepoRoot) {
3901
- const path = join3(targetRepoRoot, PROJECT_IDENTITY_REL);
3902
- if (!existsSync(path)) {
3978
+ const path = join4(targetRepoRoot, PROJECT_IDENTITY_REL);
3979
+ if (!existsSync2(path)) {
3903
3980
  return { action: "add", after: serializeProjectIdentity(mintIdentity(targetRepoRoot)) };
3904
3981
  }
3905
3982
  const repaired = plannedRemoteRepair(targetRepoRoot);
@@ -3907,7 +3984,7 @@ function planProjectIdentity(targetRepoRoot) {
3907
3984
  return { action: "change", after: repaired };
3908
3985
  let current;
3909
3986
  try {
3910
- current = readFileSync2(path, "utf-8");
3987
+ current = readFileSync3(path, "utf-8");
3911
3988
  } catch {
3912
3989
  current = null;
3913
3990
  }
@@ -3917,8 +3994,8 @@ function serializeProjectIdentity(identity) {
3917
3994
  return JSON.stringify(identity, null, 2) + "\n";
3918
3995
  }
3919
3996
  function createProjectIdentityFile(targetRepoRoot, text) {
3920
- const path = join3(targetRepoRoot, PROJECT_IDENTITY_REL);
3921
- mkdirSync2(dirname2(path), { recursive: true });
3997
+ const path = join4(targetRepoRoot, PROJECT_IDENTITY_REL);
3998
+ mkdirSync2(dirname3(path), { recursive: true });
3922
3999
  const temporary = `${path}.${process.pid}.${randomUUID()}.tmp`;
3923
4000
  try {
3924
4001
  writeFileSync2(temporary, text ?? serializeProjectIdentity(mintIdentity(targetRepoRoot)), {
@@ -3937,8 +4014,8 @@ function createProjectIdentityFile(targetRepoRoot, text) {
3937
4014
  }
3938
4015
 
3939
4016
  // dist/install-plan.js
3940
- import { existsSync as existsSync3, readFileSync as readFileSync4 } from "node:fs";
3941
- import { join as join5, posix } from "node:path";
4017
+ import { existsSync as existsSync4, readFileSync as readFileSync5 } from "node:fs";
4018
+ import { join as join6, posix } from "node:path";
3942
4019
 
3943
4020
  // dist/install-manifest.js
3944
4021
  import { Buffer as Buffer2 } from "node:buffer";
@@ -4289,8 +4366,8 @@ function union(a, b) {
4289
4366
  }
4290
4367
 
4291
4368
  // dist/scan.js
4292
- import { existsSync as existsSync2, mkdirSync as mkdirSync3, readFileSync as readFileSync3, writeFileSync as writeFileSync3 } from "node:fs";
4293
- import { join as join4 } from "node:path";
4369
+ import { existsSync as existsSync3, mkdirSync as mkdirSync3, readFileSync as readFileSync4, writeFileSync as writeFileSync3 } from "node:fs";
4370
+ import { join as join5 } from "node:path";
4294
4371
  var HALFCYCLE_STATE_FORMAT = "halfcycle-state-file/v1";
4295
4372
  var HALFCYCLE_STATE_NOTE = [
4296
4373
  "This file is a local cache written ONCE when Halfcycle was installed. It is not a",
@@ -4313,7 +4390,7 @@ var LAYER_ARTEFACT_PATHS = {
4313
4390
  function scanLayers(targetRepo, scannedAt = (/* @__PURE__ */ new Date()).toISOString()) {
4314
4391
  const layers = {};
4315
4392
  for (const [layer, paths] of Object.entries(LAYER_ARTEFACT_PATHS)) {
4316
- const found = paths.filter((p) => existsSync2(join4(targetRepo, p)));
4393
+ const found = paths.filter((p) => existsSync3(join5(targetRepo, p)));
4317
4394
  if (found.length > 0) {
4318
4395
  layers[layer] = {
4319
4396
  value: "asserted",
@@ -4333,17 +4410,17 @@ function runBootstrapScan(targetRepo) {
4333
4410
  return { state: planned.state, ran: planned.text !== null };
4334
4411
  }
4335
4412
  function planBootstrapScan(targetRepo) {
4336
- const statePath = join4(targetRepo, BOOTSTRAP_STATE_REL);
4337
- if (existsSync2(statePath)) {
4338
- const existing = JSON.parse(readFileSync3(statePath, "utf-8"));
4413
+ const statePath = join5(targetRepo, BOOTSTRAP_STATE_REL);
4414
+ if (existsSync3(statePath)) {
4415
+ const existing = JSON.parse(readFileSync4(statePath, "utf-8"));
4339
4416
  return { state: existing, text: null };
4340
4417
  }
4341
4418
  const state = scanLayers(targetRepo);
4342
4419
  return { state, text: JSON.stringify(state, null, 2) + "\n" };
4343
4420
  }
4344
4421
  function writeBootstrapState(targetRepo, text) {
4345
- mkdirSync3(join4(targetRepo, ".halfcycle"), { recursive: true });
4346
- writeFileSync3(join4(targetRepo, BOOTSTRAP_STATE_REL), text, "utf-8");
4422
+ mkdirSync3(join5(targetRepo, ".halfcycle"), { recursive: true });
4423
+ writeFileSync3(join5(targetRepo, BOOTSTRAP_STATE_REL), text, "utf-8");
4347
4424
  }
4348
4425
  function mergeWithRecords(state, layersWithRealRecords) {
4349
4426
  const merged = {};
@@ -4508,12 +4585,12 @@ function planInstall(options) {
4508
4585
  throw new Error("[halfcycle] a plan for a project needs its kind as well as its id.");
4509
4586
  }
4510
4587
  }
4511
- if (!existsSync3(targetRepo)) {
4588
+ if (!existsSync4(targetRepo)) {
4512
4589
  throw new Error(`[halfcycle] Target repo does not exist: ${targetRepo}`);
4513
4590
  }
4514
4591
  const writeSet = closedWriteSet();
4515
4592
  const previous = readPreviousManifest(targetRepo);
4516
- const dirsBefore = new Set([...writeSet.dirs].filter((dir) => existsSync3(join5(targetRepo, dir))));
4593
+ const dirsBefore = new Set([...writeSet.dirs].filter((dir) => existsSync4(join6(targetRepo, dir))));
4517
4594
  const { version, commands } = planCommandStubs(targetRepo, previous, writeSet);
4518
4595
  const codexSkills = planCodexSkills(targetRepo, previous, writeSet);
4519
4596
  const vendored = planVendoredBinary(targetRepo);
@@ -4535,7 +4612,7 @@ function planInstall(options) {
4535
4612
  const gitattributes = planGitattributes(targetRepo);
4536
4613
  const identity = planProjectIdentity(targetRepo);
4537
4614
  if (identity.after === null)
4538
- readFileSync4(join5(targetRepo, PROJECT_IDENTITY_REL));
4615
+ readFileSync5(join6(targetRepo, PROJECT_IDENTITY_REL));
4539
4616
  const credentialPlan = !preview && credential !== void 0 ? planEngagementCredential(credential, engagementId, home) : null;
4540
4617
  const credentialWrite = credentialPlan === null ? null : { outcome: credentialPlan.outcome, existed: credentialPlan.existed, path: engagementEnvPath(engagementId, home) };
4541
4618
  const storedAfter = credentialPlan?.storedAfter ?? (preview ? Object.fromEntries(ENGAGEMENT_ENV_KEYS.map((key) => [key, "supplied at sign-in"])) : readEngagementEnv(engagementId, home) ?? {});
@@ -4668,7 +4745,7 @@ function entriesOf(steps, record2) {
4668
4745
  }
4669
4746
  };
4670
4747
  const pending = (rel) => {
4671
- const there = existsSync3(join5(targetRepo, rel));
4748
+ const there = existsSync4(join6(targetRepo, rel));
4672
4749
  entries.push({ path: rel, action: there ? "change" : "add", kind: kindOf(rel), bytes: PENDING });
4673
4750
  };
4674
4751
  steps.commands.forEach(fromFile);
@@ -4716,7 +4793,7 @@ function entriesOf(steps, record2) {
4716
4793
  action: "leave",
4717
4794
  kind: "codex-server",
4718
4795
  bytes: null,
4719
- ...existsSync3(join5(targetRepo, CODEX_CONFIG_REL2)) ? { yours: true } : {}
4796
+ ...existsSync4(join6(targetRepo, CODEX_CONFIG_REL2)) ? { yours: true } : {}
4720
4797
  });
4721
4798
  break;
4722
4799
  }
@@ -4795,10 +4872,10 @@ function applyInstallPlan(plan) {
4795
4872
  putPlannedCodexHooks(targetRepo, steps.codexHooks);
4796
4873
  putPlannedCodexConfig(targetRepo, steps.codexConfig);
4797
4874
  const gitignore = putPlannedGitignore(targetRepo, steps.gitignore);
4798
- const gitignorePath = join5(targetRepo, GITIGNORE_REL);
4799
- const gitignoreAfter = existsSync3(gitignorePath) ? readFileSync4(gitignorePath, "utf-8") : null;
4875
+ const gitignorePath = join6(targetRepo, GITIGNORE_REL);
4876
+ const gitignoreAfter = existsSync4(gitignorePath) ? readFileSync5(gitignorePath, "utf-8") : null;
4800
4877
  const gitattributes = putPlannedGitattributes(targetRepo, steps.gitattributes);
4801
- const gitattributesAfter = readTextOrNull(join5(targetRepo, GITATTRIBUTES_REL));
4878
+ const gitattributesAfter = readTextOrNull(join6(targetRepo, GITATTRIBUTES_REL));
4802
4879
  writeProjectIdentity(targetRepo, steps.identity);
4803
4880
  const credential = steps.credential;
4804
4881
  const credentialOutcome = steps.credentialWrite !== null && credential !== void 0 ? writeEngagementCredential(credential, engagementId, steps.home) : null;
@@ -4819,9 +4896,9 @@ function applyInstallPlan(plan) {
4819
4896
  gitattributesAfter,
4820
4897
  credential: credentialOutcome,
4821
4898
  legacy,
4822
- bytesAfter: (rel) => readFileSync4(join5(targetRepo, rel)),
4823
- dirExistsAfter: (dir) => existsSync3(join5(targetRepo, dir)),
4824
- stateExistsAfter: existsSync3(join5(targetRepo, BOOTSTRAP_STATE_REL))
4899
+ bytesAfter: (rel) => readFileSync5(join6(targetRepo, rel)),
4900
+ dirExistsAfter: (dir) => existsSync4(join6(targetRepo, dir)),
4901
+ stateExistsAfter: existsSync4(join6(targetRepo, BOOTSTRAP_STATE_REL))
4825
4902
  });
4826
4903
  const record2 = planInstallRecord(targetRepo, manifestText);
4827
4904
  putPlannedFile(targetRepo, record2);
@@ -5172,8 +5249,8 @@ function crewProfileRel(agent, callsign) {
5172
5249
  }
5173
5250
  function crewProfileTarget(targetRepo, rel) {
5174
5251
  const folder = rel.startsWith(`${CLAUDE_AGENTS_DIR}/`) ? CLAUDE_AGENTS_DIR : CODEX_AGENTS_DIR;
5175
- const target = resolve(join6(targetRepo, rel));
5176
- if (dirname3(target) !== resolve(targetRepo, folder)) {
5252
+ const target = resolve(join7(targetRepo, rel));
5253
+ if (dirname4(target) !== resolve(targetRepo, folder)) {
5177
5254
  throw new Error(`[halfcycle] crew profile "${rel}" does not sit directly in ${folder}.`);
5178
5255
  }
5179
5256
  return target;
@@ -5256,41 +5333,25 @@ function renderCrewProfiles(roster = CREW_ROSTER) {
5256
5333
  ])
5257
5334
  ];
5258
5335
  }
5259
- var __filename = fileURLToPath(import.meta.url);
5260
- var __dirname = dirname3(__filename);
5261
- function findBundleRoot(startDir) {
5262
- let candidate = startDir;
5263
- for (let i = 0; i < 4; i++) {
5264
- const manifest = join6(candidate, ".claude-plugin", "plugin.json");
5265
- try {
5266
- if (existsSync4(manifest))
5267
- return candidate;
5268
- } catch {
5269
- }
5270
- candidate = dirname3(candidate);
5271
- }
5272
- return join6(startDir, "..");
5273
- }
5274
- var BUNDLE_ROOT = findBundleRoot(__dirname);
5275
5336
  function resolveVendoredBinary() {
5276
5337
  const candidates = [
5277
- join6(BUNDLE_ROOT, "bin", "bin.bundle.mjs"),
5278
- join6(BUNDLE_ROOT, "..", "runner", "dist", "bin.bundle.mjs")
5338
+ join7(BUNDLE_ROOT, "bin", "bin.bundle.mjs"),
5339
+ join7(BUNDLE_ROOT, "..", "runner", "dist", "bin.bundle.mjs")
5279
5340
  ];
5280
5341
  for (const candidate of candidates) {
5281
- if (existsSync4(candidate))
5342
+ if (existsSync5(candidate))
5282
5343
  return candidate;
5283
5344
  }
5284
5345
  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(", ")}.`);
5285
5346
  }
5286
5347
  function resolvePreFilterShell() {
5287
5348
  const candidates = [
5288
- join6(BUNDLE_ROOT, "bin", PRE_FILTER_SHELL_FILENAME),
5289
- join6(BUNDLE_ROOT, "..", "runner", "dist", PRE_FILTER_SHELL_FILENAME)
5349
+ join7(BUNDLE_ROOT, "bin", PRE_FILTER_SHELL_FILENAME),
5350
+ join7(BUNDLE_ROOT, "..", "runner", "dist", PRE_FILTER_SHELL_FILENAME)
5290
5351
  ];
5291
5352
  for (const candidate of candidates) {
5292
- if (existsSync4(candidate))
5293
- return readFileSync5(candidate, "utf-8");
5353
+ if (existsSync5(candidate))
5354
+ return readFileSync6(candidate, "utf-8");
5294
5355
  }
5295
5356
  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(", ")}.`);
5296
5357
  }
@@ -5329,7 +5390,7 @@ function isAllowlisted(targetRelPath) {
5329
5390
  var CREW_ROSTER_REL = ".halfcycle/crew.json";
5330
5391
  var CAPTURED_INDEX_REL = "test/fixtures/captured/manifest.json";
5331
5392
  function capturedIndexScaffold() {
5332
- return join6(BUNDLE_ROOT, "scaffolding", "test", "fixtures", "captured", "manifest.json");
5393
+ return join7(BUNDLE_ROOT, "scaffolding", "test", "fixtures", "captured", "manifest.json");
5333
5394
  }
5334
5395
  var GITIGNORE_REL = ".gitignore";
5335
5396
  var GITATTRIBUTES_REL = ".gitattributes";
@@ -5404,14 +5465,15 @@ function closedWriteSet() {
5404
5465
  function sha256OfWhatThisVersionWrites(commands) {
5405
5466
  const out = /* @__PURE__ */ new Map();
5406
5467
  const fromFile = (rel, src) => {
5407
- if (existsSync4(src))
5408
- out.set(rel, fileSha256(readFileSync5(src)));
5468
+ if (existsSync5(src))
5469
+ out.set(rel, fileSha256(readFileSync6(src)));
5409
5470
  };
5410
5471
  for (const cmd of commands) {
5411
- const srcFile = join6(BUNDLE_ROOT, ".claude-plugin", cmd.path);
5412
- fromFile(commandStubRel(cmd.name), srcFile);
5413
- if (existsSync4(srcFile)) {
5414
- out.set(skillRel(cmd.name), fileSha256(Buffer.from(codexSkillContent(cmd.name, readFileSync5(srcFile, "utf-8")), "utf-8")));
5472
+ const srcFile = join7(BUNDLE_ROOT, ".claude-plugin", cmd.path);
5473
+ if (existsSync5(srcFile)) {
5474
+ const text = commandFileText(readFileSync6(srcFile, "utf-8"), ownVersion());
5475
+ out.set(commandStubRel(cmd.name), fileSha256(Buffer.from(text, "utf-8")));
5476
+ out.set(skillRel(cmd.name), fileSha256(Buffer.from(codexSkillContent(cmd.name, text), "utf-8")));
5415
5477
  }
5416
5478
  }
5417
5479
  for (const [rel, content] of hookScriptsThisVersionWrites())
@@ -5428,7 +5490,7 @@ function bytesAreOurs(path, bytes, recorded, writeSet) {
5428
5490
  }
5429
5491
  function readPreviousManifest(targetRepo) {
5430
5492
  try {
5431
- return parseManifest(readFileSync5(join6(targetRepo, INSTALL_MANIFEST_REL), "utf-8"));
5493
+ return parseManifest(readFileSync6(join7(targetRepo, INSTALL_MANIFEST_REL), "utf-8"));
5432
5494
  } catch {
5433
5495
  return null;
5434
5496
  }
@@ -5532,8 +5594,8 @@ function findNotShared(targetRepo, manifest, collidedPaths) {
5532
5594
  for (let dir = posix2.dirname(rel); dir !== "."; dir = posix2.dirname(dir)) {
5533
5595
  let plain = dirVerdicts.get(dir);
5534
5596
  if (plain === void 0) {
5535
- const abs = join6(targetRepo, dir);
5536
- plain = !lstatOrNull(abs)?.isSymbolicLink() && lstatOrNull(join6(abs, ".git")) === null;
5597
+ const abs = join7(targetRepo, dir);
5598
+ plain = !lstatOrNull(abs)?.isSymbolicLink() && lstatOrNull(join7(abs, ".git")) === null;
5537
5599
  dirVerdicts.set(dir, plain);
5538
5600
  }
5539
5601
  if (!plain)
@@ -5547,7 +5609,7 @@ function findNotShared(targetRepo, manifest, collidedPaths) {
5547
5609
  ...merged,
5548
5610
  INSTALL_MANIFEST_REL
5549
5611
  ])
5550
- ].filter((rel) => !collided.has(rel)).filter((rel) => lstatOrNull(join6(targetRepo, rel)) !== null).filter(travelsThroughPlainDirs).sort();
5612
+ ].filter((rel) => !collided.has(rel)).filter((rel) => lstatOrNull(join7(targetRepo, rel)) !== null).filter(travelsThroughPlainDirs).sort();
5551
5613
  const ignored = readIgnoredPaths(targetRepo, paths);
5552
5614
  if (ignored === null)
5553
5615
  return null;
@@ -5570,7 +5632,7 @@ function lstatOrNull(path) {
5570
5632
  }
5571
5633
  function readFileOrNull(path) {
5572
5634
  try {
5573
- return readFileSync5(path, "utf-8");
5635
+ return readFileSync6(path, "utf-8");
5574
5636
  } catch {
5575
5637
  return null;
5576
5638
  }
@@ -5579,8 +5641,8 @@ function isPlainObject(value) {
5579
5641
  return value !== null && typeof value === "object" && !Array.isArray(value);
5580
5642
  }
5581
5643
  function readPluginManifest() {
5582
- const manifestPath = join6(BUNDLE_ROOT, ".claude-plugin", "plugin.json");
5583
- const raw = readFileSync5(manifestPath, "utf-8");
5644
+ const manifestPath = join7(BUNDLE_ROOT, ".claude-plugin", "plugin.json");
5645
+ const raw = readFileSync6(manifestPath, "utf-8");
5584
5646
  return JSON.parse(raw);
5585
5647
  }
5586
5648
  function parseCommandStub(raw, name) {
@@ -5623,6 +5685,9 @@ function codexSkillBody(name, sourceBody) {
5623
5685
  }
5624
5686
  return transformed;
5625
5687
  }
5688
+ function commandFileText(source, version) {
5689
+ return rewriteHalfcycleCommands(source, releaseTagOfVersion(version));
5690
+ }
5626
5691
  function codexSkillContent(name, sourceRaw) {
5627
5692
  const { description, body } = parseCommandStub(sourceRaw, name);
5628
5693
  const transformed = codexSkillBody(name, body);
@@ -5635,14 +5700,14 @@ ${transformed}`;
5635
5700
  function planManifestCommands(manifest, targetRepo, previous, writeSet) {
5636
5701
  const recordedFiles = new Map((previous?.files ?? []).filter((file) => writeSet.files.has(file.path)).map((file) => [file.path, file]));
5637
5702
  return (manifest.commands ?? []).map((cmd) => {
5638
- const srcFile = join6(BUNDLE_ROOT, ".claude-plugin", cmd.path);
5639
- if (!existsSync4(srcFile)) {
5703
+ const srcFile = join7(BUNDLE_ROOT, ".claude-plugin", cmd.path);
5704
+ if (!existsSync5(srcFile)) {
5640
5705
  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.`);
5641
5706
  }
5642
5707
  const rel = commandStubRel(cmd.name);
5643
- const content = readFileSync5(srcFile, "utf-8");
5708
+ const content = commandFileText(readFileSync6(srcFile, "utf-8"), ownVersion());
5644
5709
  const recorded = recordedFiles.get(rel);
5645
- const planned = decideCollisionSafe(join6(targetRepo, rel), targetRepo, content, (current) => bytesAreOurs(rel, current, recorded, writeSet));
5710
+ const planned = decideCollisionSafe(join7(targetRepo, rel), targetRepo, content, (current) => bytesAreOurs(rel, current, recorded, writeSet));
5646
5711
  return { ...planned, editedCollided: planned.outcome === "collided" && recorded !== void 0 };
5647
5712
  });
5648
5713
  }
@@ -5652,8 +5717,8 @@ function putPlannedFile(targetRepoRoot, planned) {
5652
5717
  if (!isAllowlisted(planned.rel)) {
5653
5718
  throw new Error(`[halfcycle] Write-allowlist violation: attempted to write "${planned.rel}". Only the method surface and scaffolding paths are writable.`);
5654
5719
  }
5655
- const target = join6(targetRepoRoot, planned.rel);
5656
- mkdirSync4(dirname3(target), { recursive: true });
5720
+ const target = join7(targetRepoRoot, planned.rel);
5721
+ mkdirSync4(dirname4(target), { recursive: true });
5657
5722
  writeFileSync4(target, planned.text, "utf-8");
5658
5723
  }
5659
5724
  function writeAllowlisted(targetAbsPath, targetRepoRoot, content, writtenPaths) {
@@ -5663,7 +5728,7 @@ function writeAllowlisted(targetAbsPath, targetRepoRoot, content, writtenPaths)
5663
5728
  }
5664
5729
  if (readFileOrNull(targetAbsPath) === content)
5665
5730
  return;
5666
- mkdirSync4(dirname3(targetAbsPath), { recursive: true });
5731
+ mkdirSync4(dirname4(targetAbsPath), { recursive: true });
5667
5732
  writeFileSync4(targetAbsPath, content, "utf-8");
5668
5733
  writtenPaths.push(rel);
5669
5734
  }
@@ -5672,7 +5737,7 @@ function decideCollisionSafe(targetAbsPath, targetRepoRoot, content, isOurs) {
5672
5737
  if (!isAllowlisted(rel)) {
5673
5738
  throw new Error(`[halfcycle] Write-allowlist violation: attempted to write "${rel}". Only the method surface and scaffolding paths are writable.`);
5674
5739
  }
5675
- const current = existsSync4(targetAbsPath) ? readFileSync5(targetAbsPath) : null;
5740
+ const current = existsSync5(targetAbsPath) ? readFileSync6(targetAbsPath) : null;
5676
5741
  const text = inLineEndingOf(current?.toString("utf-8") ?? null, content);
5677
5742
  if (current === null)
5678
5743
  return { rel, text, outcome: "written", replacedExisting: false };
@@ -5695,7 +5760,7 @@ function decideOwned(targetAbsPath, targetRepoRoot, content) {
5695
5760
  const text = inLineEndingOf(current, content);
5696
5761
  if (current === text)
5697
5762
  return { rel, text, outcome: "skipped", replacedExisting: false };
5698
- return { rel, text, outcome: "written", replacedExisting: existsSync4(targetAbsPath) };
5763
+ return { rel, text, outcome: "written", replacedExisting: existsSync5(targetAbsPath) };
5699
5764
  }
5700
5765
  var GENERATED_HEADER_LINE_INDEX = 1;
5701
5766
  function decideOwnedGenerated(targetAbsPath, targetRepoRoot, content, generatedMarker) {
@@ -5703,9 +5768,9 @@ function decideOwnedGenerated(targetAbsPath, targetRepoRoot, content, generatedM
5703
5768
  if (!isAllowlisted(rel)) {
5704
5769
  throw new Error(`[halfcycle] Write-allowlist violation: attempted to write "${rel}". Only the method surface and scaffolding paths are writable.`);
5705
5770
  }
5706
- if (!existsSync4(targetAbsPath))
5771
+ if (!existsSync5(targetAbsPath))
5707
5772
  return { rel, text: content, outcome: "written", replacedExisting: false };
5708
- const current = readFileSync5(targetAbsPath, "utf-8");
5773
+ const current = readFileSync6(targetAbsPath, "utf-8");
5709
5774
  if (current === content)
5710
5775
  return { rel, text: content, outcome: "skipped", replacedExisting: false };
5711
5776
  const headerLine = current.split("\n")[GENERATED_HEADER_LINE_INDEX] ?? "";
@@ -5735,8 +5800,8 @@ var DENY_PATTERNS = [
5735
5800
  ];
5736
5801
  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.";
5737
5802
  function readExistingSettings(targetRepo) {
5738
- const settingsPath = join6(targetRepo, SETTINGS_REL);
5739
- return existsSync4(settingsPath) ? JSON.parse(readFileSync5(settingsPath, "utf-8")) : void 0;
5803
+ const settingsPath = join7(targetRepo, SETTINGS_REL);
5804
+ return existsSync5(settingsPath) ? JSON.parse(readFileSync6(settingsPath, "utf-8")) : void 0;
5740
5805
  }
5741
5806
  var SettingsParseRefused = class extends Error {
5742
5807
  constructor(message) {
@@ -5745,14 +5810,14 @@ var SettingsParseRefused = class extends Error {
5745
5810
  }
5746
5811
  };
5747
5812
  function settingsParseRefusal(targetRepo, err) {
5748
- const file = join6(targetRepo, SETTINGS_REL);
5813
+ const file = join7(targetRepo, SETTINGS_REL);
5749
5814
  const offset = /position (\d+)/.exec(err instanceof Error ? err.message : "")?.[1];
5750
5815
  let where = "";
5751
5816
  if (offset !== void 0) {
5752
- const before = readFileSync5(file, "utf-8").slice(0, Number(offset)).split("\n");
5817
+ const before = readFileSync6(file, "utf-8").slice(0, Number(offset)).split("\n");
5753
5818
  where = ` (line ${before.length}, column ${before[before.length - 1].length + 1})`;
5754
5819
  }
5755
- return `${file} could not be read as JSON${where} \u2014 fix it and run "npx halfcycle" again.`;
5820
+ return `${file} could not be read as JSON${where} \u2014 fix it and run "${ownBareCommand()}" again.`;
5756
5821
  }
5757
5822
  var SETTINGS_SCHEMA = "https://json.schemastore.org/claude-code-settings.json";
5758
5823
  var HALFCYCLE_STATUS_LINE = Object.freeze({
@@ -5760,18 +5825,18 @@ var HALFCYCLE_STATUS_LINE = Object.freeze({
5760
5825
  command: 'bash "$CLAUDE_PROJECT_DIR"/.claude/hooks/guard-runner.sh statusline'
5761
5826
  });
5762
5827
  function statusLineSetElsewhere(targetRepo, home = homedir2(), env = process.env) {
5763
- const places = [join6(targetRepo, ".claude", "settings.local.json"), join6(home, ".claude", "settings.json")];
5828
+ const places = [join7(targetRepo, ".claude", "settings.local.json"), join7(home, ".claude", "settings.json")];
5764
5829
  const configDir = env["CLAUDE_CONFIG_DIR"];
5765
5830
  if (configDir !== void 0 && configDir !== "") {
5766
- const expanded = configDir === "~" ? home : configDir.startsWith("~/") ? join6(home, configDir.slice(2)) : configDir;
5767
- places.push(join6(isAbsolute2(expanded) ? expanded : resolve(expanded), "settings.json"));
5831
+ const expanded = configDir === "~" ? home : configDir.startsWith("~/") ? join7(home, configDir.slice(2)) : configDir;
5832
+ places.push(join7(isAbsolute2(expanded) ? expanded : resolve(expanded), "settings.json"));
5768
5833
  }
5769
5834
  return places.some((path) => {
5770
- if (!existsSync4(path))
5835
+ if (!existsSync5(path))
5771
5836
  return false;
5772
5837
  let value;
5773
5838
  try {
5774
- value = JSON.parse(readFileSync5(path, "utf-8"));
5839
+ value = JSON.parse(readFileSync6(path, "utf-8"));
5775
5840
  } catch {
5776
5841
  return true;
5777
5842
  }
@@ -6238,12 +6303,12 @@ function isTrackedAndClean(targetRepo, relPath) {
6238
6303
  return true;
6239
6304
  }
6240
6305
  function decideRetiredRemoval(targetRepo, rel, headerLineIndex, knownHeaderHashes) {
6241
- const path = join6(targetRepo, rel);
6242
- if (!existsSync4(path))
6306
+ const path = join7(targetRepo, rel);
6307
+ if (!existsSync5(path))
6243
6308
  return "absent";
6244
6309
  let raw;
6245
6310
  try {
6246
- raw = readFileSync5(path, "utf-8");
6311
+ raw = readFileSync6(path, "utf-8");
6247
6312
  } catch {
6248
6313
  return "failed";
6249
6314
  }
@@ -6257,7 +6322,7 @@ function decideRetiredRemoval(targetRepo, rel, headerLineIndex, knownHeaderHashe
6257
6322
  }
6258
6323
  function removePlannedRetiredFile(targetRepo, rel) {
6259
6324
  try {
6260
- rmSync2(join6(targetRepo, rel));
6325
+ rmSync2(join7(targetRepo, rel));
6261
6326
  } catch {
6262
6327
  return "failed";
6263
6328
  }
@@ -6278,7 +6343,7 @@ var MCP_HEADERS_HELPER_COMMAND = `node -e "${nodeFloorProgram([
6278
6343
  `const u=p.dirname(d);`,
6279
6344
  `if(u===d)break;`,
6280
6345
  `d=u}`,
6281
- `console.error('halfcycle: no ${VENDORED_BIN_REL} at or above '+process.cwd()+'; run npx halfcycle in the project folder');`,
6346
+ `console.error('halfcycle: no ${VENDORED_BIN_REL} at or above '+process.cwd()+'; run ${ownBareCommand()} in the project folder');`,
6282
6347
  `process.exit(1)`
6283
6348
  ].join(""))}"`;
6284
6349
  var CODEX_ADDRESS_PATTERN = /^https?:\/\/[A-Za-z0-9.-]+(:[0-9]{1,5})?(\/[A-Za-z0-9._-]+)*\/mcp$/;
@@ -6302,7 +6367,7 @@ function codexMcpHeadersHelperCommand(address) {
6302
6367
  `if(u===d)break;`,
6303
6368
  `d=u}`,
6304
6369
  `if(b===''){`,
6305
- `console.error('halfcycle: no ${VENDORED_BIN_REL} at or above '+process.cwd()+'; run npx halfcycle in the project folder');`,
6370
+ `console.error('halfcycle: no ${VENDORED_BIN_REL} at or above '+process.cwd()+'; run ${ownBareCommand()} in the project folder');`,
6306
6371
  `n()}else{`,
6307
6372
  `const r=c.spawnSync(process.execPath,[b,'mcp-headers','--url','${address}'],{stdio:['ignore','pipe','inherit']});`,
6308
6373
  `if(r.status===0&&r.stdout&&r.stdout.length){o=1;process.stdout.write(r.stdout)}else{n()}}`,
@@ -6628,9 +6693,9 @@ function gitignoreCovers(content) {
6628
6693
  return REQUIRED_GITIGNORE_ENTRIES.every((entry) => lines.has(entry));
6629
6694
  }
6630
6695
  function planGitignore(targetRepo) {
6631
- const gitignorePath = join6(targetRepo, GITIGNORE_REL);
6696
+ const gitignorePath = join7(targetRepo, GITIGNORE_REL);
6632
6697
  const header = GITIGNORE_HEADER;
6633
- const before = existsSync4(gitignorePath) ? readFileSync5(gitignorePath, "utf-8") : null;
6698
+ const before = existsSync5(gitignorePath) ? readFileSync6(gitignorePath, "utf-8") : null;
6634
6699
  if (before === null) {
6635
6700
  return { before, after: [header, ...REQUIRED_GITIGNORE_ENTRIES].join("\n") + "\n", outcome: "written" };
6636
6701
  }
@@ -6646,7 +6711,7 @@ function putPlannedGitignore(targetRepo, planned) {
6646
6711
  if (planned.outcome !== "written" || planned.after === null)
6647
6712
  return planned.outcome;
6648
6713
  try {
6649
- writeFileSync4(join6(targetRepo, GITIGNORE_REL), planned.after, "utf-8");
6714
+ writeFileSync4(join7(targetRepo, GITIGNORE_REL), planned.after, "utf-8");
6650
6715
  return "written";
6651
6716
  } catch {
6652
6717
  return "failed";
@@ -6655,7 +6720,7 @@ function putPlannedGitignore(targetRepo, planned) {
6655
6720
  var GITATTRIBUTES_HEADER = "# Halfcycle \u2014 generated scripts keep LF line endings";
6656
6721
  function readTextOrNull(path) {
6657
6722
  try {
6658
- return readFileSync5(path, "utf-8");
6723
+ return readFileSync6(path, "utf-8");
6659
6724
  } catch {
6660
6725
  return null;
6661
6726
  }
@@ -6665,12 +6730,12 @@ function lfRules(paths) {
6665
6730
  }
6666
6731
  var REQUIRED_GITATTRIBUTES_RULES = lfRules(Object.keys(OWNED_GENERATED_HEADERS));
6667
6732
  function planGitattributes(targetRepo) {
6668
- const path = join6(targetRepo, GITATTRIBUTES_REL);
6733
+ const path = join7(targetRepo, GITATTRIBUTES_REL);
6669
6734
  const rules = REQUIRED_GITATTRIBUTES_RULES;
6670
6735
  const before = readTextOrNull(path);
6671
6736
  if (lstatOrNull(path)?.isSymbolicLink() === true)
6672
6737
  return { before, after: before, outcome: "failed" };
6673
- if (!existsSync4(path)) {
6738
+ if (!existsSync5(path)) {
6674
6739
  return { before, after: [GITATTRIBUTES_HEADER, ...rules].join("\n") + "\n", outcome: "written" };
6675
6740
  }
6676
6741
  if (before === null)
@@ -6688,7 +6753,7 @@ function putPlannedGitattributes(targetRepo, planned) {
6688
6753
  if (planned.outcome !== "written" || planned.after === null)
6689
6754
  return planned.outcome;
6690
6755
  try {
6691
- writeAllowlisted(join6(targetRepo, GITATTRIBUTES_REL), targetRepo, planned.after, []);
6756
+ writeAllowlisted(join7(targetRepo, GITATTRIBUTES_REL), targetRepo, planned.after, []);
6692
6757
  return "written";
6693
6758
  } catch {
6694
6759
  return "failed";
@@ -6722,7 +6787,7 @@ function planBundlePin(targetRepoRoot, version, engagementId, engagementType, cr
6722
6787
  ...carriedMethodVersion !== void 0 ? { methodVersion: carriedMethodVersion } : {},
6723
6788
  ...projectPage !== void 0 ? { projectPage } : {}
6724
6789
  };
6725
- const current = readFileOrNull(join6(targetRepoRoot, BUNDLE_PIN_REL));
6790
+ const current = readFileOrNull(join7(targetRepoRoot, BUNDLE_PIN_REL));
6726
6791
  const text = inLineEndingOf(current, JSON.stringify(pin, null, 2) + "\n");
6727
6792
  return {
6728
6793
  rel: BUNDLE_PIN_REL,
@@ -6753,7 +6818,7 @@ function planBundlePinPreview(targetRepoRoot, version, home) {
6753
6818
  return planBundlePin(targetRepoRoot, version, existing.engagementId, existing.engagementType, void 0, void 0);
6754
6819
  }
6755
6820
  function writeBundlePin(targetRepoRoot, planned, writtenPaths) {
6756
- const pinPath = join6(targetRepoRoot, BUNDLE_PIN_REL);
6821
+ const pinPath = join7(targetRepoRoot, BUNDLE_PIN_REL);
6757
6822
  writeAllowlisted(pinPath, targetRepoRoot, planned.text, writtenPaths);
6758
6823
  }
6759
6824
  function renderCrewRoster() {
@@ -6768,10 +6833,10 @@ function pinCreatedByAccountId(pin) {
6768
6833
  return nonEmpty(pin?.createdByAccountId) ?? nonEmpty(pin?.accountId);
6769
6834
  }
6770
6835
  function readBundlePin(targetRepoRoot) {
6771
- const pinPath = join6(targetRepoRoot, BUNDLE_PIN_REL);
6772
- if (!existsSync4(pinPath))
6836
+ const pinPath = join7(targetRepoRoot, BUNDLE_PIN_REL);
6837
+ if (!existsSync5(pinPath))
6773
6838
  return null;
6774
- return JSON.parse(readFileSync5(pinPath, "utf-8"));
6839
+ return JSON.parse(readFileSync6(pinPath, "utf-8"));
6775
6840
  }
6776
6841
  function readPinnedEngagement(targetRepoRoot, home) {
6777
6842
  const pin = readBundlePin(targetRepoRoot);
@@ -6794,7 +6859,7 @@ function readPinnedEngagement(targetRepoRoot, home) {
6794
6859
  }
6795
6860
  function readLegacyEnvLocal(targetRepoRoot) {
6796
6861
  try {
6797
- return parseEnvText(readFileSync5(join6(targetRepoRoot, LEGACY_ENV_LOCAL_REL), "utf-8"));
6862
+ return parseEnvText(readFileSync6(join7(targetRepoRoot, LEGACY_ENV_LOCAL_REL), "utf-8"));
6798
6863
  } catch {
6799
6864
  return null;
6800
6865
  }
@@ -6843,7 +6908,7 @@ function writeEngagementCredential(credential, engagementId, home) {
6843
6908
  function planEngagementCredential(credential, engagementId, home) {
6844
6909
  let existing;
6845
6910
  try {
6846
- existing = readFileSync5(engagementEnvPath(engagementId, home), "utf-8");
6911
+ existing = readFileSync6(engagementEnvPath(engagementId, home), "utf-8");
6847
6912
  } catch {
6848
6913
  existing = null;
6849
6914
  }
@@ -6886,12 +6951,12 @@ function engagementCredentialValues(credential, engagementId) {
6886
6951
  }
6887
6952
  var LEGACY_ENV_LOCAL_REL = ".env.local";
6888
6953
  function migrateLegacyEnvLocal(targetRepo, stored) {
6889
- const path = join6(targetRepo, LEGACY_ENV_LOCAL_REL);
6890
- if (!existsSync4(path))
6954
+ const path = join7(targetRepo, LEGACY_ENV_LOCAL_REL);
6955
+ if (!existsSync5(path))
6891
6956
  return { outcome: "absent", unbacked: [], missingForAdoption: [] };
6892
6957
  let raw;
6893
6958
  try {
6894
- raw = readFileSync5(path, "utf-8");
6959
+ raw = readFileSync6(path, "utf-8");
6895
6960
  } catch {
6896
6961
  return { outcome: "failed", unbacked: [], missingForAdoption: [] };
6897
6962
  }
@@ -6917,7 +6982,7 @@ function migrateLegacyEnvLocal(targetRepo, stored) {
6917
6982
  return { outcome: "stripped", unbacked: [], missingForAdoption: [], keptText: kept.join("\n") };
6918
6983
  }
6919
6984
  function applyLegacyMigration(targetRepo, decided) {
6920
- const path = join6(targetRepo, LEGACY_ENV_LOCAL_REL);
6985
+ const path = join7(targetRepo, LEGACY_ENV_LOCAL_REL);
6921
6986
  try {
6922
6987
  if (decided.outcome === "removed") {
6923
6988
  rmSync2(path);
@@ -6943,23 +7008,23 @@ function planCodexSkills(targetRepo, previous, writeSet) {
6943
7008
  const manifest = readPluginManifest();
6944
7009
  const recordedFiles = new Map((previous?.files ?? []).filter((file) => writeSet.files.has(file.path)).map((file) => [file.path, file]));
6945
7010
  return (manifest.commands ?? []).map((cmd) => {
6946
- const srcFile = join6(BUNDLE_ROOT, ".claude-plugin", cmd.path);
6947
- if (!existsSync4(srcFile)) {
7011
+ const srcFile = join7(BUNDLE_ROOT, ".claude-plugin", cmd.path);
7012
+ if (!existsSync5(srcFile)) {
6948
7013
  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.`);
6949
7014
  }
6950
7015
  const rel = skillRel(cmd.name);
6951
- const content = codexSkillContent(cmd.name, readFileSync5(srcFile, "utf-8"));
7016
+ const content = codexSkillContent(cmd.name, commandFileText(readFileSync6(srcFile, "utf-8"), ownVersion()));
6952
7017
  const recorded = recordedFiles.get(rel);
6953
- const planned = decideCollisionSafe(join6(targetRepo, rel), targetRepo, content, (current) => bytesAreOurs(rel, current, recorded, writeSet));
7018
+ const planned = decideCollisionSafe(join7(targetRepo, rel), targetRepo, content, (current) => bytesAreOurs(rel, current, recorded, writeSet));
6954
7019
  return { ...planned, editedCollided: planned.outcome === "collided" && recorded !== void 0 };
6955
7020
  });
6956
7021
  }
6957
7022
  function planVendoredBinary(targetRepo) {
6958
- return decideOwned(join6(targetRepo, ".halfcycle", "bin", "bin.bundle.mjs"), targetRepo, readFileSync5(resolveVendoredBinary(), "utf-8"));
7023
+ return decideOwned(join7(targetRepo, ".halfcycle", "bin", "bin.bundle.mjs"), targetRepo, readFileSync6(resolveVendoredBinary(), "utf-8"));
6959
7024
  }
6960
7025
  function planSettings(targetRepo, previous, home, env) {
6961
- const settingsPath = join6(targetRepo, SETTINGS_REL);
6962
- const preexisted = existsSync4(settingsPath);
7026
+ const settingsPath = join7(targetRepo, SETTINGS_REL);
7027
+ const preexisted = existsSync5(settingsPath);
6963
7028
  const generated = JSON.parse(generateSettingsJson());
6964
7029
  let existing;
6965
7030
  try {
@@ -6983,11 +7048,11 @@ function planSettings(targetRepo, previous, home, env) {
6983
7048
  return { file, preexisted, existing, generated, merged, retiredOurs };
6984
7049
  }
6985
7050
  function planHookScripts(targetRepo) {
6986
- return hookScriptsThisVersionWrites().map(([rel, content]) => decideOwnedGenerated(join6(targetRepo, rel), targetRepo, content, OWNED_GENERATED_HEADERS[rel]));
7051
+ return hookScriptsThisVersionWrites().map(([rel, content]) => decideOwnedGenerated(join7(targetRepo, rel), targetRepo, content, OWNED_GENERATED_HEADERS[rel]));
6987
7052
  }
6988
7053
  function planMcpRegistration(targetRepo, credential) {
6989
- const mcpPath = join6(targetRepo, MCP_REGISTRATION_REL);
6990
- const existing = existsSync4(mcpPath) ? readFileSync5(mcpPath, "utf-8") : null;
7054
+ const mcpPath = join7(targetRepo, MCP_REGISTRATION_REL);
7055
+ const existing = existsSync5(mcpPath) ? readFileSync6(mcpPath, "utf-8") : null;
6991
7056
  if (credential === void 0)
6992
7057
  return { kind: "none", existing };
6993
7058
  if (credential === "pending")
@@ -6999,11 +7064,11 @@ function planMcpRegistration(targetRepo, credential) {
6999
7064
  function putPlannedMcp(targetRepo, planned) {
7000
7065
  if (planned.kind !== "write" || planned.outcome === "skipped")
7001
7066
  return;
7002
- writeAllowlisted(join6(targetRepo, MCP_REGISTRATION_REL), targetRepo, planned.text, []);
7067
+ writeAllowlisted(join7(targetRepo, MCP_REGISTRATION_REL), targetRepo, planned.text, []);
7003
7068
  }
7004
7069
  function fileInFolderPlace(root, rel) {
7005
7070
  for (let dir = posix2.dirname(rel); dir !== "."; dir = posix2.dirname(dir)) {
7006
- const found = lstatOrNull(join6(root, dir));
7071
+ const found = lstatOrNull(join7(root, dir));
7007
7072
  if (found !== null && !found.isDirectory())
7008
7073
  return true;
7009
7074
  }
@@ -7012,7 +7077,7 @@ function fileInFolderPlace(root, rel) {
7012
7077
  function symlinkOnPath(root, rel) {
7013
7078
  let current = root;
7014
7079
  for (const segment of rel.split("/")) {
7015
- current = join6(current, segment);
7080
+ current = join7(current, segment);
7016
7081
  try {
7017
7082
  if (lstatSync(current).isSymbolicLink())
7018
7083
  return true;
@@ -7033,14 +7098,14 @@ function planCodexHooks(targetRepo) {
7033
7098
  });
7034
7099
  if (symlinkOnPath(targetRepo, CODEX_HOOKS_REL2))
7035
7100
  return leave("link", null);
7036
- const folder = lstatOrNull(join6(targetRepo, posix2.dirname(CODEX_HOOKS_REL2)));
7101
+ const folder = lstatOrNull(join7(targetRepo, posix2.dirname(CODEX_HOOKS_REL2)));
7037
7102
  if (folder !== null && !folder.isDirectory())
7038
7103
  return leave("unreadable", null);
7039
- const path = join6(targetRepo, CODEX_HOOKS_REL2);
7104
+ const path = join7(targetRepo, CODEX_HOOKS_REL2);
7040
7105
  let before = null;
7041
7106
  if (lstatOrNull(path) !== null) {
7042
7107
  try {
7043
- before = readFileSync5(path, "utf-8");
7108
+ before = readFileSync6(path, "utf-8");
7044
7109
  } catch {
7045
7110
  return leave("unreadable", null);
7046
7111
  }
@@ -7074,13 +7139,13 @@ function planCodexHooks(targetRepo) {
7074
7139
  function putPlannedCodexHooks(targetRepo, planned) {
7075
7140
  if (planned.outcome !== "written" || planned.text === null)
7076
7141
  return;
7077
- writeAllowlisted(join6(targetRepo, CODEX_HOOKS_REL2), targetRepo, planned.text, []);
7142
+ writeAllowlisted(join7(targetRepo, CODEX_HOOKS_REL2), targetRepo, planned.text, []);
7078
7143
  }
7079
7144
  function readCodexConfig(targetRepo) {
7080
- const folder = lstatOrNull(join6(targetRepo, posix2.dirname(CODEX_CONFIG_REL2)));
7145
+ const folder = lstatOrNull(join7(targetRepo, posix2.dirname(CODEX_CONFIG_REL2)));
7081
7146
  if (folder !== null && !folder.isDirectory())
7082
7147
  return "unreadable";
7083
- const path = join6(targetRepo, CODEX_CONFIG_REL2);
7148
+ const path = join7(targetRepo, CODEX_CONFIG_REL2);
7084
7149
  const stat = lstatOrNull(path);
7085
7150
  if (stat === null)
7086
7151
  return { text: null };
@@ -7088,7 +7153,7 @@ function readCodexConfig(targetRepo) {
7088
7153
  return "unreadable";
7089
7154
  let bytes;
7090
7155
  try {
7091
- bytes = readFileSync5(path);
7156
+ bytes = readFileSync6(path);
7092
7157
  } catch {
7093
7158
  return "unreadable";
7094
7159
  }
@@ -7134,10 +7199,10 @@ function planCodexConfig(targetRepo, credential) {
7134
7199
  function putPlannedCodexConfig(targetRepo, planned) {
7135
7200
  if (planned.outcome !== "written" || planned.text === null)
7136
7201
  return;
7137
- writeAllowlisted(join6(targetRepo, CODEX_CONFIG_REL2), targetRepo, planned.text, []);
7202
+ writeAllowlisted(join7(targetRepo, CODEX_CONFIG_REL2), targetRepo, planned.text, []);
7138
7203
  }
7139
7204
  function planCrewRoster(targetRepo) {
7140
- return decideOwned(join6(targetRepo, CREW_ROSTER_REL), targetRepo, renderCrewRoster());
7205
+ return decideOwned(join7(targetRepo, CREW_ROSTER_REL), targetRepo, renderCrewRoster());
7141
7206
  }
7142
7207
  function planCrewProfiles(targetRepo, previous, writeSet) {
7143
7208
  const recordedFiles = new Map((previous?.files ?? []).filter((file) => writeSet.files.has(file.path)).map((file) => [file.path, file]));
@@ -7152,7 +7217,7 @@ function planCrewProfiles(targetRepo, previous, writeSet) {
7152
7217
  });
7153
7218
  }
7154
7219
  function planInstallRecord(targetRepo, text) {
7155
- return decideOwned(join6(targetRepo, INSTALL_MANIFEST_REL), targetRepo, text);
7220
+ return decideOwned(join7(targetRepo, INSTALL_MANIFEST_REL), targetRepo, text);
7156
7221
  }
7157
7222
  function accountInstall(steps, actual) {
7158
7223
  const { targetRepo, writeSet, pin } = steps;
@@ -7526,8 +7591,8 @@ async function requestEngagementValues(method, url, requestBody, credential, sha
7526
7591
  }
7527
7592
 
7528
7593
  // dist/setup/manifest.js
7529
- import { existsSync as existsSync5, lstatSync as lstatSync2, mkdirSync as mkdirSync5, readFileSync as readFileSync6, statSync, writeFileSync as writeFileSync5 } from "node:fs";
7530
- import { dirname as dirname4, join as join7, posix as posix3 } from "node:path";
7594
+ import { existsSync as existsSync6, lstatSync as lstatSync2, mkdirSync as mkdirSync5, readFileSync as readFileSync7, statSync, writeFileSync as writeFileSync5 } from "node:fs";
7595
+ import { dirname as dirname5, join as join8, posix as posix3 } from "node:path";
7531
7596
  var DECLARED_ROOT_FILES = ["CLAUDE.md", "AGENTS.md", "ENGAGEMENT.md"];
7532
7597
  var DECLARED_DIR_PREFIX = "docs";
7533
7598
  var RESERVED_METHOD_PATH = "docs/method";
@@ -7612,7 +7677,7 @@ function firstSymlinkOnPath(targetRepo, path) {
7612
7677
  const segments = path.split("/").filter((s) => s.length > 0);
7613
7678
  let walked = targetRepo;
7614
7679
  for (let i = 0; i < segments.length; i++) {
7615
- walked = join7(walked, segments[i]);
7680
+ walked = join8(walked, segments[i]);
7616
7681
  try {
7617
7682
  if (lstatSync2(walked).isSymbolicLink())
7618
7683
  return segments.slice(0, i + 1).join("/");
@@ -7630,30 +7695,30 @@ function symlinkEntry(path, link, isDirectoryRow) {
7630
7695
  };
7631
7696
  }
7632
7697
  function applyFileRow(targetRepo, path, content) {
7633
- const abs = join7(targetRepo, path);
7698
+ const abs = join8(targetRepo, path);
7634
7699
  const link = firstSymlinkOnPath(targetRepo, path);
7635
7700
  if (link !== null)
7636
7701
  return symlinkEntry(path, link, false);
7637
- if (existsSync5(abs)) {
7638
- const current = readFileSync6(abs, "utf-8");
7702
+ if (existsSync6(abs)) {
7703
+ const current = readFileSync7(abs, "utf-8");
7639
7704
  return current === content ? { path, outcome: "already-present", note: "already present and correct \u2014 nothing written" } : { path, outcome: "left-alone", note: "you already own this file \u2014 left exactly as it was" };
7640
7705
  }
7641
- mkdirSync5(dirname4(abs), { recursive: true });
7706
+ mkdirSync5(dirname5(abs), { recursive: true });
7642
7707
  writeFileSync5(abs, content, "utf-8");
7643
7708
  return { path, outcome: "created", note: "created" };
7644
7709
  }
7645
7710
  function applySectionRow(targetRepo, path, heading, body) {
7646
- const abs = join7(targetRepo, path);
7711
+ const abs = join8(targetRepo, path);
7647
7712
  const link = firstSymlinkOnPath(targetRepo, path);
7648
7713
  if (link !== null)
7649
7714
  return symlinkEntry(path, link, false);
7650
7715
  const section = renderSection(heading, body);
7651
- if (!existsSync5(abs)) {
7652
- mkdirSync5(dirname4(abs), { recursive: true });
7716
+ if (!existsSync6(abs)) {
7717
+ mkdirSync5(dirname5(abs), { recursive: true });
7653
7718
  writeFileSync5(abs, section, "utf-8");
7654
7719
  return { path, outcome: "created", note: `created, with a "${heading}" section` };
7655
7720
  }
7656
- const existing = readFileSync6(abs, "utf-8");
7721
+ const existing = readFileSync7(abs, "utf-8");
7657
7722
  if (hasSection(existing, heading)) {
7658
7723
  return {
7659
7724
  path,
@@ -7670,11 +7735,11 @@ function applySectionRow(targetRepo, path, heading, body) {
7670
7735
  };
7671
7736
  }
7672
7737
  function applyDirectoryRow(targetRepo, path) {
7673
- const abs = join7(targetRepo, path);
7738
+ const abs = join8(targetRepo, path);
7674
7739
  const link = firstSymlinkOnPath(targetRepo, path);
7675
7740
  if (link !== null)
7676
7741
  return symlinkEntry(path, link, true);
7677
- if (existsSync5(abs)) {
7742
+ if (existsSync6(abs)) {
7678
7743
  const kind = statSync(abs).isDirectory() ? "directory" : "file";
7679
7744
  return kind === "directory" ? { path: `${path}/`, outcome: "already-present", note: "already there \u2014 nothing written" } : { path, outcome: "left-alone", note: "you already own this path \u2014 left exactly as it was" };
7680
7745
  }
@@ -7682,7 +7747,7 @@ function applyDirectoryRow(targetRepo, path) {
7682
7747
  return { path: `${path}/`, outcome: "created", note: "created" };
7683
7748
  }
7684
7749
  function applySetupWrites(targetRepo, rows) {
7685
- if (!existsSync5(targetRepo)) {
7750
+ if (!existsSync6(targetRepo)) {
7686
7751
  throw new Error(`[halfcycle setup] The project directory does not exist: ${targetRepo}`);
7687
7752
  }
7688
7753
  assertNoConflictingDuplicates(rows);
@@ -7793,8 +7858,8 @@ function trackerHomeRow(home = FLAT_FILE_TRACKER_HOME) {
7793
7858
  }
7794
7859
 
7795
7860
  // dist/setup/discovery.js
7796
- import { existsSync as existsSync6, readFileSync as readFileSync7, readdirSync, statSync as statSync2 } from "node:fs";
7797
- import { join as join8 } from "node:path";
7861
+ import { existsSync as existsSync7, readFileSync as readFileSync8, readdirSync, statSync as statSync2 } from "node:fs";
7862
+ import { join as join9 } from "node:path";
7798
7863
  function renderAnchor(anchor) {
7799
7864
  return anchor.kind === "file" ? `${anchor.path}:${anchor.line}` : `$ ${anchor.command}`;
7800
7865
  }
@@ -7818,8 +7883,8 @@ function lineOf(text, needle) {
7818
7883
  return null;
7819
7884
  }
7820
7885
  function readOrNull(repo, rel) {
7821
- const abs = join8(repo, rel);
7822
- return existsSync6(abs) && statSync2(abs).isFile() ? readFileSync7(abs, "utf-8") : null;
7886
+ const abs = join9(repo, rel);
7887
+ return existsSync7(abs) && statSync2(abs).isFile() ? readFileSync8(abs, "utf-8") : null;
7823
7888
  }
7824
7889
  function anchoredIn(repo, rel, needle, value) {
7825
7890
  const text = readOrNull(repo, rel);
@@ -7831,8 +7896,8 @@ function anchoredIn(repo, rel, needle, value) {
7831
7896
  return { value, anchor: { kind: "file", path: rel, line } };
7832
7897
  }
7833
7898
  function presenceAnchor(repo, rel) {
7834
- const abs = join8(repo, rel);
7835
- if (!existsSync6(abs))
7899
+ const abs = join9(repo, rel);
7900
+ if (!existsSync7(abs))
7836
7901
  return null;
7837
7902
  return statSync2(abs).isDirectory() ? { kind: "command", command: `ls -d ${rel}`, output: `${rel}/` } : { kind: "file", path: rel, line: 1 };
7838
7903
  }
@@ -7873,8 +7938,8 @@ function firstMatch(items, f) {
7873
7938
  }
7874
7939
  var CONTEXT_INDEX_FILES = ["AGENTS.md", "CLAUDE.md"];
7875
7940
  function ciWorkflows(repo) {
7876
- const dir = join8(repo, ".github", "workflows");
7877
- if (!existsSync6(dir) || !statSync2(dir).isDirectory())
7941
+ const dir = join9(repo, ".github", "workflows");
7942
+ if (!existsSync7(dir) || !statSync2(dir).isDirectory())
7878
7943
  return [];
7879
7944
  return readdirSync(dir).filter((f) => f.endsWith(".yml") || f.endsWith(".yaml")).sort();
7880
7945
  }
@@ -7882,21 +7947,21 @@ function existingLayerDocuments(repo) {
7882
7947
  const found = [];
7883
7948
  for (const paths of Object.values(LAYER_ARTEFACT_PATHS)) {
7884
7949
  for (const p of paths ?? []) {
7885
- if (existsSync6(join8(repo, p)))
7950
+ if (existsSync7(join9(repo, p)))
7886
7951
  found.push(p);
7887
7952
  }
7888
7953
  }
7889
7954
  return found.sort();
7890
7955
  }
7891
7956
  function discoverRepository(targetRepo) {
7892
- if (!existsSync6(targetRepo)) {
7957
+ if (!existsSync7(targetRepo)) {
7893
7958
  throw new Error(`[halfcycle setup] The project directory does not exist: ${targetRepo}`);
7894
7959
  }
7895
7960
  const findings = [];
7896
7961
  const questions = [];
7897
7962
  settleOrAsk("project-language", firstMatch(MANIFESTS, ([file, needle, language]) => anchoredIn(targetRepo, file, needle, language)), "What is this project written in?", findings, questions);
7898
7963
  settleOrAsk("package-manager", firstMatch(LOCKFILES, ([file, manager]) => anchoredByPresence(targetRepo, file, manager)), "Which package manager does this project use?", findings, questions);
7899
- settleOrAsk("repo-structure", firstMatch(WORKSPACE_MARKERS, ([file, needle, shape]) => anchoredIn(targetRepo, file, needle, shape)) ?? (existsSync6(join8(targetRepo, "package.json")) || existsSync6(join8(targetRepo, "go.mod")) || existsSync6(join8(targetRepo, "pyproject.toml")) ? anchoredByPresence(targetRepo, "package.json", "a single-package repository") ?? anchoredByPresence(targetRepo, "go.mod", "a single-package repository") ?? anchoredByPresence(targetRepo, "pyproject.toml", "a single-package repository") : null), "Is this one repository for one product, or one of several?", findings, questions);
7964
+ settleOrAsk("repo-structure", firstMatch(WORKSPACE_MARKERS, ([file, needle, shape]) => anchoredIn(targetRepo, file, needle, shape)) ?? (existsSync7(join9(targetRepo, "package.json")) || existsSync7(join9(targetRepo, "go.mod")) || existsSync7(join9(targetRepo, "pyproject.toml")) ? anchoredByPresence(targetRepo, "package.json", "a single-package repository") ?? anchoredByPresence(targetRepo, "go.mod", "a single-package repository") ?? anchoredByPresence(targetRepo, "pyproject.toml", "a single-package repository") : null), "Is this one repository for one product, or one of several?", findings, questions);
7900
7965
  const workflows = ciWorkflows(targetRepo);
7901
7966
  settleOrAsk("ci-strategy", workflows.length > 0 ? {
7902
7967
  value: `GitHub Actions \u2014 ${workflows.length === 1 ? "one workflow" : `${workflows.length} workflows`}: ${workflows.join(", ")}`,
@@ -8566,11 +8631,11 @@ function renderBuildRecordV2Markdown(record2) {
8566
8631
  }
8567
8632
 
8568
8633
  // dist/build-record/write.js
8569
- import { existsSync as existsSync7, mkdirSync as mkdirSync6, rmSync as rmSync4 } from "node:fs";
8570
- import { basename, join as join9, resolve as resolve2, relative as relative2, isAbsolute as isAbsolute3 } from "node:path";
8634
+ import { existsSync as existsSync8, mkdirSync as mkdirSync6, rmSync as rmSync4 } from "node:fs";
8635
+ import { basename, join as join10, resolve as resolve2, relative as relative2, isAbsolute as isAbsolute3 } from "node:path";
8571
8636
 
8572
8637
  // dist/build-record/pair-io.js
8573
- import { closeSync, openSync, readFileSync as readFileSync8, renameSync as renameSync2, rmSync as rmSync3, writeSync } from "node:fs";
8638
+ import { closeSync, openSync, readFileSync as readFileSync9, renameSync as renameSync2, rmSync as rmSync3, writeSync } from "node:fs";
8574
8639
  function createExclusiveOnDisk(path, data) {
8575
8640
  const bytes = typeof data === "string" ? Buffer.from(data, "utf-8") : data;
8576
8641
  const fd = openSync(path, "wx");
@@ -8601,7 +8666,7 @@ function isEexist(err) {
8601
8666
  }
8602
8667
  function readIfPresent(path) {
8603
8668
  try {
8604
- return readFileSync8(path, "utf-8");
8669
+ return readFileSync9(path, "utf-8");
8605
8670
  } catch {
8606
8671
  return null;
8607
8672
  }
@@ -8633,7 +8698,7 @@ function replaceOne(io, tmp, finalPath, text) {
8633
8698
  }
8634
8699
 
8635
8700
  // dist/build-record/write.js
8636
- var BUILD_RECORD_DIR = join9("docs", "build-records");
8701
+ var BUILD_RECORD_DIR = join10("docs", "build-records");
8637
8702
  function assertInsideOutDir(outDir, candidate, identity) {
8638
8703
  const rel = relative2(outDir, resolve2(candidate));
8639
8704
  const isPlainFilename = rel !== "" && rel !== ".." && !isAbsolute3(rel) && rel === basename(rel);
@@ -8671,8 +8736,8 @@ function writeRecordPair(repoRoot, phase, rawRecord, record2, io = NODE_RECORD_P
8671
8736
  }
8672
8737
  const outDir = resolve2(repoRoot, BUILD_RECORD_DIR);
8673
8738
  const segment = renderPhaseSegment(phase);
8674
- const jsonPath = join9(outDir, `${segment}.json`);
8675
- const mdPath = join9(outDir, `${segment}.md`);
8739
+ const jsonPath = join10(outDir, `${segment}.json`);
8740
+ const mdPath = join10(outDir, `${segment}.md`);
8676
8741
  const jsonTmp = `${jsonPath}.tmp`;
8677
8742
  const mdTmp = `${mdPath}.tmp`;
8678
8743
  for (const candidate of [jsonPath, mdPath, jsonTmp, mdTmp]) {
@@ -8686,7 +8751,7 @@ function writeRecordPair(repoRoot, phase, rawRecord, record2, io = NODE_RECORD_P
8686
8751
  }
8687
8752
  const existingJson = readIfPresent(jsonPath);
8688
8753
  if (existingJson === null) {
8689
- return { kind: "in-the-way", path: existsSync7(jsonPath) ? jsonPath : mdPath };
8754
+ return { kind: "in-the-way", path: existsSync8(jsonPath) ? jsonPath : mdPath };
8690
8755
  }
8691
8756
  if (existingJson === jsonText) {
8692
8757
  replaceOne(io, mdTmp, mdPath, mdText);
@@ -8748,20 +8813,20 @@ function isMissingRoute(method, status, body) {
8748
8813
  }
8749
8814
 
8750
8815
  // dist/build-record/write-change.js
8751
- import { basename as basename2, isAbsolute as isAbsolute4, join as join10, relative as relative3, resolve as resolve3 } from "node:path";
8752
- var CHANGE_RECORD_DIR = join10("docs", "build-records", "changes");
8816
+ import { basename as basename2, isAbsolute as isAbsolute4, join as join11, relative as relative3, resolve as resolve3 } from "node:path";
8817
+ var CHANGE_RECORD_DIR = join11("docs", "build-records", "changes");
8753
8818
 
8754
8819
  // dist/change.js
8755
- 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>]
8756
- ${HALFCYCLE_COMMAND} change up <mini|full> (--change <id> | --phase <phase>) [--repo <root>]
8757
- ${HALFCYCLE_COMMAND} change done <id> [--repo <root>]`;
8820
+ var CHANGE_USAGE = `${ownCommand()} change sort --request "<one line>" --touches <path>\u2026 [--contract <from>:<to>]\u2026 [--stored-data] [--standing-rule] [--new-part] [--repo <root>]
8821
+ ${ownCommand()} change up <mini|full> (--change <id> | --phase <phase>) [--repo <root>]
8822
+ ${ownCommand()} change done <id> [--repo <root>]`;
8758
8823
 
8759
8824
  // dist/plan.js
8760
- var PLAN_USAGE = `${HALFCYCLE_COMMAND} plan <phase> [--show] [--repo <root>]`;
8825
+ var PLAN_USAGE = `${ownCommand()} plan <phase> [--show] [--repo <root>]`;
8761
8826
 
8762
8827
  // dist/review.js
8763
- var REVIEW_USAGE = `${HALFCYCLE_COMMAND} review <phase> (--show | --answer <part> "<answer>") [--repo <root>]`;
8764
- var RV9_LINE = `halfcycle review: this project is not signed in on this computer. ${HOOK_REMEDY}`;
8828
+ var REVIEW_USAGE = `${ownCommand()} review <phase> (--show | --answer <part> "<answer>") [--repo <root>]`;
8829
+ var RV9_LINE = `halfcycle review: this project is not signed in on this computer. ${hookRemedy(ownVersion())}`;
8765
8830
 
8766
8831
  // dist/open-phase.js
8767
8832
  var OPEN_FROM_SIZE_REFUSALS = Object.freeze({
@@ -8877,16 +8942,16 @@ async function obtainBuildRecord(credential, repoRoot, phase, io = NODE_RECORD_P
8877
8942
  };
8878
8943
  }
8879
8944
  function refetchCommand(phase) {
8880
- return `${HALFCYCLE_COMMAND} build-record ${quoteForShell(phase)}`;
8945
+ return `${ownCommand()} build-record ${quoteForShell(phase)}`;
8881
8946
  }
8882
8947
 
8883
8948
  // dist/check-tasks.js
8884
- var CHECK_TASKS_USAGE = `${HALFCYCLE_COMMAND} check-tasks <phase> [--with-task-list] [--repo <root>]`;
8949
+ var CHECK_TASKS_USAGE = `${ownCommand()} check-tasks <phase> [--with-task-list] [--repo <root>]`;
8885
8950
  var MISSING_PLAN_LINE = `there is no ${TASK_PLAN_FILE} in this project yet. Run the task-graph step to write it, then run this again.`;
8886
8951
  var SPEC_FOLDER = LAYER_ARTEFACT_PATHS.l4[0];
8887
8952
 
8888
8953
  // dist/ready.js
8889
- var READY_USAGE = `${HALFCYCLE_COMMAND} ready <phase> [--cap <n>] [--repo <root>]`;
8954
+ var READY_USAGE = `${ownCommand()} ready <phase> [--cap <n>] [--repo <root>]`;
8890
8955
  var STATES_TEXT = buildTaskStateSchema.options.join(", ");
8891
8956
  var READY_LINES = {
8892
8957
  start: (id, title) => `Ready to start: ${named(id, title)}`,