codeplain 0.2.5__tar.gz → 0.2.7__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 (193) hide show
  1. {codeplain-0.2.5 → codeplain-0.2.7}/PKG-INFO +1 -1
  2. {codeplain-0.2.5 → codeplain-0.2.7}/codeplain_REST_api.py +47 -11
  3. {codeplain-0.2.5 → codeplain-0.2.7}/config/system_config.yaml +1 -1
  4. codeplain-0.2.7/docs/plain2code_cli.md +63 -0
  5. {codeplain-0.2.5 → codeplain-0.2.7}/examples/example_hello_world_python/config.yaml +1 -1
  6. {codeplain-0.2.5 → codeplain-0.2.7}/git_utils.py +20 -0
  7. {codeplain-0.2.5 → codeplain-0.2.7}/install/walkthrough.sh +47 -50
  8. {codeplain-0.2.5 → codeplain-0.2.7}/module_renderer.py +3 -1
  9. {codeplain-0.2.5 → codeplain-0.2.7}/plain2code.py +58 -10
  10. {codeplain-0.2.5 → codeplain-0.2.7}/plain2code_arguments.py +24 -20
  11. {codeplain-0.2.5 → codeplain-0.2.7}/plain2code_events.py +3 -1
  12. {codeplain-0.2.5 → codeplain-0.2.7}/plain2code_exceptions.py +7 -1
  13. {codeplain-0.2.5 → codeplain-0.2.7}/plain2code_utils.py +5 -5
  14. {codeplain-0.2.5 → codeplain-0.2.7}/plain_file.py +2 -2
  15. {codeplain-0.2.5 → codeplain-0.2.7}/pyproject.toml +1 -1
  16. {codeplain-0.2.5 → codeplain-0.2.7}/render_machine/actions/analyze_specification_ambiguity.py +2 -1
  17. {codeplain-0.2.5 → codeplain-0.2.7}/render_machine/actions/fix_conformance_test.py +2 -2
  18. {codeplain-0.2.5 → codeplain-0.2.7}/render_machine/actions/fix_unit_tests.py +2 -2
  19. {codeplain-0.2.5 → codeplain-0.2.7}/render_machine/conformance_tests.py +2 -1
  20. {codeplain-0.2.5 → codeplain-0.2.7}/standard_template_library/typescript-react-app-template.plain +6 -0
  21. {codeplain-0.2.5 → codeplain-0.2.7}/tests/test_requires.py +2 -2
  22. {codeplain-0.2.5 → codeplain-0.2.7}/tui/plain2code_tui.py +8 -16
  23. {codeplain-0.2.5 → codeplain-0.2.7}/tui/state_handlers.py +9 -4
  24. {codeplain-0.2.5 → codeplain-0.2.7}/tui/widget_helpers.py +14 -3
  25. codeplain-0.2.5/docs/plain2code_cli.md +0 -66
  26. {codeplain-0.2.5 → codeplain-0.2.7}/.flake8 +0 -0
  27. {codeplain-0.2.5 → codeplain-0.2.7}/.github/workflows/lint-and-test.yml +0 -0
  28. {codeplain-0.2.5 → codeplain-0.2.7}/.github/workflows/nofity-slack-on-main-merge.yml +0 -0
  29. {codeplain-0.2.5 → codeplain-0.2.7}/.github/workflows/publish-install-script.yml +0 -0
  30. {codeplain-0.2.5 → codeplain-0.2.7}/.github/workflows/publish-to-pypi.yml +0 -0
  31. {codeplain-0.2.5 → codeplain-0.2.7}/.gitignore +0 -0
  32. {codeplain-0.2.5 → codeplain-0.2.7}/.vscode/launch.json +0 -0
  33. {codeplain-0.2.5 → codeplain-0.2.7}/.vscode/settings.json +0 -0
  34. {codeplain-0.2.5 → codeplain-0.2.7}/LICENSE +0 -0
  35. {codeplain-0.2.5 → codeplain-0.2.7}/README.md +0 -0
  36. {codeplain-0.2.5 → codeplain-0.2.7}/concept_utils.py +0 -0
  37. {codeplain-0.2.5 → codeplain-0.2.7}/config/__init__.py +0 -0
  38. {codeplain-0.2.5 → codeplain-0.2.7}/diff_utils.py +0 -0
  39. {codeplain-0.2.5 → codeplain-0.2.7}/docs/generate_cli.py +0 -0
  40. {codeplain-0.2.5 → codeplain-0.2.7}/docs/plain_language_specification.md +0 -0
  41. {codeplain-0.2.5 → codeplain-0.2.7}/docs/starting_a_plain_project_from_scratch.md +0 -0
  42. {codeplain-0.2.5 → codeplain-0.2.7}/event_bus.py +0 -0
  43. {codeplain-0.2.5 → codeplain-0.2.7}/examples/example_hello_world_golang/config.yaml +0 -0
  44. {codeplain-0.2.5 → codeplain-0.2.7}/examples/example_hello_world_golang/harness_tests/hello_world_test.go +0 -0
  45. {codeplain-0.2.5 → codeplain-0.2.7}/examples/example_hello_world_golang/hello_world_golang.plain +0 -0
  46. {codeplain-0.2.5 → codeplain-0.2.7}/examples/example_hello_world_golang/run.sh +0 -0
  47. {codeplain-0.2.5 → codeplain-0.2.7}/examples/example_hello_world_python/harness_tests/hello_world_display/test_hello_world.py +0 -0
  48. {codeplain-0.2.5 → codeplain-0.2.7}/examples/example_hello_world_python/hello_world_python.plain +0 -0
  49. {codeplain-0.2.5 → codeplain-0.2.7}/examples/example_hello_world_python/run.sh +0 -0
  50. {codeplain-0.2.5 → codeplain-0.2.7}/examples/example_hello_world_react/config.yaml +0 -0
  51. {codeplain-0.2.5 → codeplain-0.2.7}/examples/example_hello_world_react/harness_tests/hello_world_display/cypress/e2e/hello_world.cy.ts +0 -0
  52. {codeplain-0.2.5 → codeplain-0.2.7}/examples/example_hello_world_react/harness_tests/hello_world_display/cypress/support/e2e.ts +0 -0
  53. {codeplain-0.2.5 → codeplain-0.2.7}/examples/example_hello_world_react/harness_tests/hello_world_display/cypress.config.ts +0 -0
  54. {codeplain-0.2.5 → codeplain-0.2.7}/examples/example_hello_world_react/harness_tests/hello_world_display/package.json +0 -0
  55. {codeplain-0.2.5 → codeplain-0.2.7}/examples/example_hello_world_react/harness_tests/hello_world_display/tsconfig.json +0 -0
  56. {codeplain-0.2.5 → codeplain-0.2.7}/examples/example_hello_world_react/hello_world_react.plain +0 -0
  57. {codeplain-0.2.5 → codeplain-0.2.7}/examples/example_hello_world_react/run.sh +0 -0
  58. {codeplain-0.2.5 → codeplain-0.2.7}/examples/run.sh +0 -0
  59. {codeplain-0.2.5 → codeplain-0.2.7}/file_utils.py +0 -0
  60. {codeplain-0.2.5 → codeplain-0.2.7}/hash_key.py +0 -0
  61. {codeplain-0.2.5 → codeplain-0.2.7}/install/examples.sh +0 -0
  62. {codeplain-0.2.5 → codeplain-0.2.7}/install/install.sh +0 -0
  63. {codeplain-0.2.5 → codeplain-0.2.7}/memory_management.py +0 -0
  64. {codeplain-0.2.5 → codeplain-0.2.7}/plain2code_console.py +0 -0
  65. {codeplain-0.2.5 → codeplain-0.2.7}/plain2code_logger.py +0 -0
  66. {codeplain-0.2.5 → codeplain-0.2.7}/plain2code_nodes.py +0 -0
  67. {codeplain-0.2.5 → codeplain-0.2.7}/plain2code_read_config.py +0 -0
  68. {codeplain-0.2.5 → codeplain-0.2.7}/plain2code_state.py +0 -0
  69. {codeplain-0.2.5 → codeplain-0.2.7}/plain_modules.py +0 -0
  70. {codeplain-0.2.5 → codeplain-0.2.7}/plain_spec.py +0 -0
  71. {codeplain-0.2.5 → codeplain-0.2.7}/pytest.ini +0 -0
  72. {codeplain-0.2.5 → codeplain-0.2.7}/render_machine/__init__.py +0 -0
  73. {codeplain-0.2.5 → codeplain-0.2.7}/render_machine/actions/base_action.py +0 -0
  74. {codeplain-0.2.5 → codeplain-0.2.7}/render_machine/actions/commit_conformance_tests_changes.py +0 -0
  75. {codeplain-0.2.5 → codeplain-0.2.7}/render_machine/actions/commit_implementation_code_changes.py +0 -0
  76. {codeplain-0.2.5 → codeplain-0.2.7}/render_machine/actions/create_dist.py +0 -0
  77. {codeplain-0.2.5 → codeplain-0.2.7}/render_machine/actions/exit_with_error.py +0 -0
  78. {codeplain-0.2.5 → codeplain-0.2.7}/render_machine/actions/finish_functional_requirement.py +0 -0
  79. {codeplain-0.2.5 → codeplain-0.2.7}/render_machine/actions/prepare_repositories.py +0 -0
  80. {codeplain-0.2.5 → codeplain-0.2.7}/render_machine/actions/prepare_testing_environment.py +0 -0
  81. {codeplain-0.2.5 → codeplain-0.2.7}/render_machine/actions/refactor_code.py +0 -0
  82. {codeplain-0.2.5 → codeplain-0.2.7}/render_machine/actions/render_conformance_tests.py +0 -0
  83. {codeplain-0.2.5 → codeplain-0.2.7}/render_machine/actions/render_functional_requirement.py +0 -0
  84. {codeplain-0.2.5 → codeplain-0.2.7}/render_machine/actions/run_conformance_tests.py +0 -0
  85. {codeplain-0.2.5 → codeplain-0.2.7}/render_machine/actions/run_unit_tests.py +0 -0
  86. {codeplain-0.2.5 → codeplain-0.2.7}/render_machine/actions/summarize_conformance_tests.py +0 -0
  87. {codeplain-0.2.5 → codeplain-0.2.7}/render_machine/code_renderer.py +0 -0
  88. {codeplain-0.2.5 → codeplain-0.2.7}/render_machine/conformance_test_helpers.py +0 -0
  89. {codeplain-0.2.5 → codeplain-0.2.7}/render_machine/implementation_code_helpers.py +0 -0
  90. {codeplain-0.2.5 → codeplain-0.2.7}/render_machine/render_context.py +0 -0
  91. {codeplain-0.2.5 → codeplain-0.2.7}/render_machine/render_types.py +0 -0
  92. {codeplain-0.2.5 → codeplain-0.2.7}/render_machine/render_utils.py +0 -0
  93. {codeplain-0.2.5 → codeplain-0.2.7}/render_machine/state_machine_config.py +0 -0
  94. {codeplain-0.2.5 → codeplain-0.2.7}/render_machine/states.py +0 -0
  95. {codeplain-0.2.5 → codeplain-0.2.7}/render_machine/triggers.py +0 -0
  96. {codeplain-0.2.5 → codeplain-0.2.7}/requirements.txt +0 -0
  97. {codeplain-0.2.5 → codeplain-0.2.7}/resources/codeplain_overview.png +0 -0
  98. {codeplain-0.2.5 → codeplain-0.2.7}/resources/plain_example.png +0 -0
  99. {codeplain-0.2.5 → codeplain-0.2.7}/standard_template_library/__init__.py +0 -0
  100. {codeplain-0.2.5 → codeplain-0.2.7}/standard_template_library/golang-console-app-template.plain +0 -0
  101. {codeplain-0.2.5 → codeplain-0.2.7}/standard_template_library/python-console-app-template.plain +0 -0
  102. {codeplain-0.2.5 → codeplain-0.2.7}/standard_template_library/typescript-react-app-boilerplate.plain +0 -0
  103. {codeplain-0.2.5 → codeplain-0.2.7}/system_config.py +0 -0
  104. {codeplain-0.2.5 → codeplain-0.2.7}/test_scripts/run_conformance_tests_cypress.sh +0 -0
  105. {codeplain-0.2.5 → codeplain-0.2.7}/test_scripts/run_conformance_tests_golang.sh +0 -0
  106. {codeplain-0.2.5 → codeplain-0.2.7}/test_scripts/run_conformance_tests_python.sh +0 -0
  107. {codeplain-0.2.5 → codeplain-0.2.7}/test_scripts/run_unittests_golang.sh +0 -0
  108. {codeplain-0.2.5 → codeplain-0.2.7}/test_scripts/run_unittests_python.sh +0 -0
  109. {codeplain-0.2.5 → codeplain-0.2.7}/test_scripts/run_unittests_react.sh +0 -0
  110. {codeplain-0.2.5 → codeplain-0.2.7}/tests/__init__.py +0 -0
  111. {codeplain-0.2.5 → codeplain-0.2.7}/tests/conftest.py +0 -0
  112. {codeplain-0.2.5 → codeplain-0.2.7}/tests/data/imports/circular_imports_1.plain +0 -0
  113. {codeplain-0.2.5 → codeplain-0.2.7}/tests/data/imports/circular_imports_2.plain +0 -0
  114. {codeplain-0.2.5 → codeplain-0.2.7}/tests/data/imports/circular_imports_main.plain +0 -0
  115. {codeplain-0.2.5 → codeplain-0.2.7}/tests/data/imports/diamond_import_1.plain +0 -0
  116. {codeplain-0.2.5 → codeplain-0.2.7}/tests/data/imports/diamond_import_2.plain +0 -0
  117. {codeplain-0.2.5 → codeplain-0.2.7}/tests/data/imports/diamond_import_common.plain +0 -0
  118. {codeplain-0.2.5 → codeplain-0.2.7}/tests/data/imports/diamond_imports_main.plain +0 -0
  119. {codeplain-0.2.5 → codeplain-0.2.7}/tests/data/imports/non_existent_import.plain +0 -0
  120. {codeplain-0.2.5 → codeplain-0.2.7}/tests/data/plainfile/duplicate_specification_heading.plain +0 -0
  121. {codeplain-0.2.5 → codeplain-0.2.7}/tests/data/plainfile/invalid_specification_order.plain +0 -0
  122. {codeplain-0.2.5 → codeplain-0.2.7}/tests/data/plainfile/missing_non_functional_requirements.plain +0 -0
  123. {codeplain-0.2.5 → codeplain-0.2.7}/tests/data/plainfile/plain_source_with_absolute_link.plain +0 -0
  124. {codeplain-0.2.5 → codeplain-0.2.7}/tests/data/plainfile/plain_source_with_url_link.plain +0 -0
  125. {codeplain-0.2.5 → codeplain-0.2.7}/tests/data/plainfile/task_manager_with_reference_links.plain +0 -0
  126. {codeplain-0.2.5 → codeplain-0.2.7}/tests/data/plainfile/without_non_functional_requirement.plain +0 -0
  127. {codeplain-0.2.5 → codeplain-0.2.7}/tests/data/plainfileparser/concept_validation_acceptance_tests.plain +0 -0
  128. {codeplain-0.2.5 → codeplain-0.2.7}/tests/data/plainfileparser/concept_validation_acceptance_tests_nondefined.plain +0 -0
  129. {codeplain-0.2.5 → codeplain-0.2.7}/tests/data/plainfileparser/concept_validation_defined_nondefined.plain +0 -0
  130. {codeplain-0.2.5 → codeplain-0.2.7}/tests/data/plainfileparser/concept_validation_defined_nondefined_2.plain +0 -0
  131. {codeplain-0.2.5 → codeplain-0.2.7}/tests/data/plainfileparser/concept_validation_definition.plain +0 -0
  132. {codeplain-0.2.5 → codeplain-0.2.7}/tests/data/plainfileparser/concept_validation_noconcepts.plain +0 -0
  133. {codeplain-0.2.5 → codeplain-0.2.7}/tests/data/plainfileparser/concept_validation_nonconcept.plain +0 -0
  134. {codeplain-0.2.5 → codeplain-0.2.7}/tests/data/plainfileparser/concept_validation_nondefined.plain +0 -0
  135. {codeplain-0.2.5 → codeplain-0.2.7}/tests/data/plainfileparser/concept_validation_redefinition.plain +0 -0
  136. {codeplain-0.2.5 → codeplain-0.2.7}/tests/data/plainfileparser/concept_validation_several_concepts.plain +0 -0
  137. {codeplain-0.2.5 → codeplain-0.2.7}/tests/data/plainfileparser/concept_validation_valid.plain +0 -0
  138. {codeplain-0.2.5 → codeplain-0.2.7}/tests/data/plainfileparser/exported_concepts.plain +0 -0
  139. {codeplain-0.2.5 → codeplain-0.2.7}/tests/data/plainfileparser/exported_concepts_base.plain +0 -0
  140. {codeplain-0.2.5 → codeplain-0.2.7}/tests/data/plainfileparser/exported_concepts_example.plain +0 -0
  141. {codeplain-0.2.5 → codeplain-0.2.7}/tests/data/plainfileparser/exported_concepts_missing.plain +0 -0
  142. {codeplain-0.2.5 → codeplain-0.2.7}/tests/data/plainfileparser/exported_concepts_missing_example.plain +0 -0
  143. {codeplain-0.2.5 → codeplain-0.2.7}/tests/data/plainfileparser/exported_concepts_nested.plain +0 -0
  144. {codeplain-0.2.5 → codeplain-0.2.7}/tests/data/plainfileparser/exported_concepts_nested_example.plain +0 -0
  145. {codeplain-0.2.5 → codeplain-0.2.7}/tests/data/plainfileparser/exported_concepts_transitive_example.plain +0 -0
  146. {codeplain-0.2.5 → codeplain-0.2.7}/tests/data/plainfileparser/exported_concepts_transitive_l1.plain +0 -0
  147. {codeplain-0.2.5 → codeplain-0.2.7}/tests/data/plainfileparser/exported_concepts_transitive_l2.plain +0 -0
  148. {codeplain-0.2.5 → codeplain-0.2.7}/tests/data/plainfileparser/plain_file_parser_with_comments.plain +0 -0
  149. {codeplain-0.2.5 → codeplain-0.2.7}/tests/data/plainfileparser/plain_file_with_comments_indented.plain +0 -0
  150. {codeplain-0.2.5 → codeplain-0.2.7}/tests/data/plainfileparser/regular_plain_source.plain +0 -0
  151. {codeplain-0.2.5 → codeplain-0.2.7}/tests/data/plainfileparser/required_concepts.plain +0 -0
  152. {codeplain-0.2.5 → codeplain-0.2.7}/tests/data/plainfileparser/required_concepts_defs.plain +0 -0
  153. {codeplain-0.2.5 → codeplain-0.2.7}/tests/data/plainfileparser/required_concepts_example.plain +0 -0
  154. {codeplain-0.2.5 → codeplain-0.2.7}/tests/data/plainfileparser/required_concepts_l1.plain +0 -0
  155. {codeplain-0.2.5 → codeplain-0.2.7}/tests/data/plainfileparser/required_concepts_l2.plain +0 -0
  156. {codeplain-0.2.5 → codeplain-0.2.7}/tests/data/plainfileparser/required_concepts_missing.plain +0 -0
  157. {codeplain-0.2.5 → codeplain-0.2.7}/tests/data/plainfileparser/required_concepts_module.plain +0 -0
  158. {codeplain-0.2.5 → codeplain-0.2.7}/tests/data/plainfileparser/required_concepts_partial.plain +0 -0
  159. {codeplain-0.2.5 → codeplain-0.2.7}/tests/data/plainfileparser/required_concepts_partial_duplicate.plain +0 -0
  160. {codeplain-0.2.5 → codeplain-0.2.7}/tests/data/plainfileparser/topological_sort.plain +0 -0
  161. {codeplain-0.2.5 → codeplain-0.2.7}/tests/data/plainfileparser/topological_sort_not_referenced.plain +0 -0
  162. {codeplain-0.2.5 → codeplain-0.2.7}/tests/data/requires/circular_requires_main.plain +0 -0
  163. {codeplain-0.2.5 → codeplain-0.2.7}/tests/data/requires/circular_requires_sub.plain +0 -0
  164. {codeplain-0.2.5 → codeplain-0.2.7}/tests/data/requires/diamond_requires_1.plain +0 -0
  165. {codeplain-0.2.5 → codeplain-0.2.7}/tests/data/requires/diamond_requires_2.plain +0 -0
  166. {codeplain-0.2.5 → codeplain-0.2.7}/tests/data/requires/diamond_requires_common.plain +0 -0
  167. {codeplain-0.2.5 → codeplain-0.2.7}/tests/data/requires/diamond_requires_main.plain +0 -0
  168. {codeplain-0.2.5 → codeplain-0.2.7}/tests/data/requires/independent_requires_1.plain +0 -0
  169. {codeplain-0.2.5 → codeplain-0.2.7}/tests/data/requires/independent_requires_2.plain +0 -0
  170. {codeplain-0.2.5 → codeplain-0.2.7}/tests/data/requires/independent_requires_main.plain +0 -0
  171. {codeplain-0.2.5 → codeplain-0.2.7}/tests/data/requires/non_existent_require.plain +0 -0
  172. {codeplain-0.2.5 → codeplain-0.2.7}/tests/data/requires/normal_requires_1.plain +0 -0
  173. {codeplain-0.2.5 → codeplain-0.2.7}/tests/data/requires/normal_requires_2.plain +0 -0
  174. {codeplain-0.2.5 → codeplain-0.2.7}/tests/data/requires/normal_requires_common.plain +0 -0
  175. {codeplain-0.2.5 → codeplain-0.2.7}/tests/data/requires/normal_requires_main.plain +0 -0
  176. {codeplain-0.2.5 → codeplain-0.2.7}/tests/data/simple.plain +0 -0
  177. {codeplain-0.2.5 → codeplain-0.2.7}/tests/data/templates/block_level_include.plain +0 -0
  178. {codeplain-0.2.5 → codeplain-0.2.7}/tests/data/templates/code_variables.plain +0 -0
  179. {codeplain-0.2.5 → codeplain-0.2.7}/tests/data/templates/header.plain +0 -0
  180. {codeplain-0.2.5 → codeplain-0.2.7}/tests/data/templates/implement.plain +0 -0
  181. {codeplain-0.2.5 → codeplain-0.2.7}/tests/data/templates/implement_2.plain +0 -0
  182. {codeplain-0.2.5 → codeplain-0.2.7}/tests/data/templates/template_include.plain +0 -0
  183. {codeplain-0.2.5 → codeplain-0.2.7}/tests/data/templates/test_hardest_problem.plain +0 -0
  184. {codeplain-0.2.5 → codeplain-0.2.7}/tests/test_git_utils.py +0 -0
  185. {codeplain-0.2.5 → codeplain-0.2.7}/tests/test_imports.py +0 -0
  186. {codeplain-0.2.5 → codeplain-0.2.7}/tests/test_plainfile.py +0 -0
  187. {codeplain-0.2.5 → codeplain-0.2.7}/tests/test_plainfileparser.py +0 -0
  188. {codeplain-0.2.5 → codeplain-0.2.7}/tests/test_plainspec.py +0 -0
  189. {codeplain-0.2.5 → codeplain-0.2.7}/tui/__init__.py +0 -0
  190. {codeplain-0.2.5 → codeplain-0.2.7}/tui/components.py +0 -0
  191. {codeplain-0.2.5 → codeplain-0.2.7}/tui/models.py +0 -0
  192. {codeplain-0.2.5 → codeplain-0.2.7}/tui/spinner.py +0 -0
  193. {codeplain-0.2.5 → codeplain-0.2.7}/tui/styles.css +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: codeplain
