Skip to content

matthewcorven/change-learn

v1.3.2MIT

AI-visualizes and AI-summarizes the changes between two source-control states (commits, staged, untracked, or combinations) as Mermaid/PlantUML diagrams and Marp slide decks, narrated high-level first and then progressively lower, with optional PDF export and token-efficient lineage analysis driven by a repo-root .change-learn.json config.

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:

  1. A portable skill (skills/change-learn/) — works in any Agent Skills client with just python3 and git. Token-efficient scripts do the heavy lifting; the agent reads compact JSON and fills in the narrative.
  2. 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.
  3. A GitHub Copilot canvas — GitHub Copilot App discovers the bundled com.github.copilot/extensions/change-learn extension 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 via session.send to 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)

ScriptPurpose
scripts/lineage.pyToken-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.pyscaffold 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"
  }
}
FieldDefaultMeaning
providercopilotcopilot (Squad + Copilot CLI) or openai / openai-compatible / openrouter (direct chat-completions)
modelprovider defaultModel ID; for copilot it pins the Copilot session model
baseUrlhttps://openrouter.ai/api/v1 for openrouterEndpoint for OpenAI-compatible providers
apiKeyEnvOPENAI_API_KEYName 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.json starts a small Node protocol bridge that negotiates the host's supported MCP protocol and proxies to scripts/mcp-stdio.sh. The launcher builds the host incrementally (output to stderr) with isolated temporary intermediates, serializing simultaneous stdio builds before it execs ChangeLearn.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 current server/discover probe 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 run serves the status page and an MCP endpoint at http://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 toolPurpose
analyze_changesCompact lineage JSON for any change window
get_file_diffsPer-file patches as temp files + manifest (token-efficient)
get_change_configResolved .change-learn.json
summarize_changesAI summary via the Squad team, deterministic fallback otherwise
generate_deckScaffold a Marp deck (requires outputPath)
export_deck_pdfRender the deck to PDF/HTML
record_decision / list_decisionsSquad 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_changes returns 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):

  1. CHANGE_LEARN_HOST_DIR — export CHANGE_LEARN_HOST_DIR=<clone>/hosts/ChangeLearn.Host
  2. the source checkout itself, when the plugin runs from this repo
  3. ~/.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