skills/google-workspace-guide/SKILL.md
Access Google Workspace (Gmail, Calendar, Drive, Docs, Sheets, Slides). Use when asked to "check email", "read inbox", "get calendar", "what's on my schedule", "find in drive", "search drive", "read doc", "open spreadsheet", "show slides", "list emails", "emails from [person]", "send email", "reply to", "schedule meeting", "create event", "cancel meeting", or any query about the user's Google account data. Handles email, calendar, documents, and files.
npx skillsauth add jitsusama/agentic-harness.pi google-workspace-guideInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Access Gmail, Google Calendar, and Google Drive through the google tool. Translates natural language requests into structured API calls.
When composing emails, drafts or event descriptions on behalf of the user, follow the user's writing voice and prose style guides. The text should sound like the user wrote it.
User must run google-auth once to authenticate. Credentials persist in session.
When the user makes a natural language request, extract the core intent and parameters, then call the appropriate action.
→ search_emails with query: "in:inbox", limit: 10-25
→ search_emails with query: "from:[email protected]"
→ search_emails with query: "topic"
"from:alice subject:budget" for "emails from Alice about budget"→ get_email with id from previous search results
search_emails response→ get_thread with message_id from current email context
→ send_email with:
to: extract from current email context (the sender)subject: "RE: " + original subjectbody: user's message textreply_to: current email ID→ create_draft with:
to: parse email address(es)subject: infer from topic or ask userbody: compose based on user's description→ send_email with:
to: parse email address(es)subject: infer or extractbody: user's message→ delete_email or archive_email with id from context
→ unarchive_email with id from context
→ mark_read or mark_unread with id from context
Build queries using these operators:
from:email - senderto:email - recipientsubject:text - subject line containsafter:YYYY-MM-DD / before:YYYY-MM-DD - date rangenewer_than:7d / older_than:30d - relative dateshas:attachment - has attachmentsis:unread / is:starred - statusfrom:alice subject:budgetfrom:alice OR from:bobToday:
→ list_events with start: "today", end: "today"
Tomorrow:
→ list_events with start: "tomorrow", calculate end as tomorrow EOD
This week / next week:
→ list_events with calculated start (Monday) and end (Sunday)
Specific date:
→ list_events with start: "YYYY-MM-DD", end: "YYYY-MM-DD"
→ list_events for upcoming week, look for ⏳ (needsAction) in results
→ get_event with event_id from previous list results
→ create_event with:
summary: meeting name/purposestart/end: parse time expressions to ISO datetimeattendees: parse email addresseslocation: extract if mentionedTime parsing:
→ Two steps:
list_events to find the meeting at specified timeupdate_event with new start/end times→ delete_event with event_id from context or search
→ respond_to_event with response: "accepted" or "declined"
→ update_event with updated attendees list (append to existing)
→ list_events with:
calendar_id: person's email addressstart/end: time windowThis shows full event details when the person has shared their calendar with
you or org-wide event visibility is enabled. If it fails with a permission
error, fall back to check_availability which only needs free/busy access.
→ check_availability with:
attendees: email addresses to checkstart/end: time window to checkYour calendar is included automatically. The response shows each person's busy blocks and common free slots where everyone (including you) is available. Supports up to 49 attendees.
The time window can span multiple days (e.g. an entire week). When the user asks to "find a time this week" or "look across next week", use a single call with the full date range rather than one call per day.
→ Two steps:
check_availability to find common free slotscreate_event with the chosen slot and attendeesPick the first free slot that meets the requested duration. When scanning a
multi-day range, filter free slots to business hours and skip weekends unless
the user says otherwise. The confirmation gate on create_event gives the
user final say before invitations go out.
→ list_files (no params = recent files, default limit 25)
→ list_files with:
query: topic keywordstype: map to "doc", "sheet", "slides", "pdf"File type mapping:
type: "doc"type: "sheet"type: "slides"type: "pdf"→ get_file with:
url: if user provided Google URLid: if referencing previous search result→ list_files with owner: "me"
→ list_files with shared: true, owner: "[email protected]"
→ list_files with modified_after: "YYYY-MM-DD" (and optionally modified_before). Dates are YYYY-MM-DD; the API converts to RFC 3339 internally.
→ Two steps:
list_shared_drives to find team driveslist_files with shared_drive_id and query→ get_file with:
include_comments: truecomments_filter: "unresolved" (default to unresolved unless user says "all")Email context:
After search_emails, remember the message list. User may say:
Calendar context:
After list_events, remember the event list. User may say:
Drive context:
After list_files, remember the file list. User may say:
Track what the user is currently working with:
This enables follow-ups like:
Always use ISO datetime with timezone:
"2026-03-10T14:00:00-05:00" (EDT)"2026-03-10T15:00:00-04:00" (EST)Get timezone from system or default to user's location.
If user doesn't specify end time:
Response includes:
details.messages[] array with full metadatadetails.nextPageToken if more results existUse message IDs for follow-up actions (get_email, delete, reply).
Response includes:
details.events[] array with full metadataUse event IDs for follow-up actions (update, delete, respond).
Response includes:
details.files[] arrayUse file IDs or URLs for follow-up (get_file).
Docs/Sheets/Slides rendered as markdown:
Comments (if requested) appear at the end with status and replies.
Google Docs can contain multiple tabs (like sheets in a spreadsheet). The get_file action automatically fetches all tabs in a document:
## 📑 Tab Title heading with a tab ID. Child tabs use deeper headings (###, ####, #####) matching their nesting level. A tab count appears in the metadata.tab=t.XXXXX parameter (from "Copy link" on a tab), the response includes a "Linked tab" hint identifying which tab was referenced.details.content.tabs[], each with id, title, body, and nestingLevel (0 for root tabs, 1+ for subtabs).When the user asks about a specific tab, reference it by its title. When summarising a multi-tab document, mention all tabs by name.
DON'T ask user for email addresses when replying - extract from context:
// ❌ Bad: asking for recipient
"Who should I send this to?"
// ✅ Good: infer from context
google({
action: "send_email",
to: [current_email_sender],
subject: "RE: " + current_email_subject,
reply_to: current_email_id
})
DON'T make user specify exact ISO datetime format - parse natural language:
// ❌ Bad: asking for ISO format
"Please provide the start time in ISO 8601 format"
// ✅ Good: parse "tomorrow at 2pm"
google({
action: "create_event",
start: "2026-03-10T14:00:00-05:00", // calculated from "tomorrow at 2pm"
end: "2026-03-10T14:30:00-05:00" // inferred 30min duration
})
DON'T forget to use page tokens when user asks for "more results":
// User: "show me more"
google({
action: "search_emails",
query: same_query_as_before,
page_token: previous_response_details.nextPageToken
})
Default limit: 25 results. If response includes nextPageToken:
// First page
const result1 = google({ action: "search_emails", query: "..." })
// If user asks for more
const result2 = google({
action: "search_emails",
query: same_query,
page_token: result1.details.nextPageToken
})
If user has multiple Google accounts:
account: "personal"google-auth --listdevelopment
Structure of a quest README and the documents that live under it: frontmatter shape, the four core and four optional body sections, emoji glyphs, ID format, alias notation, Cast bullets and Journey entries. Use when writing or editing a quest README, a plan, research, brief or report document under a quest. Pairs with quest-convention for choices like kind, promotion and reordering. Follow the prose-standard for voice.
tools
Operational conventions for the quest system: when to use a quest versus a subquest versus a sidequest, when to scaffold a plan or research document, how to reorder priorities, when to add optional sections, when to conclude versus retire, the resuscitate pattern. Use when driving the quest tool, deciding kind, promoting or parking work, or organising a project as quests. Pairs with quest-format for the on-disk shape.
development
Markdown structure rules: Title Case headings with their exceptions, the line-width target and its legitimate exceptions, reference-style links, fenced code blocks with language tags, tables and lists. Use when writing or editing any markdown file (README, AGENTS, docs, plans, skill files), or when adding a heading, link, table or code block. Owns markdown structure; pairs with prose-standard, which owns voice, grammar, spelling and punctuation.
tools
How to measure whether convention corrections keep recurring in the pi session logs, by category and by week. Use to record a baseline before the convention gates take effect and to re-run afterwards to confirm the recurring categories bend down. Pairs with the convention gates (pr-guardian, issue-guardian, commit-guardian, slack-integration) and the convention-context extension.