codeplain 0.3.6__tar.gz → 0.3.8__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 (256) hide show
  1. codeplain-0.3.8/.github/workflows/publish-to-pypi.yml +70 -0
  2. {codeplain-0.3.6 → codeplain-0.3.8}/PKG-INFO +7 -7
  3. {codeplain-0.3.6 → codeplain-0.3.8}/cli_output/render_summary.py +2 -2
  4. {codeplain-0.3.6 → codeplain-0.3.8}/cli_output/status.py +2 -2
  5. {codeplain-0.3.6 → codeplain-0.3.8}/codeplain_REST_api.py +6 -2
  6. codeplain-0.3.8/docs/plain2code_cli.md +133 -0
  7. {codeplain-0.3.6 → codeplain-0.3.8}/file_utils.py +1 -1
  8. {codeplain-0.3.6 → codeplain-0.3.8}/install/bash/examples.sh +5 -11
  9. {codeplain-0.3.6 → codeplain-0.3.8}/install/bash/install.sh +3 -1
  10. {codeplain-0.3.6 → codeplain-0.3.8}/install/powershell/examples.ps1 +5 -11
  11. {codeplain-0.3.6 → codeplain-0.3.8}/install/powershell/install.ps1 +5 -1
  12. {codeplain-0.3.6 → codeplain-0.3.8}/module_renderer.py +3 -2
  13. codeplain-0.3.8/path_resolution.py +64 -0
  14. {codeplain-0.3.6 → codeplain-0.3.8}/plain2code.py +6 -10
  15. {codeplain-0.3.6 → codeplain-0.3.8}/plain2code_arguments.py +200 -99
  16. codeplain-0.3.8/plain2code_cli.md +78 -0
  17. {codeplain-0.3.6 → codeplain-0.3.8}/plain2code_console.py +34 -22
  18. {codeplain-0.3.6 → codeplain-0.3.8}/plain2code_events.py +1 -0
  19. {codeplain-0.3.6 → codeplain-0.3.8}/plain2code_logger.py +13 -22
  20. {codeplain-0.3.6 → codeplain-0.3.8}/plain2code_state.py +17 -1
  21. {codeplain-0.3.6 → codeplain-0.3.8}/plain2code_utils.py +17 -13
  22. {codeplain-0.3.6 → codeplain-0.3.8}/plain_file.py +39 -28
  23. {codeplain-0.3.6 → codeplain-0.3.8}/pyproject.toml +11 -8
  24. {codeplain-0.3.6 → codeplain-0.3.8}/render_machine/actions/create_dist.py +2 -2
  25. {codeplain-0.3.6 → codeplain-0.3.8}/render_machine/actions/exit_with_error.py +2 -1
  26. {codeplain-0.3.6 → codeplain-0.3.8}/render_machine/actions/fix_conformance_test.py +4 -2
  27. {codeplain-0.3.6 → codeplain-0.3.8}/render_machine/actions/render_functional_requirement.py +4 -3
  28. {codeplain-0.3.6 → codeplain-0.3.8}/render_machine/render_context.py +5 -3
  29. {codeplain-0.3.6 → codeplain-0.3.8}/render_machine/render_utils.py +14 -8
  30. {codeplain-0.3.6 → codeplain-0.3.8}/requirements.txt +6 -6
  31. codeplain-0.3.8/system_config.py +68 -0
  32. {codeplain-0.3.6 → codeplain-0.3.8}/test_scripts/run_conformance_tests_cypress.ps1 +30 -19
  33. {codeplain-0.3.6 → codeplain-0.3.8}/test_scripts/run_conformance_tests_cypress.sh +6 -2
  34. {codeplain-0.3.6 → codeplain-0.3.8}/test_scripts/run_conformance_tests_golang.ps1 +14 -5
  35. {codeplain-0.3.6 → codeplain-0.3.8}/test_scripts/run_conformance_tests_golang.sh +6 -6
  36. {codeplain-0.3.6 → codeplain-0.3.8}/test_scripts/run_conformance_tests_python.ps1 +11 -2
  37. {codeplain-0.3.6 → codeplain-0.3.8}/test_scripts/run_conformance_tests_python.sh +3 -3
  38. {codeplain-0.3.6 → codeplain-0.3.8}/test_scripts/run_unittests_flutter.ps1 +4 -1
  39. codeplain-0.3.8/test_scripts/run_unittests_flutter.sh +55 -0
  40. {codeplain-0.3.6 → codeplain-0.3.8}/test_scripts/run_unittests_golang.ps1 +4 -1
  41. {codeplain-0.3.6 → codeplain-0.3.8}/test_scripts/run_unittests_golang.sh +3 -1
  42. {codeplain-0.3.6 → codeplain-0.3.8}/test_scripts/run_unittests_python.ps1 +4 -1
  43. {codeplain-0.3.6 → codeplain-0.3.8}/test_scripts/run_unittests_python.sh +3 -1
  44. {codeplain-0.3.6 → codeplain-0.3.8}/test_scripts/run_unittests_react.ps1 +4 -1
  45. {codeplain-0.3.6 → codeplain-0.3.8}/test_scripts/run_unittests_react.sh +3 -1
  46. codeplain-0.3.8/tests/test_arg_provenance.py +89 -0
  47. {codeplain-0.3.6 → codeplain-0.3.8}/tests/test_cli_output.py +2 -2
  48. codeplain-0.3.8/tests/test_console_log_styles.py +112 -0
  49. codeplain-0.3.8/tests/test_folder_path_resolution.py +165 -0
  50. codeplain-0.3.8/tests/test_log_file_name_resolution.py +89 -0
  51. codeplain-0.3.8/tests/test_logging_config_path_resolution.py +67 -0
  52. codeplain-0.3.8/tests/test_path_resolution.py +65 -0
  53. codeplain-0.3.8/tests/test_plain2code_state.py +36 -0
  54. codeplain-0.3.8/tests/test_plain2code_utils.py +36 -0
  55. {codeplain-0.3.6 → codeplain-0.3.8}/tests/test_plainfileparser.py +2 -2
  56. codeplain-0.3.8/tests/test_script_path_resolution.py +107 -0
  57. codeplain-0.3.8/tests/test_tui_usage.py +209 -0
  58. codeplain-0.3.8/tests/test_usage_summary.py +28 -0
  59. {codeplain-0.3.6 → codeplain-0.3.8}/tui/components.py +29 -20
  60. {codeplain-0.3.6 → codeplain-0.3.8}/tui/plain2code_tui.py +71 -2
  61. {codeplain-0.3.6 → codeplain-0.3.8}/tui/styles.css +4 -0
  62. {codeplain-0.3.6 → codeplain-0.3.8}/tui/widget_helpers.py +20 -1
  63. codeplain-0.3.8/usage_summary.py +30 -0
  64. codeplain-0.3.6/.github/workflows/publish-to-pypi.yml +0 -109
  65. codeplain-0.3.6/_version.py +0 -1
  66. codeplain-0.3.6/docs/plain2code_cli.md +0 -85
  67. codeplain-0.3.6/system_config.py +0 -34
  68. {codeplain-0.3.6 → codeplain-0.3.8}/.claude/settings.json +0 -0
  69. {codeplain-0.3.6 → codeplain-0.3.8}/.flake8 +0 -0
  70. {codeplain-0.3.6 → codeplain-0.3.8}/.github/dependabot.yml +0 -0
  71. {codeplain-0.3.6 → codeplain-0.3.8}/.github/workflows/e2e.yml +0 -0
  72. {codeplain-0.3.6 → codeplain-0.3.8}/.github/workflows/lint-and-test.yml +0 -0
  73. {codeplain-0.3.6 → codeplain-0.3.8}/.github/workflows/nofity-slack-on-main-merge.yml +0 -0
  74. {codeplain-0.3.6 → codeplain-0.3.8}/.github/workflows/publish-install-script.yml +0 -0
  75. {codeplain-0.3.6 → codeplain-0.3.8}/.gitignore +0 -0
  76. {codeplain-0.3.6 → codeplain-0.3.8}/.vscode/launch.json +0 -0
  77. {codeplain-0.3.6 → codeplain-0.3.8}/.vscode/settings.json +0 -0
  78. {codeplain-0.3.6 → codeplain-0.3.8}/CLAUDE.md +0 -0
  79. {codeplain-0.3.6 → codeplain-0.3.8}/LICENSE +0 -0
  80. {codeplain-0.3.6 → codeplain-0.3.8}/README.md +0 -0
  81. {codeplain-0.3.6 → codeplain-0.3.8}/change_detection.py +0 -0
  82. {codeplain-0.3.6 → codeplain-0.3.8}/cli_output/__init__.py +0 -0
  83. {codeplain-0.3.6 → codeplain-0.3.8}/cli_output/dry_run.py +0 -0
  84. {codeplain-0.3.6 → codeplain-0.3.8}/concept_utils.py +0 -0
  85. {codeplain-0.3.6 → codeplain-0.3.8}/config/__init__.py +0 -0
  86. {codeplain-0.3.6 → codeplain-0.3.8}/config/system_config.yaml +0 -0
  87. {codeplain-0.3.6 → codeplain-0.3.8}/diff_utils.py +0 -0
  88. {codeplain-0.3.6 → codeplain-0.3.8}/docs/generate_cli.py +0 -0
  89. {codeplain-0.3.6 → codeplain-0.3.8}/docs/starting_a_plain_project_from_scratch.md +0 -0
  90. {codeplain-0.3.6 → codeplain-0.3.8}/event_bus.py +0 -0
  91. {codeplain-0.3.6 → codeplain-0.3.8}/examples/example_hello_world_golang/config.yaml +0 -0
  92. {codeplain-0.3.6 → codeplain-0.3.8}/examples/example_hello_world_golang/harness_tests/hello_world_test.go +0 -0
  93. {codeplain-0.3.6 → codeplain-0.3.8}/examples/example_hello_world_golang/hello_world_golang.plain +0 -0
  94. {codeplain-0.3.6 → codeplain-0.3.8}/examples/example_hello_world_golang/run.sh +0 -0
  95. {codeplain-0.3.6 → codeplain-0.3.8}/examples/example_hello_world_python/config.yaml +0 -0
  96. {codeplain-0.3.6 → codeplain-0.3.8}/examples/example_hello_world_python/harness_tests/hello_world_display/test_hello_world.py +0 -0
  97. {codeplain-0.3.6 → codeplain-0.3.8}/examples/example_hello_world_python/hello_world_python.plain +0 -0
  98. {codeplain-0.3.6 → codeplain-0.3.8}/examples/example_hello_world_python/run.sh +0 -0
  99. {codeplain-0.3.6 → codeplain-0.3.8}/examples/example_hello_world_react/config.yaml +0 -0
  100. {codeplain-0.3.6 → codeplain-0.3.8}/examples/example_hello_world_react/harness_tests/hello_world_display/cypress/e2e/hello_world.cy.ts +0 -0
  101. {codeplain-0.3.6 → codeplain-0.3.8}/examples/example_hello_world_react/harness_tests/hello_world_display/cypress/support/e2e.ts +0 -0
  102. {codeplain-0.3.6 → codeplain-0.3.8}/examples/example_hello_world_react/harness_tests/hello_world_display/cypress.config.ts +0 -0
  103. {codeplain-0.3.6 → codeplain-0.3.8}/examples/example_hello_world_react/harness_tests/hello_world_display/package.json +0 -0
  104. {codeplain-0.3.6 → codeplain-0.3.8}/examples/example_hello_world_react/harness_tests/hello_world_display/tsconfig.json +0 -0
  105. {codeplain-0.3.6 → codeplain-0.3.8}/examples/example_hello_world_react/hello_world_react.plain +0 -0
  106. {codeplain-0.3.6 → codeplain-0.3.8}/examples/example_hello_world_react/run.sh +0 -0
  107. {codeplain-0.3.6 → codeplain-0.3.8}/examples/run.sh +0 -0
  108. {codeplain-0.3.6 → codeplain-0.3.8}/git_utils.py +0 -0
  109. {codeplain-0.3.6 → codeplain-0.3.8}/install/bash/walkthrough.sh +0 -0
  110. {codeplain-0.3.6 → codeplain-0.3.8}/install/powershell/walkthrough.ps1 +0 -0
  111. {codeplain-0.3.6 → codeplain-0.3.8}/memory_management.py +0 -0
  112. {codeplain-0.3.6 → codeplain-0.3.8}/partial_rendering.py +0 -0
  113. {codeplain-0.3.6 → codeplain-0.3.8}/plain2code_exceptions.py +0 -0
  114. {codeplain-0.3.6 → codeplain-0.3.8}/plain2code_nodes.py +0 -0
  115. {codeplain-0.3.6 → codeplain-0.3.8}/plain2code_read_config.py +0 -0
  116. {codeplain-0.3.6 → codeplain-0.3.8}/plain2code_telemetry.py +0 -0
  117. {codeplain-0.3.6 → codeplain-0.3.8}/plain_modules.py +0 -0
  118. {codeplain-0.3.6 → codeplain-0.3.8}/plain_spec.py +0 -0
  119. {codeplain-0.3.6 → codeplain-0.3.8}/pytest.ini +0 -0
  120. {codeplain-0.3.6 → codeplain-0.3.8}/render_machine/__init__.py +0 -0
  121. {codeplain-0.3.6 → codeplain-0.3.8}/render_machine/actions/analyze_specification_ambiguity.py +0 -0
  122. {codeplain-0.3.6 → codeplain-0.3.8}/render_machine/actions/base_action.py +0 -0
  123. {codeplain-0.3.6 → codeplain-0.3.8}/render_machine/actions/commit_conformance_tests_changes.py +0 -0
  124. {codeplain-0.3.6 → codeplain-0.3.8}/render_machine/actions/commit_implementation_code_changes.py +0 -0
  125. {codeplain-0.3.6 → codeplain-0.3.8}/render_machine/actions/finish_functional_requirement.py +0 -0
  126. {codeplain-0.3.6 → codeplain-0.3.8}/render_machine/actions/fix_unit_tests.py +0 -0
  127. {codeplain-0.3.6 → codeplain-0.3.8}/render_machine/actions/prepare_repositories.py +0 -0
  128. {codeplain-0.3.6 → codeplain-0.3.8}/render_machine/actions/prepare_testing_environment.py +0 -0
  129. {codeplain-0.3.6 → codeplain-0.3.8}/render_machine/actions/refactor_code.py +0 -0
  130. {codeplain-0.3.6 → codeplain-0.3.8}/render_machine/actions/render_conformance_tests.py +0 -0
  131. {codeplain-0.3.6 → codeplain-0.3.8}/render_machine/actions/run_conformance_tests.py +0 -0
  132. {codeplain-0.3.6 → codeplain-0.3.8}/render_machine/actions/run_unit_tests.py +0 -0
  133. {codeplain-0.3.6 → codeplain-0.3.8}/render_machine/actions/summarize_conformance_tests.py +0 -0
  134. {codeplain-0.3.6 → codeplain-0.3.8}/render_machine/code_renderer.py +0 -0
  135. {codeplain-0.3.6 → codeplain-0.3.8}/render_machine/conformance_tests.py +0 -0
  136. {codeplain-0.3.6 → codeplain-0.3.8}/render_machine/implementation_code_helpers.py +0 -0
  137. {codeplain-0.3.6 → codeplain-0.3.8}/render_machine/render_types.py +0 -0
  138. {codeplain-0.3.6 → codeplain-0.3.8}/render_machine/state_machine_config.py +0 -0
  139. {codeplain-0.3.6 → codeplain-0.3.8}/render_machine/states.py +0 -0
  140. {codeplain-0.3.6 → codeplain-0.3.8}/render_machine/triggers.py +0 -0
  141. {codeplain-0.3.6 → codeplain-0.3.8}/resources/codeplain_overview.png +0 -0
  142. {codeplain-0.3.6 → codeplain-0.3.8}/resources/plain_example.png +0 -0
  143. {codeplain-0.3.6 → codeplain-0.3.8}/standard_template_library/golang-console-app-template.plain +0 -0
  144. {codeplain-0.3.6 → codeplain-0.3.8}/standard_template_library/python-console-app-template.plain +0 -0
  145. {codeplain-0.3.6 → codeplain-0.3.8}/standard_template_library/typescript-react-app-boilerplate.plain +0 -0
  146. {codeplain-0.3.6 → codeplain-0.3.8}/standard_template_library/typescript-react-app-template.plain +0 -0
  147. {codeplain-0.3.6 → codeplain-0.3.8}/tests/__init__.py +0 -0
  148. {codeplain-0.3.6 → codeplain-0.3.8}/tests/conftest.py +0 -0
  149. {codeplain-0.3.6 → codeplain-0.3.8}/tests/data/acceptance_tests_warning/main_requiring_acceptance_tests.plain +0 -0
  150. {codeplain-0.3.6 → codeplain-0.3.8}/tests/data/acceptance_tests_warning/required_with_acceptance_tests.plain +0 -0
  151. {codeplain-0.3.6 → codeplain-0.3.8}/tests/data/imports/circular_imports_1.plain +0 -0
  152. {codeplain-0.3.6 → codeplain-0.3.8}/tests/data/imports/circular_imports_2.plain +0 -0
  153. {codeplain-0.3.6 → codeplain-0.3.8}/tests/data/imports/circular_imports_main.plain +0 -0
  154. {codeplain-0.3.6 → codeplain-0.3.8}/tests/data/imports/diamond_import_1.plain +0 -0
  155. {codeplain-0.3.6 → codeplain-0.3.8}/tests/data/imports/diamond_import_2.plain +0 -0
  156. {codeplain-0.3.6 → codeplain-0.3.8}/tests/data/imports/diamond_import_common.plain +0 -0
  157. {codeplain-0.3.6 → codeplain-0.3.8}/tests/data/imports/diamond_imports_main.plain +0 -0
  158. {codeplain-0.3.6 → codeplain-0.3.8}/tests/data/imports/import_with_functionalities.plain +0 -0
  159. {codeplain-0.3.6 → codeplain-0.3.8}/tests/data/imports/imports_module_with_functionalities.plain +0 -0
  160. {codeplain-0.3.6 → codeplain-0.3.8}/tests/data/imports/non_existent_import.plain +0 -0
  161. {codeplain-0.3.6 → codeplain-0.3.8}/tests/data/partial_rendering/pr_code_var.plain +0 -0
  162. {codeplain-0.3.6 → codeplain-0.3.8}/tests/data/partial_rendering/pr_implement.plain +0 -0
  163. {codeplain-0.3.6 → codeplain-0.3.8}/tests/data/partial_rendering/pr_leaf.plain +0 -0
  164. {codeplain-0.3.6 → codeplain-0.3.8}/tests/data/partial_rendering/pr_middle.plain +0 -0
  165. {codeplain-0.3.6 → codeplain-0.3.8}/tests/data/partial_rendering/pr_root.plain +0 -0
  166. {codeplain-0.3.6 → codeplain-0.3.8}/tests/data/partial_rendering/pr_solo.plain +0 -0
  167. {codeplain-0.3.6 → codeplain-0.3.8}/tests/data/plainfile/add_new_task_modal_specification.yaml +0 -0
  168. {codeplain-0.3.6 → codeplain-0.3.8}/tests/data/plainfile/duplicate_specification_heading.plain +0 -0
  169. {codeplain-0.3.6 → codeplain-0.3.8}/tests/data/plainfile/empty_functional_specs_section.plain +0 -0
  170. {codeplain-0.3.6 → codeplain-0.3.8}/tests/data/plainfile/invalid_specification_order.plain +0 -0
  171. {codeplain-0.3.6 → codeplain-0.3.8}/tests/data/plainfile/missing_non_functional_requirements.plain +0 -0
  172. {codeplain-0.3.6 → codeplain-0.3.8}/tests/data/plainfile/no_functional_specs_section.plain +0 -0
  173. {codeplain-0.3.6 → codeplain-0.3.8}/tests/data/plainfile/plain_source_with_absolute_link.plain +0 -0
  174. {codeplain-0.3.6 → codeplain-0.3.8}/tests/data/plainfile/plain_source_with_url_link.plain +0 -0
  175. {codeplain-0.3.6 → codeplain-0.3.8}/tests/data/plainfile/task_list_ui_specification.yaml +0 -0
  176. {codeplain-0.3.6 → codeplain-0.3.8}/tests/data/plainfile/task_manager_with_reference_links.plain +0 -0
  177. {codeplain-0.3.6 → codeplain-0.3.8}/tests/data/plainfile/without_non_functional_requirement.plain +0 -0
  178. {codeplain-0.3.6 → codeplain-0.3.8}/tests/data/plainfileparser/concept_validation_acceptance_tests.plain +0 -0
  179. {codeplain-0.3.6 → codeplain-0.3.8}/tests/data/plainfileparser/concept_validation_acceptance_tests_nondefined.plain +0 -0
  180. {codeplain-0.3.6 → codeplain-0.3.8}/tests/data/plainfileparser/concept_validation_defined_nondefined.plain +0 -0
  181. {codeplain-0.3.6 → codeplain-0.3.8}/tests/data/plainfileparser/concept_validation_defined_nondefined_2.plain +0 -0
  182. {codeplain-0.3.6 → codeplain-0.3.8}/tests/data/plainfileparser/concept_validation_definition.plain +0 -0
  183. {codeplain-0.3.6 → codeplain-0.3.8}/tests/data/plainfileparser/concept_validation_noconcepts.plain +0 -0
  184. {codeplain-0.3.6 → codeplain-0.3.8}/tests/data/plainfileparser/concept_validation_nonconcept.plain +0 -0
  185. {codeplain-0.3.6 → codeplain-0.3.8}/tests/data/plainfileparser/concept_validation_nondefined.plain +0 -0
  186. {codeplain-0.3.6 → codeplain-0.3.8}/tests/data/plainfileparser/concept_validation_redefinition.plain +0 -0
  187. {codeplain-0.3.6 → codeplain-0.3.8}/tests/data/plainfileparser/concept_validation_several_concepts.plain +0 -0
  188. {codeplain-0.3.6 → codeplain-0.3.8}/tests/data/plainfileparser/concept_validation_valid.plain +0 -0
  189. {codeplain-0.3.6 → codeplain-0.3.8}/tests/data/plainfileparser/exported_concepts.plain +0 -0
  190. {codeplain-0.3.6 → codeplain-0.3.8}/tests/data/plainfileparser/exported_concepts_base.plain +0 -0
  191. {codeplain-0.3.6 → codeplain-0.3.8}/tests/data/plainfileparser/exported_concepts_example.plain +0 -0
  192. {codeplain-0.3.6 → codeplain-0.3.8}/tests/data/plainfileparser/exported_concepts_missing.plain +0 -0
  193. {codeplain-0.3.6 → codeplain-0.3.8}/tests/data/plainfileparser/exported_concepts_missing_example.plain +0 -0
  194. {codeplain-0.3.6 → codeplain-0.3.8}/tests/data/plainfileparser/exported_concepts_nested.plain +0 -0
  195. {codeplain-0.3.6 → codeplain-0.3.8}/tests/data/plainfileparser/exported_concepts_nested_example.plain +0 -0
  196. {codeplain-0.3.6 → codeplain-0.3.8}/tests/data/plainfileparser/exported_concepts_transitive_example.plain +0 -0
  197. {codeplain-0.3.6 → codeplain-0.3.8}/tests/data/plainfileparser/exported_concepts_transitive_l1.plain +0 -0
  198. {codeplain-0.3.6 → codeplain-0.3.8}/tests/data/plainfileparser/exported_concepts_transitive_l2.plain +0 -0
  199. {codeplain-0.3.6 → codeplain-0.3.8}/tests/data/plainfileparser/plain_file_parser_with_comments.plain +0 -0
  200. {codeplain-0.3.6 → codeplain-0.3.8}/tests/data/plainfileparser/plain_file_with_comments_indented.plain +0 -0
  201. {codeplain-0.3.6 → codeplain-0.3.8}/tests/data/plainfileparser/regular_plain_source.plain +0 -0
  202. {codeplain-0.3.6 → codeplain-0.3.8}/tests/data/plainfileparser/required_concepts.plain +0 -0
  203. {codeplain-0.3.6 → codeplain-0.3.8}/tests/data/plainfileparser/required_concepts_defs.plain +0 -0
  204. {codeplain-0.3.6 → codeplain-0.3.8}/tests/data/plainfileparser/required_concepts_example.plain +0 -0
  205. {codeplain-0.3.6 → codeplain-0.3.8}/tests/data/plainfileparser/required_concepts_l1.plain +0 -0
  206. {codeplain-0.3.6 → codeplain-0.3.8}/tests/data/plainfileparser/required_concepts_l2.plain +0 -0
  207. {codeplain-0.3.6 → codeplain-0.3.8}/tests/data/plainfileparser/required_concepts_missing.plain +0 -0
  208. {codeplain-0.3.6 → codeplain-0.3.8}/tests/data/plainfileparser/required_concepts_module.plain +0 -0
  209. {codeplain-0.3.6 → codeplain-0.3.8}/tests/data/plainfileparser/required_concepts_partial.plain +0 -0
  210. {codeplain-0.3.6 → codeplain-0.3.8}/tests/data/plainfileparser/required_concepts_partial_duplicate.plain +0 -0
  211. {codeplain-0.3.6 → codeplain-0.3.8}/tests/data/plainfileparser/topological_sort.plain +0 -0
  212. {codeplain-0.3.6 → codeplain-0.3.8}/tests/data/plainfileparser/topological_sort_not_referenced.plain +0 -0
  213. {codeplain-0.3.6 → codeplain-0.3.8}/tests/data/requires/circular_requires_main.plain +0 -0
  214. {codeplain-0.3.6 → codeplain-0.3.8}/tests/data/requires/circular_requires_sub.plain +0 -0
  215. {codeplain-0.3.6 → codeplain-0.3.8}/tests/data/requires/diamond_requires_1.plain +0 -0
  216. {codeplain-0.3.6 → codeplain-0.3.8}/tests/data/requires/diamond_requires_2.plain +0 -0
  217. {codeplain-0.3.6 → codeplain-0.3.8}/tests/data/requires/diamond_requires_common.plain +0 -0
  218. {codeplain-0.3.6 → codeplain-0.3.8}/tests/data/requires/diamond_requires_main.plain +0 -0
  219. {codeplain-0.3.6 → codeplain-0.3.8}/tests/data/requires/independent_requires_1.plain +0 -0
  220. {codeplain-0.3.6 → codeplain-0.3.8}/tests/data/requires/independent_requires_2.plain +0 -0
  221. {codeplain-0.3.6 → codeplain-0.3.8}/tests/data/requires/independent_requires_main.plain +0 -0
  222. {codeplain-0.3.6 → codeplain-0.3.8}/tests/data/requires/non_existent_require.plain +0 -0
  223. {codeplain-0.3.6 → codeplain-0.3.8}/tests/data/requires/normal_requires_1.plain +0 -0
  224. {codeplain-0.3.6 → codeplain-0.3.8}/tests/data/requires/normal_requires_2.plain +0 -0
  225. {codeplain-0.3.6 → codeplain-0.3.8}/tests/data/requires/normal_requires_common.plain +0 -0
  226. {codeplain-0.3.6 → codeplain-0.3.8}/tests/data/requires/normal_requires_main.plain +0 -0
  227. {codeplain-0.3.6 → codeplain-0.3.8}/tests/data/sample_base64_image.txt +0 -0
  228. {codeplain-0.3.6 → codeplain-0.3.8}/tests/data/simple.plain +0 -0
  229. {codeplain-0.3.6 → codeplain-0.3.8}/tests/data/templates/block_level_include.plain +0 -0
  230. {codeplain-0.3.6 → codeplain-0.3.8}/tests/data/templates/code_variables.plain +0 -0
  231. {codeplain-0.3.6 → codeplain-0.3.8}/tests/data/templates/header.plain +0 -0
  232. {codeplain-0.3.6 → codeplain-0.3.8}/tests/data/templates/implement.plain +0 -0
  233. {codeplain-0.3.6 → codeplain-0.3.8}/tests/data/templates/implement_2.plain +0 -0
  234. {codeplain-0.3.6 → codeplain-0.3.8}/tests/data/templates/template_include.plain +0 -0
  235. {codeplain-0.3.6 → codeplain-0.3.8}/tests/data/templates/test_hardest_problem.plain +0 -0
  236. {codeplain-0.3.6 → codeplain-0.3.8}/tests/e2e/Dockerfile +0 -0
  237. {codeplain-0.3.6 → codeplain-0.3.8}/tests/e2e/conftest.py +0 -0
  238. {codeplain-0.3.6 → codeplain-0.3.8}/tests/e2e/test_hello_world_python.py +0 -0
  239. {codeplain-0.3.6 → codeplain-0.3.8}/tests/e2e/test_hello_world_python_windows.py +0 -0
  240. {codeplain-0.3.6 → codeplain-0.3.8}/tests/test_change_detection.py +0 -0
  241. {codeplain-0.3.6 → codeplain-0.3.8}/tests/test_file_utils.py +0 -0
  242. {codeplain-0.3.6 → codeplain-0.3.8}/tests/test_git_utils.py +0 -0
  243. {codeplain-0.3.6 → codeplain-0.3.8}/tests/test_imports.py +0 -0
  244. {codeplain-0.3.6 → codeplain-0.3.8}/tests/test_partial_rendering.py +0 -0
  245. {codeplain-0.3.6 → codeplain-0.3.8}/tests/test_plain2code.py +0 -0
  246. {codeplain-0.3.6 → codeplain-0.3.8}/tests/test_plain_modules.py +0 -0
  247. {codeplain-0.3.6 → codeplain-0.3.8}/tests/test_plainfile.py +0 -0
  248. {codeplain-0.3.6 → codeplain-0.3.8}/tests/test_plainspec.py +0 -0
  249. {codeplain-0.3.6 → codeplain-0.3.8}/tests/test_requires.py +0 -0
  250. {codeplain-0.3.6 → codeplain-0.3.8}/tests/test_resolve_config_file.py +0 -0
  251. {codeplain-0.3.6 → codeplain-0.3.8}/tests/test_telemetry.py +0 -0
  252. {codeplain-0.3.6 → codeplain-0.3.8}/tui/__init__.py +0 -0
  253. {codeplain-0.3.6 → codeplain-0.3.8}/tui/models.py +0 -0
  254. {codeplain-0.3.6 → codeplain-0.3.8}/tui/plain_module_render_choice_tui.py +0 -0
  255. {codeplain-0.3.6 → codeplain-0.3.8}/tui/spinner.py +0 -0
  256. {codeplain-0.3.6 → codeplain-0.3.8}/tui/state_handlers.py +0 -0
