create-promptex-py 0.0.1a1__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.
@@ -0,0 +1,3 @@
1
+ __pycache__/
2
+ *.egg-info/
3
+ tmp/
@@ -0,0 +1,204 @@
1
+ Apache License
2
+ Version 2.0, January 2004
3
+ http://www.apache.org/licenses/
4
+
5
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6
+
7
+ 1. Definitions.
8
+
9
+ "License" shall mean the terms and conditions for use, reproduction,
10
+ and distribution as defined by Sections 1 through 9 of this document.
11
+
12
+ "Licensor" shall mean the copyright owner or entity authorized by
13
+ the copyright owner that is granting the License.
14
+
15
+ "Legal Entity" shall mean the union of the acting entity and all
16
+ other entities that control, are controlled by, or are under common
17
+ control with that entity. For the purposes of this definition,
18
+ "control" means (i) the power, direct or indirect, to cause the
19
+ direction or management of such entity, whether by contract or
20
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
21
+ outstanding shares, or (iii) beneficial ownership of such entity.
22
+
23
+ "You" (or "Your") shall mean an individual or Legal Entity
24
+ exercising permissions granted by this License.
25
+
26
+ "Source" form shall mean the preferred form for making modifications,
27
+ including but not limited to software source code, documentation
28
+ source, and configuration files.
29
+
30
+ "Object" form shall mean any form resulting from mechanical
31
+ transformation or translation of a Source form, including but
32
+ not limited to compiled object code, generated documentation,
33
+ and conversions to other media types.
34
+
35
+ "Work" shall mean the work of authorship, whether in Source or
36
+ Object form, made available under the License, as indicated by a
37
+ copyright notice that is included in or attached to the work
38
+ (an example is provided in the Appendix below).
39
+
40
+ "Derivative Works" shall mean any work, whether in Source or Object
41
+ form, that is based on (or derived from) the Work and for which the
42
+ editorial revisions, annotations, elaborations, or other modifications
43
+ represent, as a whole, an original work of authorship. For the
44
+ purposes of this License, Derivative Works shall not include works
45
+ that remain separable from, or merely link (or bind by name) to the
46
+ interfaces of, the Work and Derivative Works thereof.
47
+
48
+ "Contribution" shall mean any work of authorship, including the
49
+ original version of the Work and any modifications or additions
50
+ to that Work or Derivative Works thereof, that is intentionally
51
+ submitted to Licensor for inclusion in the Work by the copyright
52
+ owner or by an individual or Legal Entity authorized to submit on
53
+ behalf of the copyright owner. For the purposes of this definition,
54
+ "submitted" means any form of electronic, verbal, or written
55
+ communication sent to the Licensor or its representatives,
56
+ including but not limited to communication on electronic mailing
57
+ lists, source code control systems, and issue tracking systems that
58
+ are managed by, or on behalf of, the Licensor for the purpose of
59
+ discussing and improving the Work, but excluding communication that
60
+ is conspicuously marked or otherwise designated in writing by the
61
+ copyright owner as "Not a Contribution."
62
+
63
+ "Contributor" shall mean Licensor and any individual or Legal Entity
64
+ on behalf of whom a Contribution has been received by Licensor and
65
+ subsequently incorporated within the Work.
66
+
67
+ 2. Grant of Copyright License. Subject to the terms and conditions of
68
+ this License, each Contributor hereby grants to You a perpetual,
69
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
70
+ copyright license to reproduce, prepare Derivative Works of,
71
+ publicly display, publicly perform, sublicense, and distribute the
72
+ Work and such Derivative Works in Source or Object form.
73
+
74
+ 3. Grant of Patent License. Subject to the terms and conditions of
75
+ this License, each Contributor hereby grants to You a perpetual,
76
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
77
+ (except as stated in this section) patent license to make, have made,
78
+ use, offer to sell, sell, import, and otherwise transfer the Work,
79
+ where such license applies only to those patent claims licensable
80
+ by such Contributor that are necessarily infringed by their
81
+ Contribution(s) alone or by combination of their Contribution(s)
82
+ with the Work to which such Contribution(s) was submitted. If You
83
+ institute patent litigation against any entity (including a
84
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
85
+ or a Contribution incorporated within the Work constitutes direct
86
+ or contributory patent infringement, then any patent licenses
87
+ granted to You under this License for that Work shall terminate
88
+ as of the date such litigation is filed.
89
+
90
+ 4. Redistribution. You may reproduce and distribute copies of the
91
+ Work or Derivative Works thereof in any medium, with or without
92
+ modifications, and in Source or Object form, provided that You
93
+ meet the following conditions:
94
+
95
+ (a) You must give any other recipients of the Work or
96
+ Derivative Works a copy of this License; and
97
+
98
+ (b) You must cause any modified files to carry prominent notices
99
+ stating that You changed the files; and
100
+
101
+ (c) You must retain, in the Source form of any Derivative Works
102
+ that You distribute, all copyright, patent, trademark, and
103
+ attribution notices from the Source form of the Work,
104
+ excluding those notices that do not pertain to any part of
105
+ the Derivative Works; and
106
+
107
+ (d) If the Work includes a "NOTICE" text file as part of its
108
+ distribution, then any Derivative Works that You distribute must
109
+ include a readable copy of the attribution notices contained
110
+ within such NOTICE file, excluding those notices that do not
111
+ pertain to any part of the Derivative Works, in at least one
112
+ of the following places: within a NOTICE text file distributed
113
+ as part of the Derivative Works; within the Source form or
114
+ documentation, if provided along with the Derivative Works; or,
115
+ within a display generated by the Derivative Works, if and
116
+ wherever such third-party notices normally appear. The contents
117
+ of the NOTICE file are for informational purposes only and
118
+ do not modify the License. You may add Your own attribution
119
+ notices within Derivative Works that You distribute, alongside
120
+ or as an addendum to the NOTICE text from the Work, provided
121
+ that such additional attribution notices cannot be construed
122
+ as modifying the License.
123
+
124
+ You may add Your own copyright statement to Your modifications and
125
+ may provide additional or different license terms and conditions
126
+ for use, reproduction, or distribution of Your modifications, or
127
+ for any such Derivative Works as a whole, provided Your use,
128
+ reproduction, and distribution of the Work otherwise complies with
129
+ the conditions stated in this License.
130
+
131
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
132
+ any Contribution intentionally submitted for inclusion in the Work
133
+ by You to the Licensor shall be under the terms and conditions of
134
+ this License, without any additional terms or conditions.
135
+ Notwithstanding the above, nothing herein shall supersede or modify
136
+ the terms of any separate license agreement you may have executed
137
+ with Licensor regarding such Contributions.
138
+
139
+ 6. Trademarks. This License does not grant permission to use the trade
140
+ names, trademarks, service marks, or product names of the Licensor,
141
+ except as required for reasonable and customary use in describing
142
+ the origin of the Work and reproducing the content of the NOTICE
143
+ file.
144
+
145
+ 7. Disclaimer of Warranty. Unless required by applicable law or
146
+ agreed to in writing, Licensor provides the Work (and each
147
+ Contributor provides its Contributions) on an "AS IS" BASIS,
148
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
149
+ implied, including, without limitation, any warranties or conditions
150
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
151
+ PARTICULAR PURPOSE. You are solely responsible for determining the
152
+ appropriateness of using or redistributing the Work and assume any
153
+ risks associated with Your exercise of permissions under this
154
+ License.
155
+
156
+ 8. Limitation of Liability. In no event and under no legal theory,
157
+ whether in tort (including negligence), contract, or otherwise,
158
+ unless required by applicable law (such as deliberate and grossly
159
+ negligent acts) or agreed to in writing, shall any Contributor be
160
+ liable to You for damages, including any direct, indirect, special,
161
+ incidental, or consequential damages of any character arising as a
162
+ result of this License or out of the use or inability to use the
163
+ Work (including but not limited to damages for loss of goodwill,
164
+ work stoppage, computer failure or malfunction, or any and all
165
+ other commercial damages or losses), even if such Contributor
166
+ has been advised of the possibility of such damages.
167
+
168
+ 9. Accepting Warranty or Additional Liability. While redistributing
169
+ the Work or Derivative Works thereof, You may choose to offer,
170
+ and charge a fee for, acceptance of support, warranty, indemnity,
171
+ or other liability obligations and/or rights consistent with this
172
+ License. However, in accepting such obligations, You may act only
173
+ on Your own behalf and on Your sole responsibility, not on behalf
174
+ of any other Contributor, and only if You agree to indemnify,
175
+ defend, and hold each Contributor harmless for any liability
176
+ incurred by, or claims asserted against, such Contributor by reason
177
+ of your accepting any such warranty or additional liability.
178
+
179
+ END OF TERMS AND CONDITIONS
180
+
181
+ APPENDIX: How to apply the Apache License to your work.
182
+
183
+ To apply the Apache License to your work, attach the following
184
+ boilerplate notice, with the fields enclosed by brackets "[]"
185
+ replaced with your own identifying information. (Don't include
186
+ the brackets!) The text should be enclosed in the appropriate
187
+ comment syntax for the file format. We also recommend that a
188
+ file or class name and description of purpose be included on the
189
+ same "printed page" as the copyright notice for easier
190
+ identification within third-party archives.
191
+
192
+ Copyright 2026 promptex-ai
193
+
194
+ Licensed under the Apache License, Version 2.0 (the "License");
195
+ you may not use this file except in compliance with the License.
196
+ You may obtain a copy of the License at
197
+
198
+ http://www.apache.org/licenses/LICENSE-2.0
199
+
200
+ Unless required by applicable law or agreed to in writing, software
201
+ distributed under the License is distributed on an "AS IS" BASIS,
202
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
203
+ See the License for the specific language governing permissions and
204
+ limitations under the License.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 promptex-ai
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,82 @@
1
+ Metadata-Version: 2.5
2
+ Name: create-promptex-py
3
+ Version: 0.0.1a1
4
+ Summary: promptex 專案鷹架的最小原型(alpha):產出可立即建置的專案骨架
5
+ Project-URL: Homepage, https://github.com/promptex-ai/promptex
6
+ Project-URL: Repository, https://github.com/promptex-ai/promptex
7
+ Project-URL: Issues, https://github.com/promptex-ai/promptex/issues
8
+ Author: promptex-ai
9
+ License: MIT OR Apache-2.0
10
+ License-File: LICENSE-APACHE
11
+ License-File: LICENSE-MIT
12
+ Keywords: agent,ai,claude,llm,prompt,prompt-engineering,scaffold,skills
13
+ Classifier: Development Status :: 3 - Alpha
14
+ Classifier: Intended Audience :: Developers
15
+ Classifier: License :: OSI Approved :: Apache Software License
16
+ Classifier: License :: OSI Approved :: MIT License
17
+ Classifier: Programming Language :: Python :: 3
18
+ Classifier: Programming Language :: Python :: 3.10
19
+ Classifier: Programming Language :: Python :: 3.11
20
+ Classifier: Programming Language :: Python :: 3.12
21
+ Classifier: Programming Language :: Python :: 3.13
22
+ Classifier: Topic :: Software Development :: Build Tools
23
+ Classifier: Topic :: Software Development :: Code Generators
24
+ Classifier: Typing :: Typed
25
+ Requires-Python: >=3.10
26
+ Description-Content-Type: text/markdown
27
+
28
+ # create-promptex-py(alpha 原型)
29
+
30
+ promptex 專案鷹架的**最小原型**。產出一個可立即建置的專案骨架:宣告一份
31
+ skill,建置後得到 Claude Code 的 `SKILL.md`。
32
+
33
+ ## 這不是什麼
34
+
35
+ 本套件是早期 alpha。骨架只有四個檔案、一份範例宣告,沒有多單元配置、沒有
36
+ 多平台選擇、沒有外掛與轉接器。正式版把骨架收在核心(`promptex init`),
37
+ 本原型自帶一份副本,正式版發布時會被整份取代。
38
+
39
+ 介面與骨架內容在正式版之前都會變,不保證任何相容性。
40
+
41
+ ## 用法
42
+
43
+ 不需要事先安裝,`uvx`(或 `pipx run`)會取得後直接執行。需要 Python 3.10 以上。
44
+
45
+ ```bash
46
+ uvx create-promptex-py my-prompts
47
+ ```
48
+
49
+ 產出:
50
+
51
+ ```text
52
+ my-prompts/
53
+ ├── .gitignore
54
+ ├── README.md
55
+ ├── package.json
56
+ └── prompts/skill.ts
57
+ ```
58
+
59
+ (上為 `--lang ts` 的形態;`--lang python` 產出 `pyproject.toml` 與
60
+ `prompts/skill.py`,`--lang rust` 產出 `Cargo.toml` 與 `src/main.rs`。)
61
+
62
+ ### 選項
63
+
64
+ ```text
65
+ uvx create-promptex-py <目錄> [--lang ts|python|rust] [--yes]
66
+
67
+ --lang <語言> 骨架語言,預設 ts
68
+ --yes 目標目錄已有同名檔案時仍覆寫
69
+ ```
70
+
71
+ 非互動:全部輸入取自位置參數與旗標,與正式版 `promptex init` 的介面一致。
72
+ 目標目錄已有同名檔案且未帶 `--yes` 時**一個檔案都不寫**,不留半套骨架。
73
+
74
+ ## 三生態等價
75
+
76
+ `create-promptex-js`、`create-promptex-py`、`create-promptex-rs` 對同一組
77
+ 參數產出逐字相同的檔案樹。正式版靠骨架住核心由結構保證,本原型是三份獨立
78
+ 實作,等價由產物比對承擔。
79
+
80
+ ## 授權
81
+
82
+ MIT OR Apache-2.0,見 `LICENSE-MIT` 與 `LICENSE-APACHE`。
@@ -0,0 +1,55 @@
1
+ # create-promptex-py(alpha 原型)
2
+
3
+ promptex 專案鷹架的**最小原型**。產出一個可立即建置的專案骨架:宣告一份
4
+ skill,建置後得到 Claude Code 的 `SKILL.md`。
5
+
6
+ ## 這不是什麼
7
+
8
+ 本套件是早期 alpha。骨架只有四個檔案、一份範例宣告,沒有多單元配置、沒有
9
+ 多平台選擇、沒有外掛與轉接器。正式版把骨架收在核心(`promptex init`),
10
+ 本原型自帶一份副本,正式版發布時會被整份取代。
11
+
12
+ 介面與骨架內容在正式版之前都會變,不保證任何相容性。
13
+
14
+ ## 用法
15
+
16
+ 不需要事先安裝,`uvx`(或 `pipx run`)會取得後直接執行。需要 Python 3.10 以上。
17
+
18
+ ```bash
19
+ uvx create-promptex-py my-prompts
20
+ ```
21
+
22
+ 產出:
23
+
24
+ ```text
25
+ my-prompts/
26
+ ├── .gitignore
27
+ ├── README.md
28
+ ├── package.json
29
+ └── prompts/skill.ts
30
+ ```
31
+
32
+ (上為 `--lang ts` 的形態;`--lang python` 產出 `pyproject.toml` 與
33
+ `prompts/skill.py`,`--lang rust` 產出 `Cargo.toml` 與 `src/main.rs`。)
34
+
35
+ ### 選項
36
+
37
+ ```text
38
+ uvx create-promptex-py <目錄> [--lang ts|python|rust] [--yes]
39
+
40
+ --lang <語言> 骨架語言,預設 ts
41
+ --yes 目標目錄已有同名檔案時仍覆寫
42
+ ```
43
+
44
+ 非互動:全部輸入取自位置參數與旗標,與正式版 `promptex init` 的介面一致。
45
+ 目標目錄已有同名檔案且未帶 `--yes` 時**一個檔案都不寫**,不留半套骨架。
46
+
47
+ ## 三生態等價
48
+
49
+ `create-promptex-js`、`create-promptex-py`、`create-promptex-rs` 對同一組
50
+ 參數產出逐字相同的檔案樹。正式版靠骨架住核心由結構保證,本原型是三份獨立
51
+ 實作,等價由產物比對承擔。
52
+
53
+ ## 授權
54
+
55
+ MIT OR Apache-2.0,見 `LICENSE-MIT` 與 `LICENSE-APACHE`。
@@ -0,0 +1,44 @@
1
+ [project]
2
+ name = "create-promptex-py"
3
+ # PEP 440 的正規化形態,與 npm/crates.io 側的 `0.0.1-alpha.1` 指同一版本。
4
+ version = "0.0.1a1"
5
+ description = "promptex 專案鷹架的最小原型(alpha):產出可立即建置的專案骨架"
6
+ requires-python = ">=3.10"
7
+ license = { text = "MIT OR Apache-2.0" }
8
+ readme = "README.md"
9
+ authors = [{ name = "promptex-ai" }]
10
+ keywords = ["prompt", "prompt-engineering", "ai", "llm", "agent", "claude", "skills", "scaffold"]
11
+ classifiers = [
12
+ "Development Status :: 3 - Alpha",
13
+ "Intended Audience :: Developers",
14
+ "License :: OSI Approved :: MIT License",
15
+ "License :: OSI Approved :: Apache Software License",
16
+ "Programming Language :: Python :: 3",
17
+ "Programming Language :: Python :: 3.10",
18
+ "Programming Language :: Python :: 3.11",
19
+ "Programming Language :: Python :: 3.12",
20
+ "Programming Language :: Python :: 3.13",
21
+ "Topic :: Software Development :: Build Tools",
22
+ "Topic :: Software Development :: Code Generators",
23
+ "Typing :: Typed",
24
+ ]
25
+
26
+ [project.urls]
27
+ Homepage = "https://github.com/promptex-ai/promptex"
28
+ Repository = "https://github.com/promptex-ai/promptex"
29
+ Issues = "https://github.com/promptex-ai/promptex/issues"
30
+
31
+ # 執行檔名與套件名逐字相同:`uvx create-promptex-py` 要能裸跑,uv 取的是
32
+ # 與套件同名的執行檔;不同名就得寫成 `uvx --from create-promptex-py <指令>`。
33
+ [project.scripts]
34
+ create-promptex-py = "create_promptex.cli:main"
35
+
36
+ [build-system]
37
+ requires = ["hatchling>=1.25"]
38
+ build-backend = "hatchling.build"
39
+
40
+ [tool.hatch.build.targets.wheel]
41
+ packages = ["src/create_promptex"]
42
+
43
+ [tool.hatch.build]
44
+ exclude = ["**/__pycache__/**"]
@@ -0,0 +1,212 @@
1
+ """create-promptex-py(alpha 原型):產出可立即建置的 promptex 專案骨架。
2
+
3
+ 骨架內容以資料承載(見 :func:`skeleton`),三個生態的 create-promptex-* 產出
4
+ 逐字相同的檔案樹。正式版把骨架收在核心(``promptex_core::init``),本原型的
5
+ 三份實作各持一份副本,等價由 ``verify-promptex-poc.sh`` 的產物比對承擔。
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ import json
11
+ import re
12
+ from dataclasses import dataclass, field
13
+ from pathlib import Path
14
+
15
+ __all__ = ["LANGS", "Outcome", "scaffold", "skeleton"]
16
+
17
+ __version__ = "0.0.1a1"
18
+
19
+ #: 骨架支援的語言。與 ``promptex init --lang`` 的取值一致。
20
+ LANGS: tuple[str, ...] = ("ts", "python", "rust")
21
+
22
+ #: 專案名稱的合法形態。作為套件名與 crate 名,字元集取三個生態的交集。
23
+ _NAME_PATTERN = re.compile(r"^[a-zA-Z0-9][a-zA-Z0-9._-]*$")
24
+
25
+ _SKILL_ID = "commit-helper"
26
+ _SKILL_NAME = "Commit 訊息協助"
27
+ _SKILL_WHEN = "當使用者要求撰寫 commit 訊息時使用"
28
+ _SKILL_P1 = "依 Conventional Commits 撰寫:type(scope): subject。"
29
+ _SKILL_P2 = "subject 用繁體中文、50 字內,不以句號結尾;body 說明變更動機而非變更內容。"
30
+
31
+ #: 各語言的建置指令,投影進骨架 README 的建置段。
32
+ _BUILD_COMMANDS: dict[str, list[str]] = {
33
+ "ts": ["npm install", "npm run build"],
34
+ "python": ["pip install --pre promptex-py", "promptex build"],
35
+ "rust": ["cargo run"],
36
+ }
37
+
38
+
39
+ def _readme(name: str, lang: str) -> str:
40
+ lines = [
41
+ "# " + name,
42
+ "",
43
+ "以 promptex 管理提示詞資源的專案骨架。",
44
+ "",
45
+ "## 建置",
46
+ "",
47
+ "```bash",
48
+ *_BUILD_COMMANDS[lang],
49
+ "```",
50
+ "",
51
+ "產出 `.claude/skills/" + _SKILL_ID + "/SKILL.md`。",
52
+ "",
53
+ "## 下一步",
54
+ "",
55
+ "新增宣告,每份宣告投影成一份產物。",
56
+ "",
57
+ ]
58
+ return "\n".join(lines)
59
+
60
+
61
+ def _ts_skeleton(name: str) -> dict[str, str]:
62
+ manifest = {
63
+ "name": name,
64
+ "private": True,
65
+ "type": "module",
66
+ "scripts": {"build": "promptex build"},
67
+ "dependencies": {"promptex-js": "0.0.1-alpha.1"},
68
+ }
69
+ return {
70
+ "package.json": json.dumps(manifest, indent=2, ensure_ascii=False) + "\n",
71
+ "prompts/skill.ts": "\n".join(
72
+ [
73
+ "import { defineSkill } from 'promptex-js'",
74
+ "",
75
+ "export const commitHelper = defineSkill({",
76
+ " id: '" + _SKILL_ID + "',",
77
+ " name: '" + _SKILL_NAME + "',",
78
+ " config: { whenToUse: '" + _SKILL_WHEN + "' },",
79
+ " content: [",
80
+ " '" + _SKILL_P1 + "',",
81
+ " '" + _SKILL_P2 + "',",
82
+ " ],",
83
+ "})",
84
+ "",
85
+ ]
86
+ ),
87
+ ".gitignore": "node_modules/\n",
88
+ "README.md": _readme(name, "ts"),
89
+ }
90
+
91
+
92
+ def _python_skeleton(name: str) -> dict[str, str]:
93
+ return {
94
+ "pyproject.toml": "\n".join(
95
+ [
96
+ "[project]",
97
+ 'name = "' + name + '"',
98
+ 'version = "0.0.0"',
99
+ 'requires-python = ">=3.10"',
100
+ 'dependencies = ["promptex-py==0.0.1a1"]',
101
+ "",
102
+ ]
103
+ ),
104
+ "prompts/skill.py": "\n".join(
105
+ [
106
+ "from promptex import config, define_skill",
107
+ "",
108
+ "commit_helper = define_skill(",
109
+ ' "' + _SKILL_ID + '",',
110
+ ' "' + _SKILL_NAME + '",',
111
+ ' config=config(when_to_use="' + _SKILL_WHEN + '"),',
112
+ " content=[",
113
+ ' "' + _SKILL_P1 + '",',
114
+ ' "' + _SKILL_P2 + '",',
115
+ " ],",
116
+ ")",
117
+ "",
118
+ ]
119
+ ),
120
+ ".gitignore": "__pycache__/\n",
121
+ "README.md": _readme(name, "python"),
122
+ }
123
+
124
+
125
+ def _rust_skeleton(name: str) -> dict[str, str]:
126
+ return {
127
+ "Cargo.toml": "\n".join(
128
+ [
129
+ "[package]",
130
+ 'name = "' + name + '"',
131
+ 'version = "0.0.0"',
132
+ 'edition = "2021"',
133
+ "",
134
+ "[dependencies]",
135
+ 'promptex-rs = "0.0.1-alpha.1"',
136
+ "",
137
+ ]
138
+ ),
139
+ "src/main.rs": "\n".join(
140
+ [
141
+ "use promptex_rs::{build, define_skill, CapabilityConfig, SkillDef};",
142
+ "",
143
+ "fn main() -> Result<(), Box<dyn std::error::Error>> {",
144
+ " define_skill(SkillDef {",
145
+ ' id: "' + _SKILL_ID + '".into(),',
146
+ ' name: "' + _SKILL_NAME + '".into(),',
147
+ ' config: CapabilityConfig::new().when_to_use("' + _SKILL_WHEN + '"),',
148
+ " content: vec![",
149
+ ' "' + _SKILL_P1 + '".into(),',
150
+ ' "' + _SKILL_P2 + '".into(),',
151
+ " ],",
152
+ " });",
153
+ "",
154
+ ' for path in build(".")? {',
155
+ ' println!("{}", path.display());',
156
+ " }",
157
+ " Ok(())",
158
+ "}",
159
+ "",
160
+ ]
161
+ ),
162
+ ".gitignore": "target/\n",
163
+ "README.md": _readme(name, "rust"),
164
+ }
165
+
166
+
167
+ def skeleton(name: str, lang: str) -> dict[str, str]:
168
+ """一份骨架的全部檔案,鍵為相對路徑。
169
+
170
+ 純計算、不碰檔案系統:讓「骨架長什麼樣」可被直接比對,不必先寫到磁碟。
171
+ """
172
+ if not _NAME_PATTERN.match(name):
173
+ raise ValueError(f"專案名稱只能含英數與 . _ -,且須以英數開頭:{name!r}")
174
+ if lang == "ts":
175
+ return _ts_skeleton(name)
176
+ if lang == "python":
177
+ return _python_skeleton(name)
178
+ if lang == "rust":
179
+ return _rust_skeleton(name)
180
+ raise ValueError(f"不支援的語言:{lang}(可用:{'、'.join(LANGS)})")
181
+
182
+
183
+ @dataclass
184
+ class Outcome:
185
+ """一次初始化的處置結果。``aborted`` 為真時 ``written`` 必為空。"""
186
+
187
+ aborted: bool
188
+ conflicts: list[str] = field(default_factory=list)
189
+ written: list[Path] = field(default_factory=list)
190
+
191
+
192
+ def scaffold(target_dir: str | Path, lang: str, assume_yes: bool) -> Outcome:
193
+ """在目標目錄產出骨架。
194
+
195
+ 全有全無:有同名檔案且未帶 ``assume_yes`` 時一個檔案都不寫,避免留下半套
196
+ 骨架。這與正式版 ``promptex init`` 的衝突處置一致。
197
+ """
198
+ directory = Path(target_dir).resolve()
199
+ files = skeleton(directory.name, lang)
200
+ relatives = sorted(files)
201
+
202
+ conflicts = [rel for rel in relatives if (directory / rel).exists()]
203
+ if conflicts and not assume_yes:
204
+ return Outcome(aborted=True, conflicts=conflicts)
205
+
206
+ written: list[Path] = []
207
+ for rel in relatives:
208
+ path = directory / rel
209
+ path.parent.mkdir(parents=True, exist_ok=True)
210
+ path.write_text(files[rel], encoding="utf-8")
211
+ written.append(path)
212
+ return Outcome(aborted=False, conflicts=conflicts, written=written)
@@ -0,0 +1,5 @@
1
+ """讓 ``python -m create_promptex`` 與執行檔走同一條路徑。"""
2
+
3
+ from .cli import main
4
+
5
+ raise SystemExit(main())
@@ -0,0 +1,45 @@
1
+ """create-promptex-py 的進入點。``uvx create-promptex-py <目錄>`` 解析到本套件。
2
+
3
+ 非互動:全部輸入取自位置參數與旗標,與正式版 ``promptex init`` 的介面一致。
4
+ 骨架決策不在此處,見 :func:`create_promptex.skeleton`。
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ import argparse
10
+ import sys
11
+
12
+ from . import LANGS, scaffold
13
+
14
+
15
+ def main(argv: list[str] | None = None) -> int:
16
+ parser = argparse.ArgumentParser(
17
+ prog="create-promptex-py", description="create-promptex-py(alpha 原型)"
18
+ )
19
+ parser.add_argument("directory", help="目標目錄")
20
+ parser.add_argument("--lang", choices=LANGS, default="ts", help="骨架語言,預設 ts")
21
+ parser.add_argument(
22
+ "-y", "--yes", action="store_true", dest="assume_yes", help="已有同名檔案時仍覆寫"
23
+ )
24
+ args = parser.parse_args(sys.argv[1:] if argv is None else argv)
25
+
26
+ try:
27
+ outcome = scaffold(args.directory, args.lang, args.assume_yes)
28
+ except ValueError as error:
29
+ print(error, file=sys.stderr)
30
+ return 1
31
+
32
+ if outcome.aborted:
33
+ print("目標目錄已有同名檔案,未寫入任何檔案:", file=sys.stderr)
34
+ for rel in outcome.conflicts:
35
+ print(f" {rel}", file=sys.stderr)
36
+ print("確定要覆寫請加 --yes。", file=sys.stderr)
37
+ return 1
38
+
39
+ for path in outcome.written:
40
+ print(path)
41
+ return 0
42
+
43
+
44
+ if __name__ == "__main__":
45
+ raise SystemExit(main())
@@ -0,0 +1,56 @@
1
+ """鷹架的單元測試。以 stdlib 的 unittest 承載,不引入測試框架依賴。
2
+
3
+ 執行:``PYTHONPATH=src python3 -m unittest discover -s tests``
4
+ """
5
+
6
+ from __future__ import annotations
7
+
8
+ import tempfile
9
+ import unittest
10
+ from pathlib import Path
11
+
12
+ from create_promptex import LANGS, scaffold, skeleton
13
+
14
+
15
+ class SkeletonTests(unittest.TestCase):
16
+ def test_every_lang_has_four_files(self) -> None:
17
+ for lang in LANGS:
18
+ with self.subTest(lang=lang):
19
+ self.assertEqual(len(skeleton("demo", lang)), 4)
20
+
21
+ def test_ts_manifest_is_two_space_json_with_trailing_newline(self) -> None:
22
+ manifest = skeleton("demo", "ts")["package.json"]
23
+ self.assertTrue(manifest.startswith('{\n "name": "demo",\n'))
24
+ self.assertTrue(manifest.endswith("}\n"))
25
+
26
+ def test_rejects_names_that_are_not_valid_package_or_crate_names(self) -> None:
27
+ for bad in ("-bad", "has space", ""):
28
+ with self.subTest(name=bad), self.assertRaisesRegex(ValueError, "專案名稱只能含英數"):
29
+ skeleton(bad, "ts")
30
+
31
+ def test_rejects_unknown_lang(self) -> None:
32
+ with self.assertRaisesRegex(ValueError, "不支援的語言"):
33
+ skeleton("demo", "go")
34
+
35
+
36
+ class ScaffoldTests(unittest.TestCase):
37
+ def test_aborts_without_writing_when_a_file_already_exists(self) -> None:
38
+ with tempfile.TemporaryDirectory() as tmp:
39
+ directory = Path(tmp) / "demo"
40
+ directory.mkdir()
41
+ (directory / "README.md").write_text("既有內容", encoding="utf-8")
42
+
43
+ outcome = scaffold(directory, "ts", False)
44
+ self.assertTrue(outcome.aborted)
45
+ self.assertEqual(outcome.conflicts, ["README.md"])
46
+ self.assertEqual(outcome.written, [])
47
+ self.assertEqual((directory / "README.md").read_text(encoding="utf-8"), "既有內容")
48
+ self.assertFalse((directory / "package.json").exists())
49
+
50
+ forced = scaffold(directory, "ts", True)
51
+ self.assertFalse(forced.aborted)
52
+ self.assertEqual(len(forced.written), 4)
53
+
54
+
55
+ if __name__ == "__main__":
56
+ unittest.main()