Export GitHub Copilot shared conversations to clean Markdown with inline attachments. No API key required!
A Python-based toolkit to extract and archive GitHub Copilot chat share pages with full authentication support, file attachment capture, and inline file linking.
- β Export Copilot shared conversations to Markdown
- β File attachment capture - Extracts CSV, TXT, JSON, YAML, XML, MD, and code files
- β Inline attachment links - Files appear where they're referenced in the conversation
- β Authenticated access via manual login (saves reusable session state)
- β Headless automation with Playwright
- β Fallback static scraper for public pages
- β JSON API extraction with DOM fallback
- β
Debugging artifacts (
page.html, network logs) - β Clean, simple folder structure (attachments only)
- β Works on macOS, Linux, and Windows
brew tap pandaxbacon/tap
brew install github-copilot-chat-exporterThat's it! The copilot-exporter command is now available.
- Python 3.9 or higher
- pip (Python package installer)
- Create a virtual environment:
python3 -m venv .venv
source .venv/bin/activate # On Windows: .venv\Scripts\activate- Install dependencies:
pip install -r requirements.txt
python -m playwright install chromium- Authenticate (One-time setup):
copilot-exporter --mode login --url https://github.com/copilot/share/YOUR-SHARE-ID- A browser window will open
- Sign in to GitHub
- Press Enter in the terminal to save authentication
- Export Conversations:
# Markdown only (text and code blocks)
copilot-exporter --mode run --url https://github.com/copilot/share/YOUR-SHARE-ID
# With file attachments (captures CSV, TXT, JSON, YAML, code files, etc.)
copilot-exporter --mode run --url https://github.com/copilot/share/YOUR-SHARE-ID --with-assets- Authenticate (One-time setup):
python scraper_playwright.py --mode login --url https://github.com/copilot/share/YOUR-SHARE-ID- A browser window will open
- Sign in to GitHub
- Press Enter in the terminal to save
storage_state.json
- Export Conversations:
python scraper_playwright.py --mode run --url https://github.com/copilot/share/YOUR-SHARE-ID
# With CSV attachments (captures uploaded and generated files)
python scraper_playwright.py --mode run --url https://github.com/copilot/share/YOUR-SHARE-ID --with-assetschat-export.md- Clean Markdown format with inline attachment linkspage.html- Full page HTML for debuggingstorage_state.json- Saved authentication state (reusable)- Asset folder (when using
--with-assets):output/<conversation>/attachments/- CSV files and user uploads with inline links
The Playwright-based scraper supports authenticated access and renders JavaScript-heavy pages.
python scraper_playwright.py --mode login --url <SHARE_URL>Opens a headed browser for manual GitHub login, then saves authentication to storage_state.json.
python scraper_playwright.py --mode run --url <SHARE_URL> [--with-assets]Runs headless using saved authentication. Exports to Markdown with optional attachment capture.
Options:
| Flag | Description | Default |
|---|---|---|
--mode |
login or run |
run |
--url |
GitHub Copilot share URL | Sample URL |
--with-assets |
Capture CSV files and attachments | False |
For publicly accessible share pages (no authentication required):
python scraper_requests.py --url <SHARE_URL>Note: This will exit with a warning if the page requires login. Use the Playwright scraper for authenticated pages.
# Assuming storage_state.json exists from login
import asyncio
from pathlib import Path
from scraper_playwright import run_export
asyncio.run(run_export(
url="https://github.com/copilot/share/YOUR-SHARE-ID",
with_assets=False # Set to True to capture CSV files
))import asyncio
from scraper_playwright import run_export
urls = [
"https://github.com/copilot/share/ID-1",
"https://github.com/copilot/share/ID-2",
"https://github.com/copilot/share/ID-3",
]
async def batch_export():
for url in urls:
await run_export(url, with_assets=True)
print(f"β Exported {url}")
asyncio.run(batch_export())# Chat Export
## User
we should not pursue a perfect cut
instead we may run multiple runs β¦ and then add another workflow at the end to judge and vote which cut is the best
## Copilot
That's exactly the right instinct:
- There isn't one optimal chunking for all tasks.
- You've already seen two "valid but different" interpretations of the same document.
...When --with-assets is set, exports are organized under output/<conversation>/:
output/
conversation-title/
chat-export.md # Markdown with inline attachment links
page.html # Debug artifact
attachments/
aia-glossary-001.csv # User-uploaded CSV
aia-glossary-cleaned-001.csv # Copilot-generated CSV
image-001.png # User-uploaded image
Inline Attachment Links:
Attachments appear directly where they're referenced in the conversation:
## User
π **Attachment:** [aia_glossary.csv](attachments/aia-glossary-001.csv)
here you are file
## Assistant
Here's the cleaned file I produced:
π **Attachment:** [aia_glossary_cleaned.csv](attachments/aia-glossary-cleaned-001.csv)See examples/sample-export/ for a complete real-world example.
Solution: Run the login mode first:
python scraper_playwright.py --mode login --url <SHARE_URL>Possible causes:
-
Selectors changed: GitHub may have updated the page structure
- Check
page.htmlto inspect the rendered DOM - Update
MESSAGE_SELECTORSinscraper_playwright.py
- Check
-
Network timing: Page didn't fully load
- Increase wait timeout in
run_export()function - Run in headed mode (
headless=False) to visually confirm rendering
- Increase wait timeout in
Cause: Authentication expired or not captured
Solution: Re-run login mode to refresh storage_state.json
| Platform | Status |
|---|---|
| β macOS | Fully supported |
| β Linux | Fully supported |
| β Windows | Fully supported |
# Install dev dependencies
pip install -r requirements-dev.txt
# Run tests
pytest
# Run tests with coverage
pytest --cov=. --cov-report=html
# View coverage report
open htmlcov/index.html# Format code
black scraper_playwright.py scraper_requests.py
# Lint
flake8 scraper_playwright.py scraper_requests.py
# Type check
mypy scraper_playwright.py scraper_requests.pyContributions are welcome! Please feel free to submit a Pull Request.
See CONTRIBUTING.md for guidelines.
This project is licensed under the MIT License - see the LICENSE file for details.
- Built with Playwright for browser automation
- Inspired by the need to archive AI-assisted development conversations
- π Documentation: See developer-notes/ for R&D details
- π Issues: Report on GitHub Issues
- β Like this project? Give it a star!
Made with β€οΈ for developers who want to own their AI conversation history