@acedatacloud/skills 2026.504.0 → 2026.504.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,201 @@
1
+ ---
2
+ name: google-drive
3
+ description: Read and search Google Drive files / folders / shared content via the Drive v3 REST API. Use when the user mentions Drive files, "my drive", shared documents, Google Docs / Sheets / Slides, exporting / downloading a Drive file, or searching by name / owner / folder.
4
+ when_to_use: |
5
+ Trigger when the user wants to list, search, read or download files
6
+ in their Google Drive — including Google-native docs (Docs / Sheets /
7
+ Slides) which need a special "export" call to get plain content. The
8
+ installed connector grants read-only scope (`drive.readonly`); writes
9
+ are out of scope.
10
+ connections: [google/drive]
11
+ allowed_tools: [Bash]
12
+ license: Apache-2.0
13
+ metadata:
14
+ author: acedatacloud
15
+ version: "1.0"
16
+ ---
17
+
18
+ Drive Google Drive via `curl + jq`. The user's OAuth bearer token is
19
+ in `$GOOGLE_DRIVE_TOKEN`; every call needs it as
20
+ `Authorization: Bearer $GOOGLE_DRIVE_TOKEN`. The token already carries
21
+ the `drive.readonly` scope the user agreed to at install plus the
22
+ identity scopes (`openid email profile`).
23
+
24
+ The Drive API returns standard JSON; failures surface as
25
+ `{"error": {"code": 401|403|..., "message": "..."}}` — show that
26
+ error verbatim to the user. `401` means the token expired and the
27
+ user must re-install the connector. `403 insufficientPermissions`
28
+ means the connector grants only `drive.readonly` and the user is
29
+ asking for a write — say so explicitly.
30
+
31
+ **Always start with `/about?fields=user`** to confirm the connection
32
+ works AND learn which Google account you're operating against.
33
+
34
+ ## Recipes
35
+
36
+ ### Verify auth (always run first)
37
+
38
+ ```sh
39
+ curl -sS -H "Authorization: Bearer $GOOGLE_DRIVE_TOKEN" \
40
+ "https://www.googleapis.com/drive/v3/about?fields=user(displayName,emailAddress,photoLink),storageQuota(usage,limit)" \
41
+ | jq '{user, quota: .storageQuota}'
42
+ ```
43
+
44
+ ### List recent files (last modified first)
45
+
46
+ ```sh
47
+ curl -sS -H "Authorization: Bearer $GOOGLE_DRIVE_TOKEN" \
48
+ "https://www.googleapis.com/drive/v3/files?orderBy=modifiedTime%20desc&pageSize=20&fields=files(id,name,mimeType,modifiedTime,owners(emailAddress),webViewLink,parents)" \
49
+ | jq '.files[] | {id, name, mimeType, modified: .modifiedTime, owner: .owners[0].emailAddress, webViewLink}'
50
+ ```
51
+
52
+ `pageSize` max is 1000; default is 100. Use `pageToken` from the
53
+ response (`nextPageToken`) for follow-up pages.
54
+
55
+ ### Search by name / fulltext
56
+
57
+ ```sh
58
+ # Exact-name fragments — note "name contains" supports tokens, not regex
59
+ Q='name contains "季度复盘" and trashed = false'
60
+ curl -sS -H "Authorization: Bearer $GOOGLE_DRIVE_TOKEN" \
61
+ --get "https://www.googleapis.com/drive/v3/files" \
62
+ --data-urlencode "q=$Q" \
63
+ --data-urlencode 'fields=files(id,name,mimeType,modifiedTime,webViewLink,owners(emailAddress))' \
64
+ --data-urlencode 'pageSize=20' \
65
+ | jq '.files[] | {id, name, modified: .modifiedTime, owner: .owners[0].emailAddress}'
66
+
67
+ # Full-text search (body + title)
68
+ Q='fullText contains "OKR 2026Q2" and trashed = false'
69
+ curl -sS -H "Authorization: Bearer $GOOGLE_DRIVE_TOKEN" \
70
+ --get "https://www.googleapis.com/drive/v3/files" \
71
+ --data-urlencode "q=$Q" \
72
+ --data-urlencode 'fields=files(id,name,modifiedTime,webViewLink)' \
73
+ | jq '.files[]'
74
+ ```
75
+
76
+ The `q` param uses [Drive's mini query language](https://developers.google.com/drive/api/guides/search-files):
77
+ `name`, `fullText`, `mimeType`, `parents`, `'<email>' in owners`,
78
+ `'<email>' in writers`, `modifiedTime > '2026-01-01T00:00:00'`,
79
+ `sharedWithMe`, `trashed`, joined by `and` / `or` / `not`.
80
+
81
+ ### List files shared with me
82
+
83
+ ```sh
84
+ curl -sS -H "Authorization: Bearer $GOOGLE_DRIVE_TOKEN" \
85
+ --get "https://www.googleapis.com/drive/v3/files" \
86
+ --data-urlencode 'q=sharedWithMe and trashed = false' \
87
+ --data-urlencode 'orderBy=sharedWithMeTime desc' \
88
+ --data-urlencode 'fields=files(id,name,mimeType,sharedWithMeTime,owners(displayName,emailAddress))' \
89
+ --data-urlencode 'pageSize=30' \
90
+ | jq '.files[] | {name, sharedAt: .sharedWithMeTime, sharedBy: .owners[0]}'
91
+ ```
92
+
93
+ ### List children of a folder
94
+
95
+ ```sh
96
+ FOLDER_ID='1A2B3CdEfGhIjKlMn'
97
+ curl -sS -H "Authorization: Bearer $GOOGLE_DRIVE_TOKEN" \
98
+ --get "https://www.googleapis.com/drive/v3/files" \
99
+ --data-urlencode "q='$FOLDER_ID' in parents and trashed = false" \
100
+ --data-urlencode 'fields=files(id,name,mimeType,size,modifiedTime),nextPageToken' \
101
+ | jq '.files'
102
+ ```
103
+
104
+ ### Get metadata for a single file
105
+
106
+ ```sh
107
+ FILE_ID='1A2B3CdEfGhIjKlMn'
108
+ curl -sS -H "Authorization: Bearer $GOOGLE_DRIVE_TOKEN" \
109
+ "https://www.googleapis.com/drive/v3/files/$FILE_ID?fields=id,name,mimeType,size,modifiedTime,parents,owners,webViewLink,description"
110
+ ```
111
+
112
+ ### Download a binary file (PDF / image / zip / …)
113
+
114
+ ```sh
115
+ FILE_ID='1A2B3CdEfGhIjKlMn'
116
+ OUT=/tmp/download.bin
117
+ curl -sS -L -H "Authorization: Bearer $GOOGLE_DRIVE_TOKEN" \
118
+ "https://www.googleapis.com/drive/v3/files/$FILE_ID?alt=media" \
119
+ -o "$OUT"
120
+ file "$OUT" && wc -c "$OUT"
121
+ ```
122
+
123
+ ### Read a Google Doc as plain markdown / text
124
+
125
+ Google-native files (Docs, Sheets, Slides) **don't have raw bytes** —
126
+ you have to ask Drive to *export* them to a concrete MIME type:
127
+
128
+ ```sh
129
+ DOC_ID='1A2B3CdEfGhIjKlMn'
130
+
131
+ # Markdown (best for chat-friendly summaries)
132
+ curl -sS -H "Authorization: Bearer $GOOGLE_DRIVE_TOKEN" \
133
+ "https://www.googleapis.com/drive/v3/files/$DOC_ID/export?mimeType=text/markdown" \
134
+ > /tmp/doc.md
135
+ head -40 /tmp/doc.md
136
+
137
+ # Plain text fallback for older docs
138
+ curl -sS -H "Authorization: Bearer $GOOGLE_DRIVE_TOKEN" \
139
+ "https://www.googleapis.com/drive/v3/files/$DOC_ID/export?mimeType=text/plain" \
140
+ > /tmp/doc.txt
141
+ ```
142
+
143
+ Common export MIME types:
144
+
145
+ | native MIME | export to |
146
+ |---|---|
147
+ | `application/vnd.google-apps.document` | `text/markdown`, `text/plain`, `text/html`, `application/pdf`, `application/vnd.openxmlformats-officedocument.wordprocessingml.document` |
148
+ | `application/vnd.google-apps.spreadsheet` | `text/csv`, `application/pdf`, `application/vnd.openxmlformats-officedocument.spreadsheetml.sheet` |
149
+ | `application/vnd.google-apps.presentation` | `application/pdf`, `text/plain`, `application/vnd.openxmlformats-officedocument.presentationml.presentation` |
150
+
151
+ ### Read a Google Sheet as CSV
152
+
153
+ ```sh
154
+ SHEET_ID='1A2B3CdEfGhIjKlMn'
155
+ curl -sS -H "Authorization: Bearer $GOOGLE_DRIVE_TOKEN" \
156
+ "https://www.googleapis.com/drive/v3/files/$SHEET_ID/export?mimeType=text/csv" \
157
+ > /tmp/sheet.csv
158
+ head /tmp/sheet.csv
159
+ ```
160
+
161
+ The Drive `export` endpoint returns the **first sheet only**. For
162
+ multi-tab access the user needs to install a separate Google Sheets
163
+ connector (currently out of catalog) — explain that and stop.
164
+
165
+ ### Get permissions / sharing on a file
166
+
167
+ ```sh
168
+ FILE_ID='1A2B3CdEfGhIjKlMn'
169
+ curl -sS -H "Authorization: Bearer $GOOGLE_DRIVE_TOKEN" \
170
+ "https://www.googleapis.com/drive/v3/files/$FILE_ID/permissions?fields=permissions(id,type,role,emailAddress,domain,deleted)" \
171
+ | jq '.permissions[] | {who: (.emailAddress // .domain // .type), role}'
172
+ ```
173
+
174
+ ### Pagination boilerplate
175
+
176
+ ```sh
177
+ PAGE_TOKEN=''
178
+ while : ; do
179
+ RESP=$(curl -sS -H "Authorization: Bearer $GOOGLE_DRIVE_TOKEN" \
180
+ --get "https://www.googleapis.com/drive/v3/files" \
181
+ --data-urlencode 'q=trashed = false' \
182
+ --data-urlencode 'fields=files(id,name),nextPageToken' \
183
+ --data-urlencode 'pageSize=200' \
184
+ ${PAGE_TOKEN:+--data-urlencode "pageToken=$PAGE_TOKEN"})
185
+ echo "$RESP" | jq -c '.files[]'
186
+ PAGE_TOKEN=$(echo "$RESP" | jq -r '.nextPageToken // empty')
187
+ [ -z "$PAGE_TOKEN" ] && break
188
+ done
189
+ ```
190
+
191
+ ## Common error codes
192
+
193
+ | HTTP | meaning | what to tell the user |
194
+ |---|---|---|
195
+ | `401 UNAUTHENTICATED` | token expired / revoked | "Reconnect the Google Drive connector on the Connections page." |
196
+ | `403 insufficientPermissions` | scope missing | "Your installed connector only grants read access — this action needs a write scope we don't have." |
197
+ | `403 userRateLimitExceeded` | quota | retry once after 5–10s; if it persists, tell the user. |
198
+ | `404 notFound` | wrong file id OR file isn't visible to this account | double-check the id; if shared, use `sharedWithMe` query above. |
199
+ | `400 invalidQuery` | malformed `q` | print the `q` you sent + the error message back to the user. |
200
+
201
+ Never log or echo `$GOOGLE_DRIVE_TOKEN` — treat it as a secret.
@@ -0,0 +1,214 @@
1
+ ---
2
+ name: google-gmail
3
+ description: Read, search and triage Gmail mail / threads / labels / attachments via the Gmail v1 REST API. Use when the user mentions Gmail, "my inbox", unread mail, recent emails from someone, summarising a thread, downloading an attachment, or finding mail by label / query.
4
+ when_to_use: |
5
+ Trigger when the user wants to read, list, search, summarise or
6
+ inspect Gmail mail — including triaging the inbox, surfacing unread,
7
+ pulling a single thread for review, or downloading an attachment.
8
+ The installed connector grants read-only scope (`gmail.readonly`);
9
+ sending / replying / archiving / labelling are out of scope.
10
+ connections: [google/gmail]
11
+ allowed_tools: [Bash]
12
+ license: Apache-2.0
13
+ metadata:
14
+ author: acedatacloud
15
+ version: "1.0"
16
+ ---
17
+
18
+ Drive Gmail via `curl + jq`. The user's OAuth bearer token is in
19
+ `$GOOGLE_GMAIL_TOKEN`; every call needs it as
20
+ `Authorization: Bearer $GOOGLE_GMAIL_TOKEN`. The token already
21
+ carries the `gmail.readonly` scope the user agreed to at install plus
22
+ the identity scopes (`openid email profile`).
23
+
24
+ The Gmail API returns standard JSON; failures surface as
25
+ `{"error": {"code": 401|403|..., "message": "..."}}` — show that
26
+ error verbatim. `401` means the token expired (re-install). `403`
27
+ or `400 insufficientPermissions` means the user is asking for a write
28
+ this connector cannot satisfy — say so.
29
+
30
+ **Always start with `users/me/profile`** to confirm the connection works
31
+ AND learn which Gmail account you're operating against. Mailbox payloads
32
+ can be huge — fetch metadata first, only `format=full` when the user
33
+ actually wants the body of a specific message.
34
+
35
+ ## Recipes
36
+
37
+ ### Verify auth (always run first)
38
+
39
+ ```sh
40
+ curl -sS -H "Authorization: Bearer $GOOGLE_GMAIL_TOKEN" \
41
+ "https://gmail.googleapis.com/gmail/v1/users/me/profile" \
42
+ | jq '{email: .emailAddress, totalMessages, totalThreads, historyId}'
43
+ ```
44
+
45
+ ### List recent unread inbox
46
+
47
+ ```sh
48
+ curl -sS -H "Authorization: Bearer $GOOGLE_GMAIL_TOKEN" \
49
+ --get "https://gmail.googleapis.com/gmail/v1/users/me/messages" \
50
+ --data-urlencode 'q=is:unread in:inbox newer_than:7d' \
51
+ --data-urlencode 'maxResults=20' \
52
+ | jq '.messages[]'
53
+ ```
54
+
55
+ The `messages.list` endpoint returns only `{id, threadId}` — you have
56
+ to fan out to `messages.get` for headers / body. Cheap pattern: list
57
+ ids → get with `format=metadata&metadataHeaders=From,Subject,Date` for
58
+ each. Use `format=full` only if the user wants the body.
59
+
60
+ ### List + enrich with headers (one-shot inbox triage)
61
+
62
+ ```sh
63
+ IDS=$(curl -sS -H "Authorization: Bearer $GOOGLE_GMAIL_TOKEN" \
64
+ --get "https://gmail.googleapis.com/gmail/v1/users/me/messages" \
65
+ --data-urlencode 'q=is:unread in:inbox' \
66
+ --data-urlencode 'maxResults=10' \
67
+ | jq -r '.messages[].id')
68
+
69
+ for ID in $IDS; do
70
+ curl -sS -H "Authorization: Bearer $GOOGLE_GMAIL_TOKEN" \
71
+ --get "https://gmail.googleapis.com/gmail/v1/users/me/messages/$ID" \
72
+ --data-urlencode 'format=metadata' \
73
+ --data-urlencode 'metadataHeaders=From' \
74
+ --data-urlencode 'metadataHeaders=Subject' \
75
+ --data-urlencode 'metadataHeaders=Date' \
76
+ | jq '{id: .id, snippet: .snippet, headers: (.payload.headers | map({(.name): .value}) | add), labels: .labelIds}'
77
+ done | jq -s '.'
78
+ ```
79
+
80
+ ### Read a single message body (plain text and html)
81
+
82
+ ```sh
83
+ ID='18f1a2b3c4d5e6f0'
84
+ RESP=$(curl -sS -H "Authorization: Bearer $GOOGLE_GMAIL_TOKEN" \
85
+ --get "https://gmail.googleapis.com/gmail/v1/users/me/messages/$ID" \
86
+ --data-urlencode 'format=full')
87
+
88
+ echo "$RESP" | jq '{id, snippet, headers: (.payload.headers | map({(.name): .value}) | add)}'
89
+
90
+ # Body is base64url-encoded inside payload.parts[].body.data — Gmail
91
+ # splits multipart messages, so collect every text/plain or text/html
92
+ # leaf and base64url-decode them.
93
+ echo "$RESP" | jq -r '
94
+ def walk(p):
95
+ if (p.parts // null) then (p.parts | map(walk(.)) | add) else [p] end;
96
+ walk(.payload)
97
+ | map(select(.mimeType=="text/plain" and (.body.data // "") != ""))
98
+ | .[].body.data' \
99
+ | tr '_-' '/+' | base64 -d 2>/dev/null
100
+ ```
101
+
102
+ If the plain-text leaf is empty, fall back to the `text/html` leaf
103
+ (same walk, swap the mimeType filter) and tell the user it's HTML.
104
+
105
+ ### Read a whole thread
106
+
107
+ ```sh
108
+ THREAD_ID='18f1a2b3c4d5e6f0'
109
+ curl -sS -H "Authorization: Bearer $GOOGLE_GMAIL_TOKEN" \
110
+ --get "https://gmail.googleapis.com/gmail/v1/users/me/threads/$THREAD_ID" \
111
+ --data-urlencode 'format=metadata' \
112
+ --data-urlencode 'metadataHeaders=From' \
113
+ --data-urlencode 'metadataHeaders=Subject' \
114
+ --data-urlencode 'metadataHeaders=Date' \
115
+ | jq '{id, historyId, messages: [.messages[] | {id, snippet, from: (.payload.headers | from_entries.From), date: (.payload.headers | from_entries.Date)}]}'
116
+ ```
117
+
118
+ ### Search by Gmail query
119
+
120
+ ```sh
121
+ # Same query DSL the Gmail UI uses: from:, to:, subject:, has:attachment,
122
+ # is:unread, label:Work, after:2026/04/01, before:2026/05/01, …
123
+ Q='from:boss@example.com subject:OKR newer_than:30d'
124
+ curl -sS -H "Authorization: Bearer $GOOGLE_GMAIL_TOKEN" \
125
+ --get "https://gmail.googleapis.com/gmail/v1/users/me/messages" \
126
+ --data-urlencode "q=$Q" \
127
+ --data-urlencode 'maxResults=20' \
128
+ | jq '.messages // []'
129
+ ```
130
+
131
+ `q` syntax reference: <https://support.google.com/mail/answer/7190> —
132
+ the model-friendly bits are `from:`, `to:`, `cc:`, `subject:`, `label:`,
133
+ `is:unread`, `is:read`, `is:starred`, `has:attachment`, `filename:pdf`,
134
+ `newer_than:7d`, `older_than:30d`, `after:YYYY/MM/DD`, `before:`, `in:inbox`,
135
+ `in:trash`. Combine with `OR` / `()` / `-`.
136
+
137
+ ### List labels (system + user-defined)
138
+
139
+ ```sh
140
+ curl -sS -H "Authorization: Bearer $GOOGLE_GMAIL_TOKEN" \
141
+ "https://gmail.googleapis.com/gmail/v1/users/me/labels" \
142
+ | jq '.labels[] | {id, name, type, color: .color.backgroundColor}'
143
+ ```
144
+
145
+ The system labels are `INBOX`, `SENT`, `DRAFT`, `IMPORTANT`, `UNREAD`,
146
+ `STARRED`, `SPAM`, `TRASH`, plus `CATEGORY_*` (Personal / Social /
147
+ Promotions / Updates / Forums).
148
+
149
+ ### Filter by label
150
+
151
+ ```sh
152
+ LABEL_ID='Label_4' # from labels.list above
153
+ curl -sS -H "Authorization: Bearer $GOOGLE_GMAIL_TOKEN" \
154
+ --get "https://gmail.googleapis.com/gmail/v1/users/me/messages" \
155
+ --data-urlencode "labelIds=$LABEL_ID" \
156
+ --data-urlencode 'maxResults=20' \
157
+ | jq '.messages // []'
158
+ ```
159
+
160
+ Multiple `labelIds` query params behave like AND.
161
+
162
+ ### Download an attachment
163
+
164
+ ```sh
165
+ MSG_ID='18f1a2b3c4d5e6f0'
166
+
167
+ # 1. find the attachment leaf
168
+ RESP=$(curl -sS -H "Authorization: Bearer $GOOGLE_GMAIL_TOKEN" \
169
+ --get "https://gmail.googleapis.com/gmail/v1/users/me/messages/$MSG_ID" \
170
+ --data-urlencode 'format=full')
171
+
172
+ echo "$RESP" | jq '
173
+ def walk(p):
174
+ if (p.parts // null) then (p.parts | map(walk(.)) | add) else [p] end;
175
+ walk(.payload)
176
+ | map(select(.body.attachmentId? != null))
177
+ | .[] | {filename, mimeType, attachmentId: .body.attachmentId, size: .body.size}'
178
+
179
+ # 2. fetch the attachment by id
180
+ ATT_ID='ANGjdJ-abc123'
181
+ OUT=/tmp/attachment.bin
182
+ curl -sS -H "Authorization: Bearer $GOOGLE_GMAIL_TOKEN" \
183
+ "https://gmail.googleapis.com/gmail/v1/users/me/messages/$MSG_ID/attachments/$ATT_ID" \
184
+ | jq -r .data | tr '_-' '/+' | base64 -d > "$OUT"
185
+ file "$OUT"
186
+ ```
187
+
188
+ ### Pagination
189
+
190
+ ```sh
191
+ PAGE_TOKEN=''
192
+ while : ; do
193
+ RESP=$(curl -sS -H "Authorization: Bearer $GOOGLE_GMAIL_TOKEN" \
194
+ --get "https://gmail.googleapis.com/gmail/v1/users/me/messages" \
195
+ --data-urlencode 'q=in:inbox' \
196
+ --data-urlencode 'maxResults=100' \
197
+ ${PAGE_TOKEN:+--data-urlencode "pageToken=$PAGE_TOKEN"})
198
+ echo "$RESP" | jq -c '.messages[]?'
199
+ PAGE_TOKEN=$(echo "$RESP" | jq -r '.nextPageToken // empty')
200
+ [ -z "$PAGE_TOKEN" ] && break
201
+ done
202
+ ```
203
+
204
+ ## Common error codes
205
+
206
+ | HTTP | meaning | what to tell the user |
207
+ |---|---|---|
208
+ | `401 UNAUTHENTICATED` | token expired / revoked | "Reconnect the Gmail connector on the Connections page." |
209
+ | `403 insufficientPermissions` | scope missing | "This connector grants only read access — modifying mail isn't possible." |
210
+ | `403 userRateLimitExceeded` / `429` | quota / throttling | back off ~5s, then retry once. |
211
+ | `404 notFound` | wrong message / thread / attachment id | double-check the id, or fall back to `messages.list` with the right query. |
212
+ | `400 invalidQuery` | malformed `q` | print the `q` you sent + the error back to the user. |
213
+
214
+ Never log or echo `$GOOGLE_GMAIL_TOKEN` — treat it as a secret.
@@ -0,0 +1,185 @@
1
+ ---
2
+ name: google-tasks
3
+ description: Read Google Tasks task lists and individual tasks via the Tasks v1 REST API. Use when the user mentions Google Tasks, todo / pending / overdue tasks, weekly task recap, or grouping todos by list.
4
+ when_to_use: |
5
+ Trigger when the user wants to inspect their Google Tasks — list
6
+ task lists, surface pending items, group by due date, or pull
7
+ details for one task. The installed connector grants read-only
8
+ scope (`tasks.readonly`); creating / updating / deleting tasks is
9
+ out of scope.
10
+ connections: [google/tasks]
11
+ allowed_tools: [Bash]
12
+ license: Apache-2.0
13
+ metadata:
14
+ author: acedatacloud
15
+ version: "1.0"
16
+ ---
17
+
18
+ Drive Google Tasks via `curl + jq`. The user's OAuth bearer token is
19
+ in `$GOOGLE_TASKS_TOKEN`; every call needs it as
20
+ `Authorization: Bearer $GOOGLE_TASKS_TOKEN`. The token already
21
+ carries the `tasks.readonly` scope the user agreed to at install plus
22
+ the identity scopes (`openid email profile`).
23
+
24
+ The Tasks API returns standard JSON; failures surface as
25
+ `{"error": {"code": 401|403|..., "message": "..."}}` — show that
26
+ error verbatim. `401` means the token expired (re-install). `403
27
+ insufficientPermissions` means the user is asking for a write this
28
+ connector cannot satisfy — say so.
29
+
30
+ **Always start with `users/@me/lists`** to discover which task lists
31
+ the account has — the user's default plus any extras they created on
32
+ calendar.google.com or in the Tasks app.
33
+
34
+ ## Recipes
35
+
36
+ ### Verify auth + list all task lists (always run first)
37
+
38
+ ```sh
39
+ curl -sS -H "Authorization: Bearer $GOOGLE_TASKS_TOKEN" \
40
+ "https://tasks.googleapis.com/tasks/v1/users/@me/lists" \
41
+ | jq '.items[] | {id, title, updated}'
42
+ ```
43
+
44
+ The default list is usually titled "我的任务" / "My Tasks" but the
45
+ **id** (a long opaque string like `MTAxMjM0NTY3OA`) is what every
46
+ subsequent `lists/{id}/tasks` call needs.
47
+
48
+ ### List all unfinished tasks across every list
49
+
50
+ ```sh
51
+ curl -sS -H "Authorization: Bearer $GOOGLE_TASKS_TOKEN" \
52
+ "https://tasks.googleapis.com/tasks/v1/users/@me/lists" \
53
+ | jq -r '.items[] | "\(.id)\t\(.title)"' | while IFS=$'\t' read LIST_ID LIST_TITLE; do
54
+ curl -sS -H "Authorization: Bearer $GOOGLE_TASKS_TOKEN" \
55
+ --get "https://tasks.googleapis.com/tasks/v1/lists/$LIST_ID/tasks" \
56
+ --data-urlencode 'showCompleted=false' \
57
+ --data-urlencode 'maxResults=100' \
58
+ | jq --arg list "$LIST_TITLE" '.items[]? | {list: $list, title, due, status, notes}'
59
+ done | jq -s '. | sort_by(.due // "9999")'
60
+ ```
61
+
62
+ `showCompleted=false` filters out done items at the API level. The
63
+ default `showCompleted=true&showHidden=false` returns done tasks too.
64
+
65
+ ### Pending tasks in one specific list, sorted by due date
66
+
67
+ ```sh
68
+ LIST_ID='MTAxMjM0NTY3OA'
69
+ curl -sS -H "Authorization: Bearer $GOOGLE_TASKS_TOKEN" \
70
+ --get "https://tasks.googleapis.com/tasks/v1/lists/$LIST_ID/tasks" \
71
+ --data-urlencode 'showCompleted=false' \
72
+ --data-urlencode 'maxResults=100' \
73
+ | jq '.items // [] | sort_by(.due // "9999") | .[] | {title, due, notes, status, position}'
74
+ ```
75
+
76
+ `position` is the user's drag-to-reorder rank inside the list — useful
77
+ when the user says "what's at the top of my tasks". Tasks without a
78
+ `due` field are open-ended.
79
+
80
+ ### Tasks due today
81
+
82
+ ```sh
83
+ TODAY=$(date -u +%Y-%m-%d)
84
+ TOMORROW=$(date -u -d "+1 day" +%Y-%m-%d 2>/dev/null \
85
+ || date -u -v+1d +%Y-%m-%d)
86
+ TODAY_START="${TODAY}T00:00:00.000Z"
87
+ TOMORROW_START="${TOMORROW}T00:00:00.000Z"
88
+
89
+ curl -sS -H "Authorization: Bearer $GOOGLE_TASKS_TOKEN" \
90
+ "https://tasks.googleapis.com/tasks/v1/users/@me/lists" \
91
+ | jq -r '.items[] | "\(.id)\t\(.title)"' | while IFS=$'\t' read LIST_ID LIST_TITLE; do
92
+ curl -sS -H "Authorization: Bearer $GOOGLE_TASKS_TOKEN" \
93
+ --get "https://tasks.googleapis.com/tasks/v1/lists/$LIST_ID/tasks" \
94
+ --data-urlencode "dueMin=$TODAY_START" \
95
+ --data-urlencode "dueMax=$TOMORROW_START" \
96
+ --data-urlencode 'showCompleted=false' \
97
+ | jq --arg list "$LIST_TITLE" '.items[]? | {list: $list, title, due, notes}'
98
+ done | jq -s
99
+ ```
100
+
101
+ `dueMin` / `dueMax` are RFC 3339 timestamps. The Tasks API stores
102
+ `due` at midnight UTC, so the local-day window is approximate around
103
+ the date boundary — that's fine for "due today" semantics, mention
104
+ the caveat only if the user pushes back.
105
+
106
+ ### Overdue tasks (everything still pending with a due date in the past)
107
+
108
+ ```sh
109
+ NOW=$(date -u +%Y-%m-%dT%H:%M:%S.000Z)
110
+ curl -sS -H "Authorization: Bearer $GOOGLE_TASKS_TOKEN" \
111
+ "https://tasks.googleapis.com/tasks/v1/users/@me/lists" \
112
+ | jq -r '.items[] | "\(.id)\t\(.title)"' | while IFS=$'\t' read LIST_ID LIST_TITLE; do
113
+ curl -sS -H "Authorization: Bearer $GOOGLE_TASKS_TOKEN" \
114
+ --get "https://tasks.googleapis.com/tasks/v1/lists/$LIST_ID/tasks" \
115
+ --data-urlencode "dueMax=$NOW" \
116
+ --data-urlencode 'showCompleted=false' \
117
+ | jq --arg list "$LIST_TITLE" '.items[]? | {list: $list, title, due, daysOverdue: (((now * 1000) - (.due | sub("Z"; "+00:00") | fromdateiso8601 * 1000)) / 86400000 | floor)}'
118
+ done | jq -s
119
+ ```
120
+
121
+ ### Recently completed tasks (this week, for a recap)
122
+
123
+ ```sh
124
+ ONE_WEEK_AGO=$(date -u -d "-7 days" +%Y-%m-%dT%H:%M:%S.000Z 2>/dev/null \
125
+ || date -u -v-7d +%Y-%m-%dT%H:%M:%S.000Z)
126
+
127
+ curl -sS -H "Authorization: Bearer $GOOGLE_TASKS_TOKEN" \
128
+ "https://tasks.googleapis.com/tasks/v1/users/@me/lists" \
129
+ | jq -r '.items[] | "\(.id)\t\(.title)"' | while IFS=$'\t' read LIST_ID LIST_TITLE; do
130
+ curl -sS -H "Authorization: Bearer $GOOGLE_TASKS_TOKEN" \
131
+ --get "https://tasks.googleapis.com/tasks/v1/lists/$LIST_ID/tasks" \
132
+ --data-urlencode 'showCompleted=true' \
133
+ --data-urlencode 'showHidden=true' \
134
+ --data-urlencode "completedMin=$ONE_WEEK_AGO" \
135
+ | jq --arg list "$LIST_TITLE" '.items[]? | select(.status=="completed") | {list: $list, title, completed}'
136
+ done | jq -s '. | sort_by(.completed)'
137
+ ```
138
+
139
+ `completedMin` / `completedMax` mirror `dueMin`/`Max` and only apply
140
+ to tasks already moved to the "completed" state. You **must** pass
141
+ `showCompleted=true` AND `showHidden=true` to see them — Google hides
142
+ completed tasks from the default list.
143
+
144
+ ### Get one task's details
145
+
146
+ ```sh
147
+ LIST_ID='MTAxMjM0NTY3OA'
148
+ TASK_ID='dGFza0lkRXhhbXBsZQ'
149
+ curl -sS -H "Authorization: Bearer $GOOGLE_TASKS_TOKEN" \
150
+ "https://tasks.googleapis.com/tasks/v1/lists/$LIST_ID/tasks/$TASK_ID" \
151
+ | jq '{title, due, status, notes, completed, position, links: .links}'
152
+ ```
153
+
154
+ `links` exposes the user's manual hyperlinks (e.g. an attached email
155
+ or Drive doc) — render them as a list to the user when present.
156
+
157
+ ### Pagination
158
+
159
+ `maxResults` caps at 100 per page. Use `nextPageToken`:
160
+
161
+ ```sh
162
+ LIST_ID='MTAxMjM0NTY3OA'
163
+ PAGE_TOKEN=''
164
+ while : ; do
165
+ RESP=$(curl -sS -H "Authorization: Bearer $GOOGLE_TASKS_TOKEN" \
166
+ --get "https://tasks.googleapis.com/tasks/v1/lists/$LIST_ID/tasks" \
167
+ --data-urlencode 'maxResults=100' \
168
+ --data-urlencode 'showCompleted=false' \
169
+ ${PAGE_TOKEN:+--data-urlencode "pageToken=$PAGE_TOKEN"})
170
+ echo "$RESP" | jq -c '.items[]?'
171
+ PAGE_TOKEN=$(echo "$RESP" | jq -r '.nextPageToken // empty')
172
+ [ -z "$PAGE_TOKEN" ] && break
173
+ done
174
+ ```
175
+
176
+ ## Common error codes
177
+
178
+ | HTTP | meaning | what to tell the user |
179
+ |---|---|---|
180
+ | `401 UNAUTHENTICATED` | token expired / revoked | "Reconnect the Google Tasks connector on the Connections page." |
181
+ | `403 insufficientPermissions` | scope missing | "This connector is read-only — adding or completing tasks isn't possible." |
182
+ | `404 notFound` | wrong list / task id | re-list with `users/@me/lists` to find the right id. |
183
+ | `429 quotaExceeded` | quota / throttling | back off ~5s, then retry once. |
184
+
185
+ Never log or echo `$GOOGLE_TASKS_TOKEN` — treat it as a secret.