@mnemonik/cli 7.104.12 → 7.105.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.
package/dist/router.js CHANGED
@@ -517,17 +517,311 @@ var init_repositoryFingerprint = __esm({
517
517
  });
518
518
 
519
519
  // packages/shared/dist/projectIdentityFile.js
520
+ import { lstat, readFile } from "node:fs/promises";
521
+ import { join as join2 } from "node:path";
522
+ function parseIdentityFile(text) {
523
+ let value;
524
+ try {
525
+ value = JSON.parse(text);
526
+ } catch (error) {
527
+ return { kind: "malformed", detail: `unparseable JSON: ${error.message}` };
528
+ }
529
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
530
+ return { kind: "malformed", detail: "identity file must contain a JSON object" };
531
+ }
532
+ const record = value;
533
+ if (record.schemaVersion !== 1) {
534
+ return { kind: "unknown_version", version: record.schemaVersion };
535
+ }
536
+ const unknownKey = Object.keys(record).find((key) => !IDENTITY_KEYS.has(key));
537
+ if (unknownKey)
538
+ return { kind: "malformed", detail: `unknown key "${unknownKey}"` };
539
+ if (!isCanonicalUuid(record.projectId)) {
540
+ return { kind: "malformed", detail: '"projectId" must be a canonical RFC 4122 UUID string' };
541
+ }
542
+ if ("projectName" in record && typeof record.projectName !== "string") {
543
+ return { kind: "malformed", detail: '"projectName" must be a string when present' };
544
+ }
545
+ if ("repositoryFingerprint" in record && (!record.repositoryFingerprint || typeof record.repositoryFingerprint !== "object" || Array.isArray(record.repositoryFingerprint))) {
546
+ return { kind: "malformed", detail: '"repositoryFingerprint" must be an object when present' };
547
+ }
548
+ const fingerprint = record.repositoryFingerprint;
549
+ const unknownFingerprintKey = fingerprint ? Object.keys(fingerprint).find((key) => !FINGERPRINT_KEYS.has(key)) : void 0;
550
+ if (unknownFingerprintKey) {
551
+ return {
552
+ kind: "malformed",
553
+ detail: `unknown repositoryFingerprint key "${unknownFingerprintKey}"`
554
+ };
555
+ }
556
+ if (fingerprint && (fingerprint.algorithmVersion !== 1 || typeof fingerprint.hash !== "string")) {
557
+ return {
558
+ kind: "malformed",
559
+ detail: '"repositoryFingerprint" must contain algorithmVersion 1 and a string hash'
560
+ };
561
+ }
562
+ const repositoryFingerprint = fingerprint ? { algorithmVersion: 1, hash: fingerprint.hash } : void 0;
563
+ return {
564
+ kind: "ok",
565
+ identity: {
566
+ schemaVersion: 1,
567
+ projectId: record.projectId,
568
+ ...record.projectName === void 0 ? {} : { projectName: record.projectName },
569
+ ...repositoryFingerprint ? { repositoryFingerprint } : {}
570
+ }
571
+ };
572
+ }
573
+ async function readIdentityFile(dir, options = {}) {
574
+ const path = join2(dir, ".mnemonik.json");
575
+ try {
576
+ const stat = await lstat(path);
577
+ if (stat.isSymbolicLink())
578
+ return { kind: "malformed", detail: "symlink" };
579
+ if (!stat.isFile())
580
+ return { kind: "malformed", detail: "identity path is not a regular file" };
581
+ const text = await readFile(path, "utf8");
582
+ if (options.selectedRoot) {
583
+ try {
584
+ const value = JSON.parse(text);
585
+ if (value && typeof value === "object" && !Array.isArray(value) && !("projectId" in value))
586
+ return { kind: "absent" };
587
+ } catch {
588
+ }
589
+ }
590
+ return parseIdentityFile(text);
591
+ } catch (error) {
592
+ if (error.code === "ENOENT")
593
+ return { kind: "absent" };
594
+ return { kind: "malformed", detail: `cannot read identity file: ${error.message}` };
595
+ }
596
+ }
597
+ var CANONICAL_UUID, isCanonicalUuid, IDENTITY_KEYS, FINGERPRINT_KEYS;
520
598
  var init_projectIdentityFile = __esm({
521
599
  "packages/shared/dist/projectIdentityFile.js"() {
522
600
  "use strict";
601
+ 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}$/;
602
+ isCanonicalUuid = (value) => typeof value === "string" && CANONICAL_UUID.test(value);
603
+ IDENTITY_KEYS = /* @__PURE__ */ new Set([
604
+ "schemaVersion",
605
+ "projectId",
606
+ "projectName",
607
+ "repositoryFingerprint"
608
+ ]);
609
+ FINGERPRINT_KEYS = /* @__PURE__ */ new Set(["algorithmVersion", "hash"]);
523
610
  }
524
611
  });
525
612
 
526
613
  // packages/shared/dist/repositoryRoot.js
