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/CHANGELOG.md CHANGED
@@ -2,6 +2,159 @@
2
2
 
3
3
  All notable changes to `ask-marcel-office-cli` are documented here.
4
4
 
5
+ ## 2.5.0
6
+
7
+ ### Fixed: `help` costs the same whoever runs it
8
+
9
+ The top-level listing is compacted to fit an LLM's token budget, and then
10
+ Commander sized its description column from `process.stdout.columns`, so the
11
+ budget depended on the reader's terminal. Measured on this surface: 35 KB at 80
12
+ columns, 64 KB at 100, 46 KB at 140, 39 KB at 200. Not even monotonic, so no
13
+ width was safe to assume.
14
+
15
+ The wrap width is now pinned at 80 and the listing is 35 KB everywhere.
16
+
17
+ This also broke `npm publish`. The byte-count guard in `cli.test.ts` asserts the
18
+ listing stays under 45 KB, and under CI or any piped run stdout is not a TTY, so
19
+ Commander fell back to 80 and the guard passed. Run from an ordinary terminal at
20
+ 90 to 150 columns it failed, which is where most terminals sit. A new test
21
+ renders the listing at four widths and asserts one size, so the guard can no
22
+ longer measure a different thing for each reader.
23
+
24
+ ### Fixed: the group conversation collections stop advertising a filter Graph refuses
25
+
26
+ `list-group-threads` and `list-group-conversations` handed out the full OData
27
+ option set, so both advertised `--filter`. Graph accepts it on neither. Probed
28
+ live on a group the signed-in user belongs to: any predicate on the threads
29
+ collection answers `ConversationFilterOther`, `isLocked eq false` answers
30
+ `ConversationFilterIsLockedEqualsFalse` because the only accepted forms are
31
+ `IsLocked eq true` and `IsLocked ne false`, and a predicate on conversations
32
+ answers `ErrorUnsupportedPathForQuery`.
33
+
34
+ That broke the promise the usage guide makes, that the CLI advertises only the
35
+ flags the endpoint honours so the manifest never lies. An agent reading the
36
+ manifest spent a round trip to find out. `--filter` is dropped from both; the
37
+ flag is now refused locally with the unknown-flag hint and no network call.
38
+ Filtering a group inbox down to its locked threads is not a use this CLI has,
39
+ so the one working predicate did not earn a caveat in its place.
40
+
41
+ `--top`, `--skip`, `--orderby`, `--select` and `--expand` are all honoured on
42
+ both collections and are unchanged.
43
+
44
+ ### Added: docx tracked changes cover the table
45
+
46
+ `extractDocxMetadata` reported formatting revisions on runs and paragraphs only,
47
+ so a reviewer who restyled a table left no trace in the metadata block. Word
48
+ records a table, row and cell property change the same way it records a run's,
49
+ the properties as they were BEFORE the edit nested inside a `*Change` element,
50
+ so the same walker reads all five. `formatChanges[].scope` gains `table`, `row`
51
+ and `cell` alongside `run` and `paragraph`.
52
+
53
+ One limit worth knowing: a property whose values hang off child elements rather
54
+ than its own attributes, and `w:tblBorders` is the common table case, compares
55
+ as unchanged. The revision is still reported with its author, date and scope; it
56
+ names no property. Structural revisions (`w:cellIns`, `w:cellDel`, `w:cellMerge`)
57
+ carry no before/after pair and are not reported.
58
+
59
+ ### Added: a gate that holds the docs to the registry's numbers
60
+
61
+ Four numeric claims drifted during the 2.4.0 cycle, and one was wrong on main for
62
+ two releases: `docs/USAGE.md` promised "the 180 READ commands" where the registry
63
+ held 188, three sections after `README.md` said 188 correctly. `mcp.ts` already
64
+ derives its counts and carries a comment saying the prose must be derived too;
65
+ nothing enforced it.
66
+
67
+ `bun run check:docs` reads the counts off the command registry and the rendered
68
+ manifest, then holds all ten numeric claims in `README.md`, `docs/USAGE.md` and
69
+ `docs/COMMANDS.md` to them, printing `file:line expected N, found M`. A claim
70
+ whose anchor text no longer matches anything fails too, so rewording a sentence
71
+ cannot silently switch its own check off. It runs in CI behind a `--selftest`
72
+ that proves the matcher rejects both a wrong number and a lost anchor.
73
+
74
+ The stale 188 is corrected in the same change.
75
+
76
+ ### Added: read what is attached to a group post
77
+
78
+ `get-group-post --expand attachments` was the only route to a post's attachments,
79
+ and it inlines every one of them at once, which is the shape that times out on a
80
+ post carrying a multi-MB file. Three commands make the group inbox readable
81
+ attachment by attachment, all on the `Group.Read.All` scope the token already has:
82
+
83
+ - `list-group-post-attachments` returns the metadata alone, with the same slim
84
+ default `--select` the mail and calendar siblings use. Graph silently ignores
85
+ `$top`, `$skip`, `$orderby` and `$filter` here (probed live: asking for one
86
+ attachment returned all seven, and `isInline eq false` returned all seven when
87
+ every one was inline), so only `--select` and `--expand` are exposed.
88
+ - `get-group-post-attachment` fetches one attachment, mirroring `contentBytes`
89
+ as `base64` so `--output-path` lands it on disk. This is also the route to an
90
+ image attached to a post.
91
+ - `convert-group-post-attachment-to-markdown` renders one through the shared
92
+ conversion pipeline, the way the mail and calendar siblings do.
93
+
94
+ ### Fixed: unconvertible attachments name a command that can reach them
95
+
96
+ The shared conversion pipeline hardcoded the mail remediation wording, so an
97
+ image or scanned PDF attached to a calendar event told the caller to run
98
+ `get-mail-attachment --message-id ...`, which cannot address an event. Each
99
+ caller now supplies its own hints: the calendar converter points at
100
+ `get-calendar-event --expand attachments` and its PDF sibling, and the new post
101
+ converter points at `get-group-post-attachment`.
102
+
103
+ ### Fixed: inline-only mail no longer hides its images
104
+
105
+ Graph reports `hasAttachments: false` for a message whose only attachments are
106
+ inline images, and both markdown converters fetched the attachment list only on
107
+ that flag. Such a message rendered its images as unnamed placeholders, left them
108
+ out of the attachments list, and embedded nothing under `--inline-images true`.
109
+ The list is now fetched whenever the body references a `cid:` image as well,
110
+ the guard `get-mail-signature` already uses, so
111
+ `convert-mail-to-markdown` and `convert-group-post-to-markdown` name the
112
+ placeholders, list the images with their ids, and embed them on request. No
113
+ extra call for a message without inline images.
114
+
115
+ ### Changed: the shared-mailbox commands say when they will 403
116
+
117
+ `list-shared-mailbox-messages`, `list-shared-mailbox-folder-messages`,
118
+ `list-shared-mailbox-folders`, `list-shared-mailbox-child-folders` and
119
+ `get-shared-mailbox-message` used to promise a 403 only "if the signed-in user
120
+ does not have shared access". The scope they need, `Mail.Read.Shared`, is on
121
+ neither token this CLI can mint, so their summaries now state that any mailbox
122
+ other than your own is expected to answer `ErrorAccessDenied` whatever delegation
123
+ Exchange holds, that your own UPN works, and that a Microsoft 365 group's mailbox
124
+ is the shared-mail path that does.
125
+
126
+ ### Added: read the posts of a Microsoft 365 group conversation
127
+
128
+ `list-group-conversations` and `list-group-threads` stopped at Graph's truncated
129
+ `preview`, and a group mailbox cannot be read through the shared-mailbox commands
130
+ (`ErrorGroupIsUsedInNonGroupURI`), so a group's mail was reachable up to its titles
131
+ and no further. Three commands close the gap, all on the `Group.Read.All` scope the
132
+ token already carries:
133
+
134
+ - `list-group-thread-posts --group-id --thread-id` returns every `post` of a thread
135
+ with its HTML body, `from`, `sender`, `receivedDateTime` and `hasAttachments`.
136
+ Graph hands back the whole thread in one call and silently ignores `$top`, `$skip`
137
+ and `$orderby` while rejecting `$filter` (probed live), so only `--select` and
138
+ `--expand` are exposed and there is no page cursor.
139
+ - `get-group-post --group-id --thread-id --post-id` is the `get-mail-message` sibling
140
+ for a group inbox; `--expand attachments` inlines the attachments with their bytes.
141
+ - `convert-group-post-to-markdown` renders one post the way `convert-mail-to-markdown`
142
+ renders a message, with the same `--inline-images` and `--keep-quoted` flags. A post
143
+ arrives from the group's own address with the writer in `sender`, so the author line
144
+ reads `Robin Chen <...> on behalf of Support <...>`; the thread `topic` is the subject
145
+ and is not repeated.
146
+
147
+ The mail-to-markdown pipeline now takes the resource path and the header renderer as
148
+ parameters; the mail command's output is unchanged. `list-group-threads` and
149
+ `list-group-conversations` point at the reader, and at `--expand posts`, which already
150
+ inlined the bodies.
151
+
152
+ ### Fixed — deleted text no longer renders in an `.odt` body
153
+
154
+ The body converter walked `text:tracked-changes` like any other block, so the
155
+ paragraphs a reviewer had deleted printed as if they were still in the
156
+ document. The region is skipped, so the body reads as the document stands.
157
+
5
158
  ## 2.4.0
