halfcycle 0.4.1 → 0.4.3

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.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "halfcycle",
3
- "version": "0.4.1",
3
+ "version": "0.4.3",
4
4
  "description": "Halfcycle Method bundle — resolution-stub slash commands, remote method-delivery registration, and governance-hook wiring for a Halfcycle engagement repo. It ships NO worker role files: a project's roles are authored from that project's own recorded decisions at orchestration kickoff (FX-2, W3-F-27).",
5
5
  "commands": [
6
6
  {
package/README.md CHANGED
@@ -165,7 +165,7 @@ Nothing you already had is changed. Existing settings are preserved, and a name
165
165
  ## Requirements
166
166
 
167
167
  - Claude Code **2.1.118+**
168
- - Node 20+
168
+ - Node 18+
169
169
  - A git repository
170
170
  - On Windows, in PowerShell, use `npx.cmd` instead of `npx` (PowerShell's default settings block the `npx` script; `npx.cmd` always works, with nothing to change).
171
171
 
@@ -15376,6 +15376,9 @@ function infraWarningText() {
15376
15376
  function localFaultText(message) {
15377
15377
  return `[Halfcycle] WARNING: Local fault \u2014 ${message}. The guard service was not contacted. Session continues (fail-open).`;
15378
15378
  }
15379
+ function noRepositoryText(failure) {
15380
+ return failure.gitMissing ? "[Halfcycle] WARNING: Git is not installed on this machine, and Halfcycle's checks need it, so nothing will be checked in this session. Install Git, then start a new session." : localFaultText(`Could not find git repo root: ${failure.message}`);
15381
+ }
15379
15382
  function contractFaultText(message) {
15380
15383
  return `[Halfcycle] WARNING: Guard response could not be understood \u2014 ${message}
15381
15384
  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).`;
@@ -15711,7 +15714,7 @@ async function attempt(url2, token, request, fetchFn, retry) {
15711
15714
 
15712
15715
  // dist/git.js
15713
15716
  import { spawnSync } from "node:child_process";
15714
- import { readFileSync } from "node:fs";
15717
+ import { existsSync, readFileSync } from "node:fs";
15715
15718
  import { relative } from "node:path";
15716
15719
  var COLOR_AWARE_SUBCOMMANDS = /* @__PURE__ */ new Set(["diff", "show", "log"]);
15717
15720
  function git(args2, cwd, timeoutMs) {
@@ -15722,7 +15725,8 @@ function git(args2, cwd, timeoutMs) {
15722
15725
  ...timeoutMs === void 0 ? {} : { timeout: timeoutMs }
15723
15726
  });
15724
15727
  if (result.error) {
15725
- return { ok: false, message: `git error: ${result.error.message}` };
15728
+ const missing = result.error.code === "ENOENT" && existsSync(cwd);
15729
+ return { ok: false, message: `git error: ${result.error.message}`, ...missing ? { gitMissing: true } : {} };
15726
15730
  }
15727
15731
  if (result.status !== 0) {
15728
15732
  const stderr = result.stderr?.trim() ?? "";
@@ -16605,7 +16609,7 @@ function resolveWireFields(lines, decls) {
16605
16609
  }
16606
16610
 
16607
16611
  // dist/fixtures-manifest.js
16608
- import { existsSync, mkdirSync, readFileSync as readFileSync3, renameSync, unlinkSync, writeFileSync } from "node:fs";
16612
+ import { existsSync as existsSync2, mkdirSync, readFileSync as readFileSync3, renameSync, unlinkSync, writeFileSync } from "node:fs";
16609
16613
  import { dirname, join as join2 } from "node:path";
16610
16614
  function manifestPath(repoRoot) {
16611
16615
  return join2(repoRoot, "test", "fixtures", "captured", "manifest.json");
@@ -16644,7 +16648,7 @@ function upsertManifest(repoRoot, entry) {
16644
16648
  }
16645
16649
  function removeFixtureIfExists(path) {
16646
16650
  try {
16647
- if (existsSync(path))
16651
+ if (existsSync2(path))
16648
16652
  unlinkSync(path);
16649
16653
  } catch {
16650
16654
  }
@@ -17266,7 +17270,7 @@ function cleanRunSummary(verdict, scope) {
17266
17270
  }
17267
17271
 
17268
17272
  // dist/credential-store.js
17269
- import { chmodSync, existsSync as existsSync2, readFileSync as readFileSync5, renameSync as renameSync2, unlinkSync as unlinkSync2, writeFileSync as writeFileSync2, realpathSync } from "node:fs";
17273
+ import { chmodSync, existsSync as existsSync3, readFileSync as readFileSync5, renameSync as renameSync2, unlinkSync as unlinkSync2, writeFileSync as writeFileSync2, realpathSync } from "node:fs";
17270
17274
  import { homedir as homedir2, platform } from "node:os";
17271
17275
  import { dirname as dirname3, join as join5 } from "node:path";
17272
17276
  var REFRESHED_TOKEN_KEYS = ["HALFCYCLE_TOKEN", "GUARD_SERVICE_TOKEN"];
@@ -17275,7 +17279,7 @@ function composeEngagementEnvPath(engagementId, home) {
17275
17279
  return join5(home ?? homedir2(), HALFCYCLE_DIR_NAME, ENGAGEMENTS_DIR_NAME, engagementId, ENGAGEMENT_ENV_FILENAME);
17276
17280
  }
17277
17281
  function hasNotThisAccountMarker(engagementId, home) {
17278
- return existsSync2(join5(home ?? homedir2(), HALFCYCLE_DIR_NAME, ENGAGEMENTS_DIR_NAME, engagementId, NOT_THIS_ACCOUNT_MARKER_FILENAME));
17282
+ return existsSync3(join5(home ?? homedir2(), HALFCYCLE_DIR_NAME, ENGAGEMENTS_DIR_NAME, engagementId, NOT_THIS_ACCOUNT_MARKER_FILENAME));
17279
17283
  }
17280
17284
  function readAccountCredential(controlOrigin, home) {
17281
17285
  let raw;
@@ -17467,7 +17471,7 @@ async function evaluateWithRefresh(opts) {
17467
17471
 
17468
17472
  // dist/hooks/marker.js
17469
17473
  import { createHash } from "node:crypto";
17470
- import { existsSync as existsSync3, readFileSync as readFileSync6, writeFileSync as writeFileSync3, unlinkSync as unlinkSync3, readdirSync as readdirSync3, statSync } from "node:fs";
17474
+ import { existsSync as existsSync4, readFileSync as readFileSync6, writeFileSync as writeFileSync3, unlinkSync as unlinkSync3, readdirSync as readdirSync3, statSync } from "node:fs";
17471
17475
  import { tmpdir } from "node:os";
17472
17476
  import { join as join6 } from "node:path";
17473
17477
  var MARKER_TTL_MS = 24 * 60 * 60 * 1e3;
@@ -17496,7 +17500,7 @@ function dedupePath(repoRoot, sessionId) {
17496
17500
  }
17497
17501
  function readMarker(repoRoot, sessionId) {
17498
17502
  const path = markerPath(repoRoot, sessionId);
17499
- if (!existsSync3(path))
17503
+ if (!existsSync4(path))
17500
17504
  return null;
17501
17505
  try {
17502
17506
  return readFileSync6(path, "utf-8").trim() || null;
@@ -17507,17 +17511,22 @@ function readMarker(repoRoot, sessionId) {
17507
17511
  function deleteMarker(repoRoot, sessionId) {
17508
17512
  for (const path of [markerPath(repoRoot, sessionId), dedupePath(repoRoot, sessionId)]) {
17509
17513
  try {
17510
- if (existsSync3(path))
17514
+ if (existsSync4(path))
17511
17515
  unlinkSync3(path);
17512
17516
  } catch {
17513
17517
  }
17514
17518
  }
17515
17519
  }
17516
- var NO_REPO_NOTICE_SUFFIX = ".norepo";
17517
- function firstNoRepoNoticeThisSession(folder, sessionId) {
17520
+ var SESSION_NOTICE_SUFFIX = {
17521
+ "no-repository": ".norepo",
17522
+ "credential-rejected": ".credential",
17523
+ "credential-renewed-for-this-run": ".renewed"
17524
+ };
17525
+ function firstNoticeThisSession(notice, folder, sessionId) {
17518
17526
  if (!sessionId)
17519
17527
  return true;
17520
- const path = join6(markerDir(), `${MARKER_PREFIX}${repoHash(folder)}-${sessionId}${NO_REPO_NOTICE_SUFFIX}`);
17528
+ const suffix = SESSION_NOTICE_SUFFIX[notice];
17529
+ const path = join6(markerDir(), `${MARKER_PREFIX}${repoHash(folder)}-${sessionId}${suffix}`);
17521
17530
  try {
17522
17531
  writeFileSync3(path, "", { encoding: "utf-8", flag: "wx" });
17523
17532
  return true;
@@ -17525,9 +17534,18 @@ function firstNoRepoNoticeThisSession(folder, sessionId) {
17525
17534
  return err.code !== "EEXIST";
17526
17535
  }
17527
17536
  }
17537
+ function warnOnceThisSession(notice, folder, sessionId) {
17538
+ return (message) => {
17539
+ if (firstNoticeThisSession(notice, folder, sessionId))
17540
+ process.stderr.write(message);
17541
+ };
17542
+ }
17543
+ function firstNoRepoNoticeThisSession(folder, sessionId) {
17544
+ return firstNoticeThisSession("no-repository", folder, sessionId);
17545
+ }
17528
17546
  function readDedupe(repoRoot, sessionId) {
17529
17547
  const path = dedupePath(repoRoot, sessionId);
17530
- if (!existsSync3(path))
17548
+ if (!existsSync4(path))
17531
17549
  return null;
17532
17550
  try {
17533
17551
  const parsed = JSON.parse(readFileSync6(path, "utf-8"));
@@ -17599,7 +17617,7 @@ function orphanSweep(repoRoot, refKnown, keepSessionId, now = Date.now()) {
17599
17617
  }
17600
17618
 
17601
17619
  // dist/seat-configuration.js
17602
- import { existsSync as existsSync4, readFileSync as readFileSync7, realpathSync as realpathSync2 } from "node:fs";
17620
+ import { existsSync as existsSync5, readFileSync as readFileSync7, realpathSync as realpathSync2 } from "node:fs";
17603
17621
  import { homedir as homedir3 } from "node:os";
17604
17622
  import { join as join7, relative as relative3, resolve } from "node:path";
17605
17623
  function gatherSeatConfiguration(repoRoot, read = readSeatConfiguration) {
@@ -17708,7 +17726,7 @@ function readPathStates(projectDir, repoRoot) {
17708
17726
  const status = getWiringStatus(HALFCYCLE_HOOK_WIRING_PATHS, projectDir);
17709
17727
  if (!status.ok)
17710
17728
  return unknown2();
17711
- const mapped = mapWiringStatus(status.value, prefix, HALFCYCLE_HOOK_WIRING_PATHS, (rel) => existsSync4(join7(projectDir, rel)));
17729
+ const mapped = mapWiringStatus(status.value, prefix, HALFCYCLE_HOOK_WIRING_PATHS, (rel) => existsSync5(join7(projectDir, rel)));
17712
17730
  return mapped ?? unknown2();
17713
17731
  }
17714
17732
  function mapWiringStatus(output, prefix, paths, onDisk) {
@@ -17774,7 +17792,7 @@ async function evaluateEdit(reply, readSeat) {
17774
17792
  const repoRootResult = getRepoRoot(process.cwd());
17775
17793
  if (!repoRootResult.ok) {
17776
17794
  if (firstNoRepoNoticeThisSession(process.cwd(), input.session_id)) {
17777
- reply.notice(localFaultText(`Could not find git repo root: ${repoRootResult.message}`));
17795
+ reply.notice(noRepositoryText(repoRootResult));
17778
17796
  }
17779
17797
  return;
17780
17798
  }
@@ -17801,7 +17819,8 @@ async function evaluateEdit(reply, readSeat) {
17801
17819
  guardServiceUrl,
17802
17820
  token: guardServiceToken,
17803
17821
  engagementId: guardEngagementId,
17804
- request: buildEvaluationRequest(guardEngagementId, changeSet, context, seatConfiguration)
17822
+ request: buildEvaluationRequest(guardEngagementId, changeSet, context, seatConfiguration),
17823
+ warn: warnOnceThisSession("credential-renewed-for-this-run", repoRoot, input.session_id)
17805
17824
  });
17806
17825
  const clientResult = evaluation.result;
17807
17826
  if (!clientResult.ok) {
@@ -17816,6 +17835,8 @@ async function evaluateEdit(reply, readSeat) {
17816
17835
  credentialRefused: failure.kind === "credential"
17817
17836
  });
17818
17837
  if (failure.kind === "credential") {
17838
+ if (!firstNoticeThisSession("credential-rejected", repoRoot, input.session_id))
17839
+ return;
17819
17840
  emitCredentialRejected(
17820
17841
  failure.statusCode,
17821
17842
  failure.message,
@@ -17934,7 +17955,7 @@ async function evaluateSession(input, kind, reply, readSeat) {
17934
17955
  const repoRootResult = getRepoRoot(process.cwd());
17935
17956
  if (!repoRootResult.ok) {
17936
17957
  if (firstNoRepoNoticeThisSession(process.cwd(), input.session_id)) {
17937
- reply.notice(localFaultText(`Could not find git repo root: ${repoRootResult.message}`));
17958
+ reply.notice(noRepositoryText(repoRootResult));
17938
17959
  }
17939
17960
  return;
17940
17961
  }
@@ -17977,7 +17998,8 @@ async function evaluateSession(input, kind, reply, readSeat) {
17977
17998
  guardServiceUrl,
17978
17999
  token: guardServiceToken,
17979
18000
  engagementId: guardEngagementId,
17980
- request: buildEvaluationRequest(guardEngagementId, changeSet, context, seatConfiguration)
18001
+ request: buildEvaluationRequest(guardEngagementId, changeSet, context, seatConfiguration),
18002
+ warn: warnOnceThisSession("credential-renewed-for-this-run", repoRoot, sessionId)
17981
18003
  });
17982
18004
  const clientResult = evaluation.result;
17983
18005
  if (!clientResult.ok) {
@@ -17993,6 +18015,8 @@ async function evaluateSession(input, kind, reply, readSeat) {
17993
18015
  credentialRefused: failure.kind === "credential"
17994
18016
  });
17995
18017
  if (failure.kind === "credential") {
18018
+ if (!firstNoticeThisSession("credential-rejected", repoRoot, sessionId))
18019
+ return;
17996
18020
  emitCredentialRejected(
17997
18021
  failure.statusCode,
17998
18022
  failure.message,
@@ -18110,14 +18134,14 @@ async function runStop() {
18110
18134
  }
18111
18135
 
18112
18136
  // dist/resolve-engagement.js
18113
- import { existsSync as existsSync5, readFileSync as readFileSync8, realpathSync as realpathSync3 } from "node:fs";
18137
+ import { existsSync as existsSync6, readFileSync as readFileSync8, realpathSync as realpathSync3 } from "node:fs";
18114
18138
  import { join as join8, sep as sep2 } from "node:path";
18115
18139
  import { homedir as homedir4 } from "node:os";
18116
18140
  function defaultRegistryPath() {
18117
18141
  return join8(homedir4(), ".halfcycle", "registry.json");
18118
18142
  }
18119
18143
  function readRegistry(registryPath = defaultRegistryPath()) {
18120
- if (!existsSync5(registryPath))
18144
+ if (!existsSync6(registryPath))
18121
18145
  return {};
18122
18146
  try {
18123
18147
  const parsed = JSON.parse(readFileSync8(registryPath, "utf-8"));
@@ -18407,7 +18431,7 @@ async function refreshCredentialToken(opts) {
18407
18431
  return { token: refreshed.token };
18408
18432
  }
18409
18433
  var CHECKED_SOURCES = /* @__PURE__ */ new Set(["startup", "resume"]);
18410
- var CONNECTED_SENTENCE = "Halfcycle connected.";
18434
+ var CONNECTED_SENTENCE = `Halfcycle's sign-in works and its service answered. If its tools are missing, turn on ${MCP_SERVER_KEY} in /mcp; if it is not listed, run "claude mcp reset-project-choices" here and restart Claude Code.`;
18411
18435
  var INITIALIZE_BODY = JSON.stringify({ jsonrpc: "2.0", id: 1, method: "initialize", params: {} });
18412
18436
  function registeredMcpUrl(projectRoot) {
18413
18437
  let parsed;
@@ -18522,8 +18546,11 @@ async function runRefreshCredential(opts) {
18522
18546
  const remedySaid = parts.some(namesTheSignInCommand);
18523
18547
  parts.push(noCoverageStatement(readNeverConfiguredReason(), remedySaid));
18524
18548
  }
18525
- for (const said of renewalSaid)
18549
+ for (const said of renewalSaid) {
18550
+ if (namesTheSignInCommand(said) && parts.some(namesTheSignInCommand))
18551
+ continue;
18526
18552
  parts.push(said.trimEnd());
18553
+ }
18527
18554
  return parts.length === 0 ? "" : `${nonBlockingReply("SessionStart", parts.join("\n"))}
18528
18555
  `;
18529
18556
  } catch (err) {
@@ -18541,7 +18568,7 @@ async function runRefreshCredential(opts) {
18541
18568
  import { fileURLToPath } from "node:url";
18542
18569
 
18543
18570
  // dist/capture.js
18544
- import { readFileSync as readFileSync11, writeFileSync as writeFileSync4, mkdirSync as mkdirSync3, existsSync as existsSync6 } from "node:fs";
18571
+ import { readFileSync as readFileSync11, writeFileSync as writeFileSync4, mkdirSync as mkdirSync3, existsSync as existsSync7 } from "node:fs";
18545
18572
  import { dirname as dirname5 } from "node:path";
18546
18573
  function runCapture(provider, endpoint, opts = {}) {
18547
18574
  const repoRoot = opts.repoRoot ?? process.cwd();
@@ -1 +1 @@
1
- {"version":3,"file":"claude-version-probe.d.ts","sourceRoot":"","sources":["../src/claude-version-probe.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAMH;0DAC0D;AAC1D,MAAM,MAAM,iBAAiB,GAAG,CAC9B,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,SAAS,MAAM,EAAE,EACvB,OAAO,EAAE;IACP,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;IAC3B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,KAAK,EAAE,SAAS,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,CAAC,CAAC;IACtD,QAAQ,CAAC,wBAAwB,CAAC,EAAE,OAAO,CAAC;CAC7C,KACE,MAAM,CAAC;AAEZ,MAAM,WAAW,sBAAsB;IACrC,oFAAoF;IACpF,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC,QAAQ,CAAC;IACpC,sFAAsF;IACtF,QAAQ,CAAC,IAAI,CAAC,EAAE,iBAAiB,CAAC;CACnC;AAED;;;;;GAKG;AACH,wBAAgB,sBAAsB,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,GAAE,sBAA2B,GAAG,MAAM,GAAG,SAAS,CAqB/G;AAMD;kCACkC;AAClC,MAAM,MAAM,mBAAmB,GAAG,QAAQ,GAAG,KAAK,GAAG,MAAM,GAAG,QAAQ,CAAC;AAEvE,eAAO,MAAM,wBAAwB,mCAAmC,CAAC;AAEzE,eAAO,MAAM,gBAAgB,EAAE,QAAQ,CAAC,MAAM,CAAC,mBAAmB,EAAE,MAAM,CAAC,CAK1E,CAAC;AAaF,MAAM,WAAW,uBAAuB;IACtC,iCAAiC;IACjC,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;IACjC,sCAAsC;IACtC,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC,QAAQ,CAAC;IACpC,iGAAiG;IACjG,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC;IAC5C;sDACkD;IAClD,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAC;CAC9C;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,4BAA4B,CAAC,IAAI,GAAE,uBAA4B,GAAG,mBAAmB,GAAG,SAAS,CAwBhH"}
1
+ {"version":3,"file":"claude-version-probe.d.ts","sourceRoot":"","sources":["../src/claude-version-probe.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAMH;0DAC0D;AAC1D,MAAM,MAAM,iBAAiB,GAAG,CAC9B,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,SAAS,MAAM,EAAE,EACvB,OAAO,EAAE;IACP,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;IAC3B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,KAAK,EAAE,SAAS,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,CAAC,CAAC;IACtD,QAAQ,CAAC,wBAAwB,CAAC,EAAE,OAAO,CAAC;CAC7C,KACE,MAAM,CAAC;AAEZ,MAAM,WAAW,sBAAsB;IACrC,oFAAoF;IACpF,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC,QAAQ,CAAC;IACpC,sFAAsF;IACtF,QAAQ,CAAC,IAAI,CAAC,EAAE,iBAAiB,CAAC;CACnC;AAED;;;;;GAKG;AACH,wBAAgB,sBAAsB,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,GAAE,sBAA2B,GAAG,MAAM,GAAG,SAAS,CAqB/G;AAMD;kCACkC;AAClC,MAAM,MAAM,mBAAmB,GAAG,QAAQ,GAAG,KAAK,GAAG,MAAM,GAAG,QAAQ,CAAC;AAEvE,eAAO,MAAM,wBAAwB,mCAAmC,CAAC;AAEzE,eAAO,MAAM,gBAAgB,EAAE,QAAQ,CAAC,MAAM,CAAC,mBAAmB,EAAE,MAAM,CAAC,CAK1E,CAAC;AAyBF,MAAM,WAAW,uBAAuB;IACtC,iCAAiC;IACjC,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;IACjC,sCAAsC;IACtC,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC,QAAQ,CAAC;IACpC,iGAAiG;IACjG,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC;IAC5C;sDACkD;IAClD,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAC;CAC9C;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,4BAA4B,CAAC,IAAI,GAAE,uBAA4B,GAAG,mBAAmB,GAAG,SAAS,CAwBhH"}