@mnemonik/cli 7.140.0 → 7.140.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (59) hide show
  1. package/dist/auth/credentials.js +40 -7
  2. package/dist/auth/index.js +40 -7
  3. package/dist/diagnostics.js +40 -7
  4. package/dist/digests.json +57 -53
  5. package/dist/help.d.ts.map +1 -1
  6. package/dist/help.js +50 -4
  7. package/dist/help.js.map +1 -1
  8. package/dist/hooksLog.d.ts +61 -0
  9. package/dist/hooksLog.d.ts.map +1 -0
  10. package/dist/hooksLog.js +350 -0
  11. package/dist/hooksLog.js.map +1 -0
  12. package/dist/humanReason.d.ts.map +1 -1
  13. package/dist/humanReason.js +0 -3
  14. package/dist/humanReason.js.map +1 -1
  15. package/dist/identity/migrate.js +101 -68
  16. package/dist/install/journal.js +40 -7
  17. package/dist/install/journey.d.ts.map +1 -1
  18. package/dist/install/journey.js +40 -10
  19. package/dist/install/journey.js.map +1 -1
  20. package/dist/install/ownership.js +40 -7
  21. package/dist/install/transaction.d.ts.map +1 -1
  22. package/dist/install/transaction.js +41 -9
  23. package/dist/install/transaction.js.map +1 -1
  24. package/dist/install/ui.js +40 -7
  25. package/dist/installation.js +40 -7
  26. package/dist/launcher.js +40 -7
  27. package/dist/preflight.js +200 -163
  28. package/dist/project/eligibility.js +40 -7
  29. package/dist/project/records.js +40 -7
  30. package/dist/project.d.ts +34 -1
  31. package/dist/project.d.ts.map +1 -1
  32. package/dist/project.js +291 -230
  33. package/dist/project.js.map +1 -1
  34. package/dist/router.d.ts +8 -0
  35. package/dist/router.d.ts.map +1 -1
  36. package/dist/router.js +328 -177
  37. package/dist/router.js.map +1 -1
  38. package/dist/runtime/releaseSource.js +40 -7
  39. package/dist/runtime/selfUpdate.js +40 -7
  40. package/dist/runtime/signers.js +781 -174
  41. package/dist/runtime/store.js +28 -0
  42. package/dist/runtime/updateCheck.js +40 -7
  43. package/dist/sbom.json +21 -21
  44. package/dist/scanner/control.js +40 -7
  45. package/dist/scanner/discover.d.ts.map +1 -1
  46. package/dist/scanner/discover.js +210 -170
  47. package/dist/scanner/discover.js.map +1 -1
  48. package/dist/scanner/enable.d.ts.map +1 -1
  49. package/dist/scanner/enable.js +45 -8
  50. package/dist/scanner/enable.js.map +1 -1
  51. package/dist/scanner/picker.d.ts.map +1 -1
  52. package/dist/scanner/picker.js +11 -3
  53. package/dist/scanner/picker.js.map +1 -1
  54. package/dist/scanner/service.js +40 -7
  55. package/dist/scanner/update.js +40 -7
  56. package/dist/scanner-release.json +24 -24
  57. package/dist/status.js +40 -7
  58. package/dist/vendor/local-setup.js +43 -10
  59. 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
 
@@ -3022,6 +3092,9 @@ var MCP_INSTRUCTIONS = getMcpInstructions();
3022
3092
 
3023
3093
  // packages/shared/dist/codeScanner.js
3024
3094
  var import_ignore = __toESM(require_ignore(), 1);
3095
+ init_secretPatterns();
3096
+ init_protectedPaths();
3097
+ init_projectIdentityFile();
3025
3098
 
3026
3099
  // node_modules/web-tree-sitter/web-tree-sitter.js
3027
3100
  var __defProp2 = Object.defineProperty;
@@ -7067,9 +7140,9 @@ var Query = class {
7067
7140
  };
7068
7141
 
7069
7142
  // packages/shared/dist/ast/grammars.js