614
+ import { lstat as lstat2, readFile as readFile2 } from "node:fs/promises";
615
+ import { execFile } from "node:child_process";
616
+ import { homedir } from "node:os";
617
+ import { dirname as dirname2, join as join3, resolve } from "node:path";
618
+ function gitEnvironment() {
619
+ const env = {};
620
+ for (const key of GIT_ENV_KEYS) {
621
+ if (process.env[key] !== void 0)
622
+ env[key] = process.env[key];
623
+ }
624
+ env.LC_ALL = "C";
625
+ return env;
626
+ }
627
+ function runGit(cwd, args2) {
628
+ return new Promise((resolvePromise, reject) => {
629
+ execFile("git", args2, { cwd, timeout: GIT_TIMEOUT_MS, encoding: "utf8", env: gitEnvironment() }, (error, stdout, stderr) => {
630
+ if (error)
631
+ reject(Object.assign(error, { stdout, stderr }));
632
+ else
633
+ resolvePromise({ stdout, stderr });
634
+ });
635
+ });
636
+ }
637
+ async function boundaryAt(path) {
638
+ try {
639
+ const stat = await lstat2(join3(path, ".git"));
640
+ if (stat.isDirectory())
641
+ return { path, kind: "directory" };
642
+ if (stat.isFile())
643
+ return { path, kind: "file" };
644
+ } catch (error) {
645
+ const code = error.code;
646
+ if (code !== "ENOENT" && code !== "ENOTDIR")
647
+ throw error;
648
+ }
649
+ return void 0;
650
+ }
651
+ async function confirmedBoundaryAt(path) {
652
+ const boundary = await boundaryAt(path);
653
+ if (!boundary)
654
+ return void 0;
655
+ try {
656
+ const { stdout } = await runGit(path, ["rev-parse", "--git-dir"]);
657
+ return stdout.trim() ? boundary : void 0;
658
+ } catch {
659
+ return void 0;
660
+ }
661
+ }
662
+ async function boundariesBetween(cwd, root) {
663
+ const boundaries = [];
664
+ let directory = cwd;
665
+ while (directory !== root) {
666
+ const boundary = await confirmedBoundaryAt(directory);
667
+ if (boundary)
668
+ boundaries.push(boundary);
669
+ const parent = dirname2(directory);
670
+ if (parent === directory)
671
+ break;
672
+ directory = parent;
673
+ }
674
+ return boundaries;
675
+ }
676
+ async function containingRepository(root) {
677
+ let directory = dirname2(root);
678
+ while (true) {
679
+ const boundary = await confirmedBoundaryAt(directory);
680
+ if (boundary)
681
+ return boundary;
682
+ const parent = dirname2(directory);
683
+ if (parent === directory)
684
+ return void 0;
685
+ directory = parent;
686
+ }
687
+ }
688
+ async function canonicalBoundaryRoot(boundary) {
689
+ if (boundary.kind === "directory")
690
+ return boundary.path;
691
+ const pointer = /^gitdir:\s*(.+)\s*$/im.exec(await readFile2(join3(boundary.path, ".git"), "utf8"))?.[1];
692
+ if (!pointer)
693
+ return boundary.path;
694
+ const gitDir = resolve(boundary.path, pointer);
695
+ try {
696
+ const commonDir = resolve(gitDir, (await readFile2(join3(gitDir, "commondir"), "utf8")).trim());
697
+ return dirname2(commonDir);
698
+ } catch (error) {
699
+ if (error.code === "ENOENT")
700
+ return boundary.path;
701
+ throw error;
702
+ }
703
+ }
704
+ async function resolveRepositoryRoot(cwd) {
705
+ const absoluteCwd = resolve(cwd);
706
+ try {
707
+ const { stdout } = await runGit(absoluteCwd, [
708
+ "rev-parse",
709
+ "--show-toplevel",
710
+ "--git-common-dir"
711
+ ]);
712
+ const [rootLine, commonLine, ...extra] = stdout.trim().split(/\r?\n/);
713
+ if (!rootLine || !commonLine || extra.length) {
714
+ return { kind: "git_unavailable", detail: "git rev-parse returned an unexpected response" };
715
+ }
716
+ const root = resolve(rootLine);
717
+ const commonDir = resolve(root, commonLine);
718
+ const isLinkedWorktree = commonDir !== join3(root, ".git");
719
+ const nested = await boundariesBetween(absoluteCwd, root);
720
+ if (!isLinkedWorktree && await containingRepository(root)) {
721
+ const boundary = await boundaryAt(root);
722
+ if (boundary)
723
+ nested.push(boundary);
724
+ }
725
+ return {
726
+ kind: "git",
727
+ root,
728
+ commonDir,
729
+ isLinkedWorktree,
730
+ nested
731
+ };
732
+ } catch (error) {
733
+ const failure = error;
734
+ if (failure.code === 128 && /not a git repository/i.test(failure.stderr ?? failure.message)) {
735
+ return { kind: "plain", root: absoluteCwd };
736
+ }
737
+ return { kind: "git_unavailable", detail: failure.message };
738
+ }
739
+ }
740
+ async function readPlainIdentity(start2) {
741
+ const home = resolve(homedir());
742
+ let directory = start2;
743
+ while (true) {
744
+ const result = await readIdentityFile(directory);
745
+ if (result.kind !== "absent")
746
+ return { root: directory, result };
747
+ if (directory === home)
748
+ break;
749
+ const parent = dirname2(directory);
750
+ if (parent === directory)
751
+ break;
752
+ directory = parent;
753
+ }
754
+ return { root: start2, result: { kind: "absent" } };
755
+ }
756
+ function withBase(repository, root, nested) {
757
+ return { root, repository, nested };
758
+ }
759
+ async function resolveProjectIdentity(cwd, options = {}) {
760
+ const resolved = await resolveRepositoryRoot(cwd);
761
+ const repository = resolved.kind === "git_unavailable" ? { kind: "plain", root: resolve(cwd) } : resolved;
762
+ if (repository.kind === "plain") {
763
+ const { root, result } = options.selectedRoot ? { root: repository.root, result: await readIdentityFile(repository.root, options) } : await readPlainIdentity(repository.root);
764
+ const base2 = withBase(repository, root, []);
765
+ if (result.kind === "ok")
766
+ return { ...base2, ...result };
767
+ if (result.kind === "unknown_version")
768
+ return { ...base2, ...result, path: root };
769
+ if (result.kind === "malformed")
770
+ return { ...base2, ...result, path: root };
771
+ return { ...base2, kind: "absent" };
772
+ }
773
+ const ownRoot = repository.isLinkedWorktree ? dirname2(repository.commonDir) : repository.root;
774
+ const outer = repository.isLinkedWorktree ? void 0 : await containingRepository(repository.root);
775
+ const own = await readIdentityFile(ownRoot, options);
776
+ if (!outer || options.selectedRoot || own.kind === "ok") {
777
+ const base2 = withBase(repository, ownRoot, repository.nested);
778
+ if (own.kind === "ok")
779
+ return { ...base2, ...own };
780
+ if (own.kind === "unknown_version")
781
+ return { ...base2, ...own, path: ownRoot };
782
+ if (own.kind === "malformed")
783
+ return { ...base2, ...own, path: ownRoot };
784
+ return { ...base2, kind: "absent" };
785
+ }
786
+ const nested = repository.nested;
787
+ const parentRoot = await canonicalBoundaryRoot(outer);
788
+ const parent = await readIdentityFile(parentRoot);
789
+ const base = withBase(repository, parentRoot, nested);
790
+ if (own.kind === "unknown_version")
791
+ return { ...base, ...own, path: ownRoot };
792
+ if (own.kind === "malformed")
793
+ return { ...base, ...own, path: ownRoot };
794
+ if (parent.kind === "unknown_version")
795
+ return { ...base, ...parent, path: parentRoot };
796
+ if (parent.kind === "malformed")
797
+ return { ...base, ...parent, path: parentRoot };
798
+ if (!options.allowNestedInherit) {
799
+ return {
800
+ ...base,
801
+ kind: "nested",
802
+ ...parent.kind === "ok" ? { parentIdentity: parent.identity } : {}
803
+ };
804
+ }
805
+ if (parent.kind === "ok")
806
+ return { ...base, kind: "ok", identity: parent.identity };
807
+ return { ...base, kind: "absent" };
808
+ }
809
+ var GIT_TIMEOUT_MS, GIT_ENV_KEYS;
527
810
  var init_repositoryRoot = __esm({
528
811
  "packages/shared/dist/repositoryRoot.js"() {
529
812
  "use strict";
530
813
  init_projectIdentityFile();
814
+ GIT_TIMEOUT_MS = 2e3;
815
+ GIT_ENV_KEYS = [
816
+ "PATH",
817
+ "HOME",
818
+ "LANG",
819
+ "LC_ALL",
820
+ "SYSTEMROOT",
821
+ "TEMP",
822
+ "TMP",
823
+ "USERPROFILE"
824
+ ];
531
825
  }
532
826
  });
