halfcycle 0.11.0 → 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/build-record/obtain.d.ts.map +1 -1
  7. package/dist/change.d.ts.map +1 -1
  8. package/dist/check-criteria.d.ts +1 -1
  9. package/dist/check-criteria.d.ts.map +1 -1
  10. package/dist/check-report.d.ts +1 -1
  11. package/dist/check-report.d.ts.map +1 -1
  12. package/dist/check-tasks.d.ts +10 -1
  13. package/dist/check-tasks.d.ts.map +1 -1
  14. package/dist/cli-contract.d.ts +1 -1
  15. package/dist/cli-contract.d.ts.map +1 -1
  16. package/dist/cli.js +1018 -731
  17. package/dist/cli.js.map +3 -3
  18. package/dist/crew.d.ts +37 -0
  19. package/dist/crew.d.ts.map +1 -0
  20. package/dist/draw/index.d.ts +1 -1
  21. package/dist/draw/index.d.ts.map +1 -1
  22. package/dist/engagement-credential.d.ts +4 -0
  23. package/dist/engagement-credential.d.ts.map +1 -1
  24. package/dist/failure-line.d.ts +3 -2
  25. package/dist/failure-line.d.ts.map +1 -1
  26. package/dist/folder-refusals.d.ts.map +1 -1
  27. package/dist/index.js +271 -206
  28. package/dist/index.js.map +3 -3
  29. package/dist/install.d.ts +20 -7
  30. package/dist/install.d.ts.map +1 -1
  31. package/dist/local-allow.d.ts +10 -3
  32. package/dist/local-allow.d.ts.map +1 -1
  33. package/dist/open-phase.d.ts +1 -1
  34. package/dist/open-phase.d.ts.map +1 -1
  35. package/dist/own-version.d.ts +42 -0
  36. package/dist/own-version.d.ts.map +1 -0
  37. package/dist/pace.d.ts +2 -2
  38. package/dist/pace.d.ts.map +1 -1
  39. package/dist/plan.d.ts +1 -1
  40. package/dist/plan.d.ts.map +1 -1
  41. package/dist/pre-create-checks.d.ts.map +1 -1
  42. package/dist/ready.d.ts +1 -1
  43. package/dist/ready.d.ts.map +1 -1
  44. package/dist/regression.d.ts +48 -0
  45. package/dist/regression.d.ts.map +1 -0
  46. package/dist/request-shapes-verb.d.ts +1 -1
  47. package/dist/request-shapes-verb.d.ts.map +1 -1
  48. package/dist/restore.d.ts +1 -1
  49. package/dist/restore.d.ts.map +1 -1
  50. package/dist/review.d.ts +2 -2
  51. package/dist/review.d.ts.map +1 -1
  52. package/dist/route.d.ts +1 -1
  53. package/dist/route.d.ts.map +1 -1
  54. package/dist/size.d.ts +6 -4
  55. package/dist/size.d.ts.map +1 -1
  56. package/dist/uninstall.d.ts.map +1 -1
  57. package/package.json +1 -1
@@ -14804,12 +14804,34 @@ function looksLikeSecret(text) {
14804
14804
  var MCP_REGISTRATION_REL = ".mcp.json";
14805
14805
  var MCP_SERVER_KEY = "halfcycle";
14806
14806
 
14807
+ // ../core/dist/release-version.js
14808
+ var RELEASE_VERSION_PATTERN = /^\d+\.\d+\.\d+$/;
14809
+
14810
+ // ../core/dist/release-tag.js
14811
+ var PRE_RELEASE_VERSION_PATTERN = /^(\d+\.\d+\.\d+)-rc\.\d+$/;
14812
+ var PACKAGE_COMMAND = "npx halfcycle";
14813
+ function isPreReleaseVersion(value) {
14814
+ return typeof value === "string" && PRE_RELEASE_VERSION_PATTERN.test(value);
14815
+ }
14816
+ function releaseTagOfVersion(version2) {
14817
+ return isPreReleaseVersion(version2) ? "next" : "latest";
14818
+ }
14819
+ function halfcycleCommand(version2) {
14820
+ return `${PACKAGE_COMMAND}@${releaseTagOfVersion(version2)}`;
14821
+ }
14822
+ function bareHalfcycleCommand(version2) {
14823
+ return releaseTagOfVersion(version2) === "next" ? `${PACKAGE_COMMAND}@next` : PACKAGE_COMMAND;
14824
+ }
14825
+
14807
14826
  // ../core/dist/shell-quote.js
14808
14827
  function quoteForShell(value) {
14809
14828
  return `'${value.replace(/'/g, `'\\''`)}'`;
14810
14829
  }
14811
- var HALFCYCLE_COMMAND = "npx halfcycle@latest";
14812
- var DOCTOR_LINE = "Run `npx halfcycle doctor` to check set up, sign-in, connection, checks and version.";
14830
+ var HALFCYCLE_COMMAND = halfcycleCommand();
14831
+ function doctorLine(version2) {
14832
+ return `Run \`${bareHalfcycleCommand(version2)} doctor\` to check set up, sign-in, connection, checks and version.`;
14833
+ }
14834
+ var DOCTOR_LINE = doctorLine();
14813
14835
 
14814
14836
  // ../core/dist/folder-trust.js
14815
14837
  var CLAUDE_STATE_FILENAME = ".claude.json";
@@ -15107,9 +15129,6 @@ function typedPhaseReference(phase, number4) {
15107
15129
  return phase.includes(":") && number4 !== null ? String(number4) : phase;
15108
15130
  }
15109
15131
 
15110
- // ../core/dist/release-version.js
15111
- var RELEASE_VERSION_PATTERN = /^\d+\.\d+\.\d+$/;
15112
-
15113
15132
  // ../core/dist/github-address.js
15114
15133
  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}$/;
15115
15134
  var HOST = "[Gg][Ii][Tt][Hh][Uu][Bb]\\.[Cc][Oo][Mm]";
@@ -15148,8 +15167,14 @@ function githubRepositoryAddress(remote) {
15148
15167
  }
15149
15168
 
15150
15169
  // ../core/dist/sign-in.js
15151
- var SIGN_IN_COMMAND = '"npx halfcycle"';
15152
- var HOOK_REMEDY = `Run ${SIGN_IN_COMMAND} in the project folder to sign in.`;
15170
+ function signInCommand(version2) {
15171
+ return `"${bareHalfcycleCommand(version2)}"`;
15172
+ }
15173
+ var SIGN_IN_COMMAND = signInCommand();
15174
+ function hookRemedy(version2) {
15175
+ return `Run ${signInCommand(version2)} in the project folder to sign in.`;
15176
+ }
15177
+ var HOOK_REMEDY = hookRemedy();
15153
15178
 
15154
15179
  // ../core/dist/task-plan.js
15155
15180
  var TASK_PLAN_FILE = "docs/build-records/task-plan.json";
@@ -15261,7 +15286,7 @@ function codexHookCommand(script, args2) {
15261
15286
  const invocation = `cd "$(git rev-parse --show-toplevel)" && bash ./${script}`;
15262
15287
  return args2 === void 0 ? invocation : `${invocation} ${args2}`;
15263
15288
  }
15264
- 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.";
15289
+ 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.`;
15265
15290
  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.";
15266
15291
  function codexHookCommandWindows(script, args2) {
15267
15292
  const tick = (text) => "`" + text + "`";
@@ -18925,6 +18950,10 @@ function paceClockOf(at, now) {
18925
18950
  return `${day} ${clock}`;
18926
18951
  }
18927
18952
  var PACE_P5 = "This build may use all of your allowance. It will not slow down to leave room for you.";
18953
+ function paceP8Of(version2) {
18954
+ return `For these to work, run "${bareHalfcycleCommand(version2)}" once more in this project.`;
18955
+ }
18956
+ var PACE_P8 = paceP8Of();
18928
18957
  function paceS1Of(window, k, until, now, keepFreePercent) {
18929
18958
  return `Halfcycle is slowing this build so you keep about ${paceShareWordsOf(keepFreePercent)} of this ${paceWindowWordsOf(window)} for your own work. It will run at most ${paceAgentsOf(k)} at once until ${paceClockOf(until, now)}.`;
18930
18959
  }
@@ -19170,7 +19199,10 @@ var CLIENT_VERSION_HEADER = "x-halfcycle-client";
19170
19199
  var TYPED_APPROVAL_OFF_SENTENCE = "Approving by typing is switched off for now.";
19171
19200
  var HEADLESS_CANNOT_APPROVE = "(a headless session cannot approve, by design)";
19172
19201
  var TYPED_REVIEW_OFF_SENTENCE = "Reviewing by typing is switched off for now.";
19173
- 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.`;
19202
+ function clientUpdateSentence(version2) {
19203
+ return `To approve by typing in a session, Halfcycle needs updating: run \`${halfcycleCommand(version2)}\` in this project's folder, then reopen your session and ask again.`;
19204
+ }
19205
+ var CLIENT_UPDATE_SENTENCE = clientUpdateSentence();
19174
19206
 
19175
19207
  // ../events/dist/device-auth.js
19176
19208
  var utcIso86012 = external_exports.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");
