[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill:01M6CAJ9F4PDSVCN2QQV2Z5D54":3,"skill-same:01M6CAJ9F4PDSVCN2QQV2Z5D54":98},{"skillNo":4,"name":5,"nameZh":6,"description":7,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":15,"categories":16,"keywords":22,"publishedAt":50,"linkOnly":51,"markdownContent":52,"markdownBody":53,"frontMatter":54,"refName":67,"commitSha":68,"skillDirectory":69,"forksCount":70,"repositoryHtmlUrl":71,"repositoryHomepageUrl":6,"ownerHtmlUrl":72,"ownerBlogUrl":6,"ownerDescription":6,"updatedAt":73,"install":74,"resources":87},"01M6CAJ9F4PDSVCN2QQV2Z5D54","google-credentials-fallback",null,"Use already-installed `gws` or `gog` (gogcli) credentials when Hermes's own Google OAuth setup has not been run. Reuses the refresh token from the working CLI to call Gmail\u002FCalendar\u002FDrive APIs directly via curl, avoiding a full OAuth re-setup. Activates when `$GSETUP --check` returns NOT_AUTHENTICATED but `gws` or `gog` is on PATH and authenticated. Also handles user-shared Google Doc URLs (docs.google.com\u002Fdocument\u002Fd\u002F\u003CID>\u002Fedit) — `gws` 403s on personal shared Docs (service-account auth), use `gog docs info` + `gog docs export --format txt` instead.","jleechanorg\u002Fjleechan-skills","jleechanorg","https:\u002F\u002Favatars.githubusercontent.com\u002Fu\u002F221404966?v=4","01M6C1VJ99XG950X1DWPEBCBGX","01M6C1VJDK5YVDNT3362RT5WSB",6,"MIT","https:\u002F\u002Fgithub.com\u002Fjleechanorg\u002Fjleechan-skills\u002Fblob\u002Fmain\u002Fhermes\u002Fskills\u002Fproductivity\u002Fgoogle-credentials-fallback\u002FSKILL.md",[17],{"categoryNo":18,"categoryName":19,"taxonomyType":20,"taxonomyTypeDesc":21},"01JBSEEDCAT000000000000002","文档处理","SCENARIO","使用场景",[23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49],"google","fallback","google credentials fallback","credentials","edit","when","re","token","shared","info","gws","gmail","personal","calendar","export","use","gog docs","returns","macos","Google-Workspace","doc","service-account","run","auth","linux","cli","gog","2026-11-10 10:12:30",false,"---\nname: google-credentials-fallback\ndescription: \"Use already-installed `gws` or `gog` (gogcli) credentials when Hermes's own Google OAuth setup has not been run. Reuses the refresh token from the working CLI to call Gmail\u002FCalendar\u002FDrive APIs directly via curl, avoiding a full OAuth re-setup. Activates when `$GSETUP --check` returns NOT_AUTHENTICATED but `gws` or `gog` is on PATH and authenticated. Also handles user-shared Google Doc URLs (docs.google.com\u002Fdocument\u002Fd\u002F\u003CID>\u002Fedit) — `gws` 403s on personal shared Docs (service-account auth), use `gog docs info` + `gog docs export --format txt` instead.\"\nversion: 1.1.0\nauthor: Hermes Agent\nlicense: MIT\nplatforms: [macos, linux]\nmetadata:\n  hermes:\n    tags: [google, gmail, calendar, drive, docs, oauth, gws, gog, gogcli, fallback]\n    related_skills: [productivity\u002Fgoogle-workspace]\n---\n\n# google-credentials-fallback\n\nReuse an already-authenticated `gws` or `gog` (gogcli) install on the same machine instead of running the full Hermes Google Workspace OAuth setup. Useful when:\n\n- `$GSETUP --check` returns `NOT_AUTHENTICATED`.\n- The user already has `gws` or `gog` installed and authorized (likely from prior work).\n- You only need to read Gmail, list calendar events, or fetch a Drive\u002FDocs file from a cron job or short-lived agent session.\n\n## References\n\n- `references\u002Fdaily-digest-cron-recipe.md` — verified working recipe for a Gmail + Calendar → Slack morning digest cron (gog v0.10.0, `--from`\u002F`--to` workaround for the `--days` bug).\n\nWhen this skill applies, the bundled `productivity\u002Fgoogle-workspace` skill's `$GAPI` wrapper cannot be used because no `~\u002F.hermes\u002Fgoogle_token.json` exists. This skill provides the working alternative.\n\n**Verified 2026-07-09** against a host with both CLIs:\n- `gws` is authorized as `firebase-adminsdk-…@worldarchitecture-ai.iam.gserviceaccount.com` (service account, empty calendar).\n- `gog` is authorized as `$USER@gmail.com` (personal OAuth).\n\n---\n\n## When to activate\n\nTrigger condition: any task needs Gmail \u002F Calendar \u002F Drive \u002F Docs \u002F Sheets and `$GSETUP --check` says `NOT_AUTHENTICATED`. Before running the 5-step OAuth setup, check if a working CLI is already on the host:\n\n```bash\nwhich gws gog 2>&1\ngog auth list 2>&1         # shows accounts + auth type (oauth \u002F service-account)\n```\n\nIf `gog auth list` shows the user account you need, skip Hermes OAuth setup and read the existing credentials file directly.\n\n---\n\n## 1. Locate the working credentials\n\n```bash\n# gws\nls -la ~\u002F.config\u002Fgws\u002F 2>&1\nls -la ~\u002FLibrary\u002FApplication\\ Support\u002Fgwscli\u002F 2>&1\n\n# gog (gogcli)\nls -la ~\u002FLibrary\u002FApplication\\ Support\u002Fgogcli\u002F 2>&1\ncat ~\u002FLibrary\u002FApplication\\ Support\u002Fgogcli\u002Fcredentials.json\ncat ~\u002FLibrary\u002FApplication\\ Support\u002Fgogcli\u002Fconfig.json\n```\n\ngog's `credentials.json` contains `client_id`, `client_secret`, and a `refresh_token` directly in the JSON — no keychain unlock required. **Treat as a secret; never paste in chat\u002Flogs.**\n\nThe `services` array and `scopes` array tell you which APIs the refresh token can hit (commonly: gmail, calendar, drive, docs).\n\n---\n\n## 2. Try the native CLIs first (fastest)\n\n### `gog` (gogcli) — preferred for personal Gmail\u002FCalendar\n\n```bash\nGOG=\"-a $USER@gmail.com --enable-commands=gmail calendar\"\n\n$gog $GOG gmail search \"is:unread newer_than:3d\" --max 15\n$gog $GOG calendar calendars          # list calendar IDs and roles\n```\n\n### `gws` (googleworkspace\u002Fcli) — preferred for Workspace \u002F service-account\n\n```bash\ngws calendar events list --params '{\"calendarId\": \"primary\", \"timeMin\": \"…\", \"timeMax\": \"…\", \"maxResults\": 10}'\ngws gmail users messages list --params '{\"userId\": \"me\", \"q\": \"is:unread newer_than:3d\", \"maxResults\": 10}'\n```\n\n### Known bugs (work around with curl below)\n\n- `gog ≤ 0.10.0`: `gog calendar events list --days 2` returns `Google API error (404 notFound)` against the OAuth default account.\n- `gws (2026-07)`: `gws gmail users messages list` returns `Precondition check failed` for personal Gmail.\n- gog positional arg parsing treats `primary` as a subcommand, not a calendarId.\n\n**Working alternative for the calendar bug (verified 2026-07-12):** use `--from` \u002F `--to` with RFC3339 UTC timestamps instead of `--days N`. Example:\n\n```bash\nFROM_TS=$(date -u -v+0H \"+%Y-%m-%dT%H:%M:%SZ\")\nTO_TS=$(date -u -v+24H \"+%Y-%m-%dT%H:%M:%SZ\")\ngog calendar list --from \"$FROM_TS\" --to \"$TO_TS\" --json --results-only -a $USER@gmail.com\n```\n\nThis works on `gog v0.10.0` against personal Gmail and returns full event details (id, summary, start\u002Fend, attendees, hangoutLink).\n\nWhen `gog calendar list` still fails, jump straight to curl — the Google REST API is unaffected.\n\n---\n\n## 3. Direct curl with the stored refresh token\n\nThe bypass that always works. Same `refresh_token` as gog, direct to Google's REST endpoints.\n\n### Get an access token\n\n```bash\nACCESS_TOKEN=$(curl -s -X POST https:\u002F\u002Foauth2.googleapis.com\u002Ftoken \\\n  -d \"client_id=$(jq -r .client_id ~\u002FLibrary\u002FApplication\\ Support\u002Fgogcli\u002Fcredentials.json)\" \\\n  -d \"client_secret=$(jq -r .client_secret ~\u002FLibrary\u002FApplication\\ Support\u002Fgogcli\u002Fcredentials.json)\" \\\n  -d \"refresh_token=$(jq -r .refresh_token ~\u002FLibrary\u002FApplication\\ Support\u002Fgogcli\u002Fcredentials.json)\" \\\n  -d \"grant_type=refresh_token\" \\\n  -d \"scope=https:\u002F\u002Fwww.googleapis.com\u002Fauth\u002Fcalendar https:\u002F\u002Fwww.googleapis.com\u002Fauth\u002Fgmail.modify openid\" \\\n  | python3 -c \"import sys,json; print(json.load(sys.stdin).get('access_token','ERROR'))\")\n\n# Always verify length, never log the value\n[ \"${#ACCESS_TOKEN}\" -gt 50 ] && echo \"got token len=${#ACCESS_TOKEN}\"\n```\n\n### Gmail: list + get metadata\n\n```bash\ncurl -s -H \"Authorization: Bearer $ACCESS_TOKEN\" \\\n  \"https:\u002F\u002Fgmail.googleapis.com\u002Fgmail\u002Fv1\u002Fusers\u002Fme\u002Fmessages?maxResults=20&q=is:unread+newer_than:2d+-category:promotions\" \\\n  | python3 -m json.tool\n\ncurl -s -H \"Authorization: Bearer $ACCESS_TOKEN\" \\\n  \"https:\u002F\u002Fgmail.googleapis.com\u002Fgmail\u002Fv1\u002Fusers\u002Fme\u002Fmessages\u002F\u003CMESSAGE_ID>?format=metadata&metadataHeaders=From&metadataHeaders=Subject&metadataHeaders=Date\"\n```\n\n### Calendar: list events (include or exclude to scope a window)\n\n```bash\ncurl -s -H \"Authorization: Bearer $ACCESS_TOKEN\" \\\n  \"https:\u002F\u002Fwww.googleapis.com\u002Fcalendar\u002Fv3\u002Fcalendars\u002F$USER%40gmail.com\u002Fevents?timeMin=2026-07-09T16:00:00Z&timeMax=2026-07-10T16:00:00Z&singleEvents=true&orderBy=startTime&maxResults=25\" \\\n  | python3 -m json.tool\n```\n\n`calendarId` in the URL must be URL-encoded (`@` → `%40`). Use `gog calendar calendars` to enumerate the IDs the user can see; the OAuth default may be a service-account calendar that is empty by design — that's not a bug, just not the one you want.\n\n### Drive \u002F Docs \u002F Sheets \u002F People\n\nSame `Bearer` token + the appropriate `https:\u002F\u002Fwww.googleapis.com\u002F{drive|docs|sheets|people}\u002Fv3\u002F...` endpoints, provided the refresh token's `services` list includes them (it usually does for `drive`, `docs`).\n\n---\n\n## 4. When to write back to Hermes-managed credentials\n\nUse the curl\u002Fgog path as a **stopgap**. If the user wants long-term canonical access from Hermes, run the bundled `google-workspace` skill's first-time setup once (Steps 2–5 in that skill's `SKILL.md`). You can speed it up by reusing the `client_id`\u002F`client_secret`\u002F`refresh_token` triple already in `~\u002FLibrary\u002FApplication Support\u002Fgogcli\u002Fcredentials.json` — write it as a Desktop OAuth JSON, then `$GSETUP --client-secret …` against it. Step 4 (auth-code exchange) may not even be needed if the refresh token was minted against the same client_id.\n\nAfter this, `$GAPI gmail search …` and `$GAPI calendar list …` work normally and should be used in place of the curl fallback.\n\n---\n\n## 5. Drive corpus search (find + read a constellation of Google Docs)\n\nWhen the user asks \"find all my old X campaigns \u002F Y documents \u002F Z shared notes\" and the answer lives as a **constellation of Google Docs** (not a single one), use the Drive search pattern. Verified 2026-07-20 against $USER@gmail.com while finding the **god-of-tyranny Aizen campaign corpus** (16+ Google Docs spanning 2025-06-14 → 2026-02-09).\n\n### Drive search — the right `gog` subcommand is `drive search`, NOT `docs search`\n\n```bash\n# CORRECT — gog 0.10.0 has no `gog docs search` subcommand\ngog drive search --query \"Aizen god\" --max 20 -a $USER@gmail.com --json\ngog drive search --query \"campaign nocturne\" --max 50 -a $USER@gmail.com --json\n\n# WRONG — returns \"unknown subcommand\" or empty\ngog docs search \"Aizen god\"\ngog drive docs search \"Aizen god\"\n```\n\n### Drive download — Google Docs export as PDF is automatic\n\n```bash\n# Google Doc → auto-exports as PDF (preserves structure, fonts, images)\ngog drive download --file-id 1L1sOStC7rVjCzE8KHhpMf55TarmvNXNuEx-RH2vwO-0 \\\n    --output ~\u002Ftmp\u002Faizen_origin.pdf -a $USER@gmail.com\n\n# Plain text file (.txt) → downloads as .txt directly\ngog drive download --file-id 1HnRXmsl8rcB5_ZNmO7WkDIxhTTZCZw0n \\\n    --output ~\u002Ftmp\u002Faizen_godhood_3.txt -a $USER@gmail.com\n\n# PDF (e.g. \"Aizen god campaign book 2.pdf\") → downloads as PDF\ngog drive download --file-id 1hkW_iJZCy7CoOZfCuuj__Q_qz6IGPEE7 \\\n    --output ~\u002Ftmp\u002Faizen_book2.pdf -a $USER@gmail.com\n```\n\nThen `pdftotext \u003Cpdf> -` to extract text. Some PDFs (especially older Google Docs) fail `pdftotext` cleanly — fall back to `pdfinfo` for metadata + the Drive `webViewLink` for manual read.\n\n### Field reference for `gog drive search --json` output\n\n| Field | What it is | Use it for |\n|---|---|---|\n| `id` | Drive file ID (used in `drive download`) | Dedupe, pass to download |\n| `name` | Filename incl. extension | Sort, filter, group by topic |\n| `mimeType` | `application\u002Fvnd.google-apps.document` = Google Doc; `text\u002Fplain` = raw text; `application\u002Fpdf` = PDF | Decide download path (Google Docs auto-export PDF; raw text stays text) |\n| `modifiedTime` | RFC3339 UTC | Sort newest-first \u002F filter by date range |\n| `size` | Bytes (only set for non-Google-Docs native files) | Estimate download time |\n| `webViewLink` | `https:\u002F\u002Fdrive.google.com\u002Ffile\u002Fd\u002F\u003Cid>\u002Fview` | Share-able URL for the user |\n\n### Recipe: \"find and inventory\" a constellation\n\n```bash\n# 1. Multi-keyword fan-out\nfor kw in \"Aizen god\" \"campaign nocturne\" \"noctune bg3\" \"campaign aizen\"; do\n    gog drive search --query \"$kw\" --max 50 -a $USER@gmail.com --json \\\n        | jq -r '.files[]? | [.id, .name, .mimeType, .modifiedTime] | @tsv'\ndone | sort -u -k1,1  # dedupe by file id\n\n# 2. For each hit, dump metadata to a manifest\n# (id, name, mimeType, modifiedTime, size, webViewLink)\ngog drive info --file-id \u003Cid> -a $USER@gmail.com --json\n\n# 3. Download all in parallel (gog supports it; xargs -P 4 is fine)\ncat ids.txt | xargs -P 4 -I{} sh -c 'gog drive download --file-id {} --output ~\u002Ftmp\u002F{}.pdf -a $USER@gmail.com'\n\n# 4. Extract text from PDFs (parallel)\nfind ~\u002Ftmp -name \"*.pdf\" | xargs -P 4 -I{} pdftotext {} -\n```\n\n### Why this exists\n\n`gws` **403s** on personal shared Google Docs (it's authorized to a service account). `gog drive search` + `gog drive download` is the working path for the user's own account, and it preserves the Google-Doc-as-PDF auto-export behavior. Verifying a Google Doc was correctly retrieved means: download size > 50 KB (Google Docs PDFs are ~100KB+ even for short docs) + `pdftotext` returns non-empty output + first paragraph matches the user's recollection.\n\n## Pitfalls\n\n- **Multiple-account confusion.** `gws` and `gog` may be authorized to *different* accounts on the same host (gws → service account; gog → personal). The active credentials vary by CLI. Confirm which account answered by reading the response's `user.email` (Gmail) or `summary` (Calendar) field, not by which CLI you invoked.\n- **Service-account calendars show no events.** A \"calendarId=primary\" call to gws returning `items: []` and a `firebase-adminsdk-…` summary is the service account's empty calendar — not an auth error. Query the user's personal `$USER@gmail.com` calendar instead.\n- **gog positional arg gotcha.** `gog calendar events list primary --days 2` parses `primary` as a subcommand. Use no positional arg for the default, or pass `--calendar=…` if supported; fall back to curl.\n- **Don't paste the access token into logs.** Echo `ACCESS_TOKEN_LEN=${#ACCESS_TOKEN}` instead of the raw value when running through a logged tool.\n- **Don't run the full 5-step OAuth setup** if gog\u002Fgws already work — read the stored credentials and use them. The user shouldn't have to re-authorize twice.\n- **The bundled `google-workspace` skill is locked.** You cannot patch its `SKILL.md` from a cron session. This skill is the user-editable mirror for the \"Hermes not set up but a CLI is\" branch.\n","# google-credentials-fallback\n\nReuse an already-authenticated `gws` or `gog` (gogcli) install on the same machine instead of running the full Hermes Google Workspace OAuth setup. Useful when:\n\n- `$GSETUP --check` returns `NOT_AUTHENTICATED`.\n- The user already has `gws` or `gog` installed and authorized (likely from prior work).\n- You only need to read Gmail, list calendar events, or fetch a Drive\u002FDocs file from a cron job or short-lived agent session.\n\n## References\n\n- `references\u002Fdaily-digest-cron-recipe.md` — verified working recipe for a Gmail + Calendar → Slack morning digest cron (gog v0.10.0, `--from`\u002F`--to` workaround for the `--days` bug).\n\nWhen this skill applies, the bundled `productivity\u002Fgoogle-workspace` skill's `$GAPI` wrapper cannot be used because no `~\u002F.hermes\u002Fgoogle_token.json` exists. This skill provides the working alternative.\n\n**Verified 2026-07-09** against a host with both CLIs:\n- `gws` is authorized as `firebase-adminsdk-…@worldarchitecture-ai.iam.gserviceaccount.com` (service account, empty calendar).\n- `gog` is authorized as `$USER@gmail.com` (personal OAuth).\n\n---\n\n## When to activate\n\nTrigger condition: any task needs Gmail \u002F Calendar \u002F Drive \u002F Docs \u002F Sheets and `$GSETUP --check` says `NOT_AUTHENTICATED`. Before running the 5-step OAuth setup, check if a working CLI is already on the host:\n\n```bash\nwhich gws gog 2>&1\ngog auth list 2>&1         # shows accounts + auth type (oauth \u002F service-account)\n```\n\nIf `gog auth list` shows the user account you need, skip Hermes OAuth setup and read the existing credentials file directly.\n\n---\n\n## 1. Locate the working credentials\n\n```bash\n# gws\nls -la ~\u002F.config\u002Fgws\u002F 2>&1\nls -la ~\u002FLibrary\u002FApplication\\ Support\u002Fgwscli\u002F 2>&1\n\n# gog (gogcli)\nls -la ~\u002FLibrary\u002FApplication\\ Support\u002Fgogcli\u002F 2>&1\ncat ~\u002FLibrary\u002FApplication\\ Support\u002Fgogcli\u002Fcredentials.json\ncat ~\u002FLibrary\u002FApplication\\ Support\u002Fgogcli\u002Fconfig.json\n```\n\ngog's `credentials.json` contains `client_id`, `client_secret`, and a `refresh_token` directly in the JSON — no keychain unlock required. **Treat as a secret; never paste in chat\u002Flogs.**\n\nThe `services` array and `scopes` array tell you which APIs the refresh token can hit (commonly: gmail, calendar, drive, docs).\n\n---\n\n## 2. Try the native CLIs first (fastest)\n\n### `gog` (gogcli) — preferred for personal Gmail\u002FCalendar\n\n```bash\nGOG=\"-a $USER@gmail.com --enable-commands=gmail calendar\"\n\n$gog $GOG gmail search \"is:unread newer_than:3d\" --max 15\n$gog $GOG calendar calendars          # list calendar IDs and roles\n```\n\n### `gws` (googleworkspace\u002Fcli) — preferred for Workspace \u002F service-account\n\n```bash\ngws calendar events list --params '{\"calendarId\": \"primary\", \"timeMin\": \"…\", \"timeMax\": \"…\", \"maxResults\": 10}'\ngws gmail users messages list --params '{\"userId\": \"me\", \"q\": \"is:unread newer_than:3d\", \"maxResults\": 10}'\n```\n\n### Known bugs (work around with curl below)\n\n- `gog ≤ 0.10.0`: `gog calendar events list --days 2` returns `Google API error (404 notFound)` against the OAuth default account.\n- `gws (2026-07)`: `gws gmail users messages list` returns `Precondition check failed` for personal Gmail.\n- gog positional arg parsing treats `primary` as a subcommand, not a calendarId.\n\n**Working alternative for the calendar bug (verified 2026-07-12):** use `--from` \u002F `--to` with RFC3339 UTC timestamps instead of `--days N`. Example:\n\n```bash\nFROM_TS=$(date -u -v+0H \"+%Y-%m-%dT%H:%M:%SZ\")\nTO_TS=$(date -u -v+24H \"+%Y-%m-%dT%H:%M:%SZ\")\ngog calendar list --from \"$FROM_TS\" --to \"$TO_TS\" --json --results-only -a $USER@gmail.com\n```\n\nThis works on `gog v0.10.0` against personal Gmail and returns full event details (id, summary, start\u002Fend, attendees, hangoutLink).\n\nWhen `gog calendar list` still fails, jump straight to curl — the Google REST API is unaffected.\n\n---\n\n## 3. Direct curl with the stored refresh token\n\nThe bypass that always works. Same `refresh_token` as gog, direct to Google's REST endpoints.\n\n### Get an access token\n\n```bash\nACCESS_TOKEN=$(curl -s -X POST https:\u002F\u002Foauth2.googleapis.com\u002Ftoken \\\n  -d \"client_id=$(jq -r .client_id ~\u002FLibrary\u002FApplication\\ Support\u002Fgogcli\u002Fcredentials.json)\" \\\n  -d \"client_secret=$(jq -r .client_secret ~\u002FLibrary\u002FApplication\\ Support\u002Fgogcli\u002Fcredentials.json)\" \\\n  -d \"refresh_token=$(jq -r .refresh_token ~\u002FLibrary\u002FApplication\\ Support\u002Fgogcli\u002Fcredentials.json)\" \\\n  -d \"grant_type=refresh_token\" \\\n  -d \"scope=https:\u002F\u002Fwww.googleapis.com\u002Fauth\u002Fcalendar https:\u002F\u002Fwww.googleapis.com\u002Fauth\u002Fgmail.modify openid\" \\\n  | python3 -c \"import sys,json; print(json.load(sys.stdin).get('access_token','ERROR'))\")\n\n# Always verify length, never log the value\n[ \"${#ACCESS_TOKEN}\" -gt 50 ] && echo \"got token len=${#ACCESS_TOKEN}\"\n```\n\n### Gmail: list + get metadata\n\n```bash\ncurl -s -H \"Authorization: Bearer $ACCESS_TOKEN\" \\\n  \"https:\u002F\u002Fgmail.googleapis.com\u002Fgmail\u002Fv1\u002Fusers\u002Fme\u002Fmessages?maxResults=20&q=is:unread+newer_than:2d+-category:promotions\" \\\n  | python3 -m json.tool\n\ncurl -s -H \"Authorization: Bearer $ACCESS_TOKEN\" \\\n  \"https:\u002F\u002Fgmail.googleapis.com\u002Fgmail\u002Fv1\u002Fusers\u002Fme\u002Fmessages\u002F\u003CMESSAGE_ID>?format=metadata&metadataHeaders=From&metadataHeaders=Subject&metadataHeaders=Date\"\n```\n\n### Calendar: list events (include or exclude to scope a window)\n\n```bash\ncurl -s -H \"Authorization: Bearer $ACCESS_TOKEN\" \\\n  \"https:\u002F\u002Fwww.googleapis.com\u002Fcalendar\u002Fv3\u002Fcalendars\u002F$USER%40gmail.com\u002Fevents?timeMin=2026-07-09T16:00:00Z&timeMax=2026-07-10T16:00:00Z&singleEvents=true&orderBy=startTime&maxResults=25\" \\\n  | python3 -m json.tool\n```\n\n`calendarId` in the URL must be URL-encoded (`@` → `%40`). Use `gog calendar calendars` to enumerate the IDs the user can see; the OAuth default may be a service-account calendar that is empty by design — that's not a bug, just not the one you want.\n\n### Drive \u002F Docs \u002F Sheets \u002F People\n\nSame `Bearer` token + the appropriate `https:\u002F\u002Fwww.googleapis.com\u002F{drive|docs|sheets|people}\u002Fv3\u002F...` endpoints, provided the refresh token's `services` list includes them (it usually does for `drive`, `docs`).\n\n---\n\n## 4. When to write back to Hermes-managed credentials\n\nUse the curl\u002Fgog path as a **stopgap**. If the user wants long-term canonical access from Hermes, run the bundled `google-workspace` skill's first-time setup once (Steps 2–5 in that skill's `SKILL.md`). You can speed it up by reusing the `client_id`\u002F`client_secret`\u002F`refresh_token` triple already in `~\u002FLibrary\u002FApplication Support\u002Fgogcli\u002Fcredentials.json` — write it as a Desktop OAuth JSON, then `$GSETUP --client-secret …` against it. Step 4 (auth-code exchange) may not even be needed if the refresh token was minted against the same client_id.\n\nAfter this, `$GAPI gmail search …` and `$GAPI calendar list …` work normally and should be used in place of the curl fallback.\n\n---\n\n## 5. Drive corpus search (find + read a constellation of Google Docs)\n\nWhen the user asks \"find all my old X campaigns \u002F Y documents \u002F Z shared notes\" and the answer lives as a **constellation of Google Docs** (not a single one), use the Drive search pattern. Verified 2026-07-20 against $USER@gmail.com while finding the **god-of-tyranny Aizen campaign corpus** (16+ Google Docs spanning 2025-06-14 → 2026-02-09).\n\n### Drive search — the right `gog` subcommand is `drive search`, NOT `docs search`\n\n```bash\n# CORRECT — gog 0.10.0 has no `gog docs search` subcommand\ngog drive search --query \"Aizen god\" --max 20 -a $USER@gmail.com --json\ngog drive search --query \"campaign nocturne\" --max 50 -a $USER@gmail.com --json\n\n# WRONG — returns \"unknown subcommand\" or empty\ngog docs search \"Aizen god\"\ngog drive docs search \"Aizen god\"\n```\n\n### Drive download — Google Docs export as PDF is automatic\n\n```bash\n# Google Doc → auto-exports as PDF (preserves structure, fonts, images)\ngog drive download --file-id 1L1sOStC7rVjCzE8KHhpMf55TarmvNXNuEx-RH2vwO-0 \\\n    --output ~\u002Ftmp\u002Faizen_origin.pdf -a $USER@gmail.com\n\n# Plain text file (.txt) → downloads as .txt directly\ngog drive download --file-id 1HnRXmsl8rcB5_ZNmO7WkDIxhTTZCZw0n \\\n    --output ~\u002Ftmp\u002Faizen_godhood_3.txt -a $USER@gmail.com\n\n# PDF (e.g. \"Aizen god campaign book 2.pdf\") → downloads as PDF\ngog drive download --file-id 1hkW_iJZCy7CoOZfCuuj__Q_qz6IGPEE7 \\\n    --output ~\u002Ftmp\u002Faizen_book2.pdf -a $USER@gmail.com\n```\n\nThen `pdftotext \u003Cpdf> -` to extract text. Some PDFs (especially older Google Docs) fail `pdftotext` cleanly — fall back to `pdfinfo` for metadata + the Drive `webViewLink` for manual read.\n\n### Field reference for `gog drive search --json` output\n\n| Field | What it is | Use it for |\n|---|---|---|\n| `id` | Drive file ID (used in `drive download`) | Dedupe, pass to download |\n| `name` | Filename incl. extension | Sort, filter, group by topic |\n| `mimeType` | `application\u002Fvnd.google-apps.document` = Google Doc; `text\u002Fplain` = raw text; `application\u002Fpdf` = PDF | Decide download path (Google Docs auto-export PDF; raw text stays text) |\n| `modifiedTime` | RFC3339 UTC | Sort newest-first \u002F filter by date range |\n| `size` | Bytes (only set for non-Google-Docs native files) | Estimate download time |\n| `webViewLink` | `https:\u002F\u002Fdrive.google.com\u002Ffile\u002Fd\u002F\u003Cid>\u002Fview` | Share-able URL for the user |\n\n### Recipe: \"find and inventory\" a constellation\n\n```bash\n# 1. Multi-keyword fan-out\nfor kw in \"Aizen god\" \"campaign nocturne\" \"noctune bg3\" \"campaign aizen\"; do\n    gog drive search --query \"$kw\" --max 50 -a $USER@gmail.com --json \\\n        | jq -r '.files[]? | [.id, .name, .mimeType, .modifiedTime] | @tsv'\ndone | sort -u -k1,1  # dedupe by file id\n\n# 2. For each hit, dump metadata to a manifest\n# (id, name, mimeType, modifiedTime, size, webViewLink)\ngog drive info --file-id \u003Cid> -a $USER@gmail.com --json\n\n# 3. Download all in parallel (gog supports it; xargs -P 4 is fine)\ncat ids.txt | xargs -P 4 -I{} sh -c 'gog drive download --file-id {} --output ~\u002Ftmp\u002F{}.pdf -a $USER@gmail.com'\n\n# 4. Extract text from PDFs (parallel)\nfind ~\u002Ftmp -name \"*.pdf\" | xargs -P 4 -I{} pdftotext {} -\n```\n\n### Why this exists\n\n`gws` **403s** on personal shared Google Docs (it's authorized to a service account). `gog drive search` + `gog drive download` is the working path for the user's own account, and it preserves the Google-Doc-as-PDF auto-export behavior. Verifying a Google Doc was correctly retrieved means: download size > 50 KB (Google Docs PDFs are ~100KB+ even for short docs) + `pdftotext` returns non-empty output + first paragraph matches the user's recollection.\n\n## Pitfalls\n\n- **Multiple-account confusion.** `gws` and `gog` may be authorized to *different* accounts on the same host (gws → service account; gog → personal). The active credentials vary by CLI. Confirm which account answered by reading the response's `user.email` (Gmail) or `summary` (Calendar) field, not by which CLI you invoked.\n- **Service-account calendars show no events.** A \"calendarId=primary\" call to gws returning `items: []` and a `firebase-adminsdk-…` summary is the service account's empty calendar — not an auth error. Query the user's personal `$USER@gmail.com` calendar instead.\n- **gog positional arg gotcha.** `gog calendar events list primary --days 2` parses `primary` as a subcommand. Use no positional arg for the default, or pass `--calendar=…` if supported; fall back to curl.\n- **Don't paste the access token into logs.** Echo `ACCESS_TOKEN_LEN=${#ACCESS_TOKEN}` instead of the raw value when running through a logged tool.\n- **Don't run the full 5-step OAuth setup** if gog\u002Fgws already work — read the stored credentials and use them. The user shouldn't have to re-authorize twice.\n- **The bundled `google-workspace` skill is locked.** You cannot patch its `SKILL.md` from a cron session. This skill is the user-editable mirror for the \"Hermes not set up but a CLI is\" branch.\n",{"name":5,"author":55,"license":14,"version":56,"metadata":57,"platforms":66,"description":7},"Hermes Agent","1.1.0",{"hermes":58},{"tags":59,"related_skills":64},[23,34,36,60,61,62,33,49,63,24],"drive","docs","oauth","gogcli",[65],"productivity\u002Fgoogle-workspace",[41,47],"main","05df7380981a2c4af868ce0fa57962ae0eea62a9","hermes\u002Fskills\u002Fproductivity\u002Fgoogle-credentials-fallback",0,"https:\u002F\u002Fgithub.com\u002Fjleechanorg\u002Fjleechan-skills","https:\u002F\u002Fgithub.com\u002Fjleechanorg","2026-11-14 03:18:13",{"source":8,"skillName":5,"commands":75,"prompt":85,"zipAvailable":86},[76,79,82],{"tool":77,"command":78},"npx","npx skills add jleechanorg\u002Fjleechan-skills --skill google-credentials-fallback",{"tool":80,"command":81},"bunx","bunx skills add jleechanorg\u002Fjleechan-skills --skill google-credentials-fallback",{"tool":83,"command":84},"pnpm","pnpm dlx skills add jleechanorg\u002Fjleechan-skills --skill google-credentials-fallback","安装技能，技能地址：https:\u002F\u002Fgithub.com\u002Fjleechanorg\u002Fjleechan-skills\u002Fblob\u002Fmain\u002Fhermes\u002Fskills\u002Fproductivity\u002Fgoogle-credentials-fallback\u002FSKILL.md",true,[88,94],{"path":89,"resourceType":90,"resourceTypeDesc":91,"sizeBytes":92,"sourceHtmlUrl":93},"references\u002Fdaily-digest-cron-recipe.md","REFERENCE","参考文档",4445,"https:\u002F\u002Fgithub.com\u002Fjleechanorg\u002Fjleechan-skills\u002Fblob\u002Fmain\u002Fhermes\u002Fskills\u002Fproductivity\u002Fgoogle-credentials-fallback\u002Freferences\u002Fdaily-digest-cron-recipe.md",{"path":95,"resourceType":90,"resourceTypeDesc":91,"sizeBytes":96,"sourceHtmlUrl":97},"references\u002Fxoxp-fallback-when-bot-not-in-channel.md",4031,"https:\u002F\u002Fgithub.com\u002Fjleechanorg\u002Fjleechan-skills\u002Fblob\u002Fmain\u002Fhermes\u002Fskills\u002Fproductivity\u002Fgoogle-credentials-fallback\u002Freferences\u002Fxoxp-fallback-when-bot-not-in-channel.md",[99,114,136,150,164,180],{"skillNo":100,"name":101,"nameZh":6,"description":102,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":103,"categories":104,"keywords":105,"publishedAt":50},"01M716Y5MTJ6GYJZ2ATXCJK8G4","instinct","Use when the user invokes \u002Finstinct or \u002Fin, or asks to \"message Instinct\", \"ask Instinct\", \"tell Instinct\", \"check Instinct\", or read replies from the configured peer assistant. iMessage first, WhatsApp Web as fallback.","https:\u002F\u002Fgithub.com\u002Fjleechanorg\u002Fjleechan-skills\u002Fblob\u002Fmain\u002F.claude\u002Fskills\u002Finstinct\u002FSKILL.md",[],[101,106,28,107,108,38,109,110,34,111,112,113],"user","replies","check","identity","whatsapp","read","message","ask",{"skillNo":115,"name":116,"nameZh":6,"description":117,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":118,"categories":119,"keywords":125,"publishedAt":50},"01M716Y4HF7FVVMJSA2CCVN5VF","hr","Compatibility alias for history-resume; produce the same fresh-state, provenance-aware resume briefing.","https:\u002F\u002Fgithub.com\u002Fjleechanorg\u002Fjleechan-skills\u002Fblob\u002Fmain\u002F.claude\u002Fskills\u002Fhr\u002FSKILL.md",[120],{"categoryNo":121,"categoryName":122,"taxonomyType":123,"taxonomyTypeDesc":124},"01JBSEEDCAT000000000000038","人力资源","PROFESSION","适用职业",[116,126,127,128,129,130,131,132,133,134,135],"resume","compatibility","provenance","for","history","produce","alias","briefing","state","fresh",{"skillNo":137,"name":138,"nameZh":6,"description":139,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":140,"categories":141,"keywords":143,"publishedAt":50},"01M716Y1FFNFKFJYYKF8HQA6T8","history-resume","Resume briefing for the current repository branch or worktree using fresh state and admitted history sources.","https:\u002F\u002Fgithub.com\u002Fjleechanorg\u002Fjleechan-skills\u002Fblob\u002Fmain\u002F.claude\u002Fskills\u002Fhistory-resume\u002FSKILL.md",[142],{"categoryNo":121,"categoryName":122,"taxonomyType":123,"taxonomyTypeDesc":124},[130,126,144,145,106,146,133,147,129,148,149,134,135],"history resume","branch","analysis","repository","worktree","sources",{"skillNo":151,"name":152,"nameZh":6,"description":153,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":154,"categories":155,"keywords":156,"publishedAt":50},"01M716XWXJ8EPHK4RH9T14TR85","grok","Alias for the canonical grokbot browser conversation workflow; \u002Fgrok talks to the Grok bot the same way \u002Fdot talks to Dot.","https:\u002F\u002Fgithub.com\u002Fjleechanorg\u002Fjleechan-skills\u002Fblob\u002Fmain\u002F.claude\u002Fskills\u002Fgrok\u002FSKILL.md",[],[152,157,158,159,129,160,132,161,162,163],"dot","bot","workflow","canonical","conversation","browser","grokbot",{"skillNo":165,"name":166,"nameZh":6,"description":167,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":168,"categories":169,"keywords":170,"publishedAt":50},"01M70N9Q07DTE0EW5KFXMMMSJ0","codexa-bench","Compare `codexa` and normal `codex` on a fair split of comparable work by default; use `--strict` for duplicate runs of the exact same task.","https:\u002F\u002Fgithub.com\u002Fjleechanorg\u002Fjleechan-skills\u002Fblob\u002Fmain\u002F.claude\u002Fskills\u002Fcodexa-bench\u002FSKILL.md",[],[171,172,173,38,174,129,175,176,177,178,179],"codexa bench","work","split","default","duplicate","task","compare","normal","codex",{"skillNo":181,"name":182,"nameZh":6,"description":183,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":184,"licenseFileUrl":185,"sourceHtmlUrl":186,"categories":187,"keywords":197,"publishedAt":50},"01M6C24S2X60SP8BBFGAEKPEGW","frontend-design","Create distinctive, production-grade frontend interfaces with high design quality. Use this skill when the user asks to build web components, pages, artifacts, posters, or applications (examples include websites, landing pages, dashboards, React components, HTML\u002FCSS layouts, or when styling\u002Fbeautifying any web UI). Generates creative, polished code and UI design that avoids generic AI aesthetics.","Apache-2.0","https:\u002F\u002Fgithub.com\u002Fjleechanorg\u002Fjleechan-skills\u002Fblob\u002Fmain\u002F.claude\u002Fskills\u002Ffrontend-design\u002FLICENSE.txt","https:\u002F\u002Fgithub.com\u002Fjleechanorg\u002Fjleechan-skills\u002Fblob\u002Fmain\u002F.claude\u002Fskills\u002Ffrontend-design\u002FSKILL.md",[188,191,194],{"categoryNo":189,"categoryName":190,"taxonomyType":20,"taxonomyTypeDesc":21},"01JBSEEDCAT000000000000009","前端开发",{"categoryNo":192,"categoryName":193,"taxonomyType":20,"taxonomyTypeDesc":21},"01JBSEEDCAT000000000000015","视觉设计",{"categoryNo":195,"categoryName":196,"taxonomyType":123,"taxonomyTypeDesc":124},"01JBSEEDCAT000000000000032","设计师",[198,199,200,38,201,202,203,106,204,205,206,207,208,209,210,211,212,213,214,215,216,217,218,219,220,221,28,222],"frontend","design","frontend design","generic","ui design","react","html","components","code","artifacts","ui","aesthetics","Production","web components","landing","applications","quality","web","css","create","examples","with","react components","web ui","pages"]