codeplain 0.3.5__py3-none-any.whl → 0.3.7__py3-none-any.whl

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.
_version.py CHANGED
@@ -1 +1 @@
1
- __version__ = "0.3.5"
1
+ __version__ = "0.3.7"
@@ -0,0 +1,144 @@
1
+ Metadata-Version: 2.4
2
+ Name: codeplain
3
+ Version: 0.3.7
4
+ Summary: Transform plain language specifications into working code
5
+ License-File: LICENSE
6
+ Classifier: Environment :: Console
7
+ Classifier: Intended Audience :: Developers
8
+ Classifier: Operating System :: OS Independent
9
+ Classifier: Topic :: Software Development :: Code Generators
10
+ Requires-Python: ==3.11.*
11
+ Requires-Dist: gitpython==3.1.50
12
+ Requires-Dist: mistletoe==1.5.1
13
+ Requires-Dist: networkx==3.6.1
14
+ Requires-Dist: python-frontmatter==1.3.0
15
+ Requires-Dist: python-liquid2==0.4.0
16
+ Requires-Dist: pyyaml==6.0.3
17
+ Requires-Dist: requests==2.34.2
18
+ Requires-Dist: rich==15.0.0
19
+ Requires-Dist: sentry-sdk==2.63.0
20
+ Requires-Dist: textual>=7.5.0
21
+ Requires-Dist: tiktoken==0.13.0
22
+ Requires-Dist: transitions==0.9.3
23
+ Provides-Extra: dev
24
+ Requires-Dist: black==26.5.1; extra == 'dev'
25
+ Requires-Dist: flake8==7.3.0; extra == 'dev'
26
+ Requires-Dist: isort==8.0.1; extra == 'dev'
27
+ Requires-Dist: mypy==2.1.0; extra == 'dev'
28
+ Requires-Dist: pytest==9.1.1; extra == 'dev'
29
+ Description-Content-Type: text/markdown
30
+
31
+ # *codeplain renderer
32
+
33
+ ## What is \*codeplain?
34
+
35
+ \*codeplain platform makes coding agents better by having them write and maintain specifications instead of code. It extends coding agents with agentic skills for working at the specification layer, and with tools that convert specifications into tested and validated implementation code.
36
+
37
+ This repository is the renderer that powers that last step, turning \*\*\*plain specs into software using the *codeplain API.
38
+
39
+ The result is a software development workflow centered on specifications, which are faster and more efficient for AI to produce and easier for developers to review and maintain than raw code.
40
+
41
+ Schematic overview of the *codeplain's code rendering:
42
+
43
+ <img src="https://raw.githubusercontent.com/Codeplain-ai/plain2code_client/main/resources/codeplain_overview.png">
44
+
45
+ ## Getting started
46
+
47
+ Get started in 15 minutes.
48
+
49
+ ### 1. Install \*codeplain in the terminal
50
+
51
+ macOS and Linux:
52
+
53
+ ```bash
54
+ curl -fsSL https://codeplain.ai/install.sh | bash
55
+ ```
56
+
57
+ Windows PowerShell:
58
+
59
+ ```powershell
60
+ irm https://codeplain.ai/install.ps1 | iex
61
+ ```
62
+
63
+ **Windows users:** Please install WSL (Windows Subsystem for Linux) as this is currently the supported environment for running plain code on Windows.
64
+
65
+ Follow the installation instructions to complete setup. The installer installs the `codeplain` CLI, walks you through setting your `CODEPLAIN_API_KEY`, and offers to set up [plain-forge](#2-let-your-agent-work-in-specs-not-code) and the [plyn editor extension](#vs-code--cursor-extension) for you in the same session.
66
+
67
+ If you'd rather not run the installer, sign up at [platform.codeplain.ai](https://platform.codeplain.ai) to get your API key and export it yourself:
68
+
69
+ ```bash
70
+ export CODEPLAIN_API_KEY="your_actual_api_key_here"
71
+ ```
72
+
73
+ ### 2. Let your agent work in specs, not code
74
+
75
+ You describe what you want in plain English, an agent helps you write a spec in \*\*\*plain language, and \*codeplain renders the actual code. This is what [plain-forge](https://github.com/Codeplain-ai/plain-forge) sets up: a toolkit of skills, rules, and docs that plugs into your AI coding agent of choice — Claude Code, Codex, ForgeCode, OpenCode, or any other agent that reads from a standard skills directory — and turns a conversation into a complete `.plain` spec, then keeps maintaining it across the lifetime of the project.
76
+
77
+ If you didn't install it during step 1, run it any time:
78
+
79
+ ```bash
80
+ npx plain-forge install
81
+ ```
82
+
83
+ ### 3. Convert specs into tested and validated code
84
+
85
+ \*codeplain extends coding agents with agentic skills for working at the specification layer, and with tools that convert specifications into tested and validated implementation code.
86
+
87
+ ```bash
88
+ codeplain your-project.plain
89
+ ```
90
+
91
+ ### Try it now
92
+
93
+ You can immediately test the system with a simple "Hello World" example:
94
+
95
+ - Change to the example folder and run the example:
96
+
97
+ ```bash
98
+ cd examples/example_hello_world_python
99
+ codeplain hello_world_python.plain
100
+ ```
101
+
102
+ _Note: Rendering will take a few minutes to complete._
103
+
104
+ - The system will generate a Python application in the `build` directory. You can run it with:
105
+ ```bash
106
+ cd build
107
+ python hello_world.py
108
+ ```
109
+
110
+ ### VS Code / Cursor extension
111
+
112
+ If you're using Visual Studio Code or Cursor IDE, we also recommend installing the [plyn extension](https://marketplace.visualstudio.com/items?itemName=Codeplain.plyn) for `***plain` syntax highlighting. The installer in step 1 offers to install it automatically if it detects `code` or `cursor` on your `PATH`.
113
+
114
+ ### Community and support
115
+
116
+ Share what you're building! Join the [\*codeplain Discord](https://discord.gg/cgbynb9hFq) if you have questions, ideas, or want to share what you built.
117
+
118
+ Hit an edge case or have feedback? Reach us at [support@codeplain.ai](mailto:support@codeplain.ai).
119
+
120
+ ## Abstracting Away Code Generation Complexity with \*\*\*plain
121
+
122
+ \*\*\*plain is the language of spec-driven development — a specification language that combines the efficiency of natural language with the control and precision of code.
123
+
124
+ Structured sections for definitions, functional specs, implementation requirements, test requirements, and acceptance tests let your specs (not scattered docs or ad hoc prompts) become the source of truth for your software's behavior, abstracting away the complexity of using large language models for code generation.
125
+
126
+ Specs are plain text, so they fit into the workflows you already use — version control, pull requests, and code review.
127
+
128
+ See the [\*\*\*plain language guide](https://www.plainlang.org/docs/language-guide/) to learn the full syntax.
129
+
130
+ An example application in \*\*\*plain
131
+
132
+ <img src="resources/plain_example.png" width="70%" height="70%">
133
+
134
+ ## Additional Resources
135
+
136
+ ### Examples and Sample Projects
137
+
138
+ - See the [examples](examples) folder for sample projects in Golang, Python, React, and Flutter.
139
+ - For an example of how to implement Pong in \*\*\*plain, see the [pong](https://github.com/Codeplain-ai/plainlang-examples/tree/main/pong) example.
140
+ - For an example of how to implement a task manager in \*\*\*plain, see the [task-manager](https://github.com/Codeplain-ai/plainlang-examples/tree/main/task-manager) example.
141
+
142
+ ### Documentation
143
+
144
+ - For more details on the ***plain format, see the [***plain language specification](https://www.plainlang.org/docs/).
@@ -1,26 +1,27 @@
1
- _version.py,sha256=ThnCuF3X7rsQSd5PAea_jfYA70ZmhLvkFcLBxBPwZnY,22
1
+ _version.py,sha256=J0I0c7-a50EOnWXMryTu_E6xhXSYFBPjVpeYP_a3vRI,22
2
2
  change_detection.py,sha256=CazORZrezeZSw2I6VBvieN-c2SEjieZPo6pNrKwmMVA,6915
3
3
  codeplain_REST_api.py,sha256=h_XoNws8fvf7OTtoVXhHh6OfLGg77UNFuggMBwaqBwE,21533
4
4
  concept_utils.py,sha256=D4qVHFBRvfJZwiIq1AD_SV0Ev-RnOMg2yGWo6Cal7PM,7913
5
5
  diff_utils.py,sha256=AjiQlqo5pRos_8hVXZo5yBurl5BzSrTMGrQv4dCtRCg,1198
6
6
  event_bus.py,sha256=g5XLwRfjrvTIcB6urjREuCJzZj5TA93hy8lKXLGXuCE,650
7
- file_utils.py,sha256=q-ApwiLLJCgmmoy6-nf1QM9DaFXwzCJek6B-jQTI9ak,11665
7
+ file_utils.py,sha256=MNZ5li1ofIMOYB6FCrMa_UkX6nkGCgSZuaqzhqVc1Ok,11682
8
8
  git_utils.py,sha256=LQQnL5DAFpH5fDBQgUZX2WfjWY4YC5OEGBPIXryTmnY,16896
9
9
  memory_management.py,sha256=nHNudgaOqp5UGkIMi62TRCc2nUeFOM0w-I1FfxBFaQE,6120
10
- module_renderer.py,sha256=pXMXKKGMhUwLLlSxppE_hpZlwpJ3WSHSfR9FQxt6rSI,6860
10
+ module_renderer.py,sha256=DZCIo_QzvhFw8EqQTKeF3HUbvfumrV2O1sz-gUhWpIA,6752
11
11
  partial_rendering.py,sha256=0ChOaGytogBUEGISpbWkbjT9Hqy8pnOkJdJXcitJDgk,12219
12
- plain2code.py,sha256=etLa8WagRjtHrWIUt1SYO1cQNAW0sc7AqMqZQTk9gGo,15269
13
- plain2code_arguments.py,sha256=J05NwaY-O04rIWVF2yIXysNB_ofZS0wJFn0u1WaCCSA,15399
14
- plain2code_console.py,sha256=YdE-Kz9qRAzIIAg7aYA1bnXRcwy_aTpbFtkR2zHbd-I,4918
12
+ path_resolution.py,sha256=0tMqU0a1g4DYhTOp7QjLVSEkv_cgasb4NI1mK809dUM,2233
13
+ plain2code.py,sha256=HGv3Or296pZRuqD5jcmuA3QnGl34aJD5f8619M7l5G4,15294
14
+ plain2code_arguments.py,sha256=3tAA9Haw-j97zztjXdhi6LxFtURP0Tfg9J_rvJDoXmw,17685
15
+ plain2code_console.py,sha256=6_PdrVBw9oDccqSGW_gVRYQjVgC_5W3ush4QT2_jGnw,4931
15
16
  plain2code_events.py,sha256=AM8KJS68uiVXRMLt861KaGhs2H4yzujlMB_xiYPfHQc,1478
16
- plain2code_exceptions.py,sha256=WQeBiqKC-gGqKuj_eBbXEtCC3X7mUJAALE9zrDBBEAE,1800
17
- plain2code_logger.py,sha256=kNKIfkS6Ydtut7Ptw5jmv5bFPgXlxB-_Ds5nd-j4d9o,4609
17
+ plain2code_exceptions.py,sha256=wkkEXw2k4LCd09PIH3_Hu0HyZvtXYjFaA653Sfey4FA,2287
18
+ plain2code_logger.py,sha256=RJNhesE9y59lPILvGhiJpAB4snTwfsHpG1Ir6EaPhiQ,4989
18
19
  plain2code_nodes.py,sha256=IwjQqJxRSJKgP6eEnkYGy3VQHIyoazgkFxAEkxkjfJ8,4426
19
20
  plain2code_read_config.py,sha256=D5CxnmTRjddupecWvstcUm0kIJWWzq5-Zmk6sU0wbIg,2235
20
21
  plain2code_state.py,sha256=1VwyxgKdA13fAJNKSiPBfBb3A20TKfGCNnOm06YdcS4,2233
21
22
  plain2code_telemetry.py,sha256=7yuTH7mQE_wGVW2H1oiVZ3UJg98ArB5HcUUm9isnv9g,4354
22
23
  plain2code_utils.py,sha256=id04t9Iez5pYygrE3fKEyrcfWDcniKFT2iQoF2CzTJs,1409
23
- plain_file.py,sha256=WMz4DqlBAGpPa1xqf9vuUj6vzXJTKeNyUtSRz1EOvso,32282
24
+ plain_file.py,sha256=k2rcq53q0kOf9_8egrZILpkrVkZdfJt7HU0gHBFskU4,35116
24
25
  plain_modules.py,sha256=uvRPfxBJwstaWi5lgYKgtQdhhf0Naiy2mS41tLw_TjY,16186
25
26
  plain_spec.py,sha256=7nb5lLJKHZ3uu9THkSXyJ5brIKkbGW0ooVfRclLdlzA,16062
26
27
  system_config.py,sha256=trgytfXKXoCn0Pgy9u00x2D0ZRHlh_tADlbkwZnF8QE,1038
@@ -65,19 +66,25 @@ standard_template_library/typescript-react-app-boilerplate.plain,sha256=6LFxhEOz
65
66
  standard_template_library/typescript-react-app-template.plain,sha256=DN5wEJcT8ZqQTf0vFKi9Z1isi9cin2UVNOZb3r28d3M,715
66
67
  tests/__init__.py,sha256=Wk73Io62J15BtlLVIzxmASDWaaJkQLevS4BLK5LDAQg,16
67
68
  tests/conftest.py,sha256=QZcp08htUlJGgmHDlRWFgsWXZ8o8IBWTD5QqJaUMlU8,790
69
+ tests/test_arg_provenance.py,sha256=Vp-ZtNbTlSNOE8iiHOin3YI9sobbN0tRPUIfrYjxttQ,3459
68
70
  tests/test_change_detection.py,sha256=jH8SEhndX400DNqIKmcYLzxHqq9WFdgmuvY3XPq-ZFc,13534
69
71
  tests/test_cli_output.py,sha256=owDP9pLHdErtPAr1paNKp1o044kuH-HxInHLIBLZQFI,18420
70
72
  tests/test_file_utils.py,sha256=R_ZIhrTtwlhHxgmbBtA8PFEU5-QB4Fi5skJ7PS2KzdU,3614
73
+ tests/test_folder_path_resolution.py,sha256=_ZvVFu5m2-AH8iQNn0fCYkA0flVxVwdh1oQiWcPNIxw,6611
71
74
  tests/test_git_utils.py,sha256=h3rWhhs4XVRYqzVzdmj51siF_AYD-GkbZiefK2iGfWo,15614
72
- tests/test_imports.py,sha256=EWYLUxGunUUdsG9iCiY_tts6T7DIVdhdB_MZlB1LsmU,2081
75
+ tests/test_imports.py,sha256=1ncbqQhtQW237kIjyV-Ufl7-R-PuduvoYFj2iS-DXW4,2608
76
+ tests/test_log_file_name_resolution.py,sha256=99g9NgPF-BZ98CvNyqDcltBAx4sLBE8gpQPAAieKiPc,3619
77
+ tests/test_logging_config_path_resolution.py,sha256=avz_ehRd18INwIXvAy1AwVzNNUz0vS8eu1nRe0bra8g,2650
73
78
  tests/test_partial_rendering.py,sha256=r0RUUd1pI4-2ZkRrM1j8_kUqv-70hrOGjuKBhZoRmH0,22758
79
+ tests/test_path_resolution.py,sha256=yQpd-Unq1lwNv0wkRPwVs5pDT8JmyyDgbpiTQsyt--Y,2615
74
80
  tests/test_plain2code.py,sha256=DQYFc0JVSkMI8LZCNaQ__2Nph-DcZZW17rprJA54eyw,3851
75
81
  tests/test_plain_modules.py,sha256=S7sd4ZYNotXOgCyZzmBd714QFLih69nlZ3tV8WNtVPo,13766
76
82
  tests/test_plainfile.py,sha256=wZ_PN4GULPWIpl_AK_r_6jkgtX0Ch8EHmN5IhX7huOE,11027
77
- tests/test_plainfileparser.py,sha256=DXY7HSKDMaB4nqUwn_T5eyySEZhnfqDjHntkuTsGhY0,21442
83
+ tests/test_plainfileparser.py,sha256=xd9P0KqZJlJPn3RKT510IQbfBotk6nQ9O8p9iLwJiQk,22815
78
84
  tests/test_plainspec.py,sha256=ng55E8ajrH2-gPC1p_QDIe_zXplPvoqHJ-d57UWKFTw,3577
79
85
  tests/test_requires.py,sha256=6tnHYaKudxN-RiQjSFoi9nHe_x0ze3QF-f-j67zAE74,1250
80
86
  tests/test_resolve_config_file.py,sha256=b87tDOvyzbsnE-QRWbZ97ou7NuyaAgskv-dbYBum31c,2830
87
+ tests/test_script_path_resolution.py,sha256=tiEqMevQRXr9YpfHN0xldOOQpLAUgnSqTzeZWkKHaUY,4156
81
88
  tests/test_telemetry.py,sha256=hjm81aRQQZcZ0RB6aVbS3WO46_nIAii_csmbbtnUh74,7110
82
89
  tests/e2e/conftest.py,sha256=5Dm67UTOE-M6hMb-mqZvwa6Ek_wmZgsR7fUv9ZEqCwY,4164
83
90
  tests/e2e/test_hello_world_python.py,sha256=PQy4elzB7WvpiWLKa6_HFO0K8HA6_6hGLaAqRi0J3OU,1627
@@ -91,8 +98,8 @@ tui/spinner.py,sha256=Ro6Gd9Przf-whuHqPRY6HwI0T57yJjyNPbhDbigZKZE,2471
91
98
  tui/state_handlers.py,sha256=4r_6m5YMcprQtgiTP0De3XFfGAF8VzgaWXHxPweYPbQ,16325
92
99
  tui/styles.css,sha256=c13WY1nOgmtkT_FH9Ad2PjXrfi8LYwr6zzD6rBOh9tU,6685
93
100
  tui/widget_helpers.py,sha256=mWDPrALsxLOzJg8KD9MXXafJKRiqG-EIp797jbOT77o,5972
94
- codeplain-0.3.5.dist-info/METADATA,sha256=Jr8-9-HWWeLthEkBUPqOklrg_gvyrjehDlD_GZUWiUw,5380
95
- codeplain-0.3.5.dist-info/WHEEL,sha256=mffPy8wBnZQn2VnJUU5jE99KsxaSfiyMHV9Yt0aLVxs,87
96
- codeplain-0.3.5.dist-info/entry_points.txt,sha256=oDZkBqu9WhtZApb_K6ia8-fn9aojwmAsgnKELceX5T4,46
97
- codeplain-0.3.5.dist-info/licenses/LICENSE,sha256=pCeKgQ1mXE5OmNUuKOfOZh1T1vqkeCUxghE7N8qgnzc,11345
98
- codeplain-0.3.5.dist-info/RECORD,,
101
+ codeplain-0.3.7.dist-info/METADATA,sha256=Gs_i0uMjyodZ11UAyrWIHyAp9vY4Zw_-todAZsYLRKg,6351
102
+ codeplain-0.3.7.dist-info/WHEEL,sha256=lCkmxWfQsSc9CfIClYeavTdQeEX2toPqufh9gI35EQA,87
103
+ codeplain-0.3.7.dist-info/entry_points.txt,sha256=oDZkBqu9WhtZApb_K6ia8-fn9aojwmAsgnKELceX5T4,46
104
+ codeplain-0.3.7.dist-info/licenses/LICENSE,sha256=pCeKgQ1mXE5OmNUuKOfOZh1T1vqkeCUxghE7N8qgnzc,11345
105
+ codeplain-0.3.7.dist-info/RECORD,,
@@ -1,4 +1,4 @@
1
1
  Wheel-Version: 1.0
2
- Generator: hatchling 1.30.1
2
+ Generator: hatchling 1.31.0
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
file_utils.py CHANGED
@@ -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:
module_renderer.py CHANGED
@@ -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))
path_resolution.py ADDED
@@ -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))
plain2code.py CHANGED
@@ -25,9 +25,11 @@ from plain2code_events import RenderFailed
25
25
  from plain2code_exceptions import (
26
26
  ConflictingRequirements,
27
27
  GitNotInstalledError,
28
+ ImportedModuleWithFunctionalitiesError,
28
29
  InvalidAPIKey,
29
30
  InvalidFridArgument,
30
31
  MissingAPIKey,
32
+ MissingFunctionalitiesError,
31
33
  MissingPreviousFunctionalitiesError,
32
34
  MissingResource,
33
35
  ModuleDoesNotExistError,
@@ -46,7 +48,6 @@ from plain2code_logger import (
46
48
  IndentedFormatter,
47
49
  LoggingHandler,
48
50
  dump_crash_logs,
49
- get_log_file_path,
50
51
  )
51
52
  from plain2code_state import RunState
52
53
  from plain2code_telemetry import capture_crash, initialize_telemetry
@@ -65,6 +66,8 @@ EXPECTED_EXCEPTIONS = (
65
66
  MissingResource,
66
67
  TemplateNotFoundError,
67
68
  PlainSyntaxError,
69
+ ImportedModuleWithFunctionalitiesError,
70
+ MissingFunctionalitiesError,
68
71
  MissingPreviousFunctionalitiesError,
69
72
  MissingAPIKey,
70
73
  InvalidAPIKey,
@@ -85,8 +88,7 @@ def setup_logging(
85
88
  event_bus: EventBus,
86
89
  run_state: RunState,
87
90
  log_to_file: bool,
88
- log_file_name: str,
89
- plain_file_path: Optional[str],
91
+ log_file_path: str,
90
92
  headless: bool = False,
91
93
  ):
92
94
  default_level = logging.DEBUG if args.verbose else logging.INFO
@@ -97,10 +99,9 @@ def setup_logging(
97
99
  logging.getLogger("transitions").setLevel(logging.ERROR)
98
100
  logging.getLogger("transitions.extensions.diagrams").setLevel(logging.ERROR)
99
101
 
100
- log_file_path = get_log_file_path(plain_file_path, log_file_name)
101
-
102
102
  # Try to load logging configuration from YAML file (takes precedence over --verbose)
103
103
  config_loaded = False
104
+
104
105
  if args.logging_config_path and os.path.exists(args.logging_config_path):
105
106
  try:
106
107
  with open(args.logging_config_path, "r") as f:
@@ -123,7 +124,7 @@ def setup_logging(
123
124
  handler.setFormatter(formatter)
124
125
  root_logger.addHandler(handler)
125
126
 
126
- if log_to_file and log_file_path:
127
+ if log_to_file:
127
128
  try:
128
129
  file_handler = logging.FileHandler(log_file_path, mode="w", encoding="utf-8")
129
130
  file_handler.setFormatter(file_formatter)
@@ -323,7 +324,7 @@ def main(): # noqa: C901
323
324
  console.error(
324
325
  "Your API key is required. Please set the CODEPLAIN_API_KEY environment variable or provide it with the --api-key argument.\n"
325
326
  )
326
- return
327
+ sys.exit(1)
327
328
 
328
329
  if not args.api:
329
330
  args.api = "https://api.codeplain.ai"
@@ -332,6 +333,7 @@ def main(): # noqa: C901
332
333
  print_status(args.api_key, args.api, system_config.client_version)
333
334
  except Exception as e:
334
335
  console.error(f"Error fetching status: {str(e)}")
336
+ sys.exit(1)
335
337
  return
336
338
 
337
339
  template_dirs = file_utils.get_template_directories(args.filename, args.template_dir, DEFAULT_TEMPLATE_DIRS)
@@ -351,7 +353,7 @@ def main(): # noqa: C901
351
353
  # Parse the plain file (and its required modules) once; reused by dry-run and rendering.
352
354
  try:
353
355
  plain_module = plain_modules.PlainModule(
354
- args.filename,
356
+ os.path.basename(args.filename),
355
357
  args.build_folder,
356
358
  args.conformance_tests_folder,
357
359
  template_dirs,
@@ -378,9 +380,7 @@ def main(): # noqa: C901
378
380
  # Suppress Rich console output.
379
381
  console.quiet = True
380
382
 
381
- default_log_level = setup_logging(
382
- args, event_bus, run_state, args.log_to_file, args.log_file_name, args.filename, args.headless
383
- )
383
+ default_log_level = setup_logging(args, event_bus, run_state, args.log_to_file, args.log_file_name, args.headless)
384
384
 
385
385
  initialize_telemetry()
386
386