@@ -19385,15 +19417,15 @@ var reviewNoticesSchema = external_exports.object({
19385
19417
  function phaseCalled(phase) {
19386
19418
  return `phase "${phase}"`;
19387
19419
  }
19388
- function questionNotice(entry) {
19389
- const answer = `${HALFCYCLE_COMMAND} review ${quoteForShell(entry.phase)} --answer ${quoteForShell(entry.part)} "<your answer>"`;
19420
+ function questionNotice(entry, version2) {
19421
+ const answer = `${halfcycleCommand(version2)} review ${quoteForShell(entry.phase)} --answer ${quoteForShell(entry.part)} "<your answer>"`;
19390
19422
  return `Before anything else: the project's owner asked about "${entry.partName}" in the review of ${phaseCalled(entry.phase)}: ${entry.question} Answer it with ${answer}.`;
19391
19423
  }
19392
19424
  function sentBackNotice2(entry) {
19393
19425
  return `Before anything else: the project's owner sent "${entry.partName}" in ${phaseCalled(entry.phase)} back to building: ${entry.note} Rebuild it: add or reopen the tasks that build it. For each task that rebuilds it, add a task that reviews the rebuild, held by a different agent from the one that built it. When each review has passed, report the build with the time each task finished, then run the close step again.`;
19394
19426
  }
19395
- function reviewNoticeText(review) {
19396
- return [...review.questions.map(questionNotice), ...review.sentBack.map(sentBackNotice2)];
19427
+ function reviewNoticeText(review, version2) {
19428
+ return [...review.questions.map((entry) => questionNotice(entry, version2)), ...review.sentBack.map(sentBackNotice2)];
19397
19429
  }
19398
19430
 
19399
19431
  // ../events/dist/credential-wire.js
@@ -19420,8 +19452,14 @@ var CREDENTIAL_PLACEHOLDERS = [
19420
19452
  UPDATE_CLAUDE_CODE_PLACEHOLDER
19421
19453
  ];
19422
19454
  var SWITCH_ACCOUNT_ANSWER = 'answer "s" when asked';
19423
- 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.';
19424
- 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}.`;
19455
+ function noCredentialSentence(version2) {
19456
+ return `Halfcycle is not connected: this machine has no working Halfcycle credential for this project. Run "${bareHalfcycleCommand(version2)}" in the project folder to sign in again.`;
19457
+ }
19458
+ var NO_CREDENTIAL_SENTENCE = noCredentialSentence();
19459
+ function notThisAccountSentence(version2) {
19460
+ return `Halfcycle is not connected: this project belongs to another Halfcycle account. To switch, run "${bareHalfcycleCommand(version2)}" in this project and ${SWITCH_ACCOUNT_ANSWER}.`;
19461
+ }
19462
+ var NOT_THIS_ACCOUNT_SENTENCE = notThisAccountSentence();
19425
19463
  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.";
19426
19464
  var CANNOT_REACH_SENTENCE = "Halfcycle is not connected: its server could not be reached, so its tools are unavailable in this session. Check your network, then reopen the project.";
19427
19465
  var SERVICE_PROBLEM_SENTENCE = "Halfcycle is not connected: its service had a problem just now, so nothing in this session is being checked. Reopen the project to try again.";
@@ -19432,6 +19470,13 @@ var PLACEHOLDER_SENTENCES = Object.freeze({
19432
19470
  [NOT_THIS_ACCOUNT_PLACEHOLDER]: NOT_THIS_ACCOUNT_SENTENCE,
19433
19471
  [UPDATE_CLAUDE_CODE_PLACEHOLDER]: UPDATE_CLAUDE_CODE_SENTENCE
19434
19472
  });
19473
+ function placeholderSentencesOf(version2) {
19474
+ return Object.freeze({
19475
+ [NO_CREDENTIAL_PLACEHOLDER]: noCredentialSentence(version2),
19476
+ [NOT_THIS_ACCOUNT_PLACEHOLDER]: notThisAccountSentence(version2),
19477
+ [UPDATE_CLAUDE_CODE_PLACEHOLDER]: UPDATE_CLAUDE_CODE_SENTENCE
19478
+ });
19479
+ }
19435
19480
 
19436
19481
  // ../events/dist/scope-chat.js
19437
19482
  var chatScopeApproveRequestSchema = external_exports.object({
@@ -19862,8 +19907,8 @@ var AUTONOMY_A5 = "Builds in this project wait for you again.";
19862
19907
  function withoutFinalStop(text) {
19863
19908
  return text.endsWith(".") ? text.slice(0, -1) : text;
19864
19909
  }
19865
- function autonomyA9(what, id) {
19866
- return `Halfcycle saved a copy before this: ${withoutFinalStop(what)}. To bring it back, run: ${HALFCYCLE_COMMAND} restore ${id} (kept for 7 days).`;
19910
+ function autonomyA9(what, id, version2) {
19911
+ return `Halfcycle saved a copy before this: ${withoutFinalStop(what)}. To bring it back, run: ${halfcycleCommand(version2)} restore ${id} (kept for 7 days).`;
19867
19912
  }
19868
19913
  function autonomyA10(typeSentence, item) {
19869
19914
  return `${typeSentence} Builds run on their own here, but this still waits for the owner. Declare it with halfcycle_declare_owner_wait, item ${String(item)}, and carry on with other work.`;
@@ -19929,6 +19974,53 @@ function checksReportOfInstructions(text) {
19929
19974
  // dist/hook-protocol.js
19930
19975
  import { writeSync } from "node:fs";
19931
19976
 
19977
+ // dist/pinned-engagement.js
19978
+ import { readFileSync } from "node:fs";
19979
+ import { resolve } from "node:path";
19980
+ function readPinnedId(root) {
19981
+ let raw;
19982
+ try {
19983
+ raw = readFileSync(resolve(root, BUNDLE_PIN_REL), "utf-8");
19984
+ } catch {
19985
+ return { problem: "no-pin" };
19986
+ }
19987
+ let pin;
19988
+ try {
19989
+ pin = JSON.parse(raw);
19990
+ } catch {
19991
+ return { problem: "no-id" };
19992
+ }
19993
+ const id = typeof pin === "object" && pin !== null ? pin[PIN_ENGAGEMENT_ID_FIELD] : void 0;
19994
+ return typeof id === "string" && id !== "" ? { id } : { problem: "no-id" };
19995
+ }
19996
+ function pinnedEngagementId(root) {
19997
+ const pinned = readPinnedId(root);
19998
+ return "id" in pinned && isEngagementId(pinned.id) ? pinned.id : void 0;
19999
+ }
20000
+ function projectFolder(env = process.env) {
20001
+ const dir = env["CLAUDE_PROJECT_DIR"];
20002
+ return dir !== void 0 && dir !== "" ? dir : process.cwd();
20003
+ }
20004
+ function readPinnedVersion(root) {
20005
+ let raw;
20006
+ try {
20007
+ raw = readFileSync(resolve(root, BUNDLE_PIN_REL), "utf-8");
20008
+ } catch {
20009
+ return void 0;
20010
+ }
20011
+ let pin;
20012
+ try {
20013
+ pin = JSON.parse(raw);
20014
+ } catch {
20015
+ return void 0;
20016
+ }
20017
+ const version2 = typeof pin === "object" && pin !== null ? pin["version"] : void 0;
20018
+ return typeof version2 === "string" && version2 !== "" ? version2 : void 0;
20019
+ }
20020
+ function installedVersion(env = process.env) {
20021
+ return readPinnedVersion(projectFolder(env));
20022
+ }
20023
+
19932
20024
  // dist/agent-argument.js
19933
20025
  var AGENT_FLAG = "--agent";
19934
20026
  function agentFromArguments(rest) {
@@ -19996,9 +20088,10 @@ function hookReply(event, profile = AGENT_PROFILES["claude-code"]) {
19996
20088
  if (!owesDoctorLine || doctorLineSaid)
19997
20089
  return;
19998
20090
  doctorLineSaid = true;
19999
- queued.push(DOCTOR_LINE);
20091
+ const doctor = doctorLine(installedVersion());
20092
+ queued.push(doctor);
20000
20093
  if (personOwedDoctorLine)
20001
- forPerson.push(DOCTOR_LINE);
20094
+ forPerson.push(doctor);
20002
20095
  };
20003
20096
  return {
20004
20097
  notice(text) {
@@ -20020,7 +20113,7 @@ function hookReply(event, profile = AGENT_PROFILES["claude-code"]) {
20020
20113
  },
20021
20114
  loud(text, exitCode, notAFailure) {
20022
20115
  const said = notAFailure !== void 0 || doctorLineSaid ? text : `${text.trimEnd()}
20023
- ${DOCTOR_LINE}
20116
+ ${doctorLine(installedVersion())}
20024
20117
  `;
20025
20118
  if (notAFailure === void 0)
20026
20119
  doctorLineSaid = true;
@@ -20112,7 +20205,7 @@ function lastResortReply(argv, text) {
20112
20205
  function answerUnexpectedError(argv, err, out) {
20113
20206
  const text = `halfcycle-runner: unexpected error: ${err instanceof Error ? err.message : String(err)}
20114
20207
  Contact hello@halfcycle.ai if this keeps happening.
20115
- ${DOCTOR_LINE}
20208
+ ${doctorLine(installedVersion())}
20116
20209
  `;
20117
20210
  const reply = lastResortReply(argv, text);
20118
20211
  if (reply !== null) {
@@ -20150,7 +20243,7 @@ function noRepositoryText(failure) {
20150
20243
  }
20151
20244
  function contractFaultText(message) {
20152
20245
  return `[Halfcycle] WARNING: Guard response could not be understood \u2014 ${message}
20153
- This is not a service outage: the guard service answered. The suspect is this repository's own installed halfcycle runner, which is out of date with what the service now sends. Update it: run "npx halfcycle" in this repository, then retry. Session continues (fail-open).`;
20246
+ This is not a service outage: the guard service answered. The suspect is this repository's own installed halfcycle runner, which is out of date with what the service now sends. Update it: run ${signInCommand(installedVersion())} in this repository, then retry. Session continues (fail-open).`;
20154
20247
  }
20155
20248
  var UNCONFIGURED_EXIT_TOOL_USE = 2;
20156
20249
  var UNCONFIGURED_EXIT_STOP_FAMILY = 1;
@@ -20166,8 +20259,8 @@ function emitNeverConfigured(exitCode, reason, engagementId, reply) {
20166
20259
  writeSync(2, text);
20167
20260
  process.exitCode = exitCode;
20168
20261
  }
20169
- function namesTheSignInCommand(text) {
20170
- return text.includes(SIGN_IN_COMMAND);
20262
+ function namesTheSignInCommand(text, version2) {
20263
+ return text.includes(signInCommand(version2 ?? installedVersion()));
20171
20264
  }
20172
20265
  var PERMISSIONS_BLOCK = "\n permissions:\n contents: read\n id-token: write\n\n";
20173
20266
  var BOTH_LINES_SENTENCE = "BOTH LINES. Declaring any permission replaces the defaults rather than adding to them, so a block naming only the identity token takes read access away from the checkout step and a private repository stops checking out before this check is reached.";
@@ -20192,13 +20285,13 @@ ${CI_OLDER_SETUP}
20192
20285
  `;
20193
20286
  }
20194
20287
  function notThisAccountReport(label, engagementId) {
20195
- return `${label} This project belongs to another Halfcycle account. Halfcycle is Solo today \u2014 one person works on a project at a time \u2014 so no credential exists for this machine and none will be issued. Nothing here needs fixing; this copy simply is not checked. ${requestAccessSentence(engagementId)} If this account is wrong, sign in as the one that owns the project and run "npx halfcycle" again.
20288
+ return `${label} This project belongs to another Halfcycle account. Halfcycle is Solo today \u2014 one person works on a project at a time \u2014 so no credential exists for this machine and none will be issued. Nothing here needs fixing; this copy simply is not checked. ${requestAccessSentence(engagementId)} If this account is wrong, sign in as the one that owns the project and run ${signInCommand(installedVersion())} again.
20196
20289
  `;
20197
20290
  }
20198
20291
  function neverConfiguredReport(reason, label = "[Halfcycle]", engagementId) {
20199
20292
  switch (reason) {
20200
20293
  case "no-credential-yet":
20201
- return neverConfiguredMessage(HOOK_REMEDY, label);
20294
+ return neverConfiguredMessage(hookRemedy(installedVersion()), label);
20202
20295
  case "not-this-account":
20203
20296
  return notThisAccountReport(label, engagementId);
20204
20297
  }
@@ -20242,7 +20335,7 @@ This is not a service outage. The guard service is up and replying; ${cause}, so
20242
20335
  ${remedy}
20243
20336
  `;
20244
20337
  }
20245
- function emitCredentialRejected(statusCode, serviceMessage, exitCode, remedy = HOOK_REMEDY, reply) {
20338
+ function emitCredentialRejected(statusCode, serviceMessage, exitCode, remedy = hookRemedy(installedVersion()), reply) {
20246
20339
  const text = credentialRejectedMessage(statusCode, serviceMessage, remedy);
20247
20340
  if (reply !== void 0) {
20248
20341
  reply.loud(text, exitCode);
@@ -20256,7 +20349,7 @@ function credentialRenewalFailedMessage(remedy, label = "[Halfcycle]") {
20256
20349
  ${remedy}
20257
20350
  `;
20258
20351
  }
20259
- function noCoverageStatement(reason, remedySaid = false, engagementId) {
20352
+ function noCoverageStatement(reason, remedySaid = false, engagementId, version2 = installedVersion()) {
20260
20353
  if (reason === "not-this-account") {
20261
20354
  return [
20262
20355
  "[halfcycle] This project belongs to another Halfcycle account, so nothing here is checked.",
@@ -20265,11 +20358,11 @@ function noCoverageStatement(reason, remedySaid = false, engagementId) {
20265
20358
  "[halfcycle] Halfcycle is Solo today \u2014 one developer per project \u2014 with Team plans coming.",
20266
20359
  `[halfcycle] ${requestAccessSentence(engagementId)}`,
20267
20360
  "[halfcycle] If this account is wrong, sign in as the one that owns the project and run",
20268
- '[halfcycle] "npx halfcycle" again.'
20361
+ `[halfcycle] ${signInCommand(version2)} again.`
20269
20362
  ].join("\n");
20270
20363
  }
20271
20364
  const statement = `${NO_SIGN_IN_HERE}, so edits made in this session won't be checked.`;
20272
- return remedySaid ? statement : `${statement} ${HOOK_REMEDY}`;
20365
+ return remedySaid ? statement : `${statement} ${hookRemedy(version2)}`;
20273
20366
  }
20274
20367
  function notRunReport(notRun, label = "[Halfcycle]") {
20275
20368
  if (!notRun || notRun.length === 0)
@@ -20494,7 +20587,7 @@ async function attempt2(url2, token, request, fetchFn, retry) {
20494
20587
 
20495
20588
  // dist/git.js
20496
20589
  import { spawnSync } from "node:child_process";
20497
- import { existsSync as existsSync2, readFileSync } from "node:fs";
20590
+ import { existsSync as existsSync2, readFileSync as readFileSync2 } from "node:fs";
20498
20591
  import { relative } from "node:path";
20499
20592
  var COLOR_AWARE_SUBCOMMANDS = /* @__PURE__ */ new Set(["diff", "show", "log"]);
20500
20593
  function spawnGit(args2, cwd, timeoutMs, extra) {
@@ -20527,7 +20620,7 @@ function getSingleFileDiff(absFilePath, repoRoot) {
20527
20620
  return diffResult.value;
20528
20621
  }
20529
20622
  try {
20530
- const content = readFileSync(absFilePath, "utf-8");
20623
+ const content = readFileSync2(absFilePath, "utf-8");
20531
20624
  if (!content.trim())
20532
20625
  return "";
20533
20626
  const lines2 = content.split("\n");
@@ -21354,7 +21447,7 @@ function detectBlastRadiusMarkers(filePath, diffContent) {
21354
21447
  }
21355
21448
 
21356
21449
  // dist/extract/numeric-columns.js
21357
- import { readFileSync as readFileSync2, readdirSync } from "node:fs";
21450
+ import { readFileSync as readFileSync3, readdirSync } from "node:fs";
21358
21451
  import { join as join2 } from "node:path";
21359
21452
  function extractNumericColumns(diffContent, context = {}) {
21360
21453
  const addedLines = [];
@@ -21432,7 +21525,7 @@ function walkForRegistrations(dir, repoRoot, acc) {
21432
21525
  continue;
21433
21526
  let content;
21434
21527
  try {
21435
- content = readFileSync2(absPath, "utf-8");
21528
+ content = readFileSync3(absPath, "utf-8");
21436
21529
  } catch {
21437
21530
  continue;
21438
21531
  }
@@ -21569,7 +21662,7 @@ function resolveWireFields(lines2, decls) {
21569
21662
  }
21570
21663
 
21571
21664
  // dist/fixtures-manifest.js
21572
- import { existsSync as existsSync3, mkdirSync, readFileSync as readFileSync3, renameSync, unlinkSync, writeFileSync } from "node:fs";
21665
+ import { existsSync as existsSync3, mkdirSync, readFileSync as readFileSync4, renameSync, unlinkSync, writeFileSync } from "node:fs";
21573
21666
  import { dirname, join as join3 } from "node:path";
21574
21667
  function manifestPath(repoRoot) {
21575
21668
  return join3(repoRoot, "test", "fixtures", "captured", "manifest.json");
@@ -21582,7 +21675,7 @@ function loadManifest(repoRoot) {
21582
21675
  return [];
21583
21676
  let raw;
21584
21677
  try {
21585
- raw = readFileSync3(manifestPath(repoRoot), "utf-8");
21678
+ raw = readFileSync4(manifestPath(repoRoot), "utf-8");
21586
21679
  } catch {
21587
21680
  return [];
21588
21681
  }
@@ -21855,7 +21948,7 @@ function toAstShapes(markers) {
21855
21948
  }
21856
21949
 
21857
21950
  // dist/extract/context.js
21858
- import { readFileSync as readFileSync4, readdirSync as readdirSync2 } from "node:fs";
21951
+ import { readFileSync as readFileSync5, readdirSync as readdirSync2 } from "node:fs";
21859
21952
  import { join as join4, relative as relative2, sep } from "node:path";
21860
21953
 
21861
21954
  // dist/extract/repo-facts.js
@@ -22005,7 +22098,7 @@ function walk(dir, repoRoot, acc, facts, underDocs = false) {
22005
22098
  noteFileName(facts, entry.name, underDocs);
22006
22099
  if (!underDocs && isFactManifest(entry.name)) {
22007
22100
  try {
22008
- noteManifest(facts, entry.name, readFileSync4(join4(dir, entry.name), "utf-8"));
22101
+ noteManifest(facts, entry.name, readFileSync5(join4(dir, entry.name), "utf-8"));
22009
22102
  } catch {
22010
22103
  continue;
22011
22104
  }
@@ -22016,7 +22109,7 @@ function walk(dir, repoRoot, acc, facts, underDocs = false) {
22016
22109
  const absPath = join4(dir, entry.name);
22017
22110
  let content;
22018
22111
  try {
22019
- content = readFileSync4(absPath, "utf-8");
22112
+ content = readFileSync5(absPath, "utf-8");
22020
22113
  } catch {
22021
22114
  continue;
22022
22115
  }
@@ -22350,34 +22443,6 @@ function cleanRunSummary(verdict, scope) {
22350
22443
  return `[Halfcycle CI] ${verdict} ${files}, ${scope.signals} signal(s) checked.`;
22351
22444
  }
22352
22445
 
22353
- // dist/pinned-engagement.js
22354
- import { readFileSync as readFileSync5 } from "node:fs";
22355
- import { resolve } from "node:path";
22356
- function readPinnedId(root) {
22357
- let raw;
22358
- try {
22359
- raw = readFileSync5(resolve(root, BUNDLE_PIN_REL), "utf-8");
22360
- } catch {
22361
- return { problem: "no-pin" };
22362
- }
22363
- let pin;
22364
- try {
22365
- pin = JSON.parse(raw);
22366
- } catch {
22367
- return { problem: "no-id" };
22368
- }
22369
- const id = typeof pin === "object" && pin !== null ? pin[PIN_ENGAGEMENT_ID_FIELD] : void 0;
22370
- return typeof id === "string" && id !== "" ? { id } : { problem: "no-id" };
22371
- }
22372
- function pinnedEngagementId(root) {
22373
- const pinned = readPinnedId(root);
22374
- return "id" in pinned && isEngagementId(pinned.id) ? pinned.id : void 0;
22375
- }
22376
- function projectFolder(env = process.env) {
22377
- const dir = env["CLAUDE_PROJECT_DIR"];
22378
- return dir !== void 0 && dir !== "" ? dir : process.cwd();
22379
- }
22380
-
22381
22446
  // dist/credential-store.js
22382
22447
  import { chmodSync, existsSync as existsSync4, readFileSync as readFileSync6, renameSync as renameSync2, unlinkSync as unlinkSync2, writeFileSync as writeFileSync2, realpathSync } from "node:fs";
22383
22448
  import { homedir as homedir3, platform } from "node:os";
@@ -22514,12 +22579,12 @@ async function mintThroughJoin(controlOrigin, engagementId, accountCredential, f
22514
22579
  }
22515
22580
  if (!response.ok) {
22516
22581
  if (response.status === UNAUTHORIZED)
22517
- return { ok: false, remedy: HOOK_REMEDY };
22582
+ return { ok: false, remedy: hookRemedy(installedVersion()) };
22518
22583
  return { ok: false, remedy: planeSentence(body) ?? UNREADABLE_REFUSAL_REMEDY };
22519
22584
  }
22520
22585
  const parsed = engagementCredentialResponseSchema.safeParse(body);
22521
22586
  if (!parsed.success)
22522
- return { ok: false, remedy: UNREADABLE_MINT_REMEDY };
22587
+ return { ok: false, remedy: unreadableMintRemedy() };
22523
22588
  return { ok: true, token: parsed.data.sessionToken, source: "mint" };
22524
22589
  }
22525
22590
  function planeSentence(body) {
@@ -22530,11 +22595,13 @@ function planeSentence(body) {
22530
22595
  }
22531
22596
  var UNREACHABLE_REMEDY = "The Halfcycle service could not be reached to renew this credential. Nothing is wrong with this repository; the next change will try again.";
22532
22597
  var UNREADABLE_REFUSAL_REMEDY = "The Halfcycle service declined to renew this credential and gave no reason this version could read.";
22533
- var UNREADABLE_MINT_REMEDY = 'The Halfcycle service answered with a credential this version could not read. Update the installed Halfcycle runner: run "npx halfcycle" in this repository.';
22598
+ function unreadableMintRemedy() {
22599
+ return `The Halfcycle service answered with a credential this version could not read. Update the installed Halfcycle runner: run ${signInCommand(installedVersion())} in this repository.`;
22600
+ }
22534
22601
  async function refreshEngagementCredential(opts) {
22535
22602
  const envFilePath = readEnvFilePath(opts.source);
22536
22603
  if (!envFilePath)
22537
- return { ok: false, remedy: HOOK_REMEDY };
22604
+ return { ok: false, remedy: hookRemedy(installedVersion()) };
22538
22605
  const stored = readStoredEngagementToken(envFilePath);
22539
22606
  if (stored !== void 0 && stored !== opts.refusedToken) {
22540
22607
  return { ok: true, token: stored, source: "disk" };
@@ -22542,7 +22609,7 @@ async function refreshEngagementCredential(opts) {
22542
22609
  const controlOrigin = readControlOrigin(opts.source).origin;
22543
22610
  const accountCredential = readAccountCredential(controlOrigin, opts.home);
22544
22611
  if (!accountCredential)
22545
- return { ok: false, remedy: HOOK_REMEDY };
22612
+ return { ok: false, remedy: hookRemedy(installedVersion()) };
22546
22613
  const minted = await mintThroughJoin(controlOrigin, opts.engagementId, accountCredential, opts.fetchFn ?? globalThis.fetch);
22547
22614
  if (!minted.ok)
22548
22615
  return minted;
@@ -22563,10 +22630,10 @@ async function evaluateWithRefresh(opts) {
22563
22630
  const fetchFn = opts.fetchFn ?? globalThis.fetch;
22564
22631
  const first = await evaluate(opts.guardServiceUrl, opts.token, opts.request, fetchFn);
22565
22632
  if (first.ok)
22566
- return { result: first, token: opts.token, remedy: HOOK_REMEDY };
22633
+ return { result: first, token: opts.token, remedy: hookRemedy(installedVersion()) };
22567
22634
  const failure = classifyFailure(first);
22568
22635
  if (failure.kind !== "credential" || failure.statusCode !== UNAUTHORIZED) {
22569
- return { result: first, token: opts.token, remedy: HOOK_REMEDY };
22636
+ return { result: first, token: opts.token, remedy: hookRemedy(installedVersion()) };
22570
22637
  }
22571
22638
  const refreshed = await refreshEngagementCredential({
22572
22639
  engagementId: opts.engagementId,
@@ -22578,7 +22645,7 @@ async function evaluateWithRefresh(opts) {
22578
22645
  if (!refreshed.ok)
22579
22646
  return { result: first, token: opts.token, remedy: refreshed.remedy };
22580
22647
  const retry = await evaluate(opts.guardServiceUrl, refreshed.token, opts.request, fetchFn);
22581
- return { result: retry, token: refreshed.token, remedy: HOOK_REMEDY };
22648
+ return { result: retry, token: refreshed.token, remedy: hookRemedy(installedVersion()) };
22582
22649
  }
22583
22650
 
22584
22651
  // dist/hooks/marker.js
@@ -23708,7 +23775,7 @@ async function observeChanges(sessionId, reply, deps = {}) {
23708
23775
  var R1_START_NOT_HERE = "Halfcycle cannot count what this phase has changed on this computer yet, because the commit the phase started from is not here. Get the project's latest history, and it counts again at the end of the next reply.";
23709
23776
  var R2_START_NOT_ON_BRANCH = "Halfcycle cannot count what this phase has changed on this branch, because the branch does not contain the commit the phase started from. Switch to the branch the phase is built on, and it counts again at the end of the next reply.";
23710
23777
  function r3NoStartRecorded(phase) {
23711
- return `Halfcycle cannot count what phase "${phase}" changes, because no starting commit was recorded when it opened. To count from where the project is now, run: ${HALFCYCLE_COMMAND} open-phase ${quoteForShell(phase)} --actor "you" --evidence "<what shows this phase is ready to start>"`;
23778
+ return `Halfcycle cannot count what phase "${phase}" changes, because no starting commit was recorded when it opened. To count from where the project is now, run: ${halfcycleCommand(installedVersion())} open-phase ${quoteForShell(phase)} --actor "you" --evidence "<what shows this phase is ready to start>"`;
23712
23779
  }
23713
23780
  function r4OutsideThePlan(parts) {
23714
23781
  const first = parts.length === 1 ? "Halfcycle saw changes to a part this phase's plan does not include:" : "Halfcycle saw changes to parts this phase's plan does not include:";
@@ -29412,21 +29479,187 @@ function recogniseSimple(cmd) {
29412
29479
  function programForms(words) {
29413
29480
  const forms = [words];
29414
29481
  const prog = basename3(words[0].text);
29482
+ const args2 = words.slice(1);
29415
29483
  let rest;
29416
29484
  if (prog === "npx" || prog === "bunx" || prog === "pnpx") {
29417
- rest = skipRunnerOptions(words.slice(1));
29485
+ rest = skipRunnerOptions(args2);
29418
29486
  } else if (prog === "npm" || prog === PNPM || prog === "yarn" || prog === "bun") {
29419
- const lead = skipLeading(words.slice(1), MANAGER_VALUE_OPTIONS);
29487
+ const lead = skipLeading(args2, MANAGER_VALUE_OPTIONS);
29420
29488
  const sub = lead[0]?.text;
29421
29489
  if (sub === "exec" || sub === "dlx" || sub === "x")
29422
29490
  rest = skipRunnerOptions(lead.slice(1));
29423
29491
  else if (sub !== void 0 && prog !== "npm")
29424
29492
  rest = lead;
29493
+ } else {
29494
+ const started = PYTHON_PROGRAM.test(prog) ? pythonStarts(args2) : prog === "ruby" ? rubyStarts(args2) : prog === "bundle" ? bundleExecStarts(args2) : prog === "go" ? goRunStarts(args2) : prog === "cargo" ? cargoRunStarts(args2) : void 0;
29495
+ if (started !== void 0 && started.length > 0 && startsPublisher(started[0]))
29496
+ rest = started;
29425
29497
  }
29426
29498
  if (rest !== void 0 && rest.length > 0)
29427
29499
  forms.push(rest);
29428
29500
  return forms;
29429
29501
  }
29502
+ var PYTHON_PROGRAM = /^python(\d+(\.\d+)*)?$/;
29503
+ function startsPublisher(word) {
29504
+ const prog = basename3(word.text);
29505
+ return Object.hasOwn(PUBLISH_SEQUENCES, prog) || Object.hasOwn(DEPLOY_SEQUENCES, prog) || prog === VERCEL;
29506
+ }
29507
+ function readCluster(args2, i, letters) {
29508
+ const word = args2[i];
29509
+ const t = word.text;
29510
+ for (let k = 1; k < t.length; k++) {
29511
+ const c = t[k];
29512
+ if (letters.stop.includes(c))
29513
+ return null;
29514
+ const tail = t.slice(k + 1);
29515
+ if (letters.start.includes(c))
29516
+ return tail !== "" ? [{ text: tail, literal: word.literal }, ...args2.slice(i + 1)] : args2.slice(i + 1);
29517
+ if (letters.value.includes(c))
29518
+ return tail !== "" ? i + 1 : i + 2;
29519
+ if (letters.attached.includes(c))
29520
+ return i + 1;
29521
+ }
29522
+ return i + 1;
29523
+ }
29524
+ function interpreterStarts(args2, letters) {
29525
+ let i = 0;
29526
+ while (i < args2.length) {
29527
+ const t = args2[i].text;
29528
+ if (t === "--")
29529
+ return args2.slice(i + 1);
29530
+ if (t === "-")
29531
+ return void 0;
29532
+ if (!t.startsWith("-"))
29533
+ return args2.slice(i);
29534
+ if (t.startsWith("--")) {
29535
+ i++;
29536
+ continue;
29537
+ }
29538
+ const next = readCluster(args2, i, letters);
29539
+ if (next === null)
29540
+ return void 0;
29541
+ if (typeof next !== "number")
29542
+ return next;
29543
+ i = next;
29544
+ }
29545
+ return void 0;
29546
+ }
29547
+ function pythonStarts(args2) {
29548
+ return interpreterStarts(args2, { stop: "c", start: "m", value: "WX", attached: "" });
29549
+ }
29550
+ function rubyStarts(args2) {
29551
+ return interpreterStarts(args2, { stop: "e", start: "S", value: "IrCE", attached: "0FiKTWx" });
29552
+ }
29553
+ function bundleExecStarts(args2) {
29554
+ const lead = skipLeading(args2, EMPTY);
29555
+ return lead[0]?.text === "exec" ? skipLeading(lead.slice(1), EMPTY) : void 0;
29556
+ }
29557
+ var GO_BUILD_VALUE_FLAGS = /* @__PURE__ */ new Set([
29558
+ "C",
29559
+ "p",
29560
+ "asmflags",
29561
+ "buildmode",
29562
+ "compiler",
29563
+ "coverpkg",
29564
+ "covermode",
29565
+ "exec",
29566
+ "gccgoflags",
29567
+ "gcflags",
29568
+ "installsuffix",
29569
+ "ldflags",
29570
+ "mod",
29571
+ "modfile",
29572
+ "overlay",
29573
+ "pgo",
29574
+ "pkgdir",
29575
+ "tags",
29576
+ "toolexec"
29577
+ ]);
29578
+ function goRunStarts(args2) {
29579
+ if (args2[0]?.text !== "run")
29580
+ return void 0;
29581
+ const rest = args2.slice(1);
29582
+ let i = 0;
29583
+ while (i < rest.length) {
29584
+ const t = rest[i].text;
29585
+ if (t === "--") {
29586
+ i++;
29587
+ break;
29588
+ }
29589
+ if (!t.startsWith("-"))
29590
+ break;
29591
+ const name = t.replace(/^--?/, "");
29592
+ i += !name.includes("=") && GO_BUILD_VALUE_FLAGS.has(name) ? 2 : 1;
29593
+ }
29594
+ const pkg = rest[i];
29595
+ if (pkg === void 0 || pkg.text.startsWith(".") || pkg.text.startsWith("/") || pkg.text.endsWith(".go"))
29596
+ return void 0;
29597
+ const parts = pkg.text.split("@")[0].split("/").filter((p) => p !== "");
29598
+ let last = parts.pop();
29599
+ if (last !== void 0 && /^v\d+$/.test(last) && parts.length > 0)
29600
+ last = parts.pop();
29601
+ return last === void 0 ? void 0 : [{ text: last, literal: pkg.literal }, ...rest.slice(i + 1)];
29602
+ }
29603
+ var CARGO_VALUE_OPTIONS = /* @__PURE__ */ new Set(["-C", "-Z", "--config", "--color"]);
29604
+ var CARGO_RUN_VALUE_OPTIONS = /* @__PURE__ */ new Set([
29605
+ "-F",
29606
+ "--features",
29607
+ "--target",
29608
+ "--target-dir",
29609
+ "--manifest-path",
29610
+ "-j",
29611
+ "--jobs",
29612
+ "--profile",
29613
+ "--color",
29614
+ "--config",
29615
+ "-Z",
29616
+ "--message-format",
29617
+ "--lockfile-path"
29618
+ ]);
29619
+ var CARGO_PROGRAM_OPTIONS = ["--bin", "--example", "-p", "--package"];
29620
+ function cargoRunStarts(args2) {
29621
+ let i = 0;
29622
+ while (i < args2.length) {
29623
+ const t = args2[i].text;
29624
+ if (t === "--" || !(t.startsWith("-") || t.startsWith("+")))
29625
+ break;
29626
+ i += CARGO_VALUE_OPTIONS.has(t) ? 2 : 1;
29627
+ }
29628
+ const sub = args2[i]?.text;
29629
+ if (sub !== "run" && sub !== "r")
29630
+ return void 0;
29631
+ const rest = args2.slice(i + 1);
29632
+ let named;
29633
+ let namedByPackage = false;
29634
+ const passed = [];
29635
+ for (let j = 0; j < rest.length; j++) {
29636
+ const word = rest[j];
29637
+ const t = word.text;
29638
+ if (t === "--") {
29639
+ passed.push(...rest.slice(j + 1));
29640
+ break;
29641
+ }
29642
+ if (!t.startsWith("-")) {
29643
+ passed.push(word);
29644
+ continue;
29645
+ }
29646
+ const eq = t.indexOf("=");
29647
+ const attachedP = /^-p[^=]/.test(t);
29648
+ const name = attachedP ? "-p" : eq < 0 ? t : t.slice(0, eq);
29649
+ if (CARGO_PROGRAM_OPTIONS.includes(name)) {
29650
+ const value = attachedP ? { text: t.slice(2), literal: word.literal } : eq < 0 ? rest[++j] : { text: t.slice(eq + 1), literal: word.literal };
29651
+ const byPackage = name === "-p" || name === "--package";
29652
+ if (value !== void 0 && (named === void 0 || namedByPackage && !byPackage)) {
29653
+ named = value;
29654
+ namedByPackage = byPackage;
29655
+ }
29656
+ continue;
29657
+ }
29658
+ if (eq < 0 && CARGO_RUN_VALUE_OPTIONS.has(name))
29659
+ j++;
29660
+ }
29661
+ return named === void 0 ? void 0 : [named, ...passed];
29662
+ }
29430
29663
  var MANAGER_VALUE_OPTIONS = /* @__PURE__ */ new Set(["--filter", "-F", "-C", "--dir", "--prefix", "--cwd", "--workspace"]);
29431
29664
  function skipLeading(words, valueOptions) {
29432
29665
  let j = 0;
@@ -30085,9 +30318,10 @@ function publishMatch(cmd, prog, args2) {
30085
30318
  return [];
30086
30319
  return containsSequence(operandTexts(args2), seqs) ? [{ type: "publish", program: prog, command: cmd }] : [];
30087
30320
  }
30321
+ var VERCEL = "vercel";
30088
30322
  var PRODUCTION_DEPLOY_OPTIONS = ["--prod", "--production"];
30089
30323
  function deployMatch(cmd, prog, args2) {
30090
- if (prog === "vercel") {
30324
+ if (prog === VERCEL) {
30091
30325
  return hasLong(args2, ...PRODUCTION_DEPLOY_OPTIONS) ? [{ type: "deploy", program: prog, command: cmd }] : [];
30092
30326
  }
30093
30327
  const seqs = DEPLOY_SEQUENCES[prog];
@@ -33187,6 +33421,97 @@ function judgeChange(input, call, deps) {
33187
33421
  };
33188
33422
  }
33189
33423
 
33424
+ // dist/risky/skip-checks.js
33425
+ var SKIP_CHECKS_GIT_SUBCOMMANDS = ["commit", "push"];
33426
+ var GIT_PROGRAM = "git";
33427
+ var NO_VERIFY = "--no-verify";
33428
+ var COMMIT_LONG_VALUE = /* @__PURE__ */ new Set([
33429
+ "--message",
33430
+ "--file",
33431
+ "--template",
33432
+ "--reuse-message",
33433
+ "--reedit-message",
33434
+ "--author",
33435
+ "--date",
33436
+ "--cleanup",
33437
+ "--fixup",
33438
+ "--squash",
33439
+ "--trailer",
33440
+ "--pathspec-from-file"
33441
+ ]);
33442
+ var COMMIT_GRAMMAR = {
33443
+ shortSkip: "n",
33444
+ shortValue: /* @__PURE__ */ new Set(["m", "F", "c", "C", "t", "u", "S"]),
33445
+ shortSeparateValue: /* @__PURE__ */ new Set(["m", "F", "c", "C", "t"]),
33446
+ longValue: COMMIT_LONG_VALUE
33447
+ };
33448
+ var PUSH_LONG_VALUE = /* @__PURE__ */ new Set(["--push-option", "--repo", "--receive-pack", "--exec"]);
33449
+ var PUSH_GRAMMAR = {
33450
+ shortSkip: null,
33451
+ shortValue: /* @__PURE__ */ new Set(["o"]),
33452
+ shortSeparateValue: /* @__PURE__ */ new Set(["o"]),
33453
+ longValue: PUSH_LONG_VALUE
33454
+ };
33455
+ var GRAMMARS = {
33456
+ commit: COMMIT_GRAMMAR,
33457
+ push: PUSH_GRAMMAR
33458
+ };
33459
+ function skipsChecks(command) {
33460
+ const out = [];
33461
+ for (const cmd of splitCommand(command)) {
33462
+ if (cmd.words.length === 0 || basename3(cmd.words[0].text) !== GIT_PROGRAM)
33463
+ continue;
33464
+ const args2 = cmd.words.slice(1);
33465
+ const { at } = gitArgsOf(args2);
33466
+ const sub = args2[at]?.text;
33467
+ const verb = SKIP_CHECKS_GIT_SUBCOMMANDS.find((v) => v === sub);
33468
+ if (verb === void 0)
33469
+ continue;
33470
+ if (carriesSkip(args2.slice(at + 1), GRAMMARS[verb]))
33471
+ out.push({ verb, command: cmd });
33472
+ }
33473
+ return out;
33474
+ }
33475
+ function carriesSkip(rest, grammar) {
33476
+ for (let i = 0; i < rest.length; i++) {
33477
+ const word = rest[i];
33478
+ const t = word.text;
33479
+ if (t === "--")
33480
+ return false;
33481
+ if (t.startsWith("--")) {
33482
+ if (!t.includes("=") && grammar.longValue.has(t)) {
33483
+ i++;
33484
+ continue;
33485
+ }
33486
+ if (hasLongAsGitReads([word], NO_VERIFY))
33487
+ return true;
33488
+ continue;
33489
+ }
33490
+ if (t.length < 2 || !t.startsWith("-"))
33491
+ continue;
33492
+ for (let k = 1; k < t.length; k++) {
33493
+ const letter = t[k];
33494
+ if (letter === grammar.shortSkip)
33495
+ return true;
33496
+ if (grammar.shortValue.has(letter)) {
33497
+ if (k === t.length - 1 && grammar.shortSeparateValue.has(letter))
33498
+ i++;
33499
+ break;
33500
+ }
33501
+ }
33502
+ }
33503
+ return false;
33504
+ }
33505
+ var SKIP_CHECKS_COMMIT_FIX = "Run it again without --no-verify or -n. If a check fails, fix what it found.";
33506
+ var SKIP_CHECKS_PUSH_FIX = "Run it again without --no-verify. If a check fails, fix what it found.";
33507
+ function skipChecksRefusal(match, description) {
33508
+ const what = match.verb === "commit" ? "A commit" : "A push";
33509
+ return {
33510
+ person: `Halfcycle: ${what} was stopped. It would skip this project's own checks, and those always run here. ${reasonSentence(description)} ${NOTHING_WAS_CHANGED}`,
33511
+ agent: match.verb === "commit" ? SKIP_CHECKS_COMMIT_FIX : SKIP_CHECKS_PUSH_FIX
33512
+ };
33513
+ }
33514
+
33190
33515
  // dist/risky/save.js
33191
33516
  import { randomBytes as randomBytes2 } from "node:crypto";
33192
33517
  import { chmodSync as chmodSync2, constants as fsConstants, copyFileSync, lstatSync as lstatSync4, mkdirSync as mkdirSync11, readdirSync as readdirSync10, readFileSync as readFileSync26, readlinkSync, realpathSync as realpathSync9, renameSync as renameSync14, rmSync as rmSync4, statSync as statSync10, symlinkSync, writeFileSync as writeFileSync16 } from "node:fs";
@@ -33789,6 +34114,9 @@ function runPreToolUse(deps) {
33789
34114
  if (calls === null || !calls.every((c) => CHECKED.has(c.toolName)))
33790
34115
  return unreadable(deps, input);
33791
34116
  const call = calls[0] ?? { toolName: stringOf(input, "tool_name") ?? "", toolInput: {} };
34117
+ const skipped = skippedChecksReply(calls);
34118
+ if (skipped !== null)
34119
+ return skipped;
33792
34120
  let answer;
33793
34121
  try {
33794
34122
  answer = riskyAnswer(calls, () => {
@@ -33859,6 +34187,24 @@ function runPreToolUse(deps) {
33859
34187
  return "";
33860
34188
  }
33861
34189
  }
34190
+ function skippedChecksReply(calls) {
34191
+ try {
34192
+ for (const c of calls) {
34193
+ const command = c.toolName === "Bash" ? c.toolInput["command"] : void 0;
34194
+ if (typeof command !== "string")
34195
+ continue;
34196
+ const match = skipsChecks(command)[0];
34197
+ if (match === void 0)
34198
+ continue;
34199
+ const text = skipChecksRefusal(match, reasonOf(c));
34200
+ return `${preToolUseReplyText("refuse", text.person, text.agent)}
34201
+ `;
34202
+ }
34203
+ } catch {
34204
+ return null;
34205
+ }
34206
+ return null;
34207
+ }
33862
34208
  function clockOf(deps) {
33863
34209
  return deps.now ?? (() => /* @__PURE__ */ new Date());
33864
34210
  }
@@ -33926,7 +34272,7 @@ function autonomousAnswer(call, staked, sendsSaves, mode, autonomy, deps) {
33926
34272
  });
33927
34273
  if (!saved.saved)
33928
34274
  return refuse(autonomyA12(typeSentenceFor(saved.reason === AUTONOMY_COULD_NOT_TELL_REASON ? [] : staked), saved.reason));
33929
- return `${preToolUseSavedText(autonomyA9(what, saved.id))}
34275
+ return `${preToolUseSavedText(autonomyA9(what, saved.id, installedVersion(deps.env ?? process.env)))}
33930
34276
  `;
33931
34277
  }
33932
34278
  default: {
@@ -34661,7 +35007,7 @@ function nothingDone(act) {
34661
35007
  return act === "approve" ? "Nothing was approved" : "Nothing was sent back";
34662
35008
  }
34663
35009
  function showCommand(phase) {
34664
- return `\`${HALFCYCLE_COMMAND} plan ${quoteForShell(phase)} --show\``;
35010
+ return `\`${halfcycleCommand(installedVersion())} plan ${quoteForShell(phase)} --show\``;
34665
35011
  }
34666
35012
  function recordedLine(act, phase, planVersion) {
34667
35013
  return act === "approve" ? line(`Approved "${phase}", plan version ${planVersion}, in your session.`) : line(`Sent "${phase}" back with your note, plan version ${planVersion}, in your session. The note is given first to the next session that asks what to do.`);
@@ -34670,13 +35016,13 @@ function notAttendedLine(act) {
34670
35016
  return line(`${nothingDone(act)}: approving or sending back by typing counts only when a person types it into an open session.`);
34671
35017
  }
34672
35018
  function notSignedInLine(act) {
34673
- return line(`${nothingDone(act)}: this project is not signed in on this computer. ${HOOK_REMEDY}`);
35019
+ return line(`${nothingDone(act)}: this project is not signed in on this computer. ${hookRemedy(installedVersion())}`);
34674
35020
  }
34675
35021
  function notOwnerLine(act) {
34676
35022
  return line(`${nothingDone(act)}: only the project's owner can approve or send back this scope.`);
34677
35023
  }
34678
35024
  function showFirstLine(act) {
34679
- return line(`${nothingDone(act)}: that phase has not been listed here. List its parts first with \`${HALFCYCLE_COMMAND} plan <phase> --show\`, then type the line again.`);
35025
+ return line(`${nothingDone(act)}: that phase has not been listed here. List its parts first with \`${halfcycleCommand(installedVersion())} plan <phase> --show\`, then type the line again.`);
34680
35026
  }
34681
35027
  function namedPhases(phases) {
34682
35028
  const quoted2 = phases.map((phase) => `"${phase}"`);
@@ -34701,7 +35047,7 @@ function noteRefusedLine() {
34701
35047
  return line("Nothing was sent back: the note was not accepted. Reword it in plain words and type the send-back again.");
34702
35048
  }
34703
35049
  function approveRefusedLine() {
34704
- return line(`${nothingDone("approve")}: Halfcycle did not accept the request. ${CLIENT_UPDATE_SENTENCE}`);
35050
+ return line(`${nothingDone("approve")}: Halfcycle did not accept the request. ${clientUpdateSentence(installedVersion())}`);
34705
35051
  }
34706
35052
  function cannotReachLine(act) {
34707
35053
  return line(`${nothingDone(act)}: Halfcycle could not be reached just now. Type the line again in a minute.`);
@@ -34742,7 +35088,7 @@ function reviewNotAttendedLine() {
34742
35088
  return line(`${NOTHING_RECORDED}: reviewing by typing counts only when a person types it into an open session.`);
34743
35089
  }
34744
35090
  function reviewNotSignedInLine() {
34745
- return line(`${NOTHING_RECORDED}: this project is not signed in on this computer. ${HOOK_REMEDY}`);
35091
+ return line(`${NOTHING_RECORDED}: this project is not signed in on this computer. ${hookRemedy(installedVersion())}`);
34746
35092
  }
34747
35093
  function reviewCannotReachLine() {
34748
35094
  return line(`${NOTHING_RECORDED}: Halfcycle could not be reached just now. Type the line again in a minute.`);
@@ -34838,7 +35184,7 @@ function reviewCloseRefusedLine(phase, steps) {
34838
35184
  return line([head, ...steps.map(unfinishedStepLine), "Then type the line again."].join(" "));
34839
35185
  }
34840
35186
  function reviewRefusedLine(act) {
34841
- return act === "accept" ? line(`${NOTHING_RECORDED}: Halfcycle did not accept the request. Update Halfcycle by running \`${HALFCYCLE_COMMAND}\` in this project's folder, then type the line again.`) : line(`${NOTHING_RECORDED}: the words were not accepted. Reword them in plain words and type the line again.`);
35187
+ return act === "accept" ? line(`${NOTHING_RECORDED}: Halfcycle did not accept the request. Update Halfcycle by running \`${halfcycleCommand(installedVersion())}\` in this project's folder, then type the line again.`) : line(`${NOTHING_RECORDED}: the words were not accepted. Reword them in plain words and type the line again.`);
34842
35188
  }
34843
35189
  function stillWaitingOf(phase, waiting) {
34844
35190
  const named = namedPhase(phase);
@@ -34955,7 +35301,7 @@ function autonomyNotAttendedLine() {
34955
35301
  return line(`${AUTONOMY_NOTHING_CHANGED}: turning this on or off by typing counts only when a person types it into an open session.`);
34956
35302
  }
34957
35303
  function autonomyNotSignedInLine() {
34958
- return line(`${AUTONOMY_NOTHING_CHANGED}: this project is not signed in on this computer. ${HOOK_REMEDY}`);
35304
+ return line(`${AUTONOMY_NOTHING_CHANGED}: this project is not signed in on this computer. ${hookRemedy(installedVersion())}`);
34959
35305
  }
34960
35306
  function autonomyNotOwnerLine() {
34961
35307
  return line(`${AUTONOMY_NOTHING_CHANGED}: only the project's owner can turn this on or off.`);
@@ -35037,7 +35383,7 @@ function waitNotAttendedLine() {
35037
35383
  return line(`${NOTHING_RECORDED}: an answer counts only when a person types it into an open session.`);
35038
35384
  }
35039
35385
  function waitNotSignedInLine() {
35040
- return line(`${NOTHING_RECORDED}: this project is not signed in on this computer. ${HOOK_REMEDY}`);
35386
+ return line(`${NOTHING_RECORDED}: this project is not signed in on this computer. ${hookRemedy(installedVersion())}`);
35041
35387
  }
35042
35388
  function waitNotOwnerLine() {
35043
35389
  return line(`${NOTHING_RECORDED}: only the project's owner can answer a decision that waits for them.`);
@@ -35124,7 +35470,7 @@ function paceNotAttendedText() {
35124
35470
  return `${PACE_NOTHING_CHANGED_OPENING}: a choice that uses more of your allowance counts only when a person types it into an open session.`;
35125
35471
  }
35126
35472
  function paceNotSignedInText() {
35127
- return `${PACE_NOTHING_CHANGED_OPENING}: this project is not signed in on this computer. ${HOOK_REMEDY}`;
35473
+ return `${PACE_NOTHING_CHANGED_OPENING}: this project is not signed in on this computer. ${hookRemedy(installedVersion())}`;
35128
35474
  }
35129
35475
  function paceNotOwnerText() {
35130
35476
  return `${PACE_NOTHING_CHANGED_OPENING}: only the project's owner can change this.`;
@@ -35884,12 +36230,11 @@ function runResolveEngagement(cwd, registryPath) {
35884
36230
  }
35885
36231
 
35886
36232
  // dist/refresh-credential.js
35887
- import { lstatSync as lstatSync5, readFileSync as readFileSync32, realpathSync as realpathSync11 } from "node:fs";
36233
+ import { lstatSync as lstatSync5, readFileSync as readFileSync31, realpathSync as realpathSync11 } from "node:fs";
35888
36234
  import { homedir as homedir19 } from "node:os";
35889
36235
  import { join as join24, resolve as resolve12 } from "node:path";
35890
36236
 
35891
36237
  // dist/mcp-headers.js
35892
- import { readFileSync as readFileSync31 } from "node:fs";
35893
36238
  import { homedir as homedir18 } from "node:os";
35894
36239
  import { dirname as dirname10, resolve as resolve11 } from "node:path";
35895
36240
  var URL_FLAG = "--url";
@@ -35920,22 +36265,6 @@ function projectRootFromBinary(binaryPath) {
35920
36265
  const depth = VENDORED_BIN_REL.split("/").length - 1;
35921
36266
  return resolve11(dirname10(binaryPath), ...Array(depth).fill(".."));
35922
36267
  }
35923
- function readPinnedVersion(root) {
35924
- let raw;
35925
- try {
35926
- raw = readFileSync31(resolve11(root, BUNDLE_PIN_REL), "utf-8");
35927
- } catch {
35928
- return void 0;
35929
- }
35930
- let pin;
35931
- try {
35932
- pin = JSON.parse(raw);
35933
- } catch {
35934
- return void 0;
35935
- }
35936
- const version2 = typeof pin === "object" && pin !== null ? pin["version"] : void 0;
35937
- return typeof version2 === "string" && version2 !== "" ? version2 : void 0;
35938
- }
35939
36268
  function resolveStore(root, home) {
35940
36269
  const pinned = readPinnedId(root);
35941
36270
  if ("problem" in pinned)
@@ -35964,23 +36293,25 @@ function sentences(lines2) {
35964
36293
  `).join("");
35965
36294
  }
35966
36295
  function badIdSentence(root) {
36296
+ const signIn = signInCommand(readPinnedVersion(root));
35967
36297
  return sentences([
35968
36298
  `${resolve11(root, BUNDLE_PIN_REL)} names an engagement id that is not a Halfcycle project id,`,
35969
- 'so no credential was read. Delete that file and run "npx halfcycle" again: this repository',
36299
+ `so no credential was read. Delete that file and run ${signIn} again: this repository`,
35970
36300
  "is then set up as a new Halfcycle project."
35971
36301
  ]);
35972
36302
  }
35973
36303
  function storeProblemSentence(problem, root, home, engagementId) {
36304
+ const signIn = signInCommand(readPinnedVersion(root));
35974
36305
  switch (problem) {
35975
36306
  case "no-pin":
35976
36307
  return sentences([
35977
36308
  `no ${BUNDLE_PIN_REL} in ${root}, so this repository has no Halfcycle engagement.`,
35978
- 'run "npx halfcycle" here to install one.'
36309
+ `run ${signIn} here to install one.`
35979
36310
  ]);
35980
36311
  case "no-id":
35981
36312
  return sentences([
35982
36313
  `${resolve11(root, BUNDLE_PIN_REL)} names no engagement id.`,
35983
- 'run "npx halfcycle" here to rewrite it.'
36314
+ `run ${signIn} here to rewrite it.`
35984
36315
  ]);
35985
36316
  case "bad-id":
35986
36317
  return badIdSentence(root);
@@ -35991,12 +36322,12 @@ function storeProblemSentence(problem, root, home, engagementId) {
35991
36322
  "broken.",
35992
36323
  requestAccessSentence(engagementId),
35993
36324
  "If this account is wrong, sign in as the account that owns it and run",
35994
- '"npx halfcycle" again.'
36325
+ `${signIn} again.`
35995
36326
  ]);
35996
36327
  case "no-credential":
35997
36328
  return sentences([
35998
36329
  `this machine holds no credential for engagement ${engagementId ?? ""}.`,
35999
- `run "npx halfcycle" in this repository \u2014 it will sign you in through your browser if needed, and write the credential to ${resolve11(home, HALFCYCLE_DIR_NAME)}, outside this repository.`
36330
+ `run ${signIn} in this repository \u2014 it will sign you in through your browser if needed, and write the credential to ${resolve11(home, HALFCYCLE_DIR_NAME)}, outside this repository.`
36000
36331
  ]);
36001
36332
  }
36002
36333
  }
@@ -36021,6 +36352,7 @@ async function chooseBearer(opts) {
36021
36352
  const writeErr = opts.writeErr ?? defaultWriteErr;
36022
36353
  const home = opts.home ?? homedir18();
36023
36354
  const root = projectRootFromBinary(opts.binaryPath);
36355
+ const signIn = signInCommand(readPinnedVersion(root));
36024
36356
  const environmentToken = opts.environmentTokenFirst ? readEnvironmentToken() : void 0;
36025
36357
  const store = resolveStore(root, home);
36026
36358
  let storedToken;
@@ -36033,7 +36365,7 @@ async function chooseBearer(opts) {
36033
36365
  if (storedToken === void 0) {
36034
36366
  writeErr(sentences([
36035
36367
  `GUARD_SERVICE_TOKEN is absent or empty in ${store.path}.`,
36036
- `run "npx halfcycle" in this repository to rewrite this machine's credential.`
36368
+ `run ${signIn} in this repository to rewrite this machine's credential.`
36037
36369
  ]));
36038
36370
  return NO_CREDENTIAL_PLACEHOLDER;
36039
36371
  }
@@ -36053,7 +36385,7 @@ async function chooseBearer(opts) {
36053
36385
  } else {
36054
36386
  writeErr(sentences([
36055
36387
  "this machine has no Halfcycle server address recorded for this repository, so the credential was not sent.",
36056
- 'run "npx halfcycle" in this repository on this machine to record it.'
36388
+ `run ${signIn} in this repository on this machine to record it.`
36057
36389
  ]));
36058
36390
  }
36059
36391
  return NO_CREDENTIAL_PLACEHOLDER;
@@ -36065,7 +36397,7 @@ async function chooseBearer(opts) {
36065
36397
  writeErr(sentences([
36066
36398
  `${registrationFile} asks for this repository's Halfcycle credential to be sent to ${shownUrl(requested ?? "")},`,
36067
36399
  `which is not this machine's Halfcycle server (${want ?? "none recorded"}). The credential was NOT sent.`,
36068
- `if nobody meant to change ${registrationFile}, treat that change as suspect; "npx halfcycle" restores the entry.`
36400
+ `if nobody meant to change ${registrationFile}, treat that change as suspect; ${signIn} restores the entry.`
36069
36401
  ]));
36070
36402
  return NO_CREDENTIAL_PLACEHOLDER;
36071
36403
  }
@@ -36085,6 +36417,7 @@ async function chooseBearer(opts) {
36085
36417
  async function mcpHeaders(opts) {
36086
36418
  const writeErr = opts.writeErr ?? defaultWriteErr;
36087
36419
  const root = projectRootFromBinary(opts.binaryPath);
36420
+ const signIn = signInCommand(readPinnedVersion(root));
36088
36421
  const variableUrl = readMcpServerUrl();
36089
36422
  try {
36090
36423
  return headerLine(await chooseBearer({
@@ -36102,7 +36435,7 @@ async function mcpHeaders(opts) {
36102
36435
  try {
36103
36436
  writeErr(sentences([
36104
36437
  `the credential could not be read: ${err instanceof Error ? err.message : String(err)}`,
36105
- `run "npx halfcycle" in this repository to rewrite this machine's credential.`
36438
+ `run ${signIn} in this repository to rewrite this machine's credential.`
36106
36439
  ]));
36107
36440
  } catch {
36108
36441
  return headerLine(NO_CREDENTIAL_PLACEHOLDER, readPinnedVersion(root));
@@ -36117,11 +36450,13 @@ async function headersVerbLine(binaryPath, rest, run = mcpHeaders, writeErr = de
36117
36450
  } catch (err) {
36118
36451
  if (url2 === void 0)
36119
36452
  throw err;
36120
- const fallback = headerLine(NO_CREDENTIAL_PLACEHOLDER, readPinnedVersion(projectRootFromBinary(binaryPath)));
36453
+ const version2 = readPinnedVersion(projectRootFromBinary(binaryPath));
36454
+ const signIn = signInCommand(version2);
36455
+ const fallback = headerLine(NO_CREDENTIAL_PLACEHOLDER, version2);
36121
36456
  try {
36122
36457
  writeErr(sentences([
36123
36458
  `the credential could not be read: ${err instanceof Error ? err.message : String(err)}`,
36124
- `run "npx halfcycle" in this repository to rewrite this machine's credential.`
36459
+ `run ${signIn} in this repository to rewrite this machine's credential.`
36125
36460
  ]));
36126
36461
  } catch {
36127
36462
  return fallback;
@@ -36185,7 +36520,7 @@ var SESSION_START_PROBE_ATTEMPTS = 2;
36185
36520
  function registeredMcpUrl(projectRoot) {
36186
36521
  let parsed;
36187
36522
  try {
36188
- parsed = JSON.parse(readFileSync32(resolve12(projectRoot, MCP_REGISTRATION_REL), "utf-8"));
36523
+ parsed = JSON.parse(readFileSync31(resolve12(projectRoot, MCP_REGISTRATION_REL), "utf-8"));
36189
36524
  } catch {
36190
36525
  return void 0;
36191
36526
  }
@@ -36220,7 +36555,7 @@ function reportOf(body) {
36220
36555
  const instructions = body["result"]["instructions"];
36221
36556
  return typeof instructions === "string" ? checksReportOfInstructions(instructions) : "none";
36222
36557
  }
36223
- async function postInitialize(url2, bearer, fetchFn, signal) {
36558
+ async function postInitialize(url2, bearer, fetchFn, signal, version2) {
36224
36559
  let res;
36225
36560
  try {
36226
36561
  res = await fetchFn(url2, {
@@ -36235,7 +36570,7 @@ async function postInitialize(url2, bearer, fetchFn, signal) {
36235
36570
  }
36236
36571
  if (res.status === 401) {
36237
36572
  discardBody(res);
36238
- return NO_CREDENTIAL_SENTENCE;
36573
+ return noCredentialSentence(version2);
36239
36574
  }
36240
36575
  if (res.status < 200 || res.status > 299) {
36241
36576
  discardBody(res);
@@ -36255,12 +36590,12 @@ async function postInitialize(url2, bearer, fetchFn, signal) {
36255
36590
  }
36256
36591
  return namesItsServer(body) ? { report: reportOf(body) } : SERVICE_PROBLEM_SENTENCE;
36257
36592
  }
36258
- async function probeDelivery(url2, bearer, fetchFn, deadlineMs, maxAttempts) {
36593
+ async function probeDelivery(url2, bearer, fetchFn, deadlineMs, maxAttempts, version2) {
36259
36594
  const controller = new AbortController();
36260
36595
  const timer = setTimeout(() => controller.abort(), deadlineMs);
36261
36596
  try {
36262
36597
  for (let attempt3 = 0; attempt3 < maxAttempts; attempt3++) {
36263
- const outcome = await postInitialize(url2, bearer, fetchFn, controller.signal);
36598
+ const outcome = await postInitialize(url2, bearer, fetchFn, controller.signal, version2);
36264
36599
  if (outcome !== CUT_OFF)
36265
36600
  return outcome;
36266
36601
  }
@@ -36271,9 +36606,11 @@ async function probeDelivery(url2, bearer, fetchFn, deadlineMs, maxAttempts) {
36271
36606
  }
36272
36607
  async function checkConnection(renewedToken, opts) {
36273
36608
  const profile = opts.profile ?? AGENT_PROFILES["claude-code"];
36274
- const url2 = registeredMcpUrl(projectRootFromBinary(opts.binaryPath));
36609
+ const root = projectRootFromBinary(opts.binaryPath);
36610
+ const url2 = registeredMcpUrl(root);
36275
36611
  if (url2 === void 0)
36276
36612
  return void 0;
36613
+ const version2 = readPinnedVersion(root);
36277
36614
  const bearer = await chooseBearer({
36278
36615
  binaryPath: opts.binaryPath,
36279
36616
  home: opts.home,
@@ -36284,9 +36621,9 @@ async function checkConnection(renewedToken, opts) {
36284
36621
  environmentTokenFirst: false
36285
36622
  });
36286
36623
  if (CREDENTIAL_PLACEHOLDERS.includes(bearer)) {
36287
- return { sentence: PLACEHOLDER_SENTENCES[bearer], url: url2, bearer, connected: false };
36624
+ return { sentence: placeholderSentencesOf(version2)[bearer], url: url2, bearer, connected: false };
36288
36625
  }
36289
- const outcome = await probeDelivery(url2, bearer, opts.fetchFn ?? globalThis.fetch, opts.checkDeadlineMs ?? CONNECTION_CHECK_DEADLINE_MS, opts.maxAttempts ?? SESSION_START_PROBE_ATTEMPTS);
36626
+ const outcome = await probeDelivery(url2, bearer, opts.fetchFn ?? globalThis.fetch, opts.checkDeadlineMs ?? CONNECTION_CHECK_DEADLINE_MS, opts.maxAttempts ?? SESSION_START_PROBE_ATTEMPTS, version2);
36290
36627
  if (typeof outcome === "string")
36291
36628
  return { sentence: outcome, url: url2, bearer, connected: false };
36292
36629
  return { sentence: connectedSentenceFor(profile), url: url2, bearer, connected: true, report: outcome.report };
@@ -36374,12 +36711,13 @@ function headlessInUntrustedFolder(input, profile, opts) {
36374
36711
  const cwd = input["cwd"];
36375
36712
  const folder2 = typeof cwd === "string" && cwd !== "" ? cwd : projectRootFromBinary(opts.binaryPath);
36376
36713
  const stateFile = join24(readClaudeConfigFolder() ?? opts.home ?? homedir19(), CLAUDE_STATE_FILENAME);
36377
- return trustOfWorkingFolder(folder2, stateFile, process.platform, { lstatSync: lstatSync5, readFileSync: readFileSync32, realpathSync: realpathSync11 }) === "not-trusted";
36714
+ return trustOfWorkingFolder(folder2, stateFile, process.platform, { lstatSync: lstatSync5, readFileSync: readFileSync31, realpathSync: realpathSync11 }) === "not-trusted";
36378
36715
  }
36379
36716
  async function runRefreshCredential(opts) {
36380
36717
  const writeErr = opts.writeErr ?? defaultWriteErr2;
36381
36718
  const profile = opts.profile ?? AGENT_PROFILES["claude-code"];
36382
36719
  const startedAt = Date.now();
36720
+ const startVersion = readPinnedVersion(projectRootFromBinary(opts.binaryPath));
36383
36721
  try {
36384
36722
  const renewalSaid = [];
36385
36723
  let renewalOpen = true;
@@ -36410,7 +36748,7 @@ async function runRefreshCredential(opts) {
36410
36748
  if (notices?.scope !== void 0)
36411
36749
  parts.push(...scopeNoticeLines(notices.scope, engagementId));
36412
36750
  if (notices?.review !== void 0)
36413
- parts.push(...reviewNoticeText(notices.review));
36751
+ parts.push(...reviewNoticeText(notices.review, startVersion));
36414
36752
  for (const entry of notices?.approvedInConsole ?? [])
36415
36753
  parts.push(approvedInConsoleSentence(entry.phase));
36416
36754
  }
@@ -36438,18 +36776,18 @@ async function runRefreshCredential(opts) {
36438
36776
  return `${nonBlockingReply("SessionStart", FOLDER_NOT_TRUSTED_NOTICE)}
36439
36777
  `;
36440
36778
  if (readConfig().config === null) {
36441
- const remedySaid = parts.some(namesTheSignInCommand);
36779
+ const remedySaid = parts.some((part) => namesTheSignInCommand(part, startVersion));
36442
36780
  aFailureOfOurs = true;
36443
- parts.push(noCoverageStatement(readNeverConfiguredReason(), remedySaid, pinnedEngagementId(projectRootFromBinary(opts.binaryPath))));
36781
+ parts.push(noCoverageStatement(readNeverConfiguredReason(), remedySaid, pinnedEngagementId(projectRootFromBinary(opts.binaryPath)), startVersion));
36444
36782
  }
36445
36783
  for (const said of renewalSaid) {
36446
36784
  aFailureOfOurs = true;
36447
- if (namesTheSignInCommand(said) && parts.some(namesTheSignInCommand))
36785
+ if (namesTheSignInCommand(said, startVersion) && parts.some((part) => namesTheSignInCommand(part, startVersion)))
36448
36786
  continue;
36449
36787
  parts.push(said.trimEnd());
36450
36788
  }
36451
36789
  if (aFailureOfOurs)
36452
- parts.push(DOCTOR_LINE);
36790
+ parts.push(doctorLine(startVersion));
36453
36791
  return parts.length === 0 ? "" : `${nonBlockingReply("SessionStart", parts.join("\n"))}
36454
36792
  `;
36455
36793
  } catch (err) {
@@ -36467,7 +36805,7 @@ async function runRefreshCredential(opts) {
36467
36805
  import { fileURLToPath } from "node:url";
36468
36806
 
36469
36807
  // dist/capture.js
36470
- import { readFileSync as readFileSync33, writeFileSync as writeFileSync20, mkdirSync as mkdirSync14, existsSync as existsSync10 } from "node:fs";
36808
+ import { readFileSync as readFileSync32, writeFileSync as writeFileSync20, mkdirSync as mkdirSync14, existsSync as existsSync10 } from "node:fs";
36471
36809
  import { dirname as dirname11 } from "node:path";
36472
36810
  function runCapture(provider, endpoint, opts = {}) {
36473
36811
  const repoRoot = opts.repoRoot ?? process.cwd();
@@ -36535,7 +36873,7 @@ function readInputFile(fromFile) {
36535
36873
  if (!fromFile)
36536
36874
  return void 0;
36537
36875
  try {
36538
- return readFileSync33(fromFile, "utf-8");
36876
+ return readFileSync32(fromFile, "utf-8");
36539
36877
  } catch {
36540
36878
  return void 0;
36541
36879
  }
@@ -36548,7 +36886,7 @@ function relativeFixturePath(repoRoot, fixture) {
36548
36886
  // dist/prove.js
36549
36887
  import { spawn as spawn2, spawnSync as spawnSync3 } from "node:child_process";
36550
36888
  import { createHash as createHash6, randomUUID as randomUUID3 } from "node:crypto";
36551
- import { createWriteStream, mkdirSync as mkdirSync15, readdirSync as readdirSync11, readFileSync as readFileSync34, renameSync as renameSync18, unlinkSync as unlinkSync12, writeFileSync as writeFileSync21 } from "node:fs";
36889
+ import { createWriteStream, mkdirSync as mkdirSync15, readdirSync as readdirSync11, readFileSync as readFileSync33, renameSync as renameSync18, unlinkSync as unlinkSync12, writeFileSync as writeFileSync21 } from "node:fs";
36552
36890
  import { homedir as homedir20 } from "node:os";
36553
36891
  import { dirname as dirname12, join as join25 } from "node:path";
36554
36892
 
@@ -36625,13 +36963,56 @@ function cargoTestCounts(output) {
36625
36963
  failed: sum(lines2.map((m) => Number(m[2])))
36626
36964
  };
36627
36965
  }
36966
+ var UNITTEST_COLOUR_CODE = /\u001b\[[0-9;]*m/g;
36967
+ var UNITTEST_RAN_LINE = /^Ran (\d+) tests? in \d+(?:\.\d+)?s\s*$/;
36968
+ var UNITTEST_STATUS_LINE = /^(?:OK|FAILED|NO TESTS RAN)(?: \((.*)\))?$/;
36969
+ var UNITTEST_PAIR = /^(failures|errors|skipped|expected failures|unexpected successes)=(\d+)$/;
36970
+ function unittestPairs(list) {
36971
+ const pairs = /* @__PURE__ */ new Map();
36972
+ if (list === void 0)
36973
+ return pairs;
36974
+ for (const part of list.split(",")) {
36975
+ const match = part.trim().match(UNITTEST_PAIR);
36976
+ if (!match)
36977
+ return null;
36978
+ pairs.set(match[1], (pairs.get(match[1]) ?? 0) + Number(match[2]));
36979
+ }
36980
+ return pairs;
36981
+ }
36982
+ function unittestCounts(output) {
36983
+ const lines2 = output.split("\n").map((line2) => line2.endsWith("\r") ? line2.slice(0, -1) : line2);
36984
+ let total = null;
36985
+ for (let i = 0; i < lines2.length; i++) {
36986
+ const ran = lines2[i].match(UNITTEST_RAN_LINE);
36987
+ if (!ran)
36988
+ continue;
36989
+ let next = i + 1;
36990
+ while (next < lines2.length && lines2[next].trim() === "")
36991
+ next++;
36992
+ if (next >= lines2.length)
36993
+ continue;
36994
+ const status = lines2[next].replace(UNITTEST_COLOUR_CODE, "").trim().match(UNITTEST_STATUS_LINE);
36995
+ if (!status)
36996
+ continue;
36997
+ const pairs = unittestPairs(status[1]);
36998
+ if (!pairs)
36999
+ continue;
37000
+ const count = (key) => pairs.get(key) ?? 0;
37001
+ const failed2 = count("failures") + count("errors") + count("unexpected successes");
37002
+ const accounted = failed2 + count("skipped") + count("expected failures");
37003
+ const passed = Math.max(0, Number(ran[1]) - accounted);
37004
+ total = total ? { passed: total.passed + passed, failed: total.failed + failed2 } : { passed, failed: failed2 };
37005
+ }
37006
+ return total;
37007
+ }
36628
37008
  var FORMAT_PARSERS = [
36629
37009
  nodeTestRunnerCounts,
36630
37010
  mochaCounts,
36631
37011
  jestCounts,
36632
37012
  vitestCounts,
36633
37013
  pytestCounts,
36634
- cargoTestCounts
37014
+ cargoTestCounts,
37015
+ unittestCounts
36635
37016
  ];
36636
37017
  function parseTestCounts(output) {
36637
37018
  let total = null;
@@ -36759,7 +37140,7 @@ function keptCheckPath(dir, subject, label) {
36759
37140
  }
36760
37141
  function readKeptCheck(dir, subject, label) {
36761
37142
  try {
36762
- const raw = JSON.parse(readFileSync34(keptCheckPath(dir, subject, label), "utf-8"));
37143
+ const raw = JSON.parse(readFileSync33(keptCheckPath(dir, subject, label), "utf-8"));
36763
37144
  if (typeof raw.command !== "string" || raw.command === "" || typeof raw.commandId !== "string")
36764
37145
  return null;
36765
37146
  const b = raw.before;
@@ -36784,7 +37165,7 @@ function readQueue(dir) {
36784
37165
  const path = join25(dir, QUEUE_DIR_NAME, name);
36785
37166
  let parsed = null;
36786
37167
  try {
36787
- parsed = testRunReportSchema.safeParse(JSON.parse(readFileSync34(path, "utf-8")));
37168
+ parsed = testRunReportSchema.safeParse(JSON.parse(readFileSync33(path, "utf-8")));
36788
37169
  } catch {
36789
37170
  parsed = null;
36790
37171
  }
@@ -37027,7 +37408,7 @@ ${PROVE_USAGE}`);
37027
37408
  const end = await runCommand(command, repoRoot, logPath2, env, deps);
37028
37409
  let output = "";
37029
37410
  try {
37030
- output = readFileSync34(logPath2, "utf-8");
37411
+ output = readFileSync33(logPath2, "utf-8");
37031
37412
  } catch {
37032
37413
  output = "";
37033
37414
  }
@@ -37077,7 +37458,7 @@ ${PROVE_USAGE}`);
37077
37458
 
37078
37459
  // dist/statusline.js
37079
37460
  import { spawn as spawn3 } from "node:child_process";
37080
- import { readFileSync as readFileSync35 } from "node:fs";
37461
+ import { readFileSync as readFileSync34 } from "node:fs";
37081
37462
  import nodePath7 from "node:path";
37082
37463
  var OWN_STATUS_LINE_DEADLINE_MS = 5e3;
37083
37464
  var PERSON_SETTINGS_FILENAME = "settings.json";
@@ -37115,7 +37496,7 @@ function halfcycleStatusLineOf(figures) {
37115
37496
  }
37116
37497
  function personStatusLineCommand(configFolder) {
37117
37498
  try {
37118
- const v = JSON.parse(readFileSync35(nodePath7.join(configFolder, PERSON_SETTINGS_FILENAME), "utf-8"));
37499
+ const v = JSON.parse(readFileSync34(nodePath7.join(configFolder, PERSON_SETTINGS_FILENAME), "utf-8"));
37119
37500
  const line2 = v["statusLine"];
37120
37501
  if (typeof line2 !== "object" || line2 === null)
37121
37502
  return null;
@@ -37233,7 +37614,7 @@ function usageFigures2(env, deps = {}) {
37233
37614
  }
37234
37615
  return figures;
37235
37616
  }
37236
- function connectionKind(bearer, sentence) {
37617
+ function connectionKind(bearer, sentence, version2) {
37237
37618
  if (bearer === NOT_THIS_ACCOUNT_PLACEHOLDER)
37238
37619
  return "other-account";
37239
37620
  if (bearer === UPDATE_CLAUDE_CODE_PLACEHOLDER)
@@ -37246,7 +37627,7 @@ function connectionKind(bearer, sentence) {
37246
37627
  return "cannot-reach";
37247
37628
  if (sentence === SERVICE_PROBLEM_SENTENCE)
37248
37629
  return "service-problem";
37249
- if (sentence === NO_CREDENTIAL_SENTENCE)
37630
+ if (sentence === noCredentialSentence(version2))
37250
37631
  return "signed-out";
37251
37632
  throw new Error("the connection check gave an outcome with no kind");
37252
37633
  }
@@ -37260,7 +37641,7 @@ async function checkConnectionLine(opts) {
37260
37641
  const withUsage = usage.length === 0 ? {} : { usage };
37261
37642
  if (result === void 0)
37262
37643
  return { kind: "not-registered", sentence: null, ...withUsage };
37263
- const line2 = { kind: connectionKind(result.bearer, result.sentence), sentence: result.sentence };
37644
+ const line2 = { kind: connectionKind(result.bearer, result.sentence, readPinnedVersion(projectRootFromBinary(opts.binaryPath))), sentence: result.sentence };
37264
37645
  const report = result.report === void 0 || result.report === "none" ? {} : { report: result.report };
37265
37646
  return { ...line2, ...report, ...withUsage };
37266
37647
  }