teamix-evo 0.3.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,13 +1,13 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  // src/index.ts
4
- import { Command as Command24 } from "commander";
5
- import { createRequire as createRequire5 } from "module";
4
+ import { Command as Command28 } from "commander";
5
+ import { createRequire as createRequire8 } from "module";
6
6
 
7
- // src/commands/design/index.ts
7
+ // src/commands/tokens/index.ts
8
8
  import { Command as Command6 } from "commander";
9
9
 
10
- // src/commands/design/init.ts
10
+ // src/commands/tokens/init.ts
11
11
  import { Command } from "commander";
12
12
 
13
13
  // src/ide/QoderAdapter.ts
@@ -66,14 +66,13 @@ function detectIde() {
66
66
  return new QoderAdapter();
67
67
  }
68
68
 
69
- // src/core/design-init.ts
70
- import * as path9 from "path";
69
+ // src/core/tokens-init.ts
70
+ import * as path8 from "path";
71
71
  import * as fs6 from "fs/promises";
72
72
  import { createRequire as createRequire2 } from "module";
73
73
  import {
74
- loadDesignPack,
75
- loadDesignPackageManifest,
76
- mergeDefaultAndVariant
74
+ loadTokensPackageManifest,
75
+ getVariantEntry
77
76
  } from "@teamix-evo/registry";
78
77
 
79
78
  // src/utils/fs.ts
@@ -158,71 +157,22 @@ function computeHash(content) {
158
157
  return `sha256:${hash}`;
159
158
  }
160
159
 
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;
177
- }
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
- };
186
- }
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 };
192
- }
193
- return { target, strategy: "regenerable", isFrozen: false };
194
- }
195
- if (FROZEN_FILES.has(relPath)) {
196
- return {
197
- target: path4.posix.join(TEAMIX_DIR, relPath),
198
- strategy: "frozen",
199
- isFrozen: true
200
- };
201
- }
202
- return {
203
- target: path4.posix.join(TEAMIX_DIR, relPath),
204
- strategy: "regenerable",
205
- isFrozen: false
206
- };
207
- }
208
-
209
160
  // src/core/state.ts
210
- import * as path5 from "path";
161
+ import * as path4 from "path";
211
162
  import {
212
163
  validateConfig,
213
164
  validateInstalled,
214
165
  validateSkillsLock,
215
- DesignPackLockSchema
166
+ TokensPackLockSchema
216
167
  } from "@teamix-evo/registry";
217
- var TEAMIX_DIR2 = ".teamix-evo";
168
+ var TEAMIX_DIR = ".teamix-evo";
218
169
  var CONFIG_FILE = "config.json";
219
170
  var MANIFEST_FILE = "manifest.json";
220
- var DESIGN_DIR = "design";
221
- var DESIGN_LOCK_FILE = "pack.lock.json";
171
+ var TOKENS_LOCK_FILE = "tokens-lock.json";
222
172
  var SKILLS_DIR = "skills";
223
173
  var SKILLS_LOCK_FILE = "manifest.lock.json";
224
174
  function getTeamixDir(projectRoot) {
225
- return path5.join(projectRoot, TEAMIX_DIR2);
175
+ return path4.join(projectRoot, TEAMIX_DIR);
226
176
  }
