teamix-evo 0.1.0 → 0.2.0
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 +196 -0
- package/dist/core/index.d.ts +364 -0
- package/dist/core/index.js +1166 -0
- package/dist/core/index.js.map +1 -0
- package/dist/index.js +1655 -261
- package/dist/index.js.map +1 -1
- package/package.json +28 -13
package/dist/index.js
CHANGED
|
@@ -1,59 +1,138 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
2
|
+
|
|
3
|
+
// src/index.ts
|
|
4
|
+
import { Command as Command15 } from "commander";
|
|
5
|
+
import { createRequire as createRequire4 } from "module";
|
|
6
|
+
|
|
7
|
+
// src/commands/design/index.ts
|
|
8
|
+
import { Command as Command5 } from "commander";
|
|
9
|
+
|
|
10
|
+
// src/commands/design/init.ts
|
|
11
|
+
import { Command } from "commander";
|
|
12
|
+
|
|
13
|
+
// src/ide/QoderAdapter.ts
|
|
14
|
+
import * as os from "os";
|
|
15
|
+
import * as path from "path";
|
|
16
|
+
var QoderAdapter = class {
|
|
17
|
+
kind = "qoder";
|
|
18
|
+
name = "qoder";
|
|
19
|
+
getProjectRoot() {
|
|
20
|
+
return process.cwd();
|
|
21
|
+
}
|
|
22
|
+
detectIde() {
|
|
23
|
+
return true;
|
|
24
|
+
}
|
|
25
|
+
getSkillTargetDir(skillName, scope, projectRoot) {
|
|
26
|
+
const base = scope === "global" ? path.join(os.homedir(), ".qoder") : path.join(projectRoot ?? this.getProjectRoot(), ".qoder");
|
|
27
|
+
return path.join(base, "skills", skillName);
|
|
28
|
+
}
|
|
6
29
|
};
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
30
|
+
|
|
31
|
+
// src/ide/ClaudeAdapter.ts
|
|
32
|
+
import * as os2 from "os";
|
|
33
|
+
import * as path2 from "path";
|
|
34
|
+
var ClaudeAdapter = class {
|
|
35
|
+
kind = "claude";
|
|
36
|
+
name = "claude";
|
|
37
|
+
getProjectRoot() {
|
|
38
|
+
return process.cwd();
|
|
39
|
+
}
|
|
40
|
+
detectIde() {
|
|
41
|
+
return Boolean(process.env.CLAUDECODE);
|
|
42
|
+
}
|
|
43
|
+
getSkillTargetDir(skillName, scope, projectRoot) {
|
|
44
|
+
const base = scope === "global" ? path2.join(os2.homedir(), ".claude") : path2.join(projectRoot ?? this.getProjectRoot(), ".claude");
|
|
45
|
+
return path2.join(base, "skills", skillName);
|
|
46
|
+
}
|
|
10
47
|
};
|
|
11
48
|
|
|
49
|
+
// src/ide/index.ts
|
|
50
|
+
var ALL_IDE_KINDS = ["qoder", "claude"];
|
|
51
|
+
function getAdapter(kind) {
|
|
52
|
+
switch (kind) {
|
|
53
|
+
case "qoder":
|
|
54
|
+
return new QoderAdapter();
|
|
55
|
+
case "claude":
|
|
56
|
+
return new ClaudeAdapter();
|
|
57
|
+
default: {
|
|
58
|
+
const _exhaustive = kind;
|
|
59
|
+
throw new Error(`Unsupported IDE kind: ${_exhaustive}`);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
function detectIde() {
|
|
64
|
+
const claude = new ClaudeAdapter();
|
|
65
|
+
if (claude.detectIde()) return claude;
|
|
66
|
+
return new QoderAdapter();
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// src/core/registry-client.ts
|
|
70
|
+
import * as path3 from "path";
|
|
71
|
+
import * as fs from "fs/promises";
|
|
72
|
+
import { createRequire } from "module";
|
|
73
|
+
import { loadVariantManifest } from "@teamix-evo/registry";
|
|
74
|
+
|
|
12
75
|
// src/utils/logger.ts
|
|
13
76
|
import { red, yellow, cyan, green, gray } from "kolorist";
|
|
14
|
-
var isDebug
|
|
15
|
-
var
|
|
16
|
-
|
|
17
|
-
"
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
debug(msg) {
|
|
33
|
-
if (isDebug) {
|
|
34
|
-
console.log(gray("\u22A1"), gray(msg));
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
};
|
|
77
|
+
var isDebug = process.env.TEAMIX_DEBUG === "1";
|
|
78
|
+
var logger = {
|
|
79
|
+
info(msg) {
|
|
80
|
+
console.log(cyan("\u2139"), msg);
|
|
81
|
+
},
|
|
82
|
+
warn(msg) {
|
|
83
|
+
console.warn(yellow("\u26A0"), msg);
|
|
84
|
+
},
|
|
85
|
+
error(msg) {
|
|
86
|
+
console.error(red("\u2716"), msg);
|
|
87
|
+
},
|
|
88
|
+
success(msg) {
|
|
89
|
+
console.log(green("\u2714"), msg);
|
|
90
|
+
},
|
|
91
|
+
debug(msg) {
|
|
92
|
+
if (isDebug) {
|
|
93
|
+
console.log(gray("\u22A1"), gray(msg));
|
|
94
|
+
}
|
|
38
95
|
}
|
|
39
|
-
}
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
// src/core/registry-client.ts
|
|
99
|
+
var require2 = createRequire(import.meta.url);
|
|
100
|
+
function resolvePackageRoot(packageName) {
|
|
101
|
+
const pkgJsonPath = require2.resolve(`${packageName}/package.json`);
|
|
102
|
+
return path3.dirname(pkgJsonPath);
|
|
103
|
+
}
|
|
104
|
+
async function loadVariantData(packageName, variant) {
|
|
105
|
+
const packageRoot = resolvePackageRoot(packageName);
|
|
106
|
+
const variantDir = path3.join(packageRoot, "library", variant);
|
|
107
|
+
logger.debug(`Resolved variant dir: ${variantDir}`);
|
|
108
|
+
logger.debug(`Package root: ${packageRoot}`);
|
|
109
|
+
const manifest = await loadVariantManifest(variantDir);
|
|
110
|
+
let data = {};
|
|
111
|
+
const dataPath = path3.join(variantDir, "_data.json");
|
|
112
|
+
try {
|
|
113
|
+
const raw = await fs.readFile(dataPath, "utf-8");
|
|
114
|
+
data = JSON.parse(raw);
|
|
115
|
+
} catch (err) {
|
|
116
|
+
if (err.code !== "ENOENT") {
|
|
117
|
+
throw err;
|
|
118
|
+
}
|
|
119
|
+
logger.debug(`No _data.json found at ${dataPath}, using empty data`);
|
|
120
|
+
}
|
|
121
|
+
return { manifest, data, variantDir, packageRoot };
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// src/core/installer.ts
|
|
125
|
+
import * as path6 from "path";
|
|
126
|
+
import * as fs5 from "fs/promises";
|
|
40
127
|
|
|
41
128
|
// src/utils/fs.ts
|
|
42
|
-
var fs_exports = {};
|
|
43
|
-
__export(fs_exports, {
|
|
44
|
-
backupFile: () => backupFile,
|
|
45
|
-
ensureDir: () => ensureDir,
|
|
46
|
-
fileExists: () => fileExists,
|
|
47
|
-
readFileOrNull: () => readFileOrNull,
|
|
48
|
-
writeFileSafe: () => writeFileSafe
|
|
49
|
-
});
|
|
50
129
|
import * as fs2 from "fs/promises";
|
|
51
|
-
import * as
|
|
130
|
+
import * as path4 from "path";
|
|
52
131
|
async function ensureDir(dir) {
|
|
53
132
|
await fs2.mkdir(dir, { recursive: true });
|
|
54
133
|
}
|
|
55
134
|
async function writeFileSafe(filePath, content) {
|
|
56
|
-
const dir =
|
|
135
|
+
const dir = path4.dirname(filePath);
|
|
57
136
|
await ensureDir(dir);
|
|
58
137
|
const tmp = filePath + ".tmp";
|
|
59
138
|
await fs2.writeFile(tmp, content, "utf-8");
|
|
@@ -75,17 +154,17 @@ async function backupFile(filePath, projectRoot) {
|
|
|
75
154
|
logger.debug(`Skip backup: ${filePath} does not exist`);
|
|
76
155
|
return;
|
|
77
156
|
}
|
|
78
|
-
const
|
|
157
|
+
const rel2 = path4.relative(projectRoot, filePath);
|
|
79
158
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
80
|
-
const backupPath =
|
|
159
|
+
const backupPath = path4.join(
|
|
81
160
|
projectRoot,
|
|
82
161
|
".teamix-evo",
|
|
83
162
|
".backups",
|
|
84
|
-
`${
|
|
163
|
+
`${rel2}.${timestamp}.bak`
|
|
85
164
|
);
|
|
86
|
-
await ensureDir(
|
|
165
|
+
await ensureDir(path4.dirname(backupPath));
|
|
87
166
|
await fs2.writeFile(backupPath, content, "utf-8");
|
|
88
|
-
logger.debug(`Backed up ${
|
|
167
|
+
logger.debug(`Backed up ${rel2} \u2192 ${path4.relative(projectRoot, backupPath)}`);
|
|
89
168
|
}
|
|
90
169
|
async function fileExists(filePath) {
|
|
91
170
|
try {
|
|
@@ -95,76 +174,6 @@ async function fileExists(filePath) {
|
|
|
95
174
|
return false;
|
|
96
175
|
}
|
|
97
176
|
}
|
|
98
|
-
var init_fs = __esm({
|
|
99
|
-
"src/utils/fs.ts"() {
|
|
100
|
-
"use strict";
|
|
101
|
-
init_logger();
|
|
102
|
-
}
|
|
103
|
-
});
|
|
104
|
-
|
|
105
|
-
// src/index.ts
|
|
106
|
-
import { Command as Command5 } from "commander";
|
|
107
|
-
|
|
108
|
-
// src/commands/design/index.ts
|
|
109
|
-
import { Command as Command4 } from "commander";
|
|
110
|
-
|
|
111
|
-
// src/commands/design/init.ts
|
|
112
|
-
import { Command } from "commander";
|
|
113
|
-
|
|
114
|
-
// src/ide/QoderAdapter.ts
|
|
115
|
-
var QoderAdapter = class {
|
|
116
|
-
name = "qoder";
|
|
117
|
-
getProjectRoot() {
|
|
118
|
-
return process.cwd();
|
|
119
|
-
}
|
|
120
|
-
detectIde() {
|
|
121
|
-
return true;
|
|
122
|
-
}
|
|
123
|
-
};
|
|
124
|
-
|
|
125
|
-
// src/ide/index.ts
|
|
126
|
-
function detectIde() {
|
|
127
|
-
return new QoderAdapter();
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
// src/core/registry-client.ts
|
|
131
|
-
init_logger();
|
|
132
|
-
import * as path from "path";
|
|
133
|
-
import * as fs from "fs/promises";
|
|
134
|
-
import { createRequire } from "module";
|
|
135
|
-
import { loadVariantManifest } from "@teamix-evo/registry";
|
|
136
|
-
function resolveVariantPackage(packageName, variant) {
|
|
137
|
-
const require2 = createRequire(import.meta.url);
|
|
138
|
-
const pkgJsonPath = require2.resolve(`${packageName}/package.json`);
|
|
139
|
-
const pkgRoot = path.dirname(pkgJsonPath);
|
|
140
|
-
return path.join(pkgRoot, "library", variant);
|
|
141
|
-
}
|
|
142
|
-
async function loadVariantData(packageName, variant) {
|
|
143
|
-
const variantDir = resolveVariantPackage(packageName, variant);
|
|
144
|
-
const require2 = createRequire(import.meta.url);
|
|
145
|
-
const pkgJsonPath = require2.resolve(`${packageName}/package.json`);
|
|
146
|
-
const packageRoot = path.dirname(pkgJsonPath);
|
|
147
|
-
logger.debug(`Resolved variant dir: ${variantDir}`);
|
|
148
|
-
logger.debug(`Package root: ${packageRoot}`);
|
|
149
|
-
const manifest = await loadVariantManifest(variantDir);
|
|
150
|
-
let data = {};
|
|
151
|
-
const dataPath = path.join(variantDir, "_data.json");
|
|
152
|
-
try {
|
|
153
|
-
const raw = await fs.readFile(dataPath, "utf-8");
|
|
154
|
-
data = JSON.parse(raw);
|
|
155
|
-
} catch (err) {
|
|
156
|
-
if (err.code !== "ENOENT") {
|
|
157
|
-
throw err;
|
|
158
|
-
}
|
|
159
|
-
logger.debug(`No _data.json found at ${dataPath}, using empty data`);
|
|
160
|
-
}
|
|
161
|
-
return { manifest, data, variantDir, packageRoot };
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
// src/core/installer.ts
|
|
165
|
-
init_fs();
|
|
166
|
-
import * as path3 from "path";
|
|
167
|
-
import * as fs4 from "fs/promises";
|
|
168
177
|
|
|
169
178
|
// src/utils/hash.ts
|
|
170
179
|
import { createHash } from "crypto";
|
|
@@ -179,16 +188,52 @@ import * as fs3 from "fs/promises";
|
|
|
179
188
|
Handlebars.registerHelper("lowercase", (str) => {
|
|
180
189
|
return typeof str === "string" ? str.toLowerCase() : String(str ?? "").toLowerCase();
|
|
181
190
|
});
|
|
191
|
+
var compiledCache = /* @__PURE__ */ new Map();
|
|
192
|
+
var MAX_CACHE_SIZE = 64;
|
|
193
|
+
function getCompiledTemplate(templateContent) {
|
|
194
|
+
let compiled = compiledCache.get(templateContent);
|
|
195
|
+
if (!compiled) {
|
|
196
|
+
if (compiledCache.size >= MAX_CACHE_SIZE) {
|
|
197
|
+
const firstKey = compiledCache.keys().next().value;
|
|
198
|
+
compiledCache.delete(firstKey);
|
|
199
|
+
}
|
|
200
|
+
compiled = Handlebars.compile(templateContent, { noEscape: true });
|
|
201
|
+
compiledCache.set(templateContent, compiled);
|
|
202
|
+
}
|
|
203
|
+
return compiled;
|
|
204
|
+
}
|
|
182
205
|
function renderTemplate(templateContent, data) {
|
|
183
|
-
const compiled =
|
|
206
|
+
const compiled = getCompiledTemplate(templateContent);
|
|
184
207
|
return compiled(data);
|
|
185
208
|
}
|
|
186
209
|
async function loadTemplateFile(filePath) {
|
|
187
210
|
return fs3.readFile(filePath, "utf-8");
|
|
188
211
|
}
|
|
189
212
|
|
|
213
|
+
// src/utils/path.ts
|
|
214
|
+
import * as path5 from "path";
|
|
215
|
+
import * as fs4 from "fs/promises";
|
|
216
|
+
function resolveSourcePath(source, variantDir, packageRoot) {
|
|
217
|
+
if (source.startsWith("_template/")) {
|
|
218
|
+
return path5.join(packageRoot, source);
|
|
219
|
+
}
|
|
220
|
+
return path5.join(variantDir, source);
|
|
221
|
+
}
|
|
222
|
+
async function walkDir(dir) {
|
|
223
|
+
const files = [];
|
|
224
|
+
const entries = await fs4.readdir(dir, { withFileTypes: true });
|
|
225
|
+
for (const entry of entries) {
|
|
226
|
+
const fullPath = path5.join(dir, entry.name);
|
|
227
|
+
if (entry.isDirectory()) {
|
|
228
|
+
files.push(...await walkDir(fullPath));
|
|
229
|
+
} else if (entry.isFile()) {
|
|
230
|
+
files.push(fullPath);
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
return files;
|
|
234
|
+
}
|
|
235
|
+
|
|
190
236
|
// src/core/installer.ts
|
|
191
|
-
init_logger();
|
|
192
237
|
async function installResources(options) {
|
|
193
238
|
const { projectRoot, manifest, data, variantDir, packageRoot } = options;
|
|
194
239
|
const installedResources = [];
|
|
@@ -220,14 +265,18 @@ async function installResources(options) {
|
|
|
220
265
|
};
|
|
221
266
|
}
|
|
222
267
|
async function installSingleResource(resource, projectRoot, data, variantDir, packageRoot) {
|
|
223
|
-
const sourcePath = resolveSourcePath(
|
|
224
|
-
|
|
268
|
+
const sourcePath = resolveSourcePath(
|
|
269
|
+
resource.source,
|
|
270
|
+
variantDir,
|
|
271
|
+
packageRoot
|
|
272
|
+
);
|
|
273
|
+
const targetPath = path6.join(projectRoot, resource.target);
|
|
225
274
|
let content;
|
|
226
275
|
if (resource.template) {
|
|
227
276
|
const templateContent = await loadTemplateFile(sourcePath);
|
|
228
277
|
content = renderTemplate(templateContent, data);
|
|
229
278
|
} else {
|
|
230
|
-
content = await
|
|
279
|
+
content = await fs5.readFile(sourcePath, "utf-8");
|
|
231
280
|
}
|
|
232
281
|
await writeFileSafe(targetPath, content);
|
|
233
282
|
const hash = computeHash(content);
|
|
@@ -240,14 +289,18 @@ async function installSingleResource(resource, projectRoot, data, variantDir, pa
|
|
|
240
289
|
};
|
|
241
290
|
}
|
|
242
291
|
async function installRecursiveResource(resource, projectRoot, data, variantDir, packageRoot) {
|
|
243
|
-
const sourcePath = resolveSourcePath(
|
|
244
|
-
|
|
292
|
+
const sourcePath = resolveSourcePath(
|
|
293
|
+
resource.source,
|
|
294
|
+
variantDir,
|
|
295
|
+
packageRoot
|
|
296
|
+
);
|
|
297
|
+
const targetDir = path6.join(projectRoot, resource.target);
|
|
245
298
|
const results = [];
|
|
246
299
|
await ensureDir(targetDir);
|
|
247
300
|
const entries = await walkDir(sourcePath);
|
|
248
301
|
for (const entry of entries) {
|
|
249
|
-
const relPath =
|
|
250
|
-
let targetFile =
|
|
302
|
+
const relPath = path6.relative(sourcePath, entry);
|
|
303
|
+
let targetFile = path6.join(targetDir, relPath);
|
|
251
304
|
if (resource.template && targetFile.endsWith(".hbs")) {
|
|
252
305
|
targetFile = targetFile.slice(0, -4);
|
|
253
306
|
}
|
|
@@ -256,11 +309,11 @@ async function installRecursiveResource(resource, projectRoot, data, variantDir,
|
|
|
256
309
|
const templateContent = await loadTemplateFile(entry);
|
|
257
310
|
content = renderTemplate(templateContent, data);
|
|
258
311
|
} else {
|
|
259
|
-
content = await
|
|
312
|
+
content = await fs5.readFile(entry, "utf-8");
|
|
260
313
|
}
|
|
261
314
|
await writeFileSafe(targetFile, content);
|
|
262
315
|
const hash = computeHash(content);
|
|
263
|
-
const targetRel =
|
|
316
|
+
const targetRel = path6.relative(projectRoot, targetFile);
|
|
264
317
|
results.push({
|
|
265
318
|
id: `${resource.id}:${relPath}`,
|
|
266
319
|
target: targetRel,
|
|
@@ -271,36 +324,15 @@ async function installRecursiveResource(resource, projectRoot, data, variantDir,
|
|
|
271
324
|
}
|
|
272
325
|
return results;
|
|
273
326
|
}
|
|
274
|
-
function resolveSourcePath(source, variantDir, packageRoot) {
|
|
275
|
-
if (source.startsWith("_template/")) {
|
|
276
|
-
return path3.join(packageRoot, source);
|
|
277
|
-
}
|
|
278
|
-
return path3.join(variantDir, source);
|
|
279
|
-
}
|
|
280
|
-
async function walkDir(dir) {
|
|
281
|
-
const files = [];
|
|
282
|
-
const entries = await fs4.readdir(dir, { withFileTypes: true });
|
|
283
|
-
for (const entry of entries) {
|
|
284
|
-
const fullPath = path3.join(dir, entry.name);
|
|
285
|
-
if (entry.isDirectory()) {
|
|
286
|
-
files.push(...await walkDir(fullPath));
|
|
287
|
-
} else if (entry.isFile()) {
|
|
288
|
-
files.push(fullPath);
|
|
289
|
-
}
|
|
290
|
-
}
|
|
291
|
-
return files;
|
|
292
|
-
}
|
|
293
327
|
|
|
294
328
|
// src/core/state.ts
|
|
295
|
-
|
|
296
|
-
init_logger();
|
|
297
|
-
import * as path4 from "path";
|
|
329
|
+
import * as path7 from "path";
|
|
298
330
|
import { validateConfig, validateInstalled } from "@teamix-evo/registry";
|
|
299
331
|
var TEAMIX_DIR = ".teamix-evo";
|
|
300
332
|
var CONFIG_FILE = "config.json";
|
|
301
333
|
var MANIFEST_FILE = "manifest.json";
|
|
302
334
|
function getTeamixDir(projectRoot) {
|
|
303
|
-
return
|
|
335
|
+
return path7.join(projectRoot, TEAMIX_DIR);
|
|
304
336
|
}
|
|
305
337
|
async function ensureTeamixDir(projectRoot) {
|
|
306
338
|
const dir = getTeamixDir(projectRoot);
|
|
@@ -308,7 +340,7 @@ async function ensureTeamixDir(projectRoot) {
|
|
|
308
340
|
return dir;
|
|
309
341
|
}
|
|
310
342
|
async function readProjectConfig(projectRoot) {
|
|
311
|
-
const configPath =
|
|
343
|
+
const configPath = path7.join(projectRoot, TEAMIX_DIR, CONFIG_FILE);
|
|
312
344
|
const raw = await readFileOrNull(configPath);
|
|
313
345
|
if (raw === null) return null;
|
|
314
346
|
try {
|
|
@@ -325,12 +357,12 @@ async function readProjectConfig(projectRoot) {
|
|
|
325
357
|
}
|
|
326
358
|
}
|
|
327
359
|
async function writeProjectConfig(projectRoot, config) {
|
|
328
|
-
const configPath =
|
|
360
|
+
const configPath = path7.join(projectRoot, TEAMIX_DIR, CONFIG_FILE);
|
|
329
361
|
await writeFileSafe(configPath, JSON.stringify(config, null, 2) + "\n");
|
|
330
362
|
logger.debug(`Wrote config \u2192 ${configPath}`);
|
|
331
363
|
}
|
|
332
364
|
async function readInstalledManifest(projectRoot) {
|
|
333
|
-
const manifestPath =
|
|
365
|
+
const manifestPath = path7.join(projectRoot, TEAMIX_DIR, MANIFEST_FILE);
|
|
334
366
|
const raw = await readFileOrNull(manifestPath);
|
|
335
367
|
if (raw === null) return null;
|
|
336
368
|
try {
|
|
@@ -347,73 +379,117 @@ async function readInstalledManifest(projectRoot) {
|
|
|
347
379
|
}
|
|
348
380
|
}
|
|
349
381
|
async function writeInstalledManifest(projectRoot, manifest) {
|
|
350
|
-
const manifestPath =
|
|
382
|
+
const manifestPath = path7.join(projectRoot, TEAMIX_DIR, MANIFEST_FILE);
|
|
351
383
|
await writeFileSafe(manifestPath, JSON.stringify(manifest, null, 2) + "\n");
|
|
352
384
|
logger.debug(`Wrote manifest \u2192 ${manifestPath}`);
|
|
353
385
|
}
|
|
354
386
|
|
|
387
|
+
// src/core/design-init.ts
|
|
388
|
+
var DEFAULT_DESIGN_PACKAGE = "@teamix-evo/design";
|
|
389
|
+
async function runDesignInit(options) {
|
|
390
|
+
const { projectRoot, variant, tailwind, ide } = options;
|
|
391
|
+
const packageName = options.packageName ?? DEFAULT_DESIGN_PACKAGE;
|
|
392
|
+
await ensureTeamixDir(projectRoot);
|
|
393
|
+
const existingConfig = await readProjectConfig(projectRoot);
|
|
394
|
+
if (existingConfig?.packages?.design) {
|
|
395
|
+
return {
|
|
396
|
+
status: "already-initialized",
|
|
397
|
+
existingVariant: existingConfig.packages.design.variant
|
|
398
|
+
};
|
|
399
|
+
}
|
|
400
|
+
const { manifest, data, variantDir, packageRoot } = await loadVariantData(
|
|
401
|
+
packageName,
|
|
402
|
+
variant
|
|
403
|
+
);
|
|
404
|
+
const result = await installResources({
|
|
405
|
+
projectRoot,
|
|
406
|
+
manifest,
|
|
407
|
+
data,
|
|
408
|
+
variantDir,
|
|
409
|
+
packageRoot
|
|
410
|
+
});
|
|
411
|
+
const config = {
|
|
412
|
+
$schema: "https://teamix-evo.dev/schema/config/v1.json",
|
|
413
|
+
schemaVersion: 1,
|
|
414
|
+
ide,
|
|
415
|
+
packages: {
|
|
416
|
+
design: {
|
|
417
|
+
variant,
|
|
418
|
+
version: manifest.version,
|
|
419
|
+
tailwind
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
};
|
|
423
|
+
await writeProjectConfig(projectRoot, config);
|
|
424
|
+
const installedManifest = {
|
|
425
|
+
schemaVersion: 1,
|
|
426
|
+
installed: [
|
|
427
|
+
{
|
|
428
|
+
package: packageName,
|
|
429
|
+
variant,
|
|
430
|
+
version: manifest.version,
|
|
431
|
+
installedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
432
|
+
resources: result.resources
|
|
433
|
+
}
|
|
434
|
+
]
|
|
435
|
+
};
|
|
436
|
+
await writeInstalledManifest(projectRoot, installedManifest);
|
|
437
|
+
return {
|
|
438
|
+
status: "installed",
|
|
439
|
+
packageName,
|
|
440
|
+
variant,
|
|
441
|
+
version: manifest.version,
|
|
442
|
+
tailwind,
|
|
443
|
+
count: result.count,
|
|
444
|
+
resources: result.resources
|
|
445
|
+
};
|
|
446
|
+
}
|
|
447
|
+
|
|
355
448
|
// src/commands/design/init.ts
|
|
356
|
-
init_logger();
|
|
357
449
|
var DEFAULT_VARIANT = "opentrek";
|
|
358
|
-
var
|
|
359
|
-
|
|
450
|
+
var DEFAULT_TAILWIND = "v4";
|
|
451
|
+
function normalizeTailwind(input) {
|
|
452
|
+
if (input === void 0) return DEFAULT_TAILWIND;
|
|
453
|
+
const lower = input.toLowerCase();
|
|
454
|
+
if (lower === "v3" || lower === "3") return "v3";
|
|
455
|
+
if (lower === "v4" || lower === "4") return "v4";
|
|
456
|
+
throw new Error(
|
|
457
|
+
`Invalid --tailwind value: "${input}". Expected "v3" or "v4".`
|
|
458
|
+
);
|
|
459
|
+
}
|
|
460
|
+
var initCommand = new Command("init").description("\u521D\u59CB\u5316\u8BBE\u8BA1\u4F53\u7CFB\u8D44\u6E90").argument("[variant]", "\u8BBE\u8BA1\u53D8\u4F53\u540D\u79F0", DEFAULT_VARIANT).option(
|
|
461
|
+
"--tailwind <version>",
|
|
462
|
+
"\u9879\u76EE\u4F7F\u7528\u7684 Tailwind CSS \u4E3B\u7248\u672C\uFF08v3 | v4\uFF09",
|
|
463
|
+
DEFAULT_TAILWIND
|
|
464
|
+
).action(async (variant, opts) => {
|
|
360
465
|
try {
|
|
466
|
+
const tailwind = normalizeTailwind(opts.tailwind);
|
|
361
467
|
const ide = detectIde();
|
|
362
468
|
const projectRoot = ide.getProjectRoot();
|
|
363
|
-
logger.info(
|
|
469
|
+
logger.info(
|
|
470
|
+
`Initializing design system: variant="${variant}", tailwind="${tailwind}"`
|
|
471
|
+
);
|
|
364
472
|
logger.debug(`Project root: ${projectRoot}`);
|
|
365
473
|
logger.debug(`IDE: ${ide.name}`);
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
474
|
+
logger.info(`Loading variant "${variant}"...`);
|
|
475
|
+
logger.info("Installing resources...");
|
|
476
|
+
const result = await runDesignInit({
|
|
477
|
+
projectRoot,
|
|
478
|
+
variant,
|
|
479
|
+
tailwind,
|
|
480
|
+
ide: ide.name
|
|
481
|
+
});
|
|
482
|
+
if (result.status === "already-initialized") {
|
|
369
483
|
logger.warn(
|
|
370
|
-
`Design system already initialized (variant: ${
|
|
484
|
+
`Design system already initialized (variant: ${result.existingVariant}). Use "teamix-evo design update" to update.`
|
|
371
485
|
);
|
|
372
486
|
return;
|
|
373
487
|
}
|
|
374
|
-
logger.info(`Loading variant "${variant}" from ${DESIGN_PACKAGE}...`);
|
|
375
|
-
const { manifest, data, variantDir, packageRoot } = await loadVariantData(DESIGN_PACKAGE, variant);
|
|
376
|
-
logger.debug(
|
|
377
|
-
`Loaded manifest: ${manifest.displayName} v${manifest.version}`
|
|
378
|
-
);
|
|
379
|
-
logger.debug(`Resources: ${manifest.resources.length}`);
|
|
380
|
-
logger.info("Installing resources...");
|
|
381
|
-
const result = await installResources({
|
|
382
|
-
projectRoot,
|
|
383
|
-
manifest,
|
|
384
|
-
data,
|
|
385
|
-
variantDir,
|
|
386
|
-
packageRoot
|
|
387
|
-
});
|
|
388
|
-
const config = {
|
|
389
|
-
$schema: "https://teamix-evo.dev/schema/config/v1.json",
|
|
390
|
-
schemaVersion: 1,
|
|
391
|
-
ide: ide.name,
|
|
392
|
-
packages: {
|
|
393
|
-
design: {
|
|
394
|
-
variant,
|
|
395
|
-
version: manifest.version
|
|
396
|
-
}
|
|
397
|
-
}
|
|
398
|
-
};
|
|
399
|
-
await writeProjectConfig(projectRoot, config);
|
|
400
|
-
const installedManifest = {
|
|
401
|
-
schemaVersion: 1,
|
|
402
|
-
installed: [
|
|
403
|
-
{
|
|
404
|
-
package: DESIGN_PACKAGE,
|
|
405
|
-
variant,
|
|
406
|
-
version: manifest.version,
|
|
407
|
-
installedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
408
|
-
resources: result.resources
|
|
409
|
-
}
|
|
410
|
-
]
|
|
411
|
-
};
|
|
412
|
-
await writeInstalledManifest(projectRoot, installedManifest);
|
|
413
488
|
logger.success(
|
|
414
|
-
`Design system initialized: ${
|
|
489
|
+
`Design system initialized: ${result.packageName} v${result.version}`
|
|
415
490
|
);
|
|
416
|
-
logger.info(` Variant:
|
|
491
|
+
logger.info(` Variant: ${result.variant}`);
|
|
492
|
+
logger.info(` Tailwind: ${result.tailwind}`);
|
|
417
493
|
logger.info(` Resources: ${result.count} files installed`);
|
|
418
494
|
logger.info("");
|
|
419
495
|
logger.info('Run "teamix-evo design update" to update resources later.');
|
|
@@ -428,14 +504,9 @@ var initCommand = new Command("init").description("\u521D\u59CB\u5316\u8BBE\u8BA
|
|
|
428
504
|
import { Command as Command2 } from "commander";
|
|
429
505
|
|
|
430
506
|
// src/core/updater.ts
|
|
431
|
-
|
|
432
|
-
import * as
|
|
433
|
-
import
|
|
434
|
-
import {
|
|
435
|
-
getUpdateAction,
|
|
436
|
-
replaceManagedRegion
|
|
437
|
-
} from "@teamix-evo/registry";
|
|
438
|
-
init_logger();
|
|
507
|
+
import * as path8 from "path";
|
|
508
|
+
import * as fs6 from "fs/promises";
|
|
509
|
+
import { getUpdateAction, replaceManagedRegion } from "@teamix-evo/registry";
|
|
439
510
|
async function updateResources(options) {
|
|
440
511
|
const {
|
|
441
512
|
projectRoot,
|
|
@@ -485,16 +556,20 @@ async function updateResources(options) {
|
|
|
485
556
|
return { resources: updatedResources, summary };
|
|
486
557
|
}
|
|
487
558
|
async function updateSingleResource(resource, projectRoot, data, variantDir, packageRoot, installedMap, summary) {
|
|
488
|
-
const targetPath =
|
|
559
|
+
const targetPath = path8.join(projectRoot, resource.target);
|
|
489
560
|
const exists = await fileExists(targetPath);
|
|
490
561
|
const installed = installedMap.get(resource.id);
|
|
491
|
-
const sourcePath =
|
|
562
|
+
const sourcePath = resolveSourcePath(
|
|
563
|
+
resource.source,
|
|
564
|
+
variantDir,
|
|
565
|
+
packageRoot
|
|
566
|
+
);
|
|
492
567
|
let newContent;
|
|
493
568
|
if (resource.template) {
|
|
494
569
|
const templateContent = await loadTemplateFile(sourcePath);
|
|
495
570
|
newContent = renderTemplate(templateContent, data);
|
|
496
571
|
} else {
|
|
497
|
-
newContent = await
|
|
572
|
+
newContent = await fs6.readFile(sourcePath, "utf-8");
|
|
498
573
|
}
|
|
499
574
|
const newHash = computeHash(newContent);
|
|
500
575
|
const action = getUpdateAction(resource.updateStrategy, {
|
|
@@ -545,7 +620,11 @@ async function updateSingleResource(resource, projectRoot, data, variantDir, pac
|
|
|
545
620
|
const regionIds = resource.managedRegions ?? [];
|
|
546
621
|
for (const regionId of regionIds) {
|
|
547
622
|
const regionPattern = new RegExp(
|
|
548
|
-
`<!-- teamix-evo:managed:start id="${escapeRegExp(
|
|
623
|
+
`<!-- teamix-evo:managed:start id="${escapeRegExp(
|
|
624
|
+
regionId
|
|
625
|
+
)}" -->([\\s\\S]*?)<!-- teamix-evo:managed:end id="${escapeRegExp(
|
|
626
|
+
regionId
|
|
627
|
+
)}" -->`
|
|
549
628
|
);
|
|
550
629
|
const match = newContent.match(regionPattern);
|
|
551
630
|
if (match) {
|
|
@@ -584,8 +663,12 @@ async function updateSingleResource(resource, projectRoot, data, variantDir, pac
|
|
|
584
663
|
}
|
|
585
664
|
}
|
|
586
665
|
async function updateRecursiveResource(resource, projectRoot, data, variantDir, packageRoot, installedMap, summary) {
|
|
587
|
-
const sourcePath =
|
|
588
|
-
|
|
666
|
+
const sourcePath = resolveSourcePath(
|
|
667
|
+
resource.source,
|
|
668
|
+
variantDir,
|
|
669
|
+
packageRoot
|
|
670
|
+
);
|
|
671
|
+
const targetDir = path8.join(projectRoot, resource.target);
|
|
589
672
|
const results = [];
|
|
590
673
|
if (resource.updateStrategy === "frozen") {
|
|
591
674
|
const anyInstalled = [...installedMap.keys()].some(
|
|
@@ -601,12 +684,11 @@ async function updateRecursiveResource(resource, projectRoot, data, variantDir,
|
|
|
601
684
|
return results;
|
|
602
685
|
}
|
|
603
686
|
}
|
|
604
|
-
|
|
605
|
-
await
|
|
606
|
-
const entries = await walkDir2(sourcePath);
|
|
687
|
+
await ensureDir(targetDir);
|
|
688
|
+
const entries = await walkDir(sourcePath);
|
|
607
689
|
for (const entry of entries) {
|
|
608
|
-
const relPath =
|
|
609
|
-
let targetFile =
|
|
690
|
+
const relPath = path8.relative(sourcePath, entry);
|
|
691
|
+
let targetFile = path8.join(targetDir, relPath);
|
|
610
692
|
if (resource.template && targetFile.endsWith(".hbs")) {
|
|
611
693
|
targetFile = targetFile.slice(0, -4);
|
|
612
694
|
}
|
|
@@ -615,11 +697,11 @@ async function updateRecursiveResource(resource, projectRoot, data, variantDir,
|
|
|
615
697
|
const templateContent = await loadTemplateFile(entry);
|
|
616
698
|
content = renderTemplate(templateContent, data);
|
|
617
699
|
} else {
|
|
618
|
-
content = await
|
|
700
|
+
content = await fs6.readFile(entry, "utf-8");
|
|
619
701
|
}
|
|
620
702
|
await writeFileSafe(targetFile, content);
|
|
621
703
|
const hash = computeHash(content);
|
|
622
|
-
const targetRel =
|
|
704
|
+
const targetRel = path8.relative(projectRoot, targetFile);
|
|
623
705
|
results.push({
|
|
624
706
|
id: `${resource.id}:${relPath}`,
|
|
625
707
|
target: targetRel,
|
|
@@ -630,32 +712,12 @@ async function updateRecursiveResource(resource, projectRoot, data, variantDir,
|
|
|
630
712
|
}
|
|
631
713
|
return results;
|
|
632
714
|
}
|
|
633
|
-
function resolveSourcePath2(source, variantDir, packageRoot) {
|
|
634
|
-
if (source.startsWith("_template/")) {
|
|
635
|
-
return path5.join(packageRoot, source);
|
|
636
|
-
}
|
|
637
|
-
return path5.join(variantDir, source);
|
|
638
|
-
}
|
|
639
|
-
async function walkDir2(dir) {
|
|
640
|
-
const files = [];
|
|
641
|
-
const entries = await fs5.readdir(dir, { withFileTypes: true });
|
|
642
|
-
for (const entry of entries) {
|
|
643
|
-
const fullPath = path5.join(dir, entry.name);
|
|
644
|
-
if (entry.isDirectory()) {
|
|
645
|
-
files.push(...await walkDir2(fullPath));
|
|
646
|
-
} else if (entry.isFile()) {
|
|
647
|
-
files.push(fullPath);
|
|
648
|
-
}
|
|
649
|
-
}
|
|
650
|
-
return files;
|
|
651
|
-
}
|
|
652
715
|
function escapeRegExp(str) {
|
|
653
716
|
return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
654
717
|
}
|
|
655
718
|
|
|
656
719
|
// src/commands/design/update.ts
|
|
657
|
-
|
|
658
|
-
var DESIGN_PACKAGE2 = "@teamix-evo/design";
|
|
720
|
+
var DESIGN_PACKAGE = "@teamix-evo/design";
|
|
659
721
|
var updateCommand = new Command2("update").description("\u66F4\u65B0\u8BBE\u8BA1\u4F53\u7CFB\u8D44\u6E90").action(async () => {
|
|
660
722
|
try {
|
|
661
723
|
const ide = detectIde();
|
|
@@ -678,8 +740,8 @@ var updateCommand = new Command2("update").description("\u66F4\u65B0\u8BBE\u8BA1
|
|
|
678
740
|
process.exitCode = 1;
|
|
679
741
|
return;
|
|
680
742
|
}
|
|
681
|
-
logger.info(`Loading variant "${variant}" from ${
|
|
682
|
-
const { manifest, data, variantDir, packageRoot } = await loadVariantData(
|
|
743
|
+
logger.info(`Loading variant "${variant}" from ${DESIGN_PACKAGE}...`);
|
|
744
|
+
const { manifest, data, variantDir, packageRoot } = await loadVariantData(DESIGN_PACKAGE, variant);
|
|
683
745
|
logger.info(
|
|
684
746
|
`Current: v${currentVersion} \u2192 Available: v${manifest.version}`
|
|
685
747
|
);
|
|
@@ -690,16 +752,16 @@ var updateCommand = new Command2("update").description("\u66F4\u65B0\u8BBE\u8BA1
|
|
|
690
752
|
variantDir,
|
|
691
753
|
packageRoot,
|
|
692
754
|
installedManifest,
|
|
693
|
-
packageName:
|
|
755
|
+
packageName: DESIGN_PACKAGE
|
|
694
756
|
});
|
|
695
757
|
config.packages.design.version = manifest.version;
|
|
696
758
|
await writeProjectConfig(projectRoot, config);
|
|
697
759
|
const updatedManifest = { ...installedManifest };
|
|
698
760
|
const pkgIdx = updatedManifest.installed.findIndex(
|
|
699
|
-
(p) => p.package ===
|
|
761
|
+
(p) => p.package === DESIGN_PACKAGE && p.variant === variant
|
|
700
762
|
);
|
|
701
763
|
const pkgEntry = {
|
|
702
|
-
package:
|
|
764
|
+
package: DESIGN_PACKAGE,
|
|
703
765
|
variant,
|
|
704
766
|
version: manifest.version,
|
|
705
767
|
installedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -728,7 +790,6 @@ var updateCommand = new Command2("update").description("\u66F4\u65B0\u8BBE\u8BA1
|
|
|
728
790
|
|
|
729
791
|
// src/commands/design/list.ts
|
|
730
792
|
import { Command as Command3 } from "commander";
|
|
731
|
-
init_logger();
|
|
732
793
|
var listCommand = new Command3("list").description("\u5217\u51FA\u5DF2\u5B89\u88C5\u7684\u8BBE\u8BA1\u53D8\u4F53").action(async () => {
|
|
733
794
|
try {
|
|
734
795
|
const ide = detectIde();
|
|
@@ -739,11 +800,11 @@ var listCommand = new Command3("list").description("\u5217\u51FA\u5DF2\u5B89\u88
|
|
|
739
800
|
logger.info('Run "teamix-evo design init [variant]" to get started.');
|
|
740
801
|
return;
|
|
741
802
|
}
|
|
742
|
-
const { variant, version } = config.packages.design;
|
|
803
|
+
const { variant, version: version2 } = config.packages.design;
|
|
743
804
|
logger.info("Installed design system:");
|
|
744
805
|
logger.info(` Package: @teamix-evo/design`);
|
|
745
806
|
logger.info(` Variant: ${variant}`);
|
|
746
|
-
logger.info(` Version: ${
|
|
807
|
+
logger.info(` Version: ${version2}`);
|
|
747
808
|
logger.info(` IDE: ${config.ide}`);
|
|
748
809
|
const manifest = await readInstalledManifest(projectRoot);
|
|
749
810
|
if (manifest) {
|
|
@@ -763,15 +824,1348 @@ var listCommand = new Command3("list").description("\u5217\u51FA\u5DF2\u5B89\u88
|
|
|
763
824
|
}
|
|
764
825
|
});
|
|
765
826
|
|
|
766
|
-
// src/commands/design/
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
827
|
+
// src/commands/design/uninstall.ts
|
|
828
|
+
import { Command as Command4 } from "commander";
|
|
829
|
+
import * as fs7 from "fs/promises";
|
|
830
|
+
import * as path9 from "path";
|
|
831
|
+
import * as prompts from "@clack/prompts";
|
|
832
|
+
var DESIGN_PACKAGE2 = "@teamix-evo/design";
|
|
833
|
+
var uninstallCommand = new Command4("uninstall").description("\u5378\u8F7D\u8BBE\u8BA1\u4F53\u7CFB\u8D44\u6E90\uFF08\u9ED8\u8BA4\u4F1A\u5220\u9664 frozen / regenerable \u8D44\u6E90\u6587\u4EF6\uFF09").option("-y, --yes", "\u8DF3\u8FC7\u786E\u8BA4").option(
|
|
834
|
+
"--keep-files",
|
|
835
|
+
"\u4EC5\u6E05\u7406 .teamix-evo \u4E2D\u7684\u8BB0\u8D26\u4FE1\u606F\uFF0C\u4E0D\u5220\u9664\u5DF2\u843D\u5730\u8D44\u6E90\u6587\u4EF6"
|
|
836
|
+
).action(async (opts) => {
|
|
837
|
+
try {
|
|
838
|
+
const ide = detectIde();
|
|
839
|
+
const projectRoot = ide.getProjectRoot();
|
|
840
|
+
const config = await readProjectConfig(projectRoot);
|
|
841
|
+
if (!config?.packages?.design) {
|
|
842
|
+
logger.info("Design system is not installed. Nothing to do.");
|
|
843
|
+
return;
|
|
844
|
+
}
|
|
845
|
+
const installedManifest = await readInstalledManifest(projectRoot);
|
|
846
|
+
const pkg = installedManifest?.installed.find(
|
|
847
|
+
(p) => p.package === DESIGN_PACKAGE2
|
|
848
|
+
);
|
|
849
|
+
const resources = pkg?.resources ?? [];
|
|
850
|
+
const removable = opts.keepFiles ? [] : resources.filter((r) => r.strategy !== "managed");
|
|
851
|
+
const kept = resources.length - removable.length;
|
|
852
|
+
logger.info(
|
|
853
|
+
`Will remove ${removable.length} file(s); keep ${kept} managed file(s).`
|
|
854
|
+
);
|
|
855
|
+
if (!opts.yes) {
|
|
856
|
+
const confirm4 = await prompts.confirm({
|
|
857
|
+
message: "\u786E\u8BA4\u5378\u8F7D\u8BBE\u8BA1\u4F53\u7CFB\uFF1F",
|
|
858
|
+
initialValue: false
|
|
859
|
+
});
|
|
860
|
+
if (prompts.isCancel(confirm4) || !confirm4) {
|
|
861
|
+
logger.info("Cancelled.");
|
|
862
|
+
return;
|
|
863
|
+
}
|
|
864
|
+
}
|
|
865
|
+
let removed = 0;
|
|
866
|
+
for (const r of removable) {
|
|
867
|
+
const target = path9.isAbsolute(r.target) ? r.target : path9.join(projectRoot, r.target);
|
|
868
|
+
try {
|
|
869
|
+
await fs7.unlink(target);
|
|
870
|
+
removed++;
|
|
871
|
+
} catch (err) {
|
|
872
|
+
if (err.code !== "ENOENT") {
|
|
873
|
+
logger.warn(
|
|
874
|
+
`Failed to remove ${target}: ${err.message}`
|
|
875
|
+
);
|
|
876
|
+
}
|
|
877
|
+
}
|
|
878
|
+
}
|
|
879
|
+
if (installedManifest) {
|
|
880
|
+
installedManifest.installed = installedManifest.installed.filter(
|
|
881
|
+
(p) => p.package !== DESIGN_PACKAGE2
|
|
882
|
+
);
|
|
883
|
+
await writeInstalledManifest(projectRoot, installedManifest);
|
|
884
|
+
}
|
|
885
|
+
delete config.packages.design;
|
|
886
|
+
await writeProjectConfig(projectRoot, config);
|
|
887
|
+
logger.success(`Uninstalled ${DESIGN_PACKAGE2}`);
|
|
888
|
+
logger.info(` Removed: ${removed} files`);
|
|
889
|
+
if (kept > 0) {
|
|
890
|
+
logger.info(
|
|
891
|
+
` Kept: ${kept} managed files (you may delete manually)`
|
|
892
|
+
);
|
|
893
|
+
}
|
|
894
|
+
} catch (err) {
|
|
895
|
+
logger.error(`Failed to uninstall: ${err.message}`);
|
|
896
|
+
logger.debug(err.stack ?? "");
|
|
897
|
+
process.exitCode = 1;
|
|
898
|
+
}
|
|
899
|
+
});
|
|
900
|
+
|
|
901
|
+
// src/commands/design/index.ts
|
|
902
|
+
var designCommand = new Command5("design").description(
|
|
903
|
+
"\u7BA1\u7406\u8BBE\u8BA1\u4F53\u7CFB\u8D44\u6E90"
|
|
904
|
+
);
|
|
905
|
+
designCommand.addCommand(initCommand);
|
|
906
|
+
designCommand.addCommand(updateCommand);
|
|
907
|
+
designCommand.addCommand(listCommand);
|
|
908
|
+
designCommand.addCommand(uninstallCommand);
|
|
909
|
+
|
|
910
|
+
// src/commands/skills/index.ts
|
|
911
|
+
import { Command as Command10 } from "commander";
|
|
912
|
+
|
|
913
|
+
// src/commands/skills/add.ts
|
|
914
|
+
import { Command as Command6 } from "commander";
|
|
915
|
+
import * as prompts2 from "@clack/prompts";
|
|
916
|
+
|
|
917
|
+
// src/core/skills-client.ts
|
|
918
|
+
import * as path10 from "path";
|
|
919
|
+
import * as fs8 from "fs/promises";
|
|
920
|
+
import { createRequire as createRequire2 } from "module";
|
|
921
|
+
import { loadSkillsPackageManifest } from "@teamix-evo/registry";
|
|
922
|
+
var require3 = createRequire2(import.meta.url);
|
|
923
|
+
function resolvePackageRoot2(packageName) {
|
|
924
|
+
const pkgJsonPath = require3.resolve(`${packageName}/package.json`);
|
|
925
|
+
return path10.dirname(pkgJsonPath);
|
|
926
|
+
}
|
|
927
|
+
async function loadSkillsData(packageName) {
|
|
928
|
+
const packageRoot = resolvePackageRoot2(packageName);
|
|
929
|
+
logger.debug(`Resolved skills package root: ${packageRoot}`);
|
|
930
|
+
const manifest = await loadSkillsPackageManifest(packageRoot);
|
|
931
|
+
let data = {};
|
|
932
|
+
const dataPath = path10.join(packageRoot, "_data.json");
|
|
933
|
+
try {
|
|
934
|
+
const raw = await fs8.readFile(dataPath, "utf-8");
|
|
935
|
+
data = JSON.parse(raw);
|
|
936
|
+
} catch (err) {
|
|
937
|
+
if (err.code !== "ENOENT") {
|
|
938
|
+
throw err;
|
|
939
|
+
}
|
|
940
|
+
logger.debug(`No _data.json found at ${dataPath}, using empty data`);
|
|
941
|
+
}
|
|
942
|
+
return { manifest, data, packageRoot };
|
|
943
|
+
}
|
|
944
|
+
|
|
945
|
+
// src/core/skills-installer.ts
|
|
946
|
+
import * as path11 from "path";
|
|
947
|
+
import * as fs9 from "fs/promises";
|
|
948
|
+
import { replaceManagedRegion as replaceManagedRegion2 } from "@teamix-evo/registry";
|
|
949
|
+
async function installSkills(options) {
|
|
950
|
+
const { manifest, ides, scope, onlyIds } = options;
|
|
951
|
+
const installed = [];
|
|
952
|
+
const targets = manifest.skills.filter(
|
|
953
|
+
(s) => !onlyIds || onlyIds.includes(s.id)
|
|
954
|
+
);
|
|
955
|
+
for (const skill of targets) {
|
|
956
|
+
const skillIdes = skill.ides.filter((i) => ides.includes(i));
|
|
957
|
+
if (skillIdes.length === 0) {
|
|
958
|
+
logger.warn(
|
|
959
|
+
`Skill "${skill.name}" supports [${skill.ides.join(
|
|
960
|
+
","
|
|
961
|
+
)}], no overlap with [${ides.join(",")}]; skipped.`
|
|
962
|
+
);
|
|
963
|
+
continue;
|
|
964
|
+
}
|
|
965
|
+
for (const ide of skillIdes) {
|
|
966
|
+
const result = await installSkillForIde(skill, ide, scope, options);
|
|
967
|
+
installed.push(...result);
|
|
968
|
+
}
|
|
969
|
+
}
|
|
970
|
+
return { resources: installed, count: installed.length };
|
|
971
|
+
}
|
|
972
|
+
async function installSkillForIde(skill, ide, scope, options) {
|
|
973
|
+
const { data, packageRoot, projectRoot } = options;
|
|
974
|
+
const adapter = getAdapter(ide);
|
|
975
|
+
const targetDir = adapter.getSkillTargetDir(skill.name, scope, projectRoot);
|
|
976
|
+
const sourceAbs = path11.resolve(packageRoot, skill.source);
|
|
977
|
+
const stat2 = await fs9.stat(sourceAbs);
|
|
978
|
+
const results = [];
|
|
979
|
+
if (stat2.isFile()) {
|
|
980
|
+
const targetFile = path11.join(targetDir, "SKILL.md");
|
|
981
|
+
const content = await renderSkillContent(sourceAbs, skill, data);
|
|
982
|
+
await writeFileSafe(targetFile, content);
|
|
983
|
+
results.push(makeInstalledRecord(skill, targetFile, content, ide, scope));
|
|
984
|
+
logger.debug(` Wrote ${ide}:${scope}: ${targetFile}`);
|
|
985
|
+
return results;
|
|
986
|
+
}
|
|
987
|
+
await ensureDir(targetDir);
|
|
988
|
+
const entries = await walkDir(sourceAbs);
|
|
989
|
+
for (const entry of entries) {
|
|
990
|
+
const rel2 = path11.relative(sourceAbs, entry);
|
|
991
|
+
let targetFile = path11.join(targetDir, rel2);
|
|
992
|
+
if (skill.template && targetFile.endsWith(".hbs")) {
|
|
993
|
+
targetFile = targetFile.slice(0, -4);
|
|
994
|
+
}
|
|
995
|
+
let content;
|
|
996
|
+
if (skill.template && entry.endsWith(".hbs")) {
|
|
997
|
+
const tpl = await loadTemplateFile(entry);
|
|
998
|
+
content = renderTemplate(tpl, { ...data, skill });
|
|
999
|
+
} else {
|
|
1000
|
+
content = await fs9.readFile(entry, "utf-8");
|
|
1001
|
+
}
|
|
1002
|
+
await writeFileSafe(targetFile, content);
|
|
1003
|
+
results.push(
|
|
1004
|
+
makeInstalledRecord(skill, targetFile, content, ide, scope, rel2)
|
|
1005
|
+
);
|
|
1006
|
+
logger.debug(` Wrote ${ide}:${scope}: ${targetFile}`);
|
|
1007
|
+
}
|
|
1008
|
+
return results;
|
|
1009
|
+
}
|
|
1010
|
+
async function renderSkillContent(sourceAbs, skill, data) {
|
|
1011
|
+
if (skill.template ?? sourceAbs.endsWith(".hbs")) {
|
|
1012
|
+
const tpl = await loadTemplateFile(sourceAbs);
|
|
1013
|
+
return renderTemplate(tpl, { ...data, skill });
|
|
1014
|
+
}
|
|
1015
|
+
return fs9.readFile(sourceAbs, "utf-8");
|
|
1016
|
+
}
|
|
1017
|
+
function makeInstalledRecord(skill, targetAbs, content, ide, scope, rel2) {
|
|
1018
|
+
const id = rel2 ? `${skill.id}:${rel2}` : skill.id;
|
|
1019
|
+
return {
|
|
1020
|
+
id,
|
|
1021
|
+
target: targetAbs,
|
|
1022
|
+
hash: computeHash(content),
|
|
1023
|
+
strategy: skill.updateStrategy,
|
|
1024
|
+
ide,
|
|
1025
|
+
scope
|
|
1026
|
+
};
|
|
1027
|
+
}
|
|
1028
|
+
async function updateSkills(options) {
|
|
1029
|
+
const { manifest, ides, scope, installed, projectRoot, data, packageRoot } = options;
|
|
1030
|
+
const summary = { overwritten: 0, managed: 0, skipped: 0, created: 0 };
|
|
1031
|
+
const updated = [];
|
|
1032
|
+
const installedMap = /* @__PURE__ */ new Map();
|
|
1033
|
+
for (const r of installed) {
|
|
1034
|
+
installedMap.set(installedKey(r), r);
|
|
1035
|
+
}
|
|
1036
|
+
for (const skill of manifest.skills) {
|
|
1037
|
+
const skillIdes = skill.ides.filter((i) => ides.includes(i));
|
|
1038
|
+
for (const ide of skillIdes) {
|
|
1039
|
+
const records = await updateSkillForIde(
|
|
1040
|
+
skill,
|
|
1041
|
+
ide,
|
|
1042
|
+
scope,
|
|
1043
|
+
data,
|
|
1044
|
+
packageRoot,
|
|
1045
|
+
projectRoot,
|
|
1046
|
+
installedMap,
|
|
1047
|
+
summary
|
|
1048
|
+
);
|
|
1049
|
+
updated.push(...records);
|
|
1050
|
+
}
|
|
1051
|
+
}
|
|
1052
|
+
return { resources: updated, summary };
|
|
1053
|
+
}
|
|
1054
|
+
async function updateSkillForIde(skill, ide, scope, data, packageRoot, projectRoot, installedMap, summary) {
|
|
1055
|
+
const adapter = getAdapter(ide);
|
|
1056
|
+
const targetDir = adapter.getSkillTargetDir(skill.name, scope, projectRoot);
|
|
1057
|
+
const sourceAbs = path11.resolve(packageRoot, skill.source);
|
|
1058
|
+
const stat2 = await fs9.stat(sourceAbs);
|
|
1059
|
+
const records = [];
|
|
1060
|
+
if (!stat2.isFile()) {
|
|
1061
|
+
const entries = await walkDir(sourceAbs);
|
|
1062
|
+
await ensureDir(targetDir);
|
|
1063
|
+
for (const entry of entries) {
|
|
1064
|
+
const rel2 = path11.relative(sourceAbs, entry);
|
|
1065
|
+
let targetFile2 = path11.join(targetDir, rel2);
|
|
1066
|
+
if (skill.template && targetFile2.endsWith(".hbs")) {
|
|
1067
|
+
targetFile2 = targetFile2.slice(0, -4);
|
|
1068
|
+
}
|
|
1069
|
+
let content;
|
|
1070
|
+
if (skill.template && entry.endsWith(".hbs")) {
|
|
1071
|
+
content = renderTemplate(await loadTemplateFile(entry), {
|
|
1072
|
+
...data,
|
|
1073
|
+
skill
|
|
1074
|
+
});
|
|
1075
|
+
} else {
|
|
1076
|
+
content = await fs9.readFile(entry, "utf-8");
|
|
1077
|
+
}
|
|
1078
|
+
const exists2 = await fileExists(targetFile2);
|
|
1079
|
+
if (exists2) {
|
|
1080
|
+
await backupFile(targetFile2, projectRoot);
|
|
1081
|
+
summary.overwritten++;
|
|
1082
|
+
} else {
|
|
1083
|
+
summary.created++;
|
|
1084
|
+
}
|
|
1085
|
+
await writeFileSafe(targetFile2, content);
|
|
1086
|
+
records.push(
|
|
1087
|
+
makeInstalledRecord(skill, targetFile2, content, ide, scope, rel2)
|
|
1088
|
+
);
|
|
1089
|
+
}
|
|
1090
|
+
return records;
|
|
1091
|
+
}
|
|
1092
|
+
const targetFile = path11.join(targetDir, "SKILL.md");
|
|
1093
|
+
const newContent = await renderSkillContent(sourceAbs, skill, data);
|
|
1094
|
+
const exists = await fileExists(targetFile);
|
|
1095
|
+
const installedKeyStr = `${skill.id}|${ide}|${scope}`;
|
|
1096
|
+
const prior = installedMap.get(installedKeyStr);
|
|
1097
|
+
if (skill.updateStrategy === "frozen") {
|
|
1098
|
+
if (exists) {
|
|
1099
|
+
summary.skipped++;
|
|
1100
|
+
return [
|
|
1101
|
+
prior ?? makeInstalledRecord(skill, targetFile, newContent, ide, scope)
|
|
1102
|
+
];
|
|
1103
|
+
}
|
|
1104
|
+
await writeFileSafe(targetFile, newContent);
|
|
1105
|
+
summary.created++;
|
|
1106
|
+
records.push(
|
|
1107
|
+
makeInstalledRecord(skill, targetFile, newContent, ide, scope)
|
|
1108
|
+
);
|
|
1109
|
+
return records;
|
|
1110
|
+
}
|
|
1111
|
+
if (skill.updateStrategy === "regenerable" || !exists) {
|
|
1112
|
+
if (exists) {
|
|
1113
|
+
await backupFile(targetFile, projectRoot);
|
|
1114
|
+
summary.overwritten++;
|
|
1115
|
+
} else {
|
|
1116
|
+
summary.created++;
|
|
1117
|
+
}
|
|
1118
|
+
await writeFileSafe(targetFile, newContent);
|
|
1119
|
+
records.push(
|
|
1120
|
+
makeInstalledRecord(skill, targetFile, newContent, ide, scope)
|
|
1121
|
+
);
|
|
1122
|
+
return records;
|
|
1123
|
+
}
|
|
1124
|
+
const current = await readFileOrNull(targetFile);
|
|
1125
|
+
let updated = current ?? newContent;
|
|
1126
|
+
const regionIds = skill.managedRegions ?? [];
|
|
1127
|
+
for (const regionId of regionIds) {
|
|
1128
|
+
const re = new RegExp(
|
|
1129
|
+
`<!-- teamix-evo:managed:start id="${escapeRegExp2(
|
|
1130
|
+
regionId
|
|
1131
|
+
)}" -->([\\s\\S]*?)<!-- teamix-evo:managed:end id="${escapeRegExp2(
|
|
1132
|
+
regionId
|
|
1133
|
+
)}" -->`
|
|
1134
|
+
);
|
|
1135
|
+
const match = newContent.match(re);
|
|
1136
|
+
if (match) {
|
|
1137
|
+
const region = match[1].replace(/^\n/, "").replace(/\n$/, "");
|
|
1138
|
+
try {
|
|
1139
|
+
updated = replaceManagedRegion2(updated, regionId, region);
|
|
1140
|
+
} catch {
|
|
1141
|
+
logger.warn(
|
|
1142
|
+
`Managed region "${regionId}" not found in ${targetFile}. Skipped.`
|
|
1143
|
+
);
|
|
1144
|
+
}
|
|
1145
|
+
}
|
|
1146
|
+
}
|
|
1147
|
+
await backupFile(targetFile, projectRoot);
|
|
1148
|
+
await writeFileSafe(targetFile, updated);
|
|
1149
|
+
summary.managed++;
|
|
1150
|
+
records.push(makeInstalledRecord(skill, targetFile, updated, ide, scope));
|
|
1151
|
+
return records;
|
|
1152
|
+
}
|
|
1153
|
+
function installedKey(r) {
|
|
1154
|
+
return `${r.id}|${r.ide ?? ""}|${r.scope ?? ""}`;
|
|
1155
|
+
}
|
|
1156
|
+
function escapeRegExp2(str) {
|
|
1157
|
+
return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
1158
|
+
}
|
|
1159
|
+
async function removeSkillFiles(records) {
|
|
1160
|
+
const removed = [];
|
|
1161
|
+
for (const r of records) {
|
|
1162
|
+
try {
|
|
1163
|
+
await fs9.unlink(r.target);
|
|
1164
|
+
removed.push(r.target);
|
|
1165
|
+
} catch (err) {
|
|
1166
|
+
if (err.code !== "ENOENT") {
|
|
1167
|
+
logger.warn(`Failed to remove ${r.target}: ${err.message}`);
|
|
1168
|
+
}
|
|
1169
|
+
}
|
|
1170
|
+
}
|
|
1171
|
+
const parents = new Set(records.map((r) => path11.dirname(r.target)));
|
|
1172
|
+
for (const dir of parents) {
|
|
1173
|
+
try {
|
|
1174
|
+
const entries = await fs9.readdir(dir);
|
|
1175
|
+
if (entries.length === 0) await fs9.rmdir(dir);
|
|
1176
|
+
} catch {
|
|
1177
|
+
}
|
|
1178
|
+
}
|
|
1179
|
+
return removed;
|
|
1180
|
+
}
|
|
1181
|
+
|
|
1182
|
+
// src/core/skills-add.ts
|
|
1183
|
+
var DEFAULT_SKILLS_PACKAGE = "@teamix-evo/skills";
|
|
1184
|
+
var FLAT_VARIANT = "_flat";
|
|
1185
|
+
async function runSkillsAdd(options) {
|
|
1186
|
+
const { projectRoot, names: requestedNames } = options;
|
|
1187
|
+
const packageName = options.packageName ?? DEFAULT_SKILLS_PACKAGE;
|
|
1188
|
+
const ideIdent = options.ide ?? "qoder";
|
|
1189
|
+
const isIncremental = !!requestedNames && requestedNames.length > 0;
|
|
1190
|
+
await ensureTeamixDir(projectRoot);
|
|
1191
|
+
const existingConfig = await readProjectConfig(projectRoot);
|
|
1192
|
+
const existingSkillsCfg = existingConfig?.packages?.skills;
|
|
1193
|
+
if (!isIncremental && existingSkillsCfg) {
|
|
1194
|
+
return { status: "already-added" };
|
|
1195
|
+
}
|
|
1196
|
+
const ides = options.ides && options.ides.length > 0 ? [...options.ides] : existingSkillsCfg?.ides ? [...existingSkillsCfg.ides] : [];
|
|
1197
|
+
const scope = options.scope ?? existingSkillsCfg?.scope;
|
|
1198
|
+
if (ides.length === 0) {
|
|
1199
|
+
throw new Error("At least one IDE must be selected.");
|
|
1200
|
+
}
|
|
1201
|
+
if (!scope) {
|
|
1202
|
+
throw new Error("Scope must be specified (project | global).");
|
|
1203
|
+
}
|
|
1204
|
+
const { manifest, data, packageRoot } = await loadSkillsData(packageName);
|
|
1205
|
+
if (isIncremental) {
|
|
1206
|
+
const known = new Set(manifest.skills.map((s) => s.id));
|
|
1207
|
+
const unknown = requestedNames.filter((n) => !known.has(n));
|
|
1208
|
+
if (unknown.length > 0) {
|
|
1209
|
+
const available = [...known].join(", ");
|
|
1210
|
+
throw new Error(
|
|
1211
|
+
`Unknown skill id(s): ${unknown.join(", ")}. Available: ${available || "(none)"}.`
|
|
1212
|
+
);
|
|
1213
|
+
}
|
|
1214
|
+
}
|
|
1215
|
+
const existingInstalled = await readInstalledManifest(projectRoot);
|
|
1216
|
+
const existingPkg = existingInstalled?.installed.find(
|
|
1217
|
+
(p) => p.package === packageName
|
|
1218
|
+
);
|
|
1219
|
+
const existingSkillIds = new Set(
|
|
1220
|
+
(existingPkg?.resources ?? []).map((r) => r.id.split(":")[0])
|
|
1221
|
+
);
|
|
1222
|
+
let onlyIds;
|
|
1223
|
+
let skippedSkillIds;
|
|
1224
|
+
if (isIncremental) {
|
|
1225
|
+
skippedSkillIds = requestedNames.filter((n) => existingSkillIds.has(n));
|
|
1226
|
+
onlyIds = requestedNames.filter((n) => !existingSkillIds.has(n));
|
|
1227
|
+
} else {
|
|
1228
|
+
skippedSkillIds = [];
|
|
1229
|
+
onlyIds = manifest.skills.map((s) => s.id);
|
|
1230
|
+
}
|
|
1231
|
+
if (isIncremental && onlyIds.length === 0) {
|
|
1232
|
+
return {
|
|
1233
|
+
status: "installed",
|
|
1234
|
+
packageName,
|
|
1235
|
+
version: existingSkillsCfg?.version ?? manifest.version,
|
|
1236
|
+
ides,
|
|
1237
|
+
scope,
|
|
1238
|
+
skillCount: 0,
|
|
1239
|
+
fileCount: 0,
|
|
1240
|
+
resources: [],
|
|
1241
|
+
addedSkillIds: [],
|
|
1242
|
+
skippedSkillIds
|
|
1243
|
+
};
|
|
1244
|
+
}
|
|
1245
|
+
const result = await installSkills({
|
|
1246
|
+
projectRoot,
|
|
1247
|
+
manifest,
|
|
1248
|
+
data,
|
|
1249
|
+
packageRoot,
|
|
1250
|
+
ides,
|
|
1251
|
+
scope,
|
|
1252
|
+
onlyIds
|
|
1253
|
+
});
|
|
1254
|
+
const config = existingConfig ?? {
|
|
1255
|
+
$schema: "https://teamix-evo.dev/schema/config/v1.json",
|
|
1256
|
+
schemaVersion: 1,
|
|
1257
|
+
ide: ideIdent,
|
|
1258
|
+
packages: {}
|
|
1259
|
+
};
|
|
1260
|
+
config.packages.skills = {
|
|
1261
|
+
variant: FLAT_VARIANT,
|
|
1262
|
+
version: manifest.version,
|
|
1263
|
+
ides,
|
|
1264
|
+
scope
|
|
1265
|
+
};
|
|
1266
|
+
await writeProjectConfig(projectRoot, config);
|
|
1267
|
+
const installedManifest = existingInstalled ?? {
|
|
1268
|
+
schemaVersion: 1,
|
|
1269
|
+
installed: []
|
|
1270
|
+
};
|
|
1271
|
+
const idx = installedManifest.installed.findIndex(
|
|
1272
|
+
(p) => p.package === packageName
|
|
1273
|
+
);
|
|
1274
|
+
const mergedResources = mergeInstalledResources(
|
|
1275
|
+
existingPkg?.resources ?? [],
|
|
1276
|
+
result.resources
|
|
1277
|
+
);
|
|
1278
|
+
const entry = {
|
|
1279
|
+
package: packageName,
|
|
1280
|
+
variant: FLAT_VARIANT,
|
|
1281
|
+
version: manifest.version,
|
|
1282
|
+
installedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1283
|
+
resources: mergedResources
|
|
1284
|
+
};
|
|
1285
|
+
if (idx >= 0) installedManifest.installed[idx] = entry;
|
|
1286
|
+
else installedManifest.installed.push(entry);
|
|
1287
|
+
await writeInstalledManifest(projectRoot, installedManifest);
|
|
1288
|
+
return {
|
|
1289
|
+
status: "installed",
|
|
1290
|
+
packageName,
|
|
1291
|
+
version: manifest.version,
|
|
1292
|
+
ides,
|
|
1293
|
+
scope,
|
|
1294
|
+
skillCount: onlyIds.length,
|
|
1295
|
+
fileCount: result.count,
|
|
1296
|
+
resources: result.resources,
|
|
1297
|
+
addedSkillIds: onlyIds,
|
|
1298
|
+
skippedSkillIds
|
|
1299
|
+
};
|
|
1300
|
+
}
|
|
1301
|
+
function mergeInstalledResources(existing, next) {
|
|
1302
|
+
const map = /* @__PURE__ */ new Map();
|
|
1303
|
+
const key = (r) => `${r.id}|${r.ide ?? ""}|${r.scope ?? ""}`;
|
|
1304
|
+
for (const r of existing) map.set(key(r), r);
|
|
1305
|
+
for (const r of next) map.set(key(r), r);
|
|
1306
|
+
return [...map.values()];
|
|
1307
|
+
}
|
|
1308
|
+
|
|
1309
|
+
// src/commands/skills/add.ts
|
|
1310
|
+
var addCommand = new Command6("add").description(
|
|
1311
|
+
"\u5411\u9879\u76EE\uFF08\u6216\u5168\u5C40 IDE \u914D\u7F6E\uFF09\u6DFB\u52A0 teamix-evo skills\uFF1B\u4E0D\u4F20 names \u5219\u6DFB\u52A0 manifest \u5185\u5168\u90E8 skill"
|
|
1312
|
+
).argument(
|
|
1313
|
+
"[names...]",
|
|
1314
|
+
"\u53EF\u9009\uFF1A\u4EC5\u6DFB\u52A0\u6307\u5B9A skill id\uFF08\u589E\u91CF\u6A21\u5F0F\uFF09\uFF1B\u7701\u7565\u5219\u6DFB\u52A0\u5168\u90E8"
|
|
1315
|
+
).option("--ide <list>", '\u9017\u53F7\u5206\u9694\u7684 IDE \u5217\u8868\uFF0C\u5982 "qoder,claude"').option(
|
|
1316
|
+
"--scope <scope>",
|
|
1317
|
+
"project | global\uFF08\u9ED8\u8BA4 project\uFF1B\u589E\u91CF\u6A21\u5F0F\u4E0B\u9ED8\u8BA4\u590D\u7528\u5DF2\u6709\u914D\u7F6E\uFF09"
|
|
1318
|
+
).option("-y, --yes", "\u4F7F\u7528\u9ED8\u8BA4\u503C\uFF0C\u8DF3\u8FC7\u4EA4\u4E92").action(async (names, opts) => {
|
|
1319
|
+
try {
|
|
1320
|
+
const ide = detectIde();
|
|
1321
|
+
const projectRoot = ide.getProjectRoot();
|
|
1322
|
+
const isIncremental = names.length > 0;
|
|
1323
|
+
const { ides, scope } = await resolveIdesAndScope({
|
|
1324
|
+
opts,
|
|
1325
|
+
projectRoot,
|
|
1326
|
+
isIncremental
|
|
1327
|
+
});
|
|
1328
|
+
logger.info(
|
|
1329
|
+
isIncremental ? `Adding skills [${names.join(",")}]: ides=[${ides.join(
|
|
1330
|
+
","
|
|
1331
|
+
)}], scope="${scope}"` : `Adding skills (all): ides=[${ides.join(",")}], scope="${scope}"`
|
|
1332
|
+
);
|
|
1333
|
+
logger.debug(`Project root: ${projectRoot}`);
|
|
1334
|
+
const result = await runSkillsAdd({
|
|
1335
|
+
projectRoot,
|
|
1336
|
+
ides,
|
|
1337
|
+
scope,
|
|
1338
|
+
ide: ide.name,
|
|
1339
|
+
names: isIncremental ? names : void 0
|
|
1340
|
+
});
|
|
1341
|
+
if (result.status === "already-added") {
|
|
1342
|
+
logger.warn(
|
|
1343
|
+
`Skills already added. Use "teamix-evo skills add <name>" to add specific skills, "teamix-evo skills update" to refresh, or "teamix-evo skills uninstall" to remove.`
|
|
1344
|
+
);
|
|
1345
|
+
return;
|
|
1346
|
+
}
|
|
1347
|
+
if (result.addedSkillIds.length === 0 && result.skippedSkillIds.length > 0) {
|
|
1348
|
+
logger.warn(
|
|
1349
|
+
`\u5DF2\u5B58\u5728\uFF0C\u65E0\u9700\u6DFB\u52A0\uFF1A${result.skippedSkillIds.join(
|
|
1350
|
+
", "
|
|
1351
|
+
)}\u3002\u5982\u9700\u5237\u65B0\u5185\u5BB9\u8BF7\u8FD0\u884C "teamix-evo skills update"\u3002`
|
|
1352
|
+
);
|
|
1353
|
+
return;
|
|
1354
|
+
}
|
|
1355
|
+
logger.success(`Skills added: ${result.skillCount} skill(s)`);
|
|
1356
|
+
logger.info(` IDEs: ${result.ides.join(", ")}`);
|
|
1357
|
+
logger.info(` Scope: ${result.scope}`);
|
|
1358
|
+
if (result.addedSkillIds.length > 0) {
|
|
1359
|
+
logger.info(` Added: ${result.addedSkillIds.join(", ")}`);
|
|
1360
|
+
}
|
|
1361
|
+
if (result.skippedSkillIds.length > 0) {
|
|
1362
|
+
logger.info(
|
|
1363
|
+
` Skipped: ${result.skippedSkillIds.join(", ")} (already added)`
|
|
1364
|
+
);
|
|
1365
|
+
}
|
|
1366
|
+
logger.info(` Files: ${result.fileCount}`);
|
|
1367
|
+
logger.info("");
|
|
1368
|
+
logger.info('Run "teamix-evo skills list" to see installed skills.');
|
|
1369
|
+
} catch (err) {
|
|
1370
|
+
logger.error(`Failed to add skills: ${err.message}`);
|
|
1371
|
+
logger.debug(err.stack ?? "");
|
|
1372
|
+
process.exitCode = 1;
|
|
1373
|
+
}
|
|
1374
|
+
});
|
|
1375
|
+
async function resolveIdesAndScope(args) {
|
|
1376
|
+
const { opts, projectRoot, isIncremental } = args;
|
|
1377
|
+
if (isIncremental && !opts.ide && !opts.scope && !opts.yes) {
|
|
1378
|
+
const existing = await readProjectConfig(projectRoot);
|
|
1379
|
+
const cfg = existing?.packages?.skills;
|
|
1380
|
+
if (cfg && cfg.ides && cfg.ides.length > 0 && cfg.scope) {
|
|
1381
|
+
logger.debug(
|
|
1382
|
+
`Reusing existing skills config: ides=[${cfg.ides.join(",")}], scope="${cfg.scope}"`
|
|
1383
|
+
);
|
|
1384
|
+
return {
|
|
1385
|
+
ides: [...cfg.ides],
|
|
1386
|
+
scope: cfg.scope
|
|
1387
|
+
};
|
|
1388
|
+
}
|
|
1389
|
+
}
|
|
1390
|
+
if (opts.ide || opts.yes) {
|
|
1391
|
+
const ides = opts.ide ? parseIdeList(opts.ide) : [...ALL_IDE_KINDS];
|
|
1392
|
+
const scope = parseScope(opts.scope);
|
|
1393
|
+
if (ides.length === 0) {
|
|
1394
|
+
throw new Error("At least one IDE must be selected.");
|
|
1395
|
+
}
|
|
1396
|
+
return { ides, scope };
|
|
1397
|
+
}
|
|
1398
|
+
const idesAns = await prompts2.multiselect({
|
|
1399
|
+
message: "\u9009\u62E9\u8981\u6CE8\u5165\u6280\u80FD\u7684 AI IDE\uFF08\u81F3\u5C11\u4E00\u4E2A\uFF09",
|
|
1400
|
+
options: ALL_IDE_KINDS.map((k) => ({
|
|
1401
|
+
value: k,
|
|
1402
|
+
label: k === "qoder" ? "Qoder" : "Claude Code"
|
|
1403
|
+
})),
|
|
1404
|
+
initialValues: [...ALL_IDE_KINDS],
|
|
1405
|
+
required: true
|
|
1406
|
+
});
|
|
1407
|
+
if (prompts2.isCancel(idesAns)) {
|
|
1408
|
+
throw new Error("Cancelled by user.");
|
|
1409
|
+
}
|
|
1410
|
+
const scopeAns = await prompts2.select({
|
|
1411
|
+
message: "\u5B89\u88C5\u8303\u56F4\uFF1F",
|
|
1412
|
+
options: [
|
|
1413
|
+
{ value: "project", label: "\u9879\u76EE\u7EA7\uFF08.qoder/.claude \u5728\u5F53\u524D\u9879\u76EE\uFF09" },
|
|
1414
|
+
{ value: "global", label: "\u5168\u5C40\uFF08~/.qoder/~/.claude\uFF09" }
|
|
1415
|
+
],
|
|
1416
|
+
initialValue: "project"
|
|
1417
|
+
});
|
|
1418
|
+
if (prompts2.isCancel(scopeAns)) {
|
|
1419
|
+
throw new Error("Cancelled by user.");
|
|
1420
|
+
}
|
|
1421
|
+
return { ides: idesAns, scope: scopeAns };
|
|
1422
|
+
}
|
|
1423
|
+
function parseIdeList(input) {
|
|
1424
|
+
const parts = input.split(",").map((s) => s.trim().toLowerCase()).filter(Boolean);
|
|
1425
|
+
const result = [];
|
|
1426
|
+
for (const p of parts) {
|
|
1427
|
+
if (p === "qoder" || p === "claude") {
|
|
1428
|
+
if (!result.includes(p)) result.push(p);
|
|
1429
|
+
} else {
|
|
1430
|
+
throw new Error(`Unknown IDE: "${p}". Expected qoder | claude.`);
|
|
1431
|
+
}
|
|
1432
|
+
}
|
|
1433
|
+
return result;
|
|
1434
|
+
}
|
|
1435
|
+
function parseScope(input) {
|
|
1436
|
+
const v = (input ?? "project").toLowerCase();
|
|
1437
|
+
if (v === "project" || v === "global") return v;
|
|
1438
|
+
throw new Error(`Invalid --scope: "${input}". Expected project | global.`);
|
|
1439
|
+
}
|
|
1440
|
+
|
|
1441
|
+
// src/commands/skills/list.ts
|
|
1442
|
+
import { Command as Command7 } from "commander";
|
|
1443
|
+
var SKILLS_PACKAGE = "@teamix-evo/skills";
|
|
1444
|
+
var listCommand2 = new Command7("list").alias("ls").description(
|
|
1445
|
+
"\u5217\u51FA teamix-evo skills\uFF08\u9ED8\u8BA4\u5C55\u793A\u5168\u90E8 skill \u5E76\u6807\u6CE8\u5DF2\u88C5/\u672A\u88C5\uFF1B--installed \u4EC5\u770B\u5DF2\u88C5\uFF09"
|
|
1446
|
+
).option("--installed", "\u4EC5\u5C55\u793A\u5DF2\u5B89\u88C5\u7684 skill\uFF08\u9690\u85CF\u672A\u5B89\u88C5\u9879\uFF09").action(async (opts) => {
|
|
1447
|
+
try {
|
|
1448
|
+
const ide = detectIde();
|
|
1449
|
+
const projectRoot = ide.getProjectRoot();
|
|
1450
|
+
const config = await readProjectConfig(projectRoot);
|
|
1451
|
+
const installedManifest = await readInstalledManifest(projectRoot);
|
|
1452
|
+
const pkg = installedManifest?.installed.find(
|
|
1453
|
+
(p) => p.package === SKILLS_PACKAGE
|
|
1454
|
+
);
|
|
1455
|
+
const installedBySkill = /* @__PURE__ */ new Map();
|
|
1456
|
+
for (const r of pkg?.resources ?? []) {
|
|
1457
|
+
const skillId = r.id.split(":")[0];
|
|
1458
|
+
installedBySkill.set(skillId, (installedBySkill.get(skillId) ?? 0) + 1);
|
|
1459
|
+
}
|
|
1460
|
+
if (opts.installed) {
|
|
1461
|
+
if (!config?.packages?.skills || !pkg) {
|
|
1462
|
+
logger.info("No skills installed.");
|
|
1463
|
+
logger.info('Run "teamix-evo skills add" to get started.');
|
|
1464
|
+
return;
|
|
1465
|
+
}
|
|
1466
|
+
printInstalledHeader(config.packages.skills, pkg.installedAt);
|
|
1467
|
+
logger.info("");
|
|
1468
|
+
logger.info("Installed skills:");
|
|
1469
|
+
for (const [skillId, count] of installedBySkill) {
|
|
1470
|
+
logger.info(` \u2713 ${skillId} (${count} file${count > 1 ? "s" : ""})`);
|
|
1471
|
+
}
|
|
1472
|
+
logger.info("");
|
|
1473
|
+
logger.info(
|
|
1474
|
+
` Total: ${pkg.resources.length} files (${installedBySkill.size} skills \xD7 ides \xD7 scope)`
|
|
1475
|
+
);
|
|
1476
|
+
return;
|
|
1477
|
+
}
|
|
1478
|
+
const { manifest } = await loadSkillsData(SKILLS_PACKAGE);
|
|
1479
|
+
const skills = [...manifest.skills].sort(
|
|
1480
|
+
(a, b) => a.id.localeCompare(b.id)
|
|
1481
|
+
);
|
|
1482
|
+
const isInstalled = !!config?.packages?.skills && !!pkg;
|
|
1483
|
+
if (isInstalled) {
|
|
1484
|
+
printInstalledHeader(config.packages.skills, pkg.installedAt);
|
|
1485
|
+
} else {
|
|
1486
|
+
logger.info("Skills package not yet added.");
|
|
1487
|
+
logger.info(
|
|
1488
|
+
'Run "teamix-evo skills add" to add all, or "teamix-evo skills add <id>" for specific skills.'
|
|
1489
|
+
);
|
|
1490
|
+
}
|
|
1491
|
+
logger.info("");
|
|
1492
|
+
logger.info(`Available skills (${SKILLS_PACKAGE}@${manifest.version}):`);
|
|
1493
|
+
let installedCount = 0;
|
|
1494
|
+
for (const s of skills) {
|
|
1495
|
+
const fileCount = installedBySkill.get(s.id);
|
|
1496
|
+
const installed = fileCount !== void 0;
|
|
1497
|
+
if (installed) installedCount++;
|
|
1498
|
+
const mark = installed ? "\u2713" : "\u25CB";
|
|
1499
|
+
const tail = installed ? `[installed, ${fileCount} file${fileCount > 1 ? "s" : ""}]` : `[not installed \u2014 run "teamix-evo skills add ${s.id}"]`;
|
|
1500
|
+
logger.info(` ${mark} ${s.id}@${s.version} ${tail}`);
|
|
1501
|
+
if (s.description) {
|
|
1502
|
+
logger.info(` ${s.description}`);
|
|
1503
|
+
}
|
|
1504
|
+
}
|
|
1505
|
+
logger.info("");
|
|
1506
|
+
logger.info(
|
|
1507
|
+
` Total: ${skills.length} skill(s) \u2014 ${installedCount} installed, ${skills.length - installedCount} available`
|
|
1508
|
+
);
|
|
1509
|
+
} catch (err) {
|
|
1510
|
+
logger.error(`Failed to list: ${err.message}`);
|
|
1511
|
+
process.exitCode = 1;
|
|
1512
|
+
}
|
|
1513
|
+
});
|
|
1514
|
+
function printInstalledHeader(cfg, installedAt) {
|
|
1515
|
+
logger.info("Installed skills package:");
|
|
1516
|
+
logger.info(` Package: ${SKILLS_PACKAGE}`);
|
|
1517
|
+
logger.info(` Version: ${cfg.version ?? "(unknown)"}`);
|
|
1518
|
+
logger.info(` IDEs: ${(cfg.ides ?? []).join(", ") || "(unknown)"}`);
|
|
1519
|
+
logger.info(` Scope: ${cfg.scope ?? "(unknown)"}`);
|
|
1520
|
+
logger.info(` Installed: ${new Date(installedAt).toLocaleString()}`);
|
|
1521
|
+
}
|
|
1522
|
+
|
|
1523
|
+
// src/commands/skills/update.ts
|
|
1524
|
+
import { Command as Command8 } from "commander";
|
|
1525
|
+
var SKILLS_PACKAGE2 = "@teamix-evo/skills";
|
|
1526
|
+
var FLAT_VARIANT2 = "_flat";
|
|
1527
|
+
var updateCommand2 = new Command8("update").description("\u66F4\u65B0\u5DF2\u5B89\u88C5\u7684 teamix-evo skills").action(async () => {
|
|
1528
|
+
try {
|
|
1529
|
+
const ide = detectIde();
|
|
1530
|
+
const projectRoot = ide.getProjectRoot();
|
|
1531
|
+
const config = await readProjectConfig(projectRoot);
|
|
1532
|
+
if (!config?.packages?.skills) {
|
|
1533
|
+
logger.error('Skills not added. Run "teamix-evo skills add" first.');
|
|
1534
|
+
process.exitCode = 1;
|
|
1535
|
+
return;
|
|
1536
|
+
}
|
|
1537
|
+
const installedManifest = await readInstalledManifest(projectRoot);
|
|
1538
|
+
if (!installedManifest) {
|
|
1539
|
+
logger.error("No installed manifest found.");
|
|
1540
|
+
process.exitCode = 1;
|
|
1541
|
+
return;
|
|
1542
|
+
}
|
|
1543
|
+
const skillsEntry = config.packages.skills;
|
|
1544
|
+
const ides = skillsEntry.ides ?? [
|
|
1545
|
+
"qoder",
|
|
1546
|
+
"claude"
|
|
1547
|
+
];
|
|
1548
|
+
const scope = skillsEntry.scope ?? "project";
|
|
1549
|
+
logger.info(`Updating skills (ides=[${ides.join(",")}], scope=${scope})`);
|
|
1550
|
+
const { manifest, data, packageRoot } = await loadSkillsData(
|
|
1551
|
+
SKILLS_PACKAGE2
|
|
1552
|
+
);
|
|
1553
|
+
const pkgInstalled = installedManifest.installed.find(
|
|
1554
|
+
(p) => p.package === SKILLS_PACKAGE2
|
|
1555
|
+
);
|
|
1556
|
+
const installedResources = pkgInstalled?.resources ?? [];
|
|
1557
|
+
logger.info(
|
|
1558
|
+
`Current: v${skillsEntry.version} \u2192 Available: v${manifest.version}`
|
|
1559
|
+
);
|
|
1560
|
+
const result = await updateSkills({
|
|
1561
|
+
projectRoot,
|
|
1562
|
+
manifest,
|
|
1563
|
+
data,
|
|
1564
|
+
packageRoot,
|
|
1565
|
+
ides,
|
|
1566
|
+
scope,
|
|
1567
|
+
installed: installedResources
|
|
1568
|
+
});
|
|
1569
|
+
config.packages.skills.version = manifest.version;
|
|
1570
|
+
await writeProjectConfig(projectRoot, config);
|
|
1571
|
+
const idx = installedManifest.installed.findIndex(
|
|
1572
|
+
(p) => p.package === SKILLS_PACKAGE2
|
|
1573
|
+
);
|
|
1574
|
+
const entry = {
|
|
1575
|
+
package: SKILLS_PACKAGE2,
|
|
1576
|
+
variant: FLAT_VARIANT2,
|
|
1577
|
+
version: manifest.version,
|
|
1578
|
+
installedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1579
|
+
resources: result.resources
|
|
1580
|
+
};
|
|
1581
|
+
if (idx >= 0) installedManifest.installed[idx] = entry;
|
|
1582
|
+
else installedManifest.installed.push(entry);
|
|
1583
|
+
await writeInstalledManifest(projectRoot, installedManifest);
|
|
1584
|
+
const { summary } = result;
|
|
1585
|
+
logger.success(`Skills updated to v${manifest.version}`);
|
|
1586
|
+
logger.info(` Created: ${summary.created}`);
|
|
1587
|
+
logger.info(` Overwritten: ${summary.overwritten}`);
|
|
1588
|
+
logger.info(` Managed: ${summary.managed}`);
|
|
1589
|
+
logger.info(` Skipped: ${summary.skipped}`);
|
|
1590
|
+
} catch (err) {
|
|
1591
|
+
logger.error(`Failed to update skills: ${err.message}`);
|
|
1592
|
+
logger.debug(err.stack ?? "");
|
|
1593
|
+
process.exitCode = 1;
|
|
1594
|
+
}
|
|
1595
|
+
});
|
|
1596
|
+
|
|
1597
|
+
// src/commands/skills/uninstall.ts
|
|
1598
|
+
import { Command as Command9 } from "commander";
|
|
1599
|
+
import * as prompts3 from "@clack/prompts";
|
|
1600
|
+
var SKILLS_PACKAGE3 = "@teamix-evo/skills";
|
|
1601
|
+
var uninstallCommand2 = new Command9("uninstall").description("\u5378\u8F7D\u5DF2\u5B89\u88C5\u7684 teamix-evo skills\uFF08\u5220\u9664\u6CE8\u5165\u5230 IDE \u7684\u6280\u80FD\u6587\u4EF6\uFF09").option("-y, --yes", "\u8DF3\u8FC7\u786E\u8BA4").action(async (opts) => {
|
|
1602
|
+
try {
|
|
1603
|
+
const ide = detectIde();
|
|
1604
|
+
const projectRoot = ide.getProjectRoot();
|
|
1605
|
+
const config = await readProjectConfig(projectRoot);
|
|
1606
|
+
if (!config?.packages?.skills) {
|
|
1607
|
+
logger.info("Skills are not installed. Nothing to do.");
|
|
1608
|
+
return;
|
|
1609
|
+
}
|
|
1610
|
+
const installedManifest = await readInstalledManifest(projectRoot);
|
|
1611
|
+
const pkg = installedManifest?.installed.find(
|
|
1612
|
+
(p) => p.package === SKILLS_PACKAGE3
|
|
1613
|
+
);
|
|
1614
|
+
const resources = pkg?.resources ?? [];
|
|
1615
|
+
logger.info(
|
|
1616
|
+
`Will remove ${resources.length} skill file(s) installed by ${SKILLS_PACKAGE3}.`
|
|
1617
|
+
);
|
|
1618
|
+
if (!opts.yes) {
|
|
1619
|
+
const confirm4 = await prompts3.confirm({
|
|
1620
|
+
message: "\u786E\u8BA4\u5378\u8F7D\uFF1F\u6B64\u64CD\u4F5C\u4F1A\u5220\u9664\u4E0A\u8FF0\u6587\u4EF6\u3002",
|
|
1621
|
+
initialValue: false
|
|
1622
|
+
});
|
|
1623
|
+
if (prompts3.isCancel(confirm4) || !confirm4) {
|
|
1624
|
+
logger.info("Cancelled.");
|
|
1625
|
+
return;
|
|
1626
|
+
}
|
|
1627
|
+
}
|
|
1628
|
+
const removed = await removeSkillFiles(resources);
|
|
1629
|
+
logger.debug(`Removed ${removed.length} files`);
|
|
1630
|
+
if (installedManifest && pkg) {
|
|
1631
|
+
installedManifest.installed = installedManifest.installed.filter(
|
|
1632
|
+
(p) => p.package !== SKILLS_PACKAGE3
|
|
1633
|
+
);
|
|
1634
|
+
await writeInstalledManifest(projectRoot, installedManifest);
|
|
1635
|
+
}
|
|
1636
|
+
delete config.packages.skills;
|
|
1637
|
+
await writeProjectConfig(projectRoot, config);
|
|
1638
|
+
logger.success(`Uninstalled ${SKILLS_PACKAGE3}`);
|
|
1639
|
+
logger.info(` Removed: ${removed.length} files`);
|
|
1640
|
+
} catch (err) {
|
|
1641
|
+
logger.error(`Failed to uninstall: ${err.message}`);
|
|
1642
|
+
logger.debug(err.stack ?? "");
|
|
1643
|
+
process.exitCode = 1;
|
|
1644
|
+
}
|
|
1645
|
+
});
|
|
1646
|
+
|
|
1647
|
+
// src/commands/skills/index.ts
|
|
1648
|
+
var skillsCommand = new Command10("skills").description(
|
|
1649
|
+
"\u7BA1\u7406 teamix-evo skills\uFF08\u5411 AI IDE \u6CE8\u5165\u6280\u80FD\uFF09"
|
|
1650
|
+
);
|
|
1651
|
+
skillsCommand.addCommand(addCommand);
|
|
1652
|
+
skillsCommand.addCommand(listCommand2);
|
|
1653
|
+
skillsCommand.addCommand(updateCommand2);
|
|
1654
|
+
skillsCommand.addCommand(uninstallCommand2);
|
|
1655
|
+
|
|
1656
|
+
// src/commands/ui/index.ts
|
|
1657
|
+
import { Command as Command14 } from "commander";
|
|
1658
|
+
|
|
1659
|
+
// src/commands/ui/init.ts
|
|
1660
|
+
import { Command as Command11 } from "commander";
|
|
1661
|
+
import * as prompts4 from "@clack/prompts";
|
|
1662
|
+
|
|
1663
|
+
// src/core/ui-init.ts
|
|
1664
|
+
var DEFAULT_UI_ALIASES = {
|
|
1665
|
+
components: "src/components/ui",
|
|
1666
|
+
hooks: "src/hooks",
|
|
1667
|
+
utils: "src/lib/utils",
|
|
1668
|
+
lib: "src/lib"
|
|
1669
|
+
};
|
|
1670
|
+
var DEFAULT_UI_ICON_LIBRARY = "lucide";
|
|
1671
|
+
async function runUiInit(options) {
|
|
1672
|
+
const { projectRoot } = options;
|
|
1673
|
+
const ideIdent = options.ide ?? "qoder";
|
|
1674
|
+
await ensureTeamixDir(projectRoot);
|
|
1675
|
+
const existingConfig = await readProjectConfig(projectRoot);
|
|
1676
|
+
if (existingConfig?.packages?.ui) {
|
|
1677
|
+
return { status: "already-initialized" };
|
|
1678
|
+
}
|
|
1679
|
+
const aliases = {
|
|
1680
|
+
components: options.aliases?.components ?? DEFAULT_UI_ALIASES.components,
|
|
1681
|
+
hooks: options.aliases?.hooks ?? DEFAULT_UI_ALIASES.hooks,
|
|
1682
|
+
utils: options.aliases?.utils ?? DEFAULT_UI_ALIASES.utils,
|
|
1683
|
+
lib: options.aliases?.lib ?? DEFAULT_UI_ALIASES.lib
|
|
1684
|
+
};
|
|
1685
|
+
const iconLibrary = options.iconLibrary ?? DEFAULT_UI_ICON_LIBRARY;
|
|
1686
|
+
const tsx = options.tsx ?? true;
|
|
1687
|
+
const rsc = options.rsc ?? false;
|
|
1688
|
+
const config = existingConfig ?? {
|
|
1689
|
+
$schema: "https://teamix-evo.dev/schema/config/v1.json",
|
|
1690
|
+
schemaVersion: 1,
|
|
1691
|
+
ide: ideIdent,
|
|
1692
|
+
packages: {}
|
|
1693
|
+
};
|
|
1694
|
+
config.packages.ui = {
|
|
1695
|
+
variant: "_flat",
|
|
1696
|
+
version: "0.0.0",
|
|
1697
|
+
aliases,
|
|
1698
|
+
iconLibrary,
|
|
1699
|
+
tsx,
|
|
1700
|
+
rsc
|
|
1701
|
+
};
|
|
1702
|
+
await writeProjectConfig(projectRoot, config);
|
|
1703
|
+
return {
|
|
1704
|
+
status: "installed",
|
|
1705
|
+
aliases,
|
|
1706
|
+
iconLibrary,
|
|
1707
|
+
tsx,
|
|
1708
|
+
rsc
|
|
1709
|
+
};
|
|
1710
|
+
}
|
|
1711
|
+
|
|
1712
|
+
// src/commands/ui/init.ts
|
|
1713
|
+
var initCommand2 = new Command11("init").description(
|
|
1714
|
+
"\u521D\u59CB\u5316 teamix-evo ui \u914D\u7F6E\uFF08\u8BE2\u95EE aliases / iconLibrary / tsx / rsc\uFF09"
|
|
1715
|
+
).option("-y, --yes", "\u4F7F\u7528\u9ED8\u8BA4\u503C\uFF0C\u8DF3\u8FC7\u4EA4\u4E92").option(
|
|
1716
|
+
"--components <path>",
|
|
1717
|
+
"\u7EC4\u4EF6 alias \u8DEF\u5F84",
|
|
1718
|
+
DEFAULT_UI_ALIASES.components
|
|
1719
|
+
).option("--hooks <path>", "hooks alias \u8DEF\u5F84", DEFAULT_UI_ALIASES.hooks).option("--utils <path>", "utils alias \u8DEF\u5F84", DEFAULT_UI_ALIASES.utils).option("--lib <path>", "lib alias \u8DEF\u5F84", DEFAULT_UI_ALIASES.lib).option("--icon-library <name>", "\u9ED8\u8BA4 icon \u5E93\uFF08\u58F0\u660E\u6027\uFF09", "lucide").option("--tsx", "\u4F7F\u7528 TSX", true).option("--rsc", "\u4F7F\u7528 React Server Components").action(async (opts) => {
|
|
1720
|
+
try {
|
|
1721
|
+
const ide = detectIde();
|
|
1722
|
+
const projectRoot = ide.getProjectRoot();
|
|
1723
|
+
const cfg = await resolveConfig(opts);
|
|
1724
|
+
const result = await runUiInit({
|
|
1725
|
+
projectRoot,
|
|
1726
|
+
aliases: cfg.aliases,
|
|
1727
|
+
iconLibrary: cfg.iconLibrary,
|
|
1728
|
+
tsx: cfg.tsx,
|
|
1729
|
+
rsc: cfg.rsc,
|
|
1730
|
+
ide: ide.name
|
|
1731
|
+
});
|
|
1732
|
+
if (result.status === "already-initialized") {
|
|
1733
|
+
logger.warn(
|
|
1734
|
+
"UI already initialized. Edit `.teamix-evo/config.json` directly to change aliases, or run `teamix-evo ui list`."
|
|
1735
|
+
);
|
|
1736
|
+
return;
|
|
1737
|
+
}
|
|
1738
|
+
logger.success("UI initialized.");
|
|
1739
|
+
logger.info(` components: ${result.aliases.components}`);
|
|
1740
|
+
logger.info(` hooks: ${result.aliases.hooks}`);
|
|
1741
|
+
logger.info(` utils: ${result.aliases.utils}`);
|
|
1742
|
+
logger.info(` lib: ${result.aliases.lib}`);
|
|
1743
|
+
logger.info(` iconLibrary: ${result.iconLibrary}`);
|
|
1744
|
+
logger.info(` tsx: ${result.tsx}, rsc: ${result.rsc}`);
|
|
1745
|
+
logger.info("");
|
|
1746
|
+
logger.info("Next: `npx teamix-evo ui add button`");
|
|
1747
|
+
} catch (err) {
|
|
1748
|
+
logger.error(`Failed to initialize ui: ${err.message}`);
|
|
1749
|
+
logger.debug(err.stack ?? "");
|
|
1750
|
+
process.exitCode = 1;
|
|
1751
|
+
}
|
|
1752
|
+
});
|
|
1753
|
+
async function resolveConfig(opts) {
|
|
1754
|
+
if (opts.yes) {
|
|
1755
|
+
return {
|
|
1756
|
+
aliases: {
|
|
1757
|
+
components: opts.components ?? DEFAULT_UI_ALIASES.components,
|
|
1758
|
+
hooks: opts.hooks ?? DEFAULT_UI_ALIASES.hooks,
|
|
1759
|
+
utils: opts.utils ?? DEFAULT_UI_ALIASES.utils,
|
|
1760
|
+
lib: opts.lib ?? DEFAULT_UI_ALIASES.lib
|
|
1761
|
+
},
|
|
1762
|
+
iconLibrary: opts.iconLibrary ?? "lucide",
|
|
1763
|
+
tsx: opts.tsx ?? true,
|
|
1764
|
+
rsc: opts.rsc ?? false
|
|
1765
|
+
};
|
|
1766
|
+
}
|
|
1767
|
+
const components = await prompts4.text({
|
|
1768
|
+
message: "components \u8DEF\u5F84\uFF08\u6CE8\u5165\u6309\u94AE\u7B49\u7EC4\u4EF6\u6E90\u7801\u7684\u76EE\u5F55\uFF09",
|
|
1769
|
+
initialValue: opts.components ?? DEFAULT_UI_ALIASES.components
|
|
1770
|
+
});
|
|
1771
|
+
if (prompts4.isCancel(components)) throw new Error("Cancelled by user.");
|
|
1772
|
+
const hooks = await prompts4.text({
|
|
1773
|
+
message: "hooks \u8DEF\u5F84",
|
|
1774
|
+
initialValue: opts.hooks ?? DEFAULT_UI_ALIASES.hooks
|
|
1775
|
+
});
|
|
1776
|
+
if (prompts4.isCancel(hooks)) throw new Error("Cancelled by user.");
|
|
1777
|
+
const utils = await prompts4.text({
|
|
1778
|
+
message: "utils \u8DEF\u5F84\uFF08cn \u7B49\u5DE5\u5177\uFF09",
|
|
1779
|
+
initialValue: opts.utils ?? DEFAULT_UI_ALIASES.utils
|
|
1780
|
+
});
|
|
1781
|
+
if (prompts4.isCancel(utils)) throw new Error("Cancelled by user.");
|
|
1782
|
+
const lib = await prompts4.text({
|
|
1783
|
+
message: "lib \u8DEF\u5F84\uFF08\u5171\u4EAB\u4EE3\u7801\u6839\uFF09",
|
|
1784
|
+
initialValue: opts.lib ?? DEFAULT_UI_ALIASES.lib
|
|
1785
|
+
});
|
|
1786
|
+
if (prompts4.isCancel(lib)) throw new Error("Cancelled by user.");
|
|
1787
|
+
const iconLibrary = await prompts4.text({
|
|
1788
|
+
message: "icon \u5E93\uFF08\u58F0\u660E\u6027\uFF0C\u7EC4\u4EF6\u6E90\u7801\u5DF2 hardcode lucide-react\uFF09",
|
|
1789
|
+
initialValue: opts.iconLibrary ?? "lucide"
|
|
1790
|
+
});
|
|
1791
|
+
if (prompts4.isCancel(iconLibrary)) throw new Error("Cancelled by user.");
|
|
1792
|
+
const tsxAns = await prompts4.confirm({
|
|
1793
|
+
message: "\u4F7F\u7528 TSX\uFF1F",
|
|
1794
|
+
initialValue: opts.tsx ?? true
|
|
1795
|
+
});
|
|
1796
|
+
if (prompts4.isCancel(tsxAns)) throw new Error("Cancelled by user.");
|
|
1797
|
+
const rscAns = await prompts4.confirm({
|
|
1798
|
+
message: "\u4F7F\u7528 React Server Components\uFF1F",
|
|
1799
|
+
initialValue: opts.rsc ?? false
|
|
1800
|
+
});
|
|
1801
|
+
if (prompts4.isCancel(rscAns)) throw new Error("Cancelled by user.");
|
|
1802
|
+
return {
|
|
1803
|
+
aliases: { components, hooks, utils, lib },
|
|
1804
|
+
iconLibrary,
|
|
1805
|
+
tsx: tsxAns,
|
|
1806
|
+
rsc: rscAns
|
|
1807
|
+
};
|
|
1808
|
+
}
|
|
1809
|
+
|
|
1810
|
+
// src/commands/ui/add.ts
|
|
1811
|
+
import { Command as Command12 } from "commander";
|
|
1812
|
+
|
|
1813
|
+
// src/core/ui-client.ts
|
|
1814
|
+
import * as path12 from "path";
|
|
1815
|
+
import * as fs10 from "fs/promises";
|
|
1816
|
+
import { createRequire as createRequire3 } from "module";
|
|
1817
|
+
import { loadUiPackageManifest } from "@teamix-evo/registry";
|
|
1818
|
+
var require4 = createRequire3(import.meta.url);
|
|
1819
|
+
function resolvePackageRoot3(packageName) {
|
|
1820
|
+
const pkgJsonPath = require4.resolve(`${packageName}/package.json`);
|
|
1821
|
+
return path12.dirname(pkgJsonPath);
|
|
1822
|
+
}
|
|
1823
|
+
async function loadUiData(packageName) {
|
|
1824
|
+
const packageRoot = resolvePackageRoot3(packageName);
|
|
1825
|
+
logger.debug(`Resolved ui package root: ${packageRoot}`);
|
|
1826
|
+
const manifest = await loadUiPackageManifest(packageRoot);
|
|
1827
|
+
let data = {};
|
|
1828
|
+
const dataPath = path12.join(packageRoot, "_data.json");
|
|
1829
|
+
try {
|
|
1830
|
+
const raw = await fs10.readFile(dataPath, "utf-8");
|
|
1831
|
+
data = JSON.parse(raw);
|
|
1832
|
+
} catch (err) {
|
|
1833
|
+
if (err.code !== "ENOENT") {
|
|
1834
|
+
throw err;
|
|
1835
|
+
}
|
|
1836
|
+
logger.debug(`No _data.json found at ${dataPath}, using empty data`);
|
|
1837
|
+
}
|
|
1838
|
+
return { manifest, data, packageRoot };
|
|
1839
|
+
}
|
|
1840
|
+
|
|
1841
|
+
// src/core/ui-installer.ts
|
|
1842
|
+
import * as path13 from "path";
|
|
1843
|
+
import * as fs11 from "fs/promises";
|
|
1844
|
+
import { resolveUiEntryOrder } from "@teamix-evo/registry";
|
|
1845
|
+
|
|
1846
|
+
// src/utils/transform-imports.ts
|
|
1847
|
+
var SOURCE_ROOT_TO_ALIAS_KEY = {
|
|
1848
|
+
components: "components",
|
|
1849
|
+
hooks: "hooks",
|
|
1850
|
+
utils: "utils",
|
|
1851
|
+
lib: "lib"
|
|
1852
|
+
};
|
|
1853
|
+
function rewriteImports(source, aliases) {
|
|
1854
|
+
return source.replace(
|
|
1855
|
+
/(['"])@\/([a-z][a-z0-9-]*)(\/[^'"]*)?\1/g,
|
|
1856
|
+
(full, quote, root, rest) => {
|
|
1857
|
+
const aliasKey = SOURCE_ROOT_TO_ALIAS_KEY[root];
|
|
1858
|
+
if (!aliasKey) return full;
|
|
1859
|
+
const alias = aliases[aliasKey];
|
|
1860
|
+
const normalized = aliasToImportPath(alias);
|
|
1861
|
+
return `${quote}${normalized}${rest ?? ""}${quote}`;
|
|
1862
|
+
}
|
|
1863
|
+
);
|
|
1864
|
+
}
|
|
1865
|
+
function aliasToImportPath(alias) {
|
|
1866
|
+
const trimmed = alias.replace(/^\.\//, "").replace(/\/$/, "");
|
|
1867
|
+
if (trimmed.startsWith("src/")) {
|
|
1868
|
+
return `@/${trimmed.slice("src/".length)}`;
|
|
1869
|
+
}
|
|
1870
|
+
return `@/${trimmed}`;
|
|
1871
|
+
}
|
|
1872
|
+
|
|
1873
|
+
// src/core/ui-installer.ts
|
|
1874
|
+
var DESIGN_COMPONENTS_DIR = ".teamix-evo/design/components";
|
|
1875
|
+
async function installUiEntries(options) {
|
|
1876
|
+
const {
|
|
1877
|
+
projectRoot,
|
|
1878
|
+
manifest,
|
|
1879
|
+
packageRoot,
|
|
1880
|
+
aliases,
|
|
1881
|
+
requested,
|
|
1882
|
+
skipExisting = true
|
|
1883
|
+
} = options;
|
|
1884
|
+
const orderedIds = resolveUiEntryOrder(manifest.entries, requested);
|
|
1885
|
+
const idToEntry = new Map(manifest.entries.map((e) => [e.id, e]));
|
|
1886
|
+
const resources = [];
|
|
1887
|
+
const npmDeps = {};
|
|
1888
|
+
const metaFiles = [];
|
|
1889
|
+
let written = 0;
|
|
1890
|
+
let skipped = 0;
|
|
1891
|
+
for (const id of orderedIds) {
|
|
1892
|
+
const entry = idToEntry.get(id);
|
|
1893
|
+
if (!entry) continue;
|
|
1894
|
+
if (entry.dependencies) {
|
|
1895
|
+
for (const [name, range] of Object.entries(entry.dependencies)) {
|
|
1896
|
+
npmDeps[name] = range;
|
|
1897
|
+
}
|
|
1898
|
+
}
|
|
1899
|
+
for (const file of entry.files) {
|
|
1900
|
+
const targetAbs = resolveTargetPath(projectRoot, aliases, entry, file);
|
|
1901
|
+
const exists = await fileExists(targetAbs);
|
|
1902
|
+
if (exists && skipExisting && (entry.updateStrategy ?? "frozen") === "frozen") {
|
|
1903
|
+
logger.info(` skip (frozen, exists): ${rel(projectRoot, targetAbs)}`);
|
|
1904
|
+
skipped++;
|
|
1905
|
+
continue;
|
|
1906
|
+
}
|
|
1907
|
+
const sourceAbs = path13.resolve(packageRoot, file.source);
|
|
1908
|
+
const raw = await fs11.readFile(sourceAbs, "utf-8");
|
|
1909
|
+
const transformed = rewriteImports(raw, aliases);
|
|
1910
|
+
await writeFileSafe(targetAbs, transformed);
|
|
1911
|
+
written++;
|
|
1912
|
+
logger.info(` write: ${rel(projectRoot, targetAbs)}`);
|
|
1913
|
+
resources.push({
|
|
1914
|
+
id: `${entry.id}:${file.targetName}`,
|
|
1915
|
+
target: targetAbs,
|
|
1916
|
+
hash: computeHash(transformed),
|
|
1917
|
+
strategy: entry.updateStrategy ?? "frozen"
|
|
1918
|
+
});
|
|
1919
|
+
}
|
|
1920
|
+
if (entry.meta) {
|
|
1921
|
+
const metaSourceAbs = path13.resolve(packageRoot, entry.meta);
|
|
1922
|
+
const metaContent = await fs11.readFile(metaSourceAbs, "utf-8");
|
|
1923
|
+
const metaTargetAbs = path13.join(
|
|
1924
|
+
projectRoot,
|
|
1925
|
+
DESIGN_COMPONENTS_DIR,
|
|
1926
|
+
`${entry.id}.meta.md`
|
|
1927
|
+
);
|
|
1928
|
+
await writeFileSafe(metaTargetAbs, metaContent);
|
|
1929
|
+
metaFiles.push(metaTargetAbs);
|
|
1930
|
+
resources.push({
|
|
1931
|
+
id: `${entry.id}:meta`,
|
|
1932
|
+
target: metaTargetAbs,
|
|
1933
|
+
hash: computeHash(metaContent),
|
|
1934
|
+
strategy: "regenerable"
|
|
1935
|
+
});
|
|
1936
|
+
logger.info(` meta: ${rel(projectRoot, metaTargetAbs)}`);
|
|
1937
|
+
}
|
|
1938
|
+
}
|
|
1939
|
+
return {
|
|
1940
|
+
orderedIds,
|
|
1941
|
+
resources,
|
|
1942
|
+
npmDependencies: npmDeps,
|
|
1943
|
+
written,
|
|
1944
|
+
skipped,
|
|
1945
|
+
metaFiles
|
|
1946
|
+
};
|
|
1947
|
+
}
|
|
1948
|
+
function resolveTargetPath(projectRoot, aliases, entry, file) {
|
|
1949
|
+
const aliasDir = aliases[file.targetAlias];
|
|
1950
|
+
if (!aliasDir) {
|
|
1951
|
+
throw new Error(
|
|
1952
|
+
`Entry "${entry.id}" requires alias "${file.targetAlias}" but it is not configured.`
|
|
1953
|
+
);
|
|
1954
|
+
}
|
|
1955
|
+
return path13.join(projectRoot, aliasDir, file.targetName);
|
|
1956
|
+
}
|
|
1957
|
+
function rel(projectRoot, abs) {
|
|
1958
|
+
return path13.relative(projectRoot, abs);
|
|
1959
|
+
}
|
|
1960
|
+
|
|
1961
|
+
// src/core/ui-add.ts
|
|
1962
|
+
var DEFAULT_UI_PACKAGE = "@teamix-evo/ui";
|
|
1963
|
+
async function runUiAdd(options) {
|
|
1964
|
+
const { projectRoot, ids, overwrite } = options;
|
|
1965
|
+
const packageName = options.packageName ?? DEFAULT_UI_PACKAGE;
|
|
1966
|
+
if (ids.length === 0) {
|
|
1967
|
+
throw new Error("At least one entry id must be provided.");
|
|
1968
|
+
}
|
|
1969
|
+
const config = await readProjectConfig(projectRoot);
|
|
1970
|
+
const uiCfg = config?.packages?.ui;
|
|
1971
|
+
if (!config || !uiCfg?.aliases) {
|
|
1972
|
+
throw new Error(
|
|
1973
|
+
"UI not initialized. Run `runUiInit` (or `teamix-evo ui init`) first."
|
|
1974
|
+
);
|
|
1975
|
+
}
|
|
1976
|
+
const { manifest, packageRoot } = await loadUiData(packageName);
|
|
1977
|
+
const knownIds = new Set(manifest.entries.map((e) => e.id));
|
|
1978
|
+
const unknown = ids.filter((id) => !knownIds.has(id));
|
|
1979
|
+
if (unknown.length > 0) {
|
|
1980
|
+
throw new Error(
|
|
1981
|
+
`Unknown entry id(s): ${unknown.map((s) => `"${s}"`).join(", ")}. Run \`teamix-evo ui list\` to see options.`
|
|
1982
|
+
);
|
|
1983
|
+
}
|
|
1984
|
+
const result = await installUiEntries({
|
|
1985
|
+
projectRoot,
|
|
1986
|
+
manifest,
|
|
1987
|
+
packageRoot,
|
|
1988
|
+
aliases: uiCfg.aliases,
|
|
1989
|
+
requested: ids,
|
|
1990
|
+
skipExisting: !overwrite
|
|
1991
|
+
});
|
|
1992
|
+
const installed = await readInstalledManifest(
|
|
1993
|
+
projectRoot
|
|
1994
|
+
) ?? { schemaVersion: 1, installed: [] };
|
|
1995
|
+
const idx = installed.installed.findIndex((p) => p.package === packageName);
|
|
1996
|
+
const prior = idx >= 0 ? installed.installed[idx] : null;
|
|
1997
|
+
const mergedResources = mergeResources(
|
|
1998
|
+
prior?.resources ?? [],
|
|
1999
|
+
result.resources
|
|
2000
|
+
);
|
|
2001
|
+
const entry = {
|
|
2002
|
+
package: packageName,
|
|
2003
|
+
variant: "_flat",
|
|
2004
|
+
version: manifest.version,
|
|
2005
|
+
installedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2006
|
+
resources: mergedResources
|
|
2007
|
+
};
|
|
2008
|
+
if (idx >= 0) installed.installed[idx] = entry;
|
|
2009
|
+
else installed.installed.push(entry);
|
|
2010
|
+
await writeInstalledManifest(projectRoot, installed);
|
|
2011
|
+
if (uiCfg.version !== manifest.version) {
|
|
2012
|
+
uiCfg.version = manifest.version;
|
|
2013
|
+
await writeProjectConfig(projectRoot, config);
|
|
2014
|
+
}
|
|
2015
|
+
return {
|
|
2016
|
+
packageName,
|
|
2017
|
+
orderedIds: result.orderedIds,
|
|
2018
|
+
written: result.written,
|
|
2019
|
+
skipped: result.skipped,
|
|
2020
|
+
metaFiles: result.metaFiles,
|
|
2021
|
+
npmDependencies: result.npmDependencies,
|
|
2022
|
+
resources: result.resources
|
|
2023
|
+
};
|
|
2024
|
+
}
|
|
2025
|
+
function mergeResources(prior, next) {
|
|
2026
|
+
const merged = /* @__PURE__ */ new Map();
|
|
2027
|
+
for (const r of prior) merged.set(r.id, r);
|
|
2028
|
+
for (const r of next) merged.set(r.id, r);
|
|
2029
|
+
return Array.from(merged.values());
|
|
2030
|
+
}
|
|
2031
|
+
|
|
2032
|
+
// src/commands/ui/add.ts
|
|
2033
|
+
var addCommand2 = new Command12("add").description(
|
|
2034
|
+
"\u5B89\u88C5\u4E00\u4E2A\u6216\u591A\u4E2A ui entry\uFF08\u6309 id\uFF0C\u81EA\u52A8\u5C55\u5F00 registryDependencies\uFF09"
|
|
2035
|
+
).argument("<ids...>", 'entry id \u5217\u8868\uFF0C\u5982 "button" "dialog"').option("--overwrite", "\u5373\u4F7F\u76EE\u6807\u6587\u4EF6\u5DF2\u5B58\u5728\u4E5F\u8986\u76D6\uFF08\u7ED5\u8FC7 frozen \u8DF3\u8FC7\uFF09").action(async (ids, opts) => {
|
|
2036
|
+
try {
|
|
2037
|
+
const ide = detectIde();
|
|
2038
|
+
const projectRoot = ide.getProjectRoot();
|
|
2039
|
+
logger.info(`Installing entries: ${ids.join(", ")}`);
|
|
2040
|
+
const result = await runUiAdd({
|
|
2041
|
+
projectRoot,
|
|
2042
|
+
ids,
|
|
2043
|
+
overwrite: opts.overwrite
|
|
2044
|
+
});
|
|
2045
|
+
logger.success(
|
|
2046
|
+
`UI add complete: ${result.written} written, ${result.skipped} skipped, ${result.metaFiles.length} meta.`
|
|
2047
|
+
);
|
|
2048
|
+
logger.info("");
|
|
2049
|
+
logger.info(`Resolved order: ${result.orderedIds.join(" \u2192 ")}`);
|
|
2050
|
+
const npmDeps = Object.entries(result.npmDependencies);
|
|
2051
|
+
if (npmDeps.length > 0) {
|
|
2052
|
+
logger.info("");
|
|
2053
|
+
logger.info("Install npm dependencies in your project:");
|
|
2054
|
+
const installCmd = npmDeps.map(([name, range]) => `${name}@${range}`).join(" ");
|
|
2055
|
+
logger.info(` pnpm add ${installCmd}`);
|
|
2056
|
+
logger.info(` # or: npm install ${installCmd}`);
|
|
2057
|
+
}
|
|
2058
|
+
if (result.metaFiles.length > 0) {
|
|
2059
|
+
logger.info("");
|
|
2060
|
+
logger.info(
|
|
2061
|
+
"Component meta dropped under .teamix-evo/design/components/ (AI-readable)."
|
|
2062
|
+
);
|
|
2063
|
+
}
|
|
2064
|
+
} catch (err) {
|
|
2065
|
+
const message = err.message;
|
|
2066
|
+
if (message.startsWith("UI not initialized")) {
|
|
2067
|
+
logger.error("UI not initialized. Run `npx teamix-evo ui init` first.");
|
|
2068
|
+
} else {
|
|
2069
|
+
logger.error(`Failed to add ui entries: ${message}`);
|
|
2070
|
+
}
|
|
2071
|
+
logger.debug(err.stack ?? "");
|
|
2072
|
+
process.exitCode = 1;
|
|
2073
|
+
}
|
|
2074
|
+
});
|
|
2075
|
+
|
|
2076
|
+
// src/commands/ui/list.ts
|
|
2077
|
+
import { Command as Command13 } from "commander";
|
|
2078
|
+
|
|
2079
|
+
// src/core/ui-list.ts
|
|
2080
|
+
var DEFAULT_UI_PACKAGE2 = "@teamix-evo/ui";
|
|
2081
|
+
async function runUiList(options) {
|
|
2082
|
+
const { projectRoot, installedOnly } = options;
|
|
2083
|
+
const packageName = options.packageName ?? DEFAULT_UI_PACKAGE2;
|
|
2084
|
+
const { manifest } = await loadUiData(packageName);
|
|
2085
|
+
const installedManifest = await readInstalledManifest(projectRoot);
|
|
2086
|
+
const installedIds = /* @__PURE__ */ new Set();
|
|
2087
|
+
const uiPkg = installedManifest?.installed.find(
|
|
2088
|
+
(p) => p.package === packageName
|
|
2089
|
+
);
|
|
2090
|
+
for (const r of uiPkg?.resources ?? []) {
|
|
2091
|
+
const colon = r.id.indexOf(":");
|
|
2092
|
+
installedIds.add(colon >= 0 ? r.id.slice(0, colon) : r.id);
|
|
2093
|
+
}
|
|
2094
|
+
const entries = manifest.entries.filter((e) => !installedOnly || installedIds.has(e.id)).map((e) => ({
|
|
2095
|
+
id: e.id,
|
|
2096
|
+
type: e.type,
|
|
2097
|
+
description: e.description,
|
|
2098
|
+
installed: installedIds.has(e.id)
|
|
2099
|
+
}));
|
|
2100
|
+
return {
|
|
2101
|
+
packageName,
|
|
2102
|
+
total: manifest.entries.length,
|
|
2103
|
+
installedCount: installedIds.size,
|
|
2104
|
+
entries
|
|
2105
|
+
};
|
|
2106
|
+
}
|
|
2107
|
+
|
|
2108
|
+
// src/commands/ui/list.ts
|
|
2109
|
+
var listCommand3 = new Command13("list").description("\u5217\u51FA @teamix-evo/ui \u7684\u6240\u6709 entry \u53CA\u5DF2\u5B89\u88C5\u72B6\u6001").option("--installed", "\u4EC5\u5C55\u793A\u5DF2\u5B89\u88C5\u7684 entry").action(async (opts) => {
|
|
2110
|
+
try {
|
|
2111
|
+
const ide = detectIde();
|
|
2112
|
+
const projectRoot = ide.getProjectRoot();
|
|
2113
|
+
const { entries, total, installedCount } = await runUiList({
|
|
2114
|
+
projectRoot,
|
|
2115
|
+
installedOnly: opts.installed
|
|
2116
|
+
});
|
|
2117
|
+
if (entries.length === 0) {
|
|
2118
|
+
logger.info(
|
|
2119
|
+
opts.installed ? "No ui entries installed." : "No ui entries available."
|
|
2120
|
+
);
|
|
2121
|
+
return;
|
|
2122
|
+
}
|
|
2123
|
+
const idWidth = Math.max(...entries.map((e) => e.id.length), 4);
|
|
2124
|
+
const typeWidth = Math.max(...entries.map((e) => e.type.length), 4);
|
|
2125
|
+
logger.info(
|
|
2126
|
+
`${"ID".padEnd(idWidth)} ${"TYPE".padEnd(
|
|
2127
|
+
typeWidth
|
|
2128
|
+
)} STATUS DESCRIPTION`
|
|
2129
|
+
);
|
|
2130
|
+
logger.info(
|
|
2131
|
+
`${"\u2500".repeat(idWidth)} ${"\u2500".repeat(
|
|
2132
|
+
typeWidth
|
|
2133
|
+
)} \u2500\u2500\u2500\u2500\u2500\u2500\u2500 \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500`
|
|
2134
|
+
);
|
|
2135
|
+
for (const e of entries) {
|
|
2136
|
+
const status = e.installed ? "INSTALLED" : "\u2013";
|
|
2137
|
+
logger.info(
|
|
2138
|
+
`${e.id.padEnd(idWidth)} ${e.type.padEnd(
|
|
2139
|
+
typeWidth
|
|
2140
|
+
)} ${status.padEnd(7)} ${e.description}`
|
|
2141
|
+
);
|
|
2142
|
+
}
|
|
2143
|
+
logger.info("");
|
|
2144
|
+
logger.info(
|
|
2145
|
+
`Total: ${total} entr${total === 1 ? "y" : "ies"}, ${installedCount} installed.`
|
|
2146
|
+
);
|
|
2147
|
+
} catch (err) {
|
|
2148
|
+
logger.error(`Failed to list ui entries: ${err.message}`);
|
|
2149
|
+
logger.debug(err.stack ?? "");
|
|
2150
|
+
process.exitCode = 1;
|
|
2151
|
+
}
|
|
2152
|
+
});
|
|
2153
|
+
|
|
2154
|
+
// src/commands/ui/index.ts
|
|
2155
|
+
var uiCommand = new Command14("ui").description(
|
|
2156
|
+
"\u7BA1\u7406 teamix-evo ui \u7EC4\u4EF6\uFF08\u6E90\u7801\u6CE8\u5165\u5F0F\u5B89\u88C5\uFF0Cshadcn \u98CE\u683C\uFF09"
|
|
2157
|
+
);
|
|
2158
|
+
uiCommand.addCommand(initCommand2);
|
|
2159
|
+
uiCommand.addCommand(addCommand2);
|
|
2160
|
+
uiCommand.addCommand(listCommand3);
|
|
771
2161
|
|
|
772
2162
|
// src/index.ts
|
|
773
|
-
var
|
|
774
|
-
|
|
2163
|
+
var require5 = createRequire4(import.meta.url);
|
|
2164
|
+
var { version } = require5("../package.json");
|
|
2165
|
+
var program = new Command15();
|
|
2166
|
+
program.name("teamix-evo").description("Where ideas evolve. \u2014 AI Coding \u5957\u4EF6").version(version);
|
|
775
2167
|
program.addCommand(designCommand);
|
|
2168
|
+
program.addCommand(skillsCommand);
|
|
2169
|
+
program.addCommand(uiCommand);
|
|
776
2170
|
program.parse();
|
|
777
2171
|
//# sourceMappingURL=index.js.map
|