3
- Version: 0.2.5
3
+ Version: 0.2.7
4
4
  Summary: Transform plain language specifications into working code
5
5
  License-File: LICENSE
6
6
  Classifier: Environment :: Console
@@ -2,6 +2,7 @@ import time
2
2
  from typing import Optional
3
3
 
4
4
  import requests
5
+ from requests.exceptions import ConnectionError, Timeout
5
6
 
6
7
  import plain2code_exceptions
7
8
  from plain2code_state import RunState
@@ -9,7 +10,6 @@ from plain2code_state import RunState
9
10
  MAX_RETRIES = 4
10
11
  RETRY_DELAY = 3
11
12
 
12
- # TODO: Handle connection errors
13
13
  RETRY_ERROR_CODES = [
14
14
  "LLMInternalError",
15
15
  ]
@@ -44,6 +44,42 @@ class CodeplainAPI:
44
44
  run_state.increment_call_count()
45
45
  payload["render_state"] = run_state.to_dict()
46
46
 
47
+ def _handle_retry_logic(
48
+ self, attempt: int, retry_delay: int, num_retries: int, error: Exception, silent: bool
49
+ ) -> int:
50
+ """
51
+ Handles retry logic with exponential backoff.
52
+
53
+ Args:
54
+ attempt: Current attempt number
55
+ retry_delay: Current retry delay in seconds
56
+ error: The exception that occurred
57
+ num_retries: Number of retries allowed
58
+ error_type: Type of error for logging (e.g., "Network error", "Error")
59
+
60
+ Returns:
61
+ Updated retry_delay for next attempt
62
+
63
+ Raises:
64
+ The original exception if max retries exceeded
65
+ """
66
+ is_connection_error = isinstance(error, ConnectionError) or isinstance(error, Timeout)
67
+ connection_error_type = "Network error" if is_connection_error else "Error"
68
+ if attempt < num_retries:
69
+ if not silent:
70
+ self.console.info(f"{connection_error_type} on attempt {attempt + 1}/{num_retries + 1}: {error}")
71
+ self.console.info(f"Retrying in {retry_delay} seconds...")
72
+ time.sleep(retry_delay)
73
+ # Exponential backoff
74
+ return retry_delay * 2
75
+ else:
76
+ if not silent:
77
+ self.console.error(f"Max retries ({num_retries}) exceeded. Last error: {error}")
78
+ if is_connection_error:
79
+ raise plain2code_exceptions.NetworkConnectionError("Failed to connect to API server.")
80
+ else:
81
+ raise error
82
+
47
83
  def _raise_for_error_code(self, response_json):
