@mnemonik/cli 7.109.3 → 7.110.0

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 (104) hide show
  1. package/dist/auth/credentials.js +297 -36
  2. package/dist/auth/index.d.ts +2 -0
  3. package/dist/auth/index.d.ts.map +1 -1
  4. package/dist/auth/index.js +1914 -19
  5. package/dist/auth/index.js.map +1 -1
  6. package/dist/auth/machineName.d.ts +8 -0
  7. package/dist/auth/machineName.d.ts.map +1 -0
  8. package/dist/auth/machineName.js +23 -0
  9. package/dist/auth/machineName.js.map +1 -0
  10. package/dist/consent.d.ts +29 -0
  11. package/dist/consent.d.ts.map +1 -0
  12. package/dist/consent.js +87 -0
  13. package/dist/consent.js.map +1 -0
  14. package/dist/diagnostics.js +301 -40
  15. package/dist/digests.json +102 -90
  16. package/dist/help.d.ts.map +1 -1
  17. package/dist/help.js +59 -44
  18. package/dist/help.js.map +1 -1
  19. package/dist/humanReason.d.ts +8 -0
  20. package/dist/humanReason.d.ts.map +1 -1
  21. package/dist/humanReason.js +44 -16
  22. package/dist/humanReason.js.map +1 -1
  23. package/dist/identity/migrate.js +1835 -44
  24. package/dist/install/hosts.d.ts +2 -0
  25. package/dist/install/hosts.d.ts.map +1 -1
  26. package/dist/install/hosts.js +23 -14
  27. package/dist/install/hosts.js.map +1 -1
  28. package/dist/install/journal.js +124 -11
  29. package/dist/install/journey.d.ts +20 -0
  30. package/dist/install/journey.d.ts.map +1 -1
  31. package/dist/install/journey.js +1913 -82
  32. package/dist/install/journey.js.map +1 -1
  33. package/dist/install/ownership.d.ts.map +1 -1
  34. package/dist/install/ownership.js +1818 -10
  35. package/dist/install/ownership.js.map +1 -1
  36. package/dist/install/transaction.js +1913 -19
  37. package/dist/install/ui.js +1910 -15
  38. package/dist/installation.js +133 -10
  39. package/dist/launcher.js +108 -4
  40. package/dist/output.d.ts +3 -0
  41. package/dist/output.d.ts.map +1 -1
  42. package/dist/output.js +11 -1
  43. package/dist/output.js.map +1 -1
  44. package/dist/preflight.d.ts +5 -0
  45. package/dist/preflight.d.ts.map +1 -1
  46. package/dist/preflight.js +14 -12
  47. package/dist/preflight.js.map +1 -1
  48. package/dist/project/eligibility.js +1917 -22
  49. package/dist/project/records.js +121 -8
  50. package/dist/project.d.ts +15 -0
  51. package/dist/project.d.ts.map +1 -1
  52. package/dist/project.js +229 -15
  53. package/dist/project.js.map +1 -1
  54. package/dist/router.d.ts +2 -0
  55. package/dist/router.d.ts.map +1 -1
  56. package/dist/router.js +2164 -122
  57. package/dist/router.js.map +1 -1
  58. package/dist/runtime/releaseSource.d.ts +6 -0
  59. package/dist/runtime/releaseSource.d.ts.map +1 -1
  60. package/dist/runtime/releaseSource.js +1933 -32
  61. package/dist/runtime/releaseSource.js.map +1 -1
  62. package/dist/runtime/selfUpdate.js +1809 -8
  63. package/dist/runtime/signers.js +28 -9
  64. package/dist/runtime/store.js +18 -6
  65. package/dist/runtime/updateCheck.d.ts +4 -0
  66. package/dist/runtime/updateCheck.d.ts.map +1 -0
  67. package/dist/runtime/updateCheck.js +6695 -0
  68. package/dist/runtime/updateCheck.js.map +1 -0
  69. package/dist/sbom.json +21 -21
  70. package/dist/scanner/control.d.ts +27 -1
  71. package/dist/scanner/control.d.ts.map +1 -1
  72. package/dist/scanner/control.js +207 -36
  73. package/dist/scanner/control.js.map +1 -1
  74. package/dist/scanner/discover.d.ts +5 -0
  75. package/dist/scanner/discover.d.ts.map +1 -1
  76. package/dist/scanner/discover.js +16 -6
  77. package/dist/scanner/discover.js.map +1 -1
  78. package/dist/scanner/enable.d.ts +2 -0
  79. package/dist/scanner/enable.d.ts.map +1 -1
  80. package/dist/scanner/enable.js +152 -14
  81. package/dist/scanner/enable.js.map +1 -1
  82. package/dist/scanner/picker.d.ts.map +1 -1
  83. package/dist/scanner/picker.js +5 -6
  84. package/dist/scanner/picker.js.map +1 -1
  85. package/dist/scanner/service.d.ts +2 -0
  86. package/dist/scanner/service.d.ts.map +1 -1
  87. package/dist/scanner/service.js +279 -54
  88. package/dist/scanner/service.js.map +1 -1
  89. package/dist/scanner/update.d.ts +8 -0
  90. package/dist/scanner/update.d.ts.map +1 -1
  91. package/dist/scanner/update.js +254 -20
  92. package/dist/scanner/update.js.map +1 -1
  93. package/dist/scanner-release.json +35 -30
  94. package/dist/screens/journey.d.ts +6 -1
  95. package/dist/screens/journey.d.ts.map +1 -1
  96. package/dist/screens/journey.js +29 -8
  97. package/dist/screens/journey.js.map +1 -1
  98. package/dist/status.d.ts +21 -0
  99. package/dist/status.d.ts.map +1 -1
  100. package/dist/status.js +1993 -39
  101. package/dist/status.js.map +1 -1
  102. package/dist/vendor/local-setup.js +256 -29
  103. package/dist/vendor/shared/runtimeSigners.js +38 -11
  104. package/package.json +31 -31
