halfcycle 0.11.1 → 0.12.0-rc.2

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 (79) hide show
  1. package/.claude-plugin/plugin.json +1 -1
  2. package/README.md +1 -1
  3. package/bin/bin.bundle.mjs +964 -351
  4. package/bin/pre-tool-use-filter.sh +4 -0
  5. package/commands/agent-teams-orchestration.md +1 -1
  6. package/commands/consistency-check.md +1 -1
  7. package/commands/context-update.md +1 -1
  8. package/commands/halfcycle-design.md +1 -1
  9. package/commands/halfcycle-setup.md +1 -1
  10. package/commands/stack-assembly-worker.md +1 -1
  11. package/commands/task-execution.md +1 -1
  12. package/commands/task-graph.md +1 -1
  13. package/commands/write-spec.md +1 -1
  14. package/dist/banner-facts.d.ts +7 -2
  15. package/dist/banner-facts.d.ts.map +1 -1
  16. package/dist/build-record/obtain.d.ts.map +1 -1
  17. package/dist/change.d.ts.map +1 -1
  18. package/dist/check-criteria.d.ts +1 -1
  19. package/dist/check-criteria.d.ts.map +1 -1
  20. package/dist/check-report.d.ts +1 -1
  21. package/dist/check-report.d.ts.map +1 -1
  22. package/dist/check-tasks.d.ts +10 -1
  23. package/dist/check-tasks.d.ts.map +1 -1
  24. package/dist/cli-contract.d.ts +1 -1
  25. package/dist/cli-contract.d.ts.map +1 -1
  26. package/dist/cli.js +1412 -873
  27. package/dist/cli.js.map +3 -3
  28. package/dist/close-phase.d.ts +5 -3
  29. package/dist/close-phase.d.ts.map +1 -1
  30. package/dist/confirm-identity.d.ts +3 -0
  31. package/dist/confirm-identity.d.ts.map +1 -1
  32. package/dist/crew.d.ts +37 -0
  33. package/dist/crew.d.ts.map +1 -0
  34. package/dist/draw/index.d.ts +1 -1
  35. package/dist/draw/index.d.ts.map +1 -1
  36. package/dist/engagement-credential.d.ts +4 -0
  37. package/dist/engagement-credential.d.ts.map +1 -1
  38. package/dist/failure-line.d.ts +3 -2
  39. package/dist/failure-line.d.ts.map +1 -1
  40. package/dist/folder-refusals.d.ts.map +1 -1
  41. package/dist/index.js +330 -224
  42. package/dist/index.js.map +3 -3
  43. package/dist/install-manifest.d.ts +5 -1
  44. package/dist/install-manifest.d.ts.map +1 -1
  45. package/dist/install-plan.d.ts.map +1 -1
  46. package/dist/install.d.ts +45 -11
  47. package/dist/install.d.ts.map +1 -1
  48. package/dist/local-allow.d.ts +72 -3
  49. package/dist/local-allow.d.ts.map +1 -1
  50. package/dist/loopback-signin.d.ts +6 -5
  51. package/dist/loopback-signin.d.ts.map +1 -1
  52. package/dist/merge-settings.d.ts +16 -1
  53. package/dist/merge-settings.d.ts.map +1 -1
  54. package/dist/open-phase.d.ts +1 -1
  55. package/dist/open-phase.d.ts.map +1 -1
  56. package/dist/own-version.d.ts +42 -0
  57. package/dist/own-version.d.ts.map +1 -0
  58. package/dist/pace.d.ts +2 -2
  59. package/dist/pace.d.ts.map +1 -1
  60. package/dist/plan.d.ts +4 -3
  61. package/dist/plan.d.ts.map +1 -1
  62. package/dist/pre-create-checks.d.ts.map +1 -1
  63. package/dist/preview.d.ts +7 -2
  64. package/dist/preview.d.ts.map +1 -1
  65. package/dist/ready.d.ts +1 -1
  66. package/dist/ready.d.ts.map +1 -1
  67. package/dist/regression.d.ts +48 -0
  68. package/dist/regression.d.ts.map +1 -0
  69. package/dist/request-shapes-verb.d.ts +1 -1
  70. package/dist/request-shapes-verb.d.ts.map +1 -1
  71. package/dist/restore.d.ts +1 -1
  72. package/dist/restore.d.ts.map +1 -1
  73. package/dist/review.d.ts +2 -2
  74. package/dist/review.d.ts.map +1 -1
  75. package/dist/route.d.ts +1 -1
  76. package/dist/route.d.ts.map +1 -1
  77. package/dist/size.d.ts.map +1 -1
  78. package/dist/uninstall.d.ts.map +1 -1
  79. 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");
