change-learn
AI-visualize and AI-summarize the changes between two source-control states — git commits, staged files, the working tree, untracked files, or any combination — as Mermaid diagrams and Marp slide decks (plain-text syntax throughout), with optional PDF export. Decks narrate the change at a high level first, then progressively lower: title → executive summary → area breakdown → commit story → per-area detail → risk flags.
This is an Agent Plugins 1.0 plugin with two layers:
- A portable skill (
skills/change-learn/) — works in any Agent Skills client with justpython3andgit. Token-efficient scripts do the heavy lifting; the agent reads compact JSON and fills in the narrative. - An MCP host (
hosts/ChangeLearn.Host/at the repo root) — a Blazor Server app exposing the same capabilities as 8 MCP tools, with a Squad agent team on the GitHub Copilot SDK + Microsoft Agent Framework for AI-written summaries and an append-only decision ledger. - A GitHub Copilot canvas — GitHub Copilot App discovers the bundled
com.github.copilot/extensions/change-learnextension automatically. It provides an interactive Change Learn panel for summaries, Mermaid source, and Marp deck slide previews. Its From commit and To commit selectors include commit SHAs, subjects, branch/ref decorations, and available GitHub pull-request metadata. Analyze span validates the selected ancestor range, generates immediate baseline artifacts (commit-span diagram, area-churn diagram, and initial Marp preview), and dispatches the full AI workflow to the Copilot session viasession.sendto write the complete narrative and load the finished deck. The direct baseline path remains available when the App cannot dispatch an MCP tool result.
The skill (portable layer)
| Script | Purpose |
|---|---|
scripts/lineage.py | Token-efficient change analysis: summary (compact JSON: totals, commits, areas, per-file stats, risk flags), diff (per-file patches written to temp files with a manifest), config (resolved .change-learn.json) |
scripts/deck.py | scaffold builds a Marp deck from an analysis JSON — frontmatter, Mermaid pie/gitGraph blocks, and <!-- ai-fill --> markers the agent replaces with narrative; export renders PDF/HTML via mermaid-cli + marp-cli |
Change window selection composes freely:
python3 scripts/lineage.py --repo . summary --from main --to HEAD # commit range
python3 scripts/lineage.py --repo . summary --staged # staged only
python3 scripts/lineage.py --repo . summary --working --untracked --no-commits
Repo-root config: .change-learn.json
Drop this at the analyzed repo's root to steer every analysis:
{
"focus": ["src/**"],
"ignore": ["**/*.lock"],
"areas": { "API": ["src/api/**"], "UI": ["src/ui/**"] },
"context_files": ["ARCHITECTURE.md"],
"highlight": ["TODO", "HACK"],
"deck": { "theme": "gaia", "title": "Sprint review" },
"max_files": 200,
"max_commits": 30
}
context_files are surfaced in the analysis so the agent can ground its
narrative in your architecture notes without re-discovering them.
AI model selection: the "ai" section
The MCP host reads an optional "ai" object from the same file to pick the
model that writes narrative summaries (summarize_changes):
{
"ai": {
"provider": "copilot",
"model": "gpt-6-luna"
}
}
{
"ai": {
"provider": "openrouter",
"model": "deepseek/deepseek-v4.1-flash",
"apiKeyEnv": "OPENROUTER_API_KEY"
}
}
| Field | Default | Meaning |
|---|---|---|
provider | copilot | copilot (Squad + Copilot CLI) or openai / openai-compatible / openrouter (direct chat-completions) |
model | provider default | Model ID; for copilot it pins the Copilot session model |
baseUrl | https://openrouter.ai/api/v1 for openrouter | Endpoint for OpenAI-compatible providers |
apiKeyEnv | OPENAI_API_KEY | Name of the env var holding the key — the key itself never lives in the file |
If the configured provider can't be reached (missing key, incompatible CLI), the host logs a warning and falls back to the deterministic template summary.
PDF export
deck.py export --deck deck.md --format pdf pre-renders Mermaid blocks with
@mermaid-js/mermaid-cli, then runs @marp-team/marp-cli --pdf (both fetched
via npx on first use; requires Node.js).
The MCP host (full architecture)
hosts/ChangeLearn.Host is a .NET 10 app with two transports:
- stdio (default for harnesses) —
mcp.jsonstarts a small Node protocol bridge that negotiates the host's supported MCP protocol and proxies toscripts/mcp-stdio.sh. The launcher builds the host incrementally (output to stderr) with isolated temporary intermediates, serializing simultaneous stdio builds before it execsChangeLearn.Host.dll --stdio; tools analyze the repo the harness was launched in. The bridge also owns the host lifecycle and stops it when the MCP client closes, its parent exits, or it receives a termination signal. It answers the currentserver/discoverprobe itself before translating tool calls to the host's supported protocol. It records lifecycle-only diagnostics—method names and request IDs, never tool arguments or results—at${TMPDIR:-/tmp}/change-learn-mcp/bridge-<pid>.log. - streamable HTTP + Blazor UI (manual) —
dotnet runserves the status page and an MCP endpoint athttp://localhost:5190/mcp.
Blazor Server (status page, :5190)
└── MCP endpoint (/mcp, ModelContextProtocol.AspNetCore)
├── GitLineageService ──wraps──▶ lineage.py
├── DeckService ──wraps──▶ deck.py
├── ChangeSummarizer ──▶ Squad team (GitHub Copilot SDK + Agent Framework)
│ └─ deterministic fallback when the CLI is unavailable
└── DecisionLedger ──▶ team/.squad/decisions.md (append-only)
| MCP tool | Purpose |
|---|---|
analyze_changes | Compact lineage JSON for any change window |
get_file_diffs | Per-file patches as temp files + manifest (token-efficient) |
get_change_config | Resolved .change-learn.json |
summarize_changes | AI summary via the Squad team, deterministic fallback otherwise |
generate_deck | Scaffold a Marp deck (requires outputPath) |
export_deck_pdf | Render the deck to PDF/HTML |
record_decision / list_decisions | Squad append-only decision ledger |
Run it manually (UI + HTTP mode):
cd hosts/ChangeLearn.Host
dotnet run # listens on http://localhost:5190
Stdio mode is normally launched by the harness, but can be probed directly:
plugins/change-learn/scripts/mcp-stdio.sh # speaks MCP on stdin/stdout
Configuration (appsettings.json, all optional — blank values fall back to
defaults): ChangeLearn:RepoPath (default: discovered git root),
ChangeLearn:SkillScriptsPath, ChangeLearn:PythonPath,
ChangeLearn:SquadFolderPath (the .squad directory itself).
Squad agent team
team/.squad/ defines the team per Brady Gaster's Squad pattern:
a charter, per-agent instruction files (narrator, cartographer, historian), and
decisions.md — the declarative, deterministic harness layer that manages the
team's charter, decisions, and history. ChangeSummarizer drives the team
through the GitHub Copilot SDK via Microsoft Agent Framework.
Known caveat: the Copilot SDK launches the locally installed Copilot CLI; if that CLI predates the SDK's protocol (symptom:
copilot-runtime: unsupported argument '--allow-all'),summarize_changesreturns a deterministic summary with an actionable compatibility warning. Update the Copilot CLI to restore AI summaries — every non-AI tool keeps working either way.
Install
copilot plugin marketplace add matthewcorven/corven-agent-tools
copilot plugin install change-learn@corven-agent-tools
The skill works immediately. The MCP server starts on demand: the harness
runs scripts/mcp-stdio.sh, which needs the .NET 10 SDK plus a local clone of
this repo to supply the host. The launcher finds the host via (in order):
CHANGE_LEARN_HOST_DIR—export CHANGE_LEARN_HOST_DIR=<clone>/hosts/ChangeLearn.Host- the source checkout itself, when the plugin runs from this repo
~/.config/change-learn/host-dir— a file containing the host path:mkdir -p ~/.config/change-learn && echo <clone>/hosts/ChangeLearn.Host > ~/.config/change-learn/host-dir
Set CHANGE_LEARN_REPO to analyze a repo other than the harness's working
directory.
Tests
python3 -m unittest discover -s plugins/change-learn/skills/change-learn/tests -v
License
MIT