[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill:01M69915F8591SQBA1ZHK6NDA7":3,"skill-same:01M69915F8591SQBA1ZHK6NDA7":68},{"skillNo":4,"name":5,"nameZh":6,"description":7,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":16,"categories":17,"keywords":18,"publishedAt":39,"linkOnly":40,"markdownContent":41,"markdownBody":42,"frontMatter":43,"refName":44,"commitSha":45,"skillDirectory":46,"forksCount":47,"repositoryHtmlUrl":48,"repositoryHomepageUrl":49,"ownerHtmlUrl":50,"ownerBlogUrl":51,"ownerDescription":52,"updatedAt":53,"install":54,"resources":67},"01M69915F8591SQBA1ZHK6NDA7","google-workspace",null,"Read\u002Fwrite Google Drive, Docs, Sheets, Gmail, Calendar, Chat, Tasks, Slides via the `gws` CLI. Needs an OAuth client or service account configured first. Use for any Google Workspace task.","google\u002Fadk-recipes","google","https:\u002F\u002Favatars.githubusercontent.com\u002Fu\u002F1342004?v=4","01M698VYEFDM0VJBZDYYJ2T83S","Google","01M6990124CWS5627VKGSKJXRD",10614,"Apache-2.0","https:\u002F\u002Fgithub.com\u002Fgoogle\u002Fadk-recipes\u002Fblob\u002Fmain\u002Fcore\u002Fpython\u002Flong-horizon-harness\u002Fhorizon\u002Fbuiltin_skills\u002Fgoogle-workspace\u002FSKILL.md",[],[9,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38],"google workspace","workspace","for","gmail","sheets","client","via","chat","gws","task","calendar","use","write","read","slides","first","OAuth","tasks","docs","cli","2026-11-10 10:12:30",false,"---\nname: google-workspace\ndescription: Read\u002Fwrite Google Drive, Docs, Sheets, Gmail, Calendar, Chat, Tasks, Slides via the `gws` CLI. Needs an OAuth client or service account configured first. Use for any Google Workspace task.\n---\n# Google Workspace via `gws`\n\n`gws` (install: `npm install -g @googleworkspace\u002Fcli`; this skill targets\n**0.22.x**) speaks every Workspace API the user is likely to ask about —\nDrive, Docs, Sheets, Gmail, Calendar, Chat, Tasks, Slides, Keep, Forms, Apps\nScript, Meet. It is a community CLI (\"not an\nofficially supported Google product\"). It wraps the REST APIs so you don't\nhand-roll `curl`, but it does **not** bundle credentials: you must configure\nan OAuth client or service account first, then authenticate.\n\n## When to use this skill\n\nAny user request that names a Google Workspace surface:\n- \"read this Drive file\", \"list my Drive folder\", \"share this doc\"\n- \"create a Google Doc with…\", \"update this sheet\", \"append a row\"\n- \"send an email\", \"draft a Gmail reply\", \"check my inbox for…\"\n- \"what's on my calendar\", \"schedule a meeting\"\n- \"post to this Chat space\"\n\nDo NOT shell out to `curl` against the raw Workspace REST APIs — `gws`\nalready wraps them and handles pagination, retries, and JSON parsing.\n\n## Auth\n\n### First, always: try the pre-injected token\n\nBefore any OAuth client, service account, or login dance, check for\na **pre-injected access token**. When the user has used **\"Connect Workspace\"**\nin the web UI, the `GOOGLE_WORKSPACE_CLI_TOKEN` secret is auto-injected into\nevery `bash` command and is `gws`'s **highest-priority** auth source — no\nOAuth client, no login, no loopback bridge. **Don't ask, don't inspect the\nenvironment, just probe with a cheap read against the surface you need:**\n\n```\nbash(command=\"export GOOGLE_WORKSPACE_CLI_CONFIG_DIR=\u002Fworkspace\u002Flha\u002Fconfig\u002Fgws GOOGLE_WORKSPACE_CLI_KEYRING_BACKEND=file && gws gmail messages list --params '{\\\"maxResults\\\": 1}'\")\n```\n\n- If it returns data, you're done — the token works. Proceed with the task.\n- If it **`403`s \u002F scope error**, the token is valid but the user didn't grant\n  this surface (or only read-only). The \"Connect Workspace\" flow is per-surface\n  (drive \u002F gmail \u002F calendar \u002F sheets \u002F docs \u002F chat \u002F tasks \u002F slides \u002F keep \u002F\n  script \u002F meet \u002F forms) × read-only|read-write,\n  **read-only by default** — ask the user to reconnect with the surface (or\n  read-write) you need; don't start a login dance.\n- If it **`401`s \u002F \"not logged in\"**, the token is truly missing or lapsed (it's\n  a ~1h token, no refresh) — *then* fall back to the OAuth-client login below, or\n  ask the user to re-click \"Connect Workspace\".\n\n**Do NOT trust `gws auth status` here.** With the env token it reports\n`auth_method: none` \u002F `credential_source: token_env_var` even though reads\nsucceed — a real read is the only reliable check. Skipping the probe and\njumping to `gws auth login` is the mistake to avoid: it drags the user through\na browser-redirect bridge that the already-present token makes unnecessary.\n\n### Otherwise: configure an OAuth client\n\n`gws` has **no embedded OAuth client**. `gws auth login` fails until one of\nthese is in place:\n\n1. **`client_secret.json`** — drop an OAuth client secret file into the gws\n   config dir. **Preferred in the sandbox** — see \"Creating the OAuth client\"\n   below. Keep that dir on `\u002Fworkspace` (next section) so it survives.\n2. **Env vars** — set `GOOGLE_WORKSPACE_CLI_CLIENT_ID` and\n   `GOOGLE_WORKSPACE_CLI_CLIENT_SECRET`.\n3. **Service account** — set `GOOGLE_APPLICATION_CREDENTIALS=\u002Fpath\u002Fto\u002Fkey.json`.\n   No `gws auth login` step is needed in this mode; calls authenticate\n   directly with the key (use for fully unattended automation, no user present).\n4. **`gws auth setup`** — provisions a GCP project + OAuth client for you, but\n   renders a **full-screen interactive TUI** that can't be driven from\n   `bash` (no keyboard input). Don't use it headless — create the\n   `client_secret.json` by hand (option 1) instead.\n\n### Creating the OAuth client (one-time)\n\nWhen building the OAuth client yourself (via `gcloud` or the Cloud console):\n\n- Choose the **\"Desktop app\"** client type, **not** \"Web application\". `gws\n  auth login` listens on a random loopback port; a Web-app client rejects that\n  with `Error 400: redirect_uri_mismatch`. Desktop-app clients accept loopback.\n- For a corporate Workspace org, set the consent-screen audience to\n  **Internal**. An internal-audience client clears the Context-Aware Access \u002F\n  \"Account restricted\" block that the default gcloud OAuth client trips on.\n- The `client_secret.json`'s `project_id` must be a project the authenticating\n  account may use — otherwise calls fail with the quota-project 403 (see\n  Failure handling).\n\n### Config dir + keyring (sandbox gotchas)\n\nSet both on **every** `gws` command — each `bash` call is a fresh non-login\nshell, so exports don't carry over between calls:\n\n```\nexport GOOGLE_WORKSPACE_CLI_CONFIG_DIR=\u002Fworkspace\u002Flha\u002Fconfig\u002Fgws\nexport GOOGLE_WORKSPACE_CLI_KEYRING_BACKEND=file\n```\n\n- `GOOGLE_WORKSPACE_CLI_CONFIG_DIR` — relocates the config dir (which holds\n  `client_secret.json`, the token, and the encryption key) onto `\u002Fworkspace`.\n  The default `~\u002F.config\u002Fgws` is not migrated on runtime upgrades, so without\n  this you'd redo the browser login after the next upgrade.\n- `GOOGLE_WORKSPACE_CLI_KEYRING_BACKEND=file` — there's no OS keyring headless;\n  the file backend writes the key into the config dir (hence also on\n  `\u002Fworkspace`).\n\n### Authenticating\n\nWith an OAuth client configured, authenticate with:\n\n```\nbash(command=\"gws auth login --readonly\")  # read-only scopes (start here)\nbash(command=\"gws auth login\")             # all default scopes\nbash(command=\"gws auth login -s drive,gmail,sheets\")  # limit the picker\nbash(command=\"gws auth login --scopes \u003Ccomma,separated,scopes>\")\n```\n\n**Start with read-only scopes** (`--readonly`) — least privilege. Add write\nscopes only when the task actually needs to write, by re-running `gws auth\nlogin --scopes \u003C…>` for the specific surface; Google's incremental consent\nmerges the new scope with what's already granted, so you never over-grant\nup front.\n\n`gws auth login` is **loopback-browser only** — it opens a browser and waits on\na local listener for the OAuth redirect. There is NO device-code \u002F\npaste-a-code flag.\n\n**Completing the loopback flow headless.** It *does* work in the sandbox — you\nbridge the redirect by hand. The browser opens on the *user's* machine, but the\nlistener is inside the container, so you relay the redirect URL across. The URL\nappears in one turn and the user pastes back in a later turn, so use the\ncross-turn process pipeline (`process(action='spawn', ...)` + `process`):\n\n```\nprocess(action='spawn', command=\"export GOOGLE_WORKSPACE_CLI_CONFIG_DIR=\u002Fworkspace\u002Flha\u002Fconfig\u002Fgws GOOGLE_WORKSPACE_CLI_KEYRING_BACKEND=file && gws auth login\")\n# read the printed auth URL, surface it VERBATIM to the user, then wait\nprocess(action='poll', session_id=\u003Cid>)\n```\n\n1. The user opens the auth URL, consents, and their browser lands on a\n   `http:\u002F\u002Flocalhost:\u003Cport>\u002F?code=...&scope=...` page that **won't load** (the\n   listener is in the sandbox, not on their machine). Ask them to copy that\n   **full redirect URL** from the address bar and paste it back.\n2. `curl` that exact URL from inside the sandbox to hand the code to the waiting\n   listener, which completes the exchange and writes the token:\n   ```\n   bash(command=\"curl -s 'http:\u002F\u002Flocalhost:\u003Cport>\u002F?code=...&scope=...'\")\n   ```\n   The backgrounded `gws auth login` then finishes.\n\n(`localhost` is on Layer A's allowlist, so this `curl` isn't gated.) For\n**fully unattended** jobs where no user is present to paste the redirect, use a\nservice account instead — the loopback bridge needs a live user.\n\nCheck state any time with `bash(command=\"gws auth status\")`.\n\n## Command form\n\n```\ngws \u003Cservice> \u003Cresource> [sub-resource] \u003Cmethod> [flags]\n```\n\nQuery parameters are passed as **JSON** via `--params`; request bodies via\n`--json`. Wrap both in single quotes so the shell preserves inner double\nquotes:\n\n```\ngws drive files list --params '{\"pageSize\": 5, \"q\": \"trashed=false\"}'\ngws drive files create --json '{\"name\": \"notes.txt\"}' --upload notes.txt\n```\n\nSeveral common operations also have ergonomic **helper commands** (prefixed\n`+`) that take plain flags instead of JSON — e.g. `gws sheets +read`,\n`gws sheets +append`, `gws gmail +send`, `gws drive +upload`.\n\nUseful global flags: `--format json|table|yaml|csv` (json default),\n`--dry-run` (validate without hitting the API), `--page-all` (auto-paginate\nto NDJSON), `-o\u002F--output \u003Cpath>` (save binary responses),\n`--sanitize \u003Ctemplate>` (screen responses through Model Armor).\n\n### Discovery — don't guess params\n\n```\nbash(command=\"gws drive --help\")                 # resources + methods\nbash(command=\"gws schema drive.files.list\")      # params, types, defaults\n```\n\n`gws schema \u003Cservice>.\u003Cresource>.\u003Cmethod>` prints the exact param shape for a\nmethod. Run it (or `gws \u003Cservice> --help`) whenever you're unsure of a flag —\nit's authoritative for the installed version.\n\n### Prefer the official per-surface skills for depth\n\nThe tables below are a quick reference, not the full surface. `gws` ships\n**maintained per-surface skills upstream** — `gws-drive`, `gws-gmail`,\n`gws-sheets`, `gws-calendar`, `gws-chat`, `gws-people`, `gws-slides`,\n`gws-tasks`, plus curated recipes — that cover the less-common methods this file\ndoesn't. For anything beyond the common operations, install the relevant skill\ninto the workspace and read it (note the path the command prints):\n\n```\nbash(command=\"npx -y skills add https:\u002F\u002Fgithub.com\u002Fgoogleworkspace\u002Fcli\u002Ftree\u002Fmain\u002Fskills\u002Fgws-drive\")\n```\n\nSwap `gws-drive` for the surface you need. These are version-pinned docs, so when\na skill and the installed binary disagree, `gws schema \u003Cservice>.\u003Cresource>.\u003Cmethod>`\nwins — it reflects the gws actually on PATH.\n\n## Common operations\n\n| You want to… | Command |\n|---|---|\n| List Drive files | `gws drive files list --params '{\"q\": \"trashed=false\", \"pageSize\": 20}'` |\n| Get Drive file metadata | `gws drive files get --params '{\"fileId\": \"ID\", \"fields\": \"id,name,mimeType\"}'` |\n| Download file contents | `gws drive files get --params '{\"fileId\": \"ID\", \"alt\": \"media\"}' -o out.bin` |\n| Export a Doc\u002FSheet | `gws drive files export --params '{\"fileId\": \"ID\", \"mimeType\": \"text\u002Fplain\"}' -o out.txt` |\n| Read a Sheet range | `gws sheets +read --spreadsheet ID --range \"Sheet1!A1:D10\"` |\n| Append a Sheet row | `gws sheets +append --spreadsheet ID --range Sheet1 --json '{\"values\": [[\"a\",\"b\"]]}'` |\n| List Gmail messages | `gws gmail messages list --params '{\"q\": \"from:alice@example.com newer_than:7d\", \"maxResults\": 10}'` |\n| Get a Gmail message | `gws gmail messages get --params '{\"id\": \"MSG_ID\", \"format\": \"full\"}'` |\n| Send mail | `gws gmail +send --to alice@example.com --subject 'Hi' --body 'Hello!'` |\n| List Calendar events | `gws calendar events list --params '{\"calendarId\": \"primary\", \"maxResults\": 10}'` |\n| List Tasks task lists | `gws tasks tasklists list` |\n| List tasks in a list | `gws tasks tasks list --params '{\"tasklist\": \"TASKLIST_ID\"}'` |\n| Add a task | `gws tasks tasks insert --params '{\"tasklist\": \"TASKLIST_ID\"}' --json '{\"title\": \"Buy milk\"}'` |\n| Read a presentation | `gws slides presentations get --params '{\"presentationId\": \"ID\"}'` |\n| List Keep notes | `gws keep notes list` |\n| Get a Form + its responses | `gws forms forms get --params '{\"formId\": \"ID\"}'` · `gws forms forms responses list --params '{\"formId\": \"ID\"}'` |\n\nConfirm the exact params with `gws schema …` before write\u002Fdelete calls — the\nshapes above are the common cases, not an exhaustive contract.\n\n**Keep caveat:** the Google Keep API is restricted to Workspace Enterprise\ndomains via service-account domain-wide delegation — an ordinary\nConnect-Workspace access token will likely `403` on `gws keep …` regardless of\nthe granted scope. If it does, tell the user it's an API restriction, not a\nmissing grant.\n\n**Under a routine, the Google token is present only if you declared its secret\nname (`GOOGLE_WORKSPACE_CLI_TOKEN`) in the routine's `secrets:`.**\n\n### Google Chat — unread state & sender names\n\nTwo Chat limitations are scope-bound, so check before promising the user a\nresult:\n\n- **\"Unread messages\" needs an extra scope, but `gws` has a native command for\n  it.** The default read scopes (`chat.spaces.readonly` +\n  `chat.messages.readonly`) let you list spaces and read messages but do **not**\n  expose per-space unread state — read-position needs\n  `chat.users.readstate.readonly`. With that scope, **don't hand-roll REST**:\n  `gws` wraps the read-state endpoint as\n  `gws chat users spaces getSpaceReadState`. Compute unread per space yourself —\n  there's no single \"list unread\" call:\n  1. List the spaces you care about (DMs\u002Fgroup chats sort by `lastActiveTime`):\n     ```\n     gws chat spaces list --params '{\"pageSize\": 1000}'\n     ```\n  2. For each space, read your last-read mark and its latest message, then\n     compare:\n     ```\n     gws chat users spaces getSpaceReadState --params '{\"name\": \"users\u002Fme\u002Fspaces\u002F\u003Cspace>\u002FspaceReadState\"}'\n     gws chat spaces messages list --params '{\"parent\": \"spaces\u002F\u003Cspace>\", \"pageSize\": 1, \"orderBy\": \"create_time DESC\"}'\n     ```\n     A space is **unread** when the latest message's `createTime` is strictly\n     newer than the read state's `lastReadTime` **and** that message's\n     `sender.name` (a `users\u002F\u003Cid>`) isn't you. (`orderBy` is `create_time DESC` —\n     snake_case field + `ASC`\u002F`DESC`, not `createTime desc`.)\n  - Resolving **your own** `users\u002F\u003Cid>` to skip self-authored messages is the one\n    snag: `people\u002Fme` 403s under the Connect-Workspace token, so pass your id in\n    explicitly (read it off any message you sent, or `gws people people get`\n    once) rather than relying on `me`.\n  - Resolve the remaining sender ids to names with `gws people people getBatchGet`\n    (see the People note above).\n  - **Without the read-state scope** you can't get true unread — be upfront, then\n    either re-auth to add it\n    (`gws auth login --scopes https:\u002F\u002Fwww.googleapis.com\u002Fauth\u002Fchat.users.readstate.readonly`;\n    incremental consent merges it) or fall back to **recency as a proxy** (list\n    spaces by `lastActiveTime`, pull each space's latest messages). Say which.\n- **Senders come back as user IDs, not names.** Chat returns each sender as\n  `users\u002F\u003Cnumeric-id>`; the Chat read scopes don't resolve those to display\n  names. The People API does (`gws` wraps it as `gws people …`), and \"Connect\n  Workspace\" now offers the grant as a separate **Directory** surface (scope\n  `directory.readonly` — read-only regardless of the read-write toggle). To turn\n  IDs into names:\n  - If a `gws people` read 403s, the user hasn't ticked **Directory** — ask them\n    to reconnect with it.\n  - **Resolve in batch — a Chat thread has many distinct senders, so collect all\n    the `\u003Cnumeric-id>`s from `users\u002F\u003Cnumeric-id>` and resolve them in one\n    `getBatchGet` call, not one `get` per ID** (the People resource id is the\n    `\u003Cnumeric-id>`):\n    ```\n    gws people people getBatchGet --params '{\"resourceNames\": [\"people\u002F\u003Cid1>\", \"people\u002F\u003Cid2>\"], \"personFields\": \"names,emailAddresses\"}'\n    ```\n    Read each name off `responses[].person.names[0].displayName`. Use single-ID\n    `gws people people get --params '{\"resourceName\": \"people\u002F\u003Cnumeric-id>\", \"personFields\": \"names\"}'`\n    only for a one-off lookup. If a sender can't be seen as a domain member, fall\n    back to `gws people people searchDirectoryPeople --params '{\"query\": \"\u003Cname-or-email>\", \"readMask\": \"names,emailAddresses\"}'`.\n  - Confirm exact param shapes with `gws schema people.people.get` before relying\n    on them.\n\n## Failure handling\n\n- `No OAuth client configured` → set up credentials first (see Auth: drop a\n  `client_secret.json`, set the `GOOGLE_WORKSPACE_CLI_CLIENT_*` env vars, or use\n  a service account).\n- `gws auth login` hangs on the listener → expected headless; bridge the\n  loopback by relaying the redirect URL and `curl`-ing it back in (see Auth →\n  \"Completing the loopback flow headless\"). Only fall back to a service account\n  when no user is present to paste the redirect.\n- Login token doesn't persist \u002F \"no keyring\" error, or asked to log in again\n  after a runtime upgrade → set both `GOOGLE_WORKSPACE_CLI_CONFIG_DIR=\u002Fworkspace\u002Flha\u002Fconfig\u002Fgws`\n  and `GOOGLE_WORKSPACE_CLI_KEYRING_BACKEND=file` on every `gws` call (config +\n  key then live on `\u002Fworkspace`, which persists; `~\u002F.config\u002Fgws` does not).\n- `Error 400: redirect_uri_mismatch` on login → the OAuth client is a\n  \"Web application\" type; recreate it as **Desktop app** (loopback ports).\n- `access_denied` \u002F \"Account restricted\" on consent → Context-Aware Access on\n  the OAuth client. Use an **Internal**-audience client (see Auth → \"Creating\n  the OAuth client\").\n- 401 \u002F `not logged in` → if `GOOGLE_WORKSPACE_CLI_TOKEN` is the auth source,\n  the ~1h token has lapsed — ask the user to re-click \"Connect Workspace\" (it\n  has no refresh). Otherwise the OAuth login expired; re-run `gws auth login`.\n- `gws auth status` shows `auth_method: none` but reads work → expected when\n  the env token (`GOOGLE_WORKSPACE_CLI_TOKEN`) is the source; `auth status`\n  doesn't reflect it. Confirm with a real read, not `auth status`.\n- **403, scope too narrow** → the granted scopes don't cover the call. If the\n  auth source is the `GOOGLE_WORKSPACE_CLI_TOKEN` env token, the \"Connect\n  Workspace\" grant was per-surface and read-only by default — ask the user to\n  reconnect including the surface (or read-write) you need. On the OAuth-login\n  path instead, re-auth with the needed scope: `gws auth login --scopes \u003C…>`\n  (Google's incremental consent merges it with what was already granted).\n- **403, `serviceUsageConsumer` \u002F quota project** → distinct from a scope 403:\n  the `client_secret.json`'s `project_id` is a project the account can't bill\n  to. Point the client at a project the account may use (the account needs\n  `roles\u002Fserviceusage.serviceUsageConsumer` on it).\n- `gws: command not found` → not installed. `npm install -g @googleworkspace\u002Fcli`.\n- Unsure of a flag or param → `gws \u003Cservice> --help` or\n  `gws schema \u003Cservice>.\u003Cresource>.\u003Cmethod>`. Don't guess.\n\n\u003C!-- find-skills:tested:false -->\n","# Google Workspace via `gws`\n\n`gws` (install: `npm install -g @googleworkspace\u002Fcli`; this skill targets\n**0.22.x**) speaks every Workspace API the user is likely to ask about —\nDrive, Docs, Sheets, Gmail, Calendar, Chat, Tasks, Slides, Keep, Forms, Apps\nScript, Meet. It is a community CLI (\"not an\nofficially supported Google product\"). It wraps the REST APIs so you don't\nhand-roll `curl`, but it does **not** bundle credentials: you must configure\nan OAuth client or service account first, then authenticate.\n\n## When to use this skill\n\nAny user request that names a Google Workspace surface:\n- \"read this Drive file\", \"list my Drive folder\", \"share this doc\"\n- \"create a Google Doc with…\", \"update this sheet\", \"append a row\"\n- \"send an email\", \"draft a Gmail reply\", \"check my inbox for…\"\n- \"what's on my calendar\", \"schedule a meeting\"\n- \"post to this Chat space\"\n\nDo NOT shell out to `curl` against the raw Workspace REST APIs — `gws`\nalready wraps them and handles pagination, retries, and JSON parsing.\n\n## Auth\n\n### First, always: try the pre-injected token\n\nBefore any OAuth client, service account, or login dance, check for\na **pre-injected access token**. When the user has used **\"Connect Workspace\"**\nin the web UI, the `GOOGLE_WORKSPACE_CLI_TOKEN` secret is auto-injected into\nevery `bash` command and is `gws`'s **highest-priority** auth source — no\nOAuth client, no login, no loopback bridge. **Don't ask, don't inspect the\nenvironment, just probe with a cheap read against the surface you need:**\n\n```\nbash(command=\"export GOOGLE_WORKSPACE_CLI_CONFIG_DIR=\u002Fworkspace\u002Flha\u002Fconfig\u002Fgws GOOGLE_WORKSPACE_CLI_KEYRING_BACKEND=file && gws gmail messages list --params '{\\\"maxResults\\\": 1}'\")\n```\n\n- If it returns data, you're done — the token works. Proceed with the task.\n- If it **`403`s \u002F scope error**, the token is valid but the user didn't grant\n  this surface (or only read-only). The \"Connect Workspace\" flow is per-surface\n  (drive \u002F gmail \u002F calendar \u002F sheets \u002F docs \u002F chat \u002F tasks \u002F slides \u002F keep \u002F\n  script \u002F meet \u002F forms) × read-only|read-write,\n  **read-only by default** — ask the user to reconnect with the surface (or\n  read-write) you need; don't start a login dance.\n- If it **`401`s \u002F \"not logged in\"**, the token is truly missing or lapsed (it's\n  a ~1h token, no refresh) — *then* fall back to the OAuth-client login below, or\n  ask the user to re-click \"Connect Workspace\".\n\n**Do NOT trust `gws auth status` here.** With the env token it reports\n`auth_method: none` \u002F `credential_source: token_env_var` even though reads\nsucceed — a real read is the only reliable check. Skipping the probe and\njumping to `gws auth login` is the mistake to avoid: it drags the user through\na browser-redirect bridge that the already-present token makes unnecessary.\n\n### Otherwise: configure an OAuth client\n\n`gws` has **no embedded OAuth client**. `gws auth login` fails until one of\nthese is in place:\n\n1. **`client_secret.json`** — drop an OAuth client secret file into the gws\n   config dir. **Preferred in the sandbox** — see \"Creating the OAuth client\"\n   below. Keep that dir on `\u002Fworkspace` (next section) so it survives.\n2. **Env vars** — set `GOOGLE_WORKSPACE_CLI_CLIENT_ID` and\n   `GOOGLE_WORKSPACE_CLI_CLIENT_SECRET`.\n3. **Service account** — set `GOOGLE_APPLICATION_CREDENTIALS=\u002Fpath\u002Fto\u002Fkey.json`.\n   No `gws auth login` step is needed in this mode; calls authenticate\n   directly with the key (use for fully unattended automation, no user present).\n4. **`gws auth setup`** — provisions a GCP project + OAuth client for you, but\n   renders a **full-screen interactive TUI** that can't be driven from\n   `bash` (no keyboard input). Don't use it headless — create the\n   `client_secret.json` by hand (option 1) instead.\n\n### Creating the OAuth client (one-time)\n\nWhen building the OAuth client yourself (via `gcloud` or the Cloud console):\n\n- Choose the **\"Desktop app\"** client type, **not** \"Web application\". `gws\n  auth login` listens on a random loopback port; a Web-app client rejects that\n  with `Error 400: redirect_uri_mismatch`. Desktop-app clients accept loopback.\n- For a corporate Workspace org, set the consent-screen audience to\n  **Internal**. An internal-audience client clears the Context-Aware Access \u002F\n  \"Account restricted\" block that the default gcloud OAuth client trips on.\n- The `client_secret.json`'s `project_id` must be a project the authenticating\n  account may use — otherwise calls fail with the quota-project 403 (see\n  Failure handling).\n\n### Config dir + keyring (sandbox gotchas)\n\nSet both on **every** `gws` command — each `bash` call is a fresh non-login\nshell, so exports don't carry over between calls:\n\n```\nexport GOOGLE_WORKSPACE_CLI_CONFIG_DIR=\u002Fworkspace\u002Flha\u002Fconfig\u002Fgws\nexport GOOGLE_WORKSPACE_CLI_KEYRING_BACKEND=file\n```\n\n- `GOOGLE_WORKSPACE_CLI_CONFIG_DIR` — relocates the config dir (which holds\n  `client_secret.json`, the token, and the encryption key) onto `\u002Fworkspace`.\n  The default `~\u002F.config\u002Fgws` is not migrated on runtime upgrades, so without\n  this you'd redo the browser login after the next upgrade.\n- `GOOGLE_WORKSPACE_CLI_KEYRING_BACKEND=file` — there's no OS keyring headless;\n  the file backend writes the key into the config dir (hence also on\n  `\u002Fworkspace`).\n\n### Authenticating\n\nWith an OAuth client configured, authenticate with:\n\n```\nbash(command=\"gws auth login --readonly\")  # read-only scopes (start here)\nbash(command=\"gws auth login\")             # all default scopes\nbash(command=\"gws auth login -s drive,gmail,sheets\")  # limit the picker\nbash(command=\"gws auth login --scopes \u003Ccomma,separated,scopes>\")\n```\n\n**Start with read-only scopes** (`--readonly`) — least privilege. Add write\nscopes only when the task actually needs to write, by re-running `gws auth\nlogin --scopes \u003C…>` for the specific surface; Google's incremental consent\nmerges the new scope with what's already granted, so you never over-grant\nup front.\n\n`gws auth login` is **loopback-browser only** — it opens a browser and waits on\na local listener for the OAuth redirect. There is NO device-code \u002F\npaste-a-code flag.\n\n**Completing the loopback flow headless.** It *does* work in the sandbox — you\nbridge the redirect by hand. The browser opens on the *user's* machine, but the\nlistener is inside the container, so you relay the redirect URL across. The URL\nappears in one turn and the user pastes back in a later turn, so use the\ncross-turn process pipeline (`process(action='spawn', ...)` + `process`):\n\n```\nprocess(action='spawn', command=\"export GOOGLE_WORKSPACE_CLI_CONFIG_DIR=\u002Fworkspace\u002Flha\u002Fconfig\u002Fgws GOOGLE_WORKSPACE_CLI_KEYRING_BACKEND=file && gws auth login\")\n# read the printed auth URL, surface it VERBATIM to the user, then wait\nprocess(action='poll', session_id=\u003Cid>)\n```\n\n1. The user opens the auth URL, consents, and their browser lands on a\n   `http:\u002F\u002Flocalhost:\u003Cport>\u002F?code=...&scope=...` page that **won't load** (the\n   listener is in the sandbox, not on their machine). Ask them to copy that\n   **full redirect URL** from the address bar and paste it back.\n2. `curl` that exact URL from inside the sandbox to hand the code to the waiting\n   listener, which completes the exchange and writes the token:\n   ```\n   bash(command=\"curl -s 'http:\u002F\u002Flocalhost:\u003Cport>\u002F?code=...&scope=...'\")\n   ```\n   The backgrounded `gws auth login` then finishes.\n\n(`localhost` is on Layer A's allowlist, so this `curl` isn't gated.) For\n**fully unattended** jobs where no user is present to paste the redirect, use a\nservice account instead — the loopback bridge needs a live user.\n\nCheck state any time with `bash(command=\"gws auth status\")`.\n\n## Command form\n\n```\ngws \u003Cservice> \u003Cresource> [sub-resource] \u003Cmethod> [flags]\n```\n\nQuery parameters are passed as **JSON** via `--params`; request bodies via\n`--json`. Wrap both in single quotes so the shell preserves inner double\nquotes:\n\n```\ngws drive files list --params '{\"pageSize\": 5, \"q\": \"trashed=false\"}'\ngws drive files create --json '{\"name\": \"notes.txt\"}' --upload notes.txt\n```\n\nSeveral common operations also have ergonomic **helper commands** (prefixed\n`+`) that take plain flags instead of JSON — e.g. `gws sheets +read`,\n`gws sheets +append`, `gws gmail +send`, `gws drive +upload`.\n\nUseful global flags: `--format json|table|yaml|csv` (json default),\n`--dry-run` (validate without hitting the API), `--page-all` (auto-paginate\nto NDJSON), `-o\u002F--output \u003Cpath>` (save binary responses),\n`--sanitize \u003Ctemplate>` (screen responses through Model Armor).\n\n### Discovery — don't guess params\n\n```\nbash(command=\"gws drive --help\")                 # resources + methods\nbash(command=\"gws schema drive.files.list\")      # params, types, defaults\n```\n\n`gws schema \u003Cservice>.\u003Cresource>.\u003Cmethod>` prints the exact param shape for a\nmethod. Run it (or `gws \u003Cservice> --help`) whenever you're unsure of a flag —\nit's authoritative for the installed version.\n\n### Prefer the official per-surface skills for depth\n\nThe tables below are a quick reference, not the full surface. `gws` ships\n**maintained per-surface skills upstream** — `gws-drive`, `gws-gmail`,\n`gws-sheets`, `gws-calendar`, `gws-chat`, `gws-people`, `gws-slides`,\n`gws-tasks`, plus curated recipes — that cover the less-common methods this file\ndoesn't. For anything beyond the common operations, install the relevant skill\ninto the workspace and read it (note the path the command prints):\n\n```\nbash(command=\"npx -y skills add https:\u002F\u002Fgithub.com\u002Fgoogleworkspace\u002Fcli\u002Ftree\u002Fmain\u002Fskills\u002Fgws-drive\")\n```\n\nSwap `gws-drive` for the surface you need. These are version-pinned docs, so when\na skill and the installed binary disagree, `gws schema \u003Cservice>.\u003Cresource>.\u003Cmethod>`\nwins — it reflects the gws actually on PATH.\n\n## Common operations\n\n| You want to… | Command |\n|---|---|\n| List Drive files | `gws drive files list --params '{\"q\": \"trashed=false\", \"pageSize\": 20}'` |\n| Get Drive file metadata | `gws drive files get --params '{\"fileId\": \"ID\", \"fields\": \"id,name,mimeType\"}'` |\n| Download file contents | `gws drive files get --params '{\"fileId\": \"ID\", \"alt\": \"media\"}' -o out.bin` |\n| Export a Doc\u002FSheet | `gws drive files export --params '{\"fileId\": \"ID\", \"mimeType\": \"text\u002Fplain\"}' -o out.txt` |\n| Read a Sheet range | `gws sheets +read --spreadsheet ID --range \"Sheet1!A1:D10\"` |\n| Append a Sheet row | `gws sheets +append --spreadsheet ID --range Sheet1 --json '{\"values\": [[\"a\",\"b\"]]}'` |\n| List Gmail messages | `gws gmail messages list --params '{\"q\": \"from:alice@example.com newer_than:7d\", \"maxResults\": 10}'` |\n| Get a Gmail message | `gws gmail messages get --params '{\"id\": \"MSG_ID\", \"format\": \"full\"}'` |\n| Send mail | `gws gmail +send --to alice@example.com --subject 'Hi' --body 'Hello!'` |\n| List Calendar events | `gws calendar events list --params '{\"calendarId\": \"primary\", \"maxResults\": 10}'` |\n| List Tasks task lists | `gws tasks tasklists list` |\n| List tasks in a list | `gws tasks tasks list --params '{\"tasklist\": \"TASKLIST_ID\"}'` |\n| Add a task | `gws tasks tasks insert --params '{\"tasklist\": \"TASKLIST_ID\"}' --json '{\"title\": \"Buy milk\"}'` |\n| Read a presentation | `gws slides presentations get --params '{\"presentationId\": \"ID\"}'` |\n| List Keep notes | `gws keep notes list` |\n| Get a Form + its responses | `gws forms forms get --params '{\"formId\": \"ID\"}'` · `gws forms forms responses list --params '{\"formId\": \"ID\"}'` |\n\nConfirm the exact params with `gws schema …` before write\u002Fdelete calls — the\nshapes above are the common cases, not an exhaustive contract.\n\n**Keep caveat:** the Google Keep API is restricted to Workspace Enterprise\ndomains via service-account domain-wide delegation — an ordinary\nConnect-Workspace access token will likely `403` on `gws keep …` regardless of\nthe granted scope. If it does, tell the user it's an API restriction, not a\nmissing grant.\n\n**Under a routine, the Google token is present only if you declared its secret\nname (`GOOGLE_WORKSPACE_CLI_TOKEN`) in the routine's `secrets:`.**\n\n### Google Chat — unread state & sender names\n\nTwo Chat limitations are scope-bound, so check before promising the user a\nresult:\n\n- **\"Unread messages\" needs an extra scope, but `gws` has a native command for\n  it.** The default read scopes (`chat.spaces.readonly` +\n  `chat.messages.readonly`) let you list spaces and read messages but do **not**\n  expose per-space unread state — read-position needs\n  `chat.users.readstate.readonly`. With that scope, **don't hand-roll REST**:\n  `gws` wraps the read-state endpoint as\n  `gws chat users spaces getSpaceReadState`. Compute unread per space yourself —\n  there's no single \"list unread\" call:\n  1. List the spaces you care about (DMs\u002Fgroup chats sort by `lastActiveTime`):\n     ```\n     gws chat spaces list --params '{\"pageSize\": 1000}'\n     ```\n  2. For each space, read your last-read mark and its latest message, then\n     compare:\n     ```\n     gws chat users spaces getSpaceReadState --params '{\"name\": \"users\u002Fme\u002Fspaces\u002F\u003Cspace>\u002FspaceReadState\"}'\n     gws chat spaces messages list --params '{\"parent\": \"spaces\u002F\u003Cspace>\", \"pageSize\": 1, \"orderBy\": \"create_time DESC\"}'\n     ```\n     A space is **unread** when the latest message's `createTime` is strictly\n     newer than the read state's `lastReadTime` **and** that message's\n     `sender.name` (a `users\u002F\u003Cid>`) isn't you. (`orderBy` is `create_time DESC` —\n     snake_case field + `ASC`\u002F`DESC`, not `createTime desc`.)\n  - Resolving **your own** `users\u002F\u003Cid>` to skip self-authored messages is the one\n    snag: `people\u002Fme` 403s under the Connect-Workspace token, so pass your id in\n    explicitly (read it off any message you sent, or `gws people people get`\n    once) rather than relying on `me`.\n  - Resolve the remaining sender ids to names with `gws people people getBatchGet`\n    (see the People note above).\n  - **Without the read-state scope** you can't get true unread — be upfront, then\n    either re-auth to add it\n    (`gws auth login --scopes https:\u002F\u002Fwww.googleapis.com\u002Fauth\u002Fchat.users.readstate.readonly`;\n    incremental consent merges it) or fall back to **recency as a proxy** (list\n    spaces by `lastActiveTime`, pull each space's latest messages). Say which.\n- **Senders come back as user IDs, not names.** Chat returns each sender as\n  `users\u002F\u003Cnumeric-id>`; the Chat read scopes don't resolve those to display\n  names. The People API does (`gws` wraps it as `gws people …`), and \"Connect\n  Workspace\" now offers the grant as a separate **Directory** surface (scope\n  `directory.readonly` — read-only regardless of the read-write toggle). To turn\n  IDs into names:\n  - If a `gws people` read 403s, the user hasn't ticked **Directory** — ask them\n    to reconnect with it.\n  - **Resolve in batch — a Chat thread has many distinct senders, so collect all\n    the `\u003Cnumeric-id>`s from `users\u002F\u003Cnumeric-id>` and resolve them in one\n    `getBatchGet` call, not one `get` per ID** (the People resource id is the\n    `\u003Cnumeric-id>`):\n    ```\n    gws people people getBatchGet --params '{\"resourceNames\": [\"people\u002F\u003Cid1>\", \"people\u002F\u003Cid2>\"], \"personFields\": \"names,emailAddresses\"}'\n    ```\n    Read each name off `responses[].person.names[0].displayName`. Use single-ID\n    `gws people people get --params '{\"resourceName\": \"people\u002F\u003Cnumeric-id>\", \"personFields\": \"names\"}'`\n    only for a one-off lookup. If a sender can't be seen as a domain member, fall\n    back to `gws people people searchDirectoryPeople --params '{\"query\": \"\u003Cname-or-email>\", \"readMask\": \"names,emailAddresses\"}'`.\n  - Confirm exact param shapes with `gws schema people.people.get` before relying\n    on them.\n\n## Failure handling\n\n- `No OAuth client configured` → set up credentials first (see Auth: drop a\n  `client_secret.json`, set the `GOOGLE_WORKSPACE_CLI_CLIENT_*` env vars, or use\n  a service account).\n- `gws auth login` hangs on the listener → expected headless; bridge the\n  loopback by relaying the redirect URL and `curl`-ing it back in (see Auth →\n  \"Completing the loopback flow headless\"). Only fall back to a service account\n  when no user is present to paste the redirect.\n- Login token doesn't persist \u002F \"no keyring\" error, or asked to log in again\n  after a runtime upgrade → set both `GOOGLE_WORKSPACE_CLI_CONFIG_DIR=\u002Fworkspace\u002Flha\u002Fconfig\u002Fgws`\n  and `GOOGLE_WORKSPACE_CLI_KEYRING_BACKEND=file` on every `gws` call (config +\n  key then live on `\u002Fworkspace`, which persists; `~\u002F.config\u002Fgws` does not).\n- `Error 400: redirect_uri_mismatch` on login → the OAuth client is a\n  \"Web application\" type; recreate it as **Desktop app** (loopback ports).\n- `access_denied` \u002F \"Account restricted\" on consent → Context-Aware Access on\n  the OAuth client. Use an **Internal**-audience client (see Auth → \"Creating\n  the OAuth client\").\n- 401 \u002F `not logged in` → if `GOOGLE_WORKSPACE_CLI_TOKEN` is the auth source,\n  the ~1h token has lapsed — ask the user to re-click \"Connect Workspace\" (it\n  has no refresh). Otherwise the OAuth login expired; re-run `gws auth login`.\n- `gws auth status` shows `auth_method: none` but reads work → expected when\n  the env token (`GOOGLE_WORKSPACE_CLI_TOKEN`) is the source; `auth status`\n  doesn't reflect it. Confirm with a real read, not `auth status`.\n- **403, scope too narrow** → the granted scopes don't cover the call. If the\n  auth source is the `GOOGLE_WORKSPACE_CLI_TOKEN` env token, the \"Connect\n  Workspace\" grant was per-surface and read-only by default — ask the user to\n  reconnect including the surface (or read-write) you need. On the OAuth-login\n  path instead, re-auth with the needed scope: `gws auth login --scopes \u003C…>`\n  (Google's incremental consent merges it with what was already granted).\n- **403, `serviceUsageConsumer` \u002F quota project** → distinct from a scope 403:\n  the `client_secret.json`'s `project_id` is a project the account can't bill\n  to. Point the client at a project the account may use (the account needs\n  `roles\u002Fserviceusage.serviceUsageConsumer` on it).\n- `gws: command not found` → not installed. `npm install -g @googleworkspace\u002Fcli`.\n- Unsure of a flag or param → `gws \u003Cservice> --help` or\n  `gws schema \u003Cservice>.\u003Cresource>.\u003Cmethod>`. Don't guess.\n\n\u003C!-- find-skills:tested:false -->\n",{"name":5,"description":7},"main","665f7b2a181686f2ba3c6c942e9b30a084b12ec3","core\u002Fpython\u002Flong-horizon-harness\u002Fhorizon\u002Fbuiltin_skills\u002Fgoogle-workspace",2957,"https:\u002F\u002Fgithub.com\u002Fgoogle\u002Fadk-recipes","https:\u002F\u002Fadk.dev","https:\u002F\u002Fgithub.com\u002Fgoogle","https:\u002F\u002Fopensource.google\u002F","Google ❤️ Open Source","2026-11-12 02:02:17",{"source":8,"skillName":5,"commands":55,"prompt":65,"zipAvailable":66},[56,59,62],{"tool":57,"command":58},"npx","npx skills add google\u002Fadk-recipes --skill google-workspace",{"tool":60,"command":61},"bunx","bunx skills add google\u002Fadk-recipes --skill google-workspace",{"tool":63,"command":64},"pnpm","pnpm dlx skills add google\u002Fadk-recipes --skill google-workspace","安装技能，技能地址：https:\u002F\u002Fgithub.com\u002Fgoogle\u002Fadk-recipes\u002Fblob\u002Fmain\u002Fcore\u002Fpython\u002Flong-horizon-harness\u002Fhorizon\u002Fbuiltin_skills\u002Fgoogle-workspace\u002FSKILL.md",true,[],[69,95,129,149,165,184],{"skillNo":70,"name":71,"nameZh":6,"description":72,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":73,"categories":74,"keywords":86,"publishedAt":39},"01M6991EKEG8XEM3VWW4VGV0ZQ","retail-virtual-tryon","Creates virtual try-on agents supporting image and video (catwalk animation) try-ons on Google Cloud (Gemini image models and Veo on Gemini Enterprise Agent Platform). Handles resource setup, user photo uploading, image\u002Fvideo generation pipelines, local testing, and evaluation.","https:\u002F\u002Fgithub.com\u002Fgoogle\u002Fadk-recipes\u002Fblob\u002Fmain\u002Fplugins\u002Fretail\u002Fskills\u002Fvirtual-tryon\u002FSKILL.md",[75,80,83],{"categoryNo":76,"categoryName":77,"taxonomyType":78,"taxonomyTypeDesc":79},"01JBSEEDCAT000000000000008","测试与质量","SCENARIO","使用场景",{"categoryNo":81,"categoryName":82,"taxonomyType":78,"taxonomyTypeDesc":79},"01JBSEEDCAT000000000000016","图像与多媒体",{"categoryNo":84,"categoryName":85,"taxonomyType":78,"taxonomyTypeDesc":79},"01JBSEEDCAT000000000000023","Skill与智能体开发",[87,88,9,89,90,91,92,93,94],"retail virtual tryon","gemini","image","testing","genai","google cloud","adk","gemini enterprise agent platform",{"skillNo":96,"name":97,"nameZh":6,"description":98,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":99,"categories":100,"keywords":110,"publishedAt":39},"01M6991ABTZW7FCPREXY8GD4RG","retail-product-search","Creates product search agents with semantic search and RAG on Google Cloud (Vector Search on Gemini Enterprise Agent Platform, BigQuery, embeddings). Use when the user wants to \"build a product search agent\", \"create an e-commerce search\", \"make a shopping assistant\", \"set up semantic catalog discovery\", \"ingest products into Vector Search\", or \"deploy a retail RAG agent\". Handles the full pipeline: catalog data ingestion to BigQuery, Gemini Enterprise Agent Platform Vector Search collection setup, ADK agent scaffolding, evaluation, and Cloud Run deployment.","https:\u002F\u002Fgithub.com\u002Fgoogle\u002Fadk-recipes\u002Fblob\u002Fmain\u002Fplugins\u002Fretail\u002Fskills\u002Fproduct-search\u002FSKILL.md",[101,104,105],{"categoryNo":102,"categoryName":103,"taxonomyType":78,"taxonomyTypeDesc":79},"01JBSEEDCAT000000000000011","运维与部署",{"categoryNo":84,"categoryName":85,"taxonomyType":78,"taxonomyTypeDesc":79},{"categoryNo":106,"categoryName":107,"taxonomyType":108,"taxonomyTypeDesc":109},"01JBSEEDCAT000000000000031","产品经理","PROFESSION","适用职业",[111,112,113,92,94,93,88,114,9,115,116,117,118,119,120,121,122,123,124,125,126,127,128],"retail product search","product","search","assistant","collection","when","commerce","scaffolding","shopping","up","ingestion","SET","pipeline","vector","apache","run","create","rag",{"skillNo":130,"name":131,"nameZh":6,"description":132,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":133,"categories":134,"keywords":136,"publishedAt":39},"01M69918HZCYZHD7K9Y9ZSD287","routines","Use for a recurring unattended task (cron; pull data, open a PR, post a digest), or to test\u002Fdry-run a routine first. Explains authoring and testing via the routine tool.","https:\u002F\u002Fgithub.com\u002Fgoogle\u002Fadk-recipes\u002Fblob\u002Fmain\u002Fcore\u002Fpython\u002Flong-horizon-harness\u002Fhorizon\u002Fbuiltin_skills\u002Froutines\u002FSKILL.md",[135],{"categoryNo":76,"categoryName":77,"taxonomyType":78,"taxonomyTypeDesc":79},[131,21,137,138,139,140,25,141,142,143,144,28,30,145,146,147,126,148,90,34],"open","routine","tool","post","dry","authoring","pr","digest","recurring","data","pull","test",{"skillNo":150,"name":151,"nameZh":6,"description":152,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":153,"categories":154,"keywords":155,"publishedAt":39},"01M699178ZDFDXEZ11DG1Y9CTF","policy","Inspect the per-workspace tool-policy overlay (.lha\u002Fpolicies.jsonl) that gates destructive or sensitive tool calls, and tell the user what to change if they want a rule added or removed.","https:\u002F\u002Fgithub.com\u002Fgoogle\u002Fadk-recipes\u002Fblob\u002Fmain\u002Fcore\u002Fpython\u002Flong-horizon-harness\u002Fhorizon\u002Fbuiltin_skills\u002Fpolicy\u002FSKILL.md",[],[151,156,157,158,159,160,161,162,163,20,139,164],"jsonl","overlay","tool calls","inspect","calls","rule","gates","user","change",{"skillNo":166,"name":167,"nameZh":6,"description":168,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":169,"categories":170,"keywords":172,"publishedAt":39},"01M69914416KTZ6Z0WD425YYJ0","find-skills","Discover\u002Finstall NEW agent skills from external sources (skills.sh). Use for capabilities you lack, e.g. \"find a skill for X\". Do NOT use to list skills you already have; see `\u003Cavailable_skills>`.","https:\u002F\u002Fgithub.com\u002Fgoogle\u002Fadk-recipes\u002Fblob\u002Fmain\u002Fcore\u002Fpython\u002Flong-horizon-harness\u002Fhorizon\u002Fbuiltin_skills\u002Ffind-skills\u002FSKILL.md",[171],{"categoryNo":84,"categoryName":85,"taxonomyType":78,"taxonomyTypeDesc":79},[173,174,175,30,176,177,178,179,180,21,181,182,183],"find","find skills","do","install","list","list skills","capabilities","new agent","you","sources","discover",{"skillNo":185,"name":186,"nameZh":6,"description":187,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":188,"categories":189,"keywords":191,"publishedAt":39},"01M6991292FVYS3XR3WE7VXZJD","bootstrap-google-tools","Install\u002Fauth CLIs the sandbox lacks - `gws` (Drive, Gmail, Sheets, Calendar), `gcloud`, `agents-cli`, `mcp-cli` (MCP servers). Use on \"command not found\" or before GCP\u002FWorkspace\u002FMCP work.","https:\u002F\u002Fgithub.com\u002Fgoogle\u002Fadk-recipes\u002Fblob\u002Fmain\u002Fcore\u002Fpython\u002Flong-horizon-harness\u002Fhorizon\u002Fbuiltin_skills\u002Fbootstrap-google-tools\u002FSKILL.md",[190],{"categoryNo":84,"categoryName":85,"taxonomyType":78,"taxonomyTypeDesc":79},[192,193,9,194,30,195,23,27,29,176,196,197,20,198,199,200,201,38,22,202],"Tools","bootstrap","bootstrap google tools","servers","command","work","gcp","auth","sandbox","gcloud","MCP"]