roboharness 0.1.1__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 (86) hide show
  1. roboharness-0.1.1/.github/ISSUE_TEMPLATE/bug_report.md +37 -0
  2. roboharness-0.1.1/.github/ISSUE_TEMPLATE/feature_request.md +27 -0
  3. roboharness-0.1.1/.github/workflows/ci.yml +89 -0
  4. roboharness-0.1.1/.github/workflows/pages.yml +68 -0
  5. roboharness-0.1.1/.github/workflows/release.yml +130 -0
  6. roboharness-0.1.1/.gitignore +34 -0
  7. roboharness-0.1.1/CONTRIBUTING.md +62 -0
  8. roboharness-0.1.1/LICENSE +21 -0
  9. roboharness-0.1.1/PKG-INFO +268 -0
  10. roboharness-0.1.1/README.md +225 -0
  11. roboharness-0.1.1/assets/00/X26_Y22_Z13/autonomous_report.json +2382 -0
  12. roboharness-0.1.1/assets/00/X26_Y22_Z13/meshcat/00_planned_front2back.png +0 -0
  13. roboharness-0.1.1/assets/00/X26_Y22_Z13/meshcat/00_planned_left2right.png +0 -0
  14. roboharness-0.1.1/assets/00/X26_Y22_Z13/meshcat/00_planned_top2down.png +0 -0
  15. roboharness-0.1.1/assets/00/X26_Y22_Z13/meshcat/01_pregrasp_done_front2back.png +0 -0
  16. roboharness-0.1.1/assets/00/X26_Y22_Z13/meshcat/01_pregrasp_done_left2right.png +0 -0
  17. roboharness-0.1.1/assets/00/X26_Y22_Z13/meshcat/01_pregrasp_done_top2down.png +0 -0
  18. roboharness-0.1.1/assets/00/X26_Y22_Z13/meshcat/02_approach_done_front2back.png +0 -0
  19. roboharness-0.1.1/assets/00/X26_Y22_Z13/meshcat/02_approach_done_left2right.png +0 -0
  20. roboharness-0.1.1/assets/00/X26_Y22_Z13/meshcat/02_approach_done_top2down.png +0 -0
  21. roboharness-0.1.1/assets/00/X26_Y22_Z13/meshcat/03_close_done_front2back.png +0 -0
  22. roboharness-0.1.1/assets/00/X26_Y22_Z13/meshcat/03_close_done_left2right.png +0 -0
  23. roboharness-0.1.1/assets/00/X26_Y22_Z13/meshcat/03_close_done_top2down.png +0 -0
  24. roboharness-0.1.1/assets/00/X26_Y22_Z13/meshcat/04_lift_done_front2back.png +0 -0
  25. roboharness-0.1.1/assets/00/X26_Y22_Z13/meshcat/04_lift_done_left2right.png +0 -0
  26. roboharness-0.1.1/assets/00/X26_Y22_Z13/meshcat/04_lift_done_top2down.png +0 -0
  27. roboharness-0.1.1/assets/00/X26_Y22_Z13/meshcat/05_holding_front2back.png +0 -0
  28. roboharness-0.1.1/assets/00/X26_Y22_Z13/meshcat/05_holding_left2right.png +0 -0
  29. roboharness-0.1.1/assets/00/X26_Y22_Z13/meshcat/05_holding_top2down.png +0 -0
  30. roboharness-0.1.1/assets/00/X26_Y22_Z13/meshcat/report.json +843 -0
  31. roboharness-0.1.1/assets/00/X26_Y22_Z13/snapshot_bundle.json +5616 -0
  32. roboharness-0.1.1/assets/00/X32_Y28_Z13/autonomous_report.json +2384 -0
  33. roboharness-0.1.1/assets/00/X32_Y28_Z13/meshcat/00_planned_front2back.png +0 -0
  34. roboharness-0.1.1/assets/00/X32_Y28_Z13/meshcat/00_planned_left2right.png +0 -0
  35. roboharness-0.1.1/assets/00/X32_Y28_Z13/meshcat/00_planned_top2down.png +0 -0
  36. roboharness-0.1.1/assets/00/X32_Y28_Z13/meshcat/01_pregrasp_done_front2back.png +0 -0
  37. roboharness-0.1.1/assets/00/X32_Y28_Z13/meshcat/01_pregrasp_done_left2right.png +0 -0
  38. roboharness-0.1.1/assets/00/X32_Y28_Z13/meshcat/01_pregrasp_done_top2down.png +0 -0
  39. roboharness-0.1.1/assets/00/X32_Y28_Z13/meshcat/02_approach_done_front2back.png +0 -0
  40. roboharness-0.1.1/assets/00/X32_Y28_Z13/meshcat/02_approach_done_left2right.png +0 -0
  41. roboharness-0.1.1/assets/00/X32_Y28_Z13/meshcat/02_approach_done_top2down.png +0 -0
  42. roboharness-0.1.1/assets/00/X32_Y28_Z13/meshcat/03_close_done_front2back.png +0 -0
  43. roboharness-0.1.1/assets/00/X32_Y28_Z13/meshcat/03_close_done_left2right.png +0 -0
  44. roboharness-0.1.1/assets/00/X32_Y28_Z13/meshcat/03_close_done_top2down.png +0 -0
  45. roboharness-0.1.1/assets/00/X32_Y28_Z13/meshcat/04_lift_done_front2back.png +0 -0
  46. roboharness-0.1.1/assets/00/X32_Y28_Z13/meshcat/04_lift_done_left2right.png +0 -0
  47. roboharness-0.1.1/assets/00/X32_Y28_Z13/meshcat/04_lift_done_top2down.png +0 -0
  48. roboharness-0.1.1/assets/00/X32_Y28_Z13/meshcat/05_holding_front2back.png +0 -0
  49. roboharness-0.1.1/assets/00/X32_Y28_Z13/meshcat/05_holding_left2right.png +0 -0
  50. roboharness-0.1.1/assets/00/X32_Y28_Z13/meshcat/05_holding_top2down.png +0 -0
  51. roboharness-0.1.1/assets/00/X32_Y28_Z13/meshcat/report.json +843 -0
  52. roboharness-0.1.1/assets/00/X32_Y28_Z13/snapshot_bundle.json +5376 -0
  53. roboharness-0.1.1/assets/X26_Y22_Z13_front_view.gif +0 -0
  54. roboharness-0.1.1/assets/X26_Y22_Z13_topdown_view.gif +0 -0
  55. roboharness-0.1.1/assets/X32_Y28_Z13_front_view.gif +0 -0
  56. roboharness-0.1.1/assets/architecture.svg +123 -0
  57. roboharness-0.1.1/assets/example_mujoco_grasp/contact_front.png +0 -0
  58. roboharness-0.1.1/assets/example_mujoco_grasp/grasp_front.png +0 -0
  59. roboharness-0.1.1/assets/example_mujoco_grasp/lift_front.png +0 -0
  60. roboharness-0.1.1/assets/example_mujoco_grasp/pre_grasp_front.png +0 -0
  61. roboharness-0.1.1/docs/ci-strategy.md +172 -0
  62. roboharness-0.1.1/docs/context.en.md +186 -0
  63. roboharness-0.1.1/docs/context.zh-CN.md +186 -0
  64. roboharness-0.1.1/docs/simulator-survey.en.md +154 -0
  65. roboharness-0.1.1/docs/simulator-survey.zh-CN.md +154 -0
  66. roboharness-0.1.1/docs/todo.md +90 -0
  67. roboharness-0.1.1/examples/grasp_task_storage.py +55 -0
  68. roboharness-0.1.1/examples/mujoco_grasp.py +342 -0
  69. roboharness-0.1.1/examples/quickstart_gymnasium.py +57 -0
  70. roboharness-0.1.1/pyproject.toml +75 -0
  71. roboharness-0.1.1/src/roboharness/__init__.py +14 -0
  72. roboharness-0.1.1/src/roboharness/backends/__init__.py +1 -0
  73. roboharness-0.1.1/src/roboharness/backends/mujoco_meshcat.py +146 -0
  74. roboharness-0.1.1/src/roboharness/core/__init__.py +0 -0
  75. roboharness-0.1.1/src/roboharness/core/capture.py +141 -0
  76. roboharness-0.1.1/src/roboharness/core/checkpoint.py +59 -0
  77. roboharness-0.1.1/src/roboharness/core/harness.py +210 -0
  78. roboharness-0.1.1/src/roboharness/storage/__init__.py +5 -0
  79. roboharness-0.1.1/src/roboharness/storage/task_store.py +209 -0
  80. roboharness-0.1.1/src/roboharness/wrappers/__init__.py +5 -0
  81. roboharness-0.1.1/src/roboharness/wrappers/gymnasium_wrapper.py +175 -0
  82. roboharness-0.1.1/tests/__init__.py +0 -0
  83. roboharness-0.1.1/tests/test_capture.py +66 -0
  84. roboharness-0.1.1/tests/test_checkpoint.py +37 -0
  85. roboharness-0.1.1/tests/test_harness.py +118 -0
  86. roboharness-0.1.1/tests/test_storage.py +80 -0
