teamix-evo 0.4.0 → 0.5.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,8 +1,8 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  // src/index.ts
4
- import { Command as Command28 } from "commander";
5
- import { createRequire as createRequire8 } from "module";
4
+ import { Command as Command29 } from "commander";
5
+ import { createRequire as createRequire7 } from "module";
6
6
 
7
7
  // src/commands/tokens/index.ts
8
8
  import { Command as Command6 } from "commander";
@@ -67,7 +67,7 @@ function detectIde() {
67
67
  }
68
68
 
69
69
  // src/core/tokens-init.ts
70
- import * as path8 from "path";
70
+ import * as path9 from "path";
71
71
  import * as fs6 from "fs/promises";
72
72
  import { createRequire as createRequire2 } from "module";
73
73
  import {
@@ -321,7 +321,10 @@ async function loadSkillsData(packageName) {
321
321
  // src/core/skills-installer.ts
322
322
  import * as path7 from "path";
323
323
  import * as fs5 from "fs/promises";
324
- import { replaceManagedRegion } from "@teamix-evo/registry";
324
+ import {
325
+ replaceManagedRegion,
326
+ hasManagedRegion
327
+ } from "@teamix-evo/registry";
325
328
 
326
329
  // src/utils/template.ts
327
330
  import Handlebars from "handlebars";
@@ -440,20 +443,63 @@ async function mirrorSkillToIde(skill, ide, scope, projectRoot) {
440
443
  const rel2 = path7.relative(sourceDir, src);
441
444
  const targetFile = path7.join(targetDir, rel2);
442
445
  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);
446
+ const writtenContent = await writeMirrorContent(
447
+ targetFile,
448
+ sourceContent,
449
+ skill.managedRegions,
450
+ src
451
+ );
450
452
  records.push(
451
- makeMirrorRecord(skill, targetFile, sourceContent, ide, scope, rel2)
453
+ makeMirrorRecord(skill, targetFile, writtenContent, ide, scope, rel2)
452
454
  );
453
455
  logger.debug(` Mirrored ${ide}:${scope}: ${targetFile}`);
454
456
  }
455
457
  return records;
456
458
  }
