@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
package/dist/router.js CHANGED
@@ -885,6 +885,116 @@ var require_ignore = __commonJS({
885
885
  }
886
886
  });
887
887
 
888
+ // packages/shared/dist/secretPatterns.js
889
+ var init_secretPatterns = __esm({
890
+ "packages/shared/dist/secretPatterns.js"() {
891
+ "use strict";
892
+ }
893
+ });
894
+
895
+ // packages/shared/dist/protectedPaths.js
896
+ var init_protectedPaths = __esm({
897
+ "packages/shared/dist/protectedPaths.js"() {
898
+ "use strict";
899
+ }
900
+ });
901
+
902
+ // packages/shared/dist/projectIdentityFile.js
903
+ import { lstat, readFile } from "node:fs/promises";
904
+ import { join } from "node:path";
905
+ function parseIdentityFile(text) {
906
+ let value;
907
+ try {
908
+ value = JSON.parse(text);
909
+ } catch (error) {
910
+ return { kind: "malformed", detail: `unparseable JSON: ${error.message}` };
911
+ }
912
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
913
+ return { kind: "malformed", detail: "identity file must contain a JSON object" };
914
+ }
915
+ const record = value;
916
+ if (record.schemaVersion !== 1) {
917
+ return { kind: "unknown_version", version: record.schemaVersion };
918
+ }
919
+ const unknownKey = Object.keys(record).find((key) => !IDENTITY_KEYS.has(key));
920
+ if (unknownKey)
921
+ return { kind: "malformed", detail: `unknown key "${unknownKey}"` };
922
+ if (!isCanonicalUuid(record.projectId)) {
923
+ return { kind: "malformed", detail: '"projectId" must be a canonical RFC 4122 UUID string' };
924
+ }
925
+ if ("projectName" in record && typeof record.projectName !== "string") {
926
+ return { kind: "malformed", detail: '"projectName" must be a string when present' };
927
+ }
928
+ if ("repositoryFingerprint" in record && (!record.repositoryFingerprint || typeof record.repositoryFingerprint !== "object" || Array.isArray(record.repositoryFingerprint))) {
929
+ return { kind: "malformed", detail: '"repositoryFingerprint" must be an object when present' };
930
+ }
931
+ const fingerprint = record.repositoryFingerprint;
932
+ const unknownFingerprintKey = fingerprint ? Object.keys(fingerprint).find((key) => !FINGERPRINT_KEYS.has(key)) : void 0;
933
+ if (unknownFingerprintKey) {
934
+ return {
935
+ kind: "malformed",
936
+ detail: `unknown repositoryFingerprint key "${unknownFingerprintKey}"`
937
+ };
938
+ }
939
+ if (fingerprint && (fingerprint.algorithmVersion !== 1 || typeof fingerprint.hash !== "string")) {
940
+ return {
941
+ kind: "malformed",
942
+ detail: '"repositoryFingerprint" must contain algorithmVersion 1 and a string hash'
943
+ };
944
+ }
945
+ const repositoryFingerprint = fingerprint ? { algorithmVersion: 1, hash: fingerprint.hash } : void 0;
946
+ return {
947
+ kind: "ok",
948
+ identity: {
949
+ schemaVersion: 1,
950
+ projectId: record.projectId,
951
+ ...record.projectName === void 0 ? {} : { projectName: record.projectName },
952
+ ...repositoryFingerprint ? { repositoryFingerprint } : {}
953
+ }
954
+ };
955
+ }
956
+ function isConfigurationOnly(text) {
957
+ try {
958
+ const value = JSON.parse(text);
959
+ return !!value && typeof value === "object" && !Array.isArray(value) && !("projectId" in value) && (!("schemaVersion" in value) || value.schemaVersion === 1);
960
+ } catch {
961
+ return false;
962
+ }
963
+ }
964
+ async function readIdentityFile(dir, options = {}) {
965
+ const path = join(dir, ".mnemonik.json");
966
+ try {
967
+ const stat = await lstat(path);
968
+ if (stat.isSymbolicLink())
969
+ return { kind: "malformed", detail: "symlink" };
970
+ if (!stat.isFile())
971
+ return { kind: "malformed", detail: "identity path is not a regular file" };
972
+ const text = await readFile(path, "utf8");
973
+ if ((options.selectedRoot || options.configurationOnlyAbsent) && isConfigurationOnly(text))
974
+ return { kind: "absent" };
975
+ return parseIdentityFile(text);
976
+ } catch (error) {
977
+ if (error.code === "ENOENT")
978
+ return { kind: "absent" };
979
+ return { kind: "malformed", detail: `cannot read identity file: ${error.message}` };
980
+ }
981
+ }
982
+ var CANONICAL_UUID, isCanonicalUuid, IDENTITY_KEYS, FINGERPRINT_KEYS;
983
+ var init_projectIdentityFile = __esm({
984
+ "packages/shared/dist/projectIdentityFile.js"() {
985
+ "use strict";
986
+ CANONICAL_UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/;
987
+ isCanonicalUuid = (value) => typeof value === "string" && CANONICAL_UUID.test(value);
988
+ IDENTITY_KEYS = /* @__PURE__ */ new Set([
989
+ "schemaVersion",
990
+ "projectId",
991
+ "projectName",
992
+ "repositoryFingerprint"
993
+ ]);
994
+ FINGERPRINT_KEYS = /* @__PURE__ */ new Set(["algorithmVersion", "hash"]);
995
+ }
996
+ });
997
+
888
998
  // packages/shared/dist/hookTimeouts.js