@@ -0,0 +1,70 @@
1
+ name: Publish to PyPI
2
+
3
+ on:
4
+ release:
5
+ types: [published]
6
+
7
+ # Least privilege by default: jobs get no GITHUB_TOKEN scopes unless they
8
+ # opt in below.
9
+ permissions: {}
10
+
11
+ jobs:
12
+ build:
13
+ name: Build distribution
14
+ runs-on: ubuntu-latest
15
+ permissions:
16
+ contents: write
17
+
18
+ steps:
19
+ - uses: actions/checkout@v4
20
+ with:
21
+ # hatch-vcs derives the version from git tags, so we need full history.
22
+ fetch-depth: 0
23
+
24
+ - name: Install uv
25
+ # uv provisions its own Python (honoring requires-python), so no
26
+ # separate actions/setup-python step is needed.
27
+ run: |
28
+ curl -LsSf https://astral.sh/uv/install.sh | sh
29
+ echo "$HOME/.local/bin" >> $GITHUB_PATH
30
+
31
+ - name: Build package
32
+ # hatch-vcs reads the version straight from the release tag.
33
+ run: |
34
+ uv build
35
+ echo "Built distributions:"
36
+ ls -l dist/
37
+
38
+ - name: Store the distribution packages
39
+ uses: actions/upload-artifact@v4
40
+ with:
41
+ name: python-package-distributions
42
+ path: dist/
43
+
44
+ - name: Upload assets to GitHub Release
45
+ env:
46
+ GH_TOKEN: ${{ github.token }}
47
+ run: |
48
+ gh release upload ${{ github.ref_name }} dist/* --clobber
49
+
50
+ publish-to-pypi:
51
+ name: Publish to PyPI
52
+ needs: build
53
+ runs-on: ubuntu-latest
54
+ environment:
55
+ name: pypi
56
+ url: https://pypi.org/p/codeplain
57
+ permissions:
58
+ # Mint a short-lived OIDC token so PyPI Trusted Publishing can
59
+ # authenticate this run — no long-lived API token needed.
60
+ id-token: write
61
+
62
+ steps:
63
+ - name: Download distribution packages
64
+ uses: actions/download-artifact@v4
65
+ with:
66
+ name: python-package-distributions
67
+ path: dist/
68
+
69
+ - name: Publish to PyPI
70
+ uses: pypa/gh-action-pypi-publish@release/v1
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: codeplain
3
- Version: 0.3.6
3
+ Version: 0.3.8
4
4
  Summary: Transform plain language specifications into working code
5
5
  License-File: LICENSE
6
6
  Classifier: Environment :: Console
@@ -8,23 +8,23 @@ Classifier: Intended Audience :: Developers
8
8
  Classifier: Operating System :: OS Independent
9
9
  Classifier: Topic :: Software Development :: Code Generators
10
10
  Requires-Python: ==3.11.*
11
- Requires-Dist: gitpython==3.1.50
12
- Requires-Dist: mistletoe==1.3.0
11
+ Requires-Dist: gitpython==3.1.52
12
+ Requires-Dist: mistletoe==1.5.1
13
13
  Requires-Dist: networkx==3.6.1
14
14
  Requires-Dist: python-frontmatter==1.3.0
15
15
  Requires-Dist: python-liquid2==0.4.0
16
16
  Requires-Dist: pyyaml==6.0.3
17
17
  Requires-Dist: requests==2.34.2
18
18
  Requires-Dist: rich==15.0.0
19
- Requires-Dist: sentry-sdk==2.63.0
19
+ Requires-Dist: sentry-sdk==2.66.0
20
20
  Requires-Dist: textual>=7.5.0
21
21
  Requires-Dist: tiktoken==0.13.0
22
22
  Requires-Dist: transitions==0.9.3
23
23
  Provides-Extra: dev
24
- Requires-Dist: black==26.3.1; extra == 'dev'
24
+ Requires-Dist: black==26.5.1; extra == 'dev'
25
25
  Requires-Dist: flake8==7.3.0; extra == 'dev'
26
- Requires-Dist: isort==5.13.2; extra == 'dev'
27
- Requires-Dist: mypy==2.1.0; extra == 'dev'
26
+ Requires-Dist: isort==8.0.1; extra == 'dev'
27
+ Requires-Dist: mypy==2.3.0; extra == 'dev'
28
28
  Requires-Dist: pytest==9.1.1; extra == 'dev'
29
29
  Description-Content-Type: text/markdown
30
30
 
@@ -4,7 +4,7 @@ from typing import Optional
4
4
 
5
5
  from plain2code_console import console
6
6
  from plain2code_state import RunState
7
- from plain2code_utils import format_duration_hms
7
+ from usage_summary import format_usage_summary
8
8
 
9
9
 
10
10
  def print_exit_summary(
@@ -24,7 +24,7 @@ def print_exit_summary(
24
24
  msg += f" [#8E8F91]render id:\t\t\t[#FFFFFF]{run_state.render_id}\n"
25
25
  msg += f" [#8E8F91]input file:\t\t\t[#FFFFFF]{spec_filename}\n"
26
26
  msg += f" [#8E8F91]generated code folder:\t[#FFFFFF]{run_state.render_generated_code_path or '-'}\n\n"
27
- msg += f"[#8E8F91]functionalities [#FFFFFF]{run_state.rendered_functionalities} [#8E8F91]used credits [#FFFFFF]{run_state.rendered_functionalities} [#8E8F91]render time [#FFFFFF]{format_duration_hms(run_state.render_time_accumulated)}\n"
27
+ msg += format_usage_summary(run_state.rendered_functionalities, run_state.render_time_accumulated) + "\n"
28
28
  console.print(msg)
29
29
 
30
30
  if not run_state.render_succeeded and error_message:
@@ -32,7 +32,7 @@ def _display_credit_line(plan_credits: dict) -> None:
32
32
  # If naive datetime, assume UTC
33
33
  if dt.tzinfo is None:
34
34
  dt = dt.replace(tzinfo=timezone.utc)
35
- formatted_date = dt.strftime("%b %-d, %Y")
35
+ formatted_date = f"{dt.strftime('%b')} {dt.day}, {dt.year}"
36
36
 
37
37
  # Check if expired
38
38
  now = datetime.now(timezone.utc)
@@ -62,7 +62,7 @@ def _display_bucket_credit_line(bucket: dict, label: str) -> None:
62
62
  # If naive datetime, assume UTC
63
63
  if dt.tzinfo is None:
64
64
  dt = dt.replace(tzinfo=timezone.utc)
65
- formatted_date = dt.strftime("%b %-d, %Y")
65
+ formatted_date = f"{dt.strftime('%b')} {dt.day}, {dt.year}"
66
66
 
67
67
  # Check if expired
68
68
  now = datetime.now(timezone.utc)
@@ -5,6 +5,7 @@ import requests
5
5
  from requests.exceptions import ConnectionError, RequestException, Timeout
6
6
 
7
7
  import plain2code_exceptions
8
+ from plain2code_console import RETRY_COLOR
8
9
  from plain2code_state import RunState
9
10
 
10
11
  MAX_RETRIES = 4
@@ -67,8 +68,11 @@ class CodeplainAPI:
67
68
  connection_error_type = "Network error" if is_connection_error else "Error"
68
69
  if attempt < num_retries:
69
70
  if not silent:
70
- self.console.debug(f"{connection_error_type} on attempt {attempt + 1}/{num_retries + 1}: {error}")
71
- self.console.debug(f"Retrying in {retry_delay} seconds...")
71
+ self.console.debug(
72
+ f" {connection_error_type} on attempt {attempt + 1}/{num_retries + 1}: {error}. "
73
+ f"Retrying in {retry_delay} seconds...",
74
+ color=RETRY_COLOR,
75
+ )
72
76
  time.sleep(retry_delay)
73
77
  # Exponential backoff
74
78
  return retry_delay * 2
@@ -0,0 +1,133 @@
1
+ # Plain2Code CLI Reference
2
+
3
+ ```text
4
+ usage: generate_cli.py [-h] [--verbose] [--base-folder BASE_FOLDER]
5
+ [--build-folder BUILD_FOLDER]
6
+ [--log-to-file | --no-log-to-file]
7
+ [--log-file-name LOG_FILE_NAME]
8
+ [--config-name CONFIG_NAME]
9
+ [--render-range RENDER_RANGE |
10
+ --render-from RENDER_FROM] [--force-render]
11
+ [--unittests-script UNITTESTS_SCRIPT]
12
+ [--conformance-tests-folder CONFORMANCE_TESTS_FOLDER]
13
+ [--conformance-tests-script CONFORMANCE_TESTS_SCRIPT]
14
+ [--prepare-environment-script PREPARE_ENVIRONMENT_SCRIPT]
15
+ [--test-script-timeout TEST_SCRIPT_TIMEOUT]
16
+ [--api [API]] [--api-key API_KEY] [--full-plain]
17
+ [--dry-run] [--replay-with REPLAY_WITH]
18
+ [--template-dir TEMPLATE_DIR] [--copy-build]
19
+ [--build-dest BUILD_DEST] [--copy-conformance-tests]
20
+ [--conformance-tests-dest CONFORMANCE_TESTS_DEST]
21
+ [--render-machine-graph]
22
+ [--logging-config-path LOGGING_CONFIG_PATH]
23
+ [--headless]
24
+ filename
25
+
26
+ Render plain code to target code. Path arguments resolve based on where they
27
+ were written: values given on the command line are resolved against the
28
+ current working directory, values read from config.yaml are resolved against
29
+ the config file's directory, and defaults are resolved against the directory
30
+ containing the plain file. Absolute paths (and paths starting with '~') are
31
+ used as-is.
32
+
33
+ positional arguments:
34
+ filename Path to the plain file to render. The directory
35
+ containing this file has highest precedence for
36
+ template loading, so you can place custom templates
37
+ here to override the defaults. See --template-dir for
38
+ more details about template loading.
39
+
40
+ options:
41
+ -h, --help show this help message and exit
42
+ --verbose, -v Enable verbose output
43
+ --base-folder BASE_FOLDER
44
+ Base folder for the build files
45
+ --build-folder BUILD_FOLDER
46
+ Folder for build files
47
+ --log-to-file, --no-log-to-file
48
+ Enable logging to a file. Defaults to True. Set to
49
+ False to disable.
50
+ --log-file-name LOG_FILE_NAME
51
+ Name of the log file. Defaults to 'codeplain.log'. If
52
+ a file already exists at the resolved path, it will be
53
+ overwritten by the current logs.
54
+ --render-range RENDER_RANGE
55
+ Specify a range of functionalities to render (e.g. `1`
56
+ , `2`, `3`). Use comma to separate start and end IDs.
57
+ If only one functionality ID is provided, only that
58
+ functionality is rendered. Range is inclusive of both
59
+ start and end IDs.
60
+ --render-from RENDER_FROM
61
+ Continue generation starting from this specific
62
+ functionality (e.g. `2`). The functionality with this
63
+ ID will be included in the output. The functionality
64
+ ID must match one of the functionalities in your plain
65
+ file.
66
+ --force-render Force re-render of all the required modules.
67
+ --unittests-script UNITTESTS_SCRIPT
68
+ Shell script to run unit tests on generated code.
69
+ Receives the build folder path as its first argument
70
+ (default: 'plain_modules').
71
+ --conformance-tests-folder CONFORMANCE_TESTS_FOLDER
72
+ Folder for conformance test files
73
+ --conformance-tests-script CONFORMANCE_TESTS_SCRIPT
74
+ Path to conformance tests shell script. Every
75
+ conformance test script should accept two arguments:
76
+ 1) Path to a folder (e.g. `plain_modules/module_name`)
77
+ containing generated source code, 2) Path to a
78
+ subfolder of the conformance tests folder (e.g.
79
+ `conformance_tests/subfoldername`) containing test
80
+ files.
81
+ --prepare-environment-script PREPARE_ENVIRONMENT_SCRIPT
82
+ Path to a shell script that prepares the testing
83
+ environment. The script should accept the source code
84
+ folder path as its first argument.
85
+ --test-script-timeout TEST_SCRIPT_TIMEOUT
86
+ Timeout for test scripts in seconds. If not provided,
87
+ the default timeout of 120 seconds is used.
88
+ --api [API] Alternative base URL for the API. Default:
89
+ `https://api.codeplain.ai`
90
+ --api-key API_KEY API key used to access the API. If not provided, the
91
+ `CODEPLAIN_API_KEY` environment variable is used.
92
+ --full-plain Full preview ***plain specification before code
93
+ generation.Use when you want to preview context of all
94
+ ***plain primitives that are going to be included in
95
+ order to render the given module.
96
+ --dry-run Dry run preview of the code generation (without
97
+ actually making any changes).
98
+ --replay-with REPLAY_WITH
99
+ --template-dir TEMPLATE_DIR
100
+ Path to a custom template directory. Templates are
101
+ searched in the following order: 1) Directory
102
+ containing the plain file, 2) Custom template
103
+ directory (if provided through this argument), 3)
104
+ Built-in standard_template_library directory
105
+ --copy-build If set, copy the rendered contents of code in `--base-
106
+ folder` folder to `--build-dest` folder after
107
+ successful rendering.
108
+ --build-dest BUILD_DEST
109
+ Target folder to copy rendered contents of code to
110
+ (used only if --copy-build is set).
111
+ --copy-conformance-tests
112
+ If set, copy the conformance tests of code in
113
+ `--conformance-tests-folder` folder to `--conformance-
114
+ tests-dest` folder successful rendering. Requires
115
+ --conformance-tests-script.
116
+ --conformance-tests-dest CONFORMANCE_TESTS_DEST
117
+ Target folder to copy conformance tests of code to
118
+ (used only if --copy-conformance-tests is set).
119
+ --render-machine-graph
120
+ If set, render the state machine graph.
121
+ --logging-config-path LOGGING_CONFIG_PATH
122
+ Path to the logging configuration file.
123
+ --headless Run in headless mode: no TUI, no terminal output
124
+ except a single render-started message. All logs are
125
+ written to the log file.
126
+
127
+ configuration:
128
+ --config-name CONFIG_NAME
129
+ Name of the config file to look for. Looked up in the
130
+ plain file directory and the current working
131
+ directory. Defaults to config.yaml.
132
+
133
+ ```
@@ -293,7 +293,7 @@ def get_template_directories(plain_file_path, custom_template_dir=None, default_
293
293
  3. Default template directory - for standard/fallback templates
294
294
  """
295
295
  template_dirs = [
296
- os.path.dirname(plain_file_path), # Highest precedence - directory containing plain file
296
+ os.path.dirname(os.path.abspath(plain_file_path)), # Highest precedence - directory containing plain file
297
297
  ]
298
298
 
299
299
  if custom_template_dir:
@@ -13,7 +13,7 @@ NC="${NC:-\033[0m}"
13
13
 
14
14
  # Examples configuration
15
15
  EXAMPLES_FOLDER_NAME="plainlang-examples"
16
- EXAMPLES_DOWNLOAD_URL="https://github.com/Codeplain-ai/plainlang-examples/archive/refs/tags/0.1.zip"
16
+ EXAMPLES_DOWNLOAD_URL="https://codeplain.ai/examples/unix"
17
17
 
18
18
  # Show current directory and ask for extraction path
19
19
  CURRENT_DIR=$(pwd)
@@ -55,18 +55,12 @@ if [ "$SKIP_DOWNLOAD" = false ]; then
55
55
  if [ $? -eq 0 ] && [ -s "$TEMP_ZIP" ]; then
56
56
  echo -e " ${GRAY}Extracting to ${EXTRACT_PATH}...${NC}"
57
57
 
58
- # Extract the zip file
59
- unzip -q -o "$TEMP_ZIP" -d "$EXTRACT_PATH" 2>/dev/null
58
+ # Extract the zip file (contents are at the zip root, so extract into the target folder)
59
+ EXTRACTED_DIR="${EXTRACT_PATH}/${EXAMPLES_FOLDER_NAME}"
60
+ rm -rf "$EXTRACTED_DIR" 2>/dev/null # Remove existing if present
61
+ unzip -q -o "$TEMP_ZIP" -d "$EXTRACTED_DIR" 2>/dev/null
60
62
 
61
63
  if [ $? -eq 0 ]; then
62
- # Find and rename extracted directory to remove version number
63
- EXTRACTED_DIR="${EXTRACT_PATH}/${EXAMPLES_FOLDER_NAME}"
64
- VERSIONED_DIR=$(find "$EXTRACT_PATH" -maxdepth 1 -type d -name "${EXAMPLES_FOLDER_NAME}-*" | head -1)
65
- if [ -n "$VERSIONED_DIR" ]; then
66
- rm -rf "$EXTRACTED_DIR" 2>/dev/null # Remove existing if present
67
- mv "$VERSIONED_DIR" "$EXTRACTED_DIR"
68
- fi
69
-
70
64
  # Remove the .gitignore file from the root of the extracted directory
71
65
  if [ -f "${EXTRACTED_DIR}/.gitignore" ]; then
72
66
  rm -f "${EXTRACTED_DIR}/.gitignore"
@@ -396,10 +396,12 @@ if [ -n "${CODEPLAIN_API_KEY:-}" ]; then
396
396
  # Ensure the freshly installed tool is findable in this session.
397
397
  export PATH="$HOME/.local/bin:$PATH"
398
398
  echo -e "${GRAY}Verifying your installation...${NC}"
399
- if codeplain --status > /dev/null 2>&1; then
399
+ if verify_output=$(codeplain --status 2>&1); then
400
400
  echo -e "${GREEN}✓${NC} Installation verified."
401
401
  else
402
402
  echo -e "${RED}Something went wrong during installation.${NC}"
403
+ echo -e "${GRAY}Output of 'codeplain --status':${NC}"
404
+ echo "$verify_output"
403
405
  echo -e "${GRAY}Please restart your terminal and try again, or reinstall with:${NC}"
404
406
  echo -e " uv tool install --force codeplain"
405
407
  exit 1
@@ -11,7 +11,7 @@ if (-not $env:NC) { $NC = "$([char]27)[0m" } else
11
11
 
12
12
  # Examples configuration
13
13
  $EXAMPLES_FOLDER_NAME = "plainlang-examples"
14
- $EXAMPLES_DOWNLOAD_URL = "https://github.com/Codeplain-ai/plainlang-examples/archive/refs/tags/0.1.zip"
14
+ $EXAMPLES_DOWNLOAD_URL = "https://codeplain.ai/examples/windows"
15
15
 
16
16
  # Show current directory and ask for extraction path
17
17
  $CURRENT_DIR = Get-Location
@@ -58,18 +58,12 @@ if (-not $SKIP_DOWNLOAD) {
58
58
  Write-Host " ${GRAY}Extracting to ${EXTRACT_PATH}...${NC}"
59
59
 
60
60
  try {
61
- Expand-Archive -Path $TEMP_ZIP -DestinationPath $EXTRACT_PATH -Force
62
-
63
- # Find and rename extracted directory to remove version number
61
+ # Extract the zip file (contents are at the zip root, so extract into the target folder)
64
62
  $EXTRACTED_DIR = Join-Path $EXTRACT_PATH $EXAMPLES_FOLDER_NAME
65
- $VERSIONED_DIR = Get-ChildItem -Path $EXTRACT_PATH -Directory -Filter "${EXAMPLES_FOLDER_NAME}-*" | Select-Object -First 1
66
-
67
- if ($VERSIONED_DIR) {
68
- if (Test-Path $EXTRACTED_DIR) {
69
- Remove-Item -Path $EXTRACTED_DIR -Recurse -Force -ErrorAction SilentlyContinue
70
- }
71
- Rename-Item -Path $VERSIONED_DIR.FullName -NewName $EXAMPLES_FOLDER_NAME
63
+ if (Test-Path $EXTRACTED_DIR) {
64
+ Remove-Item -Path $EXTRACTED_DIR -Recurse -Force -ErrorAction SilentlyContinue
72
65
  }
66
+ Expand-Archive -Path $TEMP_ZIP -DestinationPath $EXTRACTED_DIR -Force
73
67
 
74
68
  # Remove the .gitignore file from the root of the extracted directory
75
69
  $GITIGNORE_PATH = Join-Path $EXTRACTED_DIR ".gitignore"
@@ -426,16 +426,20 @@ if ($downloadExamples -notmatch '^[Nn]$') {
426
426
  if ($env:CODEPLAIN_API_KEY) {
427
427
  Write-Host "${GRAY}Verifying your installation...${NC}"
428
428
  $verifyOk = $false
429
+ $verifyOutput = ""
429
430
  try {
430
- & codeplain --status *> $null
431
+ $verifyOutput = & codeplain --status 2>&1 | Out-String
431
432
  $verifyOk = ($LASTEXITCODE -eq 0)
432
433
  } catch {
434
+ $verifyOutput = $_ | Out-String
433
435
  $verifyOk = $false
434
436
  }
435
437
  if ($verifyOk) {
436
438
  Write-Host "${GREEN}✓${NC} Installation verified."
437
439
  } else {
438
440
  Write-Host "${RED}Something went wrong during installation.${NC}"
441
+ Write-Host "${GRAY}Output of 'codeplain --status':${NC}"
442
+ Write-Host $verifyOutput
439
443
  Write-Host "${GRAY}Please restart your terminal and try again, or reinstall with:${NC}"
440
444
  Write-Host " uv tool install --force codeplain"
441
445
  exit 1
@@ -158,13 +158,14 @@ class ModuleRenderer:
158
158
  module.wipe_module()
159
159
 
160
160
  self.loaded_modules = list[PlainModule]()
161
+
161
162
  _, rendering_failed = self._render_module(self.plain_module, self.render_range, True)
163
+
162
164
  if not rendering_failed:
163
- # Get the last module that completed rendering
164
165
  if self.args.copy_build:
165
166
  rendered_code_path = f"{self.args.build_dest}/"
166
167
  else:
167
- rendered_code_path = f"{os.path.join(self.args.build_folder, self.plain_module.module_name)}/"
168
+ rendered_code_path = self.args.build_folder
168
169
 
169
170
  self.run_state.set_render_generated_code_path(rendered_code_path)
170
171
  self.event_bus.publish(RenderCompleted(rendered_code_path=rendered_code_path))
@@ -0,0 +1,64 @@
1
+ """Path resolution for CLI / config / default path arguments.
2
+
3
+ The rule, in one sentence: the resolution base for a relative path is determined
4
+ by *where the value was written*, not by which option key it sets.
5
+
6
+ - Values supplied on the command line resolve against the current working
7
+ directory (so they match what shell tab completion just produced).
8
+ - Values read from ``config.yaml`` resolve against the directory containing
9
+ that config file.
10
+ - Values left at their default (not supplied anywhere) resolve against the
11
+ directory containing the spec file.
12
+
13
+ Paths are expanded for ``~`` and returned as absolute paths. Canonicalization
14
+ (symlink resolution) is left to the caller, to be done just before I/O that
15
+ needs it (writing output, executing scripts) so we don't accidentally
16
+ dereference links the user wanted preserved.
17
+ """
18
+
19
+ import os
20
+ from typing import Literal, Optional
21
+
22
+ PathSource = Literal["cli", "config", "default"]
23
+
24
+
25
+ def resolve_path(
26
+ value: str,
27
+ source: PathSource,
28
+ *,
29
+ cwd: str,
30
+ config_dir: Optional[str] = None,
31
+ spec_dir: str,
32
+ ) -> str:
33
+ """Resolve *value* to an absolute path using the base anchor for *source*.
34
+
35
+ Args:
36
+ value: The path string as written by the user. May be absolute, relative,
37
+ or contain a leading ``~``.
38
+ source: Where the value came from -- ``"cli"``, ``"config"``, or
39
+ ``"default"``.
40
+ cwd: Base directory for ``"cli"`` values.
41
+ config_dir: Base directory for ``"config"`` values. Must be provided
42
+ whenever ``source == "config"``.
43
+ spec_dir: Base directory for ``"default"`` values.
44
+
45
+ Returns:
46
+ Absolute path with ``~`` expanded. Symlinks are not resolved.
47
+ """
48
+ expanded = os.path.expanduser(value)
49
+
50
+ if os.path.isabs(expanded):
51
+ return os.path.normpath(expanded)
52
+
53
+ if source == "cli":
54
+ base = cwd
55
+ elif source == "config":
56
+ if config_dir is None:
57
+ raise ValueError("config_dir must be provided when source == 'config'")
58
+ base = config_dir
59
+ elif source == "default":
60
+ base = spec_dir
61
+ else:
62
+ raise ValueError(f"Unknown path source: {source!r}")
63
+
64
+ return os.path.normpath(os.path.join(base, expanded))
@@ -48,7 +48,6 @@ from plain2code_logger import (
48
48
  IndentedFormatter,
49
49
  LoggingHandler,
50
50
  dump_crash_logs,
51
- get_log_file_path,
52
51
  )
53
52
  from plain2code_state import RunState
54
53
  from plain2code_telemetry import capture_crash, initialize_telemetry
@@ -89,8 +88,7 @@ def setup_logging(
89
88
  event_bus: EventBus,
90
89
  run_state: RunState,
91
90
  log_to_file: bool,
92
- log_file_name: str,
93
- plain_file_path: Optional[str],
91
+ log_file_path: str,
94
92
  headless: bool = False,
95
93
  ):
96
94
  default_level = logging.DEBUG if args.verbose else logging.INFO
@@ -101,10 +99,9 @@ def setup_logging(
101
99
  logging.getLogger("transitions").setLevel(logging.ERROR)
102
100
  logging.getLogger("transitions.extensions.diagrams").setLevel(logging.ERROR)
103
101
 
104
- log_file_path = get_log_file_path(plain_file_path, log_file_name)
105
-
106
102
  # Try to load logging configuration from YAML file (takes precedence over --verbose)
107
103
  config_loaded = False
104
+
108
105
  if args.logging_config_path and os.path.exists(args.logging_config_path):
109
106
  try:
110
107
  with open(args.logging_config_path, "r") as f:
@@ -127,7 +124,7 @@ def setup_logging(
127
124
  handler.setFormatter(formatter)
128
125
  root_logger.addHandler(handler)
129
126
 
130
- if log_to_file and log_file_path:
127
+ if log_to_file:
131
128
  try:
132
129
  file_handler = logging.FileHandler(log_file_path, mode="w", encoding="utf-8")
133
130
  file_handler.setFormatter(file_formatter)
@@ -289,6 +286,7 @@ def render( # noqa: C901
289
286
  render_thread = threading.Thread(target=run_render, daemon=True)
290
287
  app = Plain2CodeTUI(
291
288
  event_bus=event_bus,
289
+ run_state=run_state,
292
290
  on_ready=render_thread.start,
293
291
  render_id=run_state.render_id,
294
292
  unittests_script=args.unittests_script,
@@ -356,7 +354,7 @@ def main(): # noqa: C901
356
354
  # Parse the plain file (and its required modules) once; reused by dry-run and rendering.
357
355
  try:
358
356
  plain_module = plain_modules.PlainModule(
359
- args.filename,
357
+ os.path.basename(args.filename),
360
358
  args.build_folder,
361
359
  args.conformance_tests_folder,
362
360
  template_dirs,
@@ -383,9 +381,7 @@ def main(): # noqa: C901
383
381
  # Suppress Rich console output.
384
382
  console.quiet = True
385
383
 
386
- default_log_level = setup_logging(
387
- args, event_bus, run_state, args.log_to_file, args.log_file_name, args.filename, args.headless
388
- )
384
+ default_log_level = setup_logging(args, event_bus, run_state, args.log_to_file, args.log_file_name, args.headless)
389
385
 
390
386
  initialize_telemetry()
391
387