minutes-mcp 0.9.1 → 0.9.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -25,4 +25,4 @@
25
25
  * All tools use execFile (not exec) to shell out to the `minutes` CLI binary.
26
26
  * No shell interpolation — safe from injection.
27
27
  */
28
- export {};
28
+ export declare const MEETING_INSIGHT_KINDS: readonly ["decision", "commitment", "question"];
package/dist/index.js CHANGED
@@ -32,13 +32,14 @@ import { z } from "zod";
32
32
  import { execFile, spawn } from "child_process";
33
33
  import { promisify } from "util";
34
34
  import { existsSync } from "fs";
35
- import { readFile } from "fs/promises";
36
- import { delimiter, dirname, join } from "path";
35
+ import { mkdir, readFile, rm, writeFile } from "fs/promises";
36
+ import { delimiter, dirname, join, resolve } from "path";
37
37
  import { fileURLToPath } from "url";
38
38
  import { homedir } from "os";
39
39
  import * as reader from "minutes-sdk";
40
40
  import { canonicalizeRoot, expandHomeLikePath, validatePathInDirectories, validatePathInDirectory, } from "./paths.js";
41
41
  const UI_RESOURCE_URI = "ui://minutes/dashboard";
42
+ export const MEETING_INSIGHT_KINDS = ["decision", "commitment", "question"];
42
43
  const execFileAsync = promisify(execFile);
43
44
  // ── QMD semantic search (optional — falls back to CLI) ──────
44
45
  let qmdAvailable = null;
@@ -147,8 +148,9 @@ function findMinutesBinary() {
147
148
  }
148
149
  let MINUTES_BIN = findMinutesBinary();
149
150
  // ── Expected CLI version (must match this MCP server release) ──
150
- const EXPECTED_CLI_VERSION = "0.8.4";
151
- const RELEASE_TAG = "v0.8.4";
151
+ const MCP_SERVER_VERSION = "0.9.2";
152
+ const EXPECTED_CLI_VERSION = MCP_SERVER_VERSION;
153
+ const RELEASE_TAG = `v${EXPECTED_CLI_VERSION}`;
152
154
  // ── CLI auto-install ────────────────────────────────────────
153
155
  // When installed via MCPB or `npx minutes-mcp`, the Rust CLI binary
154
156
  // may not be present. We attempt to install it automatically so
@@ -338,6 +340,82 @@ async function isCliAvailable() {
338
340
  }
339
341
  return cliAvailable;
340
342
  }