533
827
 
@@ -2183,13 +2477,13 @@ async function Module2(moduleArg = {}) {
2183
2477
  }
2184
2478
  readAsync = /* @__PURE__ */ __name(async (url) => {
2185
2479
  if (isFileURI(url)) {
2186
- return new Promise((resolve, reject) => {
2480
+ return new Promise((resolve2, reject) => {
2187
2481
  var xhr = new XMLHttpRequest();
2188
2482
  xhr.open("GET", url, true);
2189
2483
  xhr.responseType = "arraybuffer";
2190
2484
  xhr.onload = () => {
2191
2485
  if (xhr.status == 200 || xhr.status == 0 && xhr.response) {
2192
- resolve(xhr.response);
2486
+ resolve2(xhr.response);
2193
2487
  return;
2194
2488
  }
2195
2489
  reject(xhr.status);
@@ -2385,9 +2679,9 @@ async function Module2(moduleArg = {}) {
2385
2679
  __name(receiveInstantiationResult, "receiveInstantiationResult");
2386
2680
  var info22 = getWasmImports();
2387
2681
  if (Module["instantiateWasm"]) {
2388
- return new Promise((resolve, reject) => {
2682
+ return new Promise((resolve2, reject) => {
2389
2683
  Module["instantiateWasm"](info22, (mod, inst) => {
2390
- resolve(receiveInstance(mod, inst));
2684
+ resolve2(receiveInstance(mod, inst));
2391
2685
  });
2392
2686
  });
2393
2687
  }
@@ -3718,8 +4012,8 @@ async function Module2(moduleArg = {}) {
3718
4012
  if (runtimeInitialized) {
3719
4013
  moduleRtn = Module;
3720
4014
  } else {
3721
- moduleRtn = new Promise((resolve, reject) => {
3722
- readyPromiseResolve = resolve;
4015
+ moduleRtn = new Promise((resolve2, reject) => {
4016
+ readyPromiseResolve = resolve2;
3723
4017
  readyPromiseReject = reject;
3724
4018
  });
3725
4019
  }
@@ -4866,10 +5160,10 @@ function apiOrigin(env = process.env) {
4866
5160
 
4867
5161
  // packages/local-setup/dist/storage.js
4868
5162
  init_hookRuntime();
4869
- import { homedir } from "node:os";
4870
- import { dirname as dirname2, join as join2, win32, posix } from "node:path";
5163
+ import { homedir as homedir2 } from "node:os";
5164
+ import { dirname as dirname3, join as join4, win32, posix } from "node:path";
4871
5165
  init_hookRuntime();
4872
- function stateDirectory(platform = process.platform, env = process.env, home = homedir()) {
5166
+ function stateDirectory(platform = process.platform, env = process.env, home = homedir2()) {
4873
5167
  if (env.MNEMONIK_STATE_DIR)
4874
5168
  return env.MNEMONIK_STATE_DIR;
4875
5169
  if (platform === "win32")
@@ -4956,12 +5250,12 @@ import {
4956
5250
  import { hostOrder, launchHostLabels } from "./install/adapters.js";
4957
5251
  import { readInstallVersions } from "./install/ownership.js";
4958
5252
  import { ensureLauncher, removeLauncher, LauncherError } from "./launcher.js";
4959
- import { readFile, realpath } from "node:fs/promises";
5253
+ import { readFile as readFile3, realpath } from "node:fs/promises";
4960
5254
  import { createInterface } from "node:readline";
4961
5255
  import { RuntimeStore, updateRuntime } from "./runtime/store.js";
4962
5256
  import { updateCli, cliUpdateHint } from "./runtime/selfUpdate.js";
4963
- import { homedir as homedir2 } from "node:os";
4964
- import { join as join3 } from "node:path";
5257
+ import { homedir as homedir3 } from "node:os";
5258
+ import { join as join5 } from "node:path";
4965
5259
  import { Output } from "./output.js";
4966
5260
  import { SCANNER_RESTART_MESSAGE } from "./scanner/service.js";
4967
5261
  import { SCANNER_FAILURE_MESSAGE, SCANNER_RETRY_MESSAGE } from "./screens/journey.js";
@@ -4978,6 +5272,7 @@ import {
4978
5272
  import { evaluateRoot } from "./project/eligibility.js";
4979
5273
  import { grantTransport, grantHost } from "./auth/status.js";
4980
5274
  import { createCliAuth } from "./auth/index.js";
5275
+ import { runEditorLogin } from "./auth/pkce.js";
4981
5276
  import { currentInstallSession, ensureInstallSession } from "./auth/installSession.js";
4982
5277
  import { runIdentityMigration } from "./identity/migrate.js";
4983
5278
  import { renderScannerStatus } from "./scanner/picker.js";
@@ -5007,6 +5302,12 @@ var removeFolderPrompt = (name2) => `Stop indexing ${name2}? Its memories stay i
5007
5302
  var connectedFolderLine = (name2) => ` \u2713 Connected ${name2}.`;
5008
5303
  var alreadyConnectedFolderLine = (name2) => ` \u2713 ${name2} is already connected and watched.`;
5009
5304
  var removedFolderLine = (name2) => ` \u2713 ${name2} is no longer connected.`;
5305
+ var projectDeletionWarning = (name2) => `Deleting ${name2} removes its memories, code index and summaries for everyone. This cannot be undone.`;
5306
+ var projectDeletedLine = (name2) => `Deleted ${name2}.`;
5307
+ var stillWatchedLine = (root) => `The folder is still being indexed. Run mnemonik remove ${root} to stop that.`;
5308
+ var identityFileKeptLine = "This folder's .mnemonik.json still points at the deleted project. Connecting the folder again creates a new project.";
5309
+ var CODEX_SIGNED_IN_MESSAGE = "Codex is signed in to Mnemonik.";
5310
+ var CONNECT_NOT_APPROVED_MESSAGE = "Sign-in timed out. Run mnemonik connect codex to try again.";
5010
5311
  function maintenanceExitCode(results) {
5011
5312
  if (results.some((result) => result.status === "FAILED")) return 1;
5012
5313
  return results.every((result) => result.status === "READY") ? 0 : 3;
@@ -5065,7 +5366,7 @@ Commands:
5065
5366
  install
5066
5367
  status
5067
5368
  connect <${hostOrder.join("|")}>
5068
- project <init|setup|status|link|ensure>
5369
+ project <init|setup|status|link|ensure|delete>
5069
5370
  add <folder>
5070
5371
  remove <folder>
5071
5372
  data delete --project <id>
@@ -5084,6 +5385,7 @@ Install consent: --accept-indexing --accept-limited --apply`;
5084
5385
  function parse(args2) {
5085
5386
  const positionals = [];
5086
5387
  const flags2 = /* @__PURE__ */ new Map();
5388
+ const namedConfirm = args2[0] === "project" && args2[1] === "delete";
5087
5389
  for (let index = 0; index < args2.length; index++) {
5088
5390
  const argument = args2[index] ?? "";
5089
5391
  if (!argument.startsWith("--")) {
@@ -5097,8 +5399,17 @@ function parse(args2) {
5097
5399
  continue;
5098
5400
  }
5099
5401
  if (booleans.has(name2)) {
5100
- if (inline !== void 0) return { positionals, flags: flags2, error: `Unknown flag: ${argument}` };
5101
- flags2.set(name2 === "accept-scanner" ? "accept-indexing" : name2, true);
5402
+ const takesName = name2 === "confirm" && namedConfirm;
5403
+ if (inline !== void 0 && !takesName)
5404
+ return { positionals, flags: flags2, error: `Unknown flag: ${argument}` };
5405
+ const next = args2[index + 1];
5406
+ let value = true;
5407
+ if (takesName && inline !== void 0) value = inline;
5408
+ else if (takesName && next !== void 0 && !next.startsWith("--")) {
5409
+ value = next;
5410
+ index++;
5411
+ }
5412
+ flags2.set(name2 === "accept-scanner" ? "accept-indexing" : name2, value);
5102
5413
  continue;
5103
5414
  }
5104
5415
  if (values.has(name2)) {
@@ -5180,7 +5491,7 @@ async function hostDependencies(deps, output, state) {
5180
5491
  if (!cliAuth.accountEmail) throw new Error("account_identity_failed");
5181
5492
  const email = await cliAuth.accountEmail(bearer);
5182
5493
  const installation = JSON.parse(
5183
- await readFile(join3(state, "installation.json"), "utf8").catch(() => "{}")
5494
+ await readFile3(join5(state, "installation.json"), "utf8").catch(() => "{}")
5184
5495
  );
5185
5496
  return {
5186
5497
  stateDir: state,
@@ -5197,7 +5508,7 @@ async function hostDependencies(deps, output, state) {
5197
5508
  async function updateHostDependencies(deps, state) {
5198
5509
  if (deps.hostManagement) return { ...deps.hostManagement, stateDir: state };
5199
5510
  const installation = JSON.parse(
5200
- await readFile(join3(state, "installation.json"), "utf8").catch(() => "{}")
5511
+ await readFile3(join5(state, "installation.json"), "utf8").catch(() => "{}")
5201
5512
  );
5202
5513
  return {
5203
5514
  stateDir: state,
@@ -5205,7 +5516,7 @@ async function updateHostDependencies(deps, state) {
5205
5516
  };
5206
5517
  }
5207
5518
  async function packageVersion() {
5208
- const contents = await readFile(new URL("../package.json", import.meta.url), "utf8");
5519
+ const contents = await readFile3(new URL("../package.json", import.meta.url), "utf8");
5209
5520
  return JSON.parse(contents).version;
5210
5521
  }
5211
5522
  async function runHostCommand(command, parsed, deps, output, scannerSelected = false) {
@@ -5237,7 +5548,7 @@ async function runHostCommand(command, parsed, deps, output, scannerSelected = f
5237
5548
  component: component2,
5238
5549
  host: name2,
5239
5550
  scope: "user",
5240
- home: deps.home ?? homedir2(),
5551
+ home: deps.home ?? homedir3(),
5241
5552
  projectRoot: deps.cwd ?? process.cwd()
5242
5553
  }))
5243
5554
  );
@@ -5306,7 +5617,7 @@ async function runHostCommand(command, parsed, deps, output, scannerSelected = f
5306
5617
  noBrowser: parsed.flags.has("no-browser"),
5307
5618
  source: dependencies.source ?? (updatedCli ? (host2) => hostSource(
5308
5619
  host2,
5309
- join3(updatedCli.directory, "node_modules/@mnemonik/cli/package.json")
5620
+ join5(updatedCli.directory, "node_modules/@mnemonik/cli/package.json")
5310
5621
  ) : void 0),
5311
5622
  instruction: json ? void 0 : (text) => output.line(text),
5312
5623
  apply: parsed.flags.has("apply")
@@ -5317,7 +5628,7 @@ async function runHostCommand(command, parsed, deps, output, scannerSelected = f
5317
5628
  let scanner;
5318
5629
  let launcher;
5319
5630
  let scannerRecovery;
5320
- if (all && await readFile(`${state}/scanner/state.json`).then(
5631
+ if (all && await readFile3(`${state}/scanner/state.json`).then(
5321
5632
  () => true,
5322
5633
  () => false
5323
5634
  )) {
@@ -5359,7 +5670,7 @@ async function runHostCommand(command, parsed, deps, output, scannerSelected = f
5359
5670
  if (fullUninstall && hostExit === 0) {
5360
5671
  const installed = await Promise.all(
5361
5672
  [new RuntimeStore(state).pointerPath("scanner"), `${state}/scanner/state.json`].map(
5362
- (path) => readFile(path).then(
5673
+ (path) => readFile3(path).then(
5363
5674
  () => true,
5364
5675
  () => false
5365
5676
  )
@@ -5403,18 +5714,29 @@ async function runHostCommand(command, parsed, deps, output, scannerSelected = f
5403
5714
  ...remaining ? { remaining } : {}
5404
5715
  });
5405
5716
  else if (command === "update") {
5406
- if (failed || hostsFailed) output.error(updateFailureMessage);
5407
- else if (codexTrustPending) {
5717
+ if (failed || hostsFailed) {
5718
+ if (cli?.status === "UPDATED") output.line("The mnemonik command updated.");
5719
+ if (result.reports.length > 0 && !hostsFailed) output.line("Your editors updated.");
5720
+ if (scanner?.status === "UPDATED") output.line("The scanner updated.");
5721
+ if (cli?.status === "FAILED") {
5722
+ output.error("The mnemonik command could not update.");
5723
+ output.error("Run npx -y @mnemonik/cli@latest install to update it.");
5724
+ }
5725
+ if (hostsFailed) {
5726
+ output.error("Your editors could not update.");
5727
+ output.error("Run mnemonik repair, then start a new session in each editor.");
5728
+ }
5729
+ if (scanner?.status === "FAILED") {
5730
+ output.error(scannerRecovery?.message ?? "The scanner could not update.");
5731
+ output.error(scannerRecovery?.action ?? SCANNER_RETRY_MESSAGE);
5732
+ }
5733
+ } else if (codexTrustPending) {
5408
5734
  output.line("Mnemonik updated.");
5409
5735
  output.line(CODEX_TRUST_MESSAGE.sentence);
5410
5736
  output.line(CODEX_TRUST_MESSAGE.nextStep);
5411
5737
  } else if (cli?.status === "UPDATED" || result.reports.length > 0 || scanner?.status === "UPDATED")
5412
5738
  output.line("Mnemonik updated.");
5413
5739
  else output.line("Mnemonik is up to date.");
5414
- if (scannerRecovery) {
5415
- output.error(scannerRecovery.message);
5416
- if (scannerRecovery.action) output.error(scannerRecovery.action);
5417
- }
5418
5740
  } else {
5419
5741
  for (const target of result.results)
5420
5742
  output.line(target.status === "READY" ? "Done." : humanReason(target.reason));
@@ -5592,7 +5914,7 @@ async function doctorCommand(parsed, deps, output) {
5592
5914
  const invalid = allowed(parsed, []);
5593
5915
  if (invalid) return output.error(invalid), 2;
5594
5916
  const result = await runPreflight({ cwd: deps.cwd, home: deps.home, ...deps.preflight });
5595
- output.setContext({ home: deps.home ?? homedir2(), projectRoot: result.project.root });
5917
+ output.setContext({ home: deps.home ?? homedir3(), projectRoot: result.project.root });
5596
5918
  const document = await collectStatusDocument({
5597
5919
  preflight: result,
5598
5920
  cwd: deps.cwd ?? process.cwd(),
@@ -5629,10 +5951,10 @@ async function collectCurrentInstallation(deps, output) {
5629
5951
  ...deps.preflight,
5630
5952
  fetch: async () => new Response(null, { status: 204 })
5631
5953
  });
5632
- output.setContext({ home: deps.home ?? homedir2(), projectRoot: result.project.root });
5954
+ output.setContext({ home: deps.home ?? homedir3(), projectRoot: result.project.root });
5633
5955
  const hostStateDir = deps.hostManagement?.stateDir ?? deps.installStateDir ?? stateDirectory(process.platform, process.env, deps.home);
5634
5956
  const localConditions = deps.projectHookConditions ? [] : [
5635
- ...await localInstallationConditions(deps.home ?? homedir2(), hostStateDir, deps.launcher),
5957
+ ...await localInstallationConditions(deps.home ?? homedir3(), hostStateDir, deps.launcher),
5636
5958
  // Installed Codex hooks that Codex has not trusted cannot run.
5637
5959
  ...await (deps.codexTrustConditions ?? (() => codexTrustConditions({
5638
5960
  stateDir: hostStateDir,
@@ -5676,6 +5998,96 @@ async function reportCurrentInstallation(deps, output, document) {
5676
5998
  ).catch(() => {
5677
5999
  });
5678
6000
  }
6001
+ async function deleteProjectCommand(parsed, rest, deps, output) {
6002
+ const json = parsed.flags.has("json");
6003
+ const cwd = deps.cwd ?? process.cwd();
6004
+ const resolve2 = deps.projectResolver?.resolveProjectIdentity ?? resolveProjectIdentity;
6005
+ const here = await resolve2(cwd, { allowNestedInherit: false });
6006
+ const hereId = here.kind === "ok" ? here.identity.projectId : void 0;
6007
+ const root = "root" in here ? here.root : cwd;
6008
+ const target = rest[0] ?? hereId;
6009
+ if (!target) {
6010
+ output.error(
6011
+ "This folder is not connected to a project. Run mnemonik project delete <name> instead."
6012
+ );
6013
+ return 3;
6014
+ }
6015
+ const bearer = await ensureCliAuth(deps, output, parsed.flags.has("no-browser"), false);
6016
+ const send = deps.grantFetch ?? fetch;
6017
+ const listed = await send(`${apiOrigin()}/api/v1/users/me/projects?days=0&limit=200`, {
6018
+ headers: { Authorization: `Bearer ${bearer}` }
6019
+ });
6020
+ if (!listed.ok) {
6021
+ output.error("Mnemonik could not be reached from this machine.");
6022
+ return 3;
6023
+ }
6024
+ const listing = await listed.json().catch(() => []);
6025
+ const projects = Array.isArray(listing) ? listing : [];
6026
+ const project = projects.find(
6027
+ (candidate) => candidate.id === target || candidate.name?.toLowerCase() === target.toLowerCase()
6028
+ );
6029
+ if (!project) {
6030
+ output.error(`There is no project called ${target} in your account.`);
6031
+ return 3;
6032
+ }
6033
+ const supplied = parsed.flags.get("confirm");
6034
+ let typed = typeof supplied === "string" ? supplied : "";
6035
+ if (typeof supplied !== "string") {
6036
+ if (parsed.flags.has("non-interactive") || json) {
6037
+ output.error(
6038
+ `To skip this check, run mnemonik project delete ${project.name} --confirm "${project.name}".`
6039
+ );
6040
+ return 3;
6041
+ }
6042
+ output.line(projectDeletionWarning(project.name));
6043
+ output.line("Type the project name to confirm.");
6044
+ const readline = createInterface({ input: deps.input ?? process.stdin, terminal: false });
6045
+ typed = String((await readline[Symbol.asyncIterator]().next()).value ?? "").trim();
6046
+ readline.close();
6047
+ if (typed.toLowerCase() !== project.name.toLowerCase()) {
6048
+ output.line("Nothing was deleted.");
6049
+ return 130;
6050
+ }
6051
+ }
6052
+ if (typed.toLowerCase() !== project.name.toLowerCase()) {
6053
+ output.error("That is not the project name. Nothing was deleted.");
6054
+ return 3;
6055
+ }
6056
+ const response = await send(`${apiOrigin()}/api/v1/projects/${encodeURIComponent(project.id)}`, {
6057
+ method: "DELETE",
6058
+ headers: { Authorization: `Bearer ${bearer}`, "content-type": "application/json" },
6059
+ body: JSON.stringify({ confirmProjectName: typed })
6060
+ });
6061
+ if (!response.ok) {
6062
+ output.error(
6063
+ response.status === 403 ? `Only the owner of ${project.name} can delete it.` : response.status === 404 ? `There is no project called ${target} in your account.` : `${project.name} could not be deleted. Try again in a moment.`
6064
+ );
6065
+ return 3;
6066
+ }
6067
+ const result = await response.json().catch(() => ({ success: true }));
6068
+ const stateDir = deps.installStateDir ?? stateDirectory(process.platform, process.env, deps.home);
6069
+ const saved = JSON.parse(
6070
+ await readFile3(`${stateDir}/scanner/state.json`, "utf8").catch(() => "null")
6071
+ );
6072
+ let stillWatched = false;
6073
+ if (hereId === project.id && saved?.config.roots.includes(root)) {
6074
+ const update = await updateScannerRoots({
6075
+ stateDir,
6076
+ bearer,
6077
+ add: [],
6078
+ remove: [root],
6079
+ fetch: deps.grantFetch
6080
+ }).catch(() => void 0);
6081
+ stillWatched = update?.status !== "updated";
6082
+ }
6083
+ if (json) output.json(result);
6084
+ else {
6085
+ output.line(projectDeletedLine(project.name));
6086
+ if (stillWatched) output.line(stillWatchedLine(root));
6087
+ if (hereId === project.id) output.line(identityFileKeptLine);
6088
+ }
6089
+ return 0;
6090
+ }
5679
6091
  async function runCli(args2, deps = {}) {
5680
6092
  const parsed = parse(args2);
5681
6093
  const silent = parsed.flags.has("automatic");
@@ -5684,7 +6096,7 @@ async function runCli(args2, deps = {}) {
5684
6096
  const stdout = silent ? discard : deps.stdout ?? process.stdout;
5685
6097
  const stderr = silent ? discard : deps.stderr ?? process.stderr;
5686
6098
  const output = new Output(stdout, stderr, {
5687
- home: deps.home ?? homedir2()
6099
+ home: deps.home ?? homedir3()
5688
6100
  });
5689
6101
  if (process.env.MNEMONIK_DEV_RELEASE_DIR && !silent)
5690
6102
  stderr.write(
@@ -5716,7 +6128,7 @@ async function runCli(args2, deps = {}) {
5716
6128
  return output.error(invalid ?? "Usage: mnemonik add <folder> or mnemonik remove <folder>"), 2;
5717
6129
  const stateDir = deps.installStateDir ?? stateDirectory(process.platform, process.env, deps.home);
5718
6130
  const saved = JSON.parse(
5719
- await readFile(`${stateDir}/scanner/state.json`, "utf8").catch(() => "null")
6131
+ await readFile3(`${stateDir}/scanner/state.json`, "utf8").catch(() => "null")
5720
6132
  );
5721
6133
  if (!saved) return actionRequired2(output, parsed.flags.has("json"), "mnemonik install");
5722
6134
  if (action === "list") {
@@ -5799,7 +6211,7 @@ async function runCli(args2, deps = {}) {
5799
6211
  if (invalid || rest.length) return output.error(invalid ?? "Unexpected argument"), 2;
5800
6212
  try {
5801
6213
  const stateDir = deps.installStateDir ?? stateDirectory(process.platform, process.env, deps.home);
5802
- const saved = JSON.parse(await readFile(`${stateDir}/scanner/state.json`, "utf8"));
6214
+ const saved = JSON.parse(await readFile3(`${stateDir}/scanner/state.json`, "utf8"));
5803
6215
  const bearer = await ensureCliAuth(deps, output, false);
5804
6216
  const response = await (deps.grantFetch ?? fetch)(
5805
6217
  `${apiOrigin()}/api/v1/component-credentials/${encodeURIComponent(saved.config.credentialFamilyId)}/revoke`,
@@ -6036,9 +6448,25 @@ Preview: ${preview.path}
6036
6448
  if (!subcommand || rest.length || !hostOrder.includes(subcommand))
6037
6449
  return output.error(`Usage: mnemonik connect <${hostOrder.join("|")}>`), 2;
6038
6450
  const host = subcommand;
6039
- const editor = (await localEditorStatus(deps.home ?? homedir2())).find(
6451
+ const editor = (await localEditorStatus(deps.home ?? homedir3())).find(
6040
6452
  (candidate) => candidate.host === host
6041
6453
  );
6454
+ if (host === "codex" && editor?.mcp === "ready" && !parsed.flags.has("json")) {
6455
+ const bearer = await auth(deps, output, false).getCliBearer().catch(() => void 0);
6456
+ if (typeof bearer === "string") {
6457
+ const outcome = await runEditorLogin({
6458
+ command: ["codex", "mcp", "login", "mnemonik"],
6459
+ apiOrigin: apiOrigin(),
6460
+ issuer: process.env.MNEMONIK_OAUTH_ISSUER ?? "https://auth.mnemonik.ai",
6461
+ bearer: () => Promise.resolve(bearer),
6462
+ print: (line) => void output.line(line),
6463
+ fetch: deps.grantFetch,
6464
+ ...deps.editorLogin
6465
+ });
6466
+ if (outcome === "signed_in") return output.line(CODEX_SIGNED_IN_MESSAGE), 0;
6467
+ if (outcome === "not_approved") return output.line(CONNECT_NOT_APPROVED_MESSAGE), 1;
6468
+ }
6469
+ }
6042
6470
  const reason = editor?.mcp === "disabled" ? `${editor.name} connection is turned off.` : editor?.mcp === "ready" ? "Finish signing in to Mnemonik in the editor." : `${launchHostLabels[host]} connection is missing.`;
6043
6471
  const actions = editor?.mcp === "ready" ? editorAuthorizationRows([host]) : [
6044
6472
  editor?.mcp === "disabled" ? mcpTurnOnAction[host] : "Run mnemonik install to set it up again."
@@ -6051,8 +6479,14 @@ Preview: ${preview.path}
6051
6479
  return 3;
6052
6480
  }
6053
6481
  if (command === "project") {
6054
- if (!subcommand || !["init", "setup", "status", "link", "ensure"].includes(subcommand))
6055
- return output.error("Usage: mnemonik project <init|setup|status|link|ensure>"), 2;
6482
+ if (!subcommand || !["init", "setup", "status", "link", "ensure", "delete"].includes(subcommand))
6483
+ return output.error("Usage: mnemonik project <init|setup|status|link|ensure|delete>"), 2;
6484
+ if (subcommand === "delete") {
6485
+ const unknown = allowed(parsed, ["confirm"]);
6486
+ if (unknown || rest.length > 1)
6487
+ return output.error(unknown ?? "Usage: mnemonik project delete [<project id or name>]"), 2;
6488
+ return deleteProjectCommand(parsed, rest, deps, output);
6489
+ }
6056
6490
  const invalid = allowed(
6057
6491
  parsed,
6058
6492
  subcommand === "ensure" ? ["agent"] : subcommand === "status" ? [] : subcommand === "link" ? ["apply", "non-git", "confirm-mismatch", "replace", "owner"] : ["apply", "non-git", "owner"]
@@ -6399,12 +6833,18 @@ Preview: ${preview.path}
6399
6833
  }
6400
6834
  var serializeReadiness2 = (input) => devReadiness(serializeReadiness(input));
6401
6835
  export {
6836
+ CODEX_SIGNED_IN_MESSAGE,
6837
+ CONNECT_NOT_APPROVED_MESSAGE,
6402
6838
  alreadyConnectedFolderLine,
6403
6839
  connectFolderPrompt,
6404
6840
  connectedFolderLine,
6405
6841
  help,
6842
+ identityFileKeptLine,
6406
6843
  maintenanceExitCode,
6844
+ projectDeletedLine,
6845
+ projectDeletionWarning,
6407
6846
  removeFolderPrompt,
6408
6847
  removedFolderLine,
6409
- runCli
6848
+ runCli,
6849
+ stillWatchedLine
6410
6850
  };