create-idp 0.1.0 → 0.2.1

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 (105) hide show
  1. package/README.md +88 -127
  2. package/dist/index.js +162 -61
  3. package/dist/index.js.map +1 -1
  4. package/package.json +1 -1
  5. package/templates/django/README.md +27 -0
  6. package/templates/django/_gitignore +20 -0
  7. package/templates/django/_template.json +14 -0
  8. package/templates/django/manage.py +22 -0
  9. package/templates/django/project_name/__init__.py +1 -0
  10. package/templates/django/project_name/asgi.py +11 -0
  11. package/templates/django/project_name/settings.py +75 -0
  12. package/templates/django/project_name/urls.py +16 -0
  13. package/templates/django/project_name/wsgi.py +11 -0
  14. package/templates/django/pyproject.toml +20 -0
  15. package/templates/express-ts/README.md +27 -0
  16. package/templates/express-ts/_gitignore +7 -0
  17. package/templates/express-ts/_template.json +13 -0
  18. package/templates/express-ts/package.json +23 -0
  19. package/templates/express-ts/src/index.ts +17 -0
  20. package/templates/express-ts/src/middleware/error-handler.ts +14 -0
  21. package/templates/express-ts/src/routes/index.ts +11 -0
  22. package/templates/express-ts/tsconfig.json +17 -0
  23. package/templates/express-ts/tsup.config.ts +9 -0
  24. package/templates/fastapi/README.md +21 -0
  25. package/templates/fastapi/_gitignore +16 -0
  26. package/templates/fastapi/_template.json +14 -0
  27. package/templates/fastapi/pyproject.toml +21 -0
  28. package/templates/fastapi/src/project_name/__init__.py +1 -0
  29. package/templates/fastapi/src/project_name/main.py +20 -0
  30. package/templates/fastapi/src/project_name/routes/__init__.py +8 -0
  31. package/templates/flask/README.md +15 -0
  32. package/templates/flask/_gitignore +17 -0
  33. package/templates/flask/_template.json +14 -0
  34. package/templates/flask/pyproject.toml +19 -0
  35. package/templates/flask/src/project_name/__init__.py +1 -0
  36. package/templates/flask/src/project_name/app.py +15 -0
  37. package/templates/flask/src/project_name/routes/__init__.py +8 -0
  38. package/templates/fullstack-react-node/README.md +35 -0
  39. package/templates/fullstack-react-node/_gitignore +7 -0
  40. package/templates/fullstack-react-node/_template.json +14 -0
  41. package/templates/fullstack-react-node/backend/package.json +22 -0
  42. package/templates/fullstack-react-node/backend/src/index.ts +20 -0
  43. package/templates/fullstack-react-node/backend/tsconfig.json +17 -0
  44. package/templates/fullstack-react-node/backend/tsup.config.ts +9 -0
  45. package/templates/fullstack-react-node/frontend/index.html +12 -0
  46. package/templates/fullstack-react-node/frontend/package.json +22 -0
  47. package/templates/fullstack-react-node/frontend/src/App.css +16 -0
  48. package/templates/fullstack-react-node/frontend/src/App.tsx +22 -0
  49. package/templates/fullstack-react-node/frontend/src/main.tsx +9 -0
  50. package/templates/fullstack-react-node/frontend/tsconfig.json +20 -0
  51. package/templates/fullstack-react-node/frontend/vite.config.ts +14 -0
  52. package/templates/fullstack-react-node/package.json +10 -0
  53. package/templates/fullstack-react-node/pnpm-workspace.yaml +3 -0
  54. package/templates/fullstack-react-python/Makefile +35 -0
  55. package/templates/fullstack-react-python/README.md +43 -0
  56. package/templates/fullstack-react-python/_gitignore +18 -0
  57. package/templates/fullstack-react-python/_template.json +25 -0
  58. package/templates/fullstack-react-python/backend/pyproject.toml +21 -0
  59. package/templates/fullstack-react-python/backend/src/project_name/__init__.py +1 -0
  60. package/templates/fullstack-react-python/backend/src/project_name/main.py +29 -0
  61. package/templates/fullstack-react-python/backend/src/project_name/routes/__init__.py +8 -0
  62. package/templates/fullstack-react-python/frontend/index.html +12 -0
  63. package/templates/fullstack-react-python/frontend/package.json +22 -0
  64. package/templates/fullstack-react-python/frontend/src/App.css +16 -0
  65. package/templates/fullstack-react-python/frontend/src/App.tsx +22 -0
  66. package/templates/fullstack-react-python/frontend/src/main.tsx +9 -0
  67. package/templates/fullstack-react-python/frontend/tsconfig.json +20 -0
  68. package/templates/fullstack-react-python/frontend/vite.config.ts +14 -0
  69. package/templates/fullstack-vue-node/README.md +35 -0
  70. package/templates/fullstack-vue-node/_gitignore +7 -0
  71. package/templates/fullstack-vue-node/_template.json +14 -0
  72. package/templates/fullstack-vue-node/backend/package.json +22 -0
  73. package/templates/fullstack-vue-node/backend/src/index.ts +20 -0
  74. package/templates/fullstack-vue-node/backend/tsconfig.json +17 -0
  75. package/templates/fullstack-vue-node/backend/tsup.config.ts +9 -0
  76. package/templates/fullstack-vue-node/frontend/index.html +12 -0
  77. package/templates/fullstack-vue-node/frontend/package.json +20 -0
  78. package/templates/fullstack-vue-node/frontend/src/App.vue +41 -0
  79. package/templates/fullstack-vue-node/frontend/src/main.ts +4 -0
  80. package/templates/fullstack-vue-node/frontend/src/vite-env.d.ts +1 -0
  81. package/templates/fullstack-vue-node/frontend/tsconfig.json +20 -0
  82. package/templates/fullstack-vue-node/frontend/vite.config.ts +14 -0
  83. package/templates/fullstack-vue-node/package.json +10 -0
  84. package/templates/fullstack-vue-node/pnpm-workspace.yaml +3 -0
  85. package/templates/next-ts/README.md +26 -0
  86. package/templates/next-ts/_gitignore +34 -0
  87. package/templates/next-ts/_template.json +13 -0
  88. package/templates/next-ts/next.config.ts +7 -0
  89. package/templates/next-ts/package.json +22 -0
  90. package/templates/next-ts/src/app/globals.css +21 -0
  91. package/templates/next-ts/src/app/layout.tsx +19 -0
  92. package/templates/next-ts/src/app/page.tsx +10 -0
  93. package/templates/next-ts/tsconfig.json +27 -0
  94. package/templates/springboot/.mvn/wrapper/maven-wrapper.properties +2 -0
  95. package/templates/springboot/README.md +27 -0
  96. package/templates/springboot/_gitignore +36 -0
  97. package/templates/springboot/_template.json +14 -0
  98. package/templates/springboot/mvnw +19 -0
  99. package/templates/springboot/mvnw.cmd +13 -0
  100. package/templates/springboot/pom.xml +46 -0
  101. package/templates/springboot/src/main/java/com/example/project_name/Application.java +12 -0
  102. package/templates/springboot/src/main/java/com/example/project_name/controller/HelloController.java +25 -0
  103. package/templates/springboot/src/main/resources/application.yml +6 -0
  104. package/templates/springboot/src/test/java/com/example/project_name/ApplicationTests.java +12 -0
  105. package/templates/template.config.json +78 -10
package/README.md CHANGED
@@ -2,110 +2,115 @@
2
2
 
3
3
  快速创建项目并自动初始化 GitHub 仓库的 CLI 脚手架工具。
4
4
 
5
- ## 特性
5
+ ## 快速开始
6
6
 
7
- - 🚀 快速创建项目
8
- - 🔌 自动初始化 Git 仓库
9
- - 🐙 内置 GitHub API 集成 (无需 gh CLI)
10
- - 🔐 安全的 Token 管理
11
- - 📦 多模板支持
12
- - ⚡ Node.js 18+ 原生支持
7
+ ```bash
8
+ # 使用 npx (推荐)
9
+ npx create-idp my-app
13
10
 
14
- ## 安装
11
+ # 或使用 npm create
12
+ npm create idp my-app
15
13
 
16
- ```bash
14
+ # 或全局安装后使用
17
15
  npm install -g create-idp
16
+ create-idp my-app
18
17
  ```
19
18
 
20
- ## 快速开始
19
+ ## 用法
21
20
 
22
- ### 1. 配置 GitHub Token (首次仅需一次)
21
+ ### 交互式创建
23
22
 
24
23
  ```bash
25
- create-idp config --setup-github
24
+ npx create-idp
26
25
  ```
27
26
 
28
- 按提示获取 GitHub Personal Access Token: https://github.com/settings/tokens/new?scopes=repo
29
-
30
- ### 2. 创建项目
27
+ ### 指定模板创建
31
28
 
32
29
  ```bash
33
- # 交互式创建
34
- create-idp
35
-
36
- # 或一条命令
37
- create-idp my-app --github --template node-ts
30
+ npx create-idp my-app --template react-ts
31
+ npx create-idp my-api --template fastapi
32
+ npx create-idp my-fullstack --template fullstack-react-node
38
33
  ```
39
34
 
40
- ### 3. 开始开发
35
+ ### 一条命令创建并推送到 GitHub
41
36
 
42
37
  ```bash
43
- cd my-app
44
- pnpm dev
38
+ npx create-idp my-app --github --template node-ts
45
39
  ```
46
40
 
47
- ## 命令
48
-
49
- ### 配置管理
41
+ ### 列出所有可用模板
50
42
 
51
43
  ```bash
52
- create-idp config --setup-github # 配置 GitHub Token
53
- create-idp config --show # 显示当前配置
54
- create-idp config --path # 显示配置文件路径
44
+ npx create-idp list
55
45
  ```
56
46
 
57
- ### 创建项目
47
+ ## 可用模板
58
48
 
59
- ```bash
60
- create-idp # 交互式创建
61
- create-idp my-app # 指定项目名
62
- create-idp my-app --template node-ts # 指定模板
63
- create-idp my-app --github # 创建 + GitHub 仓库
64
- create-idp my-app --github --public # 创建公开仓库
65
- create-idp my-app --no-git # 不初始化 Git
66
- ```
49
+ ### 单体项目
67
50
 
68
- ## 使用场景
51
+ | 模板名 | 描述 |
52
+ |--------|------|
53
+ | `node-ts` | Node.js + TypeScript |
54
+ | `express-ts` | Express + TypeScript API |
55
+ | `react-ts` | React 18 + Vite + TypeScript |
56
+ | `vue-ts` | Vue 3 + Vite + TypeScript |
57
+ | `next-ts` | Next.js 14 + App Router |
58
+ | `python-uv` | Python + uv 包管理器 |
59
+ | `fastapi` | FastAPI + uv |
60
+ | `django` | Django + uv |
61
+ | `flask` | Flask + uv |
62
+ | `springboot` | Spring Boot 3.x + Maven |
63
+ | `empty` | 空项目 |
69
64
 
70
- ### 个人项目
65
+ ### 前后端分离
71
66
 
72
- ```bash
73
- create-idp my-project --github
74
- cd my-project
75
- pnpm dev
76
- ```
67
+ | 模板名 | 描述 |
68
+ |--------|------|
69
+ | `fullstack-react-node` | React + Express (pnpm workspace) |
70
+ | `fullstack-vue-node` | Vue 3 + Express (pnpm workspace) |
71
+ | `fullstack-react-python` | React + FastAPI (Makefile) |
77
72
 
78
- ### 为组织创建项目
73
+ ## 命令参考
79
74
 
80
- 首次配置时选择设置默认组织,之后所有项目都会在该组织下创建。
75
+ ### 创建项目
81
76
 
82
77
  ```bash
83
- create-idp config --setup-github
84
- # 选择: 设置默认组织 → my-company
85
-
86
- create-idp my-project --github
87
- # 仓库创建在: https://github.com/my-company/my-project
78
+ npx create-idp # 交互式创建
79
+ npx create-idp my-app # 指定项目名
80
+ npx create-idp my-app --template node-ts # 指定模板
81
+ npx create-idp my-app --github # 创建 + GitHub 仓库
82
+ npx create-idp my-app --github --public # 创建公开仓库
83
+ npx create-idp my-app --no-git # 不初始化 Git
84
+ npx create-idp my-app --no-install # 跳过依赖安装
88
85
  ```
89
86
 
90
- ### 公开项目
87
+ ### 列出模板
91
88
 
92
89
  ```bash
93
- create-idp awesome-lib --github --public
90
+ npx create-idp list
94
91
  ```
95
92
 
96
- ## 配置文件
93
+ ### 配置管理
97
94
 
98
- 配置文件位置: `~/.create-idp.json`
95
+ ```bash
96
+ create-idp config --setup-github # 配置 GitHub Token
97
+ create-idp config --show # 显示当前配置
98
+ create-idp config --path # 显示配置文件路径
99
+ ```
100
+
101
+ ## GitHub 集成
99
102
 
100
- ### 查看配置
103
+ ### 首次使用配置 Token
101
104
 
102
105
  ```bash
103
- create-idp config --show
106
+ npx create-idp config --setup-github
104
107
  ```
105
108
 
106
- ### 手动配置 (可选)
109
+ 按提示获取 Token: https://github.com/settings/tokens/new?scopes=repo
107
110
 
108
- 编辑 `~/.create-idp.json`:
111
+ ### 配置文件
112
+
113
+ 配置文件位置: `~/.create-idp.json`
109
114
 
110
115
  ```json
111
116
  {
@@ -121,85 +126,41 @@ create-idp config --show
121
126
  }
122
127
  ```
123
128
 
124
- ## GitHub API 集成
125
-
126
- - ✅ Token 验证 (GET /user)
127
- - ✅ 创建个人仓库 (POST /user/repos)
128
- - ✅ 创建组织仓库 (POST /orgs/{org}/repos)
129
- - ✅ 自动配置 git remote
130
- - ✅ 自动推送初始提交
131
-
132
- ## 安全性
133
-
134
- - 配置文件权限自动设置为 600 (仅所有者读写)
135
- - Token 存储在本地,不会上传
136
- - 使用 HTTPS 连接 GitHub
137
- - 完善的错误处理
129
+ ## 项目结构示例
138
130
 
139
- ## 常见问题
131
+ ### 单体项目 (node-ts)
140
132
 
141
- ### Q: Token 失效了怎么办?
142
-
143
- ```bash
144
- create-idp config --setup-github
145
133
  ```
146
-
147
- 重新配置新的 Token。
148
-
149
- ### Q: 包名被占用了怎么办?
150
-
151
- 使用不同的项目名称重新创建。
152
-
153
- ### Q: 如何跳过创建 GitHub 仓库?
154
-
155
- ```bash
156
- create-idp my-app --no-git
134
+ my-app/
135
+ ├── src/
136
+ │ └── index.ts
137
+ ├── package.json
138
+ ├── tsconfig.json
139
+ └── README.md
157
140
  ```
158
141
 
159
- ### Q: 如何为不同账户创建项目?
142
+ ### 全栈项目 (fullstack-react-node)
160
143
 
161
- ```bash
162
- create-idp config --setup-github
163
144
  ```
164
-
165
- 输入不同账户的 Token 进行切换。
166
-
167
- ## 文档
168
-
169
- - [快速开始](./QUICK_START.md) - 5 分钟快速上手
170
- - [完整使用指南](./USAGE_GUIDE.md) - 所有功能详解
171
- - [npm 发布指南](./NPM_QUICK_PUBLISH.md) - 如何发布到 npm
172
-
173
- ## 支持的模板
174
-
175
- - `node-ts` - Node.js + TypeScript
176
- - `react-ts` - React + TypeScript
177
- - `vue-ts` - Vue + TypeScript
178
- - 更多自定义模板
145
+ my-app/
146
+ ├── frontend/
147
+ │ ├── src/
148
+ │ ├── package.json
149
+ │ └── vite.config.ts
150
+ ├── backend/
151
+ │ ├── src/
152
+ │ └── package.json
153
+ ├── package.json
154
+ └── pnpm-workspace.yaml
155
+ ```
179
156
 
180
157
  ## 系统要求
181
158
 
182
159
  - Node.js >= 18.0.0
183
- - npm pnpm
160
+ - pnpm (推荐) / npm / yarn
161
+ - uv (Python 模板)
162
+ - Maven (Spring Boot 模板)
184
163
 
185
164
  ## 许可证
186
165
 
187
166
  MIT
188
-
189
- ## 相关链接
190
-
191
- - [GitHub](https://github.com/your-username/create-idp)
192
- - [npm Package](https://www.npmjs.com/package/create-idp)
193
-
194
- ## 贡献
195
-
196
- 欢迎提交 Issue 和 Pull Request!
197
-
198
- ## 变更日志
199
-
200
- ### v0.1.0
201
-
202
- - 初始发布
203
- - GitHub API 集成
204
- - CLI 命令完整
205
- - 完整的文档和指南
package/dist/index.js CHANGED
@@ -7,16 +7,18 @@ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require
7
7
  });
8
8
 
9
9
  // src/cli.ts
10
+ import path7 from "path";
11
+ import fs5 from "fs";
10
12
  import { Command } from "commander";
11
13
  import pc6 from "picocolors";
12
14
 
13
15
  // package.json
14
- var version = "0.1.0";
16
+ var version = "0.2.1";
15
17
 
16
18
  // src/prompts.ts
17
19
  import path2 from "path";
18
20
  import fs from "fs";
19
- import { input, select, confirm } from "@inquirer/prompts";
21
+ import { input, select, confirm, Separator } from "@inquirer/prompts";
20
22
  import pc from "picocolors";
21
23
 
22
24
  // src/utils.ts
@@ -35,11 +37,11 @@ function toValidPackageName(name) {
35
37
  return name.trim().toLowerCase().replace(/\s+/g, "-").replace(/^[._]/, "").replace(/[^a-z0-9-~]+/g, "-");
36
38
  }
37
39
  function isEmpty(dirPath) {
38
- const fs5 = __require("fs");
39
- if (!fs5.existsSync(dirPath)) {
40
+ const fs6 = __require("fs");
41
+ if (!fs6.existsSync(dirPath)) {
40
42
  return true;
41
43
  }
42
- const files = fs5.readdirSync(dirPath);
44
+ const files = fs6.readdirSync(dirPath);
43
45
  return files.length === 0 || files.length === 1 && files[0] === ".git";
44
46
  }
45
47
  function execAsync(command, args, options = {}) {
@@ -109,10 +111,25 @@ async function runPrompts(argProjectName, options, userConfig = {}) {
109
111
  let template = options.template;
110
112
  if (!template) {
111
113
  const defaultTemplate = defaults.template;
112
- const templateChoices = registry.templates.map((t) => ({
113
- name: `${t.displayName} ${pc.dim(`- ${t.description}`)}`,
114
- value: t.name
115
- }));
114
+ const singleTemplates = registry.templates.filter((t) => t.category !== "fullstack");
115
+ const fullstackTemplates = registry.templates.filter((t) => t.category === "fullstack");
116
+ const templateChoices = [];
117
+ templateChoices.push(new Separator("\u2500\u2500 \u5355\u4F53\u9879\u76EE \u2500\u2500"));
118
+ for (const t of singleTemplates) {
119
+ templateChoices.push({
120
+ name: `${t.displayName} ${pc.dim(`- ${t.description}`)}`,
121
+ value: t.name
122
+ });
123
+ }
124
+ if (fullstackTemplates.length > 0) {
125
+ templateChoices.push(new Separator("\u2500\u2500 \u524D\u540E\u7AEF\u5206\u79BB \u2500\u2500"));
126
+ for (const t of fullstackTemplates) {
127
+ templateChoices.push({
128
+ name: `${t.displayName} ${pc.dim(`- ${t.description}`)}`,
129
+ value: t.name
130
+ });
131
+ }
132
+ }
116
133
  template = await select({
117
134
  message: "\u9009\u62E9\u6A21\u677F:",
118
135
  choices: templateChoices,
@@ -266,6 +283,22 @@ import fs3 from "fs";
266
283
  import path4 from "path";
267
284
  import os from "os";
268
285
  var CONFIG_PATH = path4.join(os.homedir(), ".create-idp.json");
286
+ var DEFAULT_CONFIG = {
287
+ defaults: {
288
+ github: false,
289
+ public: false,
290
+ template: ""
291
+ },
292
+ github: {
293
+ token: "",
294
+ defaultOrg: ""
295
+ }
296
+ };
297
+ function ensureConfigExists() {
298
+ if (!fs3.existsSync(CONFIG_PATH)) {
299
+ saveConfig(DEFAULT_CONFIG);
300
+ }
301
+ }
269
302
  function loadConfig() {
270
303
  try {
271
304
  if (fs3.existsSync(CONFIG_PATH)) {
@@ -430,6 +463,7 @@ async function createGithubRepo(repoName, targetDir, isPublic) {
430
463
  }
431
464
 
432
465
  // src/post-init.ts
466
+ import path5 from "path";
433
467
  import pc3 from "picocolors";
434
468
  async function runPostInit(targetDir, commands) {
435
469
  for (const cmd of commands) {
@@ -447,13 +481,27 @@ async function runPostInit(targetDir, commands) {
447
481
  }
448
482
  }
449
483
  }
484
+ async function runWorkspacePostInit(targetDir, config) {
485
+ if (config.postInit && config.postInit.length > 0) {
486
+ await runPostInit(targetDir, config.postInit);
487
+ }
488
+ if (config.workspaces) {
489
+ for (const ws of config.workspaces) {
490
+ if (ws.postInit && ws.postInit.length > 0) {
491
+ const wsDir = path5.join(targetDir, ws.name);
492
+ console.log(pc3.dim(` [${ws.name}]`));
493
+ await runPostInit(wsDir, ws.postInit);
494
+ }
495
+ }
496
+ }
497
+ }
450
498
 
451
499
  // src/update-check.ts
452
500
  import fs4 from "fs";
453
- import path5 from "path";
501
+ import path6 from "path";
454
502
  import os2 from "os";
455
503
  import pc4 from "picocolors";
456
- var CACHE_PATH = path5.join(os2.homedir(), ".create-idp-update-check");
504
+ var CACHE_PATH = path6.join(os2.homedir(), ".create-idp-update-check");
457
505
  var CACHE_TTL = 24 * 60 * 60 * 1e3;
458
506
  var PACKAGE_NAME = "create-idp";
459
507
  function readCache() {
@@ -521,6 +569,10 @@ async function checkForUpdates(currentVersion) {
521
569
  import { input as input2, confirm as confirm2 } from "@inquirer/prompts";
522
570
  import pc5 from "picocolors";
523
571
  var TOKEN_HELP_URL = "https://github.com/settings/tokens/new?scopes=repo&description=create-idp";
572
+ function isUserCancelled(err) {
573
+ const error = err;
574
+ return error.message === "PROMPT_CANCELLED" || error.name === "ExitPromptError" || error.message.includes("User force closed");
575
+ }
524
576
  async function runGitHubSetup() {
525
577
  console.log();
526
578
  console.log(pc5.cyan("\u914D\u7F6E GitHub Token"));
@@ -535,58 +587,66 @@ async function runGitHubSetup() {
535
587
  console.log();
536
588
  console.log(pc5.dim(`\u5FEB\u6377\u94FE\u63A5: ${TOKEN_HELP_URL}`));
537
589
  console.log();
538
- const token = await input2({
539
- message: "\u8BF7\u8F93\u5165 GitHub Token:",
540
- validate: (value) => {
541
- if (!value.trim()) {
542
- return "Token \u4E0D\u80FD\u4E3A\u7A7A";
543
- }
544
- if (!value.startsWith("ghp_") && !value.startsWith("github_pat_")) {
545
- return "Token \u683C\u5F0F\u4E0D\u6B63\u786E\uFF0C\u5E94\u4EE5 ghp_ \u6216 github_pat_ \u5F00\u5934";
546
- }
547
- return true;
548
- }
549
- });
550
- console.log();
551
- console.log(pc5.dim("\u6B63\u5728\u9A8C\u8BC1 Token..."));
552
- const result = await validateToken(token.trim());
553
- if (!result.success) {
554
- console.log(pc5.red(`\u2717 Token \u9A8C\u8BC1\u5931\u8D25: ${result.error}`));
555
- if (result.rateLimitReset) {
556
- console.log(pc5.dim(` Rate limit \u5C06\u5728 ${result.rateLimitReset.toLocaleString()} \u91CD\u7F6E`));
557
- }
558
- return false;
559
- }
560
- const user = result.data;
561
- console.log(pc5.green(`\u2714 Token \u6709\u6548\uFF0C\u5DF2\u767B\u5F55\u4E3A: ${user.login}${user.name ? ` (${user.name})` : ""}`));
562
- console.log();
563
- const setOrg = await confirm2({
564
- message: "\u662F\u5426\u8BBE\u7F6E\u9ED8\u8BA4\u7EC4\u7EC7? (\u5426\u5219\u4ED3\u5E93\u5C06\u521B\u5EFA\u5728\u4E2A\u4EBA\u8D26\u6237\u4E0B)",
565
- default: false
566
- });
567
- let defaultOrg;
568
- if (setOrg) {
569
- defaultOrg = await input2({
570
- message: "\u9ED8\u8BA4\u7EC4\u7EC7\u540D\u79F0:",
590
+ try {
591
+ const token = await input2({
592
+ message: "\u8BF7\u8F93\u5165 GitHub Token:",
571
593
  validate: (value) => {
572
594
  if (!value.trim()) {
573
- return "\u7EC4\u7EC7\u540D\u79F0\u4E0D\u80FD\u4E3A\u7A7A";
595
+ return "Token \u4E0D\u80FD\u4E3A\u7A7A";
596
+ }
597
+ if (!value.startsWith("ghp_") && !value.startsWith("github_pat_")) {
598
+ return "Token \u683C\u5F0F\u4E0D\u6B63\u786E\uFF0C\u5E94\u4EE5 ghp_ \u6216 github_pat_ \u5F00\u5934";
574
599
  }
575
600
  return true;
576
601
  }
577
602
  });
578
- defaultOrg = defaultOrg.trim();
603
+ console.log();
604
+ console.log(pc5.dim("\u6B63\u5728\u9A8C\u8BC1 Token..."));
605
+ const result = await validateToken(token.trim());
606
+ if (!result.success) {
607
+ console.log(pc5.red(`\u2717 Token \u9A8C\u8BC1\u5931\u8D25: ${result.error}`));
608
+ if (result.rateLimitReset) {
609
+ console.log(pc5.dim(` Rate limit \u5C06\u5728 ${result.rateLimitReset.toLocaleString()} \u91CD\u7F6E`));
610
+ }
611
+ return false;
612
+ }
613
+ const user = result.data;
614
+ console.log(pc5.green(`\u2714 Token \u6709\u6548\uFF0C\u5DF2\u767B\u5F55\u4E3A: ${user.login}${user.name ? ` (${user.name})` : ""}`));
615
+ console.log();
616
+ const setOrg = await confirm2({
617
+ message: "\u662F\u5426\u8BBE\u7F6E\u9ED8\u8BA4\u7EC4\u7EC7? (\u5426\u5219\u4ED3\u5E93\u5C06\u521B\u5EFA\u5728\u4E2A\u4EBA\u8D26\u6237\u4E0B)",
618
+ default: false
619
+ });
620
+ let defaultOrg;
621
+ if (setOrg) {
622
+ defaultOrg = await input2({
623
+ message: "\u9ED8\u8BA4\u7EC4\u7EC7\u540D\u79F0:",
624
+ validate: (value) => {
625
+ if (!value.trim()) {
626
+ return "\u7EC4\u7EC7\u540D\u79F0\u4E0D\u80FD\u4E3A\u7A7A";
627
+ }
628
+ return true;
629
+ }
630
+ });
631
+ defaultOrg = defaultOrg.trim();
632
+ }
633
+ const config = loadConfig();
634
+ config.github = {
635
+ token: token.trim(),
636
+ ...defaultOrg && { defaultOrg }
637
+ };
638
+ saveConfig(config);
639
+ console.log();
640
+ console.log(pc5.green("\u2714 GitHub \u914D\u7F6E\u5DF2\u4FDD\u5B58"));
641
+ console.log(pc5.dim(" \u914D\u7F6E\u6587\u4EF6\u5DF2\u8BBE\u7F6E\u4E3A\u4EC5\u5F53\u524D\u7528\u6237\u53EF\u8BFB"));
642
+ return true;
643
+ } catch (err) {
644
+ if (isUserCancelled(err)) {
645
+ console.log(pc5.yellow("\n\u64CD\u4F5C\u5DF2\u53D6\u6D88"));
646
+ return false;
647
+ }
648
+ throw err;
579
649
  }
580
- const config = loadConfig();
581
- config.github = {
582
- token: token.trim(),
583
- ...defaultOrg && { defaultOrg }
584
- };
585
- saveConfig(config);
586
- console.log();
587
- console.log(pc5.green("\u2714 GitHub \u914D\u7F6E\u5DF2\u4FDD\u5B58"));
588
- console.log(pc5.dim(" \u914D\u7F6E\u6587\u4EF6\u5DF2\u8BBE\u7F6E\u4E3A\u4EC5\u5F53\u524D\u7528\u6237\u53EF\u8BFB"));
589
- return true;
590
650
  }
591
651
  function showConfig() {
592
652
  const config = loadConfig();
@@ -635,13 +695,36 @@ async function createCli() {
635
695
  const projectOptions = await runPrompts(projectName, options, userConfig);
636
696
  await executeProject(projectOptions);
637
697
  } catch (err) {
638
- if (err.message === "PROMPT_CANCELLED") {
698
+ const error = err;
699
+ if (error.message === "PROMPT_CANCELLED" || error.name === "ExitPromptError" || error.message.includes("User force closed")) {
639
700
  console.log(pc6.yellow("\n\u64CD\u4F5C\u5DF2\u53D6\u6D88"));
640
701
  process.exit(0);
641
702
  }
642
703
  throw err;
643
704
  }
644
705
  });
706
+ program.command("list").description("\u5217\u51FA\u6240\u6709\u53EF\u7528\u6A21\u677F").action(() => {
707
+ const templatesDir = getTemplatesDir();
708
+ const registryPath = path7.join(templatesDir, "template.config.json");
709
+ const registry = JSON.parse(
710
+ fs5.readFileSync(registryPath, "utf-8")
711
+ );
712
+ const singleTemplates = registry.templates.filter((t) => t.category !== "fullstack");
713
+ const fullstackTemplates = registry.templates.filter((t) => t.category === "fullstack");
714
+ console.log();
715
+ console.log(pc6.cyan(" \u5355\u4F53\u9879\u76EE:"));
716
+ for (const t of singleTemplates) {
717
+ console.log(` ${pc6.green(t.name.padEnd(24))} ${pc6.dim(t.displayName)}`);
718
+ }
719
+ if (fullstackTemplates.length > 0) {
720
+ console.log();
721
+ console.log(pc6.cyan(" \u524D\u540E\u7AEF\u5206\u79BB:"));
722
+ for (const t of fullstackTemplates) {
723
+ console.log(` ${pc6.green(t.name.padEnd(24))} ${pc6.dim(t.displayName)}`);
724
+ }
725
+ }
726
+ console.log();
727
+ });
645
728
  program.command("config").description("\u7BA1\u7406\u914D\u7F6E").option("--setup-github", "\u914D\u7F6E GitHub Token").option("--show", "\u663E\u793A\u5F53\u524D\u914D\u7F6E").option("--path", "\u663E\u793A\u914D\u7F6E\u6587\u4EF6\u8DEF\u5F84").action(async (options) => {
646
729
  if (options.setupGithub) {
647
730
  const success = await runGitHubSetup();
@@ -665,9 +748,14 @@ async function createCli() {
665
748
  async function executeProject(options) {
666
749
  const templateConfig = await scaffoldProject(options);
667
750
  console.log(pc6.green("\u2714 \u9879\u76EE\u6587\u4EF6\u5DF2\u751F\u6210"));
668
- if (!options.noInstall && templateConfig?.postInit) {
669
- await runPostInit(options.targetDir, templateConfig.postInit);
670
- console.log(pc6.green("\u2714 \u4F9D\u8D56\u5B89\u88C5\u5B8C\u6210"));
751
+ if (!options.noInstall && templateConfig) {
752
+ if (templateConfig.type === "workspace") {
753
+ await runWorkspacePostInit(options.targetDir, templateConfig);
754
+ console.log(pc6.green("\u2714 \u4F9D\u8D56\u5B89\u88C5\u5B8C\u6210"));
755
+ } else if (templateConfig.postInit) {
756
+ await runPostInit(options.targetDir, templateConfig.postInit);
757
+ console.log(pc6.green("\u2714 \u4F9D\u8D56\u5B89\u88C5\u5B8C\u6210"));
758
+ }
671
759
  }
672
760
  if (options.initGit) {
673
761
  await initGit(options.targetDir);
@@ -686,12 +774,25 @@ async function executeProject(options) {
686
774
  console.log();
687
775
  const pm = templateConfig?.packageManager ?? "pnpm";
688
776
  const cdCmd = options.targetDir !== process.cwd() ? `cd ${options.projectName} && ` : "";
689
- console.log(pc6.dim(` ${cdCmd}${pm} dev`));
777
+ if (templateConfig?.type === "workspace") {
778
+ if (pm === "pnpm") {
779
+ console.log(pc6.dim(` ${cdCmd}pnpm dev`));
780
+ } else {
781
+ console.log(pc6.dim(` ${cdCmd}make dev`));
782
+ }
783
+ } else if (pm === "maven") {
784
+ console.log(pc6.dim(` ${cdCmd}./mvnw spring-boot:run`));
785
+ } else if (pm === "gradle") {
786
+ console.log(pc6.dim(` ${cdCmd}./gradlew bootRun`));
787
+ } else {
788
+ console.log(pc6.dim(` ${cdCmd}${pm} dev`));
789
+ }
690
790
  console.log();
691
791
  }
692
792
 
693
793
  // src/index.ts
694
794
  async function main() {
795
+ ensureConfigExists();
695
796
  const program = await createCli();
696
797
  await program.parseAsync(process.argv);
697
798
  }