teamix-evo 0.2.0 → 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/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 Command15 } from "commander";
5
- import { createRequire as createRequire4 } from "module";
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 Command5 } from "commander";
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/registry-client.ts
70
- import * as path3 from "path";
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 { createRequire } from "module";
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 fs2.mkdir(dir, { recursive: true });
108
+ await fs.mkdir(dir, { recursive: true });
133
109
  }
134
110
  async function writeFileSafe(filePath, content) {
135
- const dir = path4.dirname(filePath);
111
+ const dir = path3.dirname(filePath);
136
112
  await ensureDir(dir);
137
113
  const tmp = filePath + ".tmp";
138
- await fs2.writeFile(tmp, content, "utf-8");
139
- await fs2.rename(tmp, filePath);
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 fs2.readFile(filePath, "utf-8");
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 = path4.relative(projectRoot, filePath);
133
+ const rel2 = path3.relative(projectRoot, filePath);
158
134
  const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
159
- const backupPath = path4.join(
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(path4.dirname(backupPath));
166
- await fs2.writeFile(backupPath, content, "utf-8");
167
- logger.debug(`Backed up ${rel2} \u2192 ${path4.relative(projectRoot, backupPath)}`);
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 fs2.access(filePath);
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/utils/template.ts
186
- import Handlebars from "handlebars";
187
- import * as fs3 from "fs/promises";
188
- Handlebars.registerHelper("lowercase", (str) => {
189
- return typeof str === "string" ? str.toLowerCase() : String(str ?? "").toLowerCase();
190
- });
191
- var compiledCache = /* @__PURE__ */ new Map();
192
- var MAX_CACHE_SIZE = 64;
193
- function getCompiledTemplate(templateContent) {
194
- let compiled = compiledCache.get(templateContent);
195
- if (!compiled) {
196
- if (compiledCache.size >= MAX_CACHE_SIZE) {
197
- const firstKey = compiledCache.keys().next().value;
198
- compiledCache.delete(firstKey);
199
- }
200
- compiled = Handlebars.compile(templateContent, { noEscape: true });
201
- compiledCache.set(templateContent, compiled);
202
- }
203
- return compiled;
204
- }
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
- return path5.join(variantDir, source);
221
- }
222
- async function walkDir(dir) {
223
- const files = [];
224
- const entries = await fs4.readdir(dir, { withFileTypes: true });
225
- for (const entry of entries) {
226
- const fullPath = path5.join(dir, entry.name);
227
- if (entry.isDirectory()) {
228
- files.push(...await walkDir(fullPath));
229
- } else if (entry.isFile()) {
230
- files.push(fullPath);
231
- }
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
- return files;
234
- }
235
-
236
- // src/core/installer.ts
237
- async function installResources(options) {
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
- return {
263
- resources: installedResources,
264
- count: installedResources.length
265
- };
266
- }
267
- async function installSingleResource(resource, projectRoot, data, variantDir, packageRoot) {
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
- id: resource.id,
286
- target: resource.target,
287
- hash,
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 path7 from "path";
330
- import { validateConfig, validateInstalled } from "@teamix-evo/registry";
331
- var TEAMIX_DIR = ".teamix-evo";
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 path7.join(projectRoot, TEAMIX_DIR);
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 = path7.join(projectRoot, TEAMIX_DIR, CONFIG_FILE);
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 = path7.join(projectRoot, TEAMIX_DIR, CONFIG_FILE);
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 = path7.join(projectRoot, TEAMIX_DIR, MANIFEST_FILE);
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 = path7.join(projectRoot, TEAMIX_DIR, MANIFEST_FILE);
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
- // src/core/design-init.ts
388
- var DEFAULT_DESIGN_PACKAGE = "@teamix-evo/design";
389
- async function runDesignInit(options) {
390
- const { projectRoot, variant, tailwind, ide } = options;
391
- const packageName = options.packageName ?? DEFAULT_DESIGN_PACKAGE;
392
- await ensureTeamixDir(projectRoot);
393
- const existingConfig = await readProjectConfig(projectRoot);
394
- if (existingConfig?.packages?.design) {
395
- return {
396
- status: "already-initialized",
397
- existingVariant: existingConfig.packages.design.variant
398
- };
399
- }
400
- const { manifest, data, variantDir, packageRoot } = await loadVariantData(
401
- packageName,
402
- variant
403
- );
404
- const result = await installResources({
276
+ async function readDesignPackLock(projectRoot) {
277
+ const lockPath = path5.join(
405
278
  projectRoot,
406
- manifest,
407
- data,
408
- variantDir,
409
- packageRoot
410
- });
411
- const config = {
412
- $schema: "https://teamix-evo.dev/schema/config/v1.json",
413
- schemaVersion: 1,
414
- ide,
415
- packages: {
416
- design: {
417
- variant,
418
- version: manifest.version,
419
- tailwind
420
- }
421
- }
422
- };
423
- await writeProjectConfig(projectRoot, config);
424
- const installedManifest = {
425
- schemaVersion: 1,
426
- installed: [
427
- {
428
- package: packageName,
429
- variant,
430
- version: manifest.version,
431
- installedAt: (/* @__PURE__ */ new Date()).toISOString(),
432
- resources: result.resources
433
- }
434
- ]
435
- };
436
- await writeInstalledManifest(projectRoot, installedManifest);
437
- return {
438
- status: "installed",
439
- packageName,
440
- variant,
441
- version: manifest.version,
442
- tailwind,
443
- count: result.count,
444
- resources: result.resources
445
- };
446
- }
447
-
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
- var initCommand = new Command("init").description("\u521D\u59CB\u5316\u8BBE\u8BA1\u4F53\u7CFB\u8D44\u6E90").argument("[variant]", "\u8BBE\u8BA1\u53D8\u4F53\u540D\u79F0", DEFAULT_VARIANT).option(
461
- "--tailwind <version>",
462
- "\u9879\u76EE\u4F7F\u7528\u7684 Tailwind CSS \u4E3B\u7248\u672C\uFF08v3 | v4\uFF09",
463
- DEFAULT_TAILWIND
464
- ).action(async (variant, opts) => {
283
+ const raw = await readFileOrNull(lockPath);
284
+ if (raw === null) return null;
465
285
  try {
466
- const tailwind = normalizeTailwind(opts.tailwind);
467
- const ide = detectIde();
468
- const projectRoot = ide.getProjectRoot();
469
- logger.info(
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
- logger.success(
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.error(`Failed to initialize: ${err.message}`);
498
- logger.debug(err.stack ?? "");
499
- process.exitCode = 1;
293
+ logger.warn(
294
+ `Failed to parse design pack.lock.json: ${err.message}`
295
+ );
296
+ return null;
500
297
  }
501
- });
502
-
503
- // src/commands/design/update.ts
504
- import { Command as Command2 } from "commander";
505
-
506
- // src/core/updater.ts
507
- import * as path8 from "path";
508
- import * as fs6 from "fs/promises";
509
- import { getUpdateAction, replaceManagedRegion } from "@teamix-evo/registry";
510
- async function updateResources(options) {
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
- manifest,
514
- data,
515
- variantDir,
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 installedMap = /* @__PURE__ */ new Map();
526
- if (installedPkg) {
527
- for (const res of installedPkg.resources) {
528
- installedMap.set(res.id, res);
529
- }
530
- }
531
- for (const resource of manifest.resources) {
532
- if (resource.recursive) {
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 updateSingleResource(resource, projectRoot, data, variantDir, packageRoot, installedMap, summary) {
559
- const targetPath = path8.join(projectRoot, resource.target);
560
- const exists = await fileExists(targetPath);
561
- const installed = installedMap.get(resource.id);
562
- const sourcePath = resolveSourcePath(
563
- resource.source,
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
- let newContent;
568
- if (resource.template) {
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 path10 from "path";
919
- import * as fs8 from "fs/promises";
920
- import { createRequire as createRequire2 } from "module";
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 require3 = createRequire2(import.meta.url);
923
- function resolvePackageRoot2(packageName) {
924
- const pkgJsonPath = require3.resolve(`${packageName}/package.json`);
925
- return path10.dirname(pkgJsonPath);
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 = resolvePackageRoot2(packageName);
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 = path10.join(packageRoot, "_data.json");
357
+ const dataPath = path6.join(packageRoot, "_data.json");
933
358
  try {
934
- const raw = await fs8.readFile(dataPath, "utf-8");
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 path11 from "path";
947
- import * as fs9 from "fs/promises";
948
- import { replaceManagedRegion as replaceManagedRegion2 } from "@teamix-evo/registry";
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 result = await installSkillForIde(skill, ide, scope, options);
967
- installed.push(...result);
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 installSkillForIde(skill, ide, scope, options) {
451
+ async function writeSkillSource(skill, options) {
973
452
  const { data, packageRoot, projectRoot } = options;
974
- const adapter = getAdapter(ide);
975
- const targetDir = adapter.getSkillTargetDir(skill.name, scope, projectRoot);
976
- const sourceAbs = path11.resolve(packageRoot, skill.source);
977
- const stat2 = await fs9.stat(sourceAbs);
978
- const results = [];
979
- if (stat2.isFile()) {
980
- const targetFile = path11.join(targetDir, "SKILL.md");
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
- results.push(makeInstalledRecord(skill, targetFile, content, ide, scope));
984
- logger.debug(` Wrote ${ide}:${scope}: ${targetFile}`);
985
- return results;
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 = path11.relative(sourceAbs, entry);
991
- let targetFile = path11.join(targetDir, rel2);
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
- let content;
996
- if (skill.template && entry.endsWith(".hbs")) {
997
- const tpl = await loadTemplateFile(entry);
998
- content = renderTemplate(tpl, { ...data, skill });
999
- } else {
1000
- content = await fs9.readFile(entry, "utf-8");
1001
- }
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
- results.push(
1004
- makeInstalledRecord(skill, targetFile, content, ide, scope, rel2)
1005
- );
1006
- logger.debug(` Wrote ${ide}:${scope}: ${targetFile}`);
475
+ const relWritten = path8.relative(targetDir, targetFile);
476
+ records.push(makeSourceRecord(skill, targetFile, content, relWritten));
477
+ logger.debug(` Wrote source: ${targetFile}`);
1007
478
  }
1008
- return results;
479
+ return records;
1009
480
  }
1010
- async function renderSkillContent(sourceAbs, skill, data) {
1011
- if (skill.template ?? sourceAbs.endsWith(".hbs")) {
1012
- const tpl = await loadTemplateFile(sourceAbs);
1013
- return renderTemplate(tpl, { ...data, skill });
1014
- }
1015
- return fs9.readFile(sourceAbs, "utf-8");
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
+ };
1016
513
  }
1017
- function makeInstalledRecord(skill, targetAbs, content, ide, scope, rel2) {
1018
- const id = rel2 ? `${skill.id}:${rel2}` : skill.id;
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, installed, projectRoot, data, packageRoot } = options;
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 records = await updateSkillForIde(
539
+ const mirrorRecords = await mirrorSkillToIde(
1040
540
  skill,
1041
541
  ide,
1042
542
  scope,
1043
- data,
1044
- packageRoot,
1045
- projectRoot,
1046
- installedMap,
1047
- summary
543
+ projectRoot
1048
544
  );
1049
- updated.push(...records);
545
+ updated.push(...mirrorRecords);
1050
546
  }
1051
547
  }
1052
548
  return { resources: updated, summary };
1053
549
  }
1054
- async function updateSkillForIde(skill, ide, scope, data, packageRoot, projectRoot, installedMap, summary) {
1055
- const adapter = getAdapter(ide);
1056
- const targetDir = adapter.getSkillTargetDir(skill.name, scope, projectRoot);
1057
- const sourceAbs = path11.resolve(packageRoot, skill.source);
1058
- const stat2 = await fs9.stat(sourceAbs);
1059
- const records = [];
1060
- if (!stat2.isFile()) {
1061
- const entries = await walkDir(sourceAbs);
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 = path11.relative(sourceAbs, entry);
1065
- let targetFile2 = path11.join(targetDir, rel2);
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
- let content;
1070
- if (skill.template && entry.endsWith(".hbs")) {
1071
- content = renderTemplate(await loadTemplateFile(entry), {
1072
- ...data,
1073
- skill
1074
- });
1075
- } else {
1076
- content = await fs9.readFile(entry, "utf-8");
1077
- }
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
- records.push(
1087
- makeInstalledRecord(skill, targetFile2, content, ide, scope, rel2)
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 = path11.join(targetDir, "SKILL.md");
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
- return [
1101
- prior ?? makeInstalledRecord(skill, targetFile, newContent, ide, scope)
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
- records.push(
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
- records.push(
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 updated = current ?? newContent;
1126
- const regionIds = skill.managedRegions ?? [];
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="${escapeRegExp2(
606
+ `<!-- teamix-evo:managed:start id="${escapeRegExp(
1130
607
  regionId
1131
- )}" -->([\\s\\S]*?)<!-- teamix-evo:managed:end id="${escapeRegExp2(
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
- updated = replaceManagedRegion2(updated, regionId, region);
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, updated);
625
+ await writeFileSafe(targetFile, merged);
1149
626
  summary.managed++;
1150
- records.push(makeInstalledRecord(skill, targetFile, updated, ide, scope));
1151
- return records;
1152
- }
1153
- function installedKey(r) {
1154
- return `${r.id}|${r.ide ?? ""}|${r.scope ?? ""}`;
627
+ return [makeSourceRecord(skill, targetFile, merged)];
1155
628
  }
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 fs9.unlink(r.target);
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) => path11.dirname(r.target)));
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 fs9.readdir(dir);
1175
- if (entries.length === 0) await fs9.rmdir(dir);
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));
@@ -1216,9 +729,13 @@ async function runSkillsAdd(options) {
1216
729
  const existingPkg = existingInstalled?.installed.find(
1217
730
  (p) => p.package === packageName
1218
731
  );
1219
- const existingSkillIds = new Set(
1220
- (existingPkg?.resources ?? []).map((r) => r.id.split(":")[0])
1221
- );
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
+ ]);
1222
739
  let onlyIds;
1223
740
  let skippedSkillIds;
1224
741
  if (isIncremental) {
@@ -1226,7 +743,22 @@ async function runSkillsAdd(options) {
1226
743
  onlyIds = requestedNames.filter((n) => !existingSkillIds.has(n));
1227
744
  } else {
1228
745
  skippedSkillIds = [];
1229
- onlyIds = manifest.skills.map((s) => s.id);
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);
1230
762
  }
1231
763
  if (isIncremental && onlyIds.length === 0) {
1232
764
  return {
@@ -1242,72 +774,562 @@ async function runSkillsAdd(options) {
1242
774
  skippedSkillIds
1243
775
  };
1244
776
  }
1245
- const result = await installSkills({
1246
- projectRoot,
1247
- manifest,
1248
- data,
1249
- packageRoot,
1250
- ides,
1251
- scope,
1252
- onlyIds
1253
- });
1254
- const config = existingConfig ?? {
1255
- $schema: "https://teamix-evo.dev/schema/config/v1.json",
1256
- schemaVersion: 1,
1257
- ide: ideIdent,
1258
- packages: {}
1259
- };
1260
- config.packages.skills = {
1261
- variant: FLAT_VARIANT,
1262
- version: manifest.version,
1263
- ides,
1264
- scope
1265
- };
1266
- await writeProjectConfig(projectRoot, config);
1267
- const installedManifest = existingInstalled ?? {
1268
- schemaVersion: 1,
1269
- installed: []
1270
- };
1271
- const idx = installedManifest.installed.findIndex(
1272
- (p) => p.package === packageName
1273
- );
1274
- const mergedResources = mergeInstalledResources(
1275
- existingPkg?.resources ?? [],
1276
- result.resources
1277
- );
1278
- const entry = {
1279
- package: packageName,
1280
- variant: FLAT_VARIANT,
1281
- version: manifest.version,
1282
- installedAt: (/* @__PURE__ */ new Date()).toISOString(),
1283
- resources: mergedResources
1284
- };
1285
- if (idx >= 0) installedManifest.installed[idx] = entry;
1286
- else installedManifest.installed.push(entry);
1287
- await writeInstalledManifest(projectRoot, installedManifest);
1288
- return {
1289
- status: "installed",
1290
- packageName,
1291
- version: manifest.version,
1292
- ides,
1293
- scope,
1294
- skillCount: onlyIds.length,
1295
- fileCount: result.count,
1296
- resources: result.resources,
1297
- addedSkillIds: onlyIds,
1298
- skippedSkillIds
1299
- };
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 mergeInstalledResources(existing, next) {
1302
- const map = /* @__PURE__ */ new Map();
1303
- const key = (r) => `${r.id}|${r.ide ?? ""}|${r.scope ?? ""}`;
1304
- for (const r of existing) map.set(key(r), r);
1305
- for (const r of next) map.set(key(r), r);
1306
- return [...map.values()];
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;
1323
+ }
1324
+ function resolveSkillsMaintenanceRoot(cwd) {
1325
+ if (isTeamixEvoProject(cwd)) return cwd;
1326
+ const globalRoot = getGlobalMetaRoot();
1327
+ if (isTeamixEvoProject(globalRoot)) return globalRoot;
1328
+ return cwd;
1307
1329
  }
1308
1330
 
1309
1331
  // src/commands/skills/add.ts
1310
- var addCommand = new Command6("add").description(
1332
+ var addCommand = new Command7("add").description(
1311
1333
  "\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
1334
  ).argument(
1313
1335
  "[names...]",
@@ -1318,13 +1340,18 @@ var addCommand = new Command6("add").description(
1318
1340
  ).option("-y, --yes", "\u4F7F\u7528\u9ED8\u8BA4\u503C\uFF0C\u8DF3\u8FC7\u4EA4\u4E92").action(async (names, opts) => {
1319
1341
  try {
1320
1342
  const ide = detectIde();
1321
- const projectRoot = ide.getProjectRoot();
1343
+ const cwd = ide.getProjectRoot();
1322
1344
  const isIncremental = names.length > 0;
1323
1345
  const { ides, scope } = await resolveIdesAndScope({
1324
1346
  opts,
1325
- projectRoot,
1347
+ projectRoot: cwd,
1326
1348
  isIncremental
1327
1349
  });
1350
+ let projectRoot = cwd;
1351
+ if (scope === "global" && !isTeamixEvoProject(cwd)) {
1352
+ projectRoot = await ensureGlobalMetaRoot();
1353
+ logger.info(`Global skill install \u2014 meta root: ${projectRoot}`);
1354
+ }
1328
1355
  logger.info(
1329
1356
  isIncremental ? `Adding skills [${names.join(",")}]: ides=[${ides.join(
1330
1357
  ","
@@ -1439,14 +1466,18 @@ function parseScope(input) {
1439
1466
  }
1440
1467
 
1441
1468
  // src/commands/skills/list.ts
1442
- import { Command as Command7 } from "commander";
1469
+ import { Command as Command8 } from "commander";
1443
1470
  var SKILLS_PACKAGE = "@teamix-evo/skills";
1444
- var listCommand2 = new Command7("list").alias("ls").description(
1471
+ var listCommand2 = new Command8("list").alias("ls").description(
1445
1472
  "\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
1473
  ).option("--installed", "\u4EC5\u5C55\u793A\u5DF2\u5B89\u88C5\u7684 skill\uFF08\u9690\u85CF\u672A\u5B89\u88C5\u9879\uFF09").action(async (opts) => {
1447
1474
  try {
1448
1475
  const ide = detectIde();
1449
- const projectRoot = ide.getProjectRoot();
1476
+ const cwd = ide.getProjectRoot();
1477
+ const projectRoot = resolveSkillsMaintenanceRoot(cwd);
1478
+ if (projectRoot !== cwd) {
1479
+ logger.info(`Using global skills meta root: ${projectRoot}`);
1480
+ }
1450
1481
  const config = await readProjectConfig(projectRoot);
1451
1482
  const installedManifest = await readInstalledManifest(projectRoot);
1452
1483
  const pkg = installedManifest?.installed.find(
@@ -1521,13 +1552,17 @@ function printInstalledHeader(cfg, installedAt) {
1521
1552
  }
1522
1553
 
1523
1554
  // src/commands/skills/update.ts
1524
- import { Command as Command8 } from "commander";
1555
+ import { Command as Command9 } from "commander";
1525
1556
  var SKILLS_PACKAGE2 = "@teamix-evo/skills";
1526
1557
  var FLAT_VARIANT2 = "_flat";
1527
- var updateCommand2 = new Command8("update").description("\u66F4\u65B0\u5DF2\u5B89\u88C5\u7684 teamix-evo skills").action(async () => {
1558
+ var updateCommand2 = new Command9("update").description("\u66F4\u65B0\u5DF2\u5B89\u88C5\u7684 teamix-evo skills").action(async () => {
1528
1559
  try {
1529
1560
  const ide = detectIde();
1530
- const projectRoot = ide.getProjectRoot();
1561
+ const cwd = ide.getProjectRoot();
1562
+ const projectRoot = resolveSkillsMaintenanceRoot(cwd);
1563
+ if (projectRoot !== cwd) {
1564
+ logger.info(`Using global skills meta root: ${projectRoot}`);
1565
+ }
1531
1566
  const config = await readProjectConfig(projectRoot);
1532
1567
  if (!config?.packages?.skills) {
1533
1568
  logger.error('Skills not added. Run "teamix-evo skills add" first.');
@@ -1550,114 +1585,537 @@ var updateCommand2 = new Command8("update").description("\u66F4\u65B0\u5DF2\u5B8
1550
1585
  const { manifest, data, packageRoot } = await loadSkillsData(
1551
1586
  SKILLS_PACKAGE2
1552
1587
  );
1553
- const pkgInstalled = installedManifest.installed.find(
1554
- (p) => p.package === SKILLS_PACKAGE2
1555
- );
1556
- const installedResources = pkgInstalled?.resources ?? [];
1557
1588
  logger.info(
1558
1589
  `Current: v${skillsEntry.version} \u2192 Available: v${manifest.version}`
1559
1590
  );
1560
1591
  const result = await updateSkills({
1561
1592
  projectRoot,
1562
- manifest,
1563
- data,
1564
- packageRoot,
1593
+ manifest,
1594
+ data,
1595
+ packageRoot,
1596
+ ides,
1597
+ scope
1598
+ });
1599
+ config.packages.skills.version = manifest.version;
1600
+ await writeProjectConfig(projectRoot, config);
1601
+ const installedAt = (/* @__PURE__ */ new Date()).toISOString();
1602
+ const idx = installedManifest.installed.findIndex(
1603
+ (p) => p.package === SKILLS_PACKAGE2
1604
+ );
1605
+ const entry = {
1606
+ package: SKILLS_PACKAGE2,
1607
+ variant: FLAT_VARIANT2,
1608
+ version: manifest.version,
1609
+ installedAt,
1610
+ resources: result.resources
1611
+ };
1612
+ if (idx >= 0) installedManifest.installed[idx] = entry;
1613
+ else installedManifest.installed.push(entry);
1614
+ await writeInstalledManifest(projectRoot, installedManifest);
1615
+ const existingLock = await readSkillsLock(projectRoot) ?? {
1616
+ schemaVersion: 1,
1617
+ skills: {}
1618
+ };
1619
+ const lock = {
1620
+ schemaVersion: 1,
1621
+ skills: { ...existingLock.skills }
1622
+ };
1623
+ for (const skill of manifest.skills) {
1624
+ const mirroredTo = skill.ides.filter((i) => ides.includes(i));
1625
+ if (mirroredTo.length === 0) continue;
1626
+ lock.skills[skill.id] = {
1627
+ version: skill.version,
1628
+ from: SKILLS_PACKAGE2,
1629
+ installedAt,
1630
+ scope,
1631
+ mirroredTo
1632
+ };
1633
+ }
1634
+ await writeSkillsLock(projectRoot, lock);
1635
+ const { summary } = result;
1636
+ logger.success(`Skills updated to v${manifest.version}`);
1637
+ logger.info(` Created: ${summary.created}`);
1638
+ logger.info(` Overwritten: ${summary.overwritten}`);
1639
+ logger.info(` Managed: ${summary.managed}`);
1640
+ logger.info(` Skipped: ${summary.skipped}`);
1641
+ } catch (err) {
1642
+ logger.error(`Failed to update skills: ${err.message}`);
1643
+ logger.debug(err.stack ?? "");
1644
+ process.exitCode = 1;
1645
+ }
1646
+ });
1647
+
1648
+ // src/commands/skills/uninstall.ts
1649
+ import { Command as Command10 } from "commander";
1650
+ import * as prompts3 from "@clack/prompts";
1651
+ import * as path12 from "path";
1652
+ import * as fs9 from "fs/promises";
1653
+ var SKILLS_PACKAGE3 = "@teamix-evo/skills";
1654
+ var uninstallCommand2 = new Command10("uninstall").description(
1655
+ "\u5378\u8F7D\u5DF2\u5B89\u88C5\u7684 teamix-evo skills\uFF1B\u4E0D\u4F20 ids \u5219\u5378\u8F7D\u6574\u5305\uFF0C\u4F20 ids \u5219\u6309 skill \u5220\u9664"
1656
+ ).argument(
1657
+ "[ids...]",
1658
+ "\u53EF\u9009\uFF1A\u4EC5\u5378\u8F7D\u6307\u5B9A skill id\uFF1B\u7701\u7565\u5219\u5378\u8F7D\u6574\u4E2A skills \u5305"
1659
+ ).option("-y, --yes", "\u8DF3\u8FC7\u786E\u8BA4").action(async (ids, opts) => {
1660
+ try {
1661
+ const ide = detectIde();
1662
+ const cwd = ide.getProjectRoot();
1663
+ const projectRoot = resolveSkillsMaintenanceRoot(cwd);
1664
+ if (projectRoot !== cwd) {
1665
+ logger.info(`Using global skills meta root: ${projectRoot}`);
1666
+ }
1667
+ const config = await readProjectConfig(projectRoot);
1668
+ if (!config?.packages?.skills) {
1669
+ logger.info("Skills are not installed. Nothing to do.");
1670
+ return;
1671
+ }
1672
+ const installedManifest = await readInstalledManifest(projectRoot);
1673
+ const pkg = installedManifest?.installed.find(
1674
+ (p) => p.package === SKILLS_PACKAGE3
1675
+ );
1676
+ const resources = pkg?.resources ?? [];
1677
+ if (ids.length === 0) {
1678
+ await runFullUninstall({
1679
+ projectRoot,
1680
+ config,
1681
+ installedManifest,
1682
+ pkg,
1683
+ resources,
1684
+ opts
1685
+ });
1686
+ return;
1687
+ }
1688
+ await runPartialUninstall({
1689
+ projectRoot,
1690
+ installedManifest,
1691
+ pkg,
1692
+ resources,
1693
+ ids,
1694
+ opts
1695
+ });
1696
+ } catch (err) {
1697
+ logger.error(`Failed to uninstall: ${err.message}`);
1698
+ logger.debug(err.stack ?? "");
1699
+ process.exitCode = 1;
1700
+ }
1701
+ });
1702
+ async function runFullUninstall(args) {
1703
+ const { projectRoot, config, installedManifest, pkg, resources, opts } = args;
1704
+ logger.info(
1705
+ `Will remove ${resources.length} skill file(s) installed by ${SKILLS_PACKAGE3}.`
1706
+ );
1707
+ if (!opts.yes) {
1708
+ const confirm4 = await prompts3.confirm({
1709
+ message: "\u786E\u8BA4\u5378\u8F7D\uFF1F\u6B64\u64CD\u4F5C\u4F1A\u5220\u9664\u4E0A\u8FF0\u6587\u4EF6\u3002",
1710
+ initialValue: false
1711
+ });
1712
+ if (prompts3.isCancel(confirm4) || !confirm4) {
1713
+ logger.info("Cancelled.");
1714
+ return;
1715
+ }
1716
+ }
1717
+ const removed = await removeSkillFiles(resources);
1718
+ logger.debug(`Removed ${removed.length} files`);
1719
+ const skillsRoot = getSkillsSourceDir(projectRoot);
1720
+ try {
1721
+ await fs9.rm(skillsRoot, { recursive: true, force: true });
1722
+ logger.debug(`Removed source dir ${skillsRoot}`);
1723
+ } catch (err) {
1724
+ logger.warn(
1725
+ `Failed to remove ${skillsRoot}: ${err.message}`
1726
+ );
1727
+ }
1728
+ if (installedManifest && pkg) {
1729
+ installedManifest.installed = installedManifest.installed.filter(
1730
+ (p) => p.package !== SKILLS_PACKAGE3
1731
+ );
1732
+ await writeInstalledManifest(projectRoot, installedManifest);
1733
+ }
1734
+ delete config.packages.skills;
1735
+ await writeProjectConfig(projectRoot, config);
1736
+ logger.success(`Uninstalled ${SKILLS_PACKAGE3}`);
1737
+ logger.info(` Removed: ${removed.length} files`);
1738
+ logger.info(` Source: ${path12.relative(projectRoot, skillsRoot)} (cleaned)`);
1739
+ }
1740
+ async function runPartialUninstall(args) {
1741
+ const { projectRoot, installedManifest, pkg, resources, ids, opts } = args;
1742
+ const grouped = groupBySkillId(resources);
1743
+ const requested = dedupe(ids);
1744
+ const matched = requested.filter((id) => grouped.has(id));
1745
+ const missing = requested.filter((id) => !grouped.has(id));
1746
+ if (missing.length > 0) {
1747
+ logger.warn(`Not installed (skipped): ${missing.join(", ")}`);
1748
+ }
1749
+ if (matched.length === 0) {
1750
+ logger.info("Nothing to remove.");
1751
+ return;
1752
+ }
1753
+ const toRemove = matched.flatMap(
1754
+ (id) => grouped.get(id) ?? []
1755
+ );
1756
+ logger.info(
1757
+ `Will remove ${matched.length} skill(s): ${matched.join(", ")} (${toRemove.length} file(s)).`
1758
+ );
1759
+ if (!opts.yes) {
1760
+ const confirm4 = await prompts3.confirm({
1761
+ message: "\u786E\u8BA4\u5378\u8F7D\uFF1F\u6B64\u64CD\u4F5C\u4F1A\u5220\u9664\u4E0A\u8FF0\u6587\u4EF6\u3002",
1762
+ initialValue: false
1763
+ });
1764
+ if (prompts3.isCancel(confirm4) || !confirm4) {
1765
+ logger.info("Cancelled.");
1766
+ return;
1767
+ }
1768
+ }
1769
+ const removed = await removeSkillFiles(toRemove);
1770
+ logger.debug(`Removed ${removed.length} files`);
1771
+ for (const id of matched) {
1772
+ const dir = getSkillsSourceDir(projectRoot, id);
1773
+ try {
1774
+ await fs9.rm(dir, { recursive: true, force: true });
1775
+ logger.debug(`Removed source dir ${dir}`);
1776
+ } catch (err) {
1777
+ logger.warn(`Failed to remove ${dir}: ${err.message}`);
1778
+ }
1779
+ }
1780
+ const lock = await readSkillsLock(projectRoot);
1781
+ if (lock) {
1782
+ for (const id of matched) delete lock.skills[id];
1783
+ await writeSkillsLock(projectRoot, lock);
1784
+ }
1785
+ if (installedManifest && pkg) {
1786
+ pkg.resources = resources.filter((r) => !matched.includes(skillIdOf(r)));
1787
+ await writeInstalledManifest(projectRoot, installedManifest);
1788
+ }
1789
+ logger.success(
1790
+ `Removed ${matched.length} skill(s): ${matched.join(", ")}`
1791
+ );
1792
+ logger.info(` Files: ${removed.length}`);
1793
+ if (missing.length > 0) {
1794
+ logger.info(` Skipped: ${missing.join(", ")} (not installed)`);
1795
+ }
1796
+ }
1797
+ function skillIdOf(r) {
1798
+ return r.id.split(":")[0];
1799
+ }
1800
+ function groupBySkillId(records) {
1801
+ const map = /* @__PURE__ */ new Map();
1802
+ for (const r of records) {
1803
+ const id = skillIdOf(r);
1804
+ const bucket = map.get(id);
1805
+ if (bucket) bucket.push(r);
1806
+ else map.set(id, [r]);
1807
+ }
1808
+ return map;
1809
+ }
1810
+ function dedupe(values) {
1811
+ return Array.from(new Set(values));
1812
+ }
1813
+
1814
+ // src/commands/skills/sync.ts
1815
+ import { Command as Command11 } from "commander";
1816
+
1817
+ // src/core/skills-sync.ts
1818
+ import * as fs10 from "fs/promises";
1819
+ var SKILLS_PACKAGE_DEFAULT = "@teamix-evo/skills";
1820
+ async function runSkillsSync(options) {
1821
+ const { projectRoot, names } = options;
1822
+ const lock = await readSkillsLock(projectRoot);
1823
+ if (!lock || Object.keys(lock.skills).length === 0) {
1824
+ return {
1825
+ status: "no-skills",
1826
+ syncedSkillIds: [],
1827
+ fileCount: 0,
1828
+ resources: [],
1829
+ missingSourceIds: []
1830
+ };
1831
+ }
1832
+ const skillIds = Object.keys(lock.skills);
1833
+ const targets = names ? skillIds.filter((id) => names.includes(id)) : skillIds;
1834
+ const allResources = [];
1835
+ const synced = [];
1836
+ const missing = [];
1837
+ for (const skillId of targets) {
1838
+ const lockEntry = lock.skills[skillId];
1839
+ if (!lockEntry) continue;
1840
+ const sourceDir = getSkillsSourceDir(projectRoot, skillId);
1841
+ if (!await dirExists(sourceDir)) {
1842
+ logger.warn(`Skill "${skillId}" has no source at ${sourceDir}; skipped.`);
1843
+ missing.push(skillId);
1844
+ continue;
1845
+ }
1846
+ const ides = options.ides ?? lockEntry.mirroredTo;
1847
+ const scope = options.scope ?? lockEntry.scope;
1848
+ if (ides.length === 0) {
1849
+ logger.warn(`Skill "${skillId}" has no IDE mirror targets; skipped.`);
1850
+ continue;
1851
+ }
1852
+ const result = await syncSkillsToIdes({
1853
+ projectRoot,
1854
+ skills: [
1855
+ {
1856
+ id: skillId,
1857
+ name: skillId,
1858
+ updateStrategy: "regenerable"
1859
+ }
1860
+ ],
1861
+ ides,
1862
+ scope
1863
+ });
1864
+ allResources.push(...result.resources);
1865
+ synced.push(skillId);
1866
+ lock.skills[skillId] = {
1867
+ ...lockEntry,
1868
+ mirroredTo: ides,
1869
+ scope,
1870
+ installedAt: (/* @__PURE__ */ new Date()).toISOString()
1871
+ };
1872
+ }
1873
+ await writeSkillsLock(projectRoot, lock);
1874
+ await refreshMirrorRecords(projectRoot, allResources);
1875
+ return {
1876
+ status: "synced",
1877
+ syncedSkillIds: synced,
1878
+ fileCount: allResources.length,
1879
+ resources: allResources,
1880
+ missingSourceIds: missing
1881
+ };
1882
+ }
1883
+ async function dirExists(p) {
1884
+ try {
1885
+ const stat4 = await fs10.stat(p);
1886
+ return stat4.isDirectory();
1887
+ } catch {
1888
+ return false;
1889
+ }
1890
+ }
1891
+ async function refreshMirrorRecords(projectRoot, newMirrorRecords) {
1892
+ const installed = await readInstalledManifest(projectRoot);
1893
+ if (!installed) return;
1894
+ const pkg = installed.installed.find((p) => p.package === SKILLS_PACKAGE_DEFAULT);
1895
+ if (!pkg) return;
1896
+ const sourceOnly = pkg.resources.filter((r) => r.ide === void 0);
1897
+ pkg.resources = [...sourceOnly, ...newMirrorRecords];
1898
+ pkg.installedAt = (/* @__PURE__ */ new Date()).toISOString();
1899
+ await writeInstalledManifest(projectRoot, installed);
1900
+ }
1901
+
1902
+ // src/commands/skills/sync.ts
1903
+ var syncCommand = new Command11("sync").description(
1904
+ "\u628A .teamix-evo/skills/ \u4E0B\u7684\u6E90\u91CD\u65B0\u955C\u50CF\u5230 IDE \u8DEF\u5F84\uFF08.qoder / .claude\uFF09"
1905
+ ).argument(
1906
+ "[names...]",
1907
+ "\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"
1908
+ ).option(
1909
+ "--ide <list>",
1910
+ "\u9017\u53F7\u5206\u9694\u7684 IDE \u5217\u8868\uFF08\u8986\u76D6 lock \u4E2D\u8BB0\u5F55\u7684 mirroredTo\uFF09"
1911
+ ).option(
1912
+ "--scope <scope>",
1913
+ "project | global\uFF08\u8986\u76D6 lock \u4E2D\u8BB0\u5F55\u7684 scope\uFF09"
1914
+ ).action(async (names, opts) => {
1915
+ try {
1916
+ const ide = detectIde();
1917
+ const cwd = ide.getProjectRoot();
1918
+ const projectRoot = resolveSkillsMaintenanceRoot(cwd);
1919
+ if (projectRoot !== cwd) {
1920
+ logger.info(`Using global skills meta root: ${projectRoot}`);
1921
+ }
1922
+ const ides = opts.ide ? parseIdeList2(opts.ide) : void 0;
1923
+ const scope = opts.scope ? parseScope2(opts.scope) : void 0;
1924
+ const result = await runSkillsSync({
1925
+ projectRoot,
1565
1926
  ides,
1566
1927
  scope,
1567
- installed: installedResources
1928
+ names: names.length > 0 ? names : void 0
1568
1929
  });
1569
- config.packages.skills.version = manifest.version;
1570
- await writeProjectConfig(projectRoot, config);
1571
- const idx = installedManifest.installed.findIndex(
1572
- (p) => p.package === SKILLS_PACKAGE2
1930
+ if (result.status === "no-skills") {
1931
+ logger.info(
1932
+ "No skills recorded in .teamix-evo/skills/manifest.lock.json. Nothing to sync."
1933
+ );
1934
+ return;
1935
+ }
1936
+ logger.success(
1937
+ `Synced ${result.syncedSkillIds.length} skill(s) \u2192 ${result.fileCount} file(s)`
1573
1938
  );
1574
- const entry = {
1575
- package: SKILLS_PACKAGE2,
1576
- variant: FLAT_VARIANT2,
1577
- version: manifest.version,
1578
- installedAt: (/* @__PURE__ */ new Date()).toISOString(),
1579
- resources: result.resources
1580
- };
1581
- if (idx >= 0) installedManifest.installed[idx] = entry;
1582
- else installedManifest.installed.push(entry);
1583
- await writeInstalledManifest(projectRoot, installedManifest);
1584
- const { summary } = result;
1585
- logger.success(`Skills updated to v${manifest.version}`);
1586
- logger.info(` Created: ${summary.created}`);
1587
- logger.info(` Overwritten: ${summary.overwritten}`);
1588
- logger.info(` Managed: ${summary.managed}`);
1589
- logger.info(` Skipped: ${summary.skipped}`);
1939
+ if (result.syncedSkillIds.length > 0) {
1940
+ logger.info(` Skills: ${result.syncedSkillIds.join(", ")}`);
1941
+ }
1942
+ if (result.missingSourceIds.length > 0) {
1943
+ logger.warn(
1944
+ ` Missing source: ${result.missingSourceIds.join(
1945
+ ", "
1946
+ )} (run "skills add <id>" to (re)install)`
1947
+ );
1948
+ }
1590
1949
  } catch (err) {
1591
- logger.error(`Failed to update skills: ${err.message}`);
1950
+ logger.error(`Failed to sync skills: ${err.message}`);
1592
1951
  logger.debug(err.stack ?? "");
1593
1952
  process.exitCode = 1;
1594
1953
  }
1595
1954
  });
1955
+ function parseIdeList2(input) {
1956
+ const parts = input.split(",").map((s) => s.trim().toLowerCase()).filter(Boolean);
1957
+ const result = [];
1958
+ for (const p of parts) {
1959
+ if (p === "qoder" || p === "claude") {
1960
+ if (!result.includes(p)) result.push(p);
1961
+ } else {
1962
+ throw new Error(
1963
+ `Unknown IDE: "${p}". Expected one of: ${ALL_IDE_KINDS.join(", ")}.`
1964
+ );
1965
+ }
1966
+ }
1967
+ return result;
1968
+ }
1969
+ function parseScope2(input) {
1970
+ const v = input.toLowerCase();
1971
+ if (v === "project" || v === "global") return v;
1972
+ throw new Error(`Invalid --scope: "${input}". Expected project | global.`);
1973
+ }
1596
1974
 
1597
- // src/commands/skills/uninstall.ts
1598
- import { Command as Command9 } from "commander";
1599
- import * as prompts3 from "@clack/prompts";
1600
- var SKILLS_PACKAGE3 = "@teamix-evo/skills";
1601
- var uninstallCommand2 = new Command9("uninstall").description("\u5378\u8F7D\u5DF2\u5B89\u88C5\u7684 teamix-evo skills\uFF08\u5220\u9664\u6CE8\u5165\u5230 IDE \u7684\u6280\u80FD\u6587\u4EF6\uFF09").option("-y, --yes", "\u8DF3\u8FC7\u786E\u8BA4").action(async (opts) => {
1975
+ // src/commands/skills/doctor.ts
1976
+ import { Command as Command12 } from "commander";
1977
+
1978
+ // src/core/skills-doctor.ts
1979
+ import * as path13 from "path";
1980
+ import * as fs11 from "fs/promises";
1981
+ async function runSkillsDoctor(options) {
1982
+ const { projectRoot } = options;
1983
+ const lock = await readSkillsLock(projectRoot);
1984
+ if (!lock || Object.keys(lock.skills).length === 0) {
1985
+ return { status: "no-skills", findings: [] };
1986
+ }
1987
+ const findings = [];
1988
+ for (const [skillId, entry] of Object.entries(lock.skills)) {
1989
+ const sourceDir = getSkillsSourceDir(projectRoot, skillId);
1990
+ if (!await dirExists2(sourceDir)) {
1991
+ findings.push({
1992
+ kind: "missing-source",
1993
+ skillId,
1994
+ path: sourceDir,
1995
+ detail: 'Run "teamix-evo skills add" to reinstall.'
1996
+ });
1997
+ continue;
1998
+ }
1999
+ const sourceFiles = await walkDir(sourceDir);
2000
+ const sourceContents = /* @__PURE__ */ new Map();
2001
+ for (const f of sourceFiles) {
2002
+ const rel2 = path13.relative(sourceDir, f);
2003
+ sourceContents.set(rel2, await fs11.readFile(f, "utf-8"));
2004
+ }
2005
+ for (const ide of entry.mirroredTo) {
2006
+ const adapter = getAdapter(ide);
2007
+ const mirrorDir = adapter.getSkillTargetDir(
2008
+ skillId,
2009
+ entry.scope,
2010
+ projectRoot
2011
+ );
2012
+ if (!await dirExists2(mirrorDir)) {
2013
+ findings.push({
2014
+ kind: "missing-mirror",
2015
+ skillId,
2016
+ ide,
2017
+ scope: entry.scope,
2018
+ path: mirrorDir,
2019
+ detail: 'Run "teamix-evo skills sync" to re-mirror.'
2020
+ });
2021
+ continue;
2022
+ }
2023
+ for (const [rel2, sourceContent] of sourceContents.entries()) {
2024
+ const mirrorFile = path13.join(mirrorDir, rel2);
2025
+ if (!await fileExists(mirrorFile)) {
2026
+ findings.push({
2027
+ kind: "missing-mirror",
2028
+ skillId,
2029
+ ide,
2030
+ scope: entry.scope,
2031
+ path: mirrorFile,
2032
+ detail: 'Run "teamix-evo skills sync" to re-mirror.'
2033
+ });
2034
+ continue;
2035
+ }
2036
+ const mirrorContent = await fs11.readFile(mirrorFile, "utf-8");
2037
+ if (computeHash(mirrorContent) !== computeHash(sourceContent)) {
2038
+ findings.push({
2039
+ kind: "mirror-drift",
2040
+ skillId,
2041
+ ide,
2042
+ scope: entry.scope,
2043
+ path: mirrorFile,
2044
+ detail: 'Mirror differs from source. Re-run "teamix-evo skills sync" to overwrite.'
2045
+ });
2046
+ }
2047
+ }
2048
+ }
2049
+ }
2050
+ return {
2051
+ status: findings.length === 0 ? "clean" : "drift",
2052
+ findings
2053
+ };
2054
+ }
2055
+ async function dirExists2(p) {
2056
+ try {
2057
+ const stat4 = await fs11.stat(p);
2058
+ return stat4.isDirectory();
2059
+ } catch {
2060
+ return false;
2061
+ }
2062
+ }
2063
+
2064
+ // src/commands/skills/doctor.ts
2065
+ var doctorCommand = new Command12("doctor").description(
2066
+ "\u68C0\u67E5 .teamix-evo/skills/ \u6E90\u4E0E IDE \u955C\u50CF\u662F\u5426\u6F02\u79FB\uFF1B\u63D0\u793A\u5982\u4F55\u4FEE\u590D"
2067
+ ).action(async () => {
1602
2068
  try {
1603
2069
  const ide = detectIde();
1604
- const projectRoot = ide.getProjectRoot();
1605
- const config = await readProjectConfig(projectRoot);
1606
- if (!config?.packages?.skills) {
1607
- logger.info("Skills are not installed. Nothing to do.");
2070
+ const cwd = ide.getProjectRoot();
2071
+ const projectRoot = resolveSkillsMaintenanceRoot(cwd);
2072
+ if (projectRoot !== cwd) {
2073
+ logger.info(`Using global skills meta root: ${projectRoot}`);
2074
+ }
2075
+ const result = await runSkillsDoctor({ projectRoot });
2076
+ if (result.status === "no-skills") {
2077
+ logger.info(
2078
+ 'No skills recorded. Run "teamix-evo skills add" first.'
2079
+ );
1608
2080
  return;
1609
2081
  }
1610
- const installedManifest = await readInstalledManifest(projectRoot);
1611
- const pkg = installedManifest?.installed.find(
1612
- (p) => p.package === SKILLS_PACKAGE3
1613
- );
1614
- const resources = pkg?.resources ?? [];
1615
- logger.info(
1616
- `Will remove ${resources.length} skill file(s) installed by ${SKILLS_PACKAGE3}.`
1617
- );
1618
- if (!opts.yes) {
1619
- const confirm4 = await prompts3.confirm({
1620
- message: "\u786E\u8BA4\u5378\u8F7D\uFF1F\u6B64\u64CD\u4F5C\u4F1A\u5220\u9664\u4E0A\u8FF0\u6587\u4EF6\u3002",
1621
- initialValue: false
1622
- });
1623
- if (prompts3.isCancel(confirm4) || !confirm4) {
1624
- logger.info("Cancelled.");
1625
- return;
1626
- }
2082
+ if (result.status === "clean") {
2083
+ logger.success("Skills are in sync. No drift detected.");
2084
+ return;
1627
2085
  }
1628
- const removed = await removeSkillFiles(resources);
1629
- logger.debug(`Removed ${removed.length} files`);
1630
- if (installedManifest && pkg) {
1631
- installedManifest.installed = installedManifest.installed.filter(
1632
- (p) => p.package !== SKILLS_PACKAGE3
1633
- );
1634
- await writeInstalledManifest(projectRoot, installedManifest);
2086
+ logger.warn(
2087
+ `Found ${result.findings.length} drift issue(s). Run "teamix-evo skills sync" to repair mirrors.`
2088
+ );
2089
+ for (const f of result.findings) {
2090
+ const idePart = f.ide ? ` [${f.ide}]` : "";
2091
+ logger.info(` - ${f.kind}${idePart}: ${f.skillId}`);
2092
+ logger.info(` ${f.path}`);
2093
+ if (f.detail) logger.info(` ${f.detail}`);
1635
2094
  }
1636
- delete config.packages.skills;
1637
- await writeProjectConfig(projectRoot, config);
1638
- logger.success(`Uninstalled ${SKILLS_PACKAGE3}`);
1639
- logger.info(` Removed: ${removed.length} files`);
2095
+ process.exitCode = 1;
1640
2096
  } catch (err) {
1641
- logger.error(`Failed to uninstall: ${err.message}`);
2097
+ logger.error(`Failed to run doctor: ${err.message}`);
1642
2098
  logger.debug(err.stack ?? "");
1643
2099
  process.exitCode = 1;
1644
2100
  }
1645
2101
  });
1646
2102
 
1647
2103
  // src/commands/skills/index.ts
1648
- var skillsCommand = new Command10("skills").description(
1649
- "\u7BA1\u7406 teamix-evo skills\uFF08\u5411 AI IDE \u6CE8\u5165\u6280\u80FD\uFF09"
2104
+ var skillsCommand = new Command13("skills").description(
2105
+ "\u7BA1\u7406 teamix-evo skills\uFF08\u5411 AI IDE \u6CE8\u5165\u6280\u80FD\uFF1Bsource-mirror \u6A21\u578B\u89C1 ADR 0013\uFF09"
1650
2106
  );
1651
2107
  skillsCommand.addCommand(addCommand);
1652
2108
  skillsCommand.addCommand(listCommand2);
1653
2109
  skillsCommand.addCommand(updateCommand2);
2110
+ skillsCommand.addCommand(syncCommand);
2111
+ skillsCommand.addCommand(doctorCommand);
1654
2112
  skillsCommand.addCommand(uninstallCommand2);
1655
2113
 
1656
2114
  // src/commands/ui/index.ts
1657
- import { Command as Command14 } from "commander";
2115
+ import { Command as Command17 } from "commander";
1658
2116
 
1659
2117
  // src/commands/ui/init.ts
1660
- import { Command as Command11 } from "commander";
2118
+ import { Command as Command14 } from "commander";
1661
2119
  import * as prompts4 from "@clack/prompts";
1662
2120
 
1663
2121
  // src/core/ui-init.ts
@@ -1665,7 +2123,9 @@ var DEFAULT_UI_ALIASES = {
1665
2123
  components: "src/components/ui",
1666
2124
  hooks: "src/hooks",
1667
2125
  utils: "src/lib/utils",
1668
- lib: "src/lib"
2126
+ lib: "src/lib",
2127
+ business: "src/components/business",
2128
+ templates: "src/templates"
1669
2129
  };
1670
2130
  var DEFAULT_UI_ICON_LIBRARY = "lucide";
1671
2131
  async function runUiInit(options) {
@@ -1680,7 +2140,9 @@ async function runUiInit(options) {
1680
2140
  components: options.aliases?.components ?? DEFAULT_UI_ALIASES.components,
1681
2141
  hooks: options.aliases?.hooks ?? DEFAULT_UI_ALIASES.hooks,
1682
2142
  utils: options.aliases?.utils ?? DEFAULT_UI_ALIASES.utils,
1683
- lib: options.aliases?.lib ?? DEFAULT_UI_ALIASES.lib
2143
+ lib: options.aliases?.lib ?? DEFAULT_UI_ALIASES.lib,
2144
+ business: options.aliases?.business ?? DEFAULT_UI_ALIASES.business,
2145
+ templates: options.aliases?.templates ?? DEFAULT_UI_ALIASES.templates
1684
2146
  };
1685
2147
  const iconLibrary = options.iconLibrary ?? DEFAULT_UI_ICON_LIBRARY;
1686
2148
  const tsx = options.tsx ?? true;
@@ -1710,7 +2172,7 @@ async function runUiInit(options) {
1710
2172
  }
1711
2173
 
1712
2174
  // src/commands/ui/init.ts
1713
- var initCommand2 = new Command11("init").description(
2175
+ var initCommand2 = new Command14("init").description(
1714
2176
  "\u521D\u59CB\u5316 teamix-evo ui \u914D\u7F6E\uFF08\u8BE2\u95EE aliases / iconLibrary / tsx / rsc\uFF09"
1715
2177
  ).option("-y, --yes", "\u4F7F\u7528\u9ED8\u8BA4\u503C\uFF0C\u8DF3\u8FC7\u4EA4\u4E92").option(
1716
2178
  "--components <path>",
@@ -1757,7 +2219,9 @@ async function resolveConfig(opts) {
1757
2219
  components: opts.components ?? DEFAULT_UI_ALIASES.components,
1758
2220
  hooks: opts.hooks ?? DEFAULT_UI_ALIASES.hooks,
1759
2221
  utils: opts.utils ?? DEFAULT_UI_ALIASES.utils,
1760
- lib: opts.lib ?? DEFAULT_UI_ALIASES.lib
2222
+ lib: opts.lib ?? DEFAULT_UI_ALIASES.lib,
2223
+ business: DEFAULT_UI_ALIASES.business,
2224
+ templates: DEFAULT_UI_ALIASES.templates
1761
2225
  },
1762
2226
  iconLibrary: opts.iconLibrary ?? "lucide",
1763
2227
  tsx: opts.tsx ?? true,
@@ -1800,7 +2264,14 @@ async function resolveConfig(opts) {
1800
2264
  });
1801
2265
  if (prompts4.isCancel(rscAns)) throw new Error("Cancelled by user.");
1802
2266
  return {
1803
- aliases: { components, hooks, utils, lib },
2267
+ aliases: {
2268
+ components,
2269
+ hooks,
2270
+ utils,
2271
+ lib,
2272
+ business: DEFAULT_UI_ALIASES.business,
2273
+ templates: DEFAULT_UI_ALIASES.templates
2274
+ },
1804
2275
  iconLibrary,
1805
2276
  tsx: tsxAns,
1806
2277
  rsc: rscAns
@@ -1808,26 +2279,26 @@ async function resolveConfig(opts) {
1808
2279
  }
1809
2280
 
1810
2281
  // src/commands/ui/add.ts
1811
- import { Command as Command12 } from "commander";
2282
+ import { Command as Command15 } from "commander";
1812
2283
 
1813
2284
  // src/core/ui-client.ts
1814
- import * as path12 from "path";
1815
- import * as fs10 from "fs/promises";
2285
+ import * as path14 from "path";
2286
+ import * as fs12 from "fs/promises";
1816
2287
  import { createRequire as createRequire3 } from "module";
1817
2288
  import { loadUiPackageManifest } from "@teamix-evo/registry";
1818
2289
  var require4 = createRequire3(import.meta.url);
1819
- function resolvePackageRoot3(packageName) {
2290
+ function resolvePackageRoot2(packageName) {
1820
2291
  const pkgJsonPath = require4.resolve(`${packageName}/package.json`);
1821
- return path12.dirname(pkgJsonPath);
2292
+ return path14.dirname(pkgJsonPath);
1822
2293
  }
1823
2294
  async function loadUiData(packageName) {
1824
- const packageRoot = resolvePackageRoot3(packageName);
2295
+ const packageRoot = resolvePackageRoot2(packageName);
1825
2296
  logger.debug(`Resolved ui package root: ${packageRoot}`);
1826
2297
  const manifest = await loadUiPackageManifest(packageRoot);
1827
2298
  let data = {};
1828
- const dataPath = path12.join(packageRoot, "_data.json");
2299
+ const dataPath = path14.join(packageRoot, "_data.json");
1829
2300
  try {
1830
- const raw = await fs10.readFile(dataPath, "utf-8");
2301
+ const raw = await fs12.readFile(dataPath, "utf-8");
1831
2302
  data = JSON.parse(raw);
1832
2303
  } catch (err) {
1833
2304
  if (err.code !== "ENOENT") {
@@ -1839,8 +2310,8 @@ async function loadUiData(packageName) {
1839
2310
  }
1840
2311
 
1841
2312
  // src/core/ui-installer.ts
1842
- import * as path13 from "path";
1843
- import * as fs11 from "fs/promises";
2313
+ import * as path15 from "path";
2314
+ import * as fs13 from "fs/promises";
1844
2315
  import { resolveUiEntryOrder } from "@teamix-evo/registry";
1845
2316
 
1846
2317
  // src/utils/transform-imports.ts
@@ -1858,10 +2329,19 @@ function rewriteImports(source, aliases) {
1858
2329
  if (!aliasKey) return full;
1859
2330
  const alias = aliases[aliasKey];
1860
2331
  const normalized = aliasToImportPath(alias);
1861
- return `${quote}${normalized}${rest ?? ""}${quote}`;
2332
+ const flatRest = flattenRestPath(rest);
2333
+ return `${quote}${normalized}${flatRest}${quote}`;
1862
2334
  }
1863
2335
  );
1864
2336
  }
2337
+ function flattenRestPath(rest) {
2338
+ if (!rest) return "";
2339
+ const segments = rest.split("/");
2340
+ if (segments.length === 3) {
2341
+ return `/${segments[2]}`;
2342
+ }
2343
+ return rest;
2344
+ }
1865
2345
  function aliasToImportPath(alias) {
1866
2346
  const trimmed = alias.replace(/^\.\//, "").replace(/\/$/, "");
1867
2347
  if (trimmed.startsWith("src/")) {
@@ -1904,8 +2384,8 @@ async function installUiEntries(options) {
1904
2384
  skipped++;
1905
2385
  continue;
1906
2386
  }
1907
- const sourceAbs = path13.resolve(packageRoot, file.source);
1908
- const raw = await fs11.readFile(sourceAbs, "utf-8");
2387
+ const sourceAbs = path15.resolve(packageRoot, file.source);
2388
+ const raw = await fs13.readFile(sourceAbs, "utf-8");
1909
2389
  const transformed = rewriteImports(raw, aliases);
1910
2390
  await writeFileSafe(targetAbs, transformed);
1911
2391
  written++;
@@ -1918,9 +2398,9 @@ async function installUiEntries(options) {
1918
2398
  });
1919
2399
  }
1920
2400
  if (entry.meta) {
1921
- const metaSourceAbs = path13.resolve(packageRoot, entry.meta);
1922
- const metaContent = await fs11.readFile(metaSourceAbs, "utf-8");
1923
- const metaTargetAbs = path13.join(
2401
+ const metaSourceAbs = path15.resolve(packageRoot, entry.meta);
2402
+ const metaContent = await fs13.readFile(metaSourceAbs, "utf-8");
2403
+ const metaTargetAbs = path15.join(
1924
2404
  projectRoot,
1925
2405
  DESIGN_COMPONENTS_DIR,
1926
2406
  `${entry.id}.meta.md`
@@ -1952,10 +2432,10 @@ function resolveTargetPath(projectRoot, aliases, entry, file) {
1952
2432
  `Entry "${entry.id}" requires alias "${file.targetAlias}" but it is not configured.`
1953
2433
  );
1954
2434
  }
1955
- return path13.join(projectRoot, aliasDir, file.targetName);
2435
+ return path15.join(projectRoot, aliasDir, file.targetName);
1956
2436
  }
1957
2437
  function rel(projectRoot, abs) {
1958
- return path13.relative(projectRoot, abs);
2438
+ return path15.relative(projectRoot, abs);
1959
2439
  }
1960
2440
 
1961
2441
  // src/core/ui-add.ts
@@ -2030,7 +2510,7 @@ function mergeResources(prior, next) {
2030
2510
  }
2031
2511
 
2032
2512
  // src/commands/ui/add.ts
2033
- var addCommand2 = new Command12("add").description(
2513
+ var addCommand2 = new Command15("add").description(
2034
2514
  "\u5B89\u88C5\u4E00\u4E2A\u6216\u591A\u4E2A ui entry\uFF08\u6309 id\uFF0C\u81EA\u52A8\u5C55\u5F00 registryDependencies\uFF09"
2035
2515
  ).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
2516
  try {
@@ -2074,7 +2554,7 @@ var addCommand2 = new Command12("add").description(
2074
2554
  });
2075
2555
 
2076
2556
  // src/commands/ui/list.ts
2077
- import { Command as Command13 } from "commander";
2557
+ import { Command as Command16 } from "commander";
2078
2558
 
2079
2559
  // src/core/ui-list.ts
2080
2560
  var DEFAULT_UI_PACKAGE2 = "@teamix-evo/ui";
@@ -2106,7 +2586,7 @@ async function runUiList(options) {
2106
2586
  }
2107
2587
 
2108
2588
  // src/commands/ui/list.ts
2109
- var listCommand3 = new Command13("list").description("\u5217\u51FA @teamix-evo/ui \u7684\u6240\u6709 entry \u53CA\u5DF2\u5B89\u88C5\u72B6\u6001").option("--installed", "\u4EC5\u5C55\u793A\u5DF2\u5B89\u88C5\u7684 entry").action(async (opts) => {
2589
+ 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
2590
  try {
2111
2591
  const ide = detectIde();
2112
2592
  const projectRoot = ide.getProjectRoot();
@@ -2152,20 +2632,301 @@ var listCommand3 = new Command13("list").description("\u5217\u51FA @teamix-evo/u
2152
2632
  });
2153
2633
 
2154
2634
  // src/commands/ui/index.ts
2155
- var uiCommand = new Command14("ui").description(
2635
+ var uiCommand = new Command17("ui").description(
2156
2636
  "\u7BA1\u7406 teamix-evo ui \u7EC4\u4EF6\uFF08\u6E90\u7801\u6CE8\u5165\u5F0F\u5B89\u88C5\uFF0Cshadcn \u98CE\u683C\uFF09"
2157
2637
  );
2158
2638
  uiCommand.addCommand(initCommand2);
2159
2639
  uiCommand.addCommand(addCommand2);
2160
2640
  uiCommand.addCommand(listCommand3);
2161
2641
 
2162
- // src/index.ts
2642
+ // src/commands/biz-ui/index.ts
2643
+ import { Command as Command20 } from "commander";
2644
+
2645
+ // src/commands/biz-ui/add.ts
2646
+ import { Command as Command18 } from "commander";
2647
+
2648
+ // src/core/variant-ui-add.ts
2649
+ import * as path16 from "path";
2650
+ import { createRequire as createRequire4 } from "module";
2651
+ import {
2652
+ loadVariantUiPackageCatalog,
2653
+ loadVariantUiPackageManifest
2654
+ } from "@teamix-evo/registry";
2163
2655
  var require5 = createRequire4(import.meta.url);
2164
- var { version } = require5("../package.json");
2165
- var program = new Command15();
2656
+ function resolvePackageRoot3(packageName) {
2657
+ const pkgJsonPath = require5.resolve(`${packageName}/package.json`);
2658
+ return path16.dirname(pkgJsonPath);
2659
+ }
2660
+ async function runVariantUiAdd(packageName, options) {
2661
+ const { projectRoot, variant, ids, overwrite } = options;
2662
+ const fullPackageName = options.packageName ?? `@teamix-evo/${packageName}`;
2663
+ if (ids.length === 0) {
2664
+ throw new Error("At least one entry id must be provided.");
2665
+ }
2666
+ const config = await readProjectConfig(projectRoot);
2667
+ const uiCfg = config?.packages?.ui;
2668
+ if (!config || !uiCfg?.aliases) {
2669
+ throw new Error(
2670
+ `UI not initialized. Run \`teamix-evo ui init\` first \u2014 \`${packageName} add\` writes into the same alias map (business / templates).`
2671
+ );
2672
+ }
2673
+ const packageRoot = options.packageRoot ?? resolvePackageRoot3(fullPackageName);
2674
+ const catalog = await loadVariantUiPackageCatalog(packageRoot);
2675
+ if (!catalog.variants.some((v) => v.name === variant)) {
2676
+ const known = catalog.variants.map((v) => v.name).join(", ");
2677
+ throw new Error(
2678
+ `Variant "${variant}" not found in ${fullPackageName}. Known variants: ${known}. Hint: \`teamix-evo ${packageName} list-variants\` shows all.`
2679
+ );
2680
+ }
2681
+ const variantDir = path16.join(packageRoot, "variants", variant);
2682
+ const variantManifest = await loadVariantUiPackageManifest(variantDir);
2683
+ const knownIds = new Set(variantManifest.entries.map((e) => e.id));
2684
+ const unknown = ids.filter((id) => !knownIds.has(id));
2685
+ if (unknown.length > 0) {
2686
+ throw new Error(
2687
+ `Unknown entry id(s) in ${packageName}#${variant}: ${unknown.map((s) => `"${s}"`).join(", ")}. Run \`teamix-evo ${packageName} list --variant ${variant}\` to see options.`
2688
+ );
2689
+ }
2690
+ const adaptedManifest = {
2691
+ schemaVersion: 1,
2692
+ package: "ui",
2693
+ version: variantManifest.version,
2694
+ engines: variantManifest.engines,
2695
+ entries: variantManifest.entries
2696
+ };
2697
+ const result = await installUiEntries({
2698
+ projectRoot,
2699
+ manifest: adaptedManifest,
2700
+ packageRoot: variantDir,
2701
+ // sources resolved relative to variant dir
2702
+ aliases: uiCfg.aliases,
2703
+ requested: ids,
2704
+ skipExisting: !overwrite
2705
+ });
2706
+ const installed = await readInstalledManifest(
2707
+ projectRoot
2708
+ ) ?? { schemaVersion: 1, installed: [] };
2709
+ const idx = installed.installed.findIndex(
2710
+ (p) => p.package === fullPackageName && p.variant === variant
2711
+ );
2712
+ const prior = idx >= 0 ? installed.installed[idx] : null;
2713
+ const mergedResources = mergeResources2(
2714
+ prior?.resources ?? [],
2715
+ result.resources
2716
+ );
2717
+ const entry = {
2718
+ package: fullPackageName,
2719
+ variant,
2720
+ version: variantManifest.version,
2721
+ installedAt: (/* @__PURE__ */ new Date()).toISOString(),
2722
+ resources: mergedResources
2723
+ };
2724
+ if (idx >= 0) installed.installed[idx] = entry;
2725
+ else installed.installed.push(entry);
2726
+ await writeInstalledManifest(projectRoot, installed);
2727
+ return {
2728
+ packageName: fullPackageName,
2729
+ variant,
2730
+ orderedIds: result.orderedIds,
2731
+ written: result.written,
2732
+ skipped: result.skipped,
2733
+ metaFiles: result.metaFiles,
2734
+ npmDependencies: result.npmDependencies,
2735
+ resources: result.resources
2736
+ };
2737
+ }
2738
+ function mergeResources2(prior, next) {
2739
+ const merged = /* @__PURE__ */ new Map();
2740
+ for (const r of prior) merged.set(r.id, r);
2741
+ for (const r of next) merged.set(r.id, r);
2742
+ return Array.from(merged.values());
2743
+ }
2744
+ async function runBizUiAdd(options) {
2745
+ return runVariantUiAdd("biz-ui", options);
2746
+ }
2747
+ async function runTemplatesAdd(options) {
2748
+ return runVariantUiAdd("templates", options);
2749
+ }
2750
+ async function listVariantUi(packageName, packageRoot) {
2751
+ const fullPackageName = `@teamix-evo/${packageName}`;
2752
+ const root = packageRoot ?? resolvePackageRoot3(fullPackageName);
2753
+ const catalog = await loadVariantUiPackageCatalog(root);
2754
+ return {
2755
+ packageName: fullPackageName,
2756
+ variants: catalog.variants.map((v) => ({
2757
+ name: v.name,
2758
+ displayName: v.displayName,
2759
+ version: v.version,
2760
+ description: v.description
2761
+ }))
2762
+ };
2763
+ }
2764
+ async function listBizUiVariants(packageRoot) {
2765
+ return listVariantUi("biz-ui", packageRoot);
2766
+ }
2767
+ async function listTemplatesVariants(packageRoot) {
2768
+ return listVariantUi("templates", packageRoot);
2769
+ }
2770
+
2771
+ // src/commands/biz-ui/add.ts
2772
+ var addCommand3 = new Command18("add").description(
2773
+ "\u5B89\u88C5\u4E00\u4E2A\u6216\u591A\u4E2A\u4E1A\u52A1 UI \u7EC4\u4EF6(\u6309 id,\u81EA\u52A8\u5C55\u5F00 ui \u5305\u7684 registryDependencies)"
2774
+ ).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(
2775
+ async (ids, opts) => {
2776
+ try {
2777
+ if (!opts.variant) {
2778
+ throw new Error(
2779
+ "--variant <name> is required. Run `teamix-evo biz-ui list-variants` to see available variants."
2780
+ );
2781
+ }
2782
+ const ide = detectIde();
2783
+ const projectRoot = ide.getProjectRoot();
2784
+ logger.info(
2785
+ `Installing biz-ui entries from variant "${opts.variant}": ${ids.join(", ")}`
2786
+ );
2787
+ const result = await runBizUiAdd({
2788
+ projectRoot,
2789
+ variant: opts.variant,
2790
+ ids,
2791
+ overwrite: opts.overwrite
2792
+ });
2793
+ logger.success(
2794
+ `biz-ui add complete: ${result.written} written, ${result.skipped} skipped, ${result.metaFiles.length} meta.`
2795
+ );
2796
+ logger.info("");
2797
+ logger.info(`Variant: ${result.variant}`);
2798
+ logger.info(`Resolved order: ${result.orderedIds.join(" \u2192 ")}`);
2799
+ const npmDeps = Object.entries(result.npmDependencies);
2800
+ if (npmDeps.length > 0) {
2801
+ logger.info("");
2802
+ logger.info("Install npm dependencies in your project:");
2803
+ const installCmd = npmDeps.map(([name, range]) => `${name}@${range}`).join(" ");
2804
+ logger.info(` pnpm add ${installCmd}`);
2805
+ }
2806
+ } catch (err) {
2807
+ logger.error(`Failed: ${err.message}`);
2808
+ logger.debug(err.stack ?? "");
2809
+ process.exitCode = 1;
2810
+ }
2811
+ }
2812
+ );
2813
+
2814
+ // src/commands/biz-ui/list-variants.ts
2815
+ import { Command as Command19 } from "commander";
2816
+ 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 () => {
2817
+ try {
2818
+ const result = await listBizUiVariants();
2819
+ logger.info(`Available biz-ui variants in ${result.packageName}:`);
2820
+ logger.info("");
2821
+ if (result.variants.length === 0) {
2822
+ logger.info(" (no variants yet)");
2823
+ return;
2824
+ }
2825
+ for (const v of result.variants) {
2826
+ logger.info(` ${v.name} (${v.displayName}) \u2014 v${v.version}`);
2827
+ if (v.description) logger.info(` ${v.description}`);
2828
+ logger.info("");
2829
+ }
2830
+ logger.info("Install from a variant: teamix-evo biz-ui add <id> --variant <name>");
2831
+ } catch (err) {
2832
+ logger.error(`Failed: ${err.message}`);
2833
+ process.exitCode = 1;
2834
+ }
2835
+ });
2836
+
2837
+ // src/commands/biz-ui/index.ts
2838
+ var bizUiCommand = new Command20("biz-ui").description(
2839
+ "\u7BA1\u7406\u4E1A\u52A1 UI \u7EC4\u4EF6(\u53D8\u4F53\u611F\u77E5 \u2014 \u4E0E design / templates \u540C\u53D8\u4F53\u540D\u7A7A\u95F4)"
2840
+ );
2841
+ bizUiCommand.addCommand(addCommand3);
2842
+ bizUiCommand.addCommand(listVariantsCommand2);
2843
+
2844
+ // src/commands/templates/index.ts
2845
+ import { Command as Command23 } from "commander";
2846
+
2847
+ // src/commands/templates/add.ts
2848
+ import { Command as Command21 } from "commander";
2849
+ var addCommand4 = new Command21("add").description(
2850
+ "\u5B89\u88C5\u4E00\u4E2A\u6216\u591A\u4E2A\u9875\u9762\u6A21\u677F(\u6309 id,\u81EA\u52A8\u5C55\u5F00 ui \u5305\u7684 registryDependencies)"
2851
+ ).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(
2852
+ async (ids, opts) => {
2853
+ try {
2854
+ if (!opts.variant) {
2855
+ throw new Error(
2856
+ "--variant <name> is required. Run `teamix-evo templates list-variants` to see available variants."
2857
+ );
2858
+ }
2859
+ const ide = detectIde();
2860
+ const projectRoot = ide.getProjectRoot();
2861
+ logger.info(
2862
+ `Installing templates from variant "${opts.variant}": ${ids.join(", ")}`
2863
+ );
2864
+ const result = await runTemplatesAdd({
2865
+ projectRoot,
2866
+ variant: opts.variant,
2867
+ ids,
2868
+ overwrite: opts.overwrite
2869
+ });
2870
+ logger.success(
2871
+ `templates add complete: ${result.written} written, ${result.skipped} skipped, ${result.metaFiles.length} meta.`
2872
+ );
2873
+ logger.info("");
2874
+ logger.info(`Variant: ${result.variant}`);
2875
+ logger.info(`Resolved order: ${result.orderedIds.join(" \u2192 ")}`);
2876
+ const npmDeps = Object.entries(result.npmDependencies);
2877
+ if (npmDeps.length > 0) {
2878
+ logger.info("");
2879
+ logger.info("Install npm dependencies in your project:");
2880
+ const installCmd = npmDeps.map(([name, range]) => `${name}@${range}`).join(" ");
2881
+ logger.info(` pnpm add ${installCmd}`);
2882
+ }
2883
+ } catch (err) {
2884
+ logger.error(`Failed: ${err.message}`);
2885
+ logger.debug(err.stack ?? "");
2886
+ process.exitCode = 1;
2887
+ }
2888
+ }
2889
+ );
2890
+
2891
+ // src/commands/templates/list-variants.ts
2892
+ import { Command as Command22 } from "commander";
2893
+ 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 () => {
2894
+ try {
2895
+ const result = await listTemplatesVariants();
2896
+ logger.info(`Available templates variants in ${result.packageName}:`);
2897
+ logger.info("");
2898
+ if (result.variants.length === 0) {
2899
+ logger.info(" (no variants yet)");
2900
+ return;
2901
+ }
2902
+ for (const v of result.variants) {
2903
+ logger.info(` ${v.name} (${v.displayName}) \u2014 v${v.version}`);
2904
+ if (v.description) logger.info(` ${v.description}`);
2905
+ logger.info("");
2906
+ }
2907
+ logger.info("Install from a variant: teamix-evo templates add <id> --variant <name>");
2908
+ } catch (err) {
2909
+ logger.error(`Failed: ${err.message}`);
2910
+ process.exitCode = 1;
2911
+ }
2912
+ });
2913
+
2914
+ // src/commands/templates/index.ts
2915
+ var templatesCommand = new Command23("templates").description(
2916
+ "\u7BA1\u7406\u9875\u9762\u6A21\u677F(\u53D8\u4F53\u611F\u77E5 \u2014 \u4E0E design / biz-ui \u540C\u53D8\u4F53\u540D\u7A7A\u95F4)"
2917
+ );
2918
+ templatesCommand.addCommand(addCommand4);
2919
+ templatesCommand.addCommand(listVariantsCommand3);
2920
+
2921
+ // src/index.ts
2922
+ var require6 = createRequire5(import.meta.url);
2923
+ var { version } = require6("../package.json");
2924
+ var program = new Command24();
2166
2925
  program.name("teamix-evo").description("Where ideas evolve. \u2014 AI Coding \u5957\u4EF6").version(version);
2167
2926
  program.addCommand(designCommand);
2168
2927
  program.addCommand(skillsCommand);
2169
2928
  program.addCommand(uiCommand);
2929
+ program.addCommand(bizUiCommand);
2930
+ program.addCommand(templatesCommand);
2170
2931
  program.parse();
2171
2932
  //# sourceMappingURL=index.js.map