ask-marcel-office-cli 2.4.0 → 2.5.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/index.js CHANGED
@@ -16471,7 +16471,10 @@ var changesIn = (root, container, propsTag, changeTag, scope) => {
16471
16471
  };
16472
16472
  var extractFormatChanges = (root) => [
16473
16473
  ...changesIn(root, "w:r", "w:rPr", "w:rPrChange", "run"),
16474
- ...changesIn(root, "w:p", "w:pPr", "w:pPrChange", "paragraph")
16474
+ ...changesIn(root, "w:p", "w:pPr", "w:pPrChange", "paragraph"),
16475
+ ...changesIn(root, "w:tbl", "w:tblPr", "w:tblPrChange", "table"),
16476
+ ...changesIn(root, "w:tr", "w:trPr", "w:trPrChange", "row"),
16477
+ ...changesIn(root, "w:tc", "w:tcPr", "w:tcPrChange", "cell")
16475
16478
  ];
16476
16479
  var extractDocxMetadata = async (bytes) => {
16477
16480
  const zipR = await openOoxmlZip(bytes);
@@ -16950,6 +16953,8 @@ var renderTable2 = (tableKids) => {
16950
16953
  `);
16951
16954
  };
16952
16955
  var renderBlock = (node, tag) => {
16956
+ if (tag === "text:tracked-changes")
16957
+ return [];
16953
16958
  const kids = kidsOf(node, tag);
16954
16959
  if (tag === "text:h")
16955
16960
  return [renderHeading(node, kids)];
@@ -17730,7 +17735,7 @@ var meta3 = {
17730
17735
  name: "include-metadata",
17731
17736
  key: "includeMetadata",
17732
17737
  required: false,
17733
- description: "Pass `--include-metadata true` to surface the side-channel content the rendered body hides. For docx (`## DOCX metadata`): core/app/custom document properties, people registry, external hyperlinks, comments, tracked changes (a deletion sitting next to an insertion by the same author is reported once as a `replacement` carrying `before` + `after`; the halves that pair with nothing stay under `insertions` / `deletions`; `moves` joins the two ends of a moved span by its range name; `formatChanges` names which run or paragraph properties a reviewer altered. Table-structure revisions — cell insert / delete / merge, numbering and section properties — are still unreported), hidden-formatted text (w:vanish), field instructions (MERGEFIELD / HYPERLINK / DOCVARIABLE), bookmarks. For xlsx (`## Workbook metadata`): core/app/custom properties, external relationships, defined names, hidden / very-hidden sheets, legacy cell comments, threaded comments, persons. For pptx (`## PPTX metadata`): properties, external relationships, slide tags, comment authors + comments (legacy + modern), and per-slide title / speaker notes / hidden flag — returned as a standalone document since pptx has no convertible body (use `download-drive-item-as-pdf` for slide visuals). For OpenDocument (`.odt`/`.ods`/`.odp`, `## OpenDocument metadata`): Dublin Core + ODF properties, keywords, user-defined custom fields — appended after the converted body. Each OOXML family also covers its macro-enabled (`.docm` / `.xlsm` / `.pptm`) and template (`.dotx` / `.xltx` / `.potx`, etc.) variants, with a `### Macros (VBA)` section flagging an embedded `vbaProject.bin`. No-op on other sources.",
17738
+ description: "Pass `--include-metadata true` to surface the side-channel content the rendered body hides. For docx (`## DOCX metadata`): core/app/custom document properties, people registry, external hyperlinks, comments, tracked changes (a deletion sitting next to an insertion by the same author is reported once as a `replacement` carrying `before` + `after`; the halves that pair with nothing stay under `insertions` / `deletions`; `moves` joins the two ends of a moved span by its range name; `formatChanges` names which run, paragraph, table, row or cell properties a reviewer altered, and a property whose values hang off child elements rather than its own attributes (`w:tblBorders` is the common one) is reported with its author and scope but names no property. Structural revisions — cell insert / delete / merge, numbering and section properties — are still unreported), hidden-formatted text (w:vanish), field instructions (MERGEFIELD / HYPERLINK / DOCVARIABLE), bookmarks. For xlsx (`## Workbook metadata`): core/app/custom properties, external relationships, defined names, hidden / very-hidden sheets, legacy cell comments, threaded comments, persons. For pptx (`## PPTX metadata`): properties, external relationships, slide tags, comment authors + comments (legacy + modern), and per-slide title / speaker notes / hidden flag — returned as a standalone document since pptx has no convertible body (use `download-drive-item-as-pdf` for slide visuals). For OpenDocument (`.odt`/`.ods`/`.odp`, `## OpenDocument metadata`): Dublin Core + ODF properties, keywords, user-defined custom fields — appended after the converted body. Each OOXML family also covers its macro-enabled (`.docm` / `.xlsm` / `.pptm`) and template (`.dotx` / `.xltx` / `.potx`, etc.) variants, with a `### Macros (VBA)` section flagging an embedded `vbaProject.bin`. No-op on other sources.",
17734
17739
  argumentHint: { kind: "magicValue", values: ["true", "false"] }
17735
17740
  },
17736
17741
  keepQuotedOption,
@@ -19392,9 +19397,12 @@ __export(exports_convert_mail_to_markdown, {
19392
19397
  attachmentsListSchema: () => attachmentsListSchema,
19393
19398
  execute: () => execute18,
19394
19399
  fetchInlineImageBytes: () => fetchInlineImageBytes,
19400
+ formatAddress: () => formatAddress2,
19395
19401
  formatBytes: () => formatBytes,
19396
19402
  isInlineImage: () => isInlineImage,
19397
19403
  meta: () => meta20,
19404
+ nonEmpty: () => nonEmpty,
19405
+ renderMessageAsMarkdown: () => renderMessageAsMarkdown,
19398
19406
  schema: () => schema18
19399
19407
  });
19400
19408
  var schema18 = exports_external.object({
@@ -19405,6 +19413,7 @@ var schema18 = exports_external.object({
19405
19413
  var INLINE_IMAGE_SIZE_LIMIT_BYTES = 2000000;
19406
19414
  var ATTACHMENT_METADATA_SELECT = "$select=id,name,contentType,size,isInline,microsoft.graph.fileAttachment/contentId";
19407
19415
  var nonEmpty = (v) => typeof v === "string" && v !== "";
19416
+ var referencesCidImage = (html) => /\bsrc\s*=\s*["']cid:/i.test(html);
19408
19417
  var formatAddress2 = (a) => {
19409
19418
  if (!nonEmpty(a?.address))
19410
19419
  return;
@@ -19453,12 +19462,12 @@ var formatBytes = (n) => {
19453
19462
  return `${(n / 1e6).toFixed(1)} MB`;
19454
19463
  };
19455
19464
  var isInlineImage = (a) => a.isInline === true && nonEmpty(a.contentType) && a.contentType.toLowerCase().startsWith("image/") && nonEmpty(a.contentId);
19456
- var fetchInlineImageBytes = async (graph, messageId, meta20) => {
19465
+ var fetchInlineImageBytes = async (graph, resourcePath, meta20) => {
19457
19466
  if ((meta20.size ?? 0) > INLINE_IMAGE_SIZE_LIMIT_BYTES)
19458
19467
  return { meta: meta20, oversize: true };
19459
19468
  if (!nonEmpty(meta20.id))
19460
19469
  return { meta: meta20, oversize: false };
19461
- const fetched = await graph.get(`/me/messages/${messageId}/attachments/${meta20.id}`);
19470
+ const fetched = await graph.get(`${resourcePath}/attachments/${meta20.id}`);
19462
19471
  if (!fetched.ok)
19463
19472
  return { meta: meta20, oversize: false };
19464
19473
  const body = fetched.value;
@@ -19480,7 +19489,7 @@ var renderOversizePlaceholders = (html, embeds) => {
19480
19489
  }
19481
19490
  return out;
19482
19491
  };
19483
- var renderFileAttachmentsList = (attachments, includeInlineImages = false) => {
19492
+ var renderFileAttachmentsList = (attachments, includeInlineImages, fetchHint) => {
19484
19493
  const fileAttachments = attachments.filter((a) => (includeInlineImages || !isInlineImage(a)) && nonEmpty(a.name));
19485
19494
  if (fileAttachments.length === 0)
19486
19495
  return "";
@@ -19490,7 +19499,7 @@ var renderFileAttachmentsList = (attachments, includeInlineImages = false) => {
19490
19499
  const id = nonEmpty(a.id) ? `, id: ${a.id}` : "";
19491
19500
  return `- ${a.name ?? ""}${size}${type}${id})`;
19492
19501
  });
19493
- return ["**Attachments:**", ...items, "_Use `convert-mail-attachment-to-pdf` or `get-mail-attachment` with the attachment id to fetch._"].join(`
19502
+ return ["**Attachments:**", ...items, fetchHint].join(`
19494
19503
  `);
19495
19504
  };
19496
19505
  var withoutTags = (body) => {
@@ -19504,37 +19513,32 @@ var removedSharePercent = (body, boundary) => {
19504
19513
  return 100;
19505
19514
  return Math.round((total - visibleLength(body.slice(0, boundary))) / total * 100);
19506
19515
  };
19507
- var execute18 = async (graph, params) => {
19508
- const parsed = schema18.safeParse(params);
19509
- if (!parsed.success)
19510
- return err({ type: "validation_error", message: formatZodError(parsed.error) });
19511
- const { messageId } = parsed.data;
19512
- const embedInlineImagesEnabled = parsed.data.inlineImages === "true";
19513
- const keepQuoted = parsed.data.keepQuoted === "true";
19514
- const fetched = await graph.get(`/me/messages/${messageId}`);
19516
+ var renderMessageAsMarkdown = async (graph, resourcePath, options) => {
19517
+ const { inlineImages: embedInlineImagesEnabled, keepQuoted } = options;
19518
+ const fetched = await graph.get(resourcePath);
19515
19519
  if (!fetched.ok)
19516
19520
  return fetched;
19517
19521
  const m = fetched.value;
19522
+ const rawHtml = m.body?.content ?? "";
19518
19523
  let attachments = [];
19519
19524
  let attachmentsListNote;
19520
- if (m.hasAttachments === true) {
19521
- const listed = await graph.get(`/me/messages/${messageId}/attachments?${ATTACHMENT_METADATA_SELECT}`);
19525
+ if (m.hasAttachments === true || referencesCidImage(rawHtml)) {
19526
+ const listed = await graph.get(`${resourcePath}/attachments?${ATTACHMENT_METADATA_SELECT}`);
19522
19527
  if (!listed.ok) {
19523
19528
  attachmentsListNote = `attachments-list fetch failed (${listed.error.type}: ${listed.error.message}) — markdown body returned without attachment metadata`;
19524
19529
  } else {
19525
- const parsed2 = attachmentsListSchema.safeParse(listed.value);
19526
- if (parsed2.success) {
19527
- attachments = parsed2.data.value ?? [];
19530
+ const parsed = attachmentsListSchema.safeParse(listed.value);
19531
+ if (parsed.success) {
19532
+ attachments = parsed.data.value ?? [];
19528
19533
  } else {
19529
- attachmentsListNote = `attachments-list returned a malformed shape (${formatZodError(parsed2.error)}) — markdown body returned without attachment metadata`;
19534
+ attachmentsListNote = `attachments-list returned a malformed shape (${formatZodError(parsed.error)}) — markdown body returned without attachment metadata`;
19530
19535
  }
19531
19536
  }
19532
19537
  }
19533
19538
  const inlineImageCandidates = embedInlineImagesEnabled ? attachments.filter(isInlineImage) : [];
19534
- const embedResults = await Promise.all(inlineImageCandidates.map((meta20) => fetchInlineImageBytes(graph, messageId, meta20)));
19539
+ const embedResults = await Promise.all(inlineImageCandidates.map((meta20) => fetchInlineImageBytes(graph, resourcePath, meta20)));
19535
19540
  const inlineImages = embedResults.flatMap((r) => r.inline ? [r.inline] : []);
19536
- const headers = renderHeaders(m);
19537
- const rawHtml = m.body?.content ?? "";
19541
+ const headers = options.renderHeaders(m);
19538
19542
  const withPlaceholders = renderOversizePlaceholders(rawHtml, embedResults);
19539
19543
  const embedded = inlineImages.length > 0 ? embedInlineImages(withPlaceholders, inlineImages) : withPlaceholders;
19540
19544
  const labelByContentId = new Map;
@@ -19560,7 +19564,7 @@ var execute18 = async (graph, params) => {
19560
19564
  quotedBoundary = findPlainTextQuoteBoundary(inlined);
19561
19565
  bodyMd = stripped.text;
19562
19566
  }
19563
- const fileList = renderFileAttachmentsList(attachments, !embedInlineImagesEnabled);
19567
+ const fileList = renderFileAttachmentsList(attachments, !embedInlineImagesEnabled, options.attachmentHint);
19564
19568
  const text = [headers, bodyMd, fileList].filter((s) => s !== "").join(`
19565
19569
 
19566
19570
  `);
@@ -19578,8 +19582,20 @@ var execute18 = async (graph, params) => {
19578
19582
  envelope.note = notes.join("; ");
19579
19583
  return ok(envelope);
19580
19584
  };
19585
+ var MAIL_ATTACHMENT_HINT = "_Use `convert-mail-attachment-to-pdf` or `get-mail-attachment` with the attachment id to fetch._";
19586
+ var execute18 = async (graph, params) => {
19587
+ const parsed = schema18.safeParse(params);
19588
+ if (!parsed.success)
19589
+ return err({ type: "validation_error", message: formatZodError(parsed.error) });
19590
+ return renderMessageAsMarkdown(graph, `/me/messages/${parsed.data.messageId}`, {
19591
+ inlineImages: parsed.data.inlineImages === "true",
19592
+ keepQuoted: parsed.data.keepQuoted === "true",
19593
+ attachmentHint: MAIL_ATTACHMENT_HINT,
19594
+ renderHeaders
19595
+ });
19596
+ };
19581
19597
  var meta20 = {
19582
- summary: "Render a single Outlook email as markdown — headers (`**Subject:**`, `**From:**`, `**To:**`, `**Cc:**` only when present, `**Date:**`), followed by the body run through turndown. By default NO image bytes are fetched: every inline `cid:` image renders as a readable `[inline image: <name>]` placeholder and the images surface in the file-attachments list, so the output stays close to the text size (an email whose 6 KB body carried 30 KB of signature-image base64 now ships at ~6 KB). Pass `--inline-images true` to embed inline images (`isInline:true` + `image/*` content-type, size ≤ 2 MB) as base64 `data:` URIs for self-contained output (non-image inline attachments are never embedded; oversize inline images keep a placeholder note; a cid whose per-image fetch fails degrades to the placeholder too). File attachments are always listed below the body by name + size + id; their bytes are NOT fetched here — call `convert-mail-attachment-to-pdf` or `get-mail-attachment` with the id when you actually need them. Staged-fetch design: one call for the body, one for the attachments-metadata list (only if `hasAttachments:true`), and with `--inline-images true` one per small inline image — replaces the old `?$expand=attachments` which timed out / truncated on messages with multi-MB attachments.",
19598
+ summary: "Render a single Outlook email as markdown — headers (`**Subject:**`, `**From:**`, `**To:**`, `**Cc:**` only when present, `**Date:**`), followed by the body run through turndown. By default NO image bytes are fetched: every inline `cid:` image renders as a readable `[inline image: <name>]` placeholder and the images surface in the file-attachments list, so the output stays close to the text size (an email whose 6 KB body carried 30 KB of signature-image base64 now ships at ~6 KB). Pass `--inline-images true` to embed inline images (`isInline:true` + `image/*` content-type, size ≤ 2 MB) as base64 `data:` URIs for self-contained output (non-image inline attachments are never embedded; oversize inline images keep a placeholder note; a cid whose per-image fetch fails degrades to the placeholder too). File attachments are always listed below the body by name + size + id; their bytes are NOT fetched here — call `convert-mail-attachment-to-pdf` or `get-mail-attachment` with the id when you actually need them. Staged-fetch design: one call for the body, one for the attachments-metadata list (when `hasAttachments` is true or the body references a `cid:` image, since Graph reports false for inline-only mail), and with `--inline-images true` one per small inline image — replaces the old `?$expand=attachments` which timed out / truncated on messages with multi-MB attachments.",
19583
19599
  category: "mail",
19584
19600
  graphMethod: "GET",
19585
19601
  graphPathTemplate: "/me/messages/{message-id}",
@@ -19655,7 +19671,7 @@ var inlineSignatureImages = async (graph, messageId, block) => {
19655
19671
  if (!parsed.success)
19656
19672
  return { html: block, count: 0, note: "the inline-image list came back in an unreadable shape, so any cid: references are unresolved" };
19657
19673
  const referenced = (parsed.data.value ?? []).filter(isInlineImage).filter((a) => block.includes(`cid:${a.contentId}`));
19658
- const fetched = await Promise.all(referenced.map((meta21) => fetchInlineImageBytes(graph, messageId, meta21)));
19674
+ const fetched = await Promise.all(referenced.map((meta21) => fetchInlineImageBytes(graph, `/me/messages/${messageId}`, meta21)));
19659
19675
  const embeddable = fetched.flatMap((f) => f.inline === undefined ? [] : [f.inline]);
19660
19676
  const skipped = fetched.filter((f) => f.inline === undefined);
19661
19677
  const html = embeddable.length > 0 ? embedInlineImages(block, embeddable) : block;
@@ -19664,7 +19680,7 @@ var inlineSignatureImages = async (graph, messageId, block) => {
19664
19680
  const named = skipped.map((f) => `${f.meta.name ?? "image"} (${formatBytes(f.meta.size ?? 0)})`).join(", ");
19665
19681
  return { html, count: embeddable.length, note: `${skipped.length} inline image${skipped.length === 1 ? "" : "s"} left as a cid: reference: ${named}` };
19666
19682
  };
19667
- var referencesCidImage = (block) => /\bsrc\s*=\s*["']cid:/i.test(block);
19683
+ var referencesCidImage2 = (block) => /\bsrc\s*=\s*["']cid:/i.test(block);
19668
19684
  var noSignatureFound = (messageId, scanned) => {
19669
19685
  const plural = scanned === 1 ? "" : "s";
19670
19686
  const scanMessage = `No OWA signature block (\`<div id="Signature">\`) was found in the last ${scanned} sent message${plural}. Mail composed in Outlook desktop does not carry the marker, so a signature may exist without being findable this way - pass --message-id to pin a message you know was sent from Outlook on the web.`;
@@ -19714,7 +19730,7 @@ var execute19 = async (graph, params) => {
19714
19730
  const block = extractSignatureBlock(message.data.body.content);
19715
19731
  if (block === undefined)
19716
19732
  continue;
19717
- const inlined = referencesCidImage(block) ? await inlineSignatureImages(graph, id, block) : { html: block, count: 0 };
19733
+ const inlined = referencesCidImage2(block) ? await inlineSignatureImages(graph, id, block) : { html: block, count: 0 };
19718
19734
  return ok(buildEnvelope(id, block, message.data.sentDateTime, inlined));
19719
19735
  }
19720
19736
  return err(noSignatureFound(messageId, candidates.value.length));
@@ -22287,6 +22303,7 @@ var meta87 = {
22287
22303
  // src/use-cases/commands/convert-mail-attachment-to-markdown.ts
22288
22304
  var exports_convert_mail_attachment_to_markdown = {};
22289
22305
  __export(exports_convert_mail_attachment_to_markdown, {
22306
+ MAIL_HINTS: () => MAIL_HINTS,
22290
22307
  convertAttachmentToMarkdown: () => convertAttachmentToMarkdown,
22291
22308
  convertFetchedAttachment: () => convertFetchedAttachment,
22292
22309
  execute: () => execute86,
@@ -22421,7 +22438,7 @@ var MAIL_HINTS = {
22421
22438
  image: (ext) => `${ext} attachment is an image and cannot be converted to markdown. Use \`get-mail-attachment --message-id <id> --attachment-id <id>\` to fetch the bytes (returned base64-encoded) and feed them into a vision-capable model directly — that's the right shape for image content. (\`convert-mail-attachment-to-pdf\` is NOT a workaround: Graph's format=pdf rejects images with InputFormatNotSupported.)`,
22422
22439
  generic: (ext) => `${ext} attachment not supported by \`convert-mail-attachment-to-markdown\`. Use \`convert-mail-attachment-to-pdf\` — Graph \`?format=pdf\` accepts 38 input extensions.`
22423
22440
  };
22424
- var convertFileAttachment = (attachment, opts) => bytesToMarkdown(base64ToBytes(attachment.contentBytes ?? ""), attachment.name ?? "unnamed", opts, MAIL_HINTS);
22441
+ var convertFileAttachment = (attachment, opts, hints) => bytesToMarkdown(base64ToBytes(attachment.contentBytes ?? ""), attachment.name ?? "unnamed", opts, hints);
22425
22442
  var convertReferenceAttachment = async (graph, attachment, opts) => {
22426
22443
  const sourceUrl = attachment.sourceUrl;
22427
22444
  if (typeof sourceUrl !== "string" || sourceUrl === "") {
@@ -22472,14 +22489,14 @@ var convertItemAttachment = (attachment) => {
22472
22489
  return err({ type: "api_error", status: 400, message: `unsupported embedded item type: ${innerType}` });
22473
22490
  }
22474
22491
  };
22475
- var convertFetchedAttachment = (graph, a, opts) => {
22492
+ var convertFetchedAttachment = (graph, a, opts, hints) => {
22476
22493
  const odataType = a["@odata.type"];
22477
22494
  if (typeof odataType !== "string") {
22478
22495
  return err({ type: "api_error", status: 400, message: "attachment response missing @odata.type discriminator" });
22479
22496
  }
22480
22497
  switch (odataType) {
22481
22498
  case "#microsoft.graph.fileAttachment":
22482
- return convertFileAttachment(a, opts);
22499
+ return convertFileAttachment(a, opts, hints);
22483
22500
  case "#microsoft.graph.referenceAttachment":
22484
22501
  return convertReferenceAttachment(graph, a, opts);
22485
22502
  case "#microsoft.graph.itemAttachment":
@@ -22488,21 +22505,18 @@ var convertFetchedAttachment = (graph, a, opts) => {
22488
22505
  return err({ type: "api_error", status: 400, message: `unsupported attachment type: ${odataType}` });
22489
22506
  }
22490
22507
  };
22491
- var convertAttachmentToMarkdown = async (graph, attachmentPath, opts) => {
22508
+ var convertAttachmentToMarkdown = async (graph, attachmentPath, opts, hints) => {
22492
22509
  const fetched = await graph.get(attachmentPath);
22493
22510
  if (!fetched.ok)
22494
22511
  return fetched;
22495
- return convertFetchedAttachment(graph, fetched.value, opts);
22512
+ return convertFetchedAttachment(graph, fetched.value, opts, hints);
22496
22513
  };
22497
22514
  var execute86 = async (graph, params) => {
22498
22515
  const parsed = schema86.safeParse(params);
22499
22516
  if (!parsed.success)
22500
22517
  return err({ type: "validation_error", message: formatZodError(parsed.error) });
22501
22518
  const { messageId, attachmentId } = parsed.data;
22502
- return convertAttachmentToMarkdown(graph, `/me/messages/${messageId}/attachments/${attachmentId}`, {
22503
- includeMetadata: parsed.data.includeMetadata === "true",
22504
- keepQuoted: parsed.data.keepQuoted === "true"
22505
- });
22519
+ return convertAttachmentToMarkdown(graph, `/me/messages/${messageId}/attachments/${attachmentId}`, { includeMetadata: parsed.data.includeMetadata === "true", keepQuoted: parsed.data.keepQuoted === "true" }, MAIL_HINTS);
22506
22520
  };
22507
22521
  var meta88 = {
22508
22522
  summary: "Convert an Outlook mail attachment to markdown. Polymorphic on the attachment’s `@odata.type`: fileAttachment decodes the inline bytes and runs them through the local conversion pipeline (docx via mammoth, xlsx via sheetjs, csv as markdown table, odt/ods/odp via content.xml, pptx as per-slide text (titles + bullets + speaker notes inline), pdf via text-layer extraction (unpdf → text/plain), legacy .xls via sheetjs and legacy .doc via word-extractor (text only), an Outlook .msg attachment rendered to markdown — headers + body (quoted reply chain stripped unless `--keep-quoted true`; inline `cid:` images shown as placeholders) with its own attachments converted recursively — plus plain-text passthrough); referenceAttachment resolves via /shares/{token}/driveItem and routes through the same dispatcher; itemAttachment (embedded mail / event / contact) is rendered locally via dedicated renderers. For pptx layout / images, `convert-mail-attachment-to-pdf` + a vision model reads the rendered deck better. A scanned / image-only PDF (no text layer), legacy .ppt, and rtf/etc. point to the PDF sibling. Loop/Fluid/Whiteboard reference-attachments use Graph `?format=html` (the four inputs Microsoft documents).",
@@ -22621,11 +22635,7 @@ var nameByContentType = (a) => {
22621
22635
  const ext = Object.hasOwn(CONTENT_TYPE_EXTENSIONS, contentType) ? CONTENT_TYPE_EXTENSIONS[contentType] : undefined;
22622
22636
  if (ext === undefined)
22623
22637
  return a;
22624
- const name = typeof a["name"] === "string" ? a["name"] : "";
22625
- const dot = name.lastIndexOf(".");
22626
- if ((dot === -1 ? "" : name.slice(dot + 1).toLowerCase()) === ext)
22627
- return a;
22628
- return { ...a, name: `${dot === -1 ? name || "attachment" : name.slice(0, dot)}.${ext}` };
22638
+ return { ...a, name: `attachment.${ext}` };
22629
22639
  };
22630
22640
  var execute87 = async (graph, params) => {
22631
22641
  const parsed = schema87.safeParse(params);
@@ -22645,7 +22655,7 @@ var execute87 = async (graph, params) => {
22645
22655
  }
22646
22656
  return convertZipArchive(base64ToBytes(contentBytes), { includeMetadata, keepQuoted });
22647
22657
  }
22648
- return convertFetchedAttachment(graph, nameByContentType(a), { includeMetadata, keepQuoted });
22658
+ return convertFetchedAttachment(graph, nameByContentType(a), { includeMetadata, keepQuoted }, MAIL_HINTS);
22649
22659
  };
22650
22660
  var meta89 = {
22651
22661
  summary: "Read an Outlook mail attachment whatever it is — one command that auto-routes by file type, preferring the content-type when the filename extension is misleading (a real `.jpg` that is actually a spreadsheet still converts), so a caller never has to choose between the convert-mail-attachment-* siblings. A `.zip` fileAttachment is unpacked and every entry converted (mirrors `convert-mail-attachment-zip-to-markdown`, returning the `{ count, files }` envelope; legacy GBK/CP437 names decoded). Any other attachment — docx/xlsx/pptx/odt/ods/odp + macro/template variants → markdown, csv → table, pdf → text layer (with `pageCount`), legacy .xls/.doc extracted, an inner Outlook .msg rendered recursively (quoted chain stripped unless `--keep-quoted true`), plain text passed through, referenceAttachment resolved via `/shares`, and itemAttachment (embedded mail/event/contact) rendered — goes through the same dispatch as `convert-mail-attachment-to-markdown` (returning its `{ contentType, size, text }` envelope). Images, scanned/image-only PDFs, and legacy .ppt return an actionable 415 pointing at `convert-mail-attachment-to-pdf` + a vision model or `get-mail-attachment` for the raw bytes. Pass `--include-metadata true` to append Office side-channel metadata. Use the explicit `convert-mail-attachment-to-markdown` / `-to-pdf` / `-zip` siblings only when you need to force a specific output format.",
@@ -22944,6 +22954,12 @@ __export(exports_convert_calendar_event_attachment_to_markdown, {
22944
22954
  meta: () => meta93,
22945
22955
  schema: () => schema91
22946
22956
  });
22957
+ var CALENDAR_HINTS = {
22958
+ pdfNoText: "pdf attachment has no extractable text layer — it looks scanned / image-only (only page images, no embedded text). Use `convert-calendar-event-attachment-to-pdf --output-path /tmp/file.pdf` to land the bytes on disk, then read the PDF with a vision-capable model, or run OCR.",
22959
+ legacyPpt: "ppt (legacy PowerPoint 97-2003, OLE binary) cannot be converted to markdown — there is no pure-JS parser for the format. Use `convert-calendar-event-attachment-to-pdf --output-path /tmp/file.pdf` to render it, then read the PDF with a vision-capable model.",
22960
+ image: (ext) => `${ext} attachment is an image and cannot be converted to markdown. Fetch the bytes with \`get-calendar-event --event-id <id> --expand attachments\`, which returns each attachment's base64 \`contentBytes\` inline, and feed them into a vision-capable model. (\`convert-calendar-event-attachment-to-pdf\` is NOT a workaround: Graph's format=pdf rejects images with InputFormatNotSupported.)`,
22961
+ generic: (ext) => `${ext} attachment not supported by \`convert-calendar-event-attachment-to-markdown\`. Use \`convert-calendar-event-attachment-to-pdf\` — Graph \`?format=pdf\` accepts 38 input extensions.`
22962
+ };
22947
22963
  var schema91 = exports_external.object({
22948
22964
  eventId: exports_external.string().min(1),
22949
22965
  attachmentId: exports_external.string().min(1),
@@ -22955,10 +22971,7 @@ var execute91 = async (graph, params) => {
22955
22971
  if (!parsed.success)
22956
22972
  return err({ type: "validation_error", message: formatZodError(parsed.error) });
22957
22973
  const { eventId, attachmentId } = parsed.data;
22958
- return convertAttachmentToMarkdown(graph, `/me/events/${eventId}/attachments/${attachmentId}`, {
22959
- includeMetadata: parsed.data.includeMetadata === "true",
22960
- keepQuoted: parsed.data.keepQuoted === "true"
22961
- });
22974
+ return convertAttachmentToMarkdown(graph, `/me/events/${eventId}/attachments/${attachmentId}`, { includeMetadata: parsed.data.includeMetadata === "true", keepQuoted: parsed.data.keepQuoted === "true" }, CALENDAR_HINTS);
22962
22975
  };
22963
22976
  var meta93 = {
22964
22977
  summary: "Convert an attachment on an Outlook calendar event to markdown. Polymorphic on the attachment’s `@odata.type` (shares the mail-attachment pipeline): fileAttachment decodes the inline bytes and runs them through the local conversion pipeline (docx via mammoth, xlsx via sheetjs, csv as markdown table, odt/ods/odp via content.xml, plus plain-text passthrough); referenceAttachment resolves via /shares/{token}/driveItem and routes through the same dispatcher; itemAttachment (embedded mail / event / contact) is rendered locally. For pptx decks attached to a meeting, `convert-calendar-event-attachment-to-pdf` preserves slide layout (a pptx here yields only its speaker notes / titles / comments via `## PPTX metadata` with `--include-metadata true`). For pdf/rtf/etc. also use the PDF sibling.",
@@ -26407,10 +26420,11 @@ __export(exports_list_group_conversations, {
26407
26420
  meta: () => meta164,
26408
26421
  schema: () => schema162
26409
26422
  });
26423
+ var HONOURED = ["top", "skip", "select", "orderby", "expand"];
26410
26424
  var baseSchema85 = exports_external.object({ groupId: exports_external.string().min(1) });
26411
- var { execute: execute162, schema: schema162 } = buildListCommand((p) => `/groups/${p.groupId}/conversations`, baseSchema85);
26425
+ var { execute: execute162, schema: schema162 } = buildPickODataListCommand((p) => `/groups/${p.groupId}/conversations`, baseSchema85, HONOURED);
26412
26426
  var meta164 = {
26413
- summary: "List conversations in a unified (Microsoft 365) group inbox. Each conversation aggregates one or more threads. Only Microsoft 365 groups have a mailbox — security and distribution groups return `MailboxNotEnabledForRESTAPI`. Verify the group is unified before calling.",
26427
+ summary: "List conversations in a unified (Microsoft 365) group inbox. Each conversation aggregates one or more threads. Only Microsoft 365 groups have a mailbox — security and distribution groups return `MailboxNotEnabledForRESTAPI`. Verify the group is unified before calling. Bodies live two levels down: `list-group-threads` then `list-group-thread-posts`, or `--expand 'threads($expand=posts)'` here to fetch conversations, threads and posts in one call.",
26414
26428
  category: "mail",
26415
26429
  graphMethod: "GET",
26416
26430
  graphPathTemplate: "/groups/{group-id}/conversations",
@@ -26422,7 +26436,7 @@ var meta164 = {
26422
26436
  required: true,
26423
26437
  description: "Azure AD group object ID for a unified (Microsoft 365) group."
26424
26438
  },
26425
- ...odataQueryOptions
26439
+ ...pickODataOptions(HONOURED)
26426
26440
  ],
26427
26441
  example: "ask-marcel-office list-group-conversations --group-id 'a1b2c3d4-...'",
26428
26442
  responseShape: "collection of Microsoft Graph `conversation` resources under `value[]`",
@@ -26436,10 +26450,11 @@ __export(exports_list_group_threads, {
26436
26450
  meta: () => meta165,
26437
26451
  schema: () => schema163
26438
26452
  });
26453
+ var HONOURED2 = ["top", "skip", "select", "orderby", "expand"];
26439
26454
  var baseSchema86 = exports_external.object({ groupId: exports_external.string().min(1) });
26440
- var { execute: execute163, schema: schema163 } = buildListCommand((p) => `/groups/${p.groupId}/threads`, baseSchema86);
26455
+ var { execute: execute163, schema: schema163 } = buildPickODataListCommand((p) => `/groups/${p.groupId}/threads`, baseSchema86, HONOURED2);
26441
26456
  var meta165 = {
26442
- summary: "List threads in a unified (Microsoft 365) group inbox. Threads are flatter than conversations — one per topic, useful when conversation-level grouping isn't needed. Only Microsoft 365 groups have a mailbox — security and distribution groups return `MailboxNotEnabledForRESTAPI`.",
26457
+ summary: "List threads in a unified (Microsoft 365) group inbox. Threads are flatter than conversations — one per topic, useful when conversation-level grouping isn't needed. Only Microsoft 365 groups have a mailbox — security and distribution groups return `MailboxNotEnabledForRESTAPI`. Each thread carries only a truncated `preview` of its latest post: read the full posts with `list-group-thread-posts`, or pass `--expand posts` here to inline them.",
26443
26458
  category: "mail",
26444
26459
  graphMethod: "GET",
26445
26460
  graphPathTemplate: "/groups/{group-id}/threads",
@@ -26451,28 +26466,363 @@ var meta165 = {
26451
26466
  required: true,
26452
26467
  description: "Azure AD group object ID for a unified (Microsoft 365) group."
26453
26468
  },
26454
- ...odataQueryOptions
26469
+ ...pickODataOptions(HONOURED2)
26455
26470
  ],
26456
26471
  example: "ask-marcel-office list-group-threads --group-id 'a1b2c3d4-...'",
26457
26472
  responseShape: "collection of Microsoft Graph `conversationThread` resources under `value[]`",
26458
26473
  pagination: true
26459
26474
  };
26460
26475
 
26461
- // src/use-cases/commands/get-mail-message-mime.ts
26462
- var exports_get_mail_message_mime = {};
26463
- __export(exports_get_mail_message_mime, {
26476
+ // src/use-cases/commands/list-group-thread-posts.ts
26477
+ var exports_list_group_thread_posts = {};
26478
+ __export(exports_list_group_thread_posts, {
26464
26479
  execute: () => execute164,
26465
26480
  meta: () => meta166,
26466
26481
  schema: () => schema164
26467
26482
  });
26468
- var schema164 = exports_external.object({ messageId: exports_external.string().min(1) });
26469
- var execute164 = async (graph, params) => {
26470
- const parsed = schema164.safeParse(params);
26483
+ var baseSchema87 = exports_external.object({ groupId: exports_external.string().min(1), threadId: exports_external.string().min(1) });
26484
+ var { execute: execute164, schema: schema164 } = buildPickODataListCommand((p) => `/groups/${p.groupId}/threads/${p.threadId}/posts`, baseSchema87, ["select", "expand"]);
26485
+ var meta166 = {
26486
+ summary: "List every post in one thread of a unified (Microsoft 365) group inbox: the full `post` resources with the HTML `body.content`, `from`, `sender`, `receivedDateTime` and `hasAttachments`, where `list-group-threads` stops at a truncated `preview`. Graph returns the whole thread in one call with no page cursor, and it silently ignores `$top`, `$skip` and `$orderby` while rejecting `$filter` (probed live 2026-09-03), so only `--select` and `--expand` are exposed; sort on `receivedDateTime` client-side if order matters. `sender` is the person who wrote the post and `from` is normally the group's own address. Render one post as markdown with `convert-group-post-to-markdown`. Access is membership-gated, not scope-gated: a group the signed-in user does not belong to answers `ErrorAccessDenied` even though `list-groups` lists it.",
26487
+ category: "mail",
26488
+ graphMethod: "GET",
26489
+ graphPathTemplate: "/groups/{group-id}/threads/{thread-id}/posts",
26490
+ graphDocsUrl: "https://learn.microsoft.com/en-us/graph/api/conversationthread-list-posts",
26491
+ options: [
26492
+ {
26493
+ name: "group-id",
26494
+ key: "groupId",
26495
+ required: true,
26496
+ description: "Azure AD group object ID for a unified (Microsoft 365) group the signed-in user belongs to."
26497
+ },
26498
+ {
26499
+ name: "thread-id",
26500
+ key: "threadId",
26501
+ required: true,
26502
+ description: "Conversation thread ID, the `id` of a `list-group-threads` entry (also inlined by `list-group-conversations --expand threads`)."
26503
+ },
26504
+ ...selectExpandOptions
26505
+ ],
26506
+ example: "ask-marcel-office list-group-thread-posts --group-id 'a1b2c3d4-...' --thread-id 'AAQkAD...'",
26507
+ responseShape: "collection of Microsoft Graph `post` resources under `value[]`: `id`, `createdDateTime`, `lastModifiedDateTime`, `changeKey`, `categories`, `receivedDateTime`, `hasAttachments`, `body { contentType, content }`, `from`, `sender`. No `nextLink` is ever emitted. `hasAttachments` is false for a post whose only attachments are inline images. `--expand attachments` inlines every attachment of every post with its base64 `contentBytes`, so use it sparingly."
26508
+ };
26509
+
26510
+ // src/use-cases/commands/get-group-post.ts
26511
+ var exports_get_group_post = {};
26512
+ __export(exports_get_group_post, {
26513
+ execute: () => execute165,
26514
+ meta: () => meta167,
26515
+ schema: () => schema165
26516
+ });
26517
+ var baseSchema88 = exports_external.object({ groupId: exports_external.string().min(1), threadId: exports_external.string().min(1), postId: exports_external.string().min(1) });
26518
+ var { execute: execute165, schema: schema165 } = buildSelectableCommand((p) => `/groups/${p.groupId}/threads/${p.threadId}/posts/${p.postId}`, baseSchema88);
26519
+ var meta167 = {
26520
+ summary: "Get a single post of a unified (Microsoft 365) group thread by ID, the sibling of `get-mail-message` for a group inbox: the full `post` resource including the HTML `body`. `--select` trims the projection. `--expand attachments` returns every attachment inline with its base64 `contentBytes`, which is convenient for a small post and the wrong shape for one carrying a multi-MB file: list them with `list-group-post-attachments` and fetch one with `get-group-post-attachment` instead. Post IDs come from `list-group-thread-posts`; use `convert-group-post-to-markdown` for a readable rendering.",
26521
+ category: "mail",
26522
+ graphMethod: "GET",
26523
+ graphPathTemplate: "/groups/{group-id}/threads/{thread-id}/posts/{post-id}",
26524
+ graphDocsUrl: "https://learn.microsoft.com/en-us/graph/api/post-get",
26525
+ options: [
26526
+ {
26527
+ name: "group-id",
26528
+ key: "groupId",
26529
+ required: true,
26530
+ description: "Azure AD group object ID for a unified (Microsoft 365) group the signed-in user belongs to."
26531
+ },
26532
+ {
26533
+ name: "thread-id",
26534
+ key: "threadId",
26535
+ required: true,
26536
+ description: "Conversation thread ID, the `id` of a `list-group-threads` entry."
26537
+ },
26538
+ {
26539
+ name: "post-id",
26540
+ key: "postId",
26541
+ required: true,
26542
+ description: "Post ID inside that thread. Returned by `list-group-thread-posts`."
26543
+ },
26544
+ ...selectExpandOptions
26545
+ ],
26546
+ example: "ask-marcel-office get-group-post --group-id 'a1b2c3d4-...' --thread-id 'AAQkAD...' --post-id 'AQMkAD...'",
26547
+ responseShape: "single Microsoft Graph `post` resource: `id`, `createdDateTime`, `lastModifiedDateTime`, `changeKey`, `categories`, `receivedDateTime`, `hasAttachments`, `body { contentType, content }`, `from` (normally the group address), `sender` (the person who wrote it). With `--expand attachments`, an `attachments[]` array of `fileAttachment` / `itemAttachment` / `referenceAttachment` entries, file attachments carrying `contentBytes` inline."
26548
+ };
26549
+
26550
+ // src/use-cases/commands/convert-group-post-to-markdown.ts
26551
+ var exports_convert_group_post_to_markdown = {};
26552
+ __export(exports_convert_group_post_to_markdown, {
26553
+ execute: () => execute166,
26554
+ meta: () => meta168,
26555
+ schema: () => schema166
26556
+ });
26557
+ var schema166 = exports_external.object({
26558
+ groupId: exports_external.string().min(1),
26559
+ threadId: exports_external.string().min(1),
26560
+ postId: exports_external.string().min(1),
26561
+ inlineImages: exports_external.enum(["true", "false"]).optional(),
26562
+ keepQuoted: exports_external.enum(["true", "false"]).optional()
26563
+ });
26564
+ var renderPostHeaders = (m) => {
26565
+ const lines = [];
26566
+ const from = formatAddress2(m.from?.emailAddress);
26567
+ const author = formatAddress2(m.sender?.emailAddress) ?? from;
26568
+ if (author !== undefined)
26569
+ lines.push(from === undefined || from === author ? `**From:** ${author}` : `**From:** ${author} on behalf of ${from}`);
26570
+ if (nonEmpty(m.receivedDateTime))
26571
+ lines.push(`**Date:** ${m.receivedDateTime}`);
26572
+ return lines.join(`
26573
+ `);
26574
+ };
26575
+ var POST_ATTACHMENT_HINT = "_Use `convert-group-post-attachment-to-markdown` or `get-group-post-attachment` with the attachment id to fetch._";
26576
+ var execute166 = async (graph, params) => {
26577
+ const parsed = schema166.safeParse(params);
26578
+ if (!parsed.success)
26579
+ return err({ type: "validation_error", message: formatZodError(parsed.error) });
26580
+ const { groupId, threadId, postId } = parsed.data;
26581
+ return renderMessageAsMarkdown(graph, `/groups/${groupId}/threads/${threadId}/posts/${postId}`, {
26582
+ inlineImages: parsed.data.inlineImages === "true",
26583
+ keepQuoted: parsed.data.keepQuoted === "true",
26584
+ attachmentHint: POST_ATTACHMENT_HINT,
26585
+ renderHeaders: renderPostHeaders
26586
+ });
26587
+ };
26588
+ var meta168 = {
26589
+ summary: "Render one post of a unified (Microsoft 365) group thread as markdown, the way `convert-mail-to-markdown` renders an Outlook message: a `**From:**` line, a `**Date:**` line, then the HTML body through turndown with quoted reply chains stripped. A post arrives from the group's own address with the writer in `sender`, so the author line reads `Robin Chen <robin.chen@contoso.com> on behalf of Support <support@contoso.com>`. There is no subject line: the thread `topic` is the subject and lives on `list-group-threads`. By default no image bytes are fetched; inline `cid:` images render as `[inline image: <name>]` placeholders unless `--inline-images true`. File attachments are listed below the body by name, size and id and their bytes are never fetched here; read one with `convert-group-post-attachment-to-markdown` or fetch it with `get-group-post-attachment`. Same staged-fetch design as the mail command: one call for the post, one for the attachment list when `hasAttachments` is true or the body references a `cid:` image (Graph reports false for a post whose only attachments are inline), and with `--inline-images true` one per small inline image.",
26590
+ category: "mail",
26591
+ graphMethod: "GET",
26592
+ graphPathTemplate: "/groups/{group-id}/threads/{thread-id}/posts/{post-id}",
26593
+ graphDocsUrl: "https://learn.microsoft.com/en-us/graph/api/post-get",
26594
+ options: [
26595
+ {
26596
+ name: "group-id",
26597
+ key: "groupId",
26598
+ required: true,
26599
+ description: "Azure AD group object ID for a unified (Microsoft 365) group the signed-in user belongs to."
26600
+ },
26601
+ {
26602
+ name: "thread-id",
26603
+ key: "threadId",
26604
+ required: true,
26605
+ description: "Conversation thread ID, the `id` of a `list-group-threads` entry."
26606
+ },
26607
+ {
26608
+ name: "post-id",
26609
+ key: "postId",
26610
+ required: true,
26611
+ description: "Post ID inside that thread. Returned by `list-group-thread-posts`."
26612
+ },
26613
+ {
26614
+ name: "inline-images",
26615
+ key: "inlineImages",
26616
+ required: false,
26617
+ description: "Pass `--inline-images true` to fetch small inline images (≤ 2 MB, `image/*` only) and embed them as base64 `data:` URIs. Default is `false`: no per-image bytes fetch, and every inline `cid:` image renders as a `[inline image: <name>]` placeholder while still appearing in the attachments list. Same rule as `convert-mail-to-markdown`.",
26618
+ argumentHint: { kind: "magicValue", values: ["true", "false"] }
26619
+ },
26620
+ {
26621
+ name: "keep-quoted",
26622
+ key: "keepQuoted",
26623
+ required: false,
26624
+ description: "Quoted reply chains and forwarded-message blocks are stripped by default and replaced with a single visible marker naming this flag; the `note` reports the share of the body text that went with them. Pass `--keep-quoted true` to preserve the full body. The markers recognised are the ones `convert-mail-to-markdown` documents.",
26625
+ argumentHint: { kind: "magicValue", values: ["true", "false"] }
26626
+ }
26627
+ ],
26628
+ example: "ask-marcel-office convert-group-post-to-markdown --group-id 'a1b2c3d4-...' --thread-id 'AAQkAD...' --post-id 'AQMkAD...'",
26629
+ responseShape: '`{ contentType: "text/markdown", size, text, note? }`, the same envelope as `convert-mail-to-markdown`: headers, the turndown-rendered body and, when present, an attachments list. The optional `note` carries the attachments-list failure hint and/or the quoted-chain notice with the share of the body text it removed.',
26630
+ producesBytes: true
26631
+ };
26632
+
26633
+ // src/use-cases/commands/list-group-post-attachments.ts
26634
+ var exports_list_group_post_attachments = {};
26635
+ __export(exports_list_group_post_attachments, {
26636
+ execute: () => execute167,
26637
+ meta: () => meta169,
26638
+ schema: () => schema167
26639
+ });
26640
+ var baseSchema89 = exports_external.object({ groupId: exports_external.string().min(1), threadId: exports_external.string().min(1), postId: exports_external.string().min(1) });
26641
+ var DEFAULT_SELECT7 = "id,name,contentType,size,isInline";
26642
+ var { execute: execute167, schema: schema167 } = buildPickODataListCommand((p) => `/groups/${p.groupId}/threads/${p.threadId}/posts/${p.postId}/attachments`, baseSchema89, ["select", "expand"], {
26643
+ defaultSelect: DEFAULT_SELECT7
26644
+ });
26645
+ var meta169 = {
26646
+ summary: "List the attachments (file, item, reference) on one post of a unified (Microsoft 365) group thread. Ships the slim default `--select=id,name,contentType,size,isInline` the mail and calendar siblings use, so a caller sees what is attached without pulling any bytes — the staged alternative to `get-group-post --expand attachments`, which inlines EVERY attachment at once and times out on a post carrying a multi-MB file. Graph returns the whole collection in one response and silently ignores `$top`, `$skip`, `$orderby` and `$filter` (probed live 2026-09-03), so only `--select` and `--expand` are exposed. A post whose only attachments are inline images reports `hasAttachments: false`, so call this whenever the body shows `cid:` references. Read one with `convert-group-post-attachment-to-markdown`, or fetch its bytes with `get-group-post-attachment`.",
26647
+ category: "mail",
26648
+ graphMethod: "GET",
26649
+ graphPathTemplate: "/groups/{group-id}/threads/{thread-id}/posts/{post-id}/attachments",
26650
+ graphDocsUrl: "https://learn.microsoft.com/en-us/graph/api/post-list-attachments",
26651
+ options: [
26652
+ {
26653
+ name: "group-id",
26654
+ key: "groupId",
26655
+ required: true,
26656
+ description: "Azure AD group object ID for a unified (Microsoft 365) group you belong to."
26657
+ },
26658
+ {
26659
+ name: "thread-id",
26660
+ key: "threadId",
26661
+ required: true,
26662
+ description: "Conversation thread ID, the `id` of a `list-group-threads` entry."
26663
+ },
26664
+ {
26665
+ name: "post-id",
26666
+ key: "postId",
26667
+ required: true,
26668
+ description: "Post ID inside that thread. Returned by `list-group-thread-posts`."
26669
+ },
26670
+ ...selectExpandOptions
26671
+ ],
26672
+ example: "ask-marcel-office list-group-post-attachments --group-id 'a1b2c3d4-...' --thread-id 'AAQkAD...' --post-id 'AQMkAD...'",
26673
+ responseShape: "collection of Microsoft Graph `attachment` resources under `value[]` (slim metadata by default — see summary), with no page cursor. Graph always includes `@odata.type` and `@odata.mediaContentType` on every entry regardless of `--select`; that discriminator is what the converting sibling branches on. An inline image carries `isInline: true` and a `contentId` matching a `cid:` reference in the post body."
26674
+ };
26675
+
26676
+ // src/use-cases/commands/get-group-post-attachment.ts
26677
+ var exports_get_group_post_attachment = {};
26678
+ __export(exports_get_group_post_attachment, {
26679
+ execute: () => execute168,
26680
+ meta: () => meta170,
26681
+ schema: () => schema168
26682
+ });
26683
+ var schema168 = exports_external.object({ groupId: exports_external.string().min(1), threadId: exports_external.string().min(1), postId: exports_external.string().min(1), attachmentId: exports_external.string().min(1) }).extend(selectExpandSchema.shape);
26684
+ var execute168 = async (graph, params) => {
26685
+ const parsed = schema168.safeParse(params);
26686
+ if (!parsed.success)
26687
+ return err({ type: "validation_error", message: formatZodError(parsed.error) });
26688
+ const { groupId, threadId, postId, attachmentId } = parsed.data;
26689
+ const path = appendOData(`/groups/${groupId}/threads/${threadId}/posts/${postId}/attachments/${attachmentId}`, parsed.data);
26690
+ const result = await graph.get(path);
26691
+ if (!result.ok)
26692
+ return result;
26693
+ const value = result.value;
26694
+ const contentBytes = value["contentBytes"];
26695
+ if (value["@odata.type"] === "#microsoft.graph.fileAttachment" && typeof contentBytes === "string") {
26696
+ return ok({ ...value, base64: contentBytes });
26697
+ }
26698
+ return ok(value);
26699
+ };
26700
+ var meta170 = {
26701
+ summary: "Get a single attachment on one post of a unified (Microsoft 365) group thread, the `get-mail-attachment` sibling for a group inbox. Prefer it over `get-group-post --expand attachments`, which expands every attachment at once. fileAttachments carry a `base64` mirror of `contentBytes` so the global output-path flag lands the bytes on disk in one call; with an output-path set both byte fields are stripped from stdout in favour of `savedTo`. Pass `--select id,name,contentType,size` for metadata only. This is also the route to an image attached to a post: fetch the bytes and feed them to a vision-capable model.",
26702
+ category: "mail",
26703
+ graphMethod: "GET",
26704
+ graphPathTemplate: "/groups/{group-id}/threads/{thread-id}/posts/{post-id}/attachments/{attachment-id}",
26705
+ graphDocsUrl: "https://learn.microsoft.com/en-us/graph/api/attachment-get",
26706
+ options: [
26707
+ {
26708
+ name: "group-id",
26709
+ key: "groupId",
26710
+ required: true,
26711
+ description: "Azure AD group object ID for a unified (Microsoft 365) group the signed-in user belongs to."
26712
+ },
26713
+ {
26714
+ name: "thread-id",
26715
+ key: "threadId",
26716
+ required: true,
26717
+ description: "Conversation thread ID, the `id` of a `list-group-threads` entry."
26718
+ },
26719
+ {
26720
+ name: "post-id",
26721
+ key: "postId",
26722
+ required: true,
26723
+ description: "Post ID inside that thread. Returned by `list-group-thread-posts`."
26724
+ },
26725
+ {
26726
+ name: "attachment-id",
26727
+ key: "attachmentId",
26728
+ required: true,
26729
+ description: "Attachment ID inside that post. Returned by `list-group-post-attachments`."
26730
+ },
26731
+ ...selectExpandOptions
26732
+ ],
26733
+ example: "ask-marcel-office get-group-post-attachment --group-id 'a1b2c3d4-...' --thread-id 'AAQkAD...' --post-id 'AQMkAD...' --attachment-id 'AAMkAD...'",
26734
+ responseShape: "single Microsoft Graph `attachment` resource. fileAttachments include `contentBytes` (Graph) AND `base64` (CLI mirror) so `--output-path` works; with `--output-path` set, both byte fields are stripped from stdout and replaced by `savedTo`. itemAttachments and referenceAttachments are returned unchanged.",
26735
+ producesBytes: true
26736
+ };
26737
+
26738
+ // src/use-cases/commands/convert-group-post-attachment-to-markdown.ts
26739
+ var exports_convert_group_post_attachment_to_markdown = {};
26740
+ __export(exports_convert_group_post_attachment_to_markdown, {
26741
+ execute: () => execute169,
26742
+ meta: () => meta171,
26743
+ schema: () => schema169
26744
+ });
26745
+ var POST_HINTS = {
26746
+ pdfNoText: "pdf attachment has no extractable text layer — it looks scanned / image-only (only page images, no embedded text). Fetch the bytes with `get-group-post-attachment --output-path /tmp/file.pdf`, then read the PDF with a vision-capable model, or run OCR.",
26747
+ legacyPpt: "ppt (legacy PowerPoint 97-2003, OLE binary) cannot be converted to markdown — there is no pure-JS parser for the format. Fetch the bytes with `get-group-post-attachment --output-path /tmp/deck.ppt` and convert them outside the CLI, or open the post in Outlook.",
26748
+ image: (ext) => `${ext} attachment is an image and cannot be converted to markdown. Use \`get-group-post-attachment\` to fetch the bytes (returned base64-encoded) and feed them into a vision-capable model directly — that's the right shape for image content.`,
26749
+ generic: (ext) => `${ext} attachment not supported by \`convert-group-post-attachment-to-markdown\`. Fetch the raw bytes with \`get-group-post-attachment\` (add \`--output-path\` to land them on disk) and handle the format outside the CLI.`
26750
+ };
26751
+ var schema169 = exports_external.object({
26752
+ groupId: exports_external.string().min(1),
26753
+ threadId: exports_external.string().min(1),
26754
+ postId: exports_external.string().min(1),
26755
+ attachmentId: exports_external.string().min(1),
26756
+ includeMetadata: exports_external.enum(["true", "false"]).optional(),
26757
+ keepQuoted: keepQuotedSchemaField
26758
+ });
26759
+ var execute169 = async (graph, params) => {
26760
+ const parsed = schema169.safeParse(params);
26761
+ if (!parsed.success)
26762
+ return err({ type: "validation_error", message: formatZodError(parsed.error) });
26763
+ const { groupId, threadId, postId, attachmentId } = parsed.data;
26764
+ return convertAttachmentToMarkdown(graph, `/groups/${groupId}/threads/${threadId}/posts/${postId}/attachments/${attachmentId}`, { includeMetadata: parsed.data.includeMetadata === "true", keepQuoted: parsed.data.keepQuoted === "true" }, POST_HINTS);
26765
+ };
26766
+ var meta171 = {
26767
+ summary: "Convert an attachment on one post of a unified (Microsoft 365) group thread to markdown, the `convert-mail-attachment-to-markdown` sibling for a group inbox. Polymorphic on the attachment’s `@odata.type` and sharing the mail pipeline: fileAttachment decodes the inline bytes and converts them locally (docx, xlsx, csv, odt/ods/odp, pptx as per-slide text, pdf text layer, legacy .xls/.doc, an Outlook `.msg` rendered recursively with its quoted chain stripped unless `--keep-quoted true`, plain text passed through); referenceAttachment resolves via `/shares/{token}/driveItem`; an embedded mail, event or contact is rendered locally. There is no PDF sibling here, so an image, a scanned PDF, a legacy `.ppt` and any other unsupported format return a 415 pointing at `get-group-post-attachment` for the raw bytes.",
26768
+ category: "mail",
26769
+ graphMethod: "GET",
26770
+ graphPathTemplate: "/groups/{group-id}/threads/{thread-id}/posts/{post-id}/attachments/{attachment-id}",
26771
+ graphDocsUrl: "https://learn.microsoft.com/en-us/graph/api/attachment-get",
26772
+ options: [
26773
+ {
26774
+ name: "group-id",
26775
+ key: "groupId",
26776
+ required: true,
26777
+ description: "Azure AD group object ID for a unified (Microsoft 365) group the signed-in user belongs to."
26778
+ },
26779
+ {
26780
+ name: "thread-id",
26781
+ key: "threadId",
26782
+ required: true,
26783
+ description: "Conversation thread ID, the `id` of a `list-group-threads` entry."
26784
+ },
26785
+ {
26786
+ name: "post-id",
26787
+ key: "postId",
26788
+ required: true,
26789
+ description: "Post ID inside that thread. Returned by `list-group-thread-posts`."
26790
+ },
26791
+ {
26792
+ name: "attachment-id",
26793
+ key: "attachmentId",
26794
+ required: true,
26795
+ description: "Attachment ID inside that post. Returned by `list-group-post-attachments`."
26796
+ },
26797
+ {
26798
+ name: "include-metadata",
26799
+ key: "includeMetadata",
26800
+ required: false,
26801
+ description: "Pass `--include-metadata true` to append the Office side-channel metadata block, exactly as `convert-mail-attachment-to-markdown` documents it. No-op on other attachment types.",
26802
+ argumentHint: { kind: "magicValue", values: ["true", "false"] }
26803
+ },
26804
+ keepQuotedOption
26805
+ ],
26806
+ example: "ask-marcel-office convert-group-post-attachment-to-markdown --group-id 'a1b2c3d4-...' --thread-id 'AAQkAD...' --post-id 'AQMkAD...' --attachment-id 'AAMkAD...'",
26807
+ responseShape: '`{ contentType: "text/markdown", size, text }` on success (file and reference attachments run through the conversion dispatch; an embedded item is rendered locally). Plain-text sources return the raw-bytes envelope, and a PDF source carries `pageCount`. Unsupported types return an api_error with status 415 naming `get-group-post-attachment` as the way to the bytes.',
26808
+ producesBytes: true
26809
+ };
26810
+
26811
+ // src/use-cases/commands/get-mail-message-mime.ts
26812
+ var exports_get_mail_message_mime = {};
26813
+ __export(exports_get_mail_message_mime, {
26814
+ execute: () => execute170,
26815
+ meta: () => meta172,
26816
+ schema: () => schema170
26817
+ });
26818
+ var schema170 = exports_external.object({ messageId: exports_external.string().min(1) });
26819
+ var execute170 = async (graph, params) => {
26820
+ const parsed = schema170.safeParse(params);
26471
26821
  if (!parsed.success)
26472
26822
  return err({ type: "validation_error", message: formatZodError(parsed.error) });
26473
26823
  return inlineBinary(graph, `/me/messages/${parsed.data.messageId}/$value`);
26474
26824
  };
26475
- var meta166 = {
26825
+ var meta172 = {
26476
26826
  summary: "Return the raw RFC 5322 MIME source of a single Outlook message — full headers, every attachment encoded inline. Useful for archiving, full-fidelity forensic inspection, or feeding into a tool that reads MIME directly. For human-readable content prefer `get-mail-message` or `convert-mail-to-markdown`.",
26477
26827
  category: "mail",
26478
26828
  graphMethod: "GET",
@@ -26494,13 +26844,13 @@ var meta166 = {
26494
26844
  // src/use-cases/commands/list-mail-folder-messages-delta.ts
26495
26845
  var exports_list_mail_folder_messages_delta = {};
26496
26846
  __export(exports_list_mail_folder_messages_delta, {
26497
- execute: () => execute165,
26498
- meta: () => meta167,
26499
- schema: () => schema165
26847
+ execute: () => execute171,
26848
+ meta: () => meta173,
26849
+ schema: () => schema171
26500
26850
  });
26501
- var schema165 = exports_external.object({ mailFolderId: exports_external.string().min(1) }).extend(pickODataShape(["top", "select", "filter", "expand"]));
26502
- var execute165 = async (graph, params) => {
26503
- const parsed = schema165.safeParse(params);
26851
+ var schema171 = exports_external.object({ mailFolderId: exports_external.string().min(1) }).extend(pickODataShape(["top", "select", "filter", "expand"]));
26852
+ var execute171 = async (graph, params) => {
26853
+ const parsed = schema171.safeParse(params);
26504
26854
  if (!parsed.success)
26505
26855
  return err({ type: "validation_error", message: formatZodError(parsed.error) });
26506
26856
  const { mailFolderId, top, ...odata } = parsed.data;
@@ -26509,7 +26859,7 @@ var execute165 = async (graph, params) => {
26509
26859
  headers["Prefer"] = `odata.maxpagesize=${top}`;
26510
26860
  return graph.get(appendOData(`/me/mailFolders/${mailFolderId}/messages/delta()`, odata), headers);
26511
26861
  };
26512
- var meta167 = {
26862
+ var meta173 = {
26513
26863
  summary: 'Track incremental changes (added / updated / deleted messages) within a single mail folder using Microsoft Graph delta tokens. The first call returns the current snapshot plus a `@odata.deltaLink`; subsequent calls with that link return only what has changed since. `--top` is translated into the `Prefer: odata.maxpagesize=N` header: as a `$top` query parameter Graph reads a satisfied count as "sync complete" and hands back a deltaLink after N items, silently abandoning the rest of the folder. `$skip` and `$orderby` are NOT exposed — Graph ignores the former on this endpoint and rejects the latter unless it merely restates the default `receivedDateTime desc`.',
26514
26864
  category: "mail",
26515
26865
  graphMethod: "GET",
@@ -26533,14 +26883,14 @@ var meta167 = {
26533
26883
  // src/use-cases/commands/list-shared-mailbox-messages.ts
26534
26884
  var exports_list_shared_mailbox_messages = {};
26535
26885
  __export(exports_list_shared_mailbox_messages, {
26536
- execute: () => execute166,
26537
- meta: () => meta168,
26538
- schema: () => schema166
26886
+ execute: () => execute172,
26887
+ meta: () => meta174,
26888
+ schema: () => schema172
26539
26889
  });
26540
- var baseSchema87 = exports_external.object({ userId: exports_external.string().min(1) });
26541
- var { execute: execute166, schema: schema166 } = buildListCommand((p) => `/users/${encodeURIComponent(p.userId)}/messages`, baseSchema87);
26542
- var meta168 = {
26543
- summary: "List messages from a shared or delegated mailbox the signed-in user has read access to. Same shape as `list-mail-messages` but scoped to a specific mailbox owner. 403 if the signed-in user does not have shared access to that mailbox.",
26890
+ var baseSchema90 = exports_external.object({ userId: exports_external.string().min(1) });
26891
+ var { execute: execute172, schema: schema172 } = buildListCommand((p) => `/users/${encodeURIComponent(p.userId)}/messages`, baseSchema90);
26892
+ var meta174 = {
26893
+ summary: "List messages from a shared or delegated mailbox the signed-in user has read access to. Same shape as `list-mail-messages` but scoped to a specific mailbox owner. Requires the delegated `Mail.Read.Shared` scope, which neither token this CLI can mint carries (verified live on two tenants, 2026-08-30), so any mailbox other than the signed-in user's own is expected to answer `ErrorAccessDenied` whatever delegation Exchange holds. Your own UPN works; a Microsoft 365 group's mailbox is the shared-mail path that does (`list-group-conversations`, `list-group-thread-posts`).",
26544
26894
  category: "mail",
26545
26895
  graphMethod: "GET",
26546
26896
  graphPathTemplate: "/users/{user-id}/messages",
@@ -26562,14 +26912,14 @@ var meta168 = {
26562
26912
  // src/use-cases/commands/list-shared-mailbox-folder-messages.ts
26563
26913
  var exports_list_shared_mailbox_folder_messages = {};
26564
26914
  __export(exports_list_shared_mailbox_folder_messages, {
26565
- execute: () => execute167,
26566
- meta: () => meta169,
26567
- schema: () => schema167
26915
+ execute: () => execute173,
26916
+ meta: () => meta175,
26917
+ schema: () => schema173
26568
26918
  });
26569
- var baseSchema88 = exports_external.object({ userId: exports_external.string().min(1), mailFolderId: exports_external.string().min(1) });
26570
- var { execute: execute167, schema: schema167 } = buildListCommand((p) => `/users/${encodeURIComponent(p.userId)}/mailFolders/${p.mailFolderId}/messages`, baseSchema88);
26571
- var meta169 = {
26572
- summary: "List messages in a single folder of a shared / delegated mailbox.",
26919
+ var baseSchema91 = exports_external.object({ userId: exports_external.string().min(1), mailFolderId: exports_external.string().min(1) });
26920
+ var { execute: execute173, schema: schema173 } = buildListCommand((p) => `/users/${encodeURIComponent(p.userId)}/mailFolders/${p.mailFolderId}/messages`, baseSchema91);
26921
+ var meta175 = {
26922
+ summary: "List messages in a single folder of a shared / delegated mailbox. Requires the delegated `Mail.Read.Shared` scope, which neither token this CLI can mint carries (verified live on two tenants, 2026-08-30), so any mailbox other than the signed-in user's own is expected to answer `ErrorAccessDenied` whatever delegation Exchange holds. Your own UPN works; a Microsoft 365 group's mailbox is the shared-mail path that does (`list-group-conversations`, `list-group-thread-posts`).",
26573
26923
  category: "mail",
26574
26924
  graphMethod: "GET",
26575
26925
  graphPathTemplate: "/users/{user-id}/mailFolders/{mail-folder-id}/messages",
@@ -26597,14 +26947,14 @@ var meta169 = {
26597
26947
  // src/use-cases/commands/list-shared-mailbox-folders.ts
26598
26948
  var exports_list_shared_mailbox_folders = {};
26599
26949
  __export(exports_list_shared_mailbox_folders, {
26600
- execute: () => execute168,
26601
- meta: () => meta170,
26602
- schema: () => schema168
26950
+ execute: () => execute174,
26951
+ meta: () => meta176,
26952
+ schema: () => schema174
26603
26953
  });
26604
- var baseSchema89 = exports_external.object({ userId: exports_external.string().min(1), includeHiddenFolders: exports_external.enum(["true", "false"]).optional() });
26605
- var { execute: execute168, schema: schema168 } = buildListCommand((p) => `/users/${encodeURIComponent(p.userId)}/mailFolders${p.includeHiddenFolders === "true" ? "?includeHiddenFolders=true" : ""}`, baseSchema89);
26606
- var meta170 = {
26607
- summary: "List the top-level mail folders of a shared or delegated mailbox. The `/me` sibling is `list-mail-folders`. Use it to discover the folder IDs that `list-shared-mailbox-folder-messages` needs: without it only the well-known names (`inbox`, `sentitems`, `drafts`, …) are reachable, so custom folders are invisible. 403 if the signed-in user does not have shared access to that mailbox.",
26954
+ var baseSchema92 = exports_external.object({ userId: exports_external.string().min(1), includeHiddenFolders: exports_external.enum(["true", "false"]).optional() });
26955
+ var { execute: execute174, schema: schema174 } = buildListCommand((p) => `/users/${encodeURIComponent(p.userId)}/mailFolders${p.includeHiddenFolders === "true" ? "?includeHiddenFolders=true" : ""}`, baseSchema92);
26956
+ var meta176 = {
26957
+ summary: "List the top-level mail folders of a shared or delegated mailbox. The `/me` sibling is `list-mail-folders`. Use it to discover the folder IDs that `list-shared-mailbox-folder-messages` needs: without it only the well-known names (`inbox`, `sentitems`, `drafts`, …) are reachable, so custom folders are invisible. Requires the delegated `Mail.Read.Shared` scope, which neither token this CLI can mint carries (verified live on two tenants, 2026-08-30), so any mailbox other than the signed-in user's own is expected to answer `ErrorAccessDenied` whatever delegation Exchange holds. Your own UPN works; a Microsoft 365 group's mailbox is the shared-mail path that does (`list-group-conversations`, `list-group-thread-posts`).",
26608
26958
  category: "mail",
26609
26959
  graphMethod: "GET",
26610
26960
  graphPathTemplate: "/users/{user-id}/mailFolders",
@@ -26627,14 +26977,14 @@ var meta170 = {
26627
26977
  // src/use-cases/commands/list-shared-mailbox-child-folders.ts
26628
26978
  var exports_list_shared_mailbox_child_folders = {};
26629
26979
  __export(exports_list_shared_mailbox_child_folders, {
26630
- execute: () => execute169,
26631
- meta: () => meta171,
26632
- schema: () => schema169
26980
+ execute: () => execute175,
26981
+ meta: () => meta177,
26982
+ schema: () => schema175
26633
26983
  });
26634
- var baseSchema90 = exports_external.object({ userId: exports_external.string().min(1), mailFolderId: exports_external.string().min(1), includeHiddenFolders: exports_external.enum(["true", "false"]).optional() });
26635
- var { execute: execute169, schema: schema169 } = buildListCommand((p) => `/users/${encodeURIComponent(p.userId)}/mailFolders/${p.mailFolderId}/childFolders${p.includeHiddenFolders === "true" ? "?includeHiddenFolders=true" : ""}`, baseSchema90);
26636
- var meta171 = {
26637
- summary: "List the subfolders of one mail folder in a shared or delegated mailbox. The `/me` sibling is `list-mail-child-folders`. Walk it from the folder IDs `list-shared-mailbox-folders` returns to reach nested custom folders. 403 if the signed-in user does not have shared access to that mailbox.",
26984
+ var baseSchema93 = exports_external.object({ userId: exports_external.string().min(1), mailFolderId: exports_external.string().min(1), includeHiddenFolders: exports_external.enum(["true", "false"]).optional() });
26985
+ var { execute: execute175, schema: schema175 } = buildListCommand((p) => `/users/${encodeURIComponent(p.userId)}/mailFolders/${p.mailFolderId}/childFolders${p.includeHiddenFolders === "true" ? "?includeHiddenFolders=true" : ""}`, baseSchema93);
26986
+ var meta177 = {
26987
+ summary: "List the subfolders of one mail folder in a shared or delegated mailbox. The `/me` sibling is `list-mail-child-folders`. Walk it from the folder IDs `list-shared-mailbox-folders` returns to reach nested custom folders. Requires the delegated `Mail.Read.Shared` scope, which neither token this CLI can mint carries (verified live on two tenants, 2026-08-30), so any mailbox other than the signed-in user's own is expected to answer `ErrorAccessDenied` whatever delegation Exchange holds. Your own UPN works; a Microsoft 365 group's mailbox is the shared-mail path that does (`list-group-conversations`, `list-group-thread-posts`).",
26638
26988
  category: "mail",
26639
26989
  graphMethod: "GET",
26640
26990
  graphPathTemplate: "/users/{user-id}/mailFolders/{mail-folder-id}/childFolders",
@@ -26663,14 +27013,14 @@ var meta171 = {
26663
27013
  // src/use-cases/commands/get-shared-mailbox-message.ts
26664
27014
  var exports_get_shared_mailbox_message = {};
26665
27015
  __export(exports_get_shared_mailbox_message, {
26666
- execute: () => execute170,
26667
- meta: () => meta172,
26668
- schema: () => schema170
27016
+ execute: () => execute176,
27017
+ meta: () => meta178,
27018
+ schema: () => schema176
26669
27019
  });
26670
- var baseSchema91 = exports_external.object({ userId: exports_external.string().min(1), messageId: exports_external.string().min(1) });
26671
- var { execute: execute170, schema: schema170 } = buildSelectableCommand((p) => `/users/${encodeURIComponent(p.userId)}/messages/${p.messageId}`, baseSchema91);
26672
- var meta172 = {
26673
- summary: "Return a single message from a shared / delegated mailbox. Use `--select` to fetch only specific fields (e.g. `--select id,subject,from,receivedDateTime`) — sibling to `get-mail-message` for /me.",
27020
+ var baseSchema94 = exports_external.object({ userId: exports_external.string().min(1), messageId: exports_external.string().min(1) });
27021
+ var { execute: execute176, schema: schema176 } = buildSelectableCommand((p) => `/users/${encodeURIComponent(p.userId)}/messages/${p.messageId}`, baseSchema94);
27022
+ var meta178 = {
27023
+ summary: "Return a single message from a shared / delegated mailbox. Use `--select` to fetch only specific fields (e.g. `--select id,subject,from,receivedDateTime`) — sibling to `get-mail-message` for /me. Requires the delegated `Mail.Read.Shared` scope, which neither token this CLI can mint carries (verified live on two tenants, 2026-08-30), so any mailbox other than the signed-in user's own is expected to answer `ErrorAccessDenied` whatever delegation Exchange holds. Your own UPN works; a Microsoft 365 group's mailbox is the shared-mail path that does (`list-group-conversations`, `list-group-thread-posts`).",
26674
27024
  category: "mail",
26675
27025
  graphMethod: "GET",
26676
27026
  graphPathTemplate: "/users/{user-id}/messages/{message-id}",
@@ -26697,21 +27047,21 @@ var meta172 = {
26697
27047
  // src/use-cases/commands/list-conversation-messages.ts
26698
27048
  var exports_list_conversation_messages = {};
26699
27049
  __export(exports_list_conversation_messages, {
26700
- execute: () => execute171,
26701
- meta: () => meta173,
26702
- schema: () => schema171
27050
+ execute: () => execute177,
27051
+ meta: () => meta179,
27052
+ schema: () => schema177
26703
27053
  });
26704
27054
  var allowedShape = Object.fromEntries(Object.entries(odataQuerySchema.shape).filter(([key]) => key !== "filter" && key !== "orderby"));
26705
27055
  var allowedOptions = odataQueryOptions.filter((o) => o.name !== "filter" && o.name !== "orderby");
26706
- var schema171 = exports_external.object({ conversationId: exports_external.string().min(1) }).extend(allowedShape);
26707
- var execute171 = async (graph, params) => {
26708
- const parsed = schema171.safeParse(params);
27056
+ var schema177 = exports_external.object({ conversationId: exports_external.string().min(1) }).extend(allowedShape);
27057
+ var execute177 = async (graph, params) => {
27058
+ const parsed = schema177.safeParse(params);
26709
27059
  if (!parsed.success)
26710
27060
  return err({ type: "validation_error", message: formatZodError(parsed.error) });
26711
27061
  const path = appendOData(`/me/messages?$filter=conversationId eq '${odataStringLiteral(parsed.data.conversationId)}'`, parsed.data);
26712
27062
  return graph.get(path);
26713
27063
  };
26714
- var meta173 = {
27064
+ var meta179 = {
26715
27065
  summary: "List every message in a single Outlook conversation (thread) using `$filter=conversationId eq '...'`. Reconstructs a complete thread regardless of which subject lines or folders the replies landed in. Accepts the OData passthrough flags top/skip/select/expand — the filter and orderby passthroughs are intentionally omitted (the path already pins a `$filter`, and Graph rejects this filter combined with `$orderby` as `InefficientFilter` since `conversationId` is not a sortable index). The caller can sort by `receivedDateTime` client-side. KQL `$search` does not index `conversationId`, so `$filter` is the only documented Graph idiom for whole-thread retrieval.",
26716
27066
  category: "mail",
26717
27067
  graphMethod: "GET",
@@ -26734,13 +27084,13 @@ var meta173 = {
26734
27084
  // src/use-cases/commands/list-focused-inbox-overrides.ts
26735
27085
  var exports_list_focused_inbox_overrides = {};
26736
27086
  __export(exports_list_focused_inbox_overrides, {
26737
- execute: () => execute172,
26738
- meta: () => meta174,
26739
- schema: () => schema172
27087
+ execute: () => execute178,
27088
+ meta: () => meta180,
27089
+ schema: () => schema178
26740
27090
  });
26741
- var baseSchema92 = exports_external.object({}).strict();
26742
- var { execute: execute172, schema: schema172 } = buildListCommand(() => "/me/inferenceClassification/overrides", baseSchema92);
26743
- var meta174 = {
27091
+ var baseSchema95 = exports_external.object({}).strict();
27092
+ var { execute: execute178, schema: schema178 } = buildListCommand(() => "/me/inferenceClassification/overrides", baseSchema95);
27093
+ var meta180 = {
26744
27094
  summary: "List the signed-in user's Focused Inbox classification overrides — sender addresses they've manually moved to Focused or Other, which override Microsoft's automatic classifier.",
26745
27095
  category: "mail",
26746
27096
  graphMethod: "GET",
@@ -26755,13 +27105,13 @@ var meta174 = {
26755
27105
  // src/use-cases/commands/list-outlook-categories.ts
26756
27106
  var exports_list_outlook_categories = {};
26757
27107
  __export(exports_list_outlook_categories, {
26758
- execute: () => execute173,
26759
- meta: () => meta175,
26760
- schema: () => schema173
27108
+ execute: () => execute179,
27109
+ meta: () => meta181,
27110
+ schema: () => schema179
26761
27111
  });
26762
- var schema173 = exports_external.object({}).strict();
26763
- var { execute: execute173 } = buildCommand(() => "/me/outlook/masterCategories", schema173);
26764
- var meta175 = {
27112
+ var schema179 = exports_external.object({}).strict();
27113
+ var { execute: execute179 } = buildCommand(() => "/me/outlook/masterCategories", schema179);
27114
+ var meta181 = {
26765
27115
  summary: "List the signed-in user's Outlook color categories — the named tags that can be applied to mail, calendar items, and contacts. Each entry has `displayName` and a `color` from Outlook's preset palette. Note: Graph silently ignores every OData passthrough on this endpoint (`$top`, `$skip`, `$select`, `$filter`, `$orderby`, `$expand`), so the CLI does not expose any of those flags — the full collection is always returned. Slice client-side.",
26766
27116
  category: "mail",
26767
27117
  graphMethod: "GET",
@@ -26775,13 +27125,13 @@ var meta175 = {
26775
27125
  // src/use-cases/commands/list-shared-calendar-events.ts
26776
27126
  var exports_list_shared_calendar_events = {};
26777
27127
  __export(exports_list_shared_calendar_events, {
26778
- execute: () => execute174,
26779
- meta: () => meta176,
26780
- schema: () => schema174
27128
+ execute: () => execute180,
27129
+ meta: () => meta182,
27130
+ schema: () => schema180
26781
27131
  });
26782
- var baseSchema93 = exports_external.object({ userId: exports_external.string().min(1) });
26783
- var { execute: execute174, schema: schema174 } = buildListCommand((p) => `/users/${encodeURIComponent(p.userId)}/calendar/events`, baseSchema93);
26784
- var meta176 = {
27132
+ var baseSchema96 = exports_external.object({ userId: exports_external.string().min(1) });
27133
+ var { execute: execute180, schema: schema180 } = buildListCommand((p) => `/users/${encodeURIComponent(p.userId)}/calendar/events`, baseSchema96);
27134
+ var meta182 = {
26785
27135
  summary: "List events from another user's primary calendar (shared / delegated access). 403 without `Calendars.Read.Shared`.",
26786
27136
  category: "calendar",
26787
27137
  graphMethod: "GET",
@@ -26804,13 +27154,13 @@ var meta176 = {
26804
27154
  // src/use-cases/commands/get-shared-calendar-view.ts
26805
27155
  var exports_get_shared_calendar_view = {};
26806
27156
  __export(exports_get_shared_calendar_view, {
26807
- execute: () => execute175,
26808
- meta: () => meta177,
26809
- schema: () => schema175
27157
+ execute: () => execute181,
27158
+ meta: () => meta183,
27159
+ schema: () => schema181
26810
27160
  });
26811
- var baseSchema94 = exports_external.object({ userId: exports_external.string().min(1), startDateTime: isoDateTimeField, endDateTime: isoDateTimeField });
26812
- var { execute: execute175, schema: schema175 } = buildListCommand((p) => `/users/${encodeURIComponent(p.userId)}/calendarView?startDateTime=${encodeURIComponent(p.startDateTime)}&endDateTime=${encodeURIComponent(p.endDateTime)}`, baseSchema94);
26813
- var meta177 = {
27161
+ var baseSchema97 = exports_external.object({ userId: exports_external.string().min(1), startDateTime: isoDateTimeField, endDateTime: isoDateTimeField });
27162
+ var { execute: execute181, schema: schema181 } = buildListCommand((p) => `/users/${encodeURIComponent(p.userId)}/calendarView?startDateTime=${encodeURIComponent(p.startDateTime)}&endDateTime=${encodeURIComponent(p.endDateTime)}`, baseSchema97);
27163
+ var meta183 = {
26814
27164
  summary: "Return a date-windowed calendar view from another user's primary calendar (shared / delegated access). Recurrences expanded into individual occurrences.",
26815
27165
  category: "calendar",
26816
27166
  graphMethod: "GET",
@@ -26835,13 +27185,13 @@ var meta177 = {
26835
27185
  // src/use-cases/commands/list-sharepoint-list-columns.ts
26836
27186
  var exports_list_sharepoint_list_columns = {};
26837
27187
  __export(exports_list_sharepoint_list_columns, {
26838
- execute: () => execute176,
26839
- meta: () => meta178,
26840
- schema: () => schema176
27188
+ execute: () => execute182,
27189
+ meta: () => meta184,
27190
+ schema: () => schema182
26841
27191
  });
26842
- var baseSchema95 = exports_external.object({ siteId: exports_external.string().min(1), listId: exports_external.string().min(1) });
26843
- var { execute: execute176, schema: schema176 } = buildSelectableCommand((p) => `/sites/${p.siteId}/lists/${p.listId}/columns`, baseSchema95);
26844
- var meta178 = {
27192
+ var baseSchema98 = exports_external.object({ siteId: exports_external.string().min(1), listId: exports_external.string().min(1) });
27193
+ var { execute: execute182, schema: schema182 } = buildSelectableCommand((p) => `/sites/${p.siteId}/lists/${p.listId}/columns`, baseSchema98);
27194
+ var meta184 = {
26845
27195
  summary: "List the column definitions (schema) of a SharePoint list. Useful before reading list items so you know which fields exist and their types. Note: Graph silently ignores `$top` and `$skip` on this endpoint, so the CLI exposes only `--select` and `--expand`.",
26846
27196
  category: "sharepoint",
26847
27197
  graphMethod: "GET",
@@ -26869,13 +27219,13 @@ var meta178 = {
26869
27219
  // src/use-cases/commands/get-sharepoint-list-column.ts
26870
27220
  var exports_get_sharepoint_list_column = {};
26871
27221
  __export(exports_get_sharepoint_list_column, {
26872
- execute: () => execute177,
26873
- meta: () => meta179,
26874
- schema: () => schema177
27222
+ execute: () => execute183,
27223
+ meta: () => meta185,
27224
+ schema: () => schema183
26875
27225
  });
26876
- var baseSchema96 = exports_external.object({ siteId: exports_external.string().min(1), listId: exports_external.string().min(1), columnId: exports_external.string().min(1) });
26877
- var { execute: execute177, schema: schema177 } = buildSelectableCommand((p) => `/sites/${p.siteId}/lists/${p.listId}/columns/${p.columnId}`, baseSchema96);
26878
- var meta179 = {
27226
+ var baseSchema99 = exports_external.object({ siteId: exports_external.string().min(1), listId: exports_external.string().min(1), columnId: exports_external.string().min(1) });
27227
+ var { execute: execute183, schema: schema183 } = buildSelectableCommand((p) => `/sites/${p.siteId}/lists/${p.listId}/columns/${p.columnId}`, baseSchema99);
27228
+ var meta185 = {
26879
27229
  summary: "Return a single column definition from a SharePoint list.",
26880
27230
  category: "sharepoint",
26881
27231
  graphMethod: "GET",
@@ -26910,9 +27260,9 @@ var meta179 = {
26910
27260
  // src/use-cases/commands/list-sharepoint-site-onenote-notebooks.ts
26911
27261
  var exports_list_sharepoint_site_onenote_notebooks = {};
26912
27262
  __export(exports_list_sharepoint_site_onenote_notebooks, {
26913
- execute: () => execute178,
26914
- meta: () => meta180,
26915
- schema: () => schema178
27263
+ execute: () => execute184,
27264
+ meta: () => meta186,
27265
+ schema: () => schema184
26916
27266
  });
26917
27267
 
26918
27268
  // src/use-cases/commands/onenote-5k-limit.ts
@@ -26933,11 +27283,11 @@ var wrapOnenote5kLimit = (inner14) => async (graph, params) => {
26933
27283
  };
26934
27284
 
26935
27285
  // src/use-cases/commands/list-sharepoint-site-onenote-notebooks.ts
26936
- var baseSchema97 = exports_external.object({ siteId: exports_external.string().min(1) });
26937
- var inner14 = buildListCommand((p) => `/sites/${p.siteId}/onenote/notebooks`, baseSchema97);
26938
- var execute178 = wrapOnenote5kLimit(inner14.execute);
26939
- var { schema: schema178 } = inner14;
26940
- var meta180 = {
27286
+ var baseSchema100 = exports_external.object({ siteId: exports_external.string().min(1) });
27287
+ var inner14 = buildListCommand((p) => `/sites/${p.siteId}/onenote/notebooks`, baseSchema100);
27288
+ var execute184 = wrapOnenote5kLimit(inner14.execute);
27289
+ var { schema: schema184 } = inner14;
27290
+ var meta186 = {
26941
27291
  summary: "List OneNote notebooks attached to a SharePoint site (separate from the personal `list-onenote-notebooks` which targets `/me`).",
26942
27292
  category: "notes",
26943
27293
  graphMethod: "GET",
@@ -26960,15 +27310,15 @@ var meta180 = {
26960
27310
  // src/use-cases/commands/list-sharepoint-site-onenote-notebook-sections.ts
26961
27311
  var exports_list_sharepoint_site_onenote_notebook_sections = {};
26962
27312
  __export(exports_list_sharepoint_site_onenote_notebook_sections, {
26963
- execute: () => execute179,
26964
- meta: () => meta181,
26965
- schema: () => schema179
27313
+ execute: () => execute185,
27314
+ meta: () => meta187,
27315
+ schema: () => schema185
26966
27316
  });
26967
- var baseSchema98 = exports_external.object({ siteId: exports_external.string().min(1), notebookId: exports_external.string().min(1) });
26968
- var inner15 = buildListCommand((p) => `/sites/${p.siteId}/onenote/notebooks/${p.notebookId}/sections`, baseSchema98);
26969
- var execute179 = wrapOnenote5kLimit(inner15.execute);
26970
- var { schema: schema179 } = inner15;
26971
- var meta181 = {
27317
+ var baseSchema101 = exports_external.object({ siteId: exports_external.string().min(1), notebookId: exports_external.string().min(1) });
27318
+ var inner15 = buildListCommand((p) => `/sites/${p.siteId}/onenote/notebooks/${p.notebookId}/sections`, baseSchema101);
27319
+ var execute185 = wrapOnenote5kLimit(inner15.execute);
27320
+ var { schema: schema185 } = inner15;
27321
+ var meta187 = {
26972
27322
  summary: "List sections inside one OneNote notebook attached to a SharePoint site.",
26973
27323
  category: "notes",
26974
27324
  graphMethod: "GET",
@@ -26997,15 +27347,15 @@ var meta181 = {
26997
27347
  // src/use-cases/commands/list-sharepoint-site-onenote-section-pages.ts
26998
27348
  var exports_list_sharepoint_site_onenote_section_pages = {};
26999
27349
  __export(exports_list_sharepoint_site_onenote_section_pages, {
27000
- execute: () => execute180,
27001
- meta: () => meta182,
27002
- schema: () => schema180
27350
+ execute: () => execute186,
27351
+ meta: () => meta188,
27352
+ schema: () => schema186
27003
27353
  });
27004
- var baseSchema99 = exports_external.object({ siteId: exports_external.string().min(1), onenoteSectionId: exports_external.string().min(1) });
27005
- var inner16 = buildListCommand((p) => `/sites/${p.siteId}/onenote/sections/${p.onenoteSectionId}/pages`, baseSchema99);
27006
- var execute180 = wrapOnenote5kLimit(inner16.execute);
27007
- var { schema: schema180 } = inner16;
27008
- var meta182 = {
27354
+ var baseSchema102 = exports_external.object({ siteId: exports_external.string().min(1), onenoteSectionId: exports_external.string().min(1) });
27355
+ var inner16 = buildListCommand((p) => `/sites/${p.siteId}/onenote/sections/${p.onenoteSectionId}/pages`, baseSchema102);
27356
+ var execute186 = wrapOnenote5kLimit(inner16.execute);
27357
+ var { schema: schema186 } = inner16;
27358
+ var meta188 = {
27009
27359
  summary: "List pages inside one section of a SharePoint-site OneNote notebook.",
27010
27360
  category: "notes",
27011
27361
  graphMethod: "GET",
@@ -27034,19 +27384,19 @@ var meta182 = {
27034
27384
  // src/use-cases/commands/get-sharepoint-site-onenote-page-content.ts
27035
27385
  var exports_get_sharepoint_site_onenote_page_content = {};
27036
27386
  __export(exports_get_sharepoint_site_onenote_page_content, {
27037
- execute: () => execute181,
27038
- meta: () => meta183,
27039
- schema: () => schema181
27387
+ execute: () => execute187,
27388
+ meta: () => meta189,
27389
+ schema: () => schema187
27040
27390
  });
27041
- var schema181 = exports_external.object({ siteId: exports_external.string().min(1), onenotePageId: exports_external.string().min(1) });
27391
+ var schema187 = exports_external.object({ siteId: exports_external.string().min(1), onenotePageId: exports_external.string().min(1) });
27042
27392
  var innerExecute = async (graph, params) => {
27043
- const parsed = schema181.safeParse(params);
27393
+ const parsed = schema187.safeParse(params);
27044
27394
  if (!parsed.success)
27045
27395
  return err({ type: "validation_error", message: formatZodError(parsed.error) });
27046
27396
  return graph.getBinary(`/sites/${parsed.data.siteId}/onenote/pages/${parsed.data.onenotePageId}/content`);
27047
27397
  };
27048
- var execute181 = wrapOnenote5kLimit(innerExecute);
27049
- var meta183 = {
27398
+ var execute187 = wrapOnenote5kLimit(innerExecute);
27399
+ var meta189 = {
27050
27400
  summary: "Return the HTML content of a single OneNote page from a SharePoint site (parallel to `get-onenote-page-content` for `/me`). The response carries the standard `{contentType: text/html, size, text}` shape so the HTML body is available verbatim under either output format.",
27051
27401
  category: "notes",
27052
27402
  graphMethod: "GET",
@@ -27074,13 +27424,13 @@ var meta183 = {
27074
27424
  // src/use-cases/commands/list-drive-item-thumbnails.ts
27075
27425
  var exports_list_drive_item_thumbnails = {};
27076
27426
  __export(exports_list_drive_item_thumbnails, {
27077
- execute: () => execute182,
27078
- meta: () => meta184,
27079
- schema: () => schema182
27427
+ execute: () => execute188,
27428
+ meta: () => meta190,
27429
+ schema: () => schema188
27080
27430
  });
27081
- var baseSchema100 = exports_external.object({ driveId: exports_external.string().min(1), itemId: exports_external.string().min(1), ...tenantIdShape });
27082
- var { execute: execute182, schema: schema182 } = buildNoSkipListCommand((p) => `/drives/${p.driveId}/items/${p.itemId}/thumbnails`, baseSchema100);
27083
- var meta184 = {
27431
+ var baseSchema103 = exports_external.object({ driveId: exports_external.string().min(1), itemId: exports_external.string().min(1), ...tenantIdShape });
27432
+ var { execute: execute188, schema: schema188 } = buildNoSkipListCommand((p) => `/drives/${p.driveId}/items/${p.itemId}/thumbnails`, baseSchema103);
27433
+ var meta190 = {
27084
27434
  summary: "List thumbnail URLs (small / medium / large) for a OneDrive / SharePoint file. Each thumbnail set has pre-signed CDN URLs you can render in a UI without further auth.",
27085
27435
  category: "drive",
27086
27436
  graphMethod: "GET",
@@ -27111,20 +27461,20 @@ var meta184 = {
27111
27461
  // src/use-cases/commands/get-excel-used-range.ts
27112
27462
  var exports_get_excel_used_range = {};
27113
27463
  __export(exports_get_excel_used_range, {
27114
- execute: () => execute183,
27115
- meta: () => meta185,
27116
- schema: () => schema183
27464
+ execute: () => execute189,
27465
+ meta: () => meta191,
27466
+ schema: () => schema189
27117
27467
  });
27118
27468
  var DEFAULT_MAX_CELLS2 = 50000;
27119
- var schema183 = exports_external.object({
27469
+ var schema189 = exports_external.object({
27120
27470
  driveId: exports_external.string().min(1),
27121
27471
  itemId: exports_external.string().min(1),
27122
27472
  worksheetId: exports_external.string().min(1),
27123
27473
  full: exports_external.enum(["true", "false"]).optional(),
27124
27474
  maxCells: exports_external.string().regex(/^[1-9]\d*$/, "must be a positive integer").optional()
27125
27475
  });
27126
- var execute183 = async (graph, params) => {
27127
- const parsed = schema183.safeParse(params);
27476
+ var execute189 = async (graph, params) => {
27477
+ const parsed = schema189.safeParse(params);
27128
27478
  if (!parsed.success)
27129
27479
  return err({ type: "validation_error", message: formatZodError(parsed.error) });
27130
27480
  const { driveId, itemId, worksheetId } = parsed.data;
@@ -27159,7 +27509,7 @@ var execute183 = async (graph, params) => {
27159
27509
  projection: "slim"
27160
27510
  });
27161
27511
  };
27162
- var meta185 = {
27512
+ var meta191 = {
27163
27513
  summary: 'Return the worksheet\'s used range — the bounding box of every non-empty cell — as a single Excel range. The CLI ships a slim default that strips the redundant `text` / `numberFormat` / `formulas` 2D arrays Graph returns (mostly `"General"` repeated cell-by-cell), keeping `address` / `rowCount` / `columnCount` / `values`. Pass `--full true` to return the raw four-array Graph shape. `--max-cells` (default 50 000) caps the size of the projected `values[]`; oversize ranges drop `values` and surface a hint pointing at `get-excel-range` for band-by-band reads. Avoids fetching the entire 1M × 16K-cell sheet when only a small data island is populated.',
27164
27514
  category: "excel",
27165
27515
  graphMethod: "GET",
@@ -27205,13 +27555,13 @@ var meta185 = {
27205
27555
  // src/use-cases/commands/list-rooms.ts
27206
27556
  var exports_list_rooms = {};
27207
27557
  __export(exports_list_rooms, {
27208
- execute: () => execute184,
27209
- meta: () => meta186,
27210
- schema: () => schema184
27558
+ execute: () => execute190,
27559
+ meta: () => meta192,
27560
+ schema: () => schema190
27211
27561
  });
27212
- var baseSchema101 = exports_external.object({}).strict();
27213
- var { execute: execute184, schema: schema184 } = buildListCommand(() => "/places/microsoft.graph.room", baseSchema101);
27214
- var meta186 = {
27562
+ var baseSchema104 = exports_external.object({}).strict();
27563
+ var { execute: execute190, schema: schema190 } = buildListCommand(() => "/places/microsoft.graph.room", baseSchema104);
27564
+ var meta192 = {
27215
27565
  summary: "List bookable meeting rooms in the tenant. Each `room` has `displayName`, `emailAddress`, `capacity`, `building`, `floorNumber`, and `isWheelChairAccessible`. Use the `emailAddress` as a meeting `attendee` for room booking. Pass `--top 5` to limit the response — large tenants return tens of KB by default.",
27216
27566
  category: "calendar",
27217
27567
  graphMethod: "GET",
@@ -27226,13 +27576,13 @@ var meta186 = {
27226
27576
  // src/use-cases/commands/list-room-lists.ts
27227
27577
  var exports_list_room_lists = {};
27228
27578
  __export(exports_list_room_lists, {
27229
- execute: () => execute185,
27230
- meta: () => meta187,
27231
- schema: () => schema185
27579
+ execute: () => execute191,
27580
+ meta: () => meta193,
27581
+ schema: () => schema191
27232
27582
  });
27233
- var baseSchema102 = exports_external.object({}).strict();
27234
- var { execute: execute185, schema: schema185 } = buildListCommand(() => "/places/microsoft.graph.roomList", baseSchema102);
27235
- var meta187 = {
27583
+ var baseSchema105 = exports_external.object({}).strict();
27584
+ var { execute: execute191, schema: schema191 } = buildListCommand(() => "/places/microsoft.graph.roomList", baseSchema105);
27585
+ var meta193 = {
27236
27586
  summary: "List room lists — usually one per building. Use these to scope a room search by location: a roomList groups the rooms in one office, then `/places/{roomList}/rooms` lists just those rooms. Pass `--top N` to limit the response on large tenants.",
27237
27587
  category: "calendar",
27238
27588
  graphMethod: "GET",
@@ -27247,13 +27597,13 @@ var meta187 = {
27247
27597
  // src/use-cases/commands/list-trending-insights.ts
27248
27598
  var exports_list_trending_insights = {};
27249
27599
  __export(exports_list_trending_insights, {
27250
- execute: () => execute186,
27251
- meta: () => meta188,
27252
- schema: () => schema186
27600
+ execute: () => execute192,
27601
+ meta: () => meta194,
27602
+ schema: () => schema192
27253
27603
  });
27254
- var baseSchema103 = exports_external.object({}).strict();
27255
- var { execute: execute186, schema: schema186 } = buildListCommand(() => "/me/insights/trending", baseSchema103);
27256
- var meta188 = {
27604
+ var baseSchema106 = exports_external.object({}).strict();
27605
+ var { execute: execute192, schema: schema192 } = buildListCommand(() => "/me/insights/trending", baseSchema106);
27606
+ var meta194 = {
27257
27607
  summary: "List documents trending around the signed-in user — files popular in their working network (colleagues' recent edits, shares, opens). Microsoft's relevance ranking, useful for surfacing unfamiliar but related work.",
27258
27608
  category: "drive",
27259
27609
  graphMethod: "GET",
@@ -27394,6 +27744,12 @@ var modules = {
27394
27744
  "list-group-calendar-view": exports_get_group_calendar_view,
27395
27745
  "list-group-conversations": exports_list_group_conversations,
27396
27746
  "list-group-threads": exports_list_group_threads,
27747
+ "list-group-thread-posts": exports_list_group_thread_posts,
27748
+ "get-group-post": exports_get_group_post,
27749
+ "convert-group-post-to-markdown": exports_convert_group_post_to_markdown,
27750
+ "list-group-post-attachments": exports_list_group_post_attachments,
27751
+ "get-group-post-attachment": exports_get_group_post_attachment,
27752
+ "convert-group-post-attachment-to-markdown": exports_convert_group_post_attachment_to_markdown,
27397
27753
  "get-mail-message-mime": exports_get_mail_message_mime,
27398
27754
  "list-mail-folder-messages-delta": exports_list_mail_folder_messages_delta,
27399
27755
  "list-shared-mailbox-messages": exports_list_shared_mailbox_messages,
@@ -27460,9 +27816,9 @@ import { join as join2 } from "node:path";
27460
27816
  var defaultCachePath = (home) => join2(home, ".ask-marcel", "token-cache.json");
27461
27817
  var commandNamesWhere = (matches) => Object.entries(commands).filter(([, cmd]) => matches(cmd.meta)).map(([name]) => name).toSorted((a, b) => a.localeCompare(b));
27462
27818
  var secondaryTokenCommands = {
27463
- elevated: commandNamesWhere((meta189) => meta189.needsElevatedToken === true),
27464
- chatsvcagg: commandNamesWhere((meta189) => meta189.needsSubstrateToken === "chatsvcagg"),
27465
- ic3: commandNamesWhere((meta189) => meta189.needsSubstrateToken === "ic3")
27819
+ elevated: commandNamesWhere((meta195) => meta195.needsElevatedToken === true),
27820
+ chatsvcagg: commandNamesWhere((meta195) => meta195.needsSubstrateToken === "chatsvcagg"),
27821
+ ic3: commandNamesWhere((meta195) => meta195.needsSubstrateToken === "ic3")
27466
27822
  };
27467
27823
  var defaultFileSystem3 = () => typeof globalThis.Bun !== "undefined" ? createBunFileSystem() : createNodeFileSystem();
27468
27824
  var defaultProcessRunner = () => typeof globalThis.Bun !== "undefined" ? createBunProcessRunner() : createNodeProcessRunner();