rosh-lang 0.9.3__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.
- rosh_lang-0.9.3/.github/workflows/ci.yml +69 -0
- rosh_lang-0.9.3/.gitignore +36 -0
- rosh_lang-0.9.3/CHANGELOG.md +91 -0
- rosh_lang-0.9.3/CLAUDE.md +263 -0
- rosh_lang-0.9.3/CONTRIBUTING.md +80 -0
- rosh_lang-0.9.3/LANGUAGE.md +819 -0
- rosh_lang-0.9.3/LICENSE +27 -0
- rosh_lang-0.9.3/PKG-INFO +630 -0
- rosh_lang-0.9.3/README.md +603 -0
- rosh_lang-0.9.3/SECURITY.md +14 -0
- rosh_lang-0.9.3/editor/vscode/.vscodeignore +4 -0
- rosh_lang-0.9.3/editor/vscode/LICENSE +21 -0
- rosh_lang-0.9.3/editor/vscode/README.md +31 -0
- rosh_lang-0.9.3/editor/vscode/install.sh +11 -0
- rosh_lang-0.9.3/editor/vscode/language-configuration.json +31 -0
- rosh_lang-0.9.3/editor/vscode/package.json +58 -0
- rosh_lang-0.9.3/editor/vscode/snippets/rosh.json +100 -0
- rosh_lang-0.9.3/editor/vscode/syntaxes/rosh.tmLanguage.json +104 -0
- rosh_lang-0.9.3/examples/breakout.rosh +105 -0
- rosh_lang-0.9.3/examples/checkpoint1.rosh +3 -0
- rosh_lang-0.9.3/examples/checkpoint2.rosh +5 -0
- rosh_lang-0.9.3/examples/click.rosh +18 -0
- rosh_lang-0.9.3/examples/composed.rosh +12 -0
- rosh_lang-0.9.3/examples/counter.rosh +4 -0
- rosh_lang-0.9.3/examples/dashboard.rosh +112 -0
- rosh_lang-0.9.3/examples/dodge.rosh +63 -0
- rosh_lang-0.9.3/examples/game.rosh +16 -0
- rosh_lang-0.9.3/examples/greeting.rosh +75 -0
- rosh_lang-0.9.3/examples/hello.rosh +1 -0
- rosh_lang-0.9.3/examples/player.rosh +6 -0
- rosh_lang-0.9.3/examples/pong.rosh +278 -0
- rosh_lang-0.9.3/examples/reference-game.rosh +77 -0
- rosh_lang-0.9.3/examples/scratch-ball.rosh +65 -0
- rosh_lang-0.9.3/examples/showcase/01-hello.rosh +22 -0
- rosh_lang-0.9.3/examples/showcase/02-click.rosh +20 -0
- rosh_lang-0.9.3/examples/showcase/03-keyboard.rosh +38 -0
- rosh_lang-0.9.3/examples/showcase/04-traffic.rosh +37 -0
- rosh_lang-0.9.3/examples/showcase/05-widgets.rosh +13 -0
- rosh_lang-0.9.3/examples/showcase/06-game.rosh +72 -0
- rosh_lang-0.9.3/examples/showcase/07-composed-game.rosh +37 -0
- rosh_lang-0.9.3/examples/showcase/08-sound-fx.rosh +88 -0
- rosh_lang-0.9.3/examples/showcase/09-enemies.rosh +62 -0
- rosh_lang-0.9.3/examples/showcase/10-scenes.rosh +74 -0
- rosh_lang-0.9.3/examples/showcase/11-animation.rosh +29 -0
- rosh_lang-0.9.3/examples/showcase/12-breakout.rosh +104 -0
- rosh_lang-0.9.3/examples/showcase/13-dodge.rosh +60 -0
- rosh_lang-0.9.3/examples/showcase/14-whack.rosh +146 -0
- rosh_lang-0.9.3/examples/showcase/15-pong.rosh +229 -0
- rosh_lang-0.9.3/examples/showcase/16-functions.rosh +90 -0
- rosh_lang-0.9.3/examples/showcase/17-repeat.rosh +29 -0
- rosh_lang-0.9.3/examples/showcase/18-reference-game.rosh +75 -0
- rosh_lang-0.9.3/examples/showcase/19-asset-defaults.rosh +14 -0
- rosh_lang-0.9.3/examples/showcase/20-museum-assets.rosh +184 -0
- rosh_lang-0.9.3/examples/showcase/player-sheet.png +0 -0
- rosh_lang-0.9.3/examples/showcase/threejs-hello.rosh +39 -0
- rosh_lang-0.9.3/examples/space-invaders.rosh +51 -0
- rosh_lang-0.9.3/examples/traffic-light.rosh +90 -0
- rosh_lang-0.9.3/examples/url-sprites-demo.rosh +78 -0
- rosh_lang-0.9.3/examples/whack.rosh +221 -0
- rosh_lang-0.9.3/examples/widgets/bullet.rosh +2 -0
- rosh_lang-0.9.3/examples/widgets/coin.rosh +2 -0
- rosh_lang-0.9.3/examples/widgets/counter.rosh +11 -0
- rosh_lang-0.9.3/examples/widgets/enemy-grid.rosh +2 -0
- rosh_lang-0.9.3/examples/widgets/explosion.rosh +2 -0
- rosh_lang-0.9.3/examples/widgets/grid.rosh +2 -0
- rosh_lang-0.9.3/examples/widgets/message.rosh +2 -0
- rosh_lang-0.9.3/examples/widgets/player.rosh +14 -0
- rosh_lang-0.9.3/examples/widgets/score.rosh +14 -0
- rosh_lang-0.9.3/examples/widgets/starfield.rosh +2 -0
- rosh_lang-0.9.3/examples/widgets/title-screen.rosh +2 -0
- rosh_lang-0.9.3/pyproject.toml +58 -0
- rosh_lang-0.9.3/src/rosh_lang/__init__.py +3 -0
- rosh_lang-0.9.3/src/rosh_lang/__main__.py +286 -0
- rosh_lang-0.9.3/src/rosh_lang/assets.py +4 -0
- rosh_lang-0.9.3/src/rosh_lang/cli/__init__.py +2 -0
- rosh_lang-0.9.3/src/rosh_lang/cli/assets_cli.py +412 -0
- rosh_lang-0.9.3/src/rosh_lang/cli/cloud.py +496 -0
- rosh_lang-0.9.3/src/rosh_lang/cli/library_cli.py +116 -0
- rosh_lang-0.9.3/src/rosh_lang/cli/scaffolder.py +213 -0
- rosh_lang-0.9.3/src/rosh_lang/cloud.py +4 -0
- rosh_lang-0.9.3/src/rosh_lang/core/__init__.py +2 -0
- rosh_lang-0.9.3/src/rosh_lang/core/model.py +355 -0
- rosh_lang-0.9.3/src/rosh_lang/core/normalise.py +295 -0
- rosh_lang-0.9.3/src/rosh_lang/core/parser.py +1112 -0
- rosh_lang-0.9.3/src/rosh_lang/core/runtime.py +1278 -0
- rosh_lang-0.9.3/src/rosh_lang/core/widgets.py +777 -0
- rosh_lang-0.9.3/src/rosh_lang/intent/__init__.py +6 -0
- rosh_lang-0.9.3/src/rosh_lang/intent/planner.py +252 -0
- rosh_lang-0.9.3/src/rosh_lang/intent/prompts.py +81 -0
- rosh_lang-0.9.3/src/rosh_lang/intent/providers.py +56 -0
- rosh_lang-0.9.3/src/rosh_lang/intent/settings.py +80 -0
- rosh_lang-0.9.3/src/rosh_lang/intent/validate.py +36 -0
- rosh_lang-0.9.3/src/rosh_lang/library/__init__.py +1 -0
- rosh_lang-0.9.3/src/rosh_lang/library/animation.py +40 -0
- rosh_lang-0.9.3/src/rosh_lang/library/ball.py +116 -0
- rosh_lang-0.9.3/src/rosh_lang/library/bullet.py +86 -0
- rosh_lang-0.9.3/src/rosh_lang/library/button.rosh +13 -0
- rosh_lang-0.9.3/src/rosh_lang/library/coin.rosh +15 -0
- rosh_lang-0.9.3/src/rosh_lang/library/controller.py +277 -0
- rosh_lang-0.9.3/src/rosh_lang/library/counter.rosh +13 -0
- rosh_lang-0.9.3/src/rosh_lang/library/enemy-grid.py +99 -0
- rosh_lang-0.9.3/src/rosh_lang/library/explosion.py +121 -0
- rosh_lang-0.9.3/src/rosh_lang/library/fps.py +49 -0
- rosh_lang-0.9.3/src/rosh_lang/library/game-lifecycle.rosh +76 -0
- rosh_lang-0.9.3/src/rosh_lang/library/grid.py +38 -0
- rosh_lang-0.9.3/src/rosh_lang/library/hazard.py +95 -0
- rosh_lang-0.9.3/src/rosh_lang/library/health-bar.py +55 -0
- rosh_lang-0.9.3/src/rosh_lang/library/label.rosh +16 -0
- rosh_lang-0.9.3/src/rosh_lang/library/lives.py +69 -0
- rosh_lang-0.9.3/src/rosh_lang/library/message.rosh +16 -0
- rosh_lang-0.9.3/src/rosh_lang/library/player.py +134 -0
- rosh_lang-0.9.3/src/rosh_lang/library/score.py +51 -0
- rosh_lang-0.9.3/src/rosh_lang/library/starfield.py +44 -0
- rosh_lang-0.9.3/src/rosh_lang/library/timer.py +62 -0
- rosh_lang-0.9.3/src/rosh_lang/library/title-screen.rosh +36 -0
- rosh_lang-0.9.3/src/rosh_lang/library_cli.py +4 -0
- rosh_lang-0.9.3/src/rosh_lang/media/__init__.py +2 -0
- rosh_lang-0.9.3/src/rosh_lang/media/asset_cache.py +57 -0
- rosh_lang-0.9.3/src/rosh_lang/media/asset_manifests/amphora.json +61 -0
- rosh_lang-0.9.3/src/rosh_lang/media/asset_manifests/ball.json +36 -0
- rosh_lang-0.9.3/src/rosh_lang/media/asset_manifests/box.json +36 -0
- rosh_lang-0.9.3/src/rosh_lang/media/asset_manifests/brooch.json +61 -0
- rosh_lang-0.9.3/src/rosh_lang/media/asset_manifests/carved_relief.json +37 -0
- rosh_lang-0.9.3/src/rosh_lang/media/asset_manifests/cauldron.json +61 -0
- rosh_lang-0.9.3/src/rosh_lang/media/asset_manifests/coin.json +36 -0
- rosh_lang-0.9.3/src/rosh_lang/media/asset_manifests/display_case.json +38 -0
- rosh_lang-0.9.3/src/rosh_lang/media/asset_manifests/door.json +37 -0
- rosh_lang-0.9.3/src/rosh_lang/media/asset_manifests/exhibit_label.json +37 -0
- rosh_lang-0.9.3/src/rosh_lang/media/asset_manifests/key.json +36 -0
- rosh_lang-0.9.3/src/rosh_lang/media/asset_manifests/necklace.json +62 -0
- rosh_lang-0.9.3/src/rosh_lang/media/asset_manifests/painting.json +36 -0
- rosh_lang-0.9.3/src/rosh_lang/media/asset_manifests/pedestal.json +37 -0
- rosh_lang-0.9.3/src/rosh_lang/media/asset_manifests/person.json +36 -0
- rosh_lang-0.9.3/src/rosh_lang/media/asset_manifests/platform.json +35 -0
- rosh_lang-0.9.3/src/rosh_lang/media/asset_manifests/spotlight.json +37 -0
- rosh_lang-0.9.3/src/rosh_lang/media/asset_manifests/stone.json +54 -0
- rosh_lang-0.9.3/src/rosh_lang/media/asset_manifests/sword.json +62 -0
- rosh_lang-0.9.3/src/rosh_lang/media/asset_manifests/torch.json +61 -0
- rosh_lang-0.9.3/src/rosh_lang/media/asset_manifests/tree.json +36 -0
- rosh_lang-0.9.3/src/rosh_lang/media/asset_providers.py +512 -0
- rosh_lang-0.9.3/src/rosh_lang/media/asset_registry.py +261 -0
- rosh_lang-0.9.3/src/rosh_lang/media/asset_requests.py +92 -0
- rosh_lang-0.9.3/src/rosh_lang/media/asset_reviews.py +318 -0
- rosh_lang-0.9.3/src/rosh_lang/media/assets/player-sheet.png +0 -0
- rosh_lang-0.9.3/src/rosh_lang/media/assets.py +55 -0
- rosh_lang-0.9.3/src/rosh_lang/media/sheets.py +235 -0
- rosh_lang-0.9.3/src/rosh_lang/media/sounds.py +221 -0
- rosh_lang-0.9.3/src/rosh_lang/media/sprites.py +309 -0
- rosh_lang-0.9.3/src/rosh_lang/model.py +4 -0
- rosh_lang-0.9.3/src/rosh_lang/parser.py +4 -0
- rosh_lang-0.9.3/src/rosh_lang/repl/__init__.py +5 -0
- rosh_lang-0.9.3/src/rosh_lang/repl/contracts.py +54 -0
- rosh_lang-0.9.3/src/rosh_lang/repl/kernel.py +490 -0
- rosh_lang-0.9.3/src/rosh_lang/repl/natural.py +288 -0
- rosh_lang-0.9.3/src/rosh_lang/repl/runtime_adapter.py +307 -0
- rosh_lang-0.9.3/src/rosh_lang/repl/shell.py +306 -0
- rosh_lang-0.9.3/src/rosh_lang/runtime.py +4 -0
- rosh_lang-0.9.3/src/rosh_lang/scaffolder.py +4 -0
- rosh_lang-0.9.3/src/rosh_lang/sheets.py +4 -0
- rosh_lang-0.9.3/src/rosh_lang/sounds.py +4 -0
- rosh_lang-0.9.3/src/rosh_lang/sprites.py +11 -0
- rosh_lang-0.9.3/src/rosh_lang/targets/__init__.py +1 -0
- rosh_lang-0.9.3/src/rosh_lang/targets/_js_codegen.py +733 -0
- rosh_lang-0.9.3/src/rosh_lang/targets/_js_runtime.py +1158 -0
- rosh_lang-0.9.3/src/rosh_lang/targets/_js_runtime_console.py +1042 -0
- rosh_lang-0.9.3/src/rosh_lang/targets/_js_runtime_phaser.py +410 -0
- rosh_lang-0.9.3/src/rosh_lang/targets/_js_runtime_threejs.py +530 -0
- rosh_lang-0.9.3/src/rosh_lang/targets/hytopia.py +226 -0
- rosh_lang-0.9.3/src/rosh_lang/targets/phaser.py +227 -0
- rosh_lang-0.9.3/src/rosh_lang/targets/rosh-natural.js +614 -0
- rosh_lang-0.9.3/src/rosh_lang/targets/scratch.py +1916 -0
- rosh_lang-0.9.3/src/rosh_lang/targets/terminal.py +13 -0
- rosh_lang-0.9.3/src/rosh_lang/targets/threejs.py +244 -0
- rosh_lang-0.9.3/src/rosh_lang/targets/web.py +554 -0
- rosh_lang-0.9.3/src/rosh_lang/targets/world.py +257 -0
- rosh_lang-0.9.3/src/rosh_lang/widgets.py +11 -0
- rosh_lang-0.9.3/tests/test_asset_cache.py +79 -0
- rosh_lang-0.9.3/tests/test_asset_providers.py +326 -0
- rosh_lang-0.9.3/tests/test_asset_registry.py +205 -0
- rosh_lang-0.9.3/tests/test_asset_requests.py +88 -0
- rosh_lang-0.9.3/tests/test_asset_reviews.py +343 -0
- rosh_lang-0.9.3/tests/test_cli.py +707 -0
- rosh_lang-0.9.3/tests/test_collections.py +383 -0
- rosh_lang-0.9.3/tests/test_conformance.py +428 -0
- rosh_lang-0.9.3/tests/test_controller.py +267 -0
- rosh_lang-0.9.3/tests/test_functions.py +381 -0
- rosh_lang-0.9.3/tests/test_intent.py +331 -0
- rosh_lang-0.9.3/tests/test_js_codegen.py +910 -0
- rosh_lang-0.9.3/tests/test_library_cli.py +64 -0
- rosh_lang-0.9.3/tests/test_normalise.py +342 -0
- rosh_lang-0.9.3/tests/test_parser.py +1067 -0
- rosh_lang-0.9.3/tests/test_phaser_target.py +246 -0
- rosh_lang-0.9.3/tests/test_public_release.py +42 -0
- rosh_lang-0.9.3/tests/test_repeat.py +140 -0
- rosh_lang-0.9.3/tests/test_runtime.py +1971 -0
- rosh_lang-0.9.3/tests/test_scaffolder.py +37 -0
- rosh_lang-0.9.3/tests/test_scratch_target.py +729 -0
- rosh_lang-0.9.3/tests/test_sheets.py +110 -0
- rosh_lang-0.9.3/tests/test_sounds.py +203 -0
- rosh_lang-0.9.3/tests/test_sprites.py +219 -0
- rosh_lang-0.9.3/tests/test_threejs_target.py +465 -0
- rosh_lang-0.9.3/tests/test_web_target.py +959 -0
- rosh_lang-0.9.3/tests/test_widgets.py +1446 -0
- rosh_lang-0.9.3/tests/test_world_target.py +294 -0
- rosh_lang-0.9.3/tools/build_showcase.py +376 -0
- rosh_lang-0.9.3/uv.lock +645 -0
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
pull_request:
|
|
6
|
+
|
|
7
|
+
jobs:
|
|
8
|
+
test:
|
|
9
|
+
strategy:
|
|
10
|
+
matrix:
|
|
11
|
+
python-version: ["3.10", "3.12"]
|
|
12
|
+
runs-on: ubuntu-latest
|
|
13
|
+
steps:
|
|
14
|
+
- uses: actions/checkout@v4
|
|
15
|
+
- uses: astral-sh/setup-uv@v6
|
|
16
|
+
with:
|
|
17
|
+
python-version: ${{ matrix.python-version }}
|
|
18
|
+
- run: uv sync --dev --frozen
|
|
19
|
+
- run: uv run pytest -q
|
|
20
|
+
|
|
21
|
+
release-artifacts:
|
|
22
|
+
runs-on: ubuntu-latest
|
|
23
|
+
steps:
|
|
24
|
+
- uses: actions/checkout@v4
|
|
25
|
+
- uses: astral-sh/setup-uv@v6
|
|
26
|
+
with:
|
|
27
|
+
python-version: "3.12"
|
|
28
|
+
- run: uv build --no-sources
|
|
29
|
+
- name: Reject private or generated release files
|
|
30
|
+
run: |
|
|
31
|
+
if tar -tf dist/rosh_lang-*.tar.gz | grep -E '(\.uv-cache|\.venv|\.pytest_cache|test-verify|\.env|\.sb3|rosh-dev|rosh-corporate)'; then
|
|
32
|
+
echo "Unexpected file found in source distribution"
|
|
33
|
+
exit 1
|
|
34
|
+
fi
|
|
35
|
+
if python3 -m zipfile -l dist/rosh_lang-*.whl | grep -E '(\.uv-cache|\.venv|\.pytest_cache|test-verify|\.env|\.sb3|rosh-dev|rosh-corporate)'; then
|
|
36
|
+
echo "Unexpected file found in the wheel"
|
|
37
|
+
exit 1
|
|
38
|
+
fi
|
|
39
|
+
- run: uv tool run --from dist/rosh_lang-*.whl rosh --version
|
|
40
|
+
- uses: actions/upload-artifact@v4
|
|
41
|
+
with:
|
|
42
|
+
name: dist
|
|
43
|
+
path: dist/
|
|
44
|
+
|
|
45
|
+
# Publishes to PyPI via Trusted Publishing (OIDC) — no stored token.
|
|
46
|
+
# Requires this workflow's environment to be registered as a trusted
|
|
47
|
+
# publisher at https://pypi.org/manage/project/rosh-lang/settings/publishing/
|
|
48
|
+
# (one-time setup, after the first manual `uv publish`).
|
|
49
|
+
publish-pypi:
|
|
50
|
+
needs: [test, release-artifacts]
|
|
51
|
+
if: startsWith(github.ref, 'refs/tags/v')
|
|
52
|
+
runs-on: ubuntu-latest
|
|
53
|
+
environment: pypi
|
|
54
|
+
permissions:
|
|
55
|
+
id-token: write
|
|
56
|
+
steps:
|
|
57
|
+
- uses: actions/download-artifact@v4
|
|
58
|
+
with:
|
|
59
|
+
name: dist
|
|
60
|
+
path: dist/
|
|
61
|
+
- name: Verify tag matches package version
|
|
62
|
+
run: |
|
|
63
|
+
tag_version="${GITHUB_REF_NAME#v}"
|
|
64
|
+
wheel_version=$(ls dist/*.whl | sed -E 's/.*rosh_lang-([0-9.]+)-.*/\1/')
|
|
65
|
+
if [ "$tag_version" != "$wheel_version" ]; then
|
|
66
|
+
echo "Tag v$tag_version does not match package version $wheel_version — refusing to publish." >&2
|
|
67
|
+
exit 1
|
|
68
|
+
fi
|
|
69
|
+
- uses: pypa/gh-action-pypi-publish@release/v1
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# Python
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.pyc
|
|
4
|
+
*.pyo
|
|
5
|
+
*.egg-info/
|
|
6
|
+
dist/
|
|
7
|
+
build/
|
|
8
|
+
.eggs/
|
|
9
|
+
|
|
10
|
+
# Virtual environments
|
|
11
|
+
.venv/
|
|
12
|
+
venv/
|
|
13
|
+
|
|
14
|
+
# Testing
|
|
15
|
+
.pytest_cache/
|
|
16
|
+
.coverage
|
|
17
|
+
htmlcov/
|
|
18
|
+
|
|
19
|
+
# IDE
|
|
20
|
+
.vscode/
|
|
21
|
+
.idea/
|
|
22
|
+
|
|
23
|
+
# OS
|
|
24
|
+
.DS_Store
|
|
25
|
+
Thumbs.db
|
|
26
|
+
|
|
27
|
+
# Environment
|
|
28
|
+
.env
|
|
29
|
+
|
|
30
|
+
# UV
|
|
31
|
+
.uv-cache/
|
|
32
|
+
|
|
33
|
+
# Generated
|
|
34
|
+
*.rosh.html
|
|
35
|
+
*.sb3
|
|
36
|
+
test-verify.rosh
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to `rosh-lang` are documented here.
|
|
4
|
+
|
|
5
|
+
The core grammar (parser/runtime statement surface) is treated as stable
|
|
6
|
+
once documented in [LANGUAGE.md](LANGUAGE.md) — see CONTRIBUTING.md's rule
|
|
7
|
+
that new keywords require discussion first. Undocumented internals (e.g.
|
|
8
|
+
widget-specific properties like `.material`) may change without a version
|
|
9
|
+
bump until they're promoted into the public spec.
|
|
10
|
+
|
|
11
|
+
## 0.9.3 - 2026-08-12
|
|
12
|
+
|
|
13
|
+
### Fixed
|
|
14
|
+
|
|
15
|
+
- `normalise`: a quoted multi-word value passed to `set X.material to "..."`
|
|
16
|
+
or `turn X into '...'` left a stray trailing quote baked into the material
|
|
17
|
+
noun (e.g. `ivory"`). Quotes are now stripped before the material/desc
|
|
18
|
+
split.
|
|
19
|
+
- `normalise`: a trailing `# comment` on an otherwise-valid line (e.g.
|
|
20
|
+
`play laser # once`) was never stripped, so the comment text was parsed
|
|
21
|
+
as real arguments instead of being ignored. Inline comments are now
|
|
22
|
+
stripped, respecting quoted strings (so `background "#1a1a2e"` and
|
|
23
|
+
`print "score #1"` are unaffected).
|
|
24
|
+
- `after <seconds> send <event>`: payload support was documented
|
|
25
|
+
(`after 0.5 send scored value=5`) but never implemented — the model had
|
|
26
|
+
no payload field and any extra tokens after the event name were silently
|
|
27
|
+
dropped on every target. `AfterStatement` now carries a `payload` dict,
|
|
28
|
+
parsed the same way as `send`, and emitted correctly in compiled JS
|
|
29
|
+
(web/Phaser/Three.js targets).
|
|
30
|
+
- `normalise`: the inline-comment stripper above treated any bare apostrophe
|
|
31
|
+
as opening a quoted string, so a possessive or contraction with no actual
|
|
32
|
+
quotes (`say Rosh's world # note`, `say don't stop # note`) made the
|
|
33
|
+
scanner think it was still "inside a string" for the rest of the line —
|
|
34
|
+
the trailing comment was never stripped. A `'` now only opens a quote when
|
|
35
|
+
it's preceded by whitespace or starts the line, which a real quote always
|
|
36
|
+
is and a contraction never is.
|
|
37
|
+
|
|
38
|
+
### Docs
|
|
39
|
+
|
|
40
|
+
- LANGUAGE.md's Scenes section documented `create scene ... end` as if it
|
|
41
|
+
scoped the statements inside it to that scene. It doesn't — `create scene`
|
|
42
|
+
only registers the scene, and everything between it and its `end` executes
|
|
43
|
+
immediately and unconditionally like any other top-level code. The section
|
|
44
|
+
now documents the real, tested pattern: register scenes, set properties
|
|
45
|
+
(`room_description`, `exits`), and gate per-scene behavior with a global
|
|
46
|
+
`when scene_enter` handler checked against the `scene` payload value.
|
|
47
|
+
- LANGUAGE.md documented `repeat items as item` for iterating a captured
|
|
48
|
+
list. `repeat` only accepts a numeric count (or `as <var>` for a counter);
|
|
49
|
+
it silently no-ops on a list. The working, tested mechanism is
|
|
50
|
+
`for each item in items`, which is now what's documented.
|
|
51
|
+
- The corrected `for each` example also fixed a second inaccuracy: captured
|
|
52
|
+
list items from `get all into items` have the shape
|
|
53
|
+
`{key, value, type}`, not `{name, ...}` — `{item.name}` never resolved.
|
|
54
|
+
The example now uses `{item.key}`.
|
|
55
|
+
- `send`/`after` payload examples used a bare positional value
|
|
56
|
+
(`send scored 10`) implying it binds to the event's declared payload
|
|
57
|
+
field. It doesn't — only `key=value` tokens are read into the payload; a
|
|
58
|
+
bare value is parsed but silently dropped. Examples now use
|
|
59
|
+
`send scored value=10`.
|
|
60
|
+
- The `event`/`when`/`send` example had the `send` before the matching
|
|
61
|
+
`when` block in program order. Handler registration happens as the
|
|
62
|
+
runtime scans forward through top-level statements, so a `send` that
|
|
63
|
+
fires before its `when` is reached has no handler to call — the example
|
|
64
|
+
now declares `when scored` before `send`ing it.
|
|
65
|
+
- The Web target section claimed `--target web` "generates a self-contained
|
|
66
|
+
HTML file... share it as a single `.html`". It doesn't — the CLI only
|
|
67
|
+
serves the rendered page from a local HTTP server; nothing is written to
|
|
68
|
+
disk. The section now describes the real behavior and points to
|
|
69
|
+
`rosh_lang.targets.web.render_html()` for anyone who wants the raw string
|
|
70
|
+
to save themselves.
|
|
71
|
+
- The Asset Registry section claimed `"ancient carved stone"`, `"carving"`,
|
|
72
|
+
and `"stone"` all fuzzy-match to the `stone` manifest. `"carving"` alone
|
|
73
|
+
actually resolves to the separate `carved_relief` manifest (it's a tag
|
|
74
|
+
there, not on `stone`). Swapped the example for `"pictish stone"`, which
|
|
75
|
+
does resolve to `stone`, and noted the near-miss.
|
|
76
|
+
|
|
77
|
+
### Release process
|
|
78
|
+
|
|
79
|
+
- Added a `publish-pypi` job to `.github/workflows/ci.yml`: on a `v*` tag
|
|
80
|
+
push, it downloads the exact wheel/sdist the `release-artifacts` job
|
|
81
|
+
already built and verified (not a fresh rebuild), checks the tag matches
|
|
82
|
+
the wheel's version (refuses to publish on a mismatch), then publishes to
|
|
83
|
+
PyPI via Trusted Publishing (OIDC) — no stored token.
|
|
84
|
+
- The private/generated-file leak check now scans the built wheel as well
|
|
85
|
+
as the sdist; previously only the sdist was checked.
|
|
86
|
+
- Removed a stale, contradictory `uv.lock` entry from `.gitignore` — the
|
|
87
|
+
lockfile is (correctly) tracked in git, so ignoring it had no effect.
|
|
88
|
+
|
|
89
|
+
## 0.9.2 and earlier
|
|
90
|
+
|
|
91
|
+
Not tracked retroactively in this file.
|
|
@@ -0,0 +1,263 @@
|
|
|
1
|
+
# Rosh Language — Agent Context
|
|
2
|
+
|
|
3
|
+
Everything an AI agent needs to write `.rosh` files and work on `rosh-lang/`.
|
|
4
|
+
|
|
5
|
+
## What Rosh Is
|
|
6
|
+
|
|
7
|
+
A plain-English language: `print "hello"` runs on terminal, browser, and game engine. Same source, different targets. "One script, many worlds."
|
|
8
|
+
|
|
9
|
+
## How to Run
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
rosh hello.rosh # terminal
|
|
13
|
+
rosh hello.rosh --target web --run # browser
|
|
14
|
+
rosh hello.rosh --target phaser --run # Phaser game
|
|
15
|
+
rosh # REPL
|
|
16
|
+
rosh new game my-game # scaffold a starter file
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## How to Test
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
cd rosh-lang && uv run pytest -q # expect the full suite to pass
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
After code changes, reinstall:
|
|
26
|
+
```bash
|
|
27
|
+
uv tool install --from /path/to/rosh-lang rosh-lang --force
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## Complete Syntax Forms
|
|
31
|
+
|
|
32
|
+
```
|
|
33
|
+
print "text" # output ({var} interpolation)
|
|
34
|
+
print # blank line (no arguments)
|
|
35
|
+
create object player # create object/number/string/list
|
|
36
|
+
set player.x to 0.5 # assign value (arithmetic: score + 1)
|
|
37
|
+
set x to random # random 0.0–1.0 (or: random 0.1 0.9)
|
|
38
|
+
set x to clamp x 0.02 0.8 # constrain to [min, max]
|
|
39
|
+
get player # query state
|
|
40
|
+
get score into saved # capture result into state variable
|
|
41
|
+
get all into items # capture all visible state as structured list
|
|
42
|
+
get count of visitors into n # capture list length as integer
|
|
43
|
+
look # inspect scene
|
|
44
|
+
look player into info # capture inspection result into state
|
|
45
|
+
look programme into stmts # capture statement list (iterate with for each)
|
|
46
|
+
say hello # broadcast text
|
|
47
|
+
when click # event handler block
|
|
48
|
+
set count to count + 1
|
|
49
|
+
end # end block
|
|
50
|
+
on update set x to x + 0.01 # one-line reactive listener
|
|
51
|
+
on keydown when key == " " play sfx # conditional listener
|
|
52
|
+
event scored value # declare named event
|
|
53
|
+
send scored 10 # emit event
|
|
54
|
+
if score > 10 # branching
|
|
55
|
+
print "win!"
|
|
56
|
+
else if score > 5 # else-if chain (single end)
|
|
57
|
+
print "close!"
|
|
58
|
+
else
|
|
59
|
+
print "keep going"
|
|
60
|
+
end
|
|
61
|
+
use score label "Points" # compose widget with config
|
|
62
|
+
go level2 # scene navigation
|
|
63
|
+
connect server wss://... # register connection
|
|
64
|
+
destroy enemy # remove object
|
|
65
|
+
sprite ship "blue spaceship" # procedural pixel art
|
|
66
|
+
sprite ship "https://example.com/ship.png" # URL sprite (any image)
|
|
67
|
+
sound laser "laser shoot" # procedural sound
|
|
68
|
+
play laser # play sound (once/loop/stop)
|
|
69
|
+
animate obj sheet "run.png" frames 4 # spritesheet animation
|
|
70
|
+
after 3 send timeout # delayed event
|
|
71
|
+
background "#1a1a2e" # canvas/scene background (colour or image)
|
|
72
|
+
create scene level1 # scene definition
|
|
73
|
+
define fire_bullet # user-defined function
|
|
74
|
+
set bullet._fire to 1
|
|
75
|
+
end
|
|
76
|
+
do fire_bullet # call a function
|
|
77
|
+
on keydown when key == " " do fire_bullet # call from event
|
|
78
|
+
repeat 5 # counted loop
|
|
79
|
+
print "hello"
|
|
80
|
+
end
|
|
81
|
+
repeat 3 as i # loop with variable (1, 2, 3)
|
|
82
|
+
print "Round {i}"
|
|
83
|
+
end
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
`into` capture for `get` and `look` is terminal/REPL-first. Browser, Phaser,
|
|
87
|
+
Three.js, Scratch, and world target capture semantics are deferred until those
|
|
88
|
+
targets have explicit `get`/`look` command-state paths.
|
|
89
|
+
|
|
90
|
+
## Object Properties
|
|
91
|
+
|
|
92
|
+
| Property | Type | Description |
|
|
93
|
+
|----------|------|-------------|
|
|
94
|
+
| `x` | float | Position (0.0–1.0 = %, >1 = px) |
|
|
95
|
+
| `y` | float | Position (0.0–1.0 = %, >1 = px) |
|
|
96
|
+
| `width` | float | Size (default 0.1) |
|
|
97
|
+
| `height` | float | Size (default 0.1) |
|
|
98
|
+
| `color` | string | Hex or named color |
|
|
99
|
+
| `label` | string | Text on object (empty by default) |
|
|
100
|
+
| `sprite` | string | Procedural sprite description or image URL |
|
|
101
|
+
| `rotation` | float | Rotation in degrees (0=up, clockwise positive) |
|
|
102
|
+
| `visible` | int | 0 hides, any other shows (cascades to children) |
|
|
103
|
+
| `vx`, `vy` | float | Velocity (per second) |
|
|
104
|
+
| `text_color` | string | Text color (default #fff) |
|
|
105
|
+
| `font_size` | string | Font size (default 14px) |
|
|
106
|
+
| `_max_output` | int | Max console lines (excess trimmed from top) |
|
|
107
|
+
|
|
108
|
+
## Events
|
|
109
|
+
|
|
110
|
+
`start`, `update` (dt), `click` (x,y), `click_<name>` (x,y), `keydown` (key), `keyup` (key), `collision` (a,b), `scene_enter` (scene), `scene_exit` (scene), `destroy` (name), `timer_done` (name), `game_start`, `game_over`, `game_restart`.
|
|
111
|
+
|
|
112
|
+
Key-hold: `_keys.ArrowLeft == 1` inside `when update`.
|
|
113
|
+
|
|
114
|
+
## Game-Building Pattern
|
|
115
|
+
|
|
116
|
+
```
|
|
117
|
+
# Minimal game: player moves, shoots, scores
|
|
118
|
+
set _max_output to 3
|
|
119
|
+
use score
|
|
120
|
+
use lives count 3
|
|
121
|
+
use player speed 0.03 move x
|
|
122
|
+
sprite player "green spaceship"
|
|
123
|
+
|
|
124
|
+
use bullet count 3 vy -0.5 color "#ffff00"
|
|
125
|
+
|
|
126
|
+
create object enemy
|
|
127
|
+
set enemy.x to 0.45
|
|
128
|
+
set enemy.y to 0.1
|
|
129
|
+
set enemy.width to 0.08
|
|
130
|
+
set enemy.height to 0.06
|
|
131
|
+
sprite enemy "red alien"
|
|
132
|
+
|
|
133
|
+
sound laser "laser shoot"
|
|
134
|
+
sound hit "explosion hit"
|
|
135
|
+
|
|
136
|
+
on keydown when key == " " set bullet._x to player.x
|
|
137
|
+
on keydown when key == " " set bullet._y to player.y
|
|
138
|
+
on keydown when key == " " set bullet._fire to 1
|
|
139
|
+
on keydown when key == " " play laser
|
|
140
|
+
|
|
141
|
+
when collision bullet.* enemy
|
|
142
|
+
set score.value to score.value + 1
|
|
143
|
+
play hit
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
# game_over auto-fires when lives.count hits 0
|
|
147
|
+
when game_over
|
|
148
|
+
print "GAME OVER — Score: {score.value}"
|
|
149
|
+
end
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
Run: `rosh game.rosh --target web --run`
|
|
153
|
+
|
|
154
|
+
## Widget Library (22 Widgets)
|
|
155
|
+
|
|
156
|
+
| Widget | Type | Config | Purpose |
|
|
157
|
+
|--------|------|--------|---------|
|
|
158
|
+
| `score` | .py | `anchor theme label x y bg text_color font_size` | Score display (HUD) |
|
|
159
|
+
| `player` | .py | `speed keys move x y width height color clamp_*` | Keyboard ship with auto-movement |
|
|
160
|
+
| `controller` | .py | `target keys touch touch_style speed move help fire fire_key fire_event clamp` | Universal input (keyboard + touch) |
|
|
161
|
+
| `counter` | .rosh | — | Click counter |
|
|
162
|
+
| `timer` | .py | `total running x y bg text_color font_size` | Auto-tick countdown (fires timer_done) |
|
|
163
|
+
| `health-bar` | .py | `max current x y bg text_color font_size` | Health display |
|
|
164
|
+
| `lives` | .py | `count auto_gameover x y bg text_color font_size` | Lives counter (auto game-over at 0) |
|
|
165
|
+
| `button` | .rosh | — | Clickable button |
|
|
166
|
+
| `label` | .rosh | `text x y bg text_color font_size` | Text label |
|
|
167
|
+
| `fps` | .py | `x y bg text_color font_size` | FPS counter |
|
|
168
|
+
| `message` | .rosh | `text x y bg text_color font_size` | Overlay message |
|
|
169
|
+
| `title-screen` | .rosh | `title subtitle bg text_color font_size` | Title screen |
|
|
170
|
+
| `coin` | .rosh | — | Collectible |
|
|
171
|
+
| `grid` | .py | `rows cols size gap color` | Cell grid |
|
|
172
|
+
| `enemy-grid` | .py | `rows cols size gap color` | Enemy formation |
|
|
173
|
+
| `starfield` | .py | `count` | Background stars |
|
|
174
|
+
| `bullet` | .py | `count vx vy color` | Pooled projectiles |
|
|
175
|
+
| `explosion` | .py | `count color` | Pooled explosions |
|
|
176
|
+
| `animation` | .py | `target sheet frames speed mode` | Spritesheet |
|
|
177
|
+
| `game-lifecycle` | .rosh | `title subtitle bg text_color font_size` | Title → playing → over flow |
|
|
178
|
+
| `ball` | .py | `x y size color vx vy walls` | Bouncing ball with wall bounce |
|
|
179
|
+
| `hazard` | .py | `count vx vy color width height sprite spawn_rate` | Auto-spawning obstacle pool |
|
|
180
|
+
|
|
181
|
+
Use: `use <widget> [key value ...]` — config pairs override defaults.
|
|
182
|
+
|
|
183
|
+
## Architecture
|
|
184
|
+
|
|
185
|
+
```
|
|
186
|
+
core/model.py → statement dataclasses + Programme
|
|
187
|
+
core/parser.py → text → Programme
|
|
188
|
+
core/runtime.py → execute Programme, state dict, events, if/else, scenes
|
|
189
|
+
core/widgets.py → widget loader: find, namespace-prefix, configure
|
|
190
|
+
media/sprites.py → name + description → data:image/png;base64 (7×9 grid)
|
|
191
|
+
media/sounds.py → name + description → Web Audio synthesis params
|
|
192
|
+
media/sheets.py → PNG spritesheet → list of frame data URIs
|
|
193
|
+
media/assets.py → resolve asset file paths (legacy)
|
|
194
|
+
media/asset_registry.py → semantic registry; find() by name/alias/tag, visibility-gated
|
|
195
|
+
media/asset_providers.py → provider connectors: MockAssetProvider + SketchfabAssetProvider (metadata-only)
|
|
196
|
+
media/asset_requests.py → load unknown-object request queue from generated _assetRequests state
|
|
197
|
+
media/asset_reviews.py → CandidateReview dataclass; validate_review(); review_to_draft_manifest(); candidates_to_review_template()
|
|
198
|
+
media/asset_manifests/ → bundled seed manifests (18 JSON files, MIT-licensed, review_status=featured)
|
|
199
|
+
cli/scaffolder.py → rosh new templates
|
|
200
|
+
cli/cloud.py → rosh.cloud login/create/publish commands
|
|
201
|
+
cli/library_cli.py → rosh library list/info (widget inspector)
|
|
202
|
+
cli/assets_cli.py → rosh assets search [--save review.json] | rosh assets register <review.json> [--output-dir DIR]
|
|
203
|
+
library/ → bundled widgets
|
|
204
|
+
targets/
|
|
205
|
+
terminal.py → print to stdout
|
|
206
|
+
web.py → self-contained HTML page (CSS divs + JS)
|
|
207
|
+
phaser.py → Phaser 3.70.0 game (canvas rendering)
|
|
208
|
+
threejs.py → Three.js 3D scene
|
|
209
|
+
scratch.py → Scratch 3 .sb3 export
|
|
210
|
+
_js_runtime.py → JS_RUNTIME_CORE (state, events) + JS_RUNTIME_DOM (CSS sync)
|
|
211
|
+
_js_runtime_phaser.py → JS_RUNTIME_PHASER (Phaser renderer, replaces DOM)
|
|
212
|
+
_js_runtime_threejs.py → JS_RUNTIME_THREEJS (Three.js renderer)
|
|
213
|
+
_js_codegen.py → compile Programme → JavaScript
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
## Rules
|
|
217
|
+
|
|
218
|
+
1. **Don't add keywords** unless they are documented in the public syntax reference or agreed in an issue first
|
|
219
|
+
2. **Test after every change**: `cd rosh-lang && uv run pytest -q`
|
|
220
|
+
3. **Rebuild showcase** after completing build steps: `cd rosh-lang && uv run python tools/build_showcase.py`
|
|
221
|
+
4. **Licence**: MIT. All widgets must declare `# licence: MIT`
|
|
222
|
+
5. **Spec before code**: check the README and https://rosh.cloud/docs/syntax before implementing language behavior
|
|
223
|
+
6. **Commits**: use `type: Short description`, with types like `feat`, `fix`, `docs`, `test`, `refactor`, or `chore`
|
|
224
|
+
7. **Reinstall after changes**: `uv tool install --from .../rosh-lang rosh-lang --force`
|
|
225
|
+
|
|
226
|
+
## Sprite Descriptions
|
|
227
|
+
|
|
228
|
+
Two modes:
|
|
229
|
+
|
|
230
|
+
1. **Procedural** — description keywords generate 7×9 pixel art. Color keywords (red, blue, green, etc.) and shape keywords (spaceship, alien, ball, bullet, crystal, star). Example: `sprite ship "blue spaceship"`
|
|
231
|
+
|
|
232
|
+
2. **URL** — any `http://` or `https://` URL loads the image directly. Works on all targets (web, phaser, threejs). Example: `sprite ship "https://cdn.jsdelivr.net/gh/twitter/twemoji@14.0.2/assets/72x72/1f680.png"`
|
|
233
|
+
|
|
234
|
+
URL sprites and procedural sprites can be mixed in the same program. If a URL fails to load, the object falls back to a colored rectangle.
|
|
235
|
+
|
|
236
|
+
**Rendering differences:**
|
|
237
|
+
- Procedural sprites: fill the entire width×height box, pixelated upscaling (crisp edges)
|
|
238
|
+
- URL sprites: `contain` fit (preserves aspect ratio), smooth rendering
|
|
239
|
+
- `rotation` property: degrees, 0=up, clockwise positive (all targets)
|
|
240
|
+
|
|
241
|
+
## Sound Families
|
|
242
|
+
|
|
243
|
+
9 preset families matched by keyword: `laser`, `explosion`, `coin`, `jump`, `hit`, `powerup`, `gameover`, `click`, `win`.
|
|
244
|
+
|
|
245
|
+
## Bullet Pool Pattern
|
|
246
|
+
|
|
247
|
+
```
|
|
248
|
+
use bullet count 3 vy -0.5 color "#ffff00"
|
|
249
|
+
|
|
250
|
+
# Fire: set position then flag
|
|
251
|
+
on keydown when key == " " set bullet._x to player.x
|
|
252
|
+
on keydown when key == " " set bullet._y to player.y
|
|
253
|
+
on keydown when key == " " set bullet._fire to 1
|
|
254
|
+
|
|
255
|
+
# Collision: bullet.* matches all pool members
|
|
256
|
+
when collision bullet.* enemy
|
|
257
|
+
set score.value to score.value + 1
|
|
258
|
+
end
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
## Collision Wildcard
|
|
262
|
+
|
|
263
|
+
`when collision bullet.* enemy` — the `*` matches any suffix. Works in either position.
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
# Contributing to Rosh
|
|
2
|
+
|
|
3
|
+
Thanks for your interest in contributing to Rosh.
|
|
4
|
+
|
|
5
|
+
## Licence
|
|
6
|
+
|
|
7
|
+
Rosh is released under the [MIT License](LICENSE). By contributing, you agree that your contributions will be licensed under MIT.
|
|
8
|
+
|
|
9
|
+
## Getting Started
|
|
10
|
+
|
|
11
|
+
### Prerequisites
|
|
12
|
+
|
|
13
|
+
- Python 3.10+
|
|
14
|
+
- [uv](https://docs.astral.sh/uv/) (recommended) or pip
|
|
15
|
+
|
|
16
|
+
### Setup
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
git clone https://github.com/rosh-studio/rosh-lang.git
|
|
20
|
+
cd rosh-lang
|
|
21
|
+
uv sync --all-extras
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
### Running Tests
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
uv run pytest -q
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
All tests must pass before submitting a pull request.
|
|
31
|
+
|
|
32
|
+
The standalone public repository also builds wheel and source-distribution
|
|
33
|
+
artifacts in CI to prevent local caches or generated files entering a release.
|
|
34
|
+
|
|
35
|
+
### Installing Locally
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
uv tool install --from . rosh-lang --force
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
Then run with `rosh`.
|
|
42
|
+
|
|
43
|
+
## What You Can Contribute
|
|
44
|
+
|
|
45
|
+
- **Bug reports** — open an issue with reproduction steps
|
|
46
|
+
- **Example programs** — new `.rosh` files in `examples/`
|
|
47
|
+
- **Widget improvements** — enhancements to existing widgets in `src/rosh_lang/library/`
|
|
48
|
+
- **Documentation** — improvements to README, inline comments, or examples
|
|
49
|
+
- **Test coverage** — new test cases for edge cases or uncovered paths
|
|
50
|
+
- **Editor support** — syntax and snippet updates in `editor/vscode/`
|
|
51
|
+
|
|
52
|
+
## What Requires Discussion First
|
|
53
|
+
|
|
54
|
+
- **New keywords** — language additions must be discussed and documented in the public syntax reference before implementation
|
|
55
|
+
- **New targets** — adding a new compilation target (e.g. Unity, Godot) is a significant change
|
|
56
|
+
- **Architecture changes** — changes to the parser, model, or code generation pipeline
|
|
57
|
+
|
|
58
|
+
Please open an issue to discuss before submitting a PR for these.
|
|
59
|
+
|
|
60
|
+
## Code Style
|
|
61
|
+
|
|
62
|
+
- Use `pathlib.Path` over `os.path`
|
|
63
|
+
- Use f-strings over `.format()` or `%`
|
|
64
|
+
- Use type hints
|
|
65
|
+
- Keep it simple — no over-engineering
|
|
66
|
+
|
|
67
|
+
## Commit Messages
|
|
68
|
+
|
|
69
|
+
```text
|
|
70
|
+
type: Short description
|
|
71
|
+
|
|
72
|
+
Co-Authored-By: Your Name <your@email.com>
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
Types: `feat`, `fix`, `docs`, `test`, `refactor`, `chore`.
|
|
76
|
+
|
|
77
|
+
## Questions?
|
|
78
|
+
|
|
79
|
+
- Discord: [discord.gg/gevBPucznD](https://discord.gg/gevBPucznD)
|
|
80
|
+
- Email: [info@rosh.cloud](mailto:info@rosh.cloud)
|