7070
- import { dirname, extname, join } from "node:path";
7143
+ import { dirname, extname, join as join2 } from "node:path";
7071
7144
  import { fileURLToPath } from "node:url";
7072
- var WASM_DIR = join(dirname(fileURLToPath(import.meta.url)), "..", "..", "wasm");
7145
+ var WASM_DIR = join2(dirname(fileURLToPath(import.meta.url)), "..", "..", "wasm");
7073
7146
 
7074
7147
  // packages/shared/dist/ast/astChunker.js
7075
7148
  var MAX_AST_PARSE_BYTES = 2 * 1024 * 1024;
@@ -7247,6 +7320,7 @@ var BUILT_IN_IGNORE_DIR_SET = new Set(BUILT_IN_IGNORE_DIRS);
7247
7320
  var import_ignore2 = __toESM(require_ignore(), 1);
7248
7321
 
7249
7322
  // packages/shared/dist/index.js
7323
+ init_secretPatterns();
7250
7324
  init_hookTimeouts();
7251
7325
 
7252
7326
  // packages/shared/dist/jitDeliveryAck.js
@@ -7262,6 +7336,7 @@ init_settingsIo();
7262
7336
  init_repositoryFingerprint();
7263
7337
  init_projectIdentityFile();
7264
7338
  init_repositoryRoot();
7339
+ init_protectedPaths();
7265
7340
 
7266
7341
  // packages/shared/dist/readiness.js
7267
7342
  var READINESS_SCHEMA_VERSION = 1;
@@ -7689,6 +7764,7 @@ public class MnemonikCredential {
7689
7764
  }`.replace(/\n\s*/gu, " ");
7690
7765
 
7691
7766
  // packages/cli/src/router.ts
7767
+ init_hookRuntime();
7692
7768
  import {
7693
7769
  runHosts,
7694
7770
  hostSource,
@@ -7697,9 +7773,15 @@ import {
7697
7773
  selectOwned
7698
7774
  } from "./install/hosts.js";
7699
7775
  import { hostOrder, launchHostLabels } from "./install/adapters.js";
7776
+ import {
7777
+ hookActivityLines,
7778
+ hooksLog,
7779
+ invocationLine,
7780
+ tracedHosts
7781
+ } from "./hooksLog.js";
7700
7782
  import { readInstallVersions } from "./install/ownership.js";
7701
7783
  import { ensureLauncher, removeLauncher, LauncherError } from "./launcher.js";
7702
- import { readFile as readFile4, realpath } from "node:fs/promises";
7784
+ import { readFile as readFile4, realpath as realpath2 } from "node:fs/promises";
7703
7785
  import { createInterface } from "node:readline";
7704
7786
  import {
7705
7787
  RuntimeStore,
@@ -7718,6 +7800,7 @@ import {
7718
7800
  createRealProjectRuntime,
7719
7801
  ensureProjectRoot,
7720
7802
  ensureProjectForAgent,
7803
+ projectFolderFor,
7721
7804
  folderRefusalMessage,
7722
7805
  projectLimitMessage,
7723
7806
  runProjectCommand
@@ -7779,6 +7862,7 @@ var removeFolderPrompt = (name2) => `Stop indexing ${name2}? Its memories stay i
7779
7862
  var connectedFolderLine = (name2) => ` \u2713 Connected ${name2}.`;
7780
7863
  var alreadyConnectedFolderLine = (name2) => ` \u2713 ${name2} is already connected and watched.`;
7781
7864
  var removedFolderLine = (name2) => ` \u2713 ${name2} is no longer connected.`;
7865
+ var insideProjectLine = (name2, projectFolder) => `${name2} is part of the project in ${projectFolder}, so Mnemonik connects that folder.`;
7782
7866
  var projectDeletionWarning = (name2) => `Deleting ${name2} removes its memories, code index and summaries for everyone. This cannot be undone.`;
7783
7867
  var projectDeletedLine = (name2) => `Deleted ${name2}.`;
7784
7868
  var NOTHING_DELETED_LINE = "Nothing was deleted.";
