yjcli 0.2.5__tar.gz → 0.3.1__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.
- {yjcli-0.2.5 → yjcli-0.3.1}/.gitignore +3 -2
- yjcli-0.3.1/PKG-INFO +180 -0
- yjcli-0.3.1/README.md +153 -0
- {yjcli-0.2.5 → yjcli-0.3.1}/pyproject.toml +4 -2
- yjcli-0.3.1/src/yjcli/catalog/__init__.py +14 -0
- yjcli-0.3.1/src/yjcli/catalog/llms.py +38 -0
- yjcli-0.3.1/src/yjcli/catalog/platforms.py +163 -0
- yjcli-0.3.1/src/yjcli/catalog/plugins.py +155 -0
- yjcli-0.3.1/src/yjcli/catalog/skills.py +64 -0
- yjcli-0.3.1/src/yjcli/cli.py +277 -0
- yjcli-0.3.1/src/yjcli/core/__init__.py +1 -0
- yjcli-0.3.1/src/yjcli/core/executor.py +66 -0
- yjcli-0.3.1/src/yjcli/core/manifest.py +98 -0
- yjcli-0.3.1/src/yjcli/core/names.py +43 -0
- yjcli-0.3.1/src/yjcli/core/planner.py +528 -0
- yjcli-0.3.1/src/yjcli/core/render.py +123 -0
- yjcli-0.3.1/src/yjcli/core/selection.py +205 -0
- yjcli-0.3.1/src/yjcli/core/state.py +39 -0
- yjcli-0.3.1/src/yjcli/data/plugins/_shared/Makefile +66 -0
- {yjcli-0.2.5/src/yjcli/data/templates → yjcli-0.3.1/src/yjcli/data/plugins/_shared}/make.bat +30 -27
- yjcli-0.3.1/src/yjcli/data/plugins/builder/build-common.sh +47 -0
- yjcli-0.3.1/src/yjcli/data/plugins/builder/deploy-common.sh +30 -0
- yjcli-0.3.1/src/yjcli/data/plugins/builder/platform/build-development.sh +24 -0
- yjcli-0.3.1/src/yjcli/data/plugins/builder/platform/build-production.sh +24 -0
- {yjcli-0.2.5/src/yjcli/data/templates/platform/scripts → yjcli-0.3.1/src/yjcli/data/plugins/builder/platform}/deploy-development.sh +6 -4
- {yjcli-0.2.5/src/yjcli/data/templates/platform/scripts → yjcli-0.3.1/src/yjcli/data/plugins/builder/platform}/deploy-production.sh +6 -4
- {yjcli-0.2.5/src/yjcli/data/templates/platform → yjcli-0.3.1/src/yjcli/data/plugins/builder}/service/Dockerfile +9 -9
- yjcli-0.3.1/src/yjcli/data/plugins/builder/service/dockerignore +17 -0
- yjcli-0.2.5/src/yjcli/data/templates/platform/scripts/plugin-build-docker.sh → yjcli-0.3.1/src/yjcli/data/plugins/builder/steps/docker.sh +20 -16
- yjcli-0.3.1/src/yjcli/data/plugins/builder/steps/proto.sh +24 -0
- yjcli-0.3.1/src/yjcli/data/plugins/builder/steps/ssr.sh +37 -0
- {yjcli-0.2.5/src/yjcli/data/templates/platform/scripts → yjcli-0.3.1/src/yjcli/data/plugins/runner}/run.bat +34 -12
- {yjcli-0.2.5/src/yjcli/data/templates/platform/scripts → yjcli-0.3.1/src/yjcli/data/plugins/runner}/run.ps1 +17 -5
- {yjcli-0.2.5/src/yjcli/data/templates/platform/scripts → yjcli-0.3.1/src/yjcli/data/plugins/runner}/run.sh +30 -14
- yjcli-0.3.1/src/yjcli/data/skills/_shared/backend-base.md +43 -0
- yjcli-0.3.1/src/yjcli/data/skills/_shared/grpc-optional.md +16 -0
- yjcli-0.3.1/src/yjcli/data/skills/_shared/plugin-builder.md +27 -0
- yjcli-0.3.1/src/yjcli/data/skills/_shared/plugin-runner.md +12 -0
- yjcli-0.3.1/src/yjcli/data/skills/_shared/plugin-secrets.md +14 -0
- yjcli-0.3.1/src/yjcli/data/skills/yj-arch-core/SKILL.md +137 -0
- yjcli-0.3.1/src/yjcli/data/skills/yj-backend/SKILL.md +48 -0
- {yjcli-0.2.5 → yjcli-0.3.1}/src/yjcli/data/skills/yj-backend-msa/SKILL.md +16 -42
- yjcli-0.3.1/src/yjcli/data/skills/yj-backend-ssr/SKILL.md +56 -0
- {yjcli-0.2.5 → yjcli-0.3.1}/src/yjcli/data/skills/yj-browser-extension/SKILL.md +9 -11
- {yjcli-0.2.5 → yjcli-0.3.1}/src/yjcli/data/skills/yj-cli/SKILL.md +6 -9
- {yjcli-0.2.5 → yjcli-0.3.1}/src/yjcli/data/skills/yj-frontend/SKILL.md +7 -9
- {yjcli-0.2.5 → yjcli-0.3.1}/src/yjcli/data/skills/yj-mobile-app/SKILL.md +5 -7
- {yjcli-0.2.5 → yjcli-0.3.1}/src/yjcli/data/skills/yj-pc-app/SKILL.md +5 -8
- {yjcli-0.2.5 → yjcli-0.3.1}/src/yjcli/data/skills/yj-scheduler/SKILL.md +8 -10
- yjcli-0.3.1/src/yjcli/data/templates/platform/envs/app/env.examples +8 -0
- yjcli-0.2.5/src/yjcli/data/templates/platform/envs/listen/.env.examples → yjcli-0.3.1/src/yjcli/data/templates/platform/envs/listen/env.examples +1 -1
- yjcli-0.2.5/src/yjcli/data/templates/platform/envs/worker/.env.examples → yjcli-0.3.1/src/yjcli/data/templates/platform/envs/worker/env.examples +1 -1
- {yjcli-0.2.5 → yjcli-0.3.1}/src/yjcli/data/templates/platform/frontend/vite.config.ts +9 -3
- yjcli-0.3.1/src/yjcli/data/templates/root/AGENTS.md +132 -0
- yjcli-0.3.1/src/yjcli/data/templates/root/agents-rules.md +15 -0
- yjcli-0.2.5/src/yjcli/data/templates/.gitignore → yjcli-0.3.1/src/yjcli/data/templates/root/gitignore +7 -14
- yjcli-0.3.1/src/yjcli/ui/__init__.py +1 -0
- yjcli-0.3.1/src/yjcli/ui/prompts.py +133 -0
- yjcli-0.3.1/src/yjcli/ui/summary.py +111 -0
- yjcli-0.3.1/src/yjcli/ui/wizard.py +247 -0
- yjcli-0.2.5/PKG-INFO +0 -230
- yjcli-0.2.5/README.md +0 -205
- yjcli-0.2.5/src/yjcli/cli.py +0 -67
- yjcli-0.2.5/src/yjcli/commands/__init__.py +0 -1
- yjcli-0.2.5/src/yjcli/commands/init_cmd.py +0 -65
- yjcli-0.2.5/src/yjcli/commands/platform.py +0 -64
- yjcli-0.2.5/src/yjcli/commands/service.py +0 -52
- yjcli-0.2.5/src/yjcli/commands/sync.py +0 -142
- yjcli-0.2.5/src/yjcli/data/skills/yj-arch-core/SKILL.md +0 -224
- yjcli-0.2.5/src/yjcli/data/skills/yj-backend-service/SKILL.md +0 -133
- yjcli-0.2.5/src/yjcli/data/templates/AGENTS.md +0 -185
- yjcli-0.2.5/src/yjcli/data/templates/Diff.md +0 -9
- yjcli-0.2.5/src/yjcli/data/templates/Makefile +0 -61
- yjcli-0.2.5/src/yjcli/data/templates/TOOLS.md +0 -170
- yjcli-0.2.5/src/yjcli/data/templates/platform/envs/app/.env.examples +0 -8
- yjcli-0.2.5/src/yjcli/data/templates/platform/scripts/build-common.sh +0 -49
- yjcli-0.2.5/src/yjcli/data/templates/platform/scripts/build-development.sh +0 -9
- yjcli-0.2.5/src/yjcli/data/templates/platform/scripts/build-production.sh +0 -9
- yjcli-0.2.5/src/yjcli/data/templates/platform/scripts/deploy-common.sh +0 -28
- yjcli-0.2.5/src/yjcli/data/templates/platform/scripts/plugin-build-proto.sh +0 -25
- yjcli-0.2.5/src/yjcli/data/templates/platform/scripts/plugin-build-ssr.sh +0 -34
- yjcli-0.2.5/src/yjcli/data/templates/platform/service/.dockerignore +0 -17
- yjcli-0.2.5/src/yjcli/modules/__init__.py +0 -1
- yjcli-0.2.5/src/yjcli/modules/constants.py +0 -31
- yjcli-0.2.5/src/yjcli/modules/fsutil.py +0 -102
- yjcli-0.2.5/src/yjcli/modules/paths.py +0 -18
- yjcli-0.2.5/src/yjcli/modules/prompt.py +0 -70
- yjcli-0.2.5/src/yjcli/services/__init__.py +0 -1
- yjcli-0.2.5/src/yjcli/services/scaffold.py +0 -308
- yjcli-0.2.5/src/yjcli/services/status.py +0 -18
- yjcli-0.2.5/src/yjcli/services/sync.py +0 -174
- yjcli-0.2.5/src/yjcli/services/wiring.py +0 -47
- {yjcli-0.2.5 → yjcli-0.3.1}/LICENSE +0 -0
- {yjcli-0.2.5 → yjcli-0.3.1}/src/yjcli/__init__.py +0 -0
- {yjcli-0.2.5 → yjcli-0.3.1}/src/yjcli/__main__.py +0 -0
- {yjcli-0.2.5 → yjcli-0.3.1}/src/yjcli/data/__init__.py +0 -0
- /yjcli-0.2.5/src/yjcli/data/templates/platform/envs/app/.env.development → /yjcli-0.3.1/src/yjcli/data/plugins/secrets/app/env.development +0 -0
- /yjcli-0.2.5/src/yjcli/data/templates/platform/envs/app/.env.local-dev → /yjcli-0.3.1/src/yjcli/data/plugins/secrets/app/env.local-dev +0 -0
- /yjcli-0.2.5/src/yjcli/data/templates/platform/envs/app/.env.production → /yjcli-0.3.1/src/yjcli/data/plugins/secrets/app/env.production +0 -0
- /yjcli-0.2.5/src/yjcli/data/templates/platform/envs/listen/.env.development → /yjcli-0.3.1/src/yjcli/data/plugins/secrets/listen/env.development +0 -0
- /yjcli-0.2.5/src/yjcli/data/templates/platform/envs/listen/.env.local-dev → /yjcli-0.3.1/src/yjcli/data/plugins/secrets/listen/env.local-dev +0 -0
- /yjcli-0.2.5/src/yjcli/data/templates/platform/envs/listen/.env.production → /yjcli-0.3.1/src/yjcli/data/plugins/secrets/listen/env.production +0 -0
- /yjcli-0.2.5/src/yjcli/data/templates/platform/envs/worker/.env.development → /yjcli-0.3.1/src/yjcli/data/plugins/secrets/worker/env.development +0 -0
- /yjcli-0.2.5/src/yjcli/data/templates/platform/envs/worker/.env.local-dev → /yjcli-0.3.1/src/yjcli/data/plugins/secrets/worker/env.local-dev +0 -0
- /yjcli-0.2.5/src/yjcli/data/templates/platform/envs/worker/.env.production → /yjcli-0.3.1/src/yjcli/data/plugins/secrets/worker/env.production +0 -0
- {yjcli-0.2.5 → yjcli-0.3.1}/src/yjcli/data/templates/platform/frontend/package.json +0 -0
- /yjcli-0.2.5/src/yjcli/data/templates/settings.json → /yjcli-0.3.1/src/yjcli/data/templates/root/claude-settings.json +0 -0
|
@@ -4,8 +4,6 @@ venv/
|
|
|
4
4
|
.env/
|
|
5
5
|
.env.*
|
|
6
6
|
!.env.examples
|
|
7
|
-
# Packaged scaffold templates must ship env samples (still ignored in user repos).
|
|
8
|
-
!src/yjcli/data/templates/**/.env.*
|
|
9
7
|
|
|
10
8
|
# Python
|
|
11
9
|
__pycache__/
|
|
@@ -38,3 +36,6 @@ Thumbs.db
|
|
|
38
36
|
# Local tooling
|
|
39
37
|
*.log
|
|
40
38
|
.cache/
|
|
39
|
+
|
|
40
|
+
# E2E sandbox (scripts/e2e.sh)
|
|
41
|
+
sandbox/
|
yjcli-0.3.1/PKG-INFO
ADDED
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: yjcli
|
|
3
|
+
Version: 0.3.1
|
|
4
|
+
Summary: Wizard that scaffolds YJ architecture repos: platforms, services, tooling plugins, and AI agent wiring (Cursor/Claude/Codex).
|
|
5
|
+
Project-URL: Homepage, https://github.com/yseiren87/yjcli
|
|
6
|
+
Project-URL: Repository, https://github.com/yseiren87/yjcli
|
|
7
|
+
Project-URL: Issues, https://github.com/yseiren87/yjcli/issues
|
|
8
|
+
Author: yseiren87
|
|
9
|
+
License-Expression: MIT
|
|
10
|
+
License-File: LICENSE
|
|
11
|
+
Keywords: agent,claude,cli,codex,cursor,scaffold
|
|
12
|
+
Classifier: Development Status :: 3 - Alpha
|
|
13
|
+
Classifier: Environment :: Console
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
16
|
+
Classifier: Programming Language :: Python :: 3
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
20
|
+
Classifier: Topic :: Software Development :: Code Generators
|
|
21
|
+
Classifier: Topic :: Utilities
|
|
22
|
+
Requires-Python: >=3.12
|
|
23
|
+
Requires-Dist: questionary>=2.1.1
|
|
24
|
+
Requires-Dist: rich>=15.0.0
|
|
25
|
+
Requires-Dist: typer>=0.12
|
|
26
|
+
Description-Content-Type: text/markdown
|
|
27
|
+
|
|
28
|
+
# yjcli
|
|
29
|
+
|
|
30
|
+
Wizard that scaffolds YJ-architecture repositories: platform code folders,
|
|
31
|
+
services, tooling plugins, and AI agent wiring (Cursor, Claude Code, Codex).
|
|
32
|
+
|
|
33
|
+
Run `yjcli` with no arguments and it walks you through a checklist. Nothing is
|
|
34
|
+
written until you confirm the plan at the end. Every step can also be answered
|
|
35
|
+
with a command-line argument, which skips that step.
|
|
36
|
+
|
|
37
|
+
## Install
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
uv tool install yjcli
|
|
41
|
+
yjcli --version
|
|
42
|
+
uv tool upgrade yjcli
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## Quick start
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
cd /path/to/your-repo
|
|
49
|
+
yjcli # interactive wizard
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
```text
|
|
53
|
+
✔ 1. LLM 대상 Cursor, Claude Code
|
|
54
|
+
✔ 2. 플랫폼 backend, frontend
|
|
55
|
+
✔ 3. 플러그인 runner, secrets
|
|
56
|
+
▸ 4. 서비스 이름 (1/2) backend
|
|
57
|
+
○ 5. 확인 및 실행
|
|
58
|
+
|
|
59
|
+
? backend 서비스 이름 (공백 또는 , 로 구분): a1, a2
|
|
60
|
+
→ backend/a1, backend/a2
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
The same thing without prompts:
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
yjcli -l cursor,claude -n backend=a1,a2 -n frontend=f1 --plugin runner,secrets -y
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## Steps
|
|
70
|
+
|
|
71
|
+
| # | Step | Argument | Notes |
|
|
72
|
+
|---|------|----------|-------|
|
|
73
|
+
| 1 | LLM targets | `-l/--llm cursor,claude,codex` · `all` | Multi-select (space toggles `[x]`) |
|
|
74
|
+
| 2 | Platforms | `-p/--platform …` · `all` | Multi-select |
|
|
75
|
+
| 3 | Plugins | `--plugin runner,secrets,builder` · `all` · `none` | Required plugins are added automatically |
|
|
76
|
+
| 4 | Service names | `-n/--name <platform>=a1,a2` (repeatable) | One screen per platform; split names with spaces or commas. `-n` alone also selects the platform |
|
|
77
|
+
| — | Platform options | `-o/--option backend=proto` | Asked right after the names of a new platform |
|
|
78
|
+
| 5 | Confirm | `-y/--yes` · `--dry-run` | Non-interactive runs need `--yes` or `--dry-run` |
|
|
79
|
+
|
|
80
|
+
`←` goes back one screen. Skills are not asked for — they follow the platforms.
|
|
81
|
+
|
|
82
|
+
## Platforms
|
|
83
|
+
|
|
84
|
+
Platforms hold **code only**: service folders, stack files and `.env.examples`.
|
|
85
|
+
|
|
86
|
+
| Platform | What | Skill |
|
|
87
|
+
|----------|------|-------|
|
|
88
|
+
| `backend` | single backend (API); optional `proto` | `yj-backend` |
|
|
89
|
+
| `backend-msa` | gRPC microservices; `proto/` always | `yj-backend-msa` |
|
|
90
|
+
| `backend-ssr` | server-rendered backend; `views/` always; optional `proto` | `yj-backend-ssr` |
|
|
91
|
+
| `frontend` | Vite + React + TS | `yj-frontend` |
|
|
92
|
+
| `mobile-app` | Flutter | `yj-mobile-app` |
|
|
93
|
+
| `pc-app` | Electron | `yj-pc-app` |
|
|
94
|
+
| `cli` | CLI apps | `yj-cli` |
|
|
95
|
+
| `browser-extension` | MV3; `native_*` services are native hosts | `yj-browser-extension` + `yj-backend` |
|
|
96
|
+
| `scheduler` | periodic batch jobs | `yj-scheduler` |
|
|
97
|
+
|
|
98
|
+
## Plugins (repository tooling under `tools/`)
|
|
99
|
+
|
|
100
|
+
| Plugin | Installs | Requires |
|
|
101
|
+
|--------|----------|----------|
|
|
102
|
+
| `runner` | `tools/runner/run.{sh,bat,ps1}`, root `Makefile` / `make.bat` → `make <platform> [NAME=<service>]` | `secrets` |
|
|
103
|
+
| `secrets` | `tools/secrets/<platform>/<service>/.env.{local-dev,development,production}` — **committed in plain text** (private repos only) | — |
|
|
104
|
+
| `builder` | `tools/builder/` (build/deploy hosts, `steps/{proto,ssr,docker}.sh`, per-platform stubs), `Dockerfile` for deployable services → `make <platform>-build-<env>` / `-deploy-<env>` | `secrets` |
|
|
105
|
+
|
|
106
|
+
Each service declares its local command as `RUN_COMMAND` in
|
|
107
|
+
`tools/secrets/<platform>/<service>/.env.local-dev`; the runner exports that file
|
|
108
|
+
and runs it. Generated build/deploy stubs fail with an implementation prompt
|
|
109
|
+
until you replace them.
|
|
110
|
+
|
|
111
|
+
## Updating
|
|
112
|
+
|
|
113
|
+
### Pick up a new yjcli release
|
|
114
|
+
|
|
115
|
+
When skills, AGENTS.md rules or managed scripts change in yjcli:
|
|
116
|
+
|
|
117
|
+
```bash
|
|
118
|
+
uv tool upgrade yjcli
|
|
119
|
+
yjcli status # tells you a newer yjcli is installed
|
|
120
|
+
yjcli update --diff # review what changes, then confirm (or -y)
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
`yjcli update` keeps your selection and only re-renders what yjcli owns. A yjcli
|
|
124
|
+
that is **older** than the one that last wrote `.yjcli/manifest.toml` refuses to
|
|
125
|
+
run (so teammates on old versions cannot roll files back); `--force` overrides.
|
|
126
|
+
|
|
127
|
+
### Change the selection
|
|
128
|
+
|
|
129
|
+
```bash
|
|
130
|
+
yjcli # wizard, stored selection as defaults
|
|
131
|
+
yjcli --plugin runner,secrets,builder -y # add the builder plugin
|
|
132
|
+
yjcli service add -n backend=a3 # add services
|
|
133
|
+
yjcli plan --diff # show the plan only
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
### What yjcli owns vs. what you own
|
|
137
|
+
|
|
138
|
+
| File kind | On update |
|
|
139
|
+
|-----------|-----------|
|
|
140
|
+
| managed (skills, `CLAUDE.md`, `tools/runner/*`, `Makefile`, builder hosts/steps) | refreshed; skipped with a warning if you edited it (`--force` overwrites) |
|
|
141
|
+
| blocks (`AGENTS.md` `<!-- yjcli:rules/skills/layout -->`, `.gitignore` `# >>> yjcli:managed`) | only the block is regenerated; everything outside is yours |
|
|
142
|
+
| seed (`AGENTS.md` body, env files, build/deploy stubs, `Dockerfile`, settings) | created once when its owner is new; yours afterwards |
|
|
143
|
+
|
|
144
|
+
Customize a skill without losing updates: write the extra rules in
|
|
145
|
+
`.yjcli/skills/<skill>.md` (e.g. `.yjcli/skills/yj-backend.md`). yjcli appends
|
|
146
|
+
them to that skill for every LLM on each render. Put project-wide agent rules
|
|
147
|
+
under `### Project rules` in AGENTS.md (outside the blocks).
|
|
148
|
+
|
|
149
|
+
yjcli never deletes services or files you own. Removing a plugin or LLM deletes
|
|
150
|
+
only its managed files; your own skills in the skills folders are left alone.
|
|
151
|
+
|
|
152
|
+
## Development
|
|
153
|
+
|
|
154
|
+
```bash
|
|
155
|
+
uv sync
|
|
156
|
+
uv run python -m unittest discover -s tests # unit tests
|
|
157
|
+
scripts/e2e.sh # end-to-end scenarios into ./sandbox
|
|
158
|
+
scripts/e2e.sh -k wizard -v # one case, with output on failure
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
## Releasing (maintainers)
|
|
162
|
+
|
|
163
|
+
```bash
|
|
164
|
+
./release.sh # bump → tests → build + PyPI dry-run → commit/tag/push → publish
|
|
165
|
+
./release.sh --publish-only # publish an already pushed release/X.Y.Z tag
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
Every step waits for `y` or `exit` (case-insensitive). Uncommitted changes do
|
|
169
|
+
not block: they are listed in step 1 and committed together with the version
|
|
170
|
+
bump in step 5, where you type the commit message. The bump step asks for
|
|
171
|
+
`major`, `minor` or `fix` (no default) and bumps the `pyproject.toml` version,
|
|
172
|
+
which always equals the last release (only `release.sh` changes it).
|
|
173
|
+
Tags follow `release/X.Y.Z`. Tokens go in `.env.release` (git-ignored):
|
|
174
|
+
|
|
175
|
+
```bash
|
|
176
|
+
UV_PUBLISH_TOKEN=pypi-... # required
|
|
177
|
+
GITHUB_TOKEN=github_pat_... # optional, used only for git push
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
Design notes: [`docs/redesign.md`](docs/redesign.md).
|
yjcli-0.3.1/README.md
ADDED
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
# yjcli
|
|
2
|
+
|
|
3
|
+
Wizard that scaffolds YJ-architecture repositories: platform code folders,
|
|
4
|
+
services, tooling plugins, and AI agent wiring (Cursor, Claude Code, Codex).
|
|
5
|
+
|
|
6
|
+
Run `yjcli` with no arguments and it walks you through a checklist. Nothing is
|
|
7
|
+
written until you confirm the plan at the end. Every step can also be answered
|
|
8
|
+
with a command-line argument, which skips that step.
|
|
9
|
+
|
|
10
|
+
## Install
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
uv tool install yjcli
|
|
14
|
+
yjcli --version
|
|
15
|
+
uv tool upgrade yjcli
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## Quick start
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
cd /path/to/your-repo
|
|
22
|
+
yjcli # interactive wizard
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
```text
|
|
26
|
+
✔ 1. LLM 대상 Cursor, Claude Code
|
|
27
|
+
✔ 2. 플랫폼 backend, frontend
|
|
28
|
+
✔ 3. 플러그인 runner, secrets
|
|
29
|
+
▸ 4. 서비스 이름 (1/2) backend
|
|
30
|
+
○ 5. 확인 및 실행
|
|
31
|
+
|
|
32
|
+
? backend 서비스 이름 (공백 또는 , 로 구분): a1, a2
|
|
33
|
+
→ backend/a1, backend/a2
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
The same thing without prompts:
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
yjcli -l cursor,claude -n backend=a1,a2 -n frontend=f1 --plugin runner,secrets -y
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## Steps
|
|
43
|
+
|
|
44
|
+
| # | Step | Argument | Notes |
|
|
45
|
+
|---|------|----------|-------|
|
|
46
|
+
| 1 | LLM targets | `-l/--llm cursor,claude,codex` · `all` | Multi-select (space toggles `[x]`) |
|
|
47
|
+
| 2 | Platforms | `-p/--platform …` · `all` | Multi-select |
|
|
48
|
+
| 3 | Plugins | `--plugin runner,secrets,builder` · `all` · `none` | Required plugins are added automatically |
|
|
49
|
+
| 4 | Service names | `-n/--name <platform>=a1,a2` (repeatable) | One screen per platform; split names with spaces or commas. `-n` alone also selects the platform |
|
|
50
|
+
| — | Platform options | `-o/--option backend=proto` | Asked right after the names of a new platform |
|
|
51
|
+
| 5 | Confirm | `-y/--yes` · `--dry-run` | Non-interactive runs need `--yes` or `--dry-run` |
|
|
52
|
+
|
|
53
|
+
`←` goes back one screen. Skills are not asked for — they follow the platforms.
|
|
54
|
+
|
|
55
|
+
## Platforms
|
|
56
|
+
|
|
57
|
+
Platforms hold **code only**: service folders, stack files and `.env.examples`.
|
|
58
|
+
|
|
59
|
+
| Platform | What | Skill |
|
|
60
|
+
|----------|------|-------|
|
|
61
|
+
| `backend` | single backend (API); optional `proto` | `yj-backend` |
|
|
62
|
+
| `backend-msa` | gRPC microservices; `proto/` always | `yj-backend-msa` |
|
|
63
|
+
| `backend-ssr` | server-rendered backend; `views/` always; optional `proto` | `yj-backend-ssr` |
|
|
64
|
+
| `frontend` | Vite + React + TS | `yj-frontend` |
|
|
65
|
+
| `mobile-app` | Flutter | `yj-mobile-app` |
|
|
66
|
+
| `pc-app` | Electron | `yj-pc-app` |
|
|
67
|
+
| `cli` | CLI apps | `yj-cli` |
|
|
68
|
+
| `browser-extension` | MV3; `native_*` services are native hosts | `yj-browser-extension` + `yj-backend` |
|
|
69
|
+
| `scheduler` | periodic batch jobs | `yj-scheduler` |
|
|
70
|
+
|
|
71
|
+
## Plugins (repository tooling under `tools/`)
|
|
72
|
+
|
|
73
|
+
| Plugin | Installs | Requires |
|
|
74
|
+
|--------|----------|----------|
|
|
75
|
+
| `runner` | `tools/runner/run.{sh,bat,ps1}`, root `Makefile` / `make.bat` → `make <platform> [NAME=<service>]` | `secrets` |
|
|
76
|
+
| `secrets` | `tools/secrets/<platform>/<service>/.env.{local-dev,development,production}` — **committed in plain text** (private repos only) | — |
|
|
77
|
+
| `builder` | `tools/builder/` (build/deploy hosts, `steps/{proto,ssr,docker}.sh`, per-platform stubs), `Dockerfile` for deployable services → `make <platform>-build-<env>` / `-deploy-<env>` | `secrets` |
|
|
78
|
+
|
|
79
|
+
Each service declares its local command as `RUN_COMMAND` in
|
|
80
|
+
`tools/secrets/<platform>/<service>/.env.local-dev`; the runner exports that file
|
|
81
|
+
and runs it. Generated build/deploy stubs fail with an implementation prompt
|
|
82
|
+
until you replace them.
|
|
83
|
+
|
|
84
|
+
## Updating
|
|
85
|
+
|
|
86
|
+
### Pick up a new yjcli release
|
|
87
|
+
|
|
88
|
+
When skills, AGENTS.md rules or managed scripts change in yjcli:
|
|
89
|
+
|
|
90
|
+
```bash
|
|
91
|
+
uv tool upgrade yjcli
|
|
92
|
+
yjcli status # tells you a newer yjcli is installed
|
|
93
|
+
yjcli update --diff # review what changes, then confirm (or -y)
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
`yjcli update` keeps your selection and only re-renders what yjcli owns. A yjcli
|
|
97
|
+
that is **older** than the one that last wrote `.yjcli/manifest.toml` refuses to
|
|
98
|
+
run (so teammates on old versions cannot roll files back); `--force` overrides.
|
|
99
|
+
|
|
100
|
+
### Change the selection
|
|
101
|
+
|
|
102
|
+
```bash
|
|
103
|
+
yjcli # wizard, stored selection as defaults
|
|
104
|
+
yjcli --plugin runner,secrets,builder -y # add the builder plugin
|
|
105
|
+
yjcli service add -n backend=a3 # add services
|
|
106
|
+
yjcli plan --diff # show the plan only
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
### What yjcli owns vs. what you own
|
|
110
|
+
|
|
111
|
+
| File kind | On update |
|
|
112
|
+
|-----------|-----------|
|
|
113
|
+
| managed (skills, `CLAUDE.md`, `tools/runner/*`, `Makefile`, builder hosts/steps) | refreshed; skipped with a warning if you edited it (`--force` overwrites) |
|
|
114
|
+
| blocks (`AGENTS.md` `<!-- yjcli:rules/skills/layout -->`, `.gitignore` `# >>> yjcli:managed`) | only the block is regenerated; everything outside is yours |
|
|
115
|
+
| seed (`AGENTS.md` body, env files, build/deploy stubs, `Dockerfile`, settings) | created once when its owner is new; yours afterwards |
|
|
116
|
+
|
|
117
|
+
Customize a skill without losing updates: write the extra rules in
|
|
118
|
+
`.yjcli/skills/<skill>.md` (e.g. `.yjcli/skills/yj-backend.md`). yjcli appends
|
|
119
|
+
them to that skill for every LLM on each render. Put project-wide agent rules
|
|
120
|
+
under `### Project rules` in AGENTS.md (outside the blocks).
|
|
121
|
+
|
|
122
|
+
yjcli never deletes services or files you own. Removing a plugin or LLM deletes
|
|
123
|
+
only its managed files; your own skills in the skills folders are left alone.
|
|
124
|
+
|
|
125
|
+
## Development
|
|
126
|
+
|
|
127
|
+
```bash
|
|
128
|
+
uv sync
|
|
129
|
+
uv run python -m unittest discover -s tests # unit tests
|
|
130
|
+
scripts/e2e.sh # end-to-end scenarios into ./sandbox
|
|
131
|
+
scripts/e2e.sh -k wizard -v # one case, with output on failure
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
## Releasing (maintainers)
|
|
135
|
+
|
|
136
|
+
```bash
|
|
137
|
+
./release.sh # bump → tests → build + PyPI dry-run → commit/tag/push → publish
|
|
138
|
+
./release.sh --publish-only # publish an already pushed release/X.Y.Z tag
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
Every step waits for `y` or `exit` (case-insensitive). Uncommitted changes do
|
|
142
|
+
not block: they are listed in step 1 and committed together with the version
|
|
143
|
+
bump in step 5, where you type the commit message. The bump step asks for
|
|
144
|
+
`major`, `minor` or `fix` (no default) and bumps the `pyproject.toml` version,
|
|
145
|
+
which always equals the last release (only `release.sh` changes it).
|
|
146
|
+
Tags follow `release/X.Y.Z`. Tokens go in `.env.release` (git-ignored):
|
|
147
|
+
|
|
148
|
+
```bash
|
|
149
|
+
UV_PUBLISH_TOKEN=pypi-... # required
|
|
150
|
+
GITHUB_TOKEN=github_pat_... # optional, used only for git push
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
Design notes: [`docs/redesign.md`](docs/redesign.md).
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "yjcli"
|
|
3
|
-
version = "0.
|
|
4
|
-
description = "
|
|
3
|
+
version = "0.3.1"
|
|
4
|
+
description = "Wizard that scaffolds YJ architecture repos: platforms, services, tooling plugins, and AI agent wiring (Cursor/Claude/Codex)."
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
requires-python = ">=3.12"
|
|
7
7
|
license = "MIT"
|
|
@@ -22,6 +22,8 @@ classifiers = [
|
|
|
22
22
|
"Topic :: Utilities",
|
|
23
23
|
]
|
|
24
24
|
dependencies = [
|
|
25
|
+
"questionary>=2.1.1",
|
|
26
|
+
"rich>=15.0.0",
|
|
25
27
|
"typer>=0.12",
|
|
26
28
|
]
|
|
27
29
|
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
"""Declarative catalog: LLM adapters, platforms, plugins, skills.
|
|
2
|
+
|
|
3
|
+
Everything the user can pick (and everything derived from a pick) is data here.
|
|
4
|
+
Planner logic reads the catalog; it never branches on a platform/plugin name.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
from importlib.resources import files
|
|
10
|
+
from pathlib import Path
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def data_root() -> Path:
|
|
14
|
+
return Path(str(files("yjcli.data")))
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
"""LLM adapters: instruction file + skills directory + extra settings."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from dataclasses import dataclass, field
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
@dataclass(frozen=True)
|
|
9
|
+
class Extra:
|
|
10
|
+
"""A seed file the adapter installs once (template path under data/templates)."""
|
|
11
|
+
|
|
12
|
+
template: str
|
|
13
|
+
dest: str
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
@dataclass(frozen=True)
|
|
17
|
+
class LLM:
|
|
18
|
+
id: str
|
|
19
|
+
label: str
|
|
20
|
+
skills_dir: str
|
|
21
|
+
# File that imports AGENTS.md (None when the tool reads AGENTS.md directly).
|
|
22
|
+
instructions: str | None = None
|
|
23
|
+
extras: tuple[Extra, ...] = field(default_factory=tuple)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
LLMS: tuple[LLM, ...] = (
|
|
27
|
+
LLM(id="cursor", label="Cursor", skills_dir=".cursor/skills"),
|
|
28
|
+
LLM(
|
|
29
|
+
id="claude",
|
|
30
|
+
label="Claude Code",
|
|
31
|
+
skills_dir=".claude/skills",
|
|
32
|
+
instructions="CLAUDE.md",
|
|
33
|
+
extras=(Extra("root/claude-settings.json", ".claude/settings.json"),),
|
|
34
|
+
),
|
|
35
|
+
LLM(id="codex", label="Codex", skills_dir=".agents/skills"),
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
LLM_BY_ID: dict[str, LLM] = {llm.id: llm for llm in LLMS}
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
"""Platforms: code-only scaffolds (service folders, stack files, .env.examples)."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from dataclasses import dataclass, field
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
@dataclass(frozen=True)
|
|
9
|
+
class Option:
|
|
10
|
+
"""Platform-level code option, asked once when the platform is created.
|
|
11
|
+
|
|
12
|
+
Whether it is "on" later is decided by disk state (the created folder), not
|
|
13
|
+
by the manifest — yjcli only creates the starting files and never deletes them.
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
id: str
|
|
17
|
+
label: str
|
|
18
|
+
creates: tuple[str, ...] # paths relative to the platform root
|
|
19
|
+
default: bool = False
|
|
20
|
+
fixed: bool = False # always applied, never asked
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
@dataclass(frozen=True)
|
|
24
|
+
class ServiceVariant:
|
|
25
|
+
"""Alternative service shape selected by name prefix (e.g. native_*)."""
|
|
26
|
+
|
|
27
|
+
prefix: str
|
|
28
|
+
env_kind: str
|
|
29
|
+
service_dirs: tuple[str, ...]
|
|
30
|
+
files: tuple[tuple[str, str], ...] = () # (dest, content)
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
@dataclass(frozen=True)
|
|
34
|
+
class Platform:
|
|
35
|
+
id: str
|
|
36
|
+
hint: str
|
|
37
|
+
role: str # English role text for the AGENTS.md layout table
|
|
38
|
+
skills: tuple[str, ...]
|
|
39
|
+
env_kind: str # listen | worker | app
|
|
40
|
+
service_dirs: tuple[str, ...]
|
|
41
|
+
env_vars: dict[str, str] = field(default_factory=dict)
|
|
42
|
+
stack_template: str | None = None # dir under data/templates/platform
|
|
43
|
+
service_files: tuple[tuple[str, str], ...] = () # (dest, content)
|
|
44
|
+
options: tuple[Option, ...] = ()
|
|
45
|
+
variants: tuple[ServiceVariant, ...] = ()
|
|
46
|
+
deployable: bool = False # builder plugin stamps a Dockerfile into each service
|
|
47
|
+
|
|
48
|
+
def variant_for(self, service: str) -> ServiceVariant | None:
|
|
49
|
+
for variant in self.variants:
|
|
50
|
+
if service.startswith(variant.prefix):
|
|
51
|
+
return variant
|
|
52
|
+
return None
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
_BACKEND_DIRS = ("apps", "services", "domains", "modules")
|
|
56
|
+
|
|
57
|
+
_PROTO = Option(
|
|
58
|
+
id="proto",
|
|
59
|
+
label="gRPC/protobuf 사용 (proto/ 생성)",
|
|
60
|
+
creates=("proto/.gitkeep", "proto/dist/.gitkeep"),
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
PLATFORMS: tuple[Platform, ...] = (
|
|
64
|
+
Platform(
|
|
65
|
+
id="backend",
|
|
66
|
+
hint="단일 백엔드 (API)",
|
|
67
|
+
role="Single backend services (API)",
|
|
68
|
+
skills=("yj-backend",),
|
|
69
|
+
env_kind="listen",
|
|
70
|
+
env_vars={"__PORT__": "8080"},
|
|
71
|
+
service_dirs=_BACKEND_DIRS,
|
|
72
|
+
options=(_PROTO,),
|
|
73
|
+
deployable=True,
|
|
74
|
+
),
|
|
75
|
+
Platform(
|
|
76
|
+
id="backend-msa",
|
|
77
|
+
hint="gRPC MSA",
|
|
78
|
+
role="gRPC microservices sharing one proto contract",
|
|
79
|
+
skills=("yj-backend-msa",),
|
|
80
|
+
env_kind="listen",
|
|
81
|
+
env_vars={"__PORT__": "8080"},
|
|
82
|
+
service_dirs=_BACKEND_DIRS,
|
|
83
|
+
options=(Option(id="proto", label=_PROTO.label, creates=_PROTO.creates, fixed=True),),
|
|
84
|
+
deployable=True,
|
|
85
|
+
),
|
|
86
|
+
Platform(
|
|
87
|
+
id="backend-ssr",
|
|
88
|
+
hint="서버 템플릿/SSR",
|
|
89
|
+
role="Server-rendered (SSR/MPA) backend services",
|
|
90
|
+
skills=("yj-backend-ssr",),
|
|
91
|
+
env_kind="listen",
|
|
92
|
+
env_vars={"__PORT__": "8080"},
|
|
93
|
+
service_dirs=(*_BACKEND_DIRS, "views"),
|
|
94
|
+
options=(_PROTO,),
|
|
95
|
+
deployable=True,
|
|
96
|
+
),
|
|
97
|
+
Platform(
|
|
98
|
+
id="frontend",
|
|
99
|
+
hint="Vite + React + TS",
|
|
100
|
+
role="Vite + React + TS web apps",
|
|
101
|
+
skills=("yj-frontend",),
|
|
102
|
+
env_kind="listen",
|
|
103
|
+
env_vars={"__PORT__": "5173"},
|
|
104
|
+
service_dirs=("src",),
|
|
105
|
+
stack_template="frontend",
|
|
106
|
+
deployable=True,
|
|
107
|
+
),
|
|
108
|
+
Platform(
|
|
109
|
+
id="mobile-app",
|
|
110
|
+
hint="Flutter",
|
|
111
|
+
role="Flutter mobile apps",
|
|
112
|
+
skills=("yj-mobile-app",),
|
|
113
|
+
env_kind="app",
|
|
114
|
+
service_dirs=("lib",),
|
|
115
|
+
),
|
|
116
|
+
Platform(
|
|
117
|
+
id="pc-app",
|
|
118
|
+
hint="Electron",
|
|
119
|
+
role="Electron desktop apps",
|
|
120
|
+
skills=("yj-pc-app",),
|
|
121
|
+
env_kind="app",
|
|
122
|
+
service_dirs=("main", "preload", "renderer/src"),
|
|
123
|
+
),
|
|
124
|
+
Platform(
|
|
125
|
+
id="cli",
|
|
126
|
+
hint="CLI",
|
|
127
|
+
role="Command-line applications",
|
|
128
|
+
skills=("yj-cli",),
|
|
129
|
+
env_kind="app",
|
|
130
|
+
service_dirs=("apps", "services", "modules"),
|
|
131
|
+
),
|
|
132
|
+
Platform(
|
|
133
|
+
id="browser-extension",
|
|
134
|
+
hint="MV3",
|
|
135
|
+
role="MV3 browser extensions (`native_*` = native messaging hosts)",
|
|
136
|
+
skills=("yj-browser-extension", "yj-backend"),
|
|
137
|
+
env_kind="app",
|
|
138
|
+
service_dirs=("src/background", "src/content", "src/popup", "src/lib"),
|
|
139
|
+
service_files=(("manifest.json", "{}\n"),),
|
|
140
|
+
variants=(
|
|
141
|
+
ServiceVariant(
|
|
142
|
+
prefix="native_",
|
|
143
|
+
env_kind="worker",
|
|
144
|
+
service_dirs=("apps", "services", "modules"),
|
|
145
|
+
),
|
|
146
|
+
),
|
|
147
|
+
),
|
|
148
|
+
Platform(
|
|
149
|
+
id="scheduler",
|
|
150
|
+
hint="주기 배치 작업",
|
|
151
|
+
role="Long-running schedulers for periodic batch jobs",
|
|
152
|
+
skills=("yj-scheduler",),
|
|
153
|
+
env_kind="worker",
|
|
154
|
+
service_dirs=_BACKEND_DIRS,
|
|
155
|
+
deployable=True,
|
|
156
|
+
),
|
|
157
|
+
)
|
|
158
|
+
|
|
159
|
+
PLATFORM_BY_ID: dict[str, Platform] = {p.id: p for p in PLATFORMS}
|
|
160
|
+
|
|
161
|
+
# Folder names under a platform root that are not services.
|
|
162
|
+
NON_SERVICE_DIRS: frozenset[str] = frozenset({"proto"})
|
|
163
|
+
RESERVED_SERVICE_NAMES: frozenset[str] = NON_SERVICE_DIRS
|