rpr-cli 0.1.6__tar.gz → 0.1.9__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.
- {rpr_cli-0.1.6 → rpr_cli-0.1.9}/PKG-INFO +1 -1
- {rpr_cli-0.1.6 → rpr_cli-0.1.9}/Sample-dot-github/instructions/frontend.instructions.md +2 -1
- {rpr_cli-0.1.6 → rpr_cli-0.1.9}/Sample-dot-github/instructions/setup-guide.instructions.md +4 -0
- {rpr_cli-0.1.6 → rpr_cli-0.1.9}/nx_uv_setup.md +36 -41
- rpr_cli-0.1.9/src/rpr/__init__.py +1 -0
- {rpr_cli-0.1.6 → rpr_cli-0.1.9}/src/rpr/checks/workspace.py +1 -1
- {rpr_cli-0.1.6 → rpr_cli-0.1.9}/src/rpr/commands/generate/api.py +9 -0
- {rpr_cli-0.1.6 → rpr_cli-0.1.9}/src/rpr/commands/generate/domain.py +9 -0
- {rpr_cli-0.1.6 → rpr_cli-0.1.9}/src/rpr/commands/generate/storybook.py +8 -4
- {rpr_cli-0.1.6 → rpr_cli-0.1.9}/src/rpr/commands/generate/ui.py +6 -3
- {rpr_cli-0.1.6 → rpr_cli-0.1.9}/src/rpr/commands/init.py +16 -3
- {rpr_cli-0.1.6 → rpr_cli-0.1.9}/src/rpr/dependencies.py +28 -2
- {rpr_cli-0.1.6 → rpr_cli-0.1.9}/src/rpr/scaffolds/instructions/frontend.instructions.md +2 -1
- {rpr_cli-0.1.6 → rpr_cli-0.1.9}/src/rpr/scaffolds/instructions/setup-guide.instructions.md +4 -0
- {rpr_cli-0.1.6 → rpr_cli-0.1.9}/src/rpr/workspace.py +28 -3
- {rpr_cli-0.1.6 → rpr_cli-0.1.9}/tests/test_check.py +2 -2
- {rpr_cli-0.1.6 → rpr_cli-0.1.9}/tests/test_generate_api.py +2 -0
- {rpr_cli-0.1.6 → rpr_cli-0.1.9}/tests/test_generate_domain.py +2 -0
- {rpr_cli-0.1.6 → rpr_cli-0.1.9}/tests/test_generate_storybook.py +4 -3
- {rpr_cli-0.1.6 → rpr_cli-0.1.9}/tests/test_generate_ui.py +100 -8
- {rpr_cli-0.1.6 → rpr_cli-0.1.9}/tests/test_init.py +4 -2
- rpr_cli-0.1.6/src/rpr/__init__.py +0 -1
- {rpr_cli-0.1.6 → rpr_cli-0.1.9}/.github/agents/story.agent.md +0 -0
- {rpr_cli-0.1.6 → rpr_cli-0.1.9}/.github/workflows/publish-pypi.yml +0 -0
- {rpr_cli-0.1.6 → rpr_cli-0.1.9}/.gitignore +0 -0
- {rpr_cli-0.1.6 → rpr_cli-0.1.9}/CLAUDE.md +0 -0
- {rpr_cli-0.1.6 → rpr_cli-0.1.9}/GEMINI.md +0 -0
- {rpr_cli-0.1.6 → rpr_cli-0.1.9}/LICENSE +0 -0
- {rpr_cli-0.1.6 → rpr_cli-0.1.9}/README.md +0 -0
- {rpr_cli-0.1.6 → rpr_cli-0.1.9}/Sample-dot-github/instructions/all.instructions.md +0 -0
- {rpr_cli-0.1.6 → rpr_cli-0.1.9}/Sample-dot-github/instructions/api.instructions.md +0 -0
- {rpr_cli-0.1.6 → rpr_cli-0.1.9}/Sample-dot-github/instructions/domain.instructions.md +0 -0
- {rpr_cli-0.1.6 → rpr_cli-0.1.9}/Sample-dot-github/instructions/rust-engine.instructions.md +0 -0
- {rpr_cli-0.1.6 → rpr_cli-0.1.9}/Sample-dot-github/instructions/tooling-setup.instructions.md +0 -0
- {rpr_cli-0.1.6 → rpr_cli-0.1.9}/Sample-dot-github/instructions/tooling.instructions.md +0 -0
- {rpr_cli-0.1.6 → rpr_cli-0.1.9}/Sample-dot-github/scaffolds/README.md +0 -0
- {rpr_cli-0.1.6 → rpr_cli-0.1.9}/Sample-dot-github/scaffolds/api/main.py +0 -0
- {rpr_cli-0.1.6 → rpr_cli-0.1.9}/Sample-dot-github/scaffolds/domain/base-repo.md +0 -0
- {rpr_cli-0.1.6 → rpr_cli-0.1.9}/Sample-dot-github/scaffolds/domain/base_entity.md +0 -0
- {rpr_cli-0.1.6 → rpr_cli-0.1.9}/Sample-dot-github/scaffolds/frontend/api.service.ts +0 -0
- {rpr_cli-0.1.6 → rpr_cli-0.1.9}/Sample-dot-github/scaffolds/frontend/useStateNavigation.ts +0 -0
- {rpr_cli-0.1.6 → rpr_cli-0.1.9}/changelogs/mui_v9.txt +0 -0
- {rpr_cli-0.1.6 → rpr_cli-0.1.9}/code-map.md +0 -0
- {rpr_cli-0.1.6 → rpr_cli-0.1.9}/patch_output.py +0 -0
- {rpr_cli-0.1.6 → rpr_cli-0.1.9}/pyproject.toml +0 -0
- {rpr_cli-0.1.6 → rpr_cli-0.1.9}/rpr-parser/Cargo.lock +0 -0
- {rpr_cli-0.1.6 → rpr_cli-0.1.9}/rpr-parser/Cargo.toml +0 -0
- {rpr_cli-0.1.6 → rpr_cli-0.1.9}/rpr-parser/pyproject.toml +0 -0
- {rpr_cli-0.1.6 → rpr_cli-0.1.9}/rpr-parser/rpr_parser.pyi +0 -0
- {rpr_cli-0.1.6 → rpr_cli-0.1.9}/rpr-parser/src/config/container.rs +0 -0
- {rpr_cli-0.1.6 → rpr_cli-0.1.9}/rpr-parser/src/config/mod.rs +0 -0
- {rpr_cli-0.1.6 → rpr_cli-0.1.9}/rpr-parser/src/config/settings.rs +0 -0
- {rpr_cli-0.1.6 → rpr_cli-0.1.9}/rpr-parser/src/graph.rs +0 -0
- {rpr_cli-0.1.6 → rpr_cli-0.1.9}/rpr-parser/src/lib.rs +0 -0
- {rpr_cli-0.1.6 → rpr_cli-0.1.9}/rpr-parser/src/models/dtos/mod.rs +0 -0
- {rpr_cli-0.1.6 → rpr_cli-0.1.9}/rpr-parser/src/models/entities/mod.rs +0 -0
- {rpr_cli-0.1.6 → rpr_cli-0.1.9}/rpr-parser/src/models/entities/users.rs +0 -0
- {rpr_cli-0.1.6 → rpr_cli-0.1.9}/rpr-parser/src/models/mod.rs +0 -0
- {rpr_cli-0.1.6 → rpr_cli-0.1.9}/rpr-parser/src/parse.rs +0 -0
- {rpr_cli-0.1.6 → rpr_cli-0.1.9}/rpr-parser/src/repos/base.rs +0 -0
- {rpr_cli-0.1.6 → rpr_cli-0.1.9}/rpr-parser/src/repos/mod.rs +0 -0
- {rpr_cli-0.1.6 → rpr_cli-0.1.9}/rpr-parser/src/repos/users.rs +0 -0
- {rpr_cli-0.1.6 → rpr_cli-0.1.9}/rpr-parser/src/resolve.rs +0 -0
- {rpr_cli-0.1.6 → rpr_cli-0.1.9}/rpr-parser/src/walker.rs +0 -0
- {rpr_cli-0.1.6 → rpr_cli-0.1.9}/rpr-parser/uv.lock +0 -0
- {rpr_cli-0.1.6 → rpr_cli-0.1.9}/special_files.md +0 -0
- {rpr_cli-0.1.6 → rpr_cli-0.1.9}/src/rpr/agent/__init__.py +0 -0
- {rpr_cli-0.1.6 → rpr_cli-0.1.9}/src/rpr/agent/approval.py +0 -0
- {rpr_cli-0.1.6 → rpr_cli-0.1.9}/src/rpr/agent/bootstrap.py +0 -0
- {rpr_cli-0.1.6 → rpr_cli-0.1.9}/src/rpr/agent/client.py +0 -0
- {rpr_cli-0.1.6 → rpr_cli-0.1.9}/src/rpr/agent/control.py +0 -0
- {rpr_cli-0.1.6 → rpr_cli-0.1.9}/src/rpr/agent/mock.py +0 -0
- {rpr_cli-0.1.6 → rpr_cli-0.1.9}/src/rpr/agent/runtime.py +0 -0
- {rpr_cli-0.1.6 → rpr_cli-0.1.9}/src/rpr/agent/session.py +0 -0
- {rpr_cli-0.1.6 → rpr_cli-0.1.9}/src/rpr/agent/tools/__init__.py +0 -0
- {rpr_cli-0.1.6 → rpr_cli-0.1.9}/src/rpr/agent/tools/base.py +0 -0
- {rpr_cli-0.1.6 → rpr_cli-0.1.9}/src/rpr/agent/tools/mutating.py +0 -0
- {rpr_cli-0.1.6 → rpr_cli-0.1.9}/src/rpr/agent/tools/readonly.py +0 -0
- {rpr_cli-0.1.6 → rpr_cli-0.1.9}/src/rpr/agent/tools/registry.py +0 -0
- {rpr_cli-0.1.6 → rpr_cli-0.1.9}/src/rpr/application/__init__.py +0 -0
- {rpr_cli-0.1.6 → rpr_cli-0.1.9}/src/rpr/application/catalog.py +0 -0
- {rpr_cli-0.1.6 → rpr_cli-0.1.9}/src/rpr/application/chat_service.py +0 -0
- {rpr_cli-0.1.6 → rpr_cli-0.1.9}/src/rpr/application/checks.py +0 -0
- {rpr_cli-0.1.6 → rpr_cli-0.1.9}/src/rpr/application/cli_adapter.py +0 -0
- {rpr_cli-0.1.6 → rpr_cli-0.1.9}/src/rpr/application/completer.py +0 -0
- {rpr_cli-0.1.6 → rpr_cli-0.1.9}/src/rpr/application/conversation_service.py +0 -0
- {rpr_cli-0.1.6 → rpr_cli-0.1.9}/src/rpr/application/prompt_service.py +0 -0
- {rpr_cli-0.1.6 → rpr_cli-0.1.9}/src/rpr/application/selector.py +0 -0
- {rpr_cli-0.1.6 → rpr_cli-0.1.9}/src/rpr/application/shell.py +0 -0
- {rpr_cli-0.1.6 → rpr_cli-0.1.9}/src/rpr/checks/__init__.py +0 -0
- {rpr_cli-0.1.6 → rpr_cli-0.1.9}/src/rpr/checks/base.py +0 -0
- {rpr_cli-0.1.6 → rpr_cli-0.1.9}/src/rpr/checks/instructions.py +0 -0
- {rpr_cli-0.1.6 → rpr_cli-0.1.9}/src/rpr/checks/packages.py +0 -0
- {rpr_cli-0.1.6 → rpr_cli-0.1.9}/src/rpr/cli.py +0 -0
- {rpr_cli-0.1.6 → rpr_cli-0.1.9}/src/rpr/commands/__init__.py +0 -0
- {rpr_cli-0.1.6 → rpr_cli-0.1.9}/src/rpr/commands/add.py +0 -0
- {rpr_cli-0.1.6 → rpr_cli-0.1.9}/src/rpr/commands/chat.py +0 -0
- {rpr_cli-0.1.6 → rpr_cli-0.1.9}/src/rpr/commands/check.py +0 -0
- {rpr_cli-0.1.6 → rpr_cli-0.1.9}/src/rpr/commands/generate/__init__.py +0 -0
- {rpr_cli-0.1.6 → rpr_cli-0.1.9}/src/rpr/commands/generate/engine.py +0 -0
- {rpr_cli-0.1.6 → rpr_cli-0.1.9}/src/rpr/commands/map.py +0 -0
- {rpr_cli-0.1.6 → rpr_cli-0.1.9}/src/rpr/commands/refresh.py +0 -0
- {rpr_cli-0.1.6 → rpr_cli-0.1.9}/src/rpr/commands/settings.py +0 -0
- {rpr_cli-0.1.6 → rpr_cli-0.1.9}/src/rpr/commands/sync.py +0 -0
- {rpr_cli-0.1.6 → rpr_cli-0.1.9}/src/rpr/context.py +0 -0
- {rpr_cli-0.1.6 → rpr_cli-0.1.9}/src/rpr/dependency_manifest_data.py +0 -0
- {rpr_cli-0.1.6 → rpr_cli-0.1.9}/src/rpr/generators/__init__.py +0 -0
- {rpr_cli-0.1.6 → rpr_cli-0.1.9}/src/rpr/generators/base.py +0 -0
- {rpr_cli-0.1.6 → rpr_cli-0.1.9}/src/rpr/generators/claude.py +0 -0
- {rpr_cli-0.1.6 → rpr_cli-0.1.9}/src/rpr/generators/copilot.py +0 -0
- {rpr_cli-0.1.6 → rpr_cli-0.1.9}/src/rpr/generators/cursor.py +0 -0
- {rpr_cli-0.1.6 → rpr_cli-0.1.9}/src/rpr/generators/gemini.py +0 -0
- {rpr_cli-0.1.6 → rpr_cli-0.1.9}/src/rpr/manifest_refresh.py +0 -0
- {rpr_cli-0.1.6 → rpr_cli-0.1.9}/src/rpr/map/__init__.py +0 -0
- {rpr_cli-0.1.6 → rpr_cli-0.1.9}/src/rpr/map/architecture.py +0 -0
- {rpr_cli-0.1.6 → rpr_cli-0.1.9}/src/rpr/map/chains.py +0 -0
- {rpr_cli-0.1.6 → rpr_cli-0.1.9}/src/rpr/map/classifier.py +0 -0
- {rpr_cli-0.1.6 → rpr_cli-0.1.9}/src/rpr/map/coverage.py +0 -0
- {rpr_cli-0.1.6 → rpr_cli-0.1.9}/src/rpr/map/dependencies.py +0 -0
- {rpr_cli-0.1.6 → rpr_cli-0.1.9}/src/rpr/map/extractor.py +0 -0
- {rpr_cli-0.1.6 → rpr_cli-0.1.9}/src/rpr/map/graph.py +0 -0
- {rpr_cli-0.1.6 → rpr_cli-0.1.9}/src/rpr/map/output.py +0 -0
- {rpr_cli-0.1.6 → rpr_cli-0.1.9}/src/rpr/map/responsibility.py +0 -0
- {rpr_cli-0.1.6 → rpr_cli-0.1.9}/src/rpr/map/topology.py +0 -0
- {rpr_cli-0.1.6 → rpr_cli-0.1.9}/src/rpr/map/walker.py +0 -0
- {rpr_cli-0.1.6 → rpr_cli-0.1.9}/src/rpr/scaffolds/domain/base_entity.md +0 -0
- {rpr_cli-0.1.6 → rpr_cli-0.1.9}/src/rpr/scaffolds/domain/base_repo.md +0 -0
- {rpr_cli-0.1.6 → rpr_cli-0.1.9}/src/rpr/scaffolds/domain/container.md +0 -0
- {rpr_cli-0.1.6 → rpr_cli-0.1.9}/src/rpr/scaffolds/domain/settings.md +0 -0
- {rpr_cli-0.1.6 → rpr_cli-0.1.9}/src/rpr/scaffolds/instructions/all.instructions.md +0 -0
- {rpr_cli-0.1.6 → rpr_cli-0.1.9}/src/rpr/scaffolds/instructions/api.instructions.md +0 -0
- {rpr_cli-0.1.6 → rpr_cli-0.1.9}/src/rpr/scaffolds/instructions/domain.instructions.md +0 -0
- {rpr_cli-0.1.6 → rpr_cli-0.1.9}/src/rpr/scaffolds/instructions/rust-engine.instructions.md +0 -0
- {rpr_cli-0.1.6 → rpr_cli-0.1.9}/src/rpr/scaffolds/instructions/tooling-setup.instructions.md +0 -0
- {rpr_cli-0.1.6 → rpr_cli-0.1.9}/src/rpr/scaffolds/instructions/tooling.instructions.md +0 -0
- {rpr_cli-0.1.6 → rpr_cli-0.1.9}/src/rpr/scaffolds/js_special_files/fetch.service.md +0 -0
- {rpr_cli-0.1.6 → rpr_cli-0.1.9}/src/rpr/scaffolds/js_special_files/sticky-navigation.md +0 -0
- {rpr_cli-0.1.6 → rpr_cli-0.1.9}/src/rpr/scaffolds/special_files/domain_container.md +0 -0
- {rpr_cli-0.1.6 → rpr_cli-0.1.9}/src/rpr/scaffolds/special_files/domain_settings.md +0 -0
- {rpr_cli-0.1.6 → rpr_cli-0.1.9}/src/rpr/scaffolds/special_files/dto_util.md +0 -0
- {rpr_cli-0.1.6 → rpr_cli-0.1.9}/src/rpr/scaffolds/special_files/encrypted_column.md +0 -0
- {rpr_cli-0.1.6 → rpr_cli-0.1.9}/src/rpr/scaffolds/special_files/mapper_util.md +0 -0
- {rpr_cli-0.1.6 → rpr_cli-0.1.9}/src/rpr/scaffolds/special_files/partial_update.md +0 -0
- {rpr_cli-0.1.6 → rpr_cli-0.1.9}/src/rpr/templates/__init__.py +0 -0
- {rpr_cli-0.1.6 → rpr_cli-0.1.9}/src/rpr/templates/registry.py +0 -0
- {rpr_cli-0.1.6 → rpr_cli-0.1.9}/src/rpr/ui/__init__.py +0 -0
- {rpr_cli-0.1.6 → rpr_cli-0.1.9}/src/rpr/ui/console.py +0 -0
- {rpr_cli-0.1.6 → rpr_cli-0.1.9}/src/rpr/ui/markdown.py +0 -0
- {rpr_cli-0.1.6 → rpr_cli-0.1.9}/src/rpr/ui/prompt_session.py +0 -0
- {rpr_cli-0.1.6 → rpr_cli-0.1.9}/src/rpr/ui/renderers.py +0 -0
- {rpr_cli-0.1.6 → rpr_cli-0.1.9}/src/rpr/ui/theme.py +0 -0
- {rpr_cli-0.1.6 → rpr_cli-0.1.9}/stories/README.md +0 -0
- {rpr_cli-0.1.6 → rpr_cli-0.1.9}/stories/completed/00-bootstrap-cli.md +0 -0
- {rpr_cli-0.1.6 → rpr_cli-0.1.9}/stories/completed/01-rpr-init.md +0 -0
- {rpr_cli-0.1.6 → rpr_cli-0.1.9}/stories/completed/02-rpr-generate-domain.md +0 -0
- {rpr_cli-0.1.6 → rpr_cli-0.1.9}/stories/completed/03-rpr-generate-api.md +0 -0
- {rpr_cli-0.1.6 → rpr_cli-0.1.9}/stories/completed/04-rpr-generate-ui.md +0 -0
- {rpr_cli-0.1.6 → rpr_cli-0.1.9}/stories/completed/05-rpr-generate-engine.md +0 -0
- {rpr_cli-0.1.6 → rpr_cli-0.1.9}/stories/completed/06-rpr-generate-storybook.md +0 -0
- {rpr_cli-0.1.6 → rpr_cli-0.1.9}/stories/completed/07-rpr-sync-rules.md +0 -0
- {rpr_cli-0.1.6 → rpr_cli-0.1.9}/stories/completed/08-rpr-add-template.md +0 -0
- {rpr_cli-0.1.6 → rpr_cli-0.1.9}/stories/completed/09-rpr-check.md +0 -0
- {rpr_cli-0.1.6 → rpr_cli-0.1.9}/stories/completed/10-rpr-domain-config.md +0 -0
- {rpr_cli-0.1.6 → rpr_cli-0.1.9}/stories/completed/chat/18-rpr-terminal-ui-foundation.md +0 -0
- {rpr_cli-0.1.6 → rpr_cli-0.1.9}/stories/completed/chat/19-rpr-markdown-rendering.md +0 -0
- {rpr_cli-0.1.6 → rpr_cli-0.1.9}/stories/completed/chat/20-rpr-chat-repl.md +0 -0
- {rpr_cli-0.1.6 → rpr_cli-0.1.9}/stories/completed/chat/21-rpr-agent-tool-loop.md +0 -0
- {rpr_cli-0.1.6 → rpr_cli-0.1.9}/stories/completed/chat/22-rpr-chat-tui.md +0 -0
- {rpr_cli-0.1.6 → rpr_cli-0.1.9}/stories/completed/chat/23-rpr-chat-unified-command-shell.md +0 -0
- {rpr_cli-0.1.6 → rpr_cli-0.1.9}/stories/completed/chat/24-rpr-async-chat-runtime-and-services.md +0 -0
- {rpr_cli-0.1.6 → rpr_cli-0.1.9}/stories/completed/chat/25-rpr-prompt-service-and-conversation-lifecycle.md +0 -0
- {rpr_cli-0.1.6 → rpr_cli-0.1.9}/stories/completed/chat/26-rpr-shared-command-catalog.md +0 -0
- {rpr_cli-0.1.6 → rpr_cli-0.1.9}/stories/completed/chat/27-rpr-slash-command-selection-panel.md +0 -0
- {rpr_cli-0.1.6 → rpr_cli-0.1.9}/stories/completed/chat/28-rpr-chat-cancellable-and-multiline-input.md +0 -0
- {rpr_cli-0.1.6 → rpr_cli-0.1.9}/stories/completed/chat/29-rpr-file-context-selection.md +0 -0
- {rpr_cli-0.1.6 → rpr_cli-0.1.9}/stories/completed/map/11-rpr-map-command.md +0 -0
- {rpr_cli-0.1.6 → rpr_cli-0.1.9}/stories/completed/map/12-rpr-map-file-walker.md +0 -0
- {rpr_cli-0.1.6 → rpr_cli-0.1.9}/stories/completed/map/13-rpr-map-rust-engine.md +0 -0
- {rpr_cli-0.1.6 → rpr_cli-0.1.9}/stories/completed/map/13a-rpr-map-gitignore.md +0 -0
- {rpr_cli-0.1.6 → rpr_cli-0.1.9}/stories/completed/map/14-rpr-map-graph-builder.md +0 -0
- {rpr_cli-0.1.6 → rpr_cli-0.1.9}/stories/completed/map/15-rpr-map-analysis-bridge.md +0 -0
- {rpr_cli-0.1.6 → rpr_cli-0.1.9}/stories/completed/map/16-rpr-map-layer-classifier.md +0 -0
- {rpr_cli-0.1.6 → rpr_cli-0.1.9}/stories/completed/map/17-rpr-map-output.md +0 -0
- {rpr_cli-0.1.6 → rpr_cli-0.1.9}/stories/completed/map/17a-rpr-map-directory-topology.md +0 -0
- {rpr_cli-0.1.6 → rpr_cli-0.1.9}/stories/completed/map/17b-rpr-map-architecture-assessment.md +0 -0
- {rpr_cli-0.1.6 → rpr_cli-0.1.9}/stories/completed/map/17c-rpr-map-representative-chains.md +0 -0
- {rpr_cli-0.1.6 → rpr_cli-0.1.9}/stories/completed/map/17d-rpr-map-multi-resolution-output.md +0 -0
- {rpr_cli-0.1.6 → rpr_cli-0.1.9}/stories/map/30-rpr-map-directory-view-and-cycle-detection.md +0 -0
- {rpr_cli-0.1.6 → rpr_cli-0.1.9}/stories/map/31-rpr-map-coupling-hotspots-and-verdict-reasoning.md +0 -0
- {rpr_cli-0.1.6 → rpr_cli-0.1.9}/stories/map/32-rpr-map-dependencies-responsibilities-and-test-coverage.md +0 -0
- {rpr_cli-0.1.6 → rpr_cli-0.1.9}/tests/__init__.py +0 -0
- {rpr_cli-0.1.6 → rpr_cli-0.1.9}/tests/test_add.py +0 -0
- {rpr_cli-0.1.6 → rpr_cli-0.1.9}/tests/test_architecture.py +0 -0
- {rpr_cli-0.1.6 → rpr_cli-0.1.9}/tests/test_architecture_unwrapped.py +0 -0
- {rpr_cli-0.1.6 → rpr_cli-0.1.9}/tests/test_catalog.py +0 -0
- {rpr_cli-0.1.6 → rpr_cli-0.1.9}/tests/test_chat.py +0 -0
- {rpr_cli-0.1.6 → rpr_cli-0.1.9}/tests/test_chat_service.py +0 -0
- {rpr_cli-0.1.6 → rpr_cli-0.1.9}/tests/test_completer.py +0 -0
- {rpr_cli-0.1.6 → rpr_cli-0.1.9}/tests/test_context.py +0 -0
- {rpr_cli-0.1.6 → rpr_cli-0.1.9}/tests/test_generate_engine.py +0 -0
- {rpr_cli-0.1.6 → rpr_cli-0.1.9}/tests/test_map.py +0 -0
- {rpr_cli-0.1.6 → rpr_cli-0.1.9}/tests/test_map_chains.py +0 -0
- {rpr_cli-0.1.6 → rpr_cli-0.1.9}/tests/test_map_classifier.py +0 -0
- {rpr_cli-0.1.6 → rpr_cli-0.1.9}/tests/test_map_coverage.py +0 -0
- {rpr_cli-0.1.6 → rpr_cli-0.1.9}/tests/test_map_dependencies.py +0 -0
- {rpr_cli-0.1.6 → rpr_cli-0.1.9}/tests/test_map_extractor.py +0 -0
- {rpr_cli-0.1.6 → rpr_cli-0.1.9}/tests/test_map_graph.py +0 -0
- {rpr_cli-0.1.6 → rpr_cli-0.1.9}/tests/test_map_output.py +0 -0
- {rpr_cli-0.1.6 → rpr_cli-0.1.9}/tests/test_map_responsibility.py +0 -0
- {rpr_cli-0.1.6 → rpr_cli-0.1.9}/tests/test_map_topology.py +0 -0
- {rpr_cli-0.1.6 → rpr_cli-0.1.9}/tests/test_map_walker.py +0 -0
- {rpr_cli-0.1.6 → rpr_cli-0.1.9}/tests/test_packaging.py +0 -0
- {rpr_cli-0.1.6 → rpr_cli-0.1.9}/tests/test_prompt_session.py +0 -0
- {rpr_cli-0.1.6 → rpr_cli-0.1.9}/tests/test_refresh.py +0 -0
- {rpr_cli-0.1.6 → rpr_cli-0.1.9}/tests/test_rpr_parser_stub.py +0 -0
- {rpr_cli-0.1.6 → rpr_cli-0.1.9}/tests/test_selector.py +0 -0
- {rpr_cli-0.1.6 → rpr_cli-0.1.9}/tests/test_settings.py +0 -0
- {rpr_cli-0.1.6 → rpr_cli-0.1.9}/tests/test_sync.py +0 -0
- {rpr_cli-0.1.6 → rpr_cli-0.1.9}/tests/test_ui.py +0 -0
- {rpr_cli-0.1.6 → rpr_cli-0.1.9}/update_output.py +0 -0
- {rpr_cli-0.1.6 → rpr_cli-0.1.9}/uv.lock +0 -0
|
@@ -11,6 +11,7 @@ Use React for UI composition, TanStack Query for server-state workflows, TanStac
|
|
|
11
11
|
|
|
12
12
|
- Use functional components.
|
|
13
13
|
- Extract reusable behavior into hooks instead of duplicating component logic.
|
|
14
|
+
- Import the generated shared UI package through the app-scoped package name `@{name}/ui`, such as `@testapp/ui`, not `@repo/{name}-ui`.
|
|
14
15
|
- Prefer a shared base API service at `src/services/base/api.service.ts` when the UI package needs centralized fetch behavior.
|
|
15
16
|
- Prefer a shared `useStateNavigation` hook at `src/hooks/useStateNavigation.ts` when UI state should persist in the URL.
|
|
16
17
|
- Reuse existing components, hooks, and service patterns before introducing a new abstraction.
|
|
@@ -84,7 +85,7 @@ export const SomeContainer = styled(Box)`
|
|
|
84
85
|
|
|
85
86
|
- Type every API payload and mutation input.
|
|
86
87
|
- Prefer `type` aliases for DTO-shaped data, `interface` for extendable service contracts, and classes only when the UI genuinely needs behavior.
|
|
87
|
-
- For components with children, prefer `React.PropsWithChildren<T>` over manually
|
|
88
|
+
- For components with children, prefer `React.PropsWithChildren<T>` over manually redeclaring a `children` field.
|
|
88
89
|
|
|
89
90
|
## Components
|
|
90
91
|
|
|
@@ -45,6 +45,9 @@ Use this guidance when validating or changing the monorepo setup against the RPR
|
|
|
45
45
|
- Generate React app and library projects in a way that preserves the expected Nx command surface.
|
|
46
46
|
- The web app should expose at least `serve`, `build`, `lint`, `test`, and `typecheck` when those capabilities exist in the generated project.
|
|
47
47
|
- The UI library should expose at least `build`, `lint`, `test`, and `typecheck`.
|
|
48
|
+
- Use app-scoped Node package names: `packages/node/{name}-ui` publishes as `@{name}/ui`, `apps/{name}-web` publishes as `@{name}/web`, and Storybook publishes as `@{name}/sb`.
|
|
49
|
+
- Import the shared UI package through `@{name}/ui`, for example `import { AppThemeProvider } from "@testapp/ui"` for a `TestApp` workspace.
|
|
50
|
+
- Link local Node workspace packages with relative `file:` dependency specs instead of `workspace:*`, because npm must be able to install the generated workspace without unsupported protocol errors.
|
|
48
51
|
- Keep Storybook as a dedicated host project at `packages/node/sb`; do not silently repurpose the UI library as the Storybook project.
|
|
49
52
|
|
|
50
53
|
## Python Expectations
|
|
@@ -81,6 +84,7 @@ npx nx run domain:migrate -- downgrade -1
|
|
|
81
84
|
- Project roots and import package names follow the kebab-case and snake_case rules.
|
|
82
85
|
- Python projects have both `src/` and `tests/`.
|
|
83
86
|
- The domain import package remains `src/{name_underscore}_domain`.
|
|
87
|
+
- Node imports use app-scoped package names such as `@{name}/ui`, not stale `@repo/*` aliases.
|
|
84
88
|
- Storybook remains hosted in `packages/node/sb`.
|
|
85
89
|
- Workspace members and workspace dependency sources are complete.
|
|
86
90
|
- Generated instructions, checks, and scaffolds refer to the current contract rather than stale paths.
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
# NX + UV Monorepo Setup Guide
|
|
2
2
|
|
|
3
|
-
This guide explains how to
|
|
3
|
+
This guide explains how to set up a monorepo that combines NX (for Node / TypeScript) and UV (for Python).
|
|
4
4
|
|
|
5
|
-
##
|
|
5
|
+
## Prerequisites
|
|
6
6
|
|
|
7
7
|
Install the following:
|
|
8
8
|
|
|
@@ -10,30 +10,30 @@ Install the following:
|
|
|
10
10
|
- **npm**: comes with Node.js
|
|
11
11
|
- **UV**: install from [uv.io](https://uv.io/)
|
|
12
12
|
- **Python**: latest version from [python.org](https://www.python.org/)
|
|
13
|
-
- **
|
|
14
|
-
- **Rust**: latest version from [rust-lang.org](https://www.rust-lang.org/)(optional, for Rust-based engines)
|
|
13
|
+
- **Git**: latest version from [git-scm.com](https://git-scm.com/)
|
|
14
|
+
- **Rust**: latest version from [rust-lang.org](https://www.rust-lang.org/) (optional, for Rust-based engines)
|
|
15
15
|
|
|
16
16
|
## Initial Setup
|
|
17
17
|
|
|
18
18
|
### Step 1: Create NX Workspace
|
|
19
19
|
|
|
20
20
|
```shell
|
|
21
|
-
npx create-nx-workspace@latest my-monorepo --
|
|
21
|
+
npx create-nx-workspace@latest my-monorepo --preset=apps --nxCloud=never --interactive=false
|
|
22
22
|
cd my-monorepo
|
|
23
23
|
```
|
|
24
24
|
|
|
25
25
|
If you already have an existing directory, you can initialize NX in it:
|
|
26
26
|
|
|
27
27
|
```shell
|
|
28
|
-
npx
|
|
28
|
+
npx nx init --interactive=false --nxCloud=false
|
|
29
29
|
```
|
|
30
30
|
|
|
31
31
|
### Step 2: Initialize UV
|
|
32
32
|
|
|
33
|
-
Create a root `pyproject.toml`file for UV workspace management
|
|
33
|
+
Create a root `pyproject.toml` file for UV workspace management
|
|
34
34
|
|
|
35
35
|
```shell
|
|
36
|
-
uv init --no-
|
|
36
|
+
uv init --no-readme --no-pin-python --python {python_version}
|
|
37
37
|
```
|
|
38
38
|
|
|
39
39
|
Edit the generated `pyproject.toml` to include the following configuration:
|
|
@@ -140,7 +140,7 @@ npx nx g @nx/storybook:configuration {sb-name} --uiFramework=@storybook/react-vi
|
|
|
140
140
|
|
|
141
141
|
## Adding Python Projects
|
|
142
142
|
|
|
143
|
-
Python
|
|
143
|
+
Python projects require manual setup since NX doesn't have built-in Python plugins.
|
|
144
144
|
|
|
145
145
|
### Step 1: Create Directory Structure
|
|
146
146
|
|
|
@@ -170,7 +170,7 @@ requires-python = ">={python_version}"
|
|
|
170
170
|
dependencies = []
|
|
171
171
|
|
|
172
172
|
[tool.uv]
|
|
173
|
-
|
|
173
|
+
required-version = ">={uv_version}" # use the latest you have installed
|
|
174
174
|
|
|
175
175
|
[tool.uv.workspace]
|
|
176
176
|
members = [
|
|
@@ -190,8 +190,8 @@ dev = [
|
|
|
190
190
|
]
|
|
191
191
|
[tool.ruff]
|
|
192
192
|
line-length = 88
|
|
193
|
-
target-version="py314" # Update to match your target Python version
|
|
194
|
-
exclude = [**/migrations/**]
|
|
193
|
+
target-version = "py314" # Update to match your target Python version
|
|
194
|
+
exclude = ["**/migrations/**"]
|
|
195
195
|
|
|
196
196
|
[tool.ruff.lint]
|
|
197
197
|
select = [
|
|
@@ -207,17 +207,17 @@ ignore = [
|
|
|
207
207
|
"C901" # Complexity, can be handled by refactoring when needed, but not a hard rule
|
|
208
208
|
]
|
|
209
209
|
|
|
210
|
-
[tool.ruff.lint.
|
|
211
|
-
"
|
|
210
|
+
[tool.ruff.lint.per-file-ignores]
|
|
211
|
+
"__init__.py" = [
|
|
212
212
|
"F401", # Allow unused imports in __init__.py for package exports
|
|
213
213
|
]
|
|
214
|
-
"tests
|
|
214
|
+
"tests/**/*.py" = ["F841"]
|
|
215
215
|
|
|
216
216
|
[tool.ruff.format]
|
|
217
|
-
quote-style="double"
|
|
218
|
-
indent-style="space"
|
|
219
|
-
skip-magic-trailing-comma=false
|
|
220
|
-
line-ending="lf"
|
|
217
|
+
quote-style = "double"
|
|
218
|
+
indent-style = "space"
|
|
219
|
+
skip-magic-trailing-comma = false
|
|
220
|
+
line-ending = "lf"
|
|
221
221
|
|
|
222
222
|
[tool.pytest.ini_options]
|
|
223
223
|
pythonpath = [
|
|
@@ -230,7 +230,7 @@ testpaths = [
|
|
|
230
230
|
]
|
|
231
231
|
```
|
|
232
232
|
|
|
233
|
-
**For a
|
|
233
|
+
**For a FastAPI application (`apps/{my-python-app}/pyproject.toml`)**
|
|
234
234
|
|
|
235
235
|
```toml
|
|
236
236
|
[project]
|
|
@@ -245,13 +245,8 @@ dependencies = [
|
|
|
245
245
|
# Add other workspace dependencies here e.g. "{my-python-lib}" must be defined in tool.uv.sources
|
|
246
246
|
]
|
|
247
247
|
|
|
248
|
-
[tool.uv.workspace]
|
|
249
|
-
members = [
|
|
250
|
-
"apps/{my-python-app}"
|
|
251
|
-
]
|
|
252
|
-
|
|
253
248
|
[tool.uv.sources]
|
|
254
|
-
{my-python-lib} = { workspace = true }
|
|
249
|
+
"{my-python-lib}" = { workspace = true }
|
|
255
250
|
|
|
256
251
|
[project.optional-dependencies]
|
|
257
252
|
dev = [
|
|
@@ -263,11 +258,11 @@ dev = [
|
|
|
263
258
|
]
|
|
264
259
|
|
|
265
260
|
[build-system]
|
|
266
|
-
requires=["hatchling"]
|
|
267
|
-
build-backend="hatchling.build"
|
|
261
|
+
requires = ["hatchling"]
|
|
262
|
+
build-backend = "hatchling.build"
|
|
268
263
|
|
|
269
|
-
[tool.hatch.build.targets.wheel
|
|
270
|
-
"src
|
|
264
|
+
[tool.hatch.build.targets.wheel]
|
|
265
|
+
packages = ["src/{my_python_app}"]
|
|
271
266
|
|
|
272
267
|
[tool.coverage.run]
|
|
273
268
|
source = ["src"]
|
|
@@ -279,8 +274,8 @@ omit = [
|
|
|
279
274
|
"*/.venv/*"
|
|
280
275
|
]
|
|
281
276
|
|
|
282
|
-
[tool.coverage.
|
|
283
|
-
output="coverage.xml"
|
|
277
|
+
[tool.coverage.xml]
|
|
278
|
+
output = "coverage.xml"
|
|
284
279
|
|
|
285
280
|
[tool.coverage.report]
|
|
286
281
|
exclude_lines = [
|
|
@@ -313,10 +308,10 @@ dependencies = []
|
|
|
313
308
|
# another-lib = { workspace = true }
|
|
314
309
|
|
|
315
310
|
[build-system]
|
|
316
|
-
requires=["hatchling"]
|
|
317
|
-
build-backend="hatchling.build"
|
|
311
|
+
requires = ["hatchling"]
|
|
312
|
+
build-backend = "hatchling.build"
|
|
318
313
|
|
|
319
|
-
[tool.hatch.build.targets.wheel
|
|
314
|
+
[tool.hatch.build.targets.wheel]
|
|
320
315
|
packages = ["src/{my_python_lib}"]
|
|
321
316
|
|
|
322
317
|
# if you have files that are not code like json or yaml you can include them in the wheel with:
|
|
@@ -497,7 +492,7 @@ Alembic autogeneration relies on model discovery, make sure the new entity modul
|
|
|
497
492
|
|
|
498
493
|
## Adding other languages (e.g. Rust)
|
|
499
494
|
|
|
500
|
-
The same pattern can be applied to other languages. Here how you would
|
|
495
|
+
The same pattern can be applied to other languages. Here is how you would set up a Rust project:
|
|
501
496
|
|
|
502
497
|
### Step 1: Create Directory Structure
|
|
503
498
|
|
|
@@ -646,7 +641,7 @@ Example:
|
|
|
646
641
|
- Use `.env.example` to document required environment variables without exposing sensitive information
|
|
647
642
|
- Never commit `.env` files to version control
|
|
648
643
|
- For python use `python-dotenv` or `pydantic-settings` to load environment variables in your application
|
|
649
|
-
- For Node.js
|
|
644
|
+
- For Node.js use `dotenv` or Vite's built-in environment variable support
|
|
650
645
|
|
|
651
646
|
### 6. Testing
|
|
652
647
|
|
|
@@ -710,7 +705,7 @@ repos:
|
|
|
710
705
|
|
|
711
706
|
- id: prettier
|
|
712
707
|
name: Prettier format
|
|
713
|
-
entry: npx prettier --write --
|
|
708
|
+
entry: npx prettier --write --log-level=warn
|
|
714
709
|
language: system
|
|
715
710
|
types_or: [javascript, jsx, ts, tsx, json, css, scss, html, markdown]
|
|
716
711
|
exclude: \.ipynb$ # Exclude Jupyter notebooks from Prettier formatting
|
|
@@ -726,7 +721,7 @@ repos:
|
|
|
726
721
|
|
|
727
722
|
### 9. CI / CD Considerations
|
|
728
723
|
|
|
729
|
-
- Use NX's
|
|
724
|
+
- Use NX's affected commands to only test/build changed projects
|
|
730
725
|
- Cache build between CI runs
|
|
731
726
|
- Example GitHub Actions:
|
|
732
727
|
|
|
@@ -815,7 +810,7 @@ uvx ruff format . # Format Python code with Ruff
|
|
|
815
810
|
**Problem:**
|
|
816
811
|
|
|
817
812
|
1. The package is not listed in the root `pyproject.toml` under `[tool.uv.workspace].members`
|
|
818
|
-
2. You have not
|
|
813
|
+
2. You have not run `uv sync --all-packages` after adding the package to the workspace configuration
|
|
819
814
|
3. The workspace dependency was not declared with `{ workspace = true }` in the `[tool.uv.sources]` section of the dependent package's `pyproject.toml`
|
|
820
815
|
|
|
821
816
|
### Issue: NX can't find Python projects
|
|
@@ -838,7 +833,7 @@ uvx ruff format . # Format Python code with Ruff
|
|
|
838
833
|
|
|
839
834
|
This setup gives you the best of both worlds:
|
|
840
835
|
|
|
841
|
-
- **NX** for Node.js/
|
|
836
|
+
- **NX** for Node.js/TypeScript projects with excellent tooling and generators
|
|
842
837
|
- **UV** for Python projects with modern dependency management and workspace support
|
|
843
838
|
- A unified monorepo structure that allows you to manage both ecosystems in one place.
|
|
844
839
|
- Extensible to other languages like Rust with manual setup.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.1.9"
|
|
@@ -73,7 +73,7 @@ def check_workspace(project_dir: Path) -> list[CheckResult]:
|
|
|
73
73
|
pytest_config = False
|
|
74
74
|
dev_dependencies_ok = False
|
|
75
75
|
if tool:
|
|
76
|
-
uv_require_version = "
|
|
76
|
+
uv_require_version = "required-version" in tool.get("uv", {})
|
|
77
77
|
pytest_config = "pytest" in tool and "ini_options" in tool["pytest"]
|
|
78
78
|
|
|
79
79
|
dependency_groups = pyproject_data.get("dependency-groups", {}) if pyproject_data else {}
|
|
@@ -42,6 +42,7 @@ def api(
|
|
|
42
42
|
ctx.ensure_dir(tests_root)
|
|
43
43
|
|
|
44
44
|
_write_api_pyproject(ctx, api_root, name, app_name, app_name_underscore)
|
|
45
|
+
_write_api_readme(ctx, api_root, name)
|
|
45
46
|
_write_api_project_json(ctx, api_root, app_name, app_name_underscore)
|
|
46
47
|
_write_main(ctx, src_root / "main.py", name, domain_underscore)
|
|
47
48
|
_write_init_file(ctx, src_root / "__init__.py")
|
|
@@ -98,6 +99,14 @@ testpaths = ["tests"]
|
|
|
98
99
|
ctx.write_file(api_root / "pyproject.toml", content)
|
|
99
100
|
|
|
100
101
|
|
|
102
|
+
def _write_api_readme(ctx: RunContext, api_root: Path, name: str) -> None:
|
|
103
|
+
content = f"""# {name} API
|
|
104
|
+
|
|
105
|
+
FastAPI application generated by RPR.
|
|
106
|
+
"""
|
|
107
|
+
ctx.write_file(api_root / "README.md", content)
|
|
108
|
+
|
|
109
|
+
|
|
101
110
|
def _write_api_project_json(
|
|
102
111
|
ctx: RunContext, api_root: Path, app_name: str, app_name_underscore: str
|
|
103
112
|
) -> None:
|
|
@@ -60,6 +60,7 @@ def domain(
|
|
|
60
60
|
|
|
61
61
|
# --- project metadata --------------------------------------------------
|
|
62
62
|
_write_domain_pyproject(ctx, domain_root, name, pkg_name_underscore)
|
|
63
|
+
_write_domain_readme(ctx, domain_root, name)
|
|
63
64
|
_write_project_json(ctx, domain_root, include_migrations=migrations)
|
|
64
65
|
|
|
65
66
|
# --- __init__.py files -------------------------------------------------
|
|
@@ -142,6 +143,14 @@ testpaths = ["tests"]
|
|
|
142
143
|
ctx.write_file(domain_root / "pyproject.toml", content)
|
|
143
144
|
|
|
144
145
|
|
|
146
|
+
def _write_domain_readme(ctx: RunContext, domain_root: Path, name: str) -> None:
|
|
147
|
+
content = f"""# {name} Domain
|
|
148
|
+
|
|
149
|
+
Domain layer package generated by RPR.
|
|
150
|
+
"""
|
|
151
|
+
ctx.write_file(domain_root / "README.md", content)
|
|
152
|
+
|
|
153
|
+
|
|
145
154
|
def _write_project_json(
|
|
146
155
|
ctx: RunContext, domain_root: Path, *, include_migrations: bool
|
|
147
156
|
) -> None:
|
|
@@ -12,7 +12,9 @@ from rpr.context import RunContext
|
|
|
12
12
|
from rpr.dependencies import (
|
|
13
13
|
ROOT_STORYBOOK_DEV_DEPENDENCIES,
|
|
14
14
|
STORYBOOK_DEV_DEPENDENCIES,
|
|
15
|
+
storybook_host_package_name,
|
|
15
16
|
storybook_package_dependencies,
|
|
17
|
+
ui_package_name,
|
|
16
18
|
)
|
|
17
19
|
from rpr.workspace import ensure_npm_workspaces, write_package_json
|
|
18
20
|
|
|
@@ -185,7 +187,7 @@ def _write_sb_package_json(ctx: RunContext, sb_root: Path, ui_pkg: str) -> None:
|
|
|
185
187
|
ctx,
|
|
186
188
|
sb_root / "package.json",
|
|
187
189
|
defaults={
|
|
188
|
-
"name":
|
|
190
|
+
"name": storybook_host_package_name(ui_pkg),
|
|
189
191
|
"version": "0.1.0",
|
|
190
192
|
"private": True,
|
|
191
193
|
"type": "module",
|
|
@@ -306,7 +308,8 @@ ReactDOM.createRoot(document.getElementById("root")!).render(
|
|
|
306
308
|
|
|
307
309
|
|
|
308
310
|
def _write_sb_app(ctx: RunContext, src_root: Path, ui_pkg: str) -> None:
|
|
309
|
-
|
|
311
|
+
ui_import_name = ui_package_name(ui_pkg)
|
|
312
|
+
content = f"""import {{ uiPackageName }} from "{ui_import_name}";
|
|
310
313
|
|
|
311
314
|
export const storybookHostTitle = "Storybook Host";
|
|
312
315
|
|
|
@@ -368,7 +371,8 @@ export default config;
|
|
|
368
371
|
|
|
369
372
|
|
|
370
373
|
def _write_sb_preview(ctx: RunContext, path: Path, ui_pkg: str) -> None:
|
|
371
|
-
|
|
374
|
+
ui_import_name = ui_package_name(ui_pkg)
|
|
375
|
+
content = """import { appTheme } from "__UI_IMPORT__";
|
|
372
376
|
import { CssBaseline, ThemeProvider } from "@mui/material";
|
|
373
377
|
import type { Preview } from "@storybook/react";
|
|
374
378
|
import { Fragment, createElement } from "react";
|
|
@@ -398,7 +402,7 @@ const preview: Preview = {
|
|
|
398
402
|
};
|
|
399
403
|
|
|
400
404
|
export default preview;
|
|
401
|
-
""".replace("
|
|
405
|
+
""".replace("__UI_IMPORT__", ui_import_name)
|
|
402
406
|
ctx.write_file(path, content)
|
|
403
407
|
|
|
404
408
|
|
|
@@ -13,7 +13,9 @@ from rpr.dependencies import (
|
|
|
13
13
|
UI_LIBRARY_DEPENDENCIES,
|
|
14
14
|
UI_LIBRARY_DEV_DEPENDENCIES,
|
|
15
15
|
WEB_APP_DEV_DEPENDENCIES,
|
|
16
|
+
ui_package_name,
|
|
16
17
|
web_app_dependencies,
|
|
18
|
+
web_package_name,
|
|
17
19
|
)
|
|
18
20
|
from rpr.workspace import ensure_npm_workspaces, write_package_json
|
|
19
21
|
|
|
@@ -140,7 +142,7 @@ def _write_ui_package_json(ctx: RunContext, lib_root: Path, ui_pkg_name: str) ->
|
|
|
140
142
|
ctx,
|
|
141
143
|
lib_root / "package.json",
|
|
142
144
|
defaults={
|
|
143
|
-
"name":
|
|
145
|
+
"name": ui_package_name(ui_pkg_name),
|
|
144
146
|
"version": "0.1.0",
|
|
145
147
|
"private": True,
|
|
146
148
|
"main": "src/index.ts",
|
|
@@ -259,7 +261,7 @@ def _write_app_package_json(
|
|
|
259
261
|
ctx,
|
|
260
262
|
app_root / "package.json",
|
|
261
263
|
defaults={
|
|
262
|
-
"name":
|
|
264
|
+
"name": web_package_name(app_name),
|
|
263
265
|
"version": "0.1.0",
|
|
264
266
|
"private": True,
|
|
265
267
|
"type": "module",
|
|
@@ -370,7 +372,8 @@ ReactDOM.createRoot(document.getElementById("root")!).render(
|
|
|
370
372
|
|
|
371
373
|
def _write_app_tsx(ctx: RunContext, src: Path, app_name: str, ui_pkg_name: str) -> None:
|
|
372
374
|
title = app_name.replace("-", " ").title()
|
|
373
|
-
|
|
375
|
+
ui_import_name = ui_package_name(ui_pkg_name)
|
|
376
|
+
content = f"""import {{ AppThemeProvider, uiPackageName }} from "{ui_import_name}";
|
|
374
377
|
|
|
375
378
|
export const appTitle = "{title}";
|
|
376
379
|
|
|
@@ -13,7 +13,7 @@ from rich.console import Console
|
|
|
13
13
|
|
|
14
14
|
from rpr.cli import app
|
|
15
15
|
from rpr.context import RunContext
|
|
16
|
-
from rpr.dependencies import ROOT_NODE_DEV_DEPENDENCIES
|
|
16
|
+
from rpr.dependencies import ROOT_NODE_DEV_DEPENDENCIES, npm_package_name
|
|
17
17
|
from rpr.generators.base import SyncConfig, load_rules
|
|
18
18
|
from rpr.generators.claude import generate_claude_rules
|
|
19
19
|
from rpr.generators.copilot import generate_copilot_rules
|
|
@@ -355,7 +355,7 @@ requires-python = ">={python_version}"
|
|
|
355
355
|
dependencies = []
|
|
356
356
|
|
|
357
357
|
[tool.uv]
|
|
358
|
-
|
|
358
|
+
required-version = "{uv_require_version}"
|
|
359
359
|
|
|
360
360
|
[tool.uv.workspace]
|
|
361
361
|
members = []
|
|
@@ -398,6 +398,18 @@ testpaths = []
|
|
|
398
398
|
ctx.update_file(ctx.project_dir / "pyproject.toml", content)
|
|
399
399
|
|
|
400
400
|
|
|
401
|
+
def _write_root_readme(ctx: RunContext, project_name: str) -> None:
|
|
402
|
+
path = ctx.project_dir / "README.md"
|
|
403
|
+
if path.exists():
|
|
404
|
+
return
|
|
405
|
+
|
|
406
|
+
content = f"""# {project_name}
|
|
407
|
+
|
|
408
|
+
RPR NX + UV monorepo.
|
|
409
|
+
"""
|
|
410
|
+
ctx.write_file(path, content)
|
|
411
|
+
|
|
412
|
+
|
|
401
413
|
def _detect_uv_require_version() -> str:
|
|
402
414
|
try:
|
|
403
415
|
result = subprocess.run(
|
|
@@ -426,7 +438,7 @@ def _write_root_package_json(ctx: RunContext, project_name: str) -> None:
|
|
|
426
438
|
else:
|
|
427
439
|
package = {}
|
|
428
440
|
|
|
429
|
-
package
|
|
441
|
+
package["name"] = npm_package_name(project_name)
|
|
430
442
|
package["private"] = True
|
|
431
443
|
|
|
432
444
|
workspaces = package.get("workspaces", [])
|
|
@@ -813,6 +825,7 @@ def init(
|
|
|
813
825
|
# ------------------------------------------------------------------
|
|
814
826
|
_write_nx_json(ctx)
|
|
815
827
|
_write_root_pyproject(ctx, project_name, python_version)
|
|
828
|
+
_write_root_readme(ctx, project_name)
|
|
816
829
|
_write_root_package_json(ctx, project_name)
|
|
817
830
|
_write_tsconfig_base(ctx)
|
|
818
831
|
_write_root_eslint_config(ctx)
|
|
@@ -67,9 +67,35 @@ PYO3_VERSION = MANIFEST["engine"]["pyo3"]["specifier"]
|
|
|
67
67
|
MATURIN_SPEC = MANIFEST["engine"]["maturin"]["specifier"]
|
|
68
68
|
|
|
69
69
|
|
|
70
|
+
def npm_package_name(package_name: str) -> str:
|
|
71
|
+
normalized = package_name.replace("_", "-").lower()
|
|
72
|
+
normalized = re.sub(r"[^a-z0-9._-]+", "-", normalized).strip(".-")
|
|
73
|
+
return normalized or "package"
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def scoped_package_name(scope_name: str, package_name: str) -> str:
|
|
77
|
+
return f"@{npm_package_name(scope_name)}/{npm_package_name(package_name)}"
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def _strip_suffix(package_name: str, suffix: str) -> str:
|
|
81
|
+
return package_name[: -len(suffix)] if package_name.endswith(suffix) else package_name
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def ui_package_name(ui_pkg_name: str) -> str:
|
|
85
|
+
return scoped_package_name(_strip_suffix(ui_pkg_name, "-ui"), "ui")
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
def web_package_name(app_name: str) -> str:
|
|
89
|
+
return scoped_package_name(_strip_suffix(app_name, "-web"), "web")
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def storybook_host_package_name(ui_pkg_name: str) -> str:
|
|
93
|
+
return scoped_package_name(_strip_suffix(ui_pkg_name, "-ui"), "sb")
|
|
94
|
+
|
|
95
|
+
|
|
70
96
|
def web_app_dependencies(ui_pkg_name: str) -> dict[str, str]:
|
|
71
97
|
return {
|
|
72
|
-
f"
|
|
98
|
+
ui_package_name(ui_pkg_name): f"file:../../packages/node/{ui_pkg_name}",
|
|
73
99
|
"react": UI_LIBRARY_DEPENDENCIES["react"],
|
|
74
100
|
"react-dom": UI_LIBRARY_DEPENDENCIES["react-dom"],
|
|
75
101
|
}
|
|
@@ -77,7 +103,7 @@ def web_app_dependencies(ui_pkg_name: str) -> dict[str, str]:
|
|
|
77
103
|
|
|
78
104
|
def storybook_package_dependencies(ui_pkg_name: str) -> dict[str, str]:
|
|
79
105
|
return {
|
|
80
|
-
f"
|
|
106
|
+
ui_package_name(ui_pkg_name): f"file:../{ui_pkg_name}",
|
|
81
107
|
**STORYBOOK_DEPENDENCIES,
|
|
82
108
|
}
|
|
83
109
|
|
|
@@ -11,6 +11,7 @@ Use React for UI composition, TanStack Query for server-state workflows, TanStac
|
|
|
11
11
|
|
|
12
12
|
- Use functional components.
|
|
13
13
|
- Extract reusable behavior into hooks instead of duplicating component logic.
|
|
14
|
+
- Import the generated shared UI package through the app-scoped package name `@{name}/ui`, such as `@testapp/ui`, not `@repo/{name}-ui`.
|
|
14
15
|
- Prefer a shared base API service at `src/services/base/api.service.ts` when the UI package needs centralized fetch behavior.
|
|
15
16
|
- Prefer a shared `useStateNavigation` hook at `src/hooks/useStateNavigation.ts` when UI state should persist in the URL.
|
|
16
17
|
- Reuse existing components, hooks, and service patterns before introducing a new abstraction.
|
|
@@ -96,7 +97,7 @@ export const SomeContainer = styled(Box)`
|
|
|
96
97
|
|
|
97
98
|
- Type every API payload and mutation input.
|
|
98
99
|
- Prefer `type` aliases for DTO-shaped data, `interface` for extendable service contracts, and classes only when the UI genuinely needs behavior.
|
|
99
|
-
- For components with children, prefer `React.PropsWithChildren<T>` over manually
|
|
100
|
+
- For components with children, prefer `React.PropsWithChildren<T>` over manually redeclaring a `children` field.
|
|
100
101
|
|
|
101
102
|
## Components
|
|
102
103
|
|
|
@@ -45,6 +45,9 @@ Use this guidance when validating or changing the monorepo setup against the RPR
|
|
|
45
45
|
- Generate React app and library projects in a way that preserves the expected Nx command surface.
|
|
46
46
|
- The web app should expose at least `serve`, `build`, `lint`, `test`, and `typecheck` when those capabilities exist in the generated project.
|
|
47
47
|
- The UI library should expose at least `build`, `lint`, `test`, and `typecheck`.
|
|
48
|
+
- Use app-scoped Node package names: `packages/node/{name}-ui` publishes as `@{name}/ui`, `apps/{name}-web` publishes as `@{name}/web`, and Storybook publishes as `@{name}/sb`.
|
|
49
|
+
- Import the shared UI package through `@{name}/ui`, for example `import { AppThemeProvider } from "@testapp/ui"` for a `TestApp` workspace.
|
|
50
|
+
- Link local Node workspace packages with relative `file:` dependency specs instead of `workspace:*`, because npm must be able to install the generated workspace without unsupported protocol errors.
|
|
48
51
|
- Keep Storybook as a dedicated host project at `packages/node/sb`; do not silently repurpose the UI library as the Storybook project.
|
|
49
52
|
|
|
50
53
|
## Python Expectations
|
|
@@ -81,6 +84,7 @@ npx nx run domain:migrate -- downgrade -1
|
|
|
81
84
|
- Project roots and import package names follow the kebab-case and snake_case rules.
|
|
82
85
|
- Python projects have both `src/` and `tests/`.
|
|
83
86
|
- The domain import package remains `src/{name_underscore}_domain`.
|
|
87
|
+
- Node imports use app-scoped package names such as `@{name}/ui`, not stale `@repo/*` aliases.
|
|
84
88
|
- Storybook remains hosted in `packages/node/sb`.
|
|
85
89
|
- Workspace members and workspace dependency sources are complete.
|
|
86
90
|
- Generated instructions, checks, and scaffolds refer to the current contract rather than stale paths.
|
|
@@ -12,6 +12,12 @@ console = Console()
|
|
|
12
12
|
DEFAULT_NPM_WORKSPACES = ["apps/*", "packages/node/*"]
|
|
13
13
|
|
|
14
14
|
|
|
15
|
+
def _npm_package_name(package_name: str) -> str:
|
|
16
|
+
normalized = package_name.replace("_", "-").lower()
|
|
17
|
+
normalized = re.sub(r"[^a-z0-9._-]+", "-", normalized).strip(".-")
|
|
18
|
+
return normalized or "package"
|
|
19
|
+
|
|
20
|
+
|
|
15
21
|
def add_workspace_member(ctx: RunContext, member: str) -> None:
|
|
16
22
|
"""Add a UV workspace member to the root pyproject.toml."""
|
|
17
23
|
pyproject_path = ctx.project_dir / "pyproject.toml"
|
|
@@ -76,7 +82,10 @@ def write_package_json(
|
|
|
76
82
|
package = {}
|
|
77
83
|
|
|
78
84
|
for key, value in (defaults or {}).items():
|
|
79
|
-
|
|
85
|
+
if key == "name":
|
|
86
|
+
package[key] = value
|
|
87
|
+
else:
|
|
88
|
+
package.setdefault(key, value)
|
|
80
89
|
|
|
81
90
|
for field_name, additions in (
|
|
82
91
|
("dependencies", dependencies or {}),
|
|
@@ -87,7 +96,23 @@ def write_package_json(
|
|
|
87
96
|
field = {}
|
|
88
97
|
package[field_name] = field
|
|
89
98
|
for dep_name, version in additions.items():
|
|
90
|
-
|
|
99
|
+
if version.startswith("file:"):
|
|
100
|
+
for existing_name, existing_value in list(field.items()):
|
|
101
|
+
if (
|
|
102
|
+
existing_name != dep_name
|
|
103
|
+
and isinstance(existing_value, str)
|
|
104
|
+
and existing_value.startswith("workspace:")
|
|
105
|
+
):
|
|
106
|
+
field.pop(existing_name)
|
|
107
|
+
existing_version = field.get(dep_name)
|
|
108
|
+
if existing_version is None:
|
|
109
|
+
field[dep_name] = version
|
|
110
|
+
elif (
|
|
111
|
+
isinstance(existing_version, str)
|
|
112
|
+
and existing_version.startswith("workspace:")
|
|
113
|
+
and version.startswith("file:")
|
|
114
|
+
):
|
|
115
|
+
field[dep_name] = version
|
|
91
116
|
|
|
92
117
|
content = json.dumps(package, indent=2) + "\n"
|
|
93
118
|
if existed:
|
|
@@ -111,7 +136,7 @@ def ensure_npm_workspaces(ctx: RunContext, *, package_name: str | None = None) -
|
|
|
111
136
|
package = {}
|
|
112
137
|
|
|
113
138
|
if package_name:
|
|
114
|
-
package
|
|
139
|
+
package["name"] = _npm_package_name(package_name)
|
|
115
140
|
package["private"] = True
|
|
116
141
|
|
|
117
142
|
workspaces = package.get("workspaces", [])
|
|
@@ -39,7 +39,7 @@ requires-python = ">=3.12"
|
|
|
39
39
|
dependencies = []
|
|
40
40
|
|
|
41
41
|
[tool.uv]
|
|
42
|
-
|
|
42
|
+
required-version = ">=0.8.0"
|
|
43
43
|
|
|
44
44
|
[tool.uv.workspace]
|
|
45
45
|
members = ["packages/python/domain", "apps/my-app-api"]
|
|
@@ -205,7 +205,7 @@ def _create_packages(base: Path) -> None:
|
|
|
205
205
|
json.dumps(
|
|
206
206
|
{
|
|
207
207
|
"dependencies": {
|
|
208
|
-
"@
|
|
208
|
+
"@my-app/ui": web_app_dependencies("my-app-ui")["@my-app/ui"],
|
|
209
209
|
**STORYBOOK_DEPENDENCIES,
|
|
210
210
|
},
|
|
211
211
|
"devDependencies": STORYBOOK_DEV_DEPENDENCIES,
|
|
@@ -59,6 +59,7 @@ def test_generate_api_scaffolds_src_layout_tests_and_build_target(monkeypatch):
|
|
|
59
59
|
assert (src_root / "__init__.py").exists()
|
|
60
60
|
assert (src_root / "routes" / "__init__.py").exists()
|
|
61
61
|
assert (tests_root / "test_health.py").exists()
|
|
62
|
+
assert (api_root / "README.md").read_text().startswith("# my-app API")
|
|
62
63
|
|
|
63
64
|
pyproject = (api_root / "pyproject.toml").read_text()
|
|
64
65
|
project = json.loads((api_root / "project.json").read_text())
|
|
@@ -67,6 +68,7 @@ def test_generate_api_scaffolds_src_layout_tests_and_build_target(monkeypatch):
|
|
|
67
68
|
root_pyproject = Path("pyproject.toml").read_text()
|
|
68
69
|
|
|
69
70
|
assert 'packages = ["src/my_app_api"]' in pyproject
|
|
71
|
+
assert 'readme = "README.md"' in pyproject
|
|
70
72
|
assert '[tool.pytest.ini_options]' in pyproject
|
|
71
73
|
assert 'testpaths = ["tests"]' in pyproject
|
|
72
74
|
assert project["name"] == "my-app-api"
|
|
@@ -68,6 +68,7 @@ def test_generate_domain_scaffolds_config_and_metadata(monkeypatch):
|
|
|
68
68
|
assert (src_root / "entities" / "base.py").exists()
|
|
69
69
|
assert (src_root / "repos" / "base.py").exists()
|
|
70
70
|
assert (domain_root / "tests" / "test_package.py").exists()
|
|
71
|
+
assert (domain_root / "README.md").read_text().startswith("# my-app Domain")
|
|
71
72
|
|
|
72
73
|
for package_dir in [
|
|
73
74
|
"",
|
|
@@ -125,6 +126,7 @@ def test_generate_domain_scaffolds_config_and_metadata(monkeypatch):
|
|
|
125
126
|
)
|
|
126
127
|
assert "migrate" not in project["targets"]
|
|
127
128
|
assert '[tool.pytest.ini_options]' in (domain_root / "pyproject.toml").read_text()
|
|
129
|
+
assert 'readme = "README.md"' in (domain_root / "pyproject.toml").read_text()
|
|
128
130
|
assert 'members = ["packages/python/domain"]' in root_pyproject
|
|
129
131
|
|
|
130
132
|
assert recorded_calls[0][0] == ["uv", "sync"]
|