rpr-cli 0.3.8__tar.gz → 0.3.11__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 (235) hide show
  1. {rpr_cli-0.3.8 → rpr_cli-0.3.11}/PKG-INFO +26 -8
  2. {rpr_cli-0.3.8 → rpr_cli-0.3.11}/README.md +24 -6
  3. {rpr_cli-0.3.8 → rpr_cli-0.3.11}/pyproject.toml +9 -2
  4. {rpr_cli-0.3.8 → rpr_cli-0.3.11}/rpr-parser/Cargo.lock +1 -1
  5. {rpr_cli-0.3.8 → rpr_cli-0.3.11}/rpr-parser/Cargo.toml +1 -1
  6. {rpr_cli-0.3.8 → rpr_cli-0.3.11}/rpr-parser/src/rename.rs +115 -17
  7. rpr_cli-0.3.11/src/rpr/__init__.py +1 -0
  8. {rpr_cli-0.3.8 → rpr_cli-0.3.11}/src/rpr/application/catalog.py +18 -1
  9. {rpr_cli-0.3.8 → rpr_cli-0.3.11}/src/rpr/application/chat_service.py +1 -1
  10. {rpr_cli-0.3.8 → rpr_cli-0.3.11}/src/rpr/application/checks.py +7 -3
  11. {rpr_cli-0.3.8 → rpr_cli-0.3.11}/src/rpr/application/prompt_service.py +1 -1
  12. {rpr_cli-0.3.8 → rpr_cli-0.3.11}/src/rpr/application/shell.py +65 -11
  13. {rpr_cli-0.3.8 → rpr_cli-0.3.11}/src/rpr/checks/base.py +0 -1
  14. {rpr_cli-0.3.8 → rpr_cli-0.3.11}/src/rpr/checks/packages.py +14 -5
  15. {rpr_cli-0.3.8 → rpr_cli-0.3.11}/src/rpr/checks/workspace.py +8 -1
  16. {rpr_cli-0.3.8 → rpr_cli-0.3.11}/src/rpr/cli.py +10 -1
  17. {rpr_cli-0.3.8 → rpr_cli-0.3.11}/src/rpr/commands/check.py +10 -1
  18. {rpr_cli-0.3.8 → rpr_cli-0.3.11}/src/rpr/commands/generate/storybook.py +21 -86
  19. {rpr_cli-0.3.8 → rpr_cli-0.3.11}/src/rpr/commands/generate/ui.py +22 -240
  20. rpr_cli-0.3.11/src/rpr/commands/generate/web.py +468 -0
  21. {rpr_cli-0.3.8 → rpr_cli-0.3.11}/src/rpr/commands/init.py +61 -57
  22. rpr_cli-0.3.11/src/rpr/commands/rename.py +452 -0
  23. rpr_cli-0.3.11/src/rpr/commands/version.py +198 -0
  24. {rpr_cli-0.3.8 → rpr_cli-0.3.11}/src/rpr/dependencies.py +17 -9
  25. {rpr_cli-0.3.8 → rpr_cli-0.3.11}/src/rpr/dependency_manifest_data.py +15 -0
  26. {rpr_cli-0.3.8 → rpr_cli-0.3.11}/src/rpr/generators/base.py +12 -0
  27. {rpr_cli-0.3.8 → rpr_cli-0.3.11}/src/rpr/generators/claude.py +2 -1
  28. {rpr_cli-0.3.8 → rpr_cli-0.3.11}/src/rpr/generators/cursor.py +3 -3
  29. {rpr_cli-0.3.8 → rpr_cli-0.3.11}/src/rpr/generators/github.py +3 -3
  30. {rpr_cli-0.3.8 → rpr_cli-0.3.11}/src/rpr/scaffolds/instructions/all.instructions.md +13 -0
  31. {rpr_cli-0.3.8 → rpr_cli-0.3.11}/src/rpr/scaffolds/instructions/frontend.instructions.md +19 -2
  32. rpr_cli-0.3.11/src/rpr/scaffolds/instructions/notebook.instructions.md +10 -0
  33. rpr_cli-0.3.11/src/rpr/scaffolds/instructions/stories-tracking.instructions.md +12 -0
  34. {rpr_cli-0.3.8 → rpr_cli-0.3.11}/src/rpr/scaffolds/instructions/tooling-setup.instructions.md +3 -0
  35. {rpr_cli-0.3.8 → rpr_cli-0.3.11}/src/rpr/templates/registry.py +9 -6
  36. {rpr_cli-0.3.8 → rpr_cli-0.3.11}/src/rpr/ui/prompt_session.py +1 -1
  37. {rpr_cli-0.3.8 → rpr_cli-0.3.11}/src/rpr/ui/theme.py +11 -10
  38. rpr_cli-0.3.11/src/rpr/workspace.py +303 -0
  39. rpr_cli-0.3.8/src/rpr/__init__.py +0 -1
  40. rpr_cli-0.3.8/src/rpr/commands/rename.py +0 -104
  41. rpr_cli-0.3.8/src/rpr/workspace.py +0 -139
  42. {rpr_cli-0.3.8 → rpr_cli-0.3.11}/LICENSE +0 -0
  43. {rpr_cli-0.3.8 → rpr_cli-0.3.11}/rpr-parser/src/config/container.rs +0 -0
  44. {rpr_cli-0.3.8 → rpr_cli-0.3.11}/rpr-parser/src/config/mod.rs +0 -0
  45. {rpr_cli-0.3.8 → rpr_cli-0.3.11}/rpr-parser/src/config/settings.rs +0 -0
  46. {rpr_cli-0.3.8 → rpr_cli-0.3.11}/rpr-parser/src/graph.rs +0 -0
  47. {rpr_cli-0.3.8 → rpr_cli-0.3.11}/rpr-parser/src/lib.rs +0 -0
  48. {rpr_cli-0.3.8 → rpr_cli-0.3.11}/rpr-parser/src/models/dtos/mod.rs +0 -0
  49. {rpr_cli-0.3.8 → rpr_cli-0.3.11}/rpr-parser/src/models/entities/mod.rs +0 -0
  50. {rpr_cli-0.3.8 → rpr_cli-0.3.11}/rpr-parser/src/models/entities/users.rs +0 -0
  51. {rpr_cli-0.3.8 → rpr_cli-0.3.11}/rpr-parser/src/models/mod.rs +0 -0
  52. {rpr_cli-0.3.8 → rpr_cli-0.3.11}/rpr-parser/src/parse.rs +0 -0
  53. {rpr_cli-0.3.8 → rpr_cli-0.3.11}/rpr-parser/src/repos/base.rs +0 -0
  54. {rpr_cli-0.3.8 → rpr_cli-0.3.11}/rpr-parser/src/repos/mod.rs +0 -0
  55. {rpr_cli-0.3.8 → rpr_cli-0.3.11}/rpr-parser/src/repos/users.rs +0 -0
  56. {rpr_cli-0.3.8 → rpr_cli-0.3.11}/rpr-parser/src/resolve.rs +0 -0
  57. {rpr_cli-0.3.8 → rpr_cli-0.3.11}/rpr-parser/src/walker.rs +0 -0
  58. {rpr_cli-0.3.8 → rpr_cli-0.3.11}/src/rpr/_native.pyi +0 -0
  59. {rpr_cli-0.3.8 → rpr_cli-0.3.11}/src/rpr/agent/__init__.py +0 -0
  60. {rpr_cli-0.3.8 → rpr_cli-0.3.11}/src/rpr/agent/approval.py +0 -0
  61. {rpr_cli-0.3.8 → rpr_cli-0.3.11}/src/rpr/agent/bootstrap.py +0 -0
  62. {rpr_cli-0.3.8 → rpr_cli-0.3.11}/src/rpr/agent/client.py +0 -0
  63. {rpr_cli-0.3.8 → rpr_cli-0.3.11}/src/rpr/agent/control.py +0 -0
  64. {rpr_cli-0.3.8 → rpr_cli-0.3.11}/src/rpr/agent/mock.py +0 -0
  65. {rpr_cli-0.3.8 → rpr_cli-0.3.11}/src/rpr/agent/runtime.py +0 -0
  66. {rpr_cli-0.3.8 → rpr_cli-0.3.11}/src/rpr/agent/session.py +0 -0
  67. {rpr_cli-0.3.8 → rpr_cli-0.3.11}/src/rpr/agent/tools/__init__.py +0 -0
  68. {rpr_cli-0.3.8 → rpr_cli-0.3.11}/src/rpr/agent/tools/base.py +0 -0
  69. {rpr_cli-0.3.8 → rpr_cli-0.3.11}/src/rpr/agent/tools/mutating.py +0 -0
  70. {rpr_cli-0.3.8 → rpr_cli-0.3.11}/src/rpr/agent/tools/readonly.py +0 -0
  71. {rpr_cli-0.3.8 → rpr_cli-0.3.11}/src/rpr/agent/tools/registry.py +0 -0
  72. {rpr_cli-0.3.8 → rpr_cli-0.3.11}/src/rpr/application/__init__.py +0 -0
  73. {rpr_cli-0.3.8 → rpr_cli-0.3.11}/src/rpr/application/cli_adapter.py +0 -0
  74. {rpr_cli-0.3.8 → rpr_cli-0.3.11}/src/rpr/application/completer.py +0 -0
  75. {rpr_cli-0.3.8 → rpr_cli-0.3.11}/src/rpr/application/conversation_service.py +0 -0
  76. {rpr_cli-0.3.8 → rpr_cli-0.3.11}/src/rpr/application/selector.py +0 -0
  77. {rpr_cli-0.3.8 → rpr_cli-0.3.11}/src/rpr/checks/__init__.py +0 -0
  78. {rpr_cli-0.3.8 → rpr_cli-0.3.11}/src/rpr/checks/instructions.py +0 -0
  79. {rpr_cli-0.3.8 → rpr_cli-0.3.11}/src/rpr/commands/__init__.py +0 -0
  80. {rpr_cli-0.3.8 → rpr_cli-0.3.11}/src/rpr/commands/add.py +0 -0
  81. {rpr_cli-0.3.8 → rpr_cli-0.3.11}/src/rpr/commands/chat.py +0 -0
  82. {rpr_cli-0.3.8 → rpr_cli-0.3.11}/src/rpr/commands/generate/__init__.py +0 -0
  83. {rpr_cli-0.3.8 → rpr_cli-0.3.11}/src/rpr/commands/generate/api.py +0 -0
  84. {rpr_cli-0.3.8 → rpr_cli-0.3.11}/src/rpr/commands/generate/domain.py +0 -0
  85. {rpr_cli-0.3.8 → rpr_cli-0.3.11}/src/rpr/commands/generate/engine.py +0 -0
  86. {rpr_cli-0.3.8 → rpr_cli-0.3.11}/src/rpr/commands/map.py +0 -0
  87. {rpr_cli-0.3.8 → rpr_cli-0.3.11}/src/rpr/commands/refresh.py +0 -0
  88. {rpr_cli-0.3.8 → rpr_cli-0.3.11}/src/rpr/commands/settings.py +0 -0
  89. {rpr_cli-0.3.8 → rpr_cli-0.3.11}/src/rpr/commands/sync.py +0 -0
  90. {rpr_cli-0.3.8 → rpr_cli-0.3.11}/src/rpr/commands/update.py +0 -0
  91. {rpr_cli-0.3.8 → rpr_cli-0.3.11}/src/rpr/context.py +0 -0
  92. {rpr_cli-0.3.8 → rpr_cli-0.3.11}/src/rpr/generators/__init__.py +0 -0
  93. {rpr_cli-0.3.8 → rpr_cli-0.3.11}/src/rpr/generators/codex.py +0 -0
  94. {rpr_cli-0.3.8 → rpr_cli-0.3.11}/src/rpr/generators/copilot.py +0 -0
  95. {rpr_cli-0.3.8 → rpr_cli-0.3.11}/src/rpr/generators/gemini.py +0 -0
  96. {rpr_cli-0.3.8 → rpr_cli-0.3.11}/src/rpr/manifest_refresh.py +0 -0
  97. {rpr_cli-0.3.8 → rpr_cli-0.3.11}/src/rpr/map/__init__.py +0 -0
  98. {rpr_cli-0.3.8 → rpr_cli-0.3.11}/src/rpr/map/architecture.py +0 -0
  99. {rpr_cli-0.3.8 → rpr_cli-0.3.11}/src/rpr/map/chains.py +0 -0
  100. {rpr_cli-0.3.8 → rpr_cli-0.3.11}/src/rpr/map/classifier.py +0 -0
  101. {rpr_cli-0.3.8 → rpr_cli-0.3.11}/src/rpr/map/coverage.py +0 -0
  102. {rpr_cli-0.3.8 → rpr_cli-0.3.11}/src/rpr/map/dependencies.py +0 -0
  103. {rpr_cli-0.3.8 → rpr_cli-0.3.11}/src/rpr/map/extractor.py +0 -0
  104. {rpr_cli-0.3.8 → rpr_cli-0.3.11}/src/rpr/map/graph.py +0 -0
  105. {rpr_cli-0.3.8 → rpr_cli-0.3.11}/src/rpr/map/output.py +0 -0
  106. {rpr_cli-0.3.8 → rpr_cli-0.3.11}/src/rpr/map/responsibility.py +0 -0
  107. {rpr_cli-0.3.8 → rpr_cli-0.3.11}/src/rpr/map/topology.py +0 -0
  108. {rpr_cli-0.3.8 → rpr_cli-0.3.11}/src/rpr/map/walker.py +0 -0
  109. {rpr_cli-0.3.8 → rpr_cli-0.3.11}/src/rpr/package_updates.py +0 -0
  110. {rpr_cli-0.3.8 → rpr_cli-0.3.11}/src/rpr/project_rename.py +0 -0
  111. {rpr_cli-0.3.8 → rpr_cli-0.3.11}/src/rpr/scaffolds/domain/base_entity.md +0 -0
  112. {rpr_cli-0.3.8 → rpr_cli-0.3.11}/src/rpr/scaffolds/domain/base_repo.md +0 -0
  113. {rpr_cli-0.3.8 → rpr_cli-0.3.11}/src/rpr/scaffolds/domain/container.md +0 -0
  114. {rpr_cli-0.3.8 → rpr_cli-0.3.11}/src/rpr/scaffolds/domain/settings.md +0 -0
  115. {rpr_cli-0.3.8 → rpr_cli-0.3.11}/src/rpr/scaffolds/instructions/api.instructions.md +0 -0
  116. {rpr_cli-0.3.8 → rpr_cli-0.3.11}/src/rpr/scaffolds/instructions/domain.instructions.md +0 -0
  117. {rpr_cli-0.3.8 → rpr_cli-0.3.11}/src/rpr/scaffolds/instructions/rust-engine.instructions.md +0 -0
  118. {rpr_cli-0.3.8 → rpr_cli-0.3.11}/src/rpr/scaffolds/instructions/setup-guide.instructions.md +0 -0
  119. {rpr_cli-0.3.8 → rpr_cli-0.3.11}/src/rpr/scaffolds/instructions/tooling.instructions.md +0 -0
  120. {rpr_cli-0.3.8 → rpr_cli-0.3.11}/src/rpr/scaffolds/js_special_files/chat/BACKEND_FLOW.md +0 -0
  121. {rpr_cli-0.3.8 → rpr_cli-0.3.11}/src/rpr/scaffolds/js_special_files/chat/README.md +0 -0
  122. {rpr_cli-0.3.8 → rpr_cli-0.3.11}/src/rpr/scaffolds/js_special_files/chat/client/content.ts +0 -0
  123. {rpr_cli-0.3.8 → rpr_cli-0.3.11}/src/rpr/scaffolds/js_special_files/chat/client/httpChatStreamClient.spec.ts +0 -0
  124. {rpr_cli-0.3.8 → rpr_cli-0.3.11}/src/rpr/scaffolds/js_special_files/chat/client/httpChatStreamClient.ts +0 -0
  125. {rpr_cli-0.3.8 → rpr_cli-0.3.11}/src/rpr/scaffolds/js_special_files/chat/client/index.ts +0 -0
  126. {rpr_cli-0.3.8 → rpr_cli-0.3.11}/src/rpr/scaffolds/js_special_files/chat/client/types.ts +0 -0
  127. {rpr_cli-0.3.8 → rpr_cli-0.3.11}/src/rpr/scaffolds/js_special_files/chat/composer/ChatComposer.spec.tsx +0 -0
  128. {rpr_cli-0.3.8 → rpr_cli-0.3.11}/src/rpr/scaffolds/js_special_files/chat/composer/ChatComposer.tsx +0 -0
  129. {rpr_cli-0.3.8 → rpr_cli-0.3.11}/src/rpr/scaffolds/js_special_files/chat/composer/__screenshots__/ChatComposer.spec.tsx/ChatComposer-renders-custom-action-buttons-beside-the-attachment-control-1.png +0 -0
  130. {rpr_cli-0.3.8 → rpr_cli-0.3.11}/src/rpr/scaffolds/js_special_files/chat/composer/fileDrop.spec.ts +0 -0
  131. {rpr_cli-0.3.8 → rpr_cli-0.3.11}/src/rpr/scaffolds/js_special_files/chat/composer/fileDrop.ts +0 -0
  132. {rpr_cli-0.3.8 → rpr_cli-0.3.11}/src/rpr/scaffolds/js_special_files/chat/composer/index.ts +0 -0
  133. {rpr_cli-0.3.8 → rpr_cli-0.3.11}/src/rpr/scaffolds/js_special_files/chat/composer/suggestions/ChatComposerMentionTokens.tsx +0 -0
  134. {rpr_cli-0.3.8 → rpr_cli-0.3.11}/src/rpr/scaffolds/js_special_files/chat/composer/suggestions/ChatSuggestionsPopover.tsx +0 -0
  135. {rpr_cli-0.3.8 → rpr_cli-0.3.11}/src/rpr/scaffolds/js_special_files/chat/composer/suggestions/LocalFileSuggestionAction.tsx +0 -0
  136. {rpr_cli-0.3.8 → rpr_cli-0.3.11}/src/rpr/scaffolds/js_special_files/chat/composer/suggestions/index.ts +0 -0
  137. {rpr_cli-0.3.8 → rpr_cli-0.3.11}/src/rpr/scaffolds/js_special_files/chat/composer/suggestions/localFileSuggestions.spec.ts +0 -0
  138. {rpr_cli-0.3.8 → rpr_cli-0.3.11}/src/rpr/scaffolds/js_special_files/chat/composer/suggestions/localFileSuggestions.ts +0 -0
  139. {rpr_cli-0.3.8 → rpr_cli-0.3.11}/src/rpr/scaffolds/js_special_files/chat/composer/suggestions/triggerDetection.spec.ts +0 -0
  140. {rpr_cli-0.3.8 → rpr_cli-0.3.11}/src/rpr/scaffolds/js_special_files/chat/composer/suggestions/triggerDetection.ts +0 -0
  141. {rpr_cli-0.3.8 → rpr_cli-0.3.11}/src/rpr/scaffolds/js_special_files/chat/composer/suggestions/types.ts +0 -0
  142. {rpr_cli-0.3.8 → rpr_cli-0.3.11}/src/rpr/scaffolds/js_special_files/chat/composer/suggestions/useChatSuggestions.ts +0 -0
  143. {rpr_cli-0.3.8 → rpr_cli-0.3.11}/src/rpr/scaffolds/js_special_files/chat/composer/types.ts +0 -0
  144. {rpr_cli-0.3.8 → rpr_cli-0.3.11}/src/rpr/scaffolds/js_special_files/chat/composer/useChatComposer.ts +0 -0
  145. {rpr_cli-0.3.8 → rpr_cli-0.3.11}/src/rpr/scaffolds/js_special_files/chat/composer/useChatStream.spec.tsx +0 -0
  146. {rpr_cli-0.3.8 → rpr_cli-0.3.11}/src/rpr/scaffolds/js_special_files/chat/composer/useChatStream.ts +0 -0
  147. {rpr_cli-0.3.8 → rpr_cli-0.3.11}/src/rpr/scaffolds/js_special_files/chat/experience/ChatExperience.spec.tsx +0 -0
  148. {rpr_cli-0.3.8 → rpr_cli-0.3.11}/src/rpr/scaffolds/js_special_files/chat/experience/ChatExperience.styles.tsx +0 -0
  149. {rpr_cli-0.3.8 → rpr_cli-0.3.11}/src/rpr/scaffolds/js_special_files/chat/experience/ChatExperience.tsx +0 -0
  150. {rpr_cli-0.3.8 → rpr_cli-0.3.11}/src/rpr/scaffolds/js_special_files/chat/experience/ChatExperienceComposer.styles.tsx +0 -0
  151. {rpr_cli-0.3.8 → rpr_cli-0.3.11}/src/rpr/scaffolds/js_special_files/chat/experience/ChatExperienceComposer.tsx +0 -0
  152. {rpr_cli-0.3.8 → rpr_cli-0.3.11}/src/rpr/scaffolds/js_special_files/chat/experience/ChatExperienceHeader.styles.tsx +0 -0
  153. {rpr_cli-0.3.8 → rpr_cli-0.3.11}/src/rpr/scaffolds/js_special_files/chat/experience/ChatExperienceHeader.tsx +0 -0
  154. {rpr_cli-0.3.8 → rpr_cli-0.3.11}/src/rpr/scaffolds/js_special_files/chat/experience/ThreadHistory.styles.tsx +0 -0
  155. {rpr_cli-0.3.8 → rpr_cli-0.3.11}/src/rpr/scaffolds/js_special_files/chat/experience/ThreadHistory.tsx +0 -0
  156. {rpr_cli-0.3.8 → rpr_cli-0.3.11}/src/rpr/scaffolds/js_special_files/chat/experience/chatStorage.spec.ts +0 -0
  157. {rpr_cli-0.3.8 → rpr_cli-0.3.11}/src/rpr/scaffolds/js_special_files/chat/experience/chatStorage.ts +0 -0
  158. {rpr_cli-0.3.8 → rpr_cli-0.3.11}/src/rpr/scaffolds/js_special_files/chat/experience/index.ts +0 -0
  159. {rpr_cli-0.3.8 → rpr_cli-0.3.11}/src/rpr/scaffolds/js_special_files/chat/experience/types.ts +0 -0
  160. {rpr_cli-0.3.8 → rpr_cli-0.3.11}/src/rpr/scaffolds/js_special_files/chat/experience/useChatExperience.spec.tsx +0 -0
  161. {rpr_cli-0.3.8 → rpr_cli-0.3.11}/src/rpr/scaffolds/js_special_files/chat/experience/useChatExperience.ts +0 -0
  162. {rpr_cli-0.3.8 → rpr_cli-0.3.11}/src/rpr/scaffolds/js_special_files/chat/index.ts +0 -0
  163. {rpr_cli-0.3.8 → rpr_cli-0.3.11}/src/rpr/scaffolds/js_special_files/chat/testing/createMockChatStreamClient.ts +0 -0
  164. {rpr_cli-0.3.8 → rpr_cli-0.3.11}/src/rpr/scaffolds/js_special_files/chat/testing/createMockSuggestionProvider.ts +0 -0
  165. {rpr_cli-0.3.8 → rpr_cli-0.3.11}/src/rpr/scaffolds/js_special_files/chat/testing/index.ts +0 -0
  166. {rpr_cli-0.3.8 → rpr_cli-0.3.11}/src/rpr/scaffolds/js_special_files/chat/thread/ChatMessage.tsx +0 -0
  167. {rpr_cli-0.3.8 → rpr_cli-0.3.11}/src/rpr/scaffolds/js_special_files/chat/thread/ChatMessageActions.tsx +0 -0
  168. {rpr_cli-0.3.8 → rpr_cli-0.3.11}/src/rpr/scaffolds/js_special_files/chat/thread/ChatMessageContent.spec.tsx +0 -0
  169. {rpr_cli-0.3.8 → rpr_cli-0.3.11}/src/rpr/scaffolds/js_special_files/chat/thread/ChatMessageContent.tsx +0 -0
  170. {rpr_cli-0.3.8 → rpr_cli-0.3.11}/src/rpr/scaffolds/js_special_files/chat/thread/ChatThread.spec.tsx +0 -0
  171. {rpr_cli-0.3.8 → rpr_cli-0.3.11}/src/rpr/scaffolds/js_special_files/chat/thread/ChatThread.tsx +0 -0
  172. {rpr_cli-0.3.8 → rpr_cli-0.3.11}/src/rpr/scaffolds/js_special_files/chat/thread/README.md +0 -0
  173. {rpr_cli-0.3.8 → rpr_cli-0.3.11}/src/rpr/scaffolds/js_special_files/chat/thread/contentFormat.ts +0 -0
  174. {rpr_cli-0.3.8 → rpr_cli-0.3.11}/src/rpr/scaffolds/js_special_files/chat/thread/index.ts +0 -0
  175. {rpr_cli-0.3.8 → rpr_cli-0.3.11}/src/rpr/scaffolds/js_special_files/chat/thread/streaming.ts +0 -0
  176. {rpr_cli-0.3.8 → rpr_cli-0.3.11}/src/rpr/scaffolds/js_special_files/chat/thread/types.ts +0 -0
  177. {rpr_cli-0.3.8 → rpr_cli-0.3.11}/src/rpr/scaffolds/js_special_files/chat/virtual-list/VirtualComponent.spec.tsx +0 -0
  178. {rpr_cli-0.3.8 → rpr_cli-0.3.11}/src/rpr/scaffolds/js_special_files/chat/virtual-list/VirtualComponent.tsx +0 -0
  179. {rpr_cli-0.3.8 → rpr_cli-0.3.11}/src/rpr/scaffolds/js_special_files/chat/virtual-list/VirtualComponentList.spec.tsx +0 -0
  180. {rpr_cli-0.3.8 → rpr_cli-0.3.11}/src/rpr/scaffolds/js_special_files/chat/virtual-list/VirtualComponentList.tsx +0 -0
  181. {rpr_cli-0.3.8 → rpr_cli-0.3.11}/src/rpr/scaffolds/js_special_files/chat/virtual-list/index.ts +0 -0
  182. {rpr_cli-0.3.8 → rpr_cli-0.3.11}/src/rpr/scaffolds/js_special_files/fetch.service.md +0 -0
  183. {rpr_cli-0.3.8 → rpr_cli-0.3.11}/src/rpr/scaffolds/js_special_files/sticky-navigation.md +0 -0
  184. {rpr_cli-0.3.8 → rpr_cli-0.3.11}/src/rpr/scaffolds/js_special_files/sticky-navigation.spec.md +0 -0
  185. {rpr_cli-0.3.8 → rpr_cli-0.3.11}/src/rpr/scaffolds/js_special_files/vitest-browser-config.md +0 -0
  186. {rpr_cli-0.3.8 → rpr_cli-0.3.11}/src/rpr/scaffolds/py_special_files/chat/api/routes/chat_routes.py +0 -0
  187. {rpr_cli-0.3.8 → rpr_cli-0.3.11}/src/rpr/scaffolds/py_special_files/chat/api/tests/test_chat.py +0 -0
  188. {rpr_cli-0.3.8 → rpr_cli-0.3.11}/src/rpr/scaffolds/py_special_files/chat/domain/models/dtos/chat_dtos.py +0 -0
  189. {rpr_cli-0.3.8 → rpr_cli-0.3.11}/src/rpr/scaffolds/py_special_files/chat/domain/models/dtos/conversation_history_dtos.py +0 -0
  190. {rpr_cli-0.3.8 → rpr_cli-0.3.11}/src/rpr/scaffolds/py_special_files/chat/domain/models/entities/conversation_history.py +0 -0
  191. {rpr_cli-0.3.8 → rpr_cli-0.3.11}/src/rpr/scaffolds/py_special_files/chat/domain/repos/conversation_history.py +0 -0
  192. {rpr_cli-0.3.8 → rpr_cli-0.3.11}/src/rpr/scaffolds/py_special_files/chat/domain/services/agent_session_codec.py +0 -0
  193. {rpr_cli-0.3.8 → rpr_cli-0.3.11}/src/rpr/scaffolds/py_special_files/chat/domain/services/chat_service.py +0 -0
  194. {rpr_cli-0.3.8 → rpr_cli-0.3.11}/src/rpr/scaffolds/py_special_files/chat/domain/services/conversation_history_service.py +0 -0
  195. {rpr_cli-0.3.8 → rpr_cli-0.3.11}/src/rpr/scaffolds/py_special_files/chat/domain/services/conversation_operations.py +0 -0
  196. {rpr_cli-0.3.8 → rpr_cli-0.3.11}/src/rpr/scaffolds/py_special_files/chat/domain/services/history_codec.py +0 -0
  197. {rpr_cli-0.3.8 → rpr_cli-0.3.11}/src/rpr/scaffolds/py_special_files/chat/domain/services/in_memory_history_store.py +0 -0
  198. {rpr_cli-0.3.8 → rpr_cli-0.3.11}/src/rpr/scaffolds/py_special_files/chat/domain/services/postgres_history_store.py +0 -0
  199. {rpr_cli-0.3.8 → rpr_cli-0.3.11}/src/rpr/scaffolds/py_special_files/chat/domain/services/protocols/conversation_history_store.py +0 -0
  200. {rpr_cli-0.3.8 → rpr_cli-0.3.11}/src/rpr/scaffolds/py_special_files/chat/domain/services/protocols/conversation_runtime.py +0 -0
  201. {rpr_cli-0.3.8 → rpr_cli-0.3.11}/src/rpr/scaffolds/py_special_files/chat/domain/services/protocols/llm_provider.py +0 -0
  202. {rpr_cli-0.3.8 → rpr_cli-0.3.11}/src/rpr/scaffolds/py_special_files/chat/domain/tests/test_chat_dtos.py +0 -0
  203. {rpr_cli-0.3.8 → rpr_cli-0.3.11}/src/rpr/scaffolds/py_special_files/chat/domain/tests/test_chat_service.py +0 -0
  204. {rpr_cli-0.3.8 → rpr_cli-0.3.11}/src/rpr/scaffolds/py_special_files/chat/domain/tests/test_history_codec.py +0 -0
  205. {rpr_cli-0.3.8 → rpr_cli-0.3.11}/src/rpr/scaffolds/py_special_files/chat/domain/tests/test_history_projection.py +0 -0
  206. {rpr_cli-0.3.8 → rpr_cli-0.3.11}/src/rpr/scaffolds/py_special_files/chat/domain/tests/test_persistence_config.py +0 -0
  207. {rpr_cli-0.3.8 → rpr_cli-0.3.11}/src/rpr/scaffolds/py_special_files/chat/domain/tests/test_postgres_history_store.py +0 -0
  208. {rpr_cli-0.3.8 → rpr_cli-0.3.11}/src/rpr/scaffolds/py_special_files/vllm_provider/domain/models/dtos/__init__.py +0 -0
  209. {rpr_cli-0.3.8 → rpr_cli-0.3.11}/src/rpr/scaffolds/py_special_files/vllm_provider/domain/models/dtos/vllm_model_dtos.py +0 -0
  210. {rpr_cli-0.3.8 → rpr_cli-0.3.11}/src/rpr/scaffolds/py_special_files/vllm_provider/domain/services/agents/VLLM_SETUP.md +0 -0
  211. {rpr_cli-0.3.8 → rpr_cli-0.3.11}/src/rpr/scaffolds/py_special_files/vllm_provider/domain/services/agents/__init__.py +0 -0
  212. {rpr_cli-0.3.8 → rpr_cli-0.3.11}/src/rpr/scaffolds/py_special_files/vllm_provider/domain/services/agents/model_catalog.py +0 -0
  213. {rpr_cli-0.3.8 → rpr_cli-0.3.11}/src/rpr/scaffolds/py_special_files/vllm_provider/domain/services/agents/vllm_agent_factory.py +0 -0
  214. {rpr_cli-0.3.8 → rpr_cli-0.3.11}/src/rpr/scaffolds/py_special_files/vllm_provider/domain/services/agents/vllm_agent_factory_protocol.py +0 -0
  215. {rpr_cli-0.3.8 → rpr_cli-0.3.11}/src/rpr/scaffolds/py_special_files/vllm_provider/domain/services/agents/vllm_inference_coordinator.py +0 -0
  216. {rpr_cli-0.3.8 → rpr_cli-0.3.11}/src/rpr/scaffolds/py_special_files/vllm_provider/domain/services/runtimes/__init__.py +0 -0
  217. {rpr_cli-0.3.8 → rpr_cli-0.3.11}/src/rpr/scaffolds/py_special_files/vllm_provider/domain/services/runtimes/agents_sdk.py +0 -0
  218. {rpr_cli-0.3.8 → rpr_cli-0.3.11}/src/rpr/scaffolds/py_special_files/vllm_provider/domain/services/runtimes/agents_sdk_message_mapping.py +0 -0
  219. {rpr_cli-0.3.8 → rpr_cli-0.3.11}/src/rpr/scaffolds/py_special_files/vllm_provider/domain/tests/test_agents_sdk_runtime.py +0 -0
  220. {rpr_cli-0.3.8 → rpr_cli-0.3.11}/src/rpr/scaffolds/py_special_files/vllm_provider/domain/tests/test_vllm_model_catalog.py +0 -0
  221. {rpr_cli-0.3.8 → rpr_cli-0.3.11}/src/rpr/scaffolds/py_special_files/vllm_provider/domain/tests/test_vllm_provider.py +0 -0
  222. {rpr_cli-0.3.8 → rpr_cli-0.3.11}/src/rpr/scaffolds/special_files/camel_case_model.md +0 -0
  223. {rpr_cli-0.3.8 → rpr_cli-0.3.11}/src/rpr/scaffolds/special_files/domain_container.md +0 -0
  224. {rpr_cli-0.3.8 → rpr_cli-0.3.11}/src/rpr/scaffolds/special_files/domain_settings.md +0 -0
  225. {rpr_cli-0.3.8 → rpr_cli-0.3.11}/src/rpr/scaffolds/special_files/dto_util.md +0 -0
  226. {rpr_cli-0.3.8 → rpr_cli-0.3.11}/src/rpr/scaffolds/special_files/encrypted_column.md +0 -0
  227. {rpr_cli-0.3.8 → rpr_cli-0.3.11}/src/rpr/scaffolds/special_files/logging.md +0 -0
  228. {rpr_cli-0.3.8 → rpr_cli-0.3.11}/src/rpr/scaffolds/special_files/mapper_util.md +0 -0
  229. {rpr_cli-0.3.8 → rpr_cli-0.3.11}/src/rpr/scaffolds/special_files/partial_update.md +0 -0
  230. {rpr_cli-0.3.8 → rpr_cli-0.3.11}/src/rpr/scaffolds/special_files/test_encrypted_column.md +0 -0
  231. {rpr_cli-0.3.8 → rpr_cli-0.3.11}/src/rpr/templates/__init__.py +0 -0
  232. {rpr_cli-0.3.8 → rpr_cli-0.3.11}/src/rpr/ui/__init__.py +0 -0
  233. {rpr_cli-0.3.8 → rpr_cli-0.3.11}/src/rpr/ui/console.py +0 -0
  234. {rpr_cli-0.3.8 → rpr_cli-0.3.11}/src/rpr/ui/markdown.py +0 -0
  235. {rpr_cli-0.3.8 → rpr_cli-0.3.11}/src/rpr/ui/renderers.py +0 -0
