mcp-gee-sweet 0.9.2.dev238__tar.gz → 0.9.2.dev243__tar.gz
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.
- {mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/PKG-INFO +1 -1
- {mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/docs/roadmap.md +3 -2
- {mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/src/mcp_gee_sweet/tools/sheets/helpers.py +2 -2
- {mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/tests/drive/test_files.py +13 -5
- {mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/tests/drive/test_transfer.py +54 -39
- mcp_gee_sweet-0.9.2.dev243/tests/fakes.py +27 -0
- {mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/tests/sheets/test_helpers.py +13 -33
- {mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/tests/sheets/test_structure.py +15 -0
- {mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/tests/test_cache.py +12 -22
- {mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/tests/test_server.py +5 -0
- {mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/.claude/commands/cleanup-worktrees.md +0 -0
- {mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/.claude/commands/merge-pr.md +0 -0
- {mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/.claude/commands/notify-kai.md +0 -0
- {mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/.claude/commands/notify-partner.md +0 -0
- {mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/.claude/commands/orchestrator.md +0 -0
- {mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/.claude/commands/prep-for-pr.md +0 -0
- {mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/.claude/commands/qa-kickoff.md +0 -0
- {mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/.claude/commands/release.md +0 -0
- {mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/.claude/commands/retro.md +0 -0
- {mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/.claude/commands/roadmap-status.md +0 -0
- {mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/.claude/commands/start-worktree.md +0 -0
- {mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/.claude/commands/team-member.md +0 -0
- {mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/.claude/commands/verify-pr.md +0 -0
- {mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/.claude/settings.json +0 -0
- {mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/.claude/team-roles/amy.md +0 -0
- {mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/.claude/team-roles/aziz.md +0 -0
- {mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/.claude/team-roles/bob.md +0 -0
- {mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/.claude/team-roles/dev.md +0 -0
- {mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/.claude/team-roles/joy.md +0 -0
- {mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/.claude/team-roles/kai.md +0 -0
- {mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/.claude/team-roles/qa.md +0 -0
- {mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/.dockerignore +0 -0
- {mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
- {mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
- {mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
- {mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/.github/dependabot.yml +0 -0
- {mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/.github/workflows/ci.yml +0 -0
- {mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/.github/workflows/docs.yml +0 -0
- {mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/.github/workflows/publish-dev.yml +0 -0
- {mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/.github/workflows/release.yml +0 -0
- {mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/.gitignore +0 -0
- {mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/.pre-commit-config.yaml +0 -0
- {mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/.python-version +0 -0
- {mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/CHANGELOG.md +0 -0
- {mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/CLAUDE.md +0 -0
- {mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/CODE_OF_CONDUCT.md +0 -0
- {mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/CONTRIBUTING.md +0 -0
- {mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/Dockerfile +0 -0
- {mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/LICENSE +0 -0
- {mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/Makefile +0 -0
- {mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/README.md +0 -0
- {mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/SECURITY.md +0 -0
- {mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/docker-compose.yml +0 -0
- {mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/docs/assets/logo.png +0 -0
- {mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/docs/auth.md +0 -0
- {mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/docs/client-setup.md +0 -0
- {mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/docs/configuration.md +0 -0
- {mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/docs/decisions/decision-async-tool-execution.md +0 -0
- {mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/docs/decisions/decision-auth-status-limitation-categories.md +0 -0
- {mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/docs/decisions/decision-cache-invalidation.md +0 -0
- {mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/docs/decisions/decision-chart-theming.md +0 -0
- {mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/docs/decisions/decision-comments-first-class.md +0 -0
- {mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/docs/decisions/decision-composite-tools.md +0 -0
- {mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/docs/decisions/decision-condition-type-docstring-generation.md +0 -0
- {mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/docs/decisions/decision-converted-doc-change-detection.md +0 -0
- {mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/docs/decisions/decision-dev-team.md +0 -0
- {mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/docs/decisions/decision-docs-formatting.md +0 -0
- {mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/docs/decisions/decision-fork.md +0 -0
- {mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/docs/decisions/decision-gmail-domain.md +0 -0
- {mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/docs/decisions/decision-grid-data-size-cap.md +0 -0
- {mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/docs/decisions/decision-local-fs-test-harness.md +0 -0
- {mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/docs/decisions/decision-mcp-v2-resource-context-access.md +0 -0
- {mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/docs/decisions/decision-pillow-image-dependency.md +0 -0
- {mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/docs/decisions/decision-prompt-qa-role.md +0 -0
- {mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/docs/decisions/decision-publishing.md +0 -0
- {mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/docs/decisions/decision-release-docs-roles.md +0 -0
- {mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/docs/decisions/decision-repositioning.md +0 -0
- {mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/docs/decisions/decision-response-size-cap-generalization.md +0 -0
- {mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/docs/decisions/decision-response-size-cap-reevaluation-519.md +0 -0
- {mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/docs/decisions/decision-review-finding-ticket-bar.md +0 -0
- {mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/docs/decisions/decision-supports-all-drives-lint-check.md +0 -0
- {mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/docs/decisions/decision-sync-folder-recursion.md +0 -0
- {mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/docs/decisions/decision-testing.md +0 -0
- {mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/docs/decisions/decision-tool-error-visibility.md +0 -0
- {mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/docs/decisions/index.md +0 -0
- {mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/docs/design/blockquote-representation.md +0 -0
- {mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/docs/design/borderless-table-columns.md +0 -0
- {mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/docs/design/doc-to-markdown.md +0 -0
- {mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/docs/design/docs-ast-pipeline.md +0 -0
- {mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/docs/design/effectiveformat-spike.md +0 -0
- {mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/docs/design/heading-anchor-resolution.md +0 -0
- {mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/docs/design/image-conversion.md +0 -0
- {mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/docs/design/index.md +0 -0
- {mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/docs/design/markdown-support.md +0 -0
- {mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/docs/design/module-history.md +0 -0
- {mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/docs/design/phase3-theme-system.md +0 -0
- {mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/docs/design/sse-consent-off-event-loop.md +0 -0
- {mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/docs/design.md +0 -0
- {mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/docs/index.md +0 -0
- {mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/docs/known-limitations.md +0 -0
- {mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/docs/notes-read.md +0 -0
- {mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/docs/qa/README.md +0 -0
- {mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/docs/qa/fixtures/qa-fixture-pixel.png +0 -0
- {mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/docs/qa/fixtures/tc-d195-create-doc.md +0 -0
- {mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/docs/qa/fixtures/tc-d196-create-doc.html +0 -0
- {mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/docs/qa/fixtures/tc-d213-dollar-escape.md +0 -0
- {mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/docs/qa/fixtures/tc-d226-heading-table.md +0 -0
- {mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/docs/qa/fixtures/tc-doc102-image-conversion.html +0 -0
- {mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/docs/qa/fixtures/tc-doc103-image-conversion.md +0 -0
- {mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/docs/qa/fixtures/tc-doc104-nested-lists.html +0 -0
- {mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/docs/qa/fixtures/tc-doc105-nested-lists-gfm.md +0 -0
- {mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/docs/qa/fixtures/tc-doc106-nested-lists-4space.md +0 -0
- {mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/docs/qa/fixtures/tc-doc135-paragraph-boundary.md +0 -0
- {mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/docs/qa/fixtures/tc-doc136-interrupted-block-boundary.md +0 -0
- {mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/docs/qa/fixtures/tc-doc137-inline-hr-no-block-ancestor.html +0 -0
- {mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/docs/qa/fixtures/tc-doc138-nbsp-spacer-paragraph.md +0 -0
- {mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/docs/qa/fixtures/tc-doc141-mismatched-list-tags.html +0 -0
- {mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/docs/qa/fixtures/tc-doc142-heading-anchors.md +0 -0
- {mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/docs/qa/fixtures/tc-doc144-anchor-in-table-cell.md +0 -0
- {mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/docs/qa/fixtures/tc-doc145-anchor-on-first-element.md +0 -0
- {mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/docs/qa/fixtures.template.md +0 -0
- {mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/docs/qa/gmail-test-plan.md +0 -0
- {mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/docs/qa/operations.yaml +0 -0
- {mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/docs/qa/playwright_oauth.md +0 -0
- {mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/docs/qa/results/.gitkeep +0 -0
- {mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/docs/qa/results/2026-06-02.md +0 -0
- {mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/docs/qa/results/2026-06-14.md +0 -0
- {mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/docs/qa/results/2026-06-17.md +0 -0
- {mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/docs/qa/results/2026-06-18.md +0 -0
- {mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/docs/qa/results/2026-06-21-drive-discovery.md +0 -0
- {mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/docs/qa/results/2026-06-21-sheets-delete-clear.md +0 -0
- {mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/docs/qa/results/2026-06-28.md +0 -0
- {mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/docs/qa/results/2026-07-04-docs.md +0 -0
- {mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/docs/qa/results/2026-07-04-drive-smoke.md +0 -0
- {mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/docs/qa/results/2026-07-04-infra.md +0 -0
- {mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/docs/qa/results/2026-07-04-sheets_read.md +0 -0
- {mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/docs/qa/results/2026-07-04.md +0 -0
- {mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/docs/qa/results/2026-09-04.md +0 -0
- {mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/docs/qa/retro-v0.8.0.md +0 -0
- {mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/docs/qa/retro-v0.9.0.md +0 -0
- {mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/docs/qa/run.md +0 -0
- {mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/docs/qa/runs/README.md +0 -0
- {mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/docs/qa/runs/v0.8.0.md +0 -0
- {mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/docs/qa/runs/v0.8.1.md +0 -0
- {mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/docs/qa/runs/v0.9.0.md +0 -0
- {mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/docs/qa/setup.md +0 -0
- {mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/docs/qa/tests/calendar.md +0 -0
- {mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/docs/qa/tests/docs_content.md +0 -0
- {mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/docs/qa/tests/docs_layout.md +0 -0
- {mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/docs/qa/tests/docs_style.md +0 -0
- {mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/docs/qa/tests/docs_tables.md +0 -0
- {mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/docs/qa/tests/drive_activity.md +0 -0
- {mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/docs/qa/tests/drive_files.md +0 -0
- {mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/docs/qa/tests/drive_sharing.md +0 -0
- {mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/docs/qa/tests/drive_transfer.md +0 -0
- {mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/docs/qa/tests/gmail.md +0 -0
- {mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/docs/qa/tests/infra.md +0 -0
- {mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/docs/qa/tests/sheets_charts.md +0 -0
- {mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/docs/qa/tests/sheets_mgmt.md +0 -0
- {mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/docs/qa/tests/sheets_read.md +0 -0
- {mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/docs/qa/tests/sheets_write.md +0 -0
- {mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/docs/style-guide.md +0 -0
- {mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/docs/tools.md +0 -0
- {mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/mkdocs.yml +0 -0
- {mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/pyproject.toml +0 -0
- {mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/pyrightconfig.json +0 -0
- {mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/scripts/check_supports_all_drives.py +0 -0
- {mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/scripts/gen_tool_docs.py +0 -0
- {mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/scripts/lane_context_hook.py +0 -0
- {mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/scripts/oauth_setup.py +0 -0
- {mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/scripts/qa_cancel_insert_local_images.py +0 -0
- {mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/scripts/qa_gmail_fixtures.py +0 -0
- {mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/scripts/setup_team.sh +0 -0
- {mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/scripts/write_team_mcp_configs.sh +0 -0
- {mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/server.json +0 -0
- {mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/src/mcp_gee_sweet/.env.template +0 -0
- {mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/src/mcp_gee_sweet/__init__.py +0 -0
- {mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/src/mcp_gee_sweet/auth.py +0 -0
- {mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/src/mcp_gee_sweet/cache.py +0 -0
- {mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/src/mcp_gee_sweet/http_transport.py +0 -0
- {mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/src/mcp_gee_sweet/server.py +0 -0
- {mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/src/mcp_gee_sweet/sse_shutdown.py +0 -0
- {mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/src/mcp_gee_sweet/tools/__init__.py +0 -0
- {mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/src/mcp_gee_sweet/tools/cache.py +0 -0
- {mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/src/mcp_gee_sweet/tools/calendar.py +0 -0
- {mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/src/mcp_gee_sweet/tools/concurrency.py +0 -0
- {mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/src/mcp_gee_sweet/tools/docs/__init__.py +0 -0
- {mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/src/mcp_gee_sweet/tools/docs/anchors.py +0 -0
- {mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/src/mcp_gee_sweet/tools/docs/ast.py +0 -0
- {mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/src/mcp_gee_sweet/tools/docs/ast_to_markdown.py +0 -0
- {mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/src/mcp_gee_sweet/tools/docs/comments.py +0 -0
- {mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/src/mcp_gee_sweet/tools/docs/content.py +0 -0
- {mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/src/mcp_gee_sweet/tools/docs/doc_to_ast.py +0 -0
- {mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/src/mcp_gee_sweet/tools/docs/editing.py +0 -0
- {mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/src/mcp_gee_sweet/tools/docs/emitter.py +0 -0
- {mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/src/mcp_gee_sweet/tools/docs/html_parser.py +0 -0
- {mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/src/mcp_gee_sweet/tools/docs/images.py +0 -0
- {mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/src/mcp_gee_sweet/tools/docs/indices.py +0 -0
- {mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/src/mcp_gee_sweet/tools/docs/layout.py +0 -0
- {mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/src/mcp_gee_sweet/tools/docs/markdown_export.py +0 -0
- {mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/src/mcp_gee_sweet/tools/docs/named_ranges.py +0 -0
- {mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/src/mcp_gee_sweet/tools/docs/style.py +0 -0
- {mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/src/mcp_gee_sweet/tools/docs/tables.py +0 -0
- {mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/src/mcp_gee_sweet/tools/drive/__init__.py +0 -0
- {mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/src/mcp_gee_sweet/tools/drive/activity.py +0 -0
- {mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/src/mcp_gee_sweet/tools/drive/files.py +0 -0
- {mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/src/mcp_gee_sweet/tools/drive/sharing.py +0 -0
- {mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/src/mcp_gee_sweet/tools/drive/transfer.py +0 -0
- {mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/src/mcp_gee_sweet/tools/gmail.py +0 -0
- {mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/src/mcp_gee_sweet/tools/pagination.py +0 -0
- {mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/src/mcp_gee_sweet/tools/response_limits.py +0 -0
- {mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/src/mcp_gee_sweet/tools/sheets/__init__.py +0 -0
- {mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/src/mcp_gee_sweet/tools/sheets/data.py +0 -0
- {mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/src/mcp_gee_sweet/tools/sheets/structure.py +0 -0
- {mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/tests/__init__.py +0 -0
- {mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/tests/conftest.py +0 -0
- {mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/tests/drive/__init__.py +0 -0
- {mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/tests/drive/test_activity.py +0 -0
- {mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/tests/drive/test_sharing.py +0 -0
- {mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/tests/integration/__init__.py +0 -0
- {mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/tests/integration/conftest.py +0 -0
- {mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/tests/integration/test_local_fs_harness.py +0 -0
- {mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/tests/sheets/__init__.py +0 -0
- {mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/tests/sheets/test_data.py +0 -0
- {mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/tests/test_auth.py +0 -0
- {mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/tests/test_calendar.py +0 -0
- {mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/tests/test_check_supports_all_drives.py +0 -0
- {mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/tests/test_concurrency.py +0 -0
- {mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/tests/test_docs_anchors.py +0 -0
- {mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/tests/test_docs_comments.py +0 -0
- {mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/tests/test_docs_content.py +0 -0
- {mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/tests/test_docs_core.py +0 -0
- {mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/tests/test_docs_editing.py +0 -0
- {mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/tests/test_docs_images.py +0 -0
- {mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/tests/test_docs_indices.py +0 -0
- {mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/tests/test_docs_layout.py +0 -0
- {mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/tests/test_docs_markdown_export.py +0 -0
- {mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/tests/test_docs_named_ranges.py +0 -0
- {mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/tests/test_docs_style.py +0 -0
- {mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/tests/test_docs_tables.py +0 -0
- {mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/tests/test_gen_tool_docs.py +0 -0
- {mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/tests/test_gmail.py +0 -0
- {mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/tests/test_lane_context_hook.py +0 -0
- {mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/tests/test_qa_gmail_fixtures.py +0 -0
- {mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/tests/test_response_limits.py +0 -0
- {mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/tests/test_server_json.py +0 -0
- {mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/tests/test_sse_shutdown.py +0 -0
- {mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/tests/test_team_mcp_configs.py +0 -0
- {mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/uv.lock +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: mcp-gee-sweet
|
|
3
|
-
Version: 0.9.2.
|
|
3
|
+
Version: 0.9.2.dev243
|
|
4
4
|
Summary: MCP server for Google Workspace — Sheets, Drive, Docs (HTML or Markdown), and Calendar.
|
|
5
5
|
Project-URL: Homepage, https://github.com/khuisman/mcp-gee-sweet
|
|
6
6
|
Project-URL: Bug Tracker, https://github.com/khuisman/mcp-gee-sweet/issues
|
|
@@ -193,7 +193,7 @@ Same shape as v0.8.1 — stabilize on defects that surfaced since v0.9.0 shipped
|
|
|
193
193
|
- [x] Lock in the isolated-run rendering rule — an isolated bullet run renders at its true depth (`depth N` → `nestingLevel N`) at *every* depth, never clamped (decided 2026-09-09: consistency over avoiding orphaned indent; the markup generator owns the depth it emits). Extend TC-DOC190 with a `min_depth >= 2` case; state the rule in `emitter.py`'s bullet-pass comment and the `docs/emitter.py` paragraph of `CLAUDE.md` (PR #731) ([#714](https://github.com/khuisman/mcp-gee-sweet/issues/714))
|
|
194
194
|
- [x] `create_doc` markdown: a fenced code block whose body is raw HTML tags renders as loose plain paragraphs (fence ignored, indentation flattened) instead of a monospaced code block — python-markdown's raw-HTML-block handling appears to pre-empt `fenced_code`. Surfaced during #714 triage (PR #730) ([#719](https://github.com/khuisman/mcp-gee-sweet/issues/719))
|
|
195
195
|
- [ ] **Bundle:** Docs write side (emitter, html_parser, images, content), one PR closing [#509](https://github.com/khuisman/mcp-gee-sweet/issues/509), [#567](https://github.com/khuisman/mcp-gee-sweet/issues/567), [#581](https://github.com/khuisman/mcp-gee-sweet/issues/581), [#638](https://github.com/khuisman/mcp-gee-sweet/issues/638), [#715](https://github.com/khuisman/mcp-gee-sweet/issues/715) — lane A ([#894](https://github.com/khuisman/mcp-gee-sweet/issues/894))
|
|
196
|
-
- [
|
|
196
|
+
- [x] **Bundle:** Calendar hardening, one PR closing [#459](https://github.com/khuisman/mcp-gee-sweet/issues/459), [#615](https://github.com/khuisman/mcp-gee-sweet/issues/615), [#626](https://github.com/khuisman/mcp-gee-sweet/issues/626), [#627](https://github.com/khuisman/mcp-gee-sweet/issues/627), [#726](https://github.com/khuisman/mcp-gee-sweet/issues/726) — lane B (PR #912) ([#890](https://github.com/khuisman/mcp-gee-sweet/issues/890))
|
|
197
197
|
- [x] `delete_file(permanent=True)` surfaces a misleading 404 when the caller can trash but not permanently delete (Shared Drive Content manager) — an agent can read it as "already gone" and skip cleanup; check `capabilities.canDelete` first — lane B ([#876](https://github.com/khuisman/mcp-gee-sweet/issues/876)) (PR #888)
|
|
198
198
|
- [x] SSE: SIGTERM with an SSE stream open logs an "Exception in ASGI application" traceback; the server still exits cleanly, pre-existing before #833 — lane A (PR #911) ([#868](https://github.com/khuisman/mcp-gee-sweet/issues/868))
|
|
199
199
|
- [x] `sync_folder`'s `convert_markdown` restamp-failure handling: quota-message gap, duplicated create()+update() try/except, test fixture isolation — companion to #420 (PR #669) ([#650](https://github.com/khuisman/mcp-gee-sweet/issues/650))
|
|
@@ -214,6 +214,7 @@ Same shape as v0.8.1 — stabilize on defects that surfaced since v0.9.0 shipped
|
|
|
214
214
|
- [x] auth: under `--transport sse`, the OAuth consent wait runs synchronously on the event loop, stalling every other session and ignoring SIGTERM until consent or timeout. #811 follow-up, found in PR #828 ([#833](https://github.com/khuisman/mcp-gee-sweet/issues/833)) (PR #867)
|
|
215
215
|
- [x] Tool errors are opaque on PyPI installs: `mcp` 2.3 (what `uvx` resolves; the lock pins 2.0.0) hides any non-`ToolError` exception text, so #811's "run `mcp-gee-sweet auth`" message and uncaught Google API errors reach the client as a bare `Error executing tool <name>`. Observed live. Urgent: lane-a, next after #844 ([#872](https://github.com/khuisman/mcp-gee-sweet/issues/872)) (PR #905)
|
|
216
216
|
- [ ] auth: offer OAuth re-authorization from the failing tool call, via URL-mode elicitation with the consent URL in a `ToolError` as fallback, then rebuild services in place. Covers a refresh token revoked mid-session too. No unprompted browser. Lane-a, after #872 ([#873](https://github.com/khuisman/mcp-gee-sweet/issues/873))
|
|
217
|
+
- [ ] sheets: an A1 range with a dangling colon (`A1:`, `A:`, `5:`, `:5`, `:B`, `:B5`) passes `_parse_a1_notation` and widens through `_grid_range` to the whole sheet or a bigger rectangle. Hits every `_grid_range_or_error` tool; for `sort_range`/`merge_cells` it changes data. Found reviewing community PR #922, which rejects only a bare `":"`. Start after #922 merges (same lines) ([#926](https://github.com/khuisman/mcp-gee-sweet/issues/926))
|
|
217
218
|
|
|
218
219
|
**Sheets hardening** _(triaged out of `backlog` 2026-09-11 — see the "Backlog triage" note below)_
|
|
219
220
|
- [x] `update_sheet_properties`/dimension-tool hardening + dedup follow-ups from PR #321's review ([#323](https://github.com/khuisman/mcp-gee-sweet/issues/323)) (PR #734)
|
|
@@ -272,7 +273,7 @@ Same shape as v0.8.1 — stabilize on defects that surfaced since v0.9.0 shipped
|
|
|
272
273
|
**Calendar hardening** _(triaged out of `backlog` 2026-09-11)_
|
|
273
274
|
|
|
274
275
|
**Testing** _(triaged out of `backlog` 2026-09-11)_
|
|
275
|
-
- [
|
|
276
|
+
- [x] **Bundle:** test-suite tightening, one PR closing [#392](https://github.com/khuisman/mcp-gee-sweet/issues/392), [#526](https://github.com/khuisman/mcp-gee-sweet/issues/526), [#551](https://github.com/khuisman/mcp-gee-sweet/issues/551), [#561](https://github.com/khuisman/mcp-gee-sweet/issues/561), [#574](https://github.com/khuisman/mcp-gee-sweet/issues/574) — lane B (PR #924) ([#891](https://github.com/khuisman/mcp-gee-sweet/issues/891))
|
|
276
277
|
- [ ] `qa`: `search_files` has no unit test coverage for the error-dict path ([#573](https://github.com/khuisman/mcp-gee-sweet/issues/573))
|
|
277
278
|
|
|
278
279
|
**Infrastructure**
|
{mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/src/mcp_gee_sweet/tools/sheets/helpers.py
RENAMED
|
@@ -65,9 +65,9 @@ def _parse_a1_notation(range_str: str) -> dict[str, int]:
|
|
|
65
65
|
if not range_str:
|
|
66
66
|
raise ValueError("Invalid A1 notation: empty string")
|
|
67
67
|
|
|
68
|
-
match = re.
|
|
68
|
+
match = re.fullmatch(r"([A-Z]+)?(\d+)?(?::([A-Z]+)?(\d+)?)?", range_str.upper())
|
|
69
69
|
|
|
70
|
-
if not match:
|
|
70
|
+
if not match or not any(match.groups()):
|
|
71
71
|
raise ValueError(f"Invalid A1 notation: {range_str}")
|
|
72
72
|
|
|
73
73
|
start_col, start_row, end_col, end_row = match.groups()
|
|
@@ -34,6 +34,14 @@ _drive_tool, _drive_tools = _make_tool_registry()
|
|
|
34
34
|
drive_files_module.register(_drive_tool)
|
|
35
35
|
|
|
36
36
|
|
|
37
|
+
def _assert_error_list(result, underlying: str) -> None:
|
|
38
|
+
"""The "error dict, not raised" contract: one {"error": ...} item carrying the
|
|
39
|
+
underlying exception's text. Doesn't pin the message's prefix wording (#574)."""
|
|
40
|
+
assert len(result) == 1
|
|
41
|
+
assert set(result[0]) == {"error"}
|
|
42
|
+
assert underlying in result[0]["error"]
|
|
43
|
+
|
|
44
|
+
|
|
37
45
|
class TestSearchSpreadsheets:
|
|
38
46
|
"""Bug: single quotes in query were interpolated raw into the Drive query string."""
|
|
39
47
|
|
|
@@ -77,7 +85,7 @@ class TestSearchSpreadsheets:
|
|
|
77
85
|
)
|
|
78
86
|
ctx = _make_ctx(drive_service=drive_svc)
|
|
79
87
|
result = await _drive_tools["search_spreadsheets"](query="budget 2024", ctx=ctx)
|
|
80
|
-
|
|
88
|
+
_assert_error_list(result, "simulated API failure")
|
|
81
89
|
|
|
82
90
|
async def test_requests_starred_field(self):
|
|
83
91
|
"""#388: search_spreadsheets shares search_files' starred exposure."""
|
|
@@ -184,7 +192,7 @@ class TestSearchFiles:
|
|
|
184
192
|
)
|
|
185
193
|
ctx = _make_ctx(drive_service=svc)
|
|
186
194
|
result = await _drive_tools["search_files"](query="budget", ctx=ctx)
|
|
187
|
-
|
|
195
|
+
_assert_error_list(result, "simulated API failure")
|
|
188
196
|
|
|
189
197
|
|
|
190
198
|
class TestFileMutations:
|
|
@@ -721,7 +729,7 @@ class TestListSharedWithMe:
|
|
|
721
729
|
)
|
|
722
730
|
ctx = _make_ctx(drive_service=svc)
|
|
723
731
|
result = await _drive_tools["list_shared_with_me"](ctx=ctx)
|
|
724
|
-
|
|
732
|
+
_assert_error_list(result, "simulated API failure")
|
|
725
733
|
|
|
726
734
|
async def test_max_results_capped_at_200(self):
|
|
727
735
|
svc = self._drive_service()
|
|
@@ -806,7 +814,7 @@ class TestListRecentFiles:
|
|
|
806
814
|
)
|
|
807
815
|
ctx = _make_ctx(drive_service=svc)
|
|
808
816
|
result = await _drive_tools["list_recent_files"](ctx=ctx)
|
|
809
|
-
|
|
817
|
+
_assert_error_list(result, "simulated API failure")
|
|
810
818
|
|
|
811
819
|
|
|
812
820
|
class TestListFiles:
|
|
@@ -920,7 +928,7 @@ class TestListFiles:
|
|
|
920
928
|
)
|
|
921
929
|
ctx = self._ctx(svc)
|
|
922
930
|
result = await _drive_tools["list_files"](folder_id="folder1", ctx=ctx)
|
|
923
|
-
|
|
931
|
+
_assert_error_list(result, "simulated API failure")
|
|
924
932
|
|
|
925
933
|
async def test_requests_next_page_token(self):
|
|
926
934
|
"""#698: nextPageToken's absence is what marks a listing exhaustive."""
|
|
@@ -5115,11 +5115,22 @@ class TestDownloadFolder:
|
|
|
5115
5115
|
assert result["downloaded"] == ["Doc One.pdf"]
|
|
5116
5116
|
assert result["failed"] == []
|
|
5117
5117
|
|
|
5118
|
-
|
|
5118
|
+
@pytest.mark.parametrize(
|
|
5119
|
+
("skip_if_exists", "skipped", "downloaded", "export_calls", "final_bytes"),
|
|
5120
|
+
[
|
|
5121
|
+
(True, ["Doc One.pdf"], [], 0, b"already here"),
|
|
5122
|
+
(False, [], ["Doc One.pdf"], 1, b"new content"),
|
|
5123
|
+
],
|
|
5124
|
+
ids=["skip", "redownload"],
|
|
5125
|
+
)
|
|
5126
|
+
async def test_skip_if_exists_with_existing_destination(
|
|
5127
|
+
self, tmp_path, skip_if_exists, skipped, downloaded, export_calls, final_bytes
|
|
5128
|
+
):
|
|
5119
5129
|
"""TC-D109 (v0.8.0 QA run, #227): skip_if_exists was live-SKIPped as
|
|
5120
|
-
local-filesystem-dependent and had no unit coverage.
|
|
5121
|
-
|
|
5122
|
-
export()
|
|
5130
|
+
local-filesystem-dependent and had no unit coverage. With the flag on, an
|
|
5131
|
+
existing destination file is left untouched and the candidate never
|
|
5132
|
+
reaches export(); with it off, the file is overwritten by a fresh export.
|
|
5133
|
+
One parametrized test so the shared mock can't drift between cases (#561)."""
|
|
5123
5134
|
svc = MagicMock()
|
|
5124
5135
|
svc.files.return_value.list.return_value.execute.return_value = {
|
|
5125
5136
|
"files": [
|
|
@@ -5130,6 +5141,7 @@ class TestDownloadFolder:
|
|
|
5130
5141
|
},
|
|
5131
5142
|
]
|
|
5132
5143
|
}
|
|
5144
|
+
svc.files.return_value.export.return_value.execute.return_value = b"new content"
|
|
5133
5145
|
dest = tmp_path / "Doc One.pdf"
|
|
5134
5146
|
dest.write_bytes(b"already here")
|
|
5135
5147
|
|
|
@@ -5137,44 +5149,14 @@ class TestDownloadFolder:
|
|
|
5137
5149
|
folder_id="root",
|
|
5138
5150
|
local_path=str(tmp_path),
|
|
5139
5151
|
export_format="pdf",
|
|
5140
|
-
skip_if_exists=
|
|
5152
|
+
skip_if_exists=skip_if_exists,
|
|
5141
5153
|
ctx=self._ctx(svc),
|
|
5142
5154
|
)
|
|
5143
|
-
assert result["skipped"] ==
|
|
5144
|
-
assert result["downloaded"] ==
|
|
5155
|
+
assert result["skipped"] == skipped
|
|
5156
|
+
assert result["downloaded"] == downloaded
|
|
5145
5157
|
assert result["failed"] == []
|
|
5146
|
-
svc.files.return_value.export.
|
|
5147
|
-
assert dest.read_bytes() ==
|
|
5148
|
-
|
|
5149
|
-
async def test_skip_if_exists_false_redownloads_existing_file(self, tmp_path):
|
|
5150
|
-
"""Companion to the skip_if_exists=True case above: with the flag off, an
|
|
5151
|
-
existing destination file is not treated as a skip candidate and gets
|
|
5152
|
-
overwritten by a fresh export."""
|
|
5153
|
-
svc = MagicMock()
|
|
5154
|
-
svc.files.return_value.list.return_value.execute.return_value = {
|
|
5155
|
-
"files": [
|
|
5156
|
-
{
|
|
5157
|
-
"id": "doc1",
|
|
5158
|
-
"name": "Doc One",
|
|
5159
|
-
"mimeType": "application/vnd.google-apps.document",
|
|
5160
|
-
},
|
|
5161
|
-
]
|
|
5162
|
-
}
|
|
5163
|
-
svc.files.return_value.export.return_value.execute.return_value = b"new content"
|
|
5164
|
-
dest = tmp_path / "Doc One.pdf"
|
|
5165
|
-
dest.write_bytes(b"stale content")
|
|
5166
|
-
|
|
5167
|
-
result = await _transfer_tools["download_folder"](
|
|
5168
|
-
folder_id="root",
|
|
5169
|
-
local_path=str(tmp_path),
|
|
5170
|
-
export_format="pdf",
|
|
5171
|
-
skip_if_exists=False,
|
|
5172
|
-
ctx=self._ctx(svc),
|
|
5173
|
-
)
|
|
5174
|
-
assert result["downloaded"] == ["Doc One.pdf"]
|
|
5175
|
-
assert result["skipped"] == []
|
|
5176
|
-
svc.files.return_value.export.assert_called_once()
|
|
5177
|
-
assert dest.read_bytes() == b"new content"
|
|
5158
|
+
assert svc.files.return_value.export.call_count == export_calls
|
|
5159
|
+
assert dest.read_bytes() == final_bytes
|
|
5178
5160
|
|
|
5179
5161
|
async def test_mime_type_filter_appended_to_drive_query(self, tmp_path):
|
|
5180
5162
|
"""TC-D110 (v0.8.0 QA run, #227): mime_type_filter is delegated entirely to
|
|
@@ -5227,6 +5209,13 @@ class TestDownloadFile:
|
|
|
5227
5209
|
def _metadata(self, name, mime_type):
|
|
5228
5210
|
return {"name": name, "mimeType": mime_type}
|
|
5229
5211
|
|
|
5212
|
+
def _assert_metadata_call(self, svc, file_id):
|
|
5213
|
+
"""#551: supportsAllDrives=True is what lets a shared-drive file
|
|
5214
|
+
resolve, and `fields` must include both keys download_file reads."""
|
|
5215
|
+
svc.files.return_value.get.assert_called_once_with(
|
|
5216
|
+
fileId=file_id, fields="name, mimeType", supportsAllDrives=True
|
|
5217
|
+
)
|
|
5218
|
+
|
|
5230
5219
|
async def test_google_doc_export(self, tmp_path):
|
|
5231
5220
|
svc = MagicMock()
|
|
5232
5221
|
svc.files.return_value.get.return_value.execute.return_value = self._metadata(
|
|
@@ -5244,6 +5233,7 @@ class TestDownloadFile:
|
|
|
5244
5233
|
svc.files.return_value.export.assert_called_once_with(
|
|
5245
5234
|
fileId="doc1", mimeType="application/pdf"
|
|
5246
5235
|
)
|
|
5236
|
+
self._assert_metadata_call(svc, "doc1")
|
|
5247
5237
|
dest = tmp_path / "My Doc.pdf"
|
|
5248
5238
|
assert result == {
|
|
5249
5239
|
"local_path": str(dest),
|
|
@@ -5274,6 +5264,30 @@ class TestDownloadFile:
|
|
|
5274
5264
|
assert result["local_path"] == str(dest)
|
|
5275
5265
|
assert dest.read_bytes() == b"xlsx bytes"
|
|
5276
5266
|
|
|
5267
|
+
async def test_str_export_content_is_utf8_encoded(self, tmp_path):
|
|
5268
|
+
"""#551: googleapiclient can hand back a text export (csv/html/txt) as
|
|
5269
|
+
str rather than bytes; download_file encodes it as UTF-8 before writing."""
|
|
5270
|
+
svc = MagicMock()
|
|
5271
|
+
svc.files.return_value.get.return_value.execute.return_value = self._metadata(
|
|
5272
|
+
"My Sheet", "application/vnd.google-apps.spreadsheet"
|
|
5273
|
+
)
|
|
5274
|
+
text = "name,city\nZoë,Zürich\n"
|
|
5275
|
+
svc.files.return_value.export.return_value.execute.return_value = text
|
|
5276
|
+
|
|
5277
|
+
result = await _transfer_tools["download_file"](
|
|
5278
|
+
file_id="sheet1",
|
|
5279
|
+
local_path=str(tmp_path),
|
|
5280
|
+
export_format="csv",
|
|
5281
|
+
ctx=self._ctx(svc),
|
|
5282
|
+
)
|
|
5283
|
+
|
|
5284
|
+
svc.files.return_value.export.assert_called_once_with(fileId="sheet1", mimeType="text/csv")
|
|
5285
|
+
self._assert_metadata_call(svc, "sheet1")
|
|
5286
|
+
dest = tmp_path / "My Sheet.csv"
|
|
5287
|
+
expected = text.encode("utf-8")
|
|
5288
|
+
assert dest.read_bytes() == expected
|
|
5289
|
+
assert result["size_bytes"] == len(expected)
|
|
5290
|
+
|
|
5277
5291
|
async def test_explicit_local_path_overrides_drive_filename(self, tmp_path):
|
|
5278
5292
|
"""When local_path names an exact file (not a directory), that name is
|
|
5279
5293
|
used as-is rather than Drive's own name + a guessed extension — the
|
|
@@ -5330,6 +5344,7 @@ class TestDownloadFile:
|
|
|
5330
5344
|
svc.files.return_value.get_media.assert_called_once_with(
|
|
5331
5345
|
fileId="bin1", supportsAllDrives=True
|
|
5332
5346
|
)
|
|
5347
|
+
self._assert_metadata_call(svc, "bin1")
|
|
5333
5348
|
dest = tmp_path / "photo.png"
|
|
5334
5349
|
assert result == {"local_path": str(dest), "name": "photo.png", "size_bytes": len(content)}
|
|
5335
5350
|
assert dest.read_bytes() == content
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"""Shared fake Google API services for unit tests."""
|
|
2
|
+
|
|
3
|
+
from types import SimpleNamespace
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class FakeSheetsService:
|
|
7
|
+
"""A minimal spreadsheets().get(...).execute() stand-in. Give it either
|
|
8
|
+
`result` (returned by every execute() call) or `exception` (raised
|
|
9
|
+
instead). Shared so a change to the mocked call shape is made once
|
|
10
|
+
(#392)."""
|
|
11
|
+
|
|
12
|
+
_http = SimpleNamespace(credentials=None)
|
|
13
|
+
|
|
14
|
+
def __init__(self, *, result: dict | None = None, exception: Exception | None = None):
|
|
15
|
+
self._result = result
|
|
16
|
+
self._exception = exception
|
|
17
|
+
|
|
18
|
+
def spreadsheets(self):
|
|
19
|
+
return self
|
|
20
|
+
|
|
21
|
+
def get(self, spreadsheetId, fields):
|
|
22
|
+
return self
|
|
23
|
+
|
|
24
|
+
def execute(self, **kwargs):
|
|
25
|
+
if self._exception is not None:
|
|
26
|
+
raise self._exception
|
|
27
|
+
return self._result
|
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
from types import SimpleNamespace
|
|
2
|
-
|
|
3
1
|
import pytest
|
|
4
2
|
|
|
5
3
|
from mcp_gee_sweet.cache import SheetInfo, SheetStructureCache
|
|
@@ -11,6 +9,8 @@ from mcp_gee_sweet.tools.sheets.helpers import (
|
|
|
11
9
|
_parse_a1_notation,
|
|
12
10
|
)
|
|
13
11
|
|
|
12
|
+
from ..fakes import FakeSheetsService
|
|
13
|
+
|
|
14
14
|
|
|
15
15
|
class TestColumnIndexToLetter:
|
|
16
16
|
def test_single_letters(self):
|
|
@@ -48,6 +48,11 @@ class TestLetterToColumnIndex:
|
|
|
48
48
|
|
|
49
49
|
|
|
50
50
|
class TestParseA1Notation:
|
|
51
|
+
@pytest.mark.parametrize("range_str", [":", "A1\n", "1:2\n", "B:D\n"])
|
|
52
|
+
def test_incomplete_or_trailing_newline_range_raises(self, range_str):
|
|
53
|
+
with pytest.raises(ValueError, match="Invalid A1 notation"):
|
|
54
|
+
_parse_a1_notation(range_str)
|
|
55
|
+
|
|
51
56
|
def test_single_cell(self):
|
|
52
57
|
result = _parse_a1_notation("A1")
|
|
53
58
|
assert result["startColumnIndex"] == 0
|
|
@@ -133,31 +138,6 @@ class TestParseA1Notation:
|
|
|
133
138
|
_parse_a1_notation("B1:A1")
|
|
134
139
|
|
|
135
140
|
|
|
136
|
-
class _FakeSheetsService:
|
|
137
|
-
"""A minimal spreadsheets().get(...).execute() stand-in. Give it either
|
|
138
|
-
`result` (returned by every execute() call) or `exception` (raised
|
|
139
|
-
instead) — covers the transient-failure, genuine-not-found, and
|
|
140
|
-
real-match fixture shapes that used to be three separate, near-identical
|
|
141
|
-
hand-rolled classes (PR #754 review)."""
|
|
142
|
-
|
|
143
|
-
_http = SimpleNamespace(credentials=None)
|
|
144
|
-
|
|
145
|
-
def __init__(self, *, result: dict | None = None, exception: Exception | None = None):
|
|
146
|
-
self._result = result
|
|
147
|
-
self._exception = exception
|
|
148
|
-
|
|
149
|
-
def spreadsheets(self):
|
|
150
|
-
return self
|
|
151
|
-
|
|
152
|
-
def get(self, spreadsheetId, fields):
|
|
153
|
-
return self
|
|
154
|
-
|
|
155
|
-
def execute(self, **kwargs):
|
|
156
|
-
if self._exception is not None:
|
|
157
|
-
raise self._exception
|
|
158
|
-
return self._result
|
|
159
|
-
|
|
160
|
-
|
|
161
141
|
_ONE_OTHER_SHEET = {"sheets": [{"properties": {"title": "Other", "sheetId": 0}}]}
|
|
162
142
|
_TWO_SHEETS = {
|
|
163
143
|
"sheets": [
|
|
@@ -167,21 +147,21 @@ _TWO_SHEETS = {
|
|
|
167
147
|
}
|
|
168
148
|
|
|
169
149
|
|
|
170
|
-
def _raising_service() ->
|
|
150
|
+
def _raising_service() -> FakeSheetsService:
|
|
171
151
|
"""Simulates a transient API failure (rate limit, timeout, auth hiccup)."""
|
|
172
|
-
return
|
|
152
|
+
return FakeSheetsService(exception=TimeoutError("simulated transient API failure"))
|
|
173
153
|
|
|
174
154
|
|
|
175
|
-
def _empty_service() ->
|
|
155
|
+
def _empty_service() -> FakeSheetsService:
|
|
176
156
|
"""A real API response where the sheet genuinely doesn't exist."""
|
|
177
|
-
return
|
|
157
|
+
return FakeSheetsService(result=_ONE_OTHER_SHEET)
|
|
178
158
|
|
|
179
159
|
|
|
180
|
-
def _matching_service() ->
|
|
160
|
+
def _matching_service() -> FakeSheetsService:
|
|
181
161
|
"""A real API response with more than one sheet, so a happy-path lookup
|
|
182
162
|
exercises picking the right match rather than trivially returning the
|
|
183
163
|
only entry present."""
|
|
184
|
-
return
|
|
164
|
+
return FakeSheetsService(result=_TWO_SHEETS)
|
|
185
165
|
|
|
186
166
|
|
|
187
167
|
class TestGetSheetIdExceptionPropagation:
|
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
from itertools import groupby
|
|
4
4
|
from unittest.mock import AsyncMock, MagicMock
|
|
5
5
|
|
|
6
|
+
import pytest
|
|
7
|
+
|
|
6
8
|
from mcp_gee_sweet.cache import SheetInfo
|
|
7
9
|
from mcp_gee_sweet.tools.sheets import structure as sheets_structure_module
|
|
8
10
|
|
|
@@ -1260,6 +1262,19 @@ class TestResizeColumns:
|
|
|
1260
1262
|
|
|
1261
1263
|
|
|
1262
1264
|
class TestFormatCells:
|
|
1265
|
+
@pytest.mark.parametrize("range_str", [":", "A1\n"])
|
|
1266
|
+
async def test_malformed_range_does_not_format_any_cells(self, range_str):
|
|
1267
|
+
svc = self._sheets_service()
|
|
1268
|
+
result = await _structure_tools["format_cells"](
|
|
1269
|
+
spreadsheet_id="ss1",
|
|
1270
|
+
sheet="Sheet1",
|
|
1271
|
+
range=range_str,
|
|
1272
|
+
bold=True,
|
|
1273
|
+
ctx=_make_ctx(sheets_service=svc, cache=None),
|
|
1274
|
+
)
|
|
1275
|
+
assert "error" in result
|
|
1276
|
+
svc.spreadsheets.return_value.batchUpdate.assert_not_called()
|
|
1277
|
+
|
|
1263
1278
|
def _sheets_service(self, sheet_id=0):
|
|
1264
1279
|
mock = MagicMock()
|
|
1265
1280
|
mock.spreadsheets.return_value.get.return_value.execute.return_value = {
|
|
@@ -19,6 +19,8 @@ from mcp_gee_sweet.cache import (
|
|
|
19
19
|
)
|
|
20
20
|
from mcp_gee_sweet.tools.sheets.helpers import _get_sheet_id
|
|
21
21
|
|
|
22
|
+
from .fakes import FakeSheetsService
|
|
23
|
+
|
|
22
24
|
# All tests use an in-memory SQLite database.
|
|
23
25
|
DB = ":memory:"
|
|
24
26
|
|
|
@@ -458,24 +460,10 @@ class TestCalendarCacheSetTtl:
|
|
|
458
460
|
assert cache.get_ttl() == 5
|
|
459
461
|
|
|
460
462
|
|
|
461
|
-
|
|
462
|
-
"""
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
class _Spreadsheets:
|
|
467
|
-
class _Request:
|
|
468
|
-
def execute(self, **kwargs):
|
|
469
|
-
return {
|
|
470
|
-
"properties": {"title": "New Title"},
|
|
471
|
-
"sheets": [{"properties": {"title": "New", "sheetId": 1}}],
|
|
472
|
-
}
|
|
473
|
-
|
|
474
|
-
def get(self, spreadsheetId, fields):
|
|
475
|
-
return self._Request()
|
|
476
|
-
|
|
477
|
-
def spreadsheets(self):
|
|
478
|
-
return self._Spreadsheets()
|
|
463
|
+
_NEW_SHEET = {
|
|
464
|
+
"properties": {"title": "New Title"},
|
|
465
|
+
"sheets": [{"properties": {"title": "New", "sheetId": 1}}],
|
|
466
|
+
}
|
|
479
467
|
|
|
480
468
|
|
|
481
469
|
class _FakeDriveService:
|
|
@@ -500,7 +488,9 @@ class TestFetchSheetsModifiedTimeValidation:
|
|
|
500
488
|
cache = SheetStructureCache(db_path=DB, ttl=1000)
|
|
501
489
|
cache.store("sid", [SheetInfo(title="Old", sheet_id=0)], modified_time="v1")
|
|
502
490
|
|
|
503
|
-
result = await fetch_sheets(
|
|
491
|
+
result = await fetch_sheets(
|
|
492
|
+
FakeSheetsService(result=_NEW_SHEET), "sid", cache, _FakeDriveService()
|
|
493
|
+
)
|
|
504
494
|
|
|
505
495
|
assert len(result) == 1
|
|
506
496
|
assert result[0].title == "New"
|
|
@@ -510,8 +500,8 @@ class TestFetchSheetsModifiedTimeValidation:
|
|
|
510
500
|
cache.store("sid", [SheetInfo(title="Old", sheet_id=0)], modified_time="v1")
|
|
511
501
|
|
|
512
502
|
# No drive_service passed → no modifiedTime check → TTL-valid cache hit,
|
|
513
|
-
# even though
|
|
514
|
-
result = await fetch_sheets(
|
|
503
|
+
# even though the Sheets fake would return a different sheet.
|
|
504
|
+
result = await fetch_sheets(FakeSheetsService(result=_NEW_SHEET), "sid", cache)
|
|
515
505
|
|
|
516
506
|
assert result[0].title == "Old"
|
|
517
507
|
|
|
@@ -533,7 +523,7 @@ class TestGetSheetIdModifiedTimePropagation:
|
|
|
533
523
|
cache.mark_dirty("sid") # force the next lookup to be a genuine miss
|
|
534
524
|
|
|
535
525
|
sheet_id = await _get_sheet_id(
|
|
536
|
-
|
|
526
|
+
FakeSheetsService(result=_NEW_SHEET), "sid", "New", cache, _FakeDriveService()
|
|
537
527
|
)
|
|
538
528
|
|
|
539
529
|
assert sheet_id == 1
|
|
@@ -166,6 +166,11 @@ class TestAuthStatusResource:
|
|
|
166
166
|
assert "create_spreadsheet" in quota["tools"]
|
|
167
167
|
assert "transfer_ownership" not in quota["tools"]
|
|
168
168
|
assert "storage quota" in quota["reason"].lower()
|
|
169
|
+
# sync_folder is listed by exact name, with its direction caveat in the
|
|
170
|
+
# reason text (#516); guard the caveat against a reword dropping it (#526).
|
|
171
|
+
assert "sync_folder" in quota["tools"]
|
|
172
|
+
assert "sync_folder" in quota["reason"]
|
|
173
|
+
assert "upload and bidirectional" in quota["reason"]
|
|
169
174
|
assert quota["alternatives"] is not None
|
|
170
175
|
|
|
171
176
|
def test_service_account_transfer_ownership_limitation(self):
|
{mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/.claude/commands/cleanup-worktrees.md
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/.claude/commands/notify-partner.md
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/.claude/commands/roadmap-status.md
RENAMED
|
File without changes
|
{mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/.claude/commands/start-worktree.md
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/.github/ISSUE_TEMPLATE/bug_report.md
RENAMED
|
File without changes
|
{mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/.github/ISSUE_TEMPLATE/feature_request.md
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/docs/decisions/decision-chart-theming.md
RENAMED
|
File without changes
|
|
File without changes
|
{mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/docs/decisions/decision-composite-tools.md
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/docs/decisions/decision-dev-team.md
RENAMED
|
File without changes
|
{mcp_gee_sweet-0.9.2.dev238 → mcp_gee_sweet-0.9.2.dev243}/docs/decisions/decision-docs-formatting.md
RENAMED
|
File without changes
|
|
File without changes
|