@@ -516,8 +516,25 @@ function readIdentity(stdout) {
516
516
  throw new Error("acl_identity_unavailable");
517
517
  return { name: match[1], sid: match[2] };
518
518
  }
519
+ function* readWindowsIdentity() {
520
+ const user = readIdentity(yield [windowsCommand("whoami.exe"), identityArgs]);
521
+ if (!ADMINISTRATOR_RID.test(user.sid))
522
+ return user;
523
+ const groups = yield [windowsCommand("whoami.exe"), groupArgs];
524
+ const local = /^"[^"\r\n]*","[^"\r\n]*","S-1-5-113",/m.test(groups);
525
+ return {
526
+ ...user,
527
+ localAdministratorSid: local ? `${user.sid.slice(0, user.sid.lastIndexOf("-"))}-500` : null
528
+ };
529
+ }
519
530
  function windowsCurrentAccountSync() {
520
- return currentIdentity ??= readIdentity(hookExecuteSync(windowsCommand("whoami.exe"), identityArgs));
531
+ if (currentIdentity)
532
+ return currentIdentity;
533
+ const commands = readWindowsIdentity();
534
+ let step = commands.next();
535
+ while (!step.done)
536
+ step = commands.next(hookExecuteSync(...step.value));
537
+ return currentIdentity = step.value;
521
538
  }