889
999
  var init_hookTimeouts = __esm({
890
1000
  "packages/shared/dist/hookTimeouts.js"() {
@@ -893,7 +1003,7 @@ var init_hookTimeouts = __esm({
893
1003
  });
894
1004
 
895
1005
  // packages/shared/dist/runtimeSigners.js
896
- import { basename, dirname as dirname2, join as join2, resolve, sep, win32 } from "node:path";
1006
+ import { basename, dirname as dirname2, join as join3, resolve, sep, win32 } from "node:path";
897
1007
  import { execFile, execFileSync } from "node:child_process";
898
1008
  function windowsCommand(file) {
899
1009
  const root = process.env.SystemRoot;
@@ -982,102 +1092,8 @@ var init_repositoryFingerprint = __esm({
982
1092
  }
983
1093
  });
984
1094
 
985
- // packages/shared/dist/projectIdentityFile.js
986
- import { lstat, readFile } from "node:fs/promises";
987
- import { join as join3 } from "node:path";
988
- function parseIdentityFile(text) {
989
- let value;
990
- try {
991
- value = JSON.parse(text);
992
- } catch (error) {
993
- return { kind: "malformed", detail: `unparseable JSON: ${error.message}` };
994
- }
995
- if (!value || typeof value !== "object" || Array.isArray(value)) {
996
- return { kind: "malformed", detail: "identity file must contain a JSON object" };
997
- }
998
- const record = value;
999
- if (record.schemaVersion !== 1) {
1000
- return { kind: "unknown_version", version: record.schemaVersion };
1001
- }
1002
- const unknownKey = Object.keys(record).find((key) => !IDENTITY_KEYS.has(key));
1003
- if (unknownKey)
1004
- return { kind: "malformed", detail: `unknown key "${unknownKey}"` };
1005
- if (!isCanonicalUuid(record.projectId)) {
1006
- return { kind: "malformed", detail: '"projectId" must be a canonical RFC 4122 UUID string' };
1007
- }
1008
- if ("projectName" in record && typeof record.projectName !== "string") {
1009
- return { kind: "malformed", detail: '"projectName" must be a string when present' };
1010
- }
1011
- if ("repositoryFingerprint" in record && (!record.repositoryFingerprint || typeof record.repositoryFingerprint !== "object" || Array.isArray(record.repositoryFingerprint))) {
1012
- return { kind: "malformed", detail: '"repositoryFingerprint" must be an object when present' };
1013
- }
1014
- const fingerprint = record.repositoryFingerprint;
1015
- const unknownFingerprintKey = fingerprint ? Object.keys(fingerprint).find((key) => !FINGERPRINT_KEYS.has(key)) : void 0;
1016
- if (unknownFingerprintKey) {
1017
- return {
1018
- kind: "malformed",
1019
- detail: `unknown repositoryFingerprint key "${unknownFingerprintKey}"`
1020
- };
1021
- }
1022
- if (fingerprint && (fingerprint.algorithmVersion !== 1 || typeof fingerprint.hash !== "string")) {
1023
- return {
1024
- kind: "malformed",
1025
- detail: '"repositoryFingerprint" must contain algorithmVersion 1 and a string hash'
1026
- };
1027
- }
1028
- const repositoryFingerprint = fingerprint ? { algorithmVersion: 1, hash: fingerprint.hash } : void 0;
1029
- return {
1030
- kind: "ok",
1031
- identity: {
1032
- schemaVersion: 1,
1033
- projectId: record.projectId,
1034
- ...record.projectName === void 0 ? {} : { projectName: record.projectName },
1035
- ...repositoryFingerprint ? { repositoryFingerprint } : {}
1036
- }
1037
- };
1038
- }
1039
- async function readIdentityFile(dir, options = {}) {
1040
- const path = join3(dir, ".mnemonik.json");
1041
- try {
1042
- const stat = await lstat(path);
1043
- if (stat.isSymbolicLink())
1044
- return { kind: "malformed", detail: "symlink" };
1045
- if (!stat.isFile())
1046
- return { kind: "malformed", detail: "identity path is not a regular file" };
1047
- const text = await readFile(path, "utf8");
1048
- if (options.selectedRoot) {
1049
- try {
1050
- const value = JSON.parse(text);
1051
- if (value && typeof value === "object" && !Array.isArray(value) && !("projectId" in value) && (!("schemaVersion" in value) || value.schemaVersion === 1))
1052
- return { kind: "absent" };
1053
- } catch {
1054
- }
1055
- }
1056
- return parseIdentityFile(text);
1057
- } catch (error) {
1058
- if (error.code === "ENOENT")
1059
- return { kind: "absent" };
1060
- return { kind: "malformed", detail: `cannot read identity file: ${error.message}` };
1061
- }
1062
- }
1063
- var CANONICAL_UUID, isCanonicalUuid, IDENTITY_KEYS, FINGERPRINT_KEYS;
1064
- var init_projectIdentityFile = __esm({
1065
- "packages/shared/dist/projectIdentityFile.js"() {
1066
- "use strict";
1067
- CANONICAL_UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/;
1068
- isCanonicalUuid = (value) => typeof value === "string" && CANONICAL_UUID.test(value);
1069
- IDENTITY_KEYS = /* @__PURE__ */ new Set([
1070
- "schemaVersion",
1071
- "projectId",
1072
- "projectName",
1073
- "repositoryFingerprint"
1074
- ]);
1075
- FINGERPRINT_KEYS = /* @__PURE__ */ new Set(["algorithmVersion", "hash"]);
1076
- }
1077
- });
1078
-
1079
1095
  // packages/shared/dist/repositoryRoot.js
1080
- import { lstat as lstat2, readFile as readFile2 } from "node:fs/promises";
1096
+ import { lstat as lstat2, readFile as readFile2, realpath } from "node:fs/promises";
1081
1097
  import { execFile as execFile2 } from "node:child_process";
1082
1098
  import { homedir } from "node:os";
1083
1099
  import { dirname as dirname3, join as join4, resolve as resolve2 } from "node:path";
@@ -1128,7 +1144,7 @@ async function confirmedBoundaryAt(path) {
1128
1144
  async function boundariesBetween(cwd, root) {
1129
1145
  const boundaries = [];
1130
1146
  let directory = cwd;
1131
- while (directory !== root) {
1147
+ while (!samePath(directory, root)) {
1132
1148
  const boundary = await confirmedBoundaryAt(directory);
1133
1149
  if (boundary)
1134
1150
  boundaries.push(boundary);
@@ -1151,37 +1167,31 @@ async function containingRepository(root) {
1151
1167
  directory = parent;
1152
1168
  }
1153
1169
  }
1154
- async function canonicalBoundaryRoot(boundary) {
1155
- if (boundary.kind === "directory")
1156
- return boundary.path;
1157
- const pointer = /^gitdir:\s*(.+)\s*$/im.exec(await readFile2(join4(boundary.path, ".git"), "utf8"))?.[1];
1158
- if (!pointer)
1159
- return boundary.path;
1160
- const gitDir = resolve2(boundary.path, pointer);
1161
- try {
1162
- const commonDir = resolve2(gitDir, (await readFile2(join4(gitDir, "commondir"), "utf8")).trim());
1163
- return dirname3(commonDir);
1164
- } catch (error) {
1165
- if (error.code === "ENOENT")
1166
- return boundary.path;
1167
- throw error;
1168
- }
1170
+ async function canonicalPath(path) {
1171
+ const absolute = resolve2(path);
1172
+ return realpath(absolute).catch(() => absolute);
1173
+ }
1174
+ function samePath(left, right) {
1175
+ return process.platform === "win32" ? left.toLowerCase() === right.toLowerCase() : left === right;
1169
1176
  }
1170
1177
  async function resolveRepositoryRoot(cwd) {
1171
- const absoluteCwd = resolve2(cwd);
1178
+ const absoluteCwd = await canonicalPath(cwd);
1172
1179
  try {
1173
1180
  const { stdout } = await runGit(absoluteCwd, [
1174
1181
  "rev-parse",
1175
1182
  "--show-toplevel",
1183
+ "--git-dir",
1176
1184
  "--git-common-dir"
1177
1185
  ]);
1178
- const [rootLine, commonLine, ...extra] = stdout.trim().split(/\r?\n/);
1179
- if (!rootLine || !commonLine || extra.length) {
1186
+ const [rootLine, gitDirLine, commonLine, ...extra] = stdout.trim().split(/\r?\n/);
1187
+ if (!rootLine || !gitDirLine || !commonLine || extra.length) {
1180
1188
  return { kind: "git_unavailable", detail: "git rev-parse returned an unexpected response" };
1181
1189
  }
1182
- const root = resolve2(rootLine);
1183
- const commonDir = resolve2(root, commonLine);
1184
- const isLinkedWorktree = commonDir !== join4(root, ".git");
1190
+ const fromGit = (line) => canonicalPath(resolve2(absoluteCwd, line));
1191
+ const root = await fromGit(rootLine);
1192
+ const gitDir = await fromGit(gitDirLine);
1193
+ const commonDir = await fromGit(commonLine);
1194
+ const isLinkedWorktree = !samePath(gitDir, commonDir);
1185
1195
  const nested = await boundariesBetween(absoluteCwd, root);
1186
1196
  if (!isLinkedWorktree && await containingRepository(root)) {
1187
1197
  const boundary = await boundaryAt(root);
@@ -1203,14 +1213,41 @@ async function resolveRepositoryRoot(cwd) {
1203
1213
  return { kind: "git_unavailable", detail: failure.message };
1204
1214
  }
1205
1215
  }
1216
+ async function linkedWorktreeCommonDir(dir) {
1217
+ try {
1218
+ if (!(await lstat2(join4(dir, ".git"))).isFile())
1219
+ return void 0;
1220
+ const pointer = /^gitdir:\s*(.+?)\s*$/im.exec(await readFile2(join4(dir, ".git"), "utf8"))?.[1];
1221
+ if (!pointer)
1222
+ return void 0;
1223
+ const gitDir = resolve2(dir, pointer);
1224
+ const common = (await readFile2(join4(gitDir, "commondir"), "utf8")).trim();
1225
+ return common ? await canonicalPath(resolve2(gitDir, common)) : void 0;
1226
+ } catch {
1227
+ return void 0;
1228
+ }
1229
+ }
1230
+ async function mainCheckout(worktreeTop, commonDir) {
1231
+ try {
1232
+ const { stdout } = await runGit(worktreeTop, ["worktree", "list", "--porcelain"]);
1233
+ const first = stdout.split(/\r?\n\r?\n/)[0] ?? "";
1234
+ const path = /^worktree (.+)$/m.exec(first)?.[1];
1235
+ if (path) {
1236
+ const main = await canonicalPath(resolve2(worktreeTop, path));
1237
+ return /^bare$/m.test(first) ? dirname3(main) : main;
1238
+ }
1239
+ } catch {
1240
+ }
1241
+ return dirname3(commonDir);
1242
+ }
1206
1243
  async function readPlainIdentity(start2) {
1207
- const home = resolve2(homedir());
1244
+ const home = await canonicalPath(homedir());
1208
1245
  let directory = start2;
1209
1246
  while (true) {
1210
- const result = await readIdentityFile(directory);
1247
+ const result = await readIdentityFile(directory, { configurationOnlyAbsent: true });
1211
1248
  if (result.kind !== "absent")
1212
1249
  return { root: directory, result };
1213
- if (directory === home)
1250
+ if (samePath(directory, home))
1214
1251
  break;
1215
1252
  const parent = dirname3(directory);
1216
1253
  if (parent === directory)
@@ -1219,58 +1256,72 @@ async function readPlainIdentity(start2) {
1219
1256
  }
1220
1257
  return { root: start2, result: { kind: "absent" } };
1221
1258
  }
1259
+ async function nearestIdentity(start2) {
1260
+ const home = await canonicalPath(homedir());
1261
+ let directory = start2;
1262
+ while (true) {
1263
+ const commonDir = await linkedWorktreeCommonDir(directory);
1264
+ if (commonDir)
1265
+ return { worktreeTop: directory, commonDir };
1266
+ const result = await readIdentityFile(directory, { configurationOnlyAbsent: true });
1267
+ if (result.kind !== "absent")
1268
+ return { root: directory, result };
1269
+ if (samePath(directory, home))
1270
+ return void 0;
1271
+ const parent = dirname3(directory);
1272
+ if (parent === directory)
1273
+ return void 0;
1274
+ directory = parent;
1275
+ }
1276
+ }
1277
+ async function resolveNearestProjectIdentity(cwd) {
1278
+ const start2 = await canonicalPath(cwd);
1279
+ const resolved = await resolveRepositoryRoot(start2);
1280
+ const repository = resolved.kind === "git_unavailable" ? { kind: "plain", root: start2 } : resolved;
1281
+ const nested = repository.kind === "git" ? repository.nested : [];
1282
+ let found = await nearestIdentity(start2);
1283
+ if (found && "worktreeTop" in found) {
1284
+ found = await readPlainIdentity(await mainCheckout(found.worktreeTop, found.commonDir));
1285
+ }
1286
+ if (found) {
1287
+ return shapedIdentity(found, await projectFolderRepository(found.root, repository), nested);
1288
+ }
1289
+ return { ...withBase(repository, repository.root, nested), kind: "absent" };
1290
+ }
1291
+ async function resolveNamedProjectIdentity(cwd, options) {
1292
+ const start2 = await canonicalPath(cwd);
1293
+ const resolved = await resolveRepositoryRoot(start2);
1294
+ const repository = resolved.kind === "git_unavailable" ? { kind: "plain", root: start2 } : resolved;
1295
+ const commonDir = await linkedWorktreeCommonDir(start2);
1296
+ const root = commonDir ? await mainCheckout(start2, commonDir) : start2;
1297
+ return shapedIdentity({ root, result: await readIdentityFile(root, options) }, await projectFolderRepository(root, repository), repository.kind === "git" ? repository.nested : []);
1298
+ }
1299
+ function shapedIdentity(found, repository, nested) {
1300
+ const base = withBase(repository, found.root, nested);
1301
+ if (found.result.kind === "ok")
1302
+ return { ...base, ...found.result };
1303
+ if (found.result.kind === "unknown_version")
1304
+ return { ...base, ...found.result, path: found.root };
1305
+ if (found.result.kind === "malformed")
1306
+ return { ...base, ...found.result, path: found.root };
1307
+ return { ...base, kind: "absent" };
1308
+ }
1309
+ async function projectFolderRepository(projectRoot, known) {
1310
+ if (known.kind === "git" && samePath(known.root, projectRoot))
1311
+ return known;
1312
+ const repository = await resolveRepositoryRoot(projectRoot);
1313
+ if (repository.kind === "git" && samePath(repository.root, projectRoot))
1314
+ return repository;
1315
+ return { kind: "plain", root: projectRoot };
1316
+ }
1222
1317
  function withBase(repository, root, nested) {
1223
1318
  return { root, repository, nested };
1224
1319
  }
1225
1320
  async function resolveProjectIdentity(cwd, options = {}) {
1226
- const resolved = await resolveRepositoryRoot(cwd);
1227
- const repository = resolved.kind === "git_unavailable" ? { kind: "plain", root: resolve2(cwd) } : resolved;
1228
- if (repository.kind === "plain") {
1229
- const { root, result } = options.selectedRoot ? { root: repository.root, result: await readIdentityFile(repository.root, options) } : await readPlainIdentity(repository.root);
1230
- const base2 = withBase(repository, root, []);
1231
- if (result.kind === "ok")
1232
- return { ...base2, ...result };
1233
- if (result.kind === "unknown_version")
1234
- return { ...base2, ...result, path: root };
1235
- if (result.kind === "malformed")
1236
- return { ...base2, ...result, path: root };
1237
- return { ...base2, kind: "absent" };
1238
- }
1239
- const ownRoot = repository.isLinkedWorktree ? dirname3(repository.commonDir) : repository.root;
1240
- const outer = repository.isLinkedWorktree ? void 0 : await containingRepository(repository.root);
1241
- const own = await readIdentityFile(ownRoot, options);
1242
- if (!outer || options.selectedRoot || own.kind === "ok") {
1243
- const base2 = withBase(repository, ownRoot, repository.nested);
1244
- if (own.kind === "ok")
1245
- return { ...base2, ...own };
1246
- if (own.kind === "unknown_version")
1247
- return { ...base2, ...own, path: ownRoot };
1248
- if (own.kind === "malformed")
1249
- return { ...base2, ...own, path: ownRoot };
1250
- return { ...base2, kind: "absent" };
1251
- }
1252
- const nested = repository.nested;
1253
- const parentRoot = await canonicalBoundaryRoot(outer);
1254
- const parent = await readIdentityFile(parentRoot);
1255
- const base = withBase(repository, parentRoot, nested);
1256
- if (own.kind === "unknown_version")
1257
- return { ...base, ...own, path: ownRoot };
1258
- if (own.kind === "malformed")
1259
- return { ...base, ...own, path: ownRoot };
1260
- if (parent.kind === "unknown_version")
1261
- return { ...base, ...parent, path: parentRoot };
1262
- if (parent.kind === "malformed")
1263
- return { ...base, ...parent, path: parentRoot };
1264
- if (!options.allowNestedInherit) {
1265
- return {
1266
- ...base,
1267
- kind: "nested",
1268
- ...parent.kind === "ok" ? { parentIdentity: parent.identity } : {}
1269
- };
1321
+ if (options.selectedRoot || options.namedRoot) {
1322
+ return resolveNamedProjectIdentity(cwd, { selectedRoot: options.selectedRoot });
1270
1323
  }
1271
- if (parent.kind === "ok")
1272
- return { ...base, kind: "ok", identity: parent.identity };
1273
- return { ...base, kind: "absent" };
1324
+ return resolveNearestProjectIdentity(cwd);
1274
1325
  }
1275
1326
  var GIT_TIMEOUT_MS, GIT_ENV_KEYS;
1276
1327
  var init_repositoryRoot = __esm({
@@ -1291,6 +1342,23 @@ var init_repositoryRoot = __esm({
1291
1342
  }
1292
1343
  });
1293
1344
 
1345
+ // packages/shared/dist/hookTrace.js
1346
+ function hookTraceEnabled(env = process.env) {
1347
+ const value = env[HOOK_TRACE_ENV]?.trim().toLowerCase();
1348
+ return !value || !["off", "0", "false", "no", "disabled"].includes(value);
1349
+ }
1350
+ var HOOK_TRACE_MAX_BYTES, HOOK_TRACE_ENV;
1351
+ var init_hookTrace = __esm({
1352
+ "packages/shared/dist/hookTrace.js"() {
1353
+ "use strict";
1354
+ init_protectedPaths();
1355
+ init_runtimeSigners();
1356
+ init_secretPatterns();
1357
+ HOOK_TRACE_MAX_BYTES = 2 * 1024 * 1024;
1358
+ HOOK_TRACE_ENV = "MNEMONIK_HOOK_TRACE";
1359
+ }
1360
+ });
1361
+
1294
1362
  // packages/shared/dist/runtimeReader.js
1295
1363
  var RuntimeError;
1296
1364
  var init_runtimeReader = __esm({
@@ -1362,10 +1430,12 @@ var init_hookRuntime = __esm({
1362
1430
  init_runtimeSigners();
1363
1431
  init_hookTimeouts();
1364
1432
  init_repositoryRoot();
1433
+ init_hookTrace();
1365
1434
  init_runtimeReader();
1366
1435
  init_runtimeSigners();
1367
1436
  init_projectSetupHandoff();
1368
1437
  init_hookConversation();
1438
+ init_hookTrace();
1369
1439
  }
1370
1440
  });
1371
1441
 
@@ -2953,6 +3023,7 @@ import {
2953
3023
  humanIdentityState,
2954
3024
  INTERRUPTED_INSTALL,
2955
3025
  INTERRUPTED_INSTALL_MESSAGE,
3026
+ REGISTRY_FAILURE,
2956
3027
  approvalMessage,
2957
3028
  knownReason,
2958
3029
  SCANNER_APPROVAL_ACTION,
@@ -2985,7 +3056,7 @@ import {
2985
3056
  } from "./scanner/service.js";
2986
3057
 
2987
3058
  // packages/shared/dist/instructions.js
2988
- var INSTRUCTIONS_CONTENT = `Mnemonik provides persistent project memory: decisions, rationale, tasks, and policies across sessions. If no block begins "PROJECT_CONTEXT schemaVersion=", session_bootstrap({ cwd }) loads project context when cwd is the real absolute path of the project root; a placeholder or relative path fails. With that block, context is loaded and no session_bootstrap call is needed. Code and docs show current artifacts, git shows changes, and memory carries the why; memory_search recalls rationale or intent, then Grep or Read verifies current source.
3059
+ var INSTRUCTIONS_CONTENT = `Mnemonik provides persistent project memory: decisions, rationale, tasks, and policies across sessions. If no block begins "PROJECT_CONTEXT schemaVersion=", session_bootstrap({}) loads project context; installed hooks supply the project. With that block, context is loaded and no session_bootstrap call is needed. Code and docs show current artifacts, git shows changes, and memory carries the why; memory_search recalls rationale or intent, then Grep or Read verifies current source.
2989
3060
 
2990
3061
  What I learn about you belongs in Companion; what I learn about this project belongs here.
2991
3062
 
@@ -3022,6 +3093,9 @@ var MCP_INSTRUCTIONS = getMcpInstructions();
3022
3093
 
3023
3094
  // packages/shared/dist/codeScanner.js
3024
3095
  var import_ignore = __toESM(require_ignore(), 1);
3096
+ init_secretPatterns();
3097
+ init_protectedPaths();
3098
+ init_projectIdentityFile();
3025
3099
 
3026
3100
  // node_modules/web-tree-sitter/web-tree-sitter.js
3027
3101
  var __defProp2 = Object.defineProperty;
@@ -7067,9 +7141,9 @@ var Query = class {
7067
7141
  };
7068
7142
 
7069
7143
  // packages/shared/dist/ast/grammars.js
7070
- import { dirname, extname, join } from "node:path";
7144
+ import { dirname, extname, join as join2 } from "node:path";
7071
7145
  import { fileURLToPath } from "node:url";
7072
- var WASM_DIR = join(dirname(fileURLToPath(import.meta.url)), "..", "..", "wasm");
7146
+ var WASM_DIR = join2(dirname(fileURLToPath(import.meta.url)), "..", "..", "wasm");
7073
7147
 
7074
7148
  // packages/shared/dist/ast/astChunker.js
7075
7149
  var MAX_AST_PARSE_BYTES = 2 * 1024 * 1024;
@@ -7247,6 +7321,7 @@ var BUILT_IN_IGNORE_DIR_SET = new Set(BUILT_IN_IGNORE_DIRS);
7247
7321
  var import_ignore2 = __toESM(require_ignore(), 1);
7248
7322
 
7249
7323
  // packages/shared/dist/index.js
7324
+ init_secretPatterns();
7250
7325
  init_hookTimeouts();
7251
7326
 
7252
7327
  // packages/shared/dist/jitDeliveryAck.js
@@ -7262,6 +7337,7 @@ init_settingsIo();
7262
7337
  init_repositoryFingerprint();
7263
7338
  init_projectIdentityFile();
7264
7339
  init_repositoryRoot();
7340
+ init_protectedPaths();
7265
7341
 
7266
7342
  // packages/shared/dist/readiness.js
7267
7343
  var READINESS_SCHEMA_VERSION = 1;
@@ -7689,6 +7765,7 @@ public class MnemonikCredential {
7689
7765
  }`.replace(/\n\s*/gu, " ");
7690
7766
 
7691
7767
  // packages/cli/src/router.ts
7768
+ init_hookRuntime();
7692
7769
  import {
7693
7770
  runHosts,
7694
7771
  hostSource,
@@ -7697,9 +7774,15 @@ import {
7697
7774
  selectOwned
7698
7775
  } from "./install/hosts.js";
7699
7776
  import { hostOrder, launchHostLabels } from "./install/adapters.js";
7777
+ import {
7778
+ hookActivityLines,
7779
+ hooksLog,
7780
+ invocationLine,
7781
+ tracedHosts
7782
+ } from "./hooksLog.js";
7700
7783
  import { readInstallVersions } from "./install/ownership.js";
7701
7784
  import { ensureLauncher, removeLauncher, LauncherError } from "./launcher.js";
7702
- import { readFile as readFile4, realpath } from "node:fs/promises";
7785
+ import { readFile as readFile4, realpath as realpath2 } from "node:fs/promises";
7703
7786
  import { createInterface } from "node:readline";
7704
7787
  import {
7705
7788
  RuntimeStore,
@@ -7718,6 +7801,8 @@ import {
7718
7801
  createRealProjectRuntime,
7719
7802
  ensureProjectRoot,
7720
7803
  ensureProjectForAgent,
7804
+ insideProjectLine,
7805
+ projectFolderFor,
7721
7806
  folderRefusalMessage,
7722
7807
  projectLimitMessage,
7723
7808
  runProjectCommand
@@ -7928,10 +8013,13 @@ function scannerConsentRequired(output, json, agent) {
7928
8013
  else for (const line of approvalLines("scanner_consent_required", agent)) output.line(line);
7929
8014
  return 3;
7930
8015
  }
8016
+ var hostTargetFailed = (target) => target.status !== "READY" && target.reason !== "codex_trust_pending";
7931
8017
  function hostUpdateFailed(result) {
7932
- return result.results.some(
7933
- (target) => target.status !== "READY" && target.reason !== "codex_trust_pending"
7934
- );
8018
+ return result.results.some(hostTargetFailed);
8019
+ }
8020
+ function registryFailure(hostError, results) {
8021
+ const reasons = hostError !== void 0 ? [hostError] : results.filter(hostTargetFailed).map((target) => target.reason);
8022
+ return reasons.length > 0 && reasons.every((reason) => REGISTRY_FAILURE.test(reason)) ? reasons[0] : void 0;
7935
8023
  }
7936
8024
  var updateFailureMessage = "Mnemonik could not update. It will try again automatically tomorrow.";
7937
8025
  var booleans = /* @__PURE__ */ new Set([
@@ -7955,7 +8043,8 @@ var booleans = /* @__PURE__ */ new Set([
7955
8043
  "automatic",
7956
8044
  "cancel",
7957
8045
  "retry",
7958
- "skip"
8046
+ "skip",
8047
+ "follow"
7959
8048
  ]);
7960
8049
  var values = /* @__PURE__ */ new Set([
7961
8050
  "components",
@@ -7967,7 +8056,8 @@ var values = /* @__PURE__ */ new Set([
7967
8056
  "rollback",
7968
8057
  "out",
7969
8058
  "project",
7970
- "exclusions"
8059
+ "exclusions",
8060
+ "last"
7971
8061
  ]);
7972
8062
  var help = helpScreen([]) ?? "";
7973
8063
  function parse(args2) {
@@ -8411,7 +8501,11 @@ async function runHostCommand(command, parsed, deps, output, scannerSelected = f
8411
8501
  output.error(INTERRUPTED_INSTALL_MESSAGE.sentence);
8412
8502
  output.error(INTERRUPTED_INSTALL_MESSAGE.nextStep);
8413
8503
  } else if (hostError === "lock_held") output.error(humanReason(hostError));
8414
- else output.error("Run mnemonik repair, then start a new session in each coding tool.");
8504
+ else {
8505
+ const registry = registryFailure(hostError, result.results);
8506
+ if (registry) for (const line of humanReport(registry).split("\n")) output.error(line);
8507
+ else output.error("Run mnemonik repair, then start a new session in each coding tool.");
8508
+ }
8415
8509
  }
8416
8510
  if (scanner?.status === "FAILED") {
8417
8511
  output.error(scannerRecovery?.message ?? "The scanner could not update.");
@@ -8614,7 +8708,7 @@ async function enableCommand(parsed, deps, output) {
8614
8708
  if (!present && !roots)
8615
8709
  return actionRequired2(output, json, "Supply --scan-roots with approved roots", "--scan-roots");
8616
8710
  for (const folder of [...roots ?? [], ...exclusions ?? []]) {
8617
- const failure = await realpath(folder).then(
8711
+ const failure = await realpath2(folder).then(
8618
8712
  () => void 0,
8619
8713
  (error) => folderFailure(error.code, folder)
8620
8714
  );
@@ -8808,6 +8902,12 @@ async function doctorCommand(parsed, deps, output) {
8808
8902
  else {
8809
8903
  renderPreflight(result, output);
8810
8904
  renderStatusSummaries(document, output, { diagnostics: true });
8905
+ for (const line of await hookActivityLines(
8906
+ deps.installStateDir ?? stateDirectory(process.platform, process.env, deps.home),
8907
+ deps.hooksLog?.now?.() ?? Date.now(),
8908
+ deps.hooksLog?.alive
8909
+ ).catch(() => []))
8910
+ output.line(line);
8811
8911
  if (document.installation.reasons.some(
8812
8912
  (reason) => reason === "scanner_failing" || reason === "scanner_signed_out"
8813
8913
  ))
@@ -8894,7 +8994,7 @@ async function deleteProjectCommand(parsed, rest, deps, output) {
8894
8994
  const json = parsed.flags.has("json");
8895
8995
  const cwd = deps.cwd ?? process.cwd();
8896
8996
  const resolve3 = deps.projectResolver?.resolveProjectIdentity ?? resolveProjectIdentity;
8897
- const here = await resolve3(cwd, { allowNestedInherit: false });
8997
+ const here = await resolve3(cwd);
8898
8998
  const hereId = here.kind === "ok" ? here.identity.projectId : void 0;
8899
8999
  const root = "root" in here ? here.root : cwd;
8900
9000
  const target = rest[0] ?? hereId;
@@ -9064,8 +9164,13 @@ async function runCli(args2, deps = {}) {
9064
9164
  if ((action === "add" || action === "remove") && !personPresent(parsed, deps) && !parsed.flags.has("apply"))
9065
9165
  return actionRequired2(output, parsed.flags.has("json"), "Rerun with --apply", "--apply");
9066
9166
  const pathArgument = actionArguments[0] ?? "";
9067
- const requested = action === "add" ? await realpath(pathArgument) : saved.config.roots.find((root) => root === pathArgument) ?? pathArgument;
9167
+ const named = action === "add" ? await realpath2(pathArgument) : saved.config.roots.find((root) => root === pathArgument) ?? pathArgument;
9168
+ const requested = action === "add" ? await projectFolderFor(named, deps.projectExecutor ?? { resolveProjectIdentity }) : named;
9068
9169
  const name2 = requested.split(/[\\/]/u).filter(Boolean).at(-1) ?? requested;
9170
+ if (requested !== named && !parsed.flags.has("json"))
9171
+ output.line(
9172
+ insideProjectLine(named.split(/[\\/]/u).filter(Boolean).at(-1) ?? named, requested)
9173
+ );
9069
9174
  if (action === "add" && saved.config.roots.includes(requested)) {
9070
9175
  if (parsed.flags.has("json")) output.json(saved.config.roots);
9071
9176
  else output.line(alreadyConnectedFolderLine(name2));
@@ -9088,7 +9193,7 @@ async function runCli(args2, deps = {}) {
9088
9193
  getCliBearer: async () => bearer,
9089
9194
  fetch: deps.grantFetch
9090
9195
  })).executor;
9091
- const resolution = await executor.resolveProjectIdentity(requested);
9196
+ const resolution = await executor.resolveProjectIdentity(requested, { namedRoot: true });
9092
9197
  const decision = await evaluateRoot(resolution, { cwd: requested, home: deps.home });
9093
9198
  if (!decision.allowed) {
9094
9199
  if (parsed.flags.has("json")) return actionRequired2(output, true, decision.reason);
@@ -9392,6 +9497,59 @@ Preview: ${preview.path}
9392
9497
  return 1;
9393
9498
  }
9394
9499
  }
9500
+ if (command === "hooks") {
9501
+ const invalid = allowed(parsed, ["host", "last", "follow"]);
9502
+ if (invalid) return flagError(output, parsed, invalid);
9503
+ if (subcommand !== "log") return usageError(["hooks"]);
9504
+ if (rest.length) return argumentError(["hooks", "log"], ["hooks", "log"], rest, 0);
9505
+ const host = parsed.flags.get("host");
9506
+ if (host !== void 0 && !tracedHosts.includes(host))
9507
+ return usageFailure(
9508
+ output,
9509
+ parsed,
9510
+ { reason: "invalid_value", path: ["hooks", "log"], detail: String(host) },
9511
+ `Unknown coding tool: ${String(host)}. Use claude-code, codex, cursor or copilot.`
9512
+ );
9513
+ const lastFlag = parsed.flags.get("last");
9514
+ const last = lastFlag === void 0 ? 20 : Number(lastFlag);
9515
+ if (!Number.isInteger(last) || last < 1 || last > 1e4)
9516
+ return usageFailure(
9517
+ output,
9518
+ parsed,
9519
+ { reason: "invalid_value", path: ["hooks", "log"], detail: String(lastFlag) },
9520
+ "--last takes a whole number from 1 to 10000."
9521
+ );
9522
+ const json = parsed.flags.has("json");
9523
+ const follow = parsed.flags.has("follow");
9524
+ const stop2 = new AbortController();
9525
+ const interrupt = () => stop2.abort();
9526
+ if (follow && !deps.hooksLog?.signal) process.once("SIGINT", interrupt);
9527
+ try {
9528
+ const shown = await hooksLog({
9529
+ stateDir: deps.installStateDir ?? stateDirectory(process.platform, process.env, deps.home),
9530
+ hosts: host ? [host] : void 0,
9531
+ alive: deps.hooksLog?.alive,
9532
+ now: deps.hooksLog?.now,
9533
+ last,
9534
+ follow: follow && {
9535
+ signal: deps.hooksLog?.signal ?? stop2.signal,
9536
+ pollMs: deps.hooksLog?.pollMs
9537
+ },
9538
+ print: (invocation) => {
9539
+ if (json && follow) output.json(invocation);
9540
+ else if (!json) output.line(invocationLine(invocation));
9541
+ }
9542
+ });
9543
+ if (json && !follow) output.json({ invocations: shown });
9544
+ else if (!json && !shown.length && !follow)
9545
+ output.line(
9546
+ hookTraceEnabled(process.env) ? "No hook runs were found in this machine's Mnemonik state folder." : "No hook runs were found in this machine's Mnemonik state folder. MNEMONIK_HOOK_TRACE=off is set here, so hooks started from this environment record nothing."
9547
+ );
9548
+ } finally {
9549
+ process.removeListener("SIGINT", interrupt);
9550
+ }
9551
+ return 0;
9552
+ }
9395
9553
  if (command === "status") {
9396
9554
  const invalid = allowed(parsed, []);
9397
9555
  if (invalid || subcommand)