@@ -1,7 +1,7 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: rpr-cli
3
- Version: 0.3.8
4
- Requires-Dist: typer>=0.24.1
3
+ Version: 0.3.11
4
+ Requires-Dist: typer>=0.24.1,<0.26
5
5
  Requires-Dist: jinja2>=3.1.5
6
6
  Requires-Dist: pyyaml>=6.0.2
7
7
  Requires-Dist: rich>=14.3.3
@@ -70,6 +70,7 @@ cd my-app
70
70
  rpr generate domain my-app # Add a Python domain package
71
71
  rpr generate api my-app # Add a FastAPI application
72
72
  rpr generate ui my-app # Add a React UI library and web app
73
+ rpr generate web landing # Add another web app; asks whether to depend on the UI library
73
74
  rpr generate engine my-app # Add a Rust/PyO3 computation engine
74
75
  rpr generate storybook # Add Storybook for UI component development
75
76
  rpr rename new-app # Rename the workspace identity and references
@@ -87,6 +88,19 @@ source and experiments to remain in rename scope. Binary contents, VCS data, and
87
88
  symlink targets are never changed. The outer workspace directory keeps its
88
89
  existing name.
89
90
 
91
+ After applying the rename, RPR synchronizes Node and Python dependencies and
92
+ runs uncached Nx build, Rust-engine build, and typecheck targets with the Nx
93
+ daemon disabled. This verifies the renamed project graph and commands without
94
+ deleting the existing Nx cache. Pass `--skip-verify` to omit these checks; a
95
+ dry run previews them without executing anything. If verification fails, the
96
+ rename remains applied and RPR prints the command to retry.
97
+
98
+ The success output includes a guarded command for optionally renaming the outer
99
+ workspace directory. Git remotes and hosted repository names are never changed.
100
+ When a hosted repository still matches the old project name, rename it through
101
+ the hosting provider if desired and then update its local URL with
102
+ `git remote set-url`. Local and remote branch names are unaffected.
103
+
90
104
  `rpr init <project-name>` creates the project directory when it does not exist. If the directory already exists and is not an NX workspace, RPR initializes Git and runs `nx init` inside that directory.
