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.
Files changed (107) hide show
  1. {yjcli-0.2.5 → yjcli-0.3.1}/.gitignore +3 -2
  2. yjcli-0.3.1/PKG-INFO +180 -0
  3. yjcli-0.3.1/README.md +153 -0
  4. {yjcli-0.2.5 → yjcli-0.3.1}/pyproject.toml +4 -2
  5. yjcli-0.3.1/src/yjcli/catalog/__init__.py +14 -0
  6. yjcli-0.3.1/src/yjcli/catalog/llms.py +38 -0
  7. yjcli-0.3.1/src/yjcli/catalog/platforms.py +163 -0
  8. yjcli-0.3.1/src/yjcli/catalog/plugins.py +155 -0
  9. yjcli-0.3.1/src/yjcli/catalog/skills.py +64 -0
  10. yjcli-0.3.1/src/yjcli/cli.py +277 -0
  11. yjcli-0.3.1/src/yjcli/core/__init__.py +1 -0
  12. yjcli-0.3.1/src/yjcli/core/executor.py +66 -0
  13. yjcli-0.3.1/src/yjcli/core/manifest.py +98 -0
  14. yjcli-0.3.1/src/yjcli/core/names.py +43 -0
  15. yjcli-0.3.1/src/yjcli/core/planner.py +528 -0
  16. yjcli-0.3.1/src/yjcli/core/render.py +123 -0
  17. yjcli-0.3.1/src/yjcli/core/selection.py +205 -0
  18. yjcli-0.3.1/src/yjcli/core/state.py +39 -0
  19. yjcli-0.3.1/src/yjcli/data/plugins/_shared/Makefile +66 -0
  20. {yjcli-0.2.5/src/yjcli/data/templates → yjcli-0.3.1/src/yjcli/data/plugins/_shared}/make.bat +30 -27
  21. yjcli-0.3.1/src/yjcli/data/plugins/builder/build-common.sh +47 -0
  22. yjcli-0.3.1/src/yjcli/data/plugins/builder/deploy-common.sh +30 -0
  23. yjcli-0.3.1/src/yjcli/data/plugins/builder/platform/build-development.sh +24 -0
  24. yjcli-0.3.1/src/yjcli/data/plugins/builder/platform/build-production.sh +24 -0
  25. {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
  26. {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
  27. {yjcli-0.2.5/src/yjcli/data/templates/platform → yjcli-0.3.1/src/yjcli/data/plugins/builder}/service/Dockerfile +9 -9
  28. yjcli-0.3.1/src/yjcli/data/plugins/builder/service/dockerignore +17 -0
  29. 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
  30. yjcli-0.3.1/src/yjcli/data/plugins/builder/steps/proto.sh +24 -0
  31. yjcli-0.3.1/src/yjcli/data/plugins/builder/steps/ssr.sh +37 -0
  32. {yjcli-0.2.5/src/yjcli/data/templates/platform/scripts → yjcli-0.3.1/src/yjcli/data/plugins/runner}/run.bat +34 -12
  33. {yjcli-0.2.5/src/yjcli/data/templates/platform/scripts → yjcli-0.3.1/src/yjcli/data/plugins/runner}/run.ps1 +17 -5
  34. {yjcli-0.2.5/src/yjcli/data/templates/platform/scripts → yjcli-0.3.1/src/yjcli/data/plugins/runner}/run.sh +30 -14
  35. yjcli-0.3.1/src/yjcli/data/skills/_shared/backend-base.md +43 -0
  36. yjcli-0.3.1/src/yjcli/data/skills/_shared/grpc-optional.md +16 -0
  37. yjcli-0.3.1/src/yjcli/data/skills/_shared/plugin-builder.md +27 -0
  38. yjcli-0.3.1/src/yjcli/data/skills/_shared/plugin-runner.md +12 -0
  39. yjcli-0.3.1/src/yjcli/data/skills/_shared/plugin-secrets.md +14 -0
  40. yjcli-0.3.1/src/yjcli/data/skills/yj-arch-core/SKILL.md +137 -0
  41. yjcli-0.3.1/src/yjcli/data/skills/yj-backend/SKILL.md +48 -0
  42. {yjcli-0.2.5 → yjcli-0.3.1}/src/yjcli/data/skills/yj-backend-msa/SKILL.md +16 -42
  43. yjcli-0.3.1/src/yjcli/data/skills/yj-backend-ssr/SKILL.md +56 -0
  44. {yjcli-0.2.5 → yjcli-0.3.1}/src/yjcli/data/skills/yj-browser-extension/SKILL.md +9 -11
  45. {yjcli-0.2.5 → yjcli-0.3.1}/src/yjcli/data/skills/yj-cli/SKILL.md +6 -9
  46. {yjcli-0.2.5 → yjcli-0.3.1}/src/yjcli/data/skills/yj-frontend/SKILL.md +7 -9
  47. {yjcli-0.2.5 → yjcli-0.3.1}/src/yjcli/data/skills/yj-mobile-app/SKILL.md +5 -7
  48. {yjcli-0.2.5 → yjcli-0.3.1}/src/yjcli/data/skills/yj-pc-app/SKILL.md +5 -8
  49. {yjcli-0.2.5 → yjcli-0.3.1}/src/yjcli/data/skills/yj-scheduler/SKILL.md +8 -10
  50. yjcli-0.3.1/src/yjcli/data/templates/platform/envs/app/env.examples +8 -0
  51. 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
  52. 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
  53. {yjcli-0.2.5 → yjcli-0.3.1}/src/yjcli/data/templates/platform/frontend/vite.config.ts +9 -3
  54. yjcli-0.3.1/src/yjcli/data/templates/root/AGENTS.md +132 -0
  55. yjcli-0.3.1/src/yjcli/data/templates/root/agents-rules.md +15 -0
  56. yjcli-0.2.5/src/yjcli/data/templates/.gitignore → yjcli-0.3.1/src/yjcli/data/templates/root/gitignore +7 -14
  57. yjcli-0.3.1/src/yjcli/ui/__init__.py +1 -0
  58. yjcli-0.3.1/src/yjcli/ui/prompts.py +133 -0
  59. yjcli-0.3.1/src/yjcli/ui/summary.py +111 -0
  60. yjcli-0.3.1/src/yjcli/ui/wizard.py +247 -0
  61. yjcli-0.2.5/PKG-INFO +0 -230
  62. yjcli-0.2.5/README.md +0 -205
  63. yjcli-0.2.5/src/yjcli/cli.py +0 -67
  64. yjcli-0.2.5/src/yjcli/commands/__init__.py +0 -1
  65. yjcli-0.2.5/src/yjcli/commands/init_cmd.py +0 -65
  66. yjcli-0.2.5/src/yjcli/commands/platform.py +0 -64
  67. yjcli-0.2.5/src/yjcli/commands/service.py +0 -52
  68. yjcli-0.2.5/src/yjcli/commands/sync.py +0 -142
  69. yjcli-0.2.5/src/yjcli/data/skills/yj-arch-core/SKILL.md +0 -224
  70. yjcli-0.2.5/src/yjcli/data/skills/yj-backend-service/SKILL.md +0 -133
  71. yjcli-0.2.5/src/yjcli/data/templates/AGENTS.md +0 -185
  72. yjcli-0.2.5/src/yjcli/data/templates/Diff.md +0 -9
  73. yjcli-0.2.5/src/yjcli/data/templates/Makefile +0 -61
  74. yjcli-0.2.5/src/yjcli/data/templates/TOOLS.md +0 -170
  75. yjcli-0.2.5/src/yjcli/data/templates/platform/envs/app/.env.examples +0 -8
  76. yjcli-0.2.5/src/yjcli/data/templates/platform/scripts/build-common.sh +0 -49
  77. yjcli-0.2.5/src/yjcli/data/templates/platform/scripts/build-development.sh +0 -9
  78. yjcli-0.2.5/src/yjcli/data/templates/platform/scripts/build-production.sh +0 -9
  79. yjcli-0.2.5/src/yjcli/data/templates/platform/scripts/deploy-common.sh +0 -28
  80. yjcli-0.2.5/src/yjcli/data/templates/platform/scripts/plugin-build-proto.sh +0 -25
  81. yjcli-0.2.5/src/yjcli/data/templates/platform/scripts/plugin-build-ssr.sh +0 -34
  82. yjcli-0.2.5/src/yjcli/data/templates/platform/service/.dockerignore +0 -17
  83. yjcli-0.2.5/src/yjcli/modules/__init__.py +0 -1
  84. yjcli-0.2.5/src/yjcli/modules/constants.py +0 -31
  85. yjcli-0.2.5/src/yjcli/modules/fsutil.py +0 -102
  86. yjcli-0.2.5/src/yjcli/modules/paths.py +0 -18
  87. yjcli-0.2.5/src/yjcli/modules/prompt.py +0 -70
  88. yjcli-0.2.5/src/yjcli/services/__init__.py +0 -1
  89. yjcli-0.2.5/src/yjcli/services/scaffold.py +0 -308
  90. yjcli-0.2.5/src/yjcli/services/status.py +0 -18
  91. yjcli-0.2.5/src/yjcli/services/sync.py +0 -174
  92. yjcli-0.2.5/src/yjcli/services/wiring.py +0 -47
  93. {yjcli-0.2.5 → yjcli-0.3.1}/LICENSE +0 -0
  94. {yjcli-0.2.5 → yjcli-0.3.1}/src/yjcli/__init__.py +0 -0
  95. {yjcli-0.2.5 → yjcli-0.3.1}/src/yjcli/__main__.py +0 -0
  96. {yjcli-0.2.5 → yjcli-0.3.1}/src/yjcli/data/__init__.py +0 -0
  97. /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
  98. /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
  99. /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
  100. /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
  101. /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
  102. /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
  103. /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
  104. /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
  105. /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
  106. {yjcli-0.2.5 → yjcli-0.3.1}/src/yjcli/data/templates/platform/frontend/package.json +0 -0
  107. /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.2.5"
4
- description = "Scaffold platforms, services, and AI agent wiring (Cursor/Claude/Codex) for YJ architecture repos."
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