522
539
  function hookInvocation(file, args2) {
523
540
  if (win32.basename(file) === "whoami.exe") {
@@ -537,11 +554,13 @@ function hookExecuteSync(file, args2, input) {
537
554
  [file, args2] = hookInvocation(file, args2);
538
555
  return execFileSync(file, args2, { ...hookOptions(file, args2), input });
539
556
  }
540
- var identityArgs, currentIdentity, ttl, hookOptions;
557
+ var identityArgs, groupArgs, ADMINISTRATOR_RID, currentIdentity, ttl, hookOptions;
541
558
  var init_runtimeSigners = __esm({
542
559
  "packages/shared/dist/runtimeSigners.js"() {
543
560
  "use strict";
544
561
  identityArgs = ["/user", "/fo", "csv", "/nh"];
562
+ groupArgs = ["/groups", "/fo", "csv", "/nh"];
563
+ ADMINISTRATOR_RID = /^S-1-5-21-\d+-\d+-\d+-500$/;
545
564
  ttl = 10 * 6e4;
546
565
  hookOptions = (file, args2) => ({
547
566
  encoding: args2.includes("/u") ? "utf16le" : "utf8",
@@ -2199,7 +2218,7 @@ var require_proper_lockfile = __commonJS({
2199
2218
  // packages/cli/src/project/records.ts
2200
2219
  import { createHash as createHash2, randomUUID as randomUUID2 } from "node:crypto";
2201
2220
  import { readFile as readFile2, readdir, unlink as unlink2 } from "node:fs/promises";
2202
- import { join as join4 } from "node:path";
2221
+ import { join as join5 } from "node:path";
2203
2222
 
2204
2223
  // packages/shared/dist/instructions.js
2205
2224
  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.
@@ -6555,6 +6574,100 @@ async function withLock(path, waitMs, work) {
6555
6574
  }
6556
6575
  }
6557
6576
 
6577
+ // packages/local-setup/dist/automaticUpdate.js
6578
+ import { spawn as nodeSpawn } from "node:child_process";
6579
+ import { realpathSync } from "node:fs";
6580
+ import { stat as nodeStat, utimes } from "node:fs/promises";
6581
+ import { homedir as homedir2 } from "node:os";
6582
+ import { join as join4 } from "node:path";
6583
+ import { fileURLToPath as fileURLToPath2 } from "node:url";
6584
+ var DAY_MS = 864e5;
6585
+ var statePath = (stateDir) => join4(stateDir, "automatic-update.json");
6586
+ function cliLauncherPath(options = {}) {
6587
+ const platform = options.platform ?? process.platform;
6588
+ const home = options.home ?? homedir2();
6589
+ return platform === "win32" ? join4(options.env?.LOCALAPPDATA || join4(home, "AppData", "Local"), "Mnemonik", "bin", "mnemonik.cmd") : join4(home, ".local", "bin", "mnemonik");
6590
+ }
6591
+ async function readState(path) {
6592
+ const bytes = await readBytes(path);
6593
+ if (!bytes)
6594
+ return {};
6595
+ const value = JSON.parse(bytes.toString());
6596
+ if (!value || typeof value !== "object" || value.lastAttempt !== void 0 && (!Number.isFinite(value.lastAttempt) || value.lastAttempt < 0))
6597
+ throw new Error("automatic_update_state_invalid");
6598
+ return value.lastAttempt === void 0 ? {} : { lastAttempt: value.lastAttempt };
6599
+ }
6600
+ async function maybeStartAutomaticUpdate(options = {}) {
6601
+ const stateDir = options.stateDir ?? stateDirectory(options.platform, options.env, options.home);
6602
+ const path = statePath(stateDir);
6603
+ try {
6604
+ const now = (options.now ?? Date.now)();
6605
+ const stat = options.stat ?? nodeStat;
6606
+ try {
6607
+ if (now - (await stat(path)).mtimeMs < DAY_MS)
6608
+ return false;
6609
+ } catch (error) {
6610
+ if (error.code !== "ENOENT")
6611
+ throw error;
6612
+ }
6613
+ if (!options.spawn && !(await stat(cliLauncherPath(options))).isFile())
6614
+ return false;
6615
+ return await withLock(path, 0, async (assertOwned) => {
6616
+ const state = await readState(path);
6617
+ if (state.lastAttempt !== void 0 && now - state.lastAttempt < DAY_MS)
6618
+ return false;
6619
+ await atomicWrite(path, Buffer.from(`${JSON.stringify({ lastAttempt: now })}
6620
+ `), void 0, assertOwned);
6621
+ await utimes(path, now / 1e3, now / 1e3);
6622
+ const platform = options.platform ?? process.platform;
6623
+ const launcher = cliLauncherPath(options);
6624
+ const command = platform === "win32" ? {
6625
+ file: options.env?.ComSpec || "cmd.exe",
6626
+ args: ["/d", "/s", "/c", `""${launcher}" update --automatic"`],
6627
+ windowsVerbatimArguments: true
6628
+ } : platform === "linux" && (options.env ?? process.env).INVOCATION_ID ? {
6629
+ // detached creates a process group, not a new systemd cgroup. An
6630
+ // updater spawned by the scanner must survive stopping that service.
6631
+ file: "systemd-run",
6632
+ args: [
6633
+ "--user",
6634
+ "--collect",
6635
+ "--quiet",
6636
+ `--setenv=MNEMONIK_STATE_DIR=${stateDir}`,
6637
+ "--",
6638
+ launcher,
6639
+ "update",
6640
+ "--automatic"
6641
+ ]
6642
+ } : { file: launcher, args: ["update", "--automatic"] };
6643
+ const child = (options.spawn ?? nodeSpawn)(command.file, command.args, {
6644
+ detached: true,
6645
+ stdio: "ignore",
6646
+ windowsHide: true,
6647
+ ..."windowsVerbatimArguments" in command ? { windowsVerbatimArguments: command.windowsVerbatimArguments } : {}
6648
+ });
6649
+ child.once("error", () => {
6650
+ });
6651
+ child.unref();
6652
+ return true;
6653
+ });
6654
+ } catch {
6655
+ return false;
6656
+ }
6657
+ }
6658
+ var HELPER_FLAG = "--mnemonik-automatic-update";
6659
+ function isHelperInvocation() {
6660
+ if (process.argv[2] !== HELPER_FLAG || !process.argv[3] || !process.argv[1])
6661
+ return false;
6662
+ try {
6663
+ return realpathSync(process.argv[1]) === realpathSync(fileURLToPath2(import.meta.url));
6664
+ } catch {
6665
+ return false;
6666
+ }
6667
+ }
6668
+ if (isHelperInvocation())
6669
+ void maybeStartAutomaticUpdate({ stateDir: process.argv[3] });
6670
+
6558
6671
  // packages/local-setup/dist/index.js
6559
6672
  var MAX_ORIGINAL_BYTES = 64 * 1024;
6560
6673
 
@@ -6562,7 +6675,7 @@ var MAX_ORIGINAL_BYTES = 64 * 1024;
6562
6675
  var digest = (bytes) => `sha256:${createHash2("sha256").update(bytes).digest("hex")}`;
6563
6676
  async function identityHash(root) {
6564
6677
  try {
6565
- return digest(await readFile2(join4(root, ".mnemonik.json")));
6678
+ return digest(await readFile2(join5(root, ".mnemonik.json")));
6566
6679
  } catch (error) {
6567
6680
  if (error.code === "ENOENT") return null;
6568
6681
  throw error;
@@ -6570,14 +6683,14 @@ async function identityHash(root) {
6570
6683
  }
6571
6684
  var ownerLabel = (owner) => !owner || owner === "personal" ? "personal" : `team:${owner.teamId}`;
6572
6685
  async function saveCommandRecord(record, stateDir = stateDirectory()) {
6573
- const directory = join4(
6686
+ const directory = join5(
6574
6687
  stateDir,
6575
6688
  "project-commands",
6576
6689
  createHash2("sha256").update(record.resolvedRoot).digest("hex")
6577
6690
  );
6578
- const lock = join4(directory, ".records");
6691
+ const lock = join5(directory, ".records");
6579
6692
  await withLock(lock, 6e4, async (assertOwned) => {
6580
- const file = join4(directory, `${Date.now().toString().padStart(16, "0")}-${randomUUID2()}.json`);
6693
+ const file = join5(directory, `${Date.now().toString().padStart(16, "0")}-${randomUUID2()}.json`);
6581
6694
  await atomicWrite(
6582
6695
  file,
6583
6696
  Buffer.from(`${JSON.stringify(record, null, 2)}
@@ -6588,7 +6701,7 @@ async function saveCommandRecord(record, stateDir = stateDirectory()) {
6588
6701
  const files = (await readdir(directory)).filter((name2) => name2.endsWith(".json")).sort();
6589
6702
  for (const stale of files.slice(0, -20)) {
6590
6703
  await assertOwned();
6591
- await unlink2(join4(directory, stale));
6704
+ await unlink2(join5(directory, stale));
6592
6705
  }
6593
6706
  });
6594
6707
  }
package/dist/project.d.ts CHANGED
@@ -10,6 +10,19 @@ export interface ProjectExecutor {
10
10
  apply(options: EnsureOptions): Promise<SetupResult>;
11
11
  rollback(options: EnsureOptions): Promise<SetupResult>;
12
12
  }
13
+ /**
14
+ * The command that gives each offered answer, for an agent that reads JSON.
15
+ * An action no command can give is left out.
16
+ */
17
+ export declare function answerCommands(actions: readonly string[], root: string, details?: {
18
+ candidates?: Array<{
19
+ projectId: string;
20
+ }>;
21
+ projectId?: string;
22
+ }): Array<{
23
+ action: string;
24
+ command: string;
25
+ }>;
13
26
  export declare function ensureProjectRoot(root: string, executor: ProjectExecutor): Promise<SetupResult>;
14
27
  /** Does Mnemonik have plain words for this state? */
15
28
  export declare const hasRefusalWords: (reason: string) => boolean;
@@ -118,6 +131,8 @@ export interface ProjectCommandInput {
118
131
  apply: boolean;
119
132
  confirmMismatch: boolean;
120
133
  replace: boolean;
134
+ /** Answer a waiting setup with cancel: clear the folder's setup notes. */
135
+ cancel?: boolean;
121
136
  owner?: string;
122
137
  }
123
138
  export declare function runProjectCommand(input: ProjectCommandInput, deps: ProjectCommandDependencies): Promise<number>;
@@ -1 +1 @@
1
- {"version":3,"file":"project.d.ts","sourceRoot":"","sources":["../src/project.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"project.d.ts","sourceRoot":"","sources":["../src/project.ts"],"names":[],"mappings":"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"}
package/dist/project.js CHANGED
@@ -516,8 +516,25 @@ function readIdentity(stdout) {
516
516
  throw new Error("acl_identity_unavailable");
517
517
  return { name: match[1], sid: match[2] };
518
518
  }
519
+ function* readWindowsIdentity() {
520
+ const user = readIdentity(yield [windowsCommand("whoami.exe"), identityArgs]);
521
+ if (!ADMINISTRATOR_RID.test(user.sid))
522
+ return user;
523
+ const groups = yield [windowsCommand("whoami.exe"), groupArgs];
524
+ const local = /^"[^"\r\n]*","[^"\r\n]*","S-1-5-113",/m.test(groups);
525
+ return {
526
+ ...user,
527
+ localAdministratorSid: local ? `${user.sid.slice(0, user.sid.lastIndexOf("-"))}-500` : null
528
+ };
529
+ }
519
530
  function windowsCurrentAccountSync() {
520
- return currentIdentity ??= readIdentity(hookExecuteSync(windowsCommand("whoami.exe"), identityArgs));
531
+ if (currentIdentity)
532
+ return currentIdentity;
533
+ const commands = readWindowsIdentity();
534
+ let step2 = commands.next();
535
+ while (!step2.done)
536
+ step2 = commands.next(hookExecuteSync(...step2.value));
537
+ return currentIdentity = step2.value;
521
538
  }
522
539
  function hookInvocation(file, args2) {
523
540
  if (win32.basename(file) === "whoami.exe") {
@@ -537,11 +554,13 @@ function hookExecuteSync(file, args2, input) {
537
554
  [file, args2] = hookInvocation(file, args2);
538
555
  return execFileSync(file, args2, { ...hookOptions(file, args2), input });
539
556
  }
540
- var identityArgs, currentIdentity, ttl, hookOptions;
557
+ var identityArgs, groupArgs, ADMINISTRATOR_RID, currentIdentity, ttl, hookOptions;
541
558
  var init_runtimeSigners = __esm({
542
559
  "packages/shared/dist/runtimeSigners.js"() {
543
560
  "use strict";
544
561
  identityArgs = ["/user", "/fo", "csv", "/nh"];
562
+ groupArgs = ["/groups", "/fo", "csv", "/nh"];
563
+ ADMINISTRATOR_RID = /^S-1-5-21-\d+-\d+-\d+-500$/;
545
564
  ttl = 10 * 6e4;
546
565
  hookOptions = (file, args2) => ({
547
566
  encoding: args2.includes("/u") ? "utf16le" : "utf8",
@@ -2636,7 +2655,7 @@ import { createCliCredentials } from "./auth/credentials.js";
2636
2655
  import { createInterface } from "node:readline";
2637
2656
  import { execFile as execFile3 } from "node:child_process";
2638
2657
  import { lstat as lstat4 } from "node:fs/promises";
2639
- import { basename as basename3, join as join7 } from "node:path";
2658
+ import { basename as basename3, join as join8 } from "node:path";
2640
2659
 
2641
2660
  // packages/local-setup/dist/index.js
2642
2661
  import { randomUUID as randomUUID2 } from "node:crypto";
@@ -6833,7 +6852,7 @@ init_repositoryRoot();
6833
6852
 
6834
6853
  // packages/local-setup/dist/index.js
6835
6854
  import { realpath, unlink as unlink2 } from "node:fs/promises";
6836
- import { basename as basename2, dirname as dirname5, join as join6 } from "node:path";
6855
+ import { basename as basename2, dirname as dirname5, join as join7 } from "node:path";
6837
6856
 
6838
6857
  // packages/local-setup/dist/contracts.js
6839
6858
  var actionRequired = (state) => ({
@@ -7006,6 +7025,100 @@ async function withLock(path, waitMs, work) {
7006
7025
  }
7007
7026
  }
7008
7027
 
7028
+ // packages/local-setup/dist/automaticUpdate.js
7029
+ import { spawn as nodeSpawn } from "node:child_process";
7030
+ import { realpathSync } from "node:fs";
7031
+ import { stat as nodeStat, utimes } from "node:fs/promises";
7032
+ import { homedir as homedir3 } from "node:os";
7033
+ import { join as join6 } from "node:path";
7034
+ import { fileURLToPath as fileURLToPath2 } from "node:url";
7035
+ var DAY_MS = 864e5;
7036
+ var statePath = (stateDir) => join6(stateDir, "automatic-update.json");
7037
+ function cliLauncherPath(options = {}) {
7038
+ const platform = options.platform ?? process.platform;
7039
+ const home = options.home ?? homedir3();
7040
+ return platform === "win32" ? join6(options.env?.LOCALAPPDATA || join6(home, "AppData", "Local"), "Mnemonik", "bin", "mnemonik.cmd") : join6(home, ".local", "bin", "mnemonik");
7041
+ }
7042
+ async function readState(path) {
7043
+ const bytes = await readBytes(path);
7044
+ if (!bytes)
7045
+ return {};
7046
+ const value = JSON.parse(bytes.toString());
7047
+ if (!value || typeof value !== "object" || value.lastAttempt !== void 0 && (!Number.isFinite(value.lastAttempt) || value.lastAttempt < 0))
7048
+ throw new Error("automatic_update_state_invalid");
7049
+ return value.lastAttempt === void 0 ? {} : { lastAttempt: value.lastAttempt };
7050
+ }
7051
+ async function maybeStartAutomaticUpdate(options = {}) {
7052
+ const stateDir = options.stateDir ?? stateDirectory(options.platform, options.env, options.home);
7053
+ const path = statePath(stateDir);
7054
+ try {
7055
+ const now = (options.now ?? Date.now)();
7056
+ const stat = options.stat ?? nodeStat;
7057
+ try {
7058
+ if (now - (await stat(path)).mtimeMs < DAY_MS)
7059
+ return false;
7060
+ } catch (error) {
7061
+ if (error.code !== "ENOENT")
7062
+ throw error;
7063
+ }
7064
+ if (!options.spawn && !(await stat(cliLauncherPath(options))).isFile())
7065
+ return false;
7066
+ return await withLock(path, 0, async (assertOwned) => {
7067
+ const state = await readState(path);
7068
+ if (state.lastAttempt !== void 0 && now - state.lastAttempt < DAY_MS)
7069
+ return false;
7070
+ await atomicWrite(path, Buffer.from(`${JSON.stringify({ lastAttempt: now })}
7071
+ `), void 0, assertOwned);
7072
+ await utimes(path, now / 1e3, now / 1e3);
7073
+ const platform = options.platform ?? process.platform;
7074
+ const launcher = cliLauncherPath(options);
7075
+ const command = platform === "win32" ? {
7076
+ file: options.env?.ComSpec || "cmd.exe",
7077
+ args: ["/d", "/s", "/c", `""${launcher}" update --automatic"`],
7078
+ windowsVerbatimArguments: true
7079
+ } : platform === "linux" && (options.env ?? process.env).INVOCATION_ID ? {
7080
+ // detached creates a process group, not a new systemd cgroup. An
7081
+ // updater spawned by the scanner must survive stopping that service.
7082
+ file: "systemd-run",
7083
+ args: [
7084
+ "--user",
7085
+ "--collect",
7086
+ "--quiet",
7087
+ `--setenv=MNEMONIK_STATE_DIR=${stateDir}`,
7088
+ "--",
7089
+ launcher,
7090
+ "update",
7091
+ "--automatic"
7092
+ ]
7093
+ } : { file: launcher, args: ["update", "--automatic"] };
7094
+ const child = (options.spawn ?? nodeSpawn)(command.file, command.args, {
7095
+ detached: true,
7096
+ stdio: "ignore",
7097
+ windowsHide: true,
7098
+ ..."windowsVerbatimArguments" in command ? { windowsVerbatimArguments: command.windowsVerbatimArguments } : {}
7099
+ });
7100
+ child.once("error", () => {
7101
+ });
7102
+ child.unref();
7103
+ return true;
7104
+ });
7105
+ } catch {
7106
+ return false;
7107
+ }
7108
+ }
7109
+ var HELPER_FLAG = "--mnemonik-automatic-update";
7110
+ function isHelperInvocation() {
7111
+ if (process.argv[2] !== HELPER_FLAG || !process.argv[3] || !process.argv[1])
7112
+ return false;
7113
+ try {
7114
+ return realpathSync(process.argv[1]) === realpathSync(fileURLToPath2(import.meta.url));
7115
+ } catch {
7116
+ return false;
7117
+ }
7118
+ }
7119
+ if (isHelperInvocation())
7120
+ void maybeStartAutomaticUpdate({ stateDir: process.argv[3] });
7121
+
7009
7122
  // packages/local-setup/dist/index.js
7010
7123
  var MAX_ORIGINAL_BYTES = 64 * 1024;
7011
7124
  var validDisplayName = (value) => typeof value === "string" && value.length <= 200 && !/[\p{C}\p{Zl}\p{Zp}@]/u.test(value);
@@ -7016,6 +7129,7 @@ var accessResponseHash = (response) => hash2(JSON.stringify({
7016
7129
  allowedActions: [...response.allowedActions].sort(),
7017
7130
  candidates: response.candidates
7018
7131
  }));
7132
+ var pristine = (record, diskHash) => !record.remote && !record.staged && !record.steps.remote.started && !record.steps.identity.started && !record.steps.rollback.started && diskHash === record.before.hash;
7019
7133
  var step = (beforeHash) => ({
7020
7134
  complete: false,
7021
7135
  beforeHash,
@@ -7046,7 +7160,7 @@ function createProjectSetupExecutor(deps) {
7046
7160
  if (await realpath(fresh.root) !== root)
7047
7161
  return actionRequired("identity_changed");
7048
7162
  resolution = fresh;
7049
- const file = join6(root, ".mnemonik.json");
7163
+ const file = join7(root, ".mnemonik.json");
7050
7164
  const bytes = await readBytes(file);
7051
7165
  const diskHash = digest(bytes);
7052
7166
  const saved = await readBytes(path);
@@ -7075,14 +7189,13 @@ function createProjectSetupExecutor(deps) {
7075
7189
  return actionRequired("record_invalid");
7076
7190
  if (record.before.base64 !== null && record.before.hash !== hash2(Buffer.from(record.before.base64, "base64")) || record.before.base64 !== null && Buffer.from(record.before.base64, "base64").length > MAX_ORIGINAL_BYTES || record.staged && hash2(record.staged.content) !== record.staged.hash || record.remote && hash2(JSON.stringify(record.remote)) !== record.steps.remote.afterHash)
7077
7191
  return actionRequired("record_invalid");
7078
- if (mode === "stage" && record.scopeKey === hash2(deps.scopeKey) && record.steps.identity.complete && !record.steps.rollback.started && resolution.kind === "ok" && resolution.identity.projectId === record.remote?.projectId && (!options.intent || options.intent.projectId === record.remote.projectId)) {
7192
+ if (mode === "stage" && (record.steps.identity.complete && !record.steps.rollback.started && resolution.kind === "ok" && resolution.identity.projectId === record.remote?.projectId && (!options.intent || options.intent.projectId === record.remote.projectId) || record.steps.rollback.complete && diskHash === record.before.hash)) {
7079
7193
  record = freshRecord();
7080
7194
  recordIsNew = true;
7081
7195
  }
7082
7196
  const settled = record.steps.identity.complete && !record.steps.rollback.started && resolution.kind === "ok" && resolution.identity.projectId === record.remote?.projectId && (!options.intent || options.intent.projectId === record.remote?.projectId);
7083
7197
  if (record.scopeKey !== hash2(deps.scopeKey) || options.owner !== void 0 && JSON.stringify(record.owner) !== JSON.stringify(options.owner) || !settled && JSON.stringify(record.intent) !== JSON.stringify(options.intent)) {
7084
- const replaceable = !record.remote && !record.staged && !record.steps.remote.started && !record.steps.identity.started && !record.steps.rollback.started && diskHash === record.before.hash;
7085
- if (replaceable) {
7198
+ if (pristine(record, diskHash)) {
7086
7199
  record = freshRecord();
7087
7200
  recordIsNew = true;
7088
7201
  } else {
@@ -7179,10 +7292,15 @@ function createProjectSetupExecutor(deps) {
7179
7292
  }
7180
7293
  if (!resumingWithoutIdentity && record.steps.identity.complete && diskHash !== record.staged?.hash)
7181
7294
  return actionRequired("identity_changed");
7295
+ if (record.evidence && JSON.stringify(record.evidence) !== JSON.stringify(evidence)) {
7296
+ if (!pristine(record, diskHash))
7297
+ return actionRequired("operation_context_changed");
7298
+ const ignored = record.ignored;
7299
+ record = { ...freshRecord(), ...ignored ? { ignored } : {} };
7300
+ recordIsNew = true;
7301
+ }
7182
7302
  if (recordIsNew)
7183
7303
  await save();
7184
- if (record.evidence && JSON.stringify(record.evidence) !== JSON.stringify(evidence))
7185
- return actionRequired("operation_context_changed");
7186
7304
  if (record.steps.identity.complete && mode === "ensure" && record.remote) {
7187
7305
  const validated = await deps.transport.issueSetupRequest({
7188
7306
  ...evidence,
@@ -7288,9 +7406,59 @@ function createProjectSetupExecutor(deps) {
7288
7406
  ensureProject: (options) => execute(options, "ensure"),
7289
7407
  stage: (options) => execute(options, "stage"),
7290
7408
  apply: (options) => execute(options, "apply"),
7291
- rollback: (options) => execute(options, "rollback")
7409
+ rollback: (options) => execute(options, "rollback"),
7410
+ cancel: async (options) => {
7411
+ const resolution = await deps.resolver.resolveProjectIdentity(options.cwd, options);
7412
+ return cancelProjectSetup("root" in resolution ? resolution.root : options.cwd, deps.stateDir, deps.waitMs);
7413
+ }
7292
7414
  };
7293
7415
  }
7416
+ async function cancelProjectSetup(folder, stateDir, waitMs = 6e4) {
7417
+ const root = await realpath(folder);
7418
+ const path = recordPath(root, stateDir);
7419
+ return withLock(path, waitMs, async (assertOwned) => {
7420
+ const saved = await readBytes(path);
7421
+ if (!saved)
7422
+ return { status: "cancelled", root, cleared: false, restored: false };
7423
+ let record = null;
7424
+ try {
7425
+ record = JSON.parse(saved.toString());
7426
+ } catch {
7427
+ }
7428
+ let restored = false;
7429
+ const staged = record?.staged;
7430
+ const before = record?.before;
7431
+ const settled = !!record?.steps?.identity?.complete && !record.steps.rollback?.started;
7432
+ if (!settled && staged && before && typeof staged.hash === "string" && staged.hash !== before.hash) {
7433
+ const file = join7(root, ".mnemonik.json");
7434
+ const earlier = typeof before.base64 === "string" ? Buffer.from(before.base64, "base64") : null;
7435
+ if (digest(await readBytes(file)) === staged.hash && (earlier ? hash2(earlier) === before.hash : before.hash === null)) {
7436
+ await assertOwned();
7437
+ if (earlier)
7438
+ await atomicWrite(file, earlier, void 0, assertOwned);
7439
+ else {
7440
+ await unlink2(file);
7441
+ await syncDirectory(root);
7442
+ }
7443
+ restored = true;
7444
+ }
7445
+ }
7446
+ await assertOwned();
7447
+ await unlink2(path).catch((error) => {
7448
+ if (error.code !== "ENOENT")
7449
+ throw error;
7450
+ });
7451
+ await syncDirectory(dirname5(path));
7452
+ const remote = record?.remote?.projectId;
7453
+ return {
7454
+ status: "cancelled",
7455
+ root,
7456
+ cleared: true,
7457
+ restored,
7458
+ ...typeof remote === "string" ? { retainedRemoteUUID: remote } : {}
7459
+ };
7460
+ });
7461
+ }
7294
7462
 
7295
7463
  // packages/cli/src/project.ts
7296
7464
  import {
@@ -7306,6 +7474,27 @@ import {
7306
7474
  readExecutorState,
7307
7475
  saveCommandRecord
7308
7476
  } from "./project/records.js";
7477
+ var shellWord = (value) => /^[\w@%+=:,./-]+$/u.test(value) ? value : `"${value.replace(/(["\\$`])/gu, "\\$1")}"`;
7478
+ function answerCommands(actions, root, details = {}) {
7479
+ const folder = shellWord(root);
7480
+ const linkFlags = (projectId, extra) => `mnemonik project link ${projectId} ${folder}${extra} --non-interactive --apply`;
7481
+ return actions.flatMap((action) => {
7482
+ if (action === "cancel")
7483
+ return [{ action, command: `mnemonik project setup ${folder} --cancel` }];
7484
+ if (action === "rerun_with_apply")
7485
+ return [{ action, command: `mnemonik project setup ${folder} --non-interactive --apply` }];
7486
+ if (action === "link")
7487
+ return (details.candidates ?? []).map((candidate) => ({
7488
+ action,
7489
+ command: linkFlags(candidate.projectId, "")
7490
+ }));
7491
+ if (action === "confirm_mismatch" && details.projectId)
7492
+ return [{ action, command: linkFlags(details.projectId, " --confirm-mismatch") }];
7493
+ if (action === "replace" && details.projectId)
7494
+ return [{ action, command: linkFlags(details.projectId, " --replace") }];
7495
+ return [];
7496
+ });
7497
+ }
7309
7498
  async function ensureProjectRoot(root, executor) {
7310
7499
  return executor.ensureProject({ cwd: root, allowCreate: true, allowNestedInherit: false });
7311
7500
  }
@@ -7315,7 +7504,7 @@ var refusalReasons = {
7315
7504
  temporary_directory: "Mnemonik does not index the temporary folder.",
7316
7505
  mnemonik_state_directory: "That folder holds Mnemonik settings.",
7317
7506
  user_data_directory: "That folder holds program settings.",
7318
- host_config_directory: "That folder holds editor settings.",
7507
+ host_config_directory: "That folder holds coding tool settings.",
7319
7508
  broad_workspace_parent: "It holds several projects, and Mnemonik would index all of them.",
7320
7509
  not_found: "Its project file names a project this account cannot open.",
7321
7510
  project_access_denied: "Its project file names a project this account cannot open.",
@@ -7366,7 +7555,7 @@ var refusalSteps = {
7366
7555
  nested: "Run mnemonik project setup in that folder and choose which project it belongs to.",
7367
7556
  conflict: "Run mnemonik project setup in that folder and choose which project it belongs to.",
7368
7557
  identity_changed: AGAIN,
7369
- operation_context_changed: "Run mnemonik project setup in that folder to finish that command.",
7558
+ operation_context_changed: "Run mnemonik project setup --cancel in that folder, then run the command again.",
7370
7559
  rollback_in_progress: "Wait for it to finish, then run the command again.",
7371
7560
  operation_rolled_back: "Run mnemonik project setup in that folder to set it up again.",
7372
7561
  not_staged: AGAIN,
@@ -7570,6 +7759,12 @@ function showResult(output, json, result, root, owner, record) {
7570
7759
  ..."candidates" in result && Array.isArray(result.candidates) ? { candidates: result.candidates } : {},
7571
7760
  ..."manualAction" in result ? { manualAction: result.manualAction } : {},
7572
7761
  ..."used" in result && "limit" in result ? { used: result.used, limit: result.limit } : {},
7762
+ ..."allowedActions" in result ? {
7763
+ commands: answerCommands(result.allowedActions, root, {
7764
+ candidates: "candidates" in result && Array.isArray(result.candidates) ? result.candidates : void 0,
7765
+ projectId: record.projectId ?? void 0
7766
+ })
7767
+ } : {},
7573
7768
  root,
7574
7769
  owner: ownerLabel(owner)
7575
7770
  });
@@ -7671,6 +7866,23 @@ async function statusCommand(input, deps) {
7671
7866
  }
7672
7867
  return 0;
7673
7868
  }
7869
+ async function cancelCommand(input, deps) {
7870
+ const cwd = input.path ?? deps.cwd;
7871
+ const resolution = await (deps.executor ?? deps.resolver ?? { resolveProjectIdentity }).resolveProjectIdentity(cwd, { allowNestedInherit: false });
7872
+ const result = await cancelProjectSetup(
7873
+ "root" in resolution ? resolution.root : cwd,
7874
+ deps.stateDir
7875
+ );
7876
+ if (input.json) deps.output.json(result);
7877
+ else {
7878
+ const name2 = basename3(result.root);
7879
+ deps.output.line(
7880
+ result.cleared ? `Setup of ${name2} was cancelled.` : `Nothing was waiting for ${name2}.`
7881
+ );
7882
+ if (result.restored) deps.output.line("Its project file is back as it was.");
7883
+ }
7884
+ return 0;
7885
+ }
7674
7886
  async function runProjectCommandInner(input, deps, prompts) {
7675
7887
  if (input.command === "status") return statusCommand(input, deps);
7676
7888
  if (!deps.executor)
@@ -7691,7 +7903,7 @@ async function runProjectCommandInner(input, deps, prompts) {
7691
7903
  const cwd = input.path ?? deps.cwd;
7692
7904
  const resolution = await executor.resolveProjectIdentity(cwd);
7693
7905
  if (resolution.kind === "nested" || resolution.kind === "conflict") {
7694
- const identityLocation = join7(resolution.root, ".mnemonik.json");
7906
+ const identityLocation = join8(resolution.root, ".mnemonik.json");
7695
7907
  if (!input.json) {
7696
7908
  deps.output.line(`Root: ${resolution.root}`);
7697
7909
  deps.output.line(`Identity: ${identityLocation}`);
@@ -7839,7 +8051,7 @@ async function runProjectCommandInner(input, deps, prompts) {
7839
8051
  if (input.command === "init" && !input.nonInteractive && !input.json) {
7840
8052
  if (result.status === "ignored") {
7841
8053
  deps.output.line(`Root: ${decision.root}`);
7842
- deps.output.line(`Identity: ${join7(decision.root, ".mnemonik.json")}`);
8054
+ deps.output.line(`Identity: ${join8(decision.root, ".mnemonik.json")}`);
7843
8055
  if (await prompts.confirm("Stop ignoring this folder and continue?"))
7844
8056
  result = await executor.ensureProject({ ...base, clearIgnore: true });
7845
8057
  } else if ("allowedActions" in result && result.allowedActions.includes("ignore") && await prompts.confirm("Ignore this folder on this machine?"))
@@ -7848,6 +8060,7 @@ async function runProjectCommandInner(input, deps, prompts) {
7848
8060
  return finish(deps, input, result, decision.root, decision.reason, owner, beforeHash);
7849
8061
  }
7850
8062
  async function runProjectCommand(input, deps) {
8063
+ if (input.cancel) return cancelCommand(input, deps);
7851
8064
  const prompts = new Prompter(deps.input, deps.output);
7852
8065
  try {
7853
8066
  let effective = deps;
@@ -7961,6 +8174,7 @@ async function rollbackProjectIdentity(options) {
7961
8174
  return result.status === "rolled_back" ? 0 : 3;
7962
8175
  }
7963
8176
  export {
8177
+ answerCommands,
7964
8178
  connectedProjectsMessage,
7965
8179
  createRealProjectRuntime,
7966
8180
  ensureProjectForAgent,