48
84
  """Raise appropriate exception based on error code in response."""
49
85
  error_code = response_json.get("error_code")
@@ -60,7 +96,13 @@ class CodeplainAPI:
60
96
  raise exception_class(message)
61
97
 
62
98
  def post_request(
63
- self, endpoint_url, headers, payload, run_state: Optional[RunState], num_retries: int = MAX_RETRIES
99
+ self,
100
+ endpoint_url,
101
+ headers,
102
+ payload,
103
+ run_state: Optional[RunState],
104
+ num_retries: int = MAX_RETRIES,
105
+ silent: bool = False,
64
106
  ):
65
107
  if run_state is not None:
66
108
  self._extend_payload_with_run_state(payload, run_state)
@@ -85,18 +127,12 @@ class CodeplainAPI:
85
127
  return response_json
86
128
 
87
129
  except Exception as e:
130
+ # For other errors, check if they should be retried
88
131
  if response_json is not None and "error_code" in response_json:
89
132
  if response_json["error_code"] not in RETRY_ERROR_CODES:
90
133
  raise e
91
134
 
92
- if attempt < num_retries:
93
- self.console.info(f"Error on attempt {attempt + 1}/{num_retries + 1}: {e}")
94
- self.console.info(f"Retrying in {retry_delay} seconds...")
95
- time.sleep(retry_delay)
96
- retry_delay *= 2 # Exponential backoff
97
- else:
98
- self.console.error(f"Max retries ({num_retries}) exceeded. Last error: {e}")
99
- raise e
135
+ retry_delay = self._handle_retry_logic(attempt, retry_delay, num_retries, e, silent)
100
136
 
