codeplain 0.2.3__tar.gz → 0.2.5__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.2.5/.github/workflows/publish-install-script.yml +39 -0
- {codeplain-0.2.3 → codeplain-0.2.5}/.gitignore +5 -3
- {codeplain-0.2.3 → codeplain-0.2.5}/PKG-INFO +3 -3
- {codeplain-0.2.3 → codeplain-0.2.5}/codeplain_REST_api.py +57 -43
- codeplain-0.2.5/config/system_config.yaml +12 -0
- {codeplain-0.2.3 → codeplain-0.2.5}/examples/example_hello_world_golang/hello_world_golang.plain +1 -1
- {codeplain-0.2.3 → codeplain-0.2.5}/examples/example_hello_world_python/hello_world_python.plain +1 -1
- codeplain-0.2.5/examples/example_hello_world_react/config.yaml +2 -0
- {codeplain-0.2.3 → codeplain-0.2.5}/file_utils.py +5 -4
- {codeplain-0.2.3 → codeplain-0.2.5}/git_utils.py +43 -13
- codeplain-0.2.5/install/examples.sh +92 -0
- {codeplain-0.2.3 → codeplain-0.2.5/install}/install.sh +112 -17
- codeplain-0.2.5/install/walkthrough.sh +178 -0
- {codeplain-0.2.3 → codeplain-0.2.5}/memory_management.py +1 -1
- {codeplain-0.2.3 → codeplain-0.2.5}/module_renderer.py +114 -0
- {codeplain-0.2.3 → codeplain-0.2.5}/plain2code.py +91 -30
- {codeplain-0.2.3 → codeplain-0.2.5}/plain2code_console.py +3 -5
- {codeplain-0.2.3 → codeplain-0.2.5}/plain2code_exceptions.py +26 -6
- {codeplain-0.2.3 → codeplain-0.2.5}/plain2code_logger.py +11 -5
- {codeplain-0.2.3 → codeplain-0.2.5}/plain2code_utils.py +7 -5
- {codeplain-0.2.3 → codeplain-0.2.5}/plain_file.py +15 -37
- {codeplain-0.2.3 → codeplain-0.2.5}/plain_modules.py +1 -4
- {codeplain-0.2.3 → codeplain-0.2.5}/plain_spec.py +24 -6
- {codeplain-0.2.3 → codeplain-0.2.5}/pyproject.toml +3 -3
- {codeplain-0.2.3 → codeplain-0.2.5}/render_machine/actions/create_dist.py +1 -1
- {codeplain-0.2.3 → codeplain-0.2.5}/render_machine/actions/exit_with_error.py +1 -1
- {codeplain-0.2.3 → codeplain-0.2.5}/render_machine/actions/prepare_testing_environment.py +1 -1
- {codeplain-0.2.3 → codeplain-0.2.5}/render_machine/actions/render_conformance_tests.py +2 -4
- {codeplain-0.2.3 → codeplain-0.2.5}/render_machine/actions/render_functional_requirement.py +6 -6
- {codeplain-0.2.3 → codeplain-0.2.5}/render_machine/actions/run_conformance_tests.py +3 -2
- {codeplain-0.2.3 → codeplain-0.2.5}/render_machine/actions/run_unit_tests.py +1 -1
- {codeplain-0.2.3 → codeplain-0.2.5}/render_machine/render_context.py +3 -3
- {codeplain-0.2.3 → codeplain-0.2.5}/render_machine/render_utils.py +14 -6
- {codeplain-0.2.3 → codeplain-0.2.5}/requirements.txt +2 -2
- {codeplain-0.2.3 → codeplain-0.2.5}/standard_template_library/golang-console-app-template.plain +2 -2
- {codeplain-0.2.3 → codeplain-0.2.5}/standard_template_library/python-console-app-template.plain +2 -2
- {codeplain-0.2.3 → codeplain-0.2.5}/standard_template_library/typescript-react-app-template.plain +2 -2
- {codeplain-0.2.3 → codeplain-0.2.5}/system_config.py +3 -11
- {codeplain-0.2.3 → codeplain-0.2.5}/test_scripts/run_unittests_python.sh +1 -1
- {codeplain-0.2.3 → codeplain-0.2.5}/tests/data/imports/circular_imports_2.plain +1 -1
- {codeplain-0.2.3 → codeplain-0.2.5}/tests/data/imports/circular_imports_main.plain +1 -1
- {codeplain-0.2.3 → codeplain-0.2.5}/tests/data/imports/diamond_import_1.plain +2 -2
- {codeplain-0.2.3 → codeplain-0.2.5}/tests/data/imports/diamond_import_2.plain +2 -2
- {codeplain-0.2.3 → codeplain-0.2.5}/tests/data/imports/diamond_import_common.plain +2 -2
- {codeplain-0.2.3 → codeplain-0.2.5}/tests/data/imports/diamond_imports_main.plain +1 -1
- {codeplain-0.2.3 → codeplain-0.2.5}/tests/data/imports/non_existent_import.plain +1 -1
- {codeplain-0.2.3 → codeplain-0.2.5}/tests/data/plainfile/invalid_specification_order.plain +1 -1
- {codeplain-0.2.3 → codeplain-0.2.5}/tests/data/plainfile/plain_source_with_absolute_link.plain +1 -1
- {codeplain-0.2.3 → codeplain-0.2.5}/tests/data/plainfile/plain_source_with_url_link.plain +1 -1
- {codeplain-0.2.3 → codeplain-0.2.5}/tests/data/plainfile/task_manager_with_reference_links.plain +2 -2
- {codeplain-0.2.3 → codeplain-0.2.5}/tests/data/plainfile/without_non_functional_requirement.plain +1 -1
- {codeplain-0.2.3 → codeplain-0.2.5}/tests/data/plainfileparser/concept_validation_acceptance_tests.plain +2 -2
- {codeplain-0.2.3 → codeplain-0.2.5}/tests/data/plainfileparser/concept_validation_acceptance_tests_nondefined.plain +2 -2
- {codeplain-0.2.3 → codeplain-0.2.5}/tests/data/plainfileparser/concept_validation_defined_nondefined.plain +2 -2
- {codeplain-0.2.3 → codeplain-0.2.5}/tests/data/plainfileparser/concept_validation_defined_nondefined_2.plain +2 -2
- {codeplain-0.2.3 → codeplain-0.2.5}/tests/data/plainfileparser/concept_validation_definition.plain +2 -2
- {codeplain-0.2.3 → codeplain-0.2.5}/tests/data/plainfileparser/concept_validation_noconcepts.plain +2 -2
- {codeplain-0.2.3 → codeplain-0.2.5}/tests/data/plainfileparser/concept_validation_nonconcept.plain +2 -2
- {codeplain-0.2.3 → codeplain-0.2.5}/tests/data/plainfileparser/concept_validation_nondefined.plain +2 -2
- {codeplain-0.2.3 → codeplain-0.2.5}/tests/data/plainfileparser/concept_validation_redefinition.plain +2 -2
- {codeplain-0.2.3 → codeplain-0.2.5}/tests/data/plainfileparser/concept_validation_several_concepts.plain +1 -1
- {codeplain-0.2.3 → codeplain-0.2.5}/tests/data/plainfileparser/concept_validation_valid.plain +2 -2
- {codeplain-0.2.3 → codeplain-0.2.5}/tests/data/plainfileparser/exported_concepts.plain +1 -1
- {codeplain-0.2.3 → codeplain-0.2.5}/tests/data/plainfileparser/exported_concepts_example.plain +1 -1
- {codeplain-0.2.3 → codeplain-0.2.5}/tests/data/plainfileparser/exported_concepts_missing.plain +1 -1
- {codeplain-0.2.3 → codeplain-0.2.5}/tests/data/plainfileparser/exported_concepts_missing_example.plain +1 -1
- {codeplain-0.2.3 → codeplain-0.2.5}/tests/data/plainfileparser/exported_concepts_nested.plain +1 -1
- {codeplain-0.2.3 → codeplain-0.2.5}/tests/data/plainfileparser/exported_concepts_nested_example.plain +1 -1
- {codeplain-0.2.3 → codeplain-0.2.5}/tests/data/plainfileparser/exported_concepts_transitive_example.plain +1 -1
- {codeplain-0.2.3 → codeplain-0.2.5}/tests/data/plainfileparser/exported_concepts_transitive_l1.plain +1 -1
- {codeplain-0.2.3 → codeplain-0.2.5}/tests/data/plainfileparser/exported_concepts_transitive_l2.plain +1 -1
- {codeplain-0.2.3 → codeplain-0.2.5}/tests/data/plainfileparser/plain_file_parser_with_comments.plain +1 -1
- {codeplain-0.2.3 → codeplain-0.2.5}/tests/data/plainfileparser/plain_file_with_comments_indented.plain +1 -1
- {codeplain-0.2.3 → codeplain-0.2.5}/tests/data/plainfileparser/regular_plain_source.plain +1 -1
- {codeplain-0.2.3 → codeplain-0.2.5}/tests/data/plainfileparser/required_concepts_example.plain +1 -1
- {codeplain-0.2.3 → codeplain-0.2.5}/tests/data/plainfileparser/required_concepts_missing.plain +1 -1
- {codeplain-0.2.3 → codeplain-0.2.5}/tests/data/plainfileparser/required_concepts_module.plain +1 -1
- {codeplain-0.2.3 → codeplain-0.2.5}/tests/data/plainfileparser/required_concepts_partial.plain +1 -1
- {codeplain-0.2.3 → codeplain-0.2.5}/tests/data/plainfileparser/required_concepts_partial_duplicate.plain +1 -1
- {codeplain-0.2.3 → codeplain-0.2.5}/tests/data/plainfileparser/topological_sort.plain +1 -1
- {codeplain-0.2.3 → codeplain-0.2.5}/tests/data/plainfileparser/topological_sort_not_referenced.plain +1 -1
- {codeplain-0.2.3 → codeplain-0.2.5}/tests/data/requires/circular_requires_main.plain +1 -1
- {codeplain-0.2.3 → codeplain-0.2.5}/tests/data/requires/diamond_requires_common.plain +1 -1
- {codeplain-0.2.3 → codeplain-0.2.5}/tests/data/requires/diamond_requires_main.plain +1 -1
- {codeplain-0.2.3 → codeplain-0.2.5}/tests/data/requires/independent_requires_main.plain +1 -1
- {codeplain-0.2.3 → codeplain-0.2.5}/tests/data/requires/non_existent_require.plain +1 -1
- {codeplain-0.2.3 → codeplain-0.2.5}/tests/data/requires/normal_requires_common.plain +1 -1
- {codeplain-0.2.3 → codeplain-0.2.5}/tests/data/requires/normal_requires_main.plain +1 -1
- {codeplain-0.2.3 → codeplain-0.2.5}/tests/data/simple.plain +1 -1
- {codeplain-0.2.3 → codeplain-0.2.5}/tests/data/templates/block_level_include.plain +1 -1
- {codeplain-0.2.3 → codeplain-0.2.5}/tests/data/templates/code_variables.plain +1 -1
- {codeplain-0.2.3 → codeplain-0.2.5}/tests/data/templates/header.plain +1 -1
- {codeplain-0.2.3 → codeplain-0.2.5}/tests/test_imports.py +2 -2
- {codeplain-0.2.3 → codeplain-0.2.5}/tests/test_plainfile.py +2 -2
- {codeplain-0.2.3 → codeplain-0.2.5}/tests/test_plainfileparser.py +10 -10
- {codeplain-0.2.3 → codeplain-0.2.5}/tests/test_plainspec.py +2 -2
- {codeplain-0.2.3 → codeplain-0.2.5}/tests/test_requires.py +2 -1
- codeplain-0.2.5/tui/components.py +580 -0
- {codeplain-0.2.3 → codeplain-0.2.5}/tui/plain2code_tui.py +101 -52
- {codeplain-0.2.3 → codeplain-0.2.5}/tui/state_handlers.py +94 -47
- codeplain-0.2.5/tui/styles.css +401 -0
- {codeplain-0.2.3 → codeplain-0.2.5}/tui/widget_helpers.py +43 -47
- codeplain-0.2.3/.github/workflows/publish-install-script.yml +0 -30
- codeplain-0.2.3/config/system_config.yaml +0 -27
- codeplain-0.2.3/tui/components.py +0 -372
- codeplain-0.2.3/tui/styles.css +0 -213
- {codeplain-0.2.3 → codeplain-0.2.5}/.flake8 +0 -0
- {codeplain-0.2.3 → codeplain-0.2.5}/.github/workflows/lint-and-test.yml +0 -0
- {codeplain-0.2.3 → codeplain-0.2.5}/.github/workflows/nofity-slack-on-main-merge.yml +0 -0
- {codeplain-0.2.3 → codeplain-0.2.5}/.github/workflows/publish-to-pypi.yml +0 -0
- {codeplain-0.2.3 → codeplain-0.2.5}/.vscode/launch.json +0 -0
- {codeplain-0.2.3 → codeplain-0.2.5}/.vscode/settings.json +0 -0
- {codeplain-0.2.3 → codeplain-0.2.5}/LICENSE +0 -0
- {codeplain-0.2.3 → codeplain-0.2.5}/README.md +0 -0
- {codeplain-0.2.3 → codeplain-0.2.5}/concept_utils.py +0 -0
- {codeplain-0.2.3 → codeplain-0.2.5}/config/__init__.py +0 -0
- {codeplain-0.2.3 → codeplain-0.2.5}/diff_utils.py +0 -0
- {codeplain-0.2.3 → codeplain-0.2.5}/docs/generate_cli.py +0 -0
- {codeplain-0.2.3 → codeplain-0.2.5}/docs/plain2code_cli.md +0 -0
- {codeplain-0.2.3 → codeplain-0.2.5}/docs/plain_language_specification.md +0 -0
- {codeplain-0.2.3 → codeplain-0.2.5}/docs/starting_a_plain_project_from_scratch.md +0 -0
- {codeplain-0.2.3 → codeplain-0.2.5}/event_bus.py +0 -0
- {codeplain-0.2.3 → codeplain-0.2.5}/examples/example_hello_world_golang/config.yaml +0 -0
- {codeplain-0.2.3 → codeplain-0.2.5}/examples/example_hello_world_golang/harness_tests/hello_world_test.go +0 -0
- {codeplain-0.2.3 → codeplain-0.2.5}/examples/example_hello_world_golang/run.sh +0 -0
- {codeplain-0.2.3 → codeplain-0.2.5}/examples/example_hello_world_python/config.yaml +0 -0
- {codeplain-0.2.3 → codeplain-0.2.5}/examples/example_hello_world_python/harness_tests/hello_world_display/test_hello_world.py +0 -0
- {codeplain-0.2.3 → codeplain-0.2.5}/examples/example_hello_world_python/run.sh +0 -0
- {codeplain-0.2.3 → codeplain-0.2.5}/examples/example_hello_world_react/harness_tests/hello_world_display/cypress/e2e/hello_world.cy.ts +0 -0
- {codeplain-0.2.3 → codeplain-0.2.5}/examples/example_hello_world_react/harness_tests/hello_world_display/cypress/support/e2e.ts +0 -0
- {codeplain-0.2.3 → codeplain-0.2.5}/examples/example_hello_world_react/harness_tests/hello_world_display/cypress.config.ts +0 -0
- {codeplain-0.2.3 → codeplain-0.2.5}/examples/example_hello_world_react/harness_tests/hello_world_display/package.json +0 -0
- {codeplain-0.2.3 → codeplain-0.2.5}/examples/example_hello_world_react/harness_tests/hello_world_display/tsconfig.json +0 -0
- {codeplain-0.2.3 → codeplain-0.2.5}/examples/example_hello_world_react/hello_world_react.plain +0 -0
- {codeplain-0.2.3 → codeplain-0.2.5}/examples/example_hello_world_react/run.sh +0 -0
- {codeplain-0.2.3 → codeplain-0.2.5}/examples/run.sh +0 -0
- {codeplain-0.2.3 → codeplain-0.2.5}/hash_key.py +0 -0
- {codeplain-0.2.3 → codeplain-0.2.5}/plain2code_arguments.py +0 -0
- {codeplain-0.2.3 → codeplain-0.2.5}/plain2code_events.py +0 -0
- {codeplain-0.2.3 → codeplain-0.2.5}/plain2code_nodes.py +0 -0
- {codeplain-0.2.3 → codeplain-0.2.5}/plain2code_read_config.py +0 -0
- {codeplain-0.2.3 → codeplain-0.2.5}/plain2code_state.py +0 -0
- {codeplain-0.2.3 → codeplain-0.2.5}/pytest.ini +0 -0
- {codeplain-0.2.3 → codeplain-0.2.5}/render_machine/__init__.py +0 -0
- {codeplain-0.2.3 → codeplain-0.2.5}/render_machine/actions/analyze_specification_ambiguity.py +0 -0
- {codeplain-0.2.3 → codeplain-0.2.5}/render_machine/actions/base_action.py +0 -0
- {codeplain-0.2.3 → codeplain-0.2.5}/render_machine/actions/commit_conformance_tests_changes.py +0 -0
- {codeplain-0.2.3 → codeplain-0.2.5}/render_machine/actions/commit_implementation_code_changes.py +0 -0
- {codeplain-0.2.3 → codeplain-0.2.5}/render_machine/actions/finish_functional_requirement.py +0 -0
- {codeplain-0.2.3 → codeplain-0.2.5}/render_machine/actions/fix_conformance_test.py +0 -0
- {codeplain-0.2.3 → codeplain-0.2.5}/render_machine/actions/fix_unit_tests.py +0 -0
- {codeplain-0.2.3 → codeplain-0.2.5}/render_machine/actions/prepare_repositories.py +0 -0
- {codeplain-0.2.3 → codeplain-0.2.5}/render_machine/actions/refactor_code.py +0 -0
- {codeplain-0.2.3 → codeplain-0.2.5}/render_machine/actions/summarize_conformance_tests.py +0 -0
- {codeplain-0.2.3 → codeplain-0.2.5}/render_machine/code_renderer.py +0 -0
- {codeplain-0.2.3 → codeplain-0.2.5}/render_machine/conformance_test_helpers.py +0 -0
- {codeplain-0.2.3 → codeplain-0.2.5}/render_machine/conformance_tests.py +0 -0
- {codeplain-0.2.3 → codeplain-0.2.5}/render_machine/implementation_code_helpers.py +0 -0
- {codeplain-0.2.3 → codeplain-0.2.5}/render_machine/render_types.py +0 -0
- {codeplain-0.2.3 → codeplain-0.2.5}/render_machine/state_machine_config.py +0 -0
- {codeplain-0.2.3 → codeplain-0.2.5}/render_machine/states.py +0 -0
- {codeplain-0.2.3 → codeplain-0.2.5}/render_machine/triggers.py +0 -0
- {codeplain-0.2.3 → codeplain-0.2.5}/resources/codeplain_overview.png +0 -0
- {codeplain-0.2.3 → codeplain-0.2.5}/resources/plain_example.png +0 -0
- {codeplain-0.2.3 → codeplain-0.2.5}/standard_template_library/__init__.py +0 -0
- {codeplain-0.2.3 → codeplain-0.2.5}/standard_template_library/typescript-react-app-boilerplate.plain +0 -0
- {codeplain-0.2.3 → codeplain-0.2.5}/test_scripts/run_conformance_tests_cypress.sh +0 -0
- {codeplain-0.2.3 → codeplain-0.2.5}/test_scripts/run_conformance_tests_golang.sh +0 -0
- {codeplain-0.2.3 → codeplain-0.2.5}/test_scripts/run_conformance_tests_python.sh +0 -0
- {codeplain-0.2.3 → codeplain-0.2.5}/test_scripts/run_unittests_golang.sh +0 -0
- {codeplain-0.2.3 → codeplain-0.2.5}/test_scripts/run_unittests_react.sh +0 -0
- {codeplain-0.2.3 → codeplain-0.2.5}/tests/__init__.py +0 -0
- {codeplain-0.2.3 → codeplain-0.2.5}/tests/conftest.py +0 -0
- {codeplain-0.2.3 → codeplain-0.2.5}/tests/data/imports/circular_imports_1.plain +0 -0
- {codeplain-0.2.3 → codeplain-0.2.5}/tests/data/plainfile/duplicate_specification_heading.plain +0 -0
- {codeplain-0.2.3 → codeplain-0.2.5}/tests/data/plainfile/missing_non_functional_requirements.plain +0 -0
- {codeplain-0.2.3 → codeplain-0.2.5}/tests/data/plainfileparser/exported_concepts_base.plain +0 -0
- {codeplain-0.2.3 → codeplain-0.2.5}/tests/data/plainfileparser/required_concepts.plain +0 -0
- {codeplain-0.2.3 → codeplain-0.2.5}/tests/data/plainfileparser/required_concepts_defs.plain +0 -0
- {codeplain-0.2.3 → codeplain-0.2.5}/tests/data/plainfileparser/required_concepts_l1.plain +0 -0
- {codeplain-0.2.3 → codeplain-0.2.5}/tests/data/plainfileparser/required_concepts_l2.plain +0 -0
- {codeplain-0.2.3 → codeplain-0.2.5}/tests/data/requires/circular_requires_sub.plain +0 -0
- {codeplain-0.2.3 → codeplain-0.2.5}/tests/data/requires/diamond_requires_1.plain +0 -0
- {codeplain-0.2.3 → codeplain-0.2.5}/tests/data/requires/diamond_requires_2.plain +0 -0
- {codeplain-0.2.3 → codeplain-0.2.5}/tests/data/requires/independent_requires_1.plain +0 -0
- {codeplain-0.2.3 → codeplain-0.2.5}/tests/data/requires/independent_requires_2.plain +0 -0
- {codeplain-0.2.3 → codeplain-0.2.5}/tests/data/requires/normal_requires_1.plain +0 -0
- {codeplain-0.2.3 → codeplain-0.2.5}/tests/data/requires/normal_requires_2.plain +0 -0
- {codeplain-0.2.3 → codeplain-0.2.5}/tests/data/templates/implement.plain +0 -0
- {codeplain-0.2.3 → codeplain-0.2.5}/tests/data/templates/implement_2.plain +0 -0
- {codeplain-0.2.3 → codeplain-0.2.5}/tests/data/templates/template_include.plain +0 -0
- {codeplain-0.2.3 → codeplain-0.2.5}/tests/data/templates/test_hardest_problem.plain +0 -0
- {codeplain-0.2.3 → codeplain-0.2.5}/tests/test_git_utils.py +0 -0
- {codeplain-0.2.3 → codeplain-0.2.5}/tui/__init__.py +0 -0
- {codeplain-0.2.3 → codeplain-0.2.5}/tui/models.py +0 -0
- {codeplain-0.2.3 → codeplain-0.2.5/tui}/spinner.py +0 -0
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
name: Publish Install Scripts to R2
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches:
|
|
6
|
+
- main
|
|
7
|
+
paths:
|
|
8
|
+
- 'install/*.sh'
|
|
9
|
+
workflow_dispatch:
|
|
10
|
+
|
|
11
|
+
jobs:
|
|
12
|
+
publish:
|
|
13
|
+
name: Upload to Cloudflare R2
|
|
14
|
+
runs-on: ubuntu-latest
|
|
15
|
+
|
|
16
|
+
steps:
|
|
17
|
+
- uses: actions/checkout@v4
|
|
18
|
+
|
|
19
|
+
- name: Upload install scripts to R2
|
|
20
|
+
env:
|
|
21
|
+
AWS_ACCESS_KEY_ID: ${{ secrets.R2_ACCESS_KEY_ID }}
|
|
22
|
+
AWS_SECRET_ACCESS_KEY: ${{ secrets.R2_SECRET_ACCESS_KEY }}
|
|
23
|
+
R2_ENDPOINT: ${{ secrets.R2_ENDPOINT }}
|
|
24
|
+
R2_BUCKET: ${{ secrets.R2_BUCKET }}
|
|
25
|
+
run: |
|
|
26
|
+
aws s3 cp install/install.sh s3://${R2_BUCKET}/install/install.sh \
|
|
27
|
+
--endpoint-url "${R2_ENDPOINT}" \
|
|
28
|
+
--content-type "text/plain"
|
|
29
|
+
echo "✓ install.sh uploaded to R2"
|
|
30
|
+
|
|
31
|
+
aws s3 cp install/walkthrough.sh s3://${R2_BUCKET}/install/walkthrough.sh \
|
|
32
|
+
--endpoint-url "${R2_ENDPOINT}" \
|
|
33
|
+
--content-type "text/plain"
|
|
34
|
+
echo "✓ walkthrough.sh uploaded to R2"
|
|
35
|
+
|
|
36
|
+
aws s3 cp install/examples.sh s3://${R2_BUCKET}/install/examples.sh \
|
|
37
|
+
--endpoint-url "${R2_ENDPOINT}" \
|
|
38
|
+
--content-type "text/plain"
|
|
39
|
+
echo "✓ examples.sh uploaded to R2"
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
__pycache__
|
|
2
2
|
.DS_Store
|
|
3
3
|
|
|
4
|
-
|
|
4
|
+
|
|
5
5
|
examples/**/build*/
|
|
6
6
|
examples/**/conformance_tests/
|
|
7
7
|
examples/**/conformance_tests.backup/
|
|
@@ -23,8 +23,10 @@ examples/**/node_plain_modules/
|
|
|
23
23
|
*.log
|
|
24
24
|
|
|
25
25
|
.venv
|
|
26
|
-
build
|
|
27
26
|
dist
|
|
28
27
|
*.egg-info
|
|
29
28
|
|
|
30
|
-
.env
|
|
29
|
+
.env
|
|
30
|
+
|
|
31
|
+
.coverage
|
|
32
|
+
logging_config.yaml
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: codeplain
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.5
|
|
4
4
|
Summary: Transform plain language specifications into working code
|
|
5
5
|
License-File: LICENSE
|
|
6
6
|
Classifier: Environment :: Console
|
|
@@ -14,13 +14,13 @@ Requires-Dist: networkx==3.6.1
|
|
|
14
14
|
Requires-Dist: python-frontmatter==1.1.0
|
|
15
15
|
Requires-Dist: python-liquid2==0.3.0
|
|
16
16
|
Requires-Dist: pyyaml==6.0.2
|
|
17
|
-
Requires-Dist: requests==2.32.
|
|
17
|
+
Requires-Dist: requests==2.32.4
|
|
18
18
|
Requires-Dist: rich==14.2.0
|
|
19
19
|
Requires-Dist: textual==1.0.0
|
|
20
20
|
Requires-Dist: tiktoken==0.12.0
|
|
21
21
|
Requires-Dist: transitions==0.9.3
|
|
22
22
|
Provides-Extra: dev
|
|
23
|
-
Requires-Dist: black==24.
|
|
23
|
+
Requires-Dist: black==24.3.0; extra == 'dev'
|
|
24
24
|
Requires-Dist: flake8==7.0.0; extra == 'dev'
|
|
25
25
|
Requires-Dist: isort==5.13.2; extra == 'dev'
|
|
26
26
|
Requires-Dist: mypy==1.11.2; extra == 'dev'
|
|
@@ -2,7 +2,6 @@ import time
|
|
|
2
2
|
from typing import Optional
|
|
3
3
|
|
|
4
4
|
import requests
|
|
5
|
-
from requests.exceptions import ConnectionError, RequestException, Timeout
|
|
6
5
|
|
|
7
6
|
import plain2code_exceptions
|
|
8
7
|
from plain2code_state import RunState
|
|
@@ -10,6 +9,22 @@ from plain2code_state import RunState
|
|
|
10
9
|
MAX_RETRIES = 4
|
|
11
10
|
RETRY_DELAY = 3
|
|
12
11
|
|
|
12
|
+
# TODO: Handle connection errors
|
|
13
|
+
RETRY_ERROR_CODES = [
|
|
14
|
+
"LLMInternalError",
|
|
15
|
+
]
|
|
16
|
+
|
|
17
|
+
# Mapping from API error codes to exception classes
|
|
18
|
+
ERROR_CODE_EXCEPTIONS = {
|
|
19
|
+
"FunctionalRequirementTooComplex": plain2code_exceptions.FunctionalRequirementTooComplex,
|
|
20
|
+
"ConflictingRequirements": plain2code_exceptions.ConflictingRequirements,
|
|
21
|
+
"CreditBalanceTooLow": plain2code_exceptions.CreditBalanceTooLow,
|
|
22
|
+
"LLMInternalError": plain2code_exceptions.LLMInternalError,
|
|
23
|
+
"MissingResource": plain2code_exceptions.MissingResource,
|
|
24
|
+
"PlainSyntaxError": plain2code_exceptions.PlainSyntaxError,
|
|
25
|
+
"InternalServerError": plain2code_exceptions.InternalServerError,
|
|
26
|
+
}
|
|
27
|
+
|
|
13
28
|
|
|
14
29
|
class CodeplainAPI:
|
|
15
30
|
|
|
@@ -29,12 +44,31 @@ class CodeplainAPI:
|
|
|
29
44
|
run_state.increment_call_count()
|
|
30
45
|
payload["render_state"] = run_state.to_dict()
|
|
31
46
|
|
|
32
|
-
def
|
|
47
|
+
def _raise_for_error_code(self, response_json):
|
|
48
|
+
"""Raise appropriate exception based on error code in response."""
|
|
49
|
+
error_code = response_json.get("error_code")
|
|
50
|
+
if error_code not in ERROR_CODE_EXCEPTIONS:
|
|
51
|
+
return
|
|
52
|
+
|
|
53
|
+
exception_class = ERROR_CODE_EXCEPTIONS[error_code]
|
|
54
|
+
message = response_json.get("message", "")
|
|
55
|
+
|
|
56
|
+
# FunctionalRequirementTooComplex has an extra parameter
|
|
57
|
+
if error_code == "FunctionalRequirementTooComplex":
|
|
58
|
+
raise exception_class(message, response_json.get("proposed_breakdown"))
|
|
59
|
+
|
|
60
|
+
raise exception_class(message)
|
|
61
|
+
|
|
62
|
+
def post_request(
|
|
63
|
+
self, endpoint_url, headers, payload, run_state: Optional[RunState], num_retries: int = MAX_RETRIES
|
|
64
|
+
):
|
|
33
65
|
if run_state is not None:
|
|
34
66
|
self._extend_payload_with_run_state(payload, run_state)
|
|
35
67
|
|
|
36
68
|
retry_delay = RETRY_DELAY
|
|
37
|
-
|
|
69
|
+
response_json = None
|
|
70
|
+
|
|
71
|
+
for attempt in range(num_retries + 1):
|
|
38
72
|
try:
|
|
39
73
|
response = requests.post(endpoint_url, headers=headers, json=payload)
|
|
40
74
|
|
|
@@ -45,53 +79,33 @@ class CodeplainAPI:
|
|
|
45
79
|
raise
|
|
46
80
|
|
|
47
81
|
if response.status_code == requests.codes.bad_request and "error_code" in response_json:
|
|
48
|
-
|
|
49
|
-
raise plain2code_exceptions.FunctionalRequirementTooComplex(
|
|
50
|
-
response_json["message"], response_json.get("proposed_breakdown")
|
|
51
|
-
)
|
|
52
|
-
|
|
53
|
-
if response_json["error_code"] == "ConflictingRequirements":
|
|
54
|
-
raise plain2code_exceptions.ConflictingRequirements(response_json["message"])
|
|
55
|
-
|
|
56
|
-
if response_json["error_code"] == "CreditBalanceTooLow":
|
|
57
|
-
raise plain2code_exceptions.CreditBalanceTooLow(response_json["message"])
|
|
58
|
-
|
|
59
|
-
if response_json["error_code"] == "LLMInternalError":
|
|
60
|
-
raise plain2code_exceptions.LLMInternalError(response_json["message"])
|
|
61
|
-
|
|
62
|
-
if response_json["error_code"] == "MissingResource":
|
|
63
|
-
raise plain2code_exceptions.MissingResource(response_json["message"])
|
|
64
|
-
|
|
65
|
-
if response_json["error_code"] == "PlainSyntaxError":
|
|
66
|
-
raise plain2code_exceptions.PlainSyntaxError(response_json["message"])
|
|
67
|
-
|
|
68
|
-
if response_json["error_code"] == "OnlyRelativeLinksAllowed":
|
|
69
|
-
raise plain2code_exceptions.OnlyRelativeLinksAllowed(response_json["message"])
|
|
70
|
-
|
|
71
|
-
if response_json["error_code"] == "LinkMustHaveTextSpecified":
|
|
72
|
-
raise plain2code_exceptions.LinkMustHaveTextSpecified(response_json["message"])
|
|
73
|
-
|
|
74
|
-
if response_json["error_code"] == "NoRenderFound":
|
|
75
|
-
raise plain2code_exceptions.NoRenderFound(response_json["message"])
|
|
76
|
-
|
|
77
|
-
if response_json["error_code"] == "MultipleRendersFound":
|
|
78
|
-
raise plain2code_exceptions.MultipleRendersFound(response_json["message"])
|
|
82
|
+
self._raise_for_error_code(response_json)
|
|
79
83
|
|
|
80
84
|
response.raise_for_status()
|
|
81
85
|
return response_json
|
|
82
86
|
|
|
83
|
-
except
|
|
84
|
-
if
|
|
85
|
-
|
|
87
|
+
except Exception as e:
|
|
88
|
+
if response_json is not None and "error_code" in response_json:
|
|
89
|
+
if response_json["error_code"] not in RETRY_ERROR_CODES:
|
|
90
|
+
raise e
|
|
91
|
+
|
|
92
|
+
if attempt < num_retries:
|
|
93
|
+
self.console.info(f"Error on attempt {attempt + 1}/{num_retries + 1}: {e}")
|
|
86
94
|
self.console.info(f"Retrying in {retry_delay} seconds...")
|
|
87
95
|
time.sleep(retry_delay)
|
|
88
|
-
# Exponential backoff
|
|
89
|
-
retry_delay *= 2
|
|
96
|
+
retry_delay *= 2 # Exponential backoff
|
|
90
97
|
else:
|
|
91
|
-
self.console.error(f"Max retries ({
|
|
92
|
-
raise
|
|
93
|
-
|
|
94
|
-
|
|
98
|
+
self.console.error(f"Max retries ({num_retries}) exceeded. Last error: {e}")
|
|
99
|
+
raise e
|
|
100
|
+
|
|
101
|
+
def connection_check(self, client_version):
|
|
102
|
+
endpoint_url = f"{self.api_url}/connection_check"
|
|
103
|
+
headers = {"Content-Type": "application/json"}
|
|
104
|
+
payload = {
|
|
105
|
+
"api_key": self.api_key,
|
|
106
|
+
"client_version": client_version,
|
|
107
|
+
}
|
|
108
|
+
return self.post_request(endpoint_url, headers, payload, None, num_retries=0)
|
|
95
109
|
|
|
96
110
|
def render_functional_requirement(
|
|
97
111
|
self,
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
client_version: "0.17.0"
|
|
2
|
+
|
|
3
|
+
error_messages:
|
|
4
|
+
template_not_found:
|
|
5
|
+
message: |
|
|
6
|
+
The required template could not be found. Templates are searched in the following order (highest to lowest precedence):
|
|
7
|
+
|
|
8
|
+
1. The directory containing your .plain file
|
|
9
|
+
2. The directory specified by --template-dir (if provided)
|
|
10
|
+
3. The built-in 'standard_template_library' directory
|
|
11
|
+
|
|
12
|
+
Please ensure that the missing template exists in one of these locations, or specify the correct --template-dir if using custom templates.
|
|
@@ -43,6 +43,8 @@ FILE_EXTENSION_MAPPING = {
|
|
|
43
43
|
".bat": "Batch File",
|
|
44
44
|
}
|
|
45
45
|
|
|
46
|
+
SYSTEM_FOLDERS = [".git", CODEPLAIN_METADATA_FOLDER, CODEPLAIN_MEMORY_SUBFOLDER]
|
|
47
|
+
|
|
46
48
|
|
|
47
49
|
def get_file_type(file_name):
|
|
48
50
|
|
|
@@ -55,10 +57,9 @@ def get_file_type(file_name):
|
|
|
55
57
|
|
|
56
58
|
def list_all_text_files(directory):
|
|
57
59
|
all_files = []
|
|
58
|
-
skip_dirs = [".git", CODEPLAIN_METADATA_FOLDER, CODEPLAIN_MEMORY_SUBFOLDER]
|
|
59
60
|
for root, dirs, files in os.walk(directory, topdown=True):
|
|
60
61
|
# Skip directories that should not be traversed
|
|
61
|
-
for skip_dir in
|
|
62
|
+
for skip_dir in SYSTEM_FOLDERS:
|
|
62
63
|
if skip_dir in dirs:
|
|
63
64
|
dirs.remove(skip_dir)
|
|
64
65
|
|
|
@@ -322,5 +323,5 @@ def copy_folder_to_output(source_folder, output_folder):
|
|
|
322
323
|
if os.path.exists(output_folder):
|
|
323
324
|
delete_files_and_subfolders(output_folder)
|
|
324
325
|
|
|
325
|
-
# Copy source folder contents directly to output folder (excluding
|
|
326
|
-
copy_folder_content(source_folder, output_folder, ignore_folders=
|
|
326
|
+
# Copy source folder contents directly to output folder (excluding SYSTEM_FOLDERS)
|
|
327
|
+
copy_folder_content(source_folder, output_folder, ignore_folders=SYSTEM_FOLDERS)
|
|
@@ -4,6 +4,7 @@ from typing import Optional, Union
|
|
|
4
4
|
from git import Repo
|
|
5
5
|
|
|
6
6
|
import file_utils
|
|
7
|
+
from plain2code_exceptions import InvalidGitRepositoryError
|
|
7
8
|
|
|
8
9
|
FUNCTIONAL_REQUIREMENT_IMPLEMENTED_COMMIT_MESSAGE = (
|
|
9
10
|
"[Codeplain] Implemented code and unit tests for functional requirement {}"
|
|
@@ -25,12 +26,6 @@ MODULE_NAME_MESSAGE = "Module name: {}"
|
|
|
25
26
|
RENDER_ID_MESSAGE = "Render ID: {}"
|
|
26
27
|
|
|
27
28
|
|
|
28
|
-
class InvalidGitRepositoryError(Exception):
|
|
29
|
-
"""Raised when the git repository is in an invalid state."""
|
|
30
|
-
|
|
31
|
-
pass
|
|
32
|
-
|
|
33
|
-
|
|
34
29
|
def _get_full_commit_message(message, module_name, frid, render_id) -> str:
|
|
35
30
|
full_message = message
|
|
36
31
|
|
|
@@ -252,7 +247,10 @@ def diff(repo_path: Union[str, os.PathLike], previous_frid: str = None) -> dict:
|
|
|
252
247
|
|
|
253
248
|
def _get_commit(repo: Repo, frid: Optional[str]) -> str:
|
|
254
249
|
if frid:
|
|
255
|
-
|
|
250
|
+
commit_with_frid = _get_commit_with_frid(repo, frid)
|
|
251
|
+
if not commit_with_frid:
|
|
252
|
+
raise InvalidGitRepositoryError(f"No commit with frid {frid} found.")
|
|
253
|
+
return commit_with_frid
|
|
256
254
|
|
|
257
255
|
base_folder_commit = _get_base_folder_commit(repo)
|
|
258
256
|
initial_commit = _get_initial_commit(repo)
|
|
@@ -261,12 +259,44 @@ def _get_commit(repo: Repo, frid: Optional[str]) -> str:
|
|
|
261
259
|
return initial_commit
|
|
262
260
|
|
|
263
261
|
|
|
264
|
-
def _get_commit_with_frid(repo: Repo, frid: str) -> str:
|
|
265
|
-
"""
|
|
266
|
-
commit
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
262
|
+
def _get_commit_with_frid(repo: Repo, frid: str, module_name: Optional[str] = None) -> str:
|
|
263
|
+
"""
|
|
264
|
+
Finds commit with given frid mentioned in the commit message.
|
|
265
|
+
|
|
266
|
+
Args:
|
|
267
|
+
repo (Repo): Git repository object
|
|
268
|
+
frid (str): Functional requirement ID
|
|
269
|
+
module_name (Optional[str]): Module name to filter by. If provided, only returns
|
|
270
|
+
commits that have both the FRID and module name.
|
|
271
|
+
|
|
272
|
+
Returns:
|
|
273
|
+
str: Commit SHA if found, empty string otherwise
|
|
274
|
+
"""
|
|
275
|
+
commit_message_pattern = FUNCTIONAL_REQUIREMENT_FINISHED_COMMIT_MESSAGE.format(frid)
|
|
276
|
+
|
|
277
|
+
# If no module name filtering is needed, use the original logic
|
|
278
|
+
if not module_name:
|
|
279
|
+
return _get_commit_with_message(repo, commit_message_pattern)
|
|
280
|
+
|
|
281
|
+
# Use multiple grep patterns with --all-match for AND condition
|
|
282
|
+
escaped_frid_message = commit_message_pattern.replace("[", "\\[").replace("]", "\\]")
|
|
283
|
+
module_name_pattern = MODULE_NAME_MESSAGE.format(module_name)
|
|
284
|
+
escaped_module_message = module_name_pattern.replace("[", "\\[").replace("]", "\\]")
|
|
285
|
+
|
|
286
|
+
return repo.git.rev_list(
|
|
287
|
+
repo.active_branch.name,
|
|
288
|
+
"--grep",
|
|
289
|
+
escaped_frid_message,
|
|
290
|
+
"--grep",
|
|
291
|
+
escaped_module_message,
|
|
292
|
+
"--all-match",
|
|
293
|
+
"-n",
|
|
294
|
+
"1",
|
|
295
|
+
)
|
|
296
|
+
|
|
297
|
+
|
|
298
|
+
def has_commit_for_frid(repo_path: Union[str, os.PathLike], frid: str, module_name: Optional[str] = None) -> bool:
|
|
299
|
+
return bool(_get_commit_with_frid(Repo(repo_path), frid, module_name))
|
|
270
300
|
|
|
271
301
|
|
|
272
302
|
def _get_base_folder_commit(repo: Repo) -> str:
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
|
|
3
|
+
set -euo pipefail
|
|
4
|
+
|
|
5
|
+
# Brand Colors (use exported colors if available, otherwise define them)
|
|
6
|
+
YELLOW="${YELLOW:-\033[38;2;224;255;110m}"
|
|
7
|
+
GREEN="${GREEN:-\033[38;2;121;252;150m}"
|
|
8
|
+
RED="${RED:-\033[38;2;239;68;68m}"
|
|
9
|
+
GRAY="${GRAY:-\033[38;2;128;128;128m}"
|
|
10
|
+
BOLD="${BOLD:-\033[1m}"
|
|
11
|
+
NC="${NC:-\033[0m}"
|
|
12
|
+
|
|
13
|
+
# Examples configuration
|
|
14
|
+
EXAMPLES_FOLDER_NAME="plainlang-examples"
|
|
15
|
+
EXAMPLES_DOWNLOAD_URL="https://github.com/Codeplain-ai/plainlang-examples/archive/refs/tags/0.1.zip"
|
|
16
|
+
|
|
17
|
+
# Show current directory and ask for extraction path
|
|
18
|
+
CURRENT_DIR=$(pwd)
|
|
19
|
+
echo -e " current folder: ${YELLOW}${CURRENT_DIR}${NC}"
|
|
20
|
+
echo ""
|
|
21
|
+
echo -e " extract examples here, or enter a different path:"
|
|
22
|
+
echo ""
|
|
23
|
+
read -r -p " [Enter for current, or type path]: " EXTRACT_PATH < /dev/tty
|
|
24
|
+
echo ""
|
|
25
|
+
|
|
26
|
+
# Use current directory if empty
|
|
27
|
+
if [ -z "${EXTRACT_PATH:-}" ]; then
|
|
28
|
+
EXTRACT_PATH="$CURRENT_DIR"
|
|
29
|
+
fi
|
|
30
|
+
|
|
31
|
+
# Expand ~ to home directory
|
|
32
|
+
EXTRACT_PATH="${EXTRACT_PATH/#\~/$HOME}"
|
|
33
|
+
|
|
34
|
+
SKIP_DOWNLOAD=false
|
|
35
|
+
|
|
36
|
+
# Check if directory exists, create if not
|
|
37
|
+
if [ ! -d "$EXTRACT_PATH" ]; then
|
|
38
|
+
echo -e " ${GRAY}creating directory...${NC}"
|
|
39
|
+
mkdir -p "$EXTRACT_PATH" 2>/dev/null
|
|
40
|
+
if [ $? -ne 0 ]; then
|
|
41
|
+
echo -e " ${RED}✗${NC} failed to create directory: ${EXTRACT_PATH}"
|
|
42
|
+
echo -e " ${GRAY}skipping example download.${NC}"
|
|
43
|
+
SKIP_DOWNLOAD=true
|
|
44
|
+
fi
|
|
45
|
+
fi
|
|
46
|
+
|
|
47
|
+
if [ "$SKIP_DOWNLOAD" = false ]; then
|
|
48
|
+
echo -e " ${GRAY}downloading examples...${NC}"
|
|
49
|
+
|
|
50
|
+
# Download the zip file
|
|
51
|
+
TEMP_ZIP=$(mktemp)
|
|
52
|
+
curl -L -s -o "$TEMP_ZIP" "$EXAMPLES_DOWNLOAD_URL"
|
|
53
|
+
|
|
54
|
+
if [ $? -eq 0 ] && [ -s "$TEMP_ZIP" ]; then
|
|
55
|
+
echo -e " ${GRAY}extracting to ${EXTRACT_PATH}...${NC}"
|
|
56
|
+
|
|
57
|
+
# Extract the zip file
|
|
58
|
+
unzip -q -o "$TEMP_ZIP" -d "$EXTRACT_PATH" 2>/dev/null
|
|
59
|
+
|
|
60
|
+
if [ $? -eq 0 ]; then
|
|
61
|
+
# Find and rename extracted directory to remove version number
|
|
62
|
+
EXTRACTED_DIR="${EXTRACT_PATH}/${EXAMPLES_FOLDER_NAME}"
|
|
63
|
+
VERSIONED_DIR=$(find "$EXTRACT_PATH" -maxdepth 1 -type d -name "${EXAMPLES_FOLDER_NAME}-*" | head -1)
|
|
64
|
+
if [ -n "$VERSIONED_DIR" ]; then
|
|
65
|
+
rm -rf "$EXTRACTED_DIR" 2>/dev/null # Remove existing if present
|
|
66
|
+
mv "$VERSIONED_DIR" "$EXTRACTED_DIR"
|
|
67
|
+
fi
|
|
68
|
+
|
|
69
|
+
# Remove the .gitignore file from the root of the extracted directory
|
|
70
|
+
if [ -f "${EXTRACTED_DIR}/.gitignore" ]; then
|
|
71
|
+
rm -f "${EXTRACTED_DIR}/.gitignore"
|
|
72
|
+
fi
|
|
73
|
+
|
|
74
|
+
echo ""
|
|
75
|
+
echo -e " ${GREEN}✓${NC} examples downloaded successfully!"
|
|
76
|
+
echo ""
|
|
77
|
+
echo -e " examples are in: ${YELLOW}${EXTRACTED_DIR}${NC}"
|
|
78
|
+
echo ""
|
|
79
|
+
else
|
|
80
|
+
echo -e " ${RED}✗${NC} failed to extract examples."
|
|
81
|
+
fi
|
|
82
|
+
|
|
83
|
+
# Clean up temp file
|
|
84
|
+
rm -f "$TEMP_ZIP"
|
|
85
|
+
else
|
|
86
|
+
echo -e " ${RED}✗${NC} failed to download examples."
|
|
87
|
+
rm -f "$TEMP_ZIP"
|
|
88
|
+
fi
|
|
89
|
+
|
|
90
|
+
echo ""
|
|
91
|
+
read -r -p " press [Enter] to continue..." < /dev/tty
|
|
92
|
+
fi
|
|
@@ -2,6 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
set -euo pipefail
|
|
4
4
|
|
|
5
|
+
# Base URL for additional scripts
|
|
6
|
+
CODEPLAIN_SCRIPTS_BASE_URL="${CODEPLAIN_SCRIPTS_BASE_URL:-https://codeplain.ai}"
|
|
7
|
+
|
|
5
8
|
# Brand Colors (True Color / 24-bit)
|
|
6
9
|
YELLOW='\033[38;2;224;255;110m' # #E0FF6E
|
|
7
10
|
GREEN='\033[38;2;121;252;150m' # #79FC96
|
|
@@ -16,6 +19,10 @@ GRAY_LIGHT='\033[38;2;211;211;211m' # #D3D3D3
|
|
|
16
19
|
BOLD='\033[1m'
|
|
17
20
|
NC='\033[0m' # No Color / Reset
|
|
18
21
|
|
|
22
|
+
# Export colors for child scripts
|
|
23
|
+
export YELLOW GREEN GREEN_LIGHT GREEN_DARK BLUE BLACK WHITE RED GRAY GRAY_LIGHT BOLD NC
|
|
24
|
+
|
|
25
|
+
clear
|
|
19
26
|
echo -e "started ${YELLOW}${BOLD}*codeplain CLI${NC} installation..."
|
|
20
27
|
|
|
21
28
|
# Install uv if not present
|
|
@@ -54,17 +61,36 @@ else
|
|
|
54
61
|
echo -e "installing codeplain...${NC}"
|
|
55
62
|
echo -e ""
|
|
56
63
|
uv tool install codeplain
|
|
64
|
+
clear
|
|
57
65
|
echo -e "${GREEN}✓ codeplain installed successfully!${NC}"
|
|
58
66
|
fi
|
|
59
67
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
echo ""
|
|
64
|
-
|
|
65
|
-
echo ""
|
|
68
|
+
# Check if API key already exists
|
|
69
|
+
SKIP_API_KEY_SETUP=false
|
|
70
|
+
if [ -n "${CODEPLAIN_API_KEY:-}" ]; then
|
|
71
|
+
echo -e " you already have an API key configured."
|
|
72
|
+
echo ""
|
|
73
|
+
echo -e " would you like to log in and get a new one?"
|
|
74
|
+
echo ""
|
|
75
|
+
read -r -p " [y/N]: " GET_NEW_KEY < /dev/tty
|
|
76
|
+
echo ""
|
|
66
77
|
|
|
67
|
-
if [
|
|
78
|
+
if [[ ! "$GET_NEW_KEY" =~ ^[Yy]$ ]]; then
|
|
79
|
+
echo -e "${GREEN}✓${NC} using existing API key."
|
|
80
|
+
SKIP_API_KEY_SETUP=true
|
|
81
|
+
fi
|
|
82
|
+
fi
|
|
83
|
+
|
|
84
|
+
if [ "$SKIP_API_KEY_SETUP" = false ]; then
|
|
85
|
+
echo -e "go to ${YELLOW}https://platform.codeplain.ai${NC} and sign up to get your API key."
|
|
86
|
+
echo ""
|
|
87
|
+
read -r -p "paste your API key here: " API_KEY < /dev/tty
|
|
88
|
+
echo ""
|
|
89
|
+
fi
|
|
90
|
+
|
|
91
|
+
if [ "$SKIP_API_KEY_SETUP" = true ]; then
|
|
92
|
+
: # API key already set, nothing to do
|
|
93
|
+
elif [ -z "${API_KEY:-}" ]; then
|
|
68
94
|
echo -e "${GRAY}no API key provided. you can set it later with:${NC}"
|
|
69
95
|
echo -e " export CODEPLAIN_API_KEY=\"your_api_key\""
|
|
70
96
|
else
|
|
@@ -74,15 +100,10 @@ else
|
|
|
74
100
|
# Detect user's default shell from $SHELL (works even when script runs in different shell)
|
|
75
101
|
case "$SHELL" in
|
|
76
102
|
*/zsh)
|
|
77
|
-
SHELL_RC="$HOME/.
|
|
103
|
+
SHELL_RC="$HOME/.zshrc"
|
|
78
104
|
;;
|
|
79
105
|
*/bash)
|
|
80
|
-
|
|
81
|
-
# macOS uses .bash_profile for login shells
|
|
82
|
-
SHELL_RC="$HOME/.bash_profile"
|
|
83
|
-
else
|
|
84
|
-
SHELL_RC="$HOME/.bashrc"
|
|
85
|
-
fi
|
|
106
|
+
SHELL_RC="$HOME/.bashrc"
|
|
86
107
|
;;
|
|
87
108
|
*)
|
|
88
109
|
SHELL_RC="$HOME/.profile"
|
|
@@ -105,12 +126,11 @@ else
|
|
|
105
126
|
else
|
|
106
127
|
sed -i "s|export CODEPLAIN_API_KEY=.*|export CODEPLAIN_API_KEY=\"$API_KEY\"|" "$SHELL_RC"
|
|
107
128
|
fi
|
|
108
|
-
echo -e "${GREEN}✓${NC} API key added to ${SHELL_RC}"
|
|
109
129
|
fi
|
|
110
|
-
|
|
111
130
|
fi
|
|
112
131
|
|
|
113
132
|
# ASCII Art Welcome
|
|
133
|
+
clear
|
|
114
134
|
echo ""
|
|
115
135
|
echo -e "${NC}"
|
|
116
136
|
echo -e "${GRAY}────────────────────────────────────────────${NC}"
|
|
@@ -124,6 +144,8 @@ cat << 'EOF'
|
|
|
124
144
|
|_|
|
|
125
145
|
EOF
|
|
126
146
|
echo ""
|
|
147
|
+
echo -e "${GREEN}✓${NC} Sign in successful."
|
|
148
|
+
echo ""
|
|
127
149
|
echo -e " ${YELLOW}welcome to *codeplain!${NC}"
|
|
128
150
|
echo ""
|
|
129
151
|
echo -e " spec-driven, production-ready code generation"
|
|
@@ -131,6 +153,79 @@ echo ""
|
|
|
131
153
|
echo ""
|
|
132
154
|
echo -e "${GRAY}────────────────────────────────────────────${NC}"
|
|
133
155
|
echo ""
|
|
156
|
+
echo -e " would you like to get a quick intro to ***plain specification language?"
|
|
157
|
+
echo ""
|
|
158
|
+
read -r -p " [Y/n]: " WALKTHROUGH_CHOICE < /dev/tty
|
|
159
|
+
echo ""
|
|
160
|
+
|
|
161
|
+
# Determine script directory for local execution
|
|
162
|
+
SCRIPT_DIR=""
|
|
163
|
+
if [ -n "${BASH_SOURCE[0]:-}" ] && [ -f "${BASH_SOURCE[0]}" ]; then
|
|
164
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
165
|
+
fi
|
|
166
|
+
|
|
167
|
+
# Helper function to run a script (local or remote)
|
|
168
|
+
run_script() {
|
|
169
|
+
local script_name="$1"
|
|
170
|
+
local script_path=""
|
|
171
|
+
|
|
172
|
+
# Check possible local paths
|
|
173
|
+
if [ -n "$SCRIPT_DIR" ] && [ -f "${SCRIPT_DIR}/${script_name}" ]; then
|
|
174
|
+
script_path="${SCRIPT_DIR}/${script_name}"
|
|
175
|
+
elif [ -f "./install/${script_name}" ]; then
|
|
176
|
+
script_path="./install/${script_name}"
|
|
177
|
+
elif [ -f "./${script_name}" ]; then
|
|
178
|
+
script_path="./${script_name}"
|
|
179
|
+
fi
|
|
180
|
+
|
|
181
|
+
if [ -n "$script_path" ]; then
|
|
182
|
+
# Run locally
|
|
183
|
+
bash "$script_path" < /dev/tty
|
|
184
|
+
else
|
|
185
|
+
# Download and run
|
|
186
|
+
bash <(curl -fsSL "${CODEPLAIN_SCRIPTS_BASE_URL}/${script_name}") < /dev/tty
|
|
187
|
+
fi
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
# Run walkthrough if user agrees
|
|
191
|
+
if [[ ! "$WALKTHROUGH_CHOICE" =~ ^[Nn]$ ]]; then
|
|
192
|
+
run_script "walkthrough.sh"
|
|
193
|
+
fi
|
|
194
|
+
|
|
195
|
+
# Download examples step
|
|
196
|
+
clear
|
|
197
|
+
echo ""
|
|
198
|
+
echo -e "${GRAY}────────────────────────────────────────────${NC}"
|
|
199
|
+
echo -e " ${YELLOW}${BOLD}Example Projects${NC}"
|
|
200
|
+
echo -e "${GRAY}────────────────────────────────────────────${NC}"
|
|
201
|
+
echo ""
|
|
202
|
+
echo -e " we've prepared some example Plain projects for you"
|
|
203
|
+
echo -e " to explore and experiment with."
|
|
204
|
+
echo ""
|
|
205
|
+
echo -e " would you like to download them?"
|
|
206
|
+
echo ""
|
|
207
|
+
read -r -p " [Y/n]: " DOWNLOAD_EXAMPLES < /dev/tty
|
|
208
|
+
echo ""
|
|
209
|
+
|
|
210
|
+
# Run examples download if user agrees
|
|
211
|
+
if [[ ! "${DOWNLOAD_EXAMPLES:-}" =~ ^[Nn]$ ]]; then
|
|
212
|
+
run_script "examples.sh"
|
|
213
|
+
fi
|
|
214
|
+
|
|
215
|
+
# Final message
|
|
216
|
+
clear
|
|
217
|
+
echo ""
|
|
218
|
+
echo -e "${GRAY}────────────────────────────────────────────${NC}"
|
|
219
|
+
echo -e " ${YELLOW}${BOLD}You're all set!${NC}"
|
|
220
|
+
echo -e "${GRAY}────────────────────────────────────────────${NC}"
|
|
221
|
+
echo ""
|
|
134
222
|
echo -e " thank you for using *codeplain!"
|
|
135
223
|
echo ""
|
|
136
|
-
echo -e "
|
|
224
|
+
echo -e " learn more at ${YELLOW}https://plainlang.org/${NC}"
|
|
225
|
+
echo ""
|
|
226
|
+
echo -e " ${GREEN}happy development!${NC} 🚀"
|
|
227
|
+
echo ""
|
|
228
|
+
|
|
229
|
+
# Replace this subshell with a fresh shell that has the new environment
|
|
230
|
+
# Reconnect stdin to terminal (needed when running via curl | bash)
|
|
231
|
+
exec "$SHELL" < /dev/tty
|