227
177
  async function ensureTeamixDir(projectRoot) {
228
178
  const dir = getTeamixDir(projectRoot);
@@ -230,84 +180,85 @@ async function ensureTeamixDir(projectRoot) {
230
180
  return dir;
231
181
  }
232
182
  async function readProjectConfig(projectRoot) {
233
- const configPath = path5.join(projectRoot, TEAMIX_DIR2, CONFIG_FILE);
183
+ const configPath = path4.join(projectRoot, TEAMIX_DIR, CONFIG_FILE);
234
184
  const raw = await readFileOrNull(configPath);
235
185
  if (raw === null) return null;
186
+ let data;
236
187
  try {
237
- const data = JSON.parse(raw);
238
- const result = validateConfig(data);
239
- if (!result.success) {
240
- logger.warn(`Invalid config.json: ${result.error}`);
241
- return null;
242
- }
243
- return result.data;
188
+ data = JSON.parse(raw);
244
189
  } catch (err) {
245
- logger.warn(`Failed to parse config.json: ${err.message}`);
246
- return null;
190
+ throw new Error(
191
+ `Corrupted config.json (${err.message}). Fix the JSON manually or remove the file to start fresh; refusing to clobber prior config.`
192
+ );
193
+ }
194
+ const result = validateConfig(data);
195
+ if (!result.success) {
196
+ throw new Error(
197
+ `Invalid config.json schema: ${result.error}. Fix the file manually or remove it to start fresh.`
198
+ );
247
199
  }
200
+ return result.data;
248
201
  }
249
202
  async function writeProjectConfig(projectRoot, config) {
250
- const configPath = path5.join(projectRoot, TEAMIX_DIR2, CONFIG_FILE);
203
+ const configPath = path4.join(projectRoot, TEAMIX_DIR, CONFIG_FILE);
251
204
  await writeFileSafe(configPath, JSON.stringify(config, null, 2) + "\n");
252
205
  logger.debug(`Wrote config \u2192 ${configPath}`);
253
206
  }
254
207
  async function readInstalledManifest(projectRoot) {
255
- const manifestPath = path5.join(projectRoot, TEAMIX_DIR2, MANIFEST_FILE);
208
+ const manifestPath = path4.join(projectRoot, TEAMIX_DIR, MANIFEST_FILE);
256
209
  const raw = await readFileOrNull(manifestPath);
257
210
  if (raw === null) return null;
211
+ let data;
258
212
  try {
259
- const data = JSON.parse(raw);
260
- const result = validateInstalled(data);
261
- if (!result.success) {
262
- logger.warn(`Invalid manifest.json: ${result.error}`);
263
- return null;
264
- }
265
- return result.data;
213
+ data = JSON.parse(raw);
266
214
  } catch (err) {
267
- logger.warn(`Failed to parse manifest.json: ${err.message}`);
268
- return null;
215
+ throw new Error(
216
+ `Corrupted manifest.json (${err.message}). Fix the JSON manually or remove the file to start fresh; refusing to clobber prior install records.`
217
+ );
269
218
  }
219
+ const result = validateInstalled(data);
220
+ if (!result.success) {
221
+ throw new Error(
222
+ `Invalid manifest.json schema: ${result.error}. Fix the file manually or remove it to start fresh.`
223
+ );
224
+ }
225
+ return result.data;
270
226
  }
271
227
  async function writeInstalledManifest(projectRoot, manifest) {
272
- const manifestPath = path5.join(projectRoot, TEAMIX_DIR2, MANIFEST_FILE);
228
+ const manifestPath = path4.join(projectRoot, TEAMIX_DIR, MANIFEST_FILE);
273
229
  await writeFileSafe(manifestPath, JSON.stringify(manifest, null, 2) + "\n");
274
230
  logger.debug(`Wrote manifest \u2192 ${manifestPath}`);
275
231
  }
276
- async function readDesignPackLock(projectRoot) {
277
- const lockPath = path5.join(
278
- projectRoot,
279
- TEAMIX_DIR2,
280
- DESIGN_DIR,
281
- DESIGN_LOCK_FILE
282
- );
232
+ async function readTokensLock(projectRoot) {
233
+ const lockPath = path4.join(projectRoot, TEAMIX_DIR, TOKENS_LOCK_FILE);
283
234
  const raw = await readFileOrNull(lockPath);
284
235
  if (raw === null) return null;
285
236
  try {
286
- const parsed = DesignPackLockSchema.safeParse(JSON.parse(raw));
237
+ const parsed = TokensPackLockSchema.safeParse(JSON.parse(raw));
287
238
  if (!parsed.success) {
288
- logger.warn(`Invalid design pack.lock.json: ${parsed.error.message}`);
239
+ logger.warn(`Invalid tokens-lock.json: ${parsed.error.message}`);
289
240
  return null;
290
241
  }
291
242
  return parsed.data;
292
243
  } catch (err) {
293
244
  logger.warn(
294
- `Failed to parse design pack.lock.json: ${err.message}`
245
+ `Failed to parse tokens-lock.json: ${err.message}`
295
246
  );
296
247
  return null;
297
248
  }
298
249
  }
299
- async function readDesignVariant(projectRoot) {
300
- const lock = await readDesignPackLock(projectRoot);
250
+ async function readTokensVariant(projectRoot) {
251
+ const lock = await readTokensLock(projectRoot);
301
252
  return lock?.variant.name ?? null;
302
253
  }
303
254
  function getSkillsSourceDir(projectRoot, skillName) {
304
- const base = path5.join(projectRoot, TEAMIX_DIR2, SKILLS_DIR);
305
- return skillName ? path5.join(base, skillName) : base;
255
+ const base = path4.join(projectRoot, TEAMIX_DIR, SKILLS_DIR);
256
+ return skillName ? path4.join(base, skillName) : base;
306
257
  }
307
258
  async function readSkillsLock(projectRoot) {
308
- const lockPath = path5.join(
259
+ const lockPath = path4.join(
309
260
  projectRoot,
310
- TEAMIX_DIR2,
261
+ TEAMIX_DIR,
311
262
  SKILLS_DIR,
312
263
  SKILLS_LOCK_FILE
313
264
  );
@@ -329,9 +280,9 @@ async function readSkillsLock(projectRoot) {
329
280
  }
330
281
  }
331
282
  async function writeSkillsLock(projectRoot, lock) {
332
- const lockPath = path5.join(
283
+ const lockPath = path4.join(
333
284
  projectRoot,
334
- TEAMIX_DIR2,
285
+ TEAMIX_DIR,
335
286
  SKILLS_DIR,
336
287
  SKILLS_LOCK_FILE
337
288
  );
@@ -340,21 +291,21 @@ async function writeSkillsLock(projectRoot, lock) {
340
291
  }
341
292
 
342
293
  // src/core/skills-client.ts
343
- import * as path6 from "path";
294
+ import * as path5 from "path";
344
295
  import * as fs2 from "fs/promises";
345
296
  import { createRequire } from "module";
346
297
  import { loadSkillsPackageManifest } from "@teamix-evo/registry";
347
298
  var require2 = createRequire(import.meta.url);
348
299
  function resolvePackageRoot(packageName) {
349
300
  const pkgJsonPath = require2.resolve(`${packageName}/package.json`);
350
- return path6.dirname(pkgJsonPath);
301
+ return path5.dirname(pkgJsonPath);
351
302
  }
352
303
  async function loadSkillsData(packageName) {
353
304
  const packageRoot = resolvePackageRoot(packageName);
354
305
  logger.debug(`Resolved skills package root: ${packageRoot}`);
355
306
  const manifest = await loadSkillsPackageManifest(packageRoot);
356
307
  let data = {};
357
- const dataPath = path6.join(packageRoot, "_data.json");
308
+ const dataPath = path5.join(packageRoot, "_data.json");
358
309
  try {
359
310
  const raw = await fs2.readFile(dataPath, "utf-8");
360
311
  data = JSON.parse(raw);
@@ -368,7 +319,7 @@ async function loadSkillsData(packageName) {
368
319
  }
369
320
 
370
321
  // src/core/skills-installer.ts
371
- import * as path8 from "path";
322
+ import * as path7 from "path";
372
323
  import * as fs5 from "fs/promises";
373
324
  import { replaceManagedRegion } from "@teamix-evo/registry";
374
325
 
@@ -401,13 +352,13 @@ async function loadTemplateFile(filePath) {
401
352
  }
402
353
 
403
354
  // src/utils/path.ts
404
- import * as path7 from "path";
355
+ import * as path6 from "path";
405
356
  import * as fs4 from "fs/promises";
406
357
  async function walkDir(dir) {
407
358
  const files = [];
408
359
  const entries = await fs4.readdir(dir, { withFileTypes: true });
409
360
  for (const entry of entries) {
410
- const fullPath = path7.join(dir, entry.name);
361
+ const fullPath = path6.join(dir, entry.name);
411
362
  if (entry.isDirectory()) {
412
363
  files.push(...await walkDir(fullPath));
413
364
  } else if (entry.isFile()) {
@@ -450,12 +401,12 @@ async function installSkills(options) {
450
401
  }
451
402
  async function writeSkillSource(skill, options) {
452
403
  const { data, packageRoot, projectRoot } = options;
453
- const sourceAbs = path8.resolve(packageRoot, skill.source);
404
+ const sourceAbs = path7.resolve(packageRoot, skill.source);
454
405
  const targetDir = getSkillsSourceDir(projectRoot, skill.name);
455
406
  const stat4 = await fs5.stat(sourceAbs);
456
407
  const records = [];
457
408
  if (stat4.isFile()) {
458
- const targetFile = path8.join(targetDir, "SKILL.md");
409
+ const targetFile = path7.join(targetDir, "SKILL.md");
459
410
  const content = await renderSkillContent(sourceAbs, skill, data);
460
411
  await writeFileSafe(targetFile, content);
461
412
  records.push(makeSourceRecord(skill, targetFile, content));
@@ -465,14 +416,14 @@ async function writeSkillSource(skill, options) {
465
416
  await ensureDir(targetDir);
466
417
  const entries = await walkDir(sourceAbs);
467
418
  for (const entry of entries) {
468
- const rel2 = path8.relative(sourceAbs, entry);
469
- let targetFile = path8.join(targetDir, rel2);
419
+ const rel2 = path7.relative(sourceAbs, entry);
420
+ let targetFile = path7.join(targetDir, rel2);
470
421
  if (skill.template && targetFile.endsWith(".hbs")) {
471
422
  targetFile = targetFile.slice(0, -4);
472
423
  }
473
424
  const content = skill.template && entry.endsWith(".hbs") ? renderTemplate(await loadTemplateFile(entry), { ...data, skill }) : await fs5.readFile(entry, "utf-8");
474
425
  await writeFileSafe(targetFile, content);
475
- const relWritten = path8.relative(targetDir, targetFile);
426
+ const relWritten = path7.relative(targetDir, targetFile);
476
427
  records.push(makeSourceRecord(skill, targetFile, content, relWritten));
477
428
  logger.debug(` Wrote source: ${targetFile}`);
478
429
  }
@@ -486,11 +437,19 @@ async function mirrorSkillToIde(skill, ide, scope, projectRoot) {
486
437
  const sourceFiles = await walkDir(sourceDir);
487
438
  await ensureDir(targetDir);
488
439
  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));
440
+ const rel2 = path7.relative(sourceDir, src);
441
+ const targetFile = path7.join(targetDir, rel2);
442
+ const sourceContent = await fs5.readFile(src, "utf-8");
443
+ const existing = await readFileOrNull(targetFile);
444
+ if (existing !== null && existing !== sourceContent) {
445
+ logger.warn(
446
+ `Mirror drift detected at ${targetFile} \u2014 overwriting from source. Edit ${src} (not the mirror) and re-run \`teamix-evo skills sync\`.`
447
+ );
448
+ }
449
+ await writeFileSafe(targetFile, sourceContent);
450
+ records.push(
451
+ makeMirrorRecord(skill, targetFile, sourceContent, ide, scope, rel2)
452
+ );
494
453
  logger.debug(` Mirrored ${ide}:${scope}: ${targetFile}`);
495
454
  }
496
455
  return records;
@@ -549,7 +508,7 @@ async function updateSkills(options) {
549
508
  }
550
509
  async function rewriteSkillSource(skill, options, summary) {
551
510
  const { data, packageRoot, projectRoot } = options;
552
- const sourceAbs = path8.resolve(packageRoot, skill.source);
511
+ const sourceAbs = path7.resolve(packageRoot, skill.source);
553
512
  const targetDir = getSkillsSourceDir(projectRoot, skill.name);
554
513
  const stat4 = await fs5.stat(sourceAbs);
555
514
  if (!stat4.isFile()) {
@@ -557,8 +516,8 @@ async function rewriteSkillSource(skill, options, summary) {
557
516
  const entries = await walkDir(sourceAbs);
558
517
  const records = [];
559
518
  for (const entry of entries) {
560
- const rel2 = path8.relative(sourceAbs, entry);
561
- let targetFile2 = path8.join(targetDir, rel2);
519
+ const rel2 = path7.relative(sourceAbs, entry);
520
+ let targetFile2 = path7.join(targetDir, rel2);
562
521
  if (skill.template && targetFile2.endsWith(".hbs")) {
563
522
  targetFile2 = targetFile2.slice(0, -4);
564
523
  }
@@ -571,12 +530,12 @@ async function rewriteSkillSource(skill, options, summary) {
571
530
  summary.created++;
572
531
  }
573
532
  await writeFileSafe(targetFile2, content);
574
- const relWritten = path8.relative(targetDir, targetFile2);
533
+ const relWritten = path7.relative(targetDir, targetFile2);
575
534
  records.push(makeSourceRecord(skill, targetFile2, content, relWritten));
576
535
  }
577
536
  return records;
578
537
  }
579
- const targetFile = path8.join(targetDir, "SKILL.md");
538
+ const targetFile = path7.join(targetDir, "SKILL.md");
580
539
  const newContent = await renderSkillContent(sourceAbs, skill, data);
581
540
  const exists = await fileExists(targetFile);
582
541
  if (skill.updateStrategy === "frozen") {
@@ -651,14 +610,20 @@ async function syncSkillsToIdes(options) {
651
610
  await ensureDir(targetDir);
652
611
  const sourceFiles = await walkDir(sourceDir);
653
612
  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);
613
+ const rel2 = path7.relative(sourceDir, src);
614
+ const targetFile = path7.join(targetDir, rel2);
615
+ const sourceContent = await fs5.readFile(src, "utf-8");
616
+ const existing = await readFileOrNull(targetFile);
617
+ if (existing !== null && existing !== sourceContent) {
618
+ logger.warn(
619
+ `Mirror drift detected at ${targetFile} \u2014 overwriting from source. Edit ${src} (not the mirror) and re-run \`teamix-evo skills sync\`.`
620
+ );
621
+ }
622
+ await writeFileSafe(targetFile, sourceContent);
658
623
  out.push({
659
624
  id: rel2 === "SKILL.md" ? skill.id : `${skill.id}:${rel2}`,
660
625
  target: targetFile,
661
- hash: computeHash(content),
626
+ hash: computeHash(sourceContent),
662
627
  strategy: skill.updateStrategy,
663
628
  ide,
664
629
  scope
@@ -680,12 +645,18 @@ async function removeSkillFiles(records) {
680
645
  }
681
646
  }
682
647
  }
683
- const parents = new Set(records.map((r) => path8.dirname(r.target)));
684
- for (const dir of parents) {
685
- try {
686
- const entries = await fs5.readdir(dir);
687
- if (entries.length === 0) await fs5.rmdir(dir);
688
- } catch {
648
+ const startDirs = new Set(records.map((r) => path7.dirname(r.target)));
649
+ for (const startDir of startDirs) {
650
+ let dir = startDir;
651
+ for (let depth = 0; depth < 8; depth++) {
652
+ try {
653
+ const entries = await fs5.readdir(dir);
654
+ if (entries.length !== 0) break;
655
+ await fs5.rmdir(dir);
656
+ } catch {
657
+ break;
658
+ }
659
+ dir = path7.dirname(dir);
689
660
  }
690
661
  }
691
662
  return removed;
@@ -714,7 +685,7 @@ async function runSkillsAdd(options) {
714
685
  throw new Error("Scope must be specified (project | global).");
715
686
  }
716
687
  const { manifest, data, packageRoot } = await loadSkillsData(packageName);
717
- const currentDesignVariant = await readDesignVariant(projectRoot);
688
+ const currentTokensVariant = await readTokensVariant(projectRoot);
718
689
  if (isIncremental) {
719
690
  const known = new Set(manifest.skills.map((s) => s.id));
720
691
  const unknown = requestedNames.filter((n) => !known.has(n));
@@ -745,15 +716,15 @@ async function runSkillsAdd(options) {
745
716
  skippedSkillIds = [];
746
717
  onlyIds = manifest.skills.filter((s) => {
747
718
  if (!s.variant) return true;
748
- if (!currentDesignVariant) {
719
+ if (!currentTokensVariant) {
749
720
  logger.debug(
750
- `Skipping variant-bound skill "${s.id}" (variant=${s.variant}): no design pack installed; will be picked up when "design init" runs.`
721
+ `Skipping variant-bound skill "${s.id}" (variant=${s.variant}): no tokens variant installed; will be picked up when "tokens init" runs.`
751
722
  );
752
723
  return false;
753
724
  }
754
- if (s.variant !== currentDesignVariant) {
725
+ if (s.variant !== currentTokensVariant) {
755
726
  logger.debug(
756
- `Skipping variant-bound skill "${s.id}" (variant=${s.variant}): current design variant is "${currentDesignVariant}".`
727
+ `Skipping variant-bound skill "${s.id}" (variant=${s.variant}): current tokens variant is "${currentTokensVariant}".`
757
728
  );
758
729
  return false;
759
730
  }
@@ -856,85 +827,108 @@ function mergeInstalledResources(existing, next) {
856
827
  return [...map.values()];
857
828
  }
858
829
 
859
- // src/core/design-init.ts
860
- var BASELINE_DESIGN_RULES_SKILL = "teamix-evo-design-rules";
830
+ // src/core/tokens-init.ts
861
831
  var DEFAULT_SKILLS_PACKAGE2 = "@teamix-evo/skills";
862
832
  var DEFAULT_AUTO_SKILL_IDES = ["qoder", "claude"];
863
833
  var DEFAULT_AUTO_SKILL_SCOPE = "project";
864
- var DEFAULT_DESIGN_PACKAGE = "@teamix-evo/design";
834
+ var DEFAULT_TOKENS_PACKAGE = "@teamix-evo/tokens";
835
+ var CONSUMER_TOKENS_DIR = "tokens";
836
+ var CONSUMER_THEME_FILE = "tokens.theme.css";
837
+ var CONSUMER_OVERRIDES_FILE = "tokens.overrides.css";
838
+ var EMPTY_OVERRIDES_TEMPLATE = `/* User-owned token overrides \u2014 frozen on subsequent installs. */
839
+ /* See @teamix-evo/tokens variant theme.css for available CSS custom properties. */
840
+ `;
865
841
  var require3 = createRequire2(import.meta.url);
866
- async function runDesignInit(options) {
842
+ async function runTokensInit(options) {
867
843
  const { projectRoot, variant, ide } = options;
868
- const packageName = options.packageName ?? DEFAULT_DESIGN_PACKAGE;
844
+ const packageName = options.packageName ?? DEFAULT_TOKENS_PACKAGE;
869
845
  await ensureTeamixDir(projectRoot);
870
846
  const existingConfig = await readProjectConfig(projectRoot);
871
- if (existingConfig?.packages?.design) {
847
+ if (existingConfig?.packages?.tokens) {
848
+ const existingVariant = existingConfig.packages.tokens.variant;
849
+ if (existingVariant === variant) {
850
+ return { status: "already-initialized", existingVariant };
851
+ }
872
852
  return {
873
- status: "already-initialized",
874
- existingVariant: existingConfig.packages.design.variant
853
+ status: "variant-mismatch",
854
+ existingVariant,
855
+ requestedVariant: variant
875
856
  };
876
857
  }
877
- const packageRoot = options.packageRoot ?? resolveDesignPackageRoot(packageName);
878
- const catalog = await loadDesignPackageManifest(packageRoot);
879
- const variantEntry = catalog.variants.find((v) => v.name === variant);
858
+ const packageRoot = options.packageRoot ?? resolveTokensPackageRoot(packageName);
859
+ const catalog = await loadTokensPackageManifest(packageRoot);
860
+ const variantEntry = getVariantEntry(catalog, variant);
880
861
  if (!variantEntry) {
881
862
  const known = catalog.variants.map((v) => v.name).join(", ");
882
863
  throw new Error(
883
- `Design variant not found: "${variant}". Known variants: ${known || "(none)"}. Hint: run "teamix-evo design list-variants" to see all.`
864
+ `Tokens variant not found: "${variant}". Known variants: ${known || "(none)"}. Hint: run "teamix-evo tokens list-variants" to see all.`
884
865
  );
885
866
  }
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
867
  const installed = [];
892
- for (const file of merge.files) {
893
- const result = await installPackFile(file, projectRoot);
868
+ for (const fileRel of variantEntry.files) {
869
+ const result = await installVariantFile(fileRel, packageRoot, projectRoot);
894
870
  if (result) installed.push(result);
895
871
  }
872
+ const overridesAbs = path8.join(
873
+ projectRoot,
874
+ CONSUMER_TOKENS_DIR,
875
+ CONSUMER_OVERRIDES_FILE
876
+ );
877
+ if (!await fileExists(overridesAbs)) {
878
+ await writeFileSafe(overridesAbs, EMPTY_OVERRIDES_TEMPLATE);
879
+ }
880
+ const overridesContent = await fs6.readFile(overridesAbs, "utf-8");
881
+ installed.push({
882
+ id: `tokens:${CONSUMER_OVERRIDES_FILE}`,
883
+ target: path8.posix.join(CONSUMER_TOKENS_DIR, CONSUMER_OVERRIDES_FILE),
884
+ hash: computeHash(overridesContent),
885
+ strategy: "frozen"
886
+ });
896
887
  const lock = {
897
888
  schemaVersion: 1,
898
- default: { version: defaultPack.version, from: packageName },
899
889
  variant: {
900
- name: variantPack.name,
901
- displayName: variantPack.displayName,
902
- version: variantPack.version,
890
+ name: variantEntry.name,
891
+ displayName: variantEntry.displayName,
892
+ version: variantEntry.version,
903
893
  from: packageName
904
894
  },
905
- linked: variantPack.linked,
895
+ packageVersion: catalog.version,
896
+ linked: variantEntry.linked,
906
897
  installedAt: (/* @__PURE__ */ new Date()).toISOString()
907
898
  };
908
899
  await writeFileSafe(
909
- path9.join(projectRoot, ".teamix-evo", "design", "pack.lock.json"),
900
+ path8.join(projectRoot, ".teamix-evo", "tokens-lock.json"),
910
901
  JSON.stringify(lock, null, 2) + "\n"
911
902
  );
912
903
  const config = {
913
904
  $schema: "https://teamix-evo.dev/schema/config/v1.json",
914
905
  schemaVersion: 1,
915
- ide,
906
+ ide: existingConfig?.ide ?? ide,
916
907
  packages: {
917
- design: {
908
+ ...existingConfig?.packages ?? {},
909
+ tokens: {
918
910
  variant,
919
- version: variantPack.version,
911
+ version: variantEntry.version,
920
912
  tailwind: "v4"
921
913
  }
922
914
  }
923
915
  };
924
916
  await writeProjectConfig(projectRoot, config);
925
- const installedManifest = {
917
+ const prior = await readInstalledManifest(projectRoot) ?? {
926
918
  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
- ]
919
+ installed: []
936
920
  };
937
- await writeInstalledManifest(projectRoot, installedManifest);
921
+ const tokensIdx = prior.installed.findIndex((p) => p.package === packageName);
922
+ const tokensEntry = {
923
+ package: packageName,
924
+ variant,
925
+ version: variantEntry.version,
926
+ installedAt: (/* @__PURE__ */ new Date()).toISOString(),
927
+ resources: installed
928
+ };
929
+ if (tokensIdx >= 0) prior.installed[tokensIdx] = tokensEntry;
930
+ else prior.installed.push(tokensEntry);
931
+ await writeInstalledManifest(projectRoot, prior);
938
932
  const skills = await tryAutoInstallVariantSkills({
939
933
  projectRoot,
940
934
  variant,
@@ -944,21 +938,16 @@ async function runDesignInit(options) {
944
938
  status: "installed",
945
939
  packageName,
946
940
  variant,
947
- version: variantPack.version,
941
+ version: variantEntry.version,
948
942
  count: installed.length,
949
943
  resources: installed,
950
- merge: {
951
- overrides: merge.overrides,
952
- variantAdds: merge.variantAdds,
953
- defaultPassThrough: merge.defaultPassThrough
954
- },
955
944
  skills
956
945
  };
957
946
  }
958
947
  async function tryAutoInstallVariantSkills(args) {
959
948
  const { projectRoot, variant, ide } = args;
960
- const variantSkillId = `${BASELINE_DESIGN_RULES_SKILL}-${variant}`;
961
- const desired = [BASELINE_DESIGN_RULES_SKILL, variantSkillId];
949
+ const variantSkillId = `teamix-evo-design-${variant}`;
950
+ const desired = [variantSkillId];
962
951
  let manifestSkillIds;
963
952
  try {
964
953
  const { manifest } = await loadSkillsData(DEFAULT_SKILLS_PACKAGE2);
@@ -978,7 +967,9 @@ async function tryAutoInstallVariantSkills(args) {
978
967
  const missing = desired.filter((id) => !manifestSkillIds.has(id));
979
968
  if (missing.length > 0) {
980
969
  logger.warn(
981
- `Skills auto-install: not found in manifest, skipping: ${missing.join(", ")}.`
970
+ `Skills auto-install: not found in manifest, skipping: ${missing.join(
971
+ ", "
972
+ )}.`
982
973
  );
983
974
  }
984
975
  if (present.length === 0) {
@@ -1023,38 +1014,60 @@ async function tryAutoInstallVariantSkills(args) {
1023
1014
  };
1024
1015
  }
1025
1016
  }
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");
1017
+ async function installVariantFile(fileRelToPackage, packageRoot, projectRoot) {
1018
+ const sourceAbs = path8.join(packageRoot, fileRelToPackage);
1019
+ const base = path8.basename(fileRelToPackage);
1020
+ if (base === "theme.css") {
1021
+ const targetRel = path8.posix.join(
1022
+ CONSUMER_TOKENS_DIR,
1023
+ CONSUMER_THEME_FILE
1024
+ );
1025
+ const targetAbs = path8.join(projectRoot, targetRel);
1026
+ const content = await fs6.readFile(sourceAbs, "utf-8");
1027
+ await writeFileSafe(targetAbs, content);
1032
1028
  return {
1033
- id: `pack:${file.relPath}`,
1034
- target: cls.target,
1035
- hash: computeHash(existing),
1036
- strategy: cls.strategy
1029
+ id: `tokens:${CONSUMER_THEME_FILE}`,
1030
+ target: targetRel,
1031
+ hash: computeHash(content),
1032
+ strategy: "regenerable"
1037
1033
  };
1038
1034
  }
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
- };
1035
+ if (base === "overrides.css" || base === "tokens.overrides.css") {
1036
+ const targetRel = path8.posix.join(
1037
+ CONSUMER_TOKENS_DIR,
1038
+ CONSUMER_OVERRIDES_FILE
1039
+ );
1040
+ const targetAbs = path8.join(projectRoot, targetRel);
1041
+ if (await fileExists(targetAbs)) {
1042
+ const existing = await fs6.readFile(targetAbs, "utf-8");
1043
+ return {
1044
+ id: `tokens:${CONSUMER_OVERRIDES_FILE}`,
1045
+ target: targetRel,
1046
+ hash: computeHash(existing),
1047
+ strategy: "frozen"
1048
+ };
1049
+ }
1050
+ const content = await fs6.readFile(sourceAbs, "utf-8");
1051
+ await writeFileSafe(targetAbs, content);
1052
+ return {
1053
+ id: `tokens:${CONSUMER_OVERRIDES_FILE}`,
1054
+ target: targetRel,
1055
+ hash: computeHash(content),
1056
+ strategy: "frozen"
1057
+ };
1058
+ }
1059
+ return null;
1047
1060
  }
1048
- function resolveDesignPackageRoot(packageName) {
1061
+ function resolveTokensPackageRoot(packageName) {
1049
1062
  const pkgJson = require3.resolve(`${packageName}/package.json`);
1050
- return path9.dirname(pkgJson);
1063
+ return path8.dirname(pkgJson);
1051
1064
  }
1052
- async function listDesignVariants(packageName = DEFAULT_DESIGN_PACKAGE, packageRoot) {
1053
- const root = packageRoot ?? resolveDesignPackageRoot(packageName);
1054
- const catalog = await loadDesignPackageManifest(root);
1065
+ async function listTokenVariants(packageName = DEFAULT_TOKENS_PACKAGE, packageRoot) {
1066
+ const root = packageRoot ?? resolveTokensPackageRoot(packageName);
1067
+ const catalog = await loadTokensPackageManifest(root);
1055
1068
  return {
1056
1069
  packageName,
1057
- defaultDescription: catalog.default.description,
1070
+ packageVersion: catalog.version,
1058
1071
  variants: catalog.variants.map((v) => ({
1059
1072
  name: v.name,
1060
1073
  displayName: v.displayName,
@@ -1065,7 +1078,7 @@ async function listDesignVariants(packageName = DEFAULT_DESIGN_PACKAGE, packageR
1065
1078
  };
1066
1079
  }
1067
1080
 
1068
- // src/commands/design/init.ts
1081
+ // src/commands/tokens/init.ts
1069
1082
  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
1083
  try {
1071
1084
  const ide = detectIde();
@@ -1075,15 +1088,28 @@ var initCommand = new Command("init").description("\u521D\u59CB\u5316\u8BBE\u8BA
1075
1088
  logger.debug(`IDE: ${ide.name}`);
1076
1089
  logger.info(`Loading variant "${variant}"...`);
1077
1090
  logger.info("Installing resources...");
1078
- const result = await runDesignInit({
1091
+ const result = await runTokensInit({
1079
1092
  projectRoot,
1080
1093
  variant,
1081
1094
  ide: ide.name
1082
1095
  });
1083
1096
  if (result.status === "already-initialized") {
1084
- logger.warn(
1085
- `Design system already initialized (variant: ${result.existingVariant}). Use "teamix-evo design update" to update.`
1097
+ logger.info(
1098
+ `Design system already initialized (variant: ${result.existingVariant}). Use "teamix-evo tokens update" to refresh resources.`
1099
+ );
1100
+ return;
1101
+ }
1102
+ if (result.status === "variant-mismatch") {
1103
+ logger.error(
1104
+ `Cannot switch tokens variant in place. Currently installed: "${result.existingVariant}"; requested: "${result.requestedVariant}".`
1105
+ );
1106
+ logger.info("To switch variants:");
1107
+ logger.info(" 1. teamix-evo tokens uninstall --yes");
1108
+ logger.info(` 2. teamix-evo tokens init ${result.requestedVariant}`);
1109
+ logger.info(
1110
+ "Note: tokens.overrides.css (frozen) is preserved across uninstall/init by default."
1086
1111
  );
1112
+ process.exitCode = 1;
1087
1113
  return;
1088
1114
  }
1089
1115
  logger.success(
@@ -1092,9 +1118,6 @@ var initCommand = new Command("init").description("\u521D\u59CB\u5316\u8BBE\u8BA
1092
1118
  logger.info(` Variant: ${result.variant}`);
1093
1119
  logger.info(` Tailwind: v4`);
1094
1120
  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
1121
  if (result.skills) {
1099
1122
  const { addedSkillIds, skippedSkillIds, missing } = result.skills;
1100
1123
  if (addedSkillIds.length > 0) {
@@ -1114,9 +1137,9 @@ var initCommand = new Command("init").description("\u521D\u59CB\u5316\u8BBE\u8BA
1114
1137
  }
1115
1138
  }
1116
1139
  logger.info("");
1117
- logger.info('Run "teamix-evo design update" to update resources later.');
1140
+ logger.info('Run "teamix-evo tokens update" to update resources later.');
1118
1141
  logger.info(
1119
- 'Run "teamix-evo design list-variants" to see all available variants.'
1142
+ 'Run "teamix-evo tokens list-variants" to see all available variants.'
1120
1143
  );
1121
1144
  } catch (err) {
1122
1145
  logger.error(`Failed to initialize: ${err.message}`);
@@ -1125,43 +1148,191 @@ var initCommand = new Command("init").description("\u521D\u59CB\u5316\u8BBE\u8BA
1125
1148
  }
1126
1149
  });
1127
1150
 
1128
- // src/commands/design/update.ts
1151
+ // src/commands/tokens/update.ts
1129
1152
  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)."
1153
+
1154
+ // src/core/tokens-update.ts
1155
+ import * as path9 from "path";
1156
+ import * as fs7 from "fs/promises";
1157
+ import { createRequire as createRequire3 } from "module";
1158
+ import {
1159
+ loadTokensPackageManifest as loadTokensPackageManifest2,
1160
+ getVariantEntry as getVariantEntry2
1161
+ } from "@teamix-evo/registry";
1162
+ var DEFAULT_TOKENS_PACKAGE2 = "@teamix-evo/tokens";
1163
+ var CONSUMER_TOKENS_DIR2 = "tokens";
1164
+ var CONSUMER_THEME_FILE2 = "tokens.theme.css";
1165
+ var require4 = createRequire3(import.meta.url);
1166
+ async function runTokensUpdate(options) {
1167
+ const { projectRoot } = options;
1168
+ const packageName = options.packageName ?? DEFAULT_TOKENS_PACKAGE2;
1169
+ const config = await readProjectConfig(projectRoot);
1170
+ if (!config?.packages?.tokens) {
1171
+ return { status: "not-initialized" };
1172
+ }
1173
+ const currentVariant = config.packages.tokens.variant;
1174
+ const currentVersion = config.packages.tokens.version;
1175
+ const packageRoot = options.packageRoot ?? resolveTokensPackageRoot2(packageName);
1176
+ const catalog = await loadTokensPackageManifest2(packageRoot);
1177
+ const variantEntry = getVariantEntry2(catalog, currentVariant);
1178
+ if (!variantEntry) {
1179
+ throw new Error(
1180
+ `Currently installed variant "${currentVariant}" no longer exists in ${packageName}@${catalog.version}. Available: ${catalog.variants.map((v) => v.name).join(", ")}. Run \`teamix-evo tokens uninstall\` then \`teamix-evo tokens init <variant>\` to switch.`
1181
+ );
1182
+ }
1183
+ if (variantEntry.version === currentVersion) {
1184
+ await rewriteRegenerableFiles(variantEntry.files, packageRoot, projectRoot);
1185
+ return {
1186
+ status: "up-to-date",
1187
+ packageName,
1188
+ variant: currentVariant,
1189
+ version: currentVersion
1190
+ };
1191
+ }
1192
+ const rewritten = await rewriteRegenerableFiles(
1193
+ variantEntry.files,
1194
+ packageRoot,
1195
+ projectRoot
1133
1196
  );
1134
- logger.info(
1135
- "Workaround: clean `.teamix-evo/design/` and re-run `teamix-evo design init <variant>`."
1197
+ const preserved = [];
1198
+ const overridesAbs = path9.join(
1199
+ projectRoot,
1200
+ CONSUMER_TOKENS_DIR2,
1201
+ "tokens.overrides.css"
1136
1202
  );
1137
- logger.info(
1138
- "Tracking: PLAN \xA712.6 v0.7 \u2014 semantic upgrade flow + tokens.overrides.css preservation."
1203
+ if (await fileExists(overridesAbs)) preserved.push("tokens.overrides.css");
1204
+ const lock = {
1205
+ schemaVersion: 1,
1206
+ variant: {
1207
+ name: variantEntry.name,
1208
+ displayName: variantEntry.displayName,
1209
+ version: variantEntry.version,
1210
+ from: packageName
1211
+ },
1212
+ packageVersion: catalog.version,
1213
+ linked: variantEntry.linked,
1214
+ installedAt: (/* @__PURE__ */ new Date()).toISOString()
1215
+ };
1216
+ await writeFileSafe(
1217
+ path9.join(projectRoot, ".teamix-evo", "tokens-lock.json"),
1218
+ JSON.stringify(lock, null, 2) + "\n"
1139
1219
  );
1140
- process.exitCode = 0;
1220
+ config.packages.tokens.version = variantEntry.version;
1221
+ await writeProjectConfig(projectRoot, config);
1222
+ const prior = await readInstalledManifest(projectRoot) ?? {
1223
+ schemaVersion: 1,
1224
+ installed: []
1225
+ };
1226
+ const idx = prior.installed.findIndex((p) => p.package === packageName);
1227
+ if (idx >= 0) {
1228
+ prior.installed[idx].version = variantEntry.version;
1229
+ prior.installed[idx].installedAt = (/* @__PURE__ */ new Date()).toISOString();
1230
+ for (const r of prior.installed[idx].resources) {
1231
+ if (r.strategy === "regenerable") {
1232
+ const abs = path9.isAbsolute(r.target) ? r.target : path9.join(projectRoot, r.target);
1233
+ if (await fileExists(abs)) {
1234
+ r.hash = computeHash(await fs7.readFile(abs, "utf-8"));
1235
+ }
1236
+ }
1237
+ }
1238
+ await writeInstalledManifest(projectRoot, prior);
1239
+ }
1240
+ return {
1241
+ status: "updated",
1242
+ packageName,
1243
+ variant: currentVariant,
1244
+ from: currentVersion,
1245
+ to: variantEntry.version,
1246
+ rewritten,
1247
+ preserved
1248
+ };
1249
+ }
1250
+ async function rewriteRegenerableFiles(files, packageRoot, projectRoot) {
1251
+ const written = [];
1252
+ for (const fileRel of files) {
1253
+ const base = path9.basename(fileRel);
1254
+ if (base !== "theme.css") continue;
1255
+ const sourceAbs = path9.join(packageRoot, fileRel);
1256
+ const targetAbs = path9.join(
1257
+ projectRoot,
1258
+ CONSUMER_TOKENS_DIR2,
1259
+ CONSUMER_THEME_FILE2
1260
+ );
1261
+ const content = await fs7.readFile(sourceAbs, "utf-8");
1262
+ await writeFileSafe(targetAbs, content);
1263
+ written.push(CONSUMER_THEME_FILE2);
1264
+ }
1265
+ return written;
1266
+ }
1267
+ function resolveTokensPackageRoot2(packageName) {
1268
+ const pkgJson = require4.resolve(`${packageName}/package.json`);
1269
+ return path9.dirname(pkgJson);
1270
+ }
1271
+
1272
+ // src/commands/tokens/update.ts
1273
+ var updateCommand = new Command2("update").description(
1274
+ "\u5237\u65B0 design tokens\uFF08regenerable \u8986\u76D6\u3001frozen \u4FDD\u7559 \u2014 ADR 0003 \u4E09\u6001\uFF09"
1275
+ ).action(async () => {
1276
+ try {
1277
+ const ide = detectIde();
1278
+ const projectRoot = ide.getProjectRoot();
1279
+ const result = await runTokensUpdate({ projectRoot });
1280
+ if (result.status === "not-initialized") {
1281
+ logger.error(
1282
+ "No tokens variant installed. Run `teamix-evo tokens init <variant>` first."
1283
+ );
1284
+ process.exitCode = 1;
1285
+ return;
1286
+ }
1287
+ if (result.status === "up-to-date") {
1288
+ logger.success(
1289
+ `Already at latest: ${result.packageName} (${result.variant} v${result.version})`
1290
+ );
1291
+ logger.info(
1292
+ " Regenerable files refreshed in case of manual edits."
1293
+ );
1294
+ return;
1295
+ }
1296
+ logger.success(
1297
+ `Updated ${result.packageName} (${result.variant}): v${result.from} \u2192 v${result.to}`
1298
+ );
1299
+ if (result.rewritten.length > 0) {
1300
+ logger.info(` Rewrote: ${result.rewritten.join(", ")}`);
1301
+ }
1302
+ if (result.preserved.length > 0) {
1303
+ logger.info(
1304
+ ` Preserved: ${result.preserved.join(", ")} (frozen \u2014 your customizations kept)`
1305
+ );
1306
+ }
1307
+ } catch (err) {
1308
+ logger.error(`Failed to update tokens: ${err.message}`);
1309
+ logger.debug(err.stack ?? "");
1310
+ process.exitCode = 1;
1311
+ }
1141
1312
  });
1142
1313
 
1143
- // src/commands/design/list.ts
1314
+ // src/commands/tokens/list.ts
1144
1315
  import { Command as Command3 } from "commander";
1145
1316
  var listCommand = new Command3("list").description("\u5217\u51FA\u5DF2\u5B89\u88C5\u7684\u8BBE\u8BA1\u53D8\u4F53").action(async () => {
1146
1317
  try {
1147
1318
  const ide = detectIde();
1148
1319
  const projectRoot = ide.getProjectRoot();
1149
1320
  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.');
1321
+ if (!config?.packages?.tokens) {
1322
+ logger.info("No tokens variant installed.");
1323
+ logger.info('Run "teamix-evo tokens init [variant]" to get started.');
1153
1324
  return;
1154
1325
  }
1155
- const { variant, version: version2 } = config.packages.design;
1156
- logger.info("Installed design system:");
1157
- logger.info(` Package: @teamix-evo/design`);
1326
+ const { variant, version: version2 } = config.packages.tokens;
1327
+ logger.info("Installed tokens variant:");
1328
+ logger.info(` Package: @teamix-evo/tokens`);
1158
1329
  logger.info(` Variant: ${variant}`);
1159
1330
  logger.info(` Version: ${version2}`);
1160
1331
  logger.info(` IDE: ${config.ide}`);
1161
1332
  const manifest = await readInstalledManifest(projectRoot);
1162
1333
  if (manifest) {
1163
1334
  const pkg = manifest.installed.find(
1164
- (p) => p.package === "@teamix-evo/design" && p.variant === variant
1335
+ (p) => p.package === "@teamix-evo/tokens" && p.variant === variant
1165
1336
  );
1166
1337
  if (pkg) {
1167
1338
  logger.info(` Resources: ${pkg.resources.length} files`);
@@ -1176,20 +1347,17 @@ var listCommand = new Command3("list").description("\u5217\u51FA\u5DF2\u5B89\u88
1176
1347
  }
1177
1348
  });
1178
1349
 
1179
- // src/commands/design/list-variants.ts
1350
+ // src/commands/tokens/list-variants.ts
1180
1351
  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 () => {
1352
+ var listVariantsCommand = new Command4("list-variants").description("\u5217\u51FA @teamix-evo/tokens \u5305\u5185\u63D0\u4F9B\u7684\u6240\u6709\u53D8\u4F53").action(async () => {
1182
1353
  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
- }
1354
+ const result = await listTokenVariants();
1355
+ logger.info(
1356
+ `Available variants in ${result.packageName} v${result.packageVersion}:`
1357
+ );
1190
1358
  logger.info("");
1191
1359
  if (result.variants.length === 0) {
1192
- logger.info(" (no variants beyond default)");
1360
+ logger.info(" (no variants advertised in manifest.json)");
1193
1361
  return;
1194
1362
  }
1195
1363
  for (const v of result.variants) {
@@ -1198,12 +1366,13 @@ var listVariantsCommand = new Command4("list-variants").description("\u5217\u51F
1198
1366
  if (v.linked) {
1199
1367
  const links = [];
1200
1368
  if (v.linked["biz-ui"]) links.push(`biz-ui: ${v.linked["biz-ui"]}`);
1201
- if (v.linked.templates) links.push(`templates: ${v.linked.templates}`);
1369
+ if (v.linked.templates)
1370
+ links.push(`templates: ${v.linked.templates}`);
1202
1371
  if (links.length) logger.info(` linked: ${links.join(" / ")}`);
1203
1372
  }
1204
1373
  logger.info("");
1205
1374
  }
1206
- logger.info("Install a variant: teamix-evo design init <name>");
1375
+ logger.info("Install a variant: teamix-evo tokens init <name>");
1207
1376
  } catch (err) {
1208
1377
  logger.error(`Failed to list variants: ${err.message}`);
1209
1378
  logger.debug(err.stack ?? "");
@@ -1211,37 +1380,42 @@ var listVariantsCommand = new Command4("list-variants").description("\u5217\u51F
1211
1380
  }
1212
1381
  });
1213
1382
 
1214
- // src/commands/design/uninstall.ts
1383
+ // src/commands/tokens/uninstall.ts
1215
1384
  import { Command as Command5 } from "commander";
1216
- import * as fs7 from "fs/promises";
1385
+ import * as fs8 from "fs/promises";
1217
1386
  import * as path10 from "path";
1218
1387
  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(
1388
+ var TOKENS_PACKAGE = "@teamix-evo/tokens";
1389
+ var uninstallCommand = new Command5("uninstall").description(
1390
+ "\u5378\u8F7D\u5DF2\u88C5\u7684 tokens \u53D8\u4F53\uFF08\u9ED8\u8BA4\u4FDD\u7559 frozen \u6587\u4EF6\u5982 tokens.overrides.css\uFF1B--purge \u624D\u4E00\u5E76\u5220\uFF09"
1391
+ ).option("-y, --yes", "\u8DF3\u8FC7\u786E\u8BA4").option(
1221
1392
  "--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"
1393
+ "\u4EC5\u6E05\u7406 .teamix-evo \u4E2D\u7684\u8BB0\u8D26\u4FE1\u606F\uFF0C\u4E0D\u5220\u9664\u4EFB\u4F55\u843D\u5730\u8D44\u6E90\u6587\u4EF6"
1394
+ ).option(
1395
+ "--purge",
1396
+ "\u540C\u65F6\u5220\u9664 frozen \u6587\u4EF6\uFF08\u9ED8\u8BA4 frozen / managed \u90FD\u4FDD\u7559 \u2014 ADR 0003\uFF09"
1223
1397
  ).action(async (opts) => {
1224
1398
  try {
1225
1399
  const ide = detectIde();
1226
1400
  const projectRoot = ide.getProjectRoot();
1227
1401
  const config = await readProjectConfig(projectRoot);
1228
- if (!config?.packages?.design) {
1229
- logger.info("Design system is not installed. Nothing to do.");
1402
+ if (!config?.packages?.tokens) {
1403
+ logger.info("No tokens variant installed. Nothing to do.");
1230
1404
  return;
1231
1405
  }
1232
1406
  const installedManifest = await readInstalledManifest(projectRoot);
1233
1407
  const pkg = installedManifest?.installed.find(
1234
- (p) => p.package === DESIGN_PACKAGE
1408
+ (p) => p.package === TOKENS_PACKAGE
1235
1409
  );
1236
1410
  const resources = pkg?.resources ?? [];
1237
- const removable = opts.keepFiles ? [] : resources.filter((r) => r.strategy !== "managed");
1411
+ const removable = opts.keepFiles ? [] : opts.purge ? resources.filter((r) => r.strategy !== "managed") : resources.filter((r) => r.strategy === "regenerable");
1238
1412
  const kept = resources.length - removable.length;
1239
1413
  logger.info(
1240
1414
  `Will remove ${removable.length} file(s); keep ${kept} managed file(s).`
1241
1415
  );
1242
1416
  if (!opts.yes) {
1243
1417
  const confirm4 = await prompts.confirm({
1244
- message: "\u786E\u8BA4\u5378\u8F7D\u8BBE\u8BA1\u4F53\u7CFB\uFF1F",
1418
+ message: "\u786E\u8BA4\u5378\u8F7D tokens \u53D8\u4F53\uFF1F",
1245
1419
  initialValue: false
1246
1420
  });
1247
1421
  if (prompts.isCancel(confirm4) || !confirm4) {
@@ -1253,7 +1427,7 @@ var uninstallCommand = new Command5("uninstall").description("\u5378\u8F7D\u8BBE
1253
1427
  for (const r of removable) {
1254
1428
  const target = path10.isAbsolute(r.target) ? r.target : path10.join(projectRoot, r.target);
1255
1429
  try {
1256
- await fs7.unlink(target);
1430
+ await fs8.unlink(target);
1257
1431
  removed++;
1258
1432
  } catch (err) {
1259
1433
  if (err.code !== "ENOENT") {
@@ -1265,18 +1439,31 @@ var uninstallCommand = new Command5("uninstall").description("\u5378\u8F7D\u8BBE
1265
1439
  }
1266
1440
  if (installedManifest) {
1267
1441
  installedManifest.installed = installedManifest.installed.filter(
1268
- (p) => p.package !== DESIGN_PACKAGE
1442
+ (p) => p.package !== TOKENS_PACKAGE
1269
1443
  );
1270
1444
  await writeInstalledManifest(projectRoot, installedManifest);
1271
1445
  }
1272
- delete config.packages.design;
1446
+ delete config.packages.tokens;
1273
1447
  await writeProjectConfig(projectRoot, config);
1274
- logger.success(`Uninstalled ${DESIGN_PACKAGE}`);
1448
+ const lockPath = path10.join(
1449
+ projectRoot,
1450
+ ".teamix-evo",
1451
+ "tokens-lock.json"
1452
+ );
1453
+ try {
1454
+ await fs8.unlink(lockPath);
1455
+ } catch (err) {
1456
+ if (err.code !== "ENOENT") {
1457
+ logger.warn(
1458
+ `Failed to remove tokens-lock.json: ${err.message}`
1459
+ );
1460
+ }
1461
+ }
1462
+ logger.success(`Uninstalled ${TOKENS_PACKAGE}`);
1275
1463
  logger.info(` Removed: ${removed} files`);
1276
1464
  if (kept > 0) {
1277
- logger.info(
1278
- ` Kept: ${kept} managed files (you may delete manually)`
1279
- );
1465
+ const note = opts.purge ? "managed (you may delete manually)" : "frozen / managed (preserved \u2014 ADR 0003; --purge to force)";
1466
+ logger.info(` Kept: ${kept} files \u2014 ${note}`);
1280
1467
  }
1281
1468
  } catch (err) {
1282
1469
  logger.error(`Failed to uninstall: ${err.message}`);
@@ -1285,15 +1472,13 @@ var uninstallCommand = new Command5("uninstall").description("\u5378\u8F7D\u8BBE
1285
1472
  }
1286
1473
  });
1287
1474
 
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);
1475
+ // src/commands/tokens/index.ts
1476
+ var tokensCommand = new Command6("tokens").description("\u7BA1\u7406 design tokens(\u53D8\u4F53\u7EA7 theme.css \u7B49)");
1477
+ tokensCommand.addCommand(initCommand);
1478
+ tokensCommand.addCommand(updateCommand);
1479
+ tokensCommand.addCommand(listCommand);
1480
+ tokensCommand.addCommand(listVariantsCommand);
1481
+ tokensCommand.addCommand(uninstallCommand);
1297
1482
 
1298
1483
  // src/commands/skills/index.ts
1299
1484
  import { Command as Command13 } from "commander";
@@ -1304,23 +1489,29 @@ import * as prompts2 from "@clack/prompts";
1304
1489
 
1305
1490
  // src/utils/global-root.ts
1306
1491
  import { existsSync } from "fs";
1307
- import * as fs8 from "fs/promises";
1492
+ import * as fs9 from "fs/promises";
1308
1493
  import * as os3 from "os";
1309
1494
  import * as path11 from "path";
1310
1495
  var GLOBAL_META_DIR = ".teamix-evo-global";
1311
- var TEAMIX_DIR3 = ".teamix-evo";
1496
+ var TEAMIX_DIR2 = ".teamix-evo";
1312
1497
  var CONFIG_FILE2 = "config.json";
1313
1498
  function getGlobalMetaRoot() {
1314
1499
  return path11.join(os3.homedir(), GLOBAL_META_DIR);
1315
1500
  }
1316
1501
  function isTeamixEvoProject(dir) {
1317
- return existsSync(path11.join(dir, TEAMIX_DIR3, CONFIG_FILE2));
1502
+ return existsSync(path11.join(dir, TEAMIX_DIR2, CONFIG_FILE2));
1318
1503
  }
1319
1504
  async function ensureGlobalMetaRoot() {
1320
1505
  const root = getGlobalMetaRoot();
1321
- await fs8.mkdir(root, { recursive: true });
1506
+ await fs9.mkdir(root, { recursive: true });
1322
1507
  return root;
1323
1508
  }
1509
+ function resolveSkillsMaintenanceRoot(cwd) {
1510
+ if (isTeamixEvoProject(cwd)) return cwd;
1511
+ const globalRoot = getGlobalMetaRoot();
1512
+ if (isTeamixEvoProject(globalRoot)) return globalRoot;
1513
+ return cwd;
1514
+ }
1324
1515
 
1325
1516
  // src/commands/skills/add.ts
1326
1517
  var addCommand = new Command7("add").description(
@@ -1467,17 +1658,27 @@ var listCommand2 = new Command8("list").alias("ls").description(
1467
1658
  ).option("--installed", "\u4EC5\u5C55\u793A\u5DF2\u5B89\u88C5\u7684 skill\uFF08\u9690\u85CF\u672A\u5B89\u88C5\u9879\uFF09").action(async (opts) => {
1468
1659
  try {
1469
1660
  const ide = detectIde();
1470
- const projectRoot = ide.getProjectRoot();
1661
+ const cwd = ide.getProjectRoot();
1662
+ const projectRoot = resolveSkillsMaintenanceRoot(cwd);
1663
+ if (projectRoot !== cwd) {
1664
+ logger.info(`Using global skills meta root: ${projectRoot}`);
1665
+ }
1471
1666
  const config = await readProjectConfig(projectRoot);
1472
1667
  const installedManifest = await readInstalledManifest(projectRoot);
1473
1668
  const pkg = installedManifest?.installed.find(
1474
1669
  (p) => p.package === SKILLS_PACKAGE
1475
1670
  );
1671
+ const skillsLock = await readSkillsLock(projectRoot);
1476
1672
  const installedBySkill = /* @__PURE__ */ new Map();
1477
1673
  for (const r of pkg?.resources ?? []) {
1478
1674
  const skillId = r.id.split(":")[0];
1479
1675
  installedBySkill.set(skillId, (installedBySkill.get(skillId) ?? 0) + 1);
1480
1676
  }
1677
+ for (const skillId of Object.keys(skillsLock?.skills ?? {})) {
1678
+ if (!installedBySkill.has(skillId)) {
1679
+ installedBySkill.set(skillId, 1);
1680
+ }
1681
+ }
1481
1682
  if (opts.installed) {
1482
1683
  if (!config?.packages?.skills || !pkg) {
1483
1684
  logger.info("No skills installed.");
@@ -1500,9 +1701,13 @@ var listCommand2 = new Command8("list").alias("ls").description(
1500
1701
  const skills = [...manifest.skills].sort(
1501
1702
  (a, b) => a.id.localeCompare(b.id)
1502
1703
  );
1503
- const isInstalled = !!config?.packages?.skills && !!pkg;
1504
- if (isInstalled) {
1704
+ const isInstalled = installedBySkill.size > 0;
1705
+ if (isInstalled && config?.packages?.skills && pkg) {
1505
1706
  printInstalledHeader(config.packages.skills, pkg.installedAt);
1707
+ } else if (isInstalled) {
1708
+ logger.info(
1709
+ `Installed (${installedBySkill.size} skill(s)) \u2014 config or manifest record missing; run "teamix-evo skills doctor" to repair.`
1710
+ );
1506
1711
  } else {
1507
1712
  logger.info("Skills package not yet added.");
1508
1713
  logger.info(
@@ -1548,7 +1753,11 @@ var FLAT_VARIANT2 = "_flat";
1548
1753
  var updateCommand2 = new Command9("update").description("\u66F4\u65B0\u5DF2\u5B89\u88C5\u7684 teamix-evo skills").action(async () => {
1549
1754
  try {
1550
1755
  const ide = detectIde();
1551
- const projectRoot = ide.getProjectRoot();
1756
+ const cwd = ide.getProjectRoot();
1757
+ const projectRoot = resolveSkillsMaintenanceRoot(cwd);
1758
+ if (projectRoot !== cwd) {
1759
+ logger.info(`Using global skills meta root: ${projectRoot}`);
1760
+ }
1552
1761
  const config = await readProjectConfig(projectRoot);
1553
1762
  if (!config?.packages?.skills) {
1554
1763
  logger.error('Skills not added. Run "teamix-evo skills add" first.');
@@ -1635,12 +1844,21 @@ var updateCommand2 = new Command9("update").description("\u66F4\u65B0\u5DF2\u5B8
1635
1844
  import { Command as Command10 } from "commander";
1636
1845
  import * as prompts3 from "@clack/prompts";
1637
1846
  import * as path12 from "path";
1638
- import * as fs9 from "fs/promises";
1847
+ import * as fs10 from "fs/promises";
1639
1848
  var SKILLS_PACKAGE3 = "@teamix-evo/skills";
1640
- var uninstallCommand2 = new Command10("uninstall").description("\u5378\u8F7D\u5DF2\u5B89\u88C5\u7684 teamix-evo skills\uFF08\u5220\u9664\u6CE8\u5165\u5230 IDE \u7684\u6280\u80FD\u6587\u4EF6\uFF09").option("-y, --yes", "\u8DF3\u8FC7\u786E\u8BA4").action(async (opts) => {
1849
+ var uninstallCommand2 = new Command10("uninstall").description(
1850
+ "\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"
1851
+ ).argument(
1852
+ "[ids...]",
1853
+ "\u53EF\u9009\uFF1A\u4EC5\u5378\u8F7D\u6307\u5B9A skill id\uFF1B\u7701\u7565\u5219\u5378\u8F7D\u6574\u4E2A skills \u5305"
1854
+ ).option("-y, --yes", "\u8DF3\u8FC7\u786E\u8BA4").action(async (ids, opts) => {
1641
1855
  try {
1642
1856
  const ide = detectIde();
1643
- const projectRoot = ide.getProjectRoot();
1857
+ const cwd = ide.getProjectRoot();
1858
+ const projectRoot = resolveSkillsMaintenanceRoot(cwd);
1859
+ if (projectRoot !== cwd) {
1860
+ logger.info(`Using global skills meta root: ${projectRoot}`);
1861
+ }
1644
1862
  const config = await readProjectConfig(projectRoot);
1645
1863
  if (!config?.packages?.skills) {
1646
1864
  logger.info("Skills are not installed. Nothing to do.");
@@ -1651,53 +1869,167 @@ var uninstallCommand2 = new Command10("uninstall").description("\u5378\u8F7D\u5D
1651
1869
  (p) => p.package === SKILLS_PACKAGE3
1652
1870
  );
1653
1871
  const resources = pkg?.resources ?? [];
1654
- logger.info(
1655
- `Will remove ${resources.length} skill file(s) installed by ${SKILLS_PACKAGE3}.`
1656
- );
1657
- if (!opts.yes) {
1658
- const confirm4 = await prompts3.confirm({
1659
- message: "\u786E\u8BA4\u5378\u8F7D\uFF1F\u6B64\u64CD\u4F5C\u4F1A\u5220\u9664\u4E0A\u8FF0\u6587\u4EF6\u3002",
1660
- initialValue: false
1872
+ if (ids.length === 0) {
1873
+ await runFullUninstall({
1874
+ projectRoot,
1875
+ config,
1876
+ installedManifest,
1877
+ pkg,
1878
+ resources,
1879
+ opts
1661
1880
  });
1662
- if (prompts3.isCancel(confirm4) || !confirm4) {
1663
- logger.info("Cancelled.");
1664
- return;
1665
- }
1666
- }
1667
- const removed = await removeSkillFiles(resources);
1668
- logger.debug(`Removed ${removed.length} files`);
1669
- const skillsRoot = getSkillsSourceDir(projectRoot);
1670
- try {
1671
- await fs9.rm(skillsRoot, { recursive: true, force: true });
1672
- logger.debug(`Removed source dir ${skillsRoot}`);
1673
- } catch (err) {
1674
- logger.warn(
1675
- `Failed to remove ${skillsRoot}: ${err.message}`
1676
- );
1677
- }
1678
- if (installedManifest && pkg) {
1679
- installedManifest.installed = installedManifest.installed.filter(
1680
- (p) => p.package !== SKILLS_PACKAGE3
1681
- );
1682
- await writeInstalledManifest(projectRoot, installedManifest);
1881
+ return;
1683
1882
  }
1684
- delete config.packages.skills;
1685
- await writeProjectConfig(projectRoot, config);
1686
- logger.success(`Uninstalled ${SKILLS_PACKAGE3}`);
1687
- logger.info(` Removed: ${removed.length} files`);
1688
- logger.info(` Source: ${path12.relative(projectRoot, skillsRoot)} (cleaned)`);
1883
+ await runPartialUninstall({
1884
+ projectRoot,
1885
+ installedManifest,
1886
+ pkg,
1887
+ resources,
1888
+ ids,
1889
+ opts
1890
+ });
1689
1891
  } catch (err) {
1690
1892
  logger.error(`Failed to uninstall: ${err.message}`);
1691
1893
  logger.debug(err.stack ?? "");
1692
1894
  process.exitCode = 1;
1693
1895
  }
1694
1896
  });
1897
+ async function runFullUninstall(args) {
1898
+ const { projectRoot, config, installedManifest, pkg, resources, opts } = args;
1899
+ logger.info(
1900
+ `Will remove ${resources.length} skill file(s) installed by ${SKILLS_PACKAGE3}.`
1901
+ );
1902
+ if (!opts.yes) {
1903
+ const confirm4 = await prompts3.confirm({
1904
+ message: "\u786E\u8BA4\u5378\u8F7D\uFF1F\u6B64\u64CD\u4F5C\u4F1A\u5220\u9664\u4E0A\u8FF0\u6587\u4EF6\u3002",
1905
+ initialValue: false
1906
+ });
1907
+ if (prompts3.isCancel(confirm4) || !confirm4) {
1908
+ logger.info("Cancelled.");
1909
+ return;
1910
+ }
1911
+ }
1912
+ const removed = await removeSkillFiles(resources);
1913
+ logger.debug(`Removed ${removed.length} files`);
1914
+ const skillsRoot = getSkillsSourceDir(projectRoot);
1915
+ try {
1916
+ await fs10.rm(skillsRoot, { recursive: true, force: true });
1917
+ logger.debug(`Removed source dir ${skillsRoot}`);
1918
+ } catch (err) {
1919
+ logger.warn(
1920
+ `Failed to remove ${skillsRoot}: ${err.message}`
1921
+ );
1922
+ }
1923
+ if (installedManifest && pkg) {
1924
+ installedManifest.installed = installedManifest.installed.filter(
1925
+ (p) => p.package !== SKILLS_PACKAGE3
1926
+ );
1927
+ await writeInstalledManifest(projectRoot, installedManifest);
1928
+ }
1929
+ delete config.packages.skills;
1930
+ await writeProjectConfig(projectRoot, config);
1931
+ logger.success(`Uninstalled ${SKILLS_PACKAGE3}`);
1932
+ logger.info(` Removed: ${removed.length} files`);
1933
+ logger.info(` Source: ${path12.relative(projectRoot, skillsRoot)} (cleaned)`);
1934
+ }
1935
+ async function runPartialUninstall(args) {
1936
+ const { projectRoot, installedManifest, pkg, resources, ids, opts } = args;
1937
+ const grouped = groupBySkillId(resources);
1938
+ const requested = dedupe(ids);
1939
+ const matched = requested.filter((id) => grouped.has(id));
1940
+ const missing = requested.filter((id) => !grouped.has(id));
1941
+ if (missing.length > 0) {
1942
+ logger.warn(`Not installed (skipped): ${missing.join(", ")}`);
1943
+ }
1944
+ if (matched.length === 0) {
1945
+ logger.info("Nothing to remove.");
1946
+ return;
1947
+ }
1948
+ const toRemove = matched.flatMap(
1949
+ (id) => grouped.get(id) ?? []
1950
+ );
1951
+ logger.info(
1952
+ `Will remove ${matched.length} skill(s): ${matched.join(", ")} (${toRemove.length} file(s)).`
1953
+ );
1954
+ if (!opts.yes) {
1955
+ const confirm4 = await prompts3.confirm({
1956
+ message: "\u786E\u8BA4\u5378\u8F7D\uFF1F\u6B64\u64CD\u4F5C\u4F1A\u5220\u9664\u4E0A\u8FF0\u6587\u4EF6\u3002",
1957
+ initialValue: false
1958
+ });
1959
+ if (prompts3.isCancel(confirm4) || !confirm4) {
1960
+ logger.info("Cancelled.");
1961
+ return;
1962
+ }
1963
+ }
1964
+ const removed = await removeSkillFiles(toRemove);
1965
+ logger.debug(`Removed ${removed.length} files`);
1966
+ for (const id of matched) {
1967
+ const dir = getSkillsSourceDir(projectRoot, id);
1968
+ try {
1969
+ await fs10.rm(dir, { recursive: true, force: true });
1970
+ logger.debug(`Removed source dir ${dir}`);
1971
+ } catch (err) {
1972
+ logger.warn(`Failed to remove ${dir}: ${err.message}`);
1973
+ }
1974
+ }
1975
+ const lock = await readSkillsLock(projectRoot);
1976
+ if (lock) {
1977
+ for (const id of matched) delete lock.skills[id];
1978
+ await writeSkillsLock(projectRoot, lock);
1979
+ }
1980
+ if (installedManifest && pkg) {
1981
+ pkg.resources = resources.filter((r) => !matched.includes(skillIdOf(r)));
1982
+ await writeInstalledManifest(projectRoot, installedManifest);
1983
+ }
1984
+ logger.success(
1985
+ `Removed ${matched.length} skill(s): ${matched.join(", ")}`
1986
+ );
1987
+ logger.info(` Files: ${removed.length}`);
1988
+ if (missing.length > 0) {
1989
+ logger.info(` Skipped: ${missing.join(", ")} (not installed)`);
1990
+ }
1991
+ }
1992
+ function skillIdOf(r) {
1993
+ return r.id.split(":")[0];
1994
+ }
1995
+ function groupBySkillId(records) {
1996
+ const map = /* @__PURE__ */ new Map();
1997
+ for (const r of records) {
1998
+ const id = skillIdOf(r);
1999
+ const bucket = map.get(id);
2000
+ if (bucket) bucket.push(r);
2001
+ else map.set(id, [r]);
2002
+ }
2003
+ return map;
2004
+ }
2005
+ function dedupe(values) {
2006
+ return Array.from(new Set(values));
2007
+ }
1695
2008
 
1696
2009
  // src/commands/skills/sync.ts
1697
2010
  import { Command as Command11 } from "commander";
1698
2011
 
1699
2012
  // src/core/skills-sync.ts
1700
- import * as fs10 from "fs/promises";
2013
+ import * as path13 from "path";
2014
+ import * as fs11 from "fs/promises";
2015
+ import { createRequire as createRequire4 } from "module";
2016
+ import { loadSkillsPackageManifest as loadSkillsPackageManifest2 } from "@teamix-evo/registry";
2017
+ var require5 = createRequire4(import.meta.url);
2018
+ async function readSkillMetaFromUpstream(skillId) {
2019
+ try {
2020
+ const pkgJson = require5.resolve("@teamix-evo/skills/package.json");
2021
+ const packageRoot = path13.dirname(pkgJson);
2022
+ const manifest = await loadSkillsPackageManifest2(packageRoot);
2023
+ const entry = manifest.skills.find((s) => s.id === skillId);
2024
+ if (!entry) return null;
2025
+ return {
2026
+ updateStrategy: entry.updateStrategy,
2027
+ managedRegions: entry.managedRegions
2028
+ };
2029
+ } catch {
2030
+ return null;
2031
+ }
2032
+ }
1701
2033
  var SKILLS_PACKAGE_DEFAULT = "@teamix-evo/skills";
1702
2034
  async function runSkillsSync(options) {
1703
2035
  const { projectRoot, names } = options;
@@ -1731,13 +2063,15 @@ async function runSkillsSync(options) {
1731
2063
  logger.warn(`Skill "${skillId}" has no IDE mirror targets; skipped.`);
1732
2064
  continue;
1733
2065
  }
2066
+ const upstreamMeta = await readSkillMetaFromUpstream(skillId);
1734
2067
  const result = await syncSkillsToIdes({
1735
2068
  projectRoot,
1736
2069
  skills: [
1737
2070
  {
1738
2071
  id: skillId,
1739
2072
  name: skillId,
1740
- updateStrategy: "regenerable"
2073
+ updateStrategy: upstreamMeta?.updateStrategy ?? "regenerable",
2074
+ managedRegions: upstreamMeta?.managedRegions
1741
2075
  }
1742
2076
  ],
1743
2077
  ides,
@@ -1764,7 +2098,7 @@ async function runSkillsSync(options) {
1764
2098
  }
1765
2099
  async function dirExists(p) {
1766
2100
  try {
1767
- const stat4 = await fs10.stat(p);
2101
+ const stat4 = await fs11.stat(p);
1768
2102
  return stat4.isDirectory();
1769
2103
  } catch {
1770
2104
  return false;
@@ -1796,7 +2130,11 @@ var syncCommand = new Command11("sync").description(
1796
2130
  ).action(async (names, opts) => {
1797
2131
  try {
1798
2132
  const ide = detectIde();
1799
- const projectRoot = ide.getProjectRoot();
2133
+ const cwd = ide.getProjectRoot();
2134
+ const projectRoot = resolveSkillsMaintenanceRoot(cwd);
2135
+ if (projectRoot !== cwd) {
2136
+ logger.info(`Using global skills meta root: ${projectRoot}`);
2137
+ }
1800
2138
  const ides = opts.ide ? parseIdeList2(opts.ide) : void 0;
1801
2139
  const scope = opts.scope ? parseScope2(opts.scope) : void 0;
1802
2140
  const result = await runSkillsSync({
@@ -1854,8 +2192,8 @@ function parseScope2(input) {
1854
2192
  import { Command as Command12 } from "commander";
1855
2193
 
1856
2194
  // src/core/skills-doctor.ts
1857
- import * as path13 from "path";
1858
- import * as fs11 from "fs/promises";
2195
+ import * as path14 from "path";
2196
+ import * as fs12 from "fs/promises";
1859
2197
  async function runSkillsDoctor(options) {
1860
2198
  const { projectRoot } = options;
1861
2199
  const lock = await readSkillsLock(projectRoot);
@@ -1877,8 +2215,8 @@ async function runSkillsDoctor(options) {
1877
2215
  const sourceFiles = await walkDir(sourceDir);
1878
2216
  const sourceContents = /* @__PURE__ */ new Map();
1879
2217
  for (const f of sourceFiles) {
1880
- const rel2 = path13.relative(sourceDir, f);
1881
- sourceContents.set(rel2, await fs11.readFile(f, "utf-8"));
2218
+ const rel2 = path14.relative(sourceDir, f);
2219
+ sourceContents.set(rel2, await fs12.readFile(f, "utf-8"));
1882
2220
  }
1883
2221
  for (const ide of entry.mirroredTo) {
1884
2222
  const adapter = getAdapter(ide);
@@ -1899,7 +2237,7 @@ async function runSkillsDoctor(options) {
1899
2237
  continue;
1900
2238
  }
1901
2239
  for (const [rel2, sourceContent] of sourceContents.entries()) {
1902
- const mirrorFile = path13.join(mirrorDir, rel2);
2240
+ const mirrorFile = path14.join(mirrorDir, rel2);
1903
2241
  if (!await fileExists(mirrorFile)) {
1904
2242
  findings.push({
1905
2243
  kind: "missing-mirror",
@@ -1911,7 +2249,7 @@ async function runSkillsDoctor(options) {
1911
2249
  });
1912
2250
  continue;
1913
2251
  }
1914
- const mirrorContent = await fs11.readFile(mirrorFile, "utf-8");
2252
+ const mirrorContent = await fs12.readFile(mirrorFile, "utf-8");
1915
2253
  if (computeHash(mirrorContent) !== computeHash(sourceContent)) {
1916
2254
  findings.push({
1917
2255
  kind: "mirror-drift",
@@ -1932,7 +2270,7 @@ async function runSkillsDoctor(options) {
1932
2270
  }
1933
2271
  async function dirExists2(p) {
1934
2272
  try {
1935
- const stat4 = await fs11.stat(p);
2273
+ const stat4 = await fs12.stat(p);
1936
2274
  return stat4.isDirectory();
1937
2275
  } catch {
1938
2276
  return false;
@@ -1945,7 +2283,11 @@ var doctorCommand = new Command12("doctor").description(
1945
2283
  ).action(async () => {
1946
2284
  try {
1947
2285
  const ide = detectIde();
1948
- const projectRoot = ide.getProjectRoot();
2286
+ const cwd = ide.getProjectRoot();
2287
+ const projectRoot = resolveSkillsMaintenanceRoot(cwd);
2288
+ if (projectRoot !== cwd) {
2289
+ logger.info(`Using global skills meta root: ${projectRoot}`);
2290
+ }
1949
2291
  const result = await runSkillsDoctor({ projectRoot });
1950
2292
  if (result.status === "no-skills") {
1951
2293
  logger.info(
@@ -1993,6 +2335,35 @@ import { Command as Command14 } from "commander";
1993
2335
  import * as prompts4 from "@clack/prompts";
1994
2336
 
1995
2337
  // src/core/ui-init.ts
2338
+ import * as fs13 from "fs/promises";
2339
+ import { createRequire as createRequire5 } from "module";
2340
+ import * as path15 from "path";
2341
+ var require6 = createRequire5(import.meta.url);
2342
+ async function deployPreferencesCss(projectRoot) {
2343
+ const targetDir = path15.join(projectRoot, "src");
2344
+ const targetPath = path15.join(targetDir, "preferences.css");
2345
+ try {
2346
+ await fs13.access(targetPath);
2347
+ logger.debug(`preferences.css already exists at ${targetPath}, skipping`);
2348
+ return "skipped";
2349
+ } catch {
2350
+ }
2351
+ let sourcePath;
2352
+ try {
2353
+ const uiPkgJson = require6.resolve("@teamix-evo/ui/package.json");
2354
+ sourcePath = path15.join(path15.dirname(uiPkgJson), "src", "preferences.css");
2355
+ await fs13.access(sourcePath);
2356
+ } catch {
2357
+ logger.debug(
2358
+ "Could not resolve @teamix-evo/ui/src/preferences.css; skipping deploy"
2359
+ );
2360
+ return "source-missing";
2361
+ }
2362
+ await fs13.mkdir(targetDir, { recursive: true });
2363
+ const content = await fs13.readFile(sourcePath, "utf-8");
2364
+ await fs13.writeFile(targetPath, content, "utf-8");
2365
+ return "deployed";
2366
+ }
1996
2367
  var DEFAULT_UI_ALIASES = {
1997
2368
  components: "src/components/ui",
1998
2369
  hooks: "src/hooks",
@@ -2036,12 +2407,14 @@ async function runUiInit(options) {
2036
2407
  rsc
2037
2408
  };
2038
2409
  await writeProjectConfig(projectRoot, config);
2410
+ const preferencesCss = await deployPreferencesCss(projectRoot);
2039
2411
  return {
2040
2412
  status: "installed",
2041
2413
  aliases,
2042
2414
  iconLibrary,
2043
2415
  tsx,
2044
- rsc
2416
+ rsc,
2417
+ preferencesCss
2045
2418
  };
2046
2419
  }
2047
2420
 
@@ -2078,6 +2451,14 @@ var initCommand2 = new Command14("init").description(
2078
2451
  logger.info(` lib: ${result.aliases.lib}`);
2079
2452
  logger.info(` iconLibrary: ${result.iconLibrary}`);
2080
2453
  logger.info(` tsx: ${result.tsx}, rsc: ${result.rsc}`);
2454
+ if (result.preferencesCss === "deployed") {
2455
+ logger.info(" preferences.css: deployed \u2192 src/preferences.css");
2456
+ logger.info(
2457
+ " \u5728 src/index.css \u9876\u90E8\u52A0 `@import './preferences.css';` \u542F\u7528 shadcn \u5168\u5C40\u504F\u597D"
2458
+ );
2459
+ } else if (result.preferencesCss === "skipped") {
2460
+ logger.info(" preferences.css: \u5DF2\u5B58\u5728,\u4FDD\u7559\u7528\u6237\u7248\u672C");
2461
+ }
2081
2462
  logger.info("");
2082
2463
  logger.info("Next: `npx teamix-evo ui add button`");
2083
2464
  } catch (err) {
@@ -2156,23 +2537,23 @@ async function resolveConfig(opts) {
2156
2537
  import { Command as Command15 } from "commander";
2157
2538
 
2158
2539
  // src/core/ui-client.ts
2159
- import * as path14 from "path";
2160
- import * as fs12 from "fs/promises";
2161
- import { createRequire as createRequire3 } from "module";
2540
+ import * as path16 from "path";
2541
+ import * as fs14 from "fs/promises";
2542
+ import { createRequire as createRequire6 } from "module";
2162
2543
  import { loadUiPackageManifest } from "@teamix-evo/registry";
2163
- var require4 = createRequire3(import.meta.url);
2544
+ var require7 = createRequire6(import.meta.url);
2164
2545
  function resolvePackageRoot2(packageName) {
2165
- const pkgJsonPath = require4.resolve(`${packageName}/package.json`);
2166
- return path14.dirname(pkgJsonPath);
2546
+ const pkgJsonPath = require7.resolve(`${packageName}/package.json`);
2547
+ return path16.dirname(pkgJsonPath);
2167
2548
  }
2168
2549
  async function loadUiData(packageName) {
2169
2550
  const packageRoot = resolvePackageRoot2(packageName);
2170
2551
  logger.debug(`Resolved ui package root: ${packageRoot}`);
2171
2552
  const manifest = await loadUiPackageManifest(packageRoot);
2172
2553
  let data = {};
2173
- const dataPath = path14.join(packageRoot, "_data.json");
2554
+ const dataPath = path16.join(packageRoot, "_data.json");
2174
2555
  try {
2175
- const raw = await fs12.readFile(dataPath, "utf-8");
2556
+ const raw = await fs14.readFile(dataPath, "utf-8");
2176
2557
  data = JSON.parse(raw);
2177
2558
  } catch (err) {
2178
2559
  if (err.code !== "ENOENT") {
@@ -2184,8 +2565,8 @@ async function loadUiData(packageName) {
2184
2565
  }
2185
2566
 
2186
2567
  // src/core/ui-installer.ts
2187
- import * as path15 from "path";
2188
- import * as fs13 from "fs/promises";
2568
+ import * as path17 from "path";
2569
+ import * as fs15 from "fs/promises";
2189
2570
  import { resolveUiEntryOrder } from "@teamix-evo/registry";
2190
2571
 
2191
2572
  // src/utils/transform-imports.ts
@@ -2225,12 +2606,12 @@ function aliasToImportPath(alias) {
2225
2606
  }
2226
2607
 
2227
2608
  // src/core/ui-installer.ts
2228
- var DESIGN_COMPONENTS_DIR = ".teamix-evo/design/components";
2229
2609
  async function installUiEntries(options) {
2230
2610
  const {
2231
2611
  projectRoot,
2232
2612
  manifest,
2233
2613
  packageRoot,
2614
+ entryPackageRoot,
2234
2615
  aliases,
2235
2616
  requested,
2236
2617
  skipExisting = true
@@ -2239,7 +2620,6 @@ async function installUiEntries(options) {
2239
2620
  const idToEntry = new Map(manifest.entries.map((e) => [e.id, e]));
2240
2621
  const resources = [];
2241
2622
  const npmDeps = {};
2242
- const metaFiles = [];
2243
2623
  let written = 0;
2244
2624
  let skipped = 0;
2245
2625
  for (const id of orderedIds) {
@@ -2258,8 +2638,9 @@ async function installUiEntries(options) {
2258
2638
  skipped++;
2259
2639
  continue;
2260
2640
  }
2261
- const sourceAbs = path15.resolve(packageRoot, file.source);
2262
- const raw = await fs13.readFile(sourceAbs, "utf-8");
2641
+ const rootForEntry = entryPackageRoot?.get(entry.id) ?? packageRoot;
2642
+ const sourceAbs = path17.resolve(rootForEntry, file.source);
2643
+ const raw = await fs15.readFile(sourceAbs, "utf-8");
2263
2644
  const transformed = rewriteImports(raw, aliases);
2264
2645
  await writeFileSafe(targetAbs, transformed);
2265
2646
  written++;
@@ -2271,32 +2652,13 @@ async function installUiEntries(options) {
2271
2652
  strategy: entry.updateStrategy ?? "frozen"
2272
2653
  });
2273
2654
  }
2274
- if (entry.meta) {
2275
- const metaSourceAbs = path15.resolve(packageRoot, entry.meta);
2276
- const metaContent = await fs13.readFile(metaSourceAbs, "utf-8");
2277
- const metaTargetAbs = path15.join(
2278
- projectRoot,
2279
- DESIGN_COMPONENTS_DIR,
2280
- `${entry.id}.meta.md`
2281
- );
2282
- await writeFileSafe(metaTargetAbs, metaContent);
2283
- metaFiles.push(metaTargetAbs);
2284
- resources.push({
2285
- id: `${entry.id}:meta`,
2286
- target: metaTargetAbs,
2287
- hash: computeHash(metaContent),
2288
- strategy: "regenerable"
2289
- });
2290
- logger.info(` meta: ${rel(projectRoot, metaTargetAbs)}`);
2291
- }
2292
2655
  }
2293
2656
  return {
2294
2657
  orderedIds,
2295
2658
  resources,
2296
2659
  npmDependencies: npmDeps,
2297
2660
  written,
2298
- skipped,
2299
- metaFiles
2661
+ skipped
2300
2662
  };
2301
2663
  }
2302
2664
  function resolveTargetPath(projectRoot, aliases, entry, file) {
@@ -2306,10 +2668,10 @@ function resolveTargetPath(projectRoot, aliases, entry, file) {
2306
2668
  `Entry "${entry.id}" requires alias "${file.targetAlias}" but it is not configured.`
2307
2669
  );
2308
2670
  }
2309
- return path15.join(projectRoot, aliasDir, file.targetName);
2671
+ return path17.join(projectRoot, aliasDir, file.targetName);
2310
2672
  }
2311
2673
  function rel(projectRoot, abs) {
2312
- return path15.relative(projectRoot, abs);
2674
+ return path17.relative(projectRoot, abs);
2313
2675
  }
2314
2676
 
2315
2677
  // src/core/ui-add.ts
@@ -2371,7 +2733,6 @@ async function runUiAdd(options) {
2371
2733
  orderedIds: result.orderedIds,
2372
2734
  written: result.written,
2373
2735
  skipped: result.skipped,
2374
- metaFiles: result.metaFiles,
2375
2736
  npmDependencies: result.npmDependencies,
2376
2737
  resources: result.resources
2377
2738
  };
@@ -2397,7 +2758,7 @@ var addCommand2 = new Command15("add").description(
2397
2758
  overwrite: opts.overwrite
2398
2759
  });
2399
2760
  logger.success(
2400
- `UI add complete: ${result.written} written, ${result.skipped} skipped, ${result.metaFiles.length} meta.`
2761
+ `UI add complete: ${result.written} written, ${result.skipped} skipped.`
2401
2762
  );
2402
2763
  logger.info("");
2403
2764
  logger.info(`Resolved order: ${result.orderedIds.join(" \u2192 ")}`);
@@ -2409,12 +2770,6 @@ var addCommand2 = new Command15("add").description(
2409
2770
  logger.info(` pnpm add ${installCmd}`);
2410
2771
  logger.info(` # or: npm install ${installCmd}`);
2411
2772
  }
2412
- if (result.metaFiles.length > 0) {
2413
- logger.info("");
2414
- logger.info(
2415
- "Component meta dropped under .teamix-evo/design/components/ (AI-readable)."
2416
- );
2417
- }
2418
2773
  } catch (err) {
2419
2774
  const message = err.message;
2420
2775
  if (message.startsWith("UI not initialized")) {
@@ -2514,22 +2869,23 @@ uiCommand.addCommand(addCommand2);
2514
2869
  uiCommand.addCommand(listCommand3);
2515
2870
 
2516
2871
  // src/commands/biz-ui/index.ts
2517
- import { Command as Command20 } from "commander";
2872
+ import { Command as Command21 } from "commander";
2518
2873
 
2519
2874
  // src/commands/biz-ui/add.ts
2520
2875
  import { Command as Command18 } from "commander";
2521
2876
 
2522
2877
  // src/core/variant-ui-add.ts
2523
- import * as path16 from "path";
2524
- import { createRequire as createRequire4 } from "module";
2878
+ import * as path18 from "path";
2879
+ import { createRequire as createRequire7 } from "module";
2525
2880
  import {
2881
+ loadUiPackageManifest as loadUiPackageManifest2,
2526
2882
  loadVariantUiPackageCatalog,
2527
2883
  loadVariantUiPackageManifest
2528
2884
  } from "@teamix-evo/registry";
2529
- var require5 = createRequire4(import.meta.url);
2885
+ var require8 = createRequire7(import.meta.url);
2530
2886
  function resolvePackageRoot3(packageName) {
2531
- const pkgJsonPath = require5.resolve(`${packageName}/package.json`);
2532
- return path16.dirname(pkgJsonPath);
2887
+ const pkgJsonPath = require8.resolve(`${packageName}/package.json`);
2888
+ return path18.dirname(pkgJsonPath);
2533
2889
  }
2534
2890
  async function runVariantUiAdd(packageName, options) {
2535
2891
  const { projectRoot, variant, ids, overwrite } = options;
@@ -2552,27 +2908,42 @@ async function runVariantUiAdd(packageName, options) {
2552
2908
  `Variant "${variant}" not found in ${fullPackageName}. Known variants: ${known}. Hint: \`teamix-evo ${packageName} list-variants\` shows all.`
2553
2909
  );
2554
2910
  }
2555
- const variantDir = path16.join(packageRoot, "variants", variant);
2911
+ const variantDir = path18.join(packageRoot, "variants", variant);
2556
2912
  const variantManifest = await loadVariantUiPackageManifest(variantDir);
2557
2913
  const knownIds = new Set(variantManifest.entries.map((e) => e.id));
2558
2914
  const unknown = ids.filter((id) => !knownIds.has(id));
2559
2915
  if (unknown.length > 0) {
2560
2916
  throw new Error(
2561
- `Unknown entry id(s) in ${packageName}#${variant}: ${unknown.map((s) => `"${s}"`).join(", ")}. Run \`teamix-evo ${packageName} list --variant ${variant}\` to see options.`
2917
+ `Unknown entry id(s) in ${packageName}#${variant}: ${unknown.map((s) => `"${s}"`).join(", ")}. Run \`teamix-evo ${packageName} list-variants\` to see this package's variants, or \`teamix-evo ${packageName} list --variant ${variant}\` for its entries.`
2562
2918
  );
2563
2919
  }
2920
+ const uiPackageRoot = resolvePackageRoot3("@teamix-evo/ui");
2921
+ const uiManifest = await loadUiPackageManifest2(uiPackageRoot);
2922
+ const entryPackageRoot = /* @__PURE__ */ new Map();
2923
+ const mergedEntries = [];
2924
+ for (const e of variantManifest.entries) {
2925
+ entryPackageRoot.set(e.id, variantDir);
2926
+ mergedEntries.push(e);
2927
+ }
2928
+ for (const e of uiManifest.entries) {
2929
+ if (entryPackageRoot.has(e.id)) continue;
2930
+ entryPackageRoot.set(e.id, uiPackageRoot);
2931
+ mergedEntries.push(e);
2932
+ }
2564
2933
  const adaptedManifest = {
2565
2934
  schemaVersion: 1,
2566
2935
  package: "ui",
2567
2936
  version: variantManifest.version,
2568
2937
  engines: variantManifest.engines,
2569
- entries: variantManifest.entries
2938
+ entries: mergedEntries
2570
2939
  };
2571
2940
  const result = await installUiEntries({
2572
2941
  projectRoot,
2573
2942
  manifest: adaptedManifest,
2574
2943
  packageRoot: variantDir,
2575
- // sources resolved relative to variant dir
2944
+ // default for variant entries
2945
+ entryPackageRoot,
2946
+ // ui entries resolve from uiPackageRoot
2576
2947
  aliases: uiCfg.aliases,
2577
2948
  requested: ids,
2578
2949
  skipExisting: !overwrite
@@ -2604,7 +2975,6 @@ async function runVariantUiAdd(packageName, options) {
2604
2975
  orderedIds: result.orderedIds,
2605
2976
  written: result.written,
2606
2977
  skipped: result.skipped,
2607
- metaFiles: result.metaFiles,
2608
2978
  npmDependencies: result.npmDependencies,
2609
2979
  resources: result.resources
2610
2980
  };
@@ -2641,6 +3011,36 @@ async function listBizUiVariants(packageRoot) {
2641
3011
  async function listTemplatesVariants(packageRoot) {
2642
3012
  return listVariantUi("templates", packageRoot);
2643
3013
  }
3014
+ async function listVariantUiEntries(packageName, variant, packageRoot) {
3015
+ const fullPackageName = `@teamix-evo/${packageName}`;
3016
+ const root = packageRoot ?? resolvePackageRoot3(fullPackageName);
3017
+ const catalog = await loadVariantUiPackageCatalog(root);
3018
+ if (!catalog.variants.some((v) => v.name === variant)) {
3019
+ const known = catalog.variants.map((v) => v.name).join(", ");
3020
+ throw new Error(
3021
+ `Variant "${variant}" not found in ${fullPackageName}. Known: ${known}.`
3022
+ );
3023
+ }
3024
+ const variantDir = path18.join(root, "variants", variant);
3025
+ const variantManifest = await loadVariantUiPackageManifest(variantDir);
3026
+ return {
3027
+ packageName: fullPackageName,
3028
+ variant,
3029
+ entries: variantManifest.entries.map((e) => ({
3030
+ id: e.id,
3031
+ name: e.name,
3032
+ type: e.type,
3033
+ description: e.description,
3034
+ registryDependencies: e.registryDependencies ?? []
3035
+ }))
3036
+ };
3037
+ }
3038
+ async function listBizUiEntries(variant, packageRoot) {
3039
+ return listVariantUiEntries("biz-ui", variant, packageRoot);
3040
+ }
3041
+ async function listTemplatesEntries(variant, packageRoot) {
3042
+ return listVariantUiEntries("templates", variant, packageRoot);
3043
+ }
2644
3044
 
2645
3045
  // src/commands/biz-ui/add.ts
2646
3046
  var addCommand3 = new Command18("add").description(
@@ -2665,7 +3065,7 @@ var addCommand3 = new Command18("add").description(
2665
3065
  overwrite: opts.overwrite
2666
3066
  });
2667
3067
  logger.success(
2668
- `biz-ui add complete: ${result.written} written, ${result.skipped} skipped, ${result.metaFiles.length} meta.`
3068
+ `biz-ui add complete: ${result.written} written, ${result.skipped} skipped.`
2669
3069
  );
2670
3070
  logger.info("");
2671
3071
  logger.info(`Variant: ${result.variant}`);
@@ -2685,9 +3085,35 @@ var addCommand3 = new Command18("add").description(
2685
3085
  }
2686
3086
  );
2687
3087
 
2688
- // src/commands/biz-ui/list-variants.ts
3088
+ // src/commands/biz-ui/list.ts
2689
3089
  import { Command as Command19 } from "commander";
2690
- var listVariantsCommand2 = new Command19("list-variants").description("\u5217\u51FA @teamix-evo/biz-ui \u5305\u5185\u63D0\u4F9B\u7684\u6240\u6709\u4E1A\u52A1\u53D8\u4F53").action(async () => {
3090
+ var listCommand4 = new Command19("list").description("\u5217\u51FA\u6307\u5B9A\u53D8\u4F53\u4E0B\u7684 biz-ui entries").requiredOption("--variant <name>", "\u53D8\u4F53\u540D\uFF08\u5982 opentrek / uni-manager\uFF09").action(async (opts) => {
3091
+ try {
3092
+ const result = await listBizUiEntries(opts.variant);
3093
+ logger.info(`${result.packageName}#${result.variant} entries:`);
3094
+ logger.info("");
3095
+ if (result.entries.length === 0) {
3096
+ logger.info(" (no entries \u2014 variant skeleton, awaiting components)");
3097
+ return;
3098
+ }
3099
+ for (const e of result.entries) {
3100
+ const deps = e.registryDependencies.length ? ` [deps: ${e.registryDependencies.join(", ")}]` : "";
3101
+ logger.info(` ${e.id} (${e.type})${deps}`);
3102
+ if (e.description) logger.info(` ${e.description}`);
3103
+ logger.info("");
3104
+ }
3105
+ logger.info(
3106
+ `Install: teamix-evo biz-ui add <id> --variant ${result.variant}`
3107
+ );
3108
+ } catch (err) {
3109
+ logger.error(`Failed: ${err.message}`);
3110
+ process.exitCode = 1;
3111
+ }
3112
+ });
3113
+
3114
+ // src/commands/biz-ui/list-variants.ts
3115
+ import { Command as Command20 } from "commander";
3116
+ var listVariantsCommand2 = new Command20("list-variants").description("\u5217\u51FA @teamix-evo/biz-ui \u5305\u5185\u63D0\u4F9B\u7684\u6240\u6709\u4E1A\u52A1\u53D8\u4F53").action(async () => {
2691
3117
  try {
2692
3118
  const result = await listBizUiVariants();
2693
3119
  logger.info(`Available biz-ui variants in ${result.packageName}:`);
@@ -2709,18 +3135,19 @@ var listVariantsCommand2 = new Command19("list-variants").description("\u5217\u5
2709
3135
  });
2710
3136
 
2711
3137
  // src/commands/biz-ui/index.ts
2712
- var bizUiCommand = new Command20("biz-ui").description(
3138
+ var bizUiCommand = new Command21("biz-ui").description(
2713
3139
  "\u7BA1\u7406\u4E1A\u52A1 UI \u7EC4\u4EF6(\u53D8\u4F53\u611F\u77E5 \u2014 \u4E0E design / templates \u540C\u53D8\u4F53\u540D\u7A7A\u95F4)"
2714
3140
  );
2715
3141
  bizUiCommand.addCommand(addCommand3);
3142
+ bizUiCommand.addCommand(listCommand4);
2716
3143
  bizUiCommand.addCommand(listVariantsCommand2);
2717
3144
 
2718
3145
  // src/commands/templates/index.ts
2719
- import { Command as Command23 } from "commander";
3146
+ import { Command as Command25 } from "commander";
2720
3147
 
2721
3148
  // src/commands/templates/add.ts
2722
- import { Command as Command21 } from "commander";
2723
- var addCommand4 = new Command21("add").description(
3149
+ import { Command as Command22 } from "commander";
3150
+ var addCommand4 = new Command22("add").description(
2724
3151
  "\u5B89\u88C5\u4E00\u4E2A\u6216\u591A\u4E2A\u9875\u9762\u6A21\u677F(\u6309 id,\u81EA\u52A8\u5C55\u5F00 ui \u5305\u7684 registryDependencies)"
2725
3152
  ).argument("<ids...>", '\u6A21\u677F id \u5217\u8868,\u5982 "list-detail-page"').option("--variant <name>", '\u53D8\u4F53 id(\u5FC5\u586B,\u5982 "opentrek"\u3001"uni-manager")').option("--overwrite", "\u5373\u4F7F\u76EE\u6807\u6587\u4EF6\u5DF2\u5B58\u5728\u4E5F\u8986\u76D6").action(
2726
3153
  async (ids, opts) => {
@@ -2742,7 +3169,7 @@ var addCommand4 = new Command21("add").description(
2742
3169
  overwrite: opts.overwrite
2743
3170
  });
2744
3171
  logger.success(
2745
- `templates add complete: ${result.written} written, ${result.skipped} skipped, ${result.metaFiles.length} meta.`
3172
+ `templates add complete: ${result.written} written, ${result.skipped} skipped.`
2746
3173
  );
2747
3174
  logger.info("");
2748
3175
  logger.info(`Variant: ${result.variant}`);
@@ -2762,9 +3189,35 @@ var addCommand4 = new Command21("add").description(
2762
3189
  }
2763
3190
  );
2764
3191
 
3192
+ // src/commands/templates/list.ts
3193
+ import { Command as Command23 } from "commander";
3194
+ var listCommand5 = new Command23("list").description("\u5217\u51FA\u6307\u5B9A\u53D8\u4F53\u4E0B\u7684 templates entries").requiredOption("--variant <name>", "\u53D8\u4F53\u540D\uFF08\u5982 opentrek / uni-manager\uFF09").action(async (opts) => {
3195
+ try {
3196
+ const result = await listTemplatesEntries(opts.variant);
3197
+ logger.info(`${result.packageName}#${result.variant} entries:`);
3198
+ logger.info("");
3199
+ if (result.entries.length === 0) {
3200
+ logger.info(" (no entries \u2014 variant skeleton, awaiting templates)");
3201
+ return;
3202
+ }
3203
+ for (const e of result.entries) {
3204
+ const deps = e.registryDependencies.length ? ` [deps: ${e.registryDependencies.join(", ")}]` : "";
3205
+ logger.info(` ${e.id} (${e.type})${deps}`);
3206
+ if (e.description) logger.info(` ${e.description}`);
3207
+ logger.info("");
3208
+ }
3209
+ logger.info(
3210
+ `Install: teamix-evo templates add <id> --variant ${result.variant}`
3211
+ );
3212
+ } catch (err) {
3213
+ logger.error(`Failed: ${err.message}`);
3214
+ process.exitCode = 1;
3215
+ }
3216
+ });
3217
+
2765
3218
  // src/commands/templates/list-variants.ts
2766
- import { Command as Command22 } from "commander";
2767
- var listVariantsCommand3 = new Command22("list-variants").description("\u5217\u51FA @teamix-evo/templates \u5305\u5185\u63D0\u4F9B\u7684\u6240\u6709\u9875\u9762\u6A21\u677F\u53D8\u4F53").action(async () => {
3219
+ import { Command as Command24 } from "commander";
3220
+ var listVariantsCommand3 = new Command24("list-variants").description("\u5217\u51FA @teamix-evo/templates \u5305\u5185\u63D0\u4F9B\u7684\u6240\u6709\u9875\u9762\u6A21\u677F\u53D8\u4F53").action(async () => {
2768
3221
  try {
2769
3222
  const result = await listTemplatesVariants();
2770
3223
  logger.info(`Available templates variants in ${result.packageName}:`);
@@ -2786,21 +3239,216 @@ var listVariantsCommand3 = new Command22("list-variants").description("\u5217\u5
2786
3239
  });
2787
3240
 
2788
3241
  // src/commands/templates/index.ts
2789
- var templatesCommand = new Command23("templates").description(
3242
+ var templatesCommand = new Command25("templates").description(
2790
3243
  "\u7BA1\u7406\u9875\u9762\u6A21\u677F(\u53D8\u4F53\u611F\u77E5 \u2014 \u4E0E design / biz-ui \u540C\u53D8\u4F53\u540D\u7A7A\u95F4)"
2791
3244
  );
2792
3245
  templatesCommand.addCommand(addCommand4);
3246
+ templatesCommand.addCommand(listCommand5);
2793
3247
  templatesCommand.addCommand(listVariantsCommand3);
2794
3248
 
3249
+ // src/commands/logs/index.ts
3250
+ import { Command as Command27 } from "commander";
3251
+
3252
+ // src/commands/logs/analyze.ts
3253
+ import { Command as Command26 } from "commander";
3254
+ import { readFileSync, readdirSync, existsSync as existsSync2, statSync } from "fs";
3255
+ import { resolve as resolve3, join as join17 } from "path";
3256
+ var DATE_DIR_RE = /^\d{4}-\d{2}-\d{2}$/;
3257
+ var logsAnalyzeCommand = new Command26("analyze").description(
3258
+ "\u6C47\u603B vibe-logger \u8F93\u51FA (.log/ai/**/*.jsonl) \u2014 \u5DE5\u5177 / \u5305\u6807\u7B7E / MCP \u8C03\u7528\u9891\u7387,\u8F85\u52A9\u751F\u6001\u4F18\u5316"
3259
+ ).option("--dir <path>", "log \u76EE\u5F55 (\u9ED8\u8BA4 <project>/.log/ai)").option(
3260
+ "--days <n>",
3261
+ "\u53EA\u770B\u6700\u8FD1 N \u5929\u7684\u76EE\u5F55 (\u9ED8\u8BA4\u5168\u90E8;\u6309\u76EE\u5F55\u540D YYYY-MM-DD \u6BD4\u5BF9,\u4E0D\u89E3\u6790\u8BB0\u5F55 ts)"
3262
+ ).option("--top <n>", "\u6BCF\u4E2A\u6392\u884C\u5C55\u793A\u524D N \u9879 (\u9ED8\u8BA4 10)", "10").option("--json", "\u4EE5 JSON \u8F93\u51FA (CI/\u5DE5\u5177\u53CB\u597D)").action((opts) => {
3263
+ const baseDir = resolve3(opts.dir ?? join17(process.cwd(), ".log", "ai"));
3264
+ if (!existsSync2(baseDir)) {
3265
+ logger.warn(`No log directory at ${baseDir}.`);
3266
+ logger.info(
3267
+ "\u8FD0\u884C vibe-logger hook \u89E6\u53D1\u540E\u4F1A\u5728\u6B64\u76EE\u5F55\u751F\u6210 JSONL \u2014 \u89C1 .claude/scripts/vibe-logger.mjs"
3268
+ );
3269
+ return;
3270
+ }
3271
+ const dayLimit = parseIntOrUndef(opts.days);
3272
+ const top = parseIntOrUndef(opts.top) ?? 10;
3273
+ const dayDirs = readdirSync(baseDir, { withFileTypes: true }).filter((e) => e.isDirectory() && DATE_DIR_RE.test(e.name)).map((e) => e.name).sort().reverse();
3274
+ const selected = dayLimit !== void 0 ? dayDirs.slice(0, dayLimit) : dayDirs;
3275
+ const records = [];
3276
+ for (const day of selected) {
3277
+ const dayPath = join17(baseDir, day);
3278
+ let entries;
3279
+ try {
3280
+ entries = readdirSync(dayPath);
3281
+ } catch {
3282
+ continue;
3283
+ }
3284
+ for (const entry of entries) {
3285
+ if (!entry.endsWith(".jsonl")) continue;
3286
+ const fp = join17(dayPath, entry);
3287
+ try {
3288
+ if (!statSync(fp).isFile()) continue;
3289
+ } catch {
3290
+ continue;
3291
+ }
3292
+ const text2 = readFileSync(fp, "utf8");
3293
+ for (const line of text2.split("\n")) {
3294
+ if (!line.trim()) continue;
3295
+ try {
3296
+ records.push(JSON.parse(line));
3297
+ } catch {
3298
+ }
3299
+ }
3300
+ }
3301
+ }
3302
+ const report = buildReport(records, top);
3303
+ if (opts.json) {
3304
+ process.stdout.write(JSON.stringify(report, null, 2) + "\n");
3305
+ return;
3306
+ }
3307
+ printReport(baseDir, selected.length, report);
3308
+ });
3309
+ function buildReport(records, top) {
3310
+ const byAgent = {};
3311
+ const byEvent = {};
3312
+ const tools = /* @__PURE__ */ new Map();
3313
+ const tags = /* @__PURE__ */ new Map();
3314
+ const mcp = /* @__PURE__ */ new Map();
3315
+ const sessions = /* @__PURE__ */ new Set();
3316
+ const filesTouched = /* @__PURE__ */ new Set();
3317
+ const errors = [];
3318
+ let from = null;
3319
+ let to = null;
3320
+ for (const r of records) {
3321
+ if (!from || r.ts < from) from = r.ts;
3322
+ if (!to || r.ts > to) to = r.ts;
3323
+ byAgent[r.agent] = (byAgent[r.agent] ?? 0) + 1;
3324
+ byEvent[r.event] = (byEvent[r.event] ?? 0) + 1;
3325
+ sessions.add(`${r.agent}::${r.session}`);
3326
+ if (r.file) filesTouched.add(r.file);
3327
+ if (r.tool) {
3328
+ tools.set(r.tool, (tools.get(r.tool) ?? 0) + 1);
3329
+ if (r.tool.startsWith("mcp__")) {
3330
+ const name = r.tool.replace(/^mcp__[^_]+__/, "");
3331
+ const slot = mcp.get(name) ?? { count: 0, keys: /* @__PURE__ */ new Set() };
3332
+ slot.count += 1;
3333
+ for (const k of r.mcpArgs ?? []) slot.keys.add(k);
3334
+ mcp.set(name, slot);
3335
+ }
3336
+ }
3337
+ for (const tag of r.tags ?? []) {
3338
+ tags.set(tag, (tags.get(tag) ?? 0) + 1);
3339
+ }
3340
+ if (r.error) {
3341
+ errors.push({
3342
+ ts: r.ts,
3343
+ tool: r.tool,
3344
+ file: r.file,
3345
+ session: r.session
3346
+ });
3347
+ }
3348
+ }
3349
+ const topTools = sortAndSlice(tools, top).map(([tool, count]) => ({
3350
+ tool,
3351
+ count
3352
+ }));
3353
+ const topTags = sortAndSlice(tags, top).map(([tag, count]) => ({
3354
+ tag,
3355
+ count
3356
+ }));
3357
+ const topMcp = [...mcp.entries()].sort((a, b) => b[1].count - a[1].count).slice(0, top).map(([name, v]) => ({
3358
+ name,
3359
+ count: v.count,
3360
+ argKeys: [...v.keys].sort()
3361
+ }));
3362
+ const recentErrors = errors.slice(-10);
3363
+ return {
3364
+ range: { from, to },
3365
+ totals: {
3366
+ records: records.length,
3367
+ sessions: sessions.size,
3368
+ errors: errors.length,
3369
+ files: filesTouched.size
3370
+ },
3371
+ byAgent,
3372
+ byEvent,
3373
+ topTools,
3374
+ topTags,
3375
+ topMcp,
3376
+ recentErrors
3377
+ };
3378
+ }
3379
+ function sortAndSlice(m, top) {
3380
+ return [...m.entries()].sort((a, b) => b[1] - a[1]).slice(0, top);
3381
+ }
3382
+ function printReport(baseDir, dayCount, r) {
3383
+ logger.info(`vibe-logger \u5206\u6790\u62A5\u544A`);
3384
+ logger.info(` \u76EE\u5F55: ${baseDir}`);
3385
+ logger.info(` \u5929\u6570: ${dayCount}`);
3386
+ logger.info(` \u65F6\u95F4: ${r.range.from ?? "-"} \u2192 ${r.range.to ?? "-"}`);
3387
+ logger.info("");
3388
+ logger.info(
3389
+ `Totals records=${r.totals.records} sessions=${r.totals.sessions} errors=${r.totals.errors} files=${r.totals.files}`
3390
+ );
3391
+ logger.info(`By agent ${formatKv(r.byAgent)}`);
3392
+ logger.info(`By event ${formatKv(r.byEvent)}`);
3393
+ logger.info("");
3394
+ logger.info("Top tools:");
3395
+ for (const { tool, count } of r.topTools) {
3396
+ logger.info(` ${pad(count, 5)} ${tool}`);
3397
+ }
3398
+ if (r.topTags.length) {
3399
+ logger.info("");
3400
+ logger.info("Top package tags (which package the AI touched):");
3401
+ for (const { tag, count } of r.topTags) {
3402
+ logger.info(` ${pad(count, 5)} ${tag}`);
3403
+ }
3404
+ }
3405
+ if (r.topMcp.length) {
3406
+ logger.info("");
3407
+ logger.info("Top MCP tools:");
3408
+ for (const { name, count, argKeys } of r.topMcp) {
3409
+ const keys = argKeys.length ? ` args: ${argKeys.join(", ")}` : "";
3410
+ logger.info(` ${pad(count, 5)} ${name}${keys}`);
3411
+ }
3412
+ }
3413
+ if (r.recentErrors.length) {
3414
+ logger.info("");
3415
+ logger.info(`Recent errors (last ${r.recentErrors.length}):`);
3416
+ for (const e of r.recentErrors) {
3417
+ logger.info(
3418
+ ` ${e.ts} session=${e.session} tool=${e.tool ?? "-"} file=${e.file ?? "-"}`
3419
+ );
3420
+ }
3421
+ }
3422
+ }
3423
+ function formatKv(o) {
3424
+ return Object.entries(o).sort((a, b) => b[1] - a[1]).map(([k, v]) => `${k}=${v}`).join(" ");
3425
+ }
3426
+ function pad(n, width) {
3427
+ const s = String(n);
3428
+ return s.length >= width ? s : " ".repeat(width - s.length) + s;
3429
+ }
3430
+ function parseIntOrUndef(v) {
3431
+ if (v === void 0) return void 0;
3432
+ const n = Number.parseInt(v, 10);
3433
+ return Number.isFinite(n) && n > 0 ? n : void 0;
3434
+ }
3435
+
3436
+ // src/commands/logs/index.ts
3437
+ var logsCommand = new Command27("logs").description(
3438
+ "\u67E5\u8BE2 vibe-logger \u8F93\u51FA (.log/ai/**/*.jsonl) \u2014 AI \u8C03\u7528\u94FE\u5206\u6790"
3439
+ );
3440
+ logsCommand.addCommand(logsAnalyzeCommand);
3441
+
2795
3442
  // src/index.ts
2796
- var require6 = createRequire5(import.meta.url);
2797
- var { version } = require6("../package.json");
2798
- var program = new Command24();
3443
+ var require9 = createRequire8(import.meta.url);
3444
+ var { version } = require9("../package.json");
3445
+ var program = new Command28();
2799
3446
  program.name("teamix-evo").description("Where ideas evolve. \u2014 AI Coding \u5957\u4EF6").version(version);
2800
- program.addCommand(designCommand);
3447
+ program.addCommand(tokensCommand);
2801
3448
  program.addCommand(skillsCommand);
2802
3449
  program.addCommand(uiCommand);
2803
3450
  program.addCommand(bizUiCommand);
2804
3451
  program.addCommand(templatesCommand);
3452
+ program.addCommand(logsCommand);
2805
3453
  program.parse();
2806
3454
  //# sourceMappingURL=index.js.map