91
105
 
92
106
  By default, `rpr init` scaffolds the root workspace and AI instructions only. To create project components during initialization, repeat `--scaffold` or pass a comma-separated list:
@@ -111,14 +125,16 @@ Available scaffolds are:
111
125
 
112
126
  ### Dependency Placement
113
127
 
114
- RPR keeps dependencies close to the package that uses them.
128
+ RPR keeps Python dependencies close to the package that uses them and Node dependencies at the workspace root.
115
129
 
116
130
  - Root Python tooling such as Ruff, pytest, pyright, pre-commit, and maturin lives in the root `pyproject.toml`.
117
131
  - Domain runtime dependencies such as `sqlmodel`, `asyncpg`, `pydantic-settings`, and optional `alembic` are added to `packages/python/domain/pyproject.toml`.
118
132
  - API runtime dependencies such as `fastapi`, `uvicorn`, and the workspace domain package are added to `apps/<name>-api/pyproject.toml`.
119
- - Node dependencies are written to the relevant package-level `package.json` files, and `npm install` runs from the root workspace.
133
+ - Node dependencies are declared and installed only in the root `package.json`, so every app and package shares one version of each. Package-level `package.json` files carry metadata and workspace links such as `@my-app/ui`. Generators install only the dependencies the root does not already declare, so adding another app never bumps existing versions.
134
+
135
+ `rpr generate web <name>` adds another web app (for example a landing page) at `apps/<name>-web`. When a UI library exists it asks whether to add the library as a dependency (`--link-ui` / `--no-link-ui` skip the prompt); linked apps get a starter page in the library's `src/pages/`, and unlinked apps are fully standalone. Each web app gets its own dev-server port (3000, 3001, ...), so `npx nx serve <name>-web` runs alongside the others. The generated workspace requires Node 24 LTS or newer.
120
136
 
