brag-book — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited brag-book (Agent Skill) and scored it 100/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 0 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 0 flagged
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
Generate a weekly brag book entry by exporting contributions from GitHub, Google Drive, Google Calendar, and optionally Jira and Slack, then analyzing for impact and career-level evidence.
This skill generates files to disk — every step writes to {output_dir}/data/, and the final output is a Markdown file. Verify each file exists before moving on.
| Parameter | Required | Default | Example |
|---|---|---|---|
| username | YES | - | jdoe |
| YES | - | [email protected] | |
| output_dir | NO | ~/bragbook-output/ | ~/Library/CloudStorage/GoogleDrive-{email}/My Drive/My Brag Book/ |
| week_start_date | NO | Monday of current week | 2026-02-09 |
| week_end_date | NO | Today | 2026-02-13 |
If username or email is missing, use AskUserQuestion to get them. Do not proceed without both.
Date defaults: Calculate Monday of the current week as week_start_date and today as week_end_date unless the user specifies otherwise.
gh CLI installed and authenticated (gh auth status)mcp__atlassian-mcp__* tools) -- non-blocking if unavailablemcp__google-drive__* tools) -- non-blocking if unavailablemcp__google-calendar-mcp__* tools) -- non-blocking if unavailablemcp__claude_ai_Slack__* tools for Slack search) -- non-blocking if unavailablegh auth statusIF AUTH FAILS: Run gh auth login --git-protocol https --web and retry.
This step is BLOCKING. Do not proceed until authentication succeeds.
mkdir -p {output_dir}/dataVERIFY: Directory {output_dir}/data exists before proceeding.
Steps 2-7 gather data from independent sources with no dependencies between them. Execute all of them in parallel to minimize total latency.
Collect PRs authored, diff stats, and reviews given by running gh CLI commands, then process the output and write CSV files.
#### 2a. Fetch authored PRs (REST search)
gh search prs --author={username} --created=">={week_start_date}" --json number,title,state,createdAt,closedAt,repository,url --limit 500Capture the JSON output. This returns all authored PRs (open, closed, merged) in the date range.
#### 2b. Fetch diff stats and PR details (GraphQL)
Run this GraphQL query to get additions, deletions, changed files, body text, and labels for all authored PRs:
gh api graphql -f query='
query {
search(query: "author:{username} is:pr created:{week_start_date}..{week_end_date}", type: ISSUE, first: 100) {
edges {
node {
... on PullRequest {
number
title
body
url
state
createdAt
mergedAt
additions
deletions
changedFiles
labels(first: 10) {
nodes {
name
}
}
repository { nameWithOwner }
}
}
}
}
}'#### 2c. Fetch reviews given (GraphQL)
Run this GraphQL query to get reviews the user has given on other people's PRs:
gh api graphql -f query='
query {
search(query: "reviewed-by:{username} is:pr created:>={week_start_date}", type: ISSUE, first: 100) {
edges {
node {
... on PullRequest {
number
title
url
author { login }
repository { nameWithOwner }
createdAt
mergedAt
state
reviews(first: 50) {
nodes {
author { login }
state
submittedAt
body
comments(first: 10) {
totalCount
}
}
}
}
}
}
}
}'#### 2d. Process and write github_authored.csv
Join the REST search results (step 2a) with the GraphQL stats (step 2b) by PR number. For each authored PR, produce a row with these columns:
pr_number,title,repository,state,created_at,merged_at,additions,deletions,files_changed,url
Processing rules:
state: lowercase (open, closed, or merged)created_at: first 10 chars of createdAt (YYYY-MM-DD)merged_at: first 10 chars of mergedAt if the PR is merged, otherwise empty stringadditions, deletions, files_changed: from GraphQL stats; empty string if PR not found in GraphQL resultsrepository: the nameWithOwner value (e.g., org/repo)created_at descendingWrite the CSV to {output_dir}/data/github_authored.csv.
#### 2e. Process and write github_reviews.csv
From the reviews GraphQL response (step 2c), extract only reviews where review.author.login equals {username}. For each such review, produce a row with these columns:
pr_number,title,repository,pr_author,review_type,reviewed_at,pr_state,pr_created_at,has_feedback,inline_comments,url
Processing rules:
pr_author: the PR's author.login (use "unknown" if null)review_type: the review's state (e.g., APPROVED, CHANGES_REQUESTED, COMMENTED)reviewed_at: first 10 chars of submittedAt (YYYY-MM-DD)pr_state: the PR's statehas_feedback: True if the review has a non-empty body OR comments.totalCount > 0, otherwise Falseinline_comments: the review's comments.totalCountreviewed_at descendingWrite the CSV to {output_dir}/data/github_reviews.csv.
VERIFY: Files {output_dir}/data/github_authored.csv and {output_dir}/data/github_reviews.csv exist.
Using the GraphQL response already fetched in step 2b, build a JSON object keyed by PR number (as a string). Each value should contain:
{
"number": 123,
"title": "PR title",
"body": "PR body text",
"url": "https://github.com/org/repo/pull/123",
"state": "MERGED",
"created_at": "2026-05-11",
"merged_at": "2026-05-12",
"additions": 45,
"deletions": 12,
"changed_files": 3,
"labels": ["bug", "priority-high"],
"repository": "org/repo"
}Processing rules:
created_at: first 10 chars of createdAt, empty string if nullmerged_at: first 10 chars of mergedAt, empty string if nulllabels: list of label name strings from labels.nodesadditions, deletions, changed_files: integers (default 0)Write to {output_dir}/data/pr_details.json with indented formatting.
VERIFY: File {output_dir}/data/pr_details.json exists and contains PR bodies.
This step is NON-BLOCKING. If Atlassian MCP tools are unavailable, skip this step and create a placeholder CSV.
Use the Atlassian MCP server to fetch Jira tickets directly.
mcp__atlassian-mcp__search_issues_advanced with:jql_query: "assignee = '{email}' AND updated >= '{week_start_date}'"fields: "summary,description,parent,project,status,issuetype,updated"max_results: 50total in the response exceeds maxResults, paginate by calling again with incremented start_at (e.g., 50, 100, ...) until all results are fetched.ticket_id: from issue key (e.g., PROJ-1234)ticket_name: from fields.summaryproject_code: from fields.project.keyepic_name: from fields.parent.fields.summary (if parent exists, otherwise empty)description: from fields.description (if returned as ADF JSON, recursively extract text from content[].content[].text nodes; if plain text, use directly; truncate to 500 chars){output_dir}/data/jira.csv with columns: ticket_id,ticket_name,project_code,epic_name,descriptionIF MCP UNAVAILABLE: Create an empty placeholder:
echo "ticket_id,ticket_name,project_code,epic_name,description" > {output_dir}/data/jira.csvThis step is NON-BLOCKING. If Google Drive MCP tools are unavailable, skip this step and note it in the output.
mcp__google-drive__list_drive_files with query={email} and maxResults=50mcp__google-drive__get_drive_file_metadata to get ownership, MIME type, and timestampsapplication/vnd.google-apps.document, application/vnd.google-apps.spreadsheet, application/vnd.google-apps.presentationmodifiedTime is between {week_start_date} and {week_end_date}mcp__google-drive__get_document_structure first to get section headings and previews, then generate synopsis from the structure (more effective than raw preview for RFCs and long docs)mcp__google-drive__get_document_preview and summarize from the first 1000 characters{output_dir}/data/gdrive.csv with columns: last_modified, doc_type, doc_title, doc_link, synopsisIF MCP UNAVAILABLE: Create an empty placeholder:
echo "last_modified,doc_type,doc_title,doc_link,synopsis" > {output_dir}/data/gdrive.csvThis step is NON-BLOCKING. If Google Calendar MCP tools are unavailable, skip this step and create a placeholder CSV.
mcp__google-calendar-mcp__list_calendar_events with:calendarId: "primary"timeMin: {week_start_date}T00:00:00ZtimeMax: {week_end_date}T23:59:59ZmaxResults: 100singleEvents: truemcp__google-calendar-mcp__get_calendar_event with calendarId: "primary" and the event ID to get attendee details.{output_dir}/data/calendar.csv with columns: date,time,title,organizer,is_organizer,attendee_count,duration_minutes,description_snippetIF MCP UNAVAILABLE: Create an empty placeholder:
echo "date,time,title,organizer,is_organizer,attendee_count,duration_minutes,description_snippet" > {output_dir}/data/calendar.csvThis step is NON-BLOCKING. If Slack MCP tools are unavailable, skip this step and create a placeholder CSV.
Limitation: mcp__claude_ai_Slack__slack_search_public provides keyword search across public Slack channels. Results are treated as supplementary evidence in the analysis step, not primary contributions.
mcp__claude_ai_Slack__slack_search_public:"{username}" (e.g., "jdoe")date,channel,snippet,source_url,search_query"date unverified"{output_dir}/data/slack_mentions.csvIF MCP UNAVAILABLE: Create an empty placeholder:
echo "date,channel,snippet,source_url,search_query" > {output_dir}/data/slack_mentions.csv{output_dir}/data/github_authored.csv{output_dir}/data/github_reviews.csv{output_dir}/data/pr_details.json{output_dir}/data/jira.csv{output_dir}/data/gdrive.csv{output_dir}/data/calendar.csv{output_dir}/data/slack_mentions.csvPROJ-, INFRA-)pr_details.json). If body is empty or minimal, fall back to PR title + diff stats (e.g., "+120/-45 across 8 files").context/career_levels.md, classify the contribution using that level's signals. Use N/A for a level if no relevant signals are present.Determine these fields from the data:
~/.claude/skills/brag-book/context/career_levels.md:~/.claude/skills/brag-book/context/contribution_categories.md~/.claude/skills/brag-book/context/brag_book_template.md{output_dir}/brag_book_week_of_{week_start_date}.mdgithub_reviews.csvgdrive.csvcalendar.csvslack_mentions.csvVERIFY: File {output_dir}/brag_book_week_of_{week_start_date}.md exists and contains all sections.
{output_dir}/brag_book_week_of_{week_start_date}.md Weekly Brag Book Summary:
- PRs authored: N (M merged)
- Lines: +X/-Y
- Reviews given: N
- Issues: N
- Key meetings: N (M organized)
- Slack mentions: N
- Primary Focus: {project}
- Sentiment: {N}/5 (NEEDS CONFIRMATION)
- Scope: {Team/Multi-Team/Guild/Org}"I've drafted sentiment as {N}/5 based on {reasoning}. Does this feel right, or would you like to adjust?"
{output_dir}/data/. If you find yourself showing results in chat without creating files, stop and go back to Step 0.Example 1: "Update my brag book" (on a Friday)
You should:
{output_dir}/brag_book_week_of_2026-02-09.mdExample 2: "Brag book entry for last week"
You should:
~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.