6
159
 
7
160
  Two community fixes from [@a-tokyo](https://github.com/a-tokyo). Nothing breaks:
package/README.md CHANGED
@@ -12,7 +12,7 @@
12
12
 
13
13
  Outlook · OneDrive · SharePoint · Calendar · Excel · Teams · Planner · To Do · OneNote · People
14
14
 
15
- [Install](#install-in-60-seconds) · [See it work](#see-it-work) · [What it reaches](#what-your-agent-can-reach) · [Files to markdown](#any-file-becomes-markdown) · [Library](#embed-it-as-a-typescript-library) · [All 186 commands](docs/COMMANDS.md)
15
+ [Install](#install-in-60-seconds) · [See it work](#see-it-work) · [What it reaches](#what-your-agent-can-reach) · [Files to markdown](#any-file-becomes-markdown) · [Library](#embed-it-as-a-typescript-library) · [All 192 commands](docs/COMMANDS.md)
16
16
 
17
17
  </div>
18
18
 
@@ -61,7 +61,7 @@ Microsoft Graph normally means registering an Azure app, chasing tenant-admin co
61
61
 
62
62
  ### 🛡️ Safe to hand to an autonomous agent
63
63
 
64
- The 186 commands break down as 178 GET, 4 read-only POST (three searches and a free/busy lookup), and 4 mail-draft operations. No `send-mail`. No `create-event`. No `upload-file`. No `delete-anything`. The worst a hallucinated tool call can do is leave an unsent draft in your Drafts folder. That is the entire blast radius, which is why you can let an agent explore a mailbox without reviewing every call. No analytics, either: the only outbound traffic is Microsoft Graph and a periodic npm version check.
64
+ The 192 commands break down as 184 GET, 4 read-only POST (three searches and a free/busy lookup), and 4 mail-draft operations. No `send-mail`. No `create-event`. No `upload-file`. No `delete-anything`. The worst a hallucinated tool call can do is leave an unsent draft in your Drafts folder. That is the entire blast radius, which is why you can let an agent explore a mailbox without reviewing every call. No analytics, either: the only outbound traffic is Microsoft Graph and a periodic npm version check.
65
65
 
66
66
  ### 🧠 Responses budgeted for a context window
67
67
 
@@ -111,7 +111,7 @@ $ ask-marcel-office download-drive-item-as-markdown --drive-id "b!abc..." --item
111
111
 
112
112
  | Surface | Commands | In practice |
113
113
  |---|---:|---|
114
- | 📧 Outlook Mail | 38 | Search and read mail as markdown, convert any attachment (down to nested `.zip` and `.msg`), resolve SharePoint links in bodies, extract your signature, find existing drafts on a thread, prepare reply / forward drafts (the only writes) |
114
+ | 📧 Outlook Mail | 44 | Search and read mail as markdown, convert any attachment (down to nested `.zip` and `.msg`), resolve SharePoint links in bodies, extract your signature, find existing drafts on a thread, prepare reply / forward drafts (the only writes) |
115
115
  | 📁 OneDrive + SharePoint | 49 | Discover every drive and site your token can reach, search files, read any document as markdown or PDF, version history, share links resolved even into partner tenants where you're a guest |
116
116
  | 📅 Calendar | 24 | "What's on this week" via relative dates (`today`, `start-of-week`, `+7d`), event details, free/busy lookups |
117
117
  | 👥 People + directory | 16 | People search, user profiles, the directory around you, your own identity and IDs in one round trip |
@@ -203,7 +203,7 @@ You get **five gateway tools**, not one per command — a schema per command wou
203
203
  |:--|:--|
204
204
  | `list-commands` | The terse manifest. Start here; `category` narrows it. |
205
205
  | `get-command-docs` | Full docs for one command: options, endpoint, example. |
206
- | `run-command` | The 182 **read** commands. `readOnlyHint: true`, so clients can auto-approve it. |
206
+ | `run-command` | The 188 **read** commands. `readOnlyHint: true`, so clients can auto-approve it. |
207
207
  | `run-write-command` | The 4 mail-draft **write** commands. Separate tool so the read tool's promise stays honest. |
208
208
  | `login` | Sign in / refresh. Opens a browser on this machine. |
209
209
 
@@ -249,7 +249,7 @@ Azure Managed Identity, an on-behalf-of flow, hand-pasted JWTs in tests: the Gra
249
249
 
250
250
  ## Deep docs
251
251
 
252
- - **[All 186 commands](docs/COMMANDS.md)**: per-category tables with required params + Graph endpoint
252
+ - **[All 192 commands](docs/COMMANDS.md)**: per-category tables with required params + Graph endpoint
253
253
  - **[Usage guide](docs/USAGE.md)**: output formats, OData passthrough, `--output-path`, pagination, library API, architecture, configuration
254
254
  - **[Machine-readable manifest](docs/commands.json)**: JSON for programmatic discovery, also importable via `import manifest from 'ask-marcel-office-cli/commands.json'`
255
255
  - **[QA playbook](docs/QA-PLAYBOOK.md)**: the repeatable full-surface health check run before each release