121
- Python dependency changes are made with `uv` so they are tracked in `pyproject.toml`. Node dependency changes are made through npm workspace package metadata and root `npm install`.
137
+ Python dependency changes are made with `uv` so they are tracked in `pyproject.toml`. Node dependency changes are made from the root with `npm install`.
122
138
 
123
139
  Update project-owned package manifests from upstream registries with `rpr update node` or `rpr update python`. Node updates target `package.json`; Python updates target `pyproject.toml` by default and fall back to `requirements.txt` when no `pyproject.toml` exists. Use `--file` to update a nested manifest, `--dry-run` to preview without changing files or running installers, and `--include-major` to opt into major-version updates.
124
140
 
@@ -271,9 +287,11 @@ No `PYPI_API_TOKEN` secret is required when publishing through GitHub Actions OI
271
287
  Release flow:
272
288
 
273
289
  ```bash
274
- # update src/rpr/__init__.py and rpr-parser/Cargo.toml to the same version
275
- git commit -am "Release 0.3.8"
276
- git tag v0.3.8
290
+ rpr version bump patch # updates Python, Cargo.toml, and Cargo.lock
291
+ # Or repair/set an explicit version: rpr version set 0.3.9
292
+ git add src/rpr/__init__.py rpr-parser/Cargo.toml rpr-parser/Cargo.lock
293
+ git commit -m "Release 0.3.9"
294
+ git tag v0.3.9
277
295
  git push origin main --tags
278
296
  ```
