ask-marcel-office-cli 1.4.0 → 1.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/CHANGELOG.md +65 -0
- package/README.md +68 -24
- package/dist/cli.js +7631 -90932
- package/dist/commands.json +1230 -70
- package/dist/composition/build-deps.d.ts +13 -0
- package/dist/composition/cli.d.ts +8 -0
- package/dist/domain/iso-datetime.d.ts +1 -1
- package/dist/domain/utilities/archive-status.d.ts +18 -0
- package/dist/domain/utilities/site-url-classifier.d.ts +15 -0
- package/dist/index.js +10421 -93852
- package/dist/infra/auth.d.ts +26 -3
- package/dist/infra/browser-auth.d.ts +25 -0
- package/dist/infra/graph-client.d.ts +3 -1
- package/dist/infra/legacy-doc-extractor.d.ts +14 -0
- package/dist/infra/msg-reader-adapter.d.ts +83 -0
- package/dist/infra/network-error.d.ts +1 -1
- package/dist/infra/ooxml-media-extractor.d.ts +27 -0
- package/dist/infra/ooxml-zip-adapter.d.ts +28 -0
- package/dist/infra/pdf-image-extractor.d.ts +18 -0
- package/dist/infra/pdf-text-extractor.d.ts +41 -0
- package/dist/infra/png-encode.d.ts +15 -0
- package/dist/infra/sheetjs-adapter.d.ts +1 -1
- package/dist/infra/system-browser-auth.d.ts +35 -0
- package/dist/infra/system-browser-loader.d.ts +3 -0
- package/dist/infra/token-callback-server.d.ts +31 -0
- package/dist/infra/zip-reader.d.ts +32 -0
- package/dist/presenter/error-hints.d.ts +2 -2
- package/dist/presenter/output.d.ts +1 -1
- package/dist/use-cases/commands/build-command.d.ts +1 -1
- package/dist/use-cases/commands/command-types.d.ts +42 -1
- package/dist/use-cases/commands/convert-calendar-event-attachment-to-markdown.d.ts +15 -0
- package/dist/use-cases/commands/convert-calendar-event-attachment-to-pdf.d.ts +11 -0
- package/dist/use-cases/commands/convert-drive-item-zip.d.ts +26 -0
- package/dist/use-cases/commands/convert-local-file.d.ts +37 -0
- package/dist/use-cases/commands/convert-mail-attachment-to-markdown.d.ts +7 -1
- package/dist/use-cases/commands/convert-mail-attachment-to-pdf.d.ts +2 -1
- package/dist/use-cases/commands/convert-mail-attachment-zip.d.ts +24 -0
- package/dist/use-cases/commands/convert-mail-to-markdown.d.ts +4 -0
- package/dist/use-cases/commands/create-mail-draft.d.ts +22 -0
- package/dist/use-cases/commands/doc-to-markdown.d.ts +14 -0
- package/dist/use-cases/commands/docs-render.d.ts +3 -0
- package/dist/use-cases/commands/docs.d.ts +1 -1
- package/dist/use-cases/commands/docx-comment-anchors.d.ts +22 -0
- package/dist/use-cases/commands/docx-metadata-to-markdown.d.ts +3 -0
- package/dist/use-cases/commands/docx-metadata.d.ts +69 -0
- package/dist/use-cases/commands/docx-to-markdown.d.ts +8 -3
- package/dist/use-cases/commands/download-drive-item-as-markdown.d.ts +9 -0
- package/dist/use-cases/commands/download-drive-item-version.d.ts +4 -0
- package/dist/use-cases/commands/extract-drive-item-images.d.ts +11 -0
- package/dist/use-cases/commands/extract-local-file-images.d.ts +21 -0
- package/dist/use-cases/commands/extract-mail-attachment-images.d.ts +11 -0
- package/dist/use-cases/commands/extract-sharepoint-links-in-documents.d.ts +33 -0
- package/dist/use-cases/commands/extract-sharepoint-links-in-mail.d.ts +1 -8
- package/dist/use-cases/commands/fetch-raw-bytes.d.ts +1 -0
- package/dist/use-cases/commands/file-counts.d.ts +7 -0
- package/dist/use-cases/commands/filter-archived-sites.d.ts +17 -0
- package/dist/use-cases/commands/first-sentence.d.ts +12 -0
- package/dist/use-cases/commands/get-excel-chart-image.d.ts +20 -0
- package/dist/use-cases/commands/get-onenote-page-as-markdown.d.ts +8 -0
- package/dist/use-cases/commands/get-todo-task.d.ts +9 -2
- package/dist/use-cases/commands/image-extraction.d.ts +10 -0
- package/dist/use-cases/commands/iso-datetime-schema.d.ts +1 -1
- package/dist/use-cases/commands/list-accessible-drives.d.ts +14 -0
- package/dist/use-cases/commands/list-calendar-event-attachments.d.ts +14 -0
- package/dist/use-cases/commands/mail-quote-stripper.d.ts +25 -0
- package/dist/use-cases/commands/markdown-dispatch.d.ts +29 -0
- package/dist/use-cases/commands/markdown-pipeline.d.ts +6 -2
- package/dist/use-cases/commands/media-files.d.ts +20 -0
- package/dist/use-cases/commands/msg-to-markdown.d.ts +28 -0
- package/dist/use-cases/commands/odf-content-to-markdown.d.ts +5 -0
- package/dist/use-cases/commands/odf-metadata-to-markdown.d.ts +3 -0
- package/dist/use-cases/commands/odf-metadata.d.ts +26 -0
- package/dist/use-cases/commands/odf-to-markdown.d.ts +17 -0
- package/dist/use-cases/commands/office-extensions.d.ts +13 -0
- package/dist/use-cases/commands/office-to-markdown.d.ts +6 -2
- package/dist/use-cases/commands/onenote-metadata.d.ts +21 -0
- package/dist/use-cases/commands/onenote-resource-embedder.d.ts +3 -0
- package/dist/use-cases/commands/ooxml-metadata-to-markdown.d.ts +16 -0
- package/dist/use-cases/commands/ooxml-metadata.d.ts +23 -0
- package/dist/use-cases/commands/ooxml-xml-walker.d.ts +36 -0
- package/dist/use-cases/commands/option-descriptions.d.ts +5 -0
- package/dist/use-cases/commands/output-path.d.ts +23 -0
- package/dist/use-cases/commands/pdf-to-markdown.d.ts +19 -0
- package/dist/use-cases/commands/pptx-comments.d.ts +24 -0
- package/dist/use-cases/commands/pptx-metadata-to-markdown.d.ts +3 -0
- package/dist/use-cases/commands/pptx-metadata.d.ts +33 -0
- package/dist/use-cases/commands/pptx-slides.d.ts +17 -0
- package/dist/use-cases/commands/pptx-to-markdown.d.ts +7 -0
- package/dist/use-cases/commands/read-mail-attachment.d.ts +15 -0
- package/dist/use-cases/commands/search-all-accessible-sites.d.ts +12 -0
- package/dist/use-cases/commands/search-index-total.d.ts +3 -0
- package/dist/use-cases/commands/search-sharepoint-sites-by-name.d.ts +3 -2
- package/dist/use-cases/commands/sharepoint-link-extractor.d.ts +17 -12
- package/dist/use-cases/commands/text-passthrough.d.ts +15 -2
- package/dist/use-cases/commands/todo-parse-uri-rewrite.d.ts +7 -0
- package/dist/use-cases/commands/update-mail-draft.d.ts +22 -0
- package/dist/use-cases/commands/xlsx-comments.d.ts +29 -0
- package/dist/use-cases/commands/xlsx-metadata-to-markdown.d.ts +3 -0
- package/dist/use-cases/commands/xlsx-metadata.d.ts +39 -0
- package/dist/use-cases/commands/xlsx-to-markdown.d.ts +9 -2
- package/dist/use-cases/commands/zip-archive-to-markdown.d.ts +30 -0
- package/dist/use-cases/ports/filesystem.d.ts +10 -0
- package/docs/COMMANDS.md +34 -18
- package/docs/USAGE.md +25 -8
- package/docs/commands.json +1230 -70
- package/package.json +14 -8
- /package/dist/use-cases/commands/{download-onedrive-file-content.d.ts → download-drive-item-content.d.ts} +0 -0
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,71 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to `ask-marcel-office-cli` are documented here.
|
|
4
4
|
|
|
5
|
+
## 1.5.0
|
|
6
|
+
|
|
7
|
+
Agent-ergonomics, a faster cold-start, a new command, and an LLM-safety pass.
|
|
8
|
+
Mostly additive; the one rename keeps its old name as an alias. **One behaviour
|
|
9
|
+
change to note**: byte commands now refuse to inline a payload over ~1 MB without
|
|
10
|
+
`--output-path` (see _Changed_).
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
|
|
14
|
+
- **`read-mail-attachment`** — a polymorphic "read any attachment" command that
|
|
15
|
+
auto-routes by content-type: a `.zip` is unpacked and every entry converted
|
|
16
|
+
(the `{ count, files }` envelope), everything else (docx/xlsx/pptx/odf/csv/pdf/
|
|
17
|
+
`.msg`/legacy/text, reference + embedded items) runs through the markdown
|
|
18
|
+
dispatch. Images / scanned PDFs / legacy `.ppt` return the actionable
|
|
19
|
+
vision-model hint. Use the explicit `convert-mail-attachment-*` siblings only
|
|
20
|
+
to force a specific output format. (Surface: 176 → 177 commands.)
|
|
21
|
+
- **`pageCount`** on the born-digital PDF text envelope (every PDF→markdown entry
|
|
22
|
+
point) so an LLM can chunk its reads without a second parse.
|
|
23
|
+
- **`--id`** is now accepted by *every* command with a single required `*-id`
|
|
24
|
+
flag (was mail-message-only) — e.g. `get-calendar-event --id`, `get-team --id`.
|
|
25
|
+
- **`--start` / `--end`** aliases for `--start-date-time` / `--end-date-time`
|
|
26
|
+
across the calendar-view family.
|
|
27
|
+
- **Token-tier flags in the manifest** — `needsElevatedToken` (now serialized to
|
|
28
|
+
`commands.json` too, not only `help-json`) and a new `needsSubstrateToken` mark
|
|
29
|
+
the Teams elevated / chat-substrate commands, so an agent can warm up an
|
|
30
|
+
interactive login before calling them instead of dead-ending on a timeout.
|
|
31
|
+
|
|
32
|
+
### Changed
|
|
33
|
+
|
|
34
|
+
- **`download-onedrive-file-content` → `download-drive-item-content`** (it works
|
|
35
|
+
on any driveItem, not just OneDrive). The old name keeps working as a
|
|
36
|
+
back-compat alias.
|
|
37
|
+
- **Friendlier error for a mistyped flag**: a bare word like `item--id` (instead
|
|
38
|
+
of `--item-id`) now explains flags need their leading `--`, rather than
|
|
39
|
+
commander's opaque "too many arguments".
|
|
40
|
+
- **Byte commands refuse to inline a multi-MB payload** without `--output-path`:
|
|
41
|
+
`get-mail-attachment`, `get-mail-message-mime`, the `download-*` family, etc.
|
|
42
|
+
now return an actionable `inline_too_large` error above ~1 MB instead of
|
|
43
|
+
flooding an LLM's context with a base64 blob. Small payloads still inline.
|
|
44
|
+
- **`read-mail-attachment` prefers content-type when the filename misleads** — a
|
|
45
|
+
real spreadsheet saved as `report.jpg` now converts as a table instead of
|
|
46
|
+
returning an image hint. The explicit `convert-*` siblings stay
|
|
47
|
+
extension-deterministic by design.
|
|
48
|
+
- **Unified `--drive-id` guidance** — the 11 terse drive-item commands now point
|
|
49
|
+
at `list-drives` / `list-sharepoint-site-drives` like the rest.
|
|
50
|
+
|
|
51
|
+
### Fixed
|
|
52
|
+
|
|
53
|
+
- **Auth no longer pops a browser per command on re-auth.** A command whose token
|
|
54
|
+
needed refreshing used to launch the Chrome extension-capture window — once per
|
|
55
|
+
process, with no cross-process throttle, so a batch/agent run stacked up
|
|
56
|
+
windows. Command re-auth now uses a headed Edge sign-in (one login re-caches all
|
|
57
|
+
tokens); the extension capture is reserved for explicit `login --use-extension`.
|
|
58
|
+
- **Stale doc numbers** — the `help-json` size hints (terse-category ~16 → ~6 KB
|
|
59
|
+
after the trim) and the README command list now match reality.
|
|
60
|
+
|
|
61
|
+
### Performance
|
|
62
|
+
|
|
63
|
+
- **Cold-start**: the heavy conversion deps are `--external` to the npm bundle
|
|
64
|
+
(`dist/cli.js` 7.4 MB → 1.2 MB; `node --version` ~1.0 s → 0.58 s). The compiled
|
|
65
|
+
standalone binaries (`build:bin`) stay self-contained.
|
|
66
|
+
- **`help-json --terse`** summaries are compacted to their first sentence, so a
|
|
67
|
+
per-category discovery fetch drops well under budget (drive 17.9 KB → 6.5 KB,
|
|
68
|
+
mail 16.8 KB → 5.2 KB; full terse 83 KB → 31 KB).
|
|
69
|
+
|
|
5
70
|
## 1.0.0
|
|
6
71
|
|
|
7
72
|
The first stable release. Two breaking changes consolidate the public output
|
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# ask-marcel-office-cli
|
|
2
2
|
|
|
3
|
-
**A Microsoft Graph CLI built for LLMs.**
|
|
3
|
+
**A Microsoft Graph CLI built for LLMs.** 177 commands across Mail, Calendar, OneDrive, SharePoint, Excel, Teams chats, Planner / To-Do, OneNote, and directory — plus local-file tools (markdown conversion, image extraction) that need no sign-in at all. Sign in once with your Microsoft 365 account — no Azure app registration, no admin consent, no client secrets.
|
|
4
4
|
|
|
5
5
|
```bash
|
|
6
6
|
npm i -g ask-marcel-office-cli
|
|
@@ -25,7 +25,7 @@ LLM tool-loops keep hitting the same three walls with Microsoft Graph:
|
|
|
25
25
|
|
|
26
26
|
### Read-only by design
|
|
27
27
|
|
|
28
|
-
**This is the most important property.**
|
|
28
|
+
**This is the most important property.** 173 GET endpoints + 2 POST (searches) + 1 POST (create draft) + 1 PATCH (update draft) = 177 commands. No `send-mail`, no `create-event`, no `upload-file`, no `delete-anything`. The only write operations are draft creation and update — a hallucinated command can at most create an unsent draft in your Drafts folder. Safe default for autonomous agents, MCP servers, and "let Claude poke around my mailbox" sessions where you can't fully review every tool call.
|
|
29
29
|
|
|
30
30
|
### One call gets the full email context
|
|
31
31
|
|
|
@@ -34,6 +34,7 @@ A typical "read this email" loop in raw Graph: GET the message → GET the attac
|
|
|
34
34
|
`convert-mail-to-markdown` collapses that into one call:
|
|
35
35
|
|
|
36
36
|
- Body rendered as markdown (turndown pipeline)
|
|
37
|
+
- Quoted reply chains / forwarded-message blocks stripped by default so a long thread doesn't duplicate earlier messages into the model's context (the cut is replaced with a visible marker; opt out with `--keep-quoted true` to keep the full body)
|
|
37
38
|
- Inline images embedded as base64 `data:` URIs (size-capped per image — opt out with `--inline-images false` to keep raw `cid:` refs)
|
|
38
39
|
- File attachments listed below the body with id + name + size, ready for follow-up calls
|
|
39
40
|
- Pair with `extract-sharepoint-links-in-mail` to resolve every SharePoint URL in the body to its driveItem in parallel (capped at 25 unique URLs per call)
|
|
@@ -42,19 +43,49 @@ A typical "read this email" loop in raw Graph: GET the message → GET the attac
|
|
|
42
43
|
|
|
43
44
|
Feed any Office-shaped file (docx, xlsx, pptx, csv, rtf, odt, …) into the local conversion pipeline OR through Graph's `?format=pdf` when slide layout and images matter:
|
|
44
45
|
|
|
45
|
-
- `download-drive-item-as-markdown` — docx via mammoth (
|
|
46
|
+
- `download-drive-item-as-markdown` — docx via mammoth (embedded images become `[image]` placeholders by default — `--inline-images true` to embed them as base64, or pull the full-resolution originals with `extract-drive-item-images`), xlsx as one markdown table per sheet (a sheet whose used range exceeds the `--max-cells` cap, default 50 000, becomes a band-by-band read hint instead of a multi-hundred-MB table that would OOM), csv as a table, odt/ods/odp via content.xml (headings, lists, tables, named sheets, per-slide text, with `office:annotation` comments folded inline), **pptx** flattened to per-slide text (titles + bullets + text boxes + table cells, speaker notes inline, as `## Slide N` sections — `download-drive-item-as-pdf` + a vision model when layout / images matter), **pdf** via text-layer extraction ([unpdf](https://github.com/unjs/unpdf) → `text/plain`; a scanned / image-only PDF with no text layer points you at `download-drive-item-as-pdf` + a vision model), **legacy OLE Office** (`.xls` read by sheetjs like `.xlsx`; `.doc` extracted by [word-extractor](https://www.npmjs.com/package/word-extractor) as plain text; `.ppt` has no pure-JS path → convert to PDF first), plain-text passthrough
|
|
46
47
|
- `download-drive-item-as-pdf` — Graph PDF conversion for anything it supports (preserves slide layout, images, charts — the right call for pptx and image-heavy docs)
|
|
47
48
|
- `convert-mail-attachment-to-markdown` / `convert-mail-attachment-to-pdf` — same pipelines but starting from an email attachment
|
|
49
|
+
- `read-mail-attachment` — one call that reads any mail attachment, auto-routing by file type (a `.zip` is unpacked and every entry converted; docx/xlsx/pptx/odf/csv/PDF/`.msg`/legacy/text → markdown; images, scanned PDFs, and legacy `.ppt` return an actionable 415 pointing at the raw-bytes / vision route) so an agent never has to choose between the `convert-mail-attachment-*` siblings
|
|
50
|
+
- `convert-local-file` — same pipelines but starting from a file **on disk** (`--path ./report.docx`); never calls Graph (works offline, no login). A `.zip` is unpacked with every contained file converted in one call. The two things it can't do locally — convert **to** PDF and Loop/Fluid/Whiteboard sources — need Graph's server-side renderer (upload to OneDrive and use the drive-item siblings)
|
|
51
|
+
- `convert-drive-item-zip` / `convert-mail-attachment-zip` — unzip an archive (OneDrive/SharePoint item, or an Outlook attachment) and convert **every** contained file in one call; legacy GBK / CP437 entry names (Chinese vendor archives from WinRAR / Windows Explorer) are decoded correctly, never mojibaked; unsupported entries are listed with a note instead of failing the archive
|
|
52
|
+
- **Outlook `.msg` files** (saved/forwarded emails) convert to markdown through every entry point above — H1 subject, From/To/Cc/Date header block, the body, and an `## Attachments` section where each attachment is itself converted recursively (depth-capped)
|
|
53
|
+
- `extract-sharepoint-links-in-documents` — the doc-side sibling of `extract-sharepoint-links-in-mail`: resolve every `*.sharepoint.com` URL embedded in a docx/xlsx/pptx (read from the package's relationship parts) or an odt/ods/odp (read from the inline `xlink:href` links in content.xml) to its driveItem, so an agent can follow references out of a document the same way it follows them out of an email
|
|
54
|
+
|
|
55
|
+
Pass `--include-metadata true` on any `*-as-markdown` (or `convert-mail-attachment-to-markdown`) command to surface the side-channel content the rendered body hides. For **docx** (`## DOCX metadata`): core/app/custom doc properties, people registry, external hyperlinks, comments (each quoting the document text span it annotates), tracked changes, hidden text (`w:vanish`), MERGEFIELD / HYPERLINK / DOCVARIABLE instructions, bookmarks. For **xlsx** (`## Workbook metadata`): properties, external relationships, defined names, hidden / very-hidden sheets, legacy + threaded cell comments (each tagged with its cell), the persons registry. For **pptx** (`## PPTX metadata`): properties, external relationships, slide tags, comment authors + comments (legacy + modern, each anchored to its slide), and per-slide title / speaker notes / hidden flag — appended after the per-slide text body (use `download-drive-item-as-pdf` + a vision model for slide visuals / layout). Each family also covers its macro-enabled (`.docm` / `.xlsm` / `.pptm`) and template (`.dotx` / `.xltx` / `.potx`, etc.) variants, and surfaces a `### Macros (VBA)` section flagging an embedded `vbaProject.bin` (the file can execute code on open). For **OpenDocument** (`.odt` / `.ods` / `.odp`) the flag appends a `## OpenDocument metadata` block (Dublin Core + ODF properties, keywords, user-defined custom fields) after the converted body. No-op on other sources.
|
|
56
|
+
|
|
57
|
+
### Extract embedded images from documents
|
|
58
|
+
|
|
59
|
+
`extract-drive-item-images` (OneDrive / SharePoint), `extract-mail-attachment-images` (Outlook attachments), and `extract-local-file-images` (a file **on disk** — no Graph, no login) pull the embedded images out of a **docx, xlsx, pptx, or pdf**. For Office files it reads the OOXML media parts (png/jpg/gif/bmp/tiff/webp/svg) — including original full-resolution / un-cropped originals and images on hidden slides that the rendered view never shows. SVG rides back as its XML source (which carries the diagram's own text labels); legacy vector (emf/wmf) and audio/video are skipped. For a PDF it walks every page via [unpdf](https://github.com/unjs/unpdf) (a pure-JS, no-native-deps pdf.js build) and re-encodes each painted image as PNG — page-oriented, so it captures images as drawn on each page (it does not reach layer-hidden/unpainted XObjects or the full uncropped original behind a clipped image). Pair with the global `--output-dir <dir>` to write every image to a folder (the directory is auto-created and each `base64` becomes a `savedTo` path); without it the bytes ride back base64-encoded so a vision model can read them directly.
|
|
48
60
|
|
|
49
61
|
The CLI follows any SharePoint media-transform redirect internally, so the LLM never has to fetch an external URL.
|
|
50
62
|
|
|
63
|
+
### Find every drive you can reach
|
|
64
|
+
|
|
65
|
+
`list-drives` only returns your personal OneDrive(s). `list-accessible-drives` unions every discovery vector the delegated token can hit — `/me/drives` (personal), `/me/joinedTeams` (Teams libraries), `/me/memberOf` Unified groups → each group's drive (SharePoint M365-group sites), `/me/drive/sharedWithMe` (drives behind files shared with you), per-team `/teams/{id}/channels` → `filesFolder` for **private/shared channels** (which live in their own sites, not the team default drive), activity signals (`/me/drive/recent`, `/me/drive/following`, `/me/insights/{trending,used,shared}`), and every **non-default document library** of each discovered site via a path-addressed `/sites/{host}:/sites/{name}:/drives` (catches secondary libraries the default-drive vectors skip) — deduped by drive id and tagged with the `sources[]` that surfaced each one (`channel` = private/shared channel drive, `activity` = a recently-used/followed/trending item drive, `siteLibrary` = a non-default site library). These vectors catch OneDrives, channel sites, and direct-link sites the tenant search index (`search-sharepoint-sites-by-name`) never returns; the index in turn returns sites you can open but aren't a member of. For that index half, `search-all-accessible-sites` deep-pages the Microsoft Search API (`POST /search/query`, `entityTypes: ['site']`) past the single-page cap of `search-sharepoint-sites-by-name`, returning the *full* security-trimmed site index (on one tenant: ~154 sites vs 80). So **the union of `search-all-accessible-sites` + `list-accessible-drives` is the practical maximum on a delegated token** (truly enumerating *every* site in the tenant needs tenant-admin app-only `/sites/getAllSites`). Both site-search commands **exclude archived sites**: each result is probed (`GET /sites/{id}?$select=…,siteCollection`) and dropped when Graph reports it archived or fails with `423 resourceLocked` — the signal a departed/unlicensed user's auto-archived OneDrive returns (no more `sharepointerror.aspx?scenario=SiteArchived` dead links in the output); the count surfaces as `archivedExcluded`. `--max-groups` caps every fan-out, and `partialErrors[]` stays signal-only: benign "can't reach this one" results (404 no-drive, 403 access-denied / non-member channel, 423 admin-locked site, 400 stale id) are dropped silently — only actionable failures (auth, throttling, 5xx, network) are listed. Both commands also surface a best-effort `fileEstimate` — the Microsoft Search index's security-trimmed `driveItem` count, i.e. roughly how many files you can access across all of SharePoint/OneDrive (index-wide, not limited to the listed drives).
|
|
66
|
+
|
|
51
67
|
### Browser-OAuth at first launch
|
|
52
68
|
|
|
53
69
|
No Azure app, no tenant admin. The CLI captures the same token the Teams web client uses — works for any Microsoft 365 account, personal or enterprise.
|
|
54
70
|
|
|
71
|
+
**Login flow:** By default, the CLI uses Playwright to open a browser for authentication. Alternatively, you can use the [Ask Marcel Companion](./browser-extension/) browser extension for faster authentication.
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
ask-marcel login # default: Playwright browser
|
|
75
|
+
ask-marcel login --use-extension # use browser extension (requires setup)
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
**Browser extension setup (optional, one-time):**
|
|
79
|
+
|
|
80
|
+
1. Open `chrome://extensions/` (Chrome) or `edge://extensions/` (Edge)
|
|
81
|
+
2. Enable "Developer mode" (top-right toggle)
|
|
82
|
+
3. Click "Load unpacked" → select the `browser-extension/` folder from this repo
|
|
83
|
+
4. **Enable in incognito/inprivate mode:** Click the extension's "Details" button, then enable "Allow in incognito" (Chrome) or "Allow in InPrivate" (Edge). **This is required** — the CLI opens an incognito/inprivate window, and extensions are disabled there by default.
|
|
84
|
+
5. Done — use `ask-marcel login --use-extension` to authenticate via the extension
|
|
85
|
+
|
|
55
86
|
### Stable error envelope with actionable hints
|
|
56
87
|
|
|
57
|
-
Every failure — Graph, CLI parser, Zod validation, substrate — comes back as `{ok: false, error, errorCode?, hint?, source}`. The `hint` field tells the model *what to do next* (e.g. "string literals MUST use single quotes; embed one by doubling it") and `source` tells it where the failure came from. Curated rules for 20+ recurring Graph errors plus cross-resolver pointers (passed a Teams URL to `resolve-mail-link`? Hint says "re-run with `resolve-teams-link`").
|
|
88
|
+
Every failure — Graph, CLI parser, Zod validation, substrate — comes back as `{ok: false, error, errorCode?, hint?, source, retryAfterSeconds?}`. The `hint` field tells the model *what to do next* (e.g. "string literals MUST use single quotes; embed one by doubling it") and `source` tells it where the failure came from. Curated rules for 20+ recurring Graph errors plus cross-resolver pointers (passed a Teams URL to `resolve-mail-link`? Hint says "re-run with `resolve-teams-link`"). When Graph throttles (HTTP 429, sometimes 503) the response's `Retry-After` is surfaced as `retryAfterSeconds` — the integer seconds to wait — so a caller running tenant-scale crawls can honor the server's backoff instead of guessing.
|
|
58
89
|
|
|
59
90
|
### Lean responses
|
|
60
91
|
|
|
@@ -66,7 +97,7 @@ A typical conversion command returns multi-MB PDF bytes. Sending 5 MB of base64
|
|
|
66
97
|
|
|
67
98
|
Two flag patterns avoid the round-trip:
|
|
68
99
|
|
|
69
|
-
- **`--output-path /path/to/file.pdf`** — the CLI decodes the bytes, writes them to disk, and replaces `base64: "..."` in the envelope with `savedTo: "/path/to/file.pdf"`. The LLM sees a 3-line confirmation instead of a 7-million-character payload. Works on every command that returns binary or text content; rejected with a clear error on plain-JSON commands so a misapplied flag is never silent.
|
|
100
|
+
- **`--output-path /path/to/file.pdf`** — the CLI decodes the bytes, writes them to disk, and replaces `base64: "..."` in the envelope with `savedTo: "/path/to/file.pdf"`. The LLM sees a 3-line confirmation instead of a 7-million-character payload. Works on every command that returns binary or text content; rejected with a clear error on plain-JSON commands so a misapplied flag is never silent. A binary payload over ~1 MB is **refused** without this flag (an `inline_too_large` error pointing you here), so a multi-MB base64 string can never flood the context by accident.
|
|
70
101
|
- **No flag, text mode** — binary commands print a one-line summary (`binary: application/pdf, 4837291 bytes — use --output-path to save`) instead of spilling base64 to stdout. The LLM sees a hint without ever pulling the bytes.
|
|
71
102
|
|
|
72
103
|
### Relative dates on calendar windows
|
|
@@ -82,7 +113,7 @@ npm i -g ask-marcel-office-cli
|
|
|
82
113
|
# authenticate (cached → refresh → browser fallback)
|
|
83
114
|
ask-marcel login
|
|
84
115
|
|
|
85
|
-
#
|
|
116
|
+
# the rest is read-only (the only writes are mail drafts) and discoverable from --help
|
|
86
117
|
ask-marcel list-drives
|
|
87
118
|
ask-marcel search-onedrive-files --drive-id "b!abc..." --query "Q3 budget"
|
|
88
119
|
ask-marcel convert-mail-to-markdown --message-id "AAMkAD..."
|
|
@@ -154,29 +185,42 @@ The `AuthManager` interface is two async methods that return `Result<T, AuthErro
|
|
|
154
185
|
|
|
155
186
|
## Deep docs
|
|
156
187
|
|
|
157
|
-
- **[All
|
|
188
|
+
- **[All 177 commands](docs/COMMANDS.md)** — per-category tables with required params + Graph endpoint
|
|
158
189
|
- **[Usage guide](docs/USAGE.md)** — output formats, OData passthrough, `--output-path`, pagination, library API, architecture, configuration, quality gates
|
|
159
190
|
- **[Machine-readable manifest](docs/commands.json)** — JSON for programmatic discovery (LLM tool-loops, IDE plugins, MCP servers); also importable via `import manifest from 'ask-marcel-office-cli/commands.json'`
|
|
191
|
+
- **[QA playbook](docs/QA-PLAYBOOK.md)** — the repeatable full-surface health-check procedure (offline gates, parameter matrix, conversion contracts, live Graph drift probes) used to audit each release
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
## Agent skill (progressive disclosure)
|
|
195
|
+
|
|
196
|
+
A [Codex skill](https://docs.anthropic.com/en/docs/agents-and-tools/codex) lives at `.agents/skills/ask-marcel-office/` and teaches agents how to use the CLI without loading all 177 commands into context at once.
|
|
197
|
+
|
|
198
|
+
**Structure**
|
|
199
|
+
|
|
200
|
+
```
|
|
201
|
+
.agents/skills/ask-marcel-office/
|
|
202
|
+
├── SKILL.md # core workflow + category index
|
|
203
|
+
└── references/ # per-domain command details, loaded on demand
|
|
204
|
+
├── marcel-mail.md (32 commands)
|
|
205
|
+
├── marcel-drive.md (30 commands)
|
|
206
|
+
├── marcel-calendar.md (23 commands)
|
|
207
|
+
├── marcel-sharepoint.md (18 commands)
|
|
208
|
+
├── marcel-user.md (15 commands)
|
|
209
|
+
├── marcel-tasks.md (15 commands)
|
|
210
|
+
├── marcel-excel.md (11 commands)
|
|
211
|
+
├── marcel-notes.md (11 commands)
|
|
212
|
+
├── marcel-chats.md (9 commands)
|
|
213
|
+
├── marcel-teams.md (7 commands)
|
|
214
|
+
└── marcel-meta.md (6 commands)
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
**How it works**
|
|
218
|
+
|
|
219
|
+
`SKILL.md` contains the authentication flow, the discovery loop (`help-json --terse --category` → `docs <cmd>` → execute), key patterns (OData passthrough, relative dates, document conversion, pagination), and a category index. The full command reference for each domain lives in `references/marcel-<category>.md` and is loaded only when the agent needs that domain — keeping the context window lean.
|
|
160
220
|
|
|
161
221
|
## Roadmap
|
|
162
222
|
|
|
163
|
-
Read-only stays the default forever.
|
|
164
|
-
|
|
165
|
-
- **Zip archive support** — extract a `.zip` from an email attachment or OneDrive item and run every contained file through the right conversion pipeline (markdown or PDF), so an agent reading "the project handover archive" doesn't have to shell out to `unzip`.
|
|
166
|
-
- **SharePoint links inside documents** — today `extract-sharepoint-links-in-mail` finds and resolves every `sharepoint.com` URL in an email body; the same pass for inline links in docx/xlsx/pptx would close the loop on "follow every reference in this document".
|
|
167
|
-
- **Bulk folder conversion** — `convert-folder --drive-id … --item-id <folder> --format markdown` walks a folder and converts every Office file in one call, returning the combined markdown (or writing per-file outputs to a directory via `--output-path`).
|
|
168
|
-
- **OneNote end-to-end markdown** — OneNote page reads currently return raw HTML; chaining the turndown pipeline (same one `convert-mail-to-markdown` uses) would give parity with the docx route.
|
|
169
|
-
- **PowerPoint as slide images** — for vision-capable LLMs, render each pptx slide as a PNG alongside the existing PDF route so the model can reason about layout + diagrams without going through PDF parsing.
|
|
170
|
-
- **Calendar meeting attachments** — `/events/{id}/attachments` isn't exposed yet; would pair with the existing `convert-mail-attachment-to-*` shape so "summarize the deck attached to my 3pm" is a single call.
|
|
171
|
-
- **Multi-tenant auth profiles** — `ask-marcel login --profile work` / `--profile personal` with separate token caches at `~/.ask-marcel/<profile>/`, for consultants and contractors who routinely switch tenants.
|
|
172
|
-
- **Streaming pagination output** — write each page to stdout as it arrives instead of accumulating, so long delta walks don't hold gigabytes in memory and an agent can start processing page 1 while page 2 is in flight.
|
|
173
|
-
- **Excel charts as PNG** — `list-excel-worksheet-charts` already returns chart metadata; add `get-excel-chart-image` that renders the chart as a base64 PNG via Graph's `.../charts/{id}/image()` endpoint so vision-capable models can read the chart, not just its title.
|
|
174
|
-
- **Yammer / Viva Engage** — community posts, threads, and replies. Currently unscoped on the Teams web-client token, so this would need a scope investigation first.
|
|
175
|
-
- **Federated tenant search expansion** — `microsoft-search-query` exists but covers a narrow entity set; expanding to people, sites, lists, and chat messages would unify discovery across all of M365 behind one search call.
|
|
176
|
-
- **Quoted-text stripping in `convert-mail-to-markdown`** — collapse "On Tuesday Alice wrote..." reply chains so long threads stop blowing the context budget on duplicated quoted content.
|
|
177
|
-
- **`--diff` flag on `download-drive-item-version` markdown** — render the markdown diff between two historical versions of the same file (e.g. "what changed in the spec last week"), so an agent can answer revision questions without manually comparing two long markdown blobs.
|
|
178
|
-
- **Full-fidelity document context** — extract every piece of written information in a document: reviewer comments, threaded comments, slide notes, revision marks, descriptions, custom metadata, and sensitivity labels — folded into the markdown output so an LLM reads the whole story of a document, not just its body text.
|
|
179
|
-
- **OCR for images and image-only PDFs** — pipe images (jpg/png attachments, inline email images) and scanned PDFs through a local OCR pass, returning extracted text as markdown. Closes the loop on screenshots, scanned receipts, and image-only attachments that today come back as opaque bytes.
|
|
223
|
+
Read-only stays the default forever. There's no fixed feature backlog — coverage grows out of real LLM workflows as they come up.
|
|
180
224
|
|
|
181
225
|
Suggestions, requests, and pull requests welcome — see the [issues page](https://github.com/vdelacou/ask-marcel-office-cli/issues).
|
|
182
226
|
|