teamix-evo 0.2.0 → 0.3.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/README.md +76 -50
- package/dist/core/index.d.ts +94 -16
- package/dist/core/index.js +727 -372
- package/dist/core/index.js.map +1 -1
- package/dist/index.js +1650 -1015
- package/dist/index.js.map +1 -1
- package/package.json +11 -5
package/dist/index.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
// src/index.ts
|
|
4
|
-
import { Command as
|
|
5
|
-
import { createRequire as
|
|
4
|
+
import { Command as Command24 } from "commander";
|
|
5
|
+
import { createRequire as createRequire5 } from "module";
|
|
6
6
|
|
|
7
7
|
// src/commands/design/index.ts
|
|
8
|
-
import { Command as
|
|
8
|
+
import { Command as Command6 } from "commander";
|
|
9
9
|
|
|
10
10
|
// src/commands/design/init.ts
|
|
11
11
|
import { Command } from "commander";
|
|
@@ -66,11 +66,19 @@ function detectIde() {
|
|
|
66
66
|
return new QoderAdapter();
|
|
67
67
|
}
|
|
68
68
|
|
|
69
|
-
// src/core/
|
|
70
|
-
import * as
|
|
69
|
+
// src/core/design-init.ts
|
|
70
|
+
import * as path9 from "path";
|
|
71
|
+
import * as fs6 from "fs/promises";
|
|
72
|
+
import { createRequire as createRequire2 } from "module";
|
|
73
|
+
import {
|
|
74
|
+
loadDesignPack,
|
|
75
|
+
loadDesignPackageManifest,
|
|
76
|
+
mergeDefaultAndVariant
|
|
77
|
+
} from "@teamix-evo/registry";
|
|
78
|
+
|
|
79
|
+
// src/utils/fs.ts
|
|
71
80
|
import * as fs from "fs/promises";
|
|
72
|
-
import
|
|
73
|
-
import { loadVariantManifest } from "@teamix-evo/registry";
|
|
81
|
+
import * as path3 from "path";
|
|
74
82
|
|
|
75
83
|
// src/utils/logger.ts
|
|
76
84
|
import { red, yellow, cyan, green, gray } from "kolorist";
|
|
@@ -95,52 +103,20 @@ var logger = {
|
|
|
95
103
|
}
|
|
96
104
|
};
|
|
97
105
|
|
|
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";
|
|
127
|
-
|
|
128
106
|
// src/utils/fs.ts
|
|
129
|
-
import * as fs2 from "fs/promises";
|
|
130
|
-
import * as path4 from "path";
|
|
131
107
|
async function ensureDir(dir) {
|
|
132
|
-
await
|
|
108
|
+
await fs.mkdir(dir, { recursive: true });
|
|
133
109
|
}
|
|
134
110
|
async function writeFileSafe(filePath, content) {
|
|
135
|
-
const dir =
|
|
111
|
+
const dir = path3.dirname(filePath);
|
|
136
112
|
await ensureDir(dir);
|
|
137
113
|
const tmp = filePath + ".tmp";
|
|
138
|
-
await
|
|
139
|
-
await
|
|
114
|
+
await fs.writeFile(tmp, content, "utf-8");
|
|
115
|
+
await fs.rename(tmp, filePath);
|
|
140
116
|
}
|
|
141
117
|
async function readFileOrNull(filePath) {
|
|
142
118
|
try {
|
|
143
|
-
return await
|
|
119
|
+
return await fs.readFile(filePath, "utf-8");
|
|
144
120
|
} catch (err) {
|
|
145
121
|
if (err.code === "ENOENT") {
|
|
146
122
|
return null;
|
|
@@ -154,21 +130,21 @@ async function backupFile(filePath, projectRoot) {
|
|
|
154
130
|
logger.debug(`Skip backup: ${filePath} does not exist`);
|
|
155
131
|
return;
|
|
156
132
|
}
|
|
157
|
-
const rel2 =
|
|
133
|
+
const rel2 = path3.relative(projectRoot, filePath);
|
|
158
134
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
159
|
-
const backupPath =
|
|
135
|
+
const backupPath = path3.join(
|
|
160
136
|
projectRoot,
|
|
161
137
|
".teamix-evo",
|
|
162
138
|
".backups",
|
|
163
139
|
`${rel2}.${timestamp}.bak`
|
|
164
140
|
);
|
|
165
|
-
await ensureDir(
|
|
166
|
-
await
|
|
167
|
-
logger.debug(`Backed up ${rel2} \u2192 ${
|
|
141
|
+
await ensureDir(path3.dirname(backupPath));
|
|
142
|
+
await fs.writeFile(backupPath, content, "utf-8");
|
|
143
|
+
logger.debug(`Backed up ${rel2} \u2192 ${path3.relative(projectRoot, backupPath)}`);
|
|
168
144
|
}
|
|
169
145
|
async function fileExists(filePath) {
|
|
170
146
|
try {
|
|
171
|
-
await
|
|
147
|
+
await fs.access(filePath);
|
|
172
148
|
return true;
|
|
173
149
|
} catch {
|
|
174
150
|
return false;
|
|
@@ -182,157 +158,71 @@ function computeHash(content) {
|
|
|
182
158
|
return `sha256:${hash}`;
|
|
183
159
|
}
|
|
184
160
|
|
|
185
|
-
// src/
|
|
186
|
-
import
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
compiledCache.set(templateContent, compiled);
|
|
202
|
-
}
|
|
203
|
-
return compiled;
|
|
204
|
-
}
|
|
205
|
-
function renderTemplate(templateContent, data) {
|
|
206
|
-
const compiled = getCompiledTemplate(templateContent);
|
|
207
|
-
return compiled(data);
|
|
208
|
-
}
|
|
209
|
-
async function loadTemplateFile(filePath) {
|
|
210
|
-
return fs3.readFile(filePath, "utf-8");
|
|
211
|
-
}
|
|
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);
|
|
161
|
+
// src/core/design-pack-classify.ts
|
|
162
|
+
import * as path4 from "path";
|
|
163
|
+
var TEAMIX_DIR = ".teamix-evo/design";
|
|
164
|
+
var TOKENS_DIR = ".teamix-evo/tokens";
|
|
165
|
+
var TOKENS_PACK_PREFIX = "foundations/tokens/";
|
|
166
|
+
var ROOT_MANAGED_FILES = {
|
|
167
|
+
"DESIGN.md": { target: "DESIGN.md", managedRegions: ["core"] },
|
|
168
|
+
"AGENTS.md": { target: "AGENTS.md", managedRegions: ["teamix-evo"] },
|
|
169
|
+
"CLAUDE.md": { target: "CLAUDE.md", managedRegions: ["teamix-evo"] }
|
|
170
|
+
};
|
|
171
|
+
var FROZEN_FILES = /* @__PURE__ */ new Set([
|
|
172
|
+
"foundations/tokens/tokens.overrides.css"
|
|
173
|
+
]);
|
|
174
|
+
function classifyPackFile(relPath) {
|
|
175
|
+
if (path4.basename(relPath) === "README.md") {
|
|
176
|
+
return null;
|
|
219
177
|
}
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
files.push(...await walkDir(fullPath));
|
|
229
|
-
} else if (entry.isFile()) {
|
|
230
|
-
files.push(fullPath);
|
|
231
|
-
}
|
|
178
|
+
const rootManaged = ROOT_MANAGED_FILES[relPath];
|
|
179
|
+
if (rootManaged) {
|
|
180
|
+
return {
|
|
181
|
+
target: rootManaged.target,
|
|
182
|
+
strategy: "managed",
|
|
183
|
+
managedRegions: rootManaged.managedRegions,
|
|
184
|
+
isFrozen: false
|
|
185
|
+
};
|
|
232
186
|
}
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
const { projectRoot, manifest, data, variantDir, packageRoot } = options;
|
|
239
|
-
const installedResources = [];
|
|
240
|
-
for (const resource of manifest.resources) {
|
|
241
|
-
logger.debug(`Installing resource: ${resource.id} \u2192 ${resource.target}`);
|
|
242
|
-
if (resource.recursive) {
|
|
243
|
-
const results = await installRecursiveResource(
|
|
244
|
-
resource,
|
|
245
|
-
projectRoot,
|
|
246
|
-
data,
|
|
247
|
-
variantDir,
|
|
248
|
-
packageRoot
|
|
249
|
-
);
|
|
250
|
-
installedResources.push(...results);
|
|
251
|
-
} else {
|
|
252
|
-
const result = await installSingleResource(
|
|
253
|
-
resource,
|
|
254
|
-
projectRoot,
|
|
255
|
-
data,
|
|
256
|
-
variantDir,
|
|
257
|
-
packageRoot
|
|
258
|
-
);
|
|
259
|
-
installedResources.push(result);
|
|
187
|
+
if (relPath.startsWith(TOKENS_PACK_PREFIX)) {
|
|
188
|
+
const rel2 = relPath.slice(TOKENS_PACK_PREFIX.length);
|
|
189
|
+
const target = path4.posix.join(TOKENS_DIR, rel2);
|
|
190
|
+
if (FROZEN_FILES.has(relPath)) {
|
|
191
|
+
return { target, strategy: "frozen", isFrozen: true };
|
|
260
192
|
}
|
|
193
|
+
return { target, strategy: "regenerable", isFrozen: false };
|
|
261
194
|
}
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
const sourcePath = resolveSourcePath(
|
|
269
|
-
resource.source,
|
|
270
|
-
variantDir,
|
|
271
|
-
packageRoot
|
|
272
|
-
);
|
|
273
|
-
const targetPath = path6.join(projectRoot, resource.target);
|
|
274
|
-
let content;
|
|
275
|
-
if (resource.template) {
|
|
276
|
-
const templateContent = await loadTemplateFile(sourcePath);
|
|
277
|
-
content = renderTemplate(templateContent, data);
|
|
278
|
-
} else {
|
|
279
|
-
content = await fs5.readFile(sourcePath, "utf-8");
|
|
195
|
+
if (FROZEN_FILES.has(relPath)) {
|
|
196
|
+
return {
|
|
197
|
+
target: path4.posix.join(TEAMIX_DIR, relPath),
|
|
198
|
+
strategy: "frozen",
|
|
199
|
+
isFrozen: true
|
|
200
|
+
};
|
|
280
201
|
}
|
|
281
|
-
await writeFileSafe(targetPath, content);
|
|
282
|
-
const hash = computeHash(content);
|
|
283
|
-
logger.debug(` Written: ${resource.target} (${resource.updateStrategy})`);
|
|
284
202
|
return {
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
strategy: resource.updateStrategy
|
|
203
|
+
target: path4.posix.join(TEAMIX_DIR, relPath),
|
|
204
|
+
strategy: "regenerable",
|
|
205
|
+
isFrozen: false
|
|
289
206
|
};
|
|
290
207
|
}
|
|
291
|
-
async function installRecursiveResource(resource, projectRoot, data, variantDir, packageRoot) {
|
|
292
|
-
const sourcePath = resolveSourcePath(
|
|
293
|
-
resource.source,
|
|
294
|
-
variantDir,
|
|
295
|
-
packageRoot
|
|
296
|
-
);
|
|
297
|
-
const targetDir = path6.join(projectRoot, resource.target);
|
|
298
|
-
const results = [];
|
|
299
|
-
await ensureDir(targetDir);
|
|
300
|
-
const entries = await walkDir(sourcePath);
|
|
301
|
-
for (const entry of entries) {
|
|
302
|
-
const relPath = path6.relative(sourcePath, entry);
|
|
303
|
-
let targetFile = path6.join(targetDir, relPath);
|
|
304
|
-
if (resource.template && targetFile.endsWith(".hbs")) {
|
|
305
|
-
targetFile = targetFile.slice(0, -4);
|
|
306
|
-
}
|
|
307
|
-
let content;
|
|
308
|
-
if (resource.template && entry.endsWith(".hbs")) {
|
|
309
|
-
const templateContent = await loadTemplateFile(entry);
|
|
310
|
-
content = renderTemplate(templateContent, data);
|
|
311
|
-
} else {
|
|
312
|
-
content = await fs5.readFile(entry, "utf-8");
|
|
313
|
-
}
|
|
314
|
-
await writeFileSafe(targetFile, content);
|
|
315
|
-
const hash = computeHash(content);
|
|
316
|
-
const targetRel = path6.relative(projectRoot, targetFile);
|
|
317
|
-
results.push({
|
|
318
|
-
id: `${resource.id}:${relPath}`,
|
|
319
|
-
target: targetRel,
|
|
320
|
-
hash,
|
|
321
|
-
strategy: resource.updateStrategy
|
|
322
|
-
});
|
|
323
|
-
logger.debug(` Written: ${targetRel}`);
|
|
324
|
-
}
|
|
325
|
-
return results;
|
|
326
|
-
}
|
|
327
208
|
|
|
328
209
|
// src/core/state.ts
|
|
329
|
-
import * as
|
|
330
|
-
import {
|
|
331
|
-
|
|
210
|
+
import * as path5 from "path";
|
|
211
|
+
import {
|
|
212
|
+
validateConfig,
|
|
213
|
+
validateInstalled,
|
|
214
|
+
validateSkillsLock,
|
|
215
|
+
DesignPackLockSchema
|
|
216
|
+
} from "@teamix-evo/registry";
|
|
217
|
+
var TEAMIX_DIR2 = ".teamix-evo";
|
|
332
218
|
var CONFIG_FILE = "config.json";
|
|
333
219
|
var MANIFEST_FILE = "manifest.json";
|
|
220
|
+
var DESIGN_DIR = "design";
|
|
221
|
+
var DESIGN_LOCK_FILE = "pack.lock.json";
|
|
222
|
+
var SKILLS_DIR = "skills";
|
|
223
|
+
var SKILLS_LOCK_FILE = "manifest.lock.json";
|
|
334
224
|
function getTeamixDir(projectRoot) {
|
|
335
|
-
return
|
|
225
|
+
return path5.join(projectRoot, TEAMIX_DIR2);
|
|
336
226
|
}
|
|
337
227
|
async function ensureTeamixDir(projectRoot) {
|
|
338
228
|
const dir = getTeamixDir(projectRoot);
|
|
@@ -340,7 +230,7 @@ async function ensureTeamixDir(projectRoot) {
|
|
|
340
230
|
return dir;
|
|
341
231
|
}
|
|
342
232
|
async function readProjectConfig(projectRoot) {
|
|
343
|
-
const configPath =
|
|
233
|
+
const configPath = path5.join(projectRoot, TEAMIX_DIR2, CONFIG_FILE);
|
|
344
234
|
const raw = await readFileOrNull(configPath);
|
|
345
235
|
if (raw === null) return null;
|
|
346
236
|
try {
|
|
@@ -357,12 +247,12 @@ async function readProjectConfig(projectRoot) {
|
|
|
357
247
|
}
|
|
358
248
|
}
|
|
359
249
|
async function writeProjectConfig(projectRoot, config) {
|
|
360
|
-
const configPath =
|
|
250
|
+
const configPath = path5.join(projectRoot, TEAMIX_DIR2, CONFIG_FILE);
|
|
361
251
|
await writeFileSafe(configPath, JSON.stringify(config, null, 2) + "\n");
|
|
362
252
|
logger.debug(`Wrote config \u2192 ${configPath}`);
|
|
363
253
|
}
|
|
364
254
|
async function readInstalledManifest(projectRoot) {
|
|
365
|
-
const manifestPath =
|
|
255
|
+
const manifestPath = path5.join(projectRoot, TEAMIX_DIR2, MANIFEST_FILE);
|
|
366
256
|
const raw = await readFileOrNull(manifestPath);
|
|
367
257
|
if (raw === null) return null;
|
|
368
258
|
try {
|
|
@@ -379,559 +269,94 @@ async function readInstalledManifest(projectRoot) {
|
|
|
379
269
|
}
|
|
380
270
|
}
|
|
381
271
|
async function writeInstalledManifest(projectRoot, manifest) {
|
|
382
|
-
const manifestPath =
|
|
272
|
+
const manifestPath = path5.join(projectRoot, TEAMIX_DIR2, MANIFEST_FILE);
|
|
383
273
|
await writeFileSafe(manifestPath, JSON.stringify(manifest, null, 2) + "\n");
|
|
384
274
|
logger.debug(`Wrote manifest \u2192 ${manifestPath}`);
|
|
385
275
|
}
|
|
386
|
-
|
|
387
|
-
|
|
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({
|
|
276
|
+
async function readDesignPackLock(projectRoot) {
|
|
277
|
+
const lockPath = path5.join(
|
|
405
278
|
projectRoot,
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
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
|
-
|
|
448
|
-
// src/commands/design/init.ts
|
|
449
|
-
var DEFAULT_VARIANT = "opentrek";
|
|
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".`
|
|
279
|
+
TEAMIX_DIR2,
|
|
280
|
+
DESIGN_DIR,
|
|
281
|
+
DESIGN_LOCK_FILE
|
|
458
282
|
);
|
|
459
|
-
|
|
460
|
-
|
|
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) => {
|
|
283
|
+
const raw = await readFileOrNull(lockPath);
|
|
284
|
+
if (raw === null) return null;
|
|
465
285
|
try {
|
|
466
|
-
const
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
`Initializing design system: variant="${variant}", tailwind="${tailwind}"`
|
|
471
|
-
);
|
|
472
|
-
logger.debug(`Project root: ${projectRoot}`);
|
|
473
|
-
logger.debug(`IDE: ${ide.name}`);
|
|
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") {
|
|
483
|
-
logger.warn(
|
|
484
|
-
`Design system already initialized (variant: ${result.existingVariant}). Use "teamix-evo design update" to update.`
|
|
485
|
-
);
|
|
486
|
-
return;
|
|
286
|
+
const parsed = DesignPackLockSchema.safeParse(JSON.parse(raw));
|
|
287
|
+
if (!parsed.success) {
|
|
288
|
+
logger.warn(`Invalid design pack.lock.json: ${parsed.error.message}`);
|
|
289
|
+
return null;
|
|
487
290
|
}
|
|
488
|
-
|
|
489
|
-
`Design system initialized: ${result.packageName} v${result.version}`
|
|
490
|
-
);
|
|
491
|
-
logger.info(` Variant: ${result.variant}`);
|
|
492
|
-
logger.info(` Tailwind: ${result.tailwind}`);
|
|
493
|
-
logger.info(` Resources: ${result.count} files installed`);
|
|
494
|
-
logger.info("");
|
|
495
|
-
logger.info('Run "teamix-evo design update" to update resources later.');
|
|
291
|
+
return parsed.data;
|
|
496
292
|
} catch (err) {
|
|
497
|
-
logger.
|
|
498
|
-
|
|
499
|
-
|
|
293
|
+
logger.warn(
|
|
294
|
+
`Failed to parse design pack.lock.json: ${err.message}`
|
|
295
|
+
);
|
|
296
|
+
return null;
|
|
500
297
|
}
|
|
501
|
-
}
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
async function
|
|
511
|
-
const
|
|
298
|
+
}
|
|
299
|
+
async function readDesignVariant(projectRoot) {
|
|
300
|
+
const lock = await readDesignPackLock(projectRoot);
|
|
301
|
+
return lock?.variant.name ?? null;
|
|
302
|
+
}
|
|
303
|
+
function getSkillsSourceDir(projectRoot, skillName) {
|
|
304
|
+
const base = path5.join(projectRoot, TEAMIX_DIR2, SKILLS_DIR);
|
|
305
|
+
return skillName ? path5.join(base, skillName) : base;
|
|
306
|
+
}
|
|
307
|
+
async function readSkillsLock(projectRoot) {
|
|
308
|
+
const lockPath = path5.join(
|
|
512
309
|
projectRoot,
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
packageRoot,
|
|
517
|
-
installedManifest,
|
|
518
|
-
packageName
|
|
519
|
-
} = options;
|
|
520
|
-
const updatedResources = [];
|
|
521
|
-
const summary = { overwritten: 0, managed: 0, skipped: 0, created: 0 };
|
|
522
|
-
const installedPkg = installedManifest.installed.find(
|
|
523
|
-
(p) => p.package === packageName && p.variant === manifest.variant
|
|
310
|
+
TEAMIX_DIR2,
|
|
311
|
+
SKILLS_DIR,
|
|
312
|
+
SKILLS_LOCK_FILE
|
|
524
313
|
);
|
|
525
|
-
const
|
|
526
|
-
if (
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
const results = await updateRecursiveResource(
|
|
534
|
-
resource,
|
|
535
|
-
projectRoot,
|
|
536
|
-
data,
|
|
537
|
-
variantDir,
|
|
538
|
-
packageRoot,
|
|
539
|
-
installedMap,
|
|
540
|
-
summary
|
|
541
|
-
);
|
|
542
|
-
updatedResources.push(...results);
|
|
543
|
-
} else {
|
|
544
|
-
const result = await updateSingleResource(
|
|
545
|
-
resource,
|
|
546
|
-
projectRoot,
|
|
547
|
-
data,
|
|
548
|
-
variantDir,
|
|
549
|
-
packageRoot,
|
|
550
|
-
installedMap,
|
|
551
|
-
summary
|
|
552
|
-
);
|
|
553
|
-
updatedResources.push(result);
|
|
314
|
+
const raw = await readFileOrNull(lockPath);
|
|
315
|
+
if (raw === null) return null;
|
|
316
|
+
try {
|
|
317
|
+
const data = JSON.parse(raw);
|
|
318
|
+
const result = validateSkillsLock(data);
|
|
319
|
+
if (!result.success) {
|
|
320
|
+
logger.warn(`Invalid skills manifest.lock.json: ${result.error}`);
|
|
321
|
+
return null;
|
|
554
322
|
}
|
|
323
|
+
return result.data;
|
|
324
|
+
} catch (err) {
|
|
325
|
+
logger.warn(
|
|
326
|
+
`Failed to parse skills manifest.lock.json: ${err.message}`
|
|
327
|
+
);
|
|
328
|
+
return null;
|
|
555
329
|
}
|
|
556
|
-
return { resources: updatedResources, summary };
|
|
557
330
|
}
|
|
558
|
-
async function
|
|
559
|
-
const
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
variantDir,
|
|
565
|
-
packageRoot
|
|
331
|
+
async function writeSkillsLock(projectRoot, lock) {
|
|
332
|
+
const lockPath = path5.join(
|
|
333
|
+
projectRoot,
|
|
334
|
+
TEAMIX_DIR2,
|
|
335
|
+
SKILLS_DIR,
|
|
336
|
+
SKILLS_LOCK_FILE
|
|
566
337
|
);
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
const templateContent = await loadTemplateFile(sourcePath);
|
|
570
|
-
newContent = renderTemplate(templateContent, data);
|
|
571
|
-
} else {
|
|
572
|
-
newContent = await fs6.readFile(sourcePath, "utf-8");
|
|
573
|
-
}
|
|
574
|
-
const newHash = computeHash(newContent);
|
|
575
|
-
const action = getUpdateAction(resource.updateStrategy, {
|
|
576
|
-
exists,
|
|
577
|
-
hash: newHash,
|
|
578
|
-
currentHash: installed?.hash
|
|
579
|
-
});
|
|
580
|
-
switch (action) {
|
|
581
|
-
case "skip": {
|
|
582
|
-
logger.debug(` Skip: ${resource.target} (${resource.updateStrategy})`);
|
|
583
|
-
summary.skipped++;
|
|
584
|
-
return installed ?? {
|
|
585
|
-
id: resource.id,
|
|
586
|
-
target: resource.target,
|
|
587
|
-
hash: newHash,
|
|
588
|
-
strategy: resource.updateStrategy
|
|
589
|
-
};
|
|
590
|
-
}
|
|
591
|
-
case "overwrite": {
|
|
592
|
-
if (exists) {
|
|
593
|
-
await backupFile(targetPath, projectRoot);
|
|
594
|
-
summary.overwritten++;
|
|
595
|
-
} else {
|
|
596
|
-
summary.created++;
|
|
597
|
-
}
|
|
598
|
-
await writeFileSafe(targetPath, newContent);
|
|
599
|
-
logger.debug(` ${exists ? "Overwrite" : "Create"}: ${resource.target}`);
|
|
600
|
-
return {
|
|
601
|
-
id: resource.id,
|
|
602
|
-
target: resource.target,
|
|
603
|
-
hash: newHash,
|
|
604
|
-
strategy: resource.updateStrategy
|
|
605
|
-
};
|
|
606
|
-
}
|
|
607
|
-
case "managed-update": {
|
|
608
|
-
const currentContent = await readFileOrNull(targetPath);
|
|
609
|
-
if (currentContent === null) {
|
|
610
|
-
await writeFileSafe(targetPath, newContent);
|
|
611
|
-
summary.created++;
|
|
612
|
-
return {
|
|
613
|
-
id: resource.id,
|
|
614
|
-
target: resource.target,
|
|
615
|
-
hash: computeHash(newContent),
|
|
616
|
-
strategy: resource.updateStrategy
|
|
617
|
-
};
|
|
618
|
-
}
|
|
619
|
-
let updatedContent = currentContent;
|
|
620
|
-
const regionIds = resource.managedRegions ?? [];
|
|
621
|
-
for (const regionId of regionIds) {
|
|
622
|
-
const regionPattern = new RegExp(
|
|
623
|
-
`<!-- teamix-evo:managed:start id="${escapeRegExp(
|
|
624
|
-
regionId
|
|
625
|
-
)}" -->([\\s\\S]*?)<!-- teamix-evo:managed:end id="${escapeRegExp(
|
|
626
|
-
regionId
|
|
627
|
-
)}" -->`
|
|
628
|
-
);
|
|
629
|
-
const match = newContent.match(regionPattern);
|
|
630
|
-
if (match) {
|
|
631
|
-
const regionContent = match[1].replace(/^\n/, "").replace(/\n$/, "");
|
|
632
|
-
try {
|
|
633
|
-
updatedContent = replaceManagedRegion(
|
|
634
|
-
updatedContent,
|
|
635
|
-
regionId,
|
|
636
|
-
regionContent
|
|
637
|
-
);
|
|
638
|
-
} catch {
|
|
639
|
-
logger.warn(
|
|
640
|
-
`Managed region "${regionId}" not found in ${resource.target}. Skipping region.`
|
|
641
|
-
);
|
|
642
|
-
}
|
|
643
|
-
}
|
|
644
|
-
}
|
|
645
|
-
await backupFile(targetPath, projectRoot);
|
|
646
|
-
await writeFileSafe(targetPath, updatedContent);
|
|
647
|
-
summary.managed++;
|
|
648
|
-
return {
|
|
649
|
-
id: resource.id,
|
|
650
|
-
target: resource.target,
|
|
651
|
-
hash: computeHash(updatedContent),
|
|
652
|
-
strategy: resource.updateStrategy
|
|
653
|
-
};
|
|
654
|
-
}
|
|
655
|
-
default:
|
|
656
|
-
summary.skipped++;
|
|
657
|
-
return installed ?? {
|
|
658
|
-
id: resource.id,
|
|
659
|
-
target: resource.target,
|
|
660
|
-
hash: newHash,
|
|
661
|
-
strategy: resource.updateStrategy
|
|
662
|
-
};
|
|
663
|
-
}
|
|
338
|
+
await writeFileSafe(lockPath, JSON.stringify(lock, null, 2) + "\n");
|
|
339
|
+
logger.debug(`Wrote skills lock \u2192 ${lockPath}`);
|
|
664
340
|
}
|
|
665
|
-
async function updateRecursiveResource(resource, projectRoot, data, variantDir, packageRoot, installedMap, summary) {
|
|
666
|
-
const sourcePath = resolveSourcePath(
|
|
667
|
-
resource.source,
|
|
668
|
-
variantDir,
|
|
669
|
-
packageRoot
|
|
670
|
-
);
|
|
671
|
-
const targetDir = path8.join(projectRoot, resource.target);
|
|
672
|
-
const results = [];
|
|
673
|
-
if (resource.updateStrategy === "frozen") {
|
|
674
|
-
const anyInstalled = [...installedMap.keys()].some(
|
|
675
|
-
(k) => k.startsWith(`${resource.id}:`)
|
|
676
|
-
);
|
|
677
|
-
if (anyInstalled) {
|
|
678
|
-
summary.skipped++;
|
|
679
|
-
for (const [id, res] of installedMap) {
|
|
680
|
-
if (id.startsWith(`${resource.id}:`)) {
|
|
681
|
-
results.push(res);
|
|
682
|
-
}
|
|
683
|
-
}
|
|
684
|
-
return results;
|
|
685
|
-
}
|
|
686
|
-
}
|
|
687
|
-
await ensureDir(targetDir);
|
|
688
|
-
const entries = await walkDir(sourcePath);
|
|
689
|
-
for (const entry of entries) {
|
|
690
|
-
const relPath = path8.relative(sourcePath, entry);
|
|
691
|
-
let targetFile = path8.join(targetDir, relPath);
|
|
692
|
-
if (resource.template && targetFile.endsWith(".hbs")) {
|
|
693
|
-
targetFile = targetFile.slice(0, -4);
|
|
694
|
-
}
|
|
695
|
-
let content;
|
|
696
|
-
if (resource.template && entry.endsWith(".hbs")) {
|
|
697
|
-
const templateContent = await loadTemplateFile(entry);
|
|
698
|
-
content = renderTemplate(templateContent, data);
|
|
699
|
-
} else {
|
|
700
|
-
content = await fs6.readFile(entry, "utf-8");
|
|
701
|
-
}
|
|
702
|
-
await writeFileSafe(targetFile, content);
|
|
703
|
-
const hash = computeHash(content);
|
|
704
|
-
const targetRel = path8.relative(projectRoot, targetFile);
|
|
705
|
-
results.push({
|
|
706
|
-
id: `${resource.id}:${relPath}`,
|
|
707
|
-
target: targetRel,
|
|
708
|
-
hash,
|
|
709
|
-
strategy: resource.updateStrategy
|
|
710
|
-
});
|
|
711
|
-
summary.overwritten++;
|
|
712
|
-
}
|
|
713
|
-
return results;
|
|
714
|
-
}
|
|
715
|
-
function escapeRegExp(str) {
|
|
716
|
-
return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
717
|
-
}
|
|
718
|
-
|
|
719
|
-
// src/commands/design/update.ts
|
|
720
|
-
var DESIGN_PACKAGE = "@teamix-evo/design";
|
|
721
|
-
var updateCommand = new Command2("update").description("\u66F4\u65B0\u8BBE\u8BA1\u4F53\u7CFB\u8D44\u6E90").action(async () => {
|
|
722
|
-
try {
|
|
723
|
-
const ide = detectIde();
|
|
724
|
-
const projectRoot = ide.getProjectRoot();
|
|
725
|
-
logger.info("Updating design system resources...");
|
|
726
|
-
const config = await readProjectConfig(projectRoot);
|
|
727
|
-
if (!config?.packages?.design) {
|
|
728
|
-
logger.error(
|
|
729
|
-
'Design system not initialized. Run "teamix-evo design init" first.'
|
|
730
|
-
);
|
|
731
|
-
process.exitCode = 1;
|
|
732
|
-
return;
|
|
733
|
-
}
|
|
734
|
-
const { variant, version: currentVersion } = config.packages.design;
|
|
735
|
-
const installedManifest = await readInstalledManifest(projectRoot);
|
|
736
|
-
if (!installedManifest) {
|
|
737
|
-
logger.error(
|
|
738
|
-
'No installed manifest found. Try re-initializing with "teamix-evo design init".'
|
|
739
|
-
);
|
|
740
|
-
process.exitCode = 1;
|
|
741
|
-
return;
|
|
742
|
-
}
|
|
743
|
-
logger.info(`Loading variant "${variant}" from ${DESIGN_PACKAGE}...`);
|
|
744
|
-
const { manifest, data, variantDir, packageRoot } = await loadVariantData(DESIGN_PACKAGE, variant);
|
|
745
|
-
logger.info(
|
|
746
|
-
`Current: v${currentVersion} \u2192 Available: v${manifest.version}`
|
|
747
|
-
);
|
|
748
|
-
const result = await updateResources({
|
|
749
|
-
projectRoot,
|
|
750
|
-
manifest,
|
|
751
|
-
data,
|
|
752
|
-
variantDir,
|
|
753
|
-
packageRoot,
|
|
754
|
-
installedManifest,
|
|
755
|
-
packageName: DESIGN_PACKAGE
|
|
756
|
-
});
|
|
757
|
-
config.packages.design.version = manifest.version;
|
|
758
|
-
await writeProjectConfig(projectRoot, config);
|
|
759
|
-
const updatedManifest = { ...installedManifest };
|
|
760
|
-
const pkgIdx = updatedManifest.installed.findIndex(
|
|
761
|
-
(p) => p.package === DESIGN_PACKAGE && p.variant === variant
|
|
762
|
-
);
|
|
763
|
-
const pkgEntry = {
|
|
764
|
-
package: DESIGN_PACKAGE,
|
|
765
|
-
variant,
|
|
766
|
-
version: manifest.version,
|
|
767
|
-
installedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
768
|
-
resources: result.resources
|
|
769
|
-
};
|
|
770
|
-
if (pkgIdx >= 0) {
|
|
771
|
-
updatedManifest.installed[pkgIdx] = pkgEntry;
|
|
772
|
-
} else {
|
|
773
|
-
updatedManifest.installed.push(pkgEntry);
|
|
774
|
-
}
|
|
775
|
-
await writeInstalledManifest(projectRoot, updatedManifest);
|
|
776
|
-
const { summary } = result;
|
|
777
|
-
logger.success(
|
|
778
|
-
`Design system updated to v${manifest.version}`
|
|
779
|
-
);
|
|
780
|
-
logger.info(` Created: ${summary.created}`);
|
|
781
|
-
logger.info(` Overwritten: ${summary.overwritten}`);
|
|
782
|
-
logger.info(` Managed: ${summary.managed}`);
|
|
783
|
-
logger.info(` Skipped: ${summary.skipped}`);
|
|
784
|
-
} catch (err) {
|
|
785
|
-
logger.error(`Failed to update: ${err.message}`);
|
|
786
|
-
logger.debug(err.stack ?? "");
|
|
787
|
-
process.exitCode = 1;
|
|
788
|
-
}
|
|
789
|
-
});
|
|
790
|
-
|
|
791
|
-
// src/commands/design/list.ts
|
|
792
|
-
import { Command as Command3 } from "commander";
|
|
793
|
-
var listCommand = new Command3("list").description("\u5217\u51FA\u5DF2\u5B89\u88C5\u7684\u8BBE\u8BA1\u53D8\u4F53").action(async () => {
|
|
794
|
-
try {
|
|
795
|
-
const ide = detectIde();
|
|
796
|
-
const projectRoot = ide.getProjectRoot();
|
|
797
|
-
const config = await readProjectConfig(projectRoot);
|
|
798
|
-
if (!config?.packages?.design) {
|
|
799
|
-
logger.info("No design system installed.");
|
|
800
|
-
logger.info('Run "teamix-evo design init [variant]" to get started.');
|
|
801
|
-
return;
|
|
802
|
-
}
|
|
803
|
-
const { variant, version: version2 } = config.packages.design;
|
|
804
|
-
logger.info("Installed design system:");
|
|
805
|
-
logger.info(` Package: @teamix-evo/design`);
|
|
806
|
-
logger.info(` Variant: ${variant}`);
|
|
807
|
-
logger.info(` Version: ${version2}`);
|
|
808
|
-
logger.info(` IDE: ${config.ide}`);
|
|
809
|
-
const manifest = await readInstalledManifest(projectRoot);
|
|
810
|
-
if (manifest) {
|
|
811
|
-
const pkg = manifest.installed.find(
|
|
812
|
-
(p) => p.package === "@teamix-evo/design" && p.variant === variant
|
|
813
|
-
);
|
|
814
|
-
if (pkg) {
|
|
815
|
-
logger.info(` Resources: ${pkg.resources.length} files`);
|
|
816
|
-
logger.info(
|
|
817
|
-
` Installed: ${new Date(pkg.installedAt).toLocaleString()}`
|
|
818
|
-
);
|
|
819
|
-
}
|
|
820
|
-
}
|
|
821
|
-
} catch (err) {
|
|
822
|
-
logger.error(`Failed to list: ${err.message}`);
|
|
823
|
-
process.exitCode = 1;
|
|
824
|
-
}
|
|
825
|
-
});
|
|
826
|
-
|
|
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
341
|
|
|
917
342
|
// src/core/skills-client.ts
|
|
918
|
-
import * as
|
|
919
|
-
import * as
|
|
920
|
-
import { createRequire
|
|
343
|
+
import * as path6 from "path";
|
|
344
|
+
import * as fs2 from "fs/promises";
|
|
345
|
+
import { createRequire } from "module";
|
|
921
346
|
import { loadSkillsPackageManifest } from "@teamix-evo/registry";
|
|
922
|
-
var
|
|
923
|
-
function
|
|
924
|
-
const pkgJsonPath =
|
|
925
|
-
return
|
|
347
|
+
var require2 = createRequire(import.meta.url);
|
|
348
|
+
function resolvePackageRoot(packageName) {
|
|
349
|
+
const pkgJsonPath = require2.resolve(`${packageName}/package.json`);
|
|
350
|
+
return path6.dirname(pkgJsonPath);
|
|
926
351
|
}
|
|
927
352
|
async function loadSkillsData(packageName) {
|
|
928
|
-
const packageRoot =
|
|
353
|
+
const packageRoot = resolvePackageRoot(packageName);
|
|
929
354
|
logger.debug(`Resolved skills package root: ${packageRoot}`);
|
|
930
355
|
const manifest = await loadSkillsPackageManifest(packageRoot);
|
|
931
356
|
let data = {};
|
|
932
|
-
const dataPath =
|
|
357
|
+
const dataPath = path6.join(packageRoot, "_data.json");
|
|
933
358
|
try {
|
|
934
|
-
const raw = await
|
|
359
|
+
const raw = await fs2.readFile(dataPath, "utf-8");
|
|
935
360
|
data = JSON.parse(raw);
|
|
936
361
|
} catch (err) {
|
|
937
362
|
if (err.code !== "ENOENT") {
|
|
@@ -943,9 +368,56 @@ async function loadSkillsData(packageName) {
|
|
|
943
368
|
}
|
|
944
369
|
|
|
945
370
|
// src/core/skills-installer.ts
|
|
946
|
-
import * as
|
|
947
|
-
import * as
|
|
948
|
-
import { replaceManagedRegion
|
|
371
|
+
import * as path8 from "path";
|
|
372
|
+
import * as fs5 from "fs/promises";
|
|
373
|
+
import { replaceManagedRegion } from "@teamix-evo/registry";
|
|
374
|
+
|
|
375
|
+
// src/utils/template.ts
|
|
376
|
+
import Handlebars from "handlebars";
|
|
377
|
+
import * as fs3 from "fs/promises";
|
|
378
|
+
Handlebars.registerHelper("lowercase", (str) => {
|
|
379
|
+
return typeof str === "string" ? str.toLowerCase() : String(str ?? "").toLowerCase();
|
|
380
|
+
});
|
|
381
|
+
var compiledCache = /* @__PURE__ */ new Map();
|
|
382
|
+
var MAX_CACHE_SIZE = 64;
|
|
383
|
+
function getCompiledTemplate(templateContent) {
|
|
384
|
+
let compiled = compiledCache.get(templateContent);
|
|
385
|
+
if (!compiled) {
|
|
386
|
+
if (compiledCache.size >= MAX_CACHE_SIZE) {
|
|
387
|
+
const firstKey = compiledCache.keys().next().value;
|
|
388
|
+
compiledCache.delete(firstKey);
|
|
389
|
+
}
|
|
390
|
+
compiled = Handlebars.compile(templateContent, { noEscape: true });
|
|
391
|
+
compiledCache.set(templateContent, compiled);
|
|
392
|
+
}
|
|
393
|
+
return compiled;
|
|
394
|
+
}
|
|
395
|
+
function renderTemplate(templateContent, data) {
|
|
396
|
+
const compiled = getCompiledTemplate(templateContent);
|
|
397
|
+
return compiled(data);
|
|
398
|
+
}
|
|
399
|
+
async function loadTemplateFile(filePath) {
|
|
400
|
+
return fs3.readFile(filePath, "utf-8");
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
// src/utils/path.ts
|
|
404
|
+
import * as path7 from "path";
|
|
405
|
+
import * as fs4 from "fs/promises";
|
|
406
|
+
async function walkDir(dir) {
|
|
407
|
+
const files = [];
|
|
408
|
+
const entries = await fs4.readdir(dir, { withFileTypes: true });
|
|
409
|
+
for (const entry of entries) {
|
|
410
|
+
const fullPath = path7.join(dir, entry.name);
|
|
411
|
+
if (entry.isDirectory()) {
|
|
412
|
+
files.push(...await walkDir(fullPath));
|
|
413
|
+
} else if (entry.isFile()) {
|
|
414
|
+
files.push(fullPath);
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
return files;
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
// src/core/skills-installer.ts
|
|
949
421
|
async function installSkills(options) {
|
|
950
422
|
const { manifest, ides, scope, onlyIds } = options;
|
|
951
423
|
const installed = [];
|
|
@@ -962,60 +434,85 @@ async function installSkills(options) {
|
|
|
962
434
|
);
|
|
963
435
|
continue;
|
|
964
436
|
}
|
|
437
|
+
const sourceRecords = await writeSkillSource(skill, options);
|
|
438
|
+
installed.push(...sourceRecords);
|
|
965
439
|
for (const ide of skillIdes) {
|
|
966
|
-
const
|
|
967
|
-
|
|
440
|
+
const mirrorRecords = await mirrorSkillToIde(
|
|
441
|
+
skill,
|
|
442
|
+
ide,
|
|
443
|
+
scope,
|
|
444
|
+
options.projectRoot
|
|
445
|
+
);
|
|
446
|
+
installed.push(...mirrorRecords);
|
|
968
447
|
}
|
|
969
448
|
}
|
|
970
449
|
return { resources: installed, count: installed.length };
|
|
971
450
|
}
|
|
972
|
-
async function
|
|
451
|
+
async function writeSkillSource(skill, options) {
|
|
973
452
|
const { data, packageRoot, projectRoot } = options;
|
|
974
|
-
const
|
|
975
|
-
const targetDir =
|
|
976
|
-
const
|
|
977
|
-
const
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
const targetFile = path11.join(targetDir, "SKILL.md");
|
|
453
|
+
const sourceAbs = path8.resolve(packageRoot, skill.source);
|
|
454
|
+
const targetDir = getSkillsSourceDir(projectRoot, skill.name);
|
|
455
|
+
const stat4 = await fs5.stat(sourceAbs);
|
|
456
|
+
const records = [];
|
|
457
|
+
if (stat4.isFile()) {
|
|
458
|
+
const targetFile = path8.join(targetDir, "SKILL.md");
|
|
981
459
|
const content = await renderSkillContent(sourceAbs, skill, data);
|
|
982
460
|
await writeFileSafe(targetFile, content);
|
|
983
|
-
|
|
984
|
-
logger.debug(` Wrote
|
|
985
|
-
return
|
|
461
|
+
records.push(makeSourceRecord(skill, targetFile, content));
|
|
462
|
+
logger.debug(` Wrote source: ${targetFile}`);
|
|
463
|
+
return records;
|
|
986
464
|
}
|
|
987
465
|
await ensureDir(targetDir);
|
|
988
466
|
const entries = await walkDir(sourceAbs);
|
|
989
467
|
for (const entry of entries) {
|
|
990
|
-
const rel2 =
|
|
991
|
-
let targetFile =
|
|
468
|
+
const rel2 = path8.relative(sourceAbs, entry);
|
|
469
|
+
let targetFile = path8.join(targetDir, rel2);
|
|
992
470
|
if (skill.template && targetFile.endsWith(".hbs")) {
|
|
993
471
|
targetFile = targetFile.slice(0, -4);
|
|
994
472
|
}
|
|
995
|
-
|
|
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
|
-
}
|
|
473
|
+
const content = skill.template && entry.endsWith(".hbs") ? renderTemplate(await loadTemplateFile(entry), { ...data, skill }) : await fs5.readFile(entry, "utf-8");
|
|
1002
474
|
await writeFileSafe(targetFile, content);
|
|
1003
|
-
|
|
1004
|
-
|
|
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 });
|
|
475
|
+
const relWritten = path8.relative(targetDir, targetFile);
|
|
476
|
+
records.push(makeSourceRecord(skill, targetFile, content, relWritten));
|
|
477
|
+
logger.debug(` Wrote source: ${targetFile}`);
|
|
1014
478
|
}
|
|
1015
|
-
return
|
|
479
|
+
return records;
|
|
1016
480
|
}
|
|
1017
|
-
function
|
|
1018
|
-
const
|
|
481
|
+
async function mirrorSkillToIde(skill, ide, scope, projectRoot) {
|
|
482
|
+
const sourceDir = getSkillsSourceDir(projectRoot, skill.name);
|
|
483
|
+
const adapter = getAdapter(ide);
|
|
484
|
+
const targetDir = adapter.getSkillTargetDir(skill.name, scope, projectRoot);
|
|
485
|
+
const records = [];
|
|
486
|
+
const sourceFiles = await walkDir(sourceDir);
|
|
487
|
+
await ensureDir(targetDir);
|
|
488
|
+
for (const src of sourceFiles) {
|
|
489
|
+
const rel2 = path8.relative(sourceDir, src);
|
|
490
|
+
const targetFile = path8.join(targetDir, rel2);
|
|
491
|
+
const content = await fs5.readFile(src, "utf-8");
|
|
492
|
+
await writeFileSafe(targetFile, content);
|
|
493
|
+
records.push(makeMirrorRecord(skill, targetFile, content, ide, scope, rel2));
|
|
494
|
+
logger.debug(` Mirrored ${ide}:${scope}: ${targetFile}`);
|
|
495
|
+
}
|
|
496
|
+
return records;
|
|
497
|
+
}
|
|
498
|
+
async function renderSkillContent(sourceAbs, skill, data) {
|
|
499
|
+
if (skill.template ?? sourceAbs.endsWith(".hbs")) {
|
|
500
|
+
const tpl = await loadTemplateFile(sourceAbs);
|
|
501
|
+
return renderTemplate(tpl, { ...data, skill });
|
|
502
|
+
}
|
|
503
|
+
return fs5.readFile(sourceAbs, "utf-8");
|
|
504
|
+
}
|
|
505
|
+
function makeSourceRecord(skill, targetAbs, content, rel2) {
|
|
506
|
+
const id = rel2 ? `${skill.id}:source:${rel2}` : `${skill.id}:source`;
|
|
507
|
+
return {
|
|
508
|
+
id,
|
|
509
|
+
target: targetAbs,
|
|
510
|
+
hash: computeHash(content),
|
|
511
|
+
strategy: skill.updateStrategy
|
|
512
|
+
};
|
|
513
|
+
}
|
|
514
|
+
function makeMirrorRecord(skill, targetAbs, content, ide, scope, rel2) {
|
|
515
|
+
const id = rel2 && rel2 !== "SKILL.md" ? `${skill.id}:${rel2}` : skill.id;
|
|
1019
516
|
return {
|
|
1020
517
|
id,
|
|
1021
518
|
target: targetAbs,
|
|
@@ -1026,55 +523,46 @@ function makeInstalledRecord(skill, targetAbs, content, ide, scope, rel2) {
|
|
|
1026
523
|
};
|
|
1027
524
|
}
|
|
1028
525
|
async function updateSkills(options) {
|
|
1029
|
-
const { manifest, ides, scope,
|
|
526
|
+
const { manifest, ides, scope, projectRoot } = options;
|
|
1030
527
|
const summary = { overwritten: 0, managed: 0, skipped: 0, created: 0 };
|
|
1031
528
|
const updated = [];
|
|
1032
|
-
const installedMap = /* @__PURE__ */ new Map();
|
|
1033
|
-
for (const r of installed) {
|
|
1034
|
-
installedMap.set(installedKey(r), r);
|
|
1035
|
-
}
|
|
1036
529
|
for (const skill of manifest.skills) {
|
|
1037
530
|
const skillIdes = skill.ides.filter((i) => ides.includes(i));
|
|
531
|
+
if (skillIdes.length === 0) continue;
|
|
532
|
+
const sourceRecords = await rewriteSkillSource(
|
|
533
|
+
skill,
|
|
534
|
+
options,
|
|
535
|
+
summary
|
|
536
|
+
);
|
|
537
|
+
updated.push(...sourceRecords);
|
|
1038
538
|
for (const ide of skillIdes) {
|
|
1039
|
-
const
|
|
539
|
+
const mirrorRecords = await mirrorSkillToIde(
|
|
1040
540
|
skill,
|
|
1041
541
|
ide,
|
|
1042
542
|
scope,
|
|
1043
|
-
|
|
1044
|
-
packageRoot,
|
|
1045
|
-
projectRoot,
|
|
1046
|
-
installedMap,
|
|
1047
|
-
summary
|
|
543
|
+
projectRoot
|
|
1048
544
|
);
|
|
1049
|
-
updated.push(...
|
|
545
|
+
updated.push(...mirrorRecords);
|
|
1050
546
|
}
|
|
1051
547
|
}
|
|
1052
548
|
return { resources: updated, summary };
|
|
1053
549
|
}
|
|
1054
|
-
async function
|
|
1055
|
-
const
|
|
1056
|
-
const
|
|
1057
|
-
const
|
|
1058
|
-
const
|
|
1059
|
-
|
|
1060
|
-
if (!stat2.isFile()) {
|
|
1061
|
-
const entries = await walkDir(sourceAbs);
|
|
550
|
+
async function rewriteSkillSource(skill, options, summary) {
|
|
551
|
+
const { data, packageRoot, projectRoot } = options;
|
|
552
|
+
const sourceAbs = path8.resolve(packageRoot, skill.source);
|
|
553
|
+
const targetDir = getSkillsSourceDir(projectRoot, skill.name);
|
|
554
|
+
const stat4 = await fs5.stat(sourceAbs);
|
|
555
|
+
if (!stat4.isFile()) {
|
|
1062
556
|
await ensureDir(targetDir);
|
|
557
|
+
const entries = await walkDir(sourceAbs);
|
|
558
|
+
const records = [];
|
|
1063
559
|
for (const entry of entries) {
|
|
1064
|
-
const rel2 =
|
|
1065
|
-
let targetFile2 =
|
|
560
|
+
const rel2 = path8.relative(sourceAbs, entry);
|
|
561
|
+
let targetFile2 = path8.join(targetDir, rel2);
|
|
1066
562
|
if (skill.template && targetFile2.endsWith(".hbs")) {
|
|
1067
563
|
targetFile2 = targetFile2.slice(0, -4);
|
|
1068
564
|
}
|
|
1069
|
-
|
|
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
|
-
}
|
|
565
|
+
const content = skill.template && entry.endsWith(".hbs") ? renderTemplate(await loadTemplateFile(entry), { ...data, skill }) : await fs5.readFile(entry, "utf-8");
|
|
1078
566
|
const exists2 = await fileExists(targetFile2);
|
|
1079
567
|
if (exists2) {
|
|
1080
568
|
await backupFile(targetFile2, projectRoot);
|
|
@@ -1083,30 +571,23 @@ async function updateSkillForIde(skill, ide, scope, data, packageRoot, projectRo
|
|
|
1083
571
|
summary.created++;
|
|
1084
572
|
}
|
|
1085
573
|
await writeFileSafe(targetFile2, content);
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
);
|
|
574
|
+
const relWritten = path8.relative(targetDir, targetFile2);
|
|
575
|
+
records.push(makeSourceRecord(skill, targetFile2, content, relWritten));
|
|
1089
576
|
}
|
|
1090
577
|
return records;
|
|
1091
578
|
}
|
|
1092
|
-
const targetFile =
|
|
579
|
+
const targetFile = path8.join(targetDir, "SKILL.md");
|
|
1093
580
|
const newContent = await renderSkillContent(sourceAbs, skill, data);
|
|
1094
581
|
const exists = await fileExists(targetFile);
|
|
1095
|
-
const installedKeyStr = `${skill.id}|${ide}|${scope}`;
|
|
1096
|
-
const prior = installedMap.get(installedKeyStr);
|
|
1097
582
|
if (skill.updateStrategy === "frozen") {
|
|
1098
583
|
if (exists) {
|
|
1099
584
|
summary.skipped++;
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
];
|
|
585
|
+
const current2 = await readFileOrNull(targetFile) ?? newContent;
|
|
586
|
+
return [makeSourceRecord(skill, targetFile, current2)];
|
|
1103
587
|
}
|
|
1104
588
|
await writeFileSafe(targetFile, newContent);
|
|
1105
589
|
summary.created++;
|
|
1106
|
-
|
|
1107
|
-
makeInstalledRecord(skill, targetFile, newContent, ide, scope)
|
|
1108
|
-
);
|
|
1109
|
-
return records;
|
|
590
|
+
return [makeSourceRecord(skill, targetFile, newContent)];
|
|
1110
591
|
}
|
|
1111
592
|
if (skill.updateStrategy === "regenerable" || !exists) {
|
|
1112
593
|
if (exists) {
|
|
@@ -1116,19 +597,15 @@ async function updateSkillForIde(skill, ide, scope, data, packageRoot, projectRo
|
|
|
1116
597
|
summary.created++;
|
|
1117
598
|
}
|
|
1118
599
|
await writeFileSafe(targetFile, newContent);
|
|
1119
|
-
|
|
1120
|
-
makeInstalledRecord(skill, targetFile, newContent, ide, scope)
|
|
1121
|
-
);
|
|
1122
|
-
return records;
|
|
600
|
+
return [makeSourceRecord(skill, targetFile, newContent)];
|
|
1123
601
|
}
|
|
1124
602
|
const current = await readFileOrNull(targetFile);
|
|
1125
|
-
let
|
|
1126
|
-
const
|
|
1127
|
-
for (const regionId of regionIds) {
|
|
603
|
+
let merged = current ?? newContent;
|
|
604
|
+
for (const regionId of skill.managedRegions ?? []) {
|
|
1128
605
|
const re = new RegExp(
|
|
1129
|
-
`<!-- teamix-evo:managed:start id="${
|
|
606
|
+
`<!-- teamix-evo:managed:start id="${escapeRegExp(
|
|
1130
607
|
regionId
|
|
1131
|
-
)}" -->([\\s\\S]*?)<!-- teamix-evo:managed:end id="${
|
|
608
|
+
)}" -->([\\s\\S]*?)<!-- teamix-evo:managed:end id="${escapeRegExp(
|
|
1132
609
|
regionId
|
|
1133
610
|
)}" -->`
|
|
1134
611
|
);
|
|
@@ -1136,7 +613,7 @@ async function updateSkillForIde(skill, ide, scope, data, packageRoot, projectRo
|
|
|
1136
613
|
if (match) {
|
|
1137
614
|
const region = match[1].replace(/^\n/, "").replace(/\n$/, "");
|
|
1138
615
|
try {
|
|
1139
|
-
|
|
616
|
+
merged = replaceManagedRegion(merged, regionId, region);
|
|
1140
617
|
} catch {
|
|
1141
618
|
logger.warn(
|
|
1142
619
|
`Managed region "${regionId}" not found in ${targetFile}. Skipped.`
|
|
@@ -1145,22 +622,57 @@ async function updateSkillForIde(skill, ide, scope, data, packageRoot, projectRo
|
|
|
1145
622
|
}
|
|
1146
623
|
}
|
|
1147
624
|
await backupFile(targetFile, projectRoot);
|
|
1148
|
-
await writeFileSafe(targetFile,
|
|
625
|
+
await writeFileSafe(targetFile, merged);
|
|
1149
626
|
summary.managed++;
|
|
1150
|
-
|
|
1151
|
-
return records;
|
|
627
|
+
return [makeSourceRecord(skill, targetFile, merged)];
|
|
1152
628
|
}
|
|
1153
|
-
function
|
|
1154
|
-
return `${r.id}|${r.ide ?? ""}|${r.scope ?? ""}`;
|
|
1155
|
-
}
|
|
1156
|
-
function escapeRegExp2(str) {
|
|
629
|
+
function escapeRegExp(str) {
|
|
1157
630
|
return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
1158
631
|
}
|
|
632
|
+
async function syncSkillsToIdes(options) {
|
|
633
|
+
const { projectRoot, skills, ides, scope, onlyIds } = options;
|
|
634
|
+
const out = [];
|
|
635
|
+
const targets = skills.filter((s) => !onlyIds || onlyIds.includes(s.id));
|
|
636
|
+
for (const skill of targets) {
|
|
637
|
+
const sourceDir = getSkillsSourceDir(projectRoot, skill.name);
|
|
638
|
+
if (!await fileExists(sourceDir)) {
|
|
639
|
+
logger.warn(
|
|
640
|
+
`Skill "${skill.id}" has no source at ${sourceDir}; skipped.`
|
|
641
|
+
);
|
|
642
|
+
continue;
|
|
643
|
+
}
|
|
644
|
+
for (const ide of ides) {
|
|
645
|
+
const adapter = getAdapter(ide);
|
|
646
|
+
const targetDir = adapter.getSkillTargetDir(
|
|
647
|
+
skill.name,
|
|
648
|
+
scope,
|
|
649
|
+
projectRoot
|
|
650
|
+
);
|
|
651
|
+
await ensureDir(targetDir);
|
|
652
|
+
const sourceFiles = await walkDir(sourceDir);
|
|
653
|
+
for (const src of sourceFiles) {
|
|
654
|
+
const rel2 = path8.relative(sourceDir, src);
|
|
655
|
+
const targetFile = path8.join(targetDir, rel2);
|
|
656
|
+
const content = await fs5.readFile(src, "utf-8");
|
|
657
|
+
await writeFileSafe(targetFile, content);
|
|
658
|
+
out.push({
|
|
659
|
+
id: rel2 === "SKILL.md" ? skill.id : `${skill.id}:${rel2}`,
|
|
660
|
+
target: targetFile,
|
|
661
|
+
hash: computeHash(content),
|
|
662
|
+
strategy: skill.updateStrategy,
|
|
663
|
+
ide,
|
|
664
|
+
scope
|
|
665
|
+
});
|
|
666
|
+
}
|
|
667
|
+
}
|
|
668
|
+
}
|
|
669
|
+
return { resources: out, count: out.length };
|
|
670
|
+
}
|
|
1159
671
|
async function removeSkillFiles(records) {
|
|
1160
672
|
const removed = [];
|
|
1161
673
|
for (const r of records) {
|
|
1162
674
|
try {
|
|
1163
|
-
await
|
|
675
|
+
await fs5.unlink(r.target);
|
|
1164
676
|
removed.push(r.target);
|
|
1165
677
|
} catch (err) {
|
|
1166
678
|
if (err.code !== "ENOENT") {
|
|
@@ -1168,11 +680,11 @@ async function removeSkillFiles(records) {
|
|
|
1168
680
|
}
|
|
1169
681
|
}
|
|
1170
682
|
}
|
|
1171
|
-
const parents = new Set(records.map((r) =>
|
|
683
|
+
const parents = new Set(records.map((r) => path8.dirname(r.target)));
|
|
1172
684
|
for (const dir of parents) {
|
|
1173
685
|
try {
|
|
1174
|
-
const entries = await
|
|
1175
|
-
if (entries.length === 0) await
|
|
686
|
+
const entries = await fs5.readdir(dir);
|
|
687
|
+
if (entries.length === 0) await fs5.rmdir(dir);
|
|
1176
688
|
} catch {
|
|
1177
689
|
}
|
|
1178
690
|
}
|
|
@@ -1202,6 +714,7 @@ async function runSkillsAdd(options) {
|
|
|
1202
714
|
throw new Error("Scope must be specified (project | global).");
|
|
1203
715
|
}
|
|
1204
716
|
const { manifest, data, packageRoot } = await loadSkillsData(packageName);
|
|
717
|
+
const currentDesignVariant = await readDesignVariant(projectRoot);
|
|
1205
718
|
if (isIncremental) {
|
|
1206
719
|
const known = new Set(manifest.skills.map((s) => s.id));
|
|
1207
720
|
const unknown = requestedNames.filter((n) => !known.has(n));
|
|
@@ -1212,102 +725,605 @@ async function runSkillsAdd(options) {
|
|
|
1212
725
|
);
|
|
1213
726
|
}
|
|
1214
727
|
}
|
|
1215
|
-
const existingInstalled = await readInstalledManifest(projectRoot);
|
|
1216
|
-
const existingPkg = existingInstalled?.installed.find(
|
|
1217
|
-
(p) => p.package === packageName
|
|
1218
|
-
);
|
|
1219
|
-
const
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
variant: FLAT_VARIANT,
|
|
1281
|
-
version: manifest.version,
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
};
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
728
|
+
const existingInstalled = await readInstalledManifest(projectRoot);
|
|
729
|
+
const existingPkg = existingInstalled?.installed.find(
|
|
730
|
+
(p) => p.package === packageName
|
|
731
|
+
);
|
|
732
|
+
const existingLock = await readSkillsLock(projectRoot);
|
|
733
|
+
const existingSkillIds = /* @__PURE__ */ new Set([
|
|
734
|
+
...Object.keys(existingLock?.skills ?? {}),
|
|
735
|
+
// Legacy fallback: pre-ADR-0013 installs only had manifest.json. Derive
|
|
736
|
+
// skill ids by stripping the trailing :source / :sub-file suffix.
|
|
737
|
+
...(existingPkg?.resources ?? []).map((r) => r.id.split(":")[0])
|
|
738
|
+
]);
|
|
739
|
+
let onlyIds;
|
|
740
|
+
let skippedSkillIds;
|
|
741
|
+
if (isIncremental) {
|
|
742
|
+
skippedSkillIds = requestedNames.filter((n) => existingSkillIds.has(n));
|
|
743
|
+
onlyIds = requestedNames.filter((n) => !existingSkillIds.has(n));
|
|
744
|
+
} else {
|
|
745
|
+
skippedSkillIds = [];
|
|
746
|
+
onlyIds = manifest.skills.filter((s) => {
|
|
747
|
+
if (!s.variant) return true;
|
|
748
|
+
if (!currentDesignVariant) {
|
|
749
|
+
logger.debug(
|
|
750
|
+
`Skipping variant-bound skill "${s.id}" (variant=${s.variant}): no design pack installed; will be picked up when "design init" runs.`
|
|
751
|
+
);
|
|
752
|
+
return false;
|
|
753
|
+
}
|
|
754
|
+
if (s.variant !== currentDesignVariant) {
|
|
755
|
+
logger.debug(
|
|
756
|
+
`Skipping variant-bound skill "${s.id}" (variant=${s.variant}): current design variant is "${currentDesignVariant}".`
|
|
757
|
+
);
|
|
758
|
+
return false;
|
|
759
|
+
}
|
|
760
|
+
return true;
|
|
761
|
+
}).map((s) => s.id);
|
|
762
|
+
}
|
|
763
|
+
if (isIncremental && onlyIds.length === 0) {
|
|
764
|
+
return {
|
|
765
|
+
status: "installed",
|
|
766
|
+
packageName,
|
|
767
|
+
version: existingSkillsCfg?.version ?? manifest.version,
|
|
768
|
+
ides,
|
|
769
|
+
scope,
|
|
770
|
+
skillCount: 0,
|
|
771
|
+
fileCount: 0,
|
|
772
|
+
resources: [],
|
|
773
|
+
addedSkillIds: [],
|
|
774
|
+
skippedSkillIds
|
|
775
|
+
};
|
|
776
|
+
}
|
|
777
|
+
const result = await installSkills({
|
|
778
|
+
projectRoot,
|
|
779
|
+
manifest,
|
|
780
|
+
data,
|
|
781
|
+
packageRoot,
|
|
782
|
+
ides,
|
|
783
|
+
scope,
|
|
784
|
+
onlyIds
|
|
785
|
+
});
|
|
786
|
+
const config = existingConfig ?? {
|
|
787
|
+
$schema: "https://teamix-evo.dev/schema/config/v1.json",
|
|
788
|
+
schemaVersion: 1,
|
|
789
|
+
ide: ideIdent,
|
|
790
|
+
packages: {}
|
|
791
|
+
};
|
|
792
|
+
config.packages.skills = {
|
|
793
|
+
variant: FLAT_VARIANT,
|
|
794
|
+
version: manifest.version,
|
|
795
|
+
ides,
|
|
796
|
+
scope
|
|
797
|
+
};
|
|
798
|
+
await writeProjectConfig(projectRoot, config);
|
|
799
|
+
const installedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
800
|
+
const installedManifest = existingInstalled ?? {
|
|
801
|
+
schemaVersion: 1,
|
|
802
|
+
installed: []
|
|
803
|
+
};
|
|
804
|
+
const idx = installedManifest.installed.findIndex(
|
|
805
|
+
(p) => p.package === packageName
|
|
806
|
+
);
|
|
807
|
+
const mergedResources = mergeInstalledResources(
|
|
808
|
+
existingPkg?.resources ?? [],
|
|
809
|
+
result.resources
|
|
810
|
+
);
|
|
811
|
+
const entry = {
|
|
812
|
+
package: packageName,
|
|
813
|
+
variant: FLAT_VARIANT,
|
|
814
|
+
version: manifest.version,
|
|
815
|
+
installedAt,
|
|
816
|
+
resources: mergedResources
|
|
817
|
+
};
|
|
818
|
+
if (idx >= 0) installedManifest.installed[idx] = entry;
|
|
819
|
+
else installedManifest.installed.push(entry);
|
|
820
|
+
await writeInstalledManifest(projectRoot, installedManifest);
|
|
821
|
+
const lock = existingLock ?? {
|
|
822
|
+
schemaVersion: 1,
|
|
823
|
+
skills: {}
|
|
824
|
+
};
|
|
825
|
+
for (const skillId of onlyIds) {
|
|
826
|
+
const skillDef = manifest.skills.find((s) => s.id === skillId);
|
|
827
|
+
if (!skillDef) continue;
|
|
828
|
+
const mirroredTo = skillDef.ides.filter((i) => ides.includes(i));
|
|
829
|
+
lock.skills[skillId] = {
|
|
830
|
+
version: skillDef.version,
|
|
831
|
+
from: packageName,
|
|
832
|
+
installedAt,
|
|
833
|
+
scope,
|
|
834
|
+
mirroredTo
|
|
835
|
+
};
|
|
836
|
+
}
|
|
837
|
+
await writeSkillsLock(projectRoot, lock);
|
|
838
|
+
return {
|
|
839
|
+
status: "installed",
|
|
840
|
+
packageName,
|
|
841
|
+
version: manifest.version,
|
|
842
|
+
ides,
|
|
843
|
+
scope,
|
|
844
|
+
skillCount: onlyIds.length,
|
|
845
|
+
fileCount: result.count,
|
|
846
|
+
resources: result.resources,
|
|
847
|
+
addedSkillIds: onlyIds,
|
|
848
|
+
skippedSkillIds
|
|
849
|
+
};
|
|
850
|
+
}
|
|
851
|
+
function mergeInstalledResources(existing, next) {
|
|
852
|
+
const map = /* @__PURE__ */ new Map();
|
|
853
|
+
const key = (r) => `${r.id}|${r.ide ?? ""}|${r.scope ?? ""}`;
|
|
854
|
+
for (const r of existing) map.set(key(r), r);
|
|
855
|
+
for (const r of next) map.set(key(r), r);
|
|
856
|
+
return [...map.values()];
|
|
857
|
+
}
|
|
858
|
+
|
|
859
|
+
// src/core/design-init.ts
|
|
860
|
+
var BASELINE_DESIGN_RULES_SKILL = "teamix-evo-design-rules";
|
|
861
|
+
var DEFAULT_SKILLS_PACKAGE2 = "@teamix-evo/skills";
|
|
862
|
+
var DEFAULT_AUTO_SKILL_IDES = ["qoder", "claude"];
|
|
863
|
+
var DEFAULT_AUTO_SKILL_SCOPE = "project";
|
|
864
|
+
var DEFAULT_DESIGN_PACKAGE = "@teamix-evo/design";
|
|
865
|
+
var require3 = createRequire2(import.meta.url);
|
|
866
|
+
async function runDesignInit(options) {
|
|
867
|
+
const { projectRoot, variant, ide } = options;
|
|
868
|
+
const packageName = options.packageName ?? DEFAULT_DESIGN_PACKAGE;
|
|
869
|
+
await ensureTeamixDir(projectRoot);
|
|
870
|
+
const existingConfig = await readProjectConfig(projectRoot);
|
|
871
|
+
if (existingConfig?.packages?.design) {
|
|
872
|
+
return {
|
|
873
|
+
status: "already-initialized",
|
|
874
|
+
existingVariant: existingConfig.packages.design.variant
|
|
875
|
+
};
|
|
876
|
+
}
|
|
877
|
+
const packageRoot = options.packageRoot ?? resolveDesignPackageRoot(packageName);
|
|
878
|
+
const catalog = await loadDesignPackageManifest(packageRoot);
|
|
879
|
+
const variantEntry = catalog.variants.find((v) => v.name === variant);
|
|
880
|
+
if (!variantEntry) {
|
|
881
|
+
const known = catalog.variants.map((v) => v.name).join(", ");
|
|
882
|
+
throw new Error(
|
|
883
|
+
`Design variant not found: "${variant}". Known variants: ${known || "(none)"}. Hint: run "teamix-evo design list-variants" to see all.`
|
|
884
|
+
);
|
|
885
|
+
}
|
|
886
|
+
const defaultDir = path9.join(packageRoot, "default");
|
|
887
|
+
const variantDir = path9.join(packageRoot, "variants", variant);
|
|
888
|
+
const defaultPack = await loadDesignPack(defaultDir);
|
|
889
|
+
const variantPack = await loadDesignPack(variantDir);
|
|
890
|
+
const merge = await mergeDefaultAndVariant(defaultDir, variantDir);
|
|
891
|
+
const installed = [];
|
|
892
|
+
for (const file of merge.files) {
|
|
893
|
+
const result = await installPackFile(file, projectRoot);
|
|
894
|
+
if (result) installed.push(result);
|
|
895
|
+
}
|
|
896
|
+
const lock = {
|
|
897
|
+
schemaVersion: 1,
|
|
898
|
+
default: { version: defaultPack.version, from: packageName },
|
|
899
|
+
variant: {
|
|
900
|
+
name: variantPack.name,
|
|
901
|
+
displayName: variantPack.displayName,
|
|
902
|
+
version: variantPack.version,
|
|
903
|
+
from: packageName
|
|
904
|
+
},
|
|
905
|
+
linked: variantPack.linked,
|
|
906
|
+
installedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
907
|
+
};
|
|
908
|
+
await writeFileSafe(
|
|
909
|
+
path9.join(projectRoot, ".teamix-evo", "design", "pack.lock.json"),
|
|
910
|
+
JSON.stringify(lock, null, 2) + "\n"
|
|
911
|
+
);
|
|
912
|
+
const config = {
|
|
913
|
+
$schema: "https://teamix-evo.dev/schema/config/v1.json",
|
|
914
|
+
schemaVersion: 1,
|
|
915
|
+
ide,
|
|
916
|
+
packages: {
|
|
917
|
+
design: {
|
|
918
|
+
variant,
|
|
919
|
+
version: variantPack.version,
|
|
920
|
+
tailwind: "v4"
|
|
921
|
+
}
|
|
922
|
+
}
|
|
923
|
+
};
|
|
924
|
+
await writeProjectConfig(projectRoot, config);
|
|
925
|
+
const installedManifest = {
|
|
926
|
+
schemaVersion: 1,
|
|
927
|
+
installed: [
|
|
928
|
+
{
|
|
929
|
+
package: packageName,
|
|
930
|
+
variant,
|
|
931
|
+
version: variantPack.version,
|
|
932
|
+
installedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
933
|
+
resources: installed
|
|
934
|
+
}
|
|
935
|
+
]
|
|
936
|
+
};
|
|
937
|
+
await writeInstalledManifest(projectRoot, installedManifest);
|
|
938
|
+
const skills = await tryAutoInstallVariantSkills({
|
|
939
|
+
projectRoot,
|
|
940
|
+
variant,
|
|
941
|
+
ide
|
|
942
|
+
});
|
|
943
|
+
return {
|
|
944
|
+
status: "installed",
|
|
945
|
+
packageName,
|
|
946
|
+
variant,
|
|
947
|
+
version: variantPack.version,
|
|
948
|
+
count: installed.length,
|
|
949
|
+
resources: installed,
|
|
950
|
+
merge: {
|
|
951
|
+
overrides: merge.overrides,
|
|
952
|
+
variantAdds: merge.variantAdds,
|
|
953
|
+
defaultPassThrough: merge.defaultPassThrough
|
|
954
|
+
},
|
|
955
|
+
skills
|
|
956
|
+
};
|
|
957
|
+
}
|
|
958
|
+
async function tryAutoInstallVariantSkills(args) {
|
|
959
|
+
const { projectRoot, variant, ide } = args;
|
|
960
|
+
const variantSkillId = `${BASELINE_DESIGN_RULES_SKILL}-${variant}`;
|
|
961
|
+
const desired = [BASELINE_DESIGN_RULES_SKILL, variantSkillId];
|
|
962
|
+
let manifestSkillIds;
|
|
963
|
+
try {
|
|
964
|
+
const { manifest } = await loadSkillsData(DEFAULT_SKILLS_PACKAGE2);
|
|
965
|
+
manifestSkillIds = new Set(manifest.skills.map((s) => s.id));
|
|
966
|
+
} catch (err) {
|
|
967
|
+
logger.warn(
|
|
968
|
+
`Skipping skills auto-install: could not load skills manifest (${err.message}).`
|
|
969
|
+
);
|
|
970
|
+
return {
|
|
971
|
+
attempted: [],
|
|
972
|
+
addedSkillIds: [],
|
|
973
|
+
skippedSkillIds: [],
|
|
974
|
+
missing: desired
|
|
975
|
+
};
|
|
976
|
+
}
|
|
977
|
+
const present = desired.filter((id) => manifestSkillIds.has(id));
|
|
978
|
+
const missing = desired.filter((id) => !manifestSkillIds.has(id));
|
|
979
|
+
if (missing.length > 0) {
|
|
980
|
+
logger.warn(
|
|
981
|
+
`Skills auto-install: not found in manifest, skipping: ${missing.join(", ")}.`
|
|
982
|
+
);
|
|
983
|
+
}
|
|
984
|
+
if (present.length === 0) {
|
|
985
|
+
return {
|
|
986
|
+
attempted: desired,
|
|
987
|
+
addedSkillIds: [],
|
|
988
|
+
skippedSkillIds: [],
|
|
989
|
+
missing
|
|
990
|
+
};
|
|
991
|
+
}
|
|
992
|
+
try {
|
|
993
|
+
const result = await runSkillsAdd({
|
|
994
|
+
projectRoot,
|
|
995
|
+
names: present,
|
|
996
|
+
ides: DEFAULT_AUTO_SKILL_IDES,
|
|
997
|
+
scope: DEFAULT_AUTO_SKILL_SCOPE,
|
|
998
|
+
ide
|
|
999
|
+
});
|
|
1000
|
+
if (result.status !== "installed") {
|
|
1001
|
+
return {
|
|
1002
|
+
attempted: desired,
|
|
1003
|
+
addedSkillIds: [],
|
|
1004
|
+
skippedSkillIds: present,
|
|
1005
|
+
missing
|
|
1006
|
+
};
|
|
1007
|
+
}
|
|
1008
|
+
return {
|
|
1009
|
+
attempted: desired,
|
|
1010
|
+
addedSkillIds: result.addedSkillIds,
|
|
1011
|
+
skippedSkillIds: result.skippedSkillIds,
|
|
1012
|
+
missing
|
|
1013
|
+
};
|
|
1014
|
+
} catch (err) {
|
|
1015
|
+
logger.warn(
|
|
1016
|
+
`Skills auto-install failed (continuing): ${err.message}`
|
|
1017
|
+
);
|
|
1018
|
+
return {
|
|
1019
|
+
attempted: desired,
|
|
1020
|
+
addedSkillIds: [],
|
|
1021
|
+
skippedSkillIds: [],
|
|
1022
|
+
missing
|
|
1023
|
+
};
|
|
1024
|
+
}
|
|
1025
|
+
}
|
|
1026
|
+
async function installPackFile(file, projectRoot) {
|
|
1027
|
+
const cls = classifyPackFile(file.relPath);
|
|
1028
|
+
if (cls === null) return null;
|
|
1029
|
+
const targetAbs = path9.join(projectRoot, cls.target);
|
|
1030
|
+
if (cls.isFrozen && await fileExists(targetAbs)) {
|
|
1031
|
+
const existing = await fs6.readFile(targetAbs, "utf-8");
|
|
1032
|
+
return {
|
|
1033
|
+
id: `pack:${file.relPath}`,
|
|
1034
|
+
target: cls.target,
|
|
1035
|
+
hash: computeHash(existing),
|
|
1036
|
+
strategy: cls.strategy
|
|
1037
|
+
};
|
|
1038
|
+
}
|
|
1039
|
+
const content = await fs6.readFile(file.sourcePath, "utf-8");
|
|
1040
|
+
await writeFileSafe(targetAbs, content);
|
|
1041
|
+
return {
|
|
1042
|
+
id: `pack:${file.relPath}`,
|
|
1043
|
+
target: cls.target,
|
|
1044
|
+
hash: computeHash(content),
|
|
1045
|
+
strategy: cls.strategy
|
|
1046
|
+
};
|
|
1047
|
+
}
|
|
1048
|
+
function resolveDesignPackageRoot(packageName) {
|
|
1049
|
+
const pkgJson = require3.resolve(`${packageName}/package.json`);
|
|
1050
|
+
return path9.dirname(pkgJson);
|
|
1051
|
+
}
|
|
1052
|
+
async function listDesignVariants(packageName = DEFAULT_DESIGN_PACKAGE, packageRoot) {
|
|
1053
|
+
const root = packageRoot ?? resolveDesignPackageRoot(packageName);
|
|
1054
|
+
const catalog = await loadDesignPackageManifest(root);
|
|
1055
|
+
return {
|
|
1056
|
+
packageName,
|
|
1057
|
+
defaultDescription: catalog.default.description,
|
|
1058
|
+
variants: catalog.variants.map((v) => ({
|
|
1059
|
+
name: v.name,
|
|
1060
|
+
displayName: v.displayName,
|
|
1061
|
+
version: v.version,
|
|
1062
|
+
description: v.description,
|
|
1063
|
+
linked: v.linked
|
|
1064
|
+
}))
|
|
1065
|
+
};
|
|
1066
|
+
}
|
|
1067
|
+
|
|
1068
|
+
// src/commands/design/init.ts
|
|
1069
|
+
var initCommand = new Command("init").description("\u521D\u59CB\u5316\u8BBE\u8BA1\u4F53\u7CFB\u8D44\u6E90\uFF08\u5FC5\u987B\u663E\u5F0F\u6307\u5B9A\u4E1A\u52A1\u53D8\u4F53\uFF09").argument("<variant>", '\u4E1A\u52A1\u53D8\u4F53\u540D\u79F0\uFF08\u5982 "opentrek"\u3001"uni-manager"\uFF09').action(async (variant) => {
|
|
1070
|
+
try {
|
|
1071
|
+
const ide = detectIde();
|
|
1072
|
+
const projectRoot = ide.getProjectRoot();
|
|
1073
|
+
logger.info(`Initializing design system: variant="${variant}"`);
|
|
1074
|
+
logger.debug(`Project root: ${projectRoot}`);
|
|
1075
|
+
logger.debug(`IDE: ${ide.name}`);
|
|
1076
|
+
logger.info(`Loading variant "${variant}"...`);
|
|
1077
|
+
logger.info("Installing resources...");
|
|
1078
|
+
const result = await runDesignInit({
|
|
1079
|
+
projectRoot,
|
|
1080
|
+
variant,
|
|
1081
|
+
ide: ide.name
|
|
1082
|
+
});
|
|
1083
|
+
if (result.status === "already-initialized") {
|
|
1084
|
+
logger.warn(
|
|
1085
|
+
`Design system already initialized (variant: ${result.existingVariant}). Use "teamix-evo design update" to update.`
|
|
1086
|
+
);
|
|
1087
|
+
return;
|
|
1088
|
+
}
|
|
1089
|
+
logger.success(
|
|
1090
|
+
`Design system initialized: ${result.packageName} (${result.variant} v${result.version})`
|
|
1091
|
+
);
|
|
1092
|
+
logger.info(` Variant: ${result.variant}`);
|
|
1093
|
+
logger.info(` Tailwind: v4`);
|
|
1094
|
+
logger.info(` Resources: ${result.count} files installed`);
|
|
1095
|
+
logger.info(
|
|
1096
|
+
` Merge: ${result.merge.overrides.length} overrides / ${result.merge.variantAdds.length} variant-only / ${result.merge.defaultPassThrough.length} default-passthrough`
|
|
1097
|
+
);
|
|
1098
|
+
if (result.skills) {
|
|
1099
|
+
const { addedSkillIds, skippedSkillIds, missing } = result.skills;
|
|
1100
|
+
if (addedSkillIds.length > 0) {
|
|
1101
|
+
logger.info(
|
|
1102
|
+
` Skills: auto-installed ${addedSkillIds.join(", ")}`
|
|
1103
|
+
);
|
|
1104
|
+
}
|
|
1105
|
+
if (skippedSkillIds.length > 0) {
|
|
1106
|
+
logger.info(
|
|
1107
|
+
` Skills: already installed (skipped) ${skippedSkillIds.join(", ")}`
|
|
1108
|
+
);
|
|
1109
|
+
}
|
|
1110
|
+
if (missing.length > 0) {
|
|
1111
|
+
logger.info(
|
|
1112
|
+
` Skills: not in manifest (skipped) ${missing.join(", ")}`
|
|
1113
|
+
);
|
|
1114
|
+
}
|
|
1115
|
+
}
|
|
1116
|
+
logger.info("");
|
|
1117
|
+
logger.info('Run "teamix-evo design update" to update resources later.');
|
|
1118
|
+
logger.info(
|
|
1119
|
+
'Run "teamix-evo design list-variants" to see all available variants.'
|
|
1120
|
+
);
|
|
1121
|
+
} catch (err) {
|
|
1122
|
+
logger.error(`Failed to initialize: ${err.message}`);
|
|
1123
|
+
logger.debug(err.stack ?? "");
|
|
1124
|
+
process.exitCode = 1;
|
|
1125
|
+
}
|
|
1126
|
+
});
|
|
1127
|
+
|
|
1128
|
+
// src/commands/design/update.ts
|
|
1129
|
+
import { Command as Command2 } from "commander";
|
|
1130
|
+
var updateCommand = new Command2("update").description("(v0.7) \u66F4\u65B0\u8BBE\u8BA1\u4F53\u7CFB\u8D44\u6E90 \u2014 \u5F53\u524D\u672A\u5B9E\u73B0,\u89C1 ADR 0010 / PLAN \xA712.6").action(() => {
|
|
1131
|
+
logger.warn(
|
|
1132
|
+
"design update is not yet implemented for the design pack model (default + variants/, ADR 0010)."
|
|
1133
|
+
);
|
|
1134
|
+
logger.info(
|
|
1135
|
+
"Workaround: clean `.teamix-evo/design/` and re-run `teamix-evo design init <variant>`."
|
|
1136
|
+
);
|
|
1137
|
+
logger.info(
|
|
1138
|
+
"Tracking: PLAN \xA712.6 v0.7 \u2014 semantic upgrade flow + tokens.overrides.css preservation."
|
|
1139
|
+
);
|
|
1140
|
+
process.exitCode = 0;
|
|
1141
|
+
});
|
|
1142
|
+
|
|
1143
|
+
// src/commands/design/list.ts
|
|
1144
|
+
import { Command as Command3 } from "commander";
|
|
1145
|
+
var listCommand = new Command3("list").description("\u5217\u51FA\u5DF2\u5B89\u88C5\u7684\u8BBE\u8BA1\u53D8\u4F53").action(async () => {
|
|
1146
|
+
try {
|
|
1147
|
+
const ide = detectIde();
|
|
1148
|
+
const projectRoot = ide.getProjectRoot();
|
|
1149
|
+
const config = await readProjectConfig(projectRoot);
|
|
1150
|
+
if (!config?.packages?.design) {
|
|
1151
|
+
logger.info("No design system installed.");
|
|
1152
|
+
logger.info('Run "teamix-evo design init [variant]" to get started.');
|
|
1153
|
+
return;
|
|
1154
|
+
}
|
|
1155
|
+
const { variant, version: version2 } = config.packages.design;
|
|
1156
|
+
logger.info("Installed design system:");
|
|
1157
|
+
logger.info(` Package: @teamix-evo/design`);
|
|
1158
|
+
logger.info(` Variant: ${variant}`);
|
|
1159
|
+
logger.info(` Version: ${version2}`);
|
|
1160
|
+
logger.info(` IDE: ${config.ide}`);
|
|
1161
|
+
const manifest = await readInstalledManifest(projectRoot);
|
|
1162
|
+
if (manifest) {
|
|
1163
|
+
const pkg = manifest.installed.find(
|
|
1164
|
+
(p) => p.package === "@teamix-evo/design" && p.variant === variant
|
|
1165
|
+
);
|
|
1166
|
+
if (pkg) {
|
|
1167
|
+
logger.info(` Resources: ${pkg.resources.length} files`);
|
|
1168
|
+
logger.info(
|
|
1169
|
+
` Installed: ${new Date(pkg.installedAt).toLocaleString()}`
|
|
1170
|
+
);
|
|
1171
|
+
}
|
|
1172
|
+
}
|
|
1173
|
+
} catch (err) {
|
|
1174
|
+
logger.error(`Failed to list: ${err.message}`);
|
|
1175
|
+
process.exitCode = 1;
|
|
1176
|
+
}
|
|
1177
|
+
});
|
|
1178
|
+
|
|
1179
|
+
// src/commands/design/list-variants.ts
|
|
1180
|
+
import { Command as Command4 } from "commander";
|
|
1181
|
+
var listVariantsCommand = new Command4("list-variants").description("\u5217\u51FA @teamix-evo/design \u5305\u5185\u63D0\u4F9B\u7684\u6240\u6709\u53D8\u4F53").action(async () => {
|
|
1182
|
+
try {
|
|
1183
|
+
const result = await listDesignVariants();
|
|
1184
|
+
logger.info(`Available design variants in ${result.packageName}:`);
|
|
1185
|
+
logger.info("");
|
|
1186
|
+
logger.info(" default \u2014 B \u7AEF\u901A\u7528\u57FA\u7EBF(\u59CB\u7EC8\u5185\u7F6E,\u65E0\u9700\u9009\u62E9)");
|
|
1187
|
+
if (result.defaultDescription) {
|
|
1188
|
+
logger.info(` ${result.defaultDescription}`);
|
|
1189
|
+
}
|
|
1190
|
+
logger.info("");
|
|
1191
|
+
if (result.variants.length === 0) {
|
|
1192
|
+
logger.info(" (no variants beyond default)");
|
|
1193
|
+
return;
|
|
1194
|
+
}
|
|
1195
|
+
for (const v of result.variants) {
|
|
1196
|
+
logger.info(` ${v.name} (${v.displayName}) \u2014 v${v.version}`);
|
|
1197
|
+
if (v.description) logger.info(` ${v.description}`);
|
|
1198
|
+
if (v.linked) {
|
|
1199
|
+
const links = [];
|
|
1200
|
+
if (v.linked["biz-ui"]) links.push(`biz-ui: ${v.linked["biz-ui"]}`);
|
|
1201
|
+
if (v.linked.templates) links.push(`templates: ${v.linked.templates}`);
|
|
1202
|
+
if (links.length) logger.info(` linked: ${links.join(" / ")}`);
|
|
1203
|
+
}
|
|
1204
|
+
logger.info("");
|
|
1205
|
+
}
|
|
1206
|
+
logger.info("Install a variant: teamix-evo design init <name>");
|
|
1207
|
+
} catch (err) {
|
|
1208
|
+
logger.error(`Failed to list variants: ${err.message}`);
|
|
1209
|
+
logger.debug(err.stack ?? "");
|
|
1210
|
+
process.exitCode = 1;
|
|
1211
|
+
}
|
|
1212
|
+
});
|
|
1213
|
+
|
|
1214
|
+
// src/commands/design/uninstall.ts
|
|
1215
|
+
import { Command as Command5 } from "commander";
|
|
1216
|
+
import * as fs7 from "fs/promises";
|
|
1217
|
+
import * as path10 from "path";
|
|
1218
|
+
import * as prompts from "@clack/prompts";
|
|
1219
|
+
var DESIGN_PACKAGE = "@teamix-evo/design";
|
|
1220
|
+
var uninstallCommand = new Command5("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(
|
|
1221
|
+
"--keep-files",
|
|
1222
|
+
"\u4EC5\u6E05\u7406 .teamix-evo \u4E2D\u7684\u8BB0\u8D26\u4FE1\u606F\uFF0C\u4E0D\u5220\u9664\u5DF2\u843D\u5730\u8D44\u6E90\u6587\u4EF6"
|
|
1223
|
+
).action(async (opts) => {
|
|
1224
|
+
try {
|
|
1225
|
+
const ide = detectIde();
|
|
1226
|
+
const projectRoot = ide.getProjectRoot();
|
|
1227
|
+
const config = await readProjectConfig(projectRoot);
|
|
1228
|
+
if (!config?.packages?.design) {
|
|
1229
|
+
logger.info("Design system is not installed. Nothing to do.");
|
|
1230
|
+
return;
|
|
1231
|
+
}
|
|
1232
|
+
const installedManifest = await readInstalledManifest(projectRoot);
|
|
1233
|
+
const pkg = installedManifest?.installed.find(
|
|
1234
|
+
(p) => p.package === DESIGN_PACKAGE
|
|
1235
|
+
);
|
|
1236
|
+
const resources = pkg?.resources ?? [];
|
|
1237
|
+
const removable = opts.keepFiles ? [] : resources.filter((r) => r.strategy !== "managed");
|
|
1238
|
+
const kept = resources.length - removable.length;
|
|
1239
|
+
logger.info(
|
|
1240
|
+
`Will remove ${removable.length} file(s); keep ${kept} managed file(s).`
|
|
1241
|
+
);
|
|
1242
|
+
if (!opts.yes) {
|
|
1243
|
+
const confirm4 = await prompts.confirm({
|
|
1244
|
+
message: "\u786E\u8BA4\u5378\u8F7D\u8BBE\u8BA1\u4F53\u7CFB\uFF1F",
|
|
1245
|
+
initialValue: false
|
|
1246
|
+
});
|
|
1247
|
+
if (prompts.isCancel(confirm4) || !confirm4) {
|
|
1248
|
+
logger.info("Cancelled.");
|
|
1249
|
+
return;
|
|
1250
|
+
}
|
|
1251
|
+
}
|
|
1252
|
+
let removed = 0;
|
|
1253
|
+
for (const r of removable) {
|
|
1254
|
+
const target = path10.isAbsolute(r.target) ? r.target : path10.join(projectRoot, r.target);
|
|
1255
|
+
try {
|
|
1256
|
+
await fs7.unlink(target);
|
|
1257
|
+
removed++;
|
|
1258
|
+
} catch (err) {
|
|
1259
|
+
if (err.code !== "ENOENT") {
|
|
1260
|
+
logger.warn(
|
|
1261
|
+
`Failed to remove ${target}: ${err.message}`
|
|
1262
|
+
);
|
|
1263
|
+
}
|
|
1264
|
+
}
|
|
1265
|
+
}
|
|
1266
|
+
if (installedManifest) {
|
|
1267
|
+
installedManifest.installed = installedManifest.installed.filter(
|
|
1268
|
+
(p) => p.package !== DESIGN_PACKAGE
|
|
1269
|
+
);
|
|
1270
|
+
await writeInstalledManifest(projectRoot, installedManifest);
|
|
1271
|
+
}
|
|
1272
|
+
delete config.packages.design;
|
|
1273
|
+
await writeProjectConfig(projectRoot, config);
|
|
1274
|
+
logger.success(`Uninstalled ${DESIGN_PACKAGE}`);
|
|
1275
|
+
logger.info(` Removed: ${removed} files`);
|
|
1276
|
+
if (kept > 0) {
|
|
1277
|
+
logger.info(
|
|
1278
|
+
` Kept: ${kept} managed files (you may delete manually)`
|
|
1279
|
+
);
|
|
1280
|
+
}
|
|
1281
|
+
} catch (err) {
|
|
1282
|
+
logger.error(`Failed to uninstall: ${err.message}`);
|
|
1283
|
+
logger.debug(err.stack ?? "");
|
|
1284
|
+
process.exitCode = 1;
|
|
1285
|
+
}
|
|
1286
|
+
});
|
|
1287
|
+
|
|
1288
|
+
// src/commands/design/index.ts
|
|
1289
|
+
var designCommand = new Command6("design").description(
|
|
1290
|
+
"\u7BA1\u7406\u8BBE\u8BA1\u4F53\u7CFB\u8D44\u6E90"
|
|
1291
|
+
);
|
|
1292
|
+
designCommand.addCommand(initCommand);
|
|
1293
|
+
designCommand.addCommand(updateCommand);
|
|
1294
|
+
designCommand.addCommand(listCommand);
|
|
1295
|
+
designCommand.addCommand(listVariantsCommand);
|
|
1296
|
+
designCommand.addCommand(uninstallCommand);
|
|
1297
|
+
|
|
1298
|
+
// src/commands/skills/index.ts
|
|
1299
|
+
import { Command as Command13 } from "commander";
|
|
1300
|
+
|
|
1301
|
+
// src/commands/skills/add.ts
|
|
1302
|
+
import { Command as Command7 } from "commander";
|
|
1303
|
+
import * as prompts2 from "@clack/prompts";
|
|
1304
|
+
|
|
1305
|
+
// src/utils/global-root.ts
|
|
1306
|
+
import { existsSync } from "fs";
|
|
1307
|
+
import * as fs8 from "fs/promises";
|
|
1308
|
+
import * as os3 from "os";
|
|
1309
|
+
import * as path11 from "path";
|
|
1310
|
+
var GLOBAL_META_DIR = ".teamix-evo-global";
|
|
1311
|
+
var TEAMIX_DIR3 = ".teamix-evo";
|
|
1312
|
+
var CONFIG_FILE2 = "config.json";
|
|
1313
|
+
function getGlobalMetaRoot() {
|
|
1314
|
+
return path11.join(os3.homedir(), GLOBAL_META_DIR);
|
|
1300
1315
|
}
|
|
1301
|
-
function
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1316
|
+
function isTeamixEvoProject(dir) {
|
|
1317
|
+
return existsSync(path11.join(dir, TEAMIX_DIR3, CONFIG_FILE2));
|
|
1318
|
+
}
|
|
1319
|
+
async function ensureGlobalMetaRoot() {
|
|
1320
|
+
const root = getGlobalMetaRoot();
|
|
1321
|
+
await fs8.mkdir(root, { recursive: true });
|
|
1322
|
+
return root;
|
|
1307
1323
|
}
|
|
1308
1324
|
|
|
1309
1325
|
// src/commands/skills/add.ts
|
|
1310
|
-
var addCommand = new
|
|
1326
|
+
var addCommand = new Command7("add").description(
|
|
1311
1327
|
"\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
1328
|
).argument(
|
|
1313
1329
|
"[names...]",
|
|
@@ -1318,13 +1334,18 @@ var addCommand = new Command6("add").description(
|
|
|
1318
1334
|
).option("-y, --yes", "\u4F7F\u7528\u9ED8\u8BA4\u503C\uFF0C\u8DF3\u8FC7\u4EA4\u4E92").action(async (names, opts) => {
|
|
1319
1335
|
try {
|
|
1320
1336
|
const ide = detectIde();
|
|
1321
|
-
const
|
|
1337
|
+
const cwd = ide.getProjectRoot();
|
|
1322
1338
|
const isIncremental = names.length > 0;
|
|
1323
1339
|
const { ides, scope } = await resolveIdesAndScope({
|
|
1324
1340
|
opts,
|
|
1325
|
-
projectRoot,
|
|
1341
|
+
projectRoot: cwd,
|
|
1326
1342
|
isIncremental
|
|
1327
1343
|
});
|
|
1344
|
+
let projectRoot = cwd;
|
|
1345
|
+
if (scope === "global" && !isTeamixEvoProject(cwd)) {
|
|
1346
|
+
projectRoot = await ensureGlobalMetaRoot();
|
|
1347
|
+
logger.info(`Global skill install \u2014 meta root: ${projectRoot}`);
|
|
1348
|
+
}
|
|
1328
1349
|
logger.info(
|
|
1329
1350
|
isIncremental ? `Adding skills [${names.join(",")}]: ides=[${ides.join(
|
|
1330
1351
|
","
|
|
@@ -1439,9 +1460,9 @@ function parseScope(input) {
|
|
|
1439
1460
|
}
|
|
1440
1461
|
|
|
1441
1462
|
// src/commands/skills/list.ts
|
|
1442
|
-
import { Command as
|
|
1463
|
+
import { Command as Command8 } from "commander";
|
|
1443
1464
|
var SKILLS_PACKAGE = "@teamix-evo/skills";
|
|
1444
|
-
var listCommand2 = new
|
|
1465
|
+
var listCommand2 = new Command8("list").alias("ls").description(
|
|
1445
1466
|
"\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
1467
|
).option("--installed", "\u4EC5\u5C55\u793A\u5DF2\u5B89\u88C5\u7684 skill\uFF08\u9690\u85CF\u672A\u5B89\u88C5\u9879\uFF09").action(async (opts) => {
|
|
1447
1468
|
try {
|
|
@@ -1521,10 +1542,10 @@ function printInstalledHeader(cfg, installedAt) {
|
|
|
1521
1542
|
}
|
|
1522
1543
|
|
|
1523
1544
|
// src/commands/skills/update.ts
|
|
1524
|
-
import { Command as
|
|
1545
|
+
import { Command as Command9 } from "commander";
|
|
1525
1546
|
var SKILLS_PACKAGE2 = "@teamix-evo/skills";
|
|
1526
1547
|
var FLAT_VARIANT2 = "_flat";
|
|
1527
|
-
var updateCommand2 = new
|
|
1548
|
+
var updateCommand2 = new Command9("update").description("\u66F4\u65B0\u5DF2\u5B89\u88C5\u7684 teamix-evo skills").action(async () => {
|
|
1528
1549
|
try {
|
|
1529
1550
|
const ide = detectIde();
|
|
1530
1551
|
const projectRoot = ide.getProjectRoot();
|
|
@@ -1550,10 +1571,6 @@ var updateCommand2 = new Command8("update").description("\u66F4\u65B0\u5DF2\u5B8
|
|
|
1550
1571
|
const { manifest, data, packageRoot } = await loadSkillsData(
|
|
1551
1572
|
SKILLS_PACKAGE2
|
|
1552
1573
|
);
|
|
1553
|
-
const pkgInstalled = installedManifest.installed.find(
|
|
1554
|
-
(p) => p.package === SKILLS_PACKAGE2
|
|
1555
|
-
);
|
|
1556
|
-
const installedResources = pkgInstalled?.resources ?? [];
|
|
1557
1574
|
logger.info(
|
|
1558
1575
|
`Current: v${skillsEntry.version} \u2192 Available: v${manifest.version}`
|
|
1559
1576
|
);
|
|
@@ -1563,101 +1580,416 @@ var updateCommand2 = new Command8("update").description("\u66F4\u65B0\u5DF2\u5B8
|
|
|
1563
1580
|
data,
|
|
1564
1581
|
packageRoot,
|
|
1565
1582
|
ides,
|
|
1566
|
-
scope
|
|
1567
|
-
installed: installedResources
|
|
1583
|
+
scope
|
|
1568
1584
|
});
|
|
1569
1585
|
config.packages.skills.version = manifest.version;
|
|
1570
1586
|
await writeProjectConfig(projectRoot, config);
|
|
1587
|
+
const installedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
1571
1588
|
const idx = installedManifest.installed.findIndex(
|
|
1572
1589
|
(p) => p.package === SKILLS_PACKAGE2
|
|
1573
1590
|
);
|
|
1574
|
-
const entry = {
|
|
1575
|
-
package: SKILLS_PACKAGE2,
|
|
1576
|
-
variant: FLAT_VARIANT2,
|
|
1577
|
-
version: manifest.version,
|
|
1578
|
-
installedAt
|
|
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
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
|
|
1591
|
+
const entry = {
|
|
1592
|
+
package: SKILLS_PACKAGE2,
|
|
1593
|
+
variant: FLAT_VARIANT2,
|
|
1594
|
+
version: manifest.version,
|
|
1595
|
+
installedAt,
|
|
1596
|
+
resources: result.resources
|
|
1597
|
+
};
|
|
1598
|
+
if (idx >= 0) installedManifest.installed[idx] = entry;
|
|
1599
|
+
else installedManifest.installed.push(entry);
|
|
1600
|
+
await writeInstalledManifest(projectRoot, installedManifest);
|
|
1601
|
+
const existingLock = await readSkillsLock(projectRoot) ?? {
|
|
1602
|
+
schemaVersion: 1,
|
|
1603
|
+
skills: {}
|
|
1604
|
+
};
|
|
1605
|
+
const lock = {
|
|
1606
|
+
schemaVersion: 1,
|
|
1607
|
+
skills: { ...existingLock.skills }
|
|
1608
|
+
};
|
|
1609
|
+
for (const skill of manifest.skills) {
|
|
1610
|
+
const mirroredTo = skill.ides.filter((i) => ides.includes(i));
|
|
1611
|
+
if (mirroredTo.length === 0) continue;
|
|
1612
|
+
lock.skills[skill.id] = {
|
|
1613
|
+
version: skill.version,
|
|
1614
|
+
from: SKILLS_PACKAGE2,
|
|
1615
|
+
installedAt,
|
|
1616
|
+
scope,
|
|
1617
|
+
mirroredTo
|
|
1618
|
+
};
|
|
1619
|
+
}
|
|
1620
|
+
await writeSkillsLock(projectRoot, lock);
|
|
1621
|
+
const { summary } = result;
|
|
1622
|
+
logger.success(`Skills updated to v${manifest.version}`);
|
|
1623
|
+
logger.info(` Created: ${summary.created}`);
|
|
1624
|
+
logger.info(` Overwritten: ${summary.overwritten}`);
|
|
1625
|
+
logger.info(` Managed: ${summary.managed}`);
|
|
1626
|
+
logger.info(` Skipped: ${summary.skipped}`);
|
|
1627
|
+
} catch (err) {
|
|
1628
|
+
logger.error(`Failed to update skills: ${err.message}`);
|
|
1629
|
+
logger.debug(err.stack ?? "");
|
|
1630
|
+
process.exitCode = 1;
|
|
1631
|
+
}
|
|
1632
|
+
});
|
|
1633
|
+
|
|
1634
|
+
// src/commands/skills/uninstall.ts
|
|
1635
|
+
import { Command as Command10 } from "commander";
|
|
1636
|
+
import * as prompts3 from "@clack/prompts";
|
|
1637
|
+
import * as path12 from "path";
|
|
1638
|
+
import * as fs9 from "fs/promises";
|
|
1639
|
+
var SKILLS_PACKAGE3 = "@teamix-evo/skills";
|
|
1640
|
+
var uninstallCommand2 = new Command10("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) => {
|
|
1641
|
+
try {
|
|
1642
|
+
const ide = detectIde();
|
|
1643
|
+
const projectRoot = ide.getProjectRoot();
|
|
1644
|
+
const config = await readProjectConfig(projectRoot);
|
|
1645
|
+
if (!config?.packages?.skills) {
|
|
1646
|
+
logger.info("Skills are not installed. Nothing to do.");
|
|
1647
|
+
return;
|
|
1648
|
+
}
|
|
1649
|
+
const installedManifest = await readInstalledManifest(projectRoot);
|
|
1650
|
+
const pkg = installedManifest?.installed.find(
|
|
1651
|
+
(p) => p.package === SKILLS_PACKAGE3
|
|
1652
|
+
);
|
|
1653
|
+
const resources = pkg?.resources ?? [];
|
|
1654
|
+
logger.info(
|
|
1655
|
+
`Will remove ${resources.length} skill file(s) installed by ${SKILLS_PACKAGE3}.`
|
|
1656
|
+
);
|
|
1657
|
+
if (!opts.yes) {
|
|
1658
|
+
const confirm4 = await prompts3.confirm({
|
|
1659
|
+
message: "\u786E\u8BA4\u5378\u8F7D\uFF1F\u6B64\u64CD\u4F5C\u4F1A\u5220\u9664\u4E0A\u8FF0\u6587\u4EF6\u3002",
|
|
1660
|
+
initialValue: false
|
|
1661
|
+
});
|
|
1662
|
+
if (prompts3.isCancel(confirm4) || !confirm4) {
|
|
1663
|
+
logger.info("Cancelled.");
|
|
1664
|
+
return;
|
|
1665
|
+
}
|
|
1666
|
+
}
|
|
1667
|
+
const removed = await removeSkillFiles(resources);
|
|
1668
|
+
logger.debug(`Removed ${removed.length} files`);
|
|
1669
|
+
const skillsRoot = getSkillsSourceDir(projectRoot);
|
|
1670
|
+
try {
|
|
1671
|
+
await fs9.rm(skillsRoot, { recursive: true, force: true });
|
|
1672
|
+
logger.debug(`Removed source dir ${skillsRoot}`);
|
|
1673
|
+
} catch (err) {
|
|
1674
|
+
logger.warn(
|
|
1675
|
+
`Failed to remove ${skillsRoot}: ${err.message}`
|
|
1676
|
+
);
|
|
1677
|
+
}
|
|
1678
|
+
if (installedManifest && pkg) {
|
|
1679
|
+
installedManifest.installed = installedManifest.installed.filter(
|
|
1680
|
+
(p) => p.package !== SKILLS_PACKAGE3
|
|
1681
|
+
);
|
|
1682
|
+
await writeInstalledManifest(projectRoot, installedManifest);
|
|
1683
|
+
}
|
|
1684
|
+
delete config.packages.skills;
|
|
1685
|
+
await writeProjectConfig(projectRoot, config);
|
|
1686
|
+
logger.success(`Uninstalled ${SKILLS_PACKAGE3}`);
|
|
1687
|
+
logger.info(` Removed: ${removed.length} files`);
|
|
1688
|
+
logger.info(` Source: ${path12.relative(projectRoot, skillsRoot)} (cleaned)`);
|
|
1689
|
+
} catch (err) {
|
|
1690
|
+
logger.error(`Failed to uninstall: ${err.message}`);
|
|
1691
|
+
logger.debug(err.stack ?? "");
|
|
1692
|
+
process.exitCode = 1;
|
|
1693
|
+
}
|
|
1694
|
+
});
|
|
1695
|
+
|
|
1696
|
+
// src/commands/skills/sync.ts
|
|
1697
|
+
import { Command as Command11 } from "commander";
|
|
1698
|
+
|
|
1699
|
+
// src/core/skills-sync.ts
|
|
1700
|
+
import * as fs10 from "fs/promises";
|
|
1701
|
+
var SKILLS_PACKAGE_DEFAULT = "@teamix-evo/skills";
|
|
1702
|
+
async function runSkillsSync(options) {
|
|
1703
|
+
const { projectRoot, names } = options;
|
|
1704
|
+
const lock = await readSkillsLock(projectRoot);
|
|
1705
|
+
if (!lock || Object.keys(lock.skills).length === 0) {
|
|
1706
|
+
return {
|
|
1707
|
+
status: "no-skills",
|
|
1708
|
+
syncedSkillIds: [],
|
|
1709
|
+
fileCount: 0,
|
|
1710
|
+
resources: [],
|
|
1711
|
+
missingSourceIds: []
|
|
1712
|
+
};
|
|
1713
|
+
}
|
|
1714
|
+
const skillIds = Object.keys(lock.skills);
|
|
1715
|
+
const targets = names ? skillIds.filter((id) => names.includes(id)) : skillIds;
|
|
1716
|
+
const allResources = [];
|
|
1717
|
+
const synced = [];
|
|
1718
|
+
const missing = [];
|
|
1719
|
+
for (const skillId of targets) {
|
|
1720
|
+
const lockEntry = lock.skills[skillId];
|
|
1721
|
+
if (!lockEntry) continue;
|
|
1722
|
+
const sourceDir = getSkillsSourceDir(projectRoot, skillId);
|
|
1723
|
+
if (!await dirExists(sourceDir)) {
|
|
1724
|
+
logger.warn(`Skill "${skillId}" has no source at ${sourceDir}; skipped.`);
|
|
1725
|
+
missing.push(skillId);
|
|
1726
|
+
continue;
|
|
1727
|
+
}
|
|
1728
|
+
const ides = options.ides ?? lockEntry.mirroredTo;
|
|
1729
|
+
const scope = options.scope ?? lockEntry.scope;
|
|
1730
|
+
if (ides.length === 0) {
|
|
1731
|
+
logger.warn(`Skill "${skillId}" has no IDE mirror targets; skipped.`);
|
|
1732
|
+
continue;
|
|
1733
|
+
}
|
|
1734
|
+
const result = await syncSkillsToIdes({
|
|
1735
|
+
projectRoot,
|
|
1736
|
+
skills: [
|
|
1737
|
+
{
|
|
1738
|
+
id: skillId,
|
|
1739
|
+
name: skillId,
|
|
1740
|
+
updateStrategy: "regenerable"
|
|
1741
|
+
}
|
|
1742
|
+
],
|
|
1743
|
+
ides,
|
|
1744
|
+
scope
|
|
1745
|
+
});
|
|
1746
|
+
allResources.push(...result.resources);
|
|
1747
|
+
synced.push(skillId);
|
|
1748
|
+
lock.skills[skillId] = {
|
|
1749
|
+
...lockEntry,
|
|
1750
|
+
mirroredTo: ides,
|
|
1751
|
+
scope,
|
|
1752
|
+
installedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
1753
|
+
};
|
|
1754
|
+
}
|
|
1755
|
+
await writeSkillsLock(projectRoot, lock);
|
|
1756
|
+
await refreshMirrorRecords(projectRoot, allResources);
|
|
1757
|
+
return {
|
|
1758
|
+
status: "synced",
|
|
1759
|
+
syncedSkillIds: synced,
|
|
1760
|
+
fileCount: allResources.length,
|
|
1761
|
+
resources: allResources,
|
|
1762
|
+
missingSourceIds: missing
|
|
1763
|
+
};
|
|
1764
|
+
}
|
|
1765
|
+
async function dirExists(p) {
|
|
1766
|
+
try {
|
|
1767
|
+
const stat4 = await fs10.stat(p);
|
|
1768
|
+
return stat4.isDirectory();
|
|
1769
|
+
} catch {
|
|
1770
|
+
return false;
|
|
1771
|
+
}
|
|
1772
|
+
}
|
|
1773
|
+
async function refreshMirrorRecords(projectRoot, newMirrorRecords) {
|
|
1774
|
+
const installed = await readInstalledManifest(projectRoot);
|
|
1775
|
+
if (!installed) return;
|
|
1776
|
+
const pkg = installed.installed.find((p) => p.package === SKILLS_PACKAGE_DEFAULT);
|
|
1777
|
+
if (!pkg) return;
|
|
1778
|
+
const sourceOnly = pkg.resources.filter((r) => r.ide === void 0);
|
|
1779
|
+
pkg.resources = [...sourceOnly, ...newMirrorRecords];
|
|
1780
|
+
pkg.installedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
1781
|
+
await writeInstalledManifest(projectRoot, installed);
|
|
1782
|
+
}
|
|
1783
|
+
|
|
1784
|
+
// src/commands/skills/sync.ts
|
|
1785
|
+
var syncCommand = new Command11("sync").description(
|
|
1786
|
+
"\u628A .teamix-evo/skills/ \u4E0B\u7684\u6E90\u91CD\u65B0\u955C\u50CF\u5230 IDE \u8DEF\u5F84\uFF08.qoder / .claude\uFF09"
|
|
1787
|
+
).argument(
|
|
1788
|
+
"[names...]",
|
|
1789
|
+
"\u53EF\u9009\uFF1A\u4EC5\u540C\u6B65\u6307\u5B9A skill id\uFF1B\u7701\u7565\u5219\u540C\u6B65\u5168\u90E8\u5DF2\u8BB0\u5F55\u5728 lock \u5185\u7684 skill"
|
|
1790
|
+
).option(
|
|
1791
|
+
"--ide <list>",
|
|
1792
|
+
"\u9017\u53F7\u5206\u9694\u7684 IDE \u5217\u8868\uFF08\u8986\u76D6 lock \u4E2D\u8BB0\u5F55\u7684 mirroredTo\uFF09"
|
|
1793
|
+
).option(
|
|
1794
|
+
"--scope <scope>",
|
|
1795
|
+
"project | global\uFF08\u8986\u76D6 lock \u4E2D\u8BB0\u5F55\u7684 scope\uFF09"
|
|
1796
|
+
).action(async (names, opts) => {
|
|
1797
|
+
try {
|
|
1798
|
+
const ide = detectIde();
|
|
1799
|
+
const projectRoot = ide.getProjectRoot();
|
|
1800
|
+
const ides = opts.ide ? parseIdeList2(opts.ide) : void 0;
|
|
1801
|
+
const scope = opts.scope ? parseScope2(opts.scope) : void 0;
|
|
1802
|
+
const result = await runSkillsSync({
|
|
1803
|
+
projectRoot,
|
|
1804
|
+
ides,
|
|
1805
|
+
scope,
|
|
1806
|
+
names: names.length > 0 ? names : void 0
|
|
1807
|
+
});
|
|
1808
|
+
if (result.status === "no-skills") {
|
|
1809
|
+
logger.info(
|
|
1810
|
+
"No skills recorded in .teamix-evo/skills/manifest.lock.json. Nothing to sync."
|
|
1811
|
+
);
|
|
1812
|
+
return;
|
|
1813
|
+
}
|
|
1814
|
+
logger.success(
|
|
1815
|
+
`Synced ${result.syncedSkillIds.length} skill(s) \u2192 ${result.fileCount} file(s)`
|
|
1816
|
+
);
|
|
1817
|
+
if (result.syncedSkillIds.length > 0) {
|
|
1818
|
+
logger.info(` Skills: ${result.syncedSkillIds.join(", ")}`);
|
|
1819
|
+
}
|
|
1820
|
+
if (result.missingSourceIds.length > 0) {
|
|
1821
|
+
logger.warn(
|
|
1822
|
+
` Missing source: ${result.missingSourceIds.join(
|
|
1823
|
+
", "
|
|
1824
|
+
)} (run "skills add <id>" to (re)install)`
|
|
1825
|
+
);
|
|
1826
|
+
}
|
|
1590
1827
|
} catch (err) {
|
|
1591
|
-
logger.error(`Failed to
|
|
1828
|
+
logger.error(`Failed to sync skills: ${err.message}`);
|
|
1592
1829
|
logger.debug(err.stack ?? "");
|
|
1593
1830
|
process.exitCode = 1;
|
|
1594
1831
|
}
|
|
1595
1832
|
});
|
|
1833
|
+
function parseIdeList2(input) {
|
|
1834
|
+
const parts = input.split(",").map((s) => s.trim().toLowerCase()).filter(Boolean);
|
|
1835
|
+
const result = [];
|
|
1836
|
+
for (const p of parts) {
|
|
1837
|
+
if (p === "qoder" || p === "claude") {
|
|
1838
|
+
if (!result.includes(p)) result.push(p);
|
|
1839
|
+
} else {
|
|
1840
|
+
throw new Error(
|
|
1841
|
+
`Unknown IDE: "${p}". Expected one of: ${ALL_IDE_KINDS.join(", ")}.`
|
|
1842
|
+
);
|
|
1843
|
+
}
|
|
1844
|
+
}
|
|
1845
|
+
return result;
|
|
1846
|
+
}
|
|
1847
|
+
function parseScope2(input) {
|
|
1848
|
+
const v = input.toLowerCase();
|
|
1849
|
+
if (v === "project" || v === "global") return v;
|
|
1850
|
+
throw new Error(`Invalid --scope: "${input}". Expected project | global.`);
|
|
1851
|
+
}
|
|
1596
1852
|
|
|
1597
|
-
// src/commands/skills/
|
|
1598
|
-
import { Command as
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1853
|
+
// src/commands/skills/doctor.ts
|
|
1854
|
+
import { Command as Command12 } from "commander";
|
|
1855
|
+
|
|
1856
|
+
// src/core/skills-doctor.ts
|
|
1857
|
+
import * as path13 from "path";
|
|
1858
|
+
import * as fs11 from "fs/promises";
|
|
1859
|
+
async function runSkillsDoctor(options) {
|
|
1860
|
+
const { projectRoot } = options;
|
|
1861
|
+
const lock = await readSkillsLock(projectRoot);
|
|
1862
|
+
if (!lock || Object.keys(lock.skills).length === 0) {
|
|
1863
|
+
return { status: "no-skills", findings: [] };
|
|
1864
|
+
}
|
|
1865
|
+
const findings = [];
|
|
1866
|
+
for (const [skillId, entry] of Object.entries(lock.skills)) {
|
|
1867
|
+
const sourceDir = getSkillsSourceDir(projectRoot, skillId);
|
|
1868
|
+
if (!await dirExists2(sourceDir)) {
|
|
1869
|
+
findings.push({
|
|
1870
|
+
kind: "missing-source",
|
|
1871
|
+
skillId,
|
|
1872
|
+
path: sourceDir,
|
|
1873
|
+
detail: 'Run "teamix-evo skills add" to reinstall.'
|
|
1874
|
+
});
|
|
1875
|
+
continue;
|
|
1876
|
+
}
|
|
1877
|
+
const sourceFiles = await walkDir(sourceDir);
|
|
1878
|
+
const sourceContents = /* @__PURE__ */ new Map();
|
|
1879
|
+
for (const f of sourceFiles) {
|
|
1880
|
+
const rel2 = path13.relative(sourceDir, f);
|
|
1881
|
+
sourceContents.set(rel2, await fs11.readFile(f, "utf-8"));
|
|
1882
|
+
}
|
|
1883
|
+
for (const ide of entry.mirroredTo) {
|
|
1884
|
+
const adapter = getAdapter(ide);
|
|
1885
|
+
const mirrorDir = adapter.getSkillTargetDir(
|
|
1886
|
+
skillId,
|
|
1887
|
+
entry.scope,
|
|
1888
|
+
projectRoot
|
|
1889
|
+
);
|
|
1890
|
+
if (!await dirExists2(mirrorDir)) {
|
|
1891
|
+
findings.push({
|
|
1892
|
+
kind: "missing-mirror",
|
|
1893
|
+
skillId,
|
|
1894
|
+
ide,
|
|
1895
|
+
scope: entry.scope,
|
|
1896
|
+
path: mirrorDir,
|
|
1897
|
+
detail: 'Run "teamix-evo skills sync" to re-mirror.'
|
|
1898
|
+
});
|
|
1899
|
+
continue;
|
|
1900
|
+
}
|
|
1901
|
+
for (const [rel2, sourceContent] of sourceContents.entries()) {
|
|
1902
|
+
const mirrorFile = path13.join(mirrorDir, rel2);
|
|
1903
|
+
if (!await fileExists(mirrorFile)) {
|
|
1904
|
+
findings.push({
|
|
1905
|
+
kind: "missing-mirror",
|
|
1906
|
+
skillId,
|
|
1907
|
+
ide,
|
|
1908
|
+
scope: entry.scope,
|
|
1909
|
+
path: mirrorFile,
|
|
1910
|
+
detail: 'Run "teamix-evo skills sync" to re-mirror.'
|
|
1911
|
+
});
|
|
1912
|
+
continue;
|
|
1913
|
+
}
|
|
1914
|
+
const mirrorContent = await fs11.readFile(mirrorFile, "utf-8");
|
|
1915
|
+
if (computeHash(mirrorContent) !== computeHash(sourceContent)) {
|
|
1916
|
+
findings.push({
|
|
1917
|
+
kind: "mirror-drift",
|
|
1918
|
+
skillId,
|
|
1919
|
+
ide,
|
|
1920
|
+
scope: entry.scope,
|
|
1921
|
+
path: mirrorFile,
|
|
1922
|
+
detail: 'Mirror differs from source. Re-run "teamix-evo skills sync" to overwrite.'
|
|
1923
|
+
});
|
|
1924
|
+
}
|
|
1925
|
+
}
|
|
1926
|
+
}
|
|
1927
|
+
}
|
|
1928
|
+
return {
|
|
1929
|
+
status: findings.length === 0 ? "clean" : "drift",
|
|
1930
|
+
findings
|
|
1931
|
+
};
|
|
1932
|
+
}
|
|
1933
|
+
async function dirExists2(p) {
|
|
1934
|
+
try {
|
|
1935
|
+
const stat4 = await fs11.stat(p);
|
|
1936
|
+
return stat4.isDirectory();
|
|
1937
|
+
} catch {
|
|
1938
|
+
return false;
|
|
1939
|
+
}
|
|
1940
|
+
}
|
|
1941
|
+
|
|
1942
|
+
// src/commands/skills/doctor.ts
|
|
1943
|
+
var doctorCommand = new Command12("doctor").description(
|
|
1944
|
+
"\u68C0\u67E5 .teamix-evo/skills/ \u6E90\u4E0E IDE \u955C\u50CF\u662F\u5426\u6F02\u79FB\uFF1B\u63D0\u793A\u5982\u4F55\u4FEE\u590D"
|
|
1945
|
+
).action(async () => {
|
|
1602
1946
|
try {
|
|
1603
1947
|
const ide = detectIde();
|
|
1604
1948
|
const projectRoot = ide.getProjectRoot();
|
|
1605
|
-
const
|
|
1606
|
-
if (
|
|
1607
|
-
logger.info(
|
|
1949
|
+
const result = await runSkillsDoctor({ projectRoot });
|
|
1950
|
+
if (result.status === "no-skills") {
|
|
1951
|
+
logger.info(
|
|
1952
|
+
'No skills recorded. Run "teamix-evo skills add" first.'
|
|
1953
|
+
);
|
|
1608
1954
|
return;
|
|
1609
1955
|
}
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
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
|
-
}
|
|
1956
|
+
if (result.status === "clean") {
|
|
1957
|
+
logger.success("Skills are in sync. No drift detected.");
|
|
1958
|
+
return;
|
|
1627
1959
|
}
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
);
|
|
1634
|
-
|
|
1960
|
+
logger.warn(
|
|
1961
|
+
`Found ${result.findings.length} drift issue(s). Run "teamix-evo skills sync" to repair mirrors.`
|
|
1962
|
+
);
|
|
1963
|
+
for (const f of result.findings) {
|
|
1964
|
+
const idePart = f.ide ? ` [${f.ide}]` : "";
|
|
1965
|
+
logger.info(` - ${f.kind}${idePart}: ${f.skillId}`);
|
|
1966
|
+
logger.info(` ${f.path}`);
|
|
1967
|
+
if (f.detail) logger.info(` ${f.detail}`);
|
|
1635
1968
|
}
|
|
1636
|
-
|
|
1637
|
-
await writeProjectConfig(projectRoot, config);
|
|
1638
|
-
logger.success(`Uninstalled ${SKILLS_PACKAGE3}`);
|
|
1639
|
-
logger.info(` Removed: ${removed.length} files`);
|
|
1969
|
+
process.exitCode = 1;
|
|
1640
1970
|
} catch (err) {
|
|
1641
|
-
logger.error(`Failed to
|
|
1971
|
+
logger.error(`Failed to run doctor: ${err.message}`);
|
|
1642
1972
|
logger.debug(err.stack ?? "");
|
|
1643
1973
|
process.exitCode = 1;
|
|
1644
1974
|
}
|
|
1645
1975
|
});
|
|
1646
1976
|
|
|
1647
1977
|
// src/commands/skills/index.ts
|
|
1648
|
-
var skillsCommand = new
|
|
1649
|
-
"\u7BA1\u7406 teamix-evo skills\uFF08\u5411 AI IDE \u6CE8\u5165\u6280\u80FD\uFF09"
|
|
1978
|
+
var skillsCommand = new Command13("skills").description(
|
|
1979
|
+
"\u7BA1\u7406 teamix-evo skills\uFF08\u5411 AI IDE \u6CE8\u5165\u6280\u80FD\uFF1Bsource-mirror \u6A21\u578B\u89C1 ADR 0013\uFF09"
|
|
1650
1980
|
);
|
|
1651
1981
|
skillsCommand.addCommand(addCommand);
|
|
1652
1982
|
skillsCommand.addCommand(listCommand2);
|
|
1653
1983
|
skillsCommand.addCommand(updateCommand2);
|
|
1984
|
+
skillsCommand.addCommand(syncCommand);
|
|
1985
|
+
skillsCommand.addCommand(doctorCommand);
|
|
1654
1986
|
skillsCommand.addCommand(uninstallCommand2);
|
|
1655
1987
|
|
|
1656
1988
|
// src/commands/ui/index.ts
|
|
1657
|
-
import { Command as
|
|
1989
|
+
import { Command as Command17 } from "commander";
|
|
1658
1990
|
|
|
1659
1991
|
// src/commands/ui/init.ts
|
|
1660
|
-
import { Command as
|
|
1992
|
+
import { Command as Command14 } from "commander";
|
|
1661
1993
|
import * as prompts4 from "@clack/prompts";
|
|
1662
1994
|
|
|
1663
1995
|
// src/core/ui-init.ts
|
|
@@ -1665,7 +1997,9 @@ var DEFAULT_UI_ALIASES = {
|
|
|
1665
1997
|
components: "src/components/ui",
|
|
1666
1998
|
hooks: "src/hooks",
|
|
1667
1999
|
utils: "src/lib/utils",
|
|
1668
|
-
lib: "src/lib"
|
|
2000
|
+
lib: "src/lib",
|
|
2001
|
+
business: "src/components/business",
|
|
2002
|
+
templates: "src/templates"
|
|
1669
2003
|
};
|
|
1670
2004
|
var DEFAULT_UI_ICON_LIBRARY = "lucide";
|
|
1671
2005
|
async function runUiInit(options) {
|
|
@@ -1680,7 +2014,9 @@ async function runUiInit(options) {
|
|
|
1680
2014
|
components: options.aliases?.components ?? DEFAULT_UI_ALIASES.components,
|
|
1681
2015
|
hooks: options.aliases?.hooks ?? DEFAULT_UI_ALIASES.hooks,
|
|
1682
2016
|
utils: options.aliases?.utils ?? DEFAULT_UI_ALIASES.utils,
|
|
1683
|
-
lib: options.aliases?.lib ?? DEFAULT_UI_ALIASES.lib
|
|
2017
|
+
lib: options.aliases?.lib ?? DEFAULT_UI_ALIASES.lib,
|
|
2018
|
+
business: options.aliases?.business ?? DEFAULT_UI_ALIASES.business,
|
|
2019
|
+
templates: options.aliases?.templates ?? DEFAULT_UI_ALIASES.templates
|
|
1684
2020
|
};
|
|
1685
2021
|
const iconLibrary = options.iconLibrary ?? DEFAULT_UI_ICON_LIBRARY;
|
|
1686
2022
|
const tsx = options.tsx ?? true;
|
|
@@ -1710,7 +2046,7 @@ async function runUiInit(options) {
|
|
|
1710
2046
|
}
|
|
1711
2047
|
|
|
1712
2048
|
// src/commands/ui/init.ts
|
|
1713
|
-
var initCommand2 = new
|
|
2049
|
+
var initCommand2 = new Command14("init").description(
|
|
1714
2050
|
"\u521D\u59CB\u5316 teamix-evo ui \u914D\u7F6E\uFF08\u8BE2\u95EE aliases / iconLibrary / tsx / rsc\uFF09"
|
|
1715
2051
|
).option("-y, --yes", "\u4F7F\u7528\u9ED8\u8BA4\u503C\uFF0C\u8DF3\u8FC7\u4EA4\u4E92").option(
|
|
1716
2052
|
"--components <path>",
|
|
@@ -1757,7 +2093,9 @@ async function resolveConfig(opts) {
|
|
|
1757
2093
|
components: opts.components ?? DEFAULT_UI_ALIASES.components,
|
|
1758
2094
|
hooks: opts.hooks ?? DEFAULT_UI_ALIASES.hooks,
|
|
1759
2095
|
utils: opts.utils ?? DEFAULT_UI_ALIASES.utils,
|
|
1760
|
-
lib: opts.lib ?? DEFAULT_UI_ALIASES.lib
|
|
2096
|
+
lib: opts.lib ?? DEFAULT_UI_ALIASES.lib,
|
|
2097
|
+
business: DEFAULT_UI_ALIASES.business,
|
|
2098
|
+
templates: DEFAULT_UI_ALIASES.templates
|
|
1761
2099
|
},
|
|
1762
2100
|
iconLibrary: opts.iconLibrary ?? "lucide",
|
|
1763
2101
|
tsx: opts.tsx ?? true,
|
|
@@ -1800,7 +2138,14 @@ async function resolveConfig(opts) {
|
|
|
1800
2138
|
});
|
|
1801
2139
|
if (prompts4.isCancel(rscAns)) throw new Error("Cancelled by user.");
|
|
1802
2140
|
return {
|
|
1803
|
-
aliases: {
|
|
2141
|
+
aliases: {
|
|
2142
|
+
components,
|
|
2143
|
+
hooks,
|
|
2144
|
+
utils,
|
|
2145
|
+
lib,
|
|
2146
|
+
business: DEFAULT_UI_ALIASES.business,
|
|
2147
|
+
templates: DEFAULT_UI_ALIASES.templates
|
|
2148
|
+
},
|
|
1804
2149
|
iconLibrary,
|
|
1805
2150
|
tsx: tsxAns,
|
|
1806
2151
|
rsc: rscAns
|
|
@@ -1808,26 +2153,26 @@ async function resolveConfig(opts) {
|
|
|
1808
2153
|
}
|
|
1809
2154
|
|
|
1810
2155
|
// src/commands/ui/add.ts
|
|
1811
|
-
import { Command as
|
|
2156
|
+
import { Command as Command15 } from "commander";
|
|
1812
2157
|
|
|
1813
2158
|
// src/core/ui-client.ts
|
|
1814
|
-
import * as
|
|
1815
|
-
import * as
|
|
2159
|
+
import * as path14 from "path";
|
|
2160
|
+
import * as fs12 from "fs/promises";
|
|
1816
2161
|
import { createRequire as createRequire3 } from "module";
|
|
1817
2162
|
import { loadUiPackageManifest } from "@teamix-evo/registry";
|
|
1818
2163
|
var require4 = createRequire3(import.meta.url);
|
|
1819
|
-
function
|
|
2164
|
+
function resolvePackageRoot2(packageName) {
|
|
1820
2165
|
const pkgJsonPath = require4.resolve(`${packageName}/package.json`);
|
|
1821
|
-
return
|
|
2166
|
+
return path14.dirname(pkgJsonPath);
|
|
1822
2167
|
}
|
|
1823
2168
|
async function loadUiData(packageName) {
|
|
1824
|
-
const packageRoot =
|
|
2169
|
+
const packageRoot = resolvePackageRoot2(packageName);
|
|
1825
2170
|
logger.debug(`Resolved ui package root: ${packageRoot}`);
|
|
1826
2171
|
const manifest = await loadUiPackageManifest(packageRoot);
|
|
1827
2172
|
let data = {};
|
|
1828
|
-
const dataPath =
|
|
2173
|
+
const dataPath = path14.join(packageRoot, "_data.json");
|
|
1829
2174
|
try {
|
|
1830
|
-
const raw = await
|
|
2175
|
+
const raw = await fs12.readFile(dataPath, "utf-8");
|
|
1831
2176
|
data = JSON.parse(raw);
|
|
1832
2177
|
} catch (err) {
|
|
1833
2178
|
if (err.code !== "ENOENT") {
|
|
@@ -1839,8 +2184,8 @@ async function loadUiData(packageName) {
|
|
|
1839
2184
|
}
|
|
1840
2185
|
|
|
1841
2186
|
// src/core/ui-installer.ts
|
|
1842
|
-
import * as
|
|
1843
|
-
import * as
|
|
2187
|
+
import * as path15 from "path";
|
|
2188
|
+
import * as fs13 from "fs/promises";
|
|
1844
2189
|
import { resolveUiEntryOrder } from "@teamix-evo/registry";
|
|
1845
2190
|
|
|
1846
2191
|
// src/utils/transform-imports.ts
|
|
@@ -1858,10 +2203,19 @@ function rewriteImports(source, aliases) {
|
|
|
1858
2203
|
if (!aliasKey) return full;
|
|
1859
2204
|
const alias = aliases[aliasKey];
|
|
1860
2205
|
const normalized = aliasToImportPath(alias);
|
|
1861
|
-
|
|
2206
|
+
const flatRest = flattenRestPath(rest);
|
|
2207
|
+
return `${quote}${normalized}${flatRest}${quote}`;
|
|
1862
2208
|
}
|
|
1863
2209
|
);
|
|
1864
2210
|
}
|
|
2211
|
+
function flattenRestPath(rest) {
|
|
2212
|
+
if (!rest) return "";
|
|
2213
|
+
const segments = rest.split("/");
|
|
2214
|
+
if (segments.length === 3) {
|
|
2215
|
+
return `/${segments[2]}`;
|
|
2216
|
+
}
|
|
2217
|
+
return rest;
|
|
2218
|
+
}
|
|
1865
2219
|
function aliasToImportPath(alias) {
|
|
1866
2220
|
const trimmed = alias.replace(/^\.\//, "").replace(/\/$/, "");
|
|
1867
2221
|
if (trimmed.startsWith("src/")) {
|
|
@@ -1904,8 +2258,8 @@ async function installUiEntries(options) {
|
|
|
1904
2258
|
skipped++;
|
|
1905
2259
|
continue;
|
|
1906
2260
|
}
|
|
1907
|
-
const sourceAbs =
|
|
1908
|
-
const raw = await
|
|
2261
|
+
const sourceAbs = path15.resolve(packageRoot, file.source);
|
|
2262
|
+
const raw = await fs13.readFile(sourceAbs, "utf-8");
|
|
1909
2263
|
const transformed = rewriteImports(raw, aliases);
|
|
1910
2264
|
await writeFileSafe(targetAbs, transformed);
|
|
1911
2265
|
written++;
|
|
@@ -1918,9 +2272,9 @@ async function installUiEntries(options) {
|
|
|
1918
2272
|
});
|
|
1919
2273
|
}
|
|
1920
2274
|
if (entry.meta) {
|
|
1921
|
-
const metaSourceAbs =
|
|
1922
|
-
const metaContent = await
|
|
1923
|
-
const metaTargetAbs =
|
|
2275
|
+
const metaSourceAbs = path15.resolve(packageRoot, entry.meta);
|
|
2276
|
+
const metaContent = await fs13.readFile(metaSourceAbs, "utf-8");
|
|
2277
|
+
const metaTargetAbs = path15.join(
|
|
1924
2278
|
projectRoot,
|
|
1925
2279
|
DESIGN_COMPONENTS_DIR,
|
|
1926
2280
|
`${entry.id}.meta.md`
|
|
@@ -1952,10 +2306,10 @@ function resolveTargetPath(projectRoot, aliases, entry, file) {
|
|
|
1952
2306
|
`Entry "${entry.id}" requires alias "${file.targetAlias}" but it is not configured.`
|
|
1953
2307
|
);
|
|
1954
2308
|
}
|
|
1955
|
-
return
|
|
2309
|
+
return path15.join(projectRoot, aliasDir, file.targetName);
|
|
1956
2310
|
}
|
|
1957
2311
|
function rel(projectRoot, abs) {
|
|
1958
|
-
return
|
|
2312
|
+
return path15.relative(projectRoot, abs);
|
|
1959
2313
|
}
|
|
1960
2314
|
|
|
1961
2315
|
// src/core/ui-add.ts
|
|
@@ -2030,7 +2384,7 @@ function mergeResources(prior, next) {
|
|
|
2030
2384
|
}
|
|
2031
2385
|
|
|
2032
2386
|
// src/commands/ui/add.ts
|
|
2033
|
-
var addCommand2 = new
|
|
2387
|
+
var addCommand2 = new Command15("add").description(
|
|
2034
2388
|
"\u5B89\u88C5\u4E00\u4E2A\u6216\u591A\u4E2A ui entry\uFF08\u6309 id\uFF0C\u81EA\u52A8\u5C55\u5F00 registryDependencies\uFF09"
|
|
2035
2389
|
).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
2390
|
try {
|
|
@@ -2074,7 +2428,7 @@ var addCommand2 = new Command12("add").description(
|
|
|
2074
2428
|
});
|
|
2075
2429
|
|
|
2076
2430
|
// src/commands/ui/list.ts
|
|
2077
|
-
import { Command as
|
|
2431
|
+
import { Command as Command16 } from "commander";
|
|
2078
2432
|
|
|
2079
2433
|
// src/core/ui-list.ts
|
|
2080
2434
|
var DEFAULT_UI_PACKAGE2 = "@teamix-evo/ui";
|
|
@@ -2106,7 +2460,7 @@ async function runUiList(options) {
|
|
|
2106
2460
|
}
|
|
2107
2461
|
|
|
2108
2462
|
// src/commands/ui/list.ts
|
|
2109
|
-
var listCommand3 = new
|
|
2463
|
+
var listCommand3 = new Command16("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
2464
|
try {
|
|
2111
2465
|
const ide = detectIde();
|
|
2112
2466
|
const projectRoot = ide.getProjectRoot();
|
|
@@ -2152,20 +2506,301 @@ var listCommand3 = new Command13("list").description("\u5217\u51FA @teamix-evo/u
|
|
|
2152
2506
|
});
|
|
2153
2507
|
|
|
2154
2508
|
// src/commands/ui/index.ts
|
|
2155
|
-
var uiCommand = new
|
|
2509
|
+
var uiCommand = new Command17("ui").description(
|
|
2156
2510
|
"\u7BA1\u7406 teamix-evo ui \u7EC4\u4EF6\uFF08\u6E90\u7801\u6CE8\u5165\u5F0F\u5B89\u88C5\uFF0Cshadcn \u98CE\u683C\uFF09"
|
|
2157
2511
|
);
|
|
2158
2512
|
uiCommand.addCommand(initCommand2);
|
|
2159
2513
|
uiCommand.addCommand(addCommand2);
|
|
2160
2514
|
uiCommand.addCommand(listCommand3);
|
|
2161
2515
|
|
|
2162
|
-
// src/index.ts
|
|
2516
|
+
// src/commands/biz-ui/index.ts
|
|
2517
|
+
import { Command as Command20 } from "commander";
|
|
2518
|
+
|
|
2519
|
+
// src/commands/biz-ui/add.ts
|
|
2520
|
+
import { Command as Command18 } from "commander";
|
|
2521
|
+
|
|
2522
|
+
// src/core/variant-ui-add.ts
|
|
2523
|
+
import * as path16 from "path";
|
|
2524
|
+
import { createRequire as createRequire4 } from "module";
|
|
2525
|
+
import {
|
|
2526
|
+
loadVariantUiPackageCatalog,
|
|
2527
|
+
loadVariantUiPackageManifest
|
|
2528
|
+
} from "@teamix-evo/registry";
|
|
2163
2529
|
var require5 = createRequire4(import.meta.url);
|
|
2164
|
-
|
|
2165
|
-
|
|
2530
|
+
function resolvePackageRoot3(packageName) {
|
|
2531
|
+
const pkgJsonPath = require5.resolve(`${packageName}/package.json`);
|
|
2532
|
+
return path16.dirname(pkgJsonPath);
|
|
2533
|
+
}
|
|
2534
|
+
async function runVariantUiAdd(packageName, options) {
|
|
2535
|
+
const { projectRoot, variant, ids, overwrite } = options;
|
|
2536
|
+
const fullPackageName = options.packageName ?? `@teamix-evo/${packageName}`;
|
|
2537
|
+
if (ids.length === 0) {
|
|
2538
|
+
throw new Error("At least one entry id must be provided.");
|
|
2539
|
+
}
|
|
2540
|
+
const config = await readProjectConfig(projectRoot);
|
|
2541
|
+
const uiCfg = config?.packages?.ui;
|
|
2542
|
+
if (!config || !uiCfg?.aliases) {
|
|
2543
|
+
throw new Error(
|
|
2544
|
+
`UI not initialized. Run \`teamix-evo ui init\` first \u2014 \`${packageName} add\` writes into the same alias map (business / templates).`
|
|
2545
|
+
);
|
|
2546
|
+
}
|
|
2547
|
+
const packageRoot = options.packageRoot ?? resolvePackageRoot3(fullPackageName);
|
|
2548
|
+
const catalog = await loadVariantUiPackageCatalog(packageRoot);
|
|
2549
|
+
if (!catalog.variants.some((v) => v.name === variant)) {
|
|
2550
|
+
const known = catalog.variants.map((v) => v.name).join(", ");
|
|
2551
|
+
throw new Error(
|
|
2552
|
+
`Variant "${variant}" not found in ${fullPackageName}. Known variants: ${known}. Hint: \`teamix-evo ${packageName} list-variants\` shows all.`
|
|
2553
|
+
);
|
|
2554
|
+
}
|
|
2555
|
+
const variantDir = path16.join(packageRoot, "variants", variant);
|
|
2556
|
+
const variantManifest = await loadVariantUiPackageManifest(variantDir);
|
|
2557
|
+
const knownIds = new Set(variantManifest.entries.map((e) => e.id));
|
|
2558
|
+
const unknown = ids.filter((id) => !knownIds.has(id));
|
|
2559
|
+
if (unknown.length > 0) {
|
|
2560
|
+
throw new Error(
|
|
2561
|
+
`Unknown entry id(s) in ${packageName}#${variant}: ${unknown.map((s) => `"${s}"`).join(", ")}. Run \`teamix-evo ${packageName} list --variant ${variant}\` to see options.`
|
|
2562
|
+
);
|
|
2563
|
+
}
|
|
2564
|
+
const adaptedManifest = {
|
|
2565
|
+
schemaVersion: 1,
|
|
2566
|
+
package: "ui",
|
|
2567
|
+
version: variantManifest.version,
|
|
2568
|
+
engines: variantManifest.engines,
|
|
2569
|
+
entries: variantManifest.entries
|
|
2570
|
+
};
|
|
2571
|
+
const result = await installUiEntries({
|
|
2572
|
+
projectRoot,
|
|
2573
|
+
manifest: adaptedManifest,
|
|
2574
|
+
packageRoot: variantDir,
|
|
2575
|
+
// sources resolved relative to variant dir
|
|
2576
|
+
aliases: uiCfg.aliases,
|
|
2577
|
+
requested: ids,
|
|
2578
|
+
skipExisting: !overwrite
|
|
2579
|
+
});
|
|
2580
|
+
const installed = await readInstalledManifest(
|
|
2581
|
+
projectRoot
|
|
2582
|
+
) ?? { schemaVersion: 1, installed: [] };
|
|
2583
|
+
const idx = installed.installed.findIndex(
|
|
2584
|
+
(p) => p.package === fullPackageName && p.variant === variant
|
|
2585
|
+
);
|
|
2586
|
+
const prior = idx >= 0 ? installed.installed[idx] : null;
|
|
2587
|
+
const mergedResources = mergeResources2(
|
|
2588
|
+
prior?.resources ?? [],
|
|
2589
|
+
result.resources
|
|
2590
|
+
);
|
|
2591
|
+
const entry = {
|
|
2592
|
+
package: fullPackageName,
|
|
2593
|
+
variant,
|
|
2594
|
+
version: variantManifest.version,
|
|
2595
|
+
installedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2596
|
+
resources: mergedResources
|
|
2597
|
+
};
|
|
2598
|
+
if (idx >= 0) installed.installed[idx] = entry;
|
|
2599
|
+
else installed.installed.push(entry);
|
|
2600
|
+
await writeInstalledManifest(projectRoot, installed);
|
|
2601
|
+
return {
|
|
2602
|
+
packageName: fullPackageName,
|
|
2603
|
+
variant,
|
|
2604
|
+
orderedIds: result.orderedIds,
|
|
2605
|
+
written: result.written,
|
|
2606
|
+
skipped: result.skipped,
|
|
2607
|
+
metaFiles: result.metaFiles,
|
|
2608
|
+
npmDependencies: result.npmDependencies,
|
|
2609
|
+
resources: result.resources
|
|
2610
|
+
};
|
|
2611
|
+
}
|
|
2612
|
+
function mergeResources2(prior, next) {
|
|
2613
|
+
const merged = /* @__PURE__ */ new Map();
|
|
2614
|
+
for (const r of prior) merged.set(r.id, r);
|
|
2615
|
+
for (const r of next) merged.set(r.id, r);
|
|
2616
|
+
return Array.from(merged.values());
|
|
2617
|
+
}
|
|
2618
|
+
async function runBizUiAdd(options) {
|
|
2619
|
+
return runVariantUiAdd("biz-ui", options);
|
|
2620
|
+
}
|
|
2621
|
+
async function runTemplatesAdd(options) {
|
|
2622
|
+
return runVariantUiAdd("templates", options);
|
|
2623
|
+
}
|
|
2624
|
+
async function listVariantUi(packageName, packageRoot) {
|
|
2625
|
+
const fullPackageName = `@teamix-evo/${packageName}`;
|
|
2626
|
+
const root = packageRoot ?? resolvePackageRoot3(fullPackageName);
|
|
2627
|
+
const catalog = await loadVariantUiPackageCatalog(root);
|
|
2628
|
+
return {
|
|
2629
|
+
packageName: fullPackageName,
|
|
2630
|
+
variants: catalog.variants.map((v) => ({
|
|
2631
|
+
name: v.name,
|
|
2632
|
+
displayName: v.displayName,
|
|
2633
|
+
version: v.version,
|
|
2634
|
+
description: v.description
|
|
2635
|
+
}))
|
|
2636
|
+
};
|
|
2637
|
+
}
|
|
2638
|
+
async function listBizUiVariants(packageRoot) {
|
|
2639
|
+
return listVariantUi("biz-ui", packageRoot);
|
|
2640
|
+
}
|
|
2641
|
+
async function listTemplatesVariants(packageRoot) {
|
|
2642
|
+
return listVariantUi("templates", packageRoot);
|
|
2643
|
+
}
|
|
2644
|
+
|
|
2645
|
+
// src/commands/biz-ui/add.ts
|
|
2646
|
+
var addCommand3 = new Command18("add").description(
|
|
2647
|
+
"\u5B89\u88C5\u4E00\u4E2A\u6216\u591A\u4E2A\u4E1A\u52A1 UI \u7EC4\u4EF6(\u6309 id,\u81EA\u52A8\u5C55\u5F00 ui \u5305\u7684 registryDependencies)"
|
|
2648
|
+
).argument("<ids...>", '\u7EC4\u4EF6 id \u5217\u8868,\u5982 "tenant-switcher" "org-picker"').option("--variant <name>", '\u53D8\u4F53 id(\u5FC5\u586B,\u5982 "opentrek"\u3001"uni-manager")').option("--overwrite", "\u5373\u4F7F\u76EE\u6807\u6587\u4EF6\u5DF2\u5B58\u5728\u4E5F\u8986\u76D6").action(
|
|
2649
|
+
async (ids, opts) => {
|
|
2650
|
+
try {
|
|
2651
|
+
if (!opts.variant) {
|
|
2652
|
+
throw new Error(
|
|
2653
|
+
"--variant <name> is required. Run `teamix-evo biz-ui list-variants` to see available variants."
|
|
2654
|
+
);
|
|
2655
|
+
}
|
|
2656
|
+
const ide = detectIde();
|
|
2657
|
+
const projectRoot = ide.getProjectRoot();
|
|
2658
|
+
logger.info(
|
|
2659
|
+
`Installing biz-ui entries from variant "${opts.variant}": ${ids.join(", ")}`
|
|
2660
|
+
);
|
|
2661
|
+
const result = await runBizUiAdd({
|
|
2662
|
+
projectRoot,
|
|
2663
|
+
variant: opts.variant,
|
|
2664
|
+
ids,
|
|
2665
|
+
overwrite: opts.overwrite
|
|
2666
|
+
});
|
|
2667
|
+
logger.success(
|
|
2668
|
+
`biz-ui add complete: ${result.written} written, ${result.skipped} skipped, ${result.metaFiles.length} meta.`
|
|
2669
|
+
);
|
|
2670
|
+
logger.info("");
|
|
2671
|
+
logger.info(`Variant: ${result.variant}`);
|
|
2672
|
+
logger.info(`Resolved order: ${result.orderedIds.join(" \u2192 ")}`);
|
|
2673
|
+
const npmDeps = Object.entries(result.npmDependencies);
|
|
2674
|
+
if (npmDeps.length > 0) {
|
|
2675
|
+
logger.info("");
|
|
2676
|
+
logger.info("Install npm dependencies in your project:");
|
|
2677
|
+
const installCmd = npmDeps.map(([name, range]) => `${name}@${range}`).join(" ");
|
|
2678
|
+
logger.info(` pnpm add ${installCmd}`);
|
|
2679
|
+
}
|
|
2680
|
+
} catch (err) {
|
|
2681
|
+
logger.error(`Failed: ${err.message}`);
|
|
2682
|
+
logger.debug(err.stack ?? "");
|
|
2683
|
+
process.exitCode = 1;
|
|
2684
|
+
}
|
|
2685
|
+
}
|
|
2686
|
+
);
|
|
2687
|
+
|
|
2688
|
+
// src/commands/biz-ui/list-variants.ts
|
|
2689
|
+
import { Command as Command19 } from "commander";
|
|
2690
|
+
var listVariantsCommand2 = new Command19("list-variants").description("\u5217\u51FA @teamix-evo/biz-ui \u5305\u5185\u63D0\u4F9B\u7684\u6240\u6709\u4E1A\u52A1\u53D8\u4F53").action(async () => {
|
|
2691
|
+
try {
|
|
2692
|
+
const result = await listBizUiVariants();
|
|
2693
|
+
logger.info(`Available biz-ui variants in ${result.packageName}:`);
|
|
2694
|
+
logger.info("");
|
|
2695
|
+
if (result.variants.length === 0) {
|
|
2696
|
+
logger.info(" (no variants yet)");
|
|
2697
|
+
return;
|
|
2698
|
+
}
|
|
2699
|
+
for (const v of result.variants) {
|
|
2700
|
+
logger.info(` ${v.name} (${v.displayName}) \u2014 v${v.version}`);
|
|
2701
|
+
if (v.description) logger.info(` ${v.description}`);
|
|
2702
|
+
logger.info("");
|
|
2703
|
+
}
|
|
2704
|
+
logger.info("Install from a variant: teamix-evo biz-ui add <id> --variant <name>");
|
|
2705
|
+
} catch (err) {
|
|
2706
|
+
logger.error(`Failed: ${err.message}`);
|
|
2707
|
+
process.exitCode = 1;
|
|
2708
|
+
}
|
|
2709
|
+
});
|
|
2710
|
+
|
|
2711
|
+
// src/commands/biz-ui/index.ts
|
|
2712
|
+
var bizUiCommand = new Command20("biz-ui").description(
|
|
2713
|
+
"\u7BA1\u7406\u4E1A\u52A1 UI \u7EC4\u4EF6(\u53D8\u4F53\u611F\u77E5 \u2014 \u4E0E design / templates \u540C\u53D8\u4F53\u540D\u7A7A\u95F4)"
|
|
2714
|
+
);
|
|
2715
|
+
bizUiCommand.addCommand(addCommand3);
|
|
2716
|
+
bizUiCommand.addCommand(listVariantsCommand2);
|
|
2717
|
+
|
|
2718
|
+
// src/commands/templates/index.ts
|
|
2719
|
+
import { Command as Command23 } from "commander";
|
|
2720
|
+
|
|
2721
|
+
// src/commands/templates/add.ts
|
|
2722
|
+
import { Command as Command21 } from "commander";
|
|
2723
|
+
var addCommand4 = new Command21("add").description(
|
|
2724
|
+
"\u5B89\u88C5\u4E00\u4E2A\u6216\u591A\u4E2A\u9875\u9762\u6A21\u677F(\u6309 id,\u81EA\u52A8\u5C55\u5F00 ui \u5305\u7684 registryDependencies)"
|
|
2725
|
+
).argument("<ids...>", '\u6A21\u677F id \u5217\u8868,\u5982 "list-detail-page"').option("--variant <name>", '\u53D8\u4F53 id(\u5FC5\u586B,\u5982 "opentrek"\u3001"uni-manager")').option("--overwrite", "\u5373\u4F7F\u76EE\u6807\u6587\u4EF6\u5DF2\u5B58\u5728\u4E5F\u8986\u76D6").action(
|
|
2726
|
+
async (ids, opts) => {
|
|
2727
|
+
try {
|
|
2728
|
+
if (!opts.variant) {
|
|
2729
|
+
throw new Error(
|
|
2730
|
+
"--variant <name> is required. Run `teamix-evo templates list-variants` to see available variants."
|
|
2731
|
+
);
|
|
2732
|
+
}
|
|
2733
|
+
const ide = detectIde();
|
|
2734
|
+
const projectRoot = ide.getProjectRoot();
|
|
2735
|
+
logger.info(
|
|
2736
|
+
`Installing templates from variant "${opts.variant}": ${ids.join(", ")}`
|
|
2737
|
+
);
|
|
2738
|
+
const result = await runTemplatesAdd({
|
|
2739
|
+
projectRoot,
|
|
2740
|
+
variant: opts.variant,
|
|
2741
|
+
ids,
|
|
2742
|
+
overwrite: opts.overwrite
|
|
2743
|
+
});
|
|
2744
|
+
logger.success(
|
|
2745
|
+
`templates add complete: ${result.written} written, ${result.skipped} skipped, ${result.metaFiles.length} meta.`
|
|
2746
|
+
);
|
|
2747
|
+
logger.info("");
|
|
2748
|
+
logger.info(`Variant: ${result.variant}`);
|
|
2749
|
+
logger.info(`Resolved order: ${result.orderedIds.join(" \u2192 ")}`);
|
|
2750
|
+
const npmDeps = Object.entries(result.npmDependencies);
|
|
2751
|
+
if (npmDeps.length > 0) {
|
|
2752
|
+
logger.info("");
|
|
2753
|
+
logger.info("Install npm dependencies in your project:");
|
|
2754
|
+
const installCmd = npmDeps.map(([name, range]) => `${name}@${range}`).join(" ");
|
|
2755
|
+
logger.info(` pnpm add ${installCmd}`);
|
|
2756
|
+
}
|
|
2757
|
+
} catch (err) {
|
|
2758
|
+
logger.error(`Failed: ${err.message}`);
|
|
2759
|
+
logger.debug(err.stack ?? "");
|
|
2760
|
+
process.exitCode = 1;
|
|
2761
|
+
}
|
|
2762
|
+
}
|
|
2763
|
+
);
|
|
2764
|
+
|
|
2765
|
+
// src/commands/templates/list-variants.ts
|
|
2766
|
+
import { Command as Command22 } from "commander";
|
|
2767
|
+
var listVariantsCommand3 = new Command22("list-variants").description("\u5217\u51FA @teamix-evo/templates \u5305\u5185\u63D0\u4F9B\u7684\u6240\u6709\u9875\u9762\u6A21\u677F\u53D8\u4F53").action(async () => {
|
|
2768
|
+
try {
|
|
2769
|
+
const result = await listTemplatesVariants();
|
|
2770
|
+
logger.info(`Available templates variants in ${result.packageName}:`);
|
|
2771
|
+
logger.info("");
|
|
2772
|
+
if (result.variants.length === 0) {
|
|
2773
|
+
logger.info(" (no variants yet)");
|
|
2774
|
+
return;
|
|
2775
|
+
}
|
|
2776
|
+
for (const v of result.variants) {
|
|
2777
|
+
logger.info(` ${v.name} (${v.displayName}) \u2014 v${v.version}`);
|
|
2778
|
+
if (v.description) logger.info(` ${v.description}`);
|
|
2779
|
+
logger.info("");
|
|
2780
|
+
}
|
|
2781
|
+
logger.info("Install from a variant: teamix-evo templates add <id> --variant <name>");
|
|
2782
|
+
} catch (err) {
|
|
2783
|
+
logger.error(`Failed: ${err.message}`);
|
|
2784
|
+
process.exitCode = 1;
|
|
2785
|
+
}
|
|
2786
|
+
});
|
|
2787
|
+
|
|
2788
|
+
// src/commands/templates/index.ts
|
|
2789
|
+
var templatesCommand = new Command23("templates").description(
|
|
2790
|
+
"\u7BA1\u7406\u9875\u9762\u6A21\u677F(\u53D8\u4F53\u611F\u77E5 \u2014 \u4E0E design / biz-ui \u540C\u53D8\u4F53\u540D\u7A7A\u95F4)"
|
|
2791
|
+
);
|
|
2792
|
+
templatesCommand.addCommand(addCommand4);
|
|
2793
|
+
templatesCommand.addCommand(listVariantsCommand3);
|
|
2794
|
+
|
|
2795
|
+
// src/index.ts
|
|
2796
|
+
var require6 = createRequire5(import.meta.url);
|
|
2797
|
+
var { version } = require6("../package.json");
|
|
2798
|
+
var program = new Command24();
|
|
2166
2799
|
program.name("teamix-evo").description("Where ideas evolve. \u2014 AI Coding \u5957\u4EF6").version(version);
|
|
2167
2800
|
program.addCommand(designCommand);
|
|
2168
2801
|
program.addCommand(skillsCommand);
|
|
2169
2802
|
program.addCommand(uiCommand);
|
|
2803
|
+
program.addCommand(bizUiCommand);
|
|
2804
|
+
program.addCommand(templatesCommand);
|
|
2170
2805
|
program.parse();
|
|
2171
2806
|
//# sourceMappingURL=index.js.map
|