279
297
 
@@ -53,6 +53,7 @@ cd my-app
53
53
  rpr generate domain my-app # Add a Python domain package
54
54
  rpr generate api my-app # Add a FastAPI application
55
55
  rpr generate ui my-app # Add a React UI library and web app
56
+ rpr generate web landing # Add another web app; asks whether to depend on the UI library
56
57
  rpr generate engine my-app # Add a Rust/PyO3 computation engine
57
58
  rpr generate storybook # Add Storybook for UI component development
58
59
  rpr rename new-app # Rename the workspace identity and references
@@ -70,6 +71,19 @@ source and experiments to remain in rename scope. Binary contents, VCS data, and
70
71
  symlink targets are never changed. The outer workspace directory keeps its
71
72
  existing name.
72
73
 
74
+ After applying the rename, RPR synchronizes Node and Python dependencies and
75
+ runs uncached Nx build, Rust-engine build, and typecheck targets with the Nx
76
+ daemon disabled. This verifies the renamed project graph and commands without
77
+ deleting the existing Nx cache. Pass `--skip-verify` to omit these checks; a
78
+ dry run previews them without executing anything. If verification fails, the
79
+ rename remains applied and RPR prints the command to retry.
80
+
81
+ The success output includes a guarded command for optionally renaming the outer
82
+ workspace directory. Git remotes and hosted repository names are never changed.
83
+ When a hosted repository still matches the old project name, rename it through
84
+ the hosting provider if desired and then update its local URL with
85
+ `git remote set-url`. Local and remote branch names are unaffected.
86
+
73
87
  `rpr init <project-name>` creates the project directory when it does not exist. If the directory already exists and is not an NX workspace, RPR initializes Git and runs `nx init` inside that directory.
