@mnemonik/cli 7.140.1 → 7.140.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.
Files changed (73) hide show
  1. package/dist/auth/credentials.js +41 -8
  2. package/dist/auth/index.js +41 -8
  3. package/dist/auth/installSession.js +1 -1
  4. package/dist/auth/status.js +1 -1
  5. package/dist/authorizationWords.js +1 -1
  6. package/dist/diagnostics.js +41 -8
  7. package/dist/digests.json +71 -67
  8. package/dist/help.d.ts.map +1 -1
  9. package/dist/help.js +50 -4
  10. package/dist/help.js.map +1 -1
  11. package/dist/hooksLog.d.ts +61 -0
  12. package/dist/hooksLog.d.ts.map +1 -0
  13. package/dist/hooksLog.js +350 -0
  14. package/dist/hooksLog.js.map +1 -0
  15. package/dist/humanReason.d.ts +7 -0
  16. package/dist/humanReason.d.ts.map +1 -1
  17. package/dist/humanReason.js +12 -4
  18. package/dist/humanReason.js.map +1 -1
  19. package/dist/identity/migrate.js +102 -69
  20. package/dist/install/hosts.js +1 -1
  21. package/dist/install/journal.js +41 -8
  22. package/dist/install/journey.d.ts.map +1 -1
  23. package/dist/install/journey.js +41 -11
  24. package/dist/install/journey.js.map +1 -1
  25. package/dist/install/ownership.js +41 -8
  26. package/dist/install/transaction.d.ts.map +1 -1
  27. package/dist/install/transaction.js +42 -10
  28. package/dist/install/transaction.js.map +1 -1
  29. package/dist/install/ui.js +41 -8
  30. package/dist/installSession.js +1 -1
  31. package/dist/installation.js +41 -8
  32. package/dist/launcher.js +41 -8
  33. package/dist/preflight.js +201 -164
  34. package/dist/project/eligibility.js +41 -8
  35. package/dist/project/records.js +41 -8
  36. package/dist/project.d.ts +35 -1
  37. package/dist/project.d.ts.map +1 -1
  38. package/dist/project.js +294 -231
  39. package/dist/project.js.map +1 -1
  40. package/dist/router.d.ts +7 -0
  41. package/dist/router.d.ts.map +1 -1
  42. package/dist/router.js +340 -182
  43. package/dist/router.js.map +1 -1
  44. package/dist/runtime/bootstrap.d.ts +22 -1
  45. package/dist/runtime/bootstrap.d.ts.map +1 -1
  46. package/dist/runtime/bootstrap.js +61 -8
  47. package/dist/runtime/bootstrap.js.map +1 -1
  48. package/dist/runtime/releaseSource.js +41 -8
  49. package/dist/runtime/selfUpdate.js +41 -8
  50. package/dist/runtime/signers.js +789 -183
  51. package/dist/runtime/store.d.ts +1 -1
  52. package/dist/runtime/store.d.ts.map +1 -1
  53. package/dist/runtime/store.js +54 -5
  54. package/dist/runtime/store.js.map +1 -1
  55. package/dist/runtime/updateCheck.js +41 -8
  56. package/dist/sbom.json +21 -21
  57. package/dist/scanner/control.js +41 -8
  58. package/dist/scanner/data.js +1 -1
  59. package/dist/scanner/discover.d.ts.map +1 -1
  60. package/dist/scanner/discover.js +211 -171
  61. package/dist/scanner/discover.js.map +1 -1
  62. package/dist/scanner/enable.d.ts.map +1 -1
  63. package/dist/scanner/enable.js +440 -57
  64. package/dist/scanner/enable.js.map +1 -1
  65. package/dist/scanner/picker.d.ts.map +1 -1
  66. package/dist/scanner/picker.js +12 -4
  67. package/dist/scanner/picker.js.map +1 -1
  68. package/dist/scanner/service.js +41 -8
  69. package/dist/scanner/update.js +41 -8
  70. package/dist/scanner-release.json +24 -24
  71. package/dist/status.js +41 -8
  72. package/dist/vendor/local-setup.js +44 -11
  73. package/package.json +31 -31
