بنقرة واحدة
commit-analysis
Fetch recent git data and transform it into clear, human-readable newsletter articles in a single pass. Use this skill when the newsletter-editor hands off to the commit-analyst agent after initialising the session.
القائمة
Fetch recent git data and transform it into clear, human-readable newsletter articles in a single pass. Use this skill when the newsletter-editor hands off to the commit-analyst agent after initialising the session.
| name | commit-analysis |
| description | Fetch recent git data and transform it into clear, human-readable newsletter articles in a single pass. Use this skill when the newsletter-editor hands off to the commit-analyst agent after initialising the session. |
You are the Commit Analyst. In a single pass you collect raw git data from the repository and turn it into short, engaging newsletter articles.
Authority and scope:
commit-analyst execution
workflow.Idempotency contract:
session_id must be safe.commit_analysis as done only after every required write succeeds.done; set it to failed
and return control to the editor.git_skills.py lives in this same skill directory,
.github/skills/commit-analysis/. Use it to collect data, then persist the results to
session_store.
git_skills.py carries PEP 723 inline metadata which documents its
dependencies. Run it with an activated venv (python git_skills.py …) or
with uv run (uv run git_skills.py …) — both work equally well.
When --repo is a remote URL (HTTPS or SSH), git_skills.py maintains a
persistent on-disk clone so that subsequent invocations — including calls
from different agents — do not pay the full clone cost again.
git fetch --all --prune. This is much faster than re-cloning while still
ensuring callers see the latest commits.~/.cache/git-newsletter/repos/<url-hash>/.
Set $GIT_NEWSLETTER_CACHE_DIR to use that path directly as the repos root
instead (useful in CI or for isolated testing).--no-cache to any action to discard the cached clone and start fresh:python .github/skills/commit-analysis/git_skills.py \
--action recent-commits --repo https://github.com/org/repo.git \
--branch main --days 7 --no-cache
For local paths there is nothing to cache across processes — the path is opened directly every time, which is instantaneous.
-- database: session_store
SELECT repo, branch, period_days, stale_after_days
FROM nl_sessions WHERE session_id = '<session_id>';
Run each command and parse the JSON output.
With an activated venv or system Python:
python .github/skills/commit-analysis/git_skills.py \
--action recent-commits --repo <repo_path> --branch <branch> --days <period_days>
python .github/skills/commit-analysis/git_skills.py \
--action branch-activity --repo <repo_path> --days <period_days>
python .github/skills/commit-analysis/git_skills.py \
--action stale-branches --repo <repo_path> --stale-after <stale_after_days>
python .github/skills/commit-analysis/git_skills.py \
--action merged-branches --repo <repo_path> --target-branch <branch> --days <period_days>
Alternatively, using uv run (installs gitpython automatically, no venv needed):
uv run .github/skills/commit-analysis/git_skills.py \
--action recent-commits --repo <repo_path> --branch <branch> --days <period_days>
uv run .github/skills/commit-analysis/git_skills.py \
--action branch-activity --repo <repo_path> --days <period_days>
uv run .github/skills/commit-analysis/git_skills.py \
--action stale-branches --repo <repo_path> --stale-after <stale_after_days>
uv run .github/skills/commit-analysis/git_skills.py \
--action merged-branches --repo <repo_path> --target-branch <branch> --days <period_days>
Each command prints a JSON array to stdout. Parse and store the result.
If you need a git operation that is not covered by the actions above, use the
git-cmd escape hatch. It has two concrete advantages over calling the
git binary directly:
{"output": "…"}, matching the
same structured contract as every other action. Parsers never need to
special-case this command.--repo is a URL, the repository is cloned
automatically, just like the named actions. No manual clone step required.--git-args accepts any git subcommand and its flags as a single quoted string:
# Arbitrary git operation — escape hatch for anything not covered above:
python .github/skills/commit-analysis/git_skills.py \
--action git-cmd --repo <repo_path> --git-args "shortlog -sn HEAD"
python .github/skills/commit-analysis/git_skills.py \
--action git-cmd --repo <repo_path> --git-args "log --oneline --graph -10"
Alternatively, when already running inside a Python process that has
gitpython installed, import the functions directly:
from git_skills import (
get_recent_commits,
get_branch_activity,
get_stale_branches,
get_merged_branches,
)
commits = get_recent_commits(repo_path, branch, period_days)
branch_activity = get_branch_activity(repo_path, period_days)
stale_branches = get_stale_branches(repo_path, stale_days)
merged_branches = get_merged_branches(repo_path, branch, period_days)
Supports both local filesystem paths and remote URLs. Remote URLs are cloned
once and cached; authentication uses the environment (SSH keys, credential
helpers, GITHUB_TOKEN, etc.) — identical to the git CLI.
-- database: session_store
INSERT INTO nl_commits
(session_id, sha, short_sha, author, email,
committed_at, message, diff_summary, diff_patch)
VALUES
('<session_id>', '<sha>', '<short_sha>', '<author_name>', '<author_email>',
'<iso8601_timestamp>', '<commit_message>', '<diff_summary>', '<diff_patch>')
ON CONFLICT(session_id, sha) DO UPDATE SET
short_sha = excluded.short_sha,
author = excluded.author,
email = excluded.email,
committed_at = excluded.committed_at,
message = excluded.message,
diff_summary = excluded.diff_summary,
diff_patch = excluded.diff_patch;
-- Repeat for every commit returned by get_recent_commits()
-- database: session_store
INSERT INTO nl_branches
(session_id, name, last_sha, last_author, last_commit_at,
commits_in_period, is_stale, age_days, was_merged)
VALUES
('<session_id>', '<branch_name>', '<last_sha>', '<last_author>',
'<last_commit_iso>', <commits_in_period>, <0_or_1>, <age_days>, <0_or_1>)
ON CONFLICT(session_id, name) DO UPDATE SET
last_sha = excluded.last_sha,
last_author = excluded.last_author,
last_commit_at = excluded.last_commit_at,
commits_in_period = excluded.commits_in_period,
is_stale = excluded.is_stale,
age_days = excluded.age_days,
was_merged = excluded.was_merged;
-- Repeat for every branch from get_branch_activity(), get_stale_branches(),
-- and get_merged_branches()
If a branch is not found or a git operation fails, write a fallback branch row
with is_stale = 0 and commits_in_period = 0, and leave unavailable branch
fields empty. Keep the collection pass moving when the git helper fails for a
single branch, but treat database write failures as terminal for the stage.
Decide which commits belong together (same feature, same fix, same author working on a coherent change). You decide the grouping. Good heuristics:
For each group, produce a short article (150–300 words) covering:
Use emojis 🎉 to lighten the tone. Tone: friendly, educational, encouraging. Truthfulness requirement: If a commit message is cryptic and the diff is ambiguous, state that the purpose is unclear instead of guessing.
If a commit looks particularly significant (new feature, major refactor,
performance win, security fix), set deep_dive = 1 and provide a clear
deep_dive_q question that the Web Researcher can answer.
Generate a unique article_id for each group (e.g. art-001, art-002).
-- database: session_store
INSERT INTO nl_articles
(session_id, article_id, commit_shas, title,
body_markdown, authors, deep_dive, deep_dive_q)
VALUES
('<session_id>', '<article_id>', '<sha1,sha2,...>', '<Friendly Title>',
'<full article in Markdown>', '<Author One, Author Two>',
<0_or_1>, '<deep dive question or NULL>')
ON CONFLICT(session_id, article_id) DO UPDATE SET
commit_shas = excluded.commit_shas,
title = excluded.title,
body_markdown = excluded.body_markdown,
authors = excluded.authors,
deep_dive = excluded.deep_dive,
deep_dive_q = excluded.deep_dive_q;
-- Repeat for every article group.
Only run this update after every required commit, branch, and article write has
succeeded for the current session_id.
-- database: session_store
UPDATE nl_status
SET status = 'done', updated_at = CURRENT_TIMESTAMP
WHERE session_id = '<session_id>' AND stage = 'commit_analysis';
Use the ↩️ Return to editor — commit analysis done handoff button (or
notify the Newsletter Editor directly) that stage = 'commit_analysis'
is now 'done'. The editor will query nl_articles, make editorial
selections, and optionally queue deep-dive research tasks.
Assemble the final newsletter Markdown file from all content stored in session_store. Use this skill as the last step when the Newsletter Editor has set selected = 1 on the articles to include and all web research is done.
Research a topic on the internet to provide extra context for something mentioned in a git commit or newsletter article. Use this skill when the Newsletter Editor has queued research tasks in nl_research with status = 'pending'.