@xiaotianxt/skills 0.1.0
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.
- package/EXCLUDED.md +42 -0
- package/LICENSE +21 -0
- package/README.md +165 -0
- package/SECURITY.md +23 -0
- package/SOURCES.md +45 -0
- package/bin/skills.mjs +241 -0
- package/package.json +38 -0
- package/skills/1password/SKILL.md +94 -0
- package/skills/1password/agents/openai.yaml +4 -0
- package/skills/1password/references/item-management.md +80 -0
- package/skills/1password/references/op-cli.md +107 -0
- package/skills/apple-calendar-event/SKILL.md +81 -0
- package/skills/apple-calendar-event/agents/openai.yaml +4 -0
- package/skills/apple-calendar-event/scripts/calendar_audit.py +201 -0
- package/skills/apple-calendar-event/scripts/calendar_event.py +164 -0
- package/skills/bro-browser/SKILL.md +118 -0
- package/skills/bro-browser/agents/openai.yaml +4 -0
- package/skills/bro-browser/references/tool-map.md +102 -0
- package/skills/bro-browser/references/workflows.md +146 -0
- package/skills/bro-browser/scripts/bro-call.mjs +189 -0
- package/skills/calendar/SKILL.md +182 -0
- package/skills/calendar/agents/openai.yaml +4 -0
- package/skills/calendar/references/operations.md +255 -0
- package/skills/calendar/scripts/calendar_list_review.py +157 -0
- package/skills/calendar/scripts/event_dedupe_preview.py +155 -0
- package/skills/canvas/SKILL.md +70 -0
- package/skills/canvas/agents/openai.yaml +4 -0
- package/skills/canvas/references/canvas-api.md +76 -0
- package/skills/course-exam-review-planner/SKILL.md +127 -0
- package/skills/cx/SKILL.md +25 -0
- package/skills/gh-fix-ci/LICENSE.txt +201 -0
- package/skills/gh-fix-ci/SKILL.md +81 -0
- package/skills/gh-fix-ci/agents/openai.yaml +6 -0
- package/skills/gh-fix-ci/assets/github-small.svg +3 -0
- package/skills/gh-fix-ci/assets/github.png +0 -0
- package/skills/gh-fix-ci/scripts/inspect_pr_checks.py +509 -0
- package/skills/gh-review-workflow/SKILL.md +61 -0
- package/skills/gh-review-workflow/agents/openai.yaml +4 -0
- package/skills/gh-review-workflow/references/workflow.md +48 -0
- package/skills/gh-review-workflow/scripts/fetch_review_state.py +222 -0
- package/skills/gh-review-workflow/scripts/resolve_review_threads.py +83 -0
- package/skills/github/SKILL.md +74 -0
- package/skills/github/agents/openai.yaml +6 -0
- package/skills/github/assets/github-small.svg +3 -0
- package/skills/github/assets/github.png +0 -0
- package/skills/gws-calendar/SKILL.md +126 -0
- package/skills/gws-calendar-agenda/SKILL.md +52 -0
- package/skills/gws-calendar-insert/SKILL.md +66 -0
- package/skills/gws-docs/SKILL.md +48 -0
- package/skills/gws-docs-write/SKILL.md +49 -0
- package/skills/gws-drive/SKILL.md +137 -0
- package/skills/gws-drive-upload/SKILL.md +52 -0
- package/skills/gws-gmail/SKILL.md +62 -0
- package/skills/gws-gmail-forward/SKILL.md +55 -0
- package/skills/gws-gmail-reply/SKILL.md +58 -0
- package/skills/gws-gmail-reply-all/SKILL.md +62 -0
- package/skills/gws-gmail-send/SKILL.md +57 -0
- package/skills/gws-gmail-triage/SKILL.md +50 -0
- package/skills/gws-gmail-watch/SKILL.md +58 -0
- package/skills/gws-shared/SKILL.md +27 -0
- package/skills/helium-browser-mcp/SKILL.md +137 -0
- package/skills/helium-browser-mcp/agents/openai.yaml +4 -0
- package/skills/helium-browser-mcp/scripts/obmcp.mjs +92 -0
- package/skills/helium-browser-mcp/scripts/openbrowsermcp-stdio-proxy.mjs +170 -0
- package/skills/learn/SKILL.md +122 -0
- package/skills/learn/agents/openai.yaml +7 -0
- package/skills/learn/assets/AGENTS.template.md +33 -0
- package/skills/learn/assets/errorlog.template.typ +61 -0
- package/skills/learn/assets/reading-sequence.template.md +23 -0
- package/skills/learn/assets/source-index.template.md +17 -0
- package/skills/learn/assets/tasklog.template.typ +57 -0
- package/skills/learn/assets/workbook.template.typ +60 -0
- package/skills/learn/references/learning-science.md +103 -0
- package/skills/learn/scripts/init_learning_workspace.py +70 -0
- package/skills/macos-messages/SKILL.md +258 -0
- package/skills/memory/SKILL.md +33 -0
- package/skills/memory/codex.md +186 -0
- package/skills/memory/opencode.md +164 -0
- package/skills/mimestreamctl/SKILL.md +170 -0
- package/skills/mimestreamctl/agents/openai.yaml +4 -0
- package/skills/mimestreamctl/scripts/mimestreamctl +33 -0
- package/skills/mon/SKILL.md +51 -0
- package/skills/mon/scripts/mon_spend_review.py +458 -0
- package/skills/ocr/SKILL.md +136 -0
- package/skills/ocr/agents/openai.yaml +4 -0
- package/skills/ocr/references/local-ocr-best-practices.md +297 -0
- package/skills/ocr/references/mineru-api.md +159 -0
- package/skills/ocr/scripts/ocr-router +22 -0
- package/skills/ocr/scripts/ocr_router.py +741 -0
- package/skills/panopto-mp4-bulk-download/SKILL.md +57 -0
- package/skills/panopto-mp4-bulk-download/agents/openai.yaml +4 -0
- package/skills/panopto-mp4-bulk-download/references/url-patterns.md +26 -0
- package/skills/panopto-mp4-bulk-download/scripts/panopto_bulk_mp4.sh +213 -0
- package/skills/rust-systems-style/SKILL.md +109 -0
- package/skills/rust-systems-style/agents/openai.yaml +4 -0
- package/skills/rust-systems-style/references/rust-review-checklist.md +77 -0
- package/skills/rust-systems-style/references/style-sources.md +68 -0
- package/skills/ship-ai-native-cli/SKILL.md +76 -0
- package/skills/ship-ai-native-cli/agents/openai.yaml +4 -0
- package/skills/ship-ai-native-cli/references/case-notes.md +83 -0
- package/skills/ship-ai-native-cli/references/product-method.md +82 -0
- package/skills/ship-ai-native-cli/references/release-checklist.md +147 -0
- package/skills/ship-ai-native-cli/references/rust-cli-shape.md +111 -0
- package/skills/telegram-mtproto-session/SKILL.md +125 -0
- package/skills/telegram-mtproto-session/agents/openai.yaml +4 -0
- package/skills/telegram-mtproto-session/scripts/telegram_session.py +687 -0
- package/skills/tg/SKILL.md +173 -0
- package/skills/things3-manager/SKILL.md +116 -0
- package/skills/things3-manager/scripts/things +42 -0
- package/skills/things3-manager/scripts/things_cli.py +514 -0
- package/skills/web-artifacts-builder/LICENSE.txt +202 -0
- package/skills/web-artifacts-builder/SKILL.md +74 -0
- package/skills/web-artifacts-builder/scripts/bundle-artifact.sh +54 -0
- package/skills/web-artifacts-builder/scripts/init-artifact.sh +379 -0
- package/skills/web-artifacts-builder/scripts/shadcn-components.tar.gz +0 -0
- package/skills/yeet/LICENSE.txt +201 -0
- package/skills/yeet/SKILL.md +71 -0
- package/skills/yeet/agents/openai.yaml +6 -0
- package/skills/yeet/assets/yeet-small.svg +3 -0
- package/skills/yeet/assets/yeet.png +0 -0
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: calendar
|
|
3
|
+
description: Govern and maintain the user's personal calendar system across Google Calendar, Apple Calendar.app, school/legacy accounts, and automation. Use when Codex needs to audit calendar state, choose the correct write target, create or verify calendar events, clean up cluttered calendar lists, rename or hide calendars, migrate away from school/Apple/iCloud/legacy sources, prevent non-authoritative accounts from owning personal events, or reason about Google Calendar vs macOS Calendar.app behavior.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Calendar
|
|
7
|
+
|
|
8
|
+
## Purpose
|
|
9
|
+
|
|
10
|
+
Use this as the top-level calendar governance skill. It decides source of truth, write targets, audit depth, and safety level before delegating to lower-level skills such as `gws-calendar`, `gws-calendar-insert`, `gws-calendar-agenda`, and `apple-calendar-event`.
|
|
11
|
+
|
|
12
|
+
## First Principles
|
|
13
|
+
|
|
14
|
+
- Treat calendars as data ownership boundaries, not folders. A separate calendar is justified only by a different owner, lifecycle, permission model, visibility need, reminder policy, or read-only source. Otherwise put context in the event title, description, location, guests, or reminders.
|
|
15
|
+
- Make `tianyupeiandy@gmail.com` in Google Calendar the durable source of truth for personal commitments. Default personal writes target calendar ID `tianyupeiandy@gmail.com`.
|
|
16
|
+
- Treat `yupeit@andrew.cmu.edu` and other school calendars as transition sources because the account may expire. Read and migrate from them; avoid new durable writes there unless the user explicitly requests school-owned placement.
|
|
17
|
+
- Treat Apple Calendar.app as a local view and legacy/transition layer. It can reveal defaults, duplicate names, and local sources, but it is not the default automation write path.
|
|
18
|
+
- Treat `1241811980i59@gmail.com`, `yupeigemini@gmail.com`, iCloud, Reminders, Siri Suggestions, subscribed calendars, and browser account defaults as non-authoritative for personal calendar ownership unless the user explicitly overrides this.
|
|
19
|
+
- Do not infer ownership from display names. Use authenticated account, calendar ID, `primary`, `accessRole`, real calendar metadata, and Apple store/account audit output.
|
|
20
|
+
- Prefer reversible changes first: `selected`, `hidden`, `summaryOverride`, and documented renames. Delete, unsubscribe, or bulk-copy only after explicit confirmation, backups, and rollback notes.
|
|
21
|
+
- Optimize for the user's real workflows: fast event capture, reliable agenda reads, low visual clutter, account-lifecycle resilience, and no surprise ownership drift.
|
|
22
|
+
|
|
23
|
+
## Default Write Policy
|
|
24
|
+
|
|
25
|
+
For event creation when the user says "my calendar" or gives no target:
|
|
26
|
+
|
|
27
|
+
1. Use `gws auth status`.
|
|
28
|
+
2. Require `user == "tianyupeiandy@gmail.com"` and `token_valid == true`.
|
|
29
|
+
3. Write to Google Calendar ID `tianyupeiandy@gmail.com`.
|
|
30
|
+
4. State the active account, target calendar ID, title, and exact local start/end before writing.
|
|
31
|
+
5. Use Apple Calendar only when the user explicitly asks for Apple Calendar or Calendar.app.
|
|
32
|
+
|
|
33
|
+
Do not rely on:
|
|
34
|
+
|
|
35
|
+
- Google Calendar web UI account switcher default labels.
|
|
36
|
+
- Calendar.app's default calendar.
|
|
37
|
+
- Display names alone when duplicate names exist.
|
|
38
|
+
- `primary` in multi-account or account-confusing contexts.
|
|
39
|
+
|
|
40
|
+
## Scenario Router
|
|
41
|
+
|
|
42
|
+
### Read or Agenda
|
|
43
|
+
|
|
44
|
+
Use `gws-calendar-agenda` or raw `gws calendar events list` against Google first. Include Apple Calendar.app only when the user asks about local Calendar.app state, missing events in Apple, duplicate display names, or default-calendar behavior.
|
|
45
|
+
|
|
46
|
+
### Create or Edit Personal Events
|
|
47
|
+
|
|
48
|
+
Use `gws-calendar-insert` or raw `gws calendar +insert` with explicit `--calendar tianyupeiandy@gmail.com`. Before writing, state the active account, calendar ID, title, exact local start/end, time zone, and whether guests/conference/reminders will be created. If the auth account is not `tianyupeiandy@gmail.com`, stop and re-auth instead of writing through another account.
|
|
49
|
+
|
|
50
|
+
### Diagnose "My Calendar Is Messy"
|
|
51
|
+
|
|
52
|
+
Inventory Google CalendarList and Apple Calendar.app first. Separate issues into ownership, visibility, naming, duplication, and lifecycle. Produce a proposed table before mutating anything: calendar ID, current label, likely owner/source, role, selected/hidden, recommended taxonomy bucket, proposed action, rollback.
|
|
53
|
+
|
|
54
|
+
### Rename, Hide, or Reduce Visual Clutter
|
|
55
|
+
|
|
56
|
+
Prefer view-level changes: `selected`, `hidden`, then `summaryOverride`. Use real `calendars.patch {"summary": ...}` only when the user wants the actual Google Calendar `My calendars` name changed and the calendar is owned/controlled by the intended account. Confirm before renaming shared, external, read-only, school, holiday, birthday, task, or subscribed calendars.
|
|
57
|
+
|
|
58
|
+
### Migrate Away From School, Apple, iCloud, or Legacy Accounts
|
|
59
|
+
|
|
60
|
+
Make an inventory first, usually future actionable events only. Compare against the primary calendar by `iCalUID`, normalized title, start time, and location. Treat dedupe previews as advisory: recurrence expansion, changed titles, changed locations, time zones, guests, Meet links, attachments, reminders, privacy, and organizer semantics need explicit review before copying. Keep the source visible but unselected during migration; do not delete or unsubscribe until the user confirms the migrated state.
|
|
61
|
+
|
|
62
|
+
### Investigate Apple Calendar.app
|
|
63
|
+
|
|
64
|
+
Use `apple-calendar-event` for local audit and explicit Apple writes only. Run its audit before any Apple write when source or duplicate names matter. Never edit `Calendar.sqlitedb`; treat it as a read-only cache.
|
|
65
|
+
|
|
66
|
+
### Delete or Unsubscribe
|
|
67
|
+
|
|
68
|
+
Default to "hide/archive" instead. Delete, unsubscribe, or remove shared calendars only after the user confirms the exact calendar ID/source and a backup exists. Built-ins such as Birthdays and Tasks are not ordinary owned cleanup targets.
|
|
69
|
+
|
|
70
|
+
## Governance Workflow
|
|
71
|
+
|
|
72
|
+
### 1. Classify Risk
|
|
73
|
+
|
|
74
|
+
- **Read-only**: agenda, inventory, audit, explain state. No backup required unless saving a report.
|
|
75
|
+
- **View mutation**: select/unselect, hide/unhide, `summaryOverride`. Backup CalendarList first.
|
|
76
|
+
- **Metadata mutation**: real calendar `summary`, description, time zone, reminders. Backup CalendarList and affected `calendars.get`.
|
|
77
|
+
- **Event mutation**: create, edit, copy, migrate, delete. Verify auth, target ID, exact timestamps, and event semantics.
|
|
78
|
+
- **Destructive mutation**: delete calendar, unsubscribe, bulk delete, remove source after migration. Require explicit confirmation and rollback/backup discussion.
|
|
79
|
+
|
|
80
|
+
### 2. Build State From Source APIs
|
|
81
|
+
|
|
82
|
+
Use these checks before decisions that affect ownership or visibility:
|
|
83
|
+
|
|
84
|
+
```bash
|
|
85
|
+
gws auth status
|
|
86
|
+
gws calendar calendarList list --params '{"showHidden":true,"maxResults":250}' > calendar-list-before.json
|
|
87
|
+
python3 /Users/yupeit/dev/skills/skills/calendar/scripts/calendar_list_review.py --calendar-list calendar-list-before.json --format tsv
|
|
88
|
+
cd /Users/yupeit/dev/skills/skills/apple-calendar-event && python3 scripts/calendar_audit.py --json
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
Interpretation rules:
|
|
92
|
+
|
|
93
|
+
- CalendarList `dataOwner` is often absent. Infer ownership from `primary`, calendar ID, `accessRole`, real calendar metadata from `calendars.get`, and Apple audit store/account fields when reconciling with Calendar.app.
|
|
94
|
+
- Google `calendarList.summaryOverride` is a per-user list label. It may not consistently change `My calendars` display names in the web UI.
|
|
95
|
+
- Google `calendars.patch {"summary": ...}` changes the real calendar title and is the right operation when the user wants the left sidebar name itself changed.
|
|
96
|
+
- Real `summary` changes can affect how shared calendars appear to other users. Confirm intent before renaming shared or externally owned calendars.
|
|
97
|
+
- If both `summaryOverride` and real `summary` are present, explain to the user that Google UI may prefer real `summary` in `My calendars` and `summaryOverride` in shared/other list contexts. Use API output to verify the exact state.
|
|
98
|
+
- Google `calendarList.patch {"selected": ..., "hidden": ...}` changes sidebar visibility without deleting calendars or events.
|
|
99
|
+
- Google built-ins such as Birthdays and Tasks are not ordinary user calendars; do not treat them as owned cleanup targets.
|
|
100
|
+
- Apple `Calendar.sqlitedb` is useful for read-only source mapping, duplicate names, and default calendar diagnosis. Never edit it directly.
|
|
101
|
+
|
|
102
|
+
### 3. Propose Before Mutating
|
|
103
|
+
|
|
104
|
+
For governance work, present the plan in concrete operations:
|
|
105
|
+
|
|
106
|
+
- `keep`: authoritative or intentionally visible.
|
|
107
|
+
- `hide`: remove visual clutter without deleting data.
|
|
108
|
+
- `label`: add/change `summaryOverride`.
|
|
109
|
+
- `rename`: change real `summary`.
|
|
110
|
+
- `migrate`: copy reviewed events to `tianyupeiandy@gmail.com`.
|
|
111
|
+
- `archive`: hide legacy source after migration.
|
|
112
|
+
- `delete/unsubscribe`: only after explicit confirmation.
|
|
113
|
+
|
|
114
|
+
Mention commands you would run and commands you would avoid when the request is a planning/governance request.
|
|
115
|
+
|
|
116
|
+
### 4. Backup Before Mutations
|
|
117
|
+
|
|
118
|
+
Before any calendar governance mutation, create a timestamped backup under:
|
|
119
|
+
|
|
120
|
+
```text
|
|
121
|
+
/Users/yupeit/Desktop/hertz/calendar-governance-backups/<timestamp>-<operation>/
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
Save at least:
|
|
125
|
+
|
|
126
|
+
- `gws-auth-status.json`
|
|
127
|
+
- `calendar-list-before.json`
|
|
128
|
+
- affected `calendars.get` JSON files for real summary/metadata edits
|
|
129
|
+
- Apple audit JSON when Apple Calendar.app state is relevant
|
|
130
|
+
- a short `report.md` with scope, changes, and rollback values
|
|
131
|
+
|
|
132
|
+
### 5. Apply The Least Powerful Change
|
|
133
|
+
|
|
134
|
+
Use this escalation order:
|
|
135
|
+
|
|
136
|
+
1. Select/unselect calendars with `calendarList.patch selected`.
|
|
137
|
+
2. Hide/unhide calendars with `calendarList.patch hidden`.
|
|
138
|
+
3. Set `summaryOverride` for list labels where it is sufficient.
|
|
139
|
+
4. Patch real `summary` when the user wants Google UI `My calendars` names to change.
|
|
140
|
+
5. Copy/migrate events only after inventory and dedupe planning.
|
|
141
|
+
6. Delete/unsubscribe only after explicit confirmation.
|
|
142
|
+
|
|
143
|
+
### 6. Verify And Report
|
|
144
|
+
|
|
145
|
+
After any write, re-read the affected resource. Report the active account, exact calendar ID, changed fields, backup path, and rollback command. If verification fails, stop and diagnose before retrying.
|
|
146
|
+
|
|
147
|
+
## Current Governance Model
|
|
148
|
+
|
|
149
|
+
The user's intended taxonomy is:
|
|
150
|
+
|
|
151
|
+
- `00 Core - Google 主号`: primary personal commitments; default automation write target.
|
|
152
|
+
- `10 Projects - ...`: active project calendars that need independent visibility.
|
|
153
|
+
- `20 Shared - ...`: other people's calendars or shared views.
|
|
154
|
+
- `30 School Transition - ...`: school-owned or school-lifecycle calendars to migrate away from.
|
|
155
|
+
- `80 Archive - ...`: hidden legacy or inactive calendars.
|
|
156
|
+
- `90 Reference - ...`: holidays and read-only reference calendars.
|
|
157
|
+
|
|
158
|
+
Current known source-of-truth assumptions:
|
|
159
|
+
|
|
160
|
+
- Primary Google account: `tianyupeiandy@gmail.com`.
|
|
161
|
+
- Preferred automation target calendar ID: `tianyupeiandy@gmail.com`.
|
|
162
|
+
- School transition source: `yupeit@andrew.cmu.edu`.
|
|
163
|
+
- Apple Calendar.app: view/legacy layer, not default write target.
|
|
164
|
+
|
|
165
|
+
## Lower-Level Skills
|
|
166
|
+
|
|
167
|
+
- Use `gws-calendar` for Calendar API discovery, list, patch, event operations, and low-level Google Calendar resources.
|
|
168
|
+
- Use `gws-calendar-insert` for straightforward Google event creation.
|
|
169
|
+
- Use `gws-calendar-agenda` for upcoming event views.
|
|
170
|
+
- Use `apple-calendar-event` for local Apple Calendar.app audits and explicit Apple Calendar event writes.
|
|
171
|
+
|
|
172
|
+
## OAuth Scope Expectations
|
|
173
|
+
|
|
174
|
+
- Read/audit operations may work with Calendar read scopes.
|
|
175
|
+
- CalendarList visibility and label changes require CalendarList write scope.
|
|
176
|
+
- Real calendar metadata edits require calendar metadata write scope.
|
|
177
|
+
- Event creation or migration requires calendar event write scope.
|
|
178
|
+
- If scope is missing, re-auth with the narrowest calendar service flow and avoid broad Workspace scopes unless the task needs them.
|
|
179
|
+
|
|
180
|
+
## Detailed Recipes
|
|
181
|
+
|
|
182
|
+
For copy-pastable commands, migration dedupe preview, and rollback patterns, read `references/operations.md` only when performing governance mutations, migrations, or Apple/Google reconciliation.
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
interface:
|
|
2
|
+
display_name: "Calendar"
|
|
3
|
+
short_description: "Govern Google-first calendars, Apple views, and school migrations."
|
|
4
|
+
default_prompt: "Use $calendar to audit, govern, migrate, and maintain my Google-first personal calendar system without accidental Apple, school, or secondary-account ownership."
|
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
# Calendar Operations
|
|
2
|
+
|
|
3
|
+
## Contents
|
|
4
|
+
|
|
5
|
+
- Snapshot and backup
|
|
6
|
+
- Active account verification
|
|
7
|
+
- Read-only inventory and governance review
|
|
8
|
+
- Visibility, labels, and real names
|
|
9
|
+
- Event creation
|
|
10
|
+
- Apple Calendar audit
|
|
11
|
+
- School and legacy migration
|
|
12
|
+
- Commands to avoid
|
|
13
|
+
- Scope expectations and rollback
|
|
14
|
+
|
|
15
|
+
Use this file for copy-pastable commands. Do not run mutation commands during a planning-only request; show the command and explain the preconditions instead.
|
|
16
|
+
|
|
17
|
+
## Snapshot
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
backup_dir="/Users/yupeit/Desktop/hertz/calendar-governance-backups/$(date +%Y%m%d-%H%M%S)-calendar"
|
|
21
|
+
mkdir -p "$backup_dir"
|
|
22
|
+
gws auth status > "$backup_dir/gws-auth-status.json"
|
|
23
|
+
gws calendar calendarList list --params '{"showHidden":true,"maxResults":250}' > "$backup_dir/calendar-list-before.json"
|
|
24
|
+
python3 /Users/yupeit/dev/skills/skills/calendar/scripts/calendar_list_review.py \
|
|
25
|
+
--calendar-list "$backup_dir/calendar-list-before.json" \
|
|
26
|
+
--format tsv > "$backup_dir/calendar-list-review.tsv"
|
|
27
|
+
cd /Users/yupeit/dev/skills/skills/apple-calendar-event
|
|
28
|
+
python3 scripts/calendar_audit.py --json > "$backup_dir/apple-calendar-audit-before.json"
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## Verify Active Account
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
gws auth status | jq -e '.user == "tianyupeiandy@gmail.com" and .token_valid == true'
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
If this fails, do not write events or patch calendar metadata. Re-auth with the narrowest needed scope, usually:
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
gws auth login --services calendar
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## Read-Only Inventory And Governance Review
|
|
44
|
+
|
|
45
|
+
Use this when the user says the calendar is messy, asks what to rename/hide/migrate, or asks why events are going to the wrong account. These commands do not mutate calendars.
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
gws calendar calendarList list --params '{"showHidden":true,"maxResults":250}' \
|
|
49
|
+
> "$backup_dir/calendar-list-before.json"
|
|
50
|
+
|
|
51
|
+
gws calendar calendarList list --params '{"showHidden":true,"maxResults":250}' \
|
|
52
|
+
| jq -r '.items[] | [
|
|
53
|
+
.summary,
|
|
54
|
+
(.summaryOverride // ""),
|
|
55
|
+
.id,
|
|
56
|
+
(.primary // false),
|
|
57
|
+
.accessRole,
|
|
58
|
+
(.selected // false),
|
|
59
|
+
(.hidden // false)
|
|
60
|
+
] | @tsv'
|
|
61
|
+
|
|
62
|
+
python3 /Users/yupeit/dev/skills/skills/calendar/scripts/calendar_list_review.py \
|
|
63
|
+
--calendar-list "$backup_dir/calendar-list-before.json" \
|
|
64
|
+
--format tsv
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
When `dataOwner` is blank, do not assume the calendar has no owner. Use:
|
|
68
|
+
|
|
69
|
+
- `primary == true` and ID `tianyupeiandy@gmail.com` for the primary source.
|
|
70
|
+
- `accessRole` to understand the current account's effective control.
|
|
71
|
+
- `gws calendar calendars get --params '{"calendarId":"CALENDAR_ID"}'` for real metadata.
|
|
72
|
+
- Apple audit `store_name`, `store_owner`, and identity fields when reconciling Calendar.app display entries.
|
|
73
|
+
|
|
74
|
+
For calendars that might be renamed or migrated, capture real metadata before proposing a final write:
|
|
75
|
+
|
|
76
|
+
```bash
|
|
77
|
+
gws calendar calendars get --params '{"calendarId":"CALENDAR_ID"}'
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
## Visibility Governance
|
|
81
|
+
|
|
82
|
+
Patch `selected` and `hidden` on the user's CalendarList entry. This affects the user's view and does not delete calendars or events.
|
|
83
|
+
|
|
84
|
+
```bash
|
|
85
|
+
gws calendar calendarList patch \
|
|
86
|
+
--params '{"calendarId":"CALENDAR_ID"}' \
|
|
87
|
+
--json '{"selected":true,"hidden":false}'
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
Hide archive calendars:
|
|
91
|
+
|
|
92
|
+
```bash
|
|
93
|
+
gws calendar calendarList patch \
|
|
94
|
+
--params '{"calendarId":"CALENDAR_ID"}' \
|
|
95
|
+
--json '{"selected":false,"hidden":true}'
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
## Display Labels vs Real Names
|
|
99
|
+
|
|
100
|
+
`summaryOverride` is a per-user CalendarList label. It is reversible and low risk, but Google Calendar web UI may still display the real `summary` under `My calendars`.
|
|
101
|
+
|
|
102
|
+
```bash
|
|
103
|
+
gws calendar calendarList patch \
|
|
104
|
+
--params '{"calendarId":"CALENDAR_ID"}' \
|
|
105
|
+
--json '{"summaryOverride":"80 Archive - Old Name"}'
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
Use real `summary` when the user wants the actual Google Calendar sidebar name changed:
|
|
109
|
+
|
|
110
|
+
```bash
|
|
111
|
+
gws calendar calendars patch \
|
|
112
|
+
--params '{"calendarId":"CALENDAR_ID"}' \
|
|
113
|
+
--json '{"summary":"00 Core - Google 主号"}'
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
If `summaryOverride` and `summary` conflict, explain this directly: Google Calendar may show the real `summary` in `My calendars`, while API/list contexts can still expose `summaryOverride`. Verify with both `calendarList.list` and `calendars.get`.
|
|
117
|
+
|
|
118
|
+
Real `summary` edits may affect what shared users see. Confirm before renaming shared calendars or calendars whose owner is not clearly `tianyupeiandy@gmail.com`.
|
|
119
|
+
|
|
120
|
+
Before real summary edits, save:
|
|
121
|
+
|
|
122
|
+
```bash
|
|
123
|
+
safe_id="$(printf '%s' 'CALENDAR_ID' | tr '/:@# ' '_____')"
|
|
124
|
+
gws calendar calendars get --params '{"calendarId":"CALENDAR_ID"}' > "$backup_dir/calendar-before-$safe_id.json"
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
## Default Event Creation
|
|
128
|
+
|
|
129
|
+
Preflight:
|
|
130
|
+
|
|
131
|
+
```bash
|
|
132
|
+
gws auth status | jq -e '.user == "tianyupeiandy@gmail.com" and .token_valid == true'
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
Then write only with an explicit target:
|
|
136
|
+
|
|
137
|
+
```bash
|
|
138
|
+
gws calendar +insert \
|
|
139
|
+
--calendar 'tianyupeiandy@gmail.com' \
|
|
140
|
+
--summary 'TITLE' \
|
|
141
|
+
--start 'YYYY-MM-DDTHH:MM:SS-07:00' \
|
|
142
|
+
--end 'YYYY-MM-DDTHH:MM:SS-07:00' \
|
|
143
|
+
--location 'LOCATION_OR_URL' \
|
|
144
|
+
--description 'NOTES'
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
Always use exact local timestamps. Do not rely on relative phrases after the planning step.
|
|
148
|
+
|
|
149
|
+
Avoid these for default personal writes:
|
|
150
|
+
|
|
151
|
+
```bash
|
|
152
|
+
# Do not use the current browser account.
|
|
153
|
+
# Do not use Calendar.app's default calendar.
|
|
154
|
+
# Do not use --calendar primary in account-confusing contexts.
|
|
155
|
+
# Do not write to yupeit@andrew.cmu.edu, iCloud, or another Google account unless explicitly requested.
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
## Apple Calendar Audit
|
|
159
|
+
|
|
160
|
+
```bash
|
|
161
|
+
cd /Users/yupeit/dev/skills/skills/apple-calendar-event
|
|
162
|
+
python3 scripts/calendar_audit.py
|
|
163
|
+
python3 scripts/calendar_audit.py --json
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
Use this to detect:
|
|
167
|
+
|
|
168
|
+
- duplicate display names
|
|
169
|
+
- Calendar.app default policy and default UUID
|
|
170
|
+
- store/account ownership such as Google, iCloud, school CalDAV, subscribed calendars, Reminders, and system sources
|
|
171
|
+
|
|
172
|
+
Do not edit `Calendar.sqlitedb`.
|
|
173
|
+
|
|
174
|
+
## School Migration Planning
|
|
175
|
+
|
|
176
|
+
Before copying school events into the primary Google calendar:
|
|
177
|
+
|
|
178
|
+
1. Inventory future events from the school calendar with `events.list`.
|
|
179
|
+
2. Compare against existing primary events by `iCalUID`, title, start time, and location.
|
|
180
|
+
3. Keep the school calendar visible but unselected while migration is in progress.
|
|
181
|
+
4. Prefer copying/importing only future actionable events.
|
|
182
|
+
5. Do not delete the school source until the user confirms the migrated state.
|
|
183
|
+
|
|
184
|
+
Example inventory:
|
|
185
|
+
|
|
186
|
+
```bash
|
|
187
|
+
gws calendar events list \
|
|
188
|
+
--params '{"calendarId":"yupeit@andrew.cmu.edu","timeMin":"YYYY-MM-DDT00:00:00-07:00","singleEvents":true,"showDeleted":false,"maxResults":2500,"orderBy":"startTime"}' \
|
|
189
|
+
> "$backup_dir/school-source-events.json"
|
|
190
|
+
|
|
191
|
+
gws calendar events list \
|
|
192
|
+
--params '{"calendarId":"tianyupeiandy@gmail.com","timeMin":"YYYY-MM-DDT00:00:00-07:00","singleEvents":true,"showDeleted":false,"maxResults":2500,"orderBy":"startTime"}' \
|
|
193
|
+
> "$backup_dir/primary-target-events.json"
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
Preview source events that do not appear in the target by `iCalUID` or normalized `summary + start + location`:
|
|
197
|
+
|
|
198
|
+
```bash
|
|
199
|
+
python3 /Users/yupeit/dev/skills/skills/calendar/scripts/event_dedupe_preview.py \
|
|
200
|
+
--source "$backup_dir/school-source-events.json" \
|
|
201
|
+
--target "$backup_dir/primary-target-events.json" \
|
|
202
|
+
--format tsv
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
Treat the preview as advisory. Review ambiguous events before copying.
|
|
206
|
+
|
|
207
|
+
Before copying events, decide which fields to preserve and which to drop:
|
|
208
|
+
|
|
209
|
+
- Usually preserve: summary, start, end, location, description, and intentional reminders.
|
|
210
|
+
- Review carefully: recurrence rules, conferenceData/Meet links, attendees, organizer, attachments, visibility/transparency, and private fields.
|
|
211
|
+
- Avoid blindly recreating attendees or conference links unless the user wants invitations or meeting changes to propagate.
|
|
212
|
+
|
|
213
|
+
## Commands To Avoid Unless Explicitly Requested
|
|
214
|
+
|
|
215
|
+
```bash
|
|
216
|
+
# Avoid defaulting to Apple Calendar for personal event creation.
|
|
217
|
+
# Avoid using "primary" where multiple Google accounts may be authenticated or visible.
|
|
218
|
+
# Avoid deleting calendars, unsubscribing, or bulk-deleting events during cleanup planning.
|
|
219
|
+
# Avoid direct writes to ~/Library/Group Containers/group.com.apple.calendar/Calendar.sqlitedb.
|
|
220
|
+
# Avoid real summary changes for shared/read-only/school/system calendars without owner confirmation.
|
|
221
|
+
# Avoid copying attendees/conferenceData during migration unless the user wants invitations or meeting updates.
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
## Scope Expectations
|
|
225
|
+
|
|
226
|
+
Use the narrowest calendar authorization that satisfies the operation:
|
|
227
|
+
|
|
228
|
+
- Read/audit: Calendar read access is enough.
|
|
229
|
+
- CalendarList `selected`, `hidden`, `summaryOverride`: CalendarList write access.
|
|
230
|
+
- Real `summary`: calendar metadata write access.
|
|
231
|
+
- Event creation/migration: event write access.
|
|
232
|
+
|
|
233
|
+
When scope is insufficient, prefer:
|
|
234
|
+
|
|
235
|
+
```bash
|
|
236
|
+
gws auth login --services calendar
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
## Rollback Pattern
|
|
240
|
+
|
|
241
|
+
Every mutation report should include exact rollback commands. For real `summary` edits:
|
|
242
|
+
|
|
243
|
+
```bash
|
|
244
|
+
gws calendar calendars patch \
|
|
245
|
+
--params '{"calendarId":"CALENDAR_ID"}' \
|
|
246
|
+
--json '{"summary":"PREVIOUS_SUMMARY"}'
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
For visibility edits:
|
|
250
|
+
|
|
251
|
+
```bash
|
|
252
|
+
gws calendar calendarList patch \
|
|
253
|
+
--params '{"calendarId":"CALENDAR_ID"}' \
|
|
254
|
+
--json '{"selected":PREVIOUS_SELECTED,"hidden":PREVIOUS_HIDDEN}'
|
|
255
|
+
```
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
|
|
3
|
+
import argparse
|
|
4
|
+
import json
|
|
5
|
+
import sys
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
from typing import Any
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
PRIMARY_ACCOUNT = "tianyupeiandy@gmail.com"
|
|
11
|
+
SCHOOL_ACCOUNT = "yupeit@andrew.cmu.edu"
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def load_items(path: Path) -> list[dict[str, Any]]:
|
|
15
|
+
with path.open(encoding="utf-8") as handle:
|
|
16
|
+
payload = json.load(handle)
|
|
17
|
+
if isinstance(payload, dict):
|
|
18
|
+
items = payload.get("items", [])
|
|
19
|
+
elif isinstance(payload, list):
|
|
20
|
+
items = payload
|
|
21
|
+
else:
|
|
22
|
+
raise SystemExit(f"Unsupported JSON shape in {path}")
|
|
23
|
+
if not isinstance(items, list):
|
|
24
|
+
raise SystemExit(f"Expected items array in {path}")
|
|
25
|
+
return [item for item in items if isinstance(item, dict)]
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def text_blob(item: dict[str, Any]) -> str:
|
|
29
|
+
values = [
|
|
30
|
+
item.get("id"),
|
|
31
|
+
item.get("summary"),
|
|
32
|
+
item.get("summaryOverride"),
|
|
33
|
+
item.get("description"),
|
|
34
|
+
]
|
|
35
|
+
return " ".join(str(value or "") for value in values).casefold()
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def classify(item: dict[str, Any]) -> dict[str, Any]:
|
|
39
|
+
calendar_id = str(item.get("id") or "")
|
|
40
|
+
summary = str(item.get("summary") or "")
|
|
41
|
+
role = str(item.get("accessRole") or "")
|
|
42
|
+
blob = text_blob(item)
|
|
43
|
+
primary = bool(item.get("primary")) or calendar_id == PRIMARY_ACCOUNT
|
|
44
|
+
selected = bool(item.get("selected"))
|
|
45
|
+
hidden = bool(item.get("hidden"))
|
|
46
|
+
|
|
47
|
+
if primary:
|
|
48
|
+
bucket = "00 Core - Google 主号"
|
|
49
|
+
action = "keep selected; use as default write target"
|
|
50
|
+
reason = "primary Google source of truth"
|
|
51
|
+
elif is_school_calendar(blob, summary):
|
|
52
|
+
bucket = "30 School Transition - review"
|
|
53
|
+
action = "inventory future events; migrate reviewed items; then hide"
|
|
54
|
+
reason = "school lifecycle risk"
|
|
55
|
+
elif is_reference_calendar(blob, summary):
|
|
56
|
+
bucket = "90 Reference - review"
|
|
57
|
+
action = "keep or hide; do not rename/delete as owned calendar"
|
|
58
|
+
reason = "system, holiday, task, birthday, or subscribed reference source"
|
|
59
|
+
elif role in {"reader", "freeBusyReader"}:
|
|
60
|
+
bucket = "20 Shared - review"
|
|
61
|
+
action = "keep visible only if useful; do not rename as owner"
|
|
62
|
+
reason = f"current account has {role} access"
|
|
63
|
+
elif hidden:
|
|
64
|
+
bucket = "80 Archive - review"
|
|
65
|
+
action = "keep hidden unless needed"
|
|
66
|
+
reason = "already hidden from CalendarList"
|
|
67
|
+
elif role in {"owner", "writer"}:
|
|
68
|
+
bucket = "10 Projects - review"
|
|
69
|
+
action = "confirm owner/lifecycle; label, hide, or keep"
|
|
70
|
+
reason = f"current account can modify calendar ({role})"
|
|
71
|
+
else:
|
|
72
|
+
bucket = "Review"
|
|
73
|
+
action = "inspect metadata before changing"
|
|
74
|
+
reason = "ownership and lifecycle are unclear"
|
|
75
|
+
|
|
76
|
+
return {
|
|
77
|
+
"bucket": bucket,
|
|
78
|
+
"action": action,
|
|
79
|
+
"reason": reason,
|
|
80
|
+
"id": calendar_id,
|
|
81
|
+
"summary": summary,
|
|
82
|
+
"summaryOverride": item.get("summaryOverride") or "",
|
|
83
|
+
"primary": primary,
|
|
84
|
+
"accessRole": role,
|
|
85
|
+
"selected": selected,
|
|
86
|
+
"hidden": hidden,
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def is_reference_calendar(blob: str, summary: str) -> bool:
|
|
91
|
+
known_summary = summary.casefold() in {
|
|
92
|
+
"birthdays",
|
|
93
|
+
"tasks",
|
|
94
|
+
"reminders",
|
|
95
|
+
"siri suggestions",
|
|
96
|
+
}
|
|
97
|
+
return (
|
|
98
|
+
known_summary
|
|
99
|
+
or "holiday" in blob
|
|
100
|
+
or "#holiday@group.v.calendar.google.com" in blob
|
|
101
|
+
or "birthday" in blob
|
|
102
|
+
or "contacts" in blob
|
|
103
|
+
or "task" in blob
|
|
104
|
+
or "subscribed" in blob
|
|
105
|
+
)
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
def is_school_calendar(blob: str, summary: str) -> bool:
|
|
109
|
+
summary_tokens = {
|
|
110
|
+
token.strip(" -_:/()[]{}").casefold()
|
|
111
|
+
for token in summary.replace("|", " ").split()
|
|
112
|
+
}
|
|
113
|
+
return (
|
|
114
|
+
SCHOOL_ACCOUNT in blob
|
|
115
|
+
or "andrew.cmu.edu" in blob
|
|
116
|
+
or "carnegie mellon" in blob
|
|
117
|
+
or "cmu" in summary_tokens
|
|
118
|
+
)
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
def print_tsv(rows: list[dict[str, Any]]) -> None:
|
|
122
|
+
fields = [
|
|
123
|
+
"bucket",
|
|
124
|
+
"action",
|
|
125
|
+
"summary",
|
|
126
|
+
"summaryOverride",
|
|
127
|
+
"id",
|
|
128
|
+
"primary",
|
|
129
|
+
"accessRole",
|
|
130
|
+
"selected",
|
|
131
|
+
"hidden",
|
|
132
|
+
"reason",
|
|
133
|
+
]
|
|
134
|
+
print("\t".join(fields))
|
|
135
|
+
for row in rows:
|
|
136
|
+
print("\t".join(str(row.get(field, "")) for field in fields))
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
def main() -> int:
|
|
140
|
+
parser = argparse.ArgumentParser(
|
|
141
|
+
description="Create an advisory governance review table from Google Calendar calendarList JSON."
|
|
142
|
+
)
|
|
143
|
+
parser.add_argument("--calendar-list", required=True, help="JSON from gws calendar calendarList list.")
|
|
144
|
+
parser.add_argument("--format", choices=["json", "tsv"], default="tsv")
|
|
145
|
+
args = parser.parse_args()
|
|
146
|
+
|
|
147
|
+
rows = [classify(item) for item in load_items(Path(args.calendar_list))]
|
|
148
|
+
if args.format == "json":
|
|
149
|
+
json.dump(rows, sys.stdout, ensure_ascii=False, indent=2)
|
|
150
|
+
print()
|
|
151
|
+
else:
|
|
152
|
+
print_tsv(rows)
|
|
153
|
+
return 0
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
if __name__ == "__main__":
|
|
157
|
+
sys.exit(main())
|