Migrate Gitea ops to MCP, update Paper Dynasty skill, sync plugins
- CLAUDE.md + commit-push-pr: prefer gitea-mcp over tea CLI - Paper Dynasty: updated api_client, cli, distribute_packs - New skill: resume-tailoring - Plugins: updated marketplaces, blocklist, install counts - Settings and MCP config updates Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
7f120f8c5c
commit
8642bb539a
16
.mcp.json
16
.mcp.json
@ -5,19 +5,5 @@
|
||||
"type": "stdio",
|
||||
"args": [],
|
||||
"env": {}
|
||||
},
|
||||
"n8n-mcp": {
|
||||
"command": "npx",
|
||||
"type": "stdio",
|
||||
"args": ["n8n-mcp"],
|
||||
"env": {
|
||||
"MCP_MODE": "stdio",
|
||||
"N8N_API_URL": "http://10.10.0.210:5678",
|
||||
"N8N_API_KEY": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIwNGQ1MTNhYi1lOGI1LTQxZjktYmNjNi05MTM1MzgyYzQ0YWEiLCJpc3MiOiJuOG4iLCJhdWQiOiJwdWJsaWMtYXBpIiwiaWF0IjoxNzcxNTU3ODMwfQ.PF6AatWZh8hwyUydT8UsJ16ML61XFTixj2IvuP9MRzo",
|
||||
"N8N_MCP_TELEMETRY_DISABLED": "true",
|
||||
"DISABLE_CONSOLE_OUTPUT": "true",
|
||||
"LOG_LEVEL": "error"
|
||||
}
|
||||
}
|
||||
}
|
||||
} }
|
||||
}
|
||||
|
||||
12
CLAUDE.md
12
CLAUDE.md
@ -14,12 +14,12 @@ Automatic loads are NOT enough — Read loads required CLAUDE.md context along t
|
||||
- Applies to: git commit, git add, git tag, git push, deploy scripts
|
||||
|
||||
## Gitea Operations
|
||||
**ALWAYS use `tea` CLI for Gitea.** Never use `gh api --hostname`.
|
||||
- Authenticated: `cal@homelab` (https://git.manticorum.com)
|
||||
- **Always pass `--repo owner/name`** — tea can't auto-detect the repo from git remotes and fails with "path segment is empty"
|
||||
- Common: `tea repos list`, `tea pulls list --repo cal/repo`, `tea issues list --repo cal/repo`
|
||||
- Create PR: `tea pulls create --repo cal/repo --head <branch> --base main --title "Title" --description "Desc"`
|
||||
- Common repos: cal/major-domo-v2, cal/major-domo-database, cal/major-domo-bot, cal/paper-dynasty, cal/paper-dynasty-database
|
||||
**Prefer the `gitea-mcp` MCP server** for all Gitea operations (PRs, issues, branches, labels, releases, Actions).
|
||||
- MCP tools use `owner` + `repo` params (e.g. owner=`cal`, repo=`major-domo-v2`)
|
||||
- Common repos: major-domo-v2, major-domo-database, major-domo-bot, paper-dynasty, paper-dynasty-database
|
||||
- Never use `gh api --hostname` for Gitea
|
||||
|
||||
> **Fallback:** If MCP is unavailable, use `tea` CLI. Always pass `--repo owner/name`.
|
||||
|
||||
## Tech Preferences
|
||||
- Python with uv for package/environment management
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
---
|
||||
allowed-tools: Bash(git checkout:*), Bash(git add:*), Bash(git status:*), Bash(git push:*), Bash(git commit:*), Bash(git branch:*), Bash(git remote:*), Bash(git symbolic-ref:*), Bash(git log:*), Bash(gh pr create:*), Bash(tea pulls create:*)
|
||||
allowed-tools: Bash(git checkout:*), Bash(git add:*), Bash(git status:*), Bash(git push:*), Bash(git commit:*), Bash(git branch:*), Bash(git remote:*), Bash(git symbolic-ref:*), Bash(git log:*), Bash(gh pr create:*), mcp__gitea-mcp__create_pull_request
|
||||
description: Commit, push, and open a PR
|
||||
---
|
||||
|
||||
@ -25,7 +25,7 @@ Based on the above changes:
|
||||
6. Push the branch to origin with `-u` flag
|
||||
7. Create a pull request:
|
||||
- If remote URL contains `github.com` → use `gh pr create --base <default-branch> --title "Title" --body "..."`
|
||||
- If remote URL contains `git.manticorum.com` or other Gitea host → use `tea pulls create --head <branch> --base <default-branch> --title "Title" --description "..."`
|
||||
- If remote URL contains `git.manticorum.com` or other Gitea host → use `mcp__gitea-mcp__create_pull_request` with `owner`, `repo`, `title`, `body`, `head`, `base`
|
||||
8. Include a summary section and test plan in the PR body
|
||||
9. Return the PR URL
|
||||
|
||||
|
||||
1
mcp-needs-auth-cache.json
Normal file
1
mcp-needs-auth-cache.json
Normal file
@ -0,0 +1 @@
|
||||
{"claude.ai Google Calendar":{"timestamp":1772062089205},"claude.ai Gmail":{"timestamp":1772062089218}}
|
||||
@ -1,5 +1,5 @@
|
||||
{
|
||||
"fetchedAt": "2026-02-20T04:07:24.860Z",
|
||||
"fetchedAt": "2026-02-25T22:40:22.318Z",
|
||||
"plugins": [
|
||||
{
|
||||
"plugin": "code-review@claude-plugins-official",
|
||||
|
||||
@ -1,230 +1,234 @@
|
||||
{
|
||||
"version": 1,
|
||||
"fetchedAt": "2026-02-19T04:03:05.754Z",
|
||||
"fetchedAt": "2026-02-22T05:53:29.726Z",
|
||||
"counts": [
|
||||
{
|
||||
"plugin": "frontend-design@claude-plugins-official",
|
||||
"unique_installs": 183759
|
||||
"unique_installs": 211218
|
||||
},
|
||||
{
|
||||
"plugin": "context7@claude-plugins-official",
|
||||
"unique_installs": 115503
|
||||
"unique_installs": 127061
|
||||
},
|
||||
{
|
||||
"plugin": "code-review@claude-plugins-official",
|
||||
"unique_installs": 91933
|
||||
},
|
||||
{
|
||||
"plugin": "github@claude-plugins-official",
|
||||
"unique_installs": 83345
|
||||
},
|
||||
{
|
||||
"plugin": "feature-dev@claude-plugins-official",
|
||||
"unique_installs": 80588
|
||||
},
|
||||
{
|
||||
"plugin": "code-simplifier@claude-plugins-official",
|
||||
"unique_installs": 73439
|
||||
"unique_installs": 103962
|
||||
},
|
||||
{
|
||||
"plugin": "superpowers@claude-plugins-official",
|
||||
"unique_installs": 72147
|
||||
"unique_installs": 92356
|
||||
},
|
||||
{
|
||||
"plugin": "github@claude-plugins-official",
|
||||
"unique_installs": 92248
|
||||
},
|
||||
{
|
||||
"plugin": "feature-dev@claude-plugins-official",
|
||||
"unique_installs": 89510
|
||||
},
|
||||
{
|
||||
"plugin": "code-simplifier@claude-plugins-official",
|
||||
"unique_installs": 84564
|
||||
},
|
||||
{
|
||||
"plugin": "ralph-loop@claude-plugins-official",
|
||||
"unique_installs": 67562
|
||||
"unique_installs": 74996
|
||||
},
|
||||
{
|
||||
"plugin": "typescript-lsp@claude-plugins-official",
|
||||
"unique_installs": 61765
|
||||
"unique_installs": 69574
|
||||
},
|
||||
{
|
||||
"plugin": "playwright@claude-plugins-official",
|
||||
"unique_installs": 60378
|
||||
"unique_installs": 69512
|
||||
},
|
||||
{
|
||||
"plugin": "commit-commands@claude-plugins-official",
|
||||
"unique_installs": 52976
|
||||
},
|
||||
{
|
||||
"plugin": "serena@claude-plugins-official",
|
||||
"unique_installs": 47364
|
||||
"unique_installs": 58414
|
||||
},
|
||||
{
|
||||
"plugin": "security-guidance@claude-plugins-official",
|
||||
"unique_installs": 47274
|
||||
"unique_installs": 53585
|
||||
},
|
||||
{
|
||||
"plugin": "serena@claude-plugins-official",
|
||||
"unique_installs": 49897
|
||||
},
|
||||
{
|
||||
"plugin": "pr-review-toolkit@claude-plugins-official",
|
||||
"unique_installs": 35736
|
||||
},
|
||||
{
|
||||
"plugin": "pyright-lsp@claude-plugins-official",
|
||||
"unique_installs": 32249
|
||||
},
|
||||
{
|
||||
"plugin": "figma@claude-plugins-official",
|
||||
"unique_installs": 31936
|
||||
},
|
||||
{
|
||||
"plugin": "supabase@claude-plugins-official",
|
||||
"unique_installs": 31746
|
||||
"unique_installs": 39827
|
||||
},
|
||||
{
|
||||
"plugin": "claude-md-management@claude-plugins-official",
|
||||
"unique_installs": 28868
|
||||
"unique_installs": 39554
|
||||
},
|
||||
{
|
||||
"plugin": "figma@claude-plugins-official",
|
||||
"unique_installs": 36609
|
||||
},
|
||||
{
|
||||
"plugin": "pyright-lsp@claude-plugins-official",
|
||||
"unique_installs": 35872
|
||||
},
|
||||
{
|
||||
"plugin": "supabase@claude-plugins-official",
|
||||
"unique_installs": 34374
|
||||
},
|
||||
{
|
||||
"plugin": "agent-sdk-dev@claude-plugins-official",
|
||||
"unique_installs": 27597
|
||||
},
|
||||
{
|
||||
"plugin": "ralph-wiggum@claude-plugins-official",
|
||||
"unique_installs": 27127
|
||||
"unique_installs": 29685
|
||||
},
|
||||
{
|
||||
"plugin": "atlassian@claude-plugins-official",
|
||||
"unique_installs": 26859
|
||||
"unique_installs": 29214
|
||||
},
|
||||
{
|
||||
"plugin": "plugin-dev@claude-plugins-official",
|
||||
"unique_installs": 21681
|
||||
},
|
||||
{
|
||||
"plugin": "explanatory-output-style@claude-plugins-official",
|
||||
"unique_installs": 21571
|
||||
},
|
||||
{
|
||||
"plugin": "greptile@claude-plugins-official",
|
||||
"unique_installs": 20163
|
||||
"plugin": "ralph-wiggum@claude-plugins-official",
|
||||
"unique_installs": 27164
|
||||
},
|
||||
{
|
||||
"plugin": "claude-code-setup@claude-plugins-official",
|
||||
"unique_installs": 19541
|
||||
"unique_installs": 25066
|
||||
},
|
||||
{
|
||||
"plugin": "plugin-dev@claude-plugins-official",
|
||||
"unique_installs": 24060
|
||||
},
|
||||
{
|
||||
"plugin": "explanatory-output-style@claude-plugins-official",
|
||||
"unique_installs": 23970
|
||||
},
|
||||
{
|
||||
"plugin": "greptile@claude-plugins-official",
|
||||
"unique_installs": 22699
|
||||
},
|
||||
{
|
||||
"plugin": "Notion@claude-plugins-official",
|
||||
"unique_installs": 18738
|
||||
"unique_installs": 20513
|
||||
},
|
||||
{
|
||||
"plugin": "hookify@claude-plugins-official",
|
||||
"unique_installs": 18502
|
||||
},
|
||||
{
|
||||
"plugin": "linear@claude-plugins-official",
|
||||
"unique_installs": 15749
|
||||
"unique_installs": 20396
|
||||
},
|
||||
{
|
||||
"plugin": "vercel@claude-plugins-official",
|
||||
"unique_installs": 15454
|
||||
"unique_installs": 17352
|
||||
},
|
||||
{
|
||||
"plugin": "linear@claude-plugins-official",
|
||||
"unique_installs": 17226
|
||||
},
|
||||
{
|
||||
"plugin": "learning-output-style@claude-plugins-official",
|
||||
"unique_installs": 14537
|
||||
"unique_installs": 16023
|
||||
},
|
||||
{
|
||||
"plugin": "slack@claude-plugins-official",
|
||||
"unique_installs": 12797
|
||||
"unique_installs": 14454
|
||||
},
|
||||
{
|
||||
"plugin": "sentry@claude-plugins-official",
|
||||
"unique_installs": 12586
|
||||
"unique_installs": 13925
|
||||
},
|
||||
{
|
||||
"plugin": "gopls-lsp@claude-plugins-official",
|
||||
"unique_installs": 12195
|
||||
"unique_installs": 13515
|
||||
},
|
||||
{
|
||||
"plugin": "csharp-lsp@claude-plugins-official",
|
||||
"unique_installs": 11692
|
||||
"unique_installs": 13187
|
||||
},
|
||||
{
|
||||
"plugin": "gitlab@claude-plugins-official",
|
||||
"unique_installs": 10720
|
||||
},
|
||||
{
|
||||
"plugin": "rust-analyzer-lsp@claude-plugins-official",
|
||||
"unique_installs": 10634
|
||||
"unique_installs": 11947
|
||||
},
|
||||
{
|
||||
"plugin": "stripe@claude-plugins-official",
|
||||
"unique_installs": 10475
|
||||
"unique_installs": 11832
|
||||
},
|
||||
{
|
||||
"plugin": "laravel-boost@claude-plugins-official",
|
||||
"unique_installs": 9552
|
||||
},
|
||||
{
|
||||
"plugin": "php-lsp@claude-plugins-official",
|
||||
"unique_installs": 9213
|
||||
},
|
||||
{
|
||||
"plugin": "jdtls-lsp@claude-plugins-official",
|
||||
"unique_installs": 9152
|
||||
"plugin": "rust-analyzer-lsp@claude-plugins-official",
|
||||
"unique_installs": 11762
|
||||
},
|
||||
{
|
||||
"plugin": "playground@claude-plugins-official",
|
||||
"unique_installs": 8414
|
||||
"unique_installs": 11389
|
||||
},
|
||||
{
|
||||
"plugin": "php-lsp@claude-plugins-official",
|
||||
"unique_installs": 10298
|
||||
},
|
||||
{
|
||||
"plugin": "laravel-boost@claude-plugins-official",
|
||||
"unique_installs": 10204
|
||||
},
|
||||
{
|
||||
"plugin": "jdtls-lsp@claude-plugins-official",
|
||||
"unique_installs": 10186
|
||||
},
|
||||
{
|
||||
"plugin": "clangd-lsp@claude-plugins-official",
|
||||
"unique_installs": 8112
|
||||
},
|
||||
{
|
||||
"plugin": "swift-lsp@claude-plugins-official",
|
||||
"unique_installs": 7732
|
||||
},
|
||||
{
|
||||
"plugin": "firebase@claude-plugins-official",
|
||||
"unique_installs": 7707
|
||||
"unique_installs": 8996
|
||||
},
|
||||
{
|
||||
"plugin": "huggingface-skills@claude-plugins-official",
|
||||
"unique_installs": 7615
|
||||
"unique_installs": 8946
|
||||
},
|
||||
{
|
||||
"plugin": "firebase@claude-plugins-official",
|
||||
"unique_installs": 8581
|
||||
},
|
||||
{
|
||||
"plugin": "swift-lsp@claude-plugins-official",
|
||||
"unique_installs": 8511
|
||||
},
|
||||
{
|
||||
"plugin": "kotlin-lsp@claude-plugins-official",
|
||||
"unique_installs": 5312
|
||||
},
|
||||
{
|
||||
"plugin": "lua-lsp@claude-plugins-official",
|
||||
"unique_installs": 4824
|
||||
"unique_installs": 6192
|
||||
},
|
||||
{
|
||||
"plugin": "coderabbit@claude-plugins-official",
|
||||
"unique_installs": 4356
|
||||
"unique_installs": 5806
|
||||
},
|
||||
{
|
||||
"plugin": "asana@claude-plugins-official",
|
||||
"unique_installs": 3331
|
||||
"plugin": "lua-lsp@claude-plugins-official",
|
||||
"unique_installs": 5368
|
||||
},
|
||||
{
|
||||
"plugin": "circleback@claude-plugins-official",
|
||||
"unique_installs": 3253
|
||||
"unique_installs": 3806
|
||||
},
|
||||
{
|
||||
"plugin": "asana@claude-plugins-official",
|
||||
"unique_installs": 3804
|
||||
},
|
||||
{
|
||||
"plugin": "pinecone@claude-plugins-official",
|
||||
"unique_installs": 2999
|
||||
"unique_installs": 3449
|
||||
},
|
||||
{
|
||||
"plugin": "firecrawl@claude-plugins-official",
|
||||
"unique_installs": 3336
|
||||
},
|
||||
{
|
||||
"plugin": "posthog@claude-plugins-official",
|
||||
"unique_installs": 2827
|
||||
},
|
||||
{
|
||||
"plugin": "claude-opus-4-5-migration@claude-plugins-official",
|
||||
"unique_installs": 2714
|
||||
},
|
||||
{
|
||||
"plugin": "posthog@claude-plugins-official",
|
||||
"unique_installs": 2258
|
||||
},
|
||||
{
|
||||
"plugin": "firecrawl@claude-plugins-official",
|
||||
"unique_installs": 1486
|
||||
},
|
||||
{
|
||||
"plugin": "sonatype-guide@claude-plugins-official",
|
||||
"unique_installs": 931
|
||||
"unique_installs": 2111
|
||||
},
|
||||
{
|
||||
"plugin": "skill-creator@claude-plugins-official",
|
||||
"unique_installs": 327
|
||||
},
|
||||
{
|
||||
"plugin": "figma-mcp@claude-plugins-official",
|
||||
"unique_installs": 99
|
||||
"unique_installs": 101
|
||||
},
|
||||
{
|
||||
"plugin": "artifact@claude-plugins-official",
|
||||
@ -235,7 +239,11 @@
|
||||
"unique_installs": 31
|
||||
},
|
||||
{
|
||||
"plugin": "pm@claude-plugins-official",
|
||||
"plugin": "document-skills@claude-plugins-official",
|
||||
"unique_installs": 2
|
||||
},
|
||||
{
|
||||
"plugin": "agent-browser@claude-plugins-official",
|
||||
"unique_installs": 2
|
||||
},
|
||||
{
|
||||
@ -243,187 +251,11 @@
|
||||
"unique_installs": 2
|
||||
},
|
||||
{
|
||||
"plugin": "codex-skills@claude-plugins-official",
|
||||
"unique_installs": 1
|
||||
"plugin": "pm@claude-plugins-official",
|
||||
"unique_installs": 2
|
||||
},
|
||||
{
|
||||
"plugin": "dev-workflow@claude-plugins-official",
|
||||
"unique_installs": 1
|
||||
},
|
||||
{
|
||||
"plugin": "datadog@claude-plugins-official",
|
||||
"unique_installs": 1
|
||||
},
|
||||
{
|
||||
"plugin": "omnisharp-lsp@claude-plugins-official",
|
||||
"unique_installs": 1
|
||||
},
|
||||
{
|
||||
"plugin": "perlnavigator-lsp@claude-plugins-official",
|
||||
"unique_installs": 1
|
||||
},
|
||||
{
|
||||
"plugin": "bun-typescript@claude-plugins-official",
|
||||
"unique_installs": 1
|
||||
},
|
||||
{
|
||||
"plugin": "typescript-native-lsp@claude-plugins-official",
|
||||
"unique_installs": 1
|
||||
},
|
||||
{
|
||||
"plugin": "docs-search-tool@claude-plugins-official",
|
||||
"unique_installs": 1
|
||||
},
|
||||
{
|
||||
"plugin": "feature-ears@claude-plugins-official",
|
||||
"unique_installs": 1
|
||||
},
|
||||
{
|
||||
"plugin": "n8n@claude-plugins-official",
|
||||
"unique_installs": 1
|
||||
},
|
||||
{
|
||||
"plugin": "lorikeet-qa@claude-plugins-official",
|
||||
"unique_installs": 1
|
||||
},
|
||||
{
|
||||
"plugin": "vectorhub-memory@claude-plugins-official",
|
||||
"unique_installs": 1
|
||||
},
|
||||
{
|
||||
"plugin": "project-collaboration-system@claude-plugins-official",
|
||||
"unique_installs": 1
|
||||
},
|
||||
{
|
||||
"plugin": "openspec@claude-plugins-official",
|
||||
"unique_installs": 1
|
||||
},
|
||||
{
|
||||
"plugin": "claude-memory@claude-plugins-official",
|
||||
"unique_installs": 1
|
||||
},
|
||||
{
|
||||
"plugin": "hardworking@claude-plugins-official",
|
||||
"unique_installs": 1
|
||||
},
|
||||
{
|
||||
"plugin": "terraform-ls@claude-plugins-official",
|
||||
"unique_installs": 1
|
||||
},
|
||||
{
|
||||
"plugin": "design-principles@claude-plugins-official",
|
||||
"unique_installs": 1
|
||||
},
|
||||
{
|
||||
"plugin": "creative-music-output-style@claude-plugins-official",
|
||||
"unique_installs": 1
|
||||
},
|
||||
{
|
||||
"plugin": "freshservice@claude-plugins-official",
|
||||
"unique_installs": 1
|
||||
},
|
||||
{
|
||||
"plugin": "n8n-skills@claude-plugins-official",
|
||||
"unique_installs": 1
|
||||
},
|
||||
{
|
||||
"plugin": "context@claude-plugins-official",
|
||||
"unique_installs": 1
|
||||
},
|
||||
{
|
||||
"plugin": "dj-content-creator@claude-plugins-official",
|
||||
"unique_installs": 1
|
||||
},
|
||||
{
|
||||
"plugin": "csharp-roslyn-lsp@claude-plugins-official",
|
||||
"unique_installs": 1
|
||||
},
|
||||
{
|
||||
"plugin": "memory-agent@claude-plugins-official",
|
||||
"unique_installs": 1
|
||||
},
|
||||
{
|
||||
"plugin": "my-time-plugin@claude-plugins-official",
|
||||
"unique_installs": 1
|
||||
},
|
||||
{
|
||||
"plugin": "miro@claude-plugins-official",
|
||||
"unique_installs": 1
|
||||
},
|
||||
{
|
||||
"plugin": "prototyper@claude-plugins-official",
|
||||
"unique_installs": 1
|
||||
},
|
||||
{
|
||||
"plugin": "lean-lsp@claude-plugins-official",
|
||||
"unique_installs": 1
|
||||
},
|
||||
{
|
||||
"plugin": "agent-browser@claude-plugins-official",
|
||||
"unique_installs": 1
|
||||
},
|
||||
{
|
||||
"plugin": "gdscript-lsp@claude-plugins-official",
|
||||
"unique_installs": 1
|
||||
},
|
||||
{
|
||||
"plugin": "dev-sandbox@claude-plugins-official",
|
||||
"unique_installs": 1
|
||||
},
|
||||
{
|
||||
"plugin": "ccpm@claude-plugins-official",
|
||||
"unique_installs": 1
|
||||
},
|
||||
{
|
||||
"plugin": "gitlab-mr-review@claude-plugins-official",
|
||||
"unique_installs": 1
|
||||
},
|
||||
{
|
||||
"plugin": "review-submission@claude-plugins-official",
|
||||
"unique_installs": 1
|
||||
},
|
||||
{
|
||||
"plugin": "document-skills@claude-plugins-official",
|
||||
"unique_installs": 1
|
||||
},
|
||||
{
|
||||
"plugin": "forge-security@claude-plugins-official",
|
||||
"unique_installs": 1
|
||||
},
|
||||
{
|
||||
"plugin": "ocpm@claude-plugins-official",
|
||||
"unique_installs": 1
|
||||
},
|
||||
{
|
||||
"plugin": "pyrefly-lsp@claude-plugins-official",
|
||||
"unique_installs": 1
|
||||
},
|
||||
{
|
||||
"plugin": "hosts-db@claude-plugins-official",
|
||||
"unique_installs": 1
|
||||
},
|
||||
{
|
||||
"plugin": "test-automation-generator@claude-plugins-official",
|
||||
"unique_installs": 1
|
||||
},
|
||||
{
|
||||
"plugin": "frontend-lab@claude-plugins-official",
|
||||
"unique_installs": 1
|
||||
},
|
||||
{
|
||||
"plugin": "dune@claude-plugins-official",
|
||||
"unique_installs": 1
|
||||
},
|
||||
{
|
||||
"plugin": "dokploy@claude-plugins-official",
|
||||
"unique_installs": 1
|
||||
},
|
||||
{
|
||||
"plugin": "monday@claude-plugins-official",
|
||||
"unique_installs": 1
|
||||
},
|
||||
{
|
||||
"plugin": "microsoft-learn@claude-plugins-official",
|
||||
"plugin": "hello-world@claude-plugins-official",
|
||||
"unique_installs": 1
|
||||
},
|
||||
{
|
||||
@ -431,11 +263,43 @@
|
||||
"unique_installs": 1
|
||||
},
|
||||
{
|
||||
"plugin": "backend-specialist@claude-plugins-official",
|
||||
"plugin": "ocpm@claude-plugins-official",
|
||||
"unique_installs": 1
|
||||
},
|
||||
{
|
||||
"plugin": "autonomous-loop@claude-plugins-official",
|
||||
"plugin": "omnisharp-lsp@claude-plugins-official",
|
||||
"unique_installs": 1
|
||||
},
|
||||
{
|
||||
"plugin": "dev-sandbox@claude-plugins-official",
|
||||
"unique_installs": 1
|
||||
},
|
||||
{
|
||||
"plugin": "feature-ears@claude-plugins-official",
|
||||
"unique_installs": 1
|
||||
},
|
||||
{
|
||||
"plugin": "context@claude-plugins-official",
|
||||
"unique_installs": 1
|
||||
},
|
||||
{
|
||||
"plugin": "creative-music-output-style@claude-plugins-official",
|
||||
"unique_installs": 1
|
||||
},
|
||||
{
|
||||
"plugin": "latex2cn@claude-plugins-official",
|
||||
"unique_installs": 1
|
||||
},
|
||||
{
|
||||
"plugin": "pdf2latex@claude-plugins-official",
|
||||
"unique_installs": 1
|
||||
},
|
||||
{
|
||||
"plugin": "n8n-skills@claude-plugins-official",
|
||||
"unique_installs": 1
|
||||
},
|
||||
{
|
||||
"plugin": "user-journey-analysis@claude-plugins-official",
|
||||
"unique_installs": 1
|
||||
},
|
||||
{
|
||||
@ -443,43 +307,235 @@
|
||||
"unique_installs": 1
|
||||
},
|
||||
{
|
||||
"plugin": "it-triage-system@claude-plugins-official",
|
||||
"plugin": "memory-agent@claude-plugins-official",
|
||||
"unique_installs": 1
|
||||
},
|
||||
{
|
||||
"plugin": "airtable@claude-plugins-official",
|
||||
"plugin": "lean-lsp@claude-plugins-official",
|
||||
"unique_installs": 1
|
||||
},
|
||||
{
|
||||
"plugin": "claude-rules-generator@claude-plugins-official",
|
||||
"plugin": "forge-security@claude-plugins-official",
|
||||
"unique_installs": 1
|
||||
},
|
||||
{
|
||||
"plugin": "beast-plan@claude-plugins-official",
|
||||
"plugin": "typescript-native-lsp@claude-plugins-official",
|
||||
"unique_installs": 1
|
||||
},
|
||||
{
|
||||
"plugin": "aws-diagram@claude-plugins-official",
|
||||
"unique_installs": 1
|
||||
},
|
||||
{
|
||||
"plugin": "freshservice@claude-plugins-official",
|
||||
"unique_installs": 1
|
||||
},
|
||||
{
|
||||
"plugin": "design-principles@claude-plugins-official",
|
||||
"unique_installs": 1
|
||||
},
|
||||
{
|
||||
"plugin": "project-collaboration-system@claude-plugins-official",
|
||||
"unique_installs": 1
|
||||
},
|
||||
{
|
||||
"plugin": "n8n@claude-plugins-official",
|
||||
"unique_installs": 1
|
||||
},
|
||||
{
|
||||
"plugin": "rs-commands@claude-plugins-official",
|
||||
"unique_installs": 1
|
||||
},
|
||||
{
|
||||
"plugin": "docs-search-tool@claude-plugins-official",
|
||||
"unique_installs": 1
|
||||
},
|
||||
{
|
||||
"plugin": "dune@claude-plugins-official",
|
||||
"unique_installs": 1
|
||||
},
|
||||
{
|
||||
"plugin": "perlnavigator-lsp@claude-plugins-official",
|
||||
"unique_installs": 1
|
||||
},
|
||||
{
|
||||
"plugin": "claude-memory@claude-plugins-official",
|
||||
"unique_installs": 1
|
||||
},
|
||||
{
|
||||
"plugin": "agent-teams@claude-plugins-official",
|
||||
"unique_installs": 1
|
||||
},
|
||||
{
|
||||
"plugin": "vertical-builder@claude-plugins-official",
|
||||
"unique_installs": 1
|
||||
},
|
||||
{
|
||||
"plugin": "gemini-consult@claude-plugins-official",
|
||||
"unique_installs": 1
|
||||
},
|
||||
{
|
||||
"plugin": "codeceptjs-e2e-tests@claude-plugins-official",
|
||||
"unique_installs": 1
|
||||
},
|
||||
{
|
||||
"plugin": "csharp-roslyn-lsp@claude-plugins-official",
|
||||
"unique_installs": 1
|
||||
},
|
||||
{
|
||||
"plugin": "dev-workflow@claude-plugins-official",
|
||||
"unique_installs": 1
|
||||
},
|
||||
{
|
||||
"plugin": "dokploy@claude-plugins-official",
|
||||
"unique_installs": 1
|
||||
},
|
||||
{
|
||||
"plugin": "autonomous-loop@claude-plugins-official",
|
||||
"unique_installs": 1
|
||||
},
|
||||
{
|
||||
"plugin": "spec-writer@claude-plugins-official",
|
||||
"unique_installs": 1
|
||||
},
|
||||
{
|
||||
"plugin": "my-time-plugin@claude-plugins-official",
|
||||
"unique_installs": 1
|
||||
},
|
||||
{
|
||||
"plugin": "hardworking@claude-plugins-official",
|
||||
"unique_installs": 1
|
||||
},
|
||||
{
|
||||
"plugin": "monday@claude-plugins-official",
|
||||
"unique_installs": 1
|
||||
},
|
||||
{
|
||||
"plugin": "lorikeet-qa@claude-plugins-official",
|
||||
"unique_installs": 1
|
||||
},
|
||||
{
|
||||
"plugin": "miro@claude-plugins-official",
|
||||
"unique_installs": 1
|
||||
},
|
||||
{
|
||||
"plugin": "gdscript-lsp@claude-plugins-official",
|
||||
"unique_installs": 1
|
||||
},
|
||||
{
|
||||
"plugin": "datadog@claude-plugins-official",
|
||||
"unique_installs": 1
|
||||
},
|
||||
{
|
||||
"plugin": "airtable@claude-plugins-official",
|
||||
"unique_installs": 1
|
||||
},
|
||||
{
|
||||
"plugin": "beast-plan@claude-plugins-official",
|
||||
"unique_installs": 1
|
||||
},
|
||||
{
|
||||
"plugin": "vectorhub-memory@claude-plugins-official",
|
||||
"unique_installs": 1
|
||||
},
|
||||
{
|
||||
"plugin": "pyrefly-lsp@claude-plugins-official",
|
||||
"unique_installs": 1
|
||||
},
|
||||
{
|
||||
"plugin": "jira@claude-plugins-official",
|
||||
"unique_installs": 1
|
||||
},
|
||||
{
|
||||
"plugin": "openspec@claude-plugins-official",
|
||||
"unique_installs": 1
|
||||
},
|
||||
{
|
||||
"plugin": "review-submission@claude-plugins-official",
|
||||
"unique_installs": 1
|
||||
},
|
||||
{
|
||||
"plugin": "microsoft-learn@claude-plugins-official",
|
||||
"unique_installs": 1
|
||||
},
|
||||
{
|
||||
"plugin": "it-triage-system@claude-plugins-official",
|
||||
"unique_installs": 1
|
||||
},
|
||||
{
|
||||
"plugin": "prototype@claude-plugins-official",
|
||||
"unique_installs": 1
|
||||
},
|
||||
{
|
||||
"plugin": "backend-specialist@claude-plugins-official",
|
||||
"unique_installs": 1
|
||||
},
|
||||
{
|
||||
"plugin": "dj-content-creator@claude-plugins-official",
|
||||
"unique_installs": 1
|
||||
},
|
||||
{
|
||||
"plugin": "gitlab-mr-review@claude-plugins-official",
|
||||
"unique_installs": 1
|
||||
},
|
||||
{
|
||||
"plugin": "frontend-lab@claude-plugins-official",
|
||||
"unique_installs": 1
|
||||
},
|
||||
{
|
||||
"plugin": "prototyper@claude-plugins-official",
|
||||
"unique_installs": 1
|
||||
},
|
||||
{
|
||||
"plugin": "universal-dev@claude-plugins-official",
|
||||
"unique_installs": 1
|
||||
},
|
||||
{
|
||||
"plugin": "ccpm@claude-plugins-official",
|
||||
"unique_installs": 1
|
||||
},
|
||||
{
|
||||
"plugin": "terraform-ls@claude-plugins-official",
|
||||
"unique_installs": 1
|
||||
},
|
||||
{
|
||||
"plugin": "silince-gutnebrg-builder@claude-plugins-official",
|
||||
"unique_installs": 1
|
||||
},
|
||||
{
|
||||
"plugin": "codex-skills@claude-plugins-official",
|
||||
"unique_installs": 1
|
||||
},
|
||||
{
|
||||
"plugin": "ai-pm-copilot@claude-plugins-official",
|
||||
"unique_installs": 1
|
||||
},
|
||||
{
|
||||
"plugin": "bun-typescript@claude-plugins-official",
|
||||
"unique_installs": 1
|
||||
},
|
||||
{
|
||||
"plugin": "claude-rules-generator@claude-plugins-official",
|
||||
"unique_installs": 1
|
||||
},
|
||||
{
|
||||
"plugin": "test-automation-generator@claude-plugins-official",
|
||||
"unique_installs": 1
|
||||
},
|
||||
{
|
||||
"plugin": "plan-guardian@claude-plugins-official",
|
||||
"unique_installs": 1
|
||||
},
|
||||
{
|
||||
"plugin": "hosts-db@claude-plugins-official",
|
||||
"unique_installs": 1
|
||||
},
|
||||
{
|
||||
"plugin": "dart-lsp@claude-plugins-official",
|
||||
"unique_installs": 1
|
||||
},
|
||||
{
|
||||
"plugin": "plan-guardian@claude-plugins-official",
|
||||
"plugin": "amber-electric@claude-plugins-official",
|
||||
"unique_installs": 1
|
||||
}
|
||||
]
|
||||
|
||||
@ -15,10 +15,10 @@
|
||||
"playground@claude-plugins-official": [
|
||||
{
|
||||
"scope": "user",
|
||||
"installPath": "/home/cal/.claude/plugins/cache/claude-plugins-official/playground/8deab8460a9d",
|
||||
"version": "8deab8460a9d",
|
||||
"installPath": "/home/cal/.claude/plugins/cache/claude-plugins-official/playground/55b58ec6e564",
|
||||
"version": "55b58ec6e564",
|
||||
"installedAt": "2026-02-18T19:51:28.422Z",
|
||||
"lastUpdated": "2026-02-19T00:58:15.373Z",
|
||||
"lastUpdated": "2026-02-25T18:35:41.447Z",
|
||||
"gitCommitSha": "261ce4fba4f2c314c490302158909a32e5889c88"
|
||||
}
|
||||
],
|
||||
@ -31,6 +31,16 @@
|
||||
"lastUpdated": "2026-02-19T04:07:27.304Z",
|
||||
"gitCommitSha": "8deab8460a9d4df5a01315ef722a5ca6b061c074"
|
||||
}
|
||||
],
|
||||
"frontend-design@claude-plugins-official": [
|
||||
{
|
||||
"scope": "user",
|
||||
"installPath": "/home/cal/.claude/plugins/cache/claude-plugins-official/frontend-design/55b58ec6e564",
|
||||
"version": "55b58ec6e564",
|
||||
"installedAt": "2026-02-22T05:53:45.091Z",
|
||||
"lastUpdated": "2026-02-25T18:35:41.440Z",
|
||||
"gitCommitSha": "aa296ec81e8ccb49c9784f167c2c0aa625a86cec"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
@ -5,7 +5,7 @@
|
||||
"url": "https://github.com/anthropics/claude-plugins-official.git"
|
||||
},
|
||||
"installLocation": "/home/cal/.claude/plugins/marketplaces/claude-plugins-official",
|
||||
"lastUpdated": "2026-02-19T11:00:01.550Z"
|
||||
"lastUpdated": "2026-02-22T05:54:15.140Z"
|
||||
},
|
||||
"claude-code-plugins": {
|
||||
"source": {
|
||||
@ -13,6 +13,6 @@
|
||||
"repo": "anthropics/claude-code"
|
||||
},
|
||||
"installLocation": "/home/cal/.claude/plugins/marketplaces/claude-code-plugins",
|
||||
"lastUpdated": "2026-02-20T04:08:05.428Z"
|
||||
"lastUpdated": "2026-02-25T23:28:10.907Z"
|
||||
}
|
||||
}
|
||||
@ -1 +1 @@
|
||||
Subproject commit 0d996a7c346e2f3aca34e3488e49f1da537d7811
|
||||
Subproject commit 76c0cbaeb563cd548b11898ba3892812f1ea510f
|
||||
@ -1 +1 @@
|
||||
Subproject commit 8deab8460a9d4df5a01315ef722a5ca6b061c074
|
||||
Subproject commit 55b58ec6e5649104f926ba7558b567dc8d33c5ff
|
||||
@ -2,7 +2,8 @@
|
||||
"$schema": "https://json.schemastore.org/claude-code-settings.json",
|
||||
"env": {
|
||||
"MCP_API_KEY": "${MCP_API_KEY}",
|
||||
"CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS": "1"
|
||||
"CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS": "1",
|
||||
"ENABLE_TOOL_SEARCH": "true"
|
||||
},
|
||||
"permissions": {
|
||||
"allow": [
|
||||
@ -56,7 +57,39 @@
|
||||
"mcp__memorygraph__get_memory",
|
||||
"Skill(notediscovery)",
|
||||
"mcp__cognitive-memory__*",
|
||||
"mcp__n8n-mcp__*"
|
||||
"mcp__n8n-mcp__*",
|
||||
"WebFetch(domain:custom-system-builder.gitlab.io)",
|
||||
"WebFetch(domain:foundryvtt.com)",
|
||||
"WebFetch(domain:10.10.0.174)",
|
||||
"WebFetch(domain:docs.openclaw.ai)",
|
||||
"WebFetch(domain:pve.proxmox.com)",
|
||||
"WebFetch(domain:git.manticorum.com)",
|
||||
"WebFetch(domain:developers.cloudflare.com)",
|
||||
"WebFetch(domain:uptime-kuma-api.readthedocs.io)",
|
||||
"WebFetch(domain:www.enworld.org)",
|
||||
"WebFetch(domain:2minutetabletop.com)",
|
||||
"WebFetch(domain:www.forgotten-adventures.net)",
|
||||
"WebFetch(domain:www.doodlesanddragons.com)",
|
||||
"WebFetch(domain:www.drivethrurpg.com)",
|
||||
"WebFetch(domain:forums.rptools.net)",
|
||||
"WebFetch(domain:www.fantasygrounds.com)",
|
||||
"WebFetch(domain:gmkeros.wordpress.com)",
|
||||
"WebFetch(domain:inkwellideas.com)",
|
||||
"WebFetch(domain:itch.io)",
|
||||
"WebFetch(domain:opengameart.org)",
|
||||
"WebFetch(domain:store.paizo.com)",
|
||||
"WebFetch(domain:forum.gitea.com)",
|
||||
"WebFetch(domain:docs.gitea.com)",
|
||||
"WebFetch(domain:gitea.com)",
|
||||
"WebFetch(domain:docs.anthropic.com)",
|
||||
"WebFetch(domain:raw.githubusercontent.com)",
|
||||
"WebFetch(domain:ra-h.app)",
|
||||
"WebFetch(domain:ollama.com)",
|
||||
"mcp__cognitive-memory__memory_store",
|
||||
"mcp__cognitive-memory__memory_episode",
|
||||
"mcp__cognitive-memory__memory_recall",
|
||||
"mcp__cognitive-memory__memory_search",
|
||||
"mcp__cognitive-memory__memory_get"
|
||||
],
|
||||
"deny": [
|
||||
"Bash(diskutil partitionDisk)",
|
||||
@ -130,7 +163,8 @@
|
||||
},
|
||||
"enabledPlugins": {
|
||||
"playground@claude-plugins-official": true,
|
||||
"claude-code-setup@claude-plugins-official": true
|
||||
"claude-code-setup@claude-plugins-official": true,
|
||||
"frontend-design@claude-plugins-official": true
|
||||
},
|
||||
"skipDangerousModePermissionPrompt": true,
|
||||
"effortLevel": "medium"
|
||||
|
||||
@ -33,7 +33,12 @@ class PaperDynastyAPI:
|
||||
api.wipe_team_cards(team_id=464)
|
||||
"""
|
||||
|
||||
def __init__(self, environment: str = 'dev', token: Optional[str] = None, verbose: bool = False):
|
||||
def __init__(
|
||||
self,
|
||||
environment: str = "dev",
|
||||
token: Optional[str] = None,
|
||||
verbose: bool = False,
|
||||
):
|
||||
"""
|
||||
Initialize API client
|
||||
|
||||
@ -44,16 +49,16 @@ class PaperDynastyAPI:
|
||||
"""
|
||||
self.env = environment.lower()
|
||||
self.base_url = (
|
||||
'https://pd.manticorum.com/api'
|
||||
if 'prod' in self.env
|
||||
else 'https://pddev.manticorum.com/api'
|
||||
"https://pd.manticorum.com/api"
|
||||
if "prod" in self.env
|
||||
else "https://pddev.manticorum.com/api"
|
||||
)
|
||||
self.token = token or os.getenv('API_TOKEN')
|
||||
self.token = token or os.getenv("API_TOKEN")
|
||||
self.verbose = verbose
|
||||
|
||||
self.headers = {'Content-Type': 'application/json'}
|
||||
self.headers = {"Content-Type": "application/json"}
|
||||
if self.token:
|
||||
self.headers['Authorization'] = f'Bearer {self.token}'
|
||||
self.headers["Authorization"] = f"Bearer {self.token}"
|
||||
|
||||
def _require_token(self):
|
||||
"""Raise if no API token is set (needed for write operations)"""
|
||||
@ -68,16 +73,22 @@ class PaperDynastyAPI:
|
||||
if self.verbose:
|
||||
print(f"[API] {message}")
|
||||
|
||||
def _build_url(self, endpoint: str, api_ver: int = 2, object_id: Optional[int] = None, params: Optional[List] = None) -> str:
|
||||
def _build_url(
|
||||
self,
|
||||
endpoint: str,
|
||||
api_ver: int = 2,
|
||||
object_id: Optional[int] = None,
|
||||
params: Optional[List] = None,
|
||||
) -> str:
|
||||
"""Build API URL with parameters"""
|
||||
url = f'{self.base_url}/v{api_ver}/{endpoint}'
|
||||
url = f"{self.base_url}/v{api_ver}/{endpoint}"
|
||||
|
||||
if object_id is not None:
|
||||
url += f'/{object_id}'
|
||||
url += f"/{object_id}"
|
||||
|
||||
if params:
|
||||
param_strs = [f'{k}={v}' for k, v in params]
|
||||
url += '?' + '&'.join(param_strs)
|
||||
param_strs = [f"{k}={v}" for k, v in params]
|
||||
url += "?" + "&".join(param_strs)
|
||||
|
||||
return url
|
||||
|
||||
@ -85,7 +96,13 @@ class PaperDynastyAPI:
|
||||
# Low-level HTTP methods
|
||||
# ====================
|
||||
|
||||
def get(self, endpoint: str, object_id: Optional[int] = None, params: Optional[List] = None, timeout: int = 10) -> Dict:
|
||||
def get(
|
||||
self,
|
||||
endpoint: str,
|
||||
object_id: Optional[int] = None,
|
||||
params: Optional[List] = None,
|
||||
timeout: int = 10,
|
||||
) -> Dict:
|
||||
"""GET request to API"""
|
||||
url = self._build_url(endpoint, object_id=object_id, params=params)
|
||||
self._log(f"GET {url}")
|
||||
@ -93,16 +110,22 @@ class PaperDynastyAPI:
|
||||
response.raise_for_status()
|
||||
return response.json()
|
||||
|
||||
def post(self, endpoint: str, payload: Optional[Dict] = None, timeout: int = 10) -> Any:
|
||||
def post(
|
||||
self, endpoint: str, payload: Optional[Dict] = None, timeout: int = 10
|
||||
) -> Any:
|
||||
"""POST request to API"""
|
||||
self._require_token()
|
||||
url = self._build_url(endpoint)
|
||||
self._log(f"POST {url}")
|
||||
response = requests.post(url, headers=self.headers, json=payload, timeout=timeout)
|
||||
response = requests.post(
|
||||
url, headers=self.headers, json=payload, timeout=timeout
|
||||
)
|
||||
response.raise_for_status()
|
||||
return response.json() if response.text else {}
|
||||
|
||||
def patch(self, endpoint: str, object_id: int, params: List, timeout: int = 10) -> Dict:
|
||||
def patch(
|
||||
self, endpoint: str, object_id: int, params: List, timeout: int = 10
|
||||
) -> Dict:
|
||||
"""PATCH request to API"""
|
||||
self._require_token()
|
||||
url = self._build_url(endpoint, object_id=object_id, params=params)
|
||||
@ -124,7 +147,9 @@ class PaperDynastyAPI:
|
||||
# Team Operations
|
||||
# ====================
|
||||
|
||||
def get_team(self, team_id: Optional[int] = None, abbrev: Optional[str] = None) -> Dict:
|
||||
def get_team(
|
||||
self, team_id: Optional[int] = None, abbrev: Optional[str] = None
|
||||
) -> Dict:
|
||||
"""
|
||||
Get a team by ID or abbreviation
|
||||
|
||||
@ -136,17 +161,19 @@ class PaperDynastyAPI:
|
||||
Team dict
|
||||
"""
|
||||
if team_id:
|
||||
return self.get('teams', object_id=team_id)
|
||||
return self.get("teams", object_id=team_id)
|
||||
elif abbrev:
|
||||
result = self.get('teams', params=[('abbrev', abbrev.upper())])
|
||||
teams = result.get('teams', [])
|
||||
result = self.get("teams", params=[("abbrev", abbrev.upper())])
|
||||
teams = result.get("teams", [])
|
||||
if not teams:
|
||||
raise ValueError(f"Team '{abbrev}' not found")
|
||||
return teams[0]
|
||||
else:
|
||||
raise ValueError("Must provide team_id or abbrev")
|
||||
|
||||
def list_teams(self, season: Optional[int] = None, event_id: Optional[int] = None) -> List[Dict]:
|
||||
def list_teams(
|
||||
self, season: Optional[int] = None, event_id: Optional[int] = None
|
||||
) -> List[Dict]:
|
||||
"""
|
||||
List teams
|
||||
|
||||
@ -159,12 +186,12 @@ class PaperDynastyAPI:
|
||||
"""
|
||||
params = []
|
||||
if season:
|
||||
params.append(('season', season))
|
||||
params.append(("season", season))
|
||||
if event_id:
|
||||
params.append(('event', event_id))
|
||||
params.append(("event", event_id))
|
||||
|
||||
result = self.get('teams', params=params if params else None)
|
||||
return result.get('teams', [])
|
||||
result = self.get("teams", params=params if params else None)
|
||||
return result.get("teams", [])
|
||||
|
||||
# ====================
|
||||
# Card Operations
|
||||
@ -180,9 +207,11 @@ class PaperDynastyAPI:
|
||||
Returns:
|
||||
API response
|
||||
"""
|
||||
return self.post(f'cards/wipe-team/{team_id}')
|
||||
return self.post(f"cards/wipe-team/{team_id}")
|
||||
|
||||
def list_cards(self, team_id: Optional[int] = None, player_id: Optional[int] = None) -> List[Dict]:
|
||||
def list_cards(
|
||||
self, team_id: Optional[int] = None, player_id: Optional[int] = None
|
||||
) -> List[Dict]:
|
||||
"""
|
||||
List cards. At least one filter is required to avoid massive unfiltered queries.
|
||||
|
||||
@ -194,16 +223,18 @@ class PaperDynastyAPI:
|
||||
List of card dicts
|
||||
"""
|
||||
if not team_id and not player_id:
|
||||
raise ValueError("list_cards requires at least one filter (team_id or player_id)")
|
||||
raise ValueError(
|
||||
"list_cards requires at least one filter (team_id or player_id)"
|
||||
)
|
||||
|
||||
params = []
|
||||
if team_id:
|
||||
params.append(('team_id', team_id))
|
||||
params.append(("team_id", team_id))
|
||||
if player_id:
|
||||
params.append(('player_id', player_id))
|
||||
params.append(("player_id", player_id))
|
||||
|
||||
result = self.get('cards', params=params if params else None)
|
||||
return result.get('cards', [])
|
||||
result = self.get("cards", params=params if params else None)
|
||||
return result.get("cards", [])
|
||||
|
||||
# ====================
|
||||
# Pack Operations
|
||||
@ -215,7 +246,7 @@ class PaperDynastyAPI:
|
||||
opened: Optional[bool] = None,
|
||||
new_to_old: bool = False,
|
||||
limit: Optional[int] = None,
|
||||
timeout: int = 10
|
||||
timeout: int = 10,
|
||||
) -> List[Dict]:
|
||||
"""
|
||||
List packs
|
||||
@ -241,20 +272,22 @@ class PaperDynastyAPI:
|
||||
packs = api.list_packs(opened=True, limit=2000, timeout=30)
|
||||
"""
|
||||
if team_id is None and opened is None:
|
||||
raise ValueError("list_packs requires at least one filter (team_id or opened)")
|
||||
raise ValueError(
|
||||
"list_packs requires at least one filter (team_id or opened)"
|
||||
)
|
||||
|
||||
params = []
|
||||
if team_id:
|
||||
params.append(('team_id', team_id))
|
||||
params.append(("team_id", team_id))
|
||||
if opened is not None:
|
||||
params.append(('opened', 'true' if opened else 'false'))
|
||||
params.append(("opened", "true" if opened else "false"))
|
||||
if new_to_old:
|
||||
params.append(('new_to_old', 'true'))
|
||||
params.append(("new_to_old", "true"))
|
||||
if limit:
|
||||
params.append(('limit', str(limit)))
|
||||
params.append(("limit", str(limit)))
|
||||
|
||||
result = self.get('packs', params=params if params else None, timeout=timeout)
|
||||
return result.get('packs', [])
|
||||
result = self.get("packs", params=params if params else None, timeout=timeout)
|
||||
return result.get("packs", [])
|
||||
|
||||
def delete_pack(self, pack_id: int) -> str:
|
||||
"""
|
||||
@ -266,10 +299,15 @@ class PaperDynastyAPI:
|
||||
Returns:
|
||||
Success message
|
||||
"""
|
||||
return self.delete('packs', object_id=pack_id)
|
||||
return self.delete("packs", object_id=pack_id)
|
||||
|
||||
def update_pack(self, pack_id: int, pack_cardset_id: Optional[int] = None,
|
||||
pack_team_id: Optional[int] = None, pack_type_id: Optional[int] = None) -> Dict:
|
||||
def update_pack(
|
||||
self,
|
||||
pack_id: int,
|
||||
pack_cardset_id: Optional[int] = None,
|
||||
pack_team_id: Optional[int] = None,
|
||||
pack_type_id: Optional[int] = None,
|
||||
) -> Dict:
|
||||
"""
|
||||
Update pack properties (PATCH)
|
||||
|
||||
@ -288,13 +326,13 @@ class PaperDynastyAPI:
|
||||
"""
|
||||
params = []
|
||||
if pack_cardset_id is not None:
|
||||
params.append(('pack_cardset_id', pack_cardset_id))
|
||||
params.append(("pack_cardset_id", pack_cardset_id))
|
||||
if pack_team_id is not None:
|
||||
params.append(('pack_team_id', pack_team_id))
|
||||
params.append(("pack_team_id", pack_team_id))
|
||||
if pack_type_id is not None:
|
||||
params.append(('pack_type_id', pack_type_id))
|
||||
params.append(("pack_type_id", pack_type_id))
|
||||
|
||||
return self.patch('packs', object_id=pack_id, params=params)
|
||||
return self.patch("packs", object_id=pack_id, params=params)
|
||||
|
||||
def create_packs(self, packs: List[Dict]) -> Any:
|
||||
"""
|
||||
@ -313,8 +351,8 @@ class PaperDynastyAPI:
|
||||
for _ in range(5)
|
||||
])
|
||||
"""
|
||||
payload = {'packs': packs}
|
||||
return self.post('packs', payload=payload)
|
||||
payload = {"packs": packs}
|
||||
return self.post("packs", payload=payload)
|
||||
|
||||
def get_packs_opened_today(self, limit: int = 2000, timeout: int = 30) -> Dict:
|
||||
"""
|
||||
@ -338,57 +376,67 @@ class PaperDynastyAPI:
|
||||
from collections import defaultdict
|
||||
|
||||
# Get recent opened packs
|
||||
packs = self.list_packs(opened=True, new_to_old=True, limit=limit, timeout=timeout)
|
||||
packs = self.list_packs(
|
||||
opened=True, new_to_old=True, limit=limit, timeout=timeout
|
||||
)
|
||||
|
||||
# Today's date (UTC)
|
||||
today = datetime.now(timezone.utc).date()
|
||||
|
||||
# Count packs by team
|
||||
teams_data = defaultdict(lambda: {'count': 0, 'abbrev': '', 'lname': '', 'first': None, 'last': None})
|
||||
teams_data = defaultdict(
|
||||
lambda: {"count": 0, "abbrev": "", "lname": "", "first": None, "last": None}
|
||||
)
|
||||
total = 0
|
||||
|
||||
for pack in packs:
|
||||
if pack.get('open_time'):
|
||||
if pack.get("open_time"):
|
||||
try:
|
||||
open_dt = datetime.fromtimestamp(pack['open_time'] / 1000, tz=timezone.utc)
|
||||
open_dt = datetime.fromtimestamp(
|
||||
pack["open_time"] / 1000, tz=timezone.utc
|
||||
)
|
||||
|
||||
if open_dt.date() == today:
|
||||
total += 1
|
||||
team_id = pack['team']['id']
|
||||
teams_data[team_id]['abbrev'] = pack['team']['abbrev']
|
||||
teams_data[team_id]['lname'] = pack['team']['lname']
|
||||
teams_data[team_id]['count'] += 1
|
||||
team_id = pack["team"]["id"]
|
||||
teams_data[team_id]["abbrev"] = pack["team"]["abbrev"]
|
||||
teams_data[team_id]["lname"] = pack["team"]["lname"]
|
||||
teams_data[team_id]["count"] += 1
|
||||
|
||||
if teams_data[team_id]['first'] is None or open_dt < teams_data[team_id]['first']:
|
||||
teams_data[team_id]['first'] = open_dt
|
||||
if teams_data[team_id]['last'] is None or open_dt > teams_data[team_id]['last']:
|
||||
teams_data[team_id]['last'] = open_dt
|
||||
if (
|
||||
teams_data[team_id]["first"] is None
|
||||
or open_dt < teams_data[team_id]["first"]
|
||||
):
|
||||
teams_data[team_id]["first"] = open_dt
|
||||
if (
|
||||
teams_data[team_id]["last"] is None
|
||||
or open_dt > teams_data[team_id]["last"]
|
||||
):
|
||||
teams_data[team_id]["last"] = open_dt
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# Format results
|
||||
teams_list = []
|
||||
for team_id, data in teams_data.items():
|
||||
teams_list.append({
|
||||
'team_id': team_id,
|
||||
'abbrev': data['abbrev'],
|
||||
'name': data['lname'],
|
||||
'packs': data['count'],
|
||||
'first_pack': data['first'].isoformat() if data['first'] else None,
|
||||
'last_pack': data['last'].isoformat() if data['last'] else None
|
||||
})
|
||||
teams_list.append(
|
||||
{
|
||||
"team_id": team_id,
|
||||
"abbrev": data["abbrev"],
|
||||
"name": data["lname"],
|
||||
"packs": data["count"],
|
||||
"first_pack": data["first"].isoformat() if data["first"] else None,
|
||||
"last_pack": data["last"].isoformat() if data["last"] else None,
|
||||
}
|
||||
)
|
||||
|
||||
# Sort by pack count
|
||||
teams_list.sort(key=lambda x: x['packs'], reverse=True)
|
||||
teams_list.sort(key=lambda x: x["packs"], reverse=True)
|
||||
|
||||
result = {
|
||||
'total': total,
|
||||
'teams': teams_list,
|
||||
'date': today.isoformat()
|
||||
}
|
||||
result = {"total": total, "teams": teams_list, "date": today.isoformat()}
|
||||
|
||||
if len(packs) == limit:
|
||||
result['note'] = f'Hit limit of {limit} packs - actual count may be higher'
|
||||
result["note"] = f"Hit limit of {limit} packs - actual count may be higher"
|
||||
|
||||
return result
|
||||
|
||||
@ -397,7 +445,8 @@ class PaperDynastyAPI:
|
||||
num_packs: int = 5,
|
||||
exclude_team_abbrev: Optional[List[str]] = None,
|
||||
pack_type_id: int = 1,
|
||||
season: Optional[int] = None
|
||||
season: Optional[int] = None,
|
||||
cardset_id: Optional[int] = None,
|
||||
) -> Dict:
|
||||
"""
|
||||
Distribute packs to all human-controlled teams
|
||||
@ -407,6 +456,7 @@ class PaperDynastyAPI:
|
||||
exclude_team_abbrev: List of team abbreviations to exclude (default: None)
|
||||
pack_type_id: Pack type ID (default: 1 = Standard packs)
|
||||
season: Season to distribute for (default: current season)
|
||||
cardset_id: Cardset ID for pack types that require it (e.g., Promo Choice = type 9)
|
||||
|
||||
Returns:
|
||||
Dict with keys:
|
||||
@ -429,8 +479,8 @@ class PaperDynastyAPI:
|
||||
|
||||
# Get current season if not specified
|
||||
if season is None:
|
||||
current = self.get('current')
|
||||
season = current['season']
|
||||
current = self.get("current")
|
||||
season = current["season"]
|
||||
|
||||
self._log(f"Distributing {num_packs} packs to season {season} teams")
|
||||
|
||||
@ -440,9 +490,9 @@ class PaperDynastyAPI:
|
||||
# Filter for human-controlled teams only
|
||||
qualifying_teams = []
|
||||
for team in all_teams:
|
||||
if not team['is_ai'] and 'gauntlet' not in team['abbrev'].lower():
|
||||
if not team["is_ai"] and "gauntlet" not in team["abbrev"].lower():
|
||||
# Check if team is in exclusion list
|
||||
if team['abbrev'].upper() in exclude_team_abbrev:
|
||||
if team["abbrev"].upper() in exclude_team_abbrev:
|
||||
self._log(f"Excluding team {team['abbrev']}: {team['sname']}")
|
||||
continue
|
||||
qualifying_teams.append(team)
|
||||
@ -457,27 +507,34 @@ class PaperDynastyAPI:
|
||||
self._log(f"Giving {num_packs} packs to {team['abbrev']} ({team['sname']})")
|
||||
|
||||
# Create pack payload
|
||||
packs = [{
|
||||
'team_id': team['id'],
|
||||
'pack_type_id': pack_type_id,
|
||||
'pack_cardset_id': None
|
||||
} for _ in range(num_packs)]
|
||||
packs = [
|
||||
{
|
||||
"team_id": team["id"],
|
||||
"pack_type_id": pack_type_id,
|
||||
"pack_cardset_id": cardset_id,
|
||||
}
|
||||
for _ in range(num_packs)
|
||||
]
|
||||
|
||||
try:
|
||||
self.create_packs(packs)
|
||||
total_packs += num_packs
|
||||
self._log(f" ✓ Successfully gave {num_packs} packs to {team['abbrev']}")
|
||||
self._log(
|
||||
f" ✓ Successfully gave {num_packs} packs to {team['abbrev']}"
|
||||
)
|
||||
except Exception as e:
|
||||
self._log(f" ✗ Failed to give packs to {team['abbrev']}: {e}")
|
||||
raise
|
||||
|
||||
result = {
|
||||
'total_packs': total_packs,
|
||||
'teams_count': len(qualifying_teams),
|
||||
'teams': qualifying_teams
|
||||
"total_packs": total_packs,
|
||||
"teams_count": len(qualifying_teams),
|
||||
"teams": qualifying_teams,
|
||||
}
|
||||
|
||||
self._log(f"Distribution complete: {total_packs} packs to {len(qualifying_teams)} teams")
|
||||
self._log(
|
||||
f"Distribution complete: {total_packs} packs to {len(qualifying_teams)} teams"
|
||||
)
|
||||
|
||||
return result
|
||||
|
||||
@ -485,7 +542,12 @@ class PaperDynastyAPI:
|
||||
# Gauntlet Operations
|
||||
# ====================
|
||||
|
||||
def list_gauntlet_runs(self, event_id: Optional[int] = None, team_id: Optional[int] = None, active_only: bool = False) -> List[Dict]:
|
||||
def list_gauntlet_runs(
|
||||
self,
|
||||
event_id: Optional[int] = None,
|
||||
team_id: Optional[int] = None,
|
||||
active_only: bool = False,
|
||||
) -> List[Dict]:
|
||||
"""
|
||||
List gauntlet runs
|
||||
|
||||
@ -499,14 +561,14 @@ class PaperDynastyAPI:
|
||||
"""
|
||||
params = []
|
||||
if event_id:
|
||||
params.append(('gauntlet_id', event_id))
|
||||
params.append(("gauntlet_id", event_id))
|
||||
if team_id:
|
||||
params.append(('team_id', team_id))
|
||||
params.append(("team_id", team_id))
|
||||
if active_only:
|
||||
params.append(('is_active', 'true'))
|
||||
params.append(("is_active", "true"))
|
||||
|
||||
result = self.get('gauntletruns', params=params if params else None)
|
||||
return result.get('runs', [])
|
||||
result = self.get("gauntletruns", params=params if params else None)
|
||||
return result.get("runs", [])
|
||||
|
||||
def end_gauntlet_run(self, run_id: int) -> Dict:
|
||||
"""
|
||||
@ -518,7 +580,7 @@ class PaperDynastyAPI:
|
||||
Returns:
|
||||
Updated run dict
|
||||
"""
|
||||
return self.patch('gauntletruns', object_id=run_id, params=[('ended', 'true')])
|
||||
return self.patch("gauntletruns", object_id=run_id, params=[("ended", "true")])
|
||||
|
||||
# ====================
|
||||
# Player Operations
|
||||
@ -534,9 +596,14 @@ class PaperDynastyAPI:
|
||||
Returns:
|
||||
Player dict
|
||||
"""
|
||||
return self.get('players', object_id=player_id)
|
||||
return self.get("players", object_id=player_id)
|
||||
|
||||
def list_players(self, cardset_id: Optional[int] = None, rarity: Optional[str] = None, timeout: int = 30) -> List[Dict]:
|
||||
def list_players(
|
||||
self,
|
||||
cardset_id: Optional[int] = None,
|
||||
rarity: Optional[str] = None,
|
||||
timeout: int = 30,
|
||||
) -> List[Dict]:
|
||||
"""
|
||||
List players. At least one filter is required to avoid massive unfiltered queries.
|
||||
|
||||
@ -549,22 +616,26 @@ class PaperDynastyAPI:
|
||||
List of player dicts
|
||||
"""
|
||||
if not cardset_id and not rarity:
|
||||
raise ValueError("list_players requires at least one filter (cardset_id or rarity)")
|
||||
raise ValueError(
|
||||
"list_players requires at least one filter (cardset_id or rarity)"
|
||||
)
|
||||
|
||||
params = []
|
||||
if cardset_id:
|
||||
params.append(('cardset', cardset_id))
|
||||
params.append(("cardset", cardset_id))
|
||||
if rarity:
|
||||
params.append(('rarity', rarity))
|
||||
params.append(("rarity", rarity))
|
||||
|
||||
result = self.get('players', params=params, timeout=timeout)
|
||||
return result.get('players', [])
|
||||
result = self.get("players", params=params, timeout=timeout)
|
||||
return result.get("players", [])
|
||||
|
||||
# ====================
|
||||
# Result/Stats Operations
|
||||
# ====================
|
||||
|
||||
def list_results(self, season: Optional[int] = None, team_id: Optional[int] = None) -> List[Dict]:
|
||||
def list_results(
|
||||
self, season: Optional[int] = None, team_id: Optional[int] = None
|
||||
) -> List[Dict]:
|
||||
"""
|
||||
List game results. At least one filter is required to avoid massive unfiltered queries.
|
||||
|
||||
@ -576,22 +647,26 @@ class PaperDynastyAPI:
|
||||
List of result dicts
|
||||
"""
|
||||
if not season and not team_id:
|
||||
raise ValueError("list_results requires at least one filter (season or team_id)")
|
||||
raise ValueError(
|
||||
"list_results requires at least one filter (season or team_id)"
|
||||
)
|
||||
|
||||
params = []
|
||||
if season:
|
||||
params.append(('season', season))
|
||||
params.append(("season", season))
|
||||
if team_id:
|
||||
params.append(('team_id', team_id))
|
||||
params.append(("team_id", team_id))
|
||||
|
||||
result = self.get('results', params=params if params else None)
|
||||
return result.get('results', [])
|
||||
result = self.get("results", params=params if params else None)
|
||||
return result.get("results", [])
|
||||
|
||||
# ====================
|
||||
# Helper Methods
|
||||
# ====================
|
||||
|
||||
def find_gauntlet_teams(self, event_id: Optional[int] = None, active_only: bool = False) -> List[Dict]:
|
||||
def find_gauntlet_teams(
|
||||
self, event_id: Optional[int] = None, active_only: bool = False
|
||||
) -> List[Dict]:
|
||||
"""
|
||||
Find gauntlet teams (teams with 'Gauntlet' in abbrev)
|
||||
|
||||
@ -607,22 +682,22 @@ class PaperDynastyAPI:
|
||||
runs = self.list_gauntlet_runs(event_id=event_id, active_only=True)
|
||||
teams_with_runs = []
|
||||
for run in runs:
|
||||
team = run['team']
|
||||
team['active_run'] = run
|
||||
team = run["team"]
|
||||
team["active_run"] = run
|
||||
teams_with_runs.append(team)
|
||||
return teams_with_runs
|
||||
else:
|
||||
# Get all teams with 'Gauntlet' in name
|
||||
all_teams = self.list_teams()
|
||||
gauntlet_teams = [t for t in all_teams if 'Gauntlet' in t.get('abbrev', '')]
|
||||
gauntlet_teams = [t for t in all_teams if "Gauntlet" in t.get("abbrev", "")]
|
||||
|
||||
# Optionally add run info
|
||||
if event_id:
|
||||
runs = self.list_gauntlet_runs(event_id=event_id)
|
||||
run_by_team = {r['team']['id']: r for r in runs}
|
||||
run_by_team = {r["team"]["id"]: r for r in runs}
|
||||
for team in gauntlet_teams:
|
||||
if team['id'] in run_by_team:
|
||||
team['run'] = run_by_team[team['id']]
|
||||
if team["id"] in run_by_team:
|
||||
team["run"] = run_by_team[team["id"]]
|
||||
|
||||
return gauntlet_teams
|
||||
|
||||
@ -631,9 +706,11 @@ def main():
|
||||
"""Example usage"""
|
||||
import argparse
|
||||
|
||||
parser = argparse.ArgumentParser(description='Paper Dynasty API Client')
|
||||
parser.add_argument('--env', choices=['prod', 'dev'], default='dev', help='Environment')
|
||||
parser.add_argument('--verbose', '-v', action='store_true', help='Verbose output')
|
||||
parser = argparse.ArgumentParser(description="Paper Dynasty API Client")
|
||||
parser.add_argument(
|
||||
"--env", choices=["prod", "dev"], default="dev", help="Environment"
|
||||
)
|
||||
parser.add_argument("--verbose", "-v", action="store_true", help="Verbose output")
|
||||
args = parser.parse_args()
|
||||
|
||||
try:
|
||||
@ -650,5 +727,5 @@ def main():
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
||||
@ -56,6 +56,7 @@ console = Console()
|
||||
|
||||
class State:
|
||||
"""Global state for API client and settings"""
|
||||
|
||||
api: Optional[PaperDynastyAPI] = None
|
||||
json_output: bool = False
|
||||
|
||||
@ -67,14 +68,19 @@ state = State()
|
||||
# Output Helpers
|
||||
# ============================================================================
|
||||
|
||||
|
||||
def output_json(data):
|
||||
"""Output data as formatted JSON"""
|
||||
console.print_json(json.dumps(data, indent=2, default=str))
|
||||
|
||||
|
||||
def output_table(title: str, columns: List[str], rows: List[List], show_lines: bool = False):
|
||||
def output_table(
|
||||
title: str, columns: List[str], rows: List[List], show_lines: bool = False
|
||||
):
|
||||
"""Output data as a rich table"""
|
||||
table = Table(title=title, show_header=True, header_style="bold cyan", show_lines=show_lines)
|
||||
table = Table(
|
||||
title=title, show_header=True, header_style="bold cyan", show_lines=show_lines
|
||||
)
|
||||
for col in columns:
|
||||
table.add_column(col)
|
||||
for row in rows:
|
||||
@ -90,7 +96,9 @@ def handle_error(e: Exception, context: str = ""):
|
||||
elif "404" in error_str:
|
||||
console.print(f"[red]Error:[/red] Not found. {context}")
|
||||
elif "Connection" in error_str or "ConnectionError" in error_str:
|
||||
console.print("[red]Error:[/red] Cannot connect to API. Check network and --env setting.")
|
||||
console.print(
|
||||
"[red]Error:[/red] Cannot connect to API. Check network and --env setting."
|
||||
)
|
||||
else:
|
||||
console.print(f"[red]Error:[/red] {e}")
|
||||
raise typer.Exit(1)
|
||||
@ -100,11 +108,16 @@ def handle_error(e: Exception, context: str = ""):
|
||||
# Main Callback (Global Options)
|
||||
# ============================================================================
|
||||
|
||||
|
||||
@app.callback()
|
||||
def main(
|
||||
env: Annotated[str, typer.Option("--env", help="Environment: prod or dev")] = "prod",
|
||||
env: Annotated[
|
||||
str, typer.Option("--env", help="Environment: prod or dev")
|
||||
] = "prod",
|
||||
json_output: Annotated[bool, typer.Option("--json", help="Output as JSON")] = False,
|
||||
verbose: Annotated[bool, typer.Option("--verbose", "-v", help="Verbose output")] = False,
|
||||
verbose: Annotated[
|
||||
bool, typer.Option("--verbose", "-v", help="Verbose output")
|
||||
] = False,
|
||||
):
|
||||
"""Paper Dynasty Baseball Card Game CLI"""
|
||||
state.api = PaperDynastyAPI(environment=env, verbose=verbose)
|
||||
@ -115,6 +128,7 @@ def main(
|
||||
# Status & Health Commands
|
||||
# ============================================================================
|
||||
|
||||
|
||||
@app.command()
|
||||
def status():
|
||||
"""Show packs opened today summary"""
|
||||
@ -125,18 +139,20 @@ def status():
|
||||
output_json(result)
|
||||
return
|
||||
|
||||
console.print(f"\n[bold cyan]Packs Opened Today ({result['date']})[/bold cyan]\n")
|
||||
console.print(
|
||||
f"\n[bold cyan]Packs Opened Today ({result['date']})[/bold cyan]\n"
|
||||
)
|
||||
console.print(f"[bold]Total:[/bold] {result['total']} packs\n")
|
||||
|
||||
if result['teams']:
|
||||
if result["teams"]:
|
||||
rows = []
|
||||
for t in result['teams']:
|
||||
rows.append([t['abbrev'], t['name'], t['packs']])
|
||||
for t in result["teams"]:
|
||||
rows.append([t["abbrev"], t["name"], t["packs"]])
|
||||
output_table("By Team", ["Abbrev", "Team", "Packs"], rows)
|
||||
else:
|
||||
console.print("[dim]No packs opened today[/dim]")
|
||||
|
||||
if result.get('note'):
|
||||
if result.get("note"):
|
||||
console.print(f"\n[yellow]Note:[/yellow] {result['note']}")
|
||||
|
||||
except Exception as e:
|
||||
@ -159,9 +175,12 @@ def health():
|
||||
# Team Commands
|
||||
# ============================================================================
|
||||
|
||||
|
||||
@team_app.command("list")
|
||||
def team_list(
|
||||
season: Annotated[Optional[int], typer.Option("--season", "-s", help="Filter by season")] = None,
|
||||
season: Annotated[
|
||||
Optional[int], typer.Option("--season", "-s", help="Filter by season")
|
||||
] = None,
|
||||
):
|
||||
"""List all teams"""
|
||||
try:
|
||||
@ -176,23 +195,27 @@ def team_list(
|
||||
return
|
||||
|
||||
# Filter out gauntlet teams for cleaner display
|
||||
regular_teams = [t for t in teams if 'Gauntlet' not in t.get('abbrev', '')]
|
||||
regular_teams = [t for t in teams if "Gauntlet" not in t.get("abbrev", "")]
|
||||
|
||||
rows = []
|
||||
for t in regular_teams:
|
||||
rows.append([
|
||||
t['abbrev'],
|
||||
t.get('sname', ''),
|
||||
t.get('season', ''),
|
||||
t.get('wallet', 0),
|
||||
t.get('ranking', 'N/A'),
|
||||
'AI' if t.get('is_ai') else 'Human'
|
||||
])
|
||||
rows.append(
|
||||
[
|
||||
t["abbrev"],
|
||||
t.get("sname", ""),
|
||||
t.get("season", ""),
|
||||
t.get("wallet", 0),
|
||||
t.get("ranking", "N/A"),
|
||||
"AI" if t.get("is_ai") else "Human",
|
||||
]
|
||||
)
|
||||
|
||||
title = "Teams"
|
||||
if season:
|
||||
title += f" - Season {season}"
|
||||
output_table(title, ["Abbrev", "Name", "Season", "Wallet", "Rank", "Type"], rows)
|
||||
output_table(
|
||||
title, ["Abbrev", "Name", "Season", "Wallet", "Rank", "Type"], rows
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
handle_error(e)
|
||||
@ -239,7 +262,7 @@ def team_cards(
|
||||
"""List team's cards"""
|
||||
try:
|
||||
team = state.api.get_team(abbrev=abbrev.upper())
|
||||
cards = state.api.list_cards(team_id=team['id'])
|
||||
cards = state.api.list_cards(team_id=team["id"])
|
||||
|
||||
if state.json_output:
|
||||
output_json(cards)
|
||||
@ -251,22 +274,26 @@ def team_cards(
|
||||
|
||||
rows = []
|
||||
for c in cards[:limit]:
|
||||
player = c.get('player', {})
|
||||
rows.append([
|
||||
c['id'],
|
||||
player.get('p_name', 'Unknown'),
|
||||
player.get('rarity', ''),
|
||||
c.get('value', 0)
|
||||
])
|
||||
player = c.get("player", {})
|
||||
rows.append(
|
||||
[
|
||||
c["id"],
|
||||
player.get("p_name", "Unknown"),
|
||||
player.get("rarity", ""),
|
||||
c.get("value", 0),
|
||||
]
|
||||
)
|
||||
|
||||
output_table(
|
||||
f"Cards for {team.get('lname', abbrev)} ({len(cards)} total)",
|
||||
["Card ID", "Player", "Rarity", "Value"],
|
||||
rows
|
||||
rows,
|
||||
)
|
||||
|
||||
if len(cards) > limit:
|
||||
console.print(f"\n[dim]Showing {limit} of {len(cards)} cards. Use --limit to see more.[/dim]")
|
||||
console.print(
|
||||
f"\n[dim]Showing {limit} of {len(cards)} cards. Use --limit to see more.[/dim]"
|
||||
)
|
||||
|
||||
except ValueError as e:
|
||||
console.print(f"[red]Error:[/red] {e}")
|
||||
@ -279,10 +306,16 @@ def team_cards(
|
||||
# Pack Commands
|
||||
# ============================================================================
|
||||
|
||||
|
||||
@pack_app.command("list")
|
||||
def pack_list(
|
||||
team: Annotated[Optional[str], typer.Option("--team", "-t", help="Filter by team abbrev")] = None,
|
||||
opened: Annotated[Optional[bool], typer.Option("--opened/--unopened", help="Filter by opened status")] = None,
|
||||
team: Annotated[
|
||||
Optional[str], typer.Option("--team", "-t", help="Filter by team abbrev")
|
||||
] = None,
|
||||
opened: Annotated[
|
||||
Optional[bool],
|
||||
typer.Option("--opened/--unopened", help="Filter by opened status"),
|
||||
] = None,
|
||||
limit: Annotated[int, typer.Option("--limit", "-n", help="Max packs to show")] = 50,
|
||||
):
|
||||
"""List packs"""
|
||||
@ -291,10 +324,12 @@ def pack_list(
|
||||
team_name = None
|
||||
if team:
|
||||
team_obj = state.api.get_team(abbrev=team.upper())
|
||||
team_id = team_obj['id']
|
||||
team_name = team_obj.get('sname', team)
|
||||
team_id = team_obj["id"]
|
||||
team_name = team_obj.get("sname", team)
|
||||
|
||||
packs = state.api.list_packs(team_id=team_id, opened=opened, new_to_old=True, limit=limit)
|
||||
packs = state.api.list_packs(
|
||||
team_id=team_id, opened=opened, new_to_old=True, limit=limit
|
||||
)
|
||||
|
||||
if state.json_output:
|
||||
output_json(packs)
|
||||
@ -306,15 +341,17 @@ def pack_list(
|
||||
|
||||
rows = []
|
||||
for p in packs:
|
||||
pack_team = p.get('team', {})
|
||||
pack_type = p.get('pack_type', {})
|
||||
is_opened = "Yes" if p.get('open_time') else "No"
|
||||
rows.append([
|
||||
p['id'],
|
||||
pack_team.get('abbrev', 'N/A'),
|
||||
pack_type.get('name', 'Unknown'),
|
||||
is_opened
|
||||
])
|
||||
pack_team = p.get("team", {})
|
||||
pack_type = p.get("pack_type", {})
|
||||
is_opened = "Yes" if p.get("open_time") else "No"
|
||||
rows.append(
|
||||
[
|
||||
p["id"],
|
||||
pack_team.get("abbrev", "N/A"),
|
||||
pack_type.get("name", "Unknown"),
|
||||
is_opened,
|
||||
]
|
||||
)
|
||||
|
||||
title = "Packs"
|
||||
if team_name:
|
||||
@ -342,30 +379,54 @@ def pack_today():
|
||||
|
||||
@pack_app.command("distribute")
|
||||
def pack_distribute(
|
||||
num: Annotated[int, typer.Option("--num", "-n", help="Number of packs per team")] = 5,
|
||||
exclude: Annotated[Optional[List[str]], typer.Option("--exclude", "-x", help="Team abbrevs to exclude")] = None,
|
||||
pack_type: Annotated[int, typer.Option("--pack-type", help="1=Standard, 2=Starter, 3=Premium, 4=Check-In, 5=MVP, 6=All Star, 7=Mario, 8=Team Choice, 9=Promo Choice")] = 1,
|
||||
dry_run: Annotated[bool, typer.Option("--dry-run", help="Show what would be done")] = False,
|
||||
num: Annotated[
|
||||
int, typer.Option("--num", "-n", help="Number of packs per team")
|
||||
] = 5,
|
||||
exclude: Annotated[
|
||||
Optional[List[str]],
|
||||
typer.Option("--exclude", "-x", help="Team abbrevs to exclude"),
|
||||
] = None,
|
||||
pack_type: Annotated[
|
||||
int,
|
||||
typer.Option(
|
||||
"--pack-type",
|
||||
help="1=Standard, 2=Starter, 3=Premium, 4=Check-In, 5=MVP, 6=All Star, 7=Mario, 8=Team Choice, 9=Promo Choice",
|
||||
),
|
||||
] = 1,
|
||||
cardset: Annotated[
|
||||
Optional[int],
|
||||
typer.Option(
|
||||
"--cardset", "-c", help="Cardset ID (required for Promo Choice packs)"
|
||||
),
|
||||
] = None,
|
||||
dry_run: Annotated[
|
||||
bool, typer.Option("--dry-run", help="Show what would be done")
|
||||
] = False,
|
||||
):
|
||||
"""Distribute packs to all human teams"""
|
||||
try:
|
||||
if dry_run:
|
||||
# Get qualifying teams to show preview
|
||||
current = state.api.get('current')
|
||||
season = current['season']
|
||||
current = state.api.get("current")
|
||||
season = current["season"]
|
||||
all_teams = state.api.list_teams(season=season)
|
||||
|
||||
exclude_upper = [e.upper() for e in (exclude or [])]
|
||||
qualifying = [
|
||||
t for t in all_teams
|
||||
if not t['is_ai']
|
||||
and 'gauntlet' not in t['abbrev'].lower()
|
||||
and t['abbrev'].upper() not in exclude_upper
|
||||
t
|
||||
for t in all_teams
|
||||
if not t["is_ai"]
|
||||
and "gauntlet" not in t["abbrev"].lower()
|
||||
and t["abbrev"].upper() not in exclude_upper
|
||||
]
|
||||
|
||||
console.print(f"\n[bold cyan]Pack Distribution Preview (DRY RUN)[/bold cyan]\n")
|
||||
console.print(
|
||||
f"\n[bold cyan]Pack Distribution Preview (DRY RUN)[/bold cyan]\n"
|
||||
)
|
||||
console.print(f"[bold]Packs per team:[/bold] {num}")
|
||||
console.print(f"[bold]Pack type:[/bold] {pack_type}")
|
||||
if cardset is not None:
|
||||
console.print(f"[bold]Cardset ID:[/bold] {cardset}")
|
||||
console.print(f"[bold]Teams:[/bold] {len(qualifying)}")
|
||||
console.print(f"[bold]Total packs:[/bold] {num * len(qualifying)}")
|
||||
|
||||
@ -381,7 +442,8 @@ def pack_distribute(
|
||||
result = state.api.distribute_packs(
|
||||
num_packs=num,
|
||||
exclude_team_abbrev=exclude,
|
||||
pack_type_id=pack_type
|
||||
pack_type_id=pack_type,
|
||||
cardset_id=cardset,
|
||||
)
|
||||
|
||||
if state.json_output:
|
||||
@ -403,10 +465,15 @@ def pack_distribute(
|
||||
# Gauntlet Commands
|
||||
# ============================================================================
|
||||
|
||||
|
||||
@gauntlet_app.command("list")
|
||||
def gauntlet_list(
|
||||
event_id: Annotated[Optional[int], typer.Option("--event-id", "-e", help="Filter by event ID")] = None,
|
||||
active: Annotated[bool, typer.Option("--active", "-a", help="Only active runs")] = False,
|
||||
event_id: Annotated[
|
||||
Optional[int], typer.Option("--event-id", "-e", help="Filter by event ID")
|
||||
] = None,
|
||||
active: Annotated[
|
||||
bool, typer.Option("--active", "-a", help="Only active runs")
|
||||
] = False,
|
||||
):
|
||||
"""List gauntlet runs"""
|
||||
try:
|
||||
@ -422,17 +489,19 @@ def gauntlet_list(
|
||||
|
||||
rows = []
|
||||
for r in runs:
|
||||
team = r.get('team', {})
|
||||
is_active = "Active" if r.get('ended') is None else "Ended"
|
||||
gauntlet = r.get('gauntlet', {})
|
||||
rows.append([
|
||||
r['id'],
|
||||
team.get('abbrev', 'N/A'),
|
||||
r.get('wins', 0),
|
||||
r.get('losses', 0),
|
||||
gauntlet.get('id', 'N/A'),
|
||||
is_active
|
||||
])
|
||||
team = r.get("team", {})
|
||||
is_active = "Active" if r.get("ended") is None else "Ended"
|
||||
gauntlet = r.get("gauntlet", {})
|
||||
rows.append(
|
||||
[
|
||||
r["id"],
|
||||
team.get("abbrev", "N/A"),
|
||||
r.get("wins", 0),
|
||||
r.get("losses", 0),
|
||||
gauntlet.get("id", "N/A"),
|
||||
is_active,
|
||||
]
|
||||
)
|
||||
|
||||
title = "Gauntlet Runs"
|
||||
if event_id:
|
||||
@ -448,8 +517,12 @@ def gauntlet_list(
|
||||
|
||||
@gauntlet_app.command("teams")
|
||||
def gauntlet_teams(
|
||||
event_id: Annotated[Optional[int], typer.Option("--event-id", "-e", help="Filter by event ID")] = None,
|
||||
active: Annotated[bool, typer.Option("--active", "-a", help="Only teams with active runs")] = False,
|
||||
event_id: Annotated[
|
||||
Optional[int], typer.Option("--event-id", "-e", help="Filter by event ID")
|
||||
] = None,
|
||||
active: Annotated[
|
||||
bool, typer.Option("--active", "-a", help="Only teams with active runs")
|
||||
] = False,
|
||||
):
|
||||
"""List gauntlet teams"""
|
||||
try:
|
||||
@ -465,16 +538,10 @@ def gauntlet_teams(
|
||||
|
||||
rows = []
|
||||
for t in teams:
|
||||
run = t.get('active_run') or t.get('run', {})
|
||||
wins = run.get('wins', '-') if run else '-'
|
||||
losses = run.get('losses', '-') if run else '-'
|
||||
rows.append([
|
||||
t['id'],
|
||||
t['abbrev'],
|
||||
t.get('sname', ''),
|
||||
wins,
|
||||
losses
|
||||
])
|
||||
run = t.get("active_run") or t.get("run", {})
|
||||
wins = run.get("wins", "-") if run else "-"
|
||||
losses = run.get("losses", "-") if run else "-"
|
||||
rows.append([t["id"], t["abbrev"], t.get("sname", ""), wins, losses])
|
||||
|
||||
title = "Gauntlet Teams"
|
||||
if active:
|
||||
@ -488,29 +555,37 @@ def gauntlet_teams(
|
||||
|
||||
@gauntlet_app.command("cleanup")
|
||||
def gauntlet_cleanup(
|
||||
team_abbrev: Annotated[str, typer.Argument(help="Team abbreviation (e.g., Gauntlet-SKB)")],
|
||||
event_id: Annotated[int, typer.Option("--event-id", "-e", help="Event ID (required)")],
|
||||
team_abbrev: Annotated[
|
||||
str, typer.Argument(help="Team abbreviation (e.g., Gauntlet-SKB)")
|
||||
],
|
||||
event_id: Annotated[
|
||||
int, typer.Option("--event-id", "-e", help="Event ID (required)")
|
||||
],
|
||||
yes: Annotated[bool, typer.Option("--yes", "-y", help="Skip confirmation")] = False,
|
||||
):
|
||||
"""Clean up a gauntlet team (wipe cards, delete packs, end run)"""
|
||||
try:
|
||||
# Find the team
|
||||
team = state.api.get_team(abbrev=team_abbrev)
|
||||
team_id = team['id']
|
||||
team_id = team["id"]
|
||||
|
||||
# Get cards and packs count
|
||||
cards = state.api.list_cards(team_id=team_id)
|
||||
packs = state.api.list_packs(team_id=team_id, opened=False)
|
||||
|
||||
# Find active run
|
||||
runs = state.api.list_gauntlet_runs(event_id=event_id, team_id=team_id, active_only=True)
|
||||
runs = state.api.list_gauntlet_runs(
|
||||
event_id=event_id, team_id=team_id, active_only=True
|
||||
)
|
||||
active_run = runs[0] if runs else None
|
||||
|
||||
console.print(f"\n[bold cyan]Gauntlet Cleanup: {team_abbrev}[/bold cyan]\n")
|
||||
console.print(f"[bold]Team ID:[/bold] {team_id}")
|
||||
console.print(f"[bold]Cards to wipe:[/bold] {len(cards)}")
|
||||
console.print(f"[bold]Packs to delete:[/bold] {len(packs)}")
|
||||
console.print(f"[bold]Active run:[/bold] {'Yes (ID: ' + str(active_run['id']) + ')' if active_run else 'No'}")
|
||||
console.print(
|
||||
f"[bold]Active run:[/bold] {'Yes (ID: ' + str(active_run['id']) + ')' if active_run else 'No'}"
|
||||
)
|
||||
|
||||
if not yes:
|
||||
console.print("\n[yellow]This is a destructive operation![/yellow]")
|
||||
@ -527,13 +602,13 @@ def gauntlet_cleanup(
|
||||
|
||||
# 2. Delete packs
|
||||
for pack in packs:
|
||||
state.api.delete_pack(pack['id'])
|
||||
state.api.delete_pack(pack["id"])
|
||||
if packs:
|
||||
results.append(f"Deleted {len(packs)} packs")
|
||||
|
||||
# 3. End gauntlet run
|
||||
if active_run:
|
||||
state.api.end_gauntlet_run(active_run['id'])
|
||||
state.api.end_gauntlet_run(active_run["id"])
|
||||
results.append(f"Ended run {active_run['id']}")
|
||||
|
||||
console.print(f"\n[green]Cleanup complete![/green]")
|
||||
@ -551,6 +626,7 @@ def gauntlet_cleanup(
|
||||
# Player Commands
|
||||
# ============================================================================
|
||||
|
||||
|
||||
@player_app.command("get")
|
||||
def player_get(
|
||||
player_id: Annotated[int, typer.Argument(help="Player ID")],
|
||||
@ -566,13 +642,13 @@ def player_get(
|
||||
# Get positions
|
||||
positions = []
|
||||
for i in range(1, 9):
|
||||
pos = player.get(f'pos_{i}')
|
||||
pos = player.get(f"pos_{i}")
|
||||
if pos:
|
||||
positions.append(pos)
|
||||
|
||||
cardset = player.get('cardset', {})
|
||||
rarity = player.get('rarity', {})
|
||||
rarity_name = rarity.get('name', 'N/A') if isinstance(rarity, dict) else rarity
|
||||
cardset = player.get("cardset", {})
|
||||
rarity = player.get("rarity", {})
|
||||
rarity_name = rarity.get("name", "N/A") if isinstance(rarity, dict) else rarity
|
||||
|
||||
panel = Panel(
|
||||
f"[bold]ID:[/bold] {player['player_id']}\n"
|
||||
@ -593,9 +669,15 @@ def player_get(
|
||||
|
||||
@player_app.command("list")
|
||||
def player_list(
|
||||
rarity: Annotated[Optional[str], typer.Option("--rarity", "-r", help="Filter by rarity")] = None,
|
||||
cardset: Annotated[Optional[int], typer.Option("--cardset", "-c", help="Filter by cardset ID")] = None,
|
||||
limit: Annotated[int, typer.Option("--limit", "-n", help="Max players to show")] = 50,
|
||||
rarity: Annotated[
|
||||
Optional[str], typer.Option("--rarity", "-r", help="Filter by rarity")
|
||||
] = None,
|
||||
cardset: Annotated[
|
||||
Optional[int], typer.Option("--cardset", "-c", help="Filter by cardset ID")
|
||||
] = None,
|
||||
limit: Annotated[
|
||||
int, typer.Option("--limit", "-n", help="Max players to show")
|
||||
] = 50,
|
||||
):
|
||||
"""List players"""
|
||||
try:
|
||||
@ -611,16 +693,18 @@ def player_list(
|
||||
|
||||
rows = []
|
||||
for p in players[:limit]:
|
||||
cs = p.get('cardset', {})
|
||||
rarity = p.get('rarity', {})
|
||||
rarity_name = rarity.get('name', '') if isinstance(rarity, dict) else rarity
|
||||
rows.append([
|
||||
p['player_id'],
|
||||
p.get('p_name', 'Unknown'),
|
||||
cs = p.get("cardset", {})
|
||||
rarity = p.get("rarity", {})
|
||||
rarity_name = rarity.get("name", "") if isinstance(rarity, dict) else rarity
|
||||
rows.append(
|
||||
[
|
||||
p["player_id"],
|
||||
p.get("p_name", "Unknown"),
|
||||
rarity_name,
|
||||
p.get('cost', 0),
|
||||
cs.get('name', 'N/A')
|
||||
])
|
||||
p.get("cost", 0),
|
||||
cs.get("name", "N/A"),
|
||||
]
|
||||
)
|
||||
|
||||
title = "Players"
|
||||
if rarity:
|
||||
@ -631,7 +715,9 @@ def player_list(
|
||||
output_table(title, ["ID", "Name", "Rarity", "Cost", "Cardset"], rows)
|
||||
|
||||
if len(players) > limit:
|
||||
console.print(f"\n[dim]Showing {limit} of {len(players)} players. Use --limit to see more.[/dim]")
|
||||
console.print(
|
||||
f"\n[dim]Showing {limit} of {len(players)} players. Use --limit to see more.[/dim]"
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
handle_error(e)
|
||||
|
||||
@ -7,6 +7,7 @@ Works with both production and development environments.
|
||||
|
||||
This script uses the Paper Dynasty API client for all operations.
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import logging
|
||||
import os
|
||||
@ -18,23 +19,31 @@ sys.path.insert(0, str(Path(__file__).parent.parent))
|
||||
from api_client import PaperDynastyAPI
|
||||
|
||||
# Set up logging
|
||||
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')
|
||||
logger = logging.getLogger('pack_distribution')
|
||||
logging.basicConfig(
|
||||
level=logging.INFO, format="%(asctime)s - %(name)s - %(levelname)s - %(message)s"
|
||||
)
|
||||
logger = logging.getLogger("pack_distribution")
|
||||
|
||||
|
||||
def distribute_packs(num_packs: int = 5, exclude_team_abbrev: list[str] = None, pack_type_id: int = 1):
|
||||
def distribute_packs(
|
||||
num_packs: int = 5,
|
||||
exclude_team_abbrev: list[str] = None,
|
||||
pack_type_id: int = 1,
|
||||
cardset_id: int = None,
|
||||
):
|
||||
"""Distribute packs to all human-controlled teams using Paper Dynasty API client
|
||||
|
||||
Args:
|
||||
num_packs: Number of packs to give to each team (default: 5)
|
||||
exclude_team_abbrev: List of team abbreviations to exclude (default: None)
|
||||
pack_type_id: Pack type ID (default: 1 = Standard packs)
|
||||
cardset_id: Cardset ID for pack types that require it (e.g., Promo Choice = type 9)
|
||||
"""
|
||||
if exclude_team_abbrev is None:
|
||||
exclude_team_abbrev = []
|
||||
|
||||
# Get environment
|
||||
database_env = os.getenv('DATABASE', 'dev').lower()
|
||||
database_env = os.getenv("DATABASE", "dev").lower()
|
||||
|
||||
try:
|
||||
# Initialize API client
|
||||
@ -44,11 +53,14 @@ def distribute_packs(num_packs: int = 5, exclude_team_abbrev: list[str] = None,
|
||||
result = api.distribute_packs(
|
||||
num_packs=num_packs,
|
||||
exclude_team_abbrev=exclude_team_abbrev,
|
||||
pack_type_id=pack_type_id
|
||||
pack_type_id=pack_type_id,
|
||||
cardset_id=cardset_id,
|
||||
)
|
||||
|
||||
# Log final summary
|
||||
logger.info(f"\n🎉 All done! Distributed {result['total_packs']} packs to {result['teams_count']} teams")
|
||||
logger.info(
|
||||
f"\n🎉 All done! Distributed {result['total_packs']} packs to {result['teams_count']} teams"
|
||||
)
|
||||
|
||||
except ValueError as e:
|
||||
logger.error(f"Configuration error: {e}")
|
||||
@ -58,9 +70,9 @@ def distribute_packs(num_packs: int = 5, exclude_team_abbrev: list[str] = None,
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
if __name__ == "__main__":
|
||||
parser = argparse.ArgumentParser(
|
||||
description='Distribute packs to all human-controlled teams',
|
||||
description="Distribute packs to all human-controlled teams",
|
||||
formatter_class=argparse.RawDescriptionHelpFormatter,
|
||||
epilog="""
|
||||
Examples:
|
||||
@ -85,25 +97,31 @@ Examples:
|
||||
Environment Variables:
|
||||
API_TOKEN - Required: API authentication token
|
||||
DATABASE - Optional: 'dev' (default) or 'prod'
|
||||
"""
|
||||
""",
|
||||
)
|
||||
parser.add_argument(
|
||||
'--num-packs',
|
||||
"--num-packs",
|
||||
type=int,
|
||||
default=5,
|
||||
help='Number of packs to give to each team (default: 5)'
|
||||
help="Number of packs to give to each team (default: 5)",
|
||||
)
|
||||
parser.add_argument(
|
||||
'--exclude-team-abbrev',
|
||||
nargs='*',
|
||||
"--exclude-team-abbrev",
|
||||
nargs="*",
|
||||
default=[],
|
||||
help='Team abbreviations to exclude (space-separated, e.g., NYY BOS LAD)'
|
||||
help="Team abbreviations to exclude (space-separated, e.g., NYY BOS LAD)",
|
||||
)
|
||||
parser.add_argument(
|
||||
'--pack-type-id',
|
||||
"--pack-type-id",
|
||||
type=int,
|
||||
default=1,
|
||||
help='Pack type ID (default: 1 = Standard packs)'
|
||||
help="Pack type ID (default: 1 = Standard packs)",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--cardset-id",
|
||||
type=int,
|
||||
default=None,
|
||||
help="Cardset ID for pack types that require it (e.g., Promo Choice = type 9)",
|
||||
)
|
||||
|
||||
args = parser.parse_args()
|
||||
@ -111,5 +129,6 @@ Environment Variables:
|
||||
distribute_packs(
|
||||
num_packs=args.num_packs,
|
||||
exclude_team_abbrev=args.exclude_team_abbrev,
|
||||
pack_type_id=args.pack_type_id
|
||||
pack_type_id=args.pack_type_id,
|
||||
cardset_id=args.cardset_id,
|
||||
)
|
||||
|
||||
1
skills/resume-tailoring
Submodule
1
skills/resume-tailoring
Submodule
@ -0,0 +1 @@
|
||||
Subproject commit 477341020c40fdb547ac2e4b50fa8e22cd36547b
|
||||
Loading…
Reference in New Issue
Block a user