codex-rule-maker 0.2.0__tar.gz → 0.4.0__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.
- codex_rule_maker-0.4.0/LICENSE +21 -0
- {codex_rule_maker-0.2.0 → codex_rule_maker-0.4.0}/PKG-INFO +16 -9
- {codex_rule_maker-0.2.0 → codex_rule_maker-0.4.0}/README.md +13 -8
- {codex_rule_maker-0.2.0 → codex_rule_maker-0.4.0}/codex_builder/cli.py +2 -2
- {codex_rule_maker-0.2.0 → codex_rule_maker-0.4.0}/codex_builder/profiles.py +217 -57
- {codex_rule_maker-0.2.0 → codex_rule_maker-0.4.0}/codex_builder/prompt.py +3 -3
- {codex_rule_maker-0.2.0 → codex_rule_maker-0.4.0}/codex_builder/template_renderer.py +39 -60
- {codex_rule_maker-0.2.0 → codex_rule_maker-0.4.0}/codex_rule_maker.egg-info/PKG-INFO +16 -9
- {codex_rule_maker-0.2.0 → codex_rule_maker-0.4.0}/codex_rule_maker.egg-info/SOURCES.txt +1 -0
- {codex_rule_maker-0.2.0 → codex_rule_maker-0.4.0}/pyproject.toml +1 -1
- {codex_rule_maker-0.2.0 → codex_rule_maker-0.4.0}/tests/test_builder.py +40 -0
- {codex_rule_maker-0.2.0 → codex_rule_maker-0.4.0}/tests/test_cli.py +9 -1
- {codex_rule_maker-0.2.0 → codex_rule_maker-0.4.0}/codex_builder/__init__.py +0 -0
- {codex_rule_maker-0.2.0 → codex_rule_maker-0.4.0}/codex_builder/builder.py +0 -0
- {codex_rule_maker-0.2.0 → codex_rule_maker-0.4.0}/codex_builder/constants.py +0 -0
- {codex_rule_maker-0.2.0 → codex_rule_maker-0.4.0}/codex_builder/models.py +0 -0
- {codex_rule_maker-0.2.0 → codex_rule_maker-0.4.0}/codex_builder/validator.py +0 -0
- {codex_rule_maker-0.2.0 → codex_rule_maker-0.4.0}/codex_rule_maker.egg-info/dependency_links.txt +0 -0
- {codex_rule_maker-0.2.0 → codex_rule_maker-0.4.0}/codex_rule_maker.egg-info/entry_points.txt +0 -0
- {codex_rule_maker-0.2.0 → codex_rule_maker-0.4.0}/codex_rule_maker.egg-info/requires.txt +0 -0
- {codex_rule_maker-0.2.0 → codex_rule_maker-0.4.0}/codex_rule_maker.egg-info/top_level.txt +0 -0
- {codex_rule_maker-0.2.0 → codex_rule_maker-0.4.0}/setup.cfg +0 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 주민재
|
|
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.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: codex-rule-maker
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.4.0
|
|
4
4
|
Summary: CLI tool that generates a .codex folder with AI developer rules and project reference documents.
|
|
5
5
|
Author: codex-rule-maker
|
|
6
6
|
Project-URL: Homepage, https://github.com/Sharon77770/codex_rule_maker
|
|
@@ -21,13 +21,17 @@ Classifier: Topic :: Software Development :: Code Generators
|
|
|
21
21
|
Classifier: Topic :: Software Development :: Documentation
|
|
22
22
|
Requires-Python: >=3.9
|
|
23
23
|
Description-Content-Type: text/markdown
|
|
24
|
+
License-File: LICENSE
|
|
24
25
|
Provides-Extra: dev
|
|
25
26
|
Requires-Dist: build<2,>=1; extra == "dev"
|
|
26
27
|
Requires-Dist: pytest<9,>=7; extra == "dev"
|
|
27
28
|
Requires-Dist: twine<7,>=5; extra == "dev"
|
|
29
|
+
Dynamic: license-file
|
|
28
30
|
|
|
29
31
|
# codex-rule-maker
|
|
30
32
|
|
|
33
|
+
현재 버전: `0.4.0`
|
|
34
|
+
|
|
31
35
|
`codex-rule-maker`는 프로젝트 루트에 `.codex` 규칙 폴더와 `docs/` 프로젝트 문서 템플릿을 자동 구성하는 Python CLI 도구입니다. 사용자가 프로젝트 이름, 설명, 스택, DB, 인증 여부, 외부 API 여부, 문서화 수준, 문서 언어를 입력하면 Codex가 작업 전에 읽을 개발 규칙과 프로젝트 문서 구조를 생성합니다.
|
|
32
36
|
|
|
33
37
|
## 설치
|
|
@@ -222,19 +226,22 @@ docs/
|
|
|
222
226
|
|
|
223
227
|
## 지원 프레임워크 프로필
|
|
224
228
|
|
|
225
|
-
- `fastapi`: controller/service/repository/schema/entity
|
|
226
|
-
- `python`:
|
|
227
|
-
- `
|
|
228
|
-
- `
|
|
229
|
-
- `
|
|
230
|
-
- `
|
|
231
|
-
- `
|
|
229
|
+
- `fastapi`: 기능 폴더 하위 controller/service/repository/schema/entity 분리
|
|
230
|
+
- `python`: 프레임워크 없는 Python 프로젝트의 기능/도메인 폴더 하위 service/adapter/model/CLI 경계 분리
|
|
231
|
+
- `typescript-javascript`: 프레임워크 없는 TypeScript/JavaScript 프로젝트의 기능/도메인 폴더 하위 service/adapter/model 경계 분리
|
|
232
|
+
- `springboot`: 기능 package 하위 controller/service/repository/entity/dto 분리
|
|
233
|
+
- `react`: 기능 폴더 하위 page/component/hook/service/store 분리
|
|
234
|
+
- `nextjs`: App Router 유지, 기능 폴더 하위 component/service/repository/type 분리
|
|
235
|
+
- `node-express`: 기능 폴더 하위 route/controller/service/repository 분리
|
|
236
|
+
- `fullstack-fastapi-react`: FastAPI 백엔드와 React 프론트엔드 모두 기능 폴더 하위 계층 분리 적용
|
|
232
237
|
|
|
233
238
|
여러 프로필은 comma로 조합할 수 있습니다.
|
|
234
239
|
|
|
235
240
|
```bash
|
|
236
241
|
codex-init --stack fastapi,react
|
|
237
242
|
codex-init --stack python
|
|
243
|
+
codex-init --stack typescript
|
|
244
|
+
codex-init --stack javascript
|
|
238
245
|
codex-init --stack fullstack-fastapi-react
|
|
239
246
|
codex-init --stack nextjs
|
|
240
247
|
```
|
|
@@ -245,7 +252,7 @@ codex-init --stack nextjs
|
|
|
245
252
|
- `--language en`을 사용하면 영어 문서를 생성합니다.
|
|
246
253
|
- `--docs strict`는 코드 변경 후 문서 업데이트 필수 규칙을 포함합니다.
|
|
247
254
|
- `--auth yes`는 인증/권한 규칙을 포함합니다.
|
|
248
|
-
- `--external-api yes`는 외부
|
|
255
|
+
- `--external-api yes`는 확인된 계약을 기준으로 실제 외부 연동을 구현하기 위한 client/adapter 및 계약 문서 규칙을 포함합니다.
|
|
249
256
|
- `--db mysql`처럼 DB를 지정하면 DB, Repository, Schema 관련 규칙을 포함합니다.
|
|
250
257
|
- 프로젝트 명세 템플릿은 `.codex/ref_docs`가 아니라 `docs/architecture`, `docs/api`, `docs/database`에 생성됩니다.
|
|
251
258
|
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# codex-rule-maker
|
|
2
2
|
|
|
3
|
+
현재 버전: `0.4.0`
|
|
4
|
+
|
|
3
5
|
`codex-rule-maker`는 프로젝트 루트에 `.codex` 규칙 폴더와 `docs/` 프로젝트 문서 템플릿을 자동 구성하는 Python CLI 도구입니다. 사용자가 프로젝트 이름, 설명, 스택, DB, 인증 여부, 외부 API 여부, 문서화 수준, 문서 언어를 입력하면 Codex가 작업 전에 읽을 개발 규칙과 프로젝트 문서 구조를 생성합니다.
|
|
4
6
|
|
|
5
7
|
## 설치
|
|
@@ -194,19 +196,22 @@ docs/
|
|
|
194
196
|
|
|
195
197
|
## 지원 프레임워크 프로필
|
|
196
198
|
|
|
197
|
-
- `fastapi`: controller/service/repository/schema/entity
|
|
198
|
-
- `python`:
|
|
199
|
-
- `
|
|
200
|
-
- `
|
|
201
|
-
- `
|
|
202
|
-
- `
|
|
203
|
-
- `
|
|
199
|
+
- `fastapi`: 기능 폴더 하위 controller/service/repository/schema/entity 분리
|
|
200
|
+
- `python`: 프레임워크 없는 Python 프로젝트의 기능/도메인 폴더 하위 service/adapter/model/CLI 경계 분리
|
|
201
|
+
- `typescript-javascript`: 프레임워크 없는 TypeScript/JavaScript 프로젝트의 기능/도메인 폴더 하위 service/adapter/model 경계 분리
|
|
202
|
+
- `springboot`: 기능 package 하위 controller/service/repository/entity/dto 분리
|
|
203
|
+
- `react`: 기능 폴더 하위 page/component/hook/service/store 분리
|
|
204
|
+
- `nextjs`: App Router 유지, 기능 폴더 하위 component/service/repository/type 분리
|
|
205
|
+
- `node-express`: 기능 폴더 하위 route/controller/service/repository 분리
|
|
206
|
+
- `fullstack-fastapi-react`: FastAPI 백엔드와 React 프론트엔드 모두 기능 폴더 하위 계층 분리 적용
|
|
204
207
|
|
|
205
208
|
여러 프로필은 comma로 조합할 수 있습니다.
|
|
206
209
|
|
|
207
210
|
```bash
|
|
208
211
|
codex-init --stack fastapi,react
|
|
209
212
|
codex-init --stack python
|
|
213
|
+
codex-init --stack typescript
|
|
214
|
+
codex-init --stack javascript
|
|
210
215
|
codex-init --stack fullstack-fastapi-react
|
|
211
216
|
codex-init --stack nextjs
|
|
212
217
|
```
|
|
@@ -217,7 +222,7 @@ codex-init --stack nextjs
|
|
|
217
222
|
- `--language en`을 사용하면 영어 문서를 생성합니다.
|
|
218
223
|
- `--docs strict`는 코드 변경 후 문서 업데이트 필수 규칙을 포함합니다.
|
|
219
224
|
- `--auth yes`는 인증/권한 규칙을 포함합니다.
|
|
220
|
-
- `--external-api yes`는 외부
|
|
225
|
+
- `--external-api yes`는 확인된 계약을 기준으로 실제 외부 연동을 구현하기 위한 client/adapter 및 계약 문서 규칙을 포함합니다.
|
|
221
226
|
- `--db mysql`처럼 DB를 지정하면 DB, Repository, Schema 관련 규칙을 포함합니다.
|
|
222
227
|
- 프로젝트 명세 템플릿은 `.codex/ref_docs`가 아니라 `docs/architecture`, `docs/api`, `docs/database`에 생성됩니다.
|
|
223
228
|
|
|
@@ -10,7 +10,7 @@ from typing import Optional
|
|
|
10
10
|
from codex_builder.builder import CodexBuilder, CodexBuildError, ExistingCodexError
|
|
11
11
|
from codex_builder.models import ConfigError
|
|
12
12
|
from codex_builder.prompt import PromptAbort, PromptSession
|
|
13
|
-
from codex_builder.profiles import UnknownProfileError,
|
|
13
|
+
from codex_builder.profiles import UnknownProfileError, supported_profile_labels
|
|
14
14
|
|
|
15
15
|
|
|
16
16
|
def build_parser() -> argparse.ArgumentParser:
|
|
@@ -22,7 +22,7 @@ def build_parser() -> argparse.ArgumentParser:
|
|
|
22
22
|
parser.add_argument("--description", help="Short project description.")
|
|
23
23
|
parser.add_argument(
|
|
24
24
|
"--stack",
|
|
25
|
-
help=f"Comma-separated stack profiles. Supported: {', '.join(
|
|
25
|
+
help=f"Comma-separated stack profiles. Supported: {', '.join(supported_profile_labels())}.",
|
|
26
26
|
)
|
|
27
27
|
parser.add_argument("--db", "--database", dest="database", help="Database name, for example mysql or postgres.")
|
|
28
28
|
parser.add_argument("--auth", help="Whether authentication is used: yes/no.")
|
|
@@ -46,17 +46,19 @@ class FrameworkProfile:
|
|
|
46
46
|
PYTHON_PROFILE = FrameworkProfile(
|
|
47
47
|
key="python",
|
|
48
48
|
display_name="Python",
|
|
49
|
-
philosophy_ko="일반 Python 프로젝트는 프레임워크 전제 없이
|
|
50
|
-
philosophy_en="General Python projects should
|
|
49
|
+
philosophy_ko="일반 Python 프로젝트는 프레임워크 전제 없이 기능/도메인 폴더를 우선하고, 각 기능 내부에서 service, adapter, CLI 경계를 명확히 분리한다.",
|
|
50
|
+
philosophy_en="General Python projects should prefer feature/domain folders first, then separate service, adapter, and CLI boundaries inside each feature without assuming a framework.",
|
|
51
51
|
architecture_ko=(
|
|
52
|
-
"패키지는 기능 또는 도메인 책임 기준으로 나누고
|
|
52
|
+
"패키지는 기능 또는 도메인 책임 기준으로 나누고 각 기능 폴더 하위에 service, adapter, model 등을 둔다.",
|
|
53
|
+
"공통 package는 여러 기능이 공유하는 정책, 설정, utility에만 사용한다.",
|
|
53
54
|
"CLI 또는 실행 진입점은 입력 파싱과 출력 변환만 담당하고 실제 판단은 service/application 계층에 위임한다.",
|
|
54
55
|
"핵심 도메인 함수와 클래스는 파일 시스템, 네트워크, 환경 변수, process args에 직접 의존하지 않게 한다.",
|
|
55
56
|
"파일, DB, 외부 API, subprocess 연동은 adapter 또는 infrastructure 모듈 뒤에 둔다.",
|
|
56
57
|
"권장 흐름: Entrypoint/CLI -> Application Service -> Domain/Adapter -> External System.",
|
|
57
58
|
),
|
|
58
59
|
architecture_en=(
|
|
59
|
-
"Split packages by feature or domain responsibility
|
|
60
|
+
"Split packages by feature or domain responsibility and place service, adapter, and model modules under each feature folder.",
|
|
61
|
+
"Use shared packages only for policies, settings, and utilities reused by multiple features.",
|
|
60
62
|
"CLI or runtime entrypoints should only parse input and map output, then delegate decisions to service/application layers.",
|
|
61
63
|
"Keep core domain functions and classes independent from filesystem, network, environment variables, and process args.",
|
|
62
64
|
"Put filesystem, database, external API, and subprocess integrations behind adapter or infrastructure modules.",
|
|
@@ -96,28 +98,105 @@ PYTHON_PROFILE = FrameworkProfile(
|
|
|
96
98
|
"Use `tmp_path`, monkeypatching, and test doubles to isolate filesystem and external dependencies.",
|
|
97
99
|
"Lock down CLI arguments, configuration parsing, and error paths with regression tests.",
|
|
98
100
|
),
|
|
99
|
-
directories=(
|
|
101
|
+
directories=(
|
|
102
|
+
"src/<package_name>/<feature>/services",
|
|
103
|
+
"src/<package_name>/<feature>/adapters",
|
|
104
|
+
"src/<package_name>/<feature>/models",
|
|
105
|
+
"src/<package_name>/shared",
|
|
106
|
+
"tests",
|
|
107
|
+
"scripts",
|
|
108
|
+
),
|
|
109
|
+
)
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
TYPESCRIPT_JAVASCRIPT_PROFILE = FrameworkProfile(
|
|
113
|
+
key="typescript-javascript",
|
|
114
|
+
display_name="TypeScript/JavaScript",
|
|
115
|
+
philosophy_ko="일반 TypeScript/JavaScript 프로젝트는 프레임워크 전제 없이 기능/도메인 폴더를 우선하고, 각 기능 내부에서 service, adapter, model 경계를 분리한다.",
|
|
116
|
+
philosophy_en="General TypeScript/JavaScript projects should prefer feature/domain folders first, then separate service, adapter, and model boundaries without assuming a framework.",
|
|
117
|
+
architecture_ko=(
|
|
118
|
+
"패키지는 기능 또는 도메인 책임 기준으로 나누고 각 기능 폴더 하위에 services, adapters, models/types 등을 둔다.",
|
|
119
|
+
"공통 모듈은 여러 기능에서 공유하는 정책, 설정, utility에만 사용한다.",
|
|
120
|
+
"실행 진입점은 입력 파싱과 출력 변환만 담당하고 실제 판단은 application/service 계층에 위임한다.",
|
|
121
|
+
"핵심 도메인 로직은 filesystem, network, environment, process 전역 객체에 직접 의존하지 않게 한다.",
|
|
122
|
+
"파일, DB, 외부 API, subprocess 연동은 adapter 또는 infrastructure 모듈 뒤에 둔다.",
|
|
123
|
+
"권장 흐름: Entrypoint -> Application Service -> Domain/Adapter -> External System.",
|
|
124
|
+
),
|
|
125
|
+
architecture_en=(
|
|
126
|
+
"Split modules by feature or domain responsibility and place services, adapters, and models/types under each feature folder.",
|
|
127
|
+
"Use shared modules only for policies, settings, and utilities reused by multiple features.",
|
|
128
|
+
"Runtime entrypoints should parse input and map output only, then delegate decisions to application/services.",
|
|
129
|
+
"Keep core domain logic independent from filesystem, network, environment, and process globals.",
|
|
130
|
+
"Put filesystem, database, external API, and subprocess integrations behind adapter or infrastructure modules.",
|
|
131
|
+
"Recommended flow: Entrypoint -> Application Service -> Domain/Adapter -> External System.",
|
|
132
|
+
),
|
|
133
|
+
framework_rules_ko=(
|
|
134
|
+
"프레임워크 전역 객체와 import 시점 부수효과에 의존하지 않는 모듈을 작성한다.",
|
|
135
|
+
"TypeScript는 공개 함수와 클래스의 타입을 명시하고, JavaScript는 필요한 경우 JSDoc으로 계약을 표현한다.",
|
|
136
|
+
"비동기 오류는 호출자가 처리할 수 있는 명확한 Error 타입과 메시지로 전달한다.",
|
|
137
|
+
"표준 런타임과 기존 dependency로 충분한 문제에 불필요한 패키지를 추가하지 않는다.",
|
|
138
|
+
"순수 계산 로직과 I/O 로직을 분리해 테스트와 재사용이 가능하게 한다.",
|
|
139
|
+
),
|
|
140
|
+
framework_rules_en=(
|
|
141
|
+
"Write modules that do not depend on framework globals or import-time side effects.",
|
|
142
|
+
"Use explicit types for public TypeScript functions and classes, and express JavaScript contracts with JSDoc when useful.",
|
|
143
|
+
"Propagate async failures with clear Error types and messages that callers can handle.",
|
|
144
|
+
"Do not add dependencies when the runtime and existing packages are sufficient.",
|
|
145
|
+
"Separate pure computation from I/O so logic stays testable and reusable.",
|
|
146
|
+
),
|
|
147
|
+
api_rules_ko=(
|
|
148
|
+
"공개 함수, 모듈 export, CLI 옵션은 외부 계약으로 보고 이름과 반환 형식을 안정적으로 관리한다.",
|
|
149
|
+
"외부 입력은 진입점에서 검증하고 내부 model로 변환한 뒤 service 계층에 전달한다.",
|
|
150
|
+
"공개 모듈의 오류와 비동기 동작은 호출자가 예측할 수 있게 문서화한다.",
|
|
151
|
+
),
|
|
152
|
+
api_rules_en=(
|
|
153
|
+
"Treat public functions, module exports, and CLI options as external contracts with stable names and return shapes.",
|
|
154
|
+
"Validate external input at the entrypoint and convert it into internal models before passing it to services.",
|
|
155
|
+
"Document public module errors and async behavior so callers can predict them.",
|
|
156
|
+
),
|
|
157
|
+
test_rules_ko=(
|
|
158
|
+
"순수 로직 테스트와 filesystem/network/environment I/O 테스트를 분리한다.",
|
|
159
|
+
"외부 의존성은 명시적인 test double과 임시 디렉토리로 격리한다.",
|
|
160
|
+
"CLI 인자, module export, 비동기 오류 경로는 회귀 테스트로 고정한다.",
|
|
161
|
+
),
|
|
162
|
+
test_rules_en=(
|
|
163
|
+
"Separate pure logic tests from filesystem, network, and environment I/O tests.",
|
|
164
|
+
"Isolate external dependencies with explicit test doubles and temporary directories.",
|
|
165
|
+
"Lock down CLI arguments, module exports, and async failure paths with regression tests.",
|
|
166
|
+
),
|
|
167
|
+
directories=(
|
|
168
|
+
"src/<feature>/services",
|
|
169
|
+
"src/<feature>/adapters",
|
|
170
|
+
"src/<feature>/models",
|
|
171
|
+
"src/shared",
|
|
172
|
+
"tests",
|
|
173
|
+
"scripts",
|
|
174
|
+
),
|
|
100
175
|
)
|
|
101
176
|
|
|
102
177
|
|
|
103
178
|
FASTAPI_PROFILE = FrameworkProfile(
|
|
104
179
|
key="fastapi",
|
|
105
180
|
display_name="FastAPI",
|
|
106
|
-
philosophy_ko="FastAPI의 가벼운 실행 모델은 유지하되,
|
|
107
|
-
philosophy_en="Keep FastAPI's lightweight runtime model, but
|
|
181
|
+
philosophy_ko="FastAPI의 가벼운 실행 모델은 유지하되, login/post/talk 같은 기능 폴더를 먼저 나누고 각 기능 안에서 Controller, Service, Repository, Schema, Entity 계층을 분리한다.",
|
|
182
|
+
philosophy_en="Keep FastAPI's lightweight runtime model, but split by feature/domain first, then separate Controller, Service, Repository, Schema, and Entity layers inside each feature.",
|
|
108
183
|
architecture_ko=(
|
|
109
|
-
"
|
|
110
|
-
"
|
|
184
|
+
"기능 경계는 `app/features/<feature>` 아래에 둔다. 예: login, post, talk.",
|
|
185
|
+
"HTTP 엔드포인트는 각 기능의 controller/router 계층에 두고 비즈니스 판단은 같은 기능의 service 계층으로 위임한다.",
|
|
186
|
+
"service 계층은 유스케이스 단위 메서드를 제공하고 같은 기능의 repository 또는 외부 연동 client를 조합한다.",
|
|
111
187
|
"repository 계층은 DB 접근만 담당하며 HTTP 요청/응답 객체를 알지 못해야 한다.",
|
|
112
|
-
"Pydantic schema는 요청/응답 계약이고 ORM entity는 저장 모델이다. 두
|
|
113
|
-
"
|
|
188
|
+
"Pydantic schema는 요청/응답 계약이고 ORM entity는 저장 모델이다. 두 모델은 같은 기능 폴더 안에서도 혼용하지 않는다.",
|
|
189
|
+
"공통 설정, DB session, 인증 dependency처럼 여러 기능이 공유하는 요소만 core/shared에 둔다.",
|
|
190
|
+
"권장 흐름: Controller -> Service -> Repository -> DB, 단 각 계층은 같은 기능 폴더 안에 둔다.",
|
|
114
191
|
),
|
|
115
192
|
architecture_en=(
|
|
116
|
-
"
|
|
117
|
-
"
|
|
193
|
+
"Use `app/features/<feature>` as the feature boundary, for example login, post, or talk.",
|
|
194
|
+
"Place HTTP endpoints in each feature's controller/router layer and delegate business decisions to the same feature's services.",
|
|
195
|
+
"Services expose use-case methods and coordinate repositories or external clients within the same feature.",
|
|
118
196
|
"Repositories only handle database access and must not know HTTP request/response objects.",
|
|
119
|
-
"Pydantic schemas are request/response contracts and ORM entities are persistence models. Do not mix them.",
|
|
120
|
-
"
|
|
197
|
+
"Pydantic schemas are request/response contracts and ORM entities are persistence models. Do not mix them even inside the same feature folder.",
|
|
198
|
+
"Put only cross-feature settings, DB sessions, auth dependencies, and shared policies in core/shared.",
|
|
199
|
+
"Recommended flow: Controller -> Service -> Repository -> DB, with each layer located inside the same feature folder.",
|
|
121
200
|
),
|
|
122
201
|
framework_rules_ko=(
|
|
123
202
|
"APIRouter는 라우팅과 의존성 연결만 담당한다.",
|
|
@@ -153,27 +232,39 @@ FASTAPI_PROFILE = FrameworkProfile(
|
|
|
153
232
|
"Use TestClient or httpx-based API tests to verify request/response contracts.",
|
|
154
233
|
"Verify repository tests against database schema and query behavior separately.",
|
|
155
234
|
),
|
|
156
|
-
directories=(
|
|
235
|
+
directories=(
|
|
236
|
+
"app/features/<feature>/controllers",
|
|
237
|
+
"app/features/<feature>/services",
|
|
238
|
+
"app/features/<feature>/repositories",
|
|
239
|
+
"app/features/<feature>/schemas",
|
|
240
|
+
"app/features/<feature>/entities",
|
|
241
|
+
"app/core",
|
|
242
|
+
"app/shared",
|
|
243
|
+
),
|
|
157
244
|
)
|
|
158
245
|
|
|
159
246
|
SPRINGBOOT_PROFILE = FrameworkProfile(
|
|
160
247
|
key="springboot",
|
|
161
248
|
display_name="Spring Boot",
|
|
162
|
-
philosophy_ko="Spring Boot 프로젝트는 Controller, Service, Repository, Entity, DTO 계층과 명시적 의존성 주입을
|
|
163
|
-
philosophy_en="Spring Boot projects should keep explicit Controller, Service, Repository, Entity, and DTO layers with dependency injection.",
|
|
249
|
+
philosophy_ko="Spring Boot 프로젝트는 기능 package를 먼저 나누고 각 기능 안에서 Controller, Service, Repository, Entity, DTO 계층과 명시적 의존성 주입을 유지한다.",
|
|
250
|
+
philosophy_en="Spring Boot projects should split by feature package first, then keep explicit Controller, Service, Repository, Entity, and DTO layers with dependency injection inside each feature.",
|
|
164
251
|
architecture_ko=(
|
|
165
|
-
"
|
|
166
|
-
"
|
|
252
|
+
"기능 경계는 `.../<feature>` package 아래에 둔다. 예: login, post, talk.",
|
|
253
|
+
"Controller는 각 기능 package 안에서 HTTP 요청/응답과 인증 컨텍스트 연결만 담당한다.",
|
|
254
|
+
"Service는 같은 기능 package 안에서 트랜잭션 경계와 비즈니스 규칙의 중심이다.",
|
|
167
255
|
"Repository는 JPA 또는 데이터 접근 인터페이스로 제한한다.",
|
|
168
256
|
"Entity는 DB 영속성 모델이고 DTO는 API 계약이다. Entity를 외부 응답으로 직접 노출하지 않는다.",
|
|
169
|
-
"
|
|
257
|
+
"공통 config, security, exception handler만 전역 package로 분리한다.",
|
|
258
|
+
"권장 흐름: Controller -> Service -> Repository -> DB, 단 각 계층은 같은 기능 package 안에 둔다.",
|
|
170
259
|
),
|
|
171
260
|
architecture_en=(
|
|
172
|
-
"
|
|
173
|
-
"
|
|
261
|
+
"Use `.../<feature>` packages as feature boundaries, for example login, post, or talk.",
|
|
262
|
+
"Controllers handle HTTP request/response mapping and security context wiring only inside each feature package.",
|
|
263
|
+
"Services own transaction boundaries and business rules inside the same feature package.",
|
|
174
264
|
"Repositories are limited to JPA or data access interfaces.",
|
|
175
265
|
"Entities are persistence models and DTOs are API contracts. Never expose entities directly in external responses.",
|
|
176
|
-
"
|
|
266
|
+
"Keep only shared config, security, and exception handlers in global packages.",
|
|
267
|
+
"Recommended flow: Controller -> Service -> Repository -> DB, with each layer located inside the same feature package.",
|
|
177
268
|
),
|
|
178
269
|
framework_rules_ko=(
|
|
179
270
|
"생성자 주입을 기본으로 사용한다.",
|
|
@@ -207,29 +298,38 @@ SPRINGBOOT_PROFILE = FrameworkProfile(
|
|
|
207
298
|
"Verify API contracts with WebMvcTest or integration tests.",
|
|
208
299
|
"Use DataJpaTest when repository queries and mappings need verification.",
|
|
209
300
|
),
|
|
210
|
-
directories=(
|
|
301
|
+
directories=(
|
|
302
|
+
"src/main/java/.../<feature>/controller",
|
|
303
|
+
"src/main/java/.../<feature>/service",
|
|
304
|
+
"src/main/java/.../<feature>/repository",
|
|
305
|
+
"src/main/java/.../<feature>/entity",
|
|
306
|
+
"src/main/java/.../<feature>/dto",
|
|
307
|
+
"src/main/java/.../global",
|
|
308
|
+
),
|
|
211
309
|
)
|
|
212
310
|
|
|
213
311
|
REACT_PROFILE = FrameworkProfile(
|
|
214
312
|
key="react",
|
|
215
313
|
display_name="React",
|
|
216
|
-
philosophy_ko="React 코드는 화면, 컴포넌트, 훅, 서비스, 상태 저장소를
|
|
217
|
-
philosophy_en="React code should separate pages, components, hooks, services, and stores without mixing UI and data access.",
|
|
314
|
+
philosophy_ko="React 코드는 기능 폴더를 먼저 나누고 각 기능 안에서 화면, 컴포넌트, 훅, 서비스, 상태 저장소를 분리해 UI와 데이터 접근을 섞지 않는다.",
|
|
315
|
+
philosophy_en="React code should split by feature first, then separate pages, components, hooks, services, and stores inside each feature without mixing UI and data access.",
|
|
218
316
|
architecture_ko=(
|
|
219
|
-
"
|
|
220
|
-
"
|
|
317
|
+
"기능 경계는 `src/features/<feature>` 아래에 둔다. 예: login, post, talk.",
|
|
318
|
+
"page는 각 기능의 라우팅 단위 화면 조립을 담당한다.",
|
|
319
|
+
"component는 기능 내부 UI와 표시 책임을 담당하고 여러 기능에서 재사용되는 것만 shared에 둔다.",
|
|
221
320
|
"hook은 화면 상태, 비동기 흐름, UI 유스케이스를 캡슐화한다.",
|
|
222
321
|
"service는 API 호출과 외부 클라이언트 경계만 담당한다.",
|
|
223
322
|
"store는 전역 상태를 담당하고 서버 응답 원본을 무분별하게 복제하지 않는다.",
|
|
224
|
-
"권장 흐름: Page -> Hook/Store -> Service -> API
|
|
323
|
+
"권장 흐름: Page -> Hook/Store -> Service -> API, 단 구현 위치는 같은 기능 폴더 안이다.",
|
|
225
324
|
),
|
|
226
325
|
architecture_en=(
|
|
227
|
-
"
|
|
228
|
-
"
|
|
326
|
+
"Use `src/features/<feature>` as the feature boundary, for example login, post, or talk.",
|
|
327
|
+
"Pages compose route-level screens inside each feature.",
|
|
328
|
+
"Components own feature UI and presentation; move only cross-feature reusable components to shared.",
|
|
229
329
|
"Hooks encapsulate screen state, async flow, and UI use cases.",
|
|
230
330
|
"Services own API calls and external client boundaries only.",
|
|
231
331
|
"Stores own global state and should not blindly duplicate raw server responses.",
|
|
232
|
-
"Recommended flow: Page -> Hook/Store -> Service -> API.",
|
|
332
|
+
"Recommended flow: Page -> Hook/Store -> Service -> API, with implementation located inside the same feature folder.",
|
|
233
333
|
),
|
|
234
334
|
framework_rules_ko=(
|
|
235
335
|
"컴포넌트 안에서 fetch/axios 호출을 직접 수행하지 않는다.",
|
|
@@ -263,27 +363,37 @@ REACT_PROFILE = FrameworkProfile(
|
|
|
263
363
|
"Hook tests verify state transitions and async flow.",
|
|
264
364
|
"Service tests verify API client boundaries and error mapping.",
|
|
265
365
|
),
|
|
266
|
-
directories=(
|
|
366
|
+
directories=(
|
|
367
|
+
"src/features/<feature>/pages",
|
|
368
|
+
"src/features/<feature>/components",
|
|
369
|
+
"src/features/<feature>/hooks",
|
|
370
|
+
"src/features/<feature>/services",
|
|
371
|
+
"src/features/<feature>/stores",
|
|
372
|
+
"src/features/<feature>/types",
|
|
373
|
+
"src/shared",
|
|
374
|
+
),
|
|
267
375
|
)
|
|
268
376
|
|
|
269
377
|
NEXTJS_PROFILE = FrameworkProfile(
|
|
270
378
|
key="nextjs",
|
|
271
379
|
display_name="Next.js",
|
|
272
|
-
philosophy_ko="Next.js는 App Router를 기본으로
|
|
273
|
-
philosophy_en="Next.js should use the App Router by default and clearly separate Server Component and Client Component responsibilities.",
|
|
380
|
+
philosophy_ko="Next.js는 App Router를 기본으로 하되, 기능 구현은 features 하위에 모으고 Server Component와 Client Component의 책임을 명확히 구분한다.",
|
|
381
|
+
philosophy_en="Next.js should use the App Router by default, keep feature implementation under features, and clearly separate Server Component and Client Component responsibilities.",
|
|
274
382
|
architecture_ko=(
|
|
275
383
|
"app 디렉토리는 라우트, 레이아웃, 서버 데이터 로딩의 기준 위치로 사용한다.",
|
|
384
|
+
"기능별 component, service, repository, type은 `features/<feature>` 아래에 둔다.",
|
|
276
385
|
"Server Component는 데이터 조회와 정적/서버 렌더링 책임을 담당한다.",
|
|
277
386
|
"Client Component는 브라우저 상태, 이벤트, 인터랙션이 필요한 경우에만 사용한다.",
|
|
278
387
|
"route handler는 API boundary이며 복잡한 비즈니스 로직은 service 계층으로 분리한다.",
|
|
279
|
-
"권장 흐름: Route/Page ->
|
|
388
|
+
"권장 흐름: Route/Page -> Feature Service -> Repository/External API.",
|
|
280
389
|
),
|
|
281
390
|
architecture_en=(
|
|
282
391
|
"Use the app directory as the place for routes, layouts, and server data loading.",
|
|
392
|
+
"Place feature components, services, repositories, and types under `features/<feature>`.",
|
|
283
393
|
"Server Components own data reads and static/server rendering.",
|
|
284
394
|
"Client Components are only for browser state, events, and interactions.",
|
|
285
395
|
"Route handlers are API boundaries; move complex business logic into services.",
|
|
286
|
-
"Recommended flow: Route/Page ->
|
|
396
|
+
"Recommended flow: Route/Page -> Feature Service -> Repository/External API.",
|
|
287
397
|
),
|
|
288
398
|
framework_rules_ko=(
|
|
289
399
|
"'use client'는 필요한 파일에만 선언한다.",
|
|
@@ -317,27 +427,39 @@ NEXTJS_PROFILE = FrameworkProfile(
|
|
|
317
427
|
"Verify Client Components around user interactions.",
|
|
318
428
|
"Verify routing and auth flow with integration or E2E tests.",
|
|
319
429
|
),
|
|
320
|
-
directories=(
|
|
430
|
+
directories=(
|
|
431
|
+
"app",
|
|
432
|
+
"features/<feature>/components",
|
|
433
|
+
"features/<feature>/services",
|
|
434
|
+
"features/<feature>/repositories",
|
|
435
|
+
"features/<feature>/types",
|
|
436
|
+
"components/shared",
|
|
437
|
+
"lib",
|
|
438
|
+
),
|
|
321
439
|
)
|
|
322
440
|
|
|
323
441
|
NODE_EXPRESS_PROFILE = FrameworkProfile(
|
|
324
442
|
key="node-express",
|
|
325
443
|
display_name="Node Express",
|
|
326
|
-
philosophy_ko="Express는 얇은 HTTP 계층으로 유지하고 route, controller, service, repository를 분리한다.",
|
|
327
|
-
philosophy_en="Keep Express as a thin HTTP layer and separate routes, controllers, services, and repositories.",
|
|
444
|
+
philosophy_ko="Express는 얇은 HTTP 계층으로 유지하고 기능 폴더 안에서 route, controller, service, repository를 분리한다.",
|
|
445
|
+
philosophy_en="Keep Express as a thin HTTP layer and separate routes, controllers, services, and repositories inside feature folders.",
|
|
328
446
|
architecture_ko=(
|
|
329
|
-
"
|
|
447
|
+
"기능 경계는 `src/features/<feature>` 아래에 둔다. 예: login, post, talk.",
|
|
448
|
+
"route는 각 기능 내부에서 URL과 middleware 연결만 담당한다.",
|
|
330
449
|
"controller는 요청 파싱, 응답 변환, service 호출만 담당한다.",
|
|
331
|
-
"service는 비즈니스 유스케이스와 트랜잭션 흐름을 담당한다.",
|
|
450
|
+
"service는 같은 기능 내부에서 비즈니스 유스케이스와 트랜잭션 흐름을 담당한다.",
|
|
332
451
|
"repository는 DB 접근을 담당하고 Express 객체를 알지 못해야 한다.",
|
|
333
|
-
"
|
|
452
|
+
"공통 middleware, config, error handler만 전역 경계로 둔다.",
|
|
453
|
+
"권장 흐름: Route -> Controller -> Service -> Repository -> DB, 단 각 계층은 같은 기능 폴더 안에 둔다.",
|
|
334
454
|
),
|
|
335
455
|
architecture_en=(
|
|
336
|
-
"
|
|
456
|
+
"Use `src/features/<feature>` as the feature boundary, for example login, post, or talk.",
|
|
457
|
+
"Routes only connect URLs and middleware inside each feature.",
|
|
337
458
|
"Controllers parse requests, map responses, and call services only.",
|
|
338
|
-
"Services own business use cases and transaction flow.",
|
|
459
|
+
"Services own business use cases and transaction flow inside the same feature.",
|
|
339
460
|
"Repositories own database access and must not know Express objects.",
|
|
340
|
-
"
|
|
461
|
+
"Keep only shared middleware, config, and error handlers in global boundaries.",
|
|
462
|
+
"Recommended flow: Route -> Controller -> Service -> Repository -> DB, with each layer located inside the same feature folder.",
|
|
341
463
|
),
|
|
342
464
|
framework_rules_ko=(
|
|
343
465
|
"비동기 오류 처리는 공통 error middleware로 모은다.",
|
|
@@ -371,24 +493,32 @@ NODE_EXPRESS_PROFILE = FrameworkProfile(
|
|
|
371
493
|
"Controller/route tests verify HTTP contracts with tools such as supertest.",
|
|
372
494
|
"Repository tests verify real query and transaction behavior.",
|
|
373
495
|
),
|
|
374
|
-
directories=(
|
|
496
|
+
directories=(
|
|
497
|
+
"src/features/<feature>/routes",
|
|
498
|
+
"src/features/<feature>/controllers",
|
|
499
|
+
"src/features/<feature>/services",
|
|
500
|
+
"src/features/<feature>/repositories",
|
|
501
|
+
"src/features/<feature>/types",
|
|
502
|
+
"src/shared",
|
|
503
|
+
"src/middlewares",
|
|
504
|
+
),
|
|
375
505
|
)
|
|
376
506
|
|
|
377
507
|
FULLSTACK_FASTAPI_REACT_PROFILE = FrameworkProfile(
|
|
378
508
|
key="fullstack-fastapi-react",
|
|
379
509
|
display_name="Fullstack FastAPI + React",
|
|
380
|
-
philosophy_ko="FastAPI 백엔드와 React 프론트엔드는 API 계약을 중심으로 분리하고,
|
|
381
|
-
philosophy_en="Separate the FastAPI backend and React frontend around the API contract while keeping
|
|
510
|
+
philosophy_ko="FastAPI 백엔드와 React 프론트엔드는 API 계약을 중심으로 분리하고, 양쪽 모두 기능 폴더 안에서 계층 규칙을 지킨다.",
|
|
511
|
+
philosophy_en="Separate the FastAPI backend and React frontend around the API contract while keeping layer rules inside feature folders on both sides.",
|
|
382
512
|
architecture_ko=(
|
|
383
|
-
"backend는 Controller -> Service -> Repository -> DB 흐름을
|
|
384
|
-
"frontend는 Page -> Hook/Store -> Service -> API 흐름을
|
|
513
|
+
"backend는 Controller -> Service -> Repository -> DB 흐름을 따르되 각 계층은 `backend/app/features/<feature>` 안에 둔다.",
|
|
514
|
+
"frontend는 Page -> Hook/Store -> Service -> API 흐름을 따르되 각 계층은 `frontend/src/features/<feature>` 안에 둔다.",
|
|
385
515
|
"docs/api/specification.md는 백엔드 response schema와 프론트엔드 service 타입의 공통 계약이다.",
|
|
386
516
|
"백엔드 Entity를 프론트엔드 타입으로 직접 취급하지 않는다.",
|
|
387
517
|
"인증, 오류, pagination, enum 값은 양쪽 구현과 문서를 함께 갱신한다.",
|
|
388
518
|
),
|
|
389
519
|
architecture_en=(
|
|
390
|
-
"Backend follows Controller -> Service -> Repository -> DB
|
|
391
|
-
"Frontend follows Page -> Hook/Store -> Service -> API
|
|
520
|
+
"Backend follows Controller -> Service -> Repository -> DB, with each layer under `backend/app/features/<feature>`.",
|
|
521
|
+
"Frontend follows Page -> Hook/Store -> Service -> API, with each layer under `frontend/src/features/<feature>`.",
|
|
392
522
|
"docs/api/specification.md is the shared contract between backend response schemas and frontend service types.",
|
|
393
523
|
"Do not treat backend entities as frontend types directly.",
|
|
394
524
|
"Update auth, errors, pagination, and enum values in both implementations and docs.",
|
|
@@ -420,14 +550,27 @@ FULLSTACK_FASTAPI_REACT_PROFILE = FrameworkProfile(
|
|
|
420
550
|
test_rules_ko=(
|
|
421
551
|
"백엔드 API 테스트와 프론트엔드 service 테스트가 같은 계약을 검증하게 한다.",
|
|
422
552
|
"주요 사용자 흐름은 E2E 또는 통합 테스트 대상으로 둔다.",
|
|
423
|
-
"API
|
|
553
|
+
"API contract test는 실제 docs/api/specification.md와 불일치하지 않게 관리한다.",
|
|
424
554
|
),
|
|
425
555
|
test_rules_en=(
|
|
426
556
|
"Backend API tests and frontend service tests should verify the same contract.",
|
|
427
557
|
"Cover major user flows with E2E or integration tests.",
|
|
428
|
-
"Keep API
|
|
558
|
+
"Keep API contract tests aligned with docs/api/specification.md.",
|
|
559
|
+
),
|
|
560
|
+
directories=(
|
|
561
|
+
"backend/app/features/<feature>/controllers",
|
|
562
|
+
"backend/app/features/<feature>/services",
|
|
563
|
+
"backend/app/features/<feature>/repositories",
|
|
564
|
+
"backend/app/features/<feature>/schemas",
|
|
565
|
+
"backend/app/features/<feature>/entities",
|
|
566
|
+
"frontend/src/features/<feature>/pages",
|
|
567
|
+
"frontend/src/features/<feature>/components",
|
|
568
|
+
"frontend/src/features/<feature>/hooks",
|
|
569
|
+
"frontend/src/features/<feature>/services",
|
|
570
|
+
"frontend/src/features/<feature>/stores",
|
|
571
|
+
"backend/app/core",
|
|
572
|
+
"frontend/src/shared",
|
|
429
573
|
),
|
|
430
|
-
directories=("backend/app/controllers", "backend/app/services", "backend/app/repositories", "frontend/src/pages", "frontend/src/components", "frontend/src/services", "frontend/src/stores"),
|
|
431
574
|
)
|
|
432
575
|
|
|
433
576
|
PROFILES: dict[str, FrameworkProfile] = {
|
|
@@ -435,6 +578,7 @@ PROFILES: dict[str, FrameworkProfile] = {
|
|
|
435
578
|
for profile in (
|
|
436
579
|
FASTAPI_PROFILE,
|
|
437
580
|
PYTHON_PROFILE,
|
|
581
|
+
TYPESCRIPT_JAVASCRIPT_PROFILE,
|
|
438
582
|
SPRINGBOOT_PROFILE,
|
|
439
583
|
REACT_PROFILE,
|
|
440
584
|
NEXTJS_PROFILE,
|
|
@@ -449,6 +593,16 @@ PROFILE_ALIASES: dict[str, str] = {
|
|
|
449
593
|
"plain-python": "python",
|
|
450
594
|
"vanilla-python": "python",
|
|
451
595
|
"general-python": "python",
|
|
596
|
+
"ts": "typescript-javascript",
|
|
597
|
+
"js": "typescript-javascript",
|
|
598
|
+
"typescript": "typescript-javascript",
|
|
599
|
+
"javascript": "typescript-javascript",
|
|
600
|
+
"ts-js": "typescript-javascript",
|
|
601
|
+
"ts/js": "typescript-javascript",
|
|
602
|
+
"plain-typescript": "typescript-javascript",
|
|
603
|
+
"plain-javascript": "typescript-javascript",
|
|
604
|
+
"vanilla-typescript": "typescript-javascript",
|
|
605
|
+
"vanilla-javascript": "typescript-javascript",
|
|
452
606
|
"fast-api": "fastapi",
|
|
453
607
|
"spring": "springboot",
|
|
454
608
|
"spring-boot": "springboot",
|
|
@@ -468,6 +622,12 @@ def supported_profile_names() -> tuple[str, ...]:
|
|
|
468
622
|
return tuple(PROFILES)
|
|
469
623
|
|
|
470
624
|
|
|
625
|
+
def supported_profile_labels() -> tuple[str, ...]:
|
|
626
|
+
"""Return user-facing labels for supported profile choices."""
|
|
627
|
+
|
|
628
|
+
return tuple(f"{profile.key} ({profile.display_name})" for profile in PROFILES.values())
|
|
629
|
+
|
|
630
|
+
|
|
471
631
|
def canonical_profile_name(name: str) -> str:
|
|
472
632
|
"""Normalize a user-provided stack name to a supported profile key."""
|
|
473
633
|
|
|
@@ -14,7 +14,7 @@ from codex_builder.constants import (
|
|
|
14
14
|
DEFAULT_STACK,
|
|
15
15
|
)
|
|
16
16
|
from codex_builder.models import ConfigError, ProjectConfig, parse_yes_no
|
|
17
|
-
from codex_builder.profiles import
|
|
17
|
+
from codex_builder.profiles import supported_profile_labels
|
|
18
18
|
from codex_builder.validator import (
|
|
19
19
|
validate_docs_level,
|
|
20
20
|
validate_existing_directory,
|
|
@@ -149,8 +149,8 @@ class PromptSession:
|
|
|
149
149
|
|
|
150
150
|
def prompt_stack(self) -> tuple[str, ...]:
|
|
151
151
|
self._write("지원 프로필:")
|
|
152
|
-
for
|
|
153
|
-
self._write(f"- {
|
|
152
|
+
for profile_label in supported_profile_labels():
|
|
153
|
+
self._write(f"- {profile_label}")
|
|
154
154
|
|
|
155
155
|
while True:
|
|
156
156
|
raw_stack = self.prompt_text("사용 스택", ",".join(DEFAULT_STACK))
|
|
@@ -85,7 +85,7 @@ class TemplateRenderer:
|
|
|
85
85
|
"- Do not put all logic in one file for convenience.",
|
|
86
86
|
"- Do not place business logic in controllers, route handlers, or UI components.",
|
|
87
87
|
"- Do not return persistence entities directly from public APIs.",
|
|
88
|
-
|
|
88
|
+
"- Implement external integrations against their confirmed contracts and keep failure behavior explicit.",
|
|
89
89
|
"- Do not add core code without documenting the intent when the behavior is non-obvious.",
|
|
90
90
|
]
|
|
91
91
|
)
|
|
@@ -119,7 +119,7 @@ class TemplateRenderer:
|
|
|
119
119
|
"- 편의를 위해 모든 로직을 한 파일에 몰아넣지 않는다.",
|
|
120
120
|
"- controller, route handler, UI component에 비즈니스 로직을 넣지 않는다.",
|
|
121
121
|
"- persistence entity를 public API 응답으로 직접 반환하지 않는다.",
|
|
122
|
-
"- 외부
|
|
122
|
+
"- 외부 연동은 확인된 계약을 기준으로 구현하고 실패 동작을 명시한다.",
|
|
123
123
|
"- 동작이 비명시적인 핵심 코드에 의도 설명 없이 코드를 추가하지 않는다.",
|
|
124
124
|
]
|
|
125
125
|
)
|
|
@@ -139,7 +139,7 @@ class TemplateRenderer:
|
|
|
139
139
|
lines.extend(
|
|
140
140
|
[
|
|
141
141
|
"- External API calls must go through a dedicated client/adapter layer.",
|
|
142
|
-
"-
|
|
142
|
+
"- Implement confirmed credentials, contracts, schemas, and failure handling together.",
|
|
143
143
|
]
|
|
144
144
|
)
|
|
145
145
|
if config.database:
|
|
@@ -160,7 +160,7 @@ class TemplateRenderer:
|
|
|
160
160
|
lines.extend(
|
|
161
161
|
[
|
|
162
162
|
"- 외부 API 호출은 반드시 전용 client/adapter 계층을 통해 수행한다.",
|
|
163
|
-
"-
|
|
163
|
+
"- 외부 연동은 인증 정보, 계약, schema, 실패 처리를 실제 구현과 함께 관리한다.",
|
|
164
164
|
]
|
|
165
165
|
)
|
|
166
166
|
if config.database:
|
|
@@ -174,7 +174,7 @@ class TemplateRenderer:
|
|
|
174
174
|
lines = [
|
|
175
175
|
f"# Architecture Rules - {config.project_name}",
|
|
176
176
|
"",
|
|
177
|
-
"This project follows a layered architecture. Keep responsibilities explicit and directional.",
|
|
177
|
+
"This project follows a feature-first layered architecture. Keep responsibilities explicit and directional.",
|
|
178
178
|
"",
|
|
179
179
|
"[Directory Structure]",
|
|
180
180
|
]
|
|
@@ -183,6 +183,11 @@ class TemplateRenderer:
|
|
|
183
183
|
lines.extend(f"- `{directory}`" for directory in profile.directories)
|
|
184
184
|
lines.extend(
|
|
185
185
|
[
|
|
186
|
+
"",
|
|
187
|
+
"[Feature Module Layout]",
|
|
188
|
+
"- Prefer feature/domain folders as the outer boundary, such as `login`, `post`, or `talk`.",
|
|
189
|
+
"- Place layer folders such as `controllers`, `services`, `repositories`, `schemas`, `entities`, `components`, or `hooks` inside each feature when the active profile uses them.",
|
|
190
|
+
"- Keep cross-cutting code in shared/core/infrastructure folders only when it is reused by multiple features.",
|
|
186
191
|
"",
|
|
187
192
|
"[Layer Responsibilities]",
|
|
188
193
|
"- Presentation/API layers handle request parsing, response mapping, routing, and UI composition only.",
|
|
@@ -203,7 +208,7 @@ class TemplateRenderer:
|
|
|
203
208
|
lines = [
|
|
204
209
|
f"# 아키텍처 규칙 - {config.project_name}",
|
|
205
210
|
"",
|
|
206
|
-
"이 프로젝트는
|
|
211
|
+
"이 프로젝트는 기능 우선 계층형 아키텍처를 따른다. 책임과 의존성 방향을 반드시 분리한다.",
|
|
207
212
|
"",
|
|
208
213
|
"[디렉토리 구조]",
|
|
209
214
|
]
|
|
@@ -212,6 +217,11 @@ class TemplateRenderer:
|
|
|
212
217
|
lines.extend(f"- `{directory}`" for directory in profile.directories)
|
|
213
218
|
lines.extend(
|
|
214
219
|
[
|
|
220
|
+
"",
|
|
221
|
+
"[기능 모듈 배치]",
|
|
222
|
+
"- `login`, `post`, `talk` 같은 기능/도메인 폴더를 바깥 경계로 우선한다.",
|
|
223
|
+
"- 활성 profile에서 사용하는 `controllers`, `services`, `repositories`, `schemas`, `entities`, `components`, `hooks` 같은 계층 폴더는 각 기능 폴더 안에 둔다.",
|
|
224
|
+
"- 여러 기능에서 공유하는 설정, 공통 middleware, 공통 client만 shared/core/infrastructure에 둔다.",
|
|
215
225
|
"",
|
|
216
226
|
"[계층 책임]",
|
|
217
227
|
"- Presentation/API 계층은 요청 파싱, 응답 변환, 라우팅, 화면 조립만 담당한다.",
|
|
@@ -253,7 +263,7 @@ class TemplateRenderer:
|
|
|
253
263
|
"[Spacing Rules]",
|
|
254
264
|
"- Keep method boundaries visually obvious. Use the repository's formatter when it enforces exact blank lines.",
|
|
255
265
|
"- When the formatter allows manual spacing, keep five blank-line units between methods.",
|
|
256
|
-
"- Inside a method, separate unrelated logic blocks with
|
|
266
|
+
"- Inside a method, separate unrelated logic blocks with three blank-line units when the formatter allows it.",
|
|
257
267
|
"- If a function signature, expression, query, JSX block, or builder chain spans multiple lines, leave a blank line before starting the next logic block.",
|
|
258
268
|
"- Do not compress validation, transformation, persistence, and response mapping into one dense block.",
|
|
259
269
|
"",
|
|
@@ -313,7 +323,7 @@ class TemplateRenderer:
|
|
|
313
323
|
"[공백 규칙]",
|
|
314
324
|
"- 메서드 경계는 눈으로 분명히 구분되어야 한다. 정확한 공백 수는 저장소 formatter가 강제하면 formatter를 따른다.",
|
|
315
325
|
"- formatter가 수동 공백을 허용하면 메서드 간 5칸 공백을 둔다.",
|
|
316
|
-
"- 메서드 내부의 연관 없는 로직 블록 간에는 formatter가 허용하는 범위에서
|
|
326
|
+
"- 메서드 내부의 연관 없는 로직 블록 간에는 formatter가 허용하는 범위에서 3칸 공백을 둔다.",
|
|
317
327
|
"- 함수 시그니처, 표현식, 쿼리, JSX block, builder chain이 여러 줄에 걸쳐 작성되면 다음 로직 블록 전에는 반드시 공백을 둔다.",
|
|
318
328
|
"- validation, transformation, persistence, response mapping을 하나의 빽빽한 블록으로 압축하지 않는다.",
|
|
319
329
|
"",
|
|
@@ -777,34 +787,16 @@ class TemplateRenderer:
|
|
|
777
787
|
"- Third-party HTTP APIs or SDKs",
|
|
778
788
|
"- Any system outside the current repository process",
|
|
779
789
|
"",
|
|
780
|
-
"[
|
|
781
|
-
"-
|
|
782
|
-
"- When the contract is missing, write only the class/method boundary and leave the body as `pass` or the language equivalent.",
|
|
783
|
-
"- Add TODO comments that state required input, expected output, upstream endpoint or SDK method, timeout, retry, and error mapping.",
|
|
790
|
+
"[Implementation]",
|
|
791
|
+
"- Implement external calls with the confirmed credentials, contract, timeout behavior, and failure mapping.",
|
|
784
792
|
"- Services call integrations through narrow methods that describe business intent.",
|
|
785
793
|
"- Convert upstream DTOs into internal DTOs before returning data to callers.",
|
|
786
|
-
"",
|
|
787
|
-
"[Python Skeleton]",
|
|
788
|
-
"```python",
|
|
789
|
-
"def request_external_job(self, request_dto: ExternalJobRequestDto) -> ExternalJobResponseDto:",
|
|
790
|
-
' """Request a job from the external system."""',
|
|
791
|
-
" # TODO: define upstream endpoint, request fields, response fields, timeout, retry, and failure mapping.",
|
|
792
|
-
" pass",
|
|
793
|
-
"```",
|
|
794
|
+
"- Keep request/response schemas and integration documentation synchronized with the implementation.",
|
|
794
795
|
"",
|
|
795
796
|
"[Prohibited]",
|
|
796
|
-
"-
|
|
797
|
-
"- Random sample data that pretends to be upstream data.",
|
|
797
|
+
"- Fabricated upstream responses or behavior that bypasses the confirmed contract.",
|
|
798
798
|
"- Calling external HTTP or SDK code directly from controllers or UI components.",
|
|
799
799
|
]
|
|
800
|
-
if not config.external_api_enabled:
|
|
801
|
-
lines.extend(
|
|
802
|
-
[
|
|
803
|
-
"",
|
|
804
|
-
"[When External APIs Are Added Later]",
|
|
805
|
-
"- Update project configuration and docs before introducing the integration.",
|
|
806
|
-
]
|
|
807
|
-
)
|
|
808
800
|
for profile in profiles:
|
|
809
801
|
lines.extend(["", f"[{profile.display_name} Integration Notes]"])
|
|
810
802
|
lines.extend(self._bullets(self._profile_external_rules(profile, config.language)))
|
|
@@ -821,34 +813,15 @@ class TemplateRenderer:
|
|
|
821
813
|
"- Third-party HTTP API 또는 SDK",
|
|
822
814
|
"- 현재 저장소 프로세스 밖의 모든 시스템",
|
|
823
815
|
"",
|
|
824
|
-
"[규칙]",
|
|
825
|
-
"- 인증 정보, 계약, timeout, 실패
|
|
826
|
-
"- 계약이 없으면 class/method 경계만 작성하고 본문은 `pass` 또는 해당 언어의 미구현 표현으로 둔다.",
|
|
827
|
-
"- TODO 주석에는 필요한 입력, 기대 출력, upstream endpoint 또는 SDK method, timeout, retry, error mapping을 명확히 적는다.",
|
|
816
|
+
"[구현 규칙]",
|
|
817
|
+
"- 확인된 인증 정보, 계약, timeout, 실패 매핑을 기준으로 외부 호출을 구현한다.",
|
|
828
818
|
"- Service는 비즈니스 의도를 드러내는 좁은 메서드로 외부 연동을 호출한다.",
|
|
829
819
|
"- Upstream DTO는 내부 DTO로 변환한 뒤 호출자에게 반환한다.",
|
|
830
|
-
"",
|
|
831
|
-
"[Python 뼈대]",
|
|
832
|
-
"```python",
|
|
833
|
-
"def request_external_job(self, request_dto: ExternalJobRequestDto) -> ExternalJobResponseDto:",
|
|
834
|
-
' """외부 시스템에 작업 생성을 요청한다."""',
|
|
835
|
-
" # TODO: upstream endpoint, request field, response field, timeout, retry, failure mapping 정의 필요.",
|
|
836
|
-
" pass",
|
|
837
|
-
"```",
|
|
820
|
+
"- Request/Response schema와 연동 문서는 실제 구현과 함께 갱신한다.",
|
|
838
821
|
"",
|
|
839
822
|
"[금지]",
|
|
840
|
-
"- 외부 API fake 구현",
|
|
841
|
-
"- upstream 데이터처럼 보이는 임의 샘플 데이터 생성",
|
|
842
823
|
"- controller 또는 UI component에서 외부 HTTP/SDK 직접 호출",
|
|
843
824
|
]
|
|
844
|
-
if not config.external_api_enabled:
|
|
845
|
-
lines.extend(
|
|
846
|
-
[
|
|
847
|
-
"",
|
|
848
|
-
"[나중에 외부 API가 추가되는 경우]",
|
|
849
|
-
"- 연동을 도입하기 전에 프로젝트 설정과 문서를 먼저 갱신한다.",
|
|
850
|
-
]
|
|
851
|
-
)
|
|
852
825
|
for profile in profiles:
|
|
853
826
|
lines.extend(["", f"[{profile.display_name} 연동 메모]"])
|
|
854
827
|
lines.extend(self._bullets(self._profile_external_rules(profile, config.language)))
|
|
@@ -978,9 +951,11 @@ class TemplateRenderer:
|
|
|
978
951
|
"- Directory path",
|
|
979
952
|
"- Major file path",
|
|
980
953
|
"- Responsibility",
|
|
981
|
-
"- Owner
|
|
954
|
+
"- Owner feature and layer",
|
|
982
955
|
"- Notes about important dependencies",
|
|
983
956
|
"",
|
|
957
|
+
"Use feature/domain folders as the outer boundary and document layer folders underneath each feature.",
|
|
958
|
+
"",
|
|
984
959
|
"## Current Structure",
|
|
985
960
|
"| Path | Type | Responsibility | Owner | Notes |",
|
|
986
961
|
"| --- | --- | --- | --- | --- |",
|
|
@@ -999,9 +974,11 @@ class TemplateRenderer:
|
|
|
999
974
|
"- 디렉토리 경로",
|
|
1000
975
|
"- 주요 파일 경로",
|
|
1001
976
|
"- 책임",
|
|
1002
|
-
"- 담당
|
|
977
|
+
"- 담당 기능 및 계층",
|
|
1003
978
|
"- 중요한 의존성 메모",
|
|
1004
979
|
"",
|
|
980
|
+
"기능/도메인 폴더를 바깥 경계로 두고, 각 기능 하위의 계층 폴더를 문서화한다.",
|
|
981
|
+
"",
|
|
1005
982
|
"## 현재 구조",
|
|
1006
983
|
"| Path | Type | Responsibility | Owner | Notes |",
|
|
1007
984
|
"| --- | --- | --- | --- | --- |",
|
|
@@ -1033,6 +1010,7 @@ class TemplateRenderer:
|
|
|
1033
1010
|
"## Layer Direction",
|
|
1034
1011
|
"- Document the real dependency direction used by this project.",
|
|
1035
1012
|
"- Explain what each layer may import and what it must not import.",
|
|
1013
|
+
"- Keep feature/domain folders as ownership boundaries and place each feature's layers underneath them.",
|
|
1036
1014
|
"",
|
|
1037
1015
|
"## Profile Architecture Notes",
|
|
1038
1016
|
]
|
|
@@ -1071,6 +1049,7 @@ class TemplateRenderer:
|
|
|
1071
1049
|
"## 계층 방향",
|
|
1072
1050
|
"- 이 프로젝트가 실제로 사용하는 의존성 방향을 기록한다.",
|
|
1073
1051
|
"- 각 계층이 import할 수 있는 대상과 금지 대상을 설명한다.",
|
|
1052
|
+
"- 기능/도메인 폴더를 소유권 경계로 두고 각 기능 하위에 계층 폴더를 둔다.",
|
|
1074
1053
|
"",
|
|
1075
1054
|
"## 프로필별 아키텍처 메모",
|
|
1076
1055
|
]
|
|
@@ -1590,7 +1569,7 @@ class TemplateRenderer:
|
|
|
1590
1569
|
" - API design",
|
|
1591
1570
|
" - Service logic",
|
|
1592
1571
|
" - Controller/route/UI boundary work",
|
|
1593
|
-
" - External integration
|
|
1572
|
+
" - External integration",
|
|
1594
1573
|
" - Refactoring",
|
|
1595
1574
|
" - Documentation",
|
|
1596
1575
|
"5. Apply the `.codex` rules that match the task type before editing files.",
|
|
@@ -1604,7 +1583,7 @@ class TemplateRenderer:
|
|
|
1604
1583
|
"- Do not invent behavior that is not defined by the request or reference documents.",
|
|
1605
1584
|
"- Do not treat `.codex/ref_docs` as generated project documentation; it is a user-managed reference space.",
|
|
1606
1585
|
"- Keep project specs in root `docs/`.",
|
|
1607
|
-
"- For external integrations
|
|
1586
|
+
"- For external integrations, implement the confirmed contract through a dedicated client/adapter and keep its documentation synchronized.",
|
|
1608
1587
|
"- After code changes, update required documentation in the same task.",
|
|
1609
1588
|
"",
|
|
1610
1589
|
"Response behavior:",
|
|
@@ -1643,7 +1622,7 @@ class TemplateRenderer:
|
|
|
1643
1622
|
" - API 설계",
|
|
1644
1623
|
" - 서비스 로직 작성",
|
|
1645
1624
|
" - 컨트롤러/라우트/UI 경계 작업",
|
|
1646
|
-
" - 외부 연동
|
|
1625
|
+
" - 외부 연동 구현",
|
|
1647
1626
|
" - 리팩토링",
|
|
1648
1627
|
" - 문서화",
|
|
1649
1628
|
"5. 판단한 작업 유형에 맞는 `.codex` 규칙을 우선 적용하라.",
|
|
@@ -1658,7 +1637,7 @@ class TemplateRenderer:
|
|
|
1658
1637
|
"- 요청이나 참고 문서에 없는 기능을 임의로 추가하지 않는다.",
|
|
1659
1638
|
"- `.codex/ref_docs`를 생성된 프로젝트 명세 위치로 취급하지 않는다. 이 위치는 사용자 관리 참고자료 공간이다.",
|
|
1660
1639
|
"- 프로젝트 명세는 프로젝트 루트의 `docs/`에 유지한다.",
|
|
1661
|
-
"-
|
|
1640
|
+
"- 외부 연동은 확인된 계약을 전용 client/adapter로 구현하고 관련 문서를 실제 동작과 함께 갱신한다.",
|
|
1662
1641
|
"- 코드 수정 후 필요한 문서도 같은 작업 안에서 함께 수정한다.",
|
|
1663
1642
|
"",
|
|
1664
1643
|
"응답 방식도 아래를 따라라.",
|
|
@@ -1837,7 +1816,7 @@ class TemplateRenderer:
|
|
|
1837
1816
|
"fullstack-fastapi-react": (
|
|
1838
1817
|
"Backend owns private upstream integrations; frontend calls backend services only.",
|
|
1839
1818
|
"Document any backend-to-upstream mapping in docs/api/specification.md when it affects public behavior.",
|
|
1840
|
-
"Keep frontend
|
|
1819
|
+
"Keep frontend API contract tests aligned with backend API contracts.",
|
|
1841
1820
|
),
|
|
1842
1821
|
}
|
|
1843
1822
|
return rules.get(profile.key, ("Keep external integrations behind framework-appropriate adapter boundaries.",))
|
|
@@ -1876,7 +1855,7 @@ class TemplateRenderer:
|
|
|
1876
1855
|
"fullstack-fastapi-react": (
|
|
1877
1856
|
"Private upstream integration은 backend가 소유하고 frontend는 backend service만 호출한다.",
|
|
1878
1857
|
"공개 동작에 영향을 주는 backend-to-upstream mapping은 docs/api/specification.md에 문서화한다.",
|
|
1879
|
-
"Frontend
|
|
1858
|
+
"Frontend API contract tests는 backend API contract와 불일치하지 않게 관리한다.",
|
|
1880
1859
|
),
|
|
1881
1860
|
}
|
|
1882
1861
|
return rules.get(profile.key, ("외부 연동은 framework에 맞는 adapter 경계 뒤에 둔다.",))
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: codex-rule-maker
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.4.0
|
|
4
4
|
Summary: CLI tool that generates a .codex folder with AI developer rules and project reference documents.
|
|
5
5
|
Author: codex-rule-maker
|
|
6
6
|
Project-URL: Homepage, https://github.com/Sharon77770/codex_rule_maker
|
|
@@ -21,13 +21,17 @@ Classifier: Topic :: Software Development :: Code Generators
|
|
|
21
21
|
Classifier: Topic :: Software Development :: Documentation
|
|
22
22
|
Requires-Python: >=3.9
|
|
23
23
|
Description-Content-Type: text/markdown
|
|
24
|
+
License-File: LICENSE
|
|
24
25
|
Provides-Extra: dev
|
|
25
26
|
Requires-Dist: build<2,>=1; extra == "dev"
|
|
26
27
|
Requires-Dist: pytest<9,>=7; extra == "dev"
|
|
27
28
|
Requires-Dist: twine<7,>=5; extra == "dev"
|
|
29
|
+
Dynamic: license-file
|
|
28
30
|
|
|
29
31
|
# codex-rule-maker
|
|
30
32
|
|
|
33
|
+
현재 버전: `0.4.0`
|
|
34
|
+
|
|
31
35
|
`codex-rule-maker`는 프로젝트 루트에 `.codex` 규칙 폴더와 `docs/` 프로젝트 문서 템플릿을 자동 구성하는 Python CLI 도구입니다. 사용자가 프로젝트 이름, 설명, 스택, DB, 인증 여부, 외부 API 여부, 문서화 수준, 문서 언어를 입력하면 Codex가 작업 전에 읽을 개발 규칙과 프로젝트 문서 구조를 생성합니다.
|
|
32
36
|
|
|
33
37
|
## 설치
|
|
@@ -222,19 +226,22 @@ docs/
|
|
|
222
226
|
|
|
223
227
|
## 지원 프레임워크 프로필
|
|
224
228
|
|
|
225
|
-
- `fastapi`: controller/service/repository/schema/entity
|
|
226
|
-
- `python`:
|
|
227
|
-
- `
|
|
228
|
-
- `
|
|
229
|
-
- `
|
|
230
|
-
- `
|
|
231
|
-
- `
|
|
229
|
+
- `fastapi`: 기능 폴더 하위 controller/service/repository/schema/entity 분리
|
|
230
|
+
- `python`: 프레임워크 없는 Python 프로젝트의 기능/도메인 폴더 하위 service/adapter/model/CLI 경계 분리
|
|
231
|
+
- `typescript-javascript`: 프레임워크 없는 TypeScript/JavaScript 프로젝트의 기능/도메인 폴더 하위 service/adapter/model 경계 분리
|
|
232
|
+
- `springboot`: 기능 package 하위 controller/service/repository/entity/dto 분리
|
|
233
|
+
- `react`: 기능 폴더 하위 page/component/hook/service/store 분리
|
|
234
|
+
- `nextjs`: App Router 유지, 기능 폴더 하위 component/service/repository/type 분리
|
|
235
|
+
- `node-express`: 기능 폴더 하위 route/controller/service/repository 분리
|
|
236
|
+
- `fullstack-fastapi-react`: FastAPI 백엔드와 React 프론트엔드 모두 기능 폴더 하위 계층 분리 적용
|
|
232
237
|
|
|
233
238
|
여러 프로필은 comma로 조합할 수 있습니다.
|
|
234
239
|
|
|
235
240
|
```bash
|
|
236
241
|
codex-init --stack fastapi,react
|
|
237
242
|
codex-init --stack python
|
|
243
|
+
codex-init --stack typescript
|
|
244
|
+
codex-init --stack javascript
|
|
238
245
|
codex-init --stack fullstack-fastapi-react
|
|
239
246
|
codex-init --stack nextjs
|
|
240
247
|
```
|
|
@@ -245,7 +252,7 @@ codex-init --stack nextjs
|
|
|
245
252
|
- `--language en`을 사용하면 영어 문서를 생성합니다.
|
|
246
253
|
- `--docs strict`는 코드 변경 후 문서 업데이트 필수 규칙을 포함합니다.
|
|
247
254
|
- `--auth yes`는 인증/권한 규칙을 포함합니다.
|
|
248
|
-
- `--external-api yes`는 외부
|
|
255
|
+
- `--external-api yes`는 확인된 계약을 기준으로 실제 외부 연동을 구현하기 위한 client/adapter 및 계약 문서 규칙을 포함합니다.
|
|
249
256
|
- `--db mysql`처럼 DB를 지정하면 DB, Repository, Schema 관련 규칙을 포함합니다.
|
|
250
257
|
- 프로젝트 명세 템플릿은 `.codex/ref_docs`가 아니라 `docs/architecture`, `docs/api`, `docs/database`에 생성됩니다.
|
|
251
258
|
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "codex-rule-maker"
|
|
7
|
-
version = "0.
|
|
7
|
+
version = "0.4.0"
|
|
8
8
|
description = "CLI tool that generates a .codex folder with AI developer rules and project reference documents."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.9"
|
|
@@ -72,6 +72,46 @@ def test_python_profile_generation(tmp_path):
|
|
|
72
72
|
assert "CLI 입력은 domain/service logic에 전달하기 전에 별도 input model로 변환한다" in domain_rules
|
|
73
73
|
|
|
74
74
|
|
|
75
|
+
def test_typescript_javascript_profile_generation(tmp_path):
|
|
76
|
+
config = ProjectConfig(project_name="sample-script", stack=("typescript",))
|
|
77
|
+
|
|
78
|
+
CodexBuilder().build(config, target_dir=tmp_path)
|
|
79
|
+
|
|
80
|
+
architecture_rules = (tmp_path / ".codex" / "ai_rule_developer" / "ARCHITECTURE_RULES.md").read_text(encoding="utf-8")
|
|
81
|
+
service_rules = (tmp_path / ".codex" / "ai_rule_developer" / "SERVICE_LAYER_RULES.md").read_text(encoding="utf-8")
|
|
82
|
+
|
|
83
|
+
assert "TypeScript/JavaScript" in service_rules
|
|
84
|
+
assert "Entrypoint -> Application Service -> Domain/Adapter -> External System" in architecture_rules
|
|
85
|
+
assert "프레임워크 전역 객체" in service_rules
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
def test_spacing_rules_use_three_and_five_units(tmp_path):
|
|
89
|
+
config = ProjectConfig(project_name="sample-spacing")
|
|
90
|
+
|
|
91
|
+
CodexBuilder().build(config, target_dir=tmp_path)
|
|
92
|
+
|
|
93
|
+
code_style = (tmp_path / ".codex" / "ai_rule_developer" / "CODE_STYLE_RULES.md").read_text(encoding="utf-8")
|
|
94
|
+
|
|
95
|
+
assert "메서드 간 5칸 공백" in code_style
|
|
96
|
+
assert "로직 블록 간에는 formatter가 허용하는 범위에서 3칸 공백" in code_style
|
|
97
|
+
assert "2칸 공백" not in code_style
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
def test_external_integration_rules_do_not_defer_to_mock_or_skeleton(tmp_path):
|
|
101
|
+
config = ProjectConfig(project_name="sample-integration", external_api_enabled=True)
|
|
102
|
+
|
|
103
|
+
CodexBuilder().build(config, target_dir=tmp_path)
|
|
104
|
+
|
|
105
|
+
integration_rules = (tmp_path / ".codex" / "ai_rule_developer" / "EXTERNAL_INTEGRATION_RULES.md").read_text(encoding="utf-8").lower()
|
|
106
|
+
start_prompt = (tmp_path / ".codex" / "codex_start_prompt.txt").read_text(encoding="utf-8").lower()
|
|
107
|
+
|
|
108
|
+
assert "mock" not in integration_rules
|
|
109
|
+
assert "skeleton" not in integration_rules
|
|
110
|
+
assert "pass" not in integration_rules
|
|
111
|
+
assert "외부 연동 구현" in start_prompt
|
|
112
|
+
assert "external integration skeleton" not in start_prompt
|
|
113
|
+
|
|
114
|
+
|
|
75
115
|
def test_react_profile_generation(tmp_path):
|
|
76
116
|
config = ProjectConfig(project_name="sample-web", stack=("react",))
|
|
77
117
|
|
|
@@ -3,7 +3,7 @@ from __future__ import annotations
|
|
|
3
3
|
from pathlib import Path
|
|
4
4
|
from typing import Optional
|
|
5
5
|
|
|
6
|
-
from codex_builder.cli import main
|
|
6
|
+
from codex_builder.cli import build_parser, main
|
|
7
7
|
|
|
8
8
|
|
|
9
9
|
def test_cli_generates_with_options_only(tmp_path):
|
|
@@ -32,6 +32,14 @@ def test_cli_generates_with_options_only(tmp_path):
|
|
|
32
32
|
assert (tmp_path / "docs" / "api" / "specification.md").exists()
|
|
33
33
|
|
|
34
34
|
|
|
35
|
+
def test_cli_profile_choices_include_plain_python_and_typescript_javascript():
|
|
36
|
+
help_text = build_parser().format_help()
|
|
37
|
+
|
|
38
|
+
assert "python (Python)" in help_text
|
|
39
|
+
assert "typescript-javascript" in help_text
|
|
40
|
+
assert "TypeScript/JavaScript" in help_text
|
|
41
|
+
|
|
42
|
+
|
|
35
43
|
def test_cli_generates_python_profile(tmp_path):
|
|
36
44
|
result = main(
|
|
37
45
|
[
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{codex_rule_maker-0.2.0 → codex_rule_maker-0.4.0}/codex_rule_maker.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
{codex_rule_maker-0.2.0 → codex_rule_maker-0.4.0}/codex_rule_maker.egg-info/entry_points.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|