Collect the user's work activity for a given day (or date range) from all available data sources (Slack, Jira, Confluence, GitHub, Google Drive). Every item must include a source link. Output is written to <workspace>/collected/.
-
Pre-flight checks: Verify the environment before collecting:
- Slack MCP: Check the Slack tools are available. They load under an MCP-server prefix (e.g.
mcp__<server>__slack_search_public_and_private) and may be deferred — registered by name but schema-unloaded until fetched. Three states, not two: (a) the tools are available → proceed; (b) only an authenticate/complete_authentication tool is present → the server is a claude.ai connector awaiting interactive sign-in, so ask the user to run /mcp, select the Slack connector, and authenticate, then continue — this is recoverable, do not fail; (c) genuinely absent → fail (see below).
- Atlassian MCP: Check the Atlassian (Jira/Confluence) tools are available. They load under an MCP-server prefix (e.g.
mcp__<server>__getJiraIssue) and may be deferred — registered by name but schema-unloaded until fetched. Three states, not two: (a) the tools are available → proceed; (b) only an authenticate/complete_authentication tool is present → the server is a claude.ai connector awaiting interactive sign-in, so ask the user to run /mcp, select the Atlassian connector, and authenticate, then continue — this is recoverable, do not fail; (c) genuinely absent → fail (see below).
gh CLI: Check if gh is available (which gh). Expected but not blocking. If missing, log a warning via /log (status=WARNING detail=gh CLI not available — GitHub data will be missing. Install with: brew install gh && gh auth login), mark as a gap, and continue.
If Slack or Atlassian MCP is missing, stop and write a failure status:
# My Activity: YYYY-MM-DD
## Status: FAILED
**Reason**: [which MCP servers were unavailable]
Also invoke /log to record the failure (see Logging section below).
-
Resolve identity — MCP first, identity.md as cache, user input as last fallback:
General principle: always probe the connected MCPs for any platform-derived value before reading identity.md, and only ask the user when both fail. identity.md is a cache, not a gate.
- Slack user ID →
slack_read_user_profile
- Atlassian account ID →
atlassianUserInfo
- Jira cloud ID →
getAccessibleAtlassianResources (returns the cloudId for accessible Atlassian sites)
- Google Workspace email — resolve from the most reliable source available:
atlassianUserInfo.email (primary — works for any engineering shop with Jira/Confluence access).
slack_read_user_profile.profile.email (fallback if Atlassian is unavailable).
mcp__claude_ai_Google_Drive__list_recent_files → owners[0].emailAddress (last automated fallback; only works if the user owns at least one file).
- Ask the user.
- GitHub username —
gh supports multiple authenticated accounts. The skill targets the cached business handle without modifying the active session (see step 4 GitHub section for the script-file token-fetch pattern). Resolve as follows:
- If cached in
## Profile, use it. Verify it's still authenticated via gh auth status (parse for Logged in to github.com account <handle>). If the cached handle isn't in gh auth status, fail loud (FAILED) with re-auth instructions: gh auth login --hostname github.com.
- If not cached, parse
gh auth status for Logged in to github.com account <handle> lines:
- One account → resolve via
gh api user --jq .login, write back to ## Profile.
- Multiple accounts → fail loud (FAILED): "Multiple
gh accounts detected. Set GitHub username manually in <workspace>/me/identity.md ## Profile before re-running. Agents cannot disambiguate which is the business account."
Write-back rule: when MCP resolves a field that is missing from identity.md's ## Profile section, append it there. Fill missing only — never overwrite an existing value. A stale MCP handle could otherwise clobber the user's curated identity. If a resolved value differs from what's already there, log a WARNING, skip the write, and surface the discrepancy at the end of the run.
If identity.md doesn't yet have a ## Profile section, create it using the template at the end of this skill and append after any existing top-of-file content. Never modify other sections (Preferences, Writing Style, Growth areas, etc.) — they are user-curated.
-
Determine date range and timezone: Use the argument if provided, otherwise default to today.
Timezone model:
- Interpret the requested date(s) in the user's local timezone, read from
identity.md's ## Profile section, field Timezone (IANA name, e.g., Asia/Dubai). Fall back to the system timezone if missing.
- Output filenames use the user's local date — matches how the user thinks about the day.
Date conversion — the local-day window is [date 00:00:00, next_day 00:00:00) in the user's IANA TZ. Compute:
tz_offset from the IANA name (e.g., Asia/Dubai → +04:00, Europe/London → +01:00 BST or +00:00 GMT depending on date).
local_start = {date}T00:00:00{tz_offset} and local_end = {next_day}T00:00:00{tz_offset}.
utc_start = local_start.astimezone(UTC) and utc_end = local_end.astimezone(UTC).
Worked example for date=2026-04-30, tz=Asia/Dubai:
local_start = 2026-04-30T00:00:00+04:00 → utc_start = 2026-04-29T20:00:00Z
local_end = 2026-05-01T00:00:00+04:00 → utc_end = 2026-04-30T20:00:00Z
Per-source query parameters:
- Slack —
after = utc_start, before = utc_end (Unix timestamps). Or use from:<@id> on:{date} query modifier; Slack interprets on: in the workspace TZ.
gh search — pass full ISO timestamps with the offset, not bare dates: --created "{local_start}..{local_end}". Bare YYYY-MM-DD..YYYY-MM-DD interprets dates in UTC and shifts the window by the TZ offset, missing/misattributing PRs at day boundaries.
- Jira / Confluence — pass
{date} and {next_day} as YYYY-MM-DD strings with the half-open >= / < boundary (see Jira section). Caveat: Jira and Confluence evaluate JQL/CQL date literals in the user's profile timezone, not UTC — if the Jira profile TZ differs from the user's local TZ, results drift by hours. Recommend aligning the Jira profile TZ to the user's local TZ.
- Drive —
viewedByMeTime and modifiedTime are RFC 3339 UTC; use utc_start / utc_end.
Multi-day ranges: Loop internally over dates, executing steps 4–7 once per date. Produce one output file per date. One day's MCP responses fit in a single context window; multiple days may not.
Status semantics — apply consistently across the file's Status: header, the /log call, and any returned summary string:
- SUCCESS — every required source responded successfully, regardless of result count. Zero items is still SUCCESS. "Quiet day", "day in progress", "low coverage", "PTO", "weekend" are all SUCCESS with zero items.
- PARTIAL — at least one required source failed mid-collection (timeout, error, permission denied during pagination) after others had already returned data. Capture what's available, list the failed source under
## Gaps with the error reason, log status=WARNING. Do not abort.
- FAILED — a required source (Slack MCP, Atlassian MCP) was unavailable from the start, or the run couldn't be processed at all. Output file is written with empty
## Activity and the failure reason under ## Gaps. /log status is FAILED.
Do not use PARTIAL to signal "the day isn't over yet" or "I expected more data". Coverage-time concerns belong in the ## Summary prose, not in the Status field.
-
Collect from sources in parallel:
Pagination — required for every source. Loop until exhausted; never trust the first page as the full result.
| Source | Mechanism |
|---|
| Slack | cursor — loop until no next cursor |
| Jira | startAt + maxResults — loop until isLast: true or returned count < maxResults |
| Confluence | Same as Jira via the Atlassian MCP |
GitHub (gh) | --limit 100 per query is effectively always enough for a single day; warn-if-hit rather than paginate further |
| Drive | pageToken — loop until no next page |
Stop-and-flag rule: this skill always operates at single-day, single-user granularity (multi-day args loop one day at a time). At that scope, if pagination hits 5+ pages on any single source — stop and flag. 500 events from one source on one day for one person is anomalous — almost always a wrong date boundary or a missing identity filter. Don't carry the assumption beyond this scope.
On partial failure: see Status semantics in step 3 — set Status=PARTIAL, /log status=WARNING, list the failed source under ## Gaps with its error reason, capture what's available, do not abort.
Slack:
- Search for messages from the user in the date range using their Slack user ID
- Use
detailed response format (never concise — it drops timestamps and permalinks). Set include_context: false to keep response size manageable; use slack_read_thread selectively for context.
- Capture: channel, timestamp, message summary, permalink
- Check threads where the user participated — decisions, guidance, approvals
- Fold low-signal items (reactions, acks) into the related activity they refer to
- Paginate using the
cursor parameter when results hit the page limit
- DMs are not accessible via MCP — ask the user about notable DMs at the end
Jira:
- Search for issues touched by the user in the date range using JQL. Two queries (no comment-specific query —
issueFunction in commented(...) is not portable across Jira instances; assignee/reporter/creator/updated paths cover the common cases).
- Updated:
(assignee = currentUser() OR reporter = currentUser()) AND updated >= "YYYY-MM-DD" AND updated < "next_day" — captures issues the user owns or reports that moved during the day.
- Created:
creator = "<accountId>" AND created >= "YYYY-MM-DD" AND created < "next_day" — captures new issues the user filed.
- Date boundary: use
>= "YYYY-MM-DD" AND < "next_day" (not <= — Jira treats <= "04-10" as "before start of 04-10").
- Timezone caveat: JQL date literals evaluate in the Jira user's profile timezone, not UTC. Confirm the Jira profile TZ matches the user's local TZ or expect drift.
- Request minimal fields:
summary, status, issuetype, priority, updated, assignee, reporter.
- Capture: key, summary, status, assignee, reporter, what changed, issue URL.
- Reporter-only (you filed it, not assigned): capture key, title, assignee, status only. Label as "Filed; assigned to {assignee-name}." Do not describe as your own delivery work.
- Assignee (you own it): full detail — what moved, what's next, blockers if any.
- Both (reporter and assignee): treat as assignee for detail.
GitHub:
-
Use a script file Claude writes + executes; never gh auth switch. Top-level Bash with $(...) substitution is blocked by the sandbox guard, but $(...) inside a script file that Claude then runs via bash <script> is not — the guard inspects top-level commands, not script contents. Fetch the cached handle's token in-process: export GH_TOKEN="$(gh auth token --user <cached-handle>)". Token never enters tool output or transcripts; no global gh state mutation; no crash window.
-
Pre-flight (read-only): gh auth status to confirm the cached handle is logged in. If not, fail with a ## Gaps note suggesting gh auth login --hostname github.com. Never gh auth switch.
-
Date range syntax — pass full ISO timestamps with the user's TZ offset, not bare YYYY-MM-DD. Bare dates are interpreted as UTC, so non-UTC user TZs slip by the offset (Asia/Dubai --created "2026-04-30..2026-04-30" covers local 04-30 04:00 → 05-01 04:00, missing/misattributing PRs at day boundaries).
-
Four queries to run inside the script (after the GH_TOKEN export), with LOCAL_START and LOCAL_END set to the day's ISO timestamps:
gh search prs --author "@me" --created "${LOCAL_START}..${LOCAL_END}" --limit 100 --json number,title,url,repository,state,createdAt,updatedAt
gh search prs --reviewed-by "@me" --updated "${LOCAL_START}..${LOCAL_END}" --limit 100 --json number,title,url,repository,state
gh search issues --author "@me" --created "${LOCAL_START}..${LOCAL_END}" --limit 100 --json number,title,url,repository,state
gh search issues --commenter "@me" --updated "${LOCAL_START}..${LOCAL_END}" --limit 100 --json number,title,url,repository
--limit 100 defends against gh search's default --limit 30 silently truncating — the JSON output has no has_more flag, so a truncated result is indistinguishable from a complete one.
-
Range syntax A..B is inclusive on both ends — different from Jira's half-open convention above. Using next_day T00:00:00 as the upper bound therefore includes events at exactly midnight local; this is a 1-second overlap with the next day's window, acceptable for daily granularity.
-
With GH_TOKEN set to the cached handle's token, @me resolves to the business user regardless of the active gh handle.
-
For each result: capture the repo, number, title, and URL.
-
If gh is not available, note this gap and continue. If the token fetch errors (revoked grant, deleted handle), the script exits non-zero — surface the gh error in ## Gaps with the same re-auth hint as pre-flight failure.
Confluence:
- Uses the same Atlassian MCP as Jira — no separate pre-flight check needed
searchConfluenceUsingCql: contributor = "{accountId}" AND lastmodified >= "YYYY-MM-DD" AND lastmodified < "next_day" (same date boundary logic as Jira)
- The
contributor field already covers comments — a comment is a Confluence contribution. No separate comment query needed.
- For each result: capture the space, title, type of contribution (created vs. updated, page vs. comment), and the URL
Google Drive:
-
The MCP's query language only supports title, fullText, mimeType, modifiedTime, viewedByMeTime as query terms. owners is not a supported field — neither 'email' in owners nor 'me' in owners works (both return "Unsupported query field"). Use a two-pronged approach instead:
-
Owned + edited by user — call mcp__claude_ai_Google_Drive__list_recent_files with orderBy: lastModifiedByMe, paginate on nextPageToken, post-filter results in code by the local-day window (createdTime/modifiedTime falling in [YYYY-MM-DDT00:00:00Z, NEXT_DAYT00:00:00Z)). This catches files the user authored or modified.
-
Touched (viewed/co-edited) by user — call mcp__claude_ai_Google_Drive__search_files with:
query: "viewedByMeTime >= 'YYYY-MM-DDT00:00:00Z'
and viewedByMeTime < 'NEXT_DAYT00:00:00Z'"
pageSize: 100
Paginate on pageToken. Use UTC bounds. Single quotes around values; escape inner singles as \'.
Deduplicate the union by id.
-
For each result: capture title, MIME type, action (created vs. modified, inferred from createdTime vs. modifiedTime), and the file URL.
-
Note on authorship inference: the search response doesn't reliably name the writer for non-owned files. For items only surfaced via query (2), the user touched the file but may not have edited it. Flag uncertain items in ## Gaps (e.g., "Drive items {ids} surfaced via viewedByMeTime — authorship not confirmed") rather than asserting they edited.
Oversized responses (50KB+): Write a targeted extraction script on the fly based on the actual response structure. Don't use pre-baked scripts — MCP schemas change.
-
Deduplicate and cross-reference: A single activity may appear in multiple sources (e.g., a Jira ticket discussed in Slack and linked in a PR). Group related items into a single entry with multiple source links rather than listing them separately.
-
Categorize each item into one or more of (categories are role-neutral — an IC, PM, EM, or director will naturally use different subsets):
engineering — Writing code, debugging, code reviews, technical problem-solving
architecture — Design decisions, technical direction, system design, RFCs, design docs
team — Mentoring, feedback, hiring, 1:1s, process improvements, onboarding
infrastructure — Infra, cost optimization, security, DevOps, incident response
delivery — Sprint work, project tracking, unblocking dependencies, release management
product — Requirements, user research, feature specs, stakeholder alignment, roadmap
operational — Process improvements, monitoring, on-call, documentation, cross-team coordination
strategic — Executive comms, OKRs, cross-org initiatives, vendor relationships, budget
ai-engineering — AI tool adoption, AI-powered workflows, AI strategy
growth — Learning, conferences, training, certifications, knowledge sharing
-
Capture a timestamp per item: For each activity item, record the earliest underlying event's timestamp as ISO 8601 with timezone offset (e.g., 2026-04-12T14:32:00+04:00). Use the user's timezone from <workspace>/me/identity.md. When an item spans multiple events (e.g., Slack thread + Jira update + PR), use the earliest. This enables downstream synthesis of time-based patterns (late-hour work, reactive windows, deferral over time).
-
Write the output file: Write to <workspace>/collected/collect-my-activity/YYYY-MM-DD-activity.md (using the user's local date). Create parent directories if missing.
Re-run on the same date: overwrite the file. Add a header line **Re-collection**: previous run superseded YYYY-MM-DD HH:MM:SS (UTC) below the title so the user can see this is a re-collection, not a fresh first run.
Gaps discipline: ## Gaps lists inaccessible sources only — sources that failed at runtime or were unavailable. A source returning zero results is not a gap; it's a successful query reflected in the absence of items under ## Activity. DMs are universally inaccessible via MCP — the user is asked about them at the end (step 9), they are not listed in ## Gaps. Typical valid Gap entries: gh CLI not available, Google Drive MCP not available, Confluence: timed out on page 2 — partial coverage, Drive items {ids} surfaced via viewedByMeTime — authorship not confirmed. Most files should have an empty Gaps section.
# My Activity: YYYY-MM-DD
## Status: SUCCESS
**Sources checked**: Slack, Jira, Confluence, GitHub (gh CLI / N/A), Google Drive (MCP / N/A)
## Summary
[1-2 sentences: what was the focus today. If a quiet day or PTO, say so.]
## Activity
### [category]
- **[brief description framed by impact, not just action]**
- Time: 2026-04-12T14:32:00+04:00
- Sources: [Slack permalink], [JIRA-123](url), [PR #45](url)
- Context: [why this matters — what decision was made, what was unblocked, what risk was mitigated]
### [category]
- ...
## Gaps
[Only pair-specific runtime issues. Empty if no source was inaccessible.]
-
Report to user:
Activity collected: YYYY-MM-DD
───────────────────────────────
Items found: [count]
Sources: Slack ([count]), Jira ([count]), Confluence ([count]), GitHub ([count or N/A]), Drive ([count or N/A])
Gaps: [list any inaccessible sources]
File: <workspace>/collected/collect-my-activity/YYYY-MM-DD-activity.md
Anything missing? Notable DMs, meetings, or whiteboard sessions to add?