343
+ function desktopControlDir() {
344
+ return join(homedir(), ".minutes", "desktop-control");
345
+ }
346
+ function desktopAppStatusPath() {
347
+ return join(desktopControlDir(), "desktop-app.json");
348
+ }
349
+ function desktopRequestPath(id) {
350
+ return join(desktopControlDir(), "requests", `${id}.json`);
351
+ }
352
+ function desktopResponsePath(id) {
353
+ return join(desktopControlDir(), "responses", `${id}.json`);
354
+ }
355
+ function isProcessAlive(pid) {
356
+ try {
357
+ process.kill(pid, 0);
358
+ return true;
359
+ }
360
+ catch {
361
+ return false;
362
+ }
363
+ }
364
+ async function readRunningDesktopAppStatus() {
365
+ try {
366
+ const raw = await readFile(desktopAppStatusPath(), "utf8");
367
+ const status = JSON.parse(raw);
368
+ const updatedAt = Date.parse(status.updated_at);
369
+ if (!Number.isFinite(updatedAt))
370
+ return null;
371
+ if (Date.now() - updatedAt > 10000)
372
+ return null;
373
+ if (!status.pid || !isProcessAlive(status.pid))
374
+ return null;
375
+ return status;
376
+ }
377
+ catch {
378
+ return null;
379
+ }
380
+ }
381
+ async function delegateCallRecordingToDesktop(args) {
382
+ const status = await readRunningDesktopAppStatus();
383
+ if (!status)
384
+ return null;
385
+ const id = `mcp-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
386
+ await mkdir(join(desktopControlDir(), "requests"), { recursive: true });
387
+ await mkdir(join(desktopControlDir(), "responses"), { recursive: true });
388
+ const request = {
389
+ id,
390
+ created_at: new Date().toISOString(),
391
+ action: {
392
+ type: "start-recording",
393
+ mode: args.mode,
394
+ intent: args.intent,
395
+ allow_degraded: args.allow_degraded,
396
+ title: args.title,
397
+ language: args.language,
398
+ },
399
+ };
400
+ const requestPath = desktopRequestPath(id);
401
+ const responsePath = desktopResponsePath(id);
402
+ await writeFile(requestPath, JSON.stringify(request, null, 2), "utf8");
403
+ const timeoutAt = Date.now() + 10000;
404
+ try {
405
+ while (Date.now() < timeoutAt) {
406
+ if (existsSync(responsePath)) {
407
+ const response = JSON.parse(await readFile(responsePath, "utf8"));
408
+ await rm(responsePath, { force: true });
409
+ return response;
410
+ }
411
+ await new Promise((resolve) => setTimeout(resolve, 250));
412
+ }
413
+ throw new Error("Minutes desktop app did not acknowledge the call recording request in time.");
414
+ }
415
+ finally {
416
+ await rm(requestPath, { force: true }).catch(() => { });
417
+ }
418
+ }
341
419
  const CLI_INSTALL_MSG = `Recording requires the minutes CLI binary.\n` +
342
420
  `Searched: ${MINUTES_BIN}\n\n` +
343
421
  `Install it:\n` +
@@ -387,7 +465,7 @@ function parseJsonOutput(stdout) {
387
465
  // ── MCP Server ──────────────────────────────────────────────
388
466
  const server = new McpServer({
389
467
  name: "minutes",
390
- version: "0.9.1",
468
+ version: MCP_SERVER_VERSION,
391
469
  });
392
470
  // Declare MCP Apps extension support so hosts classify this server as interactive.
393
471
  // The `extensions` field is part of the draft MCP spec (SEP-1724) — not yet in the
@@ -434,15 +512,24 @@ registerAppResource(server, "Minutes Dashboard", UI_RESOURCE_URI, { description:
434
512
  };
435
513
  });
436
514
  // ── Tool: start_recording ───────────────────────────────────
437
- server.tool("start_recording", "Start recording audio from the default input device. The recording runs until stop_recording is called.", {
515
+ server.tool("start_recording", "Start recording audio with call-aware preflight. When a known call app is active, Minutes can infer call intent and block silent mic-only call captures unless explicitly allowed.", {
438
516
  title: z.string().optional().describe("Optional title for this recording"),
439
517
  mode: z
440
518
  .enum(["meeting", "quick-thought"])
441
519
  .optional()
442
520
  .default("meeting")
443
521
  .describe("Live capture mode"),
522
+ intent: z
523
+ .enum(["memo", "room", "call"])
524
+ .optional()
525
+ .describe("Optional recording intent. If omitted and a known call app is active, Minutes may infer call intent."),
526
+ allow_degraded: z
527
+ .boolean()
528
+ .optional()
529
+ .default(false)
530
+ .describe("Allow a mic-only capture to continue even if Minutes detects a call but no system-audio route is configured."),
444
531
  language: z.string().optional().describe("Transcription language code (e.g. 'en', 'ur', 'es', 'zh'). Overrides config.toml setting."),
445
- }, { title: "Start Recording", readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: false }, async ({ title, mode, language }) => {
532
+ }, { title: "Start Recording", readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: false }, async ({ title, mode, intent, allow_degraded, language }) => {
446
533
  if (!(await isCliAvailable())) {
447
534
  return { content: [{ type: "text", text: CLI_INSTALL_MSG }] };
448
535
  }
@@ -458,11 +545,62 @@ server.tool("start_recording", "Start recording audio from the default input dev
458
545
  ],
459
546
  };
460
547
  }
548
+ const preflightArgs = ["preflight-record", "--json", "--mode", mode, "--intent", intent || "auto"];
549
+ if (allow_degraded)
550
+ preflightArgs.push("--allow-degraded");
551
+ const { stdout: preflightOut } = await runMinutes(preflightArgs);
552
+ const preflight = parseJsonOutput(preflightOut);
553
+ if (preflight.intent === "call") {
554
+ const response = await delegateCallRecordingToDesktop({
555
+ title,
556
+ mode,
557
+ intent: "call",
558
+ allow_degraded,
559
+ language,
560
+ });
561
+ if (response) {
562
+ if (!response.accepted) {
563
+ return {
564
+ content: [{ type: "text", text: response.detail }],
565
+ structuredContent: { preflight, desktop_response: response },
566
+ };
567
+ }
568
+ await new Promise((r) => setTimeout(r, 750));
569
+ const { stdout: newStatus } = await runMinutes(["status"]);
570
+ const result = parseJsonOutput(newStatus);
571
+ return {
572
+ content: [
573
+ {
574
+ type: "text",
575
+ text: result.recording
576
+ ? `Recording started in the running Minutes desktop app (PID: ${result.pid}).${Array.isArray(preflight.warnings) && preflight.warnings.length ? ` ${preflight.warnings[0]}` : ""} Say "stop recording" when done.`
577
+ : response.detail,
578
+ },
579
+ ],
580
+ structuredContent: { preflight, desktop_response: response },
581
+ };
582
+ }
583
+ }
584
+ if (preflight.blocking_reason) {
585
+ return {
586
+ content: [
587
+ {
588
+ type: "text",
589
+ text: preflight.blocking_reason,
590
+ },
591
+ ],
592
+ structuredContent: { preflight },
593
+ };
594
+ }
461
595
  // Spawn detached — recording is a foreground process that blocks,
462
596
  // so we spawn it and let it run independently
463
597
  const args = ["record", "--mode", mode];
464
598
  if (title)
465
599
  args.push("--title", title);
600
+ if (intent)
601
+ args.push("--intent", intent);
602
+ if (allow_degraded)
603
+ args.push("--allow-degraded");
466
604
  if (language)
467
605
  args.push("--language", language);
468
606
  const child = spawn(MINUTES_BIN, args, {
@@ -480,7 +618,7 @@ server.tool("start_recording", "Start recording audio from the default input dev
480
618
  {
481
619
  type: "text",
482
620
  text: result.recording
483
- ? `${result.recording_mode === "quick-thought" ? "Quick thought" : "Recording"} started (PID: ${result.pid}). Say "stop recording" when done.`
621
+ ? `${result.recording_mode === "quick-thought" ? "Quick thought" : "Recording"} started (PID: ${result.pid}).${Array.isArray(preflight.warnings) && preflight.warnings.length ? ` ${preflight.warnings[0]}` : ""} Say "stop recording" when done.`
484
622
  : "Recording failed to start. Check `minutes logs` for details.",
485
623
  },
486
624
  ],
@@ -571,7 +709,7 @@ server.tool("get_status", "Check if a recording is currently in progress.", {},
571
709
  : "No recording in progress.";
572
710
  return { content: [{ type: "text", text }] };
573
711
  });
574
- server.tool("list_processing_jobs", "List background processing jobs for recent recordings, including queued, transcript-ready, failed, and completed work.", {
712
+ server.tool("list_processing_jobs", "List background processing jobs for recent recordings, including queued, transcript-ready, needs-review, failed, and completed work.", {
575
713
  limit: z.number().optional().default(10).describe("Maximum number of jobs"),
576
714
  include_completed: z.boolean().optional().default(true).describe("Include completed and failed jobs"),
577
715
  }, { title: "Processing Jobs", readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: false }, async ({ limit, include_completed }) => {
@@ -1502,8 +1640,8 @@ server.tool("confirm_speaker", "Confirm or correct a speaker attribution in a me
1502
1640
  }
1503
1641
  });
1504
1642
  // ── Tool: get_meeting_insights ─────────────────────────────
1505
- server.tool("get_meeting_insights", "Query structured insights extracted from meetings — decisions, commitments, approvals, questions, blockers, follow-ups, and risks. Each insight has a confidence level (tentative/inferred/strong/explicit). Use this to find what was decided, who committed to what, and what's still open across all meetings. External systems can subscribe to these events for workflow automation.", {
1506
- kind: z.enum(["decision", "commitment", "approval", "question", "blocker", "follow_up", "risk"]).optional().describe("Filter by insight type"),
1643
+ server.tool("get_meeting_insights", "Query structured insights extracted from meetings — decisions, commitments, and questions with confidence levels. Use this to find what was decided, who committed to what, and what's still open across all meetings. External systems can subscribe to these events for workflow automation.", {
1644
+ kind: z.enum(MEETING_INSIGHT_KINDS).optional().describe("Filter by insight type"),
1507
1645
  confidence: z.enum(["tentative", "inferred", "strong", "explicit"]).optional().describe("Minimum confidence level"),
1508
1646
  participant: z.string().optional().describe("Filter by participant name (partial match)"),
1509
1647
  since: z.string().optional().describe("Only insights since this date (YYYY-MM-DD)"),
@@ -1699,7 +1837,9 @@ async function main() {
1699
1837
  await server.connect(transport);
1700
1838
  console.error("Minutes MCP server running on stdio");
1701
1839
  }
1702
- main().catch((error) => {
1703
- console.error("Fatal error:", error);
1704
- process.exit(1);
1705
- });
1840
+ if (process.argv[1] && resolve(process.argv[1]) === __filename) {
1841
+ main().catch((error) => {
1842
+ console.error("Fatal error:", error);
1843
+ process.exit(1);
1844
+ });
1845
+ }
@@ -181,7 +181,7 @@ ${n}`}]})}let he=!1;function dn(){he=!he,B.requestDisplayMode({mode:he?"fullscre
181
181
  ${s.latest_follow_up?`<div class="intent-source">Latest: ${y(s.latest_follow_up.title||"")}</div>`:""}
182
182
  </div>`).join("")}
183
183
  </div>`),J(k("report-body"),o.join("")),Q("report")}function ke(e){const t=e.structuredContent,n=e._meta,o=t?.view||n?.view,s=t?.view?t:n?.view?n:t||n||{};if(!o){const i=e.content?.map(r=>"text"in r?r.text:"").join("");i&&pn({content:i,path:""});return}if(o==="detail"&&!s.content){const i=e.content?.map(r=>"text"in r?r.text:"").join("");i&&(s.content=i)}switch(o){case"dashboard":jt(s);break;case"detail":pn(s);break;case"person":Il(s);break;case"relationship_map":case"commitments":Pl(s.people||[]);break;case"report":xl(s);break;default:we(`Unknown view: ${o}`)}}B.ontoolresult=async e=>{ke(e)};function vo(e){e.theme&&yl(e.theme),e.styles?.variables&&wl(e.styles.variables)}B.onhostcontextchanged=vo;B.connect().then(()=>{const e=B.getHostContext();e&&vo(e)});</script>
184
- <style rel="stylesheet" crossorigin>*,*:before,*:after{box-sizing:border-box;margin:0;padding:0}:root{--bg-primary: #1a1a1a;--bg-secondary: #242424;--bg-tertiary: #2e2e2e;--bg-hover: #363636;--text-primary: #e4e4e4;--text-secondary: #a0a0a0;--text-muted: #6b6b6b;--border-color: #333;--accent: #6b9fff;--accent-dim: #4a7fe0;--badge-meeting: #3b82f6;--badge-memo: #8b5cf6;--action-color: #f59e0b;--action-overdue: #ef4444;--decision-color: #10b981;--conflict-color: #ef4444;--stale-color: #f59e0b;--radius: 6px;--radius-lg: 10px;--font-sans: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;--font-mono: "SF Mono", "Fira Code", "Cascadia Code", monospace}[data-theme=light]{--bg-primary: #ffffff;--bg-secondary: #f5f5f5;--bg-tertiary: #ececec;--bg-hover: #e2e2e2;--text-primary: #1a1a1a;--text-secondary: #555;--text-muted: #999;--border-color: #ddd}html,body{height:100%;font-family:var(--font-sans);font-size:13px;line-height:1.5;color:var(--text-primary);background:var(--bg-primary);-webkit-font-smoothing:antialiased}.main{min-height:100%;display:flex;flex-direction:column}#loading{display:flex;align-items:center;justify-content:center;min-height:120px;color:var(--text-secondary)}.loading-content{display:flex;flex-direction:column;align-items:center;gap:12px}.spinner{width:24px;height:24px;border:2px solid var(--border-color);border-top-color:var(--accent);border-radius:50%;animation:spin .8s linear infinite}@keyframes spin{to{transform:rotate(360deg)}}#error{display:flex;flex-direction:column;align-items:center;justify-content:center;min-height:120px;gap:8px;color:var(--conflict-color)}.error-icon{width:32px;height:32px;border-radius:50%;background:var(--conflict-color);color:#fff;display:flex;align-items:center;justify-content:center;font-weight:700;font-size:18px}#error-message{color:var(--text-secondary);text-align:center;max-width:400px}.stats-bar{display:flex;gap:16px;padding:12px 16px;border-bottom:1px solid var(--border-color);background:var(--bg-secondary)}.stat{display:flex;align-items:baseline;gap:6px}.stat-value{font-size:20px;font-weight:600;font-variant-numeric:tabular-nums;color:var(--text-primary)}.stat-label{font-size:12px;color:var(--text-secondary);text-transform:uppercase;letter-spacing:.04em}.dashboard-body{display:flex;flex:1;overflow:hidden}.meetings-list{flex:1;overflow-y:auto;padding:8px;display:flex;flex-direction:column;gap:4px}.meeting-card{padding:10px 12px;border-radius:var(--radius);cursor:pointer;border:1px solid transparent;transition:background .15s,border-color .15s}.meeting-card:hover{background:var(--bg-hover);border-color:var(--border-color)}.meeting-date{font-size:11px;font-family:var(--font-mono);color:var(--text-muted);margin-bottom:2px}.meeting-title{font-weight:500;color:var(--text-primary);margin-bottom:4px}.meeting-meta{display:flex;align-items:center;gap:8px;font-size:11px;color:var(--text-secondary)}.meeting-snippet{font-size:12px;color:var(--text-muted);margin-top:4px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.badge{display:inline-block;padding:1px 6px;border-radius:3px;font-size:10px;font-weight:600;text-transform:uppercase;letter-spacing:.04em}.badge-meeting{background:color-mix(in srgb,var(--badge-meeting) 20%,transparent);color:var(--badge-meeting)}.badge-memo{background:color-mix(in srgb,var(--badge-memo) 20%,transparent);color:var(--badge-memo)}.word-count,.duration{font-family:var(--font-mono);font-size:11px}.actions-sidebar{width:280px;border-left:1px solid var(--border-color);overflow-y:auto;padding:12px;background:var(--bg-secondary);flex-shrink:0}.actions-sidebar:empty{display:none}.sidebar-title{font-size:12px;font-weight:600;text-transform:uppercase;letter-spacing:.05em;color:var(--text-secondary);margin-bottom:10px}.intent-item{padding:8px 10px;border-radius:var(--radius);background:var(--bg-tertiary);margin-bottom:6px;border-left:3px solid var(--border-color)}.intent-item.action{border-left-color:var(--action-color)}.intent-item.action.overdue{border-left-color:var(--action-overdue)}.intent-item.decision{border-left-color:var(--decision-color)}.intent-item.conflict{border-left-color:var(--conflict-color)}.intent-item.stale{border-left-color:var(--stale-color)}.intent-what{font-size:13px;color:var(--text-primary);margin-bottom:4px}.intent-who{font-size:11px;color:var(--accent);font-weight:500}.intent-date{font-size:11px;font-family:var(--font-mono);color:var(--text-muted)}.intent-source{font-size:11px;color:var(--text-muted);margin-top:2px}.intent-reasons{font-size:11px;color:var(--stale-color);margin-top:2px}.back-btn{display:inline-flex;align-items:center;gap:4px;padding:6px 12px;margin:12px 16px 0;font-size:12px;color:var(--accent);background:none;border:1px solid var(--border-color);border-radius:var(--radius);cursor:pointer;font-family:inherit;transition:background .15s}.back-btn:hover{background:var(--bg-hover)}.detail-header{padding:16px;border-bottom:1px solid var(--border-color)}.detail-header h1{font-size:18px;font-weight:600;margin-bottom:8px}.detail-meta{display:flex;flex-wrap:wrap;align-items:center;gap:12px;margin-bottom:8px}.meta-item{font-size:12px;color:var(--text-secondary)}.attendees{font-size:12px;color:var(--text-secondary);margin-bottom:4px}.attendee{display:inline-block;padding:1px 6px;background:var(--bg-tertiary);border-radius:3px;margin:0 2px;color:var(--text-primary);font-weight:500}.tags{display:flex;gap:4px;flex-wrap:wrap}.tag{display:inline-block;padding:1px 8px;background:color-mix(in srgb,var(--accent) 15%,transparent);color:var(--accent);border-radius:10px;font-size:11px}.detail-body{display:flex;flex:1;overflow:hidden}.detail-content{flex:1;overflow-y:auto;padding:16px;line-height:1.7}.detail-content h2{font-size:15px;font-weight:600;margin:20px 0 8px;padding-bottom:4px;border-bottom:1px solid var(--border-color);color:var(--text-primary)}.detail-content h2:first-child{margin-top:0}.detail-content h3{font-size:14px;font-weight:600;margin:16px 0 6px;color:var(--text-primary)}.detail-content p{margin-bottom:8px;color:var(--text-primary)}.detail-content ul,.detail-content ol{padding-left:20px;margin-bottom:8px}.detail-content li{margin-bottom:4px}.detail-content blockquote{border-left:3px solid var(--accent-dim);padding-left:12px;color:var(--text-secondary);margin:8px 0}.detail-content code{font-family:var(--font-mono);font-size:12px;padding:1px 4px;background:var(--bg-tertiary);border-radius:3px}.detail-content pre{background:var(--bg-tertiary);padding:12px;border-radius:var(--radius);overflow-x:auto;margin:8px 0}.detail-content pre code{padding:0;background:none}.detail-panels{width:260px;border-left:1px solid var(--border-color);overflow-y:auto;padding:12px;background:var(--bg-secondary);flex-shrink:0}.detail-panels:empty{display:none}.panel{margin-bottom:16px}.panel h3{font-size:12px;font-weight:600;text-transform:uppercase;letter-spacing:.05em;color:var(--text-secondary);margin-bottom:8px}.person-header{padding:16px;border-bottom:1px solid var(--border-color)}.person-header h1{font-size:18px;font-weight:600}.person-header .person-stats{display:flex;gap:16px;margin-top:8px}.person-body{padding:16px;display:flex;flex-direction:column;gap:20px}.person-section h3{font-size:12px;font-weight:600;text-transform:uppercase;letter-spacing:.05em;color:var(--text-secondary);margin-bottom:8px}.topic-item{display:flex;justify-content:space-between;padding:6px 10px;border-radius:var(--radius);background:var(--bg-secondary);margin-bottom:4px}.topic-name{color:var(--text-primary)}.topic-count{font-family:var(--font-mono);font-size:11px;color:var(--text-muted)}.person-meeting{padding:6px 10px;border-radius:var(--radius);background:var(--bg-secondary);margin-bottom:4px;cursor:pointer;transition:background .15s}.person-meeting:hover{background:var(--bg-hover)}.person-meeting-date{font-size:11px;font-family:var(--font-mono);color:var(--text-muted)}.person-meeting-title{color:var(--text-primary)}.report-header{padding:16px;border-bottom:1px solid var(--border-color)}.report-header h1{font-size:18px;font-weight:600}.report-body{padding:16px;display:flex;flex-direction:column;gap:20px}.report-section h3{font-size:12px;font-weight:600;text-transform:uppercase;letter-spacing:.05em;color:var(--text-secondary);margin-bottom:8px}.report-empty{padding:24px;text-align:center;color:var(--text-muted)}#recording-banner{display:flex;align-items:center;gap:10px;padding:8px 16px;background:color-mix(in srgb,var(--action-overdue) 12%,var(--bg-secondary));border-bottom:1px solid color-mix(in srgb,var(--action-overdue) 30%,var(--border-color))}.rec-dot{width:10px;height:10px;border-radius:50%;background:var(--action-overdue);animation:pulse 1.5s ease-in-out infinite}@keyframes pulse{0%,to{opacity:1}50%{opacity:.3}}#rec-label{font-weight:600;font-size:12px;color:var(--action-overdue);text-transform:uppercase;letter-spacing:.05em}.rec-elapsed{font-family:var(--font-mono);font-size:13px;color:var(--text-primary)}.rec-stop-btn{margin-left:auto;padding:4px 12px;font-size:11px;font-weight:600;font-family:inherit;color:#fff;background:var(--action-overdue);border:none;border-radius:var(--radius);cursor:pointer;text-transform:uppercase;letter-spacing:.04em;transition:opacity .15s}.rec-stop-btn:hover{opacity:.85}.filter-bar{display:flex;align-items:center;gap:8px;padding:8px 12px;border-bottom:1px solid var(--border-color)}#filter-input{flex:1;padding:5px 10px;font-size:12px;font-family:inherit;background:var(--bg-tertiary);color:var(--text-primary);border:1px solid var(--border-color);border-radius:var(--radius);outline:none;transition:border-color .15s}#filter-input:focus{border-color:var(--accent)}#filter-input::placeholder{color:var(--text-muted)}.filter-toggles{display:flex;gap:2px}.filter-btn{padding:4px 10px;font-size:11px;font-family:inherit;color:var(--text-secondary);background:none;border:1px solid var(--border-color);cursor:pointer;transition:all .15s}.filter-btn:first-child{border-radius:var(--radius) 0 0 var(--radius)}.filter-btn:last-child{border-radius:0 var(--radius) var(--radius) 0}.filter-btn:not(:first-child){border-left:none}.filter-btn.active{background:var(--accent);color:#fff;border-color:var(--accent)}.filter-btn.active+.filter-btn{border-left-color:var(--accent)}.detail-toolbar{display:flex;align-items:center;justify-content:space-between;padding:8px 16px;border-bottom:1px solid var(--border-color);background:var(--bg-secondary)}.detail-toolbar .back-btn{margin:0}.detail-toolbar-actions{display:flex;gap:6px}.toolbar-btn{padding:5px 12px;font-size:11px;font-family:inherit;color:var(--text-secondary);background:var(--bg-tertiary);border:1px solid var(--border-color);border-radius:var(--radius);cursor:pointer;transition:all .15s}.toolbar-btn:hover{background:var(--bg-hover);color:var(--text-primary)}#context-btn{color:var(--accent);border-color:var(--accent-dim)}#context-btn:hover{background:color-mix(in srgb,var(--accent) 15%,var(--bg-tertiary))}.attendee.clickable{cursor:pointer;transition:background .15s,color .15s}.attendee.clickable:hover{background:var(--accent);color:#fff}@media(max-width:600px){.dashboard-body{flex-direction:column}.actions-sidebar{width:100%;border-left:none;border-top:1px solid var(--border-color);max-height:200px}.detail-body{flex-direction:column}.detail-panels{width:100%;border-left:none;border-top:1px solid var(--border-color)}}.people-header{padding:16px 20px 8px}.people-header h2{margin:0 0 8px;font-size:18px;font-weight:600}.people-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(220px,1fr));gap:12px;padding:8px 20px 20px}.person-card{border:1px solid var(--border-color);border-radius:8px;padding:12px;cursor:pointer;transition:border-color .15s,box-shadow .15s}.person-card:hover{border-color:var(--accent-color, #4a9eff);box-shadow:0 2px 8px #00000014}.person-card.losing-touch{border-left:3px solid #f59e0b}.person-card.has-commitments{border-left:3px solid #6366f1}.person-card-name{font-weight:600;font-size:14px;margin-bottom:4px}.person-card-meta{display:flex;gap:8px;font-size:12px;color:var(--muted-color, #888);margin-bottom:6px}.person-card-topics{font-size:11px;color:var(--muted-color, #888);margin-bottom:6px;font-style:italic}.person-card-status{font-size:12px;font-weight:500}.person-card-status.losing-touch{color:#f59e0b}.person-card-status.has-commitments{color:#6366f1}.person-card-status.clear{color:#22c55e}.person-card-score{font-size:11px;color:var(--muted-color, #888);margin-top:4px}.stat-warn .stat-value{color:#f59e0b}</style>
184
+ <style rel="stylesheet" crossorigin>*,*:before,*:after{box-sizing:border-box;margin:0;padding:0}:root{--bg-primary: #1a1a1a;--bg-secondary: #242424;--bg-tertiary: #2e2e2e;--bg-hover: #363636;--text-primary: #e4e4e4;--text-secondary: #a0a0a0;--text-muted: #6b6b6b;--border-color: #333;--accent: #4da8d9;--accent-dim: #3890b8;--badge-meeting: #4da8d9;--badge-memo: #9b85c4;--action-color: #d4a03c;--action-overdue: #e5534b;--decision-color: #46c072;--conflict-color: #e5534b;--stale-color: #d4a03c;--commit-color: #8b7cf6;--clear-color: #46c072;--radius: 6px;--radius-lg: 10px;--font-sans: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;--font-mono: "SF Mono", "Fira Code", "Cascadia Code", monospace}[data-theme=light]{--bg-primary: #ffffff;--bg-secondary: #f5f5f5;--bg-tertiary: #ececec;--bg-hover: #e2e2e2;--text-primary: #1a1a1a;--text-secondary: #555;--text-muted: #999;--border-color: #ddd;--accent: #3a8bb8;--accent-dim: #2f7da8;--badge-meeting: #3a8bb8;--badge-memo: #7e6ba8;--action-color: #b88a28;--action-overdue: #d04440;--decision-color: #2ea35c;--conflict-color: #d04440;--stale-color: #b88a28;--commit-color: #5f4ec0;--clear-color: #2ea35c}html,body{height:100%;font-family:var(--font-sans);font-size:13px;line-height:1.5;color:var(--text-primary);background:var(--bg-primary);-webkit-font-smoothing:antialiased}.main{min-height:100%;display:flex;flex-direction:column}#loading{display:flex;align-items:center;justify-content:center;min-height:120px;color:var(--text-secondary)}.loading-content{display:flex;flex-direction:column;align-items:center;gap:12px}.spinner{width:24px;height:24px;border:2px solid var(--border-color);border-top-color:var(--accent);border-radius:50%;animation:spin .8s linear infinite}@keyframes spin{to{transform:rotate(360deg)}}#error{display:flex;flex-direction:column;align-items:center;justify-content:center;min-height:120px;gap:8px;color:var(--conflict-color)}.error-icon{width:32px;height:32px;border-radius:50%;background:var(--conflict-color);color:#fff;display:flex;align-items:center;justify-content:center;font-weight:700;font-size:18px}#error-message{color:var(--text-secondary);text-align:center;max-width:400px}.stats-bar{display:flex;gap:16px;padding:12px 16px;border-bottom:1px solid var(--border-color);background:var(--bg-secondary)}.stat{display:flex;align-items:baseline;gap:6px}.stat-value{font-size:20px;font-weight:600;font-variant-numeric:tabular-nums;color:var(--text-primary)}.stat-label{font-size:12px;color:var(--text-secondary);text-transform:uppercase;letter-spacing:.04em}.dashboard-body{display:flex;flex:1;overflow:hidden}.meetings-list{flex:1;overflow-y:auto;padding:8px;display:flex;flex-direction:column;gap:4px}.meeting-card{padding:10px 12px;border-radius:var(--radius);cursor:pointer;border:1px solid transparent;transition:background .15s,border-color .15s}.meeting-card:hover{background:var(--bg-hover);border-color:var(--border-color)}.meeting-date{font-size:11px;font-family:var(--font-mono);color:var(--text-muted);margin-bottom:2px}.meeting-title{font-weight:500;color:var(--text-primary);margin-bottom:4px}.meeting-meta{display:flex;align-items:center;gap:8px;font-size:11px;color:var(--text-secondary)}.meeting-snippet{font-size:12px;color:var(--text-muted);margin-top:4px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.badge{display:inline-block;padding:1px 6px;border-radius:3px;font-size:10px;font-weight:600;text-transform:uppercase;letter-spacing:.04em}.badge-meeting{background:color-mix(in srgb,var(--badge-meeting) 20%,transparent);color:var(--badge-meeting)}.badge-memo{background:color-mix(in srgb,var(--badge-memo) 20%,transparent);color:var(--badge-memo)}.word-count,.duration{font-family:var(--font-mono);font-size:11px}.actions-sidebar{width:280px;border-left:1px solid var(--border-color);overflow-y:auto;padding:12px;background:var(--bg-secondary);flex-shrink:0}.actions-sidebar:empty{display:none}.sidebar-title{font-size:12px;font-weight:600;text-transform:uppercase;letter-spacing:.05em;color:var(--text-secondary);margin-bottom:10px}.intent-item{padding:8px 10px;border-radius:var(--radius);background:var(--bg-tertiary);margin-bottom:6px;border-left:3px solid var(--border-color)}.intent-item.action{border-left-color:var(--action-color)}.intent-item.action.overdue{border-left-color:var(--action-overdue)}.intent-item.decision{border-left-color:var(--decision-color)}.intent-item.conflict{border-left-color:var(--conflict-color)}.intent-item.stale{border-left-color:var(--stale-color)}.intent-what{font-size:13px;color:var(--text-primary);margin-bottom:4px}.intent-who{font-size:11px;color:var(--accent);font-weight:500}.intent-date{font-size:11px;font-family:var(--font-mono);color:var(--text-muted)}.intent-source{font-size:11px;color:var(--text-muted);margin-top:2px}.intent-reasons{font-size:11px;color:var(--stale-color);margin-top:2px}.back-btn{display:inline-flex;align-items:center;gap:4px;padding:6px 12px;margin:12px 16px 0;font-size:12px;color:var(--accent);background:none;border:1px solid var(--border-color);border-radius:var(--radius);cursor:pointer;font-family:inherit;transition:background .15s}.back-btn:hover{background:var(--bg-hover)}.detail-header{padding:16px;border-bottom:1px solid var(--border-color)}.detail-header h1{font-size:18px;font-weight:600;margin-bottom:8px}.detail-meta{display:flex;flex-wrap:wrap;align-items:center;gap:12px;margin-bottom:8px}.meta-item{font-size:12px;color:var(--text-secondary)}.attendees{font-size:12px;color:var(--text-secondary);margin-bottom:4px}.attendee{display:inline-block;padding:1px 6px;background:var(--bg-tertiary);border-radius:3px;margin:0 2px;color:var(--text-primary);font-weight:500}.tags{display:flex;gap:4px;flex-wrap:wrap}.tag{display:inline-block;padding:1px 8px;background:color-mix(in srgb,var(--accent) 15%,transparent);color:var(--accent);border-radius:10px;font-size:11px}.detail-body{display:flex;flex:1;overflow:hidden}.detail-content{flex:1;overflow-y:auto;padding:16px;line-height:1.7}.detail-content h2{font-size:15px;font-weight:600;margin:20px 0 8px;padding-bottom:4px;border-bottom:1px solid var(--border-color);color:var(--text-primary)}.detail-content h2:first-child{margin-top:0}.detail-content h3{font-size:14px;font-weight:600;margin:16px 0 6px;color:var(--text-primary)}.detail-content p{margin-bottom:8px;color:var(--text-primary)}.detail-content ul,.detail-content ol{padding-left:20px;margin-bottom:8px}.detail-content li{margin-bottom:4px}.detail-content blockquote{border-left:3px solid var(--accent-dim);padding-left:12px;color:var(--text-secondary);margin:8px 0}.detail-content code{font-family:var(--font-mono);font-size:12px;padding:1px 4px;background:var(--bg-tertiary);border-radius:3px}.detail-content pre{background:var(--bg-tertiary);padding:12px;border-radius:var(--radius);overflow-x:auto;margin:8px 0}.detail-content pre code{padding:0;background:none}.detail-panels{width:260px;border-left:1px solid var(--border-color);overflow-y:auto;padding:12px;background:var(--bg-secondary);flex-shrink:0}.detail-panels:empty{display:none}.panel{margin-bottom:16px}.panel h3{font-size:12px;font-weight:600;text-transform:uppercase;letter-spacing:.05em;color:var(--text-secondary);margin-bottom:8px}.person-header{padding:16px;border-bottom:1px solid var(--border-color)}.person-header h1{font-size:18px;font-weight:600}.person-header .person-stats{display:flex;gap:16px;margin-top:8px}.person-body{padding:16px;display:flex;flex-direction:column;gap:20px}.person-section h3{font-size:12px;font-weight:600;text-transform:uppercase;letter-spacing:.05em;color:var(--text-secondary);margin-bottom:8px}.topic-item{display:flex;justify-content:space-between;padding:6px 10px;border-radius:var(--radius);background:var(--bg-secondary);margin-bottom:4px}.topic-name{color:var(--text-primary)}.topic-count{font-family:var(--font-mono);font-size:11px;color:var(--text-muted)}.person-meeting{padding:6px 10px;border-radius:var(--radius);background:var(--bg-secondary);margin-bottom:4px;cursor:pointer;transition:background .15s}.person-meeting:hover{background:var(--bg-hover)}.person-meeting-date{font-size:11px;font-family:var(--font-mono);color:var(--text-muted)}.person-meeting-title{color:var(--text-primary)}.report-header{padding:16px;border-bottom:1px solid var(--border-color)}.report-header h1{font-size:18px;font-weight:600}.report-body{padding:16px;display:flex;flex-direction:column;gap:20px}.report-section h3{font-size:12px;font-weight:600;text-transform:uppercase;letter-spacing:.05em;color:var(--text-secondary);margin-bottom:8px}.report-empty{padding:24px;text-align:center;color:var(--text-muted)}#recording-banner{display:flex;align-items:center;gap:10px;padding:8px 16px;background:color-mix(in srgb,var(--action-overdue) 12%,var(--bg-secondary));border-bottom:1px solid color-mix(in srgb,var(--action-overdue) 30%,var(--border-color))}.rec-dot{width:10px;height:10px;border-radius:50%;background:var(--action-overdue);animation:pulse 1.5s ease-in-out infinite}@keyframes pulse{0%,to{opacity:1}50%{opacity:.3}}#rec-label{font-weight:600;font-size:12px;color:var(--action-overdue);text-transform:uppercase;letter-spacing:.05em}.rec-elapsed{font-family:var(--font-mono);font-size:13px;color:var(--text-primary)}.rec-stop-btn{margin-left:auto;padding:4px 12px;font-size:11px;font-weight:600;font-family:inherit;color:#fff;background:var(--action-overdue);border:none;border-radius:var(--radius);cursor:pointer;text-transform:uppercase;letter-spacing:.04em;transition:opacity .15s}.rec-stop-btn:hover{opacity:.85}.filter-bar{display:flex;align-items:center;gap:8px;padding:8px 12px;border-bottom:1px solid var(--border-color)}#filter-input{flex:1;padding:5px 10px;font-size:12px;font-family:inherit;background:var(--bg-tertiary);color:var(--text-primary);border:1px solid var(--border-color);border-radius:var(--radius);outline:none;transition:border-color .15s}#filter-input:focus{border-color:var(--accent)}#filter-input::placeholder{color:var(--text-muted)}.filter-toggles{display:flex;gap:2px}.filter-btn{padding:4px 10px;font-size:11px;font-family:inherit;color:var(--text-secondary);background:none;border:1px solid var(--border-color);cursor:pointer;transition:all .15s}.filter-btn:first-child{border-radius:var(--radius) 0 0 var(--radius)}.filter-btn:last-child{border-radius:0 var(--radius) var(--radius) 0}.filter-btn:not(:first-child){border-left:none}.filter-btn.active{background:var(--accent);color:#fff;border-color:var(--accent)}.filter-btn.active+.filter-btn{border-left-color:var(--accent)}.detail-toolbar{display:flex;align-items:center;justify-content:space-between;padding:8px 16px;border-bottom:1px solid var(--border-color);background:var(--bg-secondary)}.detail-toolbar .back-btn{margin:0}.detail-toolbar-actions{display:flex;gap:6px}.toolbar-btn{padding:5px 12px;font-size:11px;font-family:inherit;color:var(--text-secondary);background:var(--bg-tertiary);border:1px solid var(--border-color);border-radius:var(--radius);cursor:pointer;transition:all .15s}.toolbar-btn:hover{background:var(--bg-hover);color:var(--text-primary)}#context-btn{color:var(--accent);border-color:var(--accent-dim)}#context-btn:hover{background:color-mix(in srgb,var(--accent) 15%,var(--bg-tertiary))}.attendee.clickable{cursor:pointer;transition:background .15s,color .15s}.attendee.clickable:hover{background:var(--accent);color:#fff}@media(max-width:600px){.dashboard-body{flex-direction:column}.actions-sidebar{width:100%;border-left:none;border-top:1px solid var(--border-color);max-height:200px}.detail-body{flex-direction:column}.detail-panels{width:100%;border-left:none;border-top:1px solid var(--border-color)}}.people-header{padding:16px 20px 8px}.people-header h2{margin:0 0 8px;font-size:18px;font-weight:600}.people-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(220px,1fr));gap:12px;padding:8px 20px 20px}.person-card{border:1px solid var(--border-color);border-radius:8px;padding:12px;cursor:pointer;transition:border-color .15s,box-shadow .15s}.person-card:hover{border-color:var(--accent);box-shadow:0 2px 8px #00000014}.person-card.losing-touch{border-left:3px solid var(--stale-color)}.person-card.has-commitments{border-left:3px solid var(--commit-color)}.person-card-name{font-weight:600;font-size:14px;margin-bottom:4px}.person-card-meta{display:flex;gap:8px;font-size:12px;color:var(--text-muted);margin-bottom:6px}.person-card-topics{font-size:11px;color:var(--text-muted);margin-bottom:6px;font-style:italic}.person-card-status{font-size:12px;font-weight:500}.person-card-status.losing-touch{color:var(--stale-color)}.person-card-status.has-commitments{color:var(--commit-color)}.person-card-status.clear{color:var(--clear-color)}.person-card-score{font-size:11px;color:var(--text-muted);margin-top:4px}.stat-warn .stat-value{color:var(--stale-color)}</style>
185
185
  </head>
186
186
  <body>
187
187
  <div class="main">
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "minutes-mcp",
3
- "version": "0.9.1",
3
+ "version": "0.9.2",
4
4
  "description": "MCP server for minutes — 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",