101
137
  def connection_check(self, client_version):
102
138
  endpoint_url = f"{self.api_url}/connection_check"
@@ -105,7 +141,7 @@ class CodeplainAPI:
105
141
  "api_key": self.api_key,
106
142
  "client_version": client_version,
107
143
  }
108
- return self.post_request(endpoint_url, headers, payload, None, num_retries=0)
144
+ return self.post_request(endpoint_url, headers, payload, None, num_retries=0, silent=True)
109
145
 
110
146
  def render_functional_requirement(
111
147
  self,
@@ -1,4 +1,4 @@
1
- client_version: "0.17.0"
1
+ client_version: "0.2.7"
2
2
 
3
3
  error_messages:
4
4
  template_not_found:
@@ -0,0 +1,63 @@
1
+ # Plain2Code CLI Reference
2
+
3
+ ```text
4
+ usage: generate_cli.py [-h] [--verbose] [--base-folder BASE_FOLDER] [--build-folder BUILD_FOLDER] [--log-to-file | --no-log-to-file] [--log-file-name LOG_FILE_NAME] [--config-name CONFIG_NAME] [--render-range RENDER_RANGE | --render-from RENDER_FROM] [--force-render]
5
+ [--unittests-script UNITTESTS_SCRIPT] [--conformance-tests-folder CONFORMANCE_TESTS_FOLDER] [--conformance-tests-script CONFORMANCE_TESTS_SCRIPT] [--prepare-environment-script PREPARE_ENVIRONMENT_SCRIPT] [--api [API]] [--api-key API_KEY]
6
+ [--full-plain] [--dry-run] [--replay-with REPLAY_WITH] [--template-dir TEMPLATE_DIR] [--copy-build] [--build-dest BUILD_DEST] [--copy-conformance-tests] [--conformance-tests-dest CONFORMANCE_TESTS_DEST] [--render-machine-graph]
7
+ [--logging-config-path]
8
+ filename
9
+
10
+ Render plain code to target code.
11
+
12
+ positional arguments:
13
+ filename Path to the plain file to render. The directory containing this file has highest precedence for template loading, so you can place custom templates here to override the defaults. See --template-dir for more details about template loading.
14
+
15
+ options:
16
+ -h, --help show this help message and exit
17
+ --verbose, -v Enable verbose output
18
+ --base-folder BASE_FOLDER
19
+ Base folder for the build files
20
+ --build-folder BUILD_FOLDER
21
+ Folder for build files
22
+ --log-to-file, --no-log-to-file
23
+ Enable logging to a file. Defaults to True. Use --no-log-to-file to disable.
24
+ --log-file-name LOG_FILE_NAME
25
+ Name of the log file. Defaults to 'codeplain.log'.Always resolved relative to the plain file directory.If file on this path already exists, it will be overwritten by the current logs.
26
+ --render-range RENDER_RANGE
27
+ Specify a range of functional requirements to render (e.g. '1.1,2.3'). Use comma to separate start and end IDs. If only one ID is provided, only that requirement is rendered. Range is inclusive of both start and end IDs.
28
+ --render-from RENDER_FROM
29
+ Continue generation starting from this specific functional requirement (e.g. '2.1'). The requirement with this ID will be included in the output. The ID must match one of the functional requirements in your plain file.
30
+ --force-render Force re-render of all the required modules.
31
+ --unittests-script UNITTESTS_SCRIPT
32
+ Shell script to run unit tests on generated code. Receives the build folder path as its first argument (default: 'plain_modules').
33
+ --conformance-tests-folder CONFORMANCE_TESTS_FOLDER
34
+ Folder for conformance test files
35
+ --conformance-tests-script CONFORMANCE_TESTS_SCRIPT
36
+ Path to conformance tests shell script. The script should accept two arguments: 1) First argument: path to a folder (e.g. 'plain_modules/module_name') containing generated source code, 2) Second argument: path to a subfolder of the conformance
37
+ tests folder (e.g. 'conformance_tests/subfoldername') containing test files.
38
+ --prepare-environment-script PREPARE_ENVIRONMENT_SCRIPT
39
+ Path to a shell script that prepares the testing environment. The script should accept the build folder path as its first argument (default: 'plain_modules').
40
+ --api [API] Alternative base URL for the API. Default: `https://api.codeplain.ai`
41
+ --api-key API_KEY API key used to access the API. If not provided, the CODEPLAIN_API_KEY environment variable is used.
42
+ --full-plain Display the complete plain specification before code generation. This shows your plain file with any included template content expanded. Useful for understanding what content is being processed.
43
+ --dry-run Preview of what Codeplain would do without actually making any changes.
44
+ --replay-with REPLAY_WITH
45
+ --template-dir TEMPLATE_DIR
46
+ Path to a custom template directory. Templates are searched in the following order: 1) directory containing the plain file, 2) this custom template directory (if provided), 3) built-in standard_template_library directory
47
+ --copy-build If set, copy the build folder to `--build-dest` after every successful rendering.
48
+ --build-dest BUILD_DEST
49
+ Target folder to copy build output to (used only if --copy-build is set).
50
+ --copy-conformance-tests
51
+ If set, copy the conformance tests folder to `--conformance-tests-dest` after every successful rendering. Requires --conformance-tests-script.
52
+ --conformance-tests-dest CONFORMANCE_TESTS_DEST
53
+ Target folder to copy conformance tests output to (used only if --copy-conformance-tests is set).
54
+ --render-machine-graph
55
+ If set, render the state machine graph.
56
+ --logging-config-path
57
+ Path to the logging configuration file.
58
+
59
+ configuration:
60
+ --config-name CONFIG_NAME
61
+ Path to the config file, defaults to config.yaml
62
+
63
+ ```
@@ -1,3 +1,3 @@
1
1
  unittests-script: ../../test_scripts/run_unittests_python.sh
2
2
  conformance-tests-script: ../../test_scripts/run_conformance_tests_python.sh
3
- verbose: true # verbose flag, defaults to false if not set
3
+ verbose: true
@@ -1,4 +1,5 @@
1
1
  import os
2
+ from configparser import NoOptionError, NoSectionError
2
3
  from typing import Optional, Union
3
4
 
4
5
  from git import Repo
@@ -42,6 +43,24 @@ def _get_full_commit_message(message, module_name, frid, render_id) -> str:
42
43
  return full_message
43
44
 
44
45
 
46
+ def _ensure_git_config(repo: Repo) -> None:
47
+ config = repo.config_reader()
48
+
49
+ try:
50
+ config.get_value("user", "name")
51
+ except (NoSectionError, NoOptionError):
52
+ # user.name not configured, set a default at repo level
53
+ with repo.config_writer(config_level="repository") as writer:
54
+ writer.set_value("user", "name", "Codeplain")
55
+
56
+ try:
57
+ config.get_value("user", "email")
58
+ except (NoSectionError, NoOptionError):
59
+ # user.email not configured, set a default at repo level
60
+ with repo.config_writer(config_level="repository") as writer:
61
+ writer.set_value("user", "email", "codeplain@localhost")
62
+
63
+
45
64
  def init_git_repo(
46
65
  path_to_repo: Union[str, os.PathLike], module_name: Optional[str] = None, render_id: Optional[str] = None
47
66
  ) -> Repo:
@@ -56,6 +75,7 @@ def init_git_repo(
56
75
  os.makedirs(path_to_repo)
57
76
 
58
77
  repo = Repo.init(path_to_repo)
78
+ _ensure_git_config(repo)
59
79
  repo.git.commit(
60
80
  "--allow-empty", "-m", _get_full_commit_message(INITIAL_COMMIT_MESSAGE, module_name, None, render_id)
61
81
  )
@@ -13,13 +13,13 @@ NC="${NC:-\033[0m}"
13
13
  # Onboarding Step 1: Introduction to Plain
14
14
  clear
15
15
  echo ""
16
- echo -e "${GRAY}────────────────────────────────────────────${NC}"
17
- echo -e " ${YELLOW}${BOLD}quick intro to ***plain specification language${NC} - Step 1 of 5"
18
- echo -e "${GRAY}────────────────────────────────────────────${NC}"
16
+ echo -e "${GRAY}─────────────────────────────────────────────────────────${NC}"
17
+ echo -e " ${YELLOW}${BOLD}***plain specification language intro${NC} - Step 1 of 5"
18
+ echo -e "${GRAY}─────────────────────────────────────────────────────────${NC}"
19
19
  echo ""
20
- echo -e " ***plain is a language of spec-driven development that allows developers to express intent on any level of detail."
20
+ echo -e " ***plain is a language of spec-driven development that allows developers to express intent at any level of detail."
21
21
  echo ""
22
- echo -e " write specs in ${YELLOW}plain English${NC}, in markdown with additional syntax"
22
+ echo -e " write specs in natural language extended with additional syntax based on markdown."
23
23
  echo ""
24
24
  echo -e " render production-ready code with *codeplain."
25
25
  echo ""
@@ -28,8 +28,8 @@ echo ""
28
28
  echo -e "${GRAY} ┌────────────────────────────────────────────────────────┐${NC}"
29
29
  echo -e "${GRAY} │${NC} ${GRAY}│${NC}"
30
30
  echo -e "${GRAY} │${NC} ${YELLOW}***definitions***${NC} - key concepts in your app ${GRAY}│${NC}"
31
- echo -e "${GRAY} │${NC} ${YELLOW}***implementation reqs***${NC} - implementation details ${GRAY}│${NC}"
32
- echo -e "${GRAY} │${NC} ${YELLOW}***test reqs***${NC} - testing requirements ${GRAY}│${NC}"
31
+ echo -e "${GRAY} │${NC} ${YELLOW}***implementation reqs***${NC} - implementation details ${GRAY}│${NC}"
32
+ echo -e "${GRAY} │${NC} ${YELLOW}***test reqs***${NC} - testing requirements ${GRAY}│${NC}"
33
33
  echo -e "${GRAY} │${NC} ${YELLOW}***functional specs***${NC} - what the app should do ${GRAY}│${NC}"
34
34
  echo -e "${GRAY} │${NC} ${GRAY}│${NC}"
35
35
  echo -e "${GRAY} └────────────────────────────────────────────────────────┘${NC}"
@@ -41,33 +41,32 @@ read -r -p " press [Enter] to continue..." < /dev/tty
41
41
  # Onboarding Step 2: Functional Specification
42
42
  clear
43
43
  echo ""
44
- echo -e "${GRAY}────────────────────────────────────────────${NC}"
45
- echo -e " ${YELLOW}${BOLD}Plain Language 101${NC} - Step 2 of 5"
46
- echo -e "${GRAY}────────────────────────────────────────────${NC}"
44
+ echo -e "${GRAY}─────────────────────────────────────────────────────────${NC}"
45
+ echo -e " ${YELLOW}${BOLD}***plain specification language intro${NC} - Step 2 of 5"
46
+ echo -e "${GRAY}─────────────────────────────────────────────────────────${NC}"
47
47
  echo ""
48
48
  echo -e " ${WHITE}${BOLD}FUNCTIONAL SPECS${NC} - what should the app do?"
49
49
  echo ""
50
50
  echo -e " This is where you describe ${GREEN}what your app should do${NC},"
51
- echo -e " written in plain English. No code, just requirements."
51
+ echo -e " written in natural language. No code, just requirements."
52
52
  echo ""
53
53
  echo -e "${GRAY} ┌────────────────────────────────────────────────────────┐${NC}"
54
54
  echo -e "${GRAY} │${NC} ${GRAY}│${NC}"
55
- echo -e "${GRAY} │${NC} ${GRAY}***definitions***${NC} ${GRAY}│${NC}"
55
+ echo -e "${GRAY} │${NC} ${GRAY}***definitions***${NC} ${GRAY}│${NC}"
56
56
  echo -e "${GRAY} │${NC} ${GRAY}│${NC}"
57
- echo -e "${GRAY} │${NC} ${GRAY}- :App: is a console application.${NC} ${GRAY}│${NC}"
57
+ echo -e "${GRAY} │${NC} ${GRAY}- :App: is a console application.${NC} ${GRAY}│${NC}"
58
58
  echo -e "${GRAY} │${NC} ${GRAY}│${NC}"
59
- echo -e "${GRAY} │${NC} ${GRAY}***implementation reqs***${NC} ${GRAY}│${NC}"
59
+ echo -e "${GRAY} │${NC} ${GRAY}***implementation reqs***${NC} ${GRAY}│${NC}"
60
60
  echo -e "${GRAY} │${NC} ${GRAY}│${NC}"
61
- echo -e "${GRAY} │${NC} ${GRAY}- :Implementation: should be in Python.${NC} ${GRAY}│${NC}"
62
- echo -e "${GRAY} │${NC} ${GRAY}- :UnitTests: should use Unittest framework.${NC} ${GRAY}│${NC}"
61
+ echo -e "${GRAY} │${NC} ${GRAY}- :Implementation: should be in Python.${NC} ${GRAY}│${NC}"
63
62
  echo -e "${GRAY} │${NC} ${GRAY}│${NC}"
64
- echo -e "${GRAY} │${NC} ${GRAY}***test reqs***${NC} ${GRAY}│${NC}"
63
+ echo -e "${GRAY} │${NC} ${GRAY}***test reqs***${NC} ${GRAY}│${NC}"
65
64
  echo -e "${GRAY} │${NC} ${GRAY}│${NC}"
66
- echo -e "${GRAY} │${NC} ${GRAY}- :ConformanceTests: should use Unittest.${NC} ${GRAY}│${NC}"
65
+ echo -e "${GRAY} │${NC} ${GRAY}- :ConformanceTests: should use pytest.${NC} ${GRAY}│${NC}"
67
66
  echo -e "${GRAY} │${NC} ${GRAY}│${NC}"
68
- echo -e "${GRAY} │${NC} ${YELLOW}${BOLD}***functional specs***${NC} ${GRAY}│${NC}"
67
+ echo -e "${GRAY} │${NC} ${YELLOW}${BOLD}***functional specs***${NC} ${GRAY}│${NC}"
69
68
  echo -e "${GRAY} │${NC} ${GRAY}│${NC}"
70
- echo -e "${GRAY} │${NC} ${GREEN}${BOLD}- :App: should display \"hello, world\".${NC} ${GRAY}│${NC}"
69
+ echo -e "${GRAY} │${NC} ${GREEN}${BOLD}- :App: should display \"hello, world\".${NC} ${GRAY}│${NC}"
71
70
  echo -e "${GRAY} │${NC} ${GRAY}│${NC}"
72
71
  echo -e "${GRAY} └────────────────────────────────────────────────────────┘${NC}"
73
72
  echo ""
@@ -79,37 +78,36 @@ read -r -p " press [Enter] to continue..." < /dev/tty
79
78
  # Onboarding Step 3: Definitions
80
79
  clear
81
80
  echo ""
82
- echo -e "${GRAY}────────────────────────────────────────────${NC}"
83
- echo -e " ${YELLOW}${BOLD}Plain Language 101${NC} - Step 3 of 5"
84
- echo -e "${GRAY}────────────────────────────────────────────${NC}"
81
+ echo -e "${GRAY}─────────────────────────────────────────────────────────${NC}"
82
+ echo -e " ${YELLOW}${BOLD}***plain specification language intro${NC} - Step 3 of 5"
83
+ echo -e "${GRAY}─────────────────────────────────────────────────────────${NC}"
85
84
  echo ""
86
- echo -e " ${WHITE}${BOLD}DEFINITIONS${NC} - identify key concepts"
85
+ echo -e " ${WHITE}${BOLD}DEFINITIONS${NC} - definitions and descriptions of key concepts"
87
86
  echo ""
88
- echo -e " Define ${GREEN}reusable concepts${NC} with the ${YELLOW}:ConceptName:${NC} syntax."
87
+ echo -e " Define ${GREEN}reusable concepts${NC} using the ${YELLOW}:Concept:${NC} notation."
89
88
  echo -e " These become building blocks you can reference anywhere."
90
89
  echo ""
91
90
  echo -e "${GRAY} ┌────────────────────────────────────────────────────────┐${NC}"
92
91
  echo -e "${GRAY} │${NC} ${GRAY}│${NC}"
93
- echo -e "${GRAY} │${NC} ${YELLOW}${BOLD}***definitions***${NC} ${GRAY}│${NC}"
92
+ echo -e "${GRAY} │${NC} ${YELLOW}${BOLD}***definitions***${NC} ${GRAY}│${NC}"
94
93
  echo -e "${GRAY} │${NC} ${GRAY}│${NC}"
95
- echo -e "${GRAY} │${NC} ${GREEN}${BOLD}- :App: is a console application.${NC} ${GRAY}│${NC}"
94
+ echo -e "${GRAY} │${NC} ${GREEN}${BOLD}- :App: is a console application.${NC} ${GRAY}│${NC}"
96
95
  echo -e "${GRAY} │${NC} ${GRAY}│${NC}"
97
- echo -e "${GRAY} │${NC} ${GRAY}***implementation reqs***${NC} ${GRAY}│${NC}"
96
+ echo -e "${GRAY} │${NC} ${GRAY}***implementation reqs***${NC} ${GRAY}│${NC}"
98
97
  echo -e "${GRAY} │${NC} ${GRAY}│${NC}"
99
- echo -e "${GRAY} │${NC} ${GRAY}- :Implementation: should be in Python.${NC} ${GRAY}│${NC}"
100
- echo -e "${GRAY} │${NC} ${GRAY}- :UnitTests: should use Unittest framework.${NC} ${GRAY}│${NC}"
98
+ echo -e "${GRAY} │${NC} ${GRAY}- :Implementation: should be in Python.${NC} ${GRAY}│${NC}"
101
99
  echo -e "${GRAY} │${NC} ${GRAY}│${NC}"
102
- echo -e "${GRAY} │${NC} ${GRAY}***test reqs***${NC} ${GRAY}│${NC}"
100
+ echo -e "${GRAY} │${NC} ${GRAY}***test reqs***${NC} ${GRAY}│${NC}"
103
101
  echo -e "${GRAY} │${NC} ${GRAY}│${NC}"
104
- echo -e "${GRAY} │${NC} ${GRAY}- :ConformanceTests: should use Unittest.${NC} ${GRAY}│${NC}"
102
+ echo -e "${GRAY} │${NC} ${GRAY}- :ConformanceTests: should use pytest.${NC} ${GRAY}│${NC}"
105
103
  echo -e "${GRAY} │${NC} ${GRAY}│${NC}"
106
- echo -e "${GRAY} │${NC} ${GRAY}***functional specs***${NC} ${GRAY}│${NC}"
104
+ echo -e "${GRAY} │${NC} ${GRAY}***functional specs***${NC} ${GRAY}│${NC}"
107
105
  echo -e "${GRAY} │${NC} ${GRAY}│${NC}"
108
- echo -e "${GRAY} │${NC} ${GRAY}- :App: should display \"hello, world\".${NC} ${GRAY}│${NC}"
106
+ echo -e "${GRAY} │${NC} ${GRAY}- :App: should display \"hello, world\".${NC} ${GRAY}│${NC}"
109
107
  echo -e "${GRAY} │${NC} ${GRAY}│${NC}"
110
108
  echo -e "${GRAY} └────────────────────────────────────────────────────────┘${NC}"
111
109
  echo ""
112
- echo -e " ${GREEN}▲${NC} The ${YELLOW}:App:${NC} concept is defined once and used throughout."
110
+ echo -e " ${GREEN}▲${NC} The ${YELLOW}:App:${NC} concept is defined once and used throughout the specs."
113
111
  echo -e " Concepts help keep your specs consistent and clear."
114
112
  echo ""
115
113
  read -r -p " press [Enter] to continue..." < /dev/tty
@@ -117,9 +115,9 @@ read -r -p " press [Enter] to continue..." < /dev/tty
117
115
  # Onboarding Step 4: Implementation & Test Reqs
118
116
  clear
119
117
  echo ""
120
- echo -e "${GRAY}────────────────────────────────────────────${NC}"
121
- echo -e " ${YELLOW}${BOLD}Plain Language 101${NC} - Step 4 of 5"
122
- echo -e "${GRAY}────────────────────────────────────────────${NC}"
118
+ echo -e "${GRAY}─────────────────────────────────────────────────────────${NC}"
119
+ echo -e " ${YELLOW}${BOLD}***plain specification language intro${NC} - Step 4 of 5"
120
+ echo -e "${GRAY}─────────────────────────────────────────────────────────${NC}"
123
121
  echo ""
124
122
  echo -e " ${WHITE}${BOLD}IMPLEMENTATION & TEST REQS${NC} - how to implement and test"
125
123
  echo ""
@@ -128,22 +126,21 @@ echo -e " This guides how the code should be generated and verified."
128
126
  echo ""
129
127
  echo -e "${GRAY} ┌────────────────────────────────────────────────────────┐${NC}"
130
128
  echo -e "${GRAY} │${NC} ${GRAY}│${NC}"
131
- echo -e "${GRAY} │${NC} ${GRAY}***definitions***${NC} ${GRAY}│${NC}"
129
+ echo -e "${GRAY} │${NC} ${GRAY}***definitions***${NC} ${GRAY}│${NC}"
132
130
  echo -e "${GRAY} │${NC} ${GRAY}│${NC}"
133
- echo -e "${GRAY} │${NC} ${GRAY}- :App: is a console application.${NC} ${GRAY}│${NC}"
131
+ echo -e "${GRAY} │${NC} ${GRAY}- :App: is a console application.${NC} ${GRAY}│${NC}"
134
132
  echo -e "${GRAY} │${NC} ${GRAY}│${NC}"
135
- echo -e "${GRAY} │${NC} ${YELLOW}${BOLD}***implementation reqs***${NC} ${GRAY}│${NC}"
133
+ echo -e "${GRAY} │${NC} ${YELLOW}${BOLD}***implementation reqs***${NC} ${GRAY}│${NC}"
136
134
  echo -e "${GRAY} │${NC} ${GRAY}│${NC}"
137
- echo -e "${GRAY} │${NC} ${GREEN}${BOLD}- :Implementation: should be in Python.${NC} ${GRAY}│${NC}"
138
- echo -e "${GRAY} │${NC} ${GREEN}${BOLD}- :UnitTests: should use Unittest framework.${NC} ${GRAY}│${NC}"
135
+ echo -e "${GRAY} │${NC} ${GREEN}${BOLD}- :Implementation: should be in Python.${NC} ${GRAY}│${NC}"
139
136
  echo -e "${GRAY} │${NC} ${GRAY}│${NC}"
140
- echo -e "${GRAY} │${NC} ${YELLOW}${BOLD}***test reqs***${NC} ${GRAY}│${NC}"
137
+ echo -e "${GRAY} │${NC} ${YELLOW}${BOLD}***test reqs***${NC} ${GRAY}│${NC}"
141
138
  echo -e "${GRAY} │${NC} ${GRAY}│${NC}"
142
- echo -e "${GRAY} │${NC} ${GREEN}${BOLD}- :ConformanceTests: should use Unittest.${NC} ${GRAY}│${NC}"
139
+ echo -e "${GRAY} │${NC} ${GREEN}${BOLD}- :ConformanceTests: should use pytest.${NC} ${GRAY}│${NC}"
143
140
  echo -e "${GRAY} │${NC} ${GRAY}│${NC}"
144
- echo -e "${GRAY} │${NC} ${GRAY}***functional specs***${NC} ${GRAY}│${NC}"
141
+ echo -e "${GRAY} │${NC} ${GRAY}***functional specs***${NC} ${GRAY}│${NC}"
145
142
  echo -e "${GRAY} │${NC} ${GRAY}│${NC}"
146
- echo -e "${GRAY} │${NC} ${GRAY}- :App: should display \"hello, world\".${NC} ${GRAY}│${NC}"
143
+ echo -e "${GRAY} │${NC} ${GRAY}- :App: should display \"hello, world\".${NC} ${GRAY}│${NC}"
147
144
  echo -e "${GRAY} │${NC} ${GRAY}│${NC}"
148
145
  echo -e "${GRAY} └────────────────────────────────────────────────────────┘${NC}"
149
146
  echo ""
@@ -155,9 +152,9 @@ read -r -p " press [Enter] to continue..." < /dev/tty
155
152
  # Onboarding Step 5: Rendering Code
156
153
  clear
157
154
  echo ""
158
- echo -e "${GRAY}────────────────────────────────────────────${NC}"
159
- echo -e " ${YELLOW}${BOLD}Plain Language 101${NC} - Step 5 of 5"
160
- echo -e "${GRAY}────────────────────────────────────────────${NC}"
155
+ echo -e "${GRAY}─────────────────────────────────────────────────────────${NC}"
156
+ echo -e " ${YELLOW}${BOLD}***plain specification language intro${NC} - Step 5 of 5"
157
+ echo -e "${GRAY}─────────────────────────────────────────────────────────${NC}"
161
158
  echo ""
162
159
  echo -e " ${WHITE}${BOLD}RENDERING CODE${NC} - generate your app"
163
160
  echo ""
@@ -260,4 +260,6 @@ class ModuleRenderer:
260
260
  self.loaded_modules = list[PlainModule]()
261
261
  _, _, rendering_failed = self._render_module(self.filename, self.render_range, True)
262
262
  if not rendering_failed:
263
- self.event_bus.publish(RenderCompleted())
263
+ # Get the last module that completed rendering
264
+ last_module_name = self.loaded_modules[-1].name if self.loaded_modules else ""
265
+ self.event_bus.publish(RenderCompleted(module_name=last_module_name, build_folder=self.args.build_folder))
@@ -3,6 +3,7 @@ import logging
3
3
  import logging.config
4
4
  import os
5
5
  import sys
6
+ from pathlib import Path
6
7
  from typing import Optional
7
8
 
8
9
  import yaml
@@ -20,6 +21,7 @@ from plain2code_console import console
20
21
  from plain2code_exceptions import (
21
22
  ConflictingRequirements,
22
23
  CreditBalanceTooLow,
24
+ InternalClientError,
23
25
  InternalServerError,
24
26
  InvalidAPIKey,
25
27
  InvalidFridArgument,
@@ -27,9 +29,10 @@ from plain2code_exceptions import (
27
29
  MissingAPIKey,
28
30
  MissingPreviousFunctionalitiesError,
29
31
  MissingResource,
32
+ ModuleDoesNotExistError,
33
+ NetworkConnectionError,
30
34
  OutdatedClientVersion,
31
35
  PlainSyntaxError,
32
- UnexpectedState,
33
36
  )
34
37
  from plain2code_logger import (
35
38
  CrashLogHandler,
@@ -40,6 +43,7 @@ from plain2code_logger import (
40
43
  get_log_file_path,
41
44
  )
42
45
  from plain2code_state import RunState
46
+ from plain2code_utils import print_dry_run_output
43
47
  from system_config import system_config
44
48
  from tui.plain2code_tui import Plain2CodeTUI
45
49
 
@@ -70,6 +74,23 @@ def get_render_range_from(start, plain_source):
70
74
  return _get_frids_range(plain_source, start)
71
75
 
72
76
 
77
+ def compute_render_range(args, plain_source_tree):
78
+ """Compute render range from --render-range or --render-from arguments.
79
+
80
+ Args:
81
+ args: Parsed command line arguments
82
+ plain_source_tree: Parsed plain source tree
83
+
84
+ Returns:
85
+ List of FRIDs to render, or None to render all
86
+ """
87
+ if args.render_range:
88
+ return get_render_range(args.render_range, plain_source_tree)
89
+ elif args.render_from:
90
+ return get_render_range_from(args.render_from, plain_source_tree)
91
+ return None
92
+
93
+
73
94
  def _get_frids_range(plain_source, start, end=None):
74
95
  frids = list(plain_spec.get_frids(plain_source))
75
96
 
@@ -165,7 +186,7 @@ def setup_logging(
165
186
  root_logger.info(f"Render ID: {render_id}") # Ensure render ID is logged in to codeplain.log file
166
187
 
167
188
 
168
- def _check_connection(codeplainAPI):
189
+ def _check_connection(codeplainAPI: codeplain_api.CodeplainAPI):
169
190
  """Check API connectivity and validate API key and client version."""
170
191
  response = codeplainAPI.connection_check(system_config.client_version)
171
192
 
@@ -184,7 +205,7 @@ def _check_connection(codeplainAPI):
184
205
  )
185
206
 
186
207
 
187
- def render(args, run_state: RunState, codeplain_api, event_bus: EventBus): # noqa: C901
208
+ def render(args, run_state: RunState, event_bus: EventBus): # noqa: C901
188
209
  template_dirs = file_utils.get_template_directories(args.filename, args.template_dir, DEFAULT_TEMPLATE_DIRS)
189
210
 
190
211
  console.info(f"Rendering {args.filename} to target code.")
@@ -194,11 +215,7 @@ def render(args, run_state: RunState, codeplain_api, event_bus: EventBus): # no
194
215
  if args.render_range or args.render_from:
195
216
  # Parse the plain file to get the plain_source for FRID extraction
196
217
  _, plain_source, _ = plain_file.plain_file_parser(args.filename, template_dirs)
197
-
198
- if args.render_range:
199
- render_range = get_render_range(args.render_range, plain_source)
200
- elif args.render_from:
201
- render_range = get_render_range_from(args.render_from, plain_source)
218
+ render_range = compute_render_range(args, plain_source)
202
219
 
203
220
  codeplainAPI = codeplain_api.CodeplainAPI(args.api_key, console)
204
221
  codeplainAPI.verbose = args.verbose
@@ -240,6 +257,29 @@ def render(args, run_state: RunState, codeplain_api, event_bus: EventBus): # no
240
257
  def main(): # noqa: C901
241
258
  args = parse_arguments()
242
259
 
260
+ # Handle early-exit flags before heavy initialization
261
+ if args.dry_run or args.full_plain:
262
+ template_dirs = file_utils.get_template_directories(args.filename, args.template_dir, DEFAULT_TEMPLATE_DIRS)
263
+
264
+ try:
265
+ if args.full_plain:
266
+ module_name = Path(args.filename).stem
267
+ plain_source = plain_file.read_module_plain_source(module_name, template_dirs)
268
+ [full_plain_source, _] = file_utils.get_loaded_templates(template_dirs, plain_source)
269
+ console.info("Full plain text:\n")
270
+ console.info(full_plain_source)
271
+ return
272
+
273
+ if args.dry_run:
274
+ console.info("Printing dry run output...\n")
275
+ _, plain_source_tree, _ = plain_file.plain_file_parser(args.filename, template_dirs)
276
+ render_range = compute_render_range(args, plain_source_tree)
277
+ print_dry_run_output(plain_source_tree, render_range)
278
+ return
279
+ except Exception as e:
280
+ console.error(f"Error: {str(e)}")
281
+ return
282
+
243
283
  event_bus = EventBus()
244
284
 
245
285
  if not args.api:
@@ -258,7 +298,7 @@ def main(): # noqa: C901
258
298
  )
259
299
 
260
300
  console.debug(f"Render ID: {run_state.render_id}") # Ensure render ID is logged to the console
261
- render(args, run_state, codeplain_api, event_bus)
301
+ render(args, run_state, event_bus)
262
302
  except InvalidFridArgument as e:
263
303
  exc_info = sys.exc_info()
264
304
  console.error(f"Invalid FRID argument: {str(e)}.\n")
@@ -291,7 +331,7 @@ def main(): # noqa: C901
291
331
  console.error(f"Invalid API key: {str(e)}\n")
292
332
  except OutdatedClientVersion as e:
293
333
  console.error(f"Outdated client version: {str(e)}\n")
294
- except (InternalServerError, UnexpectedState):
334
+ except (InternalServerError, InternalClientError):
295
335
  exc_info = sys.exc_info()
296
336
  console.error(
297
337
  f"Internal server error.\n\nPlease report the error to support@codeplain.ai with the attached {args.log_file_name} file."
@@ -313,6 +353,14 @@ def main(): # noqa: C901
313
353
  exc_info = sys.exc_info()
314
354
  console.error(f"Missing resource: {str(e)}\n")
315
355
  console.debug(f"Render ID: {run_state.render_id}")
356
+ except NetworkConnectionError as e:
357
+ exc_info = sys.exc_info()
358
+ console.error(f"Connection error: {str(e)}\n")
359
+ console.error("Please check that your internet connection is working.")
360
+ except ModuleDoesNotExistError as e:
361
+ exc_info = sys.exc_info()
362
+ console.error(f"Module does not exist: {str(e)}\n")
363
+ console.debug(f"Render ID: {run_state.render_id}")
316
364
  except Exception as e:
317
365
  exc_info = sys.exc_info()
318
366
  console.error(f"Error rendering plain code: {str(e)}\n")