@@ -469,8 +469,8 @@ var init_runtimeSigners = __esm({
469
469
  const result = promisify(execFile)(file, args);
470
470
  if (input === void 0)
471
471
  return result;
472
- return new Promise((resolve5, reject2) => {
473
- void result.then(resolve5, reject2);
472
+ return new Promise((resolve6, reject2) => {
473
+ void result.then(resolve6, reject2);
474
474
  result.child.stdin?.on("error", reject2);
475
475
  result.child.stdin?.end(input);
476
476
  });
@@ -503,8 +503,8 @@ var init_runtimeSigners = __esm({
503
503
  const options = hookOptions(file, args);
504
504
  if (treeListing)
505
505
  options.timeout = 3e4;
506
- return new Promise((resolve5, reject2) => {
507
- const child = execFile(file, args, options, (error, stdout2) => error ? reject2(error) : resolve5({ stdout: stdout2 }));
506
+ return new Promise((resolve6, reject2) => {
507
+ const child = execFile(file, args, options, (error, stdout2) => error ? reject2(error) : resolve6({ stdout: stdout2 }));
508
508
  if (input !== void 0) {
509
509
  child.stdin?.on("error", reject2);
510
510
  child.stdin?.end(input);
@@ -593,6 +593,14 @@ function parseIdentityFile(text) {
593
593
  }
594
594
  };
595
595
  }
596
+ function isConfigurationOnly(text) {
597
+ try {
598
+ const value = JSON.parse(text);
599
+ return !!value && typeof value === "object" && !Array.isArray(value) && !("projectId" in value) && (!("schemaVersion" in value) || value.schemaVersion === 1);
600
+ } catch {
601
+ return false;
602
+ }
603
+ }
596
604
  async function readIdentityFile(dir, options = {}) {
597
605
  const path = join2(dir, ".mnemonik.json");
598
606
  try {
@@ -602,14 +610,8 @@ async function readIdentityFile(dir, options = {}) {
602
610
  if (!stat2.isFile())
603
611
  return { kind: "malformed", detail: "identity path is not a regular file" };
604
612
  const text = await readFile2(path, "utf8");
605
- if (options.selectedRoot) {
606
- try {
607
- const value = JSON.parse(text);
608
- if (value && typeof value === "object" && !Array.isArray(value) && !("projectId" in value) && (!("schemaVersion" in value) || value.schemaVersion === 1))
609
- return { kind: "absent" };
610
- } catch {
611
- }
612
- }
613
+ if ((options.selectedRoot || options.configurationOnlyAbsent) && isConfigurationOnly(text))
614
+ return { kind: "absent" };
613
615
  return parseIdentityFile(text);
614
616
  } catch (error) {
615
617
  if (error.code === "ENOENT")
@@ -634,7 +636,7 @@ var init_projectIdentityFile = __esm({
634
636
  });
635
637
 
636
638
  // packages/shared/dist/repositoryRoot.js
637
- import { lstat as lstat2, readFile as readFile3 } from "node:fs/promises";
639
+ import { lstat as lstat2, readFile as readFile3, realpath } from "node:fs/promises";
638
640
  import { execFile as execFile2 } from "node:child_process";
639
641
  import { homedir } from "node:os";
640
642
  import { dirname as dirname2, join as join3, resolve as resolve2 } from "node:path";
@@ -685,7 +687,7 @@ async function confirmedBoundaryAt(path) {
685
687
  async function boundariesBetween(cwd, root) {
686
688
  const boundaries = [];
687
689
  let directory = cwd;
688
- while (directory !== root) {
690
+ while (!samePath(directory, root)) {
689
691
  const boundary = await confirmedBoundaryAt(directory);
690
692
  if (boundary)
691
693
  boundaries.push(boundary);
@@ -708,37 +710,31 @@ async function containingRepository(root) {
708
710
  directory = parent;
709
711
  }
710
712
  }
711
- async function canonicalBoundaryRoot(boundary) {
712
- if (boundary.kind === "directory")
713
- return boundary.path;
714
- const pointer = /^gitdir:\s*(.+)\s*$/im.exec(await readFile3(join3(boundary.path, ".git"), "utf8"))?.[1];
715
- if (!pointer)
716
- return boundary.path;
717
- const gitDir = resolve2(boundary.path, pointer);
718
- try {
719
- const commonDir = resolve2(gitDir, (await readFile3(join3(gitDir, "commondir"), "utf8")).trim());
720
- return dirname2(commonDir);
721
- } catch (error) {
722
- if (error.code === "ENOENT")
723
- return boundary.path;
724
- throw error;
725
- }
713
+ async function canonicalPath(path) {
714
+ const absolute = resolve2(path);
715
+ return realpath(absolute).catch(() => absolute);
716
+ }
717
+ function samePath(left, right) {
718
+ return process.platform === "win32" ? left.toLowerCase() === right.toLowerCase() : left === right;
726
719
  }
727
720
  async function resolveRepositoryRoot(cwd) {
728
- const absoluteCwd = resolve2(cwd);
721
+ const absoluteCwd = await canonicalPath(cwd);
729
722
  try {
730
723
  const { stdout: stdout2 } = await runGit(absoluteCwd, [
731
724
  "rev-parse",
732
725
  "--show-toplevel",
726
+ "--git-dir",
733
727
  "--git-common-dir"
734
728
  ]);
735
- const [rootLine, commonLine, ...extra] = stdout2.trim().split(/\r?\n/);
736
- if (!rootLine || !commonLine || extra.length) {
729
+ const [rootLine, gitDirLine, commonLine, ...extra] = stdout2.trim().split(/\r?\n/);
730
+ if (!rootLine || !gitDirLine || !commonLine || extra.length) {
737
731
  return { kind: "git_unavailable", detail: "git rev-parse returned an unexpected response" };
738
732
  }
739
- const root = resolve2(rootLine);
740
- const commonDir = resolve2(root, commonLine);
741
- const isLinkedWorktree = commonDir !== join3(root, ".git");
733
+ const fromGit = (line) => canonicalPath(resolve2(absoluteCwd, line));
734
+ const root = await fromGit(rootLine);
735
+ const gitDir = await fromGit(gitDirLine);
736
+ const commonDir = await fromGit(commonLine);
737
+ const isLinkedWorktree = !samePath(gitDir, commonDir);
742
738
  const nested = await boundariesBetween(absoluteCwd, root);
743
739
  if (!isLinkedWorktree && await containingRepository(root)) {
744
740
  const boundary = await boundaryAt(root);
@@ -760,14 +756,41 @@ async function resolveRepositoryRoot(cwd) {
760
756
  return { kind: "git_unavailable", detail: failure.message };
761
757
  }
762
758
  }
759
+ async function linkedWorktreeCommonDir(dir) {
760
+ try {
761
+ if (!(await lstat2(join3(dir, ".git"))).isFile())
762
+ return void 0;
763
+ const pointer = /^gitdir:\s*(.+?)\s*$/im.exec(await readFile3(join3(dir, ".git"), "utf8"))?.[1];
764
+ if (!pointer)
765
+ return void 0;
766
+ const gitDir = resolve2(dir, pointer);
767
+ const common = (await readFile3(join3(gitDir, "commondir"), "utf8")).trim();
768
+ return common ? await canonicalPath(resolve2(gitDir, common)) : void 0;
769
+ } catch {
770
+ return void 0;
771
+ }
772
+ }
773
+ async function mainCheckout(worktreeTop, commonDir) {
774
+ try {
775
+ const { stdout: stdout2 } = await runGit(worktreeTop, ["worktree", "list", "--porcelain"]);
776
+ const first = stdout2.split(/\r?\n\r?\n/)[0] ?? "";
777
+ const path = /^worktree (.+)$/m.exec(first)?.[1];
778
+ if (path) {
779
+ const main = await canonicalPath(resolve2(worktreeTop, path));
780
+ return /^bare$/m.test(first) ? dirname2(main) : main;
781
+ }
782
+ } catch {
783
+ }
784
+ return dirname2(commonDir);
785
+ }
763
786
  async function readPlainIdentity(start) {
764
- const home = resolve2(homedir());
787
+ const home = await canonicalPath(homedir());
765
788
  let directory = start;
766
789
  while (true) {
767
- const result = await readIdentityFile(directory);
790
+ const result = await readIdentityFile(directory, { configurationOnlyAbsent: true });
768
791
  if (result.kind !== "absent")
769
792
  return { root: directory, result };
770
- if (directory === home)
793
+ if (samePath(directory, home))
771
794
  break;
772
795
  const parent = dirname2(directory);
773
796
  if (parent === directory)
@@ -776,59 +799,59 @@ async function readPlainIdentity(start) {
776
799
  }
777
800
  return { root: start, result: { kind: "absent" } };
778
801
  }
779
- function withBase(repository, root, nested) {
780
- return { root, repository, nested };
781
- }
782
- async function resolveProjectIdentity(cwd, options = {}) {
783
- const resolved = await resolveRepositoryRoot(cwd);
784
- const repository = resolved.kind === "git_unavailable" ? { kind: "plain", root: resolve2(cwd) } : resolved;
785
- if (repository.kind === "plain") {
786
- const { root, result } = options.selectedRoot ? { root: repository.root, result: await readIdentityFile(repository.root, options) } : await readPlainIdentity(repository.root);
787
- const base2 = withBase(repository, root, []);
788
- if (result.kind === "ok")
789
- return { ...base2, ...result };
790
- if (result.kind === "unknown_version")
791
- return { ...base2, ...result, path: root };
792
- if (result.kind === "malformed")
793
- return { ...base2, ...result, path: root };
794
- return { ...base2, kind: "absent" };
795
- }
796
- const ownRoot = repository.isLinkedWorktree ? dirname2(repository.commonDir) : repository.root;
797
- const outer = repository.isLinkedWorktree ? void 0 : await containingRepository(repository.root);
798
- const own = await readIdentityFile(ownRoot, options);
799
- if (!outer || options.selectedRoot || own.kind === "ok") {
800
- const base2 = withBase(repository, ownRoot, repository.nested);
801
- if (own.kind === "ok")
802
- return { ...base2, ...own };
803
- if (own.kind === "unknown_version")
804
- return { ...base2, ...own, path: ownRoot };
805
- if (own.kind === "malformed")
806
- return { ...base2, ...own, path: ownRoot };
807
- return { ...base2, kind: "absent" };
808
- }
809
- const nested = repository.nested;
810
- const parentRoot = await canonicalBoundaryRoot(outer);
811
- const parent = await readIdentityFile(parentRoot);
812
- const base = withBase(repository, parentRoot, nested);
813
- if (own.kind === "unknown_version")
814
- return { ...base, ...own, path: ownRoot };
815
- if (own.kind === "malformed")
816
- return { ...base, ...own, path: ownRoot };
817
- if (parent.kind === "unknown_version")
818
- return { ...base, ...parent, path: parentRoot };
819
- if (parent.kind === "malformed")
820
- return { ...base, ...parent, path: parentRoot };
821
- if (!options.allowNestedInherit) {
822
- return {
823
- ...base,
824
- kind: "nested",
825
- ...parent.kind === "ok" ? { parentIdentity: parent.identity } : {}
826
- };
802
+ async function nearestIdentity(start) {
803
+ const home = await canonicalPath(homedir());
804
+ let directory = start;
805
+ while (true) {
806
+ const commonDir = await linkedWorktreeCommonDir(directory);
807
+ if (commonDir)
808
+ return { worktreeTop: directory, commonDir };
809
+ const result = await readIdentityFile(directory, { configurationOnlyAbsent: true });
810
+ if (result.kind !== "absent")
811
+ return { root: directory, result };
812
+ if (samePath(directory, home))
813
+ return void 0;
814
+ const parent = dirname2(directory);
815
+ if (parent === directory)
816
+ return void 0;
817
+ directory = parent;
827
818
  }
828
- if (parent.kind === "ok")
829
- return { ...base, kind: "ok", identity: parent.identity };
819
+ }
820
+ async function resolveNearestProjectIdentity(cwd) {
821
+ const start = await canonicalPath(cwd);
822
+ const resolved = await resolveRepositoryRoot(start);
823
+ const repository = resolved.kind === "git_unavailable" ? { kind: "plain", root: start } : resolved;
824
+ const nested = repository.kind === "git" ? repository.nested : [];
825
+ let found = await nearestIdentity(start);
826
+ if (found && "worktreeTop" in found) {
827
+ found = await readPlainIdentity(await mainCheckout(found.worktreeTop, found.commonDir));
828
+ }
829
+ if (found) {
830
+ return shapedIdentity(found, await projectFolderRepository(found.root, repository), nested);
831
+ }
832
+ return { ...withBase(repository, repository.root, nested), kind: "absent" };
833
+ }
834
+ function shapedIdentity(found, repository, nested) {
835
+ const base = withBase(repository, found.root, nested);
836
+ if (found.result.kind === "ok")
837
+ return { ...base, ...found.result };
838
+ if (found.result.kind === "unknown_version")
839
+ return { ...base, ...found.result, path: found.root };
840
+ if (found.result.kind === "malformed")
841
+ return { ...base, ...found.result, path: found.root };
830
842
  return { ...base, kind: "absent" };
831
843
  }
844
+ async function projectFolderRepository(projectRoot, known) {
845
+ if (known.kind === "git" && samePath(known.root, projectRoot))
846
+ return known;
847
+ const repository = await resolveRepositoryRoot(projectRoot);
848
+ if (repository.kind === "git" && samePath(repository.root, projectRoot))
849
+ return repository;
850
+ return { kind: "plain", root: projectRoot };
851
+ }
852
+ function withBase(repository, root, nested) {
853
+ return { root, repository, nested };
854
+ }
832
855
  var GIT_TIMEOUT_MS, GIT_ENV_KEYS;
833
856
  var init_repositoryRoot = __esm({
834
857
  "packages/shared/dist/repositoryRoot.js"() {
@@ -848,11 +871,564 @@ var init_repositoryRoot = __esm({
848
871
  }
849
872
  });
850
873
 
874
+ // packages/shared/dist/protectedPaths.js
875
+ import { homedir as homedir2 } from "node:os";
876
+ import { posix, win32 as win322 } from "node:path";
877
+ function mnemonikStateDirectory(platform = process.platform, env = process.env, home = homedir2()) {
878
+ if (env.MNEMONIK_STATE_DIR)
879
+ return env.MNEMONIK_STATE_DIR;
880
+ if (platform === "win32")
881
+ return win322.join(env.LOCALAPPDATA || win322.join(home, "AppData", "Local"), "Mnemonik");
882
+ if (platform === "darwin")
883
+ return posix.join(home, "Library", "Application Support", "Mnemonik");
884
+ return posix.join(env.XDG_STATE_HOME || posix.join(home, ".local", "state"), "mnemonik");
885
+ }
886
+ var init_protectedPaths = __esm({
887
+ "packages/shared/dist/protectedPaths.js"() {
888
+ "use strict";
889
+ }
890
+ });
891
+
892
+ // packages/shared/dist/secretPatterns.js
893
+ function shannonEntropy(token) {
894
+ const counts = /* @__PURE__ */ new Map();
895
+ for (const ch of token)
896
+ counts.set(ch, (counts.get(ch) ?? 0) + 1);
897
+ let bits = 0;
898
+ for (const count of counts.values()) {
899
+ const p = count / token.length;
900
+ bits -= p * Math.log2(p);
901
+ }
902
+ return bits;
903
+ }
904
+ function maxLowercaseRun(token) {
905
+ let max = 0;
906
+ let current = 0;
907
+ for (const ch of token) {
908
+ if (ch >= "a" && ch <= "z") {
909
+ current += 1;
910
+ if (current > max)
911
+ max = current;
912
+ } else {
913
+ current = 0;
914
+ }
915
+ }
916
+ return max;
917
+ }
918
+ function isHighEntropySecret(token) {
919
+ if (token.length < ENTROPY_MIN_LENGTH)
920
+ return false;
921
+ const stripped = token.replace(/[-_]/g, "");
922
+ if (stripped.length > 0 && ENTROPY_HEXISH_RE.test(stripped))
923
+ return false;
924
+ if (!/[a-z]/.test(token) || !/[A-Z]/.test(token) || !/[0-9]/.test(token))
925
+ return false;
926
+ if (ENTROPY_STRUCTURED_RE.test(token) && token.split(/[/_-]/).every((seg) => seg.length <= ENTROPY_MAX_STRUCTURED_SEGMENT)) {
927
+ return false;
928
+ }
929
+ if (maxLowercaseRun(token) > ENTROPY_MAX_LOWERCASE_RUN)
930
+ return false;
931
+ return shannonEntropy(token) >= ENTROPY_MIN_BITS_PER_CHAR;
932
+ }
933
+ function redactHighEntropyTokens(text) {
934
+ if (!text)
935
+ return text;
936
+ return text.replace(ENTROPY_CANDIDATE_RE, (match, offset) => {
937
+ const before = text.slice(Math.max(0, offset - 8), offset);
938
+ if (!ENTROPY_CONTEXT_RE.test(before))
939
+ return match;
940
+ const operatorContext = before.replace(/["'`]$/, "").trimEnd().slice(0, -1);
941
+ if (ENTROPY_OPERATOR_TAIL_RE.test(operatorContext))
942
+ return match;
943
+ if (ENV_READ_VALUE_RE.test(before + match))
944
+ return match;
945
+ return isHighEntropySecret(match) ? SECRET_REDACTION_PLACEHOLDER : match;
946
+ });
947
+ }
948
+ function scrubSecrets(text) {
949
+ if (!text)
950
+ return text;
951
+ let result = text;
952
+ for (const pattern of SECRET_PATTERNS) {
953
+ result = result.replace(pattern, (match) => ENV_READ_VALUE_RE.test(match) ? match : SECRET_REDACTION_PLACEHOLDER);
954
+ }
955
+ return redactHighEntropyTokens(result);
956
+ }
957
+ var SECRET_REDACTION_PLACEHOLDER, SECRET_PATTERNS, ENV_READ_VALUE_RE, ENTROPY_MIN_LENGTH, ENTROPY_MIN_BITS_PER_CHAR, ENTROPY_MAX_STRUCTURED_SEGMENT, ENTROPY_MAX_LOWERCASE_RUN, ENTROPY_CANDIDATE_RE, ENTROPY_CONTEXT_RE, ENTROPY_OPERATOR_TAIL_RE, ENTROPY_HEXISH_RE, ENTROPY_STRUCTURED_RE;
958
+ var init_secretPatterns = __esm({
959
+ "packages/shared/dist/secretPatterns.js"() {
960
+ "use strict";
961
+ SECRET_REDACTION_PLACEHOLDER = "[REDACTED]";
962
+ SECRET_PATTERNS = [
963
+ /(?:api[_-]?key|secret|token|password|credential|auth)\s*[:=]\s*(?=\S*[A-Za-z0-9])\S+/gi,
964
+ // Stripe-shape: (sk|pk)_(live|test)_<24+ alphanumerics>. Catches modern
965
+ // Stripe keys whose body is split by an environment separator that
966
+ // breaks the contiguous-alphanum pattern below. Required `live|test`
967
+ // literal prevents false-positives on snake_case identifiers like
968
+ // pkg_install_helper_function_xyz_abc_def.
969
+ /(?:sk|pk)_(?:live|test)_[a-zA-Z0-9]{24,}/g,
970
+ /(?:sk|pk)[-_][a-zA-Z0-9]{20,}/g,
971
+ /ghp_[a-zA-Z0-9]{36}/g,
972
+ /glpat-[a-zA-Z0-9-]{20,}/g,
973
+ // PEM private keys. The BLOCK pattern runs first so header->footer (the
974
+ // actual key material) is redacted as one unit; the header-only pattern
975
+ // stays as the fallback for the case a chunk boundary lands between
976
+ // header and footer, where the block never closes inside the chunk.
977
+ // `[\s\S]*?` is lazy so two adjacent keys don't merge into one match.
978
+ /-----BEGIN (?:RSA |EC |DSA |OPENSSH )?PRIVATE KEY-----[\s\S]*?-----END (?:RSA |EC |DSA |OPENSSH )?PRIVATE KEY-----/g,
979
+ /-----BEGIN (?:RSA |EC |DSA |OPENSSH )?PRIVATE KEY-----/g,
980
+ // AWS access key IDs. The 16-char body must be uppercase-alphanumeric and
981
+ // \b-delimited, so prose ("AKIAMIA is a place name") cannot match.
982
+ /\bAKIA[0-9A-Z]{16}\b/g,
983
+ /\bASIA[0-9A-Z]{16}\b/g,
984
+ // AWS secret access key, labeled. The generic key=value pattern above does
985
+ // NOT cover this: its `secret` alternative must be followed immediately by
986
+ // `[:=]`, and here it is followed by `_access_key`.
987
+ /\baws_secret_access_key\s*[:=]\s*[A-Za-z0-9/+=]{40}\b/gi,
988
+ // JWT: three base64url segments. Length minimums keep dotted identifiers
989
+ // (`payload.header.signature`) out.
990
+ /\beyJ[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{5,}\b/g,
991
+ /\bxox[baprs]-[A-Za-z0-9-]{10,}\b/g,
992
+ /\bAIza[0-9A-Za-z_-]{35}\b/g,
993
+ /\bsk-ant-[A-Za-z0-9_-]{20,}\b/g,
994
+ /\b(?:npm_|npms_)[A-Za-z0-9]{36,}\b/g,
995
+ // scheme://userinfo@: only the credential segment is consumed, so the
996
+ // host and path survive for anyone reading the code. Userinfo may contain
997
+ // empty fields or additional `:` / `@` characters; greediness selects its
998
+ // final `@`, while `/`, `?`, and `#` keep paths and queries out of the match.
999
+ /\b[a-z][a-z0-9+.-]*:\/\/(?=[^/\s?#]*[A-Za-z0-9][^/\s?#]*@)[^/\s?#]*@/gi,
1000
+ // Authorization: Bearer <token>. The token class deliberately excludes
1001
+ // `<` and `$`, so documentation placeholders (`Bearer <your-api-key>`,
1002
+ // `Bearer $MNEMONIK_PROXY_TOKEN`) are left intact - redacting those would
1003
+ // destroy the instruction without hiding a secret.
1004
+ /Authorization\s*:\s*Bearer\s+(?=[._~+/-]*[A-Za-z0-9])[A-Za-z0-9._~+/-]+=*/gi,
1005
+ // sshpass -p <password> (also -p'<password>' and -p"<password>"): only the
1006
+ // password word is consumed, so the command stays readable. A shell
1007
+ // expansion (`$PASS`, `"$PASS"`), a `<placeholder>`, an earlier redaction
1008
+ // and quoted punctuation are left, and so is prose about the flag: a bare all-lower-case
1009
+ // word counts only when the command sshpass runs (ssh, scp, ...) follows it.
1010
+ // Eleven scanned chunks in production carried a root password this way
1011
+ // (2026-10-12); the server's memory redactor (src/core/redactCredentials.ts)
1012
+ // has the same rule.
1013
+ /(?<=\bsshpass(?:[ \t]+-[a-oq-zA-Z]\S*)*[ \t]+-p[ \t]*)(?:'(?![$<]|\[REDACTED\])(?=[^'\n]*[A-Za-z0-9])[^'\n]+'|"(?![$<]|\[REDACTED\])(?=[^"\n]*[A-Za-z0-9])[^"\n]+"|(?![$<'"`(),[{\\]|\[REDACTED\]|[a-z]+[.,:;!?)]*(?![^\s'"`])(?![ \t]+(?:ssh|scp|sftp|rsync|ssh-copy-id|ansible[\w-]*)(?![\w-])))[^\s'"`;|&]+)/g
1014
+ ];
1015
+ ENV_READ_VALUE_RE = /[:=]\s*(?:await\s+)?(?:process\.env[.[]|import\.meta\.env[.[])/;
1016
+ ENTROPY_MIN_LENGTH = 36;
1017
+ ENTROPY_MIN_BITS_PER_CHAR = 4;
1018
+ ENTROPY_MAX_STRUCTURED_SEGMENT = 16;
1019
+ ENTROPY_MAX_LOWERCASE_RUN = 5;
1020
+ ENTROPY_CANDIDATE_RE = /[A-Za-z0-9+/_-]{35}[A-Za-z0-9+/_-]+={0,2}/g;
1021
+ ENTROPY_CONTEXT_RE = /(?:["'`]|[:=]\s*["'`]?)$/;
1022
+ ENTROPY_OPERATOR_TAIL_RE = /[=!<>+\-*/%&|^]$/;
1023
+ ENTROPY_HEXISH_RE = /^[0-9a-fA-F]+$/;
1024
+ ENTROPY_STRUCTURED_RE = /^[A-Za-z0-9]+(?:[/_-][A-Za-z0-9]+)+$/;
1025
+ }
1026
+ });
1027
+
1028
+ // packages/shared/dist/hookTrace.js
1029
+ import { randomBytes } from "node:crypto";
1030
+ import { closeSync, constants, fstatSync, lstatSync as lstatSync2, mkdirSync as mkdirSync2, openSync, renameSync as renameSync2, statSync, unlinkSync as unlinkSync2, writeSync } from "node:fs";
1031
+ import { dirname as dirname3, join as join4 } from "node:path";
1032
+ function hookTraceEnabled(env = process.env) {
1033
+ const value = env[HOOK_TRACE_ENV]?.trim().toLowerCase();
1034
+ return !value || !["off", "0", "false", "no", "disabled"].includes(value);
1035
+ }
1036
+ function hookTraceDirectory(stateDir = mnemonikStateDirectory()) {
1037
+ return join4(stateDir, "hook-trace");
1038
+ }
1039
+ function hookTraceFile(host, stateDir) {
1040
+ return join4(hookTraceDirectory(stateDir), `${host}.jsonl`);
1041
+ }
1042
+ function ensurePrivateDirectory(directory) {
1043
+ if (preparedDirectories.has(directory))
1044
+ return;
1045
+ if (process.platform === "win32")
1046
+ ensureWindowsPrivateDirectorySync(directory);
1047
+ else {
1048
+ mkdirSync2(directory, { recursive: true, mode: 448 });
1049
+ const stat2 = lstatSync2(directory);
1050
+ if (!stat2.isDirectory() || stat2.isSymbolicLink() || process.getuid && stat2.uid !== process.getuid() || stat2.mode & 63)
1051
+ throw new Error("hook trace directory is not private");
1052
+ }
1053
+ preparedDirectories.add(directory);
1054
+ }
1055
+ function openTrace(file) {
1056
+ return openSync(file, constants.O_WRONLY | constants.O_APPEND | constants.O_CREAT | (constants.O_NOFOLLOW ?? 0), 384);
1057
+ }
1058
+ function sameFile(file, fd) {
1059
+ try {
1060
+ const opened = fstatSync(fd, { bigint: true });
1061
+ const named = statSync(file, { bigint: true });
1062
+ return opened.ino === named.ino && opened.dev === named.dev;
1063
+ } catch {
1064
+ return false;
1065
+ }
1066
+ }
1067
+ function takeLock(lock) {
1068
+ for (let attempt = 0; attempt < 2; attempt += 1) {
1069
+ try {
1070
+ closeSync(openSync(lock, "wx", 384));
1071
+ return true;
1072
+ } catch {
1073
+ try {
1074
+ if (attempt > 0 || Date.now() - lstatSync2(lock).mtimeMs < STALE_LOCK_MS)
1075
+ return false;
1076
+ unlinkSync2(lock);
1077
+ } catch {
1078
+ }
1079
+ }
1080
+ }
1081
+ return false;
1082
+ }
1083
+ function rotate(file, fd) {
1084
+ const lock = `${file}.lock`;
1085
+ if (!takeLock(lock))
1086
+ return;
1087
+ try {
1088
+ if (sameFile(file, fd))
1089
+ renameSync2(file, `${file}.1`);
1090
+ } finally {
1091
+ try {
1092
+ unlinkSync2(lock);
1093
+ } catch {
1094
+ }
1095
+ }
1096
+ }
1097
+ function appendHookTraceLine(file, record2) {
1098
+ ensurePrivateDirectory(dirname3(file));
1099
+ const line = `${JSON.stringify(record2)}
1100
+ `;
1101
+ let fd = openTrace(file);
1102
+ try {
1103
+ const stat2 = fstatSync(fd);
1104
+ if (!stat2.isFile() || process.getuid && stat2.uid !== process.getuid())
1105
+ throw new Error("hook trace file is not ours");
1106
+ if (stat2.size > 0 && stat2.size + Buffer.byteLength(line) > HOOK_TRACE_MAX_BYTES) {
1107
+ try {
1108
+ rotate(file, fd);
1109
+ } finally {
1110
+ closeSync(fd);
1111
+ fd = -1;
1112
+ }
1113
+ fd = openTrace(file);
1114
+ }
1115
+ writeSync(fd, line);
1116
+ } finally {
1117
+ if (fd >= 0)
1118
+ closeSync(fd);
1119
+ }
1120
+ }
1121
+ function byteSize(value) {
1122
+ if (typeof value === "string")
1123
+ return Buffer.byteLength(value);
1124
+ try {
1125
+ return Buffer.byteLength(JSON.stringify(value) ?? "");
1126
+ } catch {
1127
+ return 0;
1128
+ }
1129
+ }
1130
+ function safe(value, pattern) {
1131
+ return typeof value === "string" && pattern.test(value) ? value : void 0;
1132
+ }
1133
+ function sizesOf(object, prefix = "") {
1134
+ const sizes = {};
1135
+ for (const key of Object.keys(object).slice(0, MAX_FIELDS))
1136
+ if (SAFE_NAME.test(key))
1137
+ sizes[prefix + key] = byteSize(object[key]);
1138
+ return sizes;
1139
+ }
1140
+ function summarizeHookOutput(text) {
1141
+ const output = { bytes: Buffer.byteLength(text), sizes: {} };
1142
+ let value;
1143
+ try {
1144
+ value = JSON.parse(text);
1145
+ } catch {
1146
+ return output;
1147
+ }
1148
+ if (!isObject(value))
1149
+ return output;
1150
+ const nested = isObject(value.hookSpecificOutput) ? value.hookSpecificOutput : void 0;
1151
+ const { hookSpecificOutput: _nested, ...top } = value;
1152
+ output.sizes = { ...sizesOf(top), ...nested ? sizesOf(nested, "hookSpecificOutput.") : {} };
1153
+ const permission = safe(value.permission ?? nested?.permissionDecision, SAFE_ENUM);
1154
+ const decision = safe(value.decision, SAFE_ENUM);
1155
+ if (permission)
1156
+ output.permission = permission;
1157
+ if (decision)
1158
+ output.decision = decision;
1159
+ if (typeof value.continue === "boolean")
1160
+ output.continue = value.continue;
1161
+ return output;
1162
+ }
1163
+ function routeOf(input) {
1164
+ try {
1165
+ const href = typeof input === "string" ? input : input instanceof URL ? input.href : input.url;
1166
+ return new URL(String(href)).pathname.split("/").map((segment) => segment.length >= 16 && /\d/.test(segment) || /[%@]/.test(segment) ? ":id" : segment).join("/").slice(0, 200);
1167
+ } catch {
1168
+ return "unknown";
1169
+ }
1170
+ }
1171
+ function errorKind(error) {
1172
+ const failure = error;
1173
+ if (failure?.name === "TimeoutError")
1174
+ return "timeout";
1175
+ if (failure?.name === "AbortError")
1176
+ return "aborted";
1177
+ const code = failure?.cause?.code ?? failure?.code;
1178
+ if (typeof code === "string" && /^[A-Z0-9_]{2,40}$/.test(code))
1179
+ return code;
1180
+ return safe(failure?.name, /^\w{1,40}$/) ?? "error";
1181
+ }
1182
+ function scrubHookTraceMessage(error) {
1183
+ const text = error instanceof Error ? error.message : String(error);
1184
+ return scrubSecrets(text.replace(/\s+/g, " ").trim()).slice(0, MAX_ERROR_CHARS);
1185
+ }
1186
+ function noteHookTraceProject(projectId) {
1187
+ active?.project(projectId);
1188
+ }
1189
+ function startHookTrace(options) {
1190
+ if (active)
1191
+ return active;
1192
+ let file;
1193
+ try {
1194
+ if (hookTraceEnabled(options.env))
1195
+ file = hookTraceFile(options.host, options.stateDir ?? mnemonikStateDirectory(process.platform, options.env ?? process.env));
1196
+ } catch {
1197
+ file = void 0;
1198
+ }
1199
+ const trace = new HookTrace(options.host, file, options.hookVersion);
1200
+ active = trace;
1201
+ if (!file)
1202
+ return trace;
1203
+ const original = globalThis.fetch;
1204
+ if (typeof original === "function") {
1205
+ globalThis.fetch = async (input, init) => {
1206
+ const token = trace.callStarted(input);
1207
+ try {
1208
+ const response = await original.call(globalThis, input, init);
1209
+ trace.settle(token, response.status);
1210
+ return response;
1211
+ } catch (error) {
1212
+ trace.settle(token, void 0, error);
1213
+ throw error;
1214
+ }
1215
+ };
1216
+ }
1217
+ process.once("exit", (code) => trace.finish(typeof code === "number" ? code : 0));
1218
+ trace.open();
1219
+ return trace;
1220
+ }
1221
+ var HOOK_TRACE_MAX_BYTES, HOOK_TRACE_ENV, MAX_FIELDS, MAX_CALLS, MAX_ERROR_CHARS, SAFE_ID, SAFE_NAME, SAFE_ENUM, preparedDirectories, STALE_LOCK_MS, isObject, HookTrace, active;
1222
+ var init_hookTrace = __esm({
1223
+ "packages/shared/dist/hookTrace.js"() {
1224
+ "use strict";
1225
+ init_protectedPaths();
1226
+ init_runtimeSigners();
1227
+ init_secretPatterns();
1228
+ HOOK_TRACE_MAX_BYTES = 2 * 1024 * 1024;
1229
+ HOOK_TRACE_ENV = "MNEMONIK_HOOK_TRACE";
1230
+ MAX_FIELDS = 64;
1231
+ MAX_CALLS = 64;
1232
+ MAX_ERROR_CHARS = 80;
1233
+ SAFE_ID = /^[A-Za-z0-9@._+:-]{1,128}$/;
1234
+ SAFE_NAME = /^[A-Za-z0-9@._+:/-]{1,120}$/;
1235
+ SAFE_ENUM = /^[A-Za-z_]{1,24}$/;
1236
+ preparedDirectories = /* @__PURE__ */ new Set();
1237
+ STALE_LOCK_MS = 1e4;
1238
+ isObject = (value) => !!value && typeof value === "object" && !Array.isArray(value);
1239
+ HookTrace = class {
1240
+ host;
1241
+ file;
1242
+ hookVersion;
1243
+ id = randomBytes(8).toString("hex");
1244
+ startedAt = performance.now();
1245
+ opened = false;
1246
+ started = false;
1247
+ ended = false;
1248
+ event = null;
1249
+ outputSummary = null;
1250
+ error;
1251
+ errorKind;
1252
+ projectId;
1253
+ calls = [];
1254
+ inFlight = /* @__PURE__ */ new Set();
1255
+ constructor(host, file, hookVersion) {
1256
+ this.host = host;
1257
+ this.file = file;
1258
+ this.hookVersion = hookVersion;
1259
+ }
1260
+ get enabled() {
1261
+ return this.file !== void 0;
1262
+ }
1263
+ write(record2) {
1264
+ if (!this.file)
1265
+ return;
1266
+ try {
1267
+ appendHookTraceLine(this.file, record2);
1268
+ } catch {
1269
+ }
1270
+ }
1271
+ /**
1272
+ * Write the `start` line: pid, run id and time since process start, nothing
1273
+ * from the editor. Runs before stdin is read, so a run killed while the
1274
+ * editor is still sending its event leaves this line.
1275
+ */
1276
+ open() {
1277
+ if (!this.file || this.opened)
1278
+ return;
1279
+ this.opened = true;
1280
+ this.write({
1281
+ v: 1,
1282
+ phase: "start",
1283
+ ts: (/* @__PURE__ */ new Date()).toISOString(),
1284
+ host: this.host,
1285
+ ...this.hookVersion ? { hookVersion: this.hookVersion } : {},
1286
+ pid: process.pid,
1287
+ id: this.id,
1288
+ processMs: Math.round(process.uptime() * 1e3)
1289
+ });
1290
+ }
1291
+ /** Write the `input` line. `input` is the parsed payload, or null when none parsed. */
1292
+ begin(input, event) {
1293
+ if (!this.file || this.started)
1294
+ return;
1295
+ this.open();
1296
+ this.started = true;
1297
+ try {
1298
+ const payload = isObject(input) ? input : {};
1299
+ this.event = safe(event, SAFE_NAME) ?? safe(payload.hook_event_name, SAFE_NAME) ?? safe(payload.hookEventName, SAFE_NAME) ?? null;
1300
+ const roots = Array.isArray(payload.workspace_roots) ? payload.workspace_roots : [];
1301
+ const cwd = typeof payload.cwd === "string" && payload.cwd ? payload.cwd : typeof roots[0] === "string" ? roots[0] : process.cwd();
1302
+ const record2 = {
1303
+ v: 1,
1304
+ phase: "input",
1305
+ ts: (/* @__PURE__ */ new Date()).toISOString(),
1306
+ host: this.host,
1307
+ pid: process.pid,
1308
+ id: this.id,
1309
+ event: this.event,
1310
+ inputBytes: isObject(input) ? byteSize(input) : 0,
1311
+ fields: sizesOf(payload),
1312
+ readMs: Math.round(performance.now() - this.startedAt)
1313
+ };
1314
+ const hostVersion = safe(payload.cursor_version ?? payload.version, /^[\w.+-]{1,40}$/);
1315
+ if (hostVersion)
1316
+ record2.hostVersion = hostVersion;
1317
+ const ids = [
1318
+ ["sessionId", "session_id"],
1319
+ ["conversationId", "conversation_id"],
1320
+ ["generationId", "generation_id"],
1321
+ ["agentId", "agent_id"],
1322
+ ["agentId", "subagent_id"],
1323
+ ["turnId", "turn_id"]
1324
+ ];
1325
+ for (const [name, key] of ids) {
1326
+ const id = safe(payload[key], SAFE_ID);
1327
+ if (id && record2[name] === void 0)
1328
+ record2[name] = id;
1329
+ }
1330
+ const tool = safe(payload.tool_name, SAFE_NAME);
1331
+ if (tool)
1332
+ record2.tool = tool;
1333
+ if (cwd.length <= 1024)
1334
+ record2.cwd = cwd;
1335
+ this.write(record2);
1336
+ } catch {
1337
+ }
1338
+ }
1339
+ /** The exact text the hook wrote to stdout (or nothing). */
1340
+ output(text) {
1341
+ if (!this.file || text === null || text === void 0)
1342
+ return;
1343
+ try {
1344
+ this.outputSummary = summarizeHookOutput(text);
1345
+ } catch {
1346
+ }
1347
+ }
1348
+ /** The error the hook failed open on. */
1349
+ fail(error) {
1350
+ if (!this.file)
1351
+ return;
1352
+ try {
1353
+ this.errorKind = errorKind(error);
1354
+ this.error = scrubHookTraceMessage(error);
1355
+ } catch {
1356
+ this.errorKind = "error";
1357
+ }
1358
+ }
1359
+ project(projectId) {
1360
+ if (projectId && !this.projectId && SAFE_ID.test(projectId))
1361
+ this.projectId = projectId;
1362
+ }
1363
+ /** A call has gone out; {@link settle} records it, {@link finish} records it as pending. */
1364
+ callStarted(input) {
1365
+ const token = { input, startedAt: performance.now() };
1366
+ if (this.file)
1367
+ this.inFlight.add(token);
1368
+ return token;
1369
+ }
1370
+ settle(token, status, error) {
1371
+ this.inFlight.delete(token);
1372
+ this.call(token.input, token.startedAt, status, error);
1373
+ }
1374
+ call(input, startedAt, status, error) {
1375
+ if (!this.file || this.calls.length >= MAX_CALLS)
1376
+ return;
1377
+ try {
1378
+ this.calls.push({
1379
+ path: routeOf(input),
1380
+ ...status !== void 0 ? { status } : { error: errorKind(error) },
1381
+ ms: Math.round(performance.now() - startedAt)
1382
+ });
1383
+ } catch {
1384
+ }
1385
+ }
1386
+ /** Write the `end` line once. Runs from the process `exit` event. */
1387
+ finish(exitCode = 0) {
1388
+ if (!this.file || this.ended)
1389
+ return;
1390
+ this.open();
1391
+ this.ended = true;
1392
+ for (const { input, startedAt } of this.inFlight) {
1393
+ if (this.calls.length >= MAX_CALLS)
1394
+ break;
1395
+ try {
1396
+ this.calls.push({
1397
+ path: routeOf(input),
1398
+ pending: true,
1399
+ ms: Math.round(performance.now() - startedAt)
1400
+ });
1401
+ } catch {
1402
+ }
1403
+ }
1404
+ this.inFlight.clear();
1405
+ this.write({
1406
+ v: 1,
1407
+ phase: "end",
1408
+ ts: (/* @__PURE__ */ new Date()).toISOString(),
1409
+ host: this.host,
1410
+ pid: process.pid,
1411
+ id: this.id,
1412
+ event: this.event,
1413
+ ...this.projectId ? { projectId: this.projectId } : {},
1414
+ output: this.outputSummary,
1415
+ calls: this.calls,
1416
+ durationMs: Math.round(performance.now() - this.startedAt),
1417
+ processMs: Math.round(process.uptime() * 1e3),
1418
+ exitCode,
1419
+ ...this.errorKind ? { errorKind: this.errorKind } : {},
1420
+ ...this.error ? { error: this.error } : {}
1421
+ });
1422
+ }
1423
+ };
1424
+ }
1425
+ });
1426
+
851
1427
  // packages/shared/dist/runtimeReader.js
852
1428
  import { createHash as createHash2 } from "node:crypto";
853
- import { constants } from "node:fs";
1429
+ import { constants as constants2 } from "node:fs";
854
1430
  import { lstat as lstat3, open, readdir } from "node:fs/promises";
855
- import { dirname as dirname3, join as join4, parse, resolve as resolve3, sep as sep2 } from "node:path";
1431
+ import { dirname as dirname4, join as join5, parse, resolve as resolve3, sep as sep2 } from "node:path";
856
1432
  function safePath(path) {
857
1433
  if (typeof path !== "string" || !/^[a-zA-Z0-9_@.+/-]+$/.test(path) || path.split("/").some((p) => !p || p === "." || p === "..") || path.includes(":"))
858
1434
  throw new RuntimeError("permission");
@@ -903,7 +1479,7 @@ var init_runtimeReader = __esm({
903
1479
  pointerPath(artifact) {
904
1480
  if (!["cli", "scanner", "claude-code", "codex", "cursor", "grok"].includes(artifact))
905
1481
  throw new RuntimeError("permission");
906
- return join4(this.state, "runtimes", artifact, "current");
1482
+ return join5(this.state, "runtimes", artifact, "current");
907
1483
  }
908
1484
  guardedPath(path) {
909
1485
  const absolute = resolve3(path);
@@ -913,7 +1489,7 @@ var init_runtimeReader = __esm({
913
1489
  }
914
1490
  auditRoot(path) {
915
1491
  const [area, artifact] = path.slice(this.state.length + 1).split(sep2);
916
- return area === "runtimes" ? join4(this.state, area, ...artifact ? [artifact] : []) : path;
1492
+ return area === "runtimes" ? join5(this.state, area, ...artifact ? [artifact] : []) : path;
917
1493
  }
918
1494
  traceAudit(event) {
919
1495
  const level = process.env.MNEMONIK_AUDIT_TRACE;
@@ -930,7 +1506,7 @@ var init_runtimeReader = __esm({
930
1506
  invalidateAudit(path) {
931
1507
  const root = this.auditRoot(path);
932
1508
  for (const key of this.windowsAudit.keys())
933
- if (key === root || (!this.options.cacheDirectories || root.startsWith(join4(this.state, "runtimes") + sep2)) && key.startsWith(root + sep2))
1509
+ if (key === root || (!this.options.cacheDirectories || root.startsWith(join5(this.state, "runtimes") + sep2)) && key.startsWith(root + sep2))
934
1510
  this.windowsAudit.delete(key);
935
1511
  }
936
1512
  audit(paths) {
@@ -964,7 +1540,7 @@ var init_runtimeReader = __esm({
964
1540
  pending.add(part);
965
1541
  if (part === this.state)
966
1542
  break;
967
- part = dirname3(part);
1543
+ part = dirname4(part);
968
1544
  }
969
1545
  }
970
1546
  if (!pending.size)
@@ -974,7 +1550,7 @@ var init_runtimeReader = __esm({
974
1550
  const ancestors = [...pending].filter((path) => path !== root && !path.startsWith(root + sep2));
975
1551
  const tree = [...pending].filter((path) => !ancestors.includes(path));
976
1552
  for (const part of [...ancestors, ...tree.length ? [root] : []]) {
977
- const recursive = part === root && root.startsWith(join4(this.state, "runtimes") + sep2);
1553
+ const recursive = part === root && root.startsWith(join5(this.state, "runtimes") + sep2);
978
1554
  this.traceAudit({ event: "audit", root: part, recursive });
979
1555
  const requested = part === root ? tree : [part];
980
1556
  if (this.options.cacheDirectories && !this.windowsDirectories.has(part) && this.windowsAudit.get(part)?.verdict === "ok") {
@@ -1002,7 +1578,7 @@ var init_runtimeReader = __esm({
1002
1578
  const windowsPaths = [];
1003
1579
  let part = parse(absolute).root;
1004
1580
  for (const name of absolute.slice(part.length).split(sep2)) {
1005
- part = join4(part, name);
1581
+ part = join5(part, name);
1006
1582
  const stat2 = await lstat3(part).catch((e) => {
1007
1583
  if (allowMissing && missing(e))
1008
1584
  return null;
@@ -1056,7 +1632,7 @@ var init_runtimeReader = __esm({
1056
1632
  if (process.platform !== "win32" || !this.options.cacheDirectories)
1057
1633
  return;
1058
1634
  const absolute = this.guardedPath(path);
1059
- const parent = dirname3(absolute);
1635
+ const parent = dirname4(absolute);
1060
1636
  const stat2 = await lstat3(parent);
1061
1637
  if (!stat2.isDirectory() || stat2.isSymbolicLink() || !this.windowsStats.get(parent)?.startsWith(`${stat2.dev}:${stat2.ino}:`))
1062
1638
  throw new RuntimeError("permission");
@@ -1066,7 +1642,7 @@ var init_runtimeReader = __esm({
1066
1642
  }
1067
1643
  async bytes(path) {
1068
1644
  await this.inspect(path);
1069
- const fd = await open(path, constants.O_RDONLY | (process.platform === "win32" ? 0 : constants.O_NOFOLLOW));
1645
+ const fd = await open(path, constants2.O_RDONLY | (process.platform === "win32" ? 0 : constants2.O_NOFOLLOW));
1070
1646
  try {
1071
1647
  return await fd.readFile();
1072
1648
  } finally {
@@ -1114,7 +1690,7 @@ var init_runtimeReader = __esm({
1114
1690
  async verifyOpen(artifact, ref, directory) {
1115
1691
  let bytes;
1116
1692
  try {
1117
- bytes = await this.bytes(join4(directory, "manifest.json"));
1693
+ bytes = await this.bytes(join5(directory, "manifest.json"));
1118
1694
  } catch (e) {
1119
1695
  if (missing(e))
1120
1696
  throw new RuntimeError("manifest_missing");
@@ -1135,7 +1711,7 @@ var init_runtimeReader = __esm({
1135
1711
  if (artifact === "cli" && (m.source.kind !== "npm" || !Array.isArray(m.source.packages) || !m.source.packages.some((p) => p.name === "@mnemonik/cli" && p.version === m.version)))
1136
1712
  throw new RuntimeError("unsigned");
1137
1713
  if (process.platform === "win32")
1138
- await this.audit(Object.keys(m.files).map((name) => join4(directory, safePath(name))));
1714
+ await this.audit(Object.keys(m.files).map((name) => join5(directory, safePath(name))));
1139
1715
  const actual = [];
1140
1716
  const walk = async (dir, prefix = "") => {
1141
1717
  await this.inspect(dir, true);
@@ -1143,7 +1719,7 @@ var init_runtimeReader = __esm({
1143
1719
  const key = prefix + name;
1144
1720
  if (!prefix && name === "manifest.json")
1145
1721
  continue;
1146
- const path = join4(dir, name);
1722
+ const path = join5(dir, name);
1147
1723
  if ((await lstat3(path)).isDirectory())
1148
1724
  await walk(path, key + "/");
1149
1725
  else {
@@ -1159,7 +1735,7 @@ var init_runtimeReader = __esm({
1159
1735
  for (const [name, expected] of Object.entries(m.files)) {
1160
1736
  if (!expected || !/^[a-f0-9]{64}$/.test(expected.sha256) || !Number.isSafeInteger(expected.size) || expected.size < 0 || typeof expected.executable !== "boolean")
1161
1737
  throw new RuntimeError("digest_mismatch");
1162
- const path = join4(directory, safePath(name));
1738
+ const path = join5(directory, safePath(name));
1163
1739
  const contents = await this.bytes(path);
1164
1740
  if (hash(contents) !== expected.sha256 || contents.length !== expected.size)
1165
1741
  throw new RuntimeError("digest_mismatch");
@@ -1170,11 +1746,11 @@ var init_runtimeReader = __esm({
1170
1746
  if (total !== m.totalSize)
1171
1747
  throw new RuntimeError("digest_mismatch");
1172
1748
  if (m.signer) {
1173
- const signer = m.signer.platform === "linux" ? { ...m.signer, signature: join4(directory, safePath(m.signer.signature)) } : m.signer;
1749
+ const signer = m.signer.platform === "linux" ? { ...m.signer, signature: join5(directory, safePath(m.signer.signature)) } : m.signer;
1174
1750
  if (m.signer.platform === "linux" && !m.files[m.signer.signature])
1175
1751
  throw new RuntimeError("unsigned");
1176
1752
  try {
1177
- const entry = join4(directory, m.entry);
1753
+ const entry = join5(directory, m.entry);
1178
1754
  if (signer.platform === "win32") {
1179
1755
  const key = JSON.stringify([entry, m.files[m.entry]?.sha256, signer.identity]);
1180
1756
  let verification = _RuntimeReader.windowsSignatures.get(key);
@@ -1192,18 +1768,18 @@ var init_runtimeReader = __esm({
1192
1768
  throw new RuntimeError("unsigned");
1193
1769
  }
1194
1770
  }
1195
- return { directory, manifest: m, entry: join4(directory, m.entry), reference: ref };
1771
+ return { directory, manifest: m, entry: join5(directory, m.entry), reference: ref };
1196
1772
  }
1197
1773
  verifyRetainedRuntime(artifact, reference) {
1198
1774
  versionName(reference.version);
1199
- return this.verifyAt(artifact, reference, join4(dirname3(this.pointerPath(artifact)), reference.version));
1775
+ return this.verifyAt(artifact, reference, join5(dirname4(this.pointerPath(artifact)), reference.version));
1200
1776
  }
1201
1777
  async verifyRuntime(artifact) {
1202
1778
  return this.operation(async () => {
1203
1779
  const p = await this.pointer(artifact);
1204
1780
  if (!p)
1205
1781
  throw new RuntimeError("manifest_missing");
1206
- return this.verifyOpen(artifact, p.current, join4(dirname3(this.pointerPath(artifact)), p.current.version));
1782
+ return this.verifyOpen(artifact, p.current, join5(dirname4(this.pointerPath(artifact)), p.current.version));
1207
1783
  });
1208
1784
  }
1209
1785
  };
@@ -1212,10 +1788,10 @@ var init_runtimeReader = __esm({
1212
1788
 
1213
1789
  // packages/shared/dist/projectSetupHandoff.js
1214
1790
  import { spawn } from "node:child_process";
1215
- import { constants as constants2 } from "node:fs";
1216
- import { lstat as lstat4, mkdir, open as open2, readdir as readdir2, realpath, rename, rm } from "node:fs/promises";
1791
+ import { constants as constants3 } from "node:fs";
1792
+ import { lstat as lstat4, mkdir, open as open2, readdir as readdir2, realpath as realpath2, rename, rm } from "node:fs/promises";
1217
1793
  import { tmpdir } from "node:os";
1218
- import { basename as basename2, dirname as dirname4, join as join5, relative } from "node:path";
1794
+ import { basename as basename2, dirname as dirname5, join as join6, relative } from "node:path";
1219
1795
  import { performance as performance2 } from "node:perf_hooks";
1220
1796
  import { randomUUID as randomUUID2 } from "node:crypto";
1221
1797
  function projectSetupActions(value) {
@@ -1255,7 +1831,7 @@ async function privateDirectory(path, session) {
1255
1831
  }
1256
1832
  }
1257
1833
  async function readPrivate(path) {
1258
- const file = await open2(path, constants2.O_RDONLY | constants2.O_NOFOLLOW);
1834
+ const file = await open2(path, constants3.O_RDONLY | constants3.O_NOFOLLOW);
1259
1835
  try {
1260
1836
  const st = await file.stat();
1261
1837
  if (!st.isFile() || st.size > 64 * 1024 || process.getuid && (st.uid !== process.getuid() || st.mode & 63))
@@ -1266,8 +1842,8 @@ async function readPrivate(path) {
1266
1842
  }
1267
1843
  }
1268
1844
  async function writeDiagnostic(path, value) {
1269
- await privateDirectory(dirname4(dirname4(path)), dirname4(path));
1270
- await privateDirectory(dirname4(path), dirname4(path));
1845
+ await privateDirectory(dirname5(dirname5(path)), dirname5(path));
1846
+ await privateDirectory(dirname5(path), dirname5(path));
1271
1847
  const temp = `${path}.${randomUUID2()}`;
1272
1848
  try {
1273
1849
  const file = await open2(temp, "wx", 384);
@@ -1307,13 +1883,13 @@ async function waitForResult(path, waitMs) {
1307
1883
  return result;
1308
1884
  if (Date.now() >= deadline)
1309
1885
  return RUNNING;
1310
- await new Promise((resolve5) => setTimeout(resolve5, Math.min(50, deadline - Date.now())));
1886
+ await new Promise((resolve6) => setTimeout(resolve6, Math.min(50, deadline - Date.now())));
1311
1887
  }
1312
1888
  }
1313
1889
  async function runEnsure(options) {
1314
- await privateDirectory(dirname4(options.directory), options.directory);
1890
+ await privateDirectory(dirname5(options.directory), options.directory);
1315
1891
  await privateDirectory(options.directory, options.directory);
1316
- const runFile = join5(options.directory, "project-setup-run.json");
1892
+ const runFile = join6(options.directory, "project-setup-run.json");
1317
1893
  let previous;
1318
1894
  try {
1319
1895
  previous = JSON.parse(await readPrivate(runFile));
@@ -1321,7 +1897,7 @@ async function runEnsure(options) {
1321
1897
  previous = void 0;
1322
1898
  }
1323
1899
  if (previous && previous.root === options.root && typeof previous.result === "string") {
1324
- const result2 = join5(options.directory, basename2(previous.result));
1900
+ const result2 = join6(options.directory, basename2(previous.result));
1325
1901
  const finished = await readResult(result2).catch(() => ({ status: "failed" }));
1326
1902
  if (finished === void 0 && Date.now() - previous.startedAt < RUN_STALE_MS) {
1327
1903
  const outcome2 = await waitForResult(result2, options.waitMs);
@@ -1334,7 +1910,7 @@ async function runEnsure(options) {
1334
1910
  if (record(finished) && finished.status === "done")
1335
1911
  return finished;
1336
1912
  }
1337
- const result = join5(options.directory, `project-setup-${randomUUID2()}.result.json`);
1913
+ const result = join6(options.directory, `project-setup-${randomUUID2()}.result.json`);
1338
1914
  const run = {
1339
1915
  requestId: options.requestId,
1340
1916
  root: options.root,
@@ -1402,28 +1978,28 @@ async function handoffProjectSetup(input) {
1402
1978
  try {
1403
1979
  if (!input.cwd)
1404
1980
  throw new Error("safe_cwd_unavailable");
1405
- const cache = JSON.parse(await readPrivate(join5(dirname4(input.stateFile), "bound-context.json")));
1981
+ const cache = JSON.parse(await readPrivate(join6(dirname5(input.stateFile), "bound-context.json")));
1406
1982
  const [, family, body] = JSON.parse(cache.key);
1407
1983
  if (!input.familyId || family !== input.familyId || body.host !== input.host || body.hostSessionId !== input.hostSessionId || cache.expiresAt <= Date.now() || !cache.canonicalRoot)
1408
1984
  throw new Error("bound_context_unavailable");
1409
- root = await realpath(cache.canonicalRoot);
1985
+ root = await realpath2(cache.canonicalRoot);
1410
1986
  const { findProjectIdentityDetailed: findProjectIdentityDetailed2 } = await Promise.resolve().then(() => (init_hookRuntime(), hookRuntime_exports));
1411
- const identity = await findProjectIdentityDetailed2(await realpath(input.cwd));
1412
- if (identity.kind === "git_unavailable" || root !== await realpath(identity.root))
1987
+ const identity = await findProjectIdentityDetailed2(await realpath2(input.cwd));
1988
+ if (identity.kind === "git_unavailable" || root !== await realpath2(identity.root))
1413
1989
  throw new Error("bound_root_changed");
1414
1990
  if (!requestId)
1415
1991
  throw new Error("manual_setup_required");
1416
1992
  const runtime = await new RuntimeReader(input.stateDir).verifyRuntime("cli").catch(() => {
1417
1993
  throw new Error("verified_cli_runtime_unavailable");
1418
1994
  });
1419
- const bin = join5(dirname4(runtime.entry), "bin.js");
1995
+ const bin = join6(dirname5(runtime.entry), "bin.js");
1420
1996
  if (!runtime.manifest.files[relative(runtime.directory, bin).replaceAll("\\", "/")])
1421
1997
  throw new Error("verified_cli_bin_missing");
1422
1998
  const result = await runEnsure({
1423
1999
  bin,
1424
2000
  root,
1425
2001
  requestId,
1426
- directory: dirname4(input.stateFile),
2002
+ directory: dirname5(input.stateFile),
1427
2003
  waitMs: input.waitMs ?? Math.max(0, hookDeadlineMs(input.host) - performance2.now())
1428
2004
  });
1429
2005
  if (result === RUNNING) {
@@ -1453,7 +2029,7 @@ async function handoffProjectSetup(input) {
1453
2029
  requestId,
1454
2030
  outcome,
1455
2031
  time: (/* @__PURE__ */ new Date()).toISOString(),
1456
- rootHash: root ? hash(root) : input.cwd ? hash(await realpath(input.cwd)) : null,
2032
+ rootHash: root ? hash(root) : input.cwd ? hash(await realpath2(input.cwd)) : null,
1457
2033
  action,
1458
2034
  reason
1459
2035
  });
@@ -1463,11 +2039,11 @@ async function handoffProjectSetup(input) {
1463
2039
  return action;
1464
2040
  }
1465
2041
  async function pendingProjectSetup(root) {
1466
- const rootHash = hash(await realpath(root));
2042
+ const rootHash = hash(await realpath2(root));
1467
2043
  const results = [];
1468
2044
  const uid = process.getuid?.() ?? "user";
1469
2045
  for (const host of ["credit", "cursor", "grok"]) {
1470
- const directory = join5(tmpdir(), `mnemonik-${host}-${uid}`);
2046
+ const directory = join6(tmpdir(), `mnemonik-${host}-${uid}`);
1471
2047
  try {
1472
2048
  const stat2 = await lstat4(directory);
1473
2049
  if (stat2.isSymbolicLink() || !stat2.isDirectory())
@@ -1476,9 +2052,9 @@ async function pendingProjectSetup(root) {
1476
2052
  if (!/^[a-f0-9]{64}$/.test(name))
1477
2053
  continue;
1478
2054
  try {
1479
- if ((await lstat4(join5(directory, name))).isSymbolicLink())
2055
+ if ((await lstat4(join6(directory, name))).isSymbolicLink())
1480
2056
  continue;
1481
- const diagnostic = JSON.parse(await readPrivate(join5(directory, name, "project-setup.json")));
2057
+ const diagnostic = JSON.parse(await readPrivate(join6(directory, name, "project-setup.json")));
1482
2058
  if ((diagnostic.rootHash === rootHash || diagnostic.rootHash === null) && ["done", "ACTION_REQUIRED", "failed"].includes(diagnostic.outcome) && typeof diagnostic.action === "string" && diagnostic.action.length < 2e3 && Date.now() - Date.parse(diagnostic.time) < 24 * 60 * 60 * 1e3)
1483
2059
  results.push(diagnostic);
1484
2060
  } catch {
@@ -1530,9 +2106,9 @@ process.stdin.on('end', () => {
1530
2106
  });
1531
2107
 
1532
2108
  // packages/shared/dist/settingsIo.js
1533
- import { createHash as createHash3, randomBytes } from "node:crypto";
1534
- import { link, lstat as lstat5, mkdir as mkdir2, open as open3, readFile as readFile4, readdir as readdir3, realpath as realpath2, rename as rename2, rm as rm2, rmdir, stat, unlink } from "node:fs/promises";
1535
- import { basename as basename3, dirname as dirname5, join as join6, resolve as resolve4 } from "node:path";
2109
+ import { createHash as createHash3, randomBytes as randomBytes2 } from "node:crypto";
2110
+ import { link, lstat as lstat5, mkdir as mkdir2, open as open3, readFile as readFile4, readdir as readdir3, realpath as realpath3, rename as rename2, rm as rm2, rmdir, stat, unlink } from "node:fs/promises";
2111
+ import { basename as basename3, dirname as dirname6, join as join7, resolve as resolve4 } from "node:path";
1536
2112
  function errorCode(error) {
1537
2113
  return error && typeof error === "object" && "code" in error ? String(error.code) : void 0;
1538
2114
  }
@@ -1579,7 +2155,7 @@ async function removeStaleLock(lockPath, staleMs = LOCK_STALE_MS) {
1579
2155
  const [entry] = entries;
1580
2156
  if (entries.length !== 1 || entry === void 0 || !entry.startsWith("owner-"))
1581
2157
  return false;
1582
- const tokenPath = join6(lockPath, entry);
2158
+ const tokenPath = join7(lockPath, entry);
1583
2159
  const owner = await readFile4(tokenPath, "utf8");
1584
2160
  if (lockOwnerIsAlive(owner))
1585
2161
  return false;
@@ -1600,13 +2176,13 @@ async function removeStaleLock(lockPath, staleMs = LOCK_STALE_MS) {
1600
2176
  }
1601
2177
  }
1602
2178
  async function acquireLock(lockPath, options = {}) {
1603
- await mkdir2(dirname5(lockPath), { recursive: true });
1604
- const owner = `${process.pid} ${Date.now()} ${randomBytes(12).toString("hex")}
2179
+ await mkdir2(dirname6(lockPath), { recursive: true });
2180
+ const owner = `${process.pid} ${Date.now()} ${randomBytes2(12).toString("hex")}
1605
2181
  `;
1606
2182
  for (let attempt = 0; attempt < (options.retries ?? LOCK_RETRIES); attempt += 1) {
1607
2183
  try {
1608
2184
  await mkdir2(lockPath, { mode: 448 });
1609
- const tokenPath = join6(lockPath, `owner-${process.pid}-${randomBytes(12).toString("hex")}`);
2185
+ const tokenPath = join7(lockPath, `owner-${process.pid}-${randomBytes2(12).toString("hex")}`);
1610
2186
  const handle = await open3(tokenPath, "wx", 384);
1611
2187
  try {
1612
2188
  await handle.writeFile(owner, "utf8");
@@ -1615,7 +2191,7 @@ async function acquireLock(lockPath, options = {}) {
1615
2191
  await handle.close();
1616
2192
  }
1617
2193
  await syncDirectory(lockPath);
1618
- await syncDirectory(dirname5(lockPath));
2194
+ await syncDirectory(dirname6(lockPath));
1619
2195
  return tokenPath;
1620
2196
  } catch (error) {
1621
2197
  if (errorCode(error) !== "EEXIST") {
@@ -1645,7 +2221,7 @@ async function withFileLock(lockPath, action, options) {
1645
2221
  try {
1646
2222
  await unlink(tokenPath);
1647
2223
  await rmdir(lockPath);
1648
- await syncDirectory(dirname5(lockPath));
2224
+ await syncDirectory(dirname6(lockPath));
1649
2225
  } catch {
1650
2226
  }
1651
2227
  }
@@ -1661,30 +2237,30 @@ var init_settingsIo = __esm({
1661
2237
  });
1662
2238
 
1663
2239
  // packages/shared/dist/hookConversation.js
1664
- import { createHash as createHash4, randomBytes as randomBytes2 } from "node:crypto";
1665
- import { constants as constants3 } from "node:fs";
2240
+ import { createHash as createHash4, randomBytes as randomBytes3 } from "node:crypto";
2241
+ import { constants as constants4 } from "node:fs";
1666
2242
  import { lstat as lstat6, mkdir as mkdir3, open as open4, rename as rename3, rm as rm3, utimes } from "node:fs/promises";
1667
- import { dirname as dirname6 } from "node:path";
2243
+ import { dirname as dirname7 } from "node:path";
1668
2244
  function boundedHandle(value) {
1669
2245
  return typeof value === "string" && /^cv1\.[0-9a-f-]{36}\.[A-Za-z0-9_-]{43}$/.test(value);
1670
2246
  }
1671
2247
  async function configureHookConversation(input) {
1672
- active = new HookConversation(input);
1673
- await active.load();
2248
+ active2 = new HookConversation(input);
2249
+ await active2.load();
1674
2250
  }
1675
2251
  function currentConversationId() {
1676
- return active?.id;
2252
+ return active2?.id;
1677
2253
  }
1678
2254
  function conversationMarker() {
1679
- return active?.id ? `<system-reminder>mnemonik-conversation: ${active.id}</system-reminder>` : void 0;
2255
+ return active2?.id ? `<system-reminder>mnemonik-conversation: ${active2.id}</system-reminder>` : void 0;
1680
2256
  }
1681
2257
  async function adoptHookConversation(result) {
1682
- await active?.adopt(result);
2258
+ await active2?.adopt(result);
1683
2259
  }
1684
2260
  function withHookConversation(url, init, post) {
1685
- return active ? active.request(url, init, post) : post(init);
2261
+ return active2 ? active2.request(url, init, post) : post(init);
1686
2262
  }
1687
- var RETENTION_MS, CONVERSATION_STATE_FILE, HookConversation, active;
2263
+ var RETENTION_MS, CONVERSATION_STATE_FILE, HookConversation, active2;
1688
2264
  var init_hookConversation = __esm({
1689
2265
  "packages/shared/dist/hookConversation.js"() {
1690
2266
  "use strict";
@@ -1712,10 +2288,10 @@ var init_hookConversation = __esm({
1712
2288
  async load() {
1713
2289
  this.conversationId = void 0;
1714
2290
  try {
1715
- const directory = await lstat6(dirname6(this.input.stateFile));
2291
+ const directory = await lstat6(dirname7(this.input.stateFile));
1716
2292
  if (!directory.isDirectory() || directory.isSymbolicLink() || process.platform !== "win32" && (process.getuid && directory.uid !== process.getuid() || directory.mode & 63))
1717
2293
  return;
1718
- const file = await open4(this.input.stateFile, constants3.O_RDONLY | constants3.O_NOFOLLOW);
2294
+ const file = await open4(this.input.stateFile, constants4.O_RDONLY | constants4.O_NOFOLLOW);
1719
2295
  try {
1720
2296
  const stat2 = await file.stat();
1721
2297
  if (!stat2.isFile() || stat2.size > 4096 || process.platform !== "win32" && (process.getuid && stat2.uid !== process.getuid() || stat2.mode & 63))
@@ -1734,7 +2310,7 @@ var init_hookConversation = __esm({
1734
2310
  }
1735
2311
  }
1736
2312
  async locked(run) {
1737
- const directory = dirname6(this.input.stateFile);
2313
+ const directory = dirname7(this.input.stateFile);
1738
2314
  await mkdir3(directory, { recursive: true, mode: 448 });
1739
2315
  const stat2 = await lstat6(directory);
1740
2316
  if (!stat2.isDirectory() || stat2.isSymbolicLink() || process.platform !== "win32" && (process.getuid && stat2.uid !== process.getuid() || stat2.mode & 63))
@@ -1747,8 +2323,8 @@ var init_hookConversation = __esm({
1747
2323
  });
1748
2324
  }
1749
2325
  async save(id) {
1750
- const temporary = `${this.input.stateFile}.${randomBytes2(8).toString("hex")}`;
1751
- const file = await open4(temporary, constants3.O_CREAT | constants3.O_EXCL | constants3.O_WRONLY | constants3.O_NOFOLLOW, 384);
2326
+ const temporary = `${this.input.stateFile}.${randomBytes3(8).toString("hex")}`;
2327
+ const file = await open4(temporary, constants4.O_CREAT | constants4.O_EXCL | constants4.O_WRONLY | constants4.O_NOFOLLOW, 384);
1752
2328
  try {
1753
2329
  await file.writeFile(JSON.stringify({ v: 1, scope: this.scope, conversationId: id, usedAt: Date.now() }));
1754
2330
  } finally {
@@ -2022,7 +2598,10 @@ var hookRuntime_exports = {};
2022
2598
  __export(hookRuntime_exports, {
2023
2599
  CONVERSATION_STATE_FILE: () => CONVERSATION_STATE_FILE,
2024
2600
  HOOK_STDIN_MAX_BYTES: () => HOOK_STDIN_MAX_BYTES,
2601
+ HOOK_TRACE_ENV: () => HOOK_TRACE_ENV,
2602
+ HOOK_TRACE_MAX_BYTES: () => HOOK_TRACE_MAX_BYTES,
2025
2603
  HookConversation: () => HookConversation,
2604
+ HookTrace: () => HookTrace,
2026
2605
  PROJECT_ENSURE_TIMEOUT_MS: () => PROJECT_ENSURE_TIMEOUT_MS,
2027
2606
  PROJECT_SETUP_MANUAL: () => PROJECT_SETUP_MANUAL,
2028
2607
  PROJECT_SETUP_RUNNING: () => PROJECT_SETUP_RUNNING,
@@ -2031,6 +2610,7 @@ __export(hookRuntime_exports, {
2031
2610
  RuntimeReader: () => RuntimeReader,
2032
2611
  aclRecords: () => aclRecords,
2033
2612
  adoptHookConversation: () => adoptHookConversation,
2613
+ appendHookTraceLine: () => appendHookTraceLine,
2034
2614
  auditWindowsPermissions: () => auditWindowsPermissions,
2035
2615
  bindHookContext: () => bindHookContext,
2036
2616
  clearProjectIdentityCache: () => clearProjectIdentityCache,
@@ -2038,21 +2618,31 @@ __export(hookRuntime_exports, {
2038
2618
  conversationMarker: () => conversationMarker,
2039
2619
  currentConversationId: () => currentConversationId,
2040
2620
  ensureWindowsPrivateDirectorySync: () => ensureWindowsPrivateDirectorySync,
2621
+ errorKind: () => errorKind,
2041
2622
  execute: () => execute,
2042
2623
  findProjectIdentity: () => findProjectIdentity,
2043
2624
  findProjectIdentityDetailed: () => findProjectIdentityDetailed,
2044
2625
  handoffProjectSetup: () => handoffProjectSetup,
2045
2626
  hash: () => hash,
2627
+ hookTraceDirectory: () => hookTraceDirectory,
2628
+ hookTraceEnabled: () => hookTraceEnabled,
2629
+ hookTraceFile: () => hookTraceFile,
2046
2630
  missing: () => missing,
2631
+ modelTypedCwd: () => modelTypedCwd,
2632
+ noteHookTraceProject: () => noteHookTraceProject,
2047
2633
  parseHookResponse: () => parseHookResponse,
2048
2634
  pendingProjectSetup: () => pendingProjectSetup,
2049
2635
  postHookBoundJson: () => postHookBoundJson,
2050
2636
  prepareWindowsAclDirectory: () => prepareWindowsAclDirectory,
2637
+ projectScope: () => projectScope,
2051
2638
  projectSetupActions: () => projectSetupActions,
2052
2639
  protectWindowsDirectory: () => protectWindowsDirectory,
2053
2640
  readHookStdin: () => readHookStdin,
2054
2641
  safePath: () => safePath,
2642
+ scrubHookTraceMessage: () => scrubHookTraceMessage,
2055
2643
  setupResponse: () => setupResponse,
2644
+ startHookTrace: () => startHookTrace,
2645
+ summarizeHookOutput: () => summarizeHookOutput,
2056
2646
  validHookSessionId: () => validHookSessionId,
2057
2647
  verifyMinisign: () => verifyMinisign,
2058
2648
  verifySigner: () => verifySigner,
@@ -2064,36 +2654,43 @@ __export(hookRuntime_exports, {
2064
2654
  withHookConversation: () => withHookConversation
2065
2655
  });
2066
2656
  import { stderr, stdin } from "node:process";
2657
+ import { homedir as homedir3 } from "node:os";
2658
+ import { join as join8, resolve as resolve5 } from "node:path";
2659
+ function modelTypedCwd(hostCwd, typed) {
2660
+ const expanded = typed === "~" ? homedir3() : /^~[\\/]/.test(typed) ? join8(homedir3(), typed.slice(2)) : typed;
2661
+ return resolve5(hostCwd, expanded);
2662
+ }
2067
2663
  function clearProjectIdentityCache() {
2068
2664
  identityCache.clear();
2069
2665
  }
2070
- async function findProjectIdentity(startCwd, options = {}) {
2666
+ async function findProjectIdentity(startCwd) {
2071
2667
  if (!startCwd)
2072
2668
  return null;
2073
- const key = JSON.stringify([
2074
- startCwd,
2075
- options.allowNestedInherit ?? null,
2076
- options.maxDepth ?? null
2077
- ]);
2669
+ const key = startCwd;
2078
2670
  let pending = identityCache.get(key);
2079
2671
  if (!pending) {
2080
- pending = findProjectIdentityDetailed(startCwd, options).then((result) => result.kind === "ok" ? {
2672
+ pending = findProjectIdentityDetailed(startCwd).then((result) => result.kind === "ok" ? {
2081
2673
  projectId: result.identity.projectId,
2082
2674
  projectName: result.identity.projectName,
2083
2675
  projectRoot: result.root
2084
2676
  } : null);
2085
2677
  identityCache.set(key, pending);
2086
2678
  pending.catch(() => identityCache.delete(key));
2679
+ pending.then((found) => noteHookTraceProject(found?.projectId), () => void 0);
2087
2680
  }
2088
2681
  return pending;
2089
2682
  }
2090
- async function findProjectIdentityDetailed(startCwd, options = {}) {
2091
- return resolveProjectIdentity(startCwd, options);
2683
+ async function projectScope(startCwd) {
2684
+ const identity = await findProjectIdentity(startCwd);
2685
+ return identity ? { projectId: identity.projectId, projectRoot: identity.projectRoot } : {};
2686
+ }
2687
+ async function findProjectIdentityDetailed(startCwd) {
2688
+ return resolveNearestProjectIdentity(startCwd);
2092
2689
  }
2093
2690
  function readHookStdin(options = {}) {
2094
2691
  const stream = options.stream ?? stdin;
2095
2692
  const maxBytes = options.maxBytes ?? HOOK_STDIN_MAX_BYTES;
2096
- return new Promise((resolve5) => {
2693
+ return new Promise((resolve6) => {
2097
2694
  const chunks = [];
2098
2695
  let bytes = 0;
2099
2696
  let settled = false;
@@ -2109,7 +2706,7 @@ function readHookStdin(options = {}) {
2109
2706
  stream.pause();
2110
2707
  stream.destroy?.();
2111
2708
  }
2112
- resolve5(value);
2709
+ resolve6(value);
2113
2710
  };
2114
2711
  const onData = (chunk) => {
2115
2712
  const buffer = typeof chunk === "string" ? Buffer.from(chunk, "utf8") : chunk;
@@ -2126,13 +2723,7 @@ function readHookStdin(options = {}) {
2126
2723
  });
2127
2724
  }
2128
2725
  async function parseHookResponse(response) {
2129
- const body = await response.json();
2130
- const result = body;
2131
- if (!mismatchEmitted && result?.ok === false && result.reason === "identity_mismatch") {
2132
- stderr.write("mnemonik-hook: project identity mismatch - .mnemonik.json points to one project, but this directory is registered to a different one on the server. Fix .mnemonik.json (delete it and let session_bootstrap rewrite it) or cd to the correct project root.\n");
2133
- mismatchEmitted = true;
2134
- }
2135
- return body;
2726
+ return await response.json();
2136
2727
  }
2137
2728
  function validHookSessionId(value) {
2138
2729
  return typeof value === "string" && /^[a-zA-Z0-9@._+:-]{1,255}$/.test(value) && !["cursor-unknown-session", "codex-unknown-session"].includes(value);
@@ -2148,21 +2739,21 @@ async function postHookBoundJson(server, path, token, body) {
2148
2739
  }
2149
2740
  async function bindHookContext(input, familyId, hmac, post, options = {}) {
2150
2741
  const { execFile: execFile3 } = await import("node:child_process");
2151
- const { constants: constants4 } = await import("node:fs");
2152
- const { lstat: lstat7, mkdir: mkdir4, open: open5, realpath: realpath3 } = await import("node:fs/promises");
2153
- const { dirname: dirname7 } = await import("node:path");
2742
+ const { constants: constants5 } = await import("node:fs");
2743
+ const { lstat: lstat7, mkdir: mkdir4, open: open5, realpath: realpath4 } = await import("node:fs/promises");
2744
+ const { dirname: dirname8 } = await import("node:path");
2154
2745
  const { selectRemote: selectRemote2 } = await Promise.resolve().then(() => (init_repositoryFingerprint(), repositoryFingerprint_exports));
2155
- const identity = await findProjectIdentityDetailed(await realpath3(input.cwd));
2746
+ const identity = await findProjectIdentityDetailed(await realpath4(input.cwd));
2156
2747
  if (identity.kind === "git_unavailable")
2157
2748
  throw new Error("context unavailable");
2158
- const root = await realpath3(identity.root);
2749
+ const root = await realpath4(identity.root);
2159
2750
  const binding = await hmac(root);
2160
2751
  const gitEnv = { LC_ALL: "C" };
2161
2752
  for (const key2 of ["PATH", "HOME", "LANG", "SYSTEMROOT", "TEMP", "TMP", "USERPROFILE"])
2162
2753
  if (process.env[key2] !== void 0)
2163
2754
  gitEnv[key2] = process.env[key2];
2164
- const git = (args) => new Promise((resolve5) => {
2165
- execFile3("git", args, { cwd: root, env: gitEnv, timeout: 2e3, encoding: "utf8" }, (error, stdout2) => resolve5(error ? [] : stdout2.trim().split(/\r?\n/).filter(Boolean)));
2755
+ const git = (args) => new Promise((resolve6) => {
2756
+ execFile3("git", args, { cwd: root, env: gitEnv, timeout: 2e3, encoding: "utf8" }, (error, stdout2) => resolve6(error ? [] : stdout2.trim().split(/\r?\n/).filter(Boolean)));
2166
2757
  });
2167
2758
  const remotes = identity.repository.kind === "git" ? await git(["remote"]) : [];
2168
2759
  const selection = selectRemote2(await Promise.all(remotes.map(async (name) => ({
@@ -2187,8 +2778,8 @@ async function bindHookContext(input, familyId, hmac, post, options = {}) {
2187
2778
  ...identity.kind === "ok" ? { projectId: identity.identity.projectId } : {}
2188
2779
  };
2189
2780
  const key = JSON.stringify([input.server, familyId, body]);
2190
- const directory = dirname7(input.stateFile);
2191
- for (const path of [dirname7(directory), directory]) {
2781
+ const directory = dirname8(input.stateFile);
2782
+ for (const path of [dirname8(directory), directory]) {
2192
2783
  const created = await mkdir4(path, { recursive: true, mode: 448 });
2193
2784
  const stat2 = await lstat7(path);
2194
2785
  if (!stat2.isDirectory() || stat2.isSymbolicLink() || (options.platform ?? process.platform) !== "win32" && (process.getuid && stat2.uid !== process.getuid() || stat2.mode & 63))
@@ -2197,7 +2788,7 @@ async function bindHookContext(input, familyId, hmac, post, options = {}) {
2197
2788
  await protectWindowsDirectory(path, created !== void 0, options.run, directory);
2198
2789
  }
2199
2790
  }
2200
- const file = await open5(input.stateFile, constants4.O_CREAT | constants4.O_RDWR | constants4.O_NOFOLLOW, 384);
2791
+ const file = await open5(input.stateFile, constants5.O_CREAT | constants5.O_RDWR | constants5.O_NOFOLLOW, 384);
2201
2792
  try {
2202
2793
  let cache;
2203
2794
  try {
@@ -2227,20 +2818,21 @@ async function bindHookContext(input, familyId, hmac, post, options = {}) {
2227
2818
  await file.close();
2228
2819
  }
2229
2820
  }
2230
- var identityCache, HOOK_STDIN_MAX_BYTES, mismatchEmitted;
2821
+ var identityCache, HOOK_STDIN_MAX_BYTES;
2231
2822
  var init_hookRuntime = __esm({
2232
2823
  "packages/shared/dist/hookRuntime.js"() {
2233
2824
  "use strict";
2234
2825
  init_runtimeSigners();
2235
2826
  init_hookTimeouts();
2236
2827
  init_repositoryRoot();
2828
+ init_hookTrace();
2237
2829
  init_runtimeReader();
2238
2830
  init_runtimeSigners();
2239
2831
  init_projectSetupHandoff();
2240
2832
  init_hookConversation();
2833
+ init_hookTrace();
2241
2834
  identityCache = /* @__PURE__ */ new Map();
2242
2835
  HOOK_STDIN_MAX_BYTES = 1048576;
2243
- mismatchEmitted = false;
2244
2836
  }
2245
2837
  });
2246
2838
 
@@ -2249,7 +2841,10 @@ init_hookRuntime();
2249
2841
  export {
2250
2842
  CONVERSATION_STATE_FILE,
2251
2843
  HOOK_STDIN_MAX_BYTES,
2844
+ HOOK_TRACE_ENV,
2845
+ HOOK_TRACE_MAX_BYTES,
2252
2846
  HookConversation,
2847
+ HookTrace,
2253
2848
  PROJECT_ENSURE_TIMEOUT_MS,
2254
2849
  PROJECT_SETUP_MANUAL,
2255
2850
  PROJECT_SETUP_RUNNING,
@@ -2258,6 +2853,7 @@ export {
2258
2853
  RuntimeReader,
2259
2854
  aclRecords,
2260
2855
  adoptHookConversation,
2856
+ appendHookTraceLine,
2261
2857
  auditWindowsPermissions,
2262
2858
  bindHookContext,
2263
2859
  clearProjectIdentityCache,
@@ -2265,21 +2861,31 @@ export {
2265
2861
  conversationMarker,
2266
2862
  currentConversationId,
2267
2863
  ensureWindowsPrivateDirectorySync,
2864
+ errorKind,
2268
2865
  execute,
2269
2866
  findProjectIdentity,
2270
2867
  findProjectIdentityDetailed,
2271
2868
  handoffProjectSetup,
2272
2869
  hash,
2870
+ hookTraceDirectory,
2871
+ hookTraceEnabled,
2872
+ hookTraceFile,
2273
2873
  missing,
2874
+ modelTypedCwd,
2875
+ noteHookTraceProject,
2274
2876
  parseHookResponse,
2275
2877
  pendingProjectSetup,
2276
2878
  postHookBoundJson,
2277
2879
  prepareWindowsAclDirectory,
2880
+ projectScope,
2278
2881
  projectSetupActions,
2279
2882
  protectWindowsDirectory,
2280
2883
  readHookStdin,
2281
2884
  safePath,
2885
+ scrubHookTraceMessage,
2282
2886
  setupResponse,
2887
+ startHookTrace,
2888
+ summarizeHookOutput,
2283
2889
  validHookSessionId,
2284
2890
  verifyMinisign,
2285
2891
  verifySigner,