@@ -0,0 +1,37 @@
1
+ ---
2
+ name: Bug Report
3
+ about: Report a bug in Roboharness
4
+ title: "[Bug] "
5
+ labels: bug
6
+ assignees: ''
7
+ ---
8
+
9
+ ## Description
10
+
11
+ A clear description of the bug.
12
+
13
+ ## Environment
14
+
15
+ - OS:
16
+ - Python version:
17
+ - Roboharness version:
18
+ - Simulator (MuJoCo/Isaac Lab/ManiSkill/etc.):
19
+ - Simulator version:
20
+
21
+ ## Steps to Reproduce
22
+
23
+ 1.
24
+ 2.
25
+ 3.
26
+
27
+ ## Expected Behavior
28
+
29
+ What you expected to happen.
30
+
31
+ ## Actual Behavior
32
+
33
+ What actually happened. Include error messages/tracebacks if applicable.
34
+
35
+ ## Additional Context
36
+
37
+ Any other relevant information (screenshots, logs, etc.)
@@ -0,0 +1,27 @@
1
+ ---
2
+ name: Feature Request
3
+ about: Suggest a new feature or improvement
4
+ title: "[Feature] "
5
+ labels: enhancement
6
+ assignees: ''
7
+ ---
8
+
9
+ ## Problem
10
+
11
+ What problem does this feature solve?
12
+
13
+ ## Proposed Solution
14
+
15
+ Describe the solution you'd like.
16
+
17
+ ## Use Case
18
+
19
+ How would you use this feature? Include code examples if helpful.
20
+
21
+ ## Alternatives Considered
22
+
23
+ Any alternative approaches you've thought about.
24
+
25
+ ## Additional Context
26
+
27
+ Any other relevant information.
@@ -0,0 +1,89 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+ branches: [main]
8
+
9
+ jobs:
10
+ lint:
11
+ runs-on: ubuntu-latest
12
+ steps:
13
+ - uses: actions/checkout@v4
14
+ - uses: actions/setup-python@v5
15
+ with:
16
+ python-version: "3.12"
17
+ - name: Install dependencies
18
+ run: pip install ruff
19
+ - name: Ruff lint
20
+ run: ruff check .
21
+
22
+ test:
23
+ runs-on: ubuntu-latest
24
+ strategy:
25
+ matrix:
26
+ python-version: ["3.10", "3.11", "3.12"]
27
+ steps:
28
+ - uses: actions/checkout@v4
29
+ - uses: actions/setup-python@v5
30
+ with:
31
+ python-version: ${{ matrix.python-version }}
32
+ - name: Install package with dev dependencies
33
+ run: pip install -e ".[dev]"
34
+ - name: Run tests
35
+ run: pytest
36
+
37
+ mujoco-example:
38
+ runs-on: ubuntu-latest
39
+ steps:
40
+ - uses: actions/checkout@v4
41
+ - uses: actions/setup-python@v5
42
+ with:
43
+ python-version: "3.12"
44
+
45
+ - name: Install system dependencies (OSMesa for headless rendering)
46
+ run: sudo apt-get update && sudo apt-get install -y libosmesa6-dev
47
+
48
+ - name: Install package with MuJoCo backend
49
+ run: pip install -e ".[mujoco]" Pillow
50
+
51
+ - name: Run MuJoCo grasp example
52
+ env:
53
+ MUJOCO_GL: osmesa
54
+ run: python examples/mujoco_grasp.py --report --output-dir ./harness_output
55
+
56
+ - name: Upload visual artifacts
57
+ uses: actions/upload-artifact@v4
58
+ with:
59
+ name: mujoco-grasp-output
60
+ path: |
61
+ harness_output/report.html
62
+ harness_output/mujoco_grasp/trial_001/**/*_rgb.png
63
+ harness_output/mujoco_grasp/trial_001/**/metadata.json
64
+ retention-days: 30
65
+
66
+ - name: Post summary with checkpoint images
67
+ run: |
68
+ echo "## MuJoCo Grasp Example Results" >> $GITHUB_STEP_SUMMARY
69
+ echo "" >> $GITHUB_STEP_SUMMARY
70
+ echo "Checkpoint captures from the scripted grasp sequence:" >> $GITHUB_STEP_SUMMARY
71
+ echo "" >> $GITHUB_STEP_SUMMARY
72
+ for cp in harness_output/mujoco_grasp/trial_001/*/; do
73
+ name=$(basename "$cp")
74
+ echo "### ${name}" >> $GITHUB_STEP_SUMMARY
75
+ echo "" >> $GITHUB_STEP_SUMMARY
76
+ # Embed front view as base64 in summary
77
+ if [ -f "${cp}front_rgb.png" ]; then
78
+ echo "![${name} front view](data:image/png;base64,$(base64 -w0 "${cp}front_rgb.png"))" >> $GITHUB_STEP_SUMMARY
79
+ fi
80
+ if [ -f "${cp}metadata.json" ]; then
81
+ step=$(python3 -c "import json; print(json.load(open('${cp}metadata.json'))['step'])")
82
+ sim_time=$(python3 -c "import json; print(f\"{json.load(open('${cp}metadata.json'))['sim_time']:.3f}\")")
83
+ echo "" >> $GITHUB_STEP_SUMMARY
84
+ echo "Step: ${step} | Sim time: ${sim_time}s" >> $GITHUB_STEP_SUMMARY
85
+ fi
86
+ echo "" >> $GITHUB_STEP_SUMMARY
87
+ done
88
+ echo "---" >> $GITHUB_STEP_SUMMARY
89
+ echo "Download the full artifact (including HTML report) from the Actions tab." >> $GITHUB_STEP_SUMMARY
@@ -0,0 +1,68 @@
1
+ name: Deploy Visual Report to GitHub Pages
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ workflow_dispatch:
7
+
8
+ permissions:
9
+ contents: read
10
+ pages: write
11
+ id-token: write
12
+
13
+ concurrency:
14
+ group: pages
15
+ cancel-in-progress: true
16
+
17
+ jobs:
18
+ build:
19
+ runs-on: ubuntu-latest
20
+ steps:
21
+ - uses: actions/checkout@v4
22
+ - uses: actions/setup-python@v5
23
+ with:
24
+ python-version: "3.12"
25
+
26
+ - name: Install system dependencies (OSMesa for headless rendering)
27
+ run: sudo apt-get update && sudo apt-get install -y libosmesa6-dev
28
+
29
+ - name: Install package with MuJoCo backend
30
+ run: pip install -e ".[mujoco]" Pillow
31
+
32
+ - name: Run MuJoCo grasp example
33
+ env:
34
+ MUJOCO_GL: osmesa
35
+ run: python examples/mujoco_grasp.py --report --output-dir ./harness_output
36
+
37
+ - name: Build Pages site
38
+ run: |
39
+ mkdir -p _site
40
+
41
+ # Copy the HTML report
42
+ cp harness_output/report.html _site/index.html
43
+
44
+ # Copy all checkpoint images (flat structure for easy linking)
45
+ for cp_dir in harness_output/mujoco_grasp/trial_001/*/; do
46
+ cp_name=$(basename "$cp_dir")
47
+ mkdir -p "_site/${cp_name}"
48
+ cp "${cp_dir}"*_rgb.png "_site/${cp_name}/" 2>/dev/null || true
49
+ cp "${cp_dir}"*_depth_viz.png "_site/${cp_name}/" 2>/dev/null || true
50
+ cp "${cp_dir}"metadata.json "_site/${cp_name}/" 2>/dev/null || true
51
+ cp "${cp_dir}"state.json "_site/${cp_name}/" 2>/dev/null || true
52
+ done
53
+
54
+ - name: Upload Pages artifact
55
+ uses: actions/upload-pages-artifact@v3
56
+ with:
57
+ path: _site
58
+
59
+ deploy:
60
+ needs: build
61
+ runs-on: ubuntu-latest
62
+ environment:
63
+ name: github-pages
64
+ url: ${{ steps.deployment.outputs.page_url }}
65
+ steps:
66
+ - name: Deploy to GitHub Pages
67
+ id: deployment
68
+ uses: actions/deploy-pages@v4
@@ -0,0 +1,130 @@
1
+ name: Release
2
+
3
+ on:
4
+ push:
5
+ branches:
6
+ - release
7
+ workflow_dispatch:
8
+ inputs:
9
+ bump:
10
+ description: "Version bump type"
11
+ required: true
12
+ default: "patch"
13
+ type: choice
14
+ options:
15
+ - patch
16
+ - minor
17
+ - major
18
+ custom_tag:
19
+ description: "Custom tag (e.g. v1.2.3). Leave empty to auto-bump."
20
+ required: false
21
+ type: string
22
+
23
+ permissions:
24
+ contents: write
25
+ id-token: write
26
+
27
+ jobs:
28
+ release:
29
+ runs-on: ubuntu-latest
30
+ environment: pypi
31
+ steps:
32
+ - uses: actions/checkout@v4
33
+ with:
34
+ fetch-depth: 0
35
+
36
+ - name: Determine new version
37
+ id: version
38
+ run: |
39
+ # Read version from pyproject.toml as source of truth
40
+ CURRENT=$(grep '^version = ' pyproject.toml | sed 's/version = "\(.*\)"/\1/')
41
+ echo "Current version in pyproject.toml: ${CURRENT}"
42
+
43
+ # Get latest semver tag
44
+ LATEST=$(git tag --sort=-v:refname | grep -E '^v[0-9]+\.[0-9]+\.[0-9]+$' | head -1)
45
+ echo "Latest tag: ${LATEST:-none}"
46
+
47
+ # For workflow_dispatch with custom_tag
48
+ if [ -n "${{ inputs.custom_tag }}" ]; then
49
+ NEW_TAG="${{ inputs.custom_tag }}"
50
+ NEW_VERSION="${NEW_TAG#v}"
51
+ # For workflow_dispatch with bump
52
+ elif [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
53
+ if [ -z "$LATEST" ]; then
54
+ LATEST="v0.0.0"
55
+ fi
56
+ VERSION="${LATEST#v}"
57
+ IFS='.' read -r MAJOR MINOR PATCH <<< "$VERSION"
58
+
59
+ case "${{ inputs.bump }}" in
60
+ major) MAJOR=$((MAJOR + 1)); MINOR=0; PATCH=0 ;;
61
+ minor) MINOR=$((MINOR + 1)); PATCH=0 ;;
62
+ patch) PATCH=$((PATCH + 1)) ;;
63
+ esac
64
+
65
+ NEW_VERSION="${MAJOR}.${MINOR}.${PATCH}"
66
+ NEW_TAG="v${NEW_VERSION}"
67
+ # For push to release branch: use pyproject.toml version directly
68
+ else
69
+ NEW_VERSION="${CURRENT}"
70
+ NEW_TAG="v${NEW_VERSION}"
71
+ fi
72
+
73
+ # Check if tag already exists
74
+ if git rev-parse "refs/tags/${NEW_TAG}" >/dev/null 2>&1; then
75
+ echo "::error::Tag ${NEW_TAG} already exists. Update the version in pyproject.toml before releasing."
76
+ exit 1
77
+ fi
78
+
79
+ echo "new_tag=${NEW_TAG}" >> "$GITHUB_OUTPUT"
80
+ echo "new_version=${NEW_VERSION}" >> "$GITHUB_OUTPUT"
81
+ echo "New release: ${NEW_TAG}"
82
+
83
+ - name: Update pyproject.toml version if needed
84
+ run: |
85
+ sed -i "s/^version = \".*\"/version = \"${{ steps.version.outputs.new_version }}\"/" pyproject.toml
86
+ echo "pyproject.toml version:"
87
+ grep '^version' pyproject.toml
88
+
89
+ - name: Commit version bump if needed
90
+ run: |
91
+ git config user.name "github-actions[bot]"
92
+ git config user.email "github-actions[bot]@users.noreply.github.com"
93
+ git add pyproject.toml
94
+ if git diff --cached --quiet; then
95
+ echo "No version change needed"
96
+ else
97
+ git commit -m "release: bump version to ${{ steps.version.outputs.new_version }}"
98
+ git push
99
+ fi
100
+
101
+ - name: Create and push tag
102
+ run: |
103
+ git tag "${{ steps.version.outputs.new_tag }}"
104
+ git push origin "${{ steps.version.outputs.new_tag }}"
105
+ echo "Created and pushed tag: ${{ steps.version.outputs.new_tag }}"
106
+
107
+ - name: Create GitHub Release
108
+ env:
109
+ GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
110
+ run: |
111
+ gh release create "${{ steps.version.outputs.new_tag }}" \
112
+ --title "${{ steps.version.outputs.new_tag }}" \
113
+ --generate-notes
114
+
115
+ - uses: actions/setup-python@v5
116
+ with:
117
+ python-version: "3.12"
118
+
119
+ - name: Build package
120
+ run: |
121
+ pip install build
122
+ python -m build
123
+
124
+ - name: Verify package
125
+ run: |
126
+ pip install dist/*.whl
127
+ python -c "import roboharness; print('Install OK')"
128
+
129
+ - name: Publish to PyPI
130
+ uses: pypa/gh-action-pypi-publish@release/v1
@@ -0,0 +1,34 @@
1
+ # Python
2
+ __pycache__/
3
+ *.py[cod]
4
+ *$py.class
5
+ *.egg-info/
6
+ dist/
7
+ build/
8
+ *.egg
9
+
10
+ # Virtual environments
11
+ .venv/
12
+ venv/
13
+ env/
14
+
15
+ # IDE
16
+ .idea/
17
+ .vscode/
18
+ *.swp
19
+ *.swo
20
+
21
+ # Testing
22
+ .pytest_cache/
23
+ .mypy_cache/
24
+ .ruff_cache/
25
+ htmlcov/
26
+ .coverage
27
+
28
+ # Harness output (don't commit captured data)
29
+ harness_output/
30
+ checkpoints/
31
+
32
+ # OS
33
+ .DS_Store
34
+ Thumbs.db
@@ -0,0 +1,62 @@
1
+ # Contributing to Roboharness
2
+
3
+ Thank you for your interest in contributing! Roboharness is in early development and we welcome contributions of all kinds.
4
+
5
+ ## How to Contribute
6
+
7
+ ### Reporting Bugs
8
+
9
+ - Open an issue on [GitHub Issues](https://github.com/MiaoDX/RobotHarness/issues)
10
+ - Include your Python version, OS, and simulator versions
11
+ - Provide a minimal reproducible example if possible
12
+
13
+ ### Suggesting Features
14
+
15
+ - Open a feature request issue
16
+ - Describe the use case and why it would be useful
17
+ - If you have a design in mind, share it — we appreciate thoughtful proposals
18
+
19
+ ### Submitting Code
20
+
21
+ 1. Fork the repository
22
+ 2. Create a feature branch: `git checkout -b feature/your-feature`
23
+ 3. Install dev dependencies: `pip install -e ".[dev]"`
24
+ 4. Make your changes
25
+ 5. Run linting and tests:
26
+ ```bash
27
+ ruff check src/ tests/
28
+ pytest
29
+ ```
30
+ 6. Commit with a clear message
31
+ 7. Open a Pull Request
32
+
33
+ ### Adding a New Simulator Backend
34
+
35
+ We especially welcome new simulator backends! To add one:
36
+
37
+ 1. Create a new file in `src/roboharness/backends/`
38
+ 2. Implement the `SimulatorBackend` protocol (see `core/harness.py`)
39
+ 3. Add an example in `examples/`
40
+ 4. Add the simulator's dependencies to `pyproject.toml` as an optional dependency group
41
+
42
+ ### Code Style
43
+
44
+ - We use [ruff](https://docs.astral.sh/ruff/) for linting and formatting
45
+ - Type hints are encouraged (Python 3.10+ style)
46
+ - Keep it simple — avoid unnecessary abstractions
47
+
48
+ ## Development Setup
49
+
50
+ ```bash
51
+ git clone https://github.com/MiaoDX/RobotHarness.git
52
+ cd RobotHarness
53
+ pip install -e ".[dev]"
54
+ ```
55
+
56
+ ## AI Agent Contributions
57
+
58
+ We actively welcome contributions from AI coding agents! If you're using [Claude Code](https://docs.anthropic.com/en/docs/claude-code), [OpenAI Codex](https://github.com/openai/codex), [OpenClaw](https://github.com/openclaw/openclaw), or other autonomous coding tools to contribute, go for it — just make sure the tests pass.
59
+
60
+ ## License
61
+
62
+ By contributing, you agree that your contributions will be licensed under the MIT License.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 MiaoDX, WLB
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.