@mnemonik/cli 7.100.0 → 7.101.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (93) hide show
  1. package/dist/auth/pkce.d.ts +2 -0
  2. package/dist/auth/pkce.d.ts.map +1 -1
  3. package/dist/auth/pkce.js +57 -6
  4. package/dist/auth/pkce.js.map +1 -1
  5. package/dist/auth/status.d.ts +1 -5
  6. package/dist/auth/status.d.ts.map +1 -1
  7. package/dist/auth/status.js +1 -71
  8. package/dist/auth/status.js.map +1 -1
  9. package/dist/digests.json +91 -91
  10. package/dist/install/hosts.d.ts +2 -12
  11. package/dist/install/hosts.d.ts.map +1 -1
  12. package/dist/install/hosts.js +57 -356
  13. package/dist/install/hosts.js.map +1 -1
  14. package/dist/install/journal.d.ts +2 -10
  15. package/dist/install/journal.d.ts.map +1 -1
  16. package/dist/install/journal.js +0 -4
  17. package/dist/install/journal.js.map +1 -1
  18. package/dist/install/journey.d.ts.map +1 -1
  19. package/dist/install/journey.js +185 -158
  20. package/dist/install/journey.js.map +1 -1
  21. package/dist/install/transaction.d.ts +2 -3
  22. package/dist/install/transaction.d.ts.map +1 -1
  23. package/dist/install/transaction.js +1 -90
  24. package/dist/install/transaction.js.map +1 -1
  25. package/dist/install/ui.d.ts.map +1 -1
  26. package/dist/install/ui.js +15 -33
  27. package/dist/install/ui.js.map +1 -1
  28. package/dist/installSession.js +0 -4
  29. package/dist/launcher.d.ts.map +1 -1
  30. package/dist/launcher.js +16 -7
  31. package/dist/launcher.js.map +1 -1
  32. package/dist/output.d.ts +1 -0
  33. package/dist/output.d.ts.map +1 -1
  34. package/dist/output.js +3 -0
  35. package/dist/output.js.map +1 -1
  36. package/dist/preflight.d.ts +2 -0
  37. package/dist/preflight.d.ts.map +1 -1
  38. package/dist/preflight.js +64 -209
  39. package/dist/preflight.js.map +1 -1
  40. package/dist/project/eligibility.d.ts +11 -1
  41. package/dist/project/eligibility.d.ts.map +1 -1
  42. package/dist/project/eligibility.js +12 -1
  43. package/dist/project/eligibility.js.map +1 -1
  44. package/dist/project.d.ts +3 -0
  45. package/dist/project.d.ts.map +1 -1
  46. package/dist/project.js +56 -21
  47. package/dist/project.js.map +1 -1
  48. package/dist/router.d.ts +5 -1
  49. package/dist/router.d.ts.map +1 -1
  50. package/dist/router.js +201 -93
  51. package/dist/router.js.map +1 -1
  52. package/dist/runtime/releaseSource.d.ts +10 -0
  53. package/dist/runtime/releaseSource.d.ts.map +1 -1
  54. package/dist/runtime/releaseSource.js +82 -6
  55. package/dist/runtime/releaseSource.js.map +1 -1
  56. package/dist/runtime/selfUpdate.d.ts +4 -1
  57. package/dist/runtime/selfUpdate.d.ts.map +1 -1
  58. package/dist/runtime/selfUpdate.js +12 -6
  59. package/dist/runtime/selfUpdate.js.map +1 -1
  60. package/dist/runtime/signers.js +15 -7
  61. package/dist/runtime/store.d.ts +2 -1
  62. package/dist/runtime/store.d.ts.map +1 -1
  63. package/dist/runtime/store.js +20 -9
  64. package/dist/runtime/store.js.map +1 -1
  65. package/dist/sbom.json +21 -23
  66. package/dist/scanner/discover.d.ts +12 -1
  67. package/dist/scanner/discover.d.ts.map +1 -1
  68. package/dist/scanner/discover.js +37 -6
  69. package/dist/scanner/discover.js.map +1 -1
  70. package/dist/scanner/enable.d.ts +23 -5
  71. package/dist/scanner/enable.d.ts.map +1 -1
  72. package/dist/scanner/enable.js +93 -21
  73. package/dist/scanner/enable.js.map +1 -1
  74. package/dist/scanner/picker.d.ts +7 -1
  75. package/dist/scanner/picker.d.ts.map +1 -1
  76. package/dist/scanner/picker.js +64 -13
  77. package/dist/scanner/picker.js.map +1 -1
  78. package/dist/scanner-release.json +25 -25
  79. package/dist/screens/journey.d.ts +13 -3
  80. package/dist/screens/journey.d.ts.map +1 -1
  81. package/dist/screens/journey.js +145 -53
  82. package/dist/screens/journey.js.map +1 -1
  83. package/dist/screens.d.ts +2 -3
  84. package/dist/screens.d.ts.map +1 -1
  85. package/dist/screens.js +6 -16
  86. package/dist/screens.js.map +1 -1
  87. package/dist/status.d.ts +5 -3
  88. package/dist/status.d.ts.map +1 -1
  89. package/dist/status.js +194 -110
  90. package/dist/status.js.map +1 -1
  91. package/dist/vendor/local-setup.js +124 -27
  92. package/dist/vendor/shared/runtimeSigners.js +11 -6
  93. package/package.json +45 -41
