codeplain 0.3.7__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.
- codeplain-0.3.8/.github/workflows/publish-to-pypi.yml +70 -0
- {codeplain-0.3.7 → codeplain-0.3.8}/PKG-INFO +4 -4
- {codeplain-0.3.7 → codeplain-0.3.8}/cli_output/render_summary.py +2 -2
- {codeplain-0.3.7 → codeplain-0.3.8}/cli_output/status.py +2 -2
- {codeplain-0.3.7 → codeplain-0.3.8}/codeplain_REST_api.py +6 -2
- {codeplain-0.3.7 → codeplain-0.3.8}/install/bash/examples.sh +5 -11
- {codeplain-0.3.7 → codeplain-0.3.8}/install/bash/install.sh +3 -1
- {codeplain-0.3.7 → codeplain-0.3.8}/install/powershell/examples.ps1 +5 -11
- {codeplain-0.3.7 → codeplain-0.3.8}/install/powershell/install.ps1 +5 -1
- {codeplain-0.3.7 → codeplain-0.3.8}/plain2code.py +1 -0
- {codeplain-0.3.7 → codeplain-0.3.8}/plain2code_console.py +32 -21
- {codeplain-0.3.7 → codeplain-0.3.8}/plain2code_events.py +1 -0
- {codeplain-0.3.7 → codeplain-0.3.8}/plain2code_logger.py +3 -7
- {codeplain-0.3.7 → codeplain-0.3.8}/plain2code_state.py +17 -1
- {codeplain-0.3.7 → codeplain-0.3.8}/plain2code_utils.py +17 -13
- {codeplain-0.3.7 → codeplain-0.3.8}/pyproject.toml +8 -5
- {codeplain-0.3.7 → codeplain-0.3.8}/render_machine/actions/create_dist.py +2 -2
- {codeplain-0.3.7 → codeplain-0.3.8}/render_machine/actions/exit_with_error.py +2 -1
- {codeplain-0.3.7 → codeplain-0.3.8}/render_machine/actions/fix_conformance_test.py +4 -2
- {codeplain-0.3.7 → codeplain-0.3.8}/render_machine/actions/render_functional_requirement.py +4 -3
- {codeplain-0.3.7 → codeplain-0.3.8}/render_machine/render_context.py +5 -3
- {codeplain-0.3.7 → codeplain-0.3.8}/render_machine/render_utils.py +14 -8
- {codeplain-0.3.7 → codeplain-0.3.8}/requirements.txt +3 -3
- codeplain-0.3.8/system_config.py +68 -0
- {codeplain-0.3.7 → codeplain-0.3.8}/tests/test_cli_output.py +2 -2
- codeplain-0.3.8/tests/test_console_log_styles.py +112 -0
- codeplain-0.3.8/tests/test_plain2code_state.py +36 -0
- codeplain-0.3.8/tests/test_plain2code_utils.py +36 -0
- codeplain-0.3.8/tests/test_tui_usage.py +209 -0
- codeplain-0.3.8/tests/test_usage_summary.py +28 -0
- {codeplain-0.3.7 → codeplain-0.3.8}/tui/components.py +29 -20
- {codeplain-0.3.7 → codeplain-0.3.8}/tui/plain2code_tui.py +71 -2
- {codeplain-0.3.7 → codeplain-0.3.8}/tui/styles.css +4 -0
- {codeplain-0.3.7 → codeplain-0.3.8}/tui/widget_helpers.py +20 -1
- codeplain-0.3.8/usage_summary.py +30 -0
- codeplain-0.3.7/.claude/settings.local.json +0 -66
- codeplain-0.3.7/.github/workflows/publish-to-pypi.yml +0 -109
- codeplain-0.3.7/.zed/settings.json +0 -46
- codeplain-0.3.7/_version.py +0 -1
- codeplain-0.3.7/system_config.py +0 -34
- {codeplain-0.3.7 → codeplain-0.3.8}/.claude/settings.json +0 -0
- {codeplain-0.3.7 → codeplain-0.3.8}/.flake8 +0 -0
- {codeplain-0.3.7 → codeplain-0.3.8}/.github/dependabot.yml +0 -0
- {codeplain-0.3.7 → codeplain-0.3.8}/.github/workflows/e2e.yml +0 -0
- {codeplain-0.3.7 → codeplain-0.3.8}/.github/workflows/lint-and-test.yml +0 -0
- {codeplain-0.3.7 → codeplain-0.3.8}/.github/workflows/nofity-slack-on-main-merge.yml +0 -0
- {codeplain-0.3.7 → codeplain-0.3.8}/.github/workflows/publish-install-script.yml +0 -0
- {codeplain-0.3.7 → codeplain-0.3.8}/.gitignore +0 -0
- {codeplain-0.3.7 → codeplain-0.3.8}/.vscode/launch.json +0 -0
- {codeplain-0.3.7 → codeplain-0.3.8}/.vscode/settings.json +0 -0
- {codeplain-0.3.7 → codeplain-0.3.8}/CLAUDE.md +0 -0
- {codeplain-0.3.7 → codeplain-0.3.8}/LICENSE +0 -0
- {codeplain-0.3.7 → codeplain-0.3.8}/README.md +0 -0
- {codeplain-0.3.7 → codeplain-0.3.8}/change_detection.py +0 -0
- {codeplain-0.3.7 → codeplain-0.3.8}/cli_output/__init__.py +0 -0
- {codeplain-0.3.7 → codeplain-0.3.8}/cli_output/dry_run.py +0 -0
- {codeplain-0.3.7 → codeplain-0.3.8}/concept_utils.py +0 -0
- {codeplain-0.3.7 → codeplain-0.3.8}/config/__init__.py +0 -0
- {codeplain-0.3.7 → codeplain-0.3.8}/config/system_config.yaml +0 -0
- {codeplain-0.3.7 → codeplain-0.3.8}/diff_utils.py +0 -0
- {codeplain-0.3.7 → codeplain-0.3.8}/docs/generate_cli.py +0 -0
- {codeplain-0.3.7 → codeplain-0.3.8}/docs/plain2code_cli.md +0 -0
- {codeplain-0.3.7 → codeplain-0.3.8}/docs/starting_a_plain_project_from_scratch.md +0 -0
- {codeplain-0.3.7 → codeplain-0.3.8}/event_bus.py +0 -0
- {codeplain-0.3.7 → codeplain-0.3.8}/examples/example_hello_world_golang/config.yaml +0 -0
- {codeplain-0.3.7 → codeplain-0.3.8}/examples/example_hello_world_golang/harness_tests/hello_world_test.go +0 -0
- {codeplain-0.3.7 → codeplain-0.3.8}/examples/example_hello_world_golang/hello_world_golang.plain +0 -0
- {codeplain-0.3.7 → codeplain-0.3.8}/examples/example_hello_world_golang/run.sh +0 -0
- {codeplain-0.3.7 → codeplain-0.3.8}/examples/example_hello_world_python/config.yaml +0 -0
- {codeplain-0.3.7 → codeplain-0.3.8}/examples/example_hello_world_python/harness_tests/hello_world_display/test_hello_world.py +0 -0
- {codeplain-0.3.7 → codeplain-0.3.8}/examples/example_hello_world_python/hello_world_python.plain +0 -0
- {codeplain-0.3.7 → codeplain-0.3.8}/examples/example_hello_world_python/run.sh +0 -0
- {codeplain-0.3.7 → codeplain-0.3.8}/examples/example_hello_world_react/config.yaml +0 -0
- {codeplain-0.3.7 → codeplain-0.3.8}/examples/example_hello_world_react/harness_tests/hello_world_display/cypress/e2e/hello_world.cy.ts +0 -0
- {codeplain-0.3.7 → codeplain-0.3.8}/examples/example_hello_world_react/harness_tests/hello_world_display/cypress/support/e2e.ts +0 -0
- {codeplain-0.3.7 → codeplain-0.3.8}/examples/example_hello_world_react/harness_tests/hello_world_display/cypress.config.ts +0 -0
- {codeplain-0.3.7 → codeplain-0.3.8}/examples/example_hello_world_react/harness_tests/hello_world_display/package.json +0 -0
- {codeplain-0.3.7 → codeplain-0.3.8}/examples/example_hello_world_react/harness_tests/hello_world_display/tsconfig.json +0 -0
- {codeplain-0.3.7 → codeplain-0.3.8}/examples/example_hello_world_react/hello_world_react.plain +0 -0
- {codeplain-0.3.7 → codeplain-0.3.8}/examples/example_hello_world_react/run.sh +0 -0
- {codeplain-0.3.7 → codeplain-0.3.8}/examples/run.sh +0 -0
- {codeplain-0.3.7 → codeplain-0.3.8}/file_utils.py +0 -0
- {codeplain-0.3.7 → codeplain-0.3.8}/git_utils.py +0 -0
- {codeplain-0.3.7 → codeplain-0.3.8}/install/bash/walkthrough.sh +0 -0
- {codeplain-0.3.7 → codeplain-0.3.8}/install/powershell/walkthrough.ps1 +0 -0
- {codeplain-0.3.7 → codeplain-0.3.8}/memory_management.py +0 -0
- {codeplain-0.3.7 → codeplain-0.3.8}/module_renderer.py +0 -0
- {codeplain-0.3.7 → codeplain-0.3.8}/partial_rendering.py +0 -0
- {codeplain-0.3.7 → codeplain-0.3.8}/path_resolution.py +0 -0
- {codeplain-0.3.7 → codeplain-0.3.8}/plain2code_arguments.py +0 -0
- {codeplain-0.3.7 → codeplain-0.3.8}/plain2code_cli.md +0 -0
- {codeplain-0.3.7 → codeplain-0.3.8}/plain2code_exceptions.py +0 -0
- {codeplain-0.3.7 → codeplain-0.3.8}/plain2code_nodes.py +0 -0
- {codeplain-0.3.7 → codeplain-0.3.8}/plain2code_read_config.py +0 -0
- {codeplain-0.3.7 → codeplain-0.3.8}/plain2code_telemetry.py +0 -0
- {codeplain-0.3.7 → codeplain-0.3.8}/plain_file.py +0 -0
- {codeplain-0.3.7 → codeplain-0.3.8}/plain_modules.py +0 -0
- {codeplain-0.3.7 → codeplain-0.3.8}/plain_spec.py +0 -0
- {codeplain-0.3.7 → codeplain-0.3.8}/pytest.ini +0 -0
- {codeplain-0.3.7 → codeplain-0.3.8}/render_machine/__init__.py +0 -0
- {codeplain-0.3.7 → codeplain-0.3.8}/render_machine/actions/analyze_specification_ambiguity.py +0 -0
- {codeplain-0.3.7 → codeplain-0.3.8}/render_machine/actions/base_action.py +0 -0
- {codeplain-0.3.7 → codeplain-0.3.8}/render_machine/actions/commit_conformance_tests_changes.py +0 -0
- {codeplain-0.3.7 → codeplain-0.3.8}/render_machine/actions/commit_implementation_code_changes.py +0 -0
- {codeplain-0.3.7 → codeplain-0.3.8}/render_machine/actions/finish_functional_requirement.py +0 -0
- {codeplain-0.3.7 → codeplain-0.3.8}/render_machine/actions/fix_unit_tests.py +0 -0
- {codeplain-0.3.7 → codeplain-0.3.8}/render_machine/actions/prepare_repositories.py +0 -0
- {codeplain-0.3.7 → codeplain-0.3.8}/render_machine/actions/prepare_testing_environment.py +0 -0
- {codeplain-0.3.7 → codeplain-0.3.8}/render_machine/actions/refactor_code.py +0 -0
- {codeplain-0.3.7 → codeplain-0.3.8}/render_machine/actions/render_conformance_tests.py +0 -0
- {codeplain-0.3.7 → codeplain-0.3.8}/render_machine/actions/run_conformance_tests.py +0 -0
- {codeplain-0.3.7 → codeplain-0.3.8}/render_machine/actions/run_unit_tests.py +0 -0
- {codeplain-0.3.7 → codeplain-0.3.8}/render_machine/actions/summarize_conformance_tests.py +0 -0
- {codeplain-0.3.7 → codeplain-0.3.8}/render_machine/code_renderer.py +0 -0
- {codeplain-0.3.7 → codeplain-0.3.8}/render_machine/conformance_tests.py +0 -0
- {codeplain-0.3.7 → codeplain-0.3.8}/render_machine/implementation_code_helpers.py +0 -0
- {codeplain-0.3.7 → codeplain-0.3.8}/render_machine/render_types.py +0 -0
- {codeplain-0.3.7 → codeplain-0.3.8}/render_machine/state_machine_config.py +0 -0
- {codeplain-0.3.7 → codeplain-0.3.8}/render_machine/states.py +0 -0
- {codeplain-0.3.7 → codeplain-0.3.8}/render_machine/triggers.py +0 -0
- {codeplain-0.3.7 → codeplain-0.3.8}/resources/codeplain_overview.png +0 -0
- {codeplain-0.3.7 → codeplain-0.3.8}/resources/plain_example.png +0 -0
- {codeplain-0.3.7 → codeplain-0.3.8}/standard_template_library/golang-console-app-template.plain +0 -0
- {codeplain-0.3.7 → codeplain-0.3.8}/standard_template_library/python-console-app-template.plain +0 -0
- {codeplain-0.3.7 → codeplain-0.3.8}/standard_template_library/typescript-react-app-boilerplate.plain +0 -0
- {codeplain-0.3.7 → codeplain-0.3.8}/standard_template_library/typescript-react-app-template.plain +0 -0
- {codeplain-0.3.7 → codeplain-0.3.8}/test_scripts/run_conformance_tests_cypress.ps1 +0 -0
- {codeplain-0.3.7 → codeplain-0.3.8}/test_scripts/run_conformance_tests_cypress.sh +0 -0
- {codeplain-0.3.7 → codeplain-0.3.8}/test_scripts/run_conformance_tests_golang.ps1 +0 -0
- {codeplain-0.3.7 → codeplain-0.3.8}/test_scripts/run_conformance_tests_golang.sh +0 -0
- {codeplain-0.3.7 → codeplain-0.3.8}/test_scripts/run_conformance_tests_python.ps1 +0 -0
- {codeplain-0.3.7 → codeplain-0.3.8}/test_scripts/run_conformance_tests_python.sh +0 -0
- {codeplain-0.3.7 → codeplain-0.3.8}/test_scripts/run_unittests_flutter.ps1 +0 -0
- {codeplain-0.3.7 → codeplain-0.3.8}/test_scripts/run_unittests_flutter.sh +0 -0
- {codeplain-0.3.7 → codeplain-0.3.8}/test_scripts/run_unittests_golang.ps1 +0 -0
- {codeplain-0.3.7 → codeplain-0.3.8}/test_scripts/run_unittests_golang.sh +0 -0
- {codeplain-0.3.7 → codeplain-0.3.8}/test_scripts/run_unittests_python.ps1 +0 -0
- {codeplain-0.3.7 → codeplain-0.3.8}/test_scripts/run_unittests_python.sh +0 -0
- {codeplain-0.3.7 → codeplain-0.3.8}/test_scripts/run_unittests_react.ps1 +0 -0
- {codeplain-0.3.7 → codeplain-0.3.8}/test_scripts/run_unittests_react.sh +0 -0
- {codeplain-0.3.7 → codeplain-0.3.8}/tests/__init__.py +0 -0
- {codeplain-0.3.7 → codeplain-0.3.8}/tests/conftest.py +0 -0
- {codeplain-0.3.7 → codeplain-0.3.8}/tests/data/acceptance_tests_warning/main_requiring_acceptance_tests.plain +0 -0
- {codeplain-0.3.7 → codeplain-0.3.8}/tests/data/acceptance_tests_warning/required_with_acceptance_tests.plain +0 -0
- {codeplain-0.3.7 → codeplain-0.3.8}/tests/data/imports/circular_imports_1.plain +0 -0
- {codeplain-0.3.7 → codeplain-0.3.8}/tests/data/imports/circular_imports_2.plain +0 -0
- {codeplain-0.3.7 → codeplain-0.3.8}/tests/data/imports/circular_imports_main.plain +0 -0
- {codeplain-0.3.7 → codeplain-0.3.8}/tests/data/imports/diamond_import_1.plain +0 -0
- {codeplain-0.3.7 → codeplain-0.3.8}/tests/data/imports/diamond_import_2.plain +0 -0
- {codeplain-0.3.7 → codeplain-0.3.8}/tests/data/imports/diamond_import_common.plain +0 -0
- {codeplain-0.3.7 → codeplain-0.3.8}/tests/data/imports/diamond_imports_main.plain +0 -0
- {codeplain-0.3.7 → codeplain-0.3.8}/tests/data/imports/import_with_functionalities.plain +0 -0
- {codeplain-0.3.7 → codeplain-0.3.8}/tests/data/imports/imports_module_with_functionalities.plain +0 -0
- {codeplain-0.3.7 → codeplain-0.3.8}/tests/data/imports/non_existent_import.plain +0 -0
- {codeplain-0.3.7 → codeplain-0.3.8}/tests/data/partial_rendering/pr_code_var.plain +0 -0
- {codeplain-0.3.7 → codeplain-0.3.8}/tests/data/partial_rendering/pr_implement.plain +0 -0
- {codeplain-0.3.7 → codeplain-0.3.8}/tests/data/partial_rendering/pr_leaf.plain +0 -0
- {codeplain-0.3.7 → codeplain-0.3.8}/tests/data/partial_rendering/pr_middle.plain +0 -0
- {codeplain-0.3.7 → codeplain-0.3.8}/tests/data/partial_rendering/pr_root.plain +0 -0
- {codeplain-0.3.7 → codeplain-0.3.8}/tests/data/partial_rendering/pr_solo.plain +0 -0
- {codeplain-0.3.7 → codeplain-0.3.8}/tests/data/plainfile/add_new_task_modal_specification.yaml +0 -0
- {codeplain-0.3.7 → codeplain-0.3.8}/tests/data/plainfile/duplicate_specification_heading.plain +0 -0
- {codeplain-0.3.7 → codeplain-0.3.8}/tests/data/plainfile/empty_functional_specs_section.plain +0 -0
- {codeplain-0.3.7 → codeplain-0.3.8}/tests/data/plainfile/invalid_specification_order.plain +0 -0
- {codeplain-0.3.7 → codeplain-0.3.8}/tests/data/plainfile/missing_non_functional_requirements.plain +0 -0
- {codeplain-0.3.7 → codeplain-0.3.8}/tests/data/plainfile/no_functional_specs_section.plain +0 -0
- {codeplain-0.3.7 → codeplain-0.3.8}/tests/data/plainfile/plain_source_with_absolute_link.plain +0 -0
- {codeplain-0.3.7 → codeplain-0.3.8}/tests/data/plainfile/plain_source_with_url_link.plain +0 -0
- {codeplain-0.3.7 → codeplain-0.3.8}/tests/data/plainfile/task_list_ui_specification.yaml +0 -0
- {codeplain-0.3.7 → codeplain-0.3.8}/tests/data/plainfile/task_manager_with_reference_links.plain +0 -0
- {codeplain-0.3.7 → codeplain-0.3.8}/tests/data/plainfile/without_non_functional_requirement.plain +0 -0
- {codeplain-0.3.7 → codeplain-0.3.8}/tests/data/plainfileparser/concept_validation_acceptance_tests.plain +0 -0
- {codeplain-0.3.7 → codeplain-0.3.8}/tests/data/plainfileparser/concept_validation_acceptance_tests_nondefined.plain +0 -0
- {codeplain-0.3.7 → codeplain-0.3.8}/tests/data/plainfileparser/concept_validation_defined_nondefined.plain +0 -0
- {codeplain-0.3.7 → codeplain-0.3.8}/tests/data/plainfileparser/concept_validation_defined_nondefined_2.plain +0 -0
- {codeplain-0.3.7 → codeplain-0.3.8}/tests/data/plainfileparser/concept_validation_definition.plain +0 -0
- {codeplain-0.3.7 → codeplain-0.3.8}/tests/data/plainfileparser/concept_validation_noconcepts.plain +0 -0
- {codeplain-0.3.7 → codeplain-0.3.8}/tests/data/plainfileparser/concept_validation_nonconcept.plain +0 -0
- {codeplain-0.3.7 → codeplain-0.3.8}/tests/data/plainfileparser/concept_validation_nondefined.plain +0 -0
- {codeplain-0.3.7 → codeplain-0.3.8}/tests/data/plainfileparser/concept_validation_redefinition.plain +0 -0
- {codeplain-0.3.7 → codeplain-0.3.8}/tests/data/plainfileparser/concept_validation_several_concepts.plain +0 -0
- {codeplain-0.3.7 → codeplain-0.3.8}/tests/data/plainfileparser/concept_validation_valid.plain +0 -0
- {codeplain-0.3.7 → codeplain-0.3.8}/tests/data/plainfileparser/exported_concepts.plain +0 -0
- {codeplain-0.3.7 → codeplain-0.3.8}/tests/data/plainfileparser/exported_concepts_base.plain +0 -0
- {codeplain-0.3.7 → codeplain-0.3.8}/tests/data/plainfileparser/exported_concepts_example.plain +0 -0
- {codeplain-0.3.7 → codeplain-0.3.8}/tests/data/plainfileparser/exported_concepts_missing.plain +0 -0
- {codeplain-0.3.7 → codeplain-0.3.8}/tests/data/plainfileparser/exported_concepts_missing_example.plain +0 -0
- {codeplain-0.3.7 → codeplain-0.3.8}/tests/data/plainfileparser/exported_concepts_nested.plain +0 -0
- {codeplain-0.3.7 → codeplain-0.3.8}/tests/data/plainfileparser/exported_concepts_nested_example.plain +0 -0
- {codeplain-0.3.7 → codeplain-0.3.8}/tests/data/plainfileparser/exported_concepts_transitive_example.plain +0 -0
- {codeplain-0.3.7 → codeplain-0.3.8}/tests/data/plainfileparser/exported_concepts_transitive_l1.plain +0 -0
- {codeplain-0.3.7 → codeplain-0.3.8}/tests/data/plainfileparser/exported_concepts_transitive_l2.plain +0 -0
- {codeplain-0.3.7 → codeplain-0.3.8}/tests/data/plainfileparser/plain_file_parser_with_comments.plain +0 -0
- {codeplain-0.3.7 → codeplain-0.3.8}/tests/data/plainfileparser/plain_file_with_comments_indented.plain +0 -0
- {codeplain-0.3.7 → codeplain-0.3.8}/tests/data/plainfileparser/regular_plain_source.plain +0 -0
- {codeplain-0.3.7 → codeplain-0.3.8}/tests/data/plainfileparser/required_concepts.plain +0 -0
- {codeplain-0.3.7 → codeplain-0.3.8}/tests/data/plainfileparser/required_concepts_defs.plain +0 -0
- {codeplain-0.3.7 → codeplain-0.3.8}/tests/data/plainfileparser/required_concepts_example.plain +0 -0
- {codeplain-0.3.7 → codeplain-0.3.8}/tests/data/plainfileparser/required_concepts_l1.plain +0 -0
- {codeplain-0.3.7 → codeplain-0.3.8}/tests/data/plainfileparser/required_concepts_l2.plain +0 -0
- {codeplain-0.3.7 → codeplain-0.3.8}/tests/data/plainfileparser/required_concepts_missing.plain +0 -0
- {codeplain-0.3.7 → codeplain-0.3.8}/tests/data/plainfileparser/required_concepts_module.plain +0 -0
- {codeplain-0.3.7 → codeplain-0.3.8}/tests/data/plainfileparser/required_concepts_partial.plain +0 -0
- {codeplain-0.3.7 → codeplain-0.3.8}/tests/data/plainfileparser/required_concepts_partial_duplicate.plain +0 -0
- {codeplain-0.3.7 → codeplain-0.3.8}/tests/data/plainfileparser/topological_sort.plain +0 -0
- {codeplain-0.3.7 → codeplain-0.3.8}/tests/data/plainfileparser/topological_sort_not_referenced.plain +0 -0
- {codeplain-0.3.7 → codeplain-0.3.8}/tests/data/requires/circular_requires_main.plain +0 -0
- {codeplain-0.3.7 → codeplain-0.3.8}/tests/data/requires/circular_requires_sub.plain +0 -0
- {codeplain-0.3.7 → codeplain-0.3.8}/tests/data/requires/diamond_requires_1.plain +0 -0
- {codeplain-0.3.7 → codeplain-0.3.8}/tests/data/requires/diamond_requires_2.plain +0 -0
- {codeplain-0.3.7 → codeplain-0.3.8}/tests/data/requires/diamond_requires_common.plain +0 -0
- {codeplain-0.3.7 → codeplain-0.3.8}/tests/data/requires/diamond_requires_main.plain +0 -0
- {codeplain-0.3.7 → codeplain-0.3.8}/tests/data/requires/independent_requires_1.plain +0 -0
- {codeplain-0.3.7 → codeplain-0.3.8}/tests/data/requires/independent_requires_2.plain +0 -0
- {codeplain-0.3.7 → codeplain-0.3.8}/tests/data/requires/independent_requires_main.plain +0 -0
- {codeplain-0.3.7 → codeplain-0.3.8}/tests/data/requires/non_existent_require.plain +0 -0
- {codeplain-0.3.7 → codeplain-0.3.8}/tests/data/requires/normal_requires_1.plain +0 -0
- {codeplain-0.3.7 → codeplain-0.3.8}/tests/data/requires/normal_requires_2.plain +0 -0
- {codeplain-0.3.7 → codeplain-0.3.8}/tests/data/requires/normal_requires_common.plain +0 -0
- {codeplain-0.3.7 → codeplain-0.3.8}/tests/data/requires/normal_requires_main.plain +0 -0
- {codeplain-0.3.7 → codeplain-0.3.8}/tests/data/sample_base64_image.txt +0 -0
- {codeplain-0.3.7 → codeplain-0.3.8}/tests/data/simple.plain +0 -0
- {codeplain-0.3.7 → codeplain-0.3.8}/tests/data/templates/block_level_include.plain +0 -0
- {codeplain-0.3.7 → codeplain-0.3.8}/tests/data/templates/code_variables.plain +0 -0
- {codeplain-0.3.7 → codeplain-0.3.8}/tests/data/templates/header.plain +0 -0
- {codeplain-0.3.7 → codeplain-0.3.8}/tests/data/templates/implement.plain +0 -0
- {codeplain-0.3.7 → codeplain-0.3.8}/tests/data/templates/implement_2.plain +0 -0
- {codeplain-0.3.7 → codeplain-0.3.8}/tests/data/templates/template_include.plain +0 -0
- {codeplain-0.3.7 → codeplain-0.3.8}/tests/data/templates/test_hardest_problem.plain +0 -0
- {codeplain-0.3.7 → codeplain-0.3.8}/tests/e2e/Dockerfile +0 -0
- {codeplain-0.3.7 → codeplain-0.3.8}/tests/e2e/conftest.py +0 -0
- {codeplain-0.3.7 → codeplain-0.3.8}/tests/e2e/test_hello_world_python.py +0 -0
- {codeplain-0.3.7 → codeplain-0.3.8}/tests/e2e/test_hello_world_python_windows.py +0 -0
- {codeplain-0.3.7 → codeplain-0.3.8}/tests/test_arg_provenance.py +0 -0
- {codeplain-0.3.7 → codeplain-0.3.8}/tests/test_change_detection.py +0 -0
- {codeplain-0.3.7 → codeplain-0.3.8}/tests/test_file_utils.py +0 -0
- {codeplain-0.3.7 → codeplain-0.3.8}/tests/test_folder_path_resolution.py +0 -0
- {codeplain-0.3.7 → codeplain-0.3.8}/tests/test_git_utils.py +0 -0
- {codeplain-0.3.7 → codeplain-0.3.8}/tests/test_imports.py +0 -0
- {codeplain-0.3.7 → codeplain-0.3.8}/tests/test_log_file_name_resolution.py +0 -0
- {codeplain-0.3.7 → codeplain-0.3.8}/tests/test_logging_config_path_resolution.py +0 -0
- {codeplain-0.3.7 → codeplain-0.3.8}/tests/test_partial_rendering.py +0 -0
- {codeplain-0.3.7 → codeplain-0.3.8}/tests/test_path_resolution.py +0 -0
- {codeplain-0.3.7 → codeplain-0.3.8}/tests/test_plain2code.py +0 -0
- {codeplain-0.3.7 → codeplain-0.3.8}/tests/test_plain_modules.py +0 -0
- {codeplain-0.3.7 → codeplain-0.3.8}/tests/test_plainfile.py +0 -0
- {codeplain-0.3.7 → codeplain-0.3.8}/tests/test_plainfileparser.py +0 -0
- {codeplain-0.3.7 → codeplain-0.3.8}/tests/test_plainspec.py +0 -0
- {codeplain-0.3.7 → codeplain-0.3.8}/tests/test_requires.py +0 -0
- {codeplain-0.3.7 → codeplain-0.3.8}/tests/test_resolve_config_file.py +0 -0
- {codeplain-0.3.7 → codeplain-0.3.8}/tests/test_script_path_resolution.py +0 -0
- {codeplain-0.3.7 → codeplain-0.3.8}/tests/test_telemetry.py +0 -0
- {codeplain-0.3.7 → codeplain-0.3.8}/tui/__init__.py +0 -0
- {codeplain-0.3.7 → codeplain-0.3.8}/tui/models.py +0 -0
- {codeplain-0.3.7 → codeplain-0.3.8}/tui/plain_module_render_choice_tui.py +0 -0
- {codeplain-0.3.7 → codeplain-0.3.8}/tui/spinner.py +0 -0
- {codeplain-0.3.7 → 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.
|
|
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,7 +8,7 @@ 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.
|
|
11
|
+
Requires-Dist: gitpython==3.1.52
|
|
12
12
|
Requires-Dist: mistletoe==1.5.1
|
|
13
13
|
Requires-Dist: networkx==3.6.1
|
|
14
14
|
Requires-Dist: python-frontmatter==1.3.0
|
|
@@ -16,7 +16,7 @@ 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.
|
|
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
|
|
@@ -24,7 +24,7 @@ Provides-Extra: dev
|
|
|
24
24
|
Requires-Dist: black==26.5.1; extra == 'dev'
|
|
25
25
|
Requires-Dist: flake8==7.3.0; extra == 'dev'
|
|
26
26
|
Requires-Dist: isort==8.0.1; extra == 'dev'
|
|
27
|
-
Requires-Dist: mypy==2.
|
|
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
|
|
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 +=
|
|
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(
|
|
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(
|
|
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(
|
|
71
|
-
|
|
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
|
|
@@ -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://
|
|
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
|
-
|
|
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
|
|
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://
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
@@ -286,6 +286,7 @@ def render( # noqa: C901
|
|
|
286
286
|
render_thread = threading.Thread(target=run_render, daemon=True)
|
|
287
287
|
app = Plain2CodeTUI(
|
|
288
288
|
event_bus=event_bus,
|
|
289
|
+
run_state=run_state,
|
|
289
290
|
on_ready=render_thread.start,
|
|
290
291
|
render_id=run_state.render_id,
|
|
291
292
|
unittests_script=args.unittests_script,
|
|
@@ -11,6 +11,12 @@ CHARACTERS_TO_TOKENS_RULE_OF_THUMB_RATIO = 4
|
|
|
11
11
|
|
|
12
12
|
logger = logging.getLogger(plain2code_logger.LOGGER_NAME)
|
|
13
13
|
|
|
14
|
+
# Colors for log messages, applied by the terminal and the TUI via the "color"
|
|
15
|
+
# parameter of the console methods. The file log always receives plain text.
|
|
16
|
+
RETRY_COLOR = "#FFB454" # Amber
|
|
17
|
+
SUCCESS_COLOR = "#79FC96" # Green
|
|
18
|
+
MUTED_COLOR = "#888888" # Grey
|
|
19
|
+
|
|
14
20
|
|
|
15
21
|
class Plain2CodeConsole(Console):
|
|
16
22
|
INFO_STYLE = Style()
|
|
@@ -33,30 +39,37 @@ class Plain2CodeConsole(Console):
|
|
|
33
39
|
logger.debug(f"Exception: {e}")
|
|
34
40
|
self.llm_encoding = None
|
|
35
41
|
|
|
36
|
-
def info(self, *args, **kwargs):
|
|
37
|
-
|
|
38
|
-
super().print(*args, **kwargs, style=self.INFO_STYLE)
|
|
42
|
+
def info(self, *args, color=None, **kwargs):
|
|
43
|
+
self._log_and_print(logging.INFO, self.INFO_STYLE, args, color, kwargs)
|
|
39
44
|
|
|
40
|
-
def warning(self, *args, **kwargs):
|
|
41
|
-
|
|
42
|
-
super().print(*args, **kwargs, style=self.WARNING_STYLE)
|
|
45
|
+
def warning(self, *args, color=None, **kwargs):
|
|
46
|
+
self._log_and_print(logging.WARNING, self.WARNING_STYLE, args, color, kwargs)
|
|
43
47
|
|
|
44
|
-
def error(self, *args, **kwargs):
|
|
45
|
-
|
|
46
|
-
super().print(*args, **kwargs, style=self.ERROR_STYLE)
|
|
48
|
+
def error(self, *args, color=None, **kwargs):
|
|
49
|
+
self._log_and_print(logging.ERROR, self.ERROR_STYLE, args, color, kwargs)
|
|
47
50
|
|
|
48
|
-
def input(self, *args, **kwargs):
|
|
51
|
+
def input(self, *args, color=None, **kwargs):
|
|
49
52
|
# We also log input as info so it shows in the toggled view
|
|
50
|
-
|
|
51
|
-
|
|
53
|
+
self._log_and_print(logging.INFO, self.INPUT_STYLE, args, color, kwargs)
|
|
54
|
+
|
|
55
|
+
def output(self, *args, color=None, **kwargs):
|
|
56
|
+
self._log_and_print(logging.INFO, self.OUTPUT_STYLE, args, color, kwargs)
|
|
52
57
|
|
|
53
|
-
def
|
|
54
|
-
|
|
55
|
-
super().print(*args, **kwargs, style=self.OUTPUT_STYLE)
|
|
58
|
+
def debug(self, *args, color=None, **kwargs):
|
|
59
|
+
self._log_and_print(logging.DEBUG, self.DEBUG_STYLE, args, color, kwargs)
|
|
56
60
|
|
|
57
|
-
def
|
|
58
|
-
|
|
59
|
-
|
|
61
|
+
def _log_and_print(self, level, base_style, args, color, kwargs):
|
|
62
|
+
"""Log the plain message text, then print it styled to the terminal.
|
|
63
|
+
|
|
64
|
+
The optional color is applied by the terminal (via style) and forwarded to
|
|
65
|
+
the TUI as the "log_color" record attribute; the file log stays plain text.
|
|
66
|
+
"""
|
|
67
|
+
logger.log(level, " ".join(map(str, args)), extra={"log_color": color})
|
|
68
|
+
style = base_style + Style(color=color) if color else base_style
|
|
69
|
+
# Log messages must render exactly as logged: don't interpret square brackets
|
|
70
|
+
# in interpolated content (error texts, file names) as Rich markup.
|
|
71
|
+
kwargs.setdefault("markup", False)
|
|
72
|
+
super().print(*args, **kwargs, style=style)
|
|
60
73
|
|
|
61
74
|
def print_list(self, items, style=None):
|
|
62
75
|
for item in items:
|
|
@@ -130,9 +143,7 @@ class Plain2CodeConsole(Console):
|
|
|
130
143
|
for resource_name in resources_list:
|
|
131
144
|
if resource_name["target"] in linked_resources:
|
|
132
145
|
file_tokens = self._count_tokens(linked_resources[resource_name["target"]])
|
|
133
|
-
self.debug(
|
|
134
|
-
f"- {resource_name['text']} [#4169E1]({resource_name['target']}, {file_tokens} tokens)[/#4169E1]"
|
|
135
|
-
)
|
|
146
|
+
self.debug(f"- {resource_name['text']} ({resource_name['target']}, {file_tokens} tokens)")
|
|
136
147
|
|
|
137
148
|
self.input()
|
|
138
149
|
|
|
@@ -44,6 +44,7 @@ class LogMessageEmitted(BaseEvent):
|
|
|
44
44
|
level: str # e.g., "INFO", "DEBUG", "ERROR"
|
|
45
45
|
message: str # The actual log message
|
|
46
46
|
timestamp: str # Formatted timestamp
|
|
47
|
+
log_color: Optional[str] = None # Color for the message (e.g. "#FFB454"); the file log stays plain text
|
|
47
48
|
|
|
48
49
|
|
|
49
50
|
@dataclass
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import logging
|
|
2
|
-
import time
|
|
3
2
|
|
|
4
3
|
from event_bus import EventBus
|
|
5
4
|
from plain2code_events import LogMessageEmitted
|
|
@@ -42,9 +41,7 @@ class ElapsedTimeFormatter(logging.Formatter):
|
|
|
42
41
|
def format(self, record):
|
|
43
42
|
# Calculate elapsed time the same way as LoggingHandler does for the TUI
|
|
44
43
|
try:
|
|
45
|
-
offset_seconds = self.run_state.
|
|
46
|
-
time.monotonic() - self.run_state.last_render_start_timestamp
|
|
47
|
-
)
|
|
44
|
+
offset_seconds = self.run_state.get_live_render_time()
|
|
48
45
|
except Exception:
|
|
49
46
|
# If RunState is not available or there's any error, default to 00:00:00
|
|
50
47
|
offset_seconds = 0
|
|
@@ -74,9 +71,7 @@ class LoggingHandler(logging.Handler):
|
|
|
74
71
|
|
|
75
72
|
def emit(self, record):
|
|
76
73
|
try:
|
|
77
|
-
offset_seconds = self.run_state.
|
|
78
|
-
time.monotonic() - self.run_state.last_render_start_timestamp
|
|
79
|
-
)
|
|
74
|
+
offset_seconds = self.run_state.get_live_render_time()
|
|
80
75
|
|
|
81
76
|
hours = offset_seconds // 3600
|
|
82
77
|
minutes = (offset_seconds % 3600) // 60
|
|
@@ -88,6 +83,7 @@ class LoggingHandler(logging.Handler):
|
|
|
88
83
|
level=record.levelname,
|
|
89
84
|
message=record.getMessage(),
|
|
90
85
|
timestamp=timestamp,
|
|
86
|
+
log_color=getattr(record, "log_color", None),
|
|
91
87
|
)
|
|
92
88
|
self.event_bus.publish(event)
|
|
93
89
|
except RuntimeError:
|
|
@@ -48,7 +48,23 @@ class RunState:
|
|
|
48
48
|
self.rendered_functionalities += 1
|
|
49
49
|
|
|
50
50
|
def add_to_render_time(self):
|
|
51
|
-
|
|
51
|
+
now = time.monotonic()
|
|
52
|
+
self.render_time_accumulated += int(now - self.last_render_start_timestamp)
|
|
53
|
+
# Reset the segment start so get_live_render_time() does not re-count the
|
|
54
|
+
# span that was just banked (e.g. after a render completes).
|
|
55
|
+
self.last_render_start_timestamp = now
|
|
56
|
+
|
|
57
|
+
def get_live_render_time(self) -> int:
|
|
58
|
+
"""Render time so far in whole seconds, including the in-progress segment.
|
|
59
|
+
|
|
60
|
+
This is the single source of truth for elapsed render time, shared by the
|
|
61
|
+
log timestamps, the TUI usage line, and the post-render summary. Pause time
|
|
62
|
+
is excluded because ``add_to_render_time`` banks the elapsed span at the
|
|
63
|
+
start of a pause and ``set_last_render_start_timestamp`` restarts the
|
|
64
|
+
segment on resume. The value is only transiently inflated while the code
|
|
65
|
+
sits inside the pause loop itself, which no consumer samples.
|
|
66
|
+
"""
|
|
67
|
+
return self.render_time_accumulated + int(time.monotonic() - self.last_render_start_timestamp)
|
|
52
68
|
|
|
53
69
|
def set_last_render_start_timestamp(self):
|
|
54
70
|
self.last_render_start_timestamp = time.monotonic()
|
|
@@ -16,19 +16,23 @@ def find_large_base64_blob(text: str) -> Optional[str]:
|
|
|
16
16
|
return match.group(0) if match else None
|
|
17
17
|
|
|
18
18
|
|
|
19
|
-
def format_duration_hms(total_seconds:
|
|
20
|
-
"""Format a duration
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
if
|
|
27
|
-
|
|
28
|
-
if
|
|
29
|
-
return f"{
|
|
30
|
-
|
|
31
|
-
|
|
19
|
+
def format_duration_hms(total_seconds: float) -> str:
|
|
20
|
+
"""Format a whole-second duration compactly (e.g. ``10s``, ``5m 49s``, ``1h 2m``).
|
|
21
|
+
|
|
22
|
+
Fractional seconds are truncated. Durations under a minute render as ``{s}s``,
|
|
23
|
+
under an hour as ``{m}m {s}s``, and beyond as ``{h}h {m}m``.
|
|
24
|
+
"""
|
|
25
|
+
elapsed = int(total_seconds)
|
|
26
|
+
if elapsed < 0:
|
|
27
|
+
elapsed = 0
|
|
28
|
+
if elapsed < 60:
|
|
29
|
+
return f"{elapsed}s"
|
|
30
|
+
minutes = elapsed // 60
|
|
31
|
+
seconds = elapsed % 60
|
|
32
|
+
if minutes < 60:
|
|
33
|
+
return f"{minutes}m {seconds}s"
|
|
34
|
+
hours = minutes // 60
|
|
35
|
+
return f"{hours}h {minutes % 60}m"
|
|
32
36
|
|
|
33
37
|
|
|
34
38
|
AMBIGUITY_CAUSES = {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
[build-system]
|
|
2
|
-
requires = ["hatchling"]
|
|
2
|
+
requires = ["hatchling", "hatch-vcs"]
|
|
3
3
|
build-backend = "hatchling.build"
|
|
4
4
|
|
|
5
5
|
[project]
|
|
@@ -20,13 +20,13 @@ dependencies = [
|
|
|
20
20
|
"requests==2.34.2",
|
|
21
21
|
"tiktoken==0.13.0",
|
|
22
22
|
"PyYAML==6.0.3",
|
|
23
|
-
"gitpython==3.1.
|
|
23
|
+
"gitpython==3.1.52",
|
|
24
24
|
"transitions==0.9.3",
|
|
25
25
|
"textual>=7.5.0",
|
|
26
26
|
"rich==15.0.0",
|
|
27
27
|
"python-frontmatter==1.3.0",
|
|
28
28
|
"networkx==3.6.1",
|
|
29
|
-
"sentry-sdk==2.
|
|
29
|
+
"sentry-sdk==2.66.0",
|
|
30
30
|
]
|
|
31
31
|
|
|
32
32
|
[project.optional-dependencies]
|
|
@@ -35,14 +35,17 @@ dev = [
|
|
|
35
35
|
"flake8==7.3.0",
|
|
36
36
|
"black==26.5.1",
|
|
37
37
|
"isort==8.0.1",
|
|
38
|
-
"mypy==2.
|
|
38
|
+
"mypy==2.3.0",
|
|
39
39
|
]
|
|
40
40
|
|
|
41
41
|
[project.scripts]
|
|
42
42
|
codeplain = "plain2code:main"
|
|
43
43
|
|
|
44
|
+
# Derive the version from the git tag (e.g. v0.3.8 -> 0.3.8). The version is
|
|
45
|
+
# baked into the package metadata at build time; system_config.py reads it back
|
|
46
|
+
# from that metadata (with a git-tag fallback for uninstalled source checkouts).
|
|
44
47
|
[tool.hatch.version]
|
|
45
|
-
|
|
48
|
+
source = "vcs"
|
|
46
49
|
|
|
47
50
|
[tool.hatch.build.targets.wheel]
|
|
48
51
|
include = [
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
from typing import Any
|
|
2
2
|
|
|
3
3
|
import file_utils
|
|
4
|
-
from plain2code_console import console
|
|
4
|
+
from plain2code_console import SUCCESS_COLOR, console
|
|
5
5
|
from render_machine.actions.base_action import BaseAction
|
|
6
6
|
from render_machine.render_context import RenderContext
|
|
7
7
|
|
|
@@ -21,6 +21,6 @@ class CreateDist(BaseAction):
|
|
|
21
21
|
render_context.conformance_tests.get_module_conformance_tests_folder(render_context.module_name),
|
|
22
22
|
render_context.conformance_tests_dest,
|
|
23
23
|
)
|
|
24
|
-
console.info(f"
|
|
24
|
+
console.info(f"✓ Render of module {render_context.module_name} completed successfully.", color=SUCCESS_COLOR)
|
|
25
25
|
|
|
26
26
|
return self.SUCCESSFUL_OUTCOME, None
|
|
@@ -20,7 +20,8 @@ class ExitWithError(BaseAction):
|
|
|
20
20
|
|
|
21
21
|
if render_context.frid_context is not None:
|
|
22
22
|
console.info(
|
|
23
|
-
f"To continue rendering from the last successfully rendered functionality,
|
|
23
|
+
f"To continue rendering from the last successfully rendered functionality, "
|
|
24
|
+
f"provide the --render-from {render_context.frid_context.frid} flag."
|
|
24
25
|
)
|
|
25
26
|
|
|
26
27
|
if render_context.run_state.render_id is not None:
|
|
@@ -4,7 +4,7 @@ import diff_utils
|
|
|
4
4
|
import file_utils
|
|
5
5
|
import plain_spec
|
|
6
6
|
from memory_management import MemoryManager
|
|
7
|
-
from plain2code_console import console
|
|
7
|
+
from plain2code_console import RETRY_COLOR, console
|
|
8
8
|
from plain2code_exceptions import InternalClientError
|
|
9
9
|
from render_machine.actions.base_action import BaseAction
|
|
10
10
|
from render_machine.implementation_code_helpers import ImplementationCodeHelpers
|
|
@@ -144,7 +144,9 @@ class FixConformanceTest(BaseAction):
|
|
|
144
144
|
render_context.conformance_tests_running_context.conflicting_module_name = current_testing_module_name
|
|
145
145
|
render_context.conformance_tests_running_context.conflicting_frid = current_testing_frid
|
|
146
146
|
console.info(
|
|
147
|
-
f"Potential conflicting functionalities detected while fixing conformance tests
|
|
147
|
+
f"↻ Potential conflicting functionalities detected while fixing conformance tests "
|
|
148
|
+
f"for functionality {current_testing_frid} in module {current_testing_module_name}.",
|
|
149
|
+
color=RETRY_COLOR,
|
|
148
150
|
)
|
|
149
151
|
|
|
150
152
|
if issue_reason_code == self.ISSUE_REASON_CODE_CONFORMANCE_TESTS:
|
|
@@ -3,7 +3,7 @@ from typing import Any
|
|
|
3
3
|
import file_utils
|
|
4
4
|
import render_machine.render_utils as render_utils
|
|
5
5
|
from memory_management import MemoryManager
|
|
6
|
-
from plain2code_console import console
|
|
6
|
+
from plain2code_console import RETRY_COLOR, console
|
|
7
7
|
from plain2code_exceptions import FunctionalRequirementTooComplex
|
|
8
8
|
from render_machine.actions.base_action import BaseAction
|
|
9
9
|
from render_machine.implementation_code_helpers import ImplementationCodeHelpers
|
|
@@ -28,8 +28,9 @@ class RenderFunctionalRequirement(BaseAction):
|
|
|
28
28
|
|
|
29
29
|
if render_context.frid_context.functional_requirement_render_attempts > 1:
|
|
30
30
|
console.info(
|
|
31
|
-
f"Unittests could not be fixed after rendering the functionality. "
|
|
32
|
-
f"Restarting rendering functionality {render_context.frid_context.frid} from scratch."
|
|
31
|
+
f"↻ Unittests could not be fixed after rendering the functionality. "
|
|
32
|
+
f"Restarting rendering functionality {render_context.frid_context.frid} from scratch.",
|
|
33
|
+
color=RETRY_COLOR,
|
|
33
34
|
)
|
|
34
35
|
|
|
35
36
|
render_utils.revert_changes_for_frid(render_context)
|
|
@@ -7,7 +7,7 @@ import git_utils
|
|
|
7
7
|
import plain_spec
|
|
8
8
|
from codeplain_REST_api import CodeplainAPI
|
|
9
9
|
from event_bus import EventBus
|
|
10
|
-
from plain2code_console import console
|
|
10
|
+
from plain2code_console import RETRY_COLOR, console
|
|
11
11
|
from plain2code_events import RenderContextSnapshot
|
|
12
12
|
from plain2code_state import RunState
|
|
13
13
|
from plain_modules import PlainModule
|
|
@@ -283,9 +283,11 @@ class RenderContext:
|
|
|
283
283
|
self.dispatch_error(error_msg)
|
|
284
284
|
else:
|
|
285
285
|
console.info(
|
|
286
|
-
f"Failed to adjust the unit tests after implementation code was updated while fixing the
|
|
286
|
+
f"↻ Failed to adjust the unit tests after implementation code was updated while fixing the "
|
|
287
|
+
f"conformance tests for functionality {self.frid_context.frid}. "
|
|
288
|
+
f"Restarting rendering the functionality {self.frid_context.frid} from scratch.",
|
|
289
|
+
color=RETRY_COLOR,
|
|
287
290
|
)
|
|
288
|
-
console.info(f"Restarting rendering the functionality {self.frid_context.frid} from scratch.")
|
|
289
291
|
self.machine.dispatch(triggers.RESTART_FRID_PROCESSING)
|
|
290
292
|
|
|
291
293
|
def _on_unit_test_limit_exceeded_in_refactoring(self):
|