459
+ async function writeMirrorContent(targetFile, sourceContent, managedRegions, sourceFile) {
460
+ const existing = await readFileOrNull(targetFile);
461
+ if (existing === null) {
462
+ await writeFileSafe(targetFile, sourceContent);
463
+ return sourceContent;
464
+ }
465
+ const regions = managedRegions ?? [];
466
+ const matchedRegions = regions.filter((id) => hasManagedRegion(existing, id));
467
+ if (matchedRegions.length === 0) {
468
+ if (existing !== sourceContent) {
469
+ logger.warn(
470
+ `Mirror drift detected at ${targetFile} \u2014 overwriting from source. Edit ${sourceFile} (not the mirror) and re-run \`teamix-evo skills sync\`.`
471
+ );
472
+ await writeFileSafe(targetFile, sourceContent);
473
+ return sourceContent;
474
+ }
475
+ return existing;
476
+ }
477
+ let merged = existing;
478
+ for (const id of matchedRegions) {
479
+ const newRegion = extractRegionBody(sourceContent, id);
480
+ if (newRegion === null) continue;
481
+ try {
482
+ merged = replaceManagedRegion(merged, id, newRegion);
483
+ } catch {
484
+ }
485
+ }
486
+ if (merged !== existing) {
487
+ await writeFileSafe(targetFile, merged);
488
+ }
489
+ return merged;
490
+ }
491
+ function extractRegionBody(content, id) {
492
+ const re = new RegExp(
493
+ `<!-- teamix-evo:managed:start id="${escapeRegExp(
494
+ id
495
+ )}" -->([\\s\\S]*?)<!-- teamix-evo:managed:end id="${escapeRegExp(
496
+ id
497
+ )}" -->`
498
+ );
499
+ const m = content.match(re);
500
+ if (!m) return null;
501
+ return m[1].replace(/^\n/, "").replace(/\n$/, "");
502
+ }
457
503
  async function renderSkillContent(sourceAbs, skill, data) {
458
504
  if (skill.template ?? sourceAbs.endsWith(".hbs")) {
459
505
  const tpl = await loadTemplateFile(sourceAbs);
@@ -613,17 +659,16 @@ async function syncSkillsToIdes(options) {
613
659
  const rel2 = path7.relative(sourceDir, src);
614
660
  const targetFile = path7.join(targetDir, rel2);
615
661
  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);
662
+ const writtenContent = await writeMirrorContent(
663
+ targetFile,
664
+ sourceContent,
665
+ skill.managedRegions,
666
+ src
667
+ );
623
668
  out.push({
624
669
  id: rel2 === "SKILL.md" ? skill.id : `${skill.id}:${rel2}`,
625
670
  target: targetFile,
626
- hash: computeHash(sourceContent),
671
+ hash: computeHash(writtenContent),
627
672
  strategy: skill.updateStrategy,
628
673
  ide,
629
674
  scope
@@ -662,6 +707,29 @@ async function removeSkillFiles(records) {
662
707
  return removed;
663
708
  }
664
709
 
710
+ // src/utils/mcp.ts
711
+ import * as path8 from "path";
712
+ var MCP_JSON_CONTENT = {
713
+ mcpServers: {
714
+ "teamix-evo": {
715
+ command: "npx",
716
+ args: ["-y", "@teamix-evo/mcp"]
717
+ }
718
+ }
719
+ };
720
+ async function ensureMcpJson(projectRoot) {
721
+ const mcpPath = path8.join(projectRoot, ".mcp.json");
722
+ if (await fileExists(mcpPath)) return "exists";
723
+ try {
724
+ await writeFileSafe(mcpPath, JSON.stringify(MCP_JSON_CONTENT, null, 2) + "\n");
725
+ logger.debug(`Wrote .mcp.json \u2192 ${mcpPath}`);
726
+ return "created";
727
+ } catch (err) {
728
+ logger.warn(`Failed to write .mcp.json: ${err.message}`);
729
+ return "failed";
730
+ }
731
+ }
732
+
665
733
  // src/core/skills-add.ts
666
734
  var DEFAULT_SKILLS_PACKAGE = "@teamix-evo/skills";
667
735
  var FLAT_VARIANT = "_flat";
@@ -673,9 +741,6 @@ async function runSkillsAdd(options) {
673
741
  await ensureTeamixDir(projectRoot);
674
742
  const existingConfig = await readProjectConfig(projectRoot);
675
743
  const existingSkillsCfg = existingConfig?.packages?.skills;
676
- if (!isIncremental && existingSkillsCfg) {
677
- return { status: "already-added" };
678
- }
679
744
  const ides = options.ides && options.ides.length > 0 ? [...options.ides] : existingSkillsCfg?.ides ? [...existingSkillsCfg.ides] : [];
680
745
  const scope = options.scope ?? existingSkillsCfg?.scope;
681
746
  if (ides.length === 0) {
@@ -713,8 +778,7 @@ async function runSkillsAdd(options) {
713
778
  skippedSkillIds = requestedNames.filter((n) => existingSkillIds.has(n));
714
779
  onlyIds = requestedNames.filter((n) => !existingSkillIds.has(n));
715
780
  } else {
716
- skippedSkillIds = [];
717
- onlyIds = manifest.skills.filter((s) => {
781
+ const candidateIds = manifest.skills.filter((s) => {
718
782
  if (!s.variant) return true;
719
783
  if (!currentTokensVariant) {
720
784
  logger.debug(
@@ -730,6 +794,11 @@ async function runSkillsAdd(options) {
730
794
  }
731
795
  return true;
732
796
  }).map((s) => s.id);
797
+ skippedSkillIds = candidateIds.filter((id) => existingSkillIds.has(id));
798
+ onlyIds = candidateIds.filter((id) => !existingSkillIds.has(id));
799
+ }
800
+ if (!isIncremental && existingSkillsCfg && onlyIds.length === 0) {
801
+ return { status: "already-added" };
733
802
  }
734
803
  if (isIncremental && onlyIds.length === 0) {
735
804
  return {
@@ -806,6 +875,7 @@ async function runSkillsAdd(options) {
806
875
  };
807
876
  }
808
877
  await writeSkillsLock(projectRoot, lock);
878
+ await ensureMcpJson(projectRoot);
809
879
  return {
810
880
  status: "installed",
811
881
  packageName,
@@ -843,6 +913,16 @@ async function runTokensInit(options) {
843
913
  const { projectRoot, variant, ide } = options;
844
914
  const packageName = options.packageName ?? DEFAULT_TOKENS_PACKAGE;
845
915
  await ensureTeamixDir(projectRoot);
916
+ const packageRoot = options.packageRoot ?? resolveTokensPackageRoot(packageName);
917
+ const catalog = await loadTokensPackageManifest(packageRoot);
918
+ const variantEntry = getVariantEntry(catalog, variant);
919
+ if (!variantEntry) {
920
+ const known = catalog.variants.map((v) => v.name).join(", ");
921
+ throw new Error(
922
+ `Unknown variant "${variant}". Available variants: ${known || "(none)"}.
923
+ Run \`teamix-evo tokens list-variants\` to see all options.`
924
+ );
925
+ }
846
926
  const existingConfig = await readProjectConfig(projectRoot);
847
927
  if (existingConfig?.packages?.tokens) {
848
928
  const existingVariant = existingConfig.packages.tokens.variant;
@@ -855,21 +935,12 @@ async function runTokensInit(options) {
855
935
  requestedVariant: variant
856
936
  };
857
937
  }
858
- const packageRoot = options.packageRoot ?? resolveTokensPackageRoot(packageName);
859
- const catalog = await loadTokensPackageManifest(packageRoot);
860
- const variantEntry = getVariantEntry(catalog, variant);
861
- if (!variantEntry) {
862
- const known = catalog.variants.map((v) => v.name).join(", ");
863
- throw new Error(
864
- `Tokens variant not found: "${variant}". Known variants: ${known || "(none)"}. Hint: run "teamix-evo tokens list-variants" to see all.`
865
- );
866
- }
867
938
  const installed = [];
868
939
  for (const fileRel of variantEntry.files) {
869
940
  const result = await installVariantFile(fileRel, packageRoot, projectRoot);
870
941
  if (result) installed.push(result);
871
942
  }
872
- const overridesAbs = path8.join(
943
+ const overridesAbs = path9.join(
873
944
  projectRoot,
874
945
  CONSUMER_TOKENS_DIR,
875
946
  CONSUMER_OVERRIDES_FILE
@@ -880,7 +951,7 @@ async function runTokensInit(options) {
880
951
  const overridesContent = await fs6.readFile(overridesAbs, "utf-8");
881
952
  installed.push({
882
953
  id: `tokens:${CONSUMER_OVERRIDES_FILE}`,
883
- target: path8.posix.join(CONSUMER_TOKENS_DIR, CONSUMER_OVERRIDES_FILE),
954
+ target: path9.posix.join(CONSUMER_TOKENS_DIR, CONSUMER_OVERRIDES_FILE),
884
955
  hash: computeHash(overridesContent),
885
956
  strategy: "frozen"
886
957
  });
@@ -897,7 +968,7 @@ async function runTokensInit(options) {
897
968
  installedAt: (/* @__PURE__ */ new Date()).toISOString()
898
969
  };
899
970
  await writeFileSafe(
900
- path8.join(projectRoot, ".teamix-evo", "tokens-lock.json"),
971
+ path9.join(projectRoot, ".teamix-evo", "tokens-lock.json"),
901
972
  JSON.stringify(lock, null, 2) + "\n"
902
973
  );
903
974
  const config = {
@@ -929,6 +1000,7 @@ async function runTokensInit(options) {
929
1000
  if (tokensIdx >= 0) prior.installed[tokensIdx] = tokensEntry;
930
1001
  else prior.installed.push(tokensEntry);
931
1002
  await writeInstalledManifest(projectRoot, prior);
1003
+ await ensureMcpJson(projectRoot);
932
1004
  const skills = await tryAutoInstallVariantSkills({
933
1005
  projectRoot,
934
1006
  variant,
@@ -1015,14 +1087,14 @@ async function tryAutoInstallVariantSkills(args) {
1015
1087
  }
1016
1088
  }
1017
1089
  async function installVariantFile(fileRelToPackage, packageRoot, projectRoot) {
1018
- const sourceAbs = path8.join(packageRoot, fileRelToPackage);
1019
- const base = path8.basename(fileRelToPackage);
1090
+ const sourceAbs = path9.join(packageRoot, fileRelToPackage);
1091
+ const base = path9.basename(fileRelToPackage);
1020
1092
  if (base === "theme.css") {
1021
- const targetRel = path8.posix.join(
1093
+ const targetRel = path9.posix.join(
1022
1094
  CONSUMER_TOKENS_DIR,
1023
1095
  CONSUMER_THEME_FILE
1024
1096
  );
1025
- const targetAbs = path8.join(projectRoot, targetRel);
1097
+ const targetAbs = path9.join(projectRoot, targetRel);
1026
1098
  const content = await fs6.readFile(sourceAbs, "utf-8");
1027
1099
  await writeFileSafe(targetAbs, content);
1028
1100
  return {
@@ -1033,11 +1105,11 @@ async function installVariantFile(fileRelToPackage, packageRoot, projectRoot) {
1033
1105
  };
1034
1106
  }
1035
1107
  if (base === "overrides.css" || base === "tokens.overrides.css") {
1036
- const targetRel = path8.posix.join(
1108
+ const targetRel = path9.posix.join(
1037
1109
  CONSUMER_TOKENS_DIR,
1038
1110
  CONSUMER_OVERRIDES_FILE
1039
1111
  );
1040
- const targetAbs = path8.join(projectRoot, targetRel);
1112
+ const targetAbs = path9.join(projectRoot, targetRel);
1041
1113
  if (await fileExists(targetAbs)) {
1042
1114
  const existing = await fs6.readFile(targetAbs, "utf-8");
1043
1115
  return {
@@ -1060,7 +1132,7 @@ async function installVariantFile(fileRelToPackage, packageRoot, projectRoot) {
1060
1132
  }
1061
1133
  function resolveTokensPackageRoot(packageName) {
1062
1134
  const pkgJson = require3.resolve(`${packageName}/package.json`);
1063
- return path8.dirname(pkgJson);
1135
+ return path9.dirname(pkgJson);
1064
1136
  }
1065
1137
  async function listTokenVariants(packageName = DEFAULT_TOKENS_PACKAGE, packageRoot) {
1066
1138
  const root = packageRoot ?? resolveTokensPackageRoot(packageName);
@@ -1078,11 +1150,46 @@ async function listTokenVariants(packageName = DEFAULT_TOKENS_PACKAGE, packageRo
1078
1150
  };
1079
1151
  }
1080
1152
 
1153
+ // src/utils/global-root.ts
1154
+ import { existsSync } from "fs";
1155
+ import * as fs7 from "fs/promises";
1156
+ import * as os3 from "os";
1157
+ import * as path10 from "path";
1158
+ var GLOBAL_META_DIR = ".teamix-evo-global";
1159
+ var TEAMIX_DIR2 = ".teamix-evo";
1160
+ var CONFIG_FILE2 = "config.json";
1161
+ function getGlobalMetaRoot() {
1162
+ return path10.join(os3.homedir(), GLOBAL_META_DIR);
1163
+ }
1164
+ function isTeamixEvoProject(dir) {
1165
+ return existsSync(path10.join(dir, TEAMIX_DIR2, CONFIG_FILE2));
1166
+ }
1167
+ async function ensureGlobalMetaRoot() {
1168
+ const root = getGlobalMetaRoot();
1169
+ await fs7.mkdir(root, { recursive: true });
1170
+ return root;
1171
+ }
1172
+ function hasPackageJson(projectRoot) {
1173
+ return existsSync(path10.join(projectRoot, "package.json"));
1174
+ }
1175
+ function resolveSkillsMaintenanceRoot(cwd) {
1176
+ if (isTeamixEvoProject(cwd)) return cwd;
1177
+ const globalRoot = getGlobalMetaRoot();
1178
+ if (isTeamixEvoProject(globalRoot)) return globalRoot;
1179
+ return cwd;
1180
+ }
1181
+
1081
1182
  // src/commands/tokens/init.ts
1082
1183
  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) => {
1083
1184
  try {
1084
1185
  const ide = detectIde();
1085
1186
  const projectRoot = ide.getProjectRoot();
1187
+ if (!hasPackageJson(projectRoot)) {
1188
+ logger.error(
1189
+ "No package.json found in current directory. Please run this command in a valid project root."
1190
+ );
1191
+ process.exit(1);
1192
+ }
1086
1193
  logger.info(`Initializing design system: variant="${variant}"`);
1087
1194
  logger.debug(`Project root: ${projectRoot}`);
1088
1195
  logger.debug(`IDE: ${ide.name}`);
@@ -1152,8 +1259,8 @@ var initCommand = new Command("init").description("\u521D\u59CB\u5316\u8BBE\u8BA
1152
1259
  import { Command as Command2 } from "commander";
1153
1260
 
1154
1261
  // src/core/tokens-update.ts
1155
- import * as path9 from "path";
1156
- import * as fs7 from "fs/promises";
1262
+ import * as path11 from "path";
1263
+ import * as fs8 from "fs/promises";
1157
1264
  import { createRequire as createRequire3 } from "module";
1158
1265
  import {
1159
1266
  loadTokensPackageManifest as loadTokensPackageManifest2,
@@ -1195,7 +1302,7 @@ async function runTokensUpdate(options) {
1195
1302
  projectRoot
1196
1303
  );
1197
1304
  const preserved = [];
1198
- const overridesAbs = path9.join(
1305
+ const overridesAbs = path11.join(
1199
1306
  projectRoot,
1200
1307
  CONSUMER_TOKENS_DIR2,
1201
1308
  "tokens.overrides.css"
@@ -1214,7 +1321,7 @@ async function runTokensUpdate(options) {
1214
1321
  installedAt: (/* @__PURE__ */ new Date()).toISOString()
1215
1322
  };
1216
1323
  await writeFileSafe(
1217
- path9.join(projectRoot, ".teamix-evo", "tokens-lock.json"),
1324
+ path11.join(projectRoot, ".teamix-evo", "tokens-lock.json"),
1218
1325
  JSON.stringify(lock, null, 2) + "\n"
1219
1326
  );
1220
1327
  config.packages.tokens.version = variantEntry.version;
@@ -1229,9 +1336,9 @@ async function runTokensUpdate(options) {
1229
1336
  prior.installed[idx].installedAt = (/* @__PURE__ */ new Date()).toISOString();
1230
1337
  for (const r of prior.installed[idx].resources) {
1231
1338
  if (r.strategy === "regenerable") {
1232
- const abs = path9.isAbsolute(r.target) ? r.target : path9.join(projectRoot, r.target);
1339
+ const abs = path11.isAbsolute(r.target) ? r.target : path11.join(projectRoot, r.target);
1233
1340
  if (await fileExists(abs)) {
1234
- r.hash = computeHash(await fs7.readFile(abs, "utf-8"));
1341
+ r.hash = computeHash(await fs8.readFile(abs, "utf-8"));
1235
1342
  }
1236
1343
  }
1237
1344
  }
@@ -1250,15 +1357,15 @@ async function runTokensUpdate(options) {
1250
1357
  async function rewriteRegenerableFiles(files, packageRoot, projectRoot) {
1251
1358
  const written = [];
1252
1359
  for (const fileRel of files) {
1253
- const base = path9.basename(fileRel);
1360
+ const base = path11.basename(fileRel);
1254
1361
  if (base !== "theme.css") continue;
1255
- const sourceAbs = path9.join(packageRoot, fileRel);
1256
- const targetAbs = path9.join(
1362
+ const sourceAbs = path11.join(packageRoot, fileRel);
1363
+ const targetAbs = path11.join(
1257
1364
  projectRoot,
1258
1365
  CONSUMER_TOKENS_DIR2,
1259
1366
  CONSUMER_THEME_FILE2
1260
1367
  );
1261
- const content = await fs7.readFile(sourceAbs, "utf-8");
1368
+ const content = await fs8.readFile(sourceAbs, "utf-8");
1262
1369
  await writeFileSafe(targetAbs, content);
1263
1370
  written.push(CONSUMER_THEME_FILE2);
1264
1371
  }
@@ -1266,7 +1373,7 @@ async function rewriteRegenerableFiles(files, packageRoot, projectRoot) {
1266
1373
  }
1267
1374
  function resolveTokensPackageRoot2(packageName) {
1268
1375
  const pkgJson = require4.resolve(`${packageName}/package.json`);
1269
- return path9.dirname(pkgJson);
1376
+ return path11.dirname(pkgJson);
1270
1377
  }
1271
1378
 
1272
1379
  // src/commands/tokens/update.ts
@@ -1382,8 +1489,8 @@ var listVariantsCommand = new Command4("list-variants").description("\u5217\u51F
1382
1489
 
1383
1490
  // src/commands/tokens/uninstall.ts
1384
1491
  import { Command as Command5 } from "commander";
1385
- import * as fs8 from "fs/promises";
1386
- import * as path10 from "path";
1492
+ import * as fs9 from "fs/promises";
1493
+ import * as path12 from "path";
1387
1494
  import * as prompts from "@clack/prompts";
1388
1495
  var TOKENS_PACKAGE = "@teamix-evo/tokens";
1389
1496
  var uninstallCommand = new Command5("uninstall").description(
@@ -1425,9 +1532,9 @@ var uninstallCommand = new Command5("uninstall").description(
1425
1532
  }
1426
1533
  let removed = 0;
1427
1534
  for (const r of removable) {
1428
- const target = path10.isAbsolute(r.target) ? r.target : path10.join(projectRoot, r.target);
1535
+ const target = path12.isAbsolute(r.target) ? r.target : path12.join(projectRoot, r.target);
1429
1536
  try {
1430
- await fs8.unlink(target);
1537
+ await fs9.unlink(target);
1431
1538
  removed++;
1432
1539
  } catch (err) {
1433
1540
  if (err.code !== "ENOENT") {
@@ -1445,13 +1552,13 @@ var uninstallCommand = new Command5("uninstall").description(
1445
1552
  }
1446
1553
  delete config.packages.tokens;
1447
1554
  await writeProjectConfig(projectRoot, config);
1448
- const lockPath = path10.join(
1555
+ const lockPath = path12.join(
1449
1556
  projectRoot,
1450
1557
  ".teamix-evo",
1451
1558
  "tokens-lock.json"
1452
1559
  );
1453
1560
  try {
1454
- await fs8.unlink(lockPath);
1561
+ await fs9.unlink(lockPath);
1455
1562
  } catch (err) {
1456
1563
  if (err.code !== "ENOENT") {
1457
1564
  logger.warn(
@@ -1486,34 +1593,6 @@ import { Command as Command13 } from "commander";
1486
1593
  // src/commands/skills/add.ts
1487
1594
  import { Command as Command7 } from "commander";
1488
1595
  import * as prompts2 from "@clack/prompts";
1489
-
1490
- // src/utils/global-root.ts
1491
- import { existsSync } from "fs";
1492
- import * as fs9 from "fs/promises";
1493
- import * as os3 from "os";
1494
- import * as path11 from "path";
1495
- var GLOBAL_META_DIR = ".teamix-evo-global";
1496
- var TEAMIX_DIR2 = ".teamix-evo";
1497
- var CONFIG_FILE2 = "config.json";
1498
- function getGlobalMetaRoot() {
1499
- return path11.join(os3.homedir(), GLOBAL_META_DIR);
1500
- }
1501
- function isTeamixEvoProject(dir) {
1502
- return existsSync(path11.join(dir, TEAMIX_DIR2, CONFIG_FILE2));
1503
- }
1504
- async function ensureGlobalMetaRoot() {
1505
- const root = getGlobalMetaRoot();
1506
- await fs9.mkdir(root, { recursive: true });
1507
- return root;
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
- }
1515
-
1516
- // src/commands/skills/add.ts
1517
1596
  var addCommand = new Command7("add").description(
1518
1597
  "\u5411\u9879\u76EE\uFF08\u6216\u5168\u5C40 IDE \u914D\u7F6E\uFF09\u6DFB\u52A0 teamix-evo skills\uFF1B\u4E0D\u4F20 names \u5219\u6DFB\u52A0 manifest \u5185\u5168\u90E8 skill"
1519
1598
  ).argument(
@@ -1536,6 +1615,11 @@ var addCommand = new Command7("add").description(
1536
1615
  if (scope === "global" && !isTeamixEvoProject(cwd)) {
1537
1616
  projectRoot = await ensureGlobalMetaRoot();
1538
1617
  logger.info(`Global skill install \u2014 meta root: ${projectRoot}`);
1618
+ } else if (scope !== "global" && !hasPackageJson(cwd)) {
1619
+ logger.error(
1620
+ "No package.json found in current directory. Please run this command in a valid project root."
1621
+ );
1622
+ process.exit(1);
1539
1623
  }
1540
1624
  logger.info(
1541
1625
  isIncremental ? `Adding skills [${names.join(",")}]: ides=[${ides.join(
@@ -1843,7 +1927,7 @@ var updateCommand2 = new Command9("update").description("\u66F4\u65B0\u5DF2\u5B8
1843
1927
  // src/commands/skills/uninstall.ts
1844
1928
  import { Command as Command10 } from "commander";
1845
1929
  import * as prompts3 from "@clack/prompts";
1846
- import * as path12 from "path";
1930
+ import * as path13 from "path";
1847
1931
  import * as fs10 from "fs/promises";
1848
1932
  var SKILLS_PACKAGE3 = "@teamix-evo/skills";
1849
1933
  var uninstallCommand2 = new Command10("uninstall").description(
@@ -1912,6 +1996,7 @@ async function runFullUninstall(args) {
1912
1996
  const removed = await removeSkillFiles(resources);
1913
1997
  logger.debug(`Removed ${removed.length} files`);
1914
1998
  const skillsRoot = getSkillsSourceDir(projectRoot);
1999
+ const sourceSkillNames = await listSkillSourceNames(skillsRoot);
1915
2000
  try {
1916
2001
  await fs10.rm(skillsRoot, { recursive: true, force: true });
1917
2002
  logger.debug(`Removed source dir ${skillsRoot}`);
@@ -1920,6 +2005,17 @@ async function runFullUninstall(args) {
1920
2005
  `Failed to remove ${skillsRoot}: ${err.message}`
1921
2006
  );
1922
2007
  }
2008
+ const skillNames = collectSkillNames({
2009
+ fromSources: sourceSkillNames,
2010
+ fromConfig: config,
2011
+ fromResources: resources
2012
+ });
2013
+ const cleanedMirrorDirs = await removeMirrorDirs(
2014
+ projectRoot,
2015
+ config?.packages?.skills?.scope,
2016
+ config?.packages?.skills?.ides,
2017
+ skillNames
2018
+ );
1923
2019
  if (installedManifest && pkg) {
1924
2020
  installedManifest.installed = installedManifest.installed.filter(
1925
2021
  (p) => p.package !== SKILLS_PACKAGE3
@@ -1930,7 +2026,12 @@ async function runFullUninstall(args) {
1930
2026
  await writeProjectConfig(projectRoot, config);
1931
2027
  logger.success(`Uninstalled ${SKILLS_PACKAGE3}`);
1932
2028
  logger.info(` Removed: ${removed.length} files`);
1933
- logger.info(` Source: ${path12.relative(projectRoot, skillsRoot)} (cleaned)`);
2029
+ logger.info(` Source: ${path13.relative(projectRoot, skillsRoot)} (cleaned)`);
2030
+ if (cleanedMirrorDirs.length > 0) {
2031
+ logger.info(
2032
+ ` Mirrors: ${cleanedMirrorDirs.map((d) => path13.relative(projectRoot, d)).join(", ")} (cleaned)`
2033
+ );
2034
+ }
1934
2035
  }
1935
2036
  async function runPartialUninstall(args) {
1936
2037
  const { projectRoot, installedManifest, pkg, resources, ids, opts } = args;
@@ -1972,6 +2073,13 @@ async function runPartialUninstall(args) {
1972
2073
  logger.warn(`Failed to remove ${dir}: ${err.message}`);
1973
2074
  }
1974
2075
  }
2076
+ const config = await readProjectConfig(projectRoot);
2077
+ const cleanedMirrorDirs = await removeMirrorDirs(
2078
+ projectRoot,
2079
+ config?.packages?.skills?.scope,
2080
+ config?.packages?.skills?.ides,
2081
+ matched
2082
+ );
1975
2083
  const lock = await readSkillsLock(projectRoot);
1976
2084
  if (lock) {
1977
2085
  for (const id of matched) delete lock.skills[id];
@@ -1985,10 +2093,69 @@ async function runPartialUninstall(args) {
1985
2093
  `Removed ${matched.length} skill(s): ${matched.join(", ")}`
1986
2094
  );
1987
2095
  logger.info(` Files: ${removed.length}`);
2096
+ if (cleanedMirrorDirs.length > 0) {
2097
+ logger.info(
2098
+ ` Mirrors: ${cleanedMirrorDirs.map((d) => path13.relative(projectRoot, d)).join(", ")} (cleaned)`
2099
+ );
2100
+ }
1988
2101
  if (missing.length > 0) {
1989
2102
  logger.info(` Skipped: ${missing.join(", ")} (not installed)`);
1990
2103
  }
1991
2104
  }
2105
+ async function listSkillSourceNames(skillsRoot) {
2106
+ try {
2107
+ const entries = await fs10.readdir(skillsRoot, { withFileTypes: true });
2108
+ return entries.filter((e) => e.isDirectory()).map((e) => e.name);
2109
+ } catch {
2110
+ return [];
2111
+ }
2112
+ }
2113
+ function collectSkillNames(args) {
2114
+ const set = new Set(args.fromSources);
2115
+ for (const r of args.fromResources) {
2116
+ set.add(r.id.split(":")[0]);
2117
+ }
2118
+ return [...set];
2119
+ }
2120
+ async function removeMirrorDirs(projectRoot, scope, ides, skillNames) {
2121
+ if (skillNames.length === 0) return [];
2122
+ const targetIdes = ides && ides.length > 0 ? ides.filter((i) => i === "qoder" || i === "claude") : [...ALL_IDE_KINDS];
2123
+ const targetScope = scope === "global" ? "global" : "project";
2124
+ const removed = [];
2125
+ for (const ide of targetIdes) {
2126
+ const adapter = getAdapter(ide);
2127
+ for (const name of skillNames) {
2128
+ const dir = adapter.getSkillTargetDir(name, targetScope, projectRoot);
2129
+ let existed = true;
2130
+ try {
2131
+ await fs10.access(dir);
2132
+ } catch {
2133
+ existed = false;
2134
+ }
2135
+ if (!existed) continue;
2136
+ try {
2137
+ await fs10.rm(dir, { recursive: true, force: true });
2138
+ removed.push(dir);
2139
+ logger.debug(`Removed mirror dir ${dir}`);
2140
+ } catch (err) {
2141
+ logger.warn(`Failed to remove ${dir}: ${err.message}`);
2142
+ }
2143
+ }
2144
+ if (removed.length > 0) {
2145
+ const skillsParent = path13.dirname(
2146
+ adapter.getSkillTargetDir("placeholder", targetScope, projectRoot)
2147
+ );
2148
+ try {
2149
+ const remaining = await fs10.readdir(skillsParent);
2150
+ if (remaining.length === 0) {
2151
+ await fs10.rmdir(skillsParent);
2152
+ }
2153
+ } catch {
2154
+ }
2155
+ }
2156
+ }
2157
+ return removed;
2158
+ }
1992
2159
  function skillIdOf(r) {
1993
2160
  return r.id.split(":")[0];
1994
2161
  }
@@ -2010,7 +2177,7 @@ function dedupe(values) {
2010
2177
  import { Command as Command11 } from "commander";
2011
2178
 
2012
2179
  // src/core/skills-sync.ts
2013
- import * as path13 from "path";
2180
+ import * as path14 from "path";
2014
2181
  import * as fs11 from "fs/promises";
2015
2182
  import { createRequire as createRequire4 } from "module";
2016
2183
  import { loadSkillsPackageManifest as loadSkillsPackageManifest2 } from "@teamix-evo/registry";
@@ -2018,7 +2185,7 @@ var require5 = createRequire4(import.meta.url);
2018
2185
  async function readSkillMetaFromUpstream(skillId) {
2019
2186
  try {
2020
2187
  const pkgJson = require5.resolve("@teamix-evo/skills/package.json");
2021
- const packageRoot = path13.dirname(pkgJson);
2188
+ const packageRoot = path14.dirname(pkgJson);
2022
2189
  const manifest = await loadSkillsPackageManifest2(packageRoot);
2023
2190
  const entry = manifest.skills.find((s) => s.id === skillId);
2024
2191
  if (!entry) return null;
@@ -2192,7 +2359,7 @@ function parseScope2(input) {
2192
2359
  import { Command as Command12 } from "commander";
2193
2360
 
2194
2361
  // src/core/skills-doctor.ts
2195
- import * as path14 from "path";
2362
+ import * as path15 from "path";
2196
2363
  import * as fs12 from "fs/promises";
2197
2364
  async function runSkillsDoctor(options) {
2198
2365
  const { projectRoot } = options;
@@ -2215,7 +2382,7 @@ async function runSkillsDoctor(options) {
2215
2382
  const sourceFiles = await walkDir(sourceDir);
2216
2383
  const sourceContents = /* @__PURE__ */ new Map();
2217
2384
  for (const f of sourceFiles) {
2218
- const rel2 = path14.relative(sourceDir, f);
2385
+ const rel2 = path15.relative(sourceDir, f);
2219
2386
  sourceContents.set(rel2, await fs12.readFile(f, "utf-8"));
2220
2387
  }
2221
2388
  for (const ide of entry.mirroredTo) {
@@ -2237,7 +2404,7 @@ async function runSkillsDoctor(options) {
2237
2404
  continue;
2238
2405
  }
2239
2406
  for (const [rel2, sourceContent] of sourceContents.entries()) {
2240
- const mirrorFile = path14.join(mirrorDir, rel2);
2407
+ const mirrorFile = path15.join(mirrorDir, rel2);
2241
2408
  if (!await fileExists(mirrorFile)) {
2242
2409
  findings.push({
2243
2410
  kind: "missing-mirror",
@@ -2335,35 +2502,6 @@ import { Command as Command14 } from "commander";
2335
2502
  import * as prompts4 from "@clack/prompts";
2336
2503
 
2337
2504
  // 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
- }
2367
2505
  var DEFAULT_UI_ALIASES = {
2368
2506
  components: "src/components/ui",
2369
2507
  hooks: "src/hooks",
@@ -2407,14 +2545,13 @@ async function runUiInit(options) {
2407
2545
  rsc
2408
2546
  };
2409
2547
  await writeProjectConfig(projectRoot, config);
2410
- const preferencesCss = await deployPreferencesCss(projectRoot);
2548
+ await ensureMcpJson(projectRoot);
2411
2549
  return {
2412
2550
  status: "installed",
2413
2551
  aliases,
2414
2552
  iconLibrary,
2415
2553
  tsx,
2416
- rsc,
2417
- preferencesCss
2554
+ rsc
2418
2555
  };
2419
2556
  }
2420
2557
 
@@ -2429,6 +2566,12 @@ var initCommand2 = new Command14("init").description(
2429
2566
  try {
2430
2567
  const ide = detectIde();
2431
2568
  const projectRoot = ide.getProjectRoot();
2569
+ if (!hasPackageJson(projectRoot)) {
2570
+ logger.error(
2571
+ "No package.json found in current directory. Please run this command in a valid project root."
2572
+ );
2573
+ process.exit(1);
2574
+ }
2432
2575
  const cfg = await resolveConfig(opts);
2433
2576
  const result = await runUiInit({
2434
2577
  projectRoot,
@@ -2451,14 +2594,6 @@ var initCommand2 = new Command14("init").description(
2451
2594
  logger.info(` lib: ${result.aliases.lib}`);
2452
2595
  logger.info(` iconLibrary: ${result.iconLibrary}`);
2453
2596
  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
- }
2462
2597
  logger.info("");
2463
2598
  logger.info("Next: `npx teamix-evo ui add button`");
2464
2599
  } catch (err) {
@@ -2538,12 +2673,12 @@ import { Command as Command15 } from "commander";
2538
2673
 
2539
2674
  // src/core/ui-client.ts
2540
2675
  import * as path16 from "path";
2541
- import * as fs14 from "fs/promises";
2542
- import { createRequire as createRequire6 } from "module";
2676
+ import * as fs13 from "fs/promises";
2677
+ import { createRequire as createRequire5 } from "module";
2543
2678
  import { loadUiPackageManifest } from "@teamix-evo/registry";
2544
- var require7 = createRequire6(import.meta.url);
2679
+ var require6 = createRequire5(import.meta.url);
2545
2680
  function resolvePackageRoot2(packageName) {
2546
- const pkgJsonPath = require7.resolve(`${packageName}/package.json`);
2681
+ const pkgJsonPath = require6.resolve(`${packageName}/package.json`);
2547
2682
  return path16.dirname(pkgJsonPath);
2548
2683
  }
2549
2684
  async function loadUiData(packageName) {
@@ -2553,7 +2688,7 @@ async function loadUiData(packageName) {
2553
2688
  let data = {};
2554
2689
  const dataPath = path16.join(packageRoot, "_data.json");
2555
2690
  try {
2556
- const raw = await fs14.readFile(dataPath, "utf-8");
2691
+ const raw = await fs13.readFile(dataPath, "utf-8");
2557
2692
  data = JSON.parse(raw);
2558
2693
  } catch (err) {
2559
2694
  if (err.code !== "ENOENT") {
@@ -2566,7 +2701,7 @@ async function loadUiData(packageName) {
2566
2701
 
2567
2702
  // src/core/ui-installer.ts
2568
2703
  import * as path17 from "path";
2569
- import * as fs15 from "fs/promises";
2704
+ import * as fs14 from "fs/promises";
2570
2705
  import { resolveUiEntryOrder } from "@teamix-evo/registry";
2571
2706
 
2572
2707
  // src/utils/transform-imports.ts
@@ -2579,13 +2714,13 @@ var SOURCE_ROOT_TO_ALIAS_KEY = {
2579
2714
  function rewriteImports(source, aliases) {
2580
2715
  return source.replace(
2581
2716
  /(['"])@\/([a-z][a-z0-9-]*)(\/[^'"]*)?\1/g,
2582
- (full, quote, root, rest) => {
2717
+ (full, quote2, root, rest) => {
2583
2718
  const aliasKey = SOURCE_ROOT_TO_ALIAS_KEY[root];
2584
2719
  if (!aliasKey) return full;
2585
2720
  const alias = aliases[aliasKey];
2586
2721
  const normalized = aliasToImportPath(alias);
2587
2722
  const flatRest = flattenRestPath(rest);
2588
- return `${quote}${normalized}${flatRest}${quote}`;
2723
+ return `${quote2}${normalized}${flatRest}${quote2}`;
2589
2724
  }
2590
2725
  );
2591
2726
  }
@@ -2640,7 +2775,7 @@ async function installUiEntries(options) {
2640
2775
  }
2641
2776
  const rootForEntry = entryPackageRoot?.get(entry.id) ?? packageRoot;
2642
2777
  const sourceAbs = path17.resolve(rootForEntry, file.source);
2643
- const raw = await fs15.readFile(sourceAbs, "utf-8");
2778
+ const raw = await fs14.readFile(sourceAbs, "utf-8");
2644
2779
  const transformed = rewriteImports(raw, aliases);
2645
2780
  await writeFileSafe(targetAbs, transformed);
2646
2781
  written++;
@@ -2677,7 +2812,7 @@ function rel(projectRoot, abs) {
2677
2812
  // src/core/ui-add.ts
2678
2813
  var DEFAULT_UI_PACKAGE = "@teamix-evo/ui";
2679
2814
  async function runUiAdd(options) {
2680
- const { projectRoot, ids, overwrite } = options;
2815
+ const { projectRoot, ids, overwrite, includeDeprecated } = options;
2681
2816
  const packageName = options.packageName ?? DEFAULT_UI_PACKAGE;
2682
2817
  if (ids.length === 0) {
2683
2818
  throw new Error("At least one entry id must be provided.");
@@ -2690,7 +2825,21 @@ async function runUiAdd(options) {
2690
2825
  );
2691
2826
  }
2692
2827
  const { manifest, packageRoot } = await loadUiData(packageName);
2693
- const knownIds = new Set(manifest.entries.map((e) => e.id));
2828
+ const archived = manifest.deprecatedEntries ?? [];
2829
+ const archivedIds = new Set(archived.map((e) => e.id));
2830
+ const activeIds = new Set(manifest.entries.map((e) => e.id));
2831
+ const requestedDeprecated = ids.filter((id) => archivedIds.has(id));
2832
+ if (requestedDeprecated.length > 0 && !includeDeprecated) {
2833
+ const list = requestedDeprecated.map((s) => `"${s}"`).join(", ");
2834
+ throw new Error(
2835
+ `Refusing to install deprecated entr${requestedDeprecated.length === 1 ? "y" : "ies"} ${list}. These entries are archived and not part of the active distribution (ADR 0028). Pass \`--include-deprecated\` to override (e.g. for migration tooling).`
2836
+ );
2837
+ }
2838
+ const effectiveManifest = includeDeprecated ? { ...manifest, entries: [...manifest.entries, ...archived] } : manifest;
2839
+ const knownIds = /* @__PURE__ */ new Set([
2840
+ ...activeIds,
2841
+ ...includeDeprecated ? archivedIds : []
2842
+ ]);
2694
2843
  const unknown = ids.filter((id) => !knownIds.has(id));
2695
2844
  if (unknown.length > 0) {
2696
2845
  throw new Error(
@@ -2699,7 +2848,7 @@ async function runUiAdd(options) {
2699
2848
  }
2700
2849
  const result = await installUiEntries({
2701
2850
  projectRoot,
2702
- manifest,
2851
+ manifest: effectiveManifest,
2703
2852
  packageRoot,
2704
2853
  aliases: uiCfg.aliases,
2705
2854
  requested: ids,
@@ -2747,40 +2896,48 @@ function mergeResources(prior, next) {
2747
2896
  // src/commands/ui/add.ts
2748
2897
  var addCommand2 = new Command15("add").description(
2749
2898
  "\u5B89\u88C5\u4E00\u4E2A\u6216\u591A\u4E2A ui entry\uFF08\u6309 id\uFF0C\u81EA\u52A8\u5C55\u5F00 registryDependencies\uFF09"
2750
- ).argument("<ids...>", 'entry id \u5217\u8868\uFF0C\u5982 "button" "dialog"').option("--overwrite", "\u5373\u4F7F\u76EE\u6807\u6587\u4EF6\u5DF2\u5B58\u5728\u4E5F\u8986\u76D6\uFF08\u7ED5\u8FC7 frozen \u8DF3\u8FC7\uFF09").action(async (ids, opts) => {
2751
- try {
2752
- const ide = detectIde();
2753
- const projectRoot = ide.getProjectRoot();
2754
- logger.info(`Installing entries: ${ids.join(", ")}`);
2755
- const result = await runUiAdd({
2756
- projectRoot,
2757
- ids,
2758
- overwrite: opts.overwrite
2759
- });
2760
- logger.success(
2761
- `UI add complete: ${result.written} written, ${result.skipped} skipped.`
2762
- );
2763
- logger.info("");
2764
- logger.info(`Resolved order: ${result.orderedIds.join(" \u2192 ")}`);
2765
- const npmDeps = Object.entries(result.npmDependencies);
2766
- if (npmDeps.length > 0) {
2899
+ ).argument("<ids...>", 'entry id \u5217\u8868\uFF0C\u5982 "button" "dialog"').option("--overwrite", "\u5373\u4F7F\u76EE\u6807\u6587\u4EF6\u5DF2\u5B58\u5728\u4E5F\u8986\u76D6\uFF08\u7ED5\u8FC7 frozen \u8DF3\u8FC7\uFF09").option(
2900
+ "--include-deprecated",
2901
+ "\u5141\u8BB8\u5B89\u88C5\u5DF2\u5F52\u6863\u7684 deprecated entry\uFF08\u4EC5\u8FC1\u79FB / \u5BA1\u8BA1\u573A\u666F\uFF0CADR 0028\uFF09"
2902
+ ).action(
2903
+ async (ids, opts) => {
2904
+ try {
2905
+ const ide = detectIde();
2906
+ const projectRoot = ide.getProjectRoot();
2907
+ logger.info(`Installing entries: ${ids.join(", ")}`);
2908
+ const result = await runUiAdd({
2909
+ projectRoot,
2910
+ ids,
2911
+ overwrite: opts.overwrite,
2912
+ includeDeprecated: opts.includeDeprecated
2913
+ });
2914
+ logger.success(
2915
+ `UI add complete: ${result.written} written, ${result.skipped} skipped.`
2916
+ );
2767
2917
  logger.info("");
2768
- logger.info("Install npm dependencies in your project:");
2769
- const installCmd = npmDeps.map(([name, range]) => `${name}@${range}`).join(" ");
2770
- logger.info(` pnpm add ${installCmd}`);
2771
- logger.info(` # or: npm install ${installCmd}`);
2772
- }
2773
- } catch (err) {
2774
- const message = err.message;
2775
- if (message.startsWith("UI not initialized")) {
2776
- logger.error("UI not initialized. Run `npx teamix-evo ui init` first.");
2777
- } else {
2778
- logger.error(`Failed to add ui entries: ${message}`);
2918
+ logger.info(`Resolved order: ${result.orderedIds.join(" \u2192 ")}`);
2919
+ const npmDeps = Object.entries(result.npmDependencies);
2920
+ if (npmDeps.length > 0) {
2921
+ logger.info("");
2922
+ logger.info("Install npm dependencies in your project:");
2923
+ const installCmd = npmDeps.map(([name, range]) => `${name}@${range}`).join(" ");
2924
+ logger.info(` pnpm add ${installCmd}`);
2925
+ logger.info(` # or: npm install ${installCmd}`);
2926
+ }
2927
+ } catch (err) {
2928
+ const message = err.message;
2929
+ if (message.startsWith("UI not initialized")) {
2930
+ logger.error(
2931
+ "UI not initialized. Run `npx teamix-evo ui init` first."
2932
+ );
2933
+ } else {
2934
+ logger.error(`Failed to add ui entries: ${message}`);
2935
+ }
2936
+ logger.debug(err.stack ?? "");
2937
+ process.exitCode = 1;
2779
2938
  }
2780
- logger.debug(err.stack ?? "");
2781
- process.exitCode = 1;
2782
2939
  }
2783
- });
2940
+ );
2784
2941
 
2785
2942
  // src/commands/ui/list.ts
2786
2943
  import { Command as Command16 } from "commander";
@@ -2788,7 +2945,7 @@ import { Command as Command16 } from "commander";
2788
2945
  // src/core/ui-list.ts
2789
2946
  var DEFAULT_UI_PACKAGE2 = "@teamix-evo/ui";
2790
2947
  async function runUiList(options) {
2791
- const { projectRoot, installedOnly } = options;
2948
+ const { projectRoot, installedOnly, includeDeprecated } = options;
2792
2949
  const packageName = options.packageName ?? DEFAULT_UI_PACKAGE2;
2793
2950
  const { manifest } = await loadUiData(packageName);
2794
2951
  const installedManifest = await readInstalledManifest(projectRoot);
@@ -2800,65 +2957,77 @@ async function runUiList(options) {
2800
2957
  const colon = r.id.indexOf(":");
2801
2958
  installedIds.add(colon >= 0 ? r.id.slice(0, colon) : r.id);
2802
2959
  }
2803
- const entries = manifest.entries.filter((e) => !installedOnly || installedIds.has(e.id)).map((e) => ({
2804
- id: e.id,
2805
- type: e.type,
2806
- description: e.description,
2807
- installed: installedIds.has(e.id)
2960
+ const archived = manifest.deprecatedEntries ?? [];
2961
+ const pool = includeDeprecated ? [
2962
+ ...manifest.entries.map((e) => ({ entry: e, deprecated: false })),
2963
+ ...archived.map((e) => ({ entry: e, deprecated: true }))
2964
+ ] : manifest.entries.map((e) => ({ entry: e, deprecated: false }));
2965
+ const entries = pool.filter(({ entry }) => !installedOnly || installedIds.has(entry.id)).map(({ entry, deprecated }) => ({
2966
+ id: entry.id,
2967
+ type: entry.type,
2968
+ description: entry.description,
2969
+ installed: installedIds.has(entry.id),
2970
+ deprecated
2808
2971
  }));
2809
2972
  return {
2810
2973
  packageName,
2811
- total: manifest.entries.length,
2974
+ total: manifest.entries.length + (includeDeprecated ? archived.length : 0),
2812
2975
  installedCount: installedIds.size,
2813
2976
  entries
2814
2977
  };
2815
2978
  }
2816
2979
 
2817
2980
  // src/commands/ui/list.ts
2818
- var listCommand3 = new Command16("list").description("\u5217\u51FA @teamix-evo/ui \u7684\u6240\u6709 entry \u53CA\u5DF2\u5B89\u88C5\u72B6\u6001").option("--installed", "\u4EC5\u5C55\u793A\u5DF2\u5B89\u88C5\u7684 entry").action(async (opts) => {
2819
- try {
2820
- const ide = detectIde();
2821
- const projectRoot = ide.getProjectRoot();
2822
- const { entries, total, installedCount } = await runUiList({
2823
- projectRoot,
2824
- installedOnly: opts.installed
2825
- });
2826
- if (entries.length === 0) {
2981
+ var listCommand3 = new Command16("list").description("\u5217\u51FA @teamix-evo/ui \u7684\u6240\u6709 entry \u53CA\u5DF2\u5B89\u88C5\u72B6\u6001").option("--installed", "\u4EC5\u5C55\u793A\u5DF2\u5B89\u88C5\u7684 entry").option(
2982
+ "--include-deprecated",
2983
+ "\u540C\u65F6\u5217\u51FA\u5DF2\u5F52\u6863\u7684 deprecated entry\uFF08\u9ED8\u8BA4\u9690\u85CF\uFF0CADR 0028\uFF09"
2984
+ ).action(
2985
+ async (opts) => {
2986
+ try {
2987
+ const ide = detectIde();
2988
+ const projectRoot = ide.getProjectRoot();
2989
+ const { entries, total, installedCount } = await runUiList({
2990
+ projectRoot,
2991
+ installedOnly: opts.installed,
2992
+ includeDeprecated: opts.includeDeprecated
2993
+ });
2994
+ if (entries.length === 0) {
2995
+ logger.info(
2996
+ opts.installed ? "No ui entries installed." : "No ui entries available."
2997
+ );
2998
+ return;
2999
+ }
3000
+ const idWidth = Math.max(...entries.map((e) => e.id.length), 4);
3001
+ const typeWidth = Math.max(...entries.map((e) => e.type.length), 4);
2827
3002
  logger.info(
2828
- opts.installed ? "No ui entries installed." : "No ui entries available."
3003
+ `${"ID".padEnd(idWidth)} ${"TYPE".padEnd(
3004
+ typeWidth
3005
+ )} STATUS DESCRIPTION`
2829
3006
  );
2830
- return;
2831
- }
2832
- const idWidth = Math.max(...entries.map((e) => e.id.length), 4);
2833
- const typeWidth = Math.max(...entries.map((e) => e.type.length), 4);
2834
- logger.info(
2835
- `${"ID".padEnd(idWidth)} ${"TYPE".padEnd(
2836
- typeWidth
2837
- )} STATUS DESCRIPTION`
2838
- );
2839
- logger.info(
2840
- `${"\u2500".repeat(idWidth)} ${"\u2500".repeat(
2841
- typeWidth
2842
- )} \u2500\u2500\u2500\u2500\u2500\u2500\u2500 \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500`
2843
- );
2844
- for (const e of entries) {
2845
- const status = e.installed ? "INSTALLED" : "\u2013";
2846
3007
  logger.info(
2847
- `${e.id.padEnd(idWidth)} ${e.type.padEnd(
3008
+ `${"\u2500".repeat(idWidth)} ${"\u2500".repeat(
2848
3009
  typeWidth
2849
- )} ${status.padEnd(7)} ${e.description}`
3010
+ )} \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500`
2850
3011
  );
3012
+ for (const e of entries) {
3013
+ const status = e.deprecated ? e.installed ? "INST/DEPR" : "DEPRECATED" : e.installed ? "INSTALLED" : "\u2013";
3014
+ logger.info(
3015
+ `${e.id.padEnd(idWidth)} ${e.type.padEnd(
3016
+ typeWidth
3017
+ )} ${status.padEnd(10)} ${e.description}`
3018
+ );
3019
+ }
3020
+ logger.info("");
3021
+ logger.info(
3022
+ `Total: ${total} entr${total === 1 ? "y" : "ies"}, ${installedCount} installed.`
3023
+ );
3024
+ } catch (err) {
3025
+ logger.error(`Failed to list ui entries: ${err.message}`);
3026
+ logger.debug(err.stack ?? "");
3027
+ process.exitCode = 1;
2851
3028
  }
2852
- logger.info("");
2853
- logger.info(
2854
- `Total: ${total} entr${total === 1 ? "y" : "ies"}, ${installedCount} installed.`
2855
- );
2856
- } catch (err) {
2857
- logger.error(`Failed to list ui entries: ${err.message}`);
2858
- logger.debug(err.stack ?? "");
2859
- process.exitCode = 1;
2860
3029
  }
2861
- });
3030
+ );
2862
3031
 
2863
3032
  // src/commands/ui/index.ts
2864
3033
  var uiCommand = new Command17("ui").description(
@@ -2876,15 +3045,15 @@ import { Command as Command18 } from "commander";
2876
3045
 
2877
3046
  // src/core/variant-ui-add.ts
2878
3047
  import * as path18 from "path";
2879
- import { createRequire as createRequire7 } from "module";
3048
+ import { createRequire as createRequire6 } from "module";
2880
3049
  import {
2881
3050
  loadUiPackageManifest as loadUiPackageManifest2,
2882
3051
  loadVariantUiPackageCatalog,
2883
3052
  loadVariantUiPackageManifest
2884
3053
  } from "@teamix-evo/registry";
2885
- var require8 = createRequire7(import.meta.url);
3054
+ var require7 = createRequire6(import.meta.url);
2886
3055
  function resolvePackageRoot3(packageName) {
2887
- const pkgJsonPath = require8.resolve(`${packageName}/package.json`);
3056
+ const pkgJsonPath = require7.resolve(`${packageName}/package.json`);
2888
3057
  return path18.dirname(pkgJsonPath);
2889
3058
  }
2890
3059
  async function runVariantUiAdd(packageName, options) {
@@ -3055,6 +3224,12 @@ var addCommand3 = new Command18("add").description(
3055
3224
  }
3056
3225
  const ide = detectIde();
3057
3226
  const projectRoot = ide.getProjectRoot();
3227
+ const projectVariant = await readTokensVariant(projectRoot);
3228
+ if (projectVariant && projectVariant !== opts.variant) {
3229
+ throw new Error(
3230
+ `Variant mismatch \u2014 project is locked to "${projectVariant}" but you requested "${opts.variant}". Fix: run \`teamix-evo tokens uninstall -y\` then \`teamix-evo tokens init ${opts.variant}\` to switch, or re-run with \`--variant ${projectVariant}\` to install for the current variant.`
3231
+ );
3232
+ }
3058
3233
  logger.info(
3059
3234
  `Installing biz-ui entries from variant "${opts.variant}": ${ids.join(", ")}`
3060
3235
  );
@@ -3159,6 +3334,12 @@ var addCommand4 = new Command22("add").description(
3159
3334
  }
3160
3335
  const ide = detectIde();
3161
3336
  const projectRoot = ide.getProjectRoot();
3337
+ const projectVariant = await readTokensVariant(projectRoot);
3338
+ if (projectVariant && projectVariant !== opts.variant) {
3339
+ throw new Error(
3340
+ `Variant mismatch \u2014 project is locked to "${projectVariant}" but you requested "${opts.variant}". Fix: run \`teamix-evo tokens uninstall -y\` then \`teamix-evo tokens init ${opts.variant}\` to switch, or re-run with \`--variant ${projectVariant}\` to install for the current variant.`
3341
+ );
3342
+ }
3162
3343
  logger.info(
3163
3344
  `Installing templates from variant "${opts.variant}": ${ids.join(", ")}`
3164
3345
  );
@@ -3247,7 +3428,7 @@ templatesCommand.addCommand(listCommand5);
3247
3428
  templatesCommand.addCommand(listVariantsCommand3);
3248
3429
 
3249
3430
  // src/commands/logs/index.ts
3250
- import { Command as Command27 } from "commander";
3431
+ import { Command as Command28 } from "commander";
3251
3432
 
3252
3433
  // src/commands/logs/analyze.ts
3253
3434
  import { Command as Command26 } from "commander";
@@ -3433,16 +3614,227 @@ function parseIntOrUndef(v) {
3433
3614
  return Number.isFinite(n) && n > 0 ? n : void 0;
3434
3615
  }
3435
3616
 
3617
+ // src/commands/logs/trace.ts
3618
+ import { Command as Command27 } from "commander";
3619
+ import { readFileSync as readFileSync2, readdirSync as readdirSync2, existsSync as existsSync3, statSync as statSync2 } from "fs";
3620
+ import { resolve as resolve4, join as join18 } from "path";
3621
+ var DATE_DIR_RE2 = /^\d{4}-\d{2}-\d{2}$/;
3622
+ var logsTraceCommand = new Command27("trace").description(
3623
+ "\u6309\u4F1A\u8BDD\u8FD8\u539F AI \u8C03\u7528\u94FE\u8DEF:\u4ECE\u7528\u6237 prompt \u8D77\u59CB,\u4E32\u8054\u540E\u7EED PreToolUse/PostToolUse \u76F4\u5230\u4E0B\u4E00\u4E2A prompt \u6216 Stop"
3624
+ ).option("--prompt <keyword>", "\u6309\u7528\u6237\u8F93\u5165\u5173\u952E\u5B57\u8FC7\u6EE4 (\u5B50\u4E32\u5339\u914D,\u4E0D\u533A\u5206\u5927\u5C0F\u5199)").option("--session <id>", "\u6307\u5B9A\u4F1A\u8BDD ID (\u524D\u7F00\u5339\u914D)").option("--days <n>", "\u53EA\u770B\u6700\u8FD1 N \u5929\u7684\u76EE\u5F55 (\u9ED8\u8BA4 7)", "7").option("--dir <path>", "log \u76EE\u5F55 (\u9ED8\u8BA4 <project>/.log/ai)").option("--json", "\u4EE5 JSON \u8F93\u51FA (CI/\u5DE5\u5177\u53CB\u597D)").action((opts) => {
3625
+ const baseDir = resolve4(opts.dir ?? join18(process.cwd(), ".log", "ai"));
3626
+ if (!existsSync3(baseDir)) {
3627
+ logger.warn(`No log directory at ${baseDir}.`);
3628
+ logger.info(
3629
+ "\u8FD0\u884C vibe-logger hook \u89E6\u53D1\u540E\u4F1A\u5728\u6B64\u76EE\u5F55\u751F\u6210 JSONL \u2014 \u89C1 .claude/scripts/vibe-logger.mjs"
3630
+ );
3631
+ return;
3632
+ }
3633
+ const dayLimit = parseIntOrUndef2(opts.days) ?? 7;
3634
+ const records = readRecords(baseDir, dayLimit);
3635
+ const report = buildTrace(records, {
3636
+ prompt: opts.prompt,
3637
+ session: opts.session
3638
+ });
3639
+ if (opts.json) {
3640
+ process.stdout.write(JSON.stringify(report, null, 2) + "\n");
3641
+ return;
3642
+ }
3643
+ printTrace(baseDir, report);
3644
+ });
3645
+ function readRecords(baseDir, dayLimit) {
3646
+ const dayDirs = readdirSync2(baseDir, { withFileTypes: true }).filter((e) => e.isDirectory() && DATE_DIR_RE2.test(e.name)).map((e) => e.name).sort().reverse();
3647
+ const selected = dayDirs.slice(0, dayLimit);
3648
+ const records = [];
3649
+ for (const day of selected) {
3650
+ const dayPath = join18(baseDir, day);
3651
+ let entries;
3652
+ try {
3653
+ entries = readdirSync2(dayPath);
3654
+ } catch {
3655
+ continue;
3656
+ }
3657
+ for (const entry of entries) {
3658
+ if (!entry.endsWith(".jsonl")) continue;
3659
+ const fp = join18(dayPath, entry);
3660
+ try {
3661
+ if (!statSync2(fp).isFile()) continue;
3662
+ } catch {
3663
+ continue;
3664
+ }
3665
+ const text2 = readFileSync2(fp, "utf8");
3666
+ for (const line of text2.split("\n")) {
3667
+ if (!line.trim()) continue;
3668
+ try {
3669
+ records.push(JSON.parse(line));
3670
+ } catch {
3671
+ }
3672
+ }
3673
+ }
3674
+ }
3675
+ return records;
3676
+ }
3677
+ function buildTrace(records, filter = {}) {
3678
+ const groups = /* @__PURE__ */ new Map();
3679
+ for (const r of records) {
3680
+ if (!r.session) continue;
3681
+ const arr = groups.get(r.session) ?? [];
3682
+ arr.push(r);
3683
+ groups.set(r.session, arr);
3684
+ }
3685
+ const promptNeedle = filter.prompt?.toLowerCase();
3686
+ const sessionPrefix = filter.session;
3687
+ const sessions = [];
3688
+ for (const [sessionId, items] of groups.entries()) {
3689
+ if (sessionPrefix && !sessionId.startsWith(sessionPrefix)) continue;
3690
+ items.sort((a, b) => a.ts < b.ts ? -1 : a.ts > b.ts ? 1 : 0);
3691
+ const chains = [];
3692
+ let current = null;
3693
+ let pending = /* @__PURE__ */ new Map();
3694
+ const closeCurrent = () => {
3695
+ if (current) chains.push(current);
3696
+ current = null;
3697
+ pending = /* @__PURE__ */ new Map();
3698
+ };
3699
+ for (const r of items) {
3700
+ if (r.event === "UserPromptSubmit") {
3701
+ if (current) closeCurrent();
3702
+ current = { prompt: r.prompt ?? "", promptTs: r.ts, steps: [] };
3703
+ pending = /* @__PURE__ */ new Map();
3704
+ continue;
3705
+ }
3706
+ if (r.event === "Stop") {
3707
+ if (current) closeCurrent();
3708
+ continue;
3709
+ }
3710
+ if (!current) continue;
3711
+ if (r.event === "PreToolUse") {
3712
+ const idx = current.steps.length;
3713
+ current.steps.push({
3714
+ ts: r.ts,
3715
+ event: "PreToolUse",
3716
+ tool: r.tool,
3717
+ file: r.file,
3718
+ tags: r.tags,
3719
+ duration_ms: r.duration_ms,
3720
+ error: r.error ?? false
3721
+ });
3722
+ if (r.tool) {
3723
+ const list = pending.get(r.tool) ?? [];
3724
+ list.push(idx);
3725
+ pending.set(r.tool, list);
3726
+ }
3727
+ } else if (r.event === "PostToolUse") {
3728
+ const list = r.tool ? pending.get(r.tool) : void 0;
3729
+ if (list && list.length > 0) {
3730
+ const idx = list.shift();
3731
+ if (list.length === 0 && r.tool) pending.delete(r.tool);
3732
+ const step = current.steps[idx];
3733
+ if (r.duration_ms !== void 0) step.duration_ms = r.duration_ms;
3734
+ if (r.error !== void 0) step.error = r.error;
3735
+ if (!step.file && r.file) step.file = r.file;
3736
+ if ((!step.tags || step.tags.length === 0) && r.tags)
3737
+ step.tags = r.tags;
3738
+ } else {
3739
+ current.steps.push({
3740
+ ts: r.ts,
3741
+ event: "PostToolUse",
3742
+ tool: r.tool,
3743
+ file: r.file,
3744
+ tags: r.tags,
3745
+ duration_ms: r.duration_ms,
3746
+ error: r.error ?? false
3747
+ });
3748
+ }
3749
+ }
3750
+ }
3751
+ if (current) closeCurrent();
3752
+ const filteredChains = promptNeedle ? chains.filter((c) => c.prompt.toLowerCase().includes(promptNeedle)) : chains;
3753
+ if (filteredChains.length === 0) continue;
3754
+ sessions.push({
3755
+ id: sessionId,
3756
+ agent: items[0]?.agent ?? "unknown",
3757
+ chains: filteredChains
3758
+ });
3759
+ }
3760
+ sessions.sort((a, b) => {
3761
+ const ta = a.chains[0]?.promptTs ?? "";
3762
+ const tb = b.chains[0]?.promptTs ?? "";
3763
+ return tb.localeCompare(ta);
3764
+ });
3765
+ return { sessions };
3766
+ }
3767
+ function printTrace(baseDir, r) {
3768
+ logger.info(`vibe-logger \u94FE\u8DEF\u8FFD\u8E2A`);
3769
+ logger.info(` \u76EE\u5F55: ${baseDir}`);
3770
+ logger.info(` \u4F1A\u8BDD: ${r.sessions.length}`);
3771
+ logger.info("");
3772
+ if (r.sessions.length === 0) {
3773
+ logger.info("\u672A\u627E\u5230\u5339\u914D\u7684\u4F1A\u8BDD/\u63D0\u793A\u3002");
3774
+ return;
3775
+ }
3776
+ for (const s of r.sessions) {
3777
+ logger.info(`\u2501\u2501\u2501 Session: ${s.id} (${s.agent}) \u2501\u2501\u2501`);
3778
+ logger.info("");
3779
+ for (const c of s.chains) {
3780
+ logger.info(
3781
+ `[${formatTime(c.promptTs)}] \u{1F4AC} Prompt: ${quote(truncate(c.prompt, 200))}`
3782
+ );
3783
+ for (const step of c.steps) {
3784
+ logger.info(` ${formatStep(step)}`);
3785
+ }
3786
+ logger.info("");
3787
+ }
3788
+ }
3789
+ }
3790
+ function formatStep(s) {
3791
+ const ts = `[${formatTime(s.ts)}]`;
3792
+ const isEdit = s.tool === "Edit" || s.tool === "Write" || s.tool === "MultiEdit" || s.tool === "create_file" || s.tool === "search_replace";
3793
+ const icon = isEdit ? "\u{1F4DD}" : "\u{1F527}";
3794
+ const head = isEdit && s.file ? `${s.tool ?? "?"} \u2192 ${s.file}` : s.tool ?? s.event ?? "?";
3795
+ const dur = s.duration_ms !== void 0 ? `${s.duration_ms}ms` : "";
3796
+ const tags = s.tags && s.tags.length ? `[${s.tags.join(",")}]` : "";
3797
+ const err = s.error ? "\u274C" : "";
3798
+ return [ts, icon, padRight(head, 40), padLeft(dur, 6), tags, err].filter((x) => x !== "").join(" ");
3799
+ }
3800
+ function formatTime(ts) {
3801
+ const d = new Date(ts);
3802
+ if (Number.isNaN(d.getTime())) return ts;
3803
+ const hh = String(d.getUTCHours()).padStart(2, "0");
3804
+ const mm = String(d.getUTCMinutes()).padStart(2, "0");
3805
+ const ss = String(d.getUTCSeconds()).padStart(2, "0");
3806
+ return `${hh}:${mm}:${ss}`;
3807
+ }
3808
+ function quote(s) {
3809
+ return `"${s.replace(/"/g, '\\"')}"`;
3810
+ }
3811
+ function truncate(s, n) {
3812
+ if (s.length <= n) return s;
3813
+ return s.slice(0, n) + "\u2026";
3814
+ }
3815
+ function padRight(s, n) {
3816
+ return s.length >= n ? s : s + " ".repeat(n - s.length);
3817
+ }
3818
+ function padLeft(s, n) {
3819
+ return s.length >= n ? s : " ".repeat(n - s.length) + s;
3820
+ }
3821
+ function parseIntOrUndef2(v) {
3822
+ if (v === void 0) return void 0;
3823
+ const n = Number.parseInt(v, 10);
3824
+ return Number.isFinite(n) && n > 0 ? n : void 0;
3825
+ }
3826
+
3436
3827
  // src/commands/logs/index.ts
3437
- var logsCommand = new Command27("logs").description(
3828
+ var logsCommand = new Command28("logs").description(
3438
3829
  "\u67E5\u8BE2 vibe-logger \u8F93\u51FA (.log/ai/**/*.jsonl) \u2014 AI \u8C03\u7528\u94FE\u5206\u6790"
3439
3830
  );
3440
3831
  logsCommand.addCommand(logsAnalyzeCommand);
3832
+ logsCommand.addCommand(logsTraceCommand);
3441
3833
 
3442
3834
  // src/index.ts
3443
- var require9 = createRequire8(import.meta.url);
3444
- var { version } = require9("../package.json");
3445
- var program = new Command28();
3835
+ var require8 = createRequire7(import.meta.url);
3836
+ var { version } = require8("../package.json");
3837
+ var program = new Command29();
3446
3838
  program.name("teamix-evo").description("Where ideas evolve. \u2014 AI Coding \u5957\u4EF6").version(version);
3447
3839
  program.addCommand(tokensCommand);
3448
3840
  program.addCommand(skillsCommand);