74
88
 
75
89
  By default, `rpr init` scaffolds the root workspace and AI instructions only. To create project components during initialization, repeat `--scaffold` or pass a comma-separated list:
@@ -94,14 +108,16 @@ Available scaffolds are:
94
108
 
95
109
  ### Dependency Placement
96
110
 
97
- RPR keeps dependencies close to the package that uses them.
111
+ RPR keeps Python dependencies close to the package that uses them and Node dependencies at the workspace root.
98
112
 
99
113
  - Root Python tooling such as Ruff, pytest, pyright, pre-commit, and maturin lives in the root `pyproject.toml`.
100
114
  - Domain runtime dependencies such as `sqlmodel`, `asyncpg`, `pydantic-settings`, and optional `alembic` are added to `packages/python/domain/pyproject.toml`.
101
115
  - API runtime dependencies such as `fastapi`, `uvicorn`, and the workspace domain package are added to `apps/<name>-api/pyproject.toml`.
102
- - Node dependencies are written to the relevant package-level `package.json` files, and `npm install` runs from the root workspace.
116
+ - Node dependencies are declared and installed only in the root `package.json`, so every app and package shares one version of each. Package-level `package.json` files carry metadata and workspace links such as `@my-app/ui`. Generators install only the dependencies the root does not already declare, so adding another app never bumps existing versions.
117
+
118
+ `rpr generate web <name>` adds another web app (for example a landing page) at `apps/<name>-web`. When a UI library exists it asks whether to add the library as a dependency (`--link-ui` / `--no-link-ui` skip the prompt); linked apps get a starter page in the library's `src/pages/`, and unlinked apps are fully standalone. Each web app gets its own dev-server port (3000, 3001, ...), so `npx nx serve <name>-web` runs alongside the others. The generated workspace requires Node 24 LTS or newer.
103
119
 
104
- Python dependency changes are made with `uv` so they are tracked in `pyproject.toml`. Node dependency changes are made through npm workspace package metadata and root `npm install`.
120
+ Python dependency changes are made with `uv` so they are tracked in `pyproject.toml`. Node dependency changes are made from the root with `npm install`.
105
121
 
106
122
  Update project-owned package manifests from upstream registries with `rpr update node` or `rpr update python`. Node updates target `package.json`; Python updates target `pyproject.toml` by default and fall back to `requirements.txt` when no `pyproject.toml` exists. Use `--file` to update a nested manifest, `--dry-run` to preview without changing files or running installers, and `--include-major` to opt into major-version updates.
107
123
 
@@ -254,9 +270,11 @@ No `PYPI_API_TOKEN` secret is required when publishing through GitHub Actions OI
254
270
  Release flow:
255
271
 
256
272
  ```bash
257
- # update src/rpr/__init__.py and rpr-parser/Cargo.toml to the same version
258
- git commit -am "Release 0.3.8"
259
- git tag v0.3.8
273
+ rpr version bump patch # updates Python, Cargo.toml, and Cargo.lock
274
+ # Or repair/set an explicit version: rpr version set 0.3.9
275
+ git add src/rpr/__init__.py rpr-parser/Cargo.toml rpr-parser/Cargo.lock
276
+ git commit -m "Release 0.3.9"
277
+ git tag v0.3.9
260
278
  git push origin main --tags
261
279
  ```
262
280
 
@@ -6,7 +6,7 @@ readme = "README.md"
6
6
  license = "MIT"
7
7
  requires-python = ">=3.14"
