gms-mcp 0.1.0.post1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (116) hide show
  1. gms_mcp-0.1.0.post1/.github/workflows/publish.yml +58 -0
  2. gms_mcp-0.1.0.post1/.gitignore +15 -0
  3. gms_mcp-0.1.0.post1/LICENSE +22 -0
  4. gms_mcp-0.1.0.post1/PKG-INFO +83 -0
  5. gms_mcp-0.1.0.post1/README.md +60 -0
  6. gms_mcp-0.1.0.post1/RELEASING.md +47 -0
  7. gms_mcp-0.1.0.post1/cli/__init__.py +1 -0
  8. gms_mcp-0.1.0.post1/cli/agent_setup.py +32 -0
  9. gms_mcp-0.1.0.post1/cli/docs/CLI_HELPER_TOOLS.md +936 -0
  10. gms_mcp-0.1.0.post1/cli/docs/README.md +240 -0
  11. gms_mcp-0.1.0.post1/cli/docs/TEST_SUITE_GUIDE.md +243 -0
  12. gms_mcp-0.1.0.post1/cli/gms +14 -0
  13. gms_mcp-0.1.0.post1/cli/gms.bat +16 -0
  14. gms_mcp-0.1.0.post1/cli/install.py +30 -0
  15. gms_mcp-0.1.0.post1/cli/reports/CLI_CRITICAL_FIXES_REPORT.md +188 -0
  16. gms_mcp-0.1.0.post1/cli/reports/CLI_DIRECTORY_VALIDATION_IMPLEMENTATION.md +188 -0
  17. gms_mcp-0.1.0.post1/cli/reports/CLI_TOOL_IMPROVEMENTS_REPORT.md +241 -0
  18. gms_mcp-0.1.0.post1/cli/reports/CONSTRUCTOR_VALIDATION_IMPLEMENTATION_SUMMARY.md +153 -0
  19. gms_mcp-0.1.0.post1/cli/test_config.py +79 -0
  20. gms_mcp-0.1.0.post1/cli/tests/python/agent_setup.py +20 -0
  21. gms_mcp-0.1.0.post1/cli/tests/python/asset_helper.py +25 -0
  22. gms_mcp-0.1.0.post1/cli/tests/python/commands/__init__.py +5 -0
  23. gms_mcp-0.1.0.post1/cli/tests/python/commands/asset_commands.py +49 -0
  24. gms_mcp-0.1.0.post1/cli/tests/python/commands/event_commands.py +37 -0
  25. gms_mcp-0.1.0.post1/cli/tests/python/commands/maintenance_commands.py +67 -0
  26. gms_mcp-0.1.0.post1/cli/tests/python/commands/room_commands.py +80 -0
  27. gms_mcp-0.1.0.post1/cli/tests/python/commands/runner_commands.py +30 -0
  28. gms_mcp-0.1.0.post1/cli/tests/python/commands/workflow_commands.py +31 -0
  29. gms_mcp-0.1.0.post1/cli/tests/python/conftest.py +16 -0
  30. gms_mcp-0.1.0.post1/cli/tests/python/debug_validate_json.py +1 -0
  31. gms_mcp-0.1.0.post1/cli/tests/python/event_helper.py +20 -0
  32. gms_mcp-0.1.0.post1/cli/tests/python/gms.py +20 -0
  33. gms_mcp-0.1.0.post1/cli/tests/python/room_helper.py +20 -0
  34. gms_mcp-0.1.0.post1/cli/tests/python/room_instance_helper.py +44 -0
  35. gms_mcp-0.1.0.post1/cli/tests/python/room_layer_helper.py +38 -0
  36. gms_mcp-0.1.0.post1/cli/tests/python/run_all_tests.py +171 -0
  37. gms_mcp-0.1.0.post1/cli/tests/python/test_agent_setup.py +329 -0
  38. gms_mcp-0.1.0.post1/cli/tests/python/test_all_phases.py +463 -0
  39. gms_mcp-0.1.0.post1/cli/tests/python/test_asset_helper.py +629 -0
  40. gms_mcp-0.1.0.post1/cli/tests/python/test_assets_comprehensive.py +1047 -0
  41. gms_mcp-0.1.0.post1/cli/tests/python/test_auto_maintenance_comprehensive.py +1545 -0
  42. gms_mcp-0.1.0.post1/cli/tests/python/test_command_modules_comprehensive.py +1001 -0
  43. gms_mcp-0.1.0.post1/cli/tests/python/test_config.py +38 -0
  44. gms_mcp-0.1.0.post1/cli/tests/python/test_directory_validation_fixed.py +290 -0
  45. gms_mcp-0.1.0.post1/cli/tests/python/test_event_helper.py +172 -0
  46. gms_mcp-0.1.0.post1/cli/tests/python/test_event_validation.py +348 -0
  47. gms_mcp-0.1.0.post1/cli/tests/python/test_final_verification.py +85 -0
  48. gms_mcp-0.1.0.post1/cli/tests/python/test_master_cli.py +148 -0
  49. gms_mcp-0.1.0.post1/cli/tests/python/test_room_instance_helper.py +919 -0
  50. gms_mcp-0.1.0.post1/cli/tests/python/test_room_layer_helper.py +658 -0
  51. gms_mcp-0.1.0.post1/cli/tests/python/test_room_operations.py +504 -0
  52. gms_mcp-0.1.0.post1/cli/tests/python/test_utils_comprehensive.py +1025 -0
  53. gms_mcp-0.1.0.post1/cli/tests/python/test_workflow.py +66 -0
  54. gms_mcp-0.1.0.post1/cli/tests/python/test_workflow_enhanced.py +551 -0
  55. gms_mcp-0.1.0.post1/cli/tests/python/utils.py +34 -0
  56. gms_mcp-0.1.0.post1/cli/update_test_imports.py +196 -0
  57. gms_mcp-0.1.0.post1/pyproject.toml +57 -0
  58. gms_mcp-0.1.0.post1/setup.cfg +4 -0
  59. gms_mcp-0.1.0.post1/src/gms_helpers/__init__.py +2 -0
  60. gms_mcp-0.1.0.post1/src/gms_helpers/__main__.py +7 -0
  61. gms_mcp-0.1.0.post1/src/gms_helpers/agent_setup.py +154 -0
  62. gms_mcp-0.1.0.post1/src/gms_helpers/asset_helper.py +1928 -0
  63. gms_mcp-0.1.0.post1/src/gms_helpers/assets.py +1220 -0
  64. gms_mcp-0.1.0.post1/src/gms_helpers/auto_maintenance.py +615 -0
  65. gms_mcp-0.1.0.post1/src/gms_helpers/base_asset.py +80 -0
  66. gms_mcp-0.1.0.post1/src/gms_helpers/cli.py +8 -0
  67. gms_mcp-0.1.0.post1/src/gms_helpers/commands/__init__.py +1 -0
  68. gms_mcp-0.1.0.post1/src/gms_helpers/commands/asset_commands.py +41 -0
  69. gms_mcp-0.1.0.post1/src/gms_helpers/commands/event_commands.py +29 -0
  70. gms_mcp-0.1.0.post1/src/gms_helpers/commands/maintenance_commands.py +58 -0
  71. gms_mcp-0.1.0.post1/src/gms_helpers/commands/room_commands.py +71 -0
  72. gms_mcp-0.1.0.post1/src/gms_helpers/commands/runner_commands.py +107 -0
  73. gms_mcp-0.1.0.post1/src/gms_helpers/commands/workflow_commands.py +25 -0
  74. gms_mcp-0.1.0.post1/src/gms_helpers/config.py +35 -0
  75. gms_mcp-0.1.0.post1/src/gms_helpers/event_helper.py +748 -0
  76. gms_mcp-0.1.0.post1/src/gms_helpers/gms.py +597 -0
  77. gms_mcp-0.1.0.post1/src/gms_helpers/install.py +148 -0
  78. gms_mcp-0.1.0.post1/src/gms_helpers/maintenance/__init__.py +33 -0
  79. gms_mcp-0.1.0.post1/src/gms_helpers/maintenance/audit/__init__.py +19 -0
  80. gms_mcp-0.1.0.post1/src/gms_helpers/maintenance/audit/reference_collector.py +468 -0
  81. gms_mcp-0.1.0.post1/src/gms_helpers/maintenance/clean_unused_assets.py +186 -0
  82. gms_mcp-0.1.0.post1/src/gms_helpers/maintenance/event_sync.py +452 -0
  83. gms_mcp-0.1.0.post1/src/gms_helpers/maintenance/lint.py +268 -0
  84. gms_mcp-0.1.0.post1/src/gms_helpers/maintenance/orphan_cleanup.py +273 -0
  85. gms_mcp-0.1.0.post1/src/gms_helpers/maintenance/orphans.py +192 -0
  86. gms_mcp-0.1.0.post1/src/gms_helpers/maintenance/path_utils.py +108 -0
  87. gms_mcp-0.1.0.post1/src/gms_helpers/maintenance/prune.py +173 -0
  88. gms_mcp-0.1.0.post1/src/gms_helpers/maintenance/static_search.py +202 -0
  89. gms_mcp-0.1.0.post1/src/gms_helpers/maintenance/tidy_json.py +93 -0
  90. gms_mcp-0.1.0.post1/src/gms_helpers/maintenance/trash/__init__.py +15 -0
  91. gms_mcp-0.1.0.post1/src/gms_helpers/maintenance/validate_paths.py +304 -0
  92. gms_mcp-0.1.0.post1/src/gms_helpers/reference_scanner.py +472 -0
  93. gms_mcp-0.1.0.post1/src/gms_helpers/room_helper.py +209 -0
  94. gms_mcp-0.1.0.post1/src/gms_helpers/room_instance_helper.py +388 -0
  95. gms_mcp-0.1.0.post1/src/gms_helpers/room_layer_helper.py +315 -0
  96. gms_mcp-0.1.0.post1/src/gms_helpers/runner.py +564 -0
  97. gms_mcp-0.1.0.post1/src/gms_helpers/templates/ui_gameover_killed_by/label.create.gml +13 -0
  98. gms_mcp-0.1.0.post1/src/gms_helpers/templates/ui_gameover_killed_by/name.create.gml +8 -0
  99. gms_mcp-0.1.0.post1/src/gms_helpers/templates/ui_gameover_killed_by/sprite.create.gml +5 -0
  100. gms_mcp-0.1.0.post1/src/gms_helpers/test_invalid.json +1 -0
  101. gms_mcp-0.1.0.post1/src/gms_helpers/utils.py +734 -0
  102. gms_mcp-0.1.0.post1/src/gms_helpers/workflow.py +397 -0
  103. gms_mcp-0.1.0.post1/src/gms_mcp/README.md +54 -0
  104. gms_mcp-0.1.0.post1/src/gms_mcp/__init__.py +5 -0
  105. gms_mcp-0.1.0.post1/src/gms_mcp/__main__.py +7 -0
  106. gms_mcp-0.1.0.post1/src/gms_mcp/bootstrap_server.py +33 -0
  107. gms_mcp-0.1.0.post1/src/gms_mcp/cli.py +14 -0
  108. gms_mcp-0.1.0.post1/src/gms_mcp/gamemaker_mcp_server.py +2421 -0
  109. gms_mcp-0.1.0.post1/src/gms_mcp/install.py +392 -0
  110. gms_mcp-0.1.0.post1/src/gms_mcp/requirements.txt +3 -0
  111. gms_mcp-0.1.0.post1/src/gms_mcp.egg-info/PKG-INFO +83 -0
  112. gms_mcp-0.1.0.post1/src/gms_mcp.egg-info/SOURCES.txt +114 -0
  113. gms_mcp-0.1.0.post1/src/gms_mcp.egg-info/dependency_links.txt +1 -0
  114. gms_mcp-0.1.0.post1/src/gms_mcp.egg-info/entry_points.txt +4 -0
  115. gms_mcp-0.1.0.post1/src/gms_mcp.egg-info/requires.txt +1 -0
  116. gms_mcp-0.1.0.post1/src/gms_mcp.egg-info/top_level.txt +2 -0
