minutes-mcp 0.25.1 → 0.25.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.
@@ -5,6 +5,7 @@ import { lstat, mkdir, open, opendir, realpath, stat } from "node:fs/promises";
5
5
  import { basename, dirname, extname, isAbsolute, join, relative } from "node:path";
6
6
  import { fileURLToPath } from "node:url";
7
7
  import { decodePolicyUtf8, fingerprintTextFileFromBoundParent, readTextFileWithRevisionFromBoundParent, writeOperatorDiagnostic, } from "./secure-read.js";
8
+ import { nodeChildEnvironment } from "./node-child.js";
8
9
  const MAX_AUTHORIZATION_ATTEMPTS = 2;
9
10
  const DEFAULT_FENCE_TIMEOUT_MS = 5_000;
10
11
  const DEFAULT_AUTHORIZATION_TIMEOUT_MS = 15_000;
@@ -1431,7 +1432,7 @@ async function dispatchStableCorpusLease(root, operation, hooks = {}) {
1431
1432
  detached: process.platform !== "win32",
1432
1433
  stdio: ["pipe", "pipe", "pipe"],
1433
1434
  windowsHide: true,
1434
- env: { ...process.env },
1435
+ env: nodeChildEnvironment(),
1435
1436
  });
1436
1437
  child.stderr?.resume();
1437
1438
  let resolveTermination;
package/dist/index.d.ts CHANGED
@@ -451,7 +451,8 @@ export type AgentTrustReadiness = {
451
451
  qmd_retirement: "ready-clean" | "blocked";
452
452
  remediation?: string;
453
453
  };
454
- export declare function readAgentTrustReadiness(runner?: MinutesRunner): Promise<AgentTrustReadiness>;
454
+ export declare function cliTooOldGuidance(installedVersion: string, minimumVersion?: string, platform?: NodeJS.Platform): string;
455
+ export declare function readAgentTrustReadiness(runner?: MinutesRunner, platform?: NodeJS.Platform): Promise<AgentTrustReadiness>;
455
456
  export declare function requireAgentTrustReadiness(runner?: MinutesRunner): Promise<AgentTrustReadiness>;
456
457
  export declare function afterAgentTrustReadiness<T>(operation: () => Promise<T>, runner?: MinutesRunner): Promise<T>;
457
458
  export declare function afterRequiredCli<T>(operation: () => Promise<T>, cliProbe?: () => Promise<boolean>): Promise<T>;
package/dist/index.js CHANGED
@@ -51,7 +51,8 @@ import { homedir } from "os";
51
51
  import { parse as parseYaml } from "yaml";
52
52
  import * as reader from "minutes-sdk";
53
53
  import { canonicalizeRoot, expandHomeLikePath, readTextFileInDirectory, validatePathInDirectories, validatePathInDirectory, } from "./paths.js";
54
- import { isCliCompatible } from "./version.js";
54
+ import { isCliCompatible, parseVersion } from "./version.js";
55
+ import { nodeChildEnvironment } from "./node-child.js";
55
56
  import { hasFeature, probeCapabilitiesSync, } from "./capabilities.js";
56
57
  import { downloadReleaseBinaryWithChecksum, extractZipWithPowerShell, } from "./autoInstall.js";
57
58
  import { attachCaptureRelay, } from "./captureRelay.js";
@@ -1569,7 +1570,7 @@ const COPILOT_SUPPORTED = hasFeature(CLI_CAPABILITIES, "copilot_realtime");
1569
1570
  // `./version.ts` (see issue #183). Hosted `.mcpb` bundles will run
1570
1571
  // against CLIs with different minor/patch numbers within the same
1571
1572
  // major; that is explicitly supported.