@@ -19268,17 +19300,31 @@ var deviceConfirmResponseSchema = external_exports.union([
19268
19300
  var DASHBOARD_URL = "https://app.halfcycle.ai";
19269
19301
 
19270
19302
  // ../events/dist/project-page.js
19271
- function projectPageUrl(engagementId) {
19272
- return isEngagementId(engagementId) ? `${DASHBOARD_URL}/${encodeURIComponent(engagementId)}` : void 0;
19303
+ var SERVICE_HOST = /^(?:control|delivery)(-[a-z0-9]+)?\.halfcycle\.ai$/;
19304
+ function consoleOrigin(serviceAddress) {
19305
+ const trimmed = serviceAddress?.trim();
19306
+ if (!trimmed)
19307
+ return DASHBOARD_URL;
19308
+ let host;
19309
+ try {
19310
+ host = new URL(trimmed.includes("://") ? trimmed : `https://${trimmed}`).hostname.toLowerCase();
19311
+ } catch {
19312
+ return DASHBOARD_URL;
19313
+ }
19314
+ const suffix = SERVICE_HOST.exec(host)?.[1];
19315
+ return suffix === void 0 ? DASHBOARD_URL : `https://app${suffix}.halfcycle.ai`;
19316
+ }
19317
+ function projectPageUrl(engagementId, serviceAddress) {
19318
+ return isEngagementId(engagementId) ? `${consoleOrigin(serviceAddress)}/${encodeURIComponent(engagementId)}` : void 0;
19273
19319
  }
19274
- function systemViewUrl(engagementId, phase) {
19275
- const page = projectPageUrl(engagementId);
19320
+ function systemViewUrl(engagementId, phase, serviceAddress) {
19321
+ const page = projectPageUrl(engagementId, serviceAddress);
19276
19322
  if (page === void 0)
19277
19323
  return void 0;
19278
19324
  return `${page}/console/system-view?${new URLSearchParams({ phase }).toString()}`;
19279
19325
  }
19280
- function requestAccessSentence(engagementId) {
19281
- const page = projectPageUrl(engagementId) ?? "this project's page on Halfcycle";
19326
+ function requestAccessSentence(engagementId, serviceAddress) {
19327
+ const page = projectPageUrl(engagementId, serviceAddress) ?? "this project's page on Halfcycle";
19282
19328
  return `To ask for access, open ${page} signed in as this account and choose "Request access". Then let the project's owner know, so they can approve it.`;
19283
19329
  }
19284
19330
 
@@ -19345,19 +19391,19 @@ function systemViewSentence(offerTyping, address) {
19345
19391
  return [];
19346
19392
  return [offerTyping === "yes" ? `Or they approve it in the System view: ${address}` : `They approve it in the System view: ${address}`];
19347
19393
  }
19348
- function waitingNotice(entry, offerTyping, engagementId) {
19394
+ function waitingNotice(entry, offerTyping, engagementId, serviceAddress) {
19349
19395
  return [
19350
19396
  ...entry.changedParts.length > 0 ? [`These parts changed since the owner last decided on this phase, and wait for their approval: ${quotedParts(entry.changedParts)}.`] : [],
19351
19397
  `The scope of ${phaseWords(entry.phase)} is waiting for the project's owner to approve it.`,
19352
19398
  approvalSentence(entry.phase, offerTyping),
19353
- ...systemViewSentence(offerTyping, systemViewUrl(engagementId, entry.phase))
19399
+ ...systemViewSentence(offerTyping, systemViewUrl(engagementId, entry.phase, serviceAddress))
19354
19400
  ].join(" ");
19355
19401
  }
19356
19402
  function scopeNoticeText(scope, options) {
19357
19403
  const omit2 = options.omitActivePhase === true ? options.activePhase : void 0;
19358
19404
  return {
19359
19405
  sentBack: scope.sentBack.map(sentBackNotice),
19360
- waiting: scope.waiting.filter((w) => omit2 === void 0 || w.phase !== omit2).map((w) => waitingNotice(w, options.offerTyping, options.engagementId))
19406
+ waiting: scope.waiting.filter((w) => omit2 === void 0 || w.phase !== omit2).map((w) => waitingNotice(w, options.offerTyping, options.engagementId, options.serviceAddress))
19361
19407
  };
19362
19408
  }
19363
19409
 
@@ -19385,15 +19431,15 @@ var reviewNoticesSchema = external_exports.object({
19385
19431
  function phaseCalled(phase) {
19386
19432
  return `phase "${phase}"`;
19387
19433
  }
19388
- function questionNotice(entry) {
19389
- const answer = `${HALFCYCLE_COMMAND} review ${quoteForShell(entry.phase)} --answer ${quoteForShell(entry.part)} "<your answer>"`;
19434
+ function questionNotice(entry, version2) {
19435
+ const answer = `${halfcycleCommand(version2)} review ${quoteForShell(entry.phase)} --answer ${quoteForShell(entry.part)} "<your answer>"`;
19390
19436
  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
19437
  }
19392
19438
  function sentBackNotice2(entry) {
19393
19439
  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
19440
  }
19395
- function reviewNoticeText(review) {
19396
- return [...review.questions.map(questionNotice), ...review.sentBack.map(sentBackNotice2)];
19441
+ function reviewNoticeText(review, version2) {
19442
+ return [...review.questions.map((entry) => questionNotice(entry, version2)), ...review.sentBack.map(sentBackNotice2)];
19397
19443
  }
19398
19444
 
19399
19445
  // ../events/dist/credential-wire.js
@@ -19420,8 +19466,14 @@ var CREDENTIAL_PLACEHOLDERS = [
19420
19466
  UPDATE_CLAUDE_CODE_PLACEHOLDER
19421
19467
  ];
19422
19468
  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}.`;
19469
+ function noCredentialSentence(version2) {
19470
+ 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.`;
19471
+ }
19472
+ var NO_CREDENTIAL_SENTENCE = noCredentialSentence();
19473
+ function notThisAccountSentence(version2) {
19474
+ return `Halfcycle is not connected: this project belongs to another Halfcycle account. To switch, run "${bareHalfcycleCommand(version2)}" in this project and ${SWITCH_ACCOUNT_ANSWER}.`;
19475
+ }
19476
+ var NOT_THIS_ACCOUNT_SENTENCE = notThisAccountSentence();
19425
19477
  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
19478
  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
19479
  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 +19484,13 @@ var PLACEHOLDER_SENTENCES = Object.freeze({
19432
19484
  [NOT_THIS_ACCOUNT_PLACEHOLDER]: NOT_THIS_ACCOUNT_SENTENCE,
19433
19485
  [UPDATE_CLAUDE_CODE_PLACEHOLDER]: UPDATE_CLAUDE_CODE_SENTENCE
19434
19486
  });
19487
+ function placeholderSentencesOf(version2) {
19488
+ return Object.freeze({
19489
+ [NO_CREDENTIAL_PLACEHOLDER]: noCredentialSentence(version2),
19490
+ [NOT_THIS_ACCOUNT_PLACEHOLDER]: notThisAccountSentence(version2),
19491
+ [UPDATE_CLAUDE_CODE_PLACEHOLDER]: UPDATE_CLAUDE_CODE_SENTENCE
19492
+ });
19493
+ }
19435
19494
 
19436
19495
  // ../events/dist/scope-chat.js
19437
19496
  var chatScopeApproveRequestSchema = external_exports.object({
@@ -19862,8 +19921,8 @@ var AUTONOMY_A5 = "Builds in this project wait for you again.";
19862
19921
  function withoutFinalStop(text) {
19863
19922
  return text.endsWith(".") ? text.slice(0, -1) : text;
19864
19923
  }
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).`;
19924
+ function autonomyA9(what, id, version2) {
19925
+ return `Halfcycle saved a copy before this: ${withoutFinalStop(what)}. To bring it back, run: ${halfcycleCommand(version2)} restore ${id} (kept for 7 days).`;
19867
19926
  }
19868
19927
  function autonomyA10(typeSentence, item) {
19869
19928
  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 +19988,53 @@ function checksReportOfInstructions(text) {
19929
19988
  // dist/hook-protocol.js
19930
19989
  import { writeSync } from "node:fs";
19931
19990
 
19991
+ // dist/pinned-engagement.js
19992
+ import { readFileSync } from "node:fs";
19993
+ import { resolve } from "node:path";
19994
+ function readPinnedId(root) {
19995
+ let raw;
19996
+ try {
19997
+ raw = readFileSync(resolve(root, BUNDLE_PIN_REL), "utf-8");
19998
+ } catch {
19999
+ return { problem: "no-pin" };
20000
+ }
20001
+ let pin;
20002
+ try {
20003
+ pin = JSON.parse(raw);
20004
+ } catch {
20005
+ return { problem: "no-id" };
20006
+ }
20007
+ const id = typeof pin === "object" && pin !== null ? pin[PIN_ENGAGEMENT_ID_FIELD] : void 0;
20008
+ return typeof id === "string" && id !== "" ? { id } : { problem: "no-id" };
20009
+ }
20010
+ function pinnedEngagementId(root) {
20011
+ const pinned = readPinnedId(root);
20012
+ return "id" in pinned && isEngagementId(pinned.id) ? pinned.id : void 0;
20013
+ }
20014
+ function projectFolder(env = process.env) {
20015
+ const dir = env["CLAUDE_PROJECT_DIR"];
20016
+ return dir !== void 0 && dir !== "" ? dir : process.cwd();
20017
+ }
20018
+ function readPinnedVersion(root) {
20019
+ let raw;
20020
+ try {
20021
+ raw = readFileSync(resolve(root, BUNDLE_PIN_REL), "utf-8");
20022
+ } catch {
20023
+ return void 0;
20024
+ }
20025
+ let pin;
20026
+ try {
20027
+ pin = JSON.parse(raw);
20028
+ } catch {
20029
+ return void 0;
20030
+ }
20031
+ const version2 = typeof pin === "object" && pin !== null ? pin["version"] : void 0;
20032
+ return typeof version2 === "string" && version2 !== "" ? version2 : void 0;
20033
+ }
20034
+ function installedVersion(env = process.env) {
20035
+ return readPinnedVersion(projectFolder(env));
20036
+ }
20037
+
19932
20038
  // dist/agent-argument.js
19933
20039
  var AGENT_FLAG = "--agent";
19934
20040
  function agentFromArguments(rest) {
@@ -19996,9 +20102,10 @@ function hookReply(event, profile = AGENT_PROFILES["claude-code"]) {
19996
20102
  if (!owesDoctorLine || doctorLineSaid)
19997
20103
  return;
19998
20104
  doctorLineSaid = true;
19999
- queued.push(DOCTOR_LINE);
20105
+ const doctor = doctorLine(installedVersion());
20106
+ queued.push(doctor);
20000
20107
  if (personOwedDoctorLine)
20001
- forPerson.push(DOCTOR_LINE);
20108
+ forPerson.push(doctor);
20002
20109
  };
20003
20110
  return {
20004
20111
  notice(text) {
@@ -20020,7 +20127,7 @@ function hookReply(event, profile = AGENT_PROFILES["claude-code"]) {
20020
20127
  },
20021
20128
  loud(text, exitCode, notAFailure) {
20022
20129
  const said = notAFailure !== void 0 || doctorLineSaid ? text : `${text.trimEnd()}
20023
- ${DOCTOR_LINE}
20130
+ ${doctorLine(installedVersion())}
20024
20131
  `;
20025
20132
  if (notAFailure === void 0)
20026
20133
  doctorLineSaid = true;
@@ -20112,7 +20219,7 @@ function lastResortReply(argv, text) {
20112
20219
  function answerUnexpectedError(argv, err, out) {
20113
20220
  const text = `halfcycle-runner: unexpected error: ${err instanceof Error ? err.message : String(err)}
20114
20221
  Contact hello@halfcycle.ai if this keeps happening.
20115
- ${DOCTOR_LINE}
20222
+ ${doctorLine(installedVersion())}
20116
20223
  `;
20117
20224
  const reply = lastResortReply(argv, text);
20118
20225
  if (reply !== null) {
@@ -20150,7 +20257,7 @@ function noRepositoryText(failure) {
20150
20257
  }
20151
20258
  function contractFaultText(message) {
20152
20259
  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).`;
20260
+ 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
20261
  }
20155
20262
  var UNCONFIGURED_EXIT_TOOL_USE = 2;
20156
20263
  var UNCONFIGURED_EXIT_STOP_FAMILY = 1;
@@ -20166,8 +20273,8 @@ function emitNeverConfigured(exitCode, reason, engagementId, reply) {
20166
20273
  writeSync(2, text);
20167
20274
  process.exitCode = exitCode;
20168
20275
  }
20169
- function namesTheSignInCommand(text) {
20170
- return text.includes(SIGN_IN_COMMAND);
20276
+ function namesTheSignInCommand(text, version2) {
20277
+ return text.includes(signInCommand(version2 ?? installedVersion()));
20171
20278
  }
20172
20279
  var PERMISSIONS_BLOCK = "\n permissions:\n contents: read\n id-token: write\n\n";
20173
20280
  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 +20299,13 @@ ${CI_OLDER_SETUP}
20192
20299
  `;
20193
20300
  }
20194
20301
  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.
20302
+ 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, readControlOrigin().origin)} If this account is wrong, sign in as the one that owns the project and run ${signInCommand(installedVersion())} again.
20196
20303
  `;
20197
20304
  }
20198
20305
  function neverConfiguredReport(reason, label = "[Halfcycle]", engagementId) {
20199
20306
  switch (reason) {
20200
20307
  case "no-credential-yet":
20201
- return neverConfiguredMessage(HOOK_REMEDY, label);
20308
+ return neverConfiguredMessage(hookRemedy(installedVersion()), label);
20202
20309
  case "not-this-account":
20203
20310
  return notThisAccountReport(label, engagementId);
20204
20311
  }
@@ -20242,7 +20349,7 @@ This is not a service outage. The guard service is up and replying; ${cause}, so
20242
20349
  ${remedy}
20243
20350
  `;
20244
20351
  }
20245
- function emitCredentialRejected(statusCode, serviceMessage, exitCode, remedy = HOOK_REMEDY, reply) {
20352
+ function emitCredentialRejected(statusCode, serviceMessage, exitCode, remedy = hookRemedy(installedVersion()), reply) {
20246
20353
  const text = credentialRejectedMessage(statusCode, serviceMessage, remedy);
20247
20354
  if (reply !== void 0) {
20248
20355
  reply.loud(text, exitCode);
@@ -20256,20 +20363,20 @@ function credentialRenewalFailedMessage(remedy, label = "[Halfcycle]") {
20256
20363
  ${remedy}
20257
20364
  `;
20258
20365
  }
20259
- function noCoverageStatement(reason, remedySaid = false, engagementId) {
20366
+ function noCoverageStatement(reason, remedySaid = false, engagementId, version2 = installedVersion()) {
20260
20367
  if (reason === "not-this-account") {
20261
20368
  return [
20262
20369
  "[halfcycle] This project belongs to another Halfcycle account, so nothing here is checked.",
20263
20370
  "[halfcycle] Your work carries on exactly as it would without Halfcycle \u2014 no edit is",
20264
20371
  "[halfcycle] evaluated, and nothing you do is reported as having passed.",
20265
20372
  "[halfcycle] Halfcycle is Solo today \u2014 one developer per project \u2014 with Team plans coming.",
20266
- `[halfcycle] ${requestAccessSentence(engagementId)}`,
20373
+ `[halfcycle] ${requestAccessSentence(engagementId, readControlOrigin().origin)}`,
20267
20374
  "[halfcycle] If this account is wrong, sign in as the one that owns the project and run",
20268
- '[halfcycle] "npx halfcycle" again.'
20375
+ `[halfcycle] ${signInCommand(version2)} again.`
20269
20376
  ].join("\n");
20270
20377
  }
20271
20378
  const statement = `${NO_SIGN_IN_HERE}, so edits made in this session won't be checked.`;
20272
- return remedySaid ? statement : `${statement} ${HOOK_REMEDY}`;
20379
+ return remedySaid ? statement : `${statement} ${hookRemedy(version2)}`;
20273
20380
  }
20274
20381
  function notRunReport(notRun, label = "[Halfcycle]") {
20275
20382
  if (!notRun || notRun.length === 0)
@@ -20494,7 +20601,7 @@ async function attempt2(url2, token, request, fetchFn, retry) {
20494
20601
 
20495
20602
  // dist/git.js
20496
20603
  import { spawnSync } from "node:child_process";
20497
- import { existsSync as existsSync2, readFileSync } from "node:fs";
20604
+ import { existsSync as existsSync2, readFileSync as readFileSync2 } from "node:fs";
20498
20605
  import { relative } from "node:path";
20499
20606
  var COLOR_AWARE_SUBCOMMANDS = /* @__PURE__ */ new Set(["diff", "show", "log"]);
20500
20607
  function spawnGit(args2, cwd, timeoutMs, extra) {
@@ -20527,7 +20634,7 @@ function getSingleFileDiff(absFilePath, repoRoot) {
20527
20634
  return diffResult.value;
20528
20635
  }
20529
20636
  try {
20530
- const content = readFileSync(absFilePath, "utf-8");
20637
+ const content = readFileSync2(absFilePath, "utf-8");
20531
20638
  if (!content.trim())
20532
20639
  return "";
20533
20640
  const lines2 = content.split("\n");
@@ -21354,7 +21461,7 @@ function detectBlastRadiusMarkers(filePath, diffContent) {
21354
21461
  }
21355
21462
 
21356
21463
  // dist/extract/numeric-columns.js
21357
- import { readFileSync as readFileSync2, readdirSync } from "node:fs";
21464
+ import { readFileSync as readFileSync3, readdirSync } from "node:fs";
21358
21465
  import { join as join2 } from "node:path";
21359
21466
  function extractNumericColumns(diffContent, context = {}) {
21360
21467
  const addedLines = [];
@@ -21432,7 +21539,7 @@ function walkForRegistrations(dir, repoRoot, acc) {
21432
21539
  continue;
21433
21540
  let content;
21434
21541
  try {
21435
- content = readFileSync2(absPath, "utf-8");
21542
+ content = readFileSync3(absPath, "utf-8");
21436
21543
  } catch {
21437
21544
  continue;
21438
21545
  }
@@ -21569,7 +21676,7 @@ function resolveWireFields(lines2, decls) {
21569
21676
  }
21570
21677
 
21571
21678
  // dist/fixtures-manifest.js
21572
- import { existsSync as existsSync3, mkdirSync, readFileSync as readFileSync3, renameSync, unlinkSync, writeFileSync } from "node:fs";
21679
+ import { existsSync as existsSync3, mkdirSync, readFileSync as readFileSync4, renameSync, unlinkSync, writeFileSync } from "node:fs";
21573
21680
  import { dirname, join as join3 } from "node:path";
21574
21681
  function manifestPath(repoRoot) {
21575
21682
  return join3(repoRoot, "test", "fixtures", "captured", "manifest.json");
@@ -21582,7 +21689,7 @@ function loadManifest(repoRoot) {
21582
21689
  return [];
21583
21690
  let raw;
21584
21691
  try {
21585
- raw = readFileSync3(manifestPath(repoRoot), "utf-8");
21692
+ raw = readFileSync4(manifestPath(repoRoot), "utf-8");
21586
21693
  } catch {
21587
21694
  return [];
21588
21695
  }
@@ -21855,7 +21962,7 @@ function toAstShapes(markers) {
21855
21962
  }
21856
21963
 
21857
21964
  // dist/extract/context.js
21858
- import { readFileSync as readFileSync4, readdirSync as readdirSync2 } from "node:fs";
21965
+ import { readFileSync as readFileSync5, readdirSync as readdirSync2 } from "node:fs";
21859
21966
  import { join as join4, relative as relative2, sep } from "node:path";
21860
21967
 
21861
21968
  // dist/extract/repo-facts.js
@@ -22005,7 +22112,7 @@ function walk(dir, repoRoot, acc, facts, underDocs = false) {
22005
22112
  noteFileName(facts, entry.name, underDocs);
22006
22113
  if (!underDocs && isFactManifest(entry.name)) {
22007
22114
  try {
22008
- noteManifest(facts, entry.name, readFileSync4(join4(dir, entry.name), "utf-8"));
22115
+ noteManifest(facts, entry.name, readFileSync5(join4(dir, entry.name), "utf-8"));
22009
22116
  } catch {
22010
22117
  continue;
22011
22118
  }
@@ -22016,7 +22123,7 @@ function walk(dir, repoRoot, acc, facts, underDocs = false) {
22016
22123
  const absPath = join4(dir, entry.name);
22017
22124
  let content;
22018
22125
  try {
22019
- content = readFileSync4(absPath, "utf-8");
22126
+ content = readFileSync5(absPath, "utf-8");
22020
22127
  } catch {
22021
22128
  continue;
22022
22129
  }
@@ -22088,11 +22195,8 @@ async function postTelemetry(run, baseUrl, token, logger, fetchFn) {
22088
22195
  if (!response.ok) {
22089
22196
  logger.warn(`[Halfcycle telemetry] Control-plane POST returned ${response.status} (dropped \u2014 best-effort)`);
22090
22197
  }
22091
- } catch (err) {
22198
+ } catch {
22092
22199
  clearTimeout(timer);
22093
- const isTimeout = err instanceof Error && err.name === "AbortError";
22094
- const msg = isTimeout ? `Control-plane timed out after ${TELEMETRY_TIMEOUT_MS}ms (dropped \u2014 best-effort)` : err instanceof Error ? `Control-plane unreachable: ${err.message} (dropped \u2014 best-effort)` : `Control-plane error: ${String(err)} (dropped \u2014 best-effort)`;
22095
- logger.warn(`[Halfcycle telemetry] ${msg}`);
22096
22200
  }
22097
22201
  }
22098
22202
  function changeSetDescriptorCount(changeSet) {
@@ -22350,34 +22454,6 @@ function cleanRunSummary(verdict, scope) {
22350
22454
  return `[Halfcycle CI] ${verdict} ${files}, ${scope.signals} signal(s) checked.`;
22351
22455
  }
22352
22456
 
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
22457
  // dist/credential-store.js
22382
22458
  import { chmodSync, existsSync as existsSync4, readFileSync as readFileSync6, renameSync as renameSync2, unlinkSync as unlinkSync2, writeFileSync as writeFileSync2, realpathSync } from "node:fs";
22383
22459
  import { homedir as homedir3, platform } from "node:os";
@@ -22514,12 +22590,12 @@ async function mintThroughJoin(controlOrigin, engagementId, accountCredential, f
22514
22590
  }
22515
22591
  if (!response.ok) {
22516
22592
  if (response.status === UNAUTHORIZED)
22517
- return { ok: false, remedy: HOOK_REMEDY };
22593
+ return { ok: false, remedy: hookRemedy(installedVersion()) };
22518
22594
  return { ok: false, remedy: planeSentence(body) ?? UNREADABLE_REFUSAL_REMEDY };
22519
22595
  }
22520
22596
  const parsed = engagementCredentialResponseSchema.safeParse(body);
22521
22597
  if (!parsed.success)
22522
- return { ok: false, remedy: UNREADABLE_MINT_REMEDY };
22598
+ return { ok: false, remedy: unreadableMintRemedy() };
22523
22599
  return { ok: true, token: parsed.data.sessionToken, source: "mint" };
22524
22600
  }
22525
22601
  function planeSentence(body) {
@@ -22530,11 +22606,13 @@ function planeSentence(body) {
22530
22606
  }
22531
22607
  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
22608
  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.';
22609
+ function unreadableMintRemedy() {
22610
+ return `The Halfcycle service answered with a credential this version could not read. Update the installed Halfcycle runner: run ${signInCommand(installedVersion())} in this repository.`;
22611
+ }
22534
22612
  async function refreshEngagementCredential(opts) {
22535
22613
  const envFilePath = readEnvFilePath(opts.source);
22536
22614
  if (!envFilePath)
22537
- return { ok: false, remedy: HOOK_REMEDY };
22615
+ return { ok: false, remedy: hookRemedy(installedVersion()) };
22538
22616
  const stored = readStoredEngagementToken(envFilePath);
22539
22617
  if (stored !== void 0 && stored !== opts.refusedToken) {
22540
22618
  return { ok: true, token: stored, source: "disk" };
@@ -22542,7 +22620,7 @@ async function refreshEngagementCredential(opts) {
22542
22620
  const controlOrigin = readControlOrigin(opts.source).origin;
22543
22621
  const accountCredential = readAccountCredential(controlOrigin, opts.home);
22544
22622
  if (!accountCredential)
22545
- return { ok: false, remedy: HOOK_REMEDY };
22623
+ return { ok: false, remedy: hookRemedy(installedVersion()) };
22546
22624
  const minted = await mintThroughJoin(controlOrigin, opts.engagementId, accountCredential, opts.fetchFn ?? globalThis.fetch);
22547
22625
  if (!minted.ok)
22548
22626
  return minted;
@@ -22563,10 +22641,10 @@ async function evaluateWithRefresh(opts) {
22563
22641
  const fetchFn = opts.fetchFn ?? globalThis.fetch;
22564
22642
  const first = await evaluate(opts.guardServiceUrl, opts.token, opts.request, fetchFn);
22565
22643
  if (first.ok)
22566
- return { result: first, token: opts.token, remedy: HOOK_REMEDY };
22644
+ return { result: first, token: opts.token, remedy: hookRemedy(installedVersion()) };
22567
22645
  const failure = classifyFailure(first);
22568
22646
  if (failure.kind !== "credential" || failure.statusCode !== UNAUTHORIZED) {
22569
- return { result: first, token: opts.token, remedy: HOOK_REMEDY };
22647
+ return { result: first, token: opts.token, remedy: hookRemedy(installedVersion()) };
22570
22648
  }
22571
22649
  const refreshed = await refreshEngagementCredential({
22572
22650
  engagementId: opts.engagementId,
@@ -22578,7 +22656,7 @@ async function evaluateWithRefresh(opts) {
22578
22656
  if (!refreshed.ok)
22579
22657
  return { result: first, token: opts.token, remedy: refreshed.remedy };
22580
22658
  const retry = await evaluate(opts.guardServiceUrl, refreshed.token, opts.request, fetchFn);
22581
- return { result: retry, token: refreshed.token, remedy: HOOK_REMEDY };
22659
+ return { result: retry, token: refreshed.token, remedy: hookRemedy(installedVersion()) };
22582
22660
  }
22583
22661
 
22584
22662
  // dist/hooks/marker.js
@@ -23708,7 +23786,7 @@ async function observeChanges(sessionId, reply, deps = {}) {
23708
23786
  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
23787
  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
23788
  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>"`;
23789
+ 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
23790
  }
23713
23791
  function r4OutsideThePlan(parts) {
23714
23792
  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:";
@@ -27197,12 +27275,12 @@ function runsBesideOtherWork(toolInput, record2) {
27197
27275
  }
27198
27276
 
27199
27277
  // dist/pace-hook.js
27200
- import { mkdirSync as mkdirSync9, readFileSync as readFileSync21, renameSync as renameSync11, unlinkSync as unlinkSync8, writeFileSync as writeFileSync13 } from "node:fs";
27278
+ import { mkdirSync as mkdirSync10, readFileSync as readFileSync22, renameSync as renameSync12, unlinkSync as unlinkSync8, writeFileSync as writeFileSync14 } from "node:fs";
27201
27279
  import nodePath6 from "node:path";
27202
27280
 
27203
27281
  // dist/pace-verb.js
27204
- import { mkdirSync as mkdirSync8, readdirSync as readdirSync8, readFileSync as readFileSync20, renameSync as renameSync10, statSync as statSync6, writeFileSync as writeFileSync12 } from "node:fs";
27205
- import { homedir as homedir9 } from "node:os";
27282
+ import { mkdirSync as mkdirSync9, readdirSync as readdirSync8, readFileSync as readFileSync21, renameSync as renameSync11, statSync as statSync6, writeFileSync as writeFileSync13 } from "node:fs";
27283
+ import { homedir as homedir10 } from "node:os";
27206
27284
  import nodePath5 from "node:path";
27207
27285
 
27208
27286
  // dist/plan-report.js
@@ -27356,12 +27434,72 @@ async function planUsageReport(input, deps = {}) {
27356
27434
  }
27357
27435
  }
27358
27436
 
27437
+ // dist/relayed-choices.js
27438
+ import { mkdirSync as mkdirSync8, readFileSync as readFileSync20, renameSync as renameSync10, rmSync as rmSync3, writeFileSync as writeFileSync12 } from "node:fs";
27439
+ import { homedir as homedir9 } from "node:os";
27440
+ import { dirname as dirname6, join as join15 } from "node:path";
27441
+ function relayedChoicesFilePath(engagementId, home) {
27442
+ if (!isEngagementId(engagementId))
27443
+ return null;
27444
+ return join15(home ?? homedir9(), HALFCYCLE_DIR_NAME, ENGAGEMENTS_DIR_NAME, engagementId, PACING_DIR_NAME, RELAYED_CHOICES_FILENAME);
27445
+ }
27446
+ function readRelayedChoices(engagementId, phase, home) {
27447
+ const path = relayedChoicesFilePath(engagementId, home);
27448
+ if (path === null)
27449
+ return null;
27450
+ try {
27451
+ const parsed = relayedChoicesFileSchema.safeParse(JSON.parse(readFileSync20(path, "utf-8")));
27452
+ return parsed.success && parsed.data.phase === phase ? parsed.data : null;
27453
+ } catch {
27454
+ return null;
27455
+ }
27456
+ }
27457
+ function keptSaid(said) {
27458
+ const cut = said.trim().slice(0, RELAYED_SAID_MAX);
27459
+ const last = cut.charCodeAt(cut.length - 1);
27460
+ return (last >= 55296 && last <= 56319 ? cut.slice(0, -1) : cut).trim();
27461
+ }
27462
+ function recordRelayedChoice(opts) {
27463
+ const target = relayedChoicesFilePath(opts.engagementId, opts.home);
27464
+ if (target === null || !phaseNameSchema.safeParse(opts.phase).success)
27465
+ return false;
27466
+ const said = keptSaid(opts.said);
27467
+ if (said === "")
27468
+ return false;
27469
+ const when = opts.now ?? /* @__PURE__ */ new Date();
27470
+ if (Number.isNaN(when.getTime()))
27471
+ return false;
27472
+ const entry = { at: when.toISOString(), said };
27473
+ const before = readRelayedChoices(opts.engagementId, opts.phase, opts.home);
27474
+ const file2 = relayedChoicesFileSchema.safeParse({
27475
+ phase: opts.phase,
27476
+ entries: [...before?.entries ?? [], entry]
27477
+ });
27478
+ if (!file2.success)
27479
+ return false;
27480
+ const temp = join15(dirname6(target), `.${RELAYED_CHOICES_FILENAME}.${process.pid}.tmp`);
27481
+ try {
27482
+ mkdirSync8(dirname6(target), { recursive: true, mode: 448 });
27483
+ writeFileSync12(temp, `${JSON.stringify(file2.data)}
27484
+ `, { mode: 384 });
27485
+ renameSync10(temp, target);
27486
+ return true;
27487
+ } catch {
27488
+ try {
27489
+ rmSync3(temp, { force: true });
27490
+ } catch {
27491
+ }
27492
+ return false;
27493
+ }
27494
+ }
27495
+
27359
27496
  // dist/pace-verb.js
27360
27497
  var PACE_WINDOW_MINUTES = 300;
27361
27498
  var PACE_BASIS_DEADLINE_MS = 5e3;
27362
27499
  var PACE_FIRST_READ_WAIT_MS = 10 * 60 * 1e3;
27363
27500
  var PACE_LATER_READ_WAIT_MS = 5e3;
27364
27501
  var BUILD_NUMBER_FILENAME = "build.json";
27502
+ var PROJECT_PACE_FILENAME = "project-pace.json";
27365
27503
  function layoutGraph(tasks, agents, medianTaskMinutes) {
27366
27504
  const index = /* @__PURE__ */ new Map();
27367
27505
  tasks.forEach((t2, i) => {
@@ -27479,14 +27617,14 @@ function weekCeilingFor(key, home) {
27479
27617
  function enginePacingDir(engagementId, home) {
27480
27618
  if (!isEngagementId(engagementId))
27481
27619
  return null;
27482
- return nodePath5.join(home ?? homedir9(), HALFCYCLE_DIR_NAME, ENGAGEMENTS_DIR_NAME, engagementId, PACING_DIR_NAME);
27620
+ return nodePath5.join(home ?? homedir10(), HALFCYCLE_DIR_NAME, ENGAGEMENTS_DIR_NAME, engagementId, PACING_DIR_NAME);
27483
27621
  }
27484
27622
  function readBuildNumber(engagementId, home) {
27485
27623
  const dir = enginePacingDir(engagementId, home);
27486
27624
  if (dir === null)
27487
27625
  return null;
27488
27626
  try {
27489
- const v = JSON.parse(readFileSync20(nodePath5.join(dir, BUILD_NUMBER_FILENAME), "utf-8"));
27627
+ const v = JSON.parse(readFileSync21(nodePath5.join(dir, BUILD_NUMBER_FILENAME), "utf-8"));
27490
27628
  const n = v["number"];
27491
27629
  if (typeof v["phase"] === "string" && typeof n === "number" && Number.isInteger(n) && n >= 1 && n <= PACE_AGENTS_MAX && typeof v["namedByOwner"] === "boolean") {
27492
27630
  return { phase: v["phase"], number: n, namedByOwner: v["namedByOwner"] };
@@ -27503,11 +27641,54 @@ function writeBuildNumber(engagementId, value, home) {
27503
27641
  const dir = enginePacingDir(engagementId, home);
27504
27642
  if (dir === null)
27505
27643
  throw new Error("not an engagement id");
27506
- mkdirSync8(dir, { recursive: true, mode: 448 });
27644
+ mkdirSync9(dir, { recursive: true, mode: 448 });
27507
27645
  const path = nodePath5.join(dir, BUILD_NUMBER_FILENAME);
27508
27646
  const tmp = `${path}.${String(process.pid)}.tmp`;
27509
- writeFileSync12(tmp, JSON.stringify(value), { mode: 384 });
27510
- renameSync10(tmp, path);
27647
+ writeFileSync13(tmp, JSON.stringify(value), { mode: 384 });
27648
+ renameSync11(tmp, path);
27649
+ }
27650
+ var NO_PROJECT_PACE = { number: null, relayed: [] };
27651
+ function readProjectPace(engagementId, home) {
27652
+ const dir = enginePacingDir(engagementId, home);
27653
+ if (dir === null)
27654
+ return NO_PROJECT_PACE;
27655
+ try {
27656
+ const v = JSON.parse(readFileSync21(nodePath5.join(dir, PROJECT_PACE_FILENAME), "utf-8"));
27657
+ const n = v["number"];
27658
+ const number4 = typeof n === "number" && Number.isInteger(n) && n >= 1 && n <= PACE_AGENTS_MAX ? n : null;
27659
+ const list = Array.isArray(v["relayed"]) ? v["relayed"] : [];
27660
+ const relayed = list.flatMap((e) => {
27661
+ if (typeof e !== "object" || e === null)
27662
+ return [];
27663
+ const { at, said } = e;
27664
+ return typeof at === "string" && typeof said === "string" ? [{ at, said }] : [];
27665
+ });
27666
+ return { number: number4, relayed };
27667
+ } catch {
27668
+ return NO_PROJECT_PACE;
27669
+ }
27670
+ }
27671
+ function writeProjectPace(engagementId, value, home) {
27672
+ const dir = enginePacingDir(engagementId, home);
27673
+ if (dir === null)
27674
+ throw new Error("not an engagement id");
27675
+ mkdirSync9(dir, { recursive: true, mode: 448 });
27676
+ const path = nodePath5.join(dir, PROJECT_PACE_FILENAME);
27677
+ const tmp = `${path}.${String(process.pid)}.tmp`;
27678
+ writeFileSync13(tmp, JSON.stringify(value), { mode: 384 });
27679
+ renameSync11(tmp, path);
27680
+ }
27681
+ function moveRelayedToPhase(engagementId, phase, project, home) {
27682
+ if (project.relayed.length === 0)
27683
+ return;
27684
+ try {
27685
+ for (const e of project.relayed) {
27686
+ const at = new Date(e.at);
27687
+ recordRelayedChoice({ engagementId, phase, said: e.said, now: Number.isNaN(at.getTime()) ? void 0 : at, home });
27688
+ }
27689
+ writeProjectPace(engagementId, { number: project.number, relayed: [] }, home);
27690
+ } catch {
27691
+ }
27511
27692
  }
27512
27693
  function liveSubAgentsIn(cwd, deps = {}) {
27513
27694
  try {
@@ -27681,7 +27862,8 @@ async function runPaceVerb(args2, deps = {}) {
27681
27862
  let limitFile = null;
27682
27863
  if (parsed.kind === "start") {
27683
27864
  const stored = buildNumberFor(engagementId, parsed.phase, deps.home);
27684
- const kept = stored !== null && stored.namedByOwner ? stored.number : null;
27865
+ const project = readProjectPace(engagementId, deps.home);
27866
+ const kept = stored !== null && stored.namedByOwner ? stored.number : project.number;
27685
27867
  const sameGraph = basis !== null && basis.phase === parsed.phase;
27686
27868
  const suggestFor = (named2) => suggestPace({
27687
27869
  keepFreePercent,
@@ -27701,6 +27883,7 @@ async function runPaceVerb(args2, deps = {}) {
27701
27883
  suggestion = { ...named === null ? suggested : suggestFor(named), ...ceiling };
27702
27884
  number4 = suggestion.number;
27703
27885
  writeBuildNumber(engagementId, { phase: parsed.phase, number: number4, namedByOwner: named !== null }, deps.home);
27886
+ moveRelayedToPhase(engagementId, parsed.phase, project, deps.home);
27704
27887
  const limitBasis = basis === null ? null : { tasks: sameGraph ? basis.tasks : [], medianTaskTokens: basis.medianTaskTokens };
27705
27888
  limitFile = limitAtStart(pacingDir2, parsed.phase, limitBasis, suggestion.minutes, readPhaseUsage(pacingDir2, parsed.phase));
27706
27889
  if (limitFile !== null) {
@@ -27715,7 +27898,7 @@ async function runPaceVerb(args2, deps = {}) {
27715
27898
  }
27716
27899
  } else {
27717
27900
  const stored = basis === null ? readBuildNumber(engagementId, deps.home) : basis.phase === null ? null : buildNumberFor(engagementId, basis.phase, deps.home);
27718
- number4 = stored?.number ?? CAREFUL_START_AGENTS;
27901
+ number4 = stored?.number ?? readProjectPace(engagementId, deps.home).number ?? CAREFUL_START_AGENTS;
27719
27902
  if (basis !== null) {
27720
27903
  limitFile = basis.phase === null ? null : limitInForce(pacingDir2, basis.phase, { tasks: basis.tasks, medianTaskTokens: basis.medianTaskTokens }, readPhaseUsage(pacingDir2, basis.phase));
27721
27904
  } else {
@@ -27785,17 +27968,17 @@ function objectOf2(v) {
27785
27968
  }
27786
27969
  function readJson(path) {
27787
27970
  try {
27788
- return objectOf2(JSON.parse(readFileSync21(path, "utf-8")));
27971
+ return objectOf2(JSON.parse(readFileSync22(path, "utf-8")));
27789
27972
  } catch {
27790
27973
  return null;
27791
27974
  }
27792
27975
  }
27793
27976
  function writeJsonWhole2(path, value) {
27794
- mkdirSync9(nodePath6.dirname(path), { recursive: true, mode: 448 });
27977
+ mkdirSync10(nodePath6.dirname(path), { recursive: true, mode: 448 });
27795
27978
  const tmp = `${path}.${String(process.pid)}.${Math.random().toString(36).slice(2, 10)}.tmp`;
27796
27979
  try {
27797
- writeFileSync13(tmp, JSON.stringify(value), { mode: 384 });
27798
- renameSync11(tmp, path);
27980
+ writeFileSync14(tmp, JSON.stringify(value), { mode: 384 });
27981
+ renameSync12(tmp, path);
27799
27982
  } catch (err) {
27800
27983
  try {
27801
27984
  unlinkSync8(tmp);
@@ -27932,7 +28115,7 @@ async function paceAtTurnEnd(input, reply, deps = {}) {
27932
28115
  }
27933
28116
  const reported = readReportedAccount(key, { home: deps.home });
27934
28117
  const build = buildNumberFor(engagementId, basis.phase, deps.home);
27935
- const number4 = build?.number ?? CAREFUL_START_AGENTS;
28118
+ const number4 = build?.number ?? readProjectPace(engagementId, deps.home).number ?? CAREFUL_START_AGENTS;
27936
28119
  const decision = paceOf({ number: number4, keepFreePercent: basis.keepFreePercent, learned, reported, now });
27937
28120
  const settled2 = {
27938
28121
  pace: decision.pace,
@@ -28104,14 +28287,14 @@ function paceHoldText(input, toolInput, deps = {}) {
28104
28287
 
28105
28288
  // dist/risky/account-names.js
28106
28289
  import { randomBytes } from "node:crypto";
28107
- import { mkdirSync as mkdirSync10, readFileSync as readFileSync23, renameSync as renameSync12, rmSync as rmSync3, statSync as statSync8, writeFileSync as writeFileSync14 } from "node:fs";
28108
- import { homedir as homedir11 } from "node:os";
28109
- import { isAbsolute as isAbsolute5, join as join16, posix as posix2, resolve as resolve7, sep as sep6 } from "node:path";
28290
+ import { mkdirSync as mkdirSync11, readFileSync as readFileSync24, renameSync as renameSync13, rmSync as rmSync4, statSync as statSync8, writeFileSync as writeFileSync15 } from "node:fs";
28291
+ import { homedir as homedir12 } from "node:os";
28292
+ import { isAbsolute as isAbsolute5, join as join17, posix as posix2, resolve as resolve7, sep as sep6 } from "node:path";
28110
28293
 
28111
28294
  // dist/risky/facts.js
28112
- import { existsSync as existsSync8, lstatSync as lstatSync3, readdirSync as readdirSync9, readFileSync as readFileSync22, realpathSync as realpathSync7, statSync as statSync7 } from "node:fs";
28113
- import { homedir as homedir10, tmpdir as tmpdir2 } from "node:os";
28114
- import { basename as pathBasename, dirname as dirname6, isAbsolute as isAbsolute4, join as join15, relative as relative7, resolve as resolve6, sep as sep5 } from "node:path";
28295
+ import { existsSync as existsSync8, lstatSync as lstatSync3, readdirSync as readdirSync9, readFileSync as readFileSync23, realpathSync as realpathSync7, statSync as statSync7 } from "node:fs";
28296
+ import { homedir as homedir11, tmpdir as tmpdir2 } from "node:os";
28297
+ import { basename as pathBasename, dirname as dirname7, isAbsolute as isAbsolute4, join as join16, relative as relative7, resolve as resolve6, sep as sep5 } from "node:path";
28115
28298
 
28116
28299
  // dist/risky/shell.js
28117
28300
  var MAX_DEPTH = 8;
@@ -29412,21 +29595,281 @@ function recogniseSimple(cmd) {
29412
29595
  function programForms(words) {
29413
29596
  const forms = [words];
29414
29597
  const prog = basename3(words[0].text);
29598
+ const args2 = words.slice(1);
29415
29599
  let rest;
29416
29600
  if (prog === "npx" || prog === "bunx" || prog === "pnpx") {
29417
- rest = skipRunnerOptions(words.slice(1));
29601
+ rest = skipRunnerOptions(args2);
29418
29602
  } else if (prog === "npm" || prog === PNPM || prog === "yarn" || prog === "bun") {
29419
- const lead = skipLeading(words.slice(1), MANAGER_VALUE_OPTIONS);
29603
+ const lead = skipLeading(args2, MANAGER_VALUE_OPTIONS);
29420
29604
  const sub = lead[0]?.text;
29421
29605
  if (sub === "exec" || sub === "dlx" || sub === "x")
29422
29606
  rest = skipRunnerOptions(lead.slice(1));
29423
29607
  else if (sub !== void 0 && prog !== "npm")
29424
29608
  rest = lead;
29609
+ } else {
29610
+ const started = PYTHON_PROGRAM.test(prog) ? pythonStarts(args2) : prog === "ruby" ? rubyStarts(args2) : prog === "bundle" ? bundleExecStarts(args2) : prog === "go" ? goRunStarts(args2) : prog === "cargo" ? cargoRunStarts(args2) : prog === "uv" ? uvRunStarts(args2) : prog === DENO ? denoRunStarts(args2) : void 0;
29611
+ if (started !== void 0 && started.length > 0 && startsPublisher(started[0]))
29612
+ rest = started;
29613
+ else if (prog === "uv" && started !== void 0 && started.length > 0) {
29614
+ for (const inner of programForms(started).slice(1))
29615
+ if (startsPublisher(inner[0]))
29616
+ forms.push(inner);
29617
+ }
29425
29618
  }
29426
29619
  if (rest !== void 0 && rest.length > 0)
29427
29620
  forms.push(rest);
29428
29621
  return forms;
29429
29622
  }
29623
+ var DENO = "deno";
29624
+ var UV_VALUE_OPTIONS = /* @__PURE__ */ new Set([
29625
+ "--directory",
29626
+ "--project",
29627
+ "--config-file",
29628
+ "--cache-dir",
29629
+ "--python",
29630
+ "-p",
29631
+ "--with",
29632
+ "--with-editable",
29633
+ "--with-requirements",
29634
+ "--env-file",
29635
+ "--package",
29636
+ "--extra",
29637
+ "--group",
29638
+ "--no-group",
29639
+ "--only-group",
29640
+ "--index",
29641
+ "--default-index",
29642
+ "--index-url",
29643
+ "-i",
29644
+ "--extra-index-url",
29645
+ "--find-links",
29646
+ "-f",
29647
+ "--index-strategy",
29648
+ "--keyring-provider",
29649
+ "--resolution",
29650
+ "--prerelease",
29651
+ "--exclude-newer",
29652
+ "--link-mode",
29653
+ "--color",
29654
+ "--refresh-package",
29655
+ "--reinstall-package",
29656
+ "--upgrade-package",
29657
+ "-P",
29658
+ "--python-preference"
29659
+ ]);
29660
+ function uvRunStarts(args2) {
29661
+ const lead = skipLeading(args2, UV_VALUE_OPTIONS);
29662
+ if (lead[0]?.text !== "run")
29663
+ return void 0;
29664
+ const rest = lead.slice(1);
29665
+ let j = 0;
29666
+ while (j < rest.length) {
29667
+ const t = rest[j].text;
29668
+ if (t === "--")
29669
+ return rest.slice(j + 1);
29670
+ if (t === "-m" || t === "--module")
29671
+ return rest.slice(j + 1);
29672
+ if (!t.startsWith("-"))
29673
+ return rest.slice(j);
29674
+ j += UV_VALUE_OPTIONS.has(t) ? 2 : 1;
29675
+ }
29676
+ return void 0;
29677
+ }
29678
+ var DENO_VALUE_OPTIONS = /* @__PURE__ */ new Set([
29679
+ "-c",
29680
+ "--config",
29681
+ "--import-map",
29682
+ "--lock",
29683
+ "--cert",
29684
+ "--location",
29685
+ "--seed",
29686
+ "--v8-flags"
29687
+ ]);
29688
+ function denoRunStarts(args2) {
29689
+ if (args2[0]?.text !== "run")
29690
+ return void 0;
29691
+ const rest = args2.slice(1);
29692
+ let j = 0;
29693
+ while (j < rest.length) {
29694
+ const t = rest[j].text;
29695
+ if (t === "--") {
29696
+ j++;
29697
+ break;
29698
+ }
29699
+ if (!t.startsWith("-"))
29700
+ break;
29701
+ j += DENO_VALUE_OPTIONS.has(t) ? 2 : 1;
29702
+ }
29703
+ const spec = rest[j];
29704
+ const m = spec === void 0 ? null : /^(?:npm|jsr):(.+)$/.exec(spec.text);
29705
+ if (spec === void 0 || m === null)
29706
+ return void 0;
29707
+ const parts = m[1].split("/").filter((p) => p !== "");
29708
+ const name = parts[0]?.startsWith("@") ? parts[1] : parts[0];
29709
+ const bare = name?.replace(/@.*$/, "");
29710
+ return bare === void 0 || bare === "" ? void 0 : [{ text: bare, literal: spec.literal }, ...rest.slice(j + 1)];
29711
+ }
29712
+ var PYTHON_PROGRAM = /^python(\d+(\.\d+)*)?$/;
29713
+ function startsPublisher(word) {
29714
+ const prog = basename3(word.text);
29715
+ return Object.hasOwn(PUBLISH_SEQUENCES, prog) || Object.hasOwn(DEPLOY_SEQUENCES, prog) || prog === VERCEL;
29716
+ }
29717
+ function readCluster(args2, i, letters) {
29718
+ const word = args2[i];
29719
+ const t = word.text;
29720
+ for (let k = 1; k < t.length; k++) {
29721
+ const c = t[k];
29722
+ if (letters.stop.includes(c))
29723
+ return null;
29724
+ const tail = t.slice(k + 1);
29725
+ if (letters.start.includes(c))
29726
+ return tail !== "" ? [{ text: tail, literal: word.literal }, ...args2.slice(i + 1)] : args2.slice(i + 1);
29727
+ if (letters.value.includes(c))
29728
+ return tail !== "" ? i + 1 : i + 2;
29729
+ if (letters.attached.includes(c))
29730
+ return i + 1;
29731
+ }
29732
+ return i + 1;
29733
+ }
29734
+ function interpreterStarts(args2, letters) {
29735
+ let i = 0;
29736
+ while (i < args2.length) {
29737
+ const t = args2[i].text;
29738
+ if (t === "--")
29739
+ return args2.slice(i + 1);
29740
+ if (t === "-")
29741
+ return void 0;
29742
+ if (!t.startsWith("-"))
29743
+ return args2.slice(i);
29744
+ if (t.startsWith("--")) {
29745
+ i++;
29746
+ continue;
29747
+ }
29748
+ const next = readCluster(args2, i, letters);
29749
+ if (next === null)
29750
+ return void 0;
29751
+ if (typeof next !== "number")
29752
+ return next;
29753
+ i = next;
29754
+ }
29755
+ return void 0;
29756
+ }
29757
+ function pythonStarts(args2) {
29758
+ return interpreterStarts(args2, { stop: "c", start: "m", value: "WX", attached: "" });
29759
+ }
29760
+ function rubyStarts(args2) {
29761
+ return interpreterStarts(args2, { stop: "e", start: "S", value: "IrCE", attached: "0FiKTWx" });
29762
+ }
29763
+ function bundleExecStarts(args2) {
29764
+ const lead = skipLeading(args2, EMPTY);
29765
+ return lead[0]?.text === "exec" ? skipLeading(lead.slice(1), EMPTY) : void 0;
29766
+ }
29767
+ var GO_BUILD_VALUE_FLAGS = /* @__PURE__ */ new Set([
29768
+ "C",
29769
+ "p",
29770
+ "asmflags",
29771
+ "buildmode",
29772
+ "compiler",
29773
+ "coverpkg",
29774
+ "covermode",
29775
+ "exec",
29776
+ "gccgoflags",
29777
+ "gcflags",
29778
+ "installsuffix",
29779
+ "ldflags",
29780
+ "mod",
29781
+ "modfile",
29782
+ "overlay",
29783
+ "pgo",
29784
+ "pkgdir",
29785
+ "tags",
29786
+ "toolexec"
29787
+ ]);
29788
+ function goRunStarts(args2) {
29789
+ if (args2[0]?.text !== "run")
29790
+ return void 0;
29791
+ const rest = args2.slice(1);
29792
+ let i = 0;
29793
+ while (i < rest.length) {
29794
+ const t = rest[i].text;
29795
+ if (t === "--") {
29796
+ i++;
29797
+ break;
29798
+ }
29799
+ if (!t.startsWith("-"))
29800
+ break;
29801
+ const name = t.replace(/^--?/, "");
29802
+ i += !name.includes("=") && GO_BUILD_VALUE_FLAGS.has(name) ? 2 : 1;
29803
+ }
29804
+ const pkg = rest[i];
29805
+ if (pkg === void 0 || pkg.text.startsWith(".") || pkg.text.startsWith("/") || pkg.text.endsWith(".go"))
29806
+ return void 0;
29807
+ const parts = pkg.text.split("@")[0].split("/").filter((p) => p !== "");
29808
+ let last = parts.pop();
29809
+ if (last !== void 0 && /^v\d+$/.test(last) && parts.length > 0)
29810
+ last = parts.pop();
29811
+ return last === void 0 ? void 0 : [{ text: last, literal: pkg.literal }, ...rest.slice(i + 1)];
29812
+ }
29813
+ var CARGO_VALUE_OPTIONS = /* @__PURE__ */ new Set(["-C", "-Z", "--config", "--color"]);
29814
+ var CARGO_RUN_VALUE_OPTIONS = /* @__PURE__ */ new Set([
29815
+ "-F",
29816
+ "--features",
29817
+ "--target",
29818
+ "--target-dir",
29819
+ "--manifest-path",
29820
+ "-j",
29821
+ "--jobs",
29822
+ "--profile",
29823
+ "--color",
29824
+ "--config",
29825
+ "-Z",
29826
+ "--message-format",
29827
+ "--lockfile-path"
29828
+ ]);
29829
+ var CARGO_PROGRAM_OPTIONS = ["--bin", "--example", "-p", "--package"];
29830
+ function cargoRunStarts(args2) {
29831
+ let i = 0;
29832
+ while (i < args2.length) {
29833
+ const t = args2[i].text;
29834
+ if (t === "--" || !(t.startsWith("-") || t.startsWith("+")))
29835
+ break;
29836
+ i += CARGO_VALUE_OPTIONS.has(t) ? 2 : 1;
29837
+ }
29838
+ const sub = args2[i]?.text;
29839
+ if (sub !== "run" && sub !== "r")
29840
+ return void 0;
29841
+ const rest = args2.slice(i + 1);
29842
+ let named;
29843
+ let namedByPackage = false;
29844
+ const passed = [];
29845
+ for (let j = 0; j < rest.length; j++) {
29846
+ const word = rest[j];
29847
+ const t = word.text;
29848
+ if (t === "--") {
29849
+ passed.push(...rest.slice(j + 1));
29850
+ break;
29851
+ }
29852
+ if (!t.startsWith("-")) {
29853
+ passed.push(word);
29854
+ continue;
29855
+ }
29856
+ const eq = t.indexOf("=");
29857
+ const attachedP = /^-p[^=]/.test(t);
29858
+ const name = attachedP ? "-p" : eq < 0 ? t : t.slice(0, eq);
29859
+ if (CARGO_PROGRAM_OPTIONS.includes(name)) {
29860
+ const value = attachedP ? { text: t.slice(2), literal: word.literal } : eq < 0 ? rest[++j] : { text: t.slice(eq + 1), literal: word.literal };
29861
+ const byPackage = name === "-p" || name === "--package";
29862
+ if (value !== void 0 && (named === void 0 || namedByPackage && !byPackage)) {
29863
+ named = value;
29864
+ namedByPackage = byPackage;
29865
+ }
29866
+ continue;
29867
+ }
29868
+ if (eq < 0 && CARGO_RUN_VALUE_OPTIONS.has(name))
29869
+ j++;
29870
+ }
29871
+ return named === void 0 ? void 0 : [named, ...passed];
29872
+ }
29430
29873
  var MANAGER_VALUE_OPTIONS = /* @__PURE__ */ new Set(["--filter", "-F", "-C", "--dir", "--prefix", "--cwd", "--workspace"]);
29431
29874
  function skipLeading(words, valueOptions) {
29432
29875
  let j = 0;
@@ -30065,7 +30508,12 @@ var PUBLISH_SEQUENCES = {
30065
30508
  docker: [["push"]],
30066
30509
  podman: [["push"]],
30067
30510
  vsce: [["publish"]],
30068
- dotnet: [["nuget", "push"]]
30511
+ dotnet: [["nuget", "push"]],
30512
+ rake: [["release"]],
30513
+ flit: [["publish"]],
30514
+ hatch: [["publish"]],
30515
+ pdm: [["publish"]],
30516
+ uv: [["publish"]]
30069
30517
  };
30070
30518
  var DEPLOY_SEQUENCES = {
30071
30519
  netlify: [["deploy"]],
@@ -30075,7 +30523,9 @@ var DEPLOY_SEQUENCES = {
30075
30523
  wrangler: [["deploy"]],
30076
30524
  terraform: [["apply"], ["destroy"]],
30077
30525
  tofu: [["apply"], ["destroy"]],
30078
- kubectl: [["apply"], ["delete"]]
30526
+ kubectl: [["apply"], ["delete"]],
30527
+ cap: [["deploy"]],
30528
+ mkdocs: [["gh-deploy"]]
30079
30529
  };
30080
30530
  function publishMatch(cmd, prog, args2) {
30081
30531
  const seqs = PUBLISH_SEQUENCES[prog];
@@ -30083,11 +30533,13 @@ function publishMatch(cmd, prog, args2) {
30083
30533
  return [];
30084
30534
  if (hasLong(args2, "--dry-run"))
30085
30535
  return [];
30086
- return containsSequence(operandTexts(args2), seqs) ? [{ type: "publish", program: prog, command: cmd }] : [];
30536
+ const words = prog === "uv" ? operandTexts(skipLeading(args2, UV_VALUE_OPTIONS)).slice(0, 1) : operandTexts(args2);
30537
+ return containsSequence(words, seqs) ? [{ type: "publish", program: prog, command: cmd }] : [];
30087
30538
  }
30539
+ var VERCEL = "vercel";
30088
30540
  var PRODUCTION_DEPLOY_OPTIONS = ["--prod", "--production"];
30089
30541
  function deployMatch(cmd, prog, args2) {
30090
- if (prog === "vercel") {
30542
+ if (prog === VERCEL) {
30091
30543
  return hasLong(args2, ...PRODUCTION_DEPLOY_OPTIONS) ? [{ type: "deploy", program: prog, command: cmd }] : [];
30092
30544
  }
30093
30545
  const seqs = DEPLOY_SEQUENCES[prog];
@@ -30323,7 +30775,7 @@ var Env = class {
30323
30775
  constructor(ctx) {
30324
30776
  this.projectDir = realOrSelf(resolve6(ctx.projectDir));
30325
30777
  this.cwd = realOrSelf(resolve6(this.projectDir, ctx.cwd));
30326
- this.home = realOrSelf(resolve6(ctx.home ?? homedir10()));
30778
+ this.home = realOrSelf(resolve6(ctx.home ?? homedir11()));
30327
30779
  this.timeoutMs = ctx.gitTimeoutMs ?? FACTS_GIT_TIMEOUT_MS;
30328
30780
  this.tempDir = realOrSelf(resolve6(ctx.tempDir ?? tmpdir2()));
30329
30781
  const roots = ctx.tempRoots ?? [this.tempDir, tmpdir2(), ...TEMP_ROOTS];
@@ -30347,7 +30799,7 @@ var Env = class {
30347
30799
  return this.description;
30348
30800
  this.description = null;
30349
30801
  try {
30350
- const raw = JSON.parse(readFileSync22(join15(repoRoot, SYSTEM_DESCRIPTION_REL), "utf-8"));
30802
+ const raw = JSON.parse(readFileSync23(join16(repoRoot, SYSTEM_DESCRIPTION_REL), "utf-8"));
30351
30803
  const checked = checkSystemDescription(raw);
30352
30804
  if (checked.ok)
30353
30805
  this.description = checked.description;
@@ -30378,7 +30830,7 @@ var Env = class {
30378
30830
  return false;
30379
30831
  let dir = abs;
30380
30832
  while (!isDirectory(dir)) {
30381
- const up = dirname6(dir);
30833
+ const up = dirname7(dir);
30382
30834
  if (up === dir)
30383
30835
  return false;
30384
30836
  dir = up;
@@ -30411,18 +30863,18 @@ function resolveHome(text, home, base) {
30411
30863
  if (text === "~")
30412
30864
  return home;
30413
30865
  if (text.startsWith("~/"))
30414
- return join15(home, text.slice(2));
30866
+ return join16(home, text.slice(2));
30415
30867
  return resolve6(base, text);
30416
30868
  }
30417
30869
  function realTarget(abs) {
30418
- const parent = dirname6(abs);
30870
+ const parent = dirname7(abs);
30419
30871
  if (parent === abs)
30420
30872
  return abs;
30421
- return join15(realOrSelf(parent), pathBasename(abs));
30873
+ return join16(realOrSelf(parent), pathBasename(abs));
30422
30874
  }
30423
30875
  function wordPath(word, base, env) {
30424
30876
  if (word.temp !== void 0) {
30425
- const root = word.temp.base === "mktemp" ? join15(env.tempDir, ".halfcycle-mktemp-new") : env.tempDir;
30877
+ const root = word.temp.base === "mktemp" ? join16(env.tempDir, ".halfcycle-mktemp-new") : env.tempDir;
30426
30878
  return resolve6(`${root}${word.temp.suffix}`);
30427
30879
  }
30428
30880
  return word.literal ? resolveHome(word.text, env.home, base) : null;
@@ -30568,7 +31020,7 @@ function globPath(abs) {
30568
31020
  const next = [];
30569
31021
  if (!/[*?[]/.test(segment)) {
30570
31022
  for (const c of current)
30571
- next.push(join15(c, segment));
31023
+ next.push(join16(c, segment));
30572
31024
  } else {
30573
31025
  const re = segmentPattern(segment);
30574
31026
  for (const c of current) {
@@ -30582,7 +31034,7 @@ function globPath(abs) {
30582
31034
  if (n.startsWith(".") && !segment.startsWith("."))
30583
31035
  continue;
30584
31036
  if (re.test(n))
30585
- next.push(join15(c, n));
31037
+ next.push(join16(c, n));
30586
31038
  }
30587
31039
  }
30588
31040
  }
@@ -30710,7 +31162,7 @@ function findKeeps(filter, roots, repoRoot) {
30710
31162
  const names = filter.names.map((n) => findPattern(n.pattern, n.fold, false));
30711
31163
  const excluded = filter.excluded.map((p) => findPattern(p, false, true));
30712
31164
  const steps = (rel) => {
30713
- const abs = join15(repoRoot, rel);
31165
+ const abs = join16(repoRoot, rel);
30714
31166
  const out = [];
30715
31167
  for (const r of roots) {
30716
31168
  if (!isInside(abs, r.abs))
@@ -30782,9 +31234,9 @@ function credentialFilesIn(folders, repoRoot, deadline, skip) {
30782
31234
  const out = [];
30783
31235
  const stack = [];
30784
31236
  for (const f of folders) {
30785
- if (isDirectory(join15(repoRoot, f)))
31237
+ if (isDirectory(join16(repoRoot, f)))
30786
31238
  stack.push(f);
30787
- else if (pathExists(join15(repoRoot, f)) && isCredentialPath(f))
31239
+ else if (pathExists(join16(repoRoot, f)) && isCredentialPath(f))
30788
31240
  out.push(f);
30789
31241
  }
30790
31242
  let seen = 0;
@@ -30792,7 +31244,7 @@ function credentialFilesIn(folders, repoRoot, deadline, skip) {
30792
31244
  const rel = stack.pop();
30793
31245
  let entries;
30794
31246
  try {
30795
- entries = readdirSync9(join15(repoRoot, rel), { withFileTypes: true });
31247
+ entries = readdirSync9(join16(repoRoot, rel), { withFileTypes: true });
30796
31248
  } catch {
30797
31249
  continue;
30798
31250
  }
@@ -30905,7 +31357,7 @@ function deleteFilesFacts(m, env) {
30905
31357
  const targets = plainFolderRefused ? inside2.filter((t) => !isDirectory(t)) : inside2;
30906
31358
  if (targets.length === 0)
30907
31359
  return silentDelete(m);
30908
- const repoRoot = env.repoRoot(dirname6(targets[0])) ?? env.repoRoot(env.projectDir);
31360
+ const repoRoot = env.repoRoot(dirname7(targets[0])) ?? env.repoRoot(env.projectDir);
30909
31361
  if (repoRoot === null || !targets.every((t) => isInside(t, repoRoot)))
30910
31362
  return cannot(thingFor([], targets, null, env));
30911
31363
  const keep = m.findFilter === void 0 ? void 0 : findKeeps(m.findFilter, targets.map((t) => ({ abs: t, text: written.get(t) ?? null })), repoRoot);
@@ -30950,7 +31402,7 @@ function closeFoldsAll(targets, env) {
30950
31402
  return false;
30951
31403
  let at = root;
30952
31404
  for (const seg of rel.split("/")) {
30953
- at = join15(at, seg);
31405
+ at = join16(at, seg);
30954
31406
  try {
30955
31407
  if (lstatSync3(at).isSymbolicLink())
30956
31408
  return false;
@@ -31533,7 +31985,7 @@ function publishFacts(m, env) {
31533
31985
  const start = startFolder(m, env);
31534
31986
  if (start !== null && PACKAGE_JSON_PROGRAMS.has(m.program)) {
31535
31987
  try {
31536
- const pkg = JSON.parse(readFileSync22(join15(start, "package.json"), "utf-8"));
31988
+ const pkg = JSON.parse(readFileSync23(join16(start, "package.json"), "utf-8"));
31537
31989
  if (typeof pkg["name"] === "string" && typeof pkg["version"] === "string" && pkg["name"] !== "" && pkg["version"] !== "") {
31538
31990
  name = pkg["name"];
31539
31991
  version2 = pkg["version"];
@@ -31583,7 +32035,7 @@ function shrinkRecordFacts(m, env) {
31583
32035
  try {
31584
32036
  if (!lstatSync3(file2.abs).isFile())
31585
32037
  continue;
31586
- before = readFileSync22(file2.abs, "utf-8");
32038
+ before = readFileSync23(file2.abs, "utf-8");
31587
32039
  } catch {
31588
32040
  continue;
31589
32041
  }
@@ -31635,7 +32087,7 @@ function recordRoot(abs, env) {
31635
32087
  return null;
31636
32088
  let dir = abs;
31637
32089
  while (!isDirectory(dir)) {
31638
- const up = dirname6(dir);
32090
+ const up = dirname7(dir);
31639
32091
  if (up === dir)
31640
32092
  return null;
31641
32093
  dir = up;
@@ -31660,7 +32112,7 @@ function recordFilesUnder(folder2, env) {
31660
32112
  const starts = /* @__PURE__ */ new Set();
31661
32113
  for (const { pattern } of PROJECT_RECORD_PATTERNS) {
31662
32114
  if (relFolder === "" || patternOwns(relFolder, pattern))
31663
- starts.add(join15(root, pattern));
32115
+ starts.add(join16(root, pattern));
31664
32116
  else if (patternOwns(pattern, relFolder))
31665
32117
  starts.add(folder2);
31666
32118
  }
@@ -31686,7 +32138,7 @@ function recordFilesUnder(folder2, env) {
31686
32138
  continue;
31687
32139
  }
31688
32140
  for (const n of names)
31689
- stack.push(join15(p, n));
32141
+ stack.push(join16(p, n));
31690
32142
  } else if (st.isFile()) {
31691
32143
  const r = recordFileAt(p, env);
31692
32144
  if (r !== null)
@@ -31699,7 +32151,7 @@ function recordHistory(files, env) {
31699
32151
  const out = /* @__PURE__ */ new Map();
31700
32152
  const byTree = /* @__PURE__ */ new Map();
31701
32153
  for (const abs of files) {
31702
- const top = env.repoRoot(dirname6(abs));
32154
+ const top = env.repoRoot(dirname7(abs));
31703
32155
  if (top === null || !isInside(abs, top))
31704
32156
  continue;
31705
32157
  const list = byTree.get(top) ?? [];
@@ -31776,9 +32228,9 @@ function isRecord2(v) {
31776
32228
  return typeof v === "object" && v !== null && !Array.isArray(v);
31777
32229
  }
31778
32230
  function placesFor(home, env) {
31779
- const claude = join16(readClaudeConfigFolder(env) ?? home, CLAUDE_SETTINGS_FILENAME);
31780
- const codex = join16(codexHomeOf(env, home), CODEX_SETTINGS_FILENAME);
31781
- return { claude, codex, home, list: join16(home, HALFCYCLE_DIR_NAME, ACCOUNT_NAMES_FILENAME), firstLine: `${claude} ${codex}` };
32231
+ const claude = join17(readClaudeConfigFolder(env) ?? home, CLAUDE_SETTINGS_FILENAME);
32232
+ const codex = join17(codexHomeOf(env, home), CODEX_SETTINGS_FILENAME);
32233
+ return { claude, codex, home, list: join17(home, HALFCYCLE_DIR_NAME, ACCOUNT_NAMES_FILENAME), firstLine: `${claude} ${codex}` };
31782
32234
  }
31783
32235
  function claudeNames(text) {
31784
32236
  const root = JSON.parse(text);
@@ -31818,7 +32270,7 @@ function canonical(stored) {
31818
32270
  function readSource(path, parse3) {
31819
32271
  let text;
31820
32272
  try {
31821
- text = readFileSync23(path, "utf-8");
32273
+ text = readFileSync24(path, "utf-8");
31822
32274
  } catch (e) {
31823
32275
  const code = e.code;
31824
32276
  return { names: [], broken: code !== "ENOENT" && code !== "ENOTDIR" };
@@ -31847,7 +32299,7 @@ function namesFromList(p) {
31847
32299
  }
31848
32300
  let lines2;
31849
32301
  try {
31850
- lines2 = readFileSync23(p.list, "utf-8").split("\n");
32302
+ lines2 = readFileSync24(p.list, "utf-8").split("\n");
31851
32303
  } catch {
31852
32304
  return null;
31853
32305
  }
@@ -31863,13 +32315,13 @@ function writeList(p, names) {
31863
32315
  const lines2 = [p.firstLine, ...comparable.length === names.length ? [] : [ACCOUNT_NAMES_ANY], ...comparable];
31864
32316
  const temp = `${p.list}.${String(process.pid)}.${randomBytes(4).toString("hex")}`;
31865
32317
  try {
31866
- mkdirSync10(join16(p.home, HALFCYCLE_DIR_NAME), { recursive: true, mode: 448 });
31867
- writeFileSync14(temp, `${lines2.join("\n")}
32318
+ mkdirSync11(join17(p.home, HALFCYCLE_DIR_NAME), { recursive: true, mode: 448 });
32319
+ writeFileSync15(temp, `${lines2.join("\n")}
31868
32320
  `, { mode: 384, flag: "wx" });
31869
- renameSync12(temp, p.list);
32321
+ renameSync13(temp, p.list);
31870
32322
  } catch {
31871
32323
  try {
31872
- rmSync3(temp, { force: true });
32324
+ rmSync4(temp, { force: true });
31873
32325
  } catch {
31874
32326
  return;
31875
32327
  }
@@ -31925,7 +32377,7 @@ function sumOf(texts, name) {
31925
32377
  }
31926
32378
  function textOf(path) {
31927
32379
  try {
31928
- return readFileSync23(path, "utf-8");
32380
+ return readFileSync24(path, "utf-8");
31929
32381
  } catch {
31930
32382
  return "";
31931
32383
  }
@@ -32020,7 +32472,7 @@ function accountNamesFacts(calls, context) {
32020
32472
  const writes = calls.filter(readsAccountNames);
32021
32473
  if (writes.length === 0)
32022
32474
  return [];
32023
- const names = readNames(placesFor(context.home ?? homedir11(), context.env ?? process.env));
32475
+ const names = readNames(placesFor(context.home ?? homedir12(), context.env ?? process.env));
32024
32476
  if (names.length === 0)
32025
32477
  return [];
32026
32478
  const perCall = context.gitTimeoutMs ?? FACTS_GIT_TIMEOUT_MS;
@@ -32548,7 +33000,7 @@ function isOneCall(call) {
32548
33000
 
32549
33001
  // dist/risky/allow-once.js
32550
33002
  import { createHash as createHash5 } from "node:crypto";
32551
- import { appendFileSync as appendFileSync5, readFileSync as readFileSync24, realpathSync as realpathSync8, renameSync as renameSync13, statSync as statSync9, unlinkSync as unlinkSync9, writeFileSync as writeFileSync15 } from "node:fs";
33003
+ import { appendFileSync as appendFileSync5, readFileSync as readFileSync25, realpathSync as realpathSync8, renameSync as renameSync14, statSync as statSync9, unlinkSync as unlinkSync9, writeFileSync as writeFileSync16 } from "node:fs";
32552
33004
  import { isAbsolute as isAbsolute6, relative as relative8, resolve as resolve8, sep as sep7 } from "node:path";
32553
33005
  var ALLOW_ONCE_MS = 5 * 60 * 1e3;
32554
33006
  var HELD_STEP_MAX_AGE_MS = 60 * 60 * 1e3;
@@ -32709,7 +33161,7 @@ function numberKept(path, key) {
32709
33161
  function readNumbers(path) {
32710
33162
  let text;
32711
33163
  try {
32712
- text = readFileSync24(path, "utf8");
33164
+ text = readFileSync25(path, "utf8");
32713
33165
  } catch (err) {
32714
33166
  return err.code === "ENOENT" ? { next: 1, steps: /* @__PURE__ */ new Map() } : null;
32715
33167
  }
@@ -32821,13 +33273,13 @@ function distinctSteps(held, numbersPath) {
32821
33273
  function claimHeld(path, session, now) {
32822
33274
  const claimed = `${path}.${process.pid}.${now}.claim`;
32823
33275
  try {
32824
- renameSync13(path, claimed);
33276
+ renameSync14(path, claimed);
32825
33277
  } catch {
32826
33278
  return [];
32827
33279
  }
32828
33280
  let text = "";
32829
33281
  try {
32830
- text = readFileSync24(claimed, "utf8");
33282
+ text = readFileSync25(claimed, "utf8");
32831
33283
  } catch {
32832
33284
  text = "";
32833
33285
  }
@@ -32882,7 +33334,7 @@ function nowOf(deps) {
32882
33334
  }
32883
33335
  function readRecord2(path, session) {
32884
33336
  try {
32885
- return parseRecord(readFileSync24(path, "utf8"), session);
33337
+ return parseRecord(readFileSync25(path, "utf8"), session);
32886
33338
  } catch {
32887
33339
  return null;
32888
33340
  }
@@ -32920,8 +33372,8 @@ function writeRecord(path, record2) {
32920
33372
  function writeWhole2(path, body) {
32921
33373
  const tmp = `${path}.${process.pid}.${Date.now()}.tmp`;
32922
33374
  try {
32923
- writeFileSync15(tmp, body, { mode: 384 });
32924
- renameSync13(tmp, path);
33375
+ writeFileSync16(tmp, body, { mode: 384 });
33376
+ renameSync14(tmp, path);
32925
33377
  return true;
32926
33378
  } catch {
32927
33379
  removeQuietly(tmp);
@@ -33021,8 +33473,8 @@ function decideAutonomous(facts, mayPublishNow) {
33021
33473
  }
33022
33474
 
33023
33475
  // dist/risky/own-copy.js
33024
- import { readFileSync as readFileSync25 } from "node:fs";
33025
- import { join as join17, posix as posix3, win32 as win322 } from "node:path";
33476
+ import { readFileSync as readFileSync26 } from "node:fs";
33477
+ import { join as join18, posix as posix3, win32 as win322 } from "node:path";
33026
33478
  var OWN_COPY_START_TOOL = "Agent";
33027
33479
  var OWN_COPY_WRITE_TOOLS = /* @__PURE__ */ new Set(["Write", "Edit", "MultiEdit", "NotebookEdit"]);
33028
33480
  var OWN_COPY_ISOLATION = "worktree";
@@ -33043,7 +33495,7 @@ function stringField2(o, key) {
33043
33495
  }
33044
33496
  function readOr(deps, path) {
33045
33497
  try {
33046
- return (deps.readFile ?? ((p) => readFileSync25(p, "utf-8")))(path);
33498
+ return (deps.readFile ?? ((p) => readFileSync26(p, "utf-8")))(path);
33047
33499
  } catch {
33048
33500
  return null;
33049
33501
  }
@@ -33083,7 +33535,7 @@ function typeCanChangeFiles(subagentType, deps) {
33083
33535
  return true;
33084
33536
  if (AGENT_FILE_NAME.test(type)) {
33085
33537
  for (const base of [deps.projectDir, deps.home]) {
33086
- const text = readOr(deps, join17(base, ".claude", "agents", `${type}.md`));
33538
+ const text = readOr(deps, join18(base, ".claude", "agents", `${type}.md`));
33087
33539
  if (text === null)
33088
33540
  continue;
33089
33541
  const tools = frontmatterTools(text);
@@ -33187,20 +33639,111 @@ function judgeChange(input, call, deps) {
33187
33639
  };
33188
33640
  }
33189
33641
 
33642
+ // dist/risky/skip-checks.js
33643
+ var SKIP_CHECKS_GIT_SUBCOMMANDS = ["commit", "push"];
33644
+ var GIT_PROGRAM = "git";
33645
+ var NO_VERIFY = "--no-verify";
33646
+ var COMMIT_LONG_VALUE = /* @__PURE__ */ new Set([
33647
+ "--message",
33648
+ "--file",
33649
+ "--template",
33650
+ "--reuse-message",
33651
+ "--reedit-message",
33652
+ "--author",
33653
+ "--date",
33654
+ "--cleanup",
33655
+ "--fixup",
33656
+ "--squash",
33657
+ "--trailer",
33658
+ "--pathspec-from-file"
33659
+ ]);
33660
+ var COMMIT_GRAMMAR = {
33661
+ shortSkip: "n",
33662
+ shortValue: /* @__PURE__ */ new Set(["m", "F", "c", "C", "t", "u", "S"]),
33663
+ shortSeparateValue: /* @__PURE__ */ new Set(["m", "F", "c", "C", "t"]),
33664
+ longValue: COMMIT_LONG_VALUE
33665
+ };
33666
+ var PUSH_LONG_VALUE = /* @__PURE__ */ new Set(["--push-option", "--repo", "--receive-pack", "--exec"]);
33667
+ var PUSH_GRAMMAR = {
33668
+ shortSkip: null,
33669
+ shortValue: /* @__PURE__ */ new Set(["o"]),
33670
+ shortSeparateValue: /* @__PURE__ */ new Set(["o"]),
33671
+ longValue: PUSH_LONG_VALUE
33672
+ };
33673
+ var GRAMMARS = {
33674
+ commit: COMMIT_GRAMMAR,
33675
+ push: PUSH_GRAMMAR
33676
+ };
33677
+ function skipsChecks(command) {
33678
+ const out = [];
33679
+ for (const cmd of splitCommand(command)) {
33680
+ if (cmd.words.length === 0 || basename3(cmd.words[0].text) !== GIT_PROGRAM)
33681
+ continue;
33682
+ const args2 = cmd.words.slice(1);
33683
+ const { at } = gitArgsOf(args2);
33684
+ const sub = args2[at]?.text;
33685
+ const verb = SKIP_CHECKS_GIT_SUBCOMMANDS.find((v) => v === sub);
33686
+ if (verb === void 0)
33687
+ continue;
33688
+ if (carriesSkip(args2.slice(at + 1), GRAMMARS[verb]))
33689
+ out.push({ verb, command: cmd });
33690
+ }
33691
+ return out;
33692
+ }
33693
+ function carriesSkip(rest, grammar) {
33694
+ for (let i = 0; i < rest.length; i++) {
33695
+ const word = rest[i];
33696
+ const t = word.text;
33697
+ if (t === "--")
33698
+ return false;
33699
+ if (t.startsWith("--")) {
33700
+ if (!t.includes("=") && grammar.longValue.has(t)) {
33701
+ i++;
33702
+ continue;
33703
+ }
33704
+ if (hasLongAsGitReads([word], NO_VERIFY))
33705
+ return true;
33706
+ continue;
33707
+ }
33708
+ if (t.length < 2 || !t.startsWith("-"))
33709
+ continue;
33710
+ for (let k = 1; k < t.length; k++) {
33711
+ const letter = t[k];
33712
+ if (letter === grammar.shortSkip)
33713
+ return true;
33714
+ if (grammar.shortValue.has(letter)) {
33715
+ if (k === t.length - 1 && grammar.shortSeparateValue.has(letter))
33716
+ i++;
33717
+ break;
33718
+ }
33719
+ }
33720
+ }
33721
+ return false;
33722
+ }
33723
+ var SKIP_CHECKS_COMMIT_FIX = "Run it again without --no-verify or -n. If a check fails, fix what it found.";
33724
+ var SKIP_CHECKS_PUSH_FIX = "Run it again without --no-verify. If a check fails, fix what it found.";
33725
+ function skipChecksRefusal(match, description) {
33726
+ const what = match.verb === "commit" ? "A commit" : "A push";
33727
+ return {
33728
+ person: `Halfcycle: ${what} was stopped. It would skip this project's own checks, and those always run here. ${reasonSentence(description)} ${NOTHING_WAS_CHANGED}`,
33729
+ agent: match.verb === "commit" ? SKIP_CHECKS_COMMIT_FIX : SKIP_CHECKS_PUSH_FIX
33730
+ };
33731
+ }
33732
+
33190
33733
  // dist/risky/save.js
33191
33734
  import { randomBytes as randomBytes2 } from "node:crypto";
33192
- 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";
33193
- import { homedir as homedir12 } from "node:os";
33194
- import { dirname as dirname7, isAbsolute as isAbsolute7, join as join18, resolve as resolve9 } from "node:path";
33735
+ import { chmodSync as chmodSync2, constants as fsConstants, copyFileSync, lstatSync as lstatSync4, mkdirSync as mkdirSync12, readdirSync as readdirSync10, readFileSync as readFileSync27, readlinkSync, realpathSync as realpathSync9, renameSync as renameSync15, rmSync as rmSync5, statSync as statSync10, symlinkSync, writeFileSync as writeFileSync17 } from "node:fs";
33736
+ import { homedir as homedir13 } from "node:os";
33737
+ import { dirname as dirname8, isAbsolute as isAbsolute7, join as join19, resolve as resolve9 } from "node:path";
33195
33738
  var SAVE_LIMIT_BYTES = 2e8;
33196
33739
  var SAVE_BUDGET_MS = 5e3;
33197
33740
  function notSaved(reason) {
33198
33741
  return { saved: false, reason };
33199
33742
  }
33200
- function savedFolderOf(engagementId, home = homedir12()) {
33743
+ function savedFolderOf(engagementId, home = homedir13()) {
33201
33744
  if (!isEngagementId(engagementId))
33202
33745
  return null;
33203
- return join18(home, HALFCYCLE_DIR_NAME, ENGAGEMENTS_DIR_NAME, engagementId, SAVED_DIR_NAME);
33746
+ return join19(home, HALFCYCLE_DIR_NAME, ENGAGEMENTS_DIR_NAME, engagementId, SAVED_DIR_NAME);
33204
33747
  }
33205
33748
  function saveFirst(staked, ctx) {
33206
33749
  const now = ctx.now ?? Date.now;
@@ -33231,20 +33774,20 @@ function saveFirst(staked, ctx) {
33231
33774
  const id = freshId(folder2, ctx.newId);
33232
33775
  if (id === null)
33233
33776
  return notSaved(AUTONOMY_SAVE_FAILED_REASON);
33234
- saveDir = join18(folder2, id);
33777
+ saveDir = join19(folder2, id);
33235
33778
  const repository = repositoryOf(g);
33236
33779
  if (repository === null)
33237
33780
  return failed(saveDir);
33238
- writeFileSync16(join18(saveDir, SAVED_REPOSITORY_FILE_NAME), `${repository}
33781
+ writeFileSync17(join19(saveDir, SAVED_REPOSITORY_FILE_NAME), `${repository}
33239
33782
  `, { mode: 384 });
33240
- const privateIndex = join18(saveDir, PRIVATE_INDEX_NAME);
33783
+ const privateIndex = join19(saveDir, PRIVATE_INDEX_NAME);
33241
33784
  const commit = saveCommit(id, headCommit2, found2.tracked, plan.lines, privateIndex, g, now());
33242
- rmSync4(privateIndex, { force: true });
33785
+ rmSync5(privateIndex, { force: true });
33243
33786
  if (commit === null)
33244
33787
  return failed(saveDir);
33245
- const copiesDir = join18(saveDir, SAVED_COPIES_DIR_NAME);
33788
+ const copiesDir = join19(saveDir, SAVED_COPIES_DIR_NAME);
33246
33789
  for (const rel of found2.copies) {
33247
- if (now() > deadline || !copyOne(join18(plan.root, rel), join18(copiesDir, rel)))
33790
+ if (now() > deadline || !copyOne(join19(plan.root, rel), join19(copiesDir, rel)))
33248
33791
  return failed(saveDir);
33249
33792
  }
33250
33793
  const description = savedSchema.safeParse({
@@ -33260,10 +33803,10 @@ function saveFirst(staked, ctx) {
33260
33803
  });
33261
33804
  if (!description.success)
33262
33805
  return failed(saveDir);
33263
- const file2 = join18(saveDir, SAVED_FILE_NAME);
33264
- writeFileSync16(`${file2}.part`, `${JSON.stringify(description.data, null, 2)}
33806
+ const file2 = join19(saveDir, SAVED_FILE_NAME);
33807
+ writeFileSync17(`${file2}.part`, `${JSON.stringify(description.data, null, 2)}
33265
33808
  `, { mode: 384 });
33266
- renameSync14(`${file2}.part`, file2);
33809
+ renameSync15(`${file2}.part`, file2);
33267
33810
  ctx.beforeRef?.();
33268
33811
  const ref = g.run(["update-ref", "--no-deref", savedRefOf(id), commit, ""]);
33269
33812
  if (!ref.ok)
@@ -33276,7 +33819,7 @@ function saveFirst(staked, ctx) {
33276
33819
  var PRIVATE_INDEX_NAME = "index.part";
33277
33820
  function failed(saveDir) {
33278
33821
  try {
33279
- rmSync4(saveDir, { recursive: true, force: true });
33822
+ rmSync5(saveDir, { recursive: true, force: true });
33280
33823
  } catch {
33281
33824
  }
33282
33825
  return notSaved(AUTONOMY_SAVE_FAILED_REASON);
@@ -33340,7 +33883,7 @@ function filesOf(plan, g, deadline) {
33340
33883
  trackedAll.add(path);
33341
33884
  if (!reached(path, plan.specs))
33342
33885
  continue;
33343
- const st = lstatOrNull(join18(plan.root, path));
33886
+ const st = lstatOrNull(join19(plan.root, path));
33344
33887
  if (st !== null && (st.isFile() || st.isSymbolicLink()))
33345
33888
  tracked.add(path);
33346
33889
  }
@@ -33352,7 +33895,7 @@ function filesOf(plan, g, deadline) {
33352
33895
  const rel = stack.pop();
33353
33896
  if (rel === ".git")
33354
33897
  continue;
33355
- const abs = rel === "." ? plan.root : join18(plan.root, rel);
33898
+ const abs = rel === "." ? plan.root : join19(plan.root, rel);
33356
33899
  const st = lstatOrNull(abs);
33357
33900
  if (st === null)
33358
33901
  continue;
@@ -33383,7 +33926,7 @@ function copyOne(src, dst) {
33383
33926
  const st = lstatOrNull(src);
33384
33927
  if (st === null)
33385
33928
  return false;
33386
- mkdirSync11(dirname7(dst), { recursive: true, mode: 448 });
33929
+ mkdirSync12(dirname8(dst), { recursive: true, mode: 448 });
33387
33930
  if (st.isSymbolicLink()) {
33388
33931
  symlinkSync(readlinkSync(src), dst);
33389
33932
  return true;
@@ -33459,13 +34002,13 @@ var SaveGit = class {
33459
34002
  }
33460
34003
  };
33461
34004
  function freshId(folder2, newId) {
33462
- mkdirSync11(folder2, { recursive: true, mode: 448 });
34005
+ mkdirSync12(folder2, { recursive: true, mode: 448 });
33463
34006
  for (let i = 0; i < 5; i++) {
33464
34007
  const id = newId?.() ?? defaultId();
33465
34008
  if (!savedIdSchema.safeParse(id).success)
33466
34009
  return null;
33467
34010
  try {
33468
- mkdirSync11(join18(folder2, id), { mode: 448 });
34011
+ mkdirSync12(join19(folder2, id), { mode: 448 });
33469
34012
  return id;
33470
34013
  } catch (e) {
33471
34014
  if (e.code !== "EEXIST")
@@ -33490,7 +34033,7 @@ function repositoryOf(g) {
33490
34033
  }
33491
34034
  function markedRepository(dir) {
33492
34035
  try {
33493
- const text = readFileSync26(join18(dir, SAVED_REPOSITORY_FILE_NAME), "utf-8").trim();
34036
+ const text = readFileSync27(join19(dir, SAVED_REPOSITORY_FILE_NAME), "utf-8").trim();
33494
34037
  return text === "" ? null : text;
33495
34038
  } catch {
33496
34039
  return null;
@@ -33524,7 +34067,7 @@ function pruneSaves(root, engagementId, ctx = {}) {
33524
34067
  if (!savedIdSchema.safeParse(id).success)
33525
34068
  continue;
33526
34069
  here.add(id);
33527
- const dir = join18(folder2, id);
34070
+ const dir = join19(folder2, id);
33528
34071
  const st = lstatOrNull(dir);
33529
34072
  const answer = savedPruneAnswer({
33530
34073
  refMadeAt: refs.get(id) ?? null,
@@ -33532,9 +34075,9 @@ function pruneSaves(root, engagementId, ctx = {}) {
33532
34075
  folderRepository: markedRepository(dir)
33533
34076
  }, { repository, now: now() });
33534
34077
  if (answer === "remove-folder")
33535
- rmSync4(dir, { recursive: true, force: true });
34078
+ rmSync5(dir, { recursive: true, force: true });
33536
34079
  else if (answer === "remove-ref-then-folder" && g.run(["update-ref", "-d", savedRefOf(id)]).ok) {
33537
- rmSync4(dir, { recursive: true, force: true });
34080
+ rmSync5(dir, { recursive: true, force: true });
33538
34081
  } else if (answer === "remove-ref")
33539
34082
  g.run(["update-ref", "-d", savedRefOf(id)]);
33540
34083
  }
@@ -33563,19 +34106,19 @@ function realOrSelf3(p) {
33563
34106
 
33564
34107
  // dist/autonomy-state.js
33565
34108
  import { spawnSync as spawnSync2 } from "node:child_process";
33566
- import { chmodSync as chmodSync3, mkdirSync as mkdirSync12, readFileSync as readFileSync27, renameSync as renameSync15, rmSync as rmSync5, writeFileSync as writeFileSync17 } from "node:fs";
33567
- import { homedir as homedir13, platform as platform2 } from "node:os";
33568
- import { dirname as dirname8, join as join19 } from "node:path";
34109
+ import { chmodSync as chmodSync3, mkdirSync as mkdirSync13, readFileSync as readFileSync28, renameSync as renameSync16, rmSync as rmSync6, writeFileSync as writeFileSync18 } from "node:fs";
34110
+ import { homedir as homedir14, platform as platform2 } from "node:os";
34111
+ import { dirname as dirname9, join as join20 } from "node:path";
33569
34112
  var AUTONOMY_STATE_FILENAME = "autonomy.json";
33570
34113
  var AUTONOMY_STATE_DEADLINE_MS = 1500;
33571
34114
  function autonomyStateFilePath(engagementId, home) {
33572
- return join19(home ?? homedir13(), HALFCYCLE_DIR_NAME, ENGAGEMENTS_DIR_NAME, engagementId, AUTONOMY_STATE_FILENAME);
34115
+ return join20(home ?? homedir14(), HALFCYCLE_DIR_NAME, ENGAGEMENTS_DIR_NAME, engagementId, AUTONOMY_STATE_FILENAME);
33573
34116
  }
33574
34117
  function readCachedAutonomy(engagementId, nowMs, home) {
33575
34118
  if (!isEngagementId(engagementId))
33576
34119
  return null;
33577
34120
  try {
33578
- const parsed = autonomyStateFileSchema.safeParse(JSON.parse(readFileSync27(autonomyStateFilePath(engagementId, home), "utf-8")));
34121
+ const parsed = autonomyStateFileSchema.safeParse(JSON.parse(readFileSync28(autonomyStateFilePath(engagementId, home), "utf-8")));
33579
34122
  if (!parsed.success || !parsed.data.on)
33580
34123
  return null;
33581
34124
  const age = nowMs - Date.parse(parsed.data.readAt);
@@ -33616,24 +34159,24 @@ var LIVE_READ_PROGRAM = [
33616
34159
  ].join("");
33617
34160
  function deleteAutonomyStateFile(engagementId, home) {
33618
34161
  try {
33619
- rmSync5(autonomyStateFilePath(engagementId, home), { force: true });
34162
+ rmSync6(autonomyStateFilePath(engagementId, home), { force: true });
33620
34163
  } catch {
33621
34164
  }
33622
34165
  }
33623
34166
  function writeAutonomyStateFile(engagementId, file2, home) {
33624
34167
  const target = autonomyStateFilePath(engagementId, home);
33625
- const temp = join19(dirname8(target), `.${AUTONOMY_STATE_FILENAME}.${process.pid}.tmp`);
34168
+ const temp = join20(dirname9(target), `.${AUTONOMY_STATE_FILENAME}.${process.pid}.tmp`);
33626
34169
  try {
33627
- mkdirSync12(dirname8(target), { recursive: true, mode: 448 });
33628
- writeFileSync17(temp, `${JSON.stringify(file2)}
34170
+ mkdirSync13(dirname9(target), { recursive: true, mode: 448 });
34171
+ writeFileSync18(temp, `${JSON.stringify(file2)}
33629
34172
  `, { mode: 384 });
33630
34173
  if (platform2() !== "win32")
33631
34174
  chmodSync3(temp, 384);
33632
- renameSync15(temp, target);
34175
+ renameSync16(temp, target);
33633
34176
  return true;
33634
34177
  } catch {
33635
34178
  try {
33636
- rmSync5(temp, { force: true });
34179
+ rmSync6(temp, { force: true });
33637
34180
  } catch {
33638
34181
  }
33639
34182
  return false;
@@ -33756,7 +34299,7 @@ function shellWord(text) {
33756
34299
 
33757
34300
  // dist/hooks/pre-tool-use.js
33758
34301
  import { performance } from "node:perf_hooks";
33759
- import { homedir as homedir14 } from "node:os";
34302
+ import { homedir as homedir15 } from "node:os";
33760
34303
  var UNREADABLE_INPUT_NOTICE = "Halfcycle could not read what the coding agent was about to do, so that step went ahead without being checked for anything that cannot be undone. If this keeps happening, update Halfcycle.";
33761
34304
  var PRE_TOOL_USE_LIMIT_MS = (HALFCYCLE_HOOK_WIRING.find((e) => e.event === "PreToolUse")?.timeout ?? 10) * 1e3;
33762
34305
  var AUTONOMY_REPLY_MARGIN_MS = 1e3;
@@ -33789,6 +34332,9 @@ function runPreToolUse(deps) {
33789
34332
  if (calls === null || !calls.every((c) => CHECKED.has(c.toolName)))
33790
34333
  return unreadable(deps, input);
33791
34334
  const call = calls[0] ?? { toolName: stringOf(input, "tool_name") ?? "", toolInput: {} };
34335
+ const skipped = skippedChecksReply(calls);
34336
+ if (skipped !== null)
34337
+ return skipped;
33792
34338
  let answer;
33793
34339
  try {
33794
34340
  answer = riskyAnswer(calls, () => {
@@ -33859,6 +34405,24 @@ function runPreToolUse(deps) {
33859
34405
  return "";
33860
34406
  }
33861
34407
  }
34408
+ function skippedChecksReply(calls) {
34409
+ try {
34410
+ for (const c of calls) {
34411
+ const command = c.toolName === "Bash" ? c.toolInput["command"] : void 0;
34412
+ if (typeof command !== "string")
34413
+ continue;
34414
+ const match = skipsChecks(command)[0];
34415
+ if (match === void 0)
34416
+ continue;
34417
+ const text = skipChecksRefusal(match, reasonOf(c));
34418
+ return `${preToolUseReplyText("refuse", text.person, text.agent)}
34419
+ `;
34420
+ }
34421
+ } catch {
34422
+ return null;
34423
+ }
34424
+ return null;
34425
+ }
33862
34426
  function clockOf(deps) {
33863
34427
  return deps.now ?? (() => /* @__PURE__ */ new Date());
33864
34428
  }
@@ -33926,7 +34490,7 @@ function autonomousAnswer(call, staked, sendsSaves, mode, autonomy, deps) {
33926
34490
  });
33927
34491
  if (!saved.saved)
33928
34492
  return refuse(autonomyA12(typeSentenceFor(saved.reason === AUTONOMY_COULD_NOT_TELL_REASON ? [] : staked), saved.reason));
33929
- return `${preToolUseSavedText(autonomyA9(what, saved.id))}
34493
+ return `${preToolUseSavedText(autonomyA9(what, saved.id, installedVersion(deps.env ?? process.env)))}
33930
34494
  `;
33931
34495
  }
33932
34496
  default: {
@@ -33983,7 +34547,7 @@ function ownCopyOf(input, call, deps) {
33983
34547
  const walk2 = deps.walk ?? deps.liveness?.walk;
33984
34548
  return judgeOwnCopy(input, call, {
33985
34549
  projectDir: projectDirOf(deps),
33986
- home: deps.home ?? homedir14(),
34550
+ home: deps.home ?? homedir15(),
33987
34551
  ...walk2 === void 0 ? {} : { walk: walk2 },
33988
34552
  ...deps.liveness === void 0 ? {} : { liveness: deps.liveness }
33989
34553
  });
@@ -34017,7 +34581,7 @@ function unreadable(deps, input) {
34017
34581
 
34018
34582
  // dist/hooks/session-diff.js
34019
34583
  import { unlinkSync as unlinkSync10 } from "node:fs";
34020
- import { join as join20 } from "node:path";
34584
+ import { join as join21 } from "node:path";
34021
34585
  var STOP_HOOK_EVENT = {
34022
34586
  stop: "Stop",
34023
34587
  "subagent-stop": "SubagentStop"
@@ -34082,7 +34646,7 @@ async function evaluateSession(input, kind, reply, readSeat, profile) {
34082
34646
  const candidates = ownEditedFiles(repoRoot, sessionId).filter((relPath) => !tracked.has(relPath));
34083
34647
  const untracked = getUntrackedFiles(repoRoot, candidates);
34084
34648
  const created = untracked.ok ? untracked.value.filter((relPath) => !tracked.has(relPath)) : [];
34085
- const getDiff = (relPath) => tracked.has(relPath) ? collected.getDiff(relPath) : getSingleFileDiff(join20(repoRoot, relPath), repoRoot);
34649
+ const getDiff = (relPath) => tracked.has(relPath) ? collected.getDiff(relPath) : getSingleFileDiff(join21(repoRoot, relPath), repoRoot);
34086
34650
  const { evaluated } = partitionInstallerOutput(collected.files);
34087
34651
  const { evaluated: createdEvaluated } = partitionInstallerOutput(created);
34088
34652
  const changedFiles2 = [...ownChangedFiles(repoRoot, evaluated, sessionId), ...createdEvaluated];
@@ -34293,65 +34857,6 @@ async function runStop(observeDeps = {}, timeDeps = {}, paceDeps = {}, profile =
34293
34857
  }, profile);
34294
34858
  }
34295
34859
 
34296
- // dist/relayed-choices.js
34297
- import { mkdirSync as mkdirSync13, readFileSync as readFileSync28, renameSync as renameSync16, rmSync as rmSync6, writeFileSync as writeFileSync18 } from "node:fs";
34298
- import { homedir as homedir15 } from "node:os";
34299
- import { dirname as dirname9, join as join21 } from "node:path";
34300
- function relayedChoicesFilePath(engagementId, home) {
34301
- if (!isEngagementId(engagementId))
34302
- return null;
34303
- return join21(home ?? homedir15(), HALFCYCLE_DIR_NAME, ENGAGEMENTS_DIR_NAME, engagementId, PACING_DIR_NAME, RELAYED_CHOICES_FILENAME);
34304
- }
34305
- function readRelayedChoices(engagementId, phase, home) {
34306
- const path = relayedChoicesFilePath(engagementId, home);
34307
- if (path === null)
34308
- return null;
34309
- try {
34310
- const parsed = relayedChoicesFileSchema.safeParse(JSON.parse(readFileSync28(path, "utf-8")));
34311
- return parsed.success && parsed.data.phase === phase ? parsed.data : null;
34312
- } catch {
34313
- return null;
34314
- }
34315
- }
34316
- function keptSaid(said) {
34317
- const cut = said.trim().slice(0, RELAYED_SAID_MAX);
34318
- const last = cut.charCodeAt(cut.length - 1);
34319
- return (last >= 55296 && last <= 56319 ? cut.slice(0, -1) : cut).trim();
34320
- }
34321
- function recordRelayedChoice(opts) {
34322
- const target = relayedChoicesFilePath(opts.engagementId, opts.home);
34323
- if (target === null || !phaseNameSchema.safeParse(opts.phase).success)
34324
- return false;
34325
- const said = keptSaid(opts.said);
34326
- if (said === "")
34327
- return false;
34328
- const when = opts.now ?? /* @__PURE__ */ new Date();
34329
- if (Number.isNaN(when.getTime()))
34330
- return false;
34331
- const entry = { at: when.toISOString(), said };
34332
- const before = readRelayedChoices(opts.engagementId, opts.phase, opts.home);
34333
- const file2 = relayedChoicesFileSchema.safeParse({
34334
- phase: opts.phase,
34335
- entries: [...before?.entries ?? [], entry]
34336
- });
34337
- if (!file2.success)
34338
- return false;
34339
- const temp = join21(dirname9(target), `.${RELAYED_CHOICES_FILENAME}.${process.pid}.tmp`);
34340
- try {
34341
- mkdirSync13(dirname9(target), { recursive: true, mode: 448 });
34342
- writeFileSync18(temp, `${JSON.stringify(file2.data)}
34343
- `, { mode: 384 });
34344
- renameSync16(temp, target);
34345
- return true;
34346
- } catch {
34347
- try {
34348
- rmSync6(temp, { force: true });
34349
- } catch {
34350
- }
34351
- return false;
34352
- }
34353
- }
34354
-
34355
34860
  // dist/hooks/scope-phrase.js
34356
34861
  var APPROVE_PREFIX = /^approve /i;
34357
34862
  var SEND_BACK_PREFIX = /^send back /i;
@@ -34449,21 +34954,61 @@ function autonomyPhraseShape(prompt) {
34449
34954
  }
34450
34955
 
34451
34956
  // dist/hooks/pace-phrase.js
34452
- var FASTER = /^(?:go )?faster$/i;
34453
- var AGENTS = /^run ([0-9]{1,3}) agents?$/i;
34957
+ var NUMBER_WORDS = [
34958
+ "one",
34959
+ "two",
34960
+ "three",
34961
+ "four",
34962
+ "five",
34963
+ "six",
34964
+ "seven",
34965
+ "eight",
34966
+ "nine",
34967
+ "ten",
34968
+ "eleven",
34969
+ "twelve",
34970
+ "thirteen",
34971
+ "fourteen",
34972
+ "fifteen",
34973
+ "sixteen",
34974
+ "seventeen",
34975
+ "eighteen",
34976
+ "nineteen",
34977
+ "twenty"
34978
+ ];
34979
+ var POLITE = /^(?:please,? )?(.*?)(?:,? please)?[.!]*$/i;
34980
+ var VERB = "(?:(?:run|use|start)(?: with)? )?";
34981
+ var QUALIFIER = "(?: (?:at once|at a time|in parallel|concurrently|simultaneously|together))?";
34982
+ var FASTER = /^(?:(?:go )?faster|(?:run )?more agents)$/i;
34983
+ var AGENTS = new RegExp(`^${VERB}(?:up to )?(-?[0-9]+(?:\\.[0-9]+)?|${NUMBER_WORDS.join("|")}) agents?${QUALIFIER}$`, "i");
34984
+ var MAX_PACE = /^(?:(?:go|run)(?: at)? )?(?:max|maximum|full|top) (?:pace|speed)$/i;
34985
+ var MAX_AGENTS = new RegExp(`^${VERB}(?:the )?(?:max|maximum)(?: possible)?(?: number of)? agents${QUALIFIER}$`, "i");
34986
+ var AS_MANY = new RegExp(`^${VERB}as many agents as (?:possible|you can)${QUALIFIER}$`, "i");
34987
+ var VAGUE = new RegExp(`^${VERB}(?:lots of|a lot of|loads of|plenty of|many|some|several|a few|a bunch of) agents${QUALIFIER}$`, "i");
34454
34988
  var KEEP_FREE = /^keep ([0-9]{1,3})% free$/i;
34455
34989
  var USE_UP_TO = /^use up to ([0-9]{1,3})%$/i;
34990
+ function countOf(said) {
34991
+ const word = NUMBER_WORDS.indexOf(said.toLowerCase());
34992
+ return word >= 0 ? word + 1 : Number(said);
34993
+ }
34456
34994
  function pacePhraseShape(prompt) {
34457
34995
  if (typeof prompt !== "string")
34458
34996
  return null;
34459
- const line2 = prompt.trim();
34460
- if (LINE_BREAK.test(line2))
34997
+ const trimmed = prompt.trim();
34998
+ if (trimmed === "" || LINE_BREAK.test(trimmed))
34999
+ return null;
35000
+ const line2 = POLITE.exec(trimmed.replace(/ +/g, " "))?.[1] ?? "";
35001
+ if (line2 === "")
34461
35002
  return null;
34462
35003
  if (FASTER.test(line2))
34463
35004
  return { kind: "faster" };
34464
35005
  const agents = AGENTS.exec(line2);
34465
35006
  if (agents !== null)
34466
- return { kind: "agents", count: Number(agents[1]) };
35007
+ return { kind: "agents", count: countOf(agents[1]) };
35008
+ if (MAX_PACE.test(line2) || MAX_AGENTS.test(line2) || AS_MANY.test(line2))
35009
+ return { kind: "max" };
35010
+ if (VAGUE.test(line2))
35011
+ return { kind: "vague" };
34467
35012
  const keep = KEEP_FREE.exec(line2);
34468
35013
  if (keep !== null)
34469
35014
  return { kind: "keep-free", percent: Number(keep[1]) };
@@ -34661,7 +35206,7 @@ function nothingDone(act) {
34661
35206
  return act === "approve" ? "Nothing was approved" : "Nothing was sent back";
34662
35207
  }
34663
35208
  function showCommand(phase) {
34664
- return `\`${HALFCYCLE_COMMAND} plan ${quoteForShell(phase)} --show\``;
35209
+ return `\`${halfcycleCommand(installedVersion())} plan ${quoteForShell(phase)} --show\``;
34665
35210
  }
34666
35211
  function recordedLine(act, phase, planVersion) {
34667
35212
  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 +35215,13 @@ function notAttendedLine(act) {
34670
35215
  return line(`${nothingDone(act)}: approving or sending back by typing counts only when a person types it into an open session.`);
34671
35216
  }
34672
35217
  function notSignedInLine(act) {
34673
- return line(`${nothingDone(act)}: this project is not signed in on this computer. ${HOOK_REMEDY}`);
35218
+ return line(`${nothingDone(act)}: this project is not signed in on this computer. ${hookRemedy(installedVersion())}`);
34674
35219
  }
34675
35220
  function notOwnerLine(act) {
34676
35221
  return line(`${nothingDone(act)}: only the project's owner can approve or send back this scope.`);
34677
35222
  }
34678
35223
  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.`);
35224
+ 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
35225
  }
34681
35226
  function namedPhases(phases) {
34682
35227
  const quoted2 = phases.map((phase) => `"${phase}"`);
@@ -34701,7 +35246,7 @@ function noteRefusedLine() {
34701
35246
  return line("Nothing was sent back: the note was not accepted. Reword it in plain words and type the send-back again.");
34702
35247
  }
34703
35248
  function approveRefusedLine() {
34704
- return line(`${nothingDone("approve")}: Halfcycle did not accept the request. ${CLIENT_UPDATE_SENTENCE}`);
35249
+ return line(`${nothingDone("approve")}: Halfcycle did not accept the request. ${clientUpdateSentence(installedVersion())}`);
34705
35250
  }
34706
35251
  function cannotReachLine(act) {
34707
35252
  return line(`${nothingDone(act)}: Halfcycle could not be reached just now. Type the line again in a minute.`);
@@ -34742,7 +35287,7 @@ function reviewNotAttendedLine() {
34742
35287
  return line(`${NOTHING_RECORDED}: reviewing by typing counts only when a person types it into an open session.`);
34743
35288
  }
34744
35289
  function reviewNotSignedInLine() {
34745
- return line(`${NOTHING_RECORDED}: this project is not signed in on this computer. ${HOOK_REMEDY}`);
35290
+ return line(`${NOTHING_RECORDED}: this project is not signed in on this computer. ${hookRemedy(installedVersion())}`);
34746
35291
  }
34747
35292
  function reviewCannotReachLine() {
34748
35293
  return line(`${NOTHING_RECORDED}: Halfcycle could not be reached just now. Type the line again in a minute.`);
@@ -34838,7 +35383,7 @@ function reviewCloseRefusedLine(phase, steps) {
34838
35383
  return line([head, ...steps.map(unfinishedStepLine), "Then type the line again."].join(" "));
34839
35384
  }
34840
35385
  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.`);
35386
+ 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
35387
  }
34843
35388
  function stillWaitingOf(phase, waiting) {
34844
35389
  const named = namedPhase(phase);
@@ -34955,7 +35500,7 @@ function autonomyNotAttendedLine() {
34955
35500
  return line(`${AUTONOMY_NOTHING_CHANGED}: turning this on or off by typing counts only when a person types it into an open session.`);
34956
35501
  }
34957
35502
  function autonomyNotSignedInLine() {
34958
- return line(`${AUTONOMY_NOTHING_CHANGED}: this project is not signed in on this computer. ${HOOK_REMEDY}`);
35503
+ return line(`${AUTONOMY_NOTHING_CHANGED}: this project is not signed in on this computer. ${hookRemedy(installedVersion())}`);
34959
35504
  }
34960
35505
  function autonomyNotOwnerLine() {
34961
35506
  return line(`${AUTONOMY_NOTHING_CHANGED}: only the project's owner can turn this on or off.`);
@@ -35037,7 +35582,7 @@ function waitNotAttendedLine() {
35037
35582
  return line(`${NOTHING_RECORDED}: an answer counts only when a person types it into an open session.`);
35038
35583
  }
35039
35584
  function waitNotSignedInLine() {
35040
- return line(`${NOTHING_RECORDED}: this project is not signed in on this computer. ${HOOK_REMEDY}`);
35585
+ return line(`${NOTHING_RECORDED}: this project is not signed in on this computer. ${hookRemedy(installedVersion())}`);
35041
35586
  }
35042
35587
  function waitNotOwnerLine() {
35043
35588
  return line(`${NOTHING_RECORDED}: only the project's owner can answer a decision that waits for them.`);
@@ -35094,10 +35639,15 @@ var PACE_NOTHING_CHANGED_OPENING = "Nothing changed";
35094
35639
  function paceAnswerLine(text, _agent) {
35095
35640
  return codexLine(text);
35096
35641
  }
35097
- function paceNumberDoneText(n, more) {
35098
- const head = `Done. This build now runs up to ${paceAgentsOf(n)} at once.`;
35642
+ function paceNumberDoneText(n, more, running) {
35643
+ const when = running ? "starting now and in every later build" : "from the next build on";
35644
+ const most = n >= PACE_AGENTS_MAX ? " That is the most Halfcycle runs." : "";
35645
+ const head = `Done. This project now runs up to ${paceAgentsOf(n)} at once, ${when}.${most}`;
35099
35646
  return more ? `${head} This uses your allowance faster.` : head;
35100
35647
  }
35648
+ function paceVagueText() {
35649
+ return `${PACE_NOTHING_CHANGED_OPENING}: say how many agents to run at once, from 1 to ${String(PACE_AGENTS_MAX)}, like "run 5 agents", or say "max pace".`;
35650
+ }
35101
35651
  function paceKeepFreeDoneText(percent) {
35102
35652
  if (percent === 0)
35103
35653
  return PACE_P5;
@@ -35109,7 +35659,7 @@ function paceUseUpToDoneText(percent) {
35109
35659
  return `Done. This build may use up to ${String(percent)}% of each usage window.`;
35110
35660
  }
35111
35661
  function paceNoBuildText() {
35112
- return `${PACE_NOTHING_CHANGED_OPENING}: no build is running in this project yet. Start one first, then say this.`;
35662
+ return `${PACE_NOTHING_CHANGED_OPENING}: no build is running in this project yet, so there is no pace to raise. Say a number instead, like "run 5 agents", or say "max pace".`;
35113
35663
  }
35114
35664
  function paceAgentsRangeText() {
35115
35665
  return `${PACE_NOTHING_CHANGED_OPENING}: run between 1 and ${String(PACE_AGENTS_MAX)} agents at once.`;
@@ -35124,7 +35674,7 @@ function paceNotAttendedText() {
35124
35674
  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
35675
  }
35126
35676
  function paceNotSignedInText() {
35127
- return `${PACE_NOTHING_CHANGED_OPENING}: this project is not signed in on this computer. ${HOOK_REMEDY}`;
35677
+ return `${PACE_NOTHING_CHANGED_OPENING}: this project is not signed in on this computer. ${hookRemedy(installedVersion())}`;
35128
35678
  }
35129
35679
  function paceNotOwnerText() {
35130
35680
  return `${PACE_NOTHING_CHANGED_OPENING}: only the project's owner can change this.`;
@@ -35350,30 +35900,51 @@ async function decidePace(shape, opts) {
35350
35900
  const say = (text) => paceAnswerLine(text, agent);
35351
35901
  const env = opts.env ?? process.env;
35352
35902
  const typed = whoTyped(opts);
35903
+ if (shape.kind === "vague")
35904
+ return say(paceVagueText());
35353
35905
  const read = readConfig(env);
35354
35906
  if (read.config === null) {
35355
35907
  return say(readNeverConfiguredReason(env) === "not-this-account" ? paceNotOwnerText() : paceNotSignedInText());
35356
35908
  }
35357
35909
  const { guardServiceToken, guardEngagementId } = read.config;
35358
35910
  const dir = enginePacingDir(guardEngagementId, opts.home);
35911
+ if (dir === null)
35912
+ return say(paceNotSignedInText());
35359
35913
  const build = readBuildNumber(guardEngagementId, opts.home);
35360
- const phase = dir === null ? null : readPaceStateFile(dir).basis?.phase ?? null;
35361
- if (dir === null || build === null || phase === null || build.phase !== phase)
35362
- return say(paceNoBuildText());
35914
+ const phase = readPaceStateFile(dir).basis?.phase ?? null;
35915
+ const running = build !== null && phase !== null && build.phase === phase ? { build, phase } : null;
35916
+ const project = readProjectPace(guardEngagementId, opts.home);
35917
+ const at = new Date((opts.now ?? Date.now)());
35363
35918
  const said = String(promptOf(opts.input)).trim();
35364
- if (shape.kind === "faster" || shape.kind === "agents") {
35365
- const number4 = shape.kind === "faster" ? build.number + 1 : shape.count;
35366
- if (number4 < 1 || number4 > PACE_AGENTS_MAX)
35919
+ const relay = () => {
35920
+ if (running !== null) {
35921
+ recordRelayedChoice({ engagementId: guardEngagementId, phase: running.phase, said, now: at, home: opts.home });
35922
+ return;
35923
+ }
35924
+ try {
35925
+ const held = readProjectPace(guardEngagementId, opts.home);
35926
+ writeProjectPace(guardEngagementId, { ...held, relayed: [...held.relayed, { at: at.toISOString(), said }] }, opts.home);
35927
+ } catch {
35928
+ }
35929
+ };
35930
+ if (shape.kind === "faster" || shape.kind === "agents" || shape.kind === "max") {
35931
+ if (shape.kind === "faster" && running === null)
35932
+ return say(paceNoBuildText());
35933
+ const number4 = shape.kind === "faster" ? running.build.number + 1 : shape.kind === "max" ? PACE_AGENTS_MAX : shape.count;
35934
+ if (!Number.isInteger(number4) || number4 < 1 || number4 > PACE_AGENTS_MAX)
35367
35935
  return say(paceAgentsRangeText());
35368
- const more = number4 > build.number;
35936
+ const before = running !== null ? running.build.number : project.number ?? CAREFUL_START_AGENTS;
35937
+ const more = number4 > before;
35369
35938
  if (more && typed === "not-attended")
35370
35939
  return say(paceNotAttendedText());
35371
- writeBuildNumber(guardEngagementId, { phase, number: number4, namedByOwner: true }, opts.home);
35372
- adjustPaceState(dir, opts, number4);
35373
- if (more && typed === "cannot-tell") {
35374
- recordRelayedChoice({ engagementId: guardEngagementId, phase, said, now: new Date((opts.now ?? Date.now)()), home: opts.home });
35940
+ if (running !== null) {
35941
+ writeBuildNumber(guardEngagementId, { phase: running.phase, number: number4, namedByOwner: true }, opts.home);
35942
+ adjustPaceState(dir, opts, number4);
35375
35943
  }
35376
- return say(paceNumberDoneText(number4, more));
35944
+ writeProjectPace(guardEngagementId, { ...project, number: number4 }, opts.home);
35945
+ if (more && typed === "cannot-tell")
35946
+ relay();
35947
+ return say(paceNumberDoneText(number4, more, running !== null));
35377
35948
  }
35378
35949
  if (shape.kind === "keep-free" && shape.percent > KEEP_FREE_MAX)
35379
35950
  return say(paceKeepFreeRangeText());
@@ -35401,9 +35972,8 @@ async function decidePace(shape, opts) {
35401
35972
  case "recorded":
35402
35973
  if (response.status !== 200)
35403
35974
  return say(paceCannotReachText());
35404
- if (givenIn === "relayed" && parsed.data.moreAllowance === true) {
35405
- recordRelayedChoice({ engagementId: guardEngagementId, phase, said, now: new Date((opts.now ?? Date.now)()), home: opts.home });
35406
- }
35975
+ if (givenIn === "relayed" && parsed.data.moreAllowance === true)
35976
+ relay();
35407
35977
  return say(done);
35408
35978
  case "needs-the-person":
35409
35979
  return say(paceNotAttendedText());
@@ -35884,12 +36454,11 @@ function runResolveEngagement(cwd, registryPath) {
35884
36454
  }
35885
36455
 
35886
36456
  // dist/refresh-credential.js
35887
- import { lstatSync as lstatSync5, readFileSync as readFileSync32, realpathSync as realpathSync11 } from "node:fs";
36457
+ import { lstatSync as lstatSync5, readFileSync as readFileSync31, realpathSync as realpathSync11 } from "node:fs";
35888
36458
  import { homedir as homedir19 } from "node:os";
35889
36459
  import { join as join24, resolve as resolve12 } from "node:path";
35890
36460
 
35891
36461
  // dist/mcp-headers.js
35892
- import { readFileSync as readFileSync31 } from "node:fs";
35893
36462
  import { homedir as homedir18 } from "node:os";
35894
36463
  import { dirname as dirname10, resolve as resolve11 } from "node:path";
35895
36464
  var URL_FLAG = "--url";
@@ -35920,22 +36489,6 @@ function projectRootFromBinary(binaryPath) {
35920
36489
  const depth = VENDORED_BIN_REL.split("/").length - 1;
35921
36490
  return resolve11(dirname10(binaryPath), ...Array(depth).fill(".."));
35922
36491
  }
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
36492
  function resolveStore(root, home) {
35940
36493
  const pinned = readPinnedId(root);
35941
36494
  if ("problem" in pinned)
@@ -35964,23 +36517,25 @@ function sentences(lines2) {
35964
36517
  `).join("");
35965
36518
  }
35966
36519
  function badIdSentence(root) {
36520
+ const signIn = signInCommand(readPinnedVersion(root));
35967
36521
  return sentences([
35968
36522
  `${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',
36523
+ `so no credential was read. Delete that file and run ${signIn} again: this repository`,
35970
36524
  "is then set up as a new Halfcycle project."
35971
36525
  ]);
35972
36526
  }
35973
36527
  function storeProblemSentence(problem, root, home, engagementId) {
36528
+ const signIn = signInCommand(readPinnedVersion(root));
35974
36529
  switch (problem) {
35975
36530
  case "no-pin":
35976
36531
  return sentences([
35977
36532
  `no ${BUNDLE_PIN_REL} in ${root}, so this repository has no Halfcycle engagement.`,
35978
- 'run "npx halfcycle" here to install one.'
36533
+ `run ${signIn} here to install one.`
35979
36534
  ]);
35980
36535
  case "no-id":
35981
36536
  return sentences([
35982
36537
  `${resolve11(root, BUNDLE_PIN_REL)} names no engagement id.`,
35983
- 'run "npx halfcycle" here to rewrite it.'
36538
+ `run ${signIn} here to rewrite it.`
35984
36539
  ]);
35985
36540
  case "bad-id":
35986
36541
  return badIdSentence(root);
@@ -35989,14 +36544,14 @@ function storeProblemSentence(problem, root, home, engagementId) {
35989
36544
  "this project belongs to another Halfcycle account, so this machine has no",
35990
36545
  "credential for it and the Halfcycle tools are not available here. Nothing is",
35991
36546
  "broken.",
35992
- requestAccessSentence(engagementId),
36547
+ requestAccessSentence(engagementId, readControlOrigin().origin),
35993
36548
  "If this account is wrong, sign in as the account that owns it and run",
35994
- '"npx halfcycle" again.'
36549
+ `${signIn} again.`
35995
36550
  ]);
35996
36551
  case "no-credential":
35997
36552
  return sentences([
35998
36553
  `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.`
36554
+ `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
36555
  ]);
36001
36556
  }
36002
36557
  }
@@ -36021,6 +36576,7 @@ async function chooseBearer(opts) {
36021
36576
  const writeErr = opts.writeErr ?? defaultWriteErr;
36022
36577
  const home = opts.home ?? homedir18();
36023
36578
  const root = projectRootFromBinary(opts.binaryPath);
36579
+ const signIn = signInCommand(readPinnedVersion(root));
36024
36580
  const environmentToken = opts.environmentTokenFirst ? readEnvironmentToken() : void 0;
36025
36581
  const store = resolveStore(root, home);
36026
36582
  let storedToken;
@@ -36033,7 +36589,7 @@ async function chooseBearer(opts) {
36033
36589
  if (storedToken === void 0) {
36034
36590
  writeErr(sentences([
36035
36591
  `GUARD_SERVICE_TOKEN is absent or empty in ${store.path}.`,
36036
- `run "npx halfcycle" in this repository to rewrite this machine's credential.`
36592
+ `run ${signIn} in this repository to rewrite this machine's credential.`
36037
36593
  ]));
36038
36594
  return NO_CREDENTIAL_PLACEHOLDER;
36039
36595
  }
@@ -36053,7 +36609,7 @@ async function chooseBearer(opts) {
36053
36609
  } else {
36054
36610
  writeErr(sentences([
36055
36611
  "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.'
36612
+ `run ${signIn} in this repository on this machine to record it.`
36057
36613
  ]));
36058
36614
  }
36059
36615
  return NO_CREDENTIAL_PLACEHOLDER;
@@ -36065,7 +36621,7 @@ async function chooseBearer(opts) {
36065
36621
  writeErr(sentences([
36066
36622
  `${registrationFile} asks for this repository's Halfcycle credential to be sent to ${shownUrl(requested ?? "")},`,
36067
36623
  `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.`
36624
+ `if nobody meant to change ${registrationFile}, treat that change as suspect; ${signIn} restores the entry.`
36069
36625
  ]));
36070
36626
  return NO_CREDENTIAL_PLACEHOLDER;
36071
36627
  }
@@ -36085,6 +36641,7 @@ async function chooseBearer(opts) {
36085
36641
  async function mcpHeaders(opts) {
36086
36642
  const writeErr = opts.writeErr ?? defaultWriteErr;
36087
36643
  const root = projectRootFromBinary(opts.binaryPath);
36644
+ const signIn = signInCommand(readPinnedVersion(root));
36088
36645
  const variableUrl = readMcpServerUrl();
36089
36646
  try {
36090
36647
  return headerLine(await chooseBearer({
@@ -36102,7 +36659,7 @@ async function mcpHeaders(opts) {
36102
36659
  try {
36103
36660
  writeErr(sentences([
36104
36661
  `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.`
36662
+ `run ${signIn} in this repository to rewrite this machine's credential.`
36106
36663
  ]));
36107
36664
  } catch {
36108
36665
  return headerLine(NO_CREDENTIAL_PLACEHOLDER, readPinnedVersion(root));
@@ -36117,11 +36674,13 @@ async function headersVerbLine(binaryPath, rest, run = mcpHeaders, writeErr = de
36117
36674
  } catch (err) {
36118
36675
  if (url2 === void 0)
36119
36676
  throw err;
36120
- const fallback = headerLine(NO_CREDENTIAL_PLACEHOLDER, readPinnedVersion(projectRootFromBinary(binaryPath)));
36677
+ const version2 = readPinnedVersion(projectRootFromBinary(binaryPath));
36678
+ const signIn = signInCommand(version2);
36679
+ const fallback = headerLine(NO_CREDENTIAL_PLACEHOLDER, version2);
36121
36680
  try {
36122
36681
  writeErr(sentences([
36123
36682
  `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.`
36683
+ `run ${signIn} in this repository to rewrite this machine's credential.`
36125
36684
  ]));
36126
36685
  } catch {
36127
36686
  return fallback;
@@ -36185,7 +36744,7 @@ var SESSION_START_PROBE_ATTEMPTS = 2;
36185
36744
  function registeredMcpUrl(projectRoot) {
36186
36745
  let parsed;
36187
36746
  try {
36188
- parsed = JSON.parse(readFileSync32(resolve12(projectRoot, MCP_REGISTRATION_REL), "utf-8"));
36747
+ parsed = JSON.parse(readFileSync31(resolve12(projectRoot, MCP_REGISTRATION_REL), "utf-8"));
36189
36748
  } catch {
36190
36749
  return void 0;
36191
36750
  }
@@ -36220,7 +36779,7 @@ function reportOf(body) {
36220
36779
  const instructions = body["result"]["instructions"];
36221
36780
  return typeof instructions === "string" ? checksReportOfInstructions(instructions) : "none";
36222
36781
  }
36223
- async function postInitialize(url2, bearer, fetchFn, signal) {
36782
+ async function postInitialize(url2, bearer, fetchFn, signal, version2) {
36224
36783
  let res;
36225
36784
  try {
36226
36785
  res = await fetchFn(url2, {
@@ -36235,7 +36794,7 @@ async function postInitialize(url2, bearer, fetchFn, signal) {
36235
36794
  }
36236
36795
  if (res.status === 401) {
36237
36796
  discardBody(res);
36238
- return NO_CREDENTIAL_SENTENCE;
36797
+ return noCredentialSentence(version2);
36239
36798
  }
36240
36799
  if (res.status < 200 || res.status > 299) {
36241
36800
  discardBody(res);
@@ -36255,12 +36814,12 @@ async function postInitialize(url2, bearer, fetchFn, signal) {
36255
36814
  }
36256
36815
  return namesItsServer(body) ? { report: reportOf(body) } : SERVICE_PROBLEM_SENTENCE;
36257
36816
  }
36258
- async function probeDelivery(url2, bearer, fetchFn, deadlineMs, maxAttempts) {
36817
+ async function probeDelivery(url2, bearer, fetchFn, deadlineMs, maxAttempts, version2) {
36259
36818
  const controller = new AbortController();
36260
36819
  const timer = setTimeout(() => controller.abort(), deadlineMs);
36261
36820
  try {
36262
36821
  for (let attempt3 = 0; attempt3 < maxAttempts; attempt3++) {
36263
- const outcome = await postInitialize(url2, bearer, fetchFn, controller.signal);
36822
+ const outcome = await postInitialize(url2, bearer, fetchFn, controller.signal, version2);
36264
36823
  if (outcome !== CUT_OFF)
36265
36824
  return outcome;
36266
36825
  }
@@ -36271,9 +36830,11 @@ async function probeDelivery(url2, bearer, fetchFn, deadlineMs, maxAttempts) {
36271
36830
  }
36272
36831
  async function checkConnection(renewedToken, opts) {
36273
36832
  const profile = opts.profile ?? AGENT_PROFILES["claude-code"];
36274
- const url2 = registeredMcpUrl(projectRootFromBinary(opts.binaryPath));
36833
+ const root = projectRootFromBinary(opts.binaryPath);
36834
+ const url2 = registeredMcpUrl(root);
36275
36835
  if (url2 === void 0)
36276
36836
  return void 0;
36837
+ const version2 = readPinnedVersion(root);
36277
36838
  const bearer = await chooseBearer({
36278
36839
  binaryPath: opts.binaryPath,
36279
36840
  home: opts.home,
@@ -36284,9 +36845,9 @@ async function checkConnection(renewedToken, opts) {
36284
36845
  environmentTokenFirst: false
36285
36846
  });
36286
36847
  if (CREDENTIAL_PLACEHOLDERS.includes(bearer)) {
36287
- return { sentence: PLACEHOLDER_SENTENCES[bearer], url: url2, bearer, connected: false };
36848
+ return { sentence: placeholderSentencesOf(version2)[bearer], url: url2, bearer, connected: false };
36288
36849
  }
36289
- const outcome = await probeDelivery(url2, bearer, opts.fetchFn ?? globalThis.fetch, opts.checkDeadlineMs ?? CONNECTION_CHECK_DEADLINE_MS, opts.maxAttempts ?? SESSION_START_PROBE_ATTEMPTS);
36850
+ const outcome = await probeDelivery(url2, bearer, opts.fetchFn ?? globalThis.fetch, opts.checkDeadlineMs ?? CONNECTION_CHECK_DEADLINE_MS, opts.maxAttempts ?? SESSION_START_PROBE_ATTEMPTS, version2);
36290
36851
  if (typeof outcome === "string")
36291
36852
  return { sentence: outcome, url: url2, bearer, connected: false };
36292
36853
  return { sentence: connectedSentenceFor(profile), url: url2, bearer, connected: true, report: outcome.report };
@@ -36361,7 +36922,11 @@ async function fetchPositionNotices(url2, bearer, engagementId, fetchFn, deadlin
36361
36922
  }
36362
36923
  }
36363
36924
  function scopeNoticeLines(scope, engagementId) {
36364
- const text = scopeNoticeText(scope, { engagementId, offerTyping: scope.typedApprovalOffered ? "yes" : "off" });
36925
+ const text = scopeNoticeText(scope, {
36926
+ engagementId,
36927
+ serviceAddress: readControlOrigin().origin,
36928
+ offerTyping: scope.typedApprovalOffered ? "yes" : "off"
36929
+ });
36365
36930
  return [...text.sentBack, ...text.waiting];
36366
36931
  }
36367
36932
  function defaultWriteErr2(text) {
@@ -36374,12 +36939,13 @@ function headlessInUntrustedFolder(input, profile, opts) {
36374
36939
  const cwd = input["cwd"];
36375
36940
  const folder2 = typeof cwd === "string" && cwd !== "" ? cwd : projectRootFromBinary(opts.binaryPath);
36376
36941
  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";
36942
+ return trustOfWorkingFolder(folder2, stateFile, process.platform, { lstatSync: lstatSync5, readFileSync: readFileSync31, realpathSync: realpathSync11 }) === "not-trusted";
36378
36943
  }
36379
36944
  async function runRefreshCredential(opts) {
36380
36945
  const writeErr = opts.writeErr ?? defaultWriteErr2;
36381
36946
  const profile = opts.profile ?? AGENT_PROFILES["claude-code"];
36382
36947
  const startedAt = Date.now();
36948
+ const startVersion = readPinnedVersion(projectRootFromBinary(opts.binaryPath));
36383
36949
  try {
36384
36950
  const renewalSaid = [];
36385
36951
  let renewalOpen = true;
@@ -36410,7 +36976,7 @@ async function runRefreshCredential(opts) {
36410
36976
  if (notices?.scope !== void 0)
36411
36977
  parts.push(...scopeNoticeLines(notices.scope, engagementId));
36412
36978
  if (notices?.review !== void 0)
36413
- parts.push(...reviewNoticeText(notices.review));
36979
+ parts.push(...reviewNoticeText(notices.review, startVersion));
36414
36980
  for (const entry of notices?.approvedInConsole ?? [])
36415
36981
  parts.push(approvedInConsoleSentence(entry.phase));
36416
36982
  }
@@ -36438,18 +37004,18 @@ async function runRefreshCredential(opts) {
36438
37004
  return `${nonBlockingReply("SessionStart", FOLDER_NOT_TRUSTED_NOTICE)}
36439
37005
  `;
36440
37006
  if (readConfig().config === null) {
36441
- const remedySaid = parts.some(namesTheSignInCommand);
37007
+ const remedySaid = parts.some((part) => namesTheSignInCommand(part, startVersion));
36442
37008
  aFailureOfOurs = true;
36443
- parts.push(noCoverageStatement(readNeverConfiguredReason(), remedySaid, pinnedEngagementId(projectRootFromBinary(opts.binaryPath))));
37009
+ parts.push(noCoverageStatement(readNeverConfiguredReason(), remedySaid, pinnedEngagementId(projectRootFromBinary(opts.binaryPath)), startVersion));
36444
37010
  }
36445
37011
  for (const said of renewalSaid) {
36446
37012
  aFailureOfOurs = true;
36447
- if (namesTheSignInCommand(said) && parts.some(namesTheSignInCommand))
37013
+ if (namesTheSignInCommand(said, startVersion) && parts.some((part) => namesTheSignInCommand(part, startVersion)))
36448
37014
  continue;
36449
37015
  parts.push(said.trimEnd());
36450
37016
  }
36451
37017
  if (aFailureOfOurs)
36452
- parts.push(DOCTOR_LINE);
37018
+ parts.push(doctorLine(startVersion));
36453
37019
  return parts.length === 0 ? "" : `${nonBlockingReply("SessionStart", parts.join("\n"))}
36454
37020
  `;
36455
37021
  } catch (err) {
@@ -36467,7 +37033,7 @@ async function runRefreshCredential(opts) {
36467
37033
  import { fileURLToPath } from "node:url";
36468
37034
 
36469
37035
  // dist/capture.js
36470
- import { readFileSync as readFileSync33, writeFileSync as writeFileSync20, mkdirSync as mkdirSync14, existsSync as existsSync10 } from "node:fs";
37036
+ import { readFileSync as readFileSync32, writeFileSync as writeFileSync20, mkdirSync as mkdirSync14, existsSync as existsSync10 } from "node:fs";
36471
37037
  import { dirname as dirname11 } from "node:path";
36472
37038
  function runCapture(provider, endpoint, opts = {}) {
36473
37039
  const repoRoot = opts.repoRoot ?? process.cwd();
@@ -36535,7 +37101,7 @@ function readInputFile(fromFile) {
36535
37101
  if (!fromFile)
36536
37102
  return void 0;
36537
37103
  try {
36538
- return readFileSync33(fromFile, "utf-8");
37104
+ return readFileSync32(fromFile, "utf-8");
36539
37105
  } catch {
36540
37106
  return void 0;
36541
37107
  }
@@ -36548,7 +37114,7 @@ function relativeFixturePath(repoRoot, fixture) {
36548
37114
  // dist/prove.js
36549
37115
  import { spawn as spawn2, spawnSync as spawnSync3 } from "node:child_process";
36550
37116
  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";
37117
+ import { createWriteStream, mkdirSync as mkdirSync15, readdirSync as readdirSync11, readFileSync as readFileSync33, renameSync as renameSync18, unlinkSync as unlinkSync12, writeFileSync as writeFileSync21 } from "node:fs";
36552
37118
  import { homedir as homedir20 } from "node:os";
36553
37119
  import { dirname as dirname12, join as join25 } from "node:path";
36554
37120
 
@@ -36560,11 +37126,15 @@ function firstNumber(line2, pattern) {
36560
37126
  const match = line2.match(pattern);
36561
37127
  return match ? Number(match[1]) : void 0;
36562
37128
  }
36563
- function nodeTestRunnerCounts(output) {
36564
- const passedLines = [...output.matchAll(/^# pass (\d+)$/gm)];
37129
+ var NODE_PASS_LINE = /^(?:# |ℹ️? )pass (\d+)[ \t\r]*$/gm;
37130
+ var NODE_FAIL_LINE = /^(?:# |ℹ️? )fail (\d+)[ \t\r]*$/gm;
37131
+ var NODE_COLOUR_CODE = /\u001b\[[0-9;]*m/g;
37132
+ function nodeTestRunnerCounts(rawOutput) {
37133
+ const output = rawOutput.replace(NODE_COLOUR_CODE, "");
37134
+ const passedLines = [...output.matchAll(NODE_PASS_LINE)];
36565
37135
  if (passedLines.length === 0)
36566
37136
  return null;
36567
- const failedLines = [...output.matchAll(/^# fail (\d+)$/gm)];
37137
+ const failedLines = [...output.matchAll(NODE_FAIL_LINE)];
36568
37138
  return {
36569
37139
  passed: sum(passedLines.map((m) => Number(m[1]))),
36570
37140
  failed: sum(failedLines.map((m) => Number(m[1])))
@@ -36625,13 +37195,56 @@ function cargoTestCounts(output) {
36625
37195
  failed: sum(lines2.map((m) => Number(m[2])))
36626
37196
  };
36627
37197
  }
37198
+ var UNITTEST_COLOUR_CODE = /\u001b\[[0-9;]*m/g;
37199
+ var UNITTEST_RAN_LINE = /^Ran (\d+) tests? in \d+(?:\.\d+)?s\s*$/;
37200
+ var UNITTEST_STATUS_LINE = /^(?:OK|FAILED|NO TESTS RAN)(?: \((.*)\))?$/;
37201
+ var UNITTEST_PAIR = /^(failures|errors|skipped|expected failures|unexpected successes)=(\d+)$/;
37202
+ function unittestPairs(list) {
37203
+ const pairs = /* @__PURE__ */ new Map();
37204
+ if (list === void 0)
37205
+ return pairs;
37206
+ for (const part of list.split(",")) {
37207
+ const match = part.trim().match(UNITTEST_PAIR);
37208
+ if (!match)
37209
+ return null;
37210
+ pairs.set(match[1], (pairs.get(match[1]) ?? 0) + Number(match[2]));
37211
+ }
37212
+ return pairs;
37213
+ }
37214
+ function unittestCounts(output) {
37215
+ const lines2 = output.split("\n").map((line2) => line2.endsWith("\r") ? line2.slice(0, -1) : line2);
37216
+ let total = null;
37217
+ for (let i = 0; i < lines2.length; i++) {
37218
+ const ran = lines2[i].match(UNITTEST_RAN_LINE);
37219
+ if (!ran)
37220
+ continue;
37221
+ let next = i + 1;
37222
+ while (next < lines2.length && lines2[next].trim() === "")
37223
+ next++;
37224
+ if (next >= lines2.length)
37225
+ continue;
37226
+ const status = lines2[next].replace(UNITTEST_COLOUR_CODE, "").trim().match(UNITTEST_STATUS_LINE);
37227
+ if (!status)
37228
+ continue;
37229
+ const pairs = unittestPairs(status[1]);
37230
+ if (!pairs)
37231
+ continue;
37232
+ const count = (key) => pairs.get(key) ?? 0;
37233
+ const failed2 = count("failures") + count("errors") + count("unexpected successes");
37234
+ const accounted = failed2 + count("skipped") + count("expected failures");
37235
+ const passed = Math.max(0, Number(ran[1]) - accounted);
37236
+ total = total ? { passed: total.passed + passed, failed: total.failed + failed2 } : { passed, failed: failed2 };
37237
+ }
37238
+ return total;
37239
+ }
36628
37240
  var FORMAT_PARSERS = [
36629
37241
  nodeTestRunnerCounts,
36630
37242
  mochaCounts,
36631
37243
  jestCounts,
36632
37244
  vitestCounts,
36633
37245
  pytestCounts,
36634
- cargoTestCounts
37246
+ cargoTestCounts,
37247
+ unittestCounts
36635
37248
  ];
36636
37249
  function parseTestCounts(output) {
36637
37250
  let total = null;
@@ -36759,7 +37372,7 @@ function keptCheckPath(dir, subject, label) {
36759
37372
  }
36760
37373
  function readKeptCheck(dir, subject, label) {
36761
37374
  try {
36762
- const raw = JSON.parse(readFileSync34(keptCheckPath(dir, subject, label), "utf-8"));
37375
+ const raw = JSON.parse(readFileSync33(keptCheckPath(dir, subject, label), "utf-8"));
36763
37376
  if (typeof raw.command !== "string" || raw.command === "" || typeof raw.commandId !== "string")
36764
37377
  return null;
36765
37378
  const b = raw.before;
@@ -36784,7 +37397,7 @@ function readQueue(dir) {
36784
37397
  const path = join25(dir, QUEUE_DIR_NAME, name);
36785
37398
  let parsed = null;
36786
37399
  try {
36787
- parsed = testRunReportSchema.safeParse(JSON.parse(readFileSync34(path, "utf-8")));
37400
+ parsed = testRunReportSchema.safeParse(JSON.parse(readFileSync33(path, "utf-8")));
36788
37401
  } catch {
36789
37402
  parsed = null;
36790
37403
  }
@@ -37027,7 +37640,7 @@ ${PROVE_USAGE}`);
37027
37640
  const end = await runCommand(command, repoRoot, logPath2, env, deps);
37028
37641
  let output = "";
37029
37642
  try {
37030
- output = readFileSync34(logPath2, "utf-8");
37643
+ output = readFileSync33(logPath2, "utf-8");
37031
37644
  } catch {
37032
37645
  output = "";
37033
37646
  }
@@ -37077,7 +37690,7 @@ ${PROVE_USAGE}`);
37077
37690
 
37078
37691
  // dist/statusline.js
37079
37692
  import { spawn as spawn3 } from "node:child_process";
37080
- import { readFileSync as readFileSync35 } from "node:fs";
37693
+ import { readFileSync as readFileSync34 } from "node:fs";
37081
37694
  import nodePath7 from "node:path";
37082
37695
  var OWN_STATUS_LINE_DEADLINE_MS = 5e3;
37083
37696
  var PERSON_SETTINGS_FILENAME = "settings.json";
@@ -37115,7 +37728,7 @@ function halfcycleStatusLineOf(figures) {
37115
37728
  }
37116
37729
  function personStatusLineCommand(configFolder) {
37117
37730
  try {
37118
- const v = JSON.parse(readFileSync35(nodePath7.join(configFolder, PERSON_SETTINGS_FILENAME), "utf-8"));
37731
+ const v = JSON.parse(readFileSync34(nodePath7.join(configFolder, PERSON_SETTINGS_FILENAME), "utf-8"));
37119
37732
  const line2 = v["statusLine"];
37120
37733
  if (typeof line2 !== "object" || line2 === null)
37121
37734
  return null;
@@ -37233,7 +37846,7 @@ function usageFigures2(env, deps = {}) {
37233
37846
  }
37234
37847
  return figures;
37235
37848
  }
37236
- function connectionKind(bearer, sentence) {
37849
+ function connectionKind(bearer, sentence, version2) {
37237
37850
  if (bearer === NOT_THIS_ACCOUNT_PLACEHOLDER)
37238
37851
  return "other-account";
37239
37852
  if (bearer === UPDATE_CLAUDE_CODE_PLACEHOLDER)
@@ -37246,7 +37859,7 @@ function connectionKind(bearer, sentence) {
37246
37859
  return "cannot-reach";
37247
37860
  if (sentence === SERVICE_PROBLEM_SENTENCE)
37248
37861
  return "service-problem";
37249
- if (sentence === NO_CREDENTIAL_SENTENCE)
37862
+ if (sentence === noCredentialSentence(version2))
37250
37863
  return "signed-out";
37251
37864
  throw new Error("the connection check gave an outcome with no kind");
37252
37865
  }
@@ -37260,7 +37873,7 @@ async function checkConnectionLine(opts) {
37260
37873
  const withUsage = usage.length === 0 ? {} : { usage };
37261
37874
  if (result === void 0)
37262
37875
  return { kind: "not-registered", sentence: null, ...withUsage };
37263
- const line2 = { kind: connectionKind(result.bearer, result.sentence), sentence: result.sentence };
37876
+ const line2 = { kind: connectionKind(result.bearer, result.sentence, readPinnedVersion(projectRootFromBinary(opts.binaryPath))), sentence: result.sentence };
37264
37877
  const report = result.report === void 0 || result.report === "none" ? {} : { report: result.report };
37265
37878
  return { ...line2, ...report, ...withUsage };
37266
37879
  }