@@ -0,0 +1,58 @@
1
+ name: Publish to PyPI
2
+
3
+ concurrency:
4
+ group: pypi-publish
5
+ cancel-in-progress: false
6
+
7
+ on:
8
+ push:
9
+ branches:
10
+ - "main"
11
+ tags:
12
+ - "v*"
13
+
14
+ jobs:
15
+ build:
16
+ runs-on: ubuntu-latest
17
+ steps:
18
+ - uses: actions/checkout@v4
19
+ with:
20
+ fetch-depth: 0
21
+ - uses: actions/setup-python@v5
22
+ with:
23
+ python-version: "3.12"
24
+ - name: Install build tooling
25
+ run: python -m pip install -U build
26
+ - name: Build
27
+ run: python -m build
28
+ - name: Upload build artifacts
29
+ uses: actions/upload-artifact@v4
30
+ with:
31
+ name: dist
32
+ path: dist/*
33
+
34
+ publish:
35
+ needs: build
36
+ runs-on: ubuntu-latest
37
+ permissions:
38
+ id-token: write
39
+ steps:
40
+ - uses: actions/checkout@v4
41
+ - uses: actions/download-artifact@v4
42
+ with:
43
+ name: dist
44
+ path: dist
45
+ - name: Verify PyPI project exists
46
+ shell: bash
47
+ run: |
48
+ set -euo pipefail
49
+ PROJECT="$(python -c 'import tomllib, pathlib; print(tomllib.loads(pathlib.Path(\"pyproject.toml\").read_bytes())[\"project\"][\"name\"])')"
50
+ CODE="$(curl -sS -o /dev/null -w "%{http_code}" "https://pypi.org/pypi/${PROJECT}/json" || true)"
51
+ if [ "${CODE}" != "200" ]; then
52
+ echo "::error::PyPI project '${PROJECT}' does not exist yet (HTTP ${CODE})."
53
+ echo "::error::You must do the first upload manually (user identity) before Trusted Publishing can work."
54
+ echo "::error::Run scripts/first_pypi_upload.ps1 (Windows) or scripts/first_pypi_upload.sh (macOS/Linux)."
55
+ exit 1
56
+ fi
57
+ - name: Publish to PyPI (Trusted Publishing)
58
+ uses: pypa/gh-action-pypi-publish@release/v1
@@ -0,0 +1,15 @@
1
+ __pycache__/
2
+ *.py[cod]
3
+ *.pyo
4
+
5
+ .venv/
6
+ venv/
7
+ .gms-mcp/
8
+
9
+ .pytest_cache/
10
+ .mypy_cache/
11
+ .ruff_cache/
12
+
13
+ build/
14
+ dist/
15
+ *.egg-info/
@@ -0,0 +1,22 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Ampersand Game Studios
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
22
+
@@ -0,0 +1,83 @@
1
+ Metadata-Version: 2.4
2
+ Name: gms-mcp
3
+ Version: 0.1.0.post1
4
+ Summary: GameMaker CLI + MCP server toolset
5
+ Author: Callum Lory, Ampersand Game Studios
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/Ampersand-Game-Studios/gms-mcp
8
+ Project-URL: Repository, https://github.com/Ampersand-Game-Studios/gms-mcp
9
+ Project-URL: Issues, https://github.com/Ampersand-Game-Studios/gms-mcp/issues
10
+ Keywords: gamemaker,mcp,cursor,cli,tools
11
+ Classifier: Programming Language :: Python :: 3
12
+ Classifier: Programming Language :: Python :: 3 :: Only
13
+ Classifier: Programming Language :: Python :: 3.9
14
+ Classifier: Programming Language :: Python :: 3.10
15
+ Classifier: Programming Language :: Python :: 3.11
16
+ Classifier: Programming Language :: Python :: 3.12
17
+ Classifier: Programming Language :: Python :: 3.13
18
+ Requires-Python: >=3.9
19
+ Description-Content-Type: text/markdown
20
+ License-File: LICENSE
21
+ Requires-Dist: mcp>=1.0.0
22
+ Dynamic: license-file
23
+
24
+ # GameMaker MCP Tools
25
+
26
+ This repo provides:
27
+ - `gms`: a Python CLI for GameMaker project operations (asset creation, maintenance, runner, etc).
28
+ - `gms-mcp`: an MCP server that exposes the same operations as MCP tools (Cursor is the primary example client).
29
+ - `gms-mcp-init`: generates shareable MCP config files for a workspace.
30
+
31
+ ## Install (recommended: pipx)
32
+
33
+ ```powershell
34
+ pipx install gms-mcp
35
+ ```
36
+
37
+ ## Publishing (maintainers)
38
+
39
+ Publishing is automated via GitHub Actions (PyPI Trusted Publishing) on every push to `main` and on tags `v*`.
40
+ See `RELEASING.md` for the one-time PyPI setup and the first manual upload helper scripts.
41
+
42
+ ## Use with a GameMaker project (multi-project friendly)
43
+
44
+ Run this inside each GameMaker project workspace (or repo) to generate config:
45
+
46
+ ```powershell
47
+ gms-mcp-init --cursor
48
+ ```
49
+
50
+ This writes `.cursor/mcp.json` and attempts to auto-detect the `.yyp` location to set `GM_PROJECT_ROOT`.
51
+
52
+ For a one-time setup that works across many projects, write Cursor's global config instead:
53
+
54
+ ```powershell
55
+ gms-mcp-init --cursor-global
56
+ ```
57
+
58
+ ## Monorepos / multiple `.yyp`
59
+
60
+ If multiple `.yyp` projects are detected in a workspace:
61
+ - `gms-mcp-init` will warn and (when interactive) prompt you to pick one.
62
+ - In non-interactive environments, it defaults `GM_PROJECT_ROOT` to `${workspaceFolder}` (safe).
63
+
64
+ Force a specific project root:
65
+
66
+ ```powershell
67
+ gms-mcp-init --cursor --gm-project-root path\\to\\project
68
+ ```
69
+
70
+ Preview output without writing files:
71
+
72
+ ```powershell
73
+ gms-mcp-init --cursor --dry-run
74
+ ```
75
+
76
+ ## CLI usage
77
+
78
+ Run from a project directory (or pass `--project-root`):
79
+
80
+ ```powershell
81
+ gms --version
82
+ gms --project-root . asset create script my_function --parent-path "folders/Scripts.yy"
83
+ ```
@@ -0,0 +1,60 @@
1
+ # GameMaker MCP Tools
2
+
3
+ This repo provides:
4
+ - `gms`: a Python CLI for GameMaker project operations (asset creation, maintenance, runner, etc).
5
+ - `gms-mcp`: an MCP server that exposes the same operations as MCP tools (Cursor is the primary example client).
6
+ - `gms-mcp-init`: generates shareable MCP config files for a workspace.
7
+
8
+ ## Install (recommended: pipx)
9
+
10
+ ```powershell
11
+ pipx install gms-mcp
12
+ ```
13
+
14
+ ## Publishing (maintainers)
15
+
16
+ Publishing is automated via GitHub Actions (PyPI Trusted Publishing) on every push to `main` and on tags `v*`.
17
+ See `RELEASING.md` for the one-time PyPI setup and the first manual upload helper scripts.
18
+
19
+ ## Use with a GameMaker project (multi-project friendly)
20
+
21
+ Run this inside each GameMaker project workspace (or repo) to generate config:
22
+
23
+ ```powershell
24
+ gms-mcp-init --cursor
25
+ ```
26
+
27
+ This writes `.cursor/mcp.json` and attempts to auto-detect the `.yyp` location to set `GM_PROJECT_ROOT`.
28
+
29
+ For a one-time setup that works across many projects, write Cursor's global config instead:
30
+
31
+ ```powershell
32
+ gms-mcp-init --cursor-global
33
+ ```
34
+
35
+ ## Monorepos / multiple `.yyp`
36
+
37
+ If multiple `.yyp` projects are detected in a workspace:
38
+ - `gms-mcp-init` will warn and (when interactive) prompt you to pick one.
39
+ - In non-interactive environments, it defaults `GM_PROJECT_ROOT` to `${workspaceFolder}` (safe).
40
+
41
+ Force a specific project root:
42
+
43
+ ```powershell
44
+ gms-mcp-init --cursor --gm-project-root path\\to\\project
45
+ ```
46
+
47
+ Preview output without writing files:
48
+
49
+ ```powershell
50
+ gms-mcp-init --cursor --dry-run
51
+ ```
52
+
53
+ ## CLI usage
54
+
55
+ Run from a project directory (or pass `--project-root`):
56
+
57
+ ```powershell
58
+ gms --version
59
+ gms --project-root . asset create script my_function --parent-path "folders/Scripts.yy"
60
+ ```
@@ -0,0 +1,47 @@
1
+ # Releasing `gms-mcp`
2
+
3
+ This project uses `setuptools-scm` to generate the package version from git tags.
4
+
5
+ - Tag releases like `v0.1.0`, `v0.2.0`, etc.
6
+ - Commits after a tag become automatic post-releases, e.g. `0.1.0.post3`.
7
+ This supports publishing on every `main` commit without manually bumping versions.
8
+
9
+ ## One-time: install tooling
10
+
11
+ ```bash
12
+ python -m pip install -U build twine
13
+ ```
14
+
15
+ ## Local build
16
+
17
+ ```bash
18
+ python -m build
19
+ ```
20
+
21
+ Artifacts are created in `dist/`.
22
+
23
+ ## Publish to PyPI
24
+
25
+ ```bash
26
+ twine upload dist/*
27
+ ```
28
+
29
+ ### First publish helper scripts
30
+
31
+ - Windows: `scripts/first_pypi_upload.ps1`
32
+ - macOS/Linux: `scripts/first_pypi_upload.sh`
33
+
34
+ These build, validate, and upload from `dist/` using a PyPI API token.
35
+
36
+ ## Recommended: GitHub “Trusted Publishing”
37
+
38
+ This repo can publish to PyPI via GitHub Actions without storing an API token.
39
+
40
+ High level steps:
41
+ 1. Create the project on PyPI (`gms-mcp`).
42
+ 2. In PyPI → “Publishing” → “Trusted publishers”, add this GitHub repo and workflow.
43
+ 3. Push to `main` to publish a post-release automatically, or push a version tag like `v0.1.0` to set a new base version.
44
+
45
+ Notes:
46
+ - The GitHub Actions workflow publishes on every push to `main` (as requested). This will create many versions on PyPI.
47
+ - If you want fewer releases, change the workflow trigger to tags-only.
@@ -0,0 +1 @@
1
+ # CLI support utilities live under this directory.
@@ -0,0 +1,32 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Convenience entrypoint for setting up the `gms` command.
4
+
5
+ For releases, prefer `gms` and `gms-mcp-init` entrypoints.
6
+ """
7
+
8
+ from pathlib import Path
9
+ import sys
10
+
11
+ HERE = Path(__file__).resolve().parent
12
+ REPO_ROOT = HERE.parent
13
+ SRC = REPO_ROOT / "src"
14
+ if str(SRC) not in sys.path:
15
+ sys.path.insert(0, str(SRC))
16
+
17
+ # Re-export the full implementation so `import agent_setup` behaves like the
18
+ # original module (some tests/importers expect helper functions to exist).
19
+ from gms_helpers.agent_setup import * # type: ignore # noqa: F403,E402
20
+ from gms_helpers.agent_setup import main as _main # noqa: E402
21
+
22
+ if __name__ == "__main__":
23
+ raise SystemExit(0 if _main() else 1)
24
+
25
+
26
+
27
+
28
+
29
+
30
+
31
+
32
+