DeckFlow helps deck builders translate decks between Moxfield and Archidekt without manual editing. It also provides ChatGPT prompt-building workflows for single-deck analysis, cEDH meta-gap analysis, and head-to-head deck comparison, Commander Spellbook combo lookup, Scryfall card and mechanic references, and a cache-backed category suggestion engine.
Repository description (≤350 characters): DeckFlow unifies Moxfield and Archidekt decks, harvests Archidekt category data, and exposes CLI/web tools for diffs, printing conflict reports, card/mechanic lookup, ChatGPT deck-analysis, cEDH meta-gap, and deck-comparison prompt generation with Scryfall references, Commander Spellbook combos, and cache-backed category suggestions.
DeckFlow.Corecontains parsers, diffing logic, exporters, and the Archidekt/Moxfield integrations.DeckFlow.Core.Loadingcentralizes deck input loading and Commander deck-size validation so the web app and CLI share the same parsing/import rules.DeckFlow.Webprovides an ASP.NET Core MVC UI for running syncs, ChatGPT prompt building, cEDH meta-gap analysis, deck comparison prompt building, card lookup, commander category browsing, and category suggestions.DeckFlow.CLIexposes deck comparison, category harvesting, and cache querying in a console tool.- The ChatGPT Analysis page is the primary single-deck analysis workflow: it resolves card text via Scryfall, looks up rules for mechanics via the WOTC rules page, queries Commander Spellbook for combos, and assembles a complete analysis prompt with reference data attached.
- The cEDH Meta Gap page compares a submitted deck against 1 to 3 EDH Top 16 reference lists for the same commander, resolves canonical card names through Scryfall, injects Commander Spellbook combo references, generates a structured
meta_gapChatGPT prompt, and renders the returned JSON as a readable upgrade path. - The Commander Categories page shows which Archidekt tags appear most often on decks where a given card is listed as commander.
- The DeckFlow Bridge browser extension lets DeckFlow fetch Moxfield decks from your logged-in browser session when the server path is blocked.
- DeckFlow can optionally prompt users to install the included DeckFlow Bridge extension when a Moxfield URL import would otherwise be blocked from the server.
- Restore/build:
dotnet build DeckFlow.sln - Run the web app:
dotnet run --project DeckFlow.Web - Use the CLI to compare or harvest decks:
dotnet run --project DeckFlow.CLI -- --help
scripts/run-web.sh— bash wrapper that rebuildsDeckFlow.Weband launches it onhttp://localhost:5173with no browser auto-launch.scripts/run-web.ps1— PowerShell equivalent for Windows terminals.
DeckFlow.Web/wwwroot/css/site-common.csscontains shared shell and view-level styles that apply regardless of the selected color theme.DeckFlow.Web/wwwroot/css/site*.cssfiles remain responsible for theme palettes and component styling.- The theme picker now includes all ten two-color guild themes in addition to the existing wedges, shards, and specialty themes.
- Keep long-lived CSS out of Razor views; prefer shared stylesheets so caching and theme behavior stay predictable.
- Browser-facing JSON POST APIs now enforce same-origin
Origin/Refererchecks before processing deck sync, suggestion, mechanic lookup, and Archidekt cache-harvest requests. - The old sessionStorage page-snapshot restore path was removed. DeckFlow no longer writes
main.content-shell.innerHTMLinto storage or rehydrates raw HTML from storage on load. - These checks are meant to reduce cross-site request abuse and avoid re-inserting stale or storage-poisoned markup into the DOM.
- Publish the web app with
dotnet publish DeckFlow.Web/DeckFlow.Web.csproj /p:PublishProfile=IIS-LocalFolder - The publish output goes to
DeckFlow.Web/bin/Release/net10.0/publish/iis-local/ - The .NET SDK generates
web.configduring publish; there is no checked-inweb.config - In IIS, create an application such as
/deckflowthat points at that publish folder - Install the ASP.NET Core Hosting Bundle on the IIS machine
- The checked-in views and scripts are path-base safe, so links and API calls stay under the IIS application path instead of jumping to
/
- A
Dockerfile,fly.toml, andrender.yamlship at the repo root for one-command builds on Fly.io or Render. - Set
MTG_DATA_DIR=/dataand mount a persistent volume there. Both the SQLite category-knowledge DB and the ChatGPT Analysis artifact folder get redirected under that single directory. - The Dockerfile's entrypoint resolves
$PORTat container start so platforms that inject a dynamic port (Render) work without changes. - Moxfield URL caveat. Moxfield's Cloudflare edge blocks requests from datacenter IP ranges with HTTP 403/5xx. When that happens, DeckFlow automatically falls back to Commander Spellbook's public
card-list-from-urlendpoint (which accepts the same Moxfield URL) and loads the deck from there instead. The UI surfaces a warning banner noting that card printings, set codes, collector numbers, author tags/categories, and sideboard/maybeboard entries are not available through the fallback. For full metadata, users should copy the Moxfield deck export text and paste it into the deck input directly — that path continues to work from anywhere. - Optional browser-extension path. The web UI now detects Moxfield deck URLs before submit. If the optional DeckFlow Bridge extension is installed and the current DeckFlow origin is allowed in extension settings, the browser fetches the Moxfield deck directly and submits it through the existing form flow. If the extension is not installed, DeckFlow can prompt the user with the included install page (
/extension-install.html), which now serves a downloadable ZIP from/extensions/deckflow-bridge.zip. Browsers do not allow the site to silently install the extension. Mobile browsers are left on the normal server/fallback path and are not prompted for the extension. The Moxfield URL fields in the web UI also include a collapsible in-app hint that links to the install page and explains the allowed-origin setup.
- Extension folder:
browser-extensions/deckflow-bridge - Download/install page:
/extension-install.htmlserves/extensions/deckflow-bridge.zip - Current install mode: download ZIP, unzip it locally, then load unpacked via
chrome://extensionsoredge://extensions - Security default: the DeckFlow bridge only responds on origins the user explicitly allows in extension options
- The extension contains:
deckflow-bridge.jsfor the optional DeckFlow web-app bridgeoptions.html/options.jsfor managing the allowed DeckFlow origin listbackground.jsfor cross-origin Moxfield API requests
The ChatGPT Analysis page (/Deck/ChatGptPackets) guides you through a 5-step workflow. Step 2 generates the analysis prompt, Step 3 parses and renders the returned deck_profile JSON, Step 4 optionally generates a set-upgrade prompt using that parsed profile, and Step 5 parses and renders the returned set_upgrade_report JSON.
Three layouts are available via the toolbar: Guided, Focused, and Expert. They present the same underlying steps with different amounts of context and guidance text.
Choose an Input method (paste text or public deck URL) and provide either a Moxfield/Archidekt deck URL or pasted deck export text. The chosen mode round-trips with the form so it survives refreshes and workflow-step navigation. The service:
- Falls back to treating leading quantity-1 entries as the commander when no Commander section header is present (Moxfield plain-text exports), then validates the inferred commander against Scryfall before continuing.
- Rejects inferred commanders that are not legal by the workflow rules: legendary creature, legendary Vehicle, or a planeswalker whose oracle text says it can be your commander.
Configure the analysis:
| Setting | Purpose |
|---|---|
| Target Commander Bracket | Bracket 1–5. ChatGPT uses this when evaluating card quality, interaction density, and upgrade suggestions. |
| Analysis questions | Select one or more questions from the buckets below. |
| Card name | Required when card-specific questions are selected. |
| Budget amount | Required when the budget-upgrade question is selected. |
| Decklist export format | Moxfield or Archidekt — required when category questions are selected; optional for versioning questions. |
| Include card versions | When checked, the original deck's set code and collector number are sent so ChatGPT can preserve the exact printing for retained cards. |
| Preferred category names | Shown when Update categories is selected. One name per line; ChatGPT will prefer these over inventing new ones. |
| Protected cards | Cards that must appear in every generated deck version. |
Click Generate Analysis Packet to build the reference data and analysis prompt. The service:
- Resolves all deck cards via Scryfall (
POST /cards/collectionin batches of 75) to supply authoritative Oracle text. - Fetches official mechanic rules text from the WOTC rules page for any keyword mechanics found on resolved cards.
- Fetches the Commander banned list.
- Queries the Commander Spellbook API if combo questions are selected.
- Fires the banned-list fetch, set-packet fetch, and Spellbook combo lookup concurrently to minimize wait time.
- Generates a suggested ChatGPT conversation title displayed in the UI with a copy button.
The generated prompt uses ## section headings (TASK, EVIDENCE RULES, BRACKET GUIDANCE, ANALYSIS QUESTIONS, OUTPUT FORMAT, REFERENCE DATA, DECKLIST) to keep long prompts structured.
Paste the fenced deck_profile JSON block or raw JSON payload returned from ChatGPT. You can also paste a saved deck_profile JSON file here directly without filling out Steps 1 and 2 again. The page validates the payload, parses it into a strongly typed model, and renders a readable summary of:
- Format and commander
- Game plan, speed, primary axes, and synergy tags
- Strengths, weaknesses, deck needs, and weak slots
- Per-question answers with basis notes
- Full deck versions when versioning questions were requested
This step is local to the returned JSON. It does not regenerate the analysis packet or call upstream services again.
Select one or more recent MTG sets, or paste a condensed set packet override. The page generates a set-upgrade prompt that references the parsed deck profile and asks ChatGPT to evaluate new cards from each set as potential inclusions, with suggested cuts, bracket-fit notes, speculative tests, and traps called out per set. The set dropdown loads asynchronously from /api/set-options so the page renders immediately. A deck in Step 1 is required; the parsed Step 3 deck profile is optional but strongly recommended — without it ChatGPT gets an empty schema and produces generic recommendations.
Paste the fenced set_upgrade_report JSON block or raw JSON payload returned from ChatGPT. The page validates the payload, parses it into a strongly typed model, and renders a readable summary of:
- Per-set panels: top adds with suggested cuts and reasoning, traps, and speculative tests
- Final shortlist broken into must-test, optional, and skip columns
Like Step 3, this step is local to the returned JSON. You can paste a saved set_upgrade_report JSON file here directly without re-running the earlier steps — Step 5 runs standalone when no deck source is present.
All ChatGPT prompts generated by this app (analysis, set-upgrade, deck comparison, meta-gap) explicitly instruct ChatGPT to return JSON inside a fenced ```json code block. Raw JSON outside a code block is rejected by the wording.
Check Save artifacts to disk to write all generated prompts and reference files to:
Documents\DeckFlow\ChatGPT Analysis\<commander-name>\<timestamp>\
Files saved: 01-request-context.txt, 00-input-summary.txt, 30-reference.txt, 31-analysis-prompt.txt, 41-deck-profile-schema.json, 50-set-upgrade-prompt.txt (when applicable), plus 40-deck-profile.json and 51-set-upgrade-response.json capturing the pasted Step 3 and Step 5 JSON responses. The standalone Step 3 and Step 5 paste flows also save their pasted JSON when Save artifacts to disk is checked.
Questions are grouped into collapsible buckets. Buckets with pre-selected questions open automatically on page load.
| Bucket | Notable questions |
|---|---|
| Core Deck Analysis | Strengths/weaknesses, win condition, consistency, power level, best meta |
| Deck Construction & Balance | Mana curve, lands and ramp, card draw, interaction count, underperformers |
| Strategy & Synergy | Key synergies, anti-synergies, commander support, protect-cards, game plan |
| Optimization & Upgrades | Cuts for strength, budget upgrades (requires amount), missing staples, faster/competitive, board-wipe resilience |
| Meta & Matchups | Performance vs. archetypes, pod weaknesses, tech options, hate pieces |
| Play Pattern & Decision Making | Ideal opening hand, tutor priorities, when to cast the commander, common misplays |
| Specific Card-Level Questions | Card worth including (requires card name), better alternatives (requires card name), weakest card, too many high-CMC cards |
| Advanced / Expert-Level | Turn clock, disruption vulnerability, keepable hand percentage, redundancy, mana-base optimization |
| Combo Analysis (Commander Spellbook) | Combos already in the deck, combos one card away within the color identity — both use live Commander Spellbook API data injected into the prompt |
| Deck Versioning & Upgrade Paths | Bracket 2/3/4/5 version, 3 named upgrade paths, assign categories, update categories |
When any versioning or category question is selected, the analysis prompt instructs ChatGPT to:
- Output the full, complete 100-card decklist for each generated version — no truncation, no "fill with basics" shorthand.
- Count cards before responding to confirm the total reaches 100.
- Use the deck builder's inline format when an export format is chosen:
- Moxfield:
1 CardName (SET) collectorNumber— or with categories:1 CardName (SET) collectorNumber #Category1 #Category2 - Archidekt:
1 CardName (SET) collectorNumber [Category1,Category2]— commander line uses[Commander]
- Moxfield:
- Output a Cards Added and Cards Cut diff after each decklist, comparing against the original.
- Output a
deck_profileJSON block for each generated deck version. - When Include card versions is checked, preserve the original printing (set code + collector number) for every retained card.
- Assign categories — ChatGPT assigns functional role categories to every card in the deck. Plain text export is not supported; Moxfield or Archidekt format is required.
- Update categories — ChatGPT updates or reassigns categories using the preferred category names you provide. Preferred names are injected into the prompt; ChatGPT may add new categories only when none of the preferred names fit.
- Basic card types (Creature, Instant, Sorcery, Enchantment, Artifact, Planeswalker, Battle) are excluded as categories. ChatGPT is instructed to use functional role labels instead (Ramp, Card Draw, Removal, Wipe, Tutor, Win Condition, Protection, etc.).
- For category questions, the prompt explicitly requires the final decklist to be returned only inside a fenced
textcode block so it can be pasted directly into Moxfield or Archidekt bulk edit.
When either combo question is selected, the service calls the Commander Spellbook find-my-combos API before building the prompt:
- Returns up to 20 included combos (all pieces are in the deck) and up to 15 almost-included combos (exactly one card missing, within the deck's color identity).
- Each combo entry lists the card names, results, and up to 300 characters of instructions.
- Results are injected as a reference block in the prompt. ChatGPT is told to treat this data as authoritative.
- Results are cached for 30 minutes keyed by the sorted deck card list.
- API failures degrade gracefully — the analysis continues without combo data rather than failing.
The Deck Comparison page (/Deck/ChatGptDeckComparison) generates structured ChatGPT prompts for comparing two Commander decklists side by side. It lives under the ChatGPT dropdown alongside the Analysis page.
Paste two decklists (Moxfield/Archidekt URL or plain-text export) and select a Commander Bracket for each deck. Optionally name each deck — the service falls back to the commander name if left blank.
The service:
- Parses both decklists, resolving cards via Scryfall
POST /cards/collectionin batches of 75. - Falls back to per-card Scryfall search when a submitted name is an alternate-art or Universes Beyond printing that does not round-trip through the collection endpoint cleanly, then labels rendered decklists as
resolved name [printed as: submitted name]. - Queries Commander Spellbook for combos in each deck.
- Builds a comparison context document with bracket definitions, role counts (ramp, draw, interaction, wipes, recursion, closing power), mana curves, color identity, category overlap, and combo gaps.
- Generates a structured comparison prompt with
## TASK,## RULES,## COMPARISON AXES,## OUTPUT FORMAT, deck sections, and comparison context. The prompt instructs ChatGPT to produce both a human-readable comparison and a fencedjsonblock matching adeck_comparisonschema. - Generates a follow-up prompt for iterative refinement of the comparison.
Comparison axes include: commander role and game plan, speed and setup tempo, ramp, draw, spot interaction, sweepers, recursion, closing power (including combos), resilience, consistency, mana stability, commander dependence, table fit, major overlap/differences, and five concrete cards or packages that best explain the gap.
Paste ChatGPT's JSON response back into the form. The page parses the deck_comparison JSON and renders a formatted view with:
- Game plans and bracket labels for each deck
- Strengths and weaknesses per deck
- Key combos per deck
- Verdict panel: speed, resilience, interaction, mana consistency, closing power, and combo comparisons
- Shared themes and major differences
- Key gap cards or packages
- Recommended-for notes per deck
- Confidence notes (when ChatGPT flags uncertainty)
If you continue asking follow-up questions in the same ChatGPT thread, use 32-comparison-follow-up-prompt.txt to have ChatGPT revise the readable comparison and regenerate the full deck_comparison JSON block.
Check Save artifacts to disk to write generated files to:
Documents\DeckFlow\ChatGPT Deck Comparison\<timestamp>\
Files saved: 00-comparison-input-summary.txt, 10-deck-a-list.txt, 11-deck-b-list.txt, 20-comparison-context.txt, 30-comparison-prompt.txt, 32-comparison-follow-up-prompt.txt.
The prompt-templates/deck-comparison/ directory contains reference templates for compact and JSON-structured comparison prompts: all-in-one, competitive meta, matchup, quick verdict, JSON matchup, JSON strict return, and JSON tuning variants. See docs/deck-comparison-prompt-cheat-sheet.md for usage guidance.
The cEDH Meta Gap page (/chatgpt-cedh-meta-gap) generates a structured ChatGPT workflow for comparing your deck against recent EDH Top 16 lists for the same commander.
Paste a public Moxfield or Archidekt URL, or paste deck export text directly. You can optionally override the commander name. The page then queries EDH Top 16 using:
- Time period
- Sort by (
TOPorNEW) - Minimum event size
- Maximum standing
The service parses the submitted deck, removes sideboard and maybeboard cards, resolves the commander, fetches matching EDH Top 16 entries, and sorts them newest-first before display.
Select 1 to 3 EDH Top 16 reference decks and generate the prompt. The service builds:
30-meta-gap-prompt.txt31-meta-gap-schema.json
While building the prompt, the service also:
- Resolves submitted-deck and reference-deck card names through Scryfall so alternate print names and reskins are converted to canonical Oracle names where possible.
- Normalizes split and multi-face names to the base/front name for prompt display.
- Queries Commander Spellbook for your deck and for each selected reference deck, then injects combo summaries into the prompt.
- Caps the reference-deck count at 3 to keep the prompt size reasonable once decklists and combo references are included.
The prompt is structured with clear sections:
ROLEEVIDENCE PRIORITYRULESINPUT DATAANALYSIS TASKOUTPUT CONTRACTJSON SHAPE
ChatGPT is instructed to:
- Write a concise human-readable meta-gap summary first.
- Then return a fenced
jsonblock whose top-level object ismeta_gap. - Prefer the supplied Commander Spellbook combo evidence over weaker inferred combo reads when they conflict.
- Fill every field, using empty strings, zero values,
false, or empty arrays when evidence is missing.
Paste the raw JSON or fenced json block back into the page. The shared JSON extractor accepts fenced responses and ignores surrounding prose or extra trailing fence noise before parsing the payload. The page renders:
- Overview and readiness score
- Win lines
- Interaction
- Speed
- Mana efficiency
- Core convergence
- Missing staples
- Potential cuts
- Top 10 adds and cuts
Check Save artifacts to disk to write generated files to:
Documents\DeckFlow\ChatGPT cEDH Meta Gap\<commander-name>\<timestamp>\
Files saved: 00-input-summary.txt, 30-meta-gap-prompt.txt, 31-meta-gap-schema.json, 40-meta-gap-response.json (when a JSON response is pasted).
The Deck Sync page (/sync) compares two decks and generates the delta import needed to bring the target deck in line with the source.
Supported sync directions:
| Direction | Description |
|---|---|
| MoxfieldToArchidekt | Moxfield as source, Archidekt as target |
| ArchidektToMoxfield | Archidekt as source, Moxfield as target |
| MoxfieldToMoxfield | Compare two Moxfield decks |
| ArchidektToArchidekt | Compare two Archidekt decks |
For same-system comparisons, column labels update dynamically to reflect the source and target platform.
The Card Lookup page (/card-lookup) has two modes:
- Single Card (default; the only mode visible on mobile) — type a card name, get live Scryfall suggestions once you've entered 4+ characters, and picking a suggestion (or pressing Look Up) renders that card's Oracle text plus WOTC rulings inline via
GET /card-lookup/single. - Card List (desktop-only) — paste up to 100 card names and download the full Scryfall output as
.txt(POST /card-lookup/download) or structured.json(POST /card-lookup/download-json). The inline line editor with per-row autocomplete is still available for editing before downloading.
Under the hood all modes use the same ICardLookupService: the card collection is fetched via POST /cards/collection in batches of 75, and rulings are fetched per-card via GET /cards/{id}/rulings.
The Single Card result panel includes an "Ask a rules question about this card →" link that deep-links into /judge-questions?card=<name>.
The Ask a Judge page (/judge-questions) leads with a prominent link to the live community judge chat at chat.magicjudges.org/mtgrules — a 24/7 IRC channel (#magicjudges-rules on Libera.Chat) staffed by certified judges and rules experts. This is the authoritative path. When the page is opened with a ?card=<name> query parameter (e.g. from Card Lookup), it pre-formats a !CardName — opening message ready to copy into the chat.
A clearly labeled secondary ChatGPT prompt generator is provided below for casual play and quick second opinions. It carries a prominent disclaimer ("ChatGPT can be confidently wrong about MTG rules") and, if a reference card is supplied, fetches that card's Oracle text and rulings via GET /card-lookup/single and embeds them in the generated prompt. The prompt itself starts with the same warning so ChatGPT cannot bury it.
The Commander Categories page shows the Archidekt tags that appear most often on decks where a given card is listed as the commander. It reports what observers assigned, not what the app infers.
The AI Category Suggestions page supports multiple lookup modes:
CachedDataReferenceDeckScryfallTaggerAll
Current behavior:
ReferenceDeckreads exact categories from a supplied Archidekt deck URL or pasted Archidekt text.CachedDataruns a short local cache sweep, then reads category hits from the local Archidekt-backed store.ScryfallTaggerreturns oracle-tag style suggestions from Scryfall Tagger.Allcombines the cached-store path and tagger path, with EDHREC as a fallback when no other source returns anything.
The page also exposes a background Archidekt harvest button so the local category store can be refreshed while the rest of the UI remains usable.
- Run
dotnet run --project DeckFlow.CLI -- archidekt-cache --minutes 5to keep the local cache fed with the latest public decks. - The CLI runs a dedicated cache session that respects rate limits via Polly, records skips for noisy decks, and persists card/category observations to
artifacts/category-knowledge.db. - The web cache service reuses the same session logic for on-demand refreshes from the MVC UI.
- The AI Category Suggestions page can start a 5-minute Archidekt harvest as a background job. The rest of the site stays usable while it runs, only one harvest is allowed at a time, and a local browser notification/banner appears when the job completes.
- Background harvest state is polled from the web app, and the start button stays disabled while the job is queued or running.
- The cache session now stays alive for the requested harvest window even when the queue runs dry, and it retries transient recent-page fetch failures instead of ending the whole job early.
- Basic card type categories (Creature, Instant, Sorcery, Enchantment, Artifact, Planeswalker, Battle) are filtered out of cache suggestions.
Swagger UI is available at /swagger when running in Development mode.
POST /api/suggestions/card
Content-Type: application/json
{
"mode": "CachedData",
"archidektInputSource": "PublicUrl",
"archidektUrl": "",
"archidektText": "",
"cardName": "Guardian Project"
}
POST /api/suggestions/commander
Content-Type: application/json
{
"commanderName": "Bello, Bard of the Brambles"
}
Start a background harvest:
POST /api/archidekt-cache-jobs
Content-Type: application/json
{
"durationSeconds": 300
}
Poll a specific job:
GET /api/archidekt-cache-jobs/{jobId}
Get the currently active job, if any:
GET /api/archidekt-cache-jobs/active
curl -X POST http://localhost:5000/api/suggestions/card \
-H "Content-Type: application/json" \
-d '{"mode":"CachedData","archidektInputSource":"PublicUrl","cardName":"Guardian Project"}'
curl -X POST http://localhost:5000/api/suggestions/commander \
-H "Content-Type: application/json" \
-d '{"commanderName":"Bello, Bard of the Brambles"}'- Scryfall is used for card-name autocomplete, commander autocomplete, the Card Lookup page, card reference resolution in the ChatGPT Analysis workflow, and async set catalog loading.
- All Scryfall clients send a real
User-Agent, an explicitAcceptheader, and usehttps. - Card lookup uses
POST /cards/collectionin batches of 75 identifiers. - The Card Lookup page is capped at 100 non-empty input lines per submission (at most two
cards/collectionrequests plus onecards/{id}/rulingsrequest per unique resolved card, all throttled). - The ChatGPT workflow uses the same batch endpoint to resolve authoritative Oracle text for all deck cards.
- The set catalog is fetched via
GET /setsand cached in memory for 6 hours; the web UI loads it asynchronously via/api/set-options.
- Scryfall enforces a soft cap of 10 requests per second at the Cloudflare edge (no proactive
X-RateLimit-*headers on 200 responses; onlyRetry-Afteron 429). ChatGptDeckPacketServicethrottles all Scryfall calls to ~110ms apart (≈9 req/s) via a process-wide semaphore so batched collection lookups plus per-card fallback searches stay under the cap.- On a 429 the wrapper reads
Retry-Afterand retries once if the cooldown is ≤5 seconds; longer cooldowns surface as a friendly "Scryfall returned HTTP 429. Try again shortly." error instead of being misattributed to card/commander validation. - The CLI ships a diagnostic
scryfall-probecommand that calls Scryfall and dumps status, headers, and body — useful for reproducing rate-limit responses. Example:dotnet run --project DeckFlow.CLI -- scryfall-probe --endpoint random --repeat 25(intentionally triggers 429).
dotnet run --project DeckFlow.CLI -- compare \
--moxfield my.deck --archidekt other.deck --out diff.txt
dotnet run --project DeckFlow.CLI -- archidekt-cache --minutes 10
dotnet run --project DeckFlow.CLI -- category-find \
--card "Guardian Project" --cache-seconds 20The DeckFlow Bridge Chrome/Edge extension lets DeckFlow fetch Moxfield decks through your logged-in browser session when direct server-side requests fail.
See browser-extensions/deckflow-bridge/README.md for load-unpacked installation instructions, or open /extension-install.html in the running app to download the current ZIP package.
- Core logic is isolated in
DeckFlow.Core(diff engine, export helpers, parsers, integration clients, knowledge store). - Web and CLI layers orchestrate requests and rely on DI to resolve shared services.
- Importers for Archidekt and Moxfield implement typed interfaces (
IMoxfieldDeckImporter,IArchidektDeckImporter) for easy test substitution. ChatGptDeckPacketServiceparallelizes independent fetches (banned-list, set-packet, Commander Spellbook) usingTask.WhenAllto reduce total build time.ChatGptDeckComparisonServiceparses two decklists, resolves cards via Scryfall, queries Commander Spellbook for both decks, derives comparison context (role counts, mana curves, combo gaps), and generates structured ChatGPT prompts with a JSON output schema.CommanderSpellbookServicecaches results for 30 minutes and degrades gracefully on API failure.CategoryKnowledgeStorepersists observations to SQLite (artifacts/category-knowledge.db) and is shared between the web app and CLI.
- The floating back-to-top control uses inline SVG in the shared layout, not the old
chevron-up.pngbitmap. - The back-to-top button stays hidden while the page is already near the top and appears only after the user scrolls down.
A persistent theme picker in the shared layout lets users switch between visual themes. The selection is stored in localStorage and applied on page load. Available themes:
- Default — the base site stylesheet
- Abzan (WBG), Bant (GWU), Esper (WUB), Grixis (UBR), Jeskai (URW), Jund (BRG), Mardu (RWB), Naya (RGW), Sultai (BGU), Temur (GUR) — color-shard/wedge-inspired palettes
- Nyx — enchantment-themed dark palette
- Planeswalker Dark — dark-mode palette
- Commander Table — warm tabletop-inspired palette