robopilot 1.1.0__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 (91) hide show
  1. robopilot-1.1.0/LICENSE +21 -0
  2. robopilot-1.1.0/PKG-INFO +207 -0
  3. robopilot-1.1.0/README.md +174 -0
  4. robopilot-1.1.0/pyproject.toml +66 -0
  5. robopilot-1.1.0/setup.cfg +4 -0
  6. robopilot-1.1.0/src/robopilot/__init__.py +0 -0
  7. robopilot-1.1.0/src/robopilot/apply/__init__.py +5 -0
  8. robopilot-1.1.0/src/robopilot/apply/apply_plan.py +218 -0
  9. robopilot-1.1.0/src/robopilot/apply_plan/__init__.py +13 -0
  10. robopilot-1.1.0/src/robopilot/apply_plan/plan.py +216 -0
  11. robopilot-1.1.0/src/robopilot/apply_preview/__init__.py +5 -0
  12. robopilot-1.1.0/src/robopilot/apply_preview/preview.py +156 -0
  13. robopilot-1.1.0/src/robopilot/debugger/__init__.py +6 -0
  14. robopilot-1.1.0/src/robopilot/debugger/log_analyzer.py +198 -0
  15. robopilot-1.1.0/src/robopilot/deps/__init__.py +5 -0
  16. robopilot-1.1.0/src/robopilot/deps/analyzer.py +440 -0
  17. robopilot-1.1.0/src/robopilot/detector/__init__.py +5 -0
  18. robopilot-1.1.0/src/robopilot/detector/project_detector.py +349 -0
  19. robopilot-1.1.0/src/robopilot/diff/__init__.py +5 -0
  20. robopilot-1.1.0/src/robopilot/diff/spec_diff.py +189 -0
  21. robopilot-1.1.0/src/robopilot/generator/__init__.py +2 -0
  22. robopilot-1.1.0/src/robopilot/generator/project_generator.py +106 -0
  23. robopilot-1.1.0/src/robopilot/generator/project_spec.py +78 -0
  24. robopilot-1.1.0/src/robopilot/generator/task_classifier.py +55 -0
  25. robopilot-1.1.0/src/robopilot/generator/template_registry.py +177 -0
  26. robopilot-1.1.0/src/robopilot/generator/templates.py +541 -0
  27. robopilot-1.1.0/src/robopilot/graph/__init__.py +6 -0
  28. robopilot-1.1.0/src/robopilot/graph/mermaid_generator.py +34 -0
  29. robopilot-1.1.0/src/robopilot/history/__init__.py +15 -0
  30. robopilot-1.1.0/src/robopilot/history/journal.py +226 -0
  31. robopilot-1.1.0/src/robopilot/inspector/__init__.py +6 -0
  32. robopilot-1.1.0/src/robopilot/inspector/project_inspector.py +287 -0
  33. robopilot-1.1.0/src/robopilot/main.py +1294 -0
  34. robopilot-1.1.0/src/robopilot/migration/__init__.py +31 -0
  35. robopilot-1.1.0/src/robopilot/migration/plan_diff.py +177 -0
  36. robopilot-1.1.0/src/robopilot/migration/plan_validator.py +142 -0
  37. robopilot-1.1.0/src/robopilot/migration/preview.py +267 -0
  38. robopilot-1.1.0/src/robopilot/migration/ros1_to_ros2.py +579 -0
  39. robopilot-1.1.0/src/robopilot/planner/__init__.py +26 -0
  40. robopilot-1.1.0/src/robopilot/planner/base.py +30 -0
  41. robopilot-1.1.0/src/robopilot/planner/llm_planner.py +127 -0
  42. robopilot-1.1.0/src/robopilot/planner/openai_client.py +67 -0
  43. robopilot-1.1.0/src/robopilot/planner/prompts.py +59 -0
  44. robopilot-1.1.0/src/robopilot/planner/provider_config.py +43 -0
  45. robopilot-1.1.0/src/robopilot/planner/rule_based_planner.py +40 -0
  46. robopilot-1.1.0/src/robopilot/refiner/__init__.py +6 -0
  47. robopilot-1.1.0/src/robopilot/refiner/llm_refiner.py +126 -0
  48. robopilot-1.1.0/src/robopilot/refiner/spec_refiner.py +172 -0
  49. robopilot-1.1.0/src/robopilot/repair/__init__.py +9 -0
  50. robopilot-1.1.0/src/robopilot/repair/repair_suggester.py +269 -0
  51. robopilot-1.1.0/src/robopilot/report/__init__.py +5 -0
  52. robopilot-1.1.0/src/robopilot/report/project_report.py +92 -0
  53. robopilot-1.1.0/src/robopilot/rollback/__init__.py +5 -0
  54. robopilot-1.1.0/src/robopilot/rollback/rollback.py +155 -0
  55. robopilot-1.1.0/src/robopilot/ros1/__init__.py +5 -0
  56. robopilot-1.1.0/src/robopilot/ros1/inspector.py +403 -0
  57. robopilot-1.1.0/src/robopilot/spec/__init__.py +13 -0
  58. robopilot-1.1.0/src/robopilot/spec/io.py +178 -0
  59. robopilot-1.1.0/src/robopilot/spec/validator.py +66 -0
  60. robopilot-1.1.0/src/robopilot/utils/__init__.py +2 -0
  61. robopilot-1.1.0/src/robopilot/utils/file_ops.py +30 -0
  62. robopilot-1.1.0/src/robopilot.egg-info/PKG-INFO +207 -0
  63. robopilot-1.1.0/src/robopilot.egg-info/SOURCES.txt +89 -0
  64. robopilot-1.1.0/src/robopilot.egg-info/dependency_links.txt +1 -0
  65. robopilot-1.1.0/src/robopilot.egg-info/entry_points.txt +2 -0
  66. robopilot-1.1.0/src/robopilot.egg-info/requires.txt +8 -0
  67. robopilot-1.1.0/src/robopilot.egg-info/top_level.txt +1 -0
  68. robopilot-1.1.0/tests/test_apply_plan.py +228 -0
  69. robopilot-1.1.0/tests/test_apply_plan_execution.py +224 -0
  70. robopilot-1.1.0/tests/test_apply_preview.py +165 -0
  71. robopilot-1.1.0/tests/test_dependency_analyzer.py +271 -0
  72. robopilot-1.1.0/tests/test_history.py +197 -0
  73. robopilot-1.1.0/tests/test_llm_refiner.py +201 -0
  74. robopilot-1.1.0/tests/test_log_analyzer.py +33 -0
  75. robopilot-1.1.0/tests/test_mermaid_generator.py +46 -0
  76. robopilot-1.1.0/tests/test_migration_plan_diff.py +194 -0
  77. robopilot-1.1.0/tests/test_migration_plan_validate.py +129 -0
  78. robopilot-1.1.0/tests/test_migration_preview.py +219 -0
  79. robopilot-1.1.0/tests/test_planner.py +298 -0
  80. robopilot-1.1.0/tests/test_project_detector.py +219 -0
  81. robopilot-1.1.0/tests/test_project_generator.py +74 -0
  82. robopilot-1.1.0/tests/test_project_inspector.py +127 -0
  83. robopilot-1.1.0/tests/test_project_report.py +125 -0
  84. robopilot-1.1.0/tests/test_repair_suggester.py +124 -0
  85. robopilot-1.1.0/tests/test_rollback.py +151 -0
  86. robopilot-1.1.0/tests/test_ros1_inspector.py +211 -0
  87. robopilot-1.1.0/tests/test_ros1_to_ros2_migration_plan.py +238 -0
  88. robopilot-1.1.0/tests/test_spec_diff.py +177 -0
  89. robopilot-1.1.0/tests/test_spec_refiner.py +129 -0
  90. robopilot-1.1.0/tests/test_spec_workflow.py +79 -0
  91. robopilot-1.1.0/tests/test_task_classifier.py +35 -0
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 J1angJJ
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.
@@ -0,0 +1,207 @@
1
+ Metadata-Version: 2.4
2
+ Name: robopilot
3
+ Version: 1.1.0
4
+ Summary: No-ROS-required static engineering toolchain for ROS-style projects.
5
+ Author: J1angJJ
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/J1angJJ/RoboPilot
8
+ Project-URL: Repository, https://github.com/J1angJJ/RoboPilot
9
+ Project-URL: Issues, https://github.com/J1angJJ/RoboPilot/issues
10
+ Project-URL: Changelog, https://github.com/J1angJJ/RoboPilot/blob/main/CHANGELOG.md
11
+ Keywords: robotics,ros,ros1,ros2,developer-tools,code-generation,static-analysis,migration
12
+ Classifier: Development Status :: 5 - Production/Stable
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: Intended Audience :: Education
15
+ Classifier: Operating System :: OS Independent
16
+ Classifier: Programming Language :: Python :: 3
17
+ Classifier: Programming Language :: Python :: 3 :: Only
18
+ Classifier: Programming Language :: Python :: 3.10
19
+ Classifier: Programming Language :: Python :: 3.11
20
+ Classifier: Topic :: Software Development :: Code Generators
21
+ Classifier: Topic :: Software Development :: Quality Assurance
22
+ Classifier: Topic :: Scientific/Engineering
23
+ Requires-Python: <3.12,>=3.10
24
+ Description-Content-Type: text/markdown
25
+ License-File: LICENSE
26
+ Requires-Dist: typer>=0.12.0
27
+ Requires-Dist: rich>=13.0.0
28
+ Provides-Extra: dev
29
+ Requires-Dist: pytest>=8.0.0; extra == "dev"
30
+ Provides-Extra: llm
31
+ Requires-Dist: openai>=1.0.0; extra == "llm"
32
+ Dynamic: license-file
33
+
34
+ # RoboPilot
35
+
36
+ [English](README.md) | [中文](README.zh-CN.md)
37
+
38
+ [![Tests](https://github.com/J1angJJ/RoboPilot/actions/workflows/tests.yml/badge.svg)](https://github.com/J1angJJ/RoboPilot/actions/workflows/tests.yml)
39
+
40
+ RoboPilot is a no-ROS-required static engineering toolchain for ROS-style projects.
41
+
42
+ It helps robotics learners and developers plan, refine, validate, generate, inspect, update, roll back, document, and review ROS/ROS2-style project structure without installing ROS, ROS2, catkin, colcon, simulator runtimes, or robot hardware.
43
+
44
+ ## What RoboPilot Does
45
+
46
+ - Creates and validates `ProjectSpec` files from robotics tasks.
47
+ - Renders deterministic ROS-style Python package skeletons.
48
+ - Refines and diffs specs before generation.
49
+ - Previews, exports, applies, backs up, rolls back, and journals safe project updates.
50
+ - Inspects projects and exports read-only reports.
51
+ - Detects RoboPilot, ROS1, ROS2, mixed, non-ROS, and unknown project types.
52
+ - Statically inspects ROS1 catkin packages.
53
+ - Analyzes declared and detected dependencies.
54
+ - Builds static ROS1-to-ROS2 migration plans, validates/diffs them, and previews file-level migration work.
55
+ - Provides small offline utilities for robotics error logs and Mermaid workflow graphs.
56
+ - Optionally uses an LLM only to produce or refine validated `ProjectSpec` data.
57
+
58
+ RoboPilot does not run ROS, ROS2, launch files, generated code, `catkin_make`, or `colcon`.
59
+
60
+ ## Quick Start
61
+
62
+ Supported Python versions for this release line are Python 3.10 and 3.11. Package metadata declares `>=3.10,<3.12`; Python 3.12 and 3.13 are not claimed until the test suite passes there.
63
+
64
+ Install from source for now:
65
+
66
+ ```bash
67
+ python -m venv .venv
68
+ .venv\Scripts\activate
69
+ python -m pip install -U pip
70
+ pip install -e ".[dev]"
71
+ robopilot --help
72
+ ```
73
+
74
+ After PyPI release:
75
+
76
+ ```bash
77
+ pip install robopilot
78
+ robopilot --help
79
+ ```
80
+
81
+ On Windows, if pytest has temporary directory permission issues:
82
+
83
+ ```bash
84
+ python -m pytest --basetemp=".pytest_tmp" -p no:cacheprovider
85
+ ```
86
+
87
+ ## Core Workflows
88
+
89
+ Spec-first generation:
90
+
91
+ ```bash
92
+ robopilot plan --name demo_detector --task "Create an object detection pipeline" --output robopilot.yaml
93
+ robopilot validate --spec robopilot.yaml
94
+ robopilot generate --spec robopilot.yaml
95
+ ```
96
+
97
+ Iterative spec review:
98
+
99
+ ```bash
100
+ robopilot refine --spec robopilot.yaml --instruction "Add a tracker node after the detector" --output refined.yaml
101
+ robopilot diff --old robopilot.yaml --new refined.yaml
102
+ ```
103
+
104
+ Safe project update loop:
105
+
106
+ ```bash
107
+ robopilot apply-preview --spec refined.yaml --project outputs/demo_detector
108
+ robopilot apply-plan --spec refined.yaml --project outputs/demo_detector --output apply_plan.yaml
109
+ robopilot apply --plan apply_plan.yaml
110
+ robopilot apply --plan apply_plan.yaml --confirm
111
+ robopilot history --project outputs/demo_detector
112
+ ```
113
+
114
+ Static project review:
115
+
116
+ ```bash
117
+ robopilot inspect examples/generated_projects/demo_detector
118
+ robopilot repair-suggest examples/generated_projects/demo_detector
119
+ robopilot report examples/generated_projects/demo_detector --output report.md
120
+ ```
121
+
122
+ ROS-style static analysis:
123
+
124
+ ```bash
125
+ robopilot detect path/to/project
126
+ robopilot inspect-ros1 path/to/ros1_package
127
+ robopilot deps path/to/project
128
+ ```
129
+
130
+ ROS1 to ROS2 migration planning:
131
+
132
+ ```bash
133
+ robopilot migrate-plan --from path/to/ros1_package --to ros2 --output migration_plan.yaml
134
+ robopilot migrate-plan-validate --plan migration_plan.yaml
135
+ robopilot migrate-preview --plan migration_plan.yaml --project path/to/ros1_package
136
+ ```
137
+
138
+ ## Documentation
139
+
140
+ - [Command Reference](docs/command_reference.md)
141
+ - [Workflows](docs/workflows.md)
142
+ - [Architecture](docs/architecture.md)
143
+ - [Developer Setup](docs/developer_setup.md)
144
+ - [Testing](docs/testing.md)
145
+ - [Release Process](docs/release_process.md)
146
+ - [PyPI Publishing](docs/pypi_publish.md)
147
+ - [Compatibility](docs/compatibility.md)
148
+ - [Known Limitations](docs/known_limitations.md)
149
+ - [Stability Policy](docs/stability_policy.md)
150
+ - [Demo Script](docs/demo_script.md)
151
+ - [v1.0.0 Scope](docs/v1_scope.md)
152
+ - [Changelog](CHANGELOG.md)
153
+ - [Roadmap](roadmap.md)
154
+
155
+ ## Safety Model
156
+
157
+ RoboPilot is designed around static analysis and explicit review:
158
+
159
+ - Default planning, validation, diff, inspection, report, detection, dependency, and migration commands are read-only.
160
+ - `apply` is dry-run by default and writes only with `--confirm`.
161
+ - Confirmed updates write only files listed in a validated apply plan.
162
+ - Existing files are backed up before updates.
163
+ - `rollback` is dry-run by default and restores only files from RoboPilot backup directories.
164
+ - Migration planning, validation, diff, and preview do not modify source projects.
165
+ - Optional LLM paths are limited to `ProjectSpec` planning/refinement and must pass validation before generation or apply workflows.
166
+
167
+ ## Example Output
168
+
169
+ A static generated demo project is committed at:
170
+
171
+ ```txt
172
+ examples/generated_projects/demo_detector/
173
+ ```
174
+
175
+ Transient generated projects should go under `outputs/`, which is intentionally ignored by git.
176
+
177
+ ## Project Status
178
+
179
+ Current release line: `v1.1.0`.
180
+
181
+ RoboPilot's no-ROS-required static engineering workflow remains the stable v1 baseline:
182
+
183
+ ```txt
184
+ plan -> refine -> diff -> validate -> generate
185
+ -> apply-preview -> apply-plan -> apply -> rollback -> history
186
+ -> inspect -> repair-suggest -> report
187
+ -> detect -> inspect-ros1 -> deps
188
+ -> migrate-plan -> migrate-plan-validate -> migrate-plan-diff -> migrate-preview
189
+ ```
190
+
191
+ ## Development
192
+
193
+ Run tests:
194
+
195
+ ```bash
196
+ python -m pytest
197
+ ```
198
+
199
+ Windows fallback:
200
+
201
+ ```bash
202
+ python -m pytest --basetemp=".pytest_tmp" -p no:cacheprovider
203
+ ```
204
+
205
+ ## License
206
+
207
+ MIT
@@ -0,0 +1,174 @@
1
+ # RoboPilot
2
+
3
+ [English](README.md) | [中文](README.zh-CN.md)
4
+
5
+ [![Tests](https://github.com/J1angJJ/RoboPilot/actions/workflows/tests.yml/badge.svg)](https://github.com/J1angJJ/RoboPilot/actions/workflows/tests.yml)
6
+
7
+ RoboPilot is a no-ROS-required static engineering toolchain for ROS-style projects.
8
+
9
+ It helps robotics learners and developers plan, refine, validate, generate, inspect, update, roll back, document, and review ROS/ROS2-style project structure without installing ROS, ROS2, catkin, colcon, simulator runtimes, or robot hardware.
10
+
11
+ ## What RoboPilot Does
12
+
13
+ - Creates and validates `ProjectSpec` files from robotics tasks.
14
+ - Renders deterministic ROS-style Python package skeletons.
15
+ - Refines and diffs specs before generation.
16
+ - Previews, exports, applies, backs up, rolls back, and journals safe project updates.
17
+ - Inspects projects and exports read-only reports.
18
+ - Detects RoboPilot, ROS1, ROS2, mixed, non-ROS, and unknown project types.
19
+ - Statically inspects ROS1 catkin packages.
20
+ - Analyzes declared and detected dependencies.
21
+ - Builds static ROS1-to-ROS2 migration plans, validates/diffs them, and previews file-level migration work.
22
+ - Provides small offline utilities for robotics error logs and Mermaid workflow graphs.
23
+ - Optionally uses an LLM only to produce or refine validated `ProjectSpec` data.
24
+
25
+ RoboPilot does not run ROS, ROS2, launch files, generated code, `catkin_make`, or `colcon`.
26
+
27
+ ## Quick Start
28
+
29
+ Supported Python versions for this release line are Python 3.10 and 3.11. Package metadata declares `>=3.10,<3.12`; Python 3.12 and 3.13 are not claimed until the test suite passes there.
30
+
31
+ Install from source for now:
32
+
33
+ ```bash
34
+ python -m venv .venv
35
+ .venv\Scripts\activate
36
+ python -m pip install -U pip
37
+ pip install -e ".[dev]"
38
+ robopilot --help
39
+ ```
40
+
41
+ After PyPI release:
42
+
43
+ ```bash
44
+ pip install robopilot
45
+ robopilot --help
46
+ ```
47
+
48
+ On Windows, if pytest has temporary directory permission issues:
49
+
50
+ ```bash
51
+ python -m pytest --basetemp=".pytest_tmp" -p no:cacheprovider
52
+ ```
53
+
54
+ ## Core Workflows
55
+
56
+ Spec-first generation:
57
+
58
+ ```bash
59
+ robopilot plan --name demo_detector --task "Create an object detection pipeline" --output robopilot.yaml
60
+ robopilot validate --spec robopilot.yaml
61
+ robopilot generate --spec robopilot.yaml
62
+ ```
63
+
64
+ Iterative spec review:
65
+
66
+ ```bash
67
+ robopilot refine --spec robopilot.yaml --instruction "Add a tracker node after the detector" --output refined.yaml
68
+ robopilot diff --old robopilot.yaml --new refined.yaml
69
+ ```
70
+
71
+ Safe project update loop:
72
+
73
+ ```bash
74
+ robopilot apply-preview --spec refined.yaml --project outputs/demo_detector
75
+ robopilot apply-plan --spec refined.yaml --project outputs/demo_detector --output apply_plan.yaml
76
+ robopilot apply --plan apply_plan.yaml
77
+ robopilot apply --plan apply_plan.yaml --confirm
78
+ robopilot history --project outputs/demo_detector
79
+ ```
80
+
81
+ Static project review:
82
+
83
+ ```bash
84
+ robopilot inspect examples/generated_projects/demo_detector
85
+ robopilot repair-suggest examples/generated_projects/demo_detector
86
+ robopilot report examples/generated_projects/demo_detector --output report.md
87
+ ```
88
+
89
+ ROS-style static analysis:
90
+
91
+ ```bash
92
+ robopilot detect path/to/project
93
+ robopilot inspect-ros1 path/to/ros1_package
94
+ robopilot deps path/to/project
95
+ ```
96
+
97
+ ROS1 to ROS2 migration planning:
98
+
99
+ ```bash
100
+ robopilot migrate-plan --from path/to/ros1_package --to ros2 --output migration_plan.yaml
101
+ robopilot migrate-plan-validate --plan migration_plan.yaml
102
+ robopilot migrate-preview --plan migration_plan.yaml --project path/to/ros1_package
103
+ ```
104
+
105
+ ## Documentation
106
+
107
+ - [Command Reference](docs/command_reference.md)
108
+ - [Workflows](docs/workflows.md)
109
+ - [Architecture](docs/architecture.md)
110
+ - [Developer Setup](docs/developer_setup.md)
111
+ - [Testing](docs/testing.md)
112
+ - [Release Process](docs/release_process.md)
113
+ - [PyPI Publishing](docs/pypi_publish.md)
114
+ - [Compatibility](docs/compatibility.md)
115
+ - [Known Limitations](docs/known_limitations.md)
116
+ - [Stability Policy](docs/stability_policy.md)
117
+ - [Demo Script](docs/demo_script.md)
118
+ - [v1.0.0 Scope](docs/v1_scope.md)
119
+ - [Changelog](CHANGELOG.md)
120
+ - [Roadmap](roadmap.md)
121
+
122
+ ## Safety Model
123
+
124
+ RoboPilot is designed around static analysis and explicit review:
125
+
126
+ - Default planning, validation, diff, inspection, report, detection, dependency, and migration commands are read-only.
127
+ - `apply` is dry-run by default and writes only with `--confirm`.
128
+ - Confirmed updates write only files listed in a validated apply plan.
129
+ - Existing files are backed up before updates.
130
+ - `rollback` is dry-run by default and restores only files from RoboPilot backup directories.
131
+ - Migration planning, validation, diff, and preview do not modify source projects.
132
+ - Optional LLM paths are limited to `ProjectSpec` planning/refinement and must pass validation before generation or apply workflows.
133
+
134
+ ## Example Output
135
+
136
+ A static generated demo project is committed at:
137
+
138
+ ```txt
139
+ examples/generated_projects/demo_detector/
140
+ ```
141
+
142
+ Transient generated projects should go under `outputs/`, which is intentionally ignored by git.
143
+
144
+ ## Project Status
145
+
146
+ Current release line: `v1.1.0`.
147
+
148
+ RoboPilot's no-ROS-required static engineering workflow remains the stable v1 baseline:
149
+
150
+ ```txt
151
+ plan -> refine -> diff -> validate -> generate
152
+ -> apply-preview -> apply-plan -> apply -> rollback -> history
153
+ -> inspect -> repair-suggest -> report
154
+ -> detect -> inspect-ros1 -> deps
155
+ -> migrate-plan -> migrate-plan-validate -> migrate-plan-diff -> migrate-preview
156
+ ```
157
+
158
+ ## Development
159
+
160
+ Run tests:
161
+
162
+ ```bash
163
+ python -m pytest
164
+ ```
165
+
166
+ Windows fallback:
167
+
168
+ ```bash
169
+ python -m pytest --basetemp=".pytest_tmp" -p no:cacheprovider
170
+ ```
171
+
172
+ ## License
173
+
174
+ MIT
@@ -0,0 +1,66 @@
1
+ [project]
2
+ name = "robopilot"
3
+ version = "1.1.0"
4
+ description = "No-ROS-required static engineering toolchain for ROS-style projects."
5
+ readme = "README.md"
6
+ requires-python = ">=3.10,<3.12"
7
+ license = "MIT"
8
+ authors = [
9
+ { name = "J1angJJ" }
10
+ ]
11
+ keywords = [
12
+ "robotics",
13
+ "ros",
14
+ "ros1",
15
+ "ros2",
16
+ "developer-tools",
17
+ "code-generation",
18
+ "static-analysis",
19
+ "migration"
20
+ ]
21
+ classifiers = [
22
+ "Development Status :: 5 - Production/Stable",
23
+ "Intended Audience :: Developers",
24
+ "Intended Audience :: Education",
25
+ "Operating System :: OS Independent",
26
+ "Programming Language :: Python :: 3",
27
+ "Programming Language :: Python :: 3 :: Only",
28
+ "Programming Language :: Python :: 3.10",
29
+ "Programming Language :: Python :: 3.11",
30
+ "Topic :: Software Development :: Code Generators",
31
+ "Topic :: Software Development :: Quality Assurance",
32
+ "Topic :: Scientific/Engineering"
33
+ ]
34
+
35
+ dependencies = [
36
+ "typer>=0.12.0",
37
+ "rich>=13.0.0"
38
+ ]
39
+
40
+ [project.optional-dependencies]
41
+ dev = [
42
+ "pytest>=8.0.0"
43
+ ]
44
+ llm = [
45
+ "openai>=1.0.0"
46
+ ]
47
+
48
+ [project.scripts]
49
+ robopilot = "robopilot.main:app"
50
+
51
+ [project.urls]
52
+ Homepage = "https://github.com/J1angJJ/RoboPilot"
53
+ Repository = "https://github.com/J1angJJ/RoboPilot"
54
+ Issues = "https://github.com/J1angJJ/RoboPilot/issues"
55
+ Changelog = "https://github.com/J1angJJ/RoboPilot/blob/main/CHANGELOG.md"
56
+
57
+ [build-system]
58
+ requires = ["setuptools>=77.0.0", "wheel"]
59
+ build-backend = "setuptools.build_meta"
60
+
61
+ [tool.setuptools.packages.find]
62
+ where = ["src"]
63
+
64
+ [tool.pytest.ini_options]
65
+ pythonpath = ["src"]
66
+ testpaths = ["tests"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
File without changes
@@ -0,0 +1,5 @@
1
+ """Safe apply execution helpers."""
2
+
3
+ from robopilot.apply.apply_plan import ApplySummary, apply_from_plan
4
+
5
+ __all__ = ["ApplySummary", "apply_from_plan"]
@@ -0,0 +1,218 @@
1
+ """Safely apply a previously exported apply plan."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import shutil
6
+ from dataclasses import dataclass
7
+ from datetime import datetime
8
+ from pathlib import Path
9
+
10
+ from robopilot.apply_plan.plan import (
11
+ apply_plan_from_preview,
12
+ load_apply_plan,
13
+ validate_apply_plan,
14
+ )
15
+ from robopilot.apply_preview.preview import preview_apply
16
+ from robopilot.generator.project_generator import render_project_files
17
+ from robopilot.history.journal import record_history_entry
18
+ from robopilot.spec.io import load_spec
19
+ from robopilot.spec.validator import validate_spec
20
+
21
+
22
+ SAFETY_NOTE = (
23
+ "RoboPilot apply validates the plan, re-runs apply-preview, refuses stale "
24
+ "plans and conflicts, and only writes files listed in files_to_create or "
25
+ "files_to_update when --confirm is provided."
26
+ )
27
+
28
+
29
+ @dataclass(frozen=True)
30
+ class ApplySummary:
31
+ """Summary of a dry-run or confirmed apply operation."""
32
+
33
+ plan_path: str
34
+ project_path: str
35
+ dry_run: bool
36
+ files_created: tuple[str, ...]
37
+ files_updated: tuple[str, ...]
38
+ files_kept: tuple[str, ...]
39
+ backups_created: tuple[str, ...]
40
+ skipped_files: tuple[str, ...]
41
+ conflicts: tuple[str, ...]
42
+ safety_note: str
43
+
44
+ def to_dict(self) -> dict[str, object]:
45
+ """Return a stable JSON-serializable representation."""
46
+ return {
47
+ "plan_path": self.plan_path,
48
+ "project_path": self.project_path,
49
+ "dry_run": self.dry_run,
50
+ "files_created": list(self.files_created),
51
+ "files_updated": list(self.files_updated),
52
+ "files_kept": list(self.files_kept),
53
+ "backups_created": list(self.backups_created),
54
+ "skipped_files": list(self.skipped_files),
55
+ "conflicts": list(self.conflicts),
56
+ "safety_note": self.safety_note,
57
+ }
58
+
59
+
60
+ def apply_from_plan(plan_path: Path, *, confirm: bool = False) -> ApplySummary:
61
+ """Dry-run or apply a saved plan after conservative safety checks."""
62
+ plan = load_apply_plan(plan_path)
63
+ validation = validate_apply_plan(plan)
64
+ if not validation.is_valid:
65
+ raise ValueError("Invalid apply plan: " + "; ".join(validation.errors))
66
+
67
+ spec_path = Path(_string_field(plan, "spec_path"))
68
+ project_path = Path(_string_field(plan, "project_path"))
69
+ spec = load_spec(spec_path)
70
+ spec_validation = validate_spec(spec)
71
+ if not spec_validation.is_valid:
72
+ raise ValueError("Invalid ProjectSpec: " + "; ".join(spec_validation.errors))
73
+
74
+ fresh_preview = preview_apply(spec_path, project_path)
75
+ fresh_plan = apply_plan_from_preview(fresh_preview)
76
+ _ensure_plan_is_fresh(plan, fresh_plan)
77
+
78
+ conflicts = tuple(_list_field(plan, "conflicts"))
79
+ if confirm and conflicts:
80
+ raise ValueError("Refusing to apply because conflicts are present.")
81
+
82
+ files_to_create = tuple(_validate_relative_paths(_list_field(plan, "files_to_create")))
83
+ files_to_update = tuple(_validate_relative_paths(_list_field(plan, "files_to_update")))
84
+ files_to_keep = tuple(_validate_relative_paths(_list_field(plan, "files_to_keep")))
85
+ expected_files = {
86
+ path.as_posix(): content for path, content in render_project_files(spec).items()
87
+ }
88
+
89
+ _ensure_planned_files_are_expected(files_to_create + files_to_update, expected_files)
90
+
91
+ if not confirm:
92
+ return ApplySummary(
93
+ plan_path=str(plan_path),
94
+ project_path=str(project_path),
95
+ dry_run=True,
96
+ files_created=(),
97
+ files_updated=(),
98
+ files_kept=files_to_keep,
99
+ backups_created=(),
100
+ skipped_files=tuple(sorted(files_to_create + files_to_update + files_to_keep + conflicts)),
101
+ conflicts=conflicts,
102
+ safety_note="Dry run only. No files were modified. " + SAFETY_NOTE,
103
+ )
104
+
105
+ created: list[str] = []
106
+ updated: list[str] = []
107
+ backups: list[str] = []
108
+ backup_root = project_path / ".robopilot_backups" / _timestamp()
109
+
110
+ for relative_path in files_to_create:
111
+ target = project_path / relative_path
112
+ if target.exists():
113
+ raise ValueError(f"Refusing to create existing file: {relative_path}")
114
+ _write_expected_file(target, expected_files[relative_path])
115
+ created.append(relative_path)
116
+
117
+ for relative_path in files_to_update:
118
+ target = project_path / relative_path
119
+ if not target.is_file():
120
+ raise ValueError(f"Refusing to update missing file: {relative_path}")
121
+ backup_path = backup_root / relative_path
122
+ backup_path.parent.mkdir(parents=True, exist_ok=True)
123
+ shutil.copy2(target, backup_path)
124
+ backups.append(backup_path.relative_to(project_path).as_posix())
125
+ _write_expected_file(target, expected_files[relative_path])
126
+ updated.append(relative_path)
127
+
128
+ summary = ApplySummary(
129
+ plan_path=str(plan_path),
130
+ project_path=str(project_path),
131
+ dry_run=False,
132
+ files_created=tuple(sorted(created)),
133
+ files_updated=tuple(sorted(updated)),
134
+ files_kept=files_to_keep,
135
+ backups_created=tuple(sorted(backups)),
136
+ skipped_files=tuple(sorted(files_to_keep + conflicts)),
137
+ conflicts=conflicts,
138
+ safety_note=SAFETY_NOTE,
139
+ )
140
+ record_history_entry(
141
+ project_path=project_path,
142
+ operation="apply",
143
+ plan_path=str(plan_path),
144
+ dry_run=False,
145
+ success=True,
146
+ files_created=summary.files_created,
147
+ files_updated=summary.files_updated,
148
+ files_kept=summary.files_kept,
149
+ conflicts=summary.conflicts,
150
+ skipped_files=summary.skipped_files,
151
+ summary=(
152
+ f"Applied plan with {len(summary.files_created)} files created "
153
+ f"and {len(summary.files_updated)} files updated."
154
+ ),
155
+ )
156
+ return summary
157
+
158
+
159
+ def _ensure_plan_is_fresh(
160
+ saved_plan: dict[str, object],
161
+ fresh_plan: dict[str, object],
162
+ ) -> None:
163
+ compared_fields = (
164
+ "spec_path",
165
+ "project_path",
166
+ "package_name",
167
+ "selected_template",
168
+ "files_to_create",
169
+ "files_to_update",
170
+ "files_to_keep",
171
+ "conflicts",
172
+ "missing_project",
173
+ )
174
+ for field in compared_fields:
175
+ if saved_plan.get(field) != fresh_plan.get(field):
176
+ raise ValueError(f"Refusing to apply stale plan: {field} changed.")
177
+
178
+
179
+ def _ensure_planned_files_are_expected(
180
+ relative_paths: tuple[str, ...],
181
+ expected_files: dict[str, str],
182
+ ) -> None:
183
+ for relative_path in relative_paths:
184
+ if relative_path not in expected_files:
185
+ raise ValueError(f"Refusing to modify unexpected file: {relative_path}")
186
+
187
+
188
+ def _write_expected_file(path: Path, content: str) -> None:
189
+ path.parent.mkdir(parents=True, exist_ok=True)
190
+ path.write_text(content, encoding="utf-8")
191
+
192
+
193
+ def _string_field(plan: dict[str, object], field: str) -> str:
194
+ value = plan.get(field)
195
+ if not isinstance(value, str):
196
+ raise ValueError(f"{field} must be a string.")
197
+ return value
198
+
199
+
200
+ def _list_field(plan: dict[str, object], field: str) -> tuple[str, ...]:
201
+ value = plan.get(field)
202
+ if not isinstance(value, list):
203
+ raise ValueError(f"{field} must be a list.")
204
+ return tuple(str(item) for item in value)
205
+
206
+
207
+ def _validate_relative_paths(paths: tuple[str, ...]) -> tuple[str, ...]:
208
+ validated: list[str] = []
209
+ for raw_path in paths:
210
+ path = Path(raw_path)
211
+ if path.is_absolute() or ".." in path.parts:
212
+ raise ValueError(f"Refusing unsafe relative path: {raw_path}")
213
+ validated.append(path.as_posix())
214
+ return tuple(validated)
215
+
216
+
217
+ def _timestamp() -> str:
218
+ return datetime.now().strftime("%Y%m%d_%H%M%S")