@@ -7955,7 +8039,8 @@ var booleans = /* @__PURE__ */ new Set([
7955
8039
  "automatic",
7956
8040
  "cancel",
7957
8041
  "retry",
7958
- "skip"
8042
+ "skip",
8043
+ "follow"
7959
8044
  ]);
7960
8045
  var values = /* @__PURE__ */ new Set([
7961
8046
  "components",
@@ -7967,7 +8052,8 @@ var values = /* @__PURE__ */ new Set([
7967
8052
  "rollback",
7968
8053
  "out",
7969
8054
  "project",
7970
- "exclusions"
8055
+ "exclusions",
8056
+ "last"
7971
8057
  ]);
7972
8058
  var help = helpScreen([]) ?? "";
7973
8059
  function parse(args2) {
@@ -8614,7 +8700,7 @@ async function enableCommand(parsed, deps, output) {
8614
8700
  if (!present && !roots)
8615
8701
  return actionRequired2(output, json, "Supply --scan-roots with approved roots", "--scan-roots");
8616
8702
  for (const folder of [...roots ?? [], ...exclusions ?? []]) {
8617
- const failure = await realpath(folder).then(
8703
+ const failure = await realpath2(folder).then(
8618
8704
  () => void 0,
8619
8705
  (error) => folderFailure(error.code, folder)
8620
8706
  );
@@ -8808,6 +8894,12 @@ async function doctorCommand(parsed, deps, output) {
8808
8894
  else {
8809
8895
  renderPreflight(result, output);
8810
8896
  renderStatusSummaries(document, output, { diagnostics: true });
8897
+ for (const line of await hookActivityLines(
8898
+ deps.installStateDir ?? stateDirectory(process.platform, process.env, deps.home),
8899
+ deps.hooksLog?.now?.() ?? Date.now(),
8900
+ deps.hooksLog?.alive
8901
+ ).catch(() => []))
8902
+ output.line(line);
8811
8903
  if (document.installation.reasons.some(
8812
8904
  (reason) => reason === "scanner_failing" || reason === "scanner_signed_out"
8813
8905
  ))
@@ -8894,7 +8986,7 @@ async function deleteProjectCommand(parsed, rest, deps, output) {
8894
8986
  const json = parsed.flags.has("json");
8895
8987
  const cwd = deps.cwd ?? process.cwd();
8896
8988
  const resolve3 = deps.projectResolver?.resolveProjectIdentity ?? resolveProjectIdentity;
8897
- const here = await resolve3(cwd, { allowNestedInherit: false });
8989
+ const here = await resolve3(cwd);
8898
8990
  const hereId = here.kind === "ok" ? here.identity.projectId : void 0;
8899
8991
  const root = "root" in here ? here.root : cwd;
8900
8992
  const target = rest[0] ?? hereId;
@@ -9064,8 +9156,13 @@ async function runCli(args2, deps = {}) {
9064
9156
  if ((action === "add" || action === "remove") && !personPresent(parsed, deps) && !parsed.flags.has("apply"))
9065
9157
  return actionRequired2(output, parsed.flags.has("json"), "Rerun with --apply", "--apply");
9066
9158
  const pathArgument = actionArguments[0] ?? "";
9067
- const requested = action === "add" ? await realpath(pathArgument) : saved.config.roots.find((root) => root === pathArgument) ?? pathArgument;
9159
+ const named = action === "add" ? await realpath2(pathArgument) : saved.config.roots.find((root) => root === pathArgument) ?? pathArgument;
9160
+ const requested = action === "add" ? await projectFolderFor(named, deps.projectExecutor ?? { resolveProjectIdentity }) : named;
9068
9161
  const name2 = requested.split(/[\\/]/u).filter(Boolean).at(-1) ?? requested;
9162
+ if (requested !== named && !parsed.flags.has("json"))
9163
+ output.line(
9164
+ insideProjectLine(named.split(/[\\/]/u).filter(Boolean).at(-1) ?? named, requested)
9165
+ );
9069
9166
  if (action === "add" && saved.config.roots.includes(requested)) {
9070
9167
  if (parsed.flags.has("json")) output.json(saved.config.roots);
9071
9168
  else output.line(alreadyConnectedFolderLine(name2));
@@ -9088,7 +9185,7 @@ async function runCli(args2, deps = {}) {
9088
9185
  getCliBearer: async () => bearer,
9089
9186
  fetch: deps.grantFetch
9090
9187
  })).executor;
9091
- const resolution = await executor.resolveProjectIdentity(requested);
9188
+ const resolution = await executor.resolveProjectIdentity(requested, { namedRoot: true });
9092
9189
  const decision = await evaluateRoot(resolution, { cwd: requested, home: deps.home });
9093
9190
  if (!decision.allowed) {
9094
9191
  if (parsed.flags.has("json")) return actionRequired2(output, true, decision.reason);
@@ -9392,6 +9489,59 @@ Preview: ${preview.path}
9392
9489
  return 1;
9393
9490
  }
9394
9491
  }
9492
+ if (command === "hooks") {
9493
+ const invalid = allowed(parsed, ["host", "last", "follow"]);
9494
+ if (invalid) return flagError(output, parsed, invalid);
9495
+ if (subcommand !== "log") return usageError(["hooks"]);
9496
+ if (rest.length) return argumentError(["hooks", "log"], ["hooks", "log"], rest, 0);
9497
+ const host = parsed.flags.get("host");
9498
+ if (host !== void 0 && !tracedHosts.includes(host))
9499
+ return usageFailure(
9500
+ output,
9501
+ parsed,
9502
+ { reason: "invalid_value", path: ["hooks", "log"], detail: String(host) },
9503
+ `Unknown coding tool: ${String(host)}. Use claude-code, codex, cursor or copilot.`
9504
+ );
9505
+ const lastFlag = parsed.flags.get("last");
9506
+ const last = lastFlag === void 0 ? 20 : Number(lastFlag);
9507
+ if (!Number.isInteger(last) || last < 1 || last > 1e4)
9508
+ return usageFailure(
9509
+ output,
9510
+ parsed,
9511
+ { reason: "invalid_value", path: ["hooks", "log"], detail: String(lastFlag) },
9512
+ "--last takes a whole number from 1 to 10000."
9513
+ );
9514
+ const json = parsed.flags.has("json");
9515
+ const follow = parsed.flags.has("follow");
9516
+ const stop2 = new AbortController();
9517
+ const interrupt = () => stop2.abort();
9518
+ if (follow && !deps.hooksLog?.signal) process.once("SIGINT", interrupt);
9519
+ try {
9520
+ const shown = await hooksLog({
9521
+ stateDir: deps.installStateDir ?? stateDirectory(process.platform, process.env, deps.home),
9522
+ hosts: host ? [host] : void 0,
9523
+ alive: deps.hooksLog?.alive,
9524
+ now: deps.hooksLog?.now,
9525
+ last,
9526
+ follow: follow && {
9527
+ signal: deps.hooksLog?.signal ?? stop2.signal,
9528
+ pollMs: deps.hooksLog?.pollMs
9529
+ },
9530
+ print: (invocation) => {
9531
+ if (json && follow) output.json(invocation);
9532
+ else if (!json) output.line(invocationLine(invocation));
9533
+ }
9534
+ });
9535
+ if (json && !follow) output.json({ invocations: shown });
9536
+ else if (!json && !shown.length && !follow)
9537
+ output.line(
9538
+ 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."
9539
+ );
9540
+ } finally {
9541
+ process.removeListener("SIGINT", interrupt);
9542
+ }
9543
+ return 0;
9544
+ }
9395
9545
  if (command === "status") {
9396
9546
  const invalid = allowed(parsed, []);
9397
9547
  if (invalid || subcommand)
@@ -9966,6 +10116,7 @@ export {
9966
10116
  dataDeletePrompt,
9967
10117
  help,
9968
10118
  identityFileKeptLine,
10119
+ insideProjectLine,
9969
10120
  maintenanceExitCode,
9970
10121
  projectDeletedLine,
9971
10122
  projectDeletionWarning,