Skip to content

Add archive command - #1

Open
nitsanavni wants to merge 5 commits into
saadiq:mainfrom
nitsanavni:add-archive-command
Open

Add archive command#1
nitsanavni wants to merge 5 commits into
saadiq:mainfrom
nitsanavni:add-archive-command

Conversation

@nitsanavni

@nitsanavni nitsanavni commented Feb 27, 2026

Copy link
Copy Markdown

Summary

  • Adds gmail archive <message-id>... command that removes the INBOX label, matching Gmail native archive behavior
  • Adds gmail.modify OAuth scope to support label modification
  • Accepts multiple message IDs in a single call

Test plan

  • Verify re-authentication flow works after scope change
  • Test archiving single and multiple message IDs
  • Test with invalid message ID

Adds gmail archive <message-id>... which removes the INBOX label,
matching Gmail archive behavior. Adds gmail.modify scope to support this.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@nitsanavni nitsanavni changed the title Add archive command Add archive and attachments commands Feb 27, 2026
@nitsanavni nitsanavni changed the title Add archive and attachments commands Add archive command Feb 27, 2026
saadiq added 4 commits July 13, 2026 23:23
The archive branch swapped the HTML-to-markdown dependency from
html_to_markdown to markdownify. markdownify is declared in neither
pyproject.toml nor uv.lock, so `gmail read` raised ModuleNotFoundError
on any HTML email. The swap is also unrelated to the archive feature.

Claude-Session: https://claude.ai/code/session_01XN4T96G5R4UczQpgA2nqES
An invalid message ID raised HttpError out of the loop, so IDs before it
were already archived while IDs after it silently never ran. Report the
failure, continue the batch, and exit non-zero.

Claude-Session: https://claude.ai/code/session_01XN4T96G5R4UczQpgA2nqES
Adding gmail.modify invalidates every existing token, but the old token
still refreshes cleanly, so the API rejected archive with an opaque 403.
Credentials.from_authorized_user_info() overrides a token's scopes with
the ones passed in, so creds.scopes cannot detect this - read the granted
scopes from the token file and re-consent when any required scope is absent.

Claude-Session: https://claude.ai/code/session_01XN4T96G5R4UczQpgA2nqES
@saadiq

saadiq commented Jul 14, 2026

Copy link
Copy Markdown
Owner

Thanks for this — the archive command itself is clean and matches the project style. I've reviewed it and pushed a few fix commits to the branch (maintainerCanModify was on; shout if you'd rather I'd left them as suggestions).

One thing I want to ask you about directly. The diff carries a change to commands.py that has nothing to do with archiving:

-from html_to_markdown import convert_to_markdown
+from markdownify import markdownify as convert_to_markdown

markdownify isn't declared in pyproject.toml, isn't in uv.lock, and isn't installed in the venv — html-to-markdown is what the project depends on. So merging as-is would break gmail read on any HTML email:

>>> import markdownify
ModuleNotFoundError: No module named 'markdownify'

It also isn't mentioned in the PR description, which is what makes me want to raise it rather than just quietly drop it.

My assumption is that this was errant — a leftover from a local experiment, or a branch that picked up an unrelated change — and I've reverted it in 57e934d on that basis. Can you confirm? Two reasons I'd rather ask than assume silently:

  1. If you did intend to switch the HTML-to-Markdown library, that's a fair thing to propose, but it deserves its own PR with the dependency actually added to pyproject.toml and a note on why markdownify is preferable.
  2. An unannounced dependency substitution tucked inside an unrelated feature PR is, structurally, what a supply-chain attack looks like. I'm confident that's not what happened here, but the reviewing habit of naming it out loud is one I'd rather keep than skip.

The rest of what I pushed, for the record:

  • 8e09151 — one bad message ID no longer strands the batch. HttpError escaped the loop, so IDs before the failure were already archived while IDs after it silently never ran. Now each failure is reported, the batch continues, and the command exits non-zero.
  • 368f827 — re-authorize when a stored token predates a required scope. This covers the unchecked re-auth item in your test plan, and it was sneakier than it looks: Credentials.from_authorized_user_info() overrides a token's stored scopes with the ones you pass in, so creds.scopes reports what you asked for rather than what Google granted. An existing token therefore looks valid and then fails with an opaque 403 on the first archive. The check reads the granted scopes from the token file instead.
  • cf6a1f2 — docs for the command and the new scope.

Worth knowing: this branch will conflict with #2 and #3, which touch the same region of commands.py and the same import line in gmail_cli.py. Happy to rebase whichever ones don't land first.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants