create-teamix-evo 0.1.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.
- package/LICENSE +21 -0
- package/README.md +75 -0
- package/dist/index.js +586 -0
- package/dist/index.js.map +1 -0
- package/overlays/console/package.json.fragment.json +5 -0
- package/overlays/console/src/App.tsx +6 -0
- package/overlays/console/src/components/_placeholder/Card.tsx +40 -0
- package/overlays/console/src/components/_placeholder/Form.tsx +52 -0
- package/overlays/console/src/components/_placeholder/Input.tsx +35 -0
- package/overlays/console/src/components/_placeholder/README.md +42 -0
- package/overlays/console/src/components/_placeholder/Table.tsx +77 -0
- package/overlays/console/src/layouts/ConsoleLayout.tsx +46 -0
- package/overlays/console/src/lib/mock-data.ts +38 -0
- package/overlays/console/src/main.tsx +13 -0
- package/overlays/console/src/pages/DashboardPage.tsx +38 -0
- package/overlays/console/src/pages/UserDetailPage.tsx +57 -0
- package/overlays/console/src/pages/UserFormPage.tsx +61 -0
- package/overlays/console/src/pages/UserListPage.tsx +67 -0
- package/overlays/console/src/routes.tsx +21 -0
- package/package.json +38 -0
- package/templates/react-ts/README.md.hbs +39 -0
- package/templates/react-ts/_editorconfig +9 -0
- package/templates/react-ts/_gitignore +24 -0
- package/templates/react-ts/index.html.hbs +12 -0
- package/templates/react-ts/package.json.hbs +26 -0
- package/templates/react-ts/src/App.tsx +20 -0
- package/templates/react-ts/src/index.css +5 -0
- package/templates/react-ts/src/main.tsx +10 -0
- package/templates/react-ts/src/vite-env.d.ts +1 -0
- package/templates/react-ts/tailwind.config.ts +13 -0
- package/templates/react-ts/tsconfig.json +29 -0
- package/templates/react-ts/vite.config.ts +14 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Teamix Evo Contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
# create-teamix-evo
|
|
2
|
+
|
|
3
|
+
Scaffold a Vite + React + TypeScript project pre-wired with [Teamix Evo](https://github.com/teamix-evo/teamix-evo) design tokens, AI skills, and UI components.
|
|
4
|
+
|
|
5
|
+
## Usage
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
# pnpm (recommended)
|
|
9
|
+
pnpm create teamix-evo my-app
|
|
10
|
+
|
|
11
|
+
# npm
|
|
12
|
+
npm create teamix-evo my-app
|
|
13
|
+
|
|
14
|
+
# yarn
|
|
15
|
+
yarn create teamix-evo my-app
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## Presets
|
|
19
|
+
|
|
20
|
+
| Preset | Description |
|
|
21
|
+
| --------- | ----------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
22
|
+
| `minimal` | Vite + React + TS + Tailwind v4 + design tokens (`opentrek` variant) + AI skills (Qoder + Claude) |
|
|
23
|
+
| `console` | Everything in `minimal` plus react-router-dom, ConsoleLayout, and Dashboard / List / Detail / Form pages with placeholder Table/Form/Input/Card |
|
|
24
|
+
|
|
25
|
+
Pick a preset interactively, or pass `--preset minimal` / `--preset console` directly.
|
|
26
|
+
|
|
27
|
+
## Options
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
create-teamix-evo <dir> [options]
|
|
31
|
+
|
|
32
|
+
Arguments:
|
|
33
|
+
dir Target directory (will be created if it does not exist)
|
|
34
|
+
|
|
35
|
+
Options:
|
|
36
|
+
--preset <id> Preset id: minimal | console
|
|
37
|
+
--pm <name> Package manager: pnpm | npm | yarn (auto-detected by default)
|
|
38
|
+
--no-git Skip git init
|
|
39
|
+
--no-install Skip dependency installation
|
|
40
|
+
--force Overwrite target directory if it exists
|
|
41
|
+
-h, --help Display help
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## What gets scaffolded
|
|
45
|
+
|
|
46
|
+
After the command finishes:
|
|
47
|
+
|
|
48
|
+
```
|
|
49
|
+
my-app/
|
|
50
|
+
├── .teamix-evo/ # design tokens + config (frozen / regenerable)
|
|
51
|
+
├── .qoder/agents/ # AI skills for Qoder
|
|
52
|
+
├── .claude/agents/ # AI skills for Claude Code
|
|
53
|
+
├── AGENTS.md / CLAUDE.md # project-level AI context (managed regions)
|
|
54
|
+
├── src/
|
|
55
|
+
│ ├── components/ui/ # ui add 落地的真实组件(如 button.tsx)
|
|
56
|
+
│ ├── components/_placeholder/ # console preset 占位组件(待 ui 包补齐)
|
|
57
|
+
│ ├── pages/, layouts/, routes.tsx
|
|
58
|
+
│ ├── App.tsx, main.tsx, index.css
|
|
59
|
+
├── package.json, vite.config.ts, ...
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
## Next steps
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
cd my-app
|
|
66
|
+
pnpm dev # 启动开发服务器
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
Want to add more UI components later? Inside the project:
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
npx teamix-evo ui add card table form input
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
Read the project-level `AGENTS.md` for the full lifecycle guide (`design update`, `skills update`, `ui add`, etc.).
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,586 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/index.ts
|
|
4
|
+
import path3 from "path";
|
|
5
|
+
import { Command } from "commander";
|
|
6
|
+
import { red as red2 } from "kolorist";
|
|
7
|
+
|
|
8
|
+
// src/orchestrator.ts
|
|
9
|
+
import fs3 from "fs/promises";
|
|
10
|
+
import path2 from "path";
|
|
11
|
+
import { fileURLToPath } from "url";
|
|
12
|
+
import { execa as execa2 } from "execa";
|
|
13
|
+
import {
|
|
14
|
+
runDesignInit,
|
|
15
|
+
runSkillsAdd,
|
|
16
|
+
runUiInit,
|
|
17
|
+
runUiAdd
|
|
18
|
+
} from "teamix-evo/core";
|
|
19
|
+
|
|
20
|
+
// src/copy.ts
|
|
21
|
+
import fs from "fs/promises";
|
|
22
|
+
import path from "path";
|
|
23
|
+
import Handlebars from "handlebars";
|
|
24
|
+
async function copyDir(srcDir, destDir, ctx) {
|
|
25
|
+
await fs.mkdir(destDir, { recursive: true });
|
|
26
|
+
const entries = await fs.readdir(srcDir, { withFileTypes: true });
|
|
27
|
+
for (const entry of entries) {
|
|
28
|
+
const srcPath = path.join(srcDir, entry.name);
|
|
29
|
+
const destName = mapDestName(entry.name);
|
|
30
|
+
if (destName === null) continue;
|
|
31
|
+
const destPath = path.join(destDir, destName);
|
|
32
|
+
if (entry.isDirectory()) {
|
|
33
|
+
await copyDir(srcPath, destPath, ctx);
|
|
34
|
+
continue;
|
|
35
|
+
}
|
|
36
|
+
if (entry.name.endsWith(".hbs")) {
|
|
37
|
+
const tpl = await fs.readFile(srcPath, "utf8");
|
|
38
|
+
const rendered = Handlebars.compile(tpl, { noEscape: true })(ctx.hbs);
|
|
39
|
+
await fs.writeFile(destPath, rendered, "utf8");
|
|
40
|
+
continue;
|
|
41
|
+
}
|
|
42
|
+
await fs.copyFile(srcPath, destPath);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
function mapDestName(name) {
|
|
46
|
+
if (name === "package.json.fragment.json") return null;
|
|
47
|
+
if (name === "_gitignore") return ".gitignore";
|
|
48
|
+
if (name === "_editorconfig") return ".editorconfig";
|
|
49
|
+
if (name === "_npmrc") return ".npmrc";
|
|
50
|
+
if (name.endsWith(".hbs")) {
|
|
51
|
+
return name.slice(0, -".hbs".length);
|
|
52
|
+
}
|
|
53
|
+
return name;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// src/git.ts
|
|
57
|
+
import { execa } from "execa";
|
|
58
|
+
async function gitInit(cwd) {
|
|
59
|
+
try {
|
|
60
|
+
await execa("git", ["init", "--quiet"], { cwd });
|
|
61
|
+
await execa("git", ["add", "-A"], { cwd });
|
|
62
|
+
await execa(
|
|
63
|
+
"git",
|
|
64
|
+
[
|
|
65
|
+
"-c",
|
|
66
|
+
"user.email=create-teamix-evo@local",
|
|
67
|
+
"-c",
|
|
68
|
+
"user.name=create-teamix-evo",
|
|
69
|
+
"commit",
|
|
70
|
+
"--quiet",
|
|
71
|
+
"--no-gpg-sign",
|
|
72
|
+
"-m",
|
|
73
|
+
"chore: scaffolded by create-teamix-evo"
|
|
74
|
+
],
|
|
75
|
+
{ cwd }
|
|
76
|
+
);
|
|
77
|
+
return { ok: true };
|
|
78
|
+
} catch (err) {
|
|
79
|
+
const reason = err instanceof Error ? err.message : String(err);
|
|
80
|
+
return { ok: false, reason };
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// src/merge-package-json.ts
|
|
85
|
+
import fs2 from "fs/promises";
|
|
86
|
+
var VersionConflictError = class extends Error {
|
|
87
|
+
constructor(key, name, base, incoming) {
|
|
88
|
+
super(
|
|
89
|
+
`Version conflict in ${key}: ${name} declared as "${base}" in base but "${incoming}" in overlay/extras.`
|
|
90
|
+
);
|
|
91
|
+
this.key = key;
|
|
92
|
+
this.name = name;
|
|
93
|
+
this.base = base;
|
|
94
|
+
this.incoming = incoming;
|
|
95
|
+
this.name = "VersionConflictError";
|
|
96
|
+
}
|
|
97
|
+
key;
|
|
98
|
+
name;
|
|
99
|
+
base;
|
|
100
|
+
incoming;
|
|
101
|
+
};
|
|
102
|
+
function mergePackageJson(base, overlay, options = {}) {
|
|
103
|
+
const merged = { ...base };
|
|
104
|
+
for (const key of ["dependencies", "devDependencies", "scripts"]) {
|
|
105
|
+
const baseMap = base[key] ?? {};
|
|
106
|
+
const overlayMap = overlay?.[key] ?? {};
|
|
107
|
+
const extrasMap = key === "dependencies" ? options.extraDependencies ?? {} : key === "devDependencies" ? options.extraDevDependencies ?? {} : {};
|
|
108
|
+
const result = { ...baseMap };
|
|
109
|
+
for (const [name, version] of Object.entries(overlayMap)) {
|
|
110
|
+
if (result[name] !== void 0 && result[name] !== version) {
|
|
111
|
+
throw new VersionConflictError(key, name, result[name], version);
|
|
112
|
+
}
|
|
113
|
+
result[name] = version;
|
|
114
|
+
}
|
|
115
|
+
for (const [name, version] of Object.entries(extrasMap)) {
|
|
116
|
+
if (result[name] !== void 0 && result[name] !== version) {
|
|
117
|
+
throw new VersionConflictError(key, name, result[name], version);
|
|
118
|
+
}
|
|
119
|
+
result[name] = version;
|
|
120
|
+
}
|
|
121
|
+
if (Object.keys(result).length > 0) {
|
|
122
|
+
merged[key] = sortObjectByKey(result);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
return merged;
|
|
126
|
+
}
|
|
127
|
+
function sortObjectByKey(obj) {
|
|
128
|
+
const sorted = {};
|
|
129
|
+
for (const k of Object.keys(obj).sort()) sorted[k] = obj[k];
|
|
130
|
+
return sorted;
|
|
131
|
+
}
|
|
132
|
+
async function readJson(filePath) {
|
|
133
|
+
const raw = await fs2.readFile(filePath, "utf8");
|
|
134
|
+
return JSON.parse(raw);
|
|
135
|
+
}
|
|
136
|
+
async function writeJson(filePath, value) {
|
|
137
|
+
await fs2.writeFile(filePath, JSON.stringify(value, null, 2) + "\n", "utf8");
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// src/pm.ts
|
|
141
|
+
function detectPackageManager(prefer) {
|
|
142
|
+
const detected = prefer ?? detectFromUserAgent() ?? "npm";
|
|
143
|
+
return buildInfo(detected);
|
|
144
|
+
}
|
|
145
|
+
function isValidPackageManager(value) {
|
|
146
|
+
return value === "pnpm" || value === "npm" || value === "yarn";
|
|
147
|
+
}
|
|
148
|
+
function detectFromUserAgent() {
|
|
149
|
+
const ua = process.env.npm_config_user_agent;
|
|
150
|
+
if (!ua) return null;
|
|
151
|
+
const head = ua.split(" ")[0] ?? "";
|
|
152
|
+
if (head.startsWith("pnpm")) return "pnpm";
|
|
153
|
+
if (head.startsWith("yarn")) return "yarn";
|
|
154
|
+
if (head.startsWith("npm")) return "npm";
|
|
155
|
+
return null;
|
|
156
|
+
}
|
|
157
|
+
function buildInfo(name) {
|
|
158
|
+
switch (name) {
|
|
159
|
+
case "pnpm":
|
|
160
|
+
return {
|
|
161
|
+
name,
|
|
162
|
+
install: ["install"],
|
|
163
|
+
run: ["run"],
|
|
164
|
+
installCommand: "pnpm install",
|
|
165
|
+
runPrefix: "pnpm"
|
|
166
|
+
};
|
|
167
|
+
case "yarn":
|
|
168
|
+
return {
|
|
169
|
+
name,
|
|
170
|
+
install: ["install"],
|
|
171
|
+
run: [],
|
|
172
|
+
installCommand: "yarn install",
|
|
173
|
+
runPrefix: "yarn"
|
|
174
|
+
};
|
|
175
|
+
case "npm":
|
|
176
|
+
default:
|
|
177
|
+
return {
|
|
178
|
+
name,
|
|
179
|
+
install: ["install"],
|
|
180
|
+
run: ["run"],
|
|
181
|
+
installCommand: "npm install",
|
|
182
|
+
runPrefix: "npm run"
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// src/presets/minimal.ts
|
|
188
|
+
var minimalPreset = {
|
|
189
|
+
id: "minimal",
|
|
190
|
+
displayName: "Minimal",
|
|
191
|
+
description: "Vite + React + TS + Tailwind v4\uFF0C\u88C5 design tokens + AI skills\uFF0C\u65E0\u8DEF\u7531\u3001\u4E0D\u88C5 UI \u7EC4\u4EF6\u3002",
|
|
192
|
+
baseTemplate: "react-ts",
|
|
193
|
+
design: {
|
|
194
|
+
variant: "opentrek",
|
|
195
|
+
tailwind: "v4"
|
|
196
|
+
},
|
|
197
|
+
skills: {
|
|
198
|
+
entries: ["teamix-evo-manage"],
|
|
199
|
+
ides: ["qoder", "claude"]
|
|
200
|
+
},
|
|
201
|
+
ui: {
|
|
202
|
+
components: []
|
|
203
|
+
},
|
|
204
|
+
overlay: null
|
|
205
|
+
};
|
|
206
|
+
|
|
207
|
+
// src/presets/console.ts
|
|
208
|
+
var consolePreset = {
|
|
209
|
+
id: "console",
|
|
210
|
+
displayName: "Console",
|
|
211
|
+
description: "\u4E2D\u540E\u53F0\u6807\u914D\uFF1Aminimal + react-router-dom + ConsoleLayout + Dashboard / List / Detail / Form \u56DB\u9875\u9762 + \u771F\u88C5 Button\u3002",
|
|
212
|
+
baseTemplate: "react-ts",
|
|
213
|
+
design: {
|
|
214
|
+
variant: "opentrek",
|
|
215
|
+
tailwind: "v4"
|
|
216
|
+
},
|
|
217
|
+
skills: {
|
|
218
|
+
entries: ["teamix-evo-manage"],
|
|
219
|
+
ides: ["qoder", "claude"]
|
|
220
|
+
},
|
|
221
|
+
ui: {
|
|
222
|
+
components: ["button"]
|
|
223
|
+
},
|
|
224
|
+
overlay: "console"
|
|
225
|
+
};
|
|
226
|
+
|
|
227
|
+
// src/presets/index.ts
|
|
228
|
+
var presets = [minimalPreset, consolePreset];
|
|
229
|
+
function findPreset(id) {
|
|
230
|
+
return presets.find((p2) => p2.id === id);
|
|
231
|
+
}
|
|
232
|
+
function listPresetIds() {
|
|
233
|
+
return presets.map((p2) => p2.id);
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
// src/utils/logger.ts
|
|
237
|
+
import { bold, cyan, dim, green, red, yellow } from "kolorist";
|
|
238
|
+
var logger = {
|
|
239
|
+
info(message) {
|
|
240
|
+
console.log(`${cyan("\u2139")} ${message}`);
|
|
241
|
+
},
|
|
242
|
+
success(message) {
|
|
243
|
+
console.log(`${green("\u2713")} ${message}`);
|
|
244
|
+
},
|
|
245
|
+
warn(message) {
|
|
246
|
+
console.warn(`${yellow("\u26A0")} ${message}`);
|
|
247
|
+
},
|
|
248
|
+
error(message) {
|
|
249
|
+
console.error(`${red("\u2717")} ${message}`);
|
|
250
|
+
},
|
|
251
|
+
step(message) {
|
|
252
|
+
console.log(`
|
|
253
|
+
${bold(cyan("\u25B8"))} ${bold(message)}`);
|
|
254
|
+
},
|
|
255
|
+
detail(message) {
|
|
256
|
+
console.log(` ${dim(message)}`);
|
|
257
|
+
},
|
|
258
|
+
blank() {
|
|
259
|
+
console.log("");
|
|
260
|
+
}
|
|
261
|
+
};
|
|
262
|
+
|
|
263
|
+
// src/orchestrator.ts
|
|
264
|
+
function getPackageRoot() {
|
|
265
|
+
const here = path2.dirname(fileURLToPath(import.meta.url));
|
|
266
|
+
return path2.resolve(here, "..");
|
|
267
|
+
}
|
|
268
|
+
async function orchestrate(options) {
|
|
269
|
+
const preset = findPreset(options.presetId);
|
|
270
|
+
if (!preset) {
|
|
271
|
+
throw new Error(
|
|
272
|
+
`Unknown preset "${options.presetId}". Available: ${[
|
|
273
|
+
"minimal",
|
|
274
|
+
"console"
|
|
275
|
+
].join(", ")}.`
|
|
276
|
+
);
|
|
277
|
+
}
|
|
278
|
+
const dir = path2.resolve(options.dir);
|
|
279
|
+
await prepareTargetDir(dir, options.force ?? false);
|
|
280
|
+
const pmInfo = detectPackageManager(options.pm);
|
|
281
|
+
const projectName = path2.basename(dir);
|
|
282
|
+
const pkgRoot = getPackageRoot();
|
|
283
|
+
logger.step("\u62F7\u8D1D base \u6A21\u677F");
|
|
284
|
+
const baseTemplateDir = path2.join(pkgRoot, "templates", preset.baseTemplate);
|
|
285
|
+
const hbsCtx = buildHbsContext({ preset, projectName, pmInfo });
|
|
286
|
+
await copyDir(baseTemplateDir, dir, { hbs: hbsCtx });
|
|
287
|
+
logger.detail(`base: ${preset.baseTemplate}`);
|
|
288
|
+
if (preset.overlay) {
|
|
289
|
+
logger.step(`\u5E94\u7528 overlay: ${preset.overlay}`);
|
|
290
|
+
const overlayDir = path2.join(pkgRoot, "overlays", preset.overlay);
|
|
291
|
+
await copyDir(overlayDir, dir, { hbs: hbsCtx });
|
|
292
|
+
}
|
|
293
|
+
logger.step("\u88C5\u8F7D design tokens");
|
|
294
|
+
const designResult = await runDesignInit({
|
|
295
|
+
projectRoot: dir,
|
|
296
|
+
variant: preset.design.variant,
|
|
297
|
+
tailwind: preset.design.tailwind,
|
|
298
|
+
ide: "qoder"
|
|
299
|
+
});
|
|
300
|
+
if (designResult.status === "installed") {
|
|
301
|
+
logger.detail(
|
|
302
|
+
`${designResult.packageName}@${designResult.version} (${designResult.count} files)`
|
|
303
|
+
);
|
|
304
|
+
}
|
|
305
|
+
logger.step("\u88C5\u8F7D AI skills");
|
|
306
|
+
const skillsResult = await runSkillsAdd({
|
|
307
|
+
projectRoot: dir,
|
|
308
|
+
ides: preset.skills.ides,
|
|
309
|
+
scope: "project"
|
|
310
|
+
});
|
|
311
|
+
if (skillsResult.status === "installed") {
|
|
312
|
+
logger.detail(
|
|
313
|
+
`${skillsResult.skillCount} skill(s) \u2192 ${skillsResult.ides.join(" + ")}`
|
|
314
|
+
);
|
|
315
|
+
}
|
|
316
|
+
logger.step("\u914D\u7F6E UI");
|
|
317
|
+
await runUiInit({ projectRoot: dir });
|
|
318
|
+
let uiAddDeps = {};
|
|
319
|
+
if (preset.ui.components.length > 0) {
|
|
320
|
+
const uiAddResult = await runUiAdd({
|
|
321
|
+
projectRoot: dir,
|
|
322
|
+
ids: preset.ui.components
|
|
323
|
+
});
|
|
324
|
+
uiAddDeps = uiAddResult.npmDependencies;
|
|
325
|
+
logger.detail(
|
|
326
|
+
`installed: ${uiAddResult.orderedIds.join(", ")} (${uiAddResult.written} files)`
|
|
327
|
+
);
|
|
328
|
+
} else {
|
|
329
|
+
logger.detail("ui init only \u2014 \u65E0\u7EC4\u4EF6\u843D\u5730");
|
|
330
|
+
}
|
|
331
|
+
logger.step("\u5408\u5E76 package.json");
|
|
332
|
+
await mergeProjectPackageJson({
|
|
333
|
+
dir,
|
|
334
|
+
overlayFragmentPath: preset.overlay ? path2.join(
|
|
335
|
+
pkgRoot,
|
|
336
|
+
"overlays",
|
|
337
|
+
preset.overlay,
|
|
338
|
+
"package.json.fragment.json"
|
|
339
|
+
) : null,
|
|
340
|
+
extraDependencies: uiAddDeps
|
|
341
|
+
});
|
|
342
|
+
if (preset.overlay === "console") {
|
|
343
|
+
await writePendingUi(dir, preset.id);
|
|
344
|
+
logger.detail(".teamix-evo/create/pending-ui.json \u5DF2\u5199\u5165");
|
|
345
|
+
}
|
|
346
|
+
if (options.install) {
|
|
347
|
+
logger.step(`\u5B89\u88C5\u4F9D\u8D56\uFF08${pmInfo.installCommand}\uFF09`);
|
|
348
|
+
try {
|
|
349
|
+
await execa2(pmInfo.name, pmInfo.install, { cwd: dir, stdio: "inherit" });
|
|
350
|
+
} catch (err) {
|
|
351
|
+
logger.warn(
|
|
352
|
+
`\u4F9D\u8D56\u5B89\u88C5\u5931\u8D25 \u2014 \u53EF\u624B\u52A8\u6267\u884C\uFF1Acd ${path2.relative(process.cwd(), dir) || "."} && ${pmInfo.installCommand}`
|
|
353
|
+
);
|
|
354
|
+
if (err instanceof Error) logger.detail(err.message);
|
|
355
|
+
}
|
|
356
|
+
} else {
|
|
357
|
+
logger.detail(`\u8DF3\u8FC7\u4F9D\u8D56\u5B89\u88C5\uFF08\u624B\u52A8\u6267\u884C\uFF1A${pmInfo.installCommand}\uFF09`);
|
|
358
|
+
}
|
|
359
|
+
if (options.git) {
|
|
360
|
+
logger.step("\u521D\u59CB\u5316 git");
|
|
361
|
+
const result = await gitInit(dir);
|
|
362
|
+
if (result.ok) logger.detail("git \u4ED3\u5E93\u5DF2\u5C31\u7EEA\uFF08\u542B\u9996\u4E2A commit\uFF09");
|
|
363
|
+
else logger.warn(`git init \u5931\u8D25\uFF1A${result.reason}`);
|
|
364
|
+
}
|
|
365
|
+
printNextSteps({ dir, pmInfo, preset });
|
|
366
|
+
}
|
|
367
|
+
async function prepareTargetDir(dir, force) {
|
|
368
|
+
let exists = false;
|
|
369
|
+
try {
|
|
370
|
+
await fs3.access(dir);
|
|
371
|
+
exists = true;
|
|
372
|
+
} catch {
|
|
373
|
+
}
|
|
374
|
+
if (!exists) {
|
|
375
|
+
await fs3.mkdir(dir, { recursive: true });
|
|
376
|
+
return;
|
|
377
|
+
}
|
|
378
|
+
const entries = await fs3.readdir(dir);
|
|
379
|
+
if (entries.length === 0) return;
|
|
380
|
+
if (entries.includes(".teamix-evo")) {
|
|
381
|
+
throw new Error(
|
|
382
|
+
`Target directory already contains a .teamix-evo/ \u2014 please run \`teamix-evo design init\` inside it instead of using create-teamix-evo.`
|
|
383
|
+
);
|
|
384
|
+
}
|
|
385
|
+
if (!force) {
|
|
386
|
+
throw new Error(
|
|
387
|
+
`Target directory "${dir}" is not empty. Use --force to overwrite.`
|
|
388
|
+
);
|
|
389
|
+
}
|
|
390
|
+
logger.warn(`--force\uFF1A\u8986\u76D6\u5DF2\u6709\u76EE\u5F55 ${dir}`);
|
|
391
|
+
}
|
|
392
|
+
function buildHbsContext(args) {
|
|
393
|
+
const { preset, projectName, pmInfo } = args;
|
|
394
|
+
return {
|
|
395
|
+
projectName,
|
|
396
|
+
designVariant: preset.design.variant,
|
|
397
|
+
pmInstall: pmInfo.installCommand,
|
|
398
|
+
pmRun: pmInfo.runPrefix,
|
|
399
|
+
uiInstalled: preset.ui.components.length > 0 ? preset.ui.components.join(", ") : "\uFF08\u65E0\uFF09"
|
|
400
|
+
};
|
|
401
|
+
}
|
|
402
|
+
async function mergeProjectPackageJson(args) {
|
|
403
|
+
const pkgPath = path2.join(args.dir, "package.json");
|
|
404
|
+
const base = await readJson(pkgPath);
|
|
405
|
+
let overlay = null;
|
|
406
|
+
if (args.overlayFragmentPath) {
|
|
407
|
+
try {
|
|
408
|
+
overlay = await readJson(
|
|
409
|
+
args.overlayFragmentPath
|
|
410
|
+
);
|
|
411
|
+
} catch {
|
|
412
|
+
overlay = null;
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
try {
|
|
416
|
+
const merged = mergePackageJson(base, overlay, {
|
|
417
|
+
extraDependencies: args.extraDependencies
|
|
418
|
+
});
|
|
419
|
+
await writeJson(pkgPath, merged);
|
|
420
|
+
} catch (err) {
|
|
421
|
+
if (err instanceof VersionConflictError) {
|
|
422
|
+
throw new Error(
|
|
423
|
+
`package.json \u5408\u5E76\u5931\u8D25\uFF1A${err.message}
|
|
424
|
+
\u8BF7\u68C0\u67E5 base \u6A21\u677F\u4E0E overlay/ui \u7EC4\u4EF6\u4F9D\u8D56\u7684\u7248\u672C\u58F0\u660E\uFF0C\u786E\u4FDD\u4E00\u81F4\u3002`
|
|
425
|
+
);
|
|
426
|
+
}
|
|
427
|
+
throw err;
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
async function writePendingUi(dir, presetId) {
|
|
431
|
+
const pendingDir = path2.join(dir, ".teamix-evo", "create");
|
|
432
|
+
await fs3.mkdir(pendingDir, { recursive: true });
|
|
433
|
+
const pendingPath = path2.join(pendingDir, "pending-ui.json");
|
|
434
|
+
const payload = {
|
|
435
|
+
$schema: "https://teamix-evo.dev/schema/pending-ui/v1.json",
|
|
436
|
+
schemaVersion: 1,
|
|
437
|
+
preset: presetId,
|
|
438
|
+
pendingComponents: [
|
|
439
|
+
{ id: "card", placeholder: "src/components/_placeholder/Card.tsx" },
|
|
440
|
+
{ id: "input", placeholder: "src/components/_placeholder/Input.tsx" },
|
|
441
|
+
{ id: "form", placeholder: "src/components/_placeholder/Form.tsx" },
|
|
442
|
+
{ id: "table", placeholder: "src/components/_placeholder/Table.tsx" }
|
|
443
|
+
]
|
|
444
|
+
};
|
|
445
|
+
await writeJson(pendingPath, payload);
|
|
446
|
+
}
|
|
447
|
+
function printNextSteps(args) {
|
|
448
|
+
const { dir, pmInfo, preset } = args;
|
|
449
|
+
const rel = path2.relative(process.cwd(), dir) || ".";
|
|
450
|
+
logger.blank();
|
|
451
|
+
logger.success(`${preset.displayName} preset \u5DF2\u5C31\u7EEA\uFF1A${dir}`);
|
|
452
|
+
logger.blank();
|
|
453
|
+
console.log("Next steps:");
|
|
454
|
+
console.log(` cd ${rel}`);
|
|
455
|
+
console.log(
|
|
456
|
+
` ${pmInfo.runPrefix}${pmInfo.runPrefix === "yarn" ? " " : " "}dev`
|
|
457
|
+
);
|
|
458
|
+
if (preset.id === "console") {
|
|
459
|
+
logger.blank();
|
|
460
|
+
console.log(
|
|
461
|
+
"\u{1F4A1} console preset \u5185\u542B\u5360\u4F4D\u7EC4\u4EF6\uFF0C\u53EF\u7528 `npx teamix-evo ui add card` \u7B49\u66FF\u6362\u4E3A\u771F\u7EC4\u4EF6\u3002"
|
|
462
|
+
);
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
// src/prompts.ts
|
|
467
|
+
import * as p from "@clack/prompts";
|
|
468
|
+
async function promptMissing(seed) {
|
|
469
|
+
p.intro("create-teamix-evo");
|
|
470
|
+
p.note(`\u{1F4E6} Target directory: ${seed.dir}`);
|
|
471
|
+
let presetId = seed.presetId;
|
|
472
|
+
if (!presetId) {
|
|
473
|
+
const choice = await p.select({
|
|
474
|
+
message: "\u9009\u62E9 preset",
|
|
475
|
+
options: presets.map((preset) => ({
|
|
476
|
+
value: preset.id,
|
|
477
|
+
label: preset.displayName,
|
|
478
|
+
hint: preset.description
|
|
479
|
+
})),
|
|
480
|
+
initialValue: "minimal"
|
|
481
|
+
});
|
|
482
|
+
if (p.isCancel(choice)) {
|
|
483
|
+
p.cancel("\u5DF2\u53D6\u6D88");
|
|
484
|
+
process.exit(0);
|
|
485
|
+
}
|
|
486
|
+
presetId = choice;
|
|
487
|
+
}
|
|
488
|
+
let pm = seed.pm && isValidPackageManager(seed.pm) ? seed.pm : void 0;
|
|
489
|
+
if (!pm) {
|
|
490
|
+
const pmChoice = await p.select({
|
|
491
|
+
message: "\u5305\u7BA1\u7406\u5668",
|
|
492
|
+
options: [
|
|
493
|
+
{ value: "auto", label: "\u81EA\u52A8\u68C0\u6D4B\uFF08\u63A8\u8350\uFF09" },
|
|
494
|
+
{ value: "pnpm", label: "pnpm" },
|
|
495
|
+
{ value: "npm", label: "npm" },
|
|
496
|
+
{ value: "yarn", label: "yarn" }
|
|
497
|
+
],
|
|
498
|
+
initialValue: "auto"
|
|
499
|
+
});
|
|
500
|
+
if (p.isCancel(pmChoice)) {
|
|
501
|
+
p.cancel("\u5DF2\u53D6\u6D88");
|
|
502
|
+
process.exit(0);
|
|
503
|
+
}
|
|
504
|
+
pm = pmChoice === "auto" ? void 0 : isValidPackageManager(pmChoice) ? pmChoice : void 0;
|
|
505
|
+
}
|
|
506
|
+
let git = seed.git;
|
|
507
|
+
if (git === void 0) {
|
|
508
|
+
const confirmed = await p.confirm({
|
|
509
|
+
message: "\u521D\u59CB\u5316 git \u4ED3\u5E93\uFF1F",
|
|
510
|
+
initialValue: true
|
|
511
|
+
});
|
|
512
|
+
if (p.isCancel(confirmed)) {
|
|
513
|
+
p.cancel("\u5DF2\u53D6\u6D88");
|
|
514
|
+
process.exit(0);
|
|
515
|
+
}
|
|
516
|
+
git = confirmed;
|
|
517
|
+
}
|
|
518
|
+
return { presetId, pm, git };
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
// src/index.ts
|
|
522
|
+
async function main() {
|
|
523
|
+
const program = new Command();
|
|
524
|
+
program.name("create-teamix-evo").description(
|
|
525
|
+
"Scaffold a Vite + React + TypeScript project pre-wired with Teamix Evo design tokens, AI skills, and UI components."
|
|
526
|
+
).argument(
|
|
527
|
+
"[dir]",
|
|
528
|
+
"target directory (will be created if it does not exist)"
|
|
529
|
+
).option("--preset <id>", `preset id (${listPresetIds().join(" | ")})`).option("--pm <name>", "package manager: pnpm | npm | yarn").option("--no-git", "skip git init").option("--no-install", "skip dependency installation").option("--force", "overwrite non-empty target directory").helpOption("-h, --help", "display help");
|
|
530
|
+
program.parse(process.argv);
|
|
531
|
+
const opts = program.opts();
|
|
532
|
+
const dirArg = program.args[0];
|
|
533
|
+
const dir = dirArg ? path3.resolve(dirArg) : path3.resolve(process.cwd(), "my-app");
|
|
534
|
+
if (!dirArg) {
|
|
535
|
+
logger.warn(`\u672A\u6307\u5B9A\u76EE\u5F55\uFF0C\u4F7F\u7528\u9ED8\u8BA4\u8DEF\u5F84\uFF1A${dir}`);
|
|
536
|
+
}
|
|
537
|
+
if (opts.preset && !listPresetIds().includes(opts.preset)) {
|
|
538
|
+
logger.error(
|
|
539
|
+
`\u672A\u77E5 preset "${opts.preset}"\u3002\u53EF\u9009\uFF1A${listPresetIds().join(", ")}`
|
|
540
|
+
);
|
|
541
|
+
process.exit(1);
|
|
542
|
+
}
|
|
543
|
+
if (opts.pm && !isValidPackageManager(opts.pm)) {
|
|
544
|
+
logger.error(`\u672A\u77E5\u5305\u7BA1\u7406\u5668 "${opts.pm}"\u3002\u53EF\u9009\uFF1Apnpm | npm | yarn`);
|
|
545
|
+
process.exit(1);
|
|
546
|
+
}
|
|
547
|
+
let presetId = opts.preset;
|
|
548
|
+
let pm = opts.pm;
|
|
549
|
+
let git = opts.git;
|
|
550
|
+
if (process.stdin.isTTY && (!presetId || git === void 0)) {
|
|
551
|
+
const answers = await promptMissing({
|
|
552
|
+
dir,
|
|
553
|
+
presetId,
|
|
554
|
+
pm,
|
|
555
|
+
git
|
|
556
|
+
});
|
|
557
|
+
presetId = answers.presetId;
|
|
558
|
+
git = answers.git;
|
|
559
|
+
if (answers.pm) pm = answers.pm;
|
|
560
|
+
} else {
|
|
561
|
+
presetId = presetId ?? "minimal";
|
|
562
|
+
git = git ?? true;
|
|
563
|
+
}
|
|
564
|
+
try {
|
|
565
|
+
await orchestrate({
|
|
566
|
+
dir,
|
|
567
|
+
presetId,
|
|
568
|
+
pm: pm && isValidPackageManager(pm) ? pm : void 0,
|
|
569
|
+
install: opts.install ?? true,
|
|
570
|
+
git: git ?? true,
|
|
571
|
+
force: opts.force ?? false
|
|
572
|
+
});
|
|
573
|
+
} catch (err) {
|
|
574
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
575
|
+
console.error(`
|
|
576
|
+
${red2("\u2717")} ${message}`);
|
|
577
|
+
process.exit(1);
|
|
578
|
+
}
|
|
579
|
+
}
|
|
580
|
+
main().catch((err) => {
|
|
581
|
+
console.error(`
|
|
582
|
+
${red2("\u2717")} \u672A\u6355\u83B7\u7684\u9519\u8BEF`);
|
|
583
|
+
console.error(err);
|
|
584
|
+
process.exit(1);
|
|
585
|
+
});
|
|
586
|
+
//# sourceMappingURL=index.js.map
|