Use when collecting work achievements for performance reviews, half-year goal evaluations, or self-assessment. Triggers on "成果収集", "実績まとめ", "achievement", "振り返り", "半期目標", "evaluation", "collect achievements".
Collect and organize work achievements from Slack, GitHub, JIRA, and Notion over a configurable period, then optionally match them against Kaonavi half-year goals.
Compute exact SINCE_DATE and UNTIL_DATE at runtime based on today's date.
digraph achievement_flow {
rankdir=TB;
start [label="Start" shape=doublecircle];
notion_user [label="0. Resolve Notion user_id\n(main agent)" shape=box];
slack_search [label="1a. Slack Search\n(main agent, needs user consent)" shape=box];
parallel [label="1b+1c+1d. GitHub + JIRA + Notion\n(parallel haiku subagents)" shape=box];
slack_detail [label="1a-2. Slack Detail\n(haiku subagent)" shape=box];
step2 [label="STEP2: Aggregate\n(cross-reference & deduplicate)" shape=box];
ask [label="Ask: Match against\nhalf-year goals?" shape=diamond];
step3 [label="STEP3: Goal Matching\n(user pastes Kaonavi goals)" shape=box];
done [label="Output Report" shape=doublecircle];
start -> notion_user;
notion_user -> slack_search;
notion_user -> parallel;
slack_search -> slack_detail;
slack_detail -> step2;
parallel -> step2;
step2 -> ask;
ask -> step3 [label="yes"];
ask -> done [label="no"];
step3 -> done;
}
Before starting collection, resolve the user's Notion user_id. This is needed for the Notion collection step.
Run in the main agent:
notion-search with query: "okamoto yosuke", query_type: "user"
Find the matching user entry (email: [email protected]) and extract the user ID.
Known ID: ad0f1d7f-86bd-4f76-b250-436c109f4476 (okamotoyosuke)
If the user has changed, re-resolve. Cache the ID for the session.
IMPORTANT: Slack MCP search tools require user consent (marked as "user action"). They CANNOT run inside subagents because the consent prompt won't reach the user. Therefore, Slack collection is split into two phases.
Phase 1: Search (Main Agent)
The main agent runs Slack searches directly (so user can approve):
Search messages FROM the user:
slack_search_public_and_private with query: from:@me after:{SINCE_DATE} before:{UNTIL_DATE}
Search messages mentioning the user:
slack_search_public_and_private with query: to:me after:{SINCE_DATE} before:{UNTIL_DATE}
Save the raw search results (channel, timestamp, snippet, permalink for each message).
Phase 2: Detail Reading (Haiku Subagent)
Pass the search results to a haiku subagent for thread reading and summarization. Thread/channel reading also requires user consent, so if the subagent fails on slack_read_thread/slack_read_channel, fall back to using the search result snippets as-is.
Subagent prompt template:
You are a helper that organizes Slack search results into a structured report.
## Input Data
Here are the raw Slack search results for the period {SINCE_DATE} to {UNTIL_DATE}:
### Messages from the user:
{RAW_FROM_ME_RESULTS}
### Messages mentioning the user:
{RAW_MENTION_RESULTS}
## Task
1. For each message result:
- Record: channel name, date, message snippet
- Try to use slack_read_thread to get thread context and write a 1-2 sentence summary
- If slack_read_thread fails (permission error), use the snippet as-is
- If the message is NOT in a thread, try slack_read_channel to read ~5 surrounding messages for context. If that also fails, use the snippet as-is.
2. Group messages by topic/channel where possible.
3. Deduplicate: if the same thread appears in both "from me" and "mentions", merge them.
## Output Format
Return EXACTLY this YAML structure:
```yaml
slack_activity:
from_me:
- channel: "#channel-name"
date: "YYYY-MM-DD"
summary: "Brief description of what was discussed"
is_thread: true/false
thread_summary: "Thread context summary (if available)"
permalink: "slack message link if available"
mentions:
- channel: "#channel-name"
date: "YYYY-MM-DD"
from: "person name"
summary: "What they said/asked regarding me"
is_thread: true/false
thread_summary: "Thread context summary (if available)"
permalink: "slack message link if available"
**Fallback:** If Slack search also fails in the main agent (e.g., workspace restrictions), ask the user to specify key channels and use `slack_read_channel` with date range to read them directly.
### 1b. GitHub Subagent (Parallel with JIRA + Slack Detail)
Launch GitHub and JIRA subagents in parallel using `model="haiku"`. Also launch the Slack detail subagent in parallel if Slack search results are ready.
**Prompt template:**
You are a helper that collects GitHub activity for a user in the moneyforward org.
Collect PRs and comments from {SINCE_DATE} to {UNTIL_DATE}.
Search PRs created by the user: gh search prs --author @me --created "{SINCE_DATE}..{UNTIL_DATE}" --owner moneyforward --limit 50 --json title,state,closedAt,mergedAt,url,number,repository,body
Search PRs where the user commented: gh search prs --commenter @me --updated "{SINCE_DATE}..{UNTIL_DATE}" --owner moneyforward --limit 50 --json title,state,url,number,repository
Search PRs where the user was requested as reviewer: gh search prs --review-requested @me --updated "{SINCE_DATE}..{UNTIL_DATE}" --owner moneyforward --limit 30 --json title,state,url,number,repository
For each authored PR:
For commented/reviewed PRs (exclude self-authored duplicates):
Return EXACTLY this YAML structure:
github_activity:
authored_prs:
- repo: "moneyforward/repo-name"
pr_number: 123
title: "PR title"
url: "https://github.com/..."
state: "merged/open/closed"
merged_at: "YYYY-MM-DD or null"
summary: "1-line summary from PR body"
reviewed_prs:
- repo: "moneyforward/repo-name"
pr_number: 456
title: "PR title"
url: "https://github.com/..."
role: "reviewer/commenter"
commented_prs:
- repo: "moneyforward/repo-name"
pr_number: 789
title: "PR title"
url: "https://github.com/..."
### 1c. JIRA Subagent
**IMPORTANT:** Use the acli-jira plugin wrapper script for all JIRA operations. Invoke `Skill("acli-jira:operating-jira")` first to get the script path and usage patterns.
**Prompt template:**
You are a helper that collects JIRA tickets for a user.
First, invoke the skill: Skill("acli-jira:operating-jira") to learn how to use the JIRA CLI wrapper script.
The wrapper script is located at: /Users/okamoto.yosuke/.claude/plugins/cache/mf-claude-plugins/acli-jira/d7ccf9b6b918/skills/operating-jira/scripts/jira-acli.sh
Use this script (referred to as JIRA_CLI below) for all JIRA operations.
Search for tickets assigned to the current user in projects NTP and API.
Search DONE tickets in the period: Run: JIRA_CLI search "project in (NTP, API) AND assignee = currentUser() AND statusCategory = Done AND updated >= '{SINCE_DATE}' ORDER BY updated DESC"
Search in-progress or recently updated tickets (for context): Run: JIRA_CLI search "project in (NTP, API) AND assignee = currentUser() AND statusCategory != Done AND updated >= '{SINCE_DATE}' ORDER BY updated DESC"
For each ticket that needs more detail, view it: Run: JIRA_CLI view NTP-123
For each ticket, extract:
Note: NTP may use custom status names with emoji prefixes (e.g., "✅Done"). Use statusCategory for filtering. If statusCategory doesn't work, fall back to listing all statuses.
Return EXACTLY this YAML structure:
jira_activity:
done_tickets:
- key: "NTP-123"
summary: "Ticket summary"
status: "Done"
story_points: 5
epic: "NTP-100 or null"
resolved_date: "YYYY-MM-DD"
in_progress_tickets:
- key: "API-456"
summary: "Ticket summary"
status: "In Progress"
story_points: 3
epic: "API-200 or null"
### 1d. Notion Collection (Main Agent — two-phase search)
**IMPORTANT:** Notion MCP tools (like Slack MCP) may require user consent and CANNOT reliably run inside subagents. Run ALL Notion searches in the main agent. This step should be executed in parallel with the Slack search (1a), while GitHub (1b) and JIRA (1c) subagents run concurrently.
Notion collection uses a **two-phase approach** to accurately identify pages created by the user.
**Phase 1: AI Search (broad, semantic)**
Run multiple AI Search queries covering the user's work domains. AI Search returns semantically related results from Notion AND connected sources (Slack, GitHub, etc.), but `created_by_user_ids` filter is NOT reliable in this mode — unrelated pages may be included.
notion-search with: query: "<domain-specific keywords from user's GIs/work areas>" query_type: "internal" filters: { created_by_user_ids: ["{NOTION_USER_ID}"], created_date_range: { start_date: "{SINCE_DATE}", end_date: "{UNTIL_DATE}" } } page_size: 25 max_highlight_length: 150
Run 3-5 queries with different keyword sets to maximize coverage. Collect all unique Notion page IDs and titles from the results (ignore Slack/GitHub results — those are covered by other steps).
**Phase 2: workspace_search (strict, keyword-based)**
For each keyword set, re-run with `content_search_mode: "workspace_search"`. This mode enforces `created_by_user_ids` strictly and only returns pages actually created by the user.
notion-search with: query: "<same keywords>" query_type: "internal" content_search_mode: "workspace_search" filters: { created_by_user_ids: ["{NOTION_USER_ID}"], created_date_range: { start_date: "{SINCE_DATE}", end_date: "{UNTIL_DATE}" } } page_size: 25 max_highlight_length: 100
**Cross-reference:** Only pages that appear in the workspace_search results are confirmed as user-authored. Pages that appeared only in AI Search but not in workspace_search should be discarded (they are likely authored by others but semantically related).
**Output format:**
```yaml
notion_activity:
authored_pages:
- title: "Page title"
url: "https://www.notion.so/..."
date: "YYYY-MM-DD"
highlight: "Brief description from search highlight"
Known limitations:
workspace_search is keyword-based, not semantic. Pages with unusual titles may be missed. Use multiple keyword variations.notion-get-comments currently returns empty results, likely due to the Notion MCP integration lacking "Read comments" permission. Pages the user commented on (but did not create) cannot be identified via API at this time.notion-fetch metadata does NOT include creator/updater user IDs, so fetching individual pages cannot be used to verify authorship either.After all subagents return (Slack, GitHub, JIRA, Notion), the main agent (opus) cross-references and groups results.
The typical work lifecycle is:
Cross-reference heuristics:
Present grouped achievements as:
## Achievement Summary ({SINCE_DATE} ~ {UNTIL_DATE})
### Achievement 1: [Descriptive title]
**Sources:**
- JIRA: NTP-123 "Implement X feature" (Done, 5pts)
- GitHub: moneyforward/repo#456 "Add X feature" (merged 2026-03-15)
- Slack: #channel-name - discussed design on 2026-03-01, review request on 2026-03-10
- Notion: "Design Document for X" (created 2026-03-05) https://notion.so/...
**Summary:** 1-3 sentence description of what was accomplished.
### Achievement 2: ...
### Uncategorized Items
Items that couldn't be grouped into a specific achievement.
After presenting STEP 2 results, ask the user:
"半期目標とのマッチングも行いますか? / Would you also like to match achievements against your half-year goals?"
If yes:
"Kaonaviの半期目標をそのまま貼り付けてください。 / Please paste your Kaonavi half-year goals as-is."
After receiving the goals, parse Goal Indicators (GI1, GI2, GI3, ...) and their descriptions, then:
For each achievement from STEP 2, determine the best-matching GI based on:
Output a categorized report:
## Goal Achievement Mapping
### GI1: [Goal Indicator title] (weight: XX%)
- Achievement A: ...
- Achievement B: ...
### GI2: [Goal Indicator title] (weight: XX%)
- Achievement C: ...
### GI3: [Goal Indicator title] (weight: XX%)
- Achievement D: ...
### Extra (not mapped to any GI)
- Achievement E: ...
slack_read_thread and slack_read_channel may also require user consent. If so, fall back to using search result snippets without thread expansion.auth-login command to re-authenticate via OAuth--owner moneyforward to scope searches'YYYY-MM-DD' with quotes; gh uses YYYY-MM-DD..YYYY-MM-DDcontent_search_mode omitted or "ai_search") is semantic but does NOT strictly enforce created_by_user_ids — unrelated pages leak in. Always cross-reference with workspace_search mode which enforces the filter strictly but only does keyword matching.notion-get-comments returns empty for all pages (as of 2026-04). Likely cause: the Notion MCP integration lacks "Read comments" permission. Pages the user commented on but did not create cannot be identified. This is a known limitation — do not waste API calls trying to check comments.notion-fetch does not return creator or last-editor user IDs in metadata. Cannot verify page authorship by fetching individual pages.workspace_search is keyword-based. Run 3-5 queries with varied keywords derived from the user's GIs/work areas to maximize coverage. A page with an unrelated title may be missed.