8
8
  dependencies = [
9
- "typer>=0.24.1",
9
+ "typer>=0.24.1,<0.26",
10
10
  "jinja2>=3.1.5",
11
11
  "pyyaml>=6.0.2",
12
12
  "rich>=14.3.3",
@@ -20,7 +20,7 @@ dependencies = [
20
20
  rpr = "rpr.cli:app"
21
21
 
22
22
  [dependency-groups]
23
- dev = ["pytest>=9.0.2", "maturin>=1.8.2"]
23
+ dev = ["pytest>=9.0.2", "maturin>=1.12.6,<2.0", "ruff>=0.15"]
24
24
 
25
25
  [build-system]
26
26
  requires = ["maturin>=1.12.6,<2.0"]
@@ -35,3 +35,10 @@ include = [{ path = "LICENSE", format = "sdist" }]
35
35
 
36
36
  [tool.pytest.ini_options]
37
37
  testpaths = ["tests"]
38
+
39
+ [tool.ruff]
40
+ # The scaffolds are templates (many are not valid Python/TS until rendered).
41
+ extend-exclude = ["src/rpr/scaffolds", "rpr-parser/target"]
42
+
43
+ [tool.ruff.lint]
44
+ select = ["E4", "E7", "E9", "F"]
@@ -1981,7 +1981,7 @@ dependencies = [
1981
1981
 
1982
1982
  [[package]]
1983
1983
  name = "rpr-parser"
1984
- version = "0.3.8"
1984
+ version = "0.3.11"
1985
1985
  dependencies = [
1986
1986
  "dirs",
1987
1987
  "dotenvy",
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "rpr-parser"
3
- version = "0.3.8"
3
+ version = "0.3.11"
4
4
  edition = "2021"
5
5
 
6
6
  [lib]
@@ -158,16 +158,27 @@ impl Replacer {
158
158
  let mut position = 0;
159
159
  let mut replacements = 0;
160
160
  while position < text.len() {
161
- if let Some(token) = self.match_new(text, position) {
162
- output.push_str(token);
163
- position += token.len();
164
- continue;
165
- }
166
- if let Some((old, new)) = self.match_old(text, position) {
167
- output.push_str(new);
168
- position += old.len();
169
- replacements += 1;
170
- continue;
161
+ let new_match = self.match_new(text, position);
162
+ let old_match = self.match_old(text, position);
163
+ match (old_match, new_match) {
164
+ (Some((old, new)), Some(existing)) if old.len() > existing.len() => {
165
+ output.push_str(new);
166
+ position += old.len();
167
+ replacements += 1;
168
+ continue;
169
+ }
170
+ (_, Some(existing)) => {
171
+ output.push_str(existing);
172
+ position += existing.len();
173
+ continue;
174
+ }
175
+ (Some((old, new)), None) => {
176
+ output.push_str(new);
177
+ position += old.len();
178
+ replacements += 1;
179
+ continue;
180
+ }
181
+ (None, None) => {}
171
182
  }
172
183
  let ch = text[position..]
173
184
  .chars()
@@ -182,12 +193,16 @@ impl Replacer {
182
193
  fn has_stale_reference(&self, text: &str) -> bool {
183
194
  let mut position = 0;
184
195
  while position < text.len() {
185
- if let Some(token) = self.match_new(text, position) {
186
- position += token.len();
187
- continue;
188
- }
189
- if self.match_old(text, position).is_some() {
190
- return true;
196
+ let new_match = self.match_new(text, position);
197
+ let old_match = self.match_old(text, position);
198
+ match (old_match, new_match) {
199
+ (Some((old, _)), Some(existing)) if old.len() > existing.len() => return true,
200
+ (_, Some(existing)) => {
201
+ position += existing.len();
202
+ continue;
203
+ }
204
+ (Some(_), None) => return true,
205
+ (None, None) => {}
191
206
  }
192
207
  position += text[position..].chars().next().unwrap().len_utf8();
193
208
  }
@@ -288,6 +303,7 @@ pub fn plan(root: PathBuf, new_name: String) -> Result<NativeRenamePlan, String>
288
303
  validate_new_name(&old_name, &new_name)?;
289
304
  let replacer = Replacer::new(build_replacements(&old_name, &new_name, variables));
290
305
  let (entries, mut skipped) = walk_workspace(&root)?;
306
+ validate_unicode_paths(&root, &entries, &skipped)?;
291
307
  let files: Vec<_> = entries
292
308
  .iter()
293
309
  .filter(|entry| entry.is_file())
@@ -653,11 +669,37 @@ fn build_path_moves(
653
669
  fn rename_relative_path(path: &Path, replacer: &Replacer) -> PathBuf {
654
670
  let mut output = PathBuf::new();
655
671
  for component in path.components() {
656
- output.push(replacer.replace(&component.as_os_str().to_string_lossy()).0);
672
+ let component = component
673
+ .as_os_str()
674
+ .to_str()
675
+ .expect("workspace paths were validated as Unicode");
676
+ output.push(replacer.replace(component).0);
657
677
  }
658
678
  output
659
679
  }
660
680
 
681
+ fn validate_unicode_paths(
682
+ root: &Path,
683
+ entries: &[PathBuf],
684
+ skipped: &[SkippedPath],
685
+ ) -> Result<(), String> {
686
+ for entry in entries.iter().chain(skipped.iter().map(|item| &item.path)) {
687
+ let relative = entry
688
+ .strip_prefix(root)
689
+ .expect("walked path is beneath root");
690
+ if relative
691
+ .components()
692
+ .any(|component| component.as_os_str().to_str().is_none())
693
+ {
694
+ return Err(format!(
695
+ "Workspace path is not valid Unicode and cannot be renamed safely: {:?}",
696
+ entry.as_os_str()
697
+ ));
698
+ }
699
+ }
700
+ Ok(())
701
+ }
702
+
661
703
  fn update_config(text: &str, new_name: &str) -> Result<(String, usize), String> {
662
704
  let mut config: Value = serde_yaml::from_str(text)
663
705
  .map_err(|error| format!("Renamed .rpr.yaml would be invalid: {error}"))?;
@@ -1005,6 +1047,28 @@ mod tests {
1005
1047
  assert_eq!(count, 1);
1006
1048
  }
1007
1049
 
1050
+ #[test]
1051
+ fn longest_token_wins_for_shortening_and_expansion() {
1052
+ let shortening = Replacer::new(HashMap::from([
1053
+ ("foo-bar".into(), "foo".into()),
1054
+ ("foo_bar".into(), "foo".into()),
1055
+ ("FooBar".into(), "Foo".into()),
1056
+ ]));
1057
+ let (output, count) =
1058
+ shortening.replace("nx serve foo-bar-web; from foo_bar_domain import FooBarService");
1059
+ assert_eq!(
1060
+ output,
1061
+ "nx serve foo-web; from foo_domain import FooService"
1062
+ );
1063
+ assert_eq!(count, 3);
1064
+ assert!(!shortening.has_stale_reference(&output));
1065
+
1066
+ let expansion = Replacer::new(HashMap::from([("foo".into(), "foo-bar".into())]));
1067
+ let (output, count) = expansion.replace("foo foo-bar");
1068
+ assert_eq!(output, "foo-bar foo-bar");
1069
+ assert_eq!(count, 1);
1070
+ }
1071
+
1008
1072
  #[test]
1009
1073
  fn bootstrap_defaults_cover_named_virtual_environments() {
1010
1074
  let temp = tempfile::tempdir().unwrap();
@@ -1046,6 +1110,40 @@ mod tests {
1046
1110
  .contains("new_app_domain"));
1047
1111
  }
1048
1112
 
1113
+ #[test]
1114
+ fn shortening_rename_updates_nested_paths_and_references() {
1115
+ let temp = workspace();
1116
+ let plan = plan(temp.path().to_path_buf(), "my".into()).unwrap();
1117
+
1118
+ apply(&plan).unwrap();
1119
+
1120
+ let renamed = temp
1121
+ .path()
1122
+ .join("packages/python/my_domain/src/my_domain/main.py");
1123
+ assert!(renamed.is_file());
1124
+ assert!(fs::read_to_string(renamed).unwrap().contains("my_domain"));
1125
+ }
1126
+
1127
+ #[cfg(unix)]
1128
+ #[test]
1129
+ fn non_unicode_workspace_path_aborts_planning() {
1130
+ use std::ffi::OsString;
1131
+ use std::os::unix::ffi::OsStringExt;
1132
+
1133
+ let temp = workspace();
1134
+ let invalid = temp
1135
+ .path()
1136
+ .join(OsString::from_vec(b"my-app-\xff.txt".to_vec()));
1137
+ write(&invalid, b"my-app\n");
1138
+
1139
+ let error = plan(temp.path().to_path_buf(), "new-app".into())
1140
+ .err()
1141
+ .expect("non-Unicode path should fail planning");
1142
+
1143
+ assert!(error.contains("not valid Unicode"));
1144
+ assert!(temp.path().join("packages/python/my_app_domain").is_dir());
1145
+ }
1146
+
1049
1147
  #[test]
1050
1148
  fn ignored_virtual_environment_is_never_planned_or_validated() {
1051
1149
  let temp = workspace();
@@ -0,0 +1 @@
1
+ __version__ = "0.3.11"
@@ -188,6 +188,23 @@ COMMAND_CATALOG: tuple[CommandDefinition, ...] = (
188
188
  ),
189
189
  ),
190
190
  ),
191
+ CommandDefinition(
192
+ name="web",
193
+ description="Generate a web app.",
194
+ action_id="generate.web",
195
+ arguments=(CommandArgument("name", "App name."),),
196
+ options=(
197
+ CommandOption(
198
+ "--ui-package",
199
+ "Target UI package.",
200
+ kind="value",
201
+ value_name="PACKAGE",
202
+ ),
203
+ CommandOption(
204
+ "--dry-run", "Preview changes without writing.", kind="flag"
205
+ ),
206
+ ),
207
+ ),
191
208
  CommandDefinition(
192
209
  name="engine",
193
210
  description="Generate a Rust engine package.",
@@ -462,7 +479,7 @@ def usage_string(cmd: CommandDefinition) -> str:
462
479
 
463
480
  Group commands show pipe-separated child names::
464
481
 
465
- /generate <domain|api|ui|engine|storybook>
482
+ /generate <domain|api|ui|web|engine|storybook>
466
483
 
467
484
  Leaf commands show arguments and options::
468
485
 
@@ -26,7 +26,7 @@ from rpr.application.completer import (
26
26
  from rpr.application.prompt_service import PromptService
27
27
  from rpr.application.shell import CommandShellService, ShellPresenter
28
28
  from rpr.context import RunContext
29
- from rpr.ui.console import console, refresh_console
29
+ from rpr.ui.console import refresh_console
30
30
  from rpr.ui.renderers import (
31
31
  render_approval_prompt,
32
32
  render_chat_hint,
@@ -3,16 +3,20 @@ from __future__ import annotations
3
3
  from dataclasses import dataclass
4
4
  from pathlib import Path
5
5
 
6
+ from rpr.checks.base import CheckResult
6
7
  from rpr.checks.instructions import check_instructions
7
8
  from rpr.checks.packages import check_packages
8
9
  from rpr.checks.workspace import check_workspace
9
10
  from rpr.generators.base import SyncConfig
10
11
 
11
12
 
13
+ CHECK_GROUPS = ("workspace", "instructions", "packages")
14
+
15
+
12
16
  @dataclass(slots=True, frozen=True)
13
17
  class CheckReport:
14
18
  project_name: str
15
- results: list[object]
19
+ results: list[CheckResult]
16
20
 
17
21
 
18
22
  def collect_check_report(project_dir: Path, group: str | None = None) -> CheckReport:
@@ -22,7 +26,7 @@ def collect_check_report(project_dir: Path, group: str | None = None) -> CheckRe
22
26
  variables = config.variables or {}
23
27
  project_name = variables.get("name", "(unknown)")
24
28
  requested_group = None if group is None else group.lower()
25
- results: list[object] = []
29
+ results: list[CheckResult] = []
26
30
 
27
31
  if requested_group in {None, "workspace"}:
28
32
  results.extend(check_workspace(project_dir))
@@ -36,7 +40,7 @@ def collect_check_report(project_dir: Path, group: str | None = None) -> CheckRe
36
40
  return CheckReport(project_name=project_name, results=results)
37
41
 
38
42
 
39
- def serialize_check_results(results: list[object]) -> list[dict[str, object]]:
43
+ def serialize_check_results(results: list[CheckResult]) -> list[dict[str, object]]:
40
44
  return [
41
45
  {
42
46
  "group": result.group,
@@ -12,7 +12,7 @@ from rpr.agent.session import ChatSession
12
12
  from rpr.application.conversation_service import (
13
13
  ConversationService,
14
14
  ConversationSummary,
15
- InMemoryConversationRepository,
15
+ InMemoryConversationRepository, # noqa: F401 (re-exported)
16
16
  )
17
17
  from rpr.ui.prompt_session import PromptSessionAdapter
18
18
 
@@ -6,6 +6,7 @@ from dataclasses import dataclass
6
6
  from pathlib import Path
7
7
  from typing import ClassVar, Protocol
8
8
 
9
+ import click
9
10
  import typer
10
11
 
11
12
  from rpr.agent.bootstrap import ChatRuntimeBundle
@@ -287,6 +288,7 @@ class CommandShellService:
287
288
  self._active_presenter.info(f"Running /init for {project_name}.")
288
289
  exit_code = self._invoke_command(
289
290
  init_command.init,
291
+ typer.Context(click.Command("init")),
290
292
  project_name=project_name,
291
293
  dry_run=namespace.dry_run,
292
294
  python_version=namespace.python,
@@ -301,6 +303,9 @@ class CommandShellService:
301
303
  from rpr.commands.generate import engine as generate_engine_command
302
304
  from rpr.commands.generate import storybook as generate_storybook_command
303
305
  from rpr.commands.generate import ui as generate_ui_command
306
+ from rpr.commands.generate import web as generate_web_command
307
+ from rpr.dependencies import node_project_name, ui_package_name
308
+ from rpr.workspace import find_ui_packages
304
309
 
305
310
  parser = ShellArgumentParser(prog="/generate", add_help=False)
306
311
  subparsers = parser.add_subparsers(dest="target")
@@ -314,11 +319,21 @@ class CommandShellService:
314
319
  storybook_parser.add_argument("--dry-run", action="store_true")
315
320
  storybook_parser.add_argument("--ui-package")
316
321
 
322
+ web_parser = subparsers.add_parser("web", add_help=False)
323
+ web_parser.add_argument("name", nargs="?")
324
+ web_parser.add_argument("--dry-run", action="store_true")
325
+ web_parser.add_argument("--ui-package")
326
+ web_parser.add_argument(
327
+ "--link-ui", action=argparse.BooleanOptionalAction, default=None
328
+ )
329
+
317
330
  namespace = self._parse_args(parser, parsed.args)
318
331
  if namespace is None:
319
332
  return True
320
333
  if namespace.target is None:
321
- raise ValueError("Usage: /generate <domain|api|ui|engine|storybook> ...")
334
+ raise ValueError(
335
+ "Usage: /generate <domain|api|ui|web|engine|storybook> ..."
336
+ )
322
337
 
323
338
  if namespace.target == "domain":
324
339
  name = namespace.name or await self._active_prompts.prompt("App name")
@@ -345,11 +360,43 @@ class CommandShellService:
345
360
  )
346
361
  return True
347
362
 
363
+ if namespace.target == "web":
364
+ name = namespace.name or await self._active_prompts.prompt("App name")
365
+ ui_package = namespace.ui_package
366
+ link_ui = namespace.link_ui
367
+ ui_packages = find_ui_packages(self._bundle.ctx.project_dir)
368
+ if ui_packages and not ui_package:
369
+ ui_package = ui_packages[0]
370
+ if len(ui_packages) > 1:
371
+ self._active_presenter.table(
372
+ "Web App Targets",
373
+ ["Package"],
374
+ [[package] for package in ui_packages],
375
+ )
376
+ ui_package = (
377
+ await self._active_prompts.prompt(
378
+ "UI package", default=ui_packages[0]
379
+ )
380
+ ).strip() or ui_packages[0]
381
+ if ui_package and link_ui is None:
382
+ answer = await self._active_prompts.prompt(
383
+ f"Add {ui_package_name(ui_package)} as a dependency of "
384
+ f"{node_project_name(name, 'web')}? (Y/n)",
385
+ default="y",
386
+ )
387
+ link_ui = answer.strip().lower() not in {"n", "no"}
388
+ self._invoke_command(
389
+ generate_web_command.web,
390
+ name=name,
391
+ ui_package=ui_package,
392
+ link_ui=link_ui,
393
+ dry_run=namespace.dry_run,
394
+ )
395
+ return True
396
+
348
397
  ui_package = namespace.ui_package
349
398
  if not ui_package:
350
- ui_packages = generate_storybook_command._find_ui_packages(
351
- self._bundle.ctx.project_dir
352
- )
399
+ ui_packages = find_ui_packages(self._bundle.ctx.project_dir)
353
400
  if len(ui_packages) == 1:
354
401
  ui_package = ui_packages[0]
355
402
  elif len(ui_packages) > 1:
@@ -376,7 +423,7 @@ class CommandShellService:
376
423
 
377
424
  parser = ShellArgumentParser(prog="/sync", add_help=False)
378
425
  parser.add_argument("action", nargs="?", default="rules")
379
- parser.add_argument("--target")
426
+ parser.add_argument("--target", action="append")
380
427
  parser.add_argument("--dry-run", action="store_true")
381
428
  namespace = self._parse_args(parser, parsed.args)
382
429
  if namespace is None:
@@ -385,7 +432,9 @@ class CommandShellService:
385
432
  raise ValueError("Usage: /sync [rules] [--target cursor] [--dry-run]")
386
433
 
387
434
  self._invoke_command(
388
- sync_command.rules, target=namespace.target, dry_run=namespace.dry_run
435
+ sync_command.rules,
436
+ target=namespace.target or [],
437
+ dry_run=namespace.dry_run,
389
438
  )
390
439
  return True
391
440
 
@@ -395,6 +444,7 @@ class CommandShellService:
395
444
  parser = ShellArgumentParser(prog="/add", add_help=False)
396
445
  parser.add_argument("action", nargs="?", default="template")
397
446
  parser.add_argument("template_id", nargs="?")
447
+ parser.add_argument("template_name", nargs="?")
398
448
  parser.add_argument("--list", action="store_true")
399
449
  parser.add_argument("--force", action="store_true")
400
450
  parser.add_argument("--name")
@@ -405,16 +455,20 @@ class CommandShellService:
405
455
  return True
406
456
  if namespace.action != "template":
407
457
  raise ValueError(
408
- "Usage: /add template <template-id> [--name app] [--force] [--dry-run]"
458
+ "Usage: /add template [node|python] <template-id> "
459
+ "[--name app] [--force] [--dry-run]"
409
460
  )
410
461
 
411
- template_id = namespace.template_id
412
- if template_id is None and not namespace.list:
413
- template_id = await self._active_prompts.prompt("Template ID")
462
+ # `/add template [lang] <id>`: the command takes the language (or the id,
463
+ # when no language is given) as its first positional argument.
464
+ lang_or_template_id = namespace.template_id
465
+ if lang_or_template_id is None and not namespace.list:
466
+ lang_or_template_id = await self._active_prompts.prompt("Template ID")
414
467
 
415
468
  self._invoke_command(
416
469
  add_command.template,
417
- template_id=template_id,
470
+ lang_or_template_id=lang_or_template_id,
471
+ template_id=namespace.template_name,
418
472
  list_templates=namespace.list,
419
473
  force=namespace.force,
420
474
  name=namespace.name,
@@ -1,7 +1,6 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  from dataclasses import dataclass
4
- from pathlib import Path
5
4
 
6
5
 
7
6
  @dataclass