@@ -562,8 +562,8 @@ var init_hookRuntime = __esm({
562
562
  });
563
563
 
564
564
  // packages/cli/src/install/journey.ts
565
- import { access, readFile } from "node:fs/promises";
566
- import { join as join3, relative, isAbsolute } from "node:path";
565
+ import { appendFile, mkdir, readFile } from "node:fs/promises";
566
+ import { join as join3 } from "node:path";
567
567
  import { homedir as homedir2 } from "node:os";
568
568
  import { setTimeout as delay } from "node:timers/promises";
569
569
 
@@ -4769,8 +4769,6 @@ var stateFor = {
4769
4769
  scanner_not_verified: "LIMITED",
4770
4770
  hook_not_verified: "LIMITED",
4771
4771
  hooks_missing: "ACTION_REQUIRED",
4772
- host_grant_unbound: "LIMITED",
4773
- host_not_connected: "LIMITED",
4774
4772
  scanner_omitted: "LIMITED",
4775
4773
  project_uncovered: "LIMITED",
4776
4774
  host_skipped: "LIMITED",
@@ -4786,8 +4784,6 @@ var rank = {
4786
4784
  FAILED: 3
4787
4785
  };
4788
4786
  function defaultAction(condition) {
4789
- if (condition.kind === "host_skipped" && condition.component)
4790
- return `Connect it later: mnemonik connect ${condition.component}`;
4791
4787
  if (condition.kind === "windows_task_creation_failed")
4792
4788
  return "Run mnemonik scanner enable to try again.";
4793
4789
  return condition.action;
@@ -4874,38 +4870,37 @@ var recordPath = (root, state = stateDirectory()) => join2(state, "project-setup
4874
4870
  var MAX_ORIGINAL_BYTES = 64 * 1024;
4875
4871
 
4876
4872
  // packages/cli/src/install/journey.ts
4877
- import { runPreflight } from "../preflight.js";
4878
- import { createRealProjectRuntime } from "../project.js";
4879
- import { discoverRepositories } from "../scanner/discover.js";
4873
+ import { nodeVersionHelp, runPreflight } from "../preflight.js";
4874
+ import {
4875
+ connectedProjectsMessage,
4876
+ createRealProjectRuntime,
4877
+ projectLimitMessage
4878
+ } from "../project.js";
4879
+ import { classifyRepository } from "../scanner/discover.js";
4880
4880
  import { prepareScanner, restoreScannerInstall } from "../scanner/enable.js";
4881
4881
  import { ScannerServiceLimited } from "../scanner/service.js";
4882
- import { collectStatusDocument, renderStatusSummaries } from "../status.js";
4882
+ import { collectStatusDocument } from "../status.js";
4883
4883
  import { devReadiness } from "../runtime/releaseSource.js";
4884
- import { renderJourney, journeyAnswers } from "../screens/journey.js";
4884
+ import {
4885
+ completedStep,
4886
+ completedLine,
4887
+ renderCustomize,
4888
+ renderJourney,
4889
+ journeyAnswers
4890
+ } from "../screens/journey.js";
4885
4891
  import { interrupted } from "./journal.js";
4886
4892
  import {
4887
4893
  runHosts,
4888
- hostNotConnectedCondition,
4889
4894
  hookStatusConditions
4890
4895
  } from "./hosts.js";
4891
4896
  import { compensate, revokeInstallComponent } from "./transaction.js";
4892
4897
  import * as ownership from "./ownership.js";
4893
4898
  import { ensureLauncher, launcherPathAction, LauncherError } from "../launcher.js";
4894
4899
  var labels = { "claude-code": "Claude Code", codex: "Codex", cursor: "Cursor", grok: "Grok" };
4895
- var launchHosts = ["claude-code", "codex", "cursor"];
4896
- var notOfferedHosts = ["grok", "vscode-copilot"];
4900
+ var launchHosts = ["claude-code", "codex", "cursor", "grok"];
4901
+ var notOfferedHosts = ["vscode-copilot"];
4897
4902
  function hostReadinessConditions(results, scanner) {
4898
4903
  return results.filter((r) => r.status !== "READY").map((r) => {
4899
- const host = r.target.split(":")[0];
4900
- const notConnected = hostNotConnectedCondition(host);
4901
- if (r.reason === notConnected.reason) return notConnected;
4902
- if (r.reason === "login_pending") {
4903
- return {
4904
- kind: "login_pending",
4905
- reason: `${labels[host]} is still connecting. Finish the sign-in in the app, then run mnemonik status.`,
4906
- action: "mnemonik status"
4907
- };
4908
- }
4909
4904
  return {
4910
4905
  kind: scanner ? "host_skipped" : "host_trust_pending",
4911
4906
  reason: scanner ? `${r.target}: ${r.reason}` : r.reason,
@@ -4941,6 +4936,7 @@ async function waitForInstallation(check, timeout, clock = {}) {
4941
4936
  }
4942
4937
  }
4943
4938
  async function joinedInstall(flags2, deps, output, authorize, management) {
4939
+ if (flags2.has("accept-scanner")) flags2.set("accept-indexing", true);
4944
4940
  const json = flags2.has("json");
4945
4941
  const automatic = json || flags2.has("non-interactive");
4946
4942
  let components = String(
@@ -4948,20 +4944,18 @@ async function joinedInstall(flags2, deps, output, authorize, management) {
4948
4944
  ).split(",");
4949
4945
  if (flags2.has("without-scanner")) components = components.filter((c) => c !== "scanner");
4950
4946
  let scanner = components.includes("scanner");
4951
- let scope = String(flags2.get("integration-scope") ?? "user");
4952
4947
  let names = flags2.has("hosts") ? String(flags2.get("hosts")).split(",") : [];
4953
- if (components.some((c) => !["hooks", "mcp", "scanner"].includes(c)) || names.some((h) => !launchHosts.includes(h)) || !["user", "project"].includes(scope)) {
4948
+ if (components.some((c) => !["hooks", "mcp", "scanner"].includes(c)) || names.some((h) => !launchHosts.includes(h))) {
4954
4949
  output.error(
4955
- `Invalid hosts, scope or components. Launch hosts: ${launchHosts.join(", ")}. Not offered at launch: ${notOfferedHosts.join(", ")}.`
4950
+ `Invalid hosts or components. Launch hosts: ${launchHosts.join(", ")}. Not offered at launch: ${notOfferedHosts.join(", ")}.`
4956
4951
  );
4957
4952
  return 2;
4958
4953
  }
4959
4954
  if (automatic || flags2.has("hosts") || flags2.has("components")) {
4960
4955
  const required = [
4961
- scanner ? "accept-scanner" : "accept-limited",
4956
+ scanner ? "accept-indexing" : "accept-limited",
4962
4957
  "apply",
4963
- ...automatic && scanner ? ["scan-roots"] : [],
4964
- ...names.length ? ["integration-scope"] : []
4958
+ ...automatic && scanner ? ["scan-roots"] : []
4965
4959
  ];
4966
4960
  for (const flag of required)
4967
4961
  if (!flags2.has(flag)) {
@@ -4998,7 +4992,6 @@ async function joinedInstall(flags2, deps, output, authorize, management) {
4998
4992
  components = previous.data.components;
4999
4993
  scanner = components.includes("scanner");
5000
4994
  names = [...new Set(previous.data.hostRequest?.selections.map((s) => s.host) ?? [])];
5001
- scope = previous.data.hostRequest?.selections[0]?.scope ?? scope;
5002
4995
  }
5003
4996
  const preflight = await runPreflight({
5004
4997
  cwd,
@@ -5007,16 +5000,35 @@ async function joinedInstall(flags2, deps, output, authorize, management) {
5007
5000
  ...deps.preflight
5008
5001
  });
5009
5002
  const root = preflight.project.root ?? cwd;
5010
- let roots = String(flags2.get("scan-roots") ?? (previous?.data.roots.join(",") || root)).split(",").filter(Boolean);
5003
+ const logPath = join3(stateDir, "install.log");
5004
+ const log = async (detail) => {
5005
+ await mkdir(stateDir, { recursive: true, mode: 448 });
5006
+ await appendFile(logPath, `${JSON.stringify({ at: (/* @__PURE__ */ new Date()).toISOString(), detail })}
5007
+ `, {
5008
+ mode: 384
5009
+ });
5010
+ };
5011
+ let roots = String(flags2.get("scan-roots") ?? previous?.data.roots.join(",") ?? "").split(",").filter(Boolean);
5011
5012
  output.setContext({ home, projectRoot: root });
5012
5013
  if (!names.length)
5013
- names = preflight.hosts.filter((h) => h.supported && h.name !== "Grok").map(
5014
+ names = preflight.hosts.filter((h) => h.supported).map(
5014
5015
  (h) => Object.keys(labels).find((key) => labels[key] === h.name) ?? ""
5015
5016
  ).filter(Boolean);
5016
- const answers = automatic ? void 0 : journeyAnswers(deps.input ?? process.stdin);
5017
+ const answers = automatic ? void 0 : journeyAnswers(deps.input ?? process.stdin, output);
5018
+ const interactive = Boolean(
5019
+ (deps.input ?? process.stdin).isTTY
5020
+ );
5021
+ const replaceScreen = (lines, replacement) => {
5022
+ if (!interactive) return;
5023
+ output.write(`\x1B[${lines}A\r\x1B[J`);
5024
+ if (replacement) output.line(replacement);
5025
+ };
5017
5026
  const choose = async (title, choices) => {
5018
5027
  output.line(` ${title}`);
5028
+ output.line(" Use the Up/Down arrow keys and Enter.");
5029
+ output.line();
5019
5030
  choices.forEach((c, i2) => output.line(` ${i2 === 0 ? ">" : " "} ${c}`));
5031
+ output.line();
5020
5032
  return answers ? answers.choose(choices) : "Skip";
5021
5033
  };
5022
5034
  let prepared;
@@ -5094,39 +5106,52 @@ async function joinedInstall(flags2, deps, output, authorize, management) {
5094
5106
  return restored.journal.phase === "rolled_back" ? 130 : 1;
5095
5107
  }
5096
5108
  if (!previous && !automatic && !flags2.has("hosts") && !flags2.has("components")) {
5097
- renderJourney("recommended", output, {
5098
- hosts: names.map((h) => labels[h]),
5099
- project: root,
5100
- node: preflight.node.version,
5101
- os: preflight.os
5102
- });
5103
- for (const host of preflight.hosts.filter((h) => !h.supported || h.name === "Grok"))
5104
- output.line(` ${host.name} (not offered at launch)`);
5105
- if (!deps.preflight && await access(join3(home, ".vscode")).then(
5106
- () => true,
5107
- () => false
5108
- ))
5109
- output.line(" VS Code Copilot (not offered at launch)");
5110
- const choice = flags2.has("customize") ? "Customize" : await answers?.choose(["Recommended", "Customize"]);
5111
- if (choice === "Cancel") return 130;
5112
- if (choice === "Customize") {
5113
- output.line(" Customize settings (press Enter to keep each value)");
5114
- output.line(` Components: ${components.join(",")}`);
5115
- components = (await answers?.text() || components.join(",")).split(",");
5116
- output.line(` Editors: ${names.join(",")}`);
5117
- names = (await answers?.text() || names.join(",")).split(",");
5118
- output.line(` Scope: ${scope} (user or project)`);
5119
- scope = await answers?.text() || scope;
5120
- output.line(` Roots: ${roots.join(",")}`);
5121
- roots = (await answers?.text() || roots.join(",")).split(",");
5122
- if (components.some((c) => !["hooks", "mcp", "scanner"].includes(c)) || names.some((h) => !launchHosts.includes(h)) || !["user", "project"].includes(scope))
5123
- throw new Error("invalid_settings");
5124
- scanner = components.includes("scanner");
5109
+ for (; ; ) {
5110
+ const recommendedLines = renderJourney("recommended", output, {
5111
+ hosts: names.map((h) => labels[h]),
5112
+ project: root,
5113
+ node: preflight.node.version,
5114
+ os: preflight.os
5115
+ });
5116
+ const choice = flags2.has("customize") ? "Customize" : await answers?.choose(["Recommended", "Customize"]);
5117
+ if (choice === "Cancel") return 130;
5118
+ if (choice !== "Customize") {
5119
+ replaceScreen(recommendedLines, completedStep(1, "Recommended setup selected"));
5120
+ break;
5121
+ }
5122
+ const items = [
5123
+ ...names.map((name2) => ({
5124
+ value: name2,
5125
+ label: labels[name2],
5126
+ checked: true
5127
+ })),
5128
+ { value: "scanner", label: "Indexing of your projects", checked: scanner }
5129
+ ];
5130
+ replaceScreen(recommendedLines);
5131
+ const customizeLines = renderCustomize(items, output);
5132
+ const customized = await answers?.customize(items);
5133
+ if (customized === "Cancel") return 130;
5134
+ if (customized === "Back") {
5135
+ replaceScreen(customizeLines);
5136
+ if (flags2.has("customize")) return 130;
5137
+ continue;
5138
+ }
5139
+ if (customized) {
5140
+ names = customized.selected.filter((item) => item !== "scanner");
5141
+ scanner = customized.selected.includes("scanner");
5142
+ components = scanner ? [.../* @__PURE__ */ new Set([...components, "scanner"])] : components.filter((component) => component !== "scanner");
5143
+ }
5144
+ replaceScreen(customizeLines, completedStep(1, "Custom setup selected"));
5145
+ break;
5125
5146
  }
5126
5147
  }
5127
5148
  if (preflight.status !== "ready") {
5128
5149
  if (!preflight.node.supported)
5129
- output.error(`Node ${preflight.node.version} found; Node 24 or newer is required.`);
5150
+ for (const line of nodeVersionHelp(
5151
+ preflight.node.version,
5152
+ deps.preflight?.platform ?? process.platform
5153
+ ))
5154
+ output.error(line);
5130
5155
  if (!preflight.network.reachable)
5131
5156
  output.error(
5132
5157
  `Discovery ${preflight.network.discoveryUrl}: ${preflight.network.detail ?? "unavailable"}`
@@ -5135,11 +5160,11 @@ async function joinedInstall(flags2, deps, output, authorize, management) {
5135
5160
  }
5136
5161
  if (!automatic && !flags2.has("apply")) {
5137
5162
  renderJourney("account", output);
5138
- renderJourney("cli_approval", output);
5139
5163
  }
5140
5164
  for (; ; ) {
5141
5165
  try {
5142
5166
  await authorize();
5167
+ if (!automatic) output.line(completedLine("Signed in"));
5143
5168
  break;
5144
5169
  } catch (error) {
5145
5170
  if (automatic || await choose("Sign-in did not finish.", ["Retry", "Skip"]) !== "Retry")
@@ -5194,24 +5219,24 @@ async function joinedInstall(flags2, deps, output, authorize, management) {
5194
5219
  (host) => components.filter((c) => c !== "scanner").sort((a, b) => a === b ? 0 : a === "hooks" ? -1 : 1).map((component) => ({
5195
5220
  host,
5196
5221
  component,
5197
- scope,
5222
+ scope: "user",
5198
5223
  home,
5199
5224
  projectRoot: root
5200
5225
  }))
5201
5226
  );
5202
- if (!previous && !automatic && !flags2.has("apply") && selections.length) {
5203
- renderJourney("host_approvals", output, {
5204
- hosts: names.map((h) => labels[h])
5205
- });
5206
- if (await answers?.choose(["Connect them", "Cancel"]) !== "Connect them") return 130;
5207
- }
5227
+ if (!automatic) output.line(completedStep(3, "Configure editors"));
5208
5228
  const afterHosts = async (journal, results, refreshHosts) => {
5229
+ if (!automatic)
5230
+ output.line(
5231
+ completedLine(`${names.length} ${names.length === 1 ? "editor" : "editors"} configured`)
5232
+ );
5209
5233
  journal.data.components = components;
5210
5234
  journal.data.roots = roots;
5211
5235
  const projectConditions = [];
5212
5236
  const projectReadiness = [];
5213
5237
  const finish = async (scannerPlan) => {
5214
5238
  prepared = scannerPlan;
5239
+ let limitMessage;
5215
5240
  if (scannerPlan) {
5216
5241
  if (!executor) {
5217
5242
  const runtime = await createRealProjectRuntime({
@@ -5221,65 +5246,66 @@ async function joinedInstall(flags2, deps, output, authorize, management) {
5221
5246
  executor = runtime.executor;
5222
5247
  projectTransport ??= runtime.transport;
5223
5248
  }
5224
- for (const selectedRoot of scannerPlan.roots) {
5225
- const discovered = await discoverRepositories(selectedRoot);
5226
- for (const repo of discovered.repositories.filter(
5227
- (r) => !scannerPlan.exclusions.some(
5228
- (e) => r.path === e || !relative(e, r.path).startsWith("..") && !isAbsolute(relative(e, r.path))
5229
- )
5230
- )) {
5231
- const options = {
5232
- cwd: repo.path,
5233
- allowCreate: true,
5234
- allowNestedInherit: false,
5235
- ...repo.nonGitSelected ? { nonGitSelected: true } : {}
5249
+ const connectedRoots = [];
5250
+ for (const [rootIndex, selectedRoot] of scannerPlan.roots.entries()) {
5251
+ const repo = await classifyRepository(selectedRoot);
5252
+ const options = {
5253
+ cwd: repo.path,
5254
+ allowCreate: true,
5255
+ allowNestedInherit: false,
5256
+ ...repo.nonGitSelected ? { nonGitSelected: true } : {}
5257
+ };
5258
+ const target = await journal.plan(join3(repo.path, ".mnemonik.json"), null, {
5259
+ kind: "project"
5260
+ });
5261
+ await journal.event("project_stage_intent", repo.path);
5262
+ const staged = await executor.stage(options);
5263
+ if (staged.status !== "staged") {
5264
+ limitMessage = projectLimitMessage(staged, scannerPlan.roots.slice(rootIndex))?.join(
5265
+ "\n"
5266
+ );
5267
+ await journal.restore(target);
5268
+ if (limitMessage) break;
5269
+ const condition = {
5270
+ kind: "project_identity_choice_pending",
5271
+ reason: `project_setup_required: ${repo.path}`,
5272
+ action: `mnemonik project init "${repo.path}"`
5236
5273
  };
5237
- const target = await journal.plan(join3(repo.path, ".mnemonik.json"), null, {
5238
- kind: "project"
5239
- });
5240
- await journal.event("project_stage_intent", repo.path);
5241
- const staged = await executor.stage(options);
5242
- if (staged.status !== "staged") {
5243
- const condition = {
5244
- kind: "project_identity_choice_pending",
5245
- reason: `project_setup_required: ${repo.path}`,
5246
- action: `mnemonik project init "${repo.path}"`
5247
- };
5248
- projectConditions.push(condition);
5249
- projectReadiness.push(
5250
- ...serializeReadiness({
5251
- installation: { conditions: [] },
5252
- projects: [{ identityFile: target.path, summary: { conditions: [condition] } }]
5253
- }).projects ?? []
5254
- );
5255
- await journal.restore(target);
5256
- continue;
5257
- }
5258
- if (!journal.data.projects.some((p) => p.root === repo.path))
5259
- journal.data.projects.push({
5260
- root: repo.path,
5261
- nonGitSelected: repo.nonGitSelected
5262
- });
5263
- const record = JSON.parse(
5264
- await readFile(recordPath(repo.path, deps.projectStateDir ?? stateDir), "utf8")
5274
+ projectConditions.push(condition);
5275
+ projectReadiness.push(
5276
+ ...serializeReadiness({
5277
+ installation: { conditions: [] },
5278
+ projects: [{ identityFile: target.path, summary: { conditions: [condition] } }]
5279
+ }).projects ?? []
5265
5280
  );
5266
- if (record.staged) await journal.propose(target, Buffer.from(record.staged.content));
5267
- await journal.stage(target);
5281
+ continue;
5268
5282
  }
5283
+ connectedRoots.push(repo.path);
5284
+ if (!journal.data.projects.some((p) => p.root === repo.path))
5285
+ journal.data.projects.push({
5286
+ root: repo.path,
5287
+ nonGitSelected: repo.nonGitSelected
5288
+ });
5289
+ const record = JSON.parse(
5290
+ await readFile(recordPath(repo.path, deps.projectStateDir ?? stateDir), "utf8")
5291
+ );
5292
+ if (record.staged) await journal.propose(target, Buffer.from(record.staged.content));
5293
+ await journal.stage(target);
5269
5294
  }
5295
+ scannerPlan.roots.splice(0, scannerPlan.roots.length, ...connectedRoots);
5296
+ roots = scannerPlan.roots;
5297
+ journal.data.roots = roots;
5270
5298
  }
5271
5299
  if (!automatic && !flags2.has("apply")) {
5272
5300
  for (; ; ) {
5273
- renderJourney("apply", output, {
5274
- connected: results.every((r) => r.status === "READY"),
5275
- files: [
5276
- ...new Set(
5277
- journal.data.targets.filter((t) => t.status !== "restored").map((t) => t.path)
5278
- ),
5279
- ...scannerPlan?.files ?? []
5280
- ]
5301
+ const applyLines = renderJourney("apply", output, {
5302
+ files: []
5281
5303
  });
5282
5304
  const choice = await answers?.choose(["Install and upload", "Back", "Cancel"]);
5305
+ replaceScreen(
5306
+ applyLines,
5307
+ choice === "Install and upload" ? completedStep(5, "Finish") : void 0
5308
+ );
5283
5309
  if (choice === "Back") {
5284
5310
  restart = true;
5285
5311
  throw new Error("install_back");
@@ -5310,15 +5336,19 @@ async function joinedInstall(flags2, deps, output, authorize, management) {
5310
5336
  let scannerDocument;
5311
5337
  if (scannerPlan) {
5312
5338
  try {
5313
- scannerDocument = await scannerPlan.apply(journal);
5314
- if (!automatic) output.line(" \u2713 Scanner connected");
5339
+ scannerDocument = await scannerPlan.apply(journal, scannerPlan.roots);
5340
+ if (!json) {
5341
+ if (scannerPlan.roots.length)
5342
+ output.line(connectedProjectsMessage(scannerPlan.roots));
5343
+ if (limitMessage) for (const line of limitMessage.split("\n")) output.line(line);
5344
+ }
5315
5345
  } catch (error) {
5316
5346
  if (!(error instanceof ScannerServiceLimited)) throw error;
5317
5347
  await scannerPlan.rollback(journal);
5318
5348
  journal.data.reports.push(
5319
- `Scanner was skipped (${error.reason}). Run mnemonik scanner enable to try again.`
5349
+ `Background indexing was skipped (${error.reason}). Run mnemonik install to try again.`
5320
5350
  );
5321
- if (!automatic && process.platform === "win32")
5351
+ if (!automatic && preflight.os === "Windows")
5322
5352
  renderJourney("windows", output, { reason: error.message });
5323
5353
  }
5324
5354
  }
@@ -5331,8 +5361,8 @@ async function joinedInstall(flags2, deps, output, authorize, management) {
5331
5361
  ...hostReadinessConditions(results, false),
5332
5362
  {
5333
5363
  kind: "scanner_omitted",
5334
- reason: "Scanner was omitted.",
5335
- action: "mnemonik scanner enable"
5364
+ reason: "Background indexing was omitted.",
5365
+ action: "mnemonik install"
5336
5366
  }
5337
5367
  ]
5338
5368
  }
@@ -5340,13 +5370,12 @@ async function joinedInstall(flags2, deps, output, authorize, management) {
5340
5370
  );
5341
5371
  return;
5342
5372
  }
5343
- if (!automatic) output.line(" Checking your installation, up to 2 minutes.");
5344
5373
  const conditions = [...hostReadinessConditions(results, true), ...projectConditions];
5345
5374
  if (!scannerDocument)
5346
5375
  conditions.push({
5347
5376
  kind: "scanner_omitted",
5348
- reason: "Scanner was skipped.",
5349
- action: "mnemonik scanner enable"
5377
+ reason: "Background indexing was skipped.",
5378
+ action: "mnemonik install"
5350
5379
  });
5351
5380
  const check = async () => collectStatusDocument({
5352
5381
  preflight: {
@@ -5361,7 +5390,6 @@ async function joinedInstall(flags2, deps, output, authorize, management) {
5361
5390
  getCliBearer: managed.getCliBearer,
5362
5391
  transport: projectTransport,
5363
5392
  configuredHosts: names,
5364
- grants: managed.grants,
5365
5393
  projectHookConditions: await hookStatusConditions(
5366
5394
  managed,
5367
5395
  components.includes("hooks") ? names.filter(
@@ -5386,7 +5414,6 @@ async function joinedInstall(flags2, deps, output, authorize, management) {
5386
5414
  actions: [...document.installation.actions, "mnemonik doctor"]
5387
5415
  }
5388
5416
  };
5389
- if (!automatic && document.installation.state === "READY") output.line(" \u2713 Checks passed");
5390
5417
  document = devReadiness({
5391
5418
  ...document,
5392
5419
  ...scannerDocument?.scanner ? { scanner: scannerDocument.scanner } : {},
@@ -5408,19 +5435,23 @@ async function joinedInstall(flags2, deps, output, authorize, management) {
5408
5435
  input: deps.input ?? process.stdin,
5409
5436
  output,
5410
5437
  nonInteractive: automatic,
5411
- roots,
5438
+ ...roots.length ? { roots } : {},
5439
+ home,
5412
5440
  noBrowser: flags2.has("no-browser"),
5441
+ approvalAnnounced: true,
5413
5442
  exclusions: String(flags2.get("exclusions") ?? "").split(",").filter(Boolean),
5414
5443
  ...deps.scannerService,
5415
5444
  ...deps.scannerEnable,
5445
+ projectExecutor: executor,
5446
+ projectStateDir: deps.projectStateDir ?? stateDir,
5416
5447
  journal,
5417
5448
  waiting: (phase) => {
5418
5449
  if (!automatic)
5419
5450
  output.line(
5420
- phase === "service" ? " Waiting for the scanner service to start, up to 2 minutes." : " Waiting for the first scanner heartbeat, up to 1 minute."
5451
+ phase === "service" ? " Waiting for background indexing to start, up to 2 minutes." : " Waiting for indexing to begin, up to 1 minute."
5421
5452
  );
5422
5453
  },
5423
- timeout: async () => await choose("Scanner did not connect.", ["Retry", "Skip"]) === "Retry" ? "retry" : "skip"
5454
+ timeout: async () => await choose("Background indexing did not start.", ["Retry", "Skip"]) === "Retry" ? "retry" : "skip"
5424
5455
  },
5425
5456
  finish
5426
5457
  );
@@ -5461,8 +5492,8 @@ async function joinedInstall(flags2, deps, output, authorize, management) {
5461
5492
  ...projectConditions,
5462
5493
  {
5463
5494
  kind: "scanner_not_verified",
5464
- reason: `Scanner could not be installed: ${reason}`,
5465
- action: "mnemonik scanner enable"
5495
+ reason: `Background indexing could not be started: ${reason}`,
5496
+ action: "mnemonik install"
5466
5497
  }
5467
5498
  ]
5468
5499
  },
@@ -5479,16 +5510,14 @@ async function joinedInstall(flags2, deps, output, authorize, management) {
5479
5510
  {
5480
5511
  ...managed,
5481
5512
  noBrowser: flags2.has("no-browser"),
5482
- approveHost: managed.approveHost ?? (async () => true),
5483
5513
  apply: true,
5484
5514
  afterHosts,
5485
5515
  rollbackInstall,
5486
5516
  installPlan: { components, roots },
5487
5517
  recovery: async () => "resume",
5488
- instruction: automatic ? void 0 : (text) => output.line(text),
5489
- timeout: managed.timeout ?? (automatic ? void 0 : async (host) => await choose(`${host} has not connected yet.`, ["Retry", "Skip"]) === "Retry" ? "retry" : "skip")
5518
+ instruction: automatic ? void 0 : (text) => output.line(text)
5490
5519
  },
5491
- flags2.has("integration-scope")
5520
+ false
5492
5521
  );
5493
5522
  if (restart && result.journal.phase === "rolled_back") {
5494
5523
  answers?.close();
@@ -5526,6 +5555,7 @@ async function joinedInstall(flags2, deps, output, authorize, management) {
5526
5555
  actions: [...final.installation.actions, launcherError.launcher.action]
5527
5556
  };
5528
5557
  final = await reportFinal({ ...final, ...launcher ? { launcher } : {} });
5558
+ await log({ preflight, journal: result.journal, targets: result.results, readiness: final });
5529
5559
  if (json)
5530
5560
  output.json({
5531
5561
  ...final,
@@ -5536,14 +5566,8 @@ async function joinedInstall(flags2, deps, output, authorize, management) {
5536
5566
  phase: result.journal.phase
5537
5567
  });
5538
5568
  else {
5539
- renderStatusSummaries(final, output);
5540
- for (const target of result.results)
5541
- output.line(
5542
- `${target.target}: ${target.status} (${target.reason}${target.detail ? `: ${target.detail}` : ""})`
5543
- );
5544
5569
  if (result.journal.state === "FAILED") {
5545
- output.line(` Installation failed. Local rollback: ${result.journal.phase}.`);
5546
- for (const line of result.reports) output.line(` ${line}`);
5570
+ output.line(` Installation failed. Details: ${logPath}`);
5547
5571
  } else if (final.installation.state === "READY")
5548
5572
  renderJourney("done", output, {
5549
5573
  total: final.indexing?.total,
@@ -5552,20 +5576,20 @@ async function joinedInstall(flags2, deps, output, authorize, management) {
5552
5576
  else
5553
5577
  renderJourney("skipped", output, {
5554
5578
  remaining: remainingReadinessCount(final.installation),
5555
- skipped: [...final.installation.reasons, ...final.installation.actions].join(" ")
5579
+ skipped: [...new Set(final.installation.actions)].join("\n") || "mnemonik status"
5556
5580
  });
5557
- output.line(
5558
- ` Status and devices: ${apiOrigin().replace("://api.mnemonik.dev", "://app.mnemonik.ai").replace("://mnemonik-api.", "://mnemonik-app.")}/settings/devices`
5559
- );
5560
- if (!launcherError && launcher?.onPath) output.line(" On this machine: mnemonik status");
5561
- else {
5562
- if (launcher && !launcher.onPath) output.line(` ${launcherPathAction(launcherOptions)}`);
5563
- output.line(" In this terminal: npx -y @mnemonik/cli@latest status");
5564
- }
5581
+ if (launcherError || launcher && !launcher.onPath)
5582
+ output.line(
5583
+ ` ${launcher ? launcherPathAction(launcherOptions) : "Run npx -y @mnemonik/cli@latest status"}`
5584
+ );
5565
5585
  }
5566
5586
  return result.journal.state === "FAILED" ? 1 : result.journal.phase === "rolled_back" ? 130 : final.installation.state === "READY" ? 0 : 3;
5567
5587
  } catch (error) {
5568
5588
  const reason = error instanceof Error ? error.message : "install_failed";
5589
+ if (reason === "install_cancelled") {
5590
+ output.line(" Installation cancelled.");
5591
+ return 130;
5592
+ }
5569
5593
  await reportFinal(
5570
5594
  serializeReadiness({
5571
5595
  installation: {
@@ -5581,7 +5605,10 @@ async function joinedInstall(flags2, deps, output, authorize, management) {
5581
5605
  reason,
5582
5606
  ...error instanceof LauncherError ? { launcher: error.launcher } : {}
5583
5607
  });
5584
- else output.error(reason);
5608
+ else {
5609
+ await log({ error: reason }).catch(() => void 0);
5610
+ output.error(`Installation stopped. Details: ${logPath}`);
5611
+ }
5585
5612
  return 3;
5586
5613
  } finally {
5587
5614
  answers?.close();