1572
- const MCP_SERVER_VERSION = "0.25.1";
1573
+ const MCP_SERVER_VERSION = "0.25.3";
1573
1574
  export function parseKnowledgeConfig(configContent) {
1574
1575
  const knowledgeMatch = configContent.match(/\[knowledge\][\s\S]*?(?=\n\[|$)/);
1575
1576
  if (!knowledgeMatch) {
@@ -2483,7 +2484,47 @@ export async function readKnowledgeStatusSnapshot(runner = runMinutes) {
2483
2484
  }
2484
2485
  return result;
2485
2486
  }
2486
- export async function readAgentTrustReadiness(runner = runMinutes) {
2487
+ const AGENT_READINESS_MIN_CLI_VERSION = "0.25.0";
2488
+ function isOlderVersion(installed, minimum) {
2489
+ const found = parseVersion(installed);
2490
+ const required = parseVersion(minimum);
2491
+ if (!found || !required)
2492
+ return false;
2493
+ if (found.major !== required.major)
2494
+ return found.major < required.major;
2495
+ if (found.minor !== required.minor)
2496
+ return found.minor < required.minor;
2497
+ return found.patch < required.patch;
2498
+ }
2499
+ function rejectedUnknownSubcommand(error, subcommand) {
2500
+ const diagnostic = [error?.stderr, error?.message]
2501
+ .filter((value) => typeof value === "string")
2502
+ .join("\n")
2503
+ .toLowerCase();
2504
+ return (diagnostic.includes(subcommand.toLowerCase()) &&
2505
+ /(?:unrecognized|unknown|invalid) subcommand/.test(diagnostic));
2506
+ }
2507
+ export function cliTooOldGuidance(installedVersion, minimumVersion = AGENT_READINESS_MIN_CLI_VERSION, platform = process.platform) {
2508
+ const update = platform === "darwin" || platform === "win32"
2509
+ ? "Install the latest free Minutes desktop app from https://useminutes.app"
2510
+ : "Update the Minutes CLI from https://useminutes.app, or run `cargo install minutes-cli`";
2511
+ return (`The Minutes engine is too old for this extension (found v${installedVersion}; ` +
2512
+ `needs v${minimumVersion} or newer). ${update}, then restart this app.`);
2513
+ }
2514
+ async function oldCliReadinessGuidance(runner, platform) {
2515
+ try {
2516
+ const { stdout } = await runner(["--version"], 5000);
2517
+ const match = stdout.match(/\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?/);
2518
+ if (!match || !isOlderVersion(match[0], AGENT_READINESS_MIN_CLI_VERSION)) {
2519
+ return null;
2520
+ }
2521
+ return cliTooOldGuidance(match[0], AGENT_READINESS_MIN_CLI_VERSION, platform);
2522
+ }
2523
+ catch {
2524
+ return null;
2525
+ }
2526
+ }
2527
+ export async function readAgentTrustReadiness(runner = runMinutes, platform = process.platform) {
2487
2528
  let stdout;
2488
2529
  try {
2489
2530
  ({ stdout } = await runner(["agent-readiness", "--json"]));
@@ -2496,6 +2537,11 @@ export async function readAgentTrustReadiness(runner = runMinutes) {
2496
2537
  // "could not be verified safely" and nothing to do about it (#774).
2497
2538
  if (error?.cliMissing)
2498
2539
  throw error;
2540
+ if (rejectedUnknownSubcommand(error, "agent-readiness")) {
2541
+ const guidance = await oldCliReadinessGuidance(runner, platform);
2542
+ if (guidance)
2543
+ throw new Error(guidance);
2544
+ }
2499
2545
  throw new Error("Minutes agent readiness could not be verified safely.");
2500
2546
  }
2501
2547
  let result;
@@ -2523,7 +2569,7 @@ export async function requireAgentTrustReadiness(runner = runMinutes) {
2523
2569
  const readiness = await readAgentTrustReadiness(runner);
2524
2570
  if (!readiness.ready) {
2525
2571
  throw new Error(readiness.remediation ||
2526
- "Run `minutes qmd cleanup`, repair or reinstall qmd if requested, then restart Minutes.");
2572
+ "This machine shows a Minutes-owned QMD registration that qmd could not confirm was removed. Make sure `qmd` runs (`qmd collection list`), then run `minutes qmd cleanup` and restart Minutes.");
2527
2573
  }
2528
2574
  return readiness;
2529
2575
  }
@@ -4854,7 +4900,7 @@ export async function runIsolatedMcpProcessAudio(filePath, allowedDirs, audioExt
4854
4900
  child = spawn(helper.binary, helper.args, {
4855
4901
  detached: true,
4856
4902
  stdio: ["pipe", "pipe", "pipe", "pipe"],
4857
- env: mcpCliChildEnv(),
4903
+ env: nodeChildEnvironment(mcpCliChildEnv()),
4858
4904
  });
4859
4905
  }
4860
4906
  catch {
@@ -5657,7 +5703,7 @@ registerTool("stop_dictation", "Stop the current dictation session.", {}, { titl
5657
5703
  };
5658
5704
  });
5659
5705
  // ── Tool: list_voices ────────────────────────────────────────
5660
- registerTool("list_voices", "List enrolled voice profiles for speaker identification. Shows who has been enrolled, sample count, and model version.", {}, { title: "Voice Profiles", readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: false }, async () => {
5706
+ registerTool("list_voices", "List active voice enrollments for speaker identification. Shows who has been enrolled, sample count, and model version.", {}, { title: "Voice Profiles", readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: false }, async () => {
5661
5707
  if (!(await isCliAvailable())) {
5662
5708
  return { content: [{ type: "text", text: "Minutes CLI not available." }] };
5663
5709
  }
@@ -5665,7 +5711,7 @@ registerTool("list_voices", "List enrolled voice profiles for speaker identifica
5665
5711
  const profiles = parseJsonOutput(stdout);
5666
5712
  if (!Array.isArray(profiles) || profiles.length === 0) {
5667
5713
  return {
5668
- content: [{ type: "text", text: "No voice profiles enrolled. The user can enroll with: minutes enroll" }],
5714
+ content: [{ type: "text", text: "No voice profiles enrolled. The user can enroll with: minutes voice enroll" }],
5669
5715
  };
5670
5716
  }
5671
5717
  const lines = profiles.map((p) => `${p.name} — ${p.sample_count} samples, ${p.source} (${p.model_version})`);
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Environment for a helper that must run as Node through process.execPath.
3
+ *
4
+ * Electron hosts such as Claude Desktop expose their app executable as
5
+ * process.execPath. Spawning that executable with a JavaScript file does not
6
+ * start Node unless ELECTRON_RUN_AS_NODE is set for the child. Keep the switch
7
+ * scoped to helpers whose argv is a Node program; ordinary Minutes CLI children
8
+ * must continue to receive their existing policy environment unchanged.
9
+ */
10
+ export declare function nodeChildEnvironment(base?: NodeJS.ProcessEnv, electronVersion?: string | undefined): NodeJS.ProcessEnv;
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Environment for a helper that must run as Node through process.execPath.
3
+ *
4
+ * Electron hosts such as Claude Desktop expose their app executable as
5
+ * process.execPath. Spawning that executable with a JavaScript file does not
6
+ * start Node unless ELECTRON_RUN_AS_NODE is set for the child. Keep the switch
7
+ * scoped to helpers whose argv is a Node program; ordinary Minutes CLI children
8
+ * must continue to receive their existing policy environment unchanged.
9
+ */
10
+ export function nodeChildEnvironment(base = process.env, electronVersion = process.versions.electron) {
11
+ const environment = { ...base };
12
+ if (electronVersion) {
13
+ environment.ELECTRON_RUN_AS_NODE = "1";
14
+ }
15
+ return environment;
16
+ }
@@ -3,6 +3,7 @@ import { createHash } from "node:crypto";
3
3
  import { lstatSync, realpathSync, statSync, } from "node:fs";
4
4
  import { basename, dirname } from "path";
5
5
  import { TextDecoder } from "node:util";
6
+ import { nodeChildEnvironment } from "./node-child.js";
6
7
  /**
7
8
  * Decode security-policy-bearing text without Unicode replacement.
8
9
  *
@@ -353,7 +354,7 @@ class BoundParentReader {
353
354
  });
354
355
  this.child = spawn(process.execPath, ["-e", BOUND_READER_SOURCE], {
355
356
  cwd: parent,
356
- env: { ...process.env, MINUTES_BOUND_PARENT: parent },
357
+ env: nodeChildEnvironment({ ...process.env, MINUTES_BOUND_PARENT: parent }),
357
358
  stdio: ["pipe", "pipe", "pipe"],
358
359
  windowsHide: true,
359
360
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "minutes-mcp",
3
- "version": "0.25.1",
3
+ "version": "0.25.3",
4
4
  "description": "MCP server for minutes \u2014 conversation memory for AI assistants. Works with Claude Desktop, Mistral Vibe, Cursor, Windsurf, and any MCP client.",
5
5
  "main": "dist/index.js",
6
6
  "type": "module",
@@ -45,7 +45,7 @@
45
45
  "dependencies": {
46
46
  "@modelcontextprotocol/ext-apps": "^1.6.0",
47
47
  "@modelcontextprotocol/sdk": "^1.29.0",
48
- "minutes-sdk": "0.25.1",
48
+ "minutes-sdk": "0.25.3",
49
49
  "yaml": "^2.8.3",
50
50
  "zod": "^3.25 || ^4.0"
51
51
  },