teamix-evo 0.7.0 → 0.8.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,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  // src/index.ts
4
- import { Command as Command32 } from "commander";
4
+ import { Command as Command37 } from "commander";
5
5
  import { createRequire as createRequire8 } from "module";
6
6
 
7
7
  // src/commands/tokens/index.ts
@@ -69,7 +69,6 @@ function detectIde() {
69
69
  // src/core/tokens-init.ts
70
70
  import * as path9 from "path";
71
71
  import * as fs6 from "fs/promises";
72
- import { createRequire as createRequire2 } from "module";
73
72
  import {
74
73
  loadTokensPackageManifest,
75
74
  getVariantEntry
@@ -165,6 +164,19 @@ import {
165
164
  validateSkillsLock,
166
165
  TokensPackLockSchema
167
166
  } from "@teamix-evo/registry";
167
+
168
+ // src/utils/error.ts
169
+ function getErrorMessage(err) {
170
+ if (err instanceof Error) return err.message;
171
+ if (typeof err === "string") return err;
172
+ try {
173
+ return JSON.stringify(err);
174
+ } catch {
175
+ return String(err);
176
+ }
177
+ }
178
+
179
+ // src/core/state.ts
168
180
  var TEAMIX_DIR = ".teamix-evo";
169
181
  var CONFIG_FILE = "config.json";
170
182
  var MANIFEST_FILE = "manifest.json";
@@ -188,7 +200,7 @@ async function readProjectConfig(projectRoot) {
188
200
  data = JSON.parse(raw);
189
201
  } catch (err) {
190
202
  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.`
203
+ `Corrupted config.json (${getErrorMessage(err)}). Fix the JSON manually or remove the file to start fresh; refusing to clobber prior config.`
192
204
  );
193
205
  }
194
206
  const result = validateConfig(data);
@@ -213,7 +225,7 @@ async function readInstalledManifest(projectRoot) {
213
225
  data = JSON.parse(raw);
214
226
  } catch (err) {
215
227
  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.`
228
+ `Corrupted manifest.json (${getErrorMessage(err)}). Fix the JSON manually or remove the file to start fresh; refusing to clobber prior install records.`
217
229
  );
218
230
  }
219
231
  const result = validateInstalled(data);
@@ -241,9 +253,7 @@ async function readTokensLock(projectRoot) {
241
253
  }
242
254
  return parsed.data;
243
255
  } catch (err) {
244
- logger.warn(
245
- `Failed to parse tokens-lock.json: ${err.message}`
246
- );
256
+ logger.warn(`Failed to parse tokens-lock.json: ${getErrorMessage(err)}`);
247
257
  return null;
248
258
  }
249
259
  }
@@ -274,7 +284,7 @@ async function readSkillsLock(projectRoot) {
274
284
  return result.data;
275
285
  } catch (err) {
276
286
  logger.warn(
277
- `Failed to parse skills manifest.lock.json: ${err.message}`
287
+ `Failed to parse skills manifest.lock.json: ${getErrorMessage(err)}`
278
288
  );
279
289
  return null;
280
290
  }
@@ -289,6 +299,12 @@ async function writeSkillsLock(projectRoot, lock) {
289
299
  await writeFileSafe(lockPath, JSON.stringify(lock, null, 2) + "\n");
290
300
  logger.debug(`Wrote skills lock \u2192 ${lockPath}`);
291
301
  }
302
+ function findInstalledPackage(installed, packageName) {
303
+ if (!installed) return null;
304
+ const matches = installed.installed.filter((p2) => p2.package === packageName);
305
+ if (matches.length === 0) return null;
306
+ return matches[matches.length - 1] ?? null;
307
+ }
292
308
 
293
309
  // src/core/skills-client.ts
294
310
  import * as path5 from "path";
@@ -323,7 +339,9 @@ import * as path7 from "path";
323
339
  import * as fs5 from "fs/promises";
324
340
  import {
325
341
  replaceManagedRegion,
326
- hasManagedRegion
342
+ hasManagedRegion,
343
+ extractFrontmatter,
344
+ replaceFrontmatter
327
345
  } from "@teamix-evo/registry";
328
346
 
329
347
  // src/utils/template.ts
@@ -357,6 +375,8 @@ async function loadTemplateFile(filePath) {
357
375
  // src/utils/path.ts
358
376
  import * as path6 from "path";
359
377
  import * as fs4 from "fs/promises";
378
+ import { createRequire as createRequire2 } from "module";
379
+ var require3 = createRequire2(import.meta.url);
360
380
  async function walkDir(dir) {
361
381
  const files = [];
362
382
  const entries = await fs4.readdir(dir, { withFileTypes: true });
@@ -370,6 +390,10 @@ async function walkDir(dir) {
370
390
  }
371
391
  return files;
372
392
  }
393
+ function resolveTokensPackageRoot(packageName) {
394
+ const pkgJson = require3.resolve(`${packageName}/package.json`);
395
+ return path6.dirname(pkgJson);
396
+ }
373
397
 
374
398
  // src/core/skills-installer.ts
375
399
  async function installSkills(options) {
@@ -406,9 +430,9 @@ async function writeSkillSource(skill, options) {
406
430
  const { data, packageRoot, projectRoot } = options;
407
431
  const sourceAbs = path7.resolve(packageRoot, skill.source);
408
432
  const targetDir = getSkillsSourceDir(projectRoot, skill.name);
409
- const stat4 = await fs5.stat(sourceAbs);
433
+ const stat7 = await fs5.stat(sourceAbs);
410
434
  const records = [];
411
- if (stat4.isFile()) {
435
+ if (stat7.isFile()) {
412
436
  const targetFile = path7.join(targetDir, "SKILL.md");
413
437
  const content = await renderSkillContent(sourceAbs, skill, data);
414
438
  await writeFileSafe(targetFile, content);
@@ -475,6 +499,10 @@ async function writeMirrorContent(targetFile, sourceContent, managedRegions, sou
475
499
  return existing;
476
500
  }
477
501
  let merged = existing;
502
+ const upstreamFm = extractFrontmatter(sourceContent);
503
+ if (upstreamFm) {
504
+ merged = replaceFrontmatter(merged, upstreamFm);
505
+ }
478
506
  for (const id of matchedRegions) {
479
507
  const newRegion = extractRegionBody(sourceContent, id);
480
508
  if (newRegion === null) continue;
@@ -536,11 +564,7 @@ async function updateSkills(options) {
536
564
  if (idFilter && !idFilter.has(skill.id)) continue;
537
565
  const skillIdes = skill.ides.filter((i) => ides.includes(i));
538
566
  if (skillIdes.length === 0) continue;
539
- const sourceRecords = await rewriteSkillSource(
540
- skill,
541
- options,
542
- summary
543
- );
567
+ const sourceRecords = await rewriteSkillSource(skill, options, summary);
544
568
  updated.push(...sourceRecords);
545
569
  for (const ide of skillIdes) {
546
570
  const mirrorRecords = await mirrorSkillToIde(
@@ -558,8 +582,8 @@ async function rewriteSkillSource(skill, options, summary) {
558
582
  const { data, packageRoot, projectRoot } = options;
559
583
  const sourceAbs = path7.resolve(packageRoot, skill.source);
560
584
  const targetDir = getSkillsSourceDir(projectRoot, skill.name);
561
- const stat4 = await fs5.stat(sourceAbs);
562
- if (!stat4.isFile()) {
585
+ const stat7 = await fs5.stat(sourceAbs);
586
+ if (!stat7.isFile()) {
563
587
  await ensureDir(targetDir);
564
588
  const entries = await walkDir(sourceAbs);
565
589
  const records = [];
@@ -630,6 +654,10 @@ async function rewriteSingleFile(args) {
630
654
  }
631
655
  const current = await readFileOrNull(targetFile);
632
656
  let merged = current ?? newContent;
657
+ const upstreamFm = extractFrontmatter(newContent);
658
+ if (upstreamFm) {
659
+ merged = replaceFrontmatter(merged, upstreamFm);
660
+ }
633
661
  for (const regionId of managedRegions ?? []) {
634
662
  const re = new RegExp(
635
663
  `<!-- teamix-evo:managed:start id="${escapeRegExp(
@@ -712,7 +740,7 @@ async function removeSkillFiles(records) {
712
740
  removed.push(r.target);
713
741
  } catch (err) {
714
742
  if (err.code !== "ENOENT") {
715
- logger.warn(`Failed to remove ${r.target}: ${err.message}`);
743
+ logger.warn(`Failed to remove ${r.target}: ${getErrorMessage(err)}`);
716
744
  }
717
745
  }
718
746
  }
@@ -747,11 +775,14 @@ async function ensureMcpJson(projectRoot) {
747
775
  const mcpPath = path8.join(projectRoot, ".mcp.json");
748
776
  if (await fileExists(mcpPath)) return "exists";
749
777
  try {
750
- await writeFileSafe(mcpPath, JSON.stringify(MCP_JSON_CONTENT, null, 2) + "\n");
778
+ await writeFileSafe(
779
+ mcpPath,
780
+ JSON.stringify(MCP_JSON_CONTENT, null, 2) + "\n"
781
+ );
751
782
  logger.debug(`Wrote .mcp.json \u2192 ${mcpPath}`);
752
783
  return "created";
753
784
  } catch (err) {
754
- logger.warn(`Failed to write .mcp.json: ${err.message}`);
785
+ logger.warn(`Failed to write .mcp.json: ${getErrorMessage(err)}`);
755
786
  return "failed";
756
787
  }
757
788
  }
@@ -889,7 +920,7 @@ async function runSkillsAdd(options) {
889
920
  }
890
921
  async function readExistingState(projectRoot, packageName) {
891
922
  const installed = await readInstalledManifest(projectRoot);
892
- const pkg = installed?.installed.find((p) => p.package === packageName);
923
+ const pkg = installed?.installed.find((p2) => p2.package === packageName);
893
924
  const lock = await readSkillsLock(projectRoot);
894
925
  const skillIds = /* @__PURE__ */ new Set([
895
926
  ...Object.keys(lock?.skills ?? {}),
@@ -924,8 +955,8 @@ async function finalizeSkillsInstall(args) {
924
955
  onlyIds
925
956
  });
926
957
  const config = existingConfig ?? {
927
- $schema: "https://teamix-evo.dev/schema/config/v1.json",
928
- schemaVersion: 1,
958
+ $schema: "https://teamix-evo.dev/schema/config/v2.json",
959
+ schemaVersion: 2,
929
960
  ide: ideIdent,
930
961
  packages: {}
931
962
  };
@@ -942,7 +973,7 @@ async function finalizeSkillsInstall(args) {
942
973
  installed: []
943
974
  };
944
975
  const idx = installedManifest.installed.findIndex(
945
- (p) => p.package === packageName
976
+ (p2) => p2.package === packageName
946
977
  );
947
978
  const mergedResources = mergeInstalledResources(
948
979
  existing.pkg?.resources ?? [],
@@ -1008,7 +1039,6 @@ var CONSUMER_OVERRIDES_FILE = "tokens.overrides.css";
1008
1039
  var EMPTY_OVERRIDES_TEMPLATE = `/* User-owned token overrides \u2014 frozen on subsequent installs. */
1009
1040
  /* See @teamix-evo/tokens variant theme.css for available CSS custom properties. */
1010
1041
  `;
1011
- var require3 = createRequire2(import.meta.url);
1012
1042
  async function runTokensInit(options) {
1013
1043
  const { projectRoot, variant, ide } = options;
1014
1044
  const packageName = options.packageName ?? DEFAULT_TOKENS_PACKAGE;
@@ -1072,8 +1102,8 @@ Run \`npx teamix-evo@latest tokens list-variants\` to see all options.`
1072
1102
  JSON.stringify(lock, null, 2) + "\n"
1073
1103
  );
1074
1104
  const config = {
1075
- $schema: "https://teamix-evo.dev/schema/config/v1.json",
1076
- schemaVersion: 1,
1105
+ $schema: "https://teamix-evo.dev/schema/config/v2.json",
1106
+ schemaVersion: 2,
1077
1107
  ide: existingConfig?.ide ?? ide,
1078
1108
  packages: {
1079
1109
  ...existingConfig?.packages ?? {},
@@ -1089,7 +1119,7 @@ Run \`npx teamix-evo@latest tokens list-variants\` to see all options.`
1089
1119
  schemaVersion: 1,
1090
1120
  installed: []
1091
1121
  };
1092
- const tokensIdx = prior.installed.findIndex((p) => p.package === packageName);
1122
+ const tokensIdx = prior.installed.findIndex((p2) => p2.package === packageName);
1093
1123
  const tokensEntry = {
1094
1124
  package: packageName,
1095
1125
  variant,
@@ -1126,7 +1156,9 @@ async function tryAutoInstallVariantSkills(args) {
1126
1156
  manifestSkillIds = new Set(manifest.skills.map((s) => s.id));
1127
1157
  } catch (err) {
1128
1158
  logger.warn(
1129
- `Skipping skills auto-install: could not load skills manifest (${err.message}).`
1159
+ `Skipping skills auto-install: could not load skills manifest (${getErrorMessage(
1160
+ err
1161
+ )}).`
1130
1162
  );
1131
1163
  return {
1132
1164
  attempted: [],
@@ -1176,7 +1208,7 @@ async function tryAutoInstallVariantSkills(args) {
1176
1208
  };
1177
1209
  } catch (err) {
1178
1210
  logger.warn(
1179
- `Skills auto-install failed (continuing): ${err.message}`
1211
+ `Skills auto-install failed (continuing): ${getErrorMessage(err)}`
1180
1212
  );
1181
1213
  return {
1182
1214
  attempted: desired,
@@ -1190,10 +1222,7 @@ async function installVariantFile(fileRelToPackage, packageRoot, projectRoot) {
1190
1222
  const sourceAbs = path9.join(packageRoot, fileRelToPackage);
1191
1223
  const base = path9.basename(fileRelToPackage);
1192
1224
  if (base === "theme.css") {
1193
- const targetRel = path9.posix.join(
1194
- CONSUMER_TOKENS_DIR,
1195
- CONSUMER_THEME_FILE
1196
- );
1225
+ const targetRel = path9.posix.join(CONSUMER_TOKENS_DIR, CONSUMER_THEME_FILE);
1197
1226
  const targetAbs = path9.join(projectRoot, targetRel);
1198
1227
  const content = await fs6.readFile(sourceAbs, "utf-8");
1199
1228
  await writeFileSafe(targetAbs, content);
@@ -1230,10 +1259,6 @@ async function installVariantFile(fileRelToPackage, packageRoot, projectRoot) {
1230
1259
  }
1231
1260
  return null;
1232
1261
  }
1233
- function resolveTokensPackageRoot(packageName) {
1234
- const pkgJson = require3.resolve(`${packageName}/package.json`);
1235
- return path9.dirname(pkgJson);
1236
- }
1237
1262
  async function listTokenVariants(packageName = DEFAULT_TOKENS_PACKAGE, packageRoot) {
1238
1263
  const root = packageRoot ?? resolveTokensPackageRoot(packageName);
1239
1264
  const catalog = await loadTokensPackageManifest(root);
@@ -1313,7 +1338,9 @@ var initCommand = new Command("init").description("\u521D\u59CB\u5316\u8BBE\u8BA
1313
1338
  );
1314
1339
  logger.info("To switch variants:");
1315
1340
  logger.info(" 1. npx teamix-evo@latest tokens uninstall --yes");
1316
- logger.info(` 2. npx teamix-evo@latest tokens init ${result.requestedVariant}`);
1341
+ logger.info(
1342
+ ` 2. npx teamix-evo@latest tokens init ${result.requestedVariant}`
1343
+ );
1317
1344
  logger.info(
1318
1345
  "Note: tokens.overrides.css (frozen) is preserved across uninstall/init by default."
1319
1346
  );
@@ -1329,13 +1356,13 @@ var initCommand = new Command("init").description("\u521D\u59CB\u5316\u8BBE\u8BA
1329
1356
  if (result.skills) {
1330
1357
  const { addedSkillIds, skippedSkillIds, missing } = result.skills;
1331
1358
  if (addedSkillIds.length > 0) {
1332
- logger.info(
1333
- ` Skills: auto-installed ${addedSkillIds.join(", ")}`
1334
- );
1359
+ logger.info(` Skills: auto-installed ${addedSkillIds.join(", ")}`);
1335
1360
  }
1336
1361
  if (skippedSkillIds.length > 0) {
1337
1362
  logger.info(
1338
- ` Skills: already installed (skipped) ${skippedSkillIds.join(", ")}`
1363
+ ` Skills: already installed (skipped) ${skippedSkillIds.join(
1364
+ ", "
1365
+ )}`
1339
1366
  );
1340
1367
  }
1341
1368
  if (missing.length > 0) {
@@ -1345,12 +1372,14 @@ var initCommand = new Command("init").description("\u521D\u59CB\u5316\u8BBE\u8BA
1345
1372
  }
1346
1373
  }
1347
1374
  logger.info("");
1348
- logger.info('Run "npx teamix-evo@latest tokens update" to update resources later.');
1375
+ logger.info(
1376
+ 'Run "npx teamix-evo@latest tokens update" to update resources later.'
1377
+ );
1349
1378
  logger.info(
1350
1379
  'Run "npx teamix-evo@latest tokens list-variants" to see all available variants.'
1351
1380
  );
1352
1381
  } catch (err) {
1353
- logger.error(`Failed to initialize: ${err.message}`);
1382
+ logger.error(`Failed to initialize: ${getErrorMessage(err)}`);
1354
1383
  logger.debug(err.stack ?? "");
1355
1384
  process.exitCode = 1;
1356
1385
  }
@@ -1360,17 +1389,94 @@ var initCommand = new Command("init").description("\u521D\u59CB\u5316\u8BBE\u8BA
1360
1389
  import { Command as Command2 } from "commander";
1361
1390
 
1362
1391
  // src/core/tokens-update.ts
1363
- import * as path11 from "path";
1392
+ import * as path12 from "path";
1364
1393
  import * as fs8 from "fs/promises";
1365
- import { createRequire as createRequire3 } from "module";
1366
1394
  import {
1367
1395
  loadTokensPackageManifest as loadTokensPackageManifest2,
1368
1396
  getVariantEntry as getVariantEntry2
1369
1397
  } from "@teamix-evo/registry";
1398
+
1399
+ // src/core/upgrade-hints.ts
1400
+ import * as path11 from "path";
1401
+ var TEAMIX_DIR3 = ".teamix-evo";
1402
+ var HINTS_DIR = ".upgrade-hints";
1403
+ function isoToFsSafe(iso) {
1404
+ return iso.replace(/[:.]/g, "-");
1405
+ }
1406
+ async function writeTokensUpgradeHint(options) {
1407
+ if (options.renames.length === 0) return null;
1408
+ const isoTs = options.isoTs ?? (/* @__PURE__ */ new Date()).toISOString();
1409
+ const fsTs = isoToFsSafe(isoTs);
1410
+ const filename = `tokens-${fsTs}.json`;
1411
+ const target = path11.join(
1412
+ options.projectRoot,
1413
+ TEAMIX_DIR3,
1414
+ HINTS_DIR,
1415
+ filename
1416
+ );
1417
+ const payload = {
1418
+ schemaVersion: 1,
1419
+ ts: isoTs,
1420
+ package: "tokens",
1421
+ trigger: options.trigger,
1422
+ fromVariant: options.fromVariant,
1423
+ toVariant: options.toVariant,
1424
+ fromVersion: options.fromVersion,
1425
+ toVersion: options.toVersion,
1426
+ renames: options.renames
1427
+ };
1428
+ await writeFileSafe(target, JSON.stringify(payload, null, 2) + "\n");
1429
+ return {
1430
+ path: target,
1431
+ ts: fsTs,
1432
+ renameCount: options.renames.length
1433
+ };
1434
+ }
1435
+ function selectApplicableRenames(renames, fromVersion, toVersion) {
1436
+ return renames.filter(
1437
+ (r) => compareSemver(r.sinceVersion, fromVersion) > 0 && compareSemver(r.sinceVersion, toVersion) <= 0
1438
+ ).sort((a, b) => compareSemver(a.sinceVersion, b.sinceVersion));
1439
+ }
1440
+ function compareSemver(a, b) {
1441
+ const [aMain = "", aRest = ""] = a.split("-", 2);
1442
+ const [bMain = "", bRest = ""] = b.split("-", 2);
1443
+ const aParts = aMain.split(".").map((n) => Number.parseInt(n, 10));
1444
+ const bParts = bMain.split(".").map((n) => Number.parseInt(n, 10));
1445
+ for (let i = 0; i < Math.max(aParts.length, bParts.length); i++) {
1446
+ const ai = aParts[i] ?? 0;
1447
+ const bi = bParts[i] ?? 0;
1448
+ if (ai !== bi) return ai - bi;
1449
+ }
1450
+ if (aRest === "" && bRest !== "") return 1;
1451
+ if (aRest !== "" && bRest === "") return -1;
1452
+ return aRest.localeCompare(bRest, void 0, { numeric: true });
1453
+ }
1454
+
1455
+ // src/core/managed-merge.ts
1456
+ import { hasManagedRegion as hasManagedRegion2, replaceManagedRegion as replaceManagedRegion2 } from "@teamix-evo/registry";
1457
+ function mergeManagedRegions(upstreamContent, consumerContent) {
1458
+ let updated = consumerContent;
1459
+ const re = /<!-- teamix-evo:managed:start id="([^"]+)" -->([\s\S]*?)<!-- teamix-evo:managed:end(?: id="\1")? -->/g;
1460
+ let match;
1461
+ while ((match = re.exec(upstreamContent)) !== null) {
1462
+ const id = match[1];
1463
+ const body = match[2].replace(/^\n/, "").replace(/\n$/, "");
1464
+ if (!hasManagedRegion2(updated, id)) {
1465
+ throw new Error(
1466
+ `Managed region "${id}" missing from consumer file \u2014 refusing to silently rewrite (ADR 0003).`
1467
+ );
1468
+ }
1469
+ updated = replaceManagedRegion2(updated, id, body);
1470
+ }
1471
+ return updated;
1472
+ }
1473
+
1474
+ // src/core/tokens-update.ts
1370
1475
  var DEFAULT_TOKENS_PACKAGE2 = "@teamix-evo/tokens";
1371
- var CONSUMER_TOKENS_DIR2 = "tokens";
1372
- var CONSUMER_THEME_FILE2 = "tokens.theme.css";
1373
- var require4 = createRequire3(import.meta.url);
1476
+ var CONSUMER_BASENAME_BY_UPSTREAM = {
1477
+ "theme.css": "tokens.theme.css",
1478
+ "overrides.css": "tokens.overrides.css"
1479
+ };
1374
1480
  async function runTokensUpdate(options) {
1375
1481
  const { projectRoot } = options;
1376
1482
  const packageName = options.packageName ?? DEFAULT_TOKENS_PACKAGE2;
@@ -1380,7 +1486,7 @@ async function runTokensUpdate(options) {
1380
1486
  }
1381
1487
  const currentVariant = config.packages.tokens.variant;
1382
1488
  const currentVersion = config.packages.tokens.version;
1383
- const packageRoot = options.packageRoot ?? resolveTokensPackageRoot2(packageName);
1489
+ const packageRoot = options.packageRoot ?? resolveTokensPackageRoot(packageName);
1384
1490
  const catalog = await loadTokensPackageManifest2(packageRoot);
1385
1491
  const variantEntry = getVariantEntry2(catalog, currentVariant);
1386
1492
  if (!variantEntry) {
@@ -1388,27 +1494,92 @@ async function runTokensUpdate(options) {
1388
1494
  `Currently installed variant "${currentVariant}" no longer exists in ${packageName}@${catalog.version}. Available: ${catalog.variants.map((v) => v.name).join(", ")}. Run \`npx teamix-evo@latest tokens uninstall\` then \`npx teamix-evo@latest tokens init <variant>\` to switch.`
1389
1495
  );
1390
1496
  }
1497
+ const upstreamByBasename = /* @__PURE__ */ new Map();
1498
+ for (const fileRel of variantEntry.files) {
1499
+ upstreamByBasename.set(
1500
+ path12.basename(fileRel),
1501
+ path12.join(packageRoot, fileRel)
1502
+ );
1503
+ }
1504
+ const prior = await readInstalledManifest(projectRoot) ?? {
1505
+ schemaVersion: 1,
1506
+ installed: []
1507
+ };
1508
+ const installedIdx = prior.installed.findIndex(
1509
+ (p2) => p2.package === packageName
1510
+ );
1511
+ const priorResources = installedIdx >= 0 ? prior.installed[installedIdx].resources : [];
1512
+ const rewritten = [];
1513
+ const managedReplaced = [];
1514
+ const preserved = [];
1515
+ const frozenDrift = [];
1516
+ const refreshedResources = [];
1517
+ for (const resource of priorResources) {
1518
+ const consumerAbs = path12.isAbsolute(resource.target) ? resource.target : path12.join(projectRoot, resource.target);
1519
+ const consumerBasename = path12.basename(resource.target);
1520
+ const upstreamBasename = lookupUpstreamBasename(consumerBasename);
1521
+ const upstreamAbs = upstreamBasename ? upstreamByBasename.get(upstreamBasename) : void 0;
1522
+ if (resource.strategy === "regenerable") {
1523
+ if (!upstreamAbs) {
1524
+ refreshedResources.push(resource);
1525
+ continue;
1526
+ }
1527
+ const content = await fs8.readFile(upstreamAbs, "utf-8");
1528
+ await writeFileSafe(consumerAbs, content);
1529
+ rewritten.push(resource.target);
1530
+ refreshedResources.push({
1531
+ ...resource,
1532
+ hash: computeHash(content)
1533
+ });
1534
+ continue;
1535
+ }
1536
+ if (resource.strategy === "managed") {
1537
+ if (!upstreamAbs || !await fileExists(consumerAbs)) {
1538
+ refreshedResources.push(resource);
1539
+ continue;
1540
+ }
1541
+ const upstreamContent = await fs8.readFile(upstreamAbs, "utf-8");
1542
+ const consumerContent = await fs8.readFile(consumerAbs, "utf-8");
1543
+ const merged = mergeManagedRegions(upstreamContent, consumerContent);
1544
+ if (merged !== consumerContent) {
1545
+ await writeFileSafe(consumerAbs, merged);
1546
+ managedReplaced.push(resource.target);
1547
+ }
1548
+ refreshedResources.push({
1549
+ ...resource,
1550
+ hash: computeHash(merged)
1551
+ });
1552
+ continue;
1553
+ }
1554
+ if (await fileExists(consumerAbs)) preserved.push(resource.target);
1555
+ if (upstreamAbs) {
1556
+ const upstreamContent = await fs8.readFile(upstreamAbs, "utf-8");
1557
+ const upstreamHash = computeHash(upstreamContent);
1558
+ if (resource.hash && upstreamHash !== resource.hash) {
1559
+ frozenDrift.push({
1560
+ target: resource.target,
1561
+ reason: "upstream-changed"
1562
+ });
1563
+ }
1564
+ }
1565
+ refreshedResources.push(resource);
1566
+ }
1391
1567
  if (variantEntry.version === currentVersion) {
1392
- await rewriteRegenerableFiles(variantEntry.files, packageRoot, projectRoot);
1568
+ if (installedIdx >= 0) {
1569
+ prior.installed[installedIdx] = {
1570
+ ...prior.installed[installedIdx],
1571
+ resources: refreshedResources
1572
+ };
1573
+ await writeInstalledManifest(projectRoot, prior);
1574
+ }
1393
1575
  return {
1394
1576
  status: "up-to-date",
1395
1577
  packageName,
1396
1578
  variant: currentVariant,
1397
- version: currentVersion
1579
+ version: currentVersion,
1580
+ frozenDrift
1398
1581
  };
1399
1582
  }
1400
- const rewritten = await rewriteRegenerableFiles(
1401
- variantEntry.files,
1402
- packageRoot,
1403
- projectRoot
1404
- );
1405
- const preserved = [];
1406
- const overridesAbs = path11.join(
1407
- projectRoot,
1408
- CONSUMER_TOKENS_DIR2,
1409
- "tokens.overrides.css"
1410
- );
1411
- if (await fileExists(overridesAbs)) preserved.push("tokens.overrides.css");
1412
1583
  const lock = {
1413
1584
  schemaVersion: 1,
1414
1585
  variant: {
@@ -1422,29 +1593,38 @@ async function runTokensUpdate(options) {
1422
1593
  installedAt: (/* @__PURE__ */ new Date()).toISOString()
1423
1594
  };
1424
1595
  await writeFileSafe(
1425
- path11.join(projectRoot, ".teamix-evo", "tokens-lock.json"),
1596
+ path12.join(projectRoot, ".teamix-evo", "tokens-lock.json"),
1426
1597
  JSON.stringify(lock, null, 2) + "\n"
1427
1598
  );
1428
1599
  config.packages.tokens.version = variantEntry.version;
1429
1600
  await writeProjectConfig(projectRoot, config);
1430
- const prior = await readInstalledManifest(projectRoot) ?? {
1431
- schemaVersion: 1,
1432
- installed: []
1433
- };
1434
- const idx = prior.installed.findIndex((p) => p.package === packageName);
1435
- if (idx >= 0) {
1436
- prior.installed[idx].version = variantEntry.version;
1437
- prior.installed[idx].installedAt = (/* @__PURE__ */ new Date()).toISOString();
1438
- for (const r of prior.installed[idx].resources) {
1439
- if (r.strategy === "regenerable") {
1440
- const abs = path11.isAbsolute(r.target) ? r.target : path11.join(projectRoot, r.target);
1441
- if (await fileExists(abs)) {
1442
- r.hash = computeHash(await fs8.readFile(abs, "utf-8"));
1443
- }
1444
- }
1445
- }
1601
+ if (installedIdx >= 0) {
1602
+ prior.installed[installedIdx] = {
1603
+ ...prior.installed[installedIdx],
1604
+ version: variantEntry.version,
1605
+ installedAt: (/* @__PURE__ */ new Date()).toISOString(),
1606
+ resources: refreshedResources
1607
+ };
1446
1608
  await writeInstalledManifest(projectRoot, prior);
1447
1609
  }
1610
+ const renames = selectApplicableRenames(
1611
+ variantEntry.renames ?? [],
1612
+ currentVersion,
1613
+ variantEntry.version
1614
+ );
1615
+ let hintPath;
1616
+ if (renames.length > 0) {
1617
+ const hint = await writeTokensUpgradeHint({
1618
+ projectRoot,
1619
+ trigger: "update",
1620
+ fromVariant: currentVariant,
1621
+ toVariant: currentVariant,
1622
+ fromVersion: currentVersion,
1623
+ toVersion: variantEntry.version,
1624
+ renames
1625
+ });
1626
+ if (hint) hintPath = hint.path;
1627
+ }
1448
1628
  return {
1449
1629
  status: "updated",
1450
1630
  packageName,
@@ -1452,34 +1632,25 @@ async function runTokensUpdate(options) {
1452
1632
  from: currentVersion,
1453
1633
  to: variantEntry.version,
1454
1634
  rewritten,
1455
- preserved
1635
+ managedReplaced,
1636
+ preserved,
1637
+ frozenDrift,
1638
+ renames,
1639
+ ...hintPath ? { hintPath } : {}
1456
1640
  };
1457
1641
  }
1458
- async function rewriteRegenerableFiles(files, packageRoot, projectRoot) {
1459
- const written = [];
1460
- for (const fileRel of files) {
1461
- const base = path11.basename(fileRel);
1462
- if (base !== "theme.css") continue;
1463
- const sourceAbs = path11.join(packageRoot, fileRel);
1464
- const targetAbs = path11.join(
1465
- projectRoot,
1466
- CONSUMER_TOKENS_DIR2,
1467
- CONSUMER_THEME_FILE2
1468
- );
1469
- const content = await fs8.readFile(sourceAbs, "utf-8");
1470
- await writeFileSafe(targetAbs, content);
1471
- written.push(CONSUMER_THEME_FILE2);
1642
+ function lookupUpstreamBasename(consumerBasename) {
1643
+ for (const [upstream, consumer] of Object.entries(
1644
+ CONSUMER_BASENAME_BY_UPSTREAM
1645
+ )) {
1646
+ if (consumer === consumerBasename) return upstream;
1472
1647
  }
1473
- return written;
1474
- }
1475
- function resolveTokensPackageRoot2(packageName) {
1476
- const pkgJson = require4.resolve(`${packageName}/package.json`);
1477
- return path11.dirname(pkgJson);
1648
+ return consumerBasename;
1478
1649
  }
1479
1650
 
1480
1651
  // src/commands/tokens/update.ts
1481
1652
  var updateCommand = new Command2("update").description(
1482
- "\u5237\u65B0 design tokens\uFF08regenerable \u8986\u76D6\u3001frozen \u4FDD\u7559 \u2014 ADR 0003 \u4E09\u6001\uFF09"
1653
+ "\u5237\u65B0 design tokens\uFF08regenerable \u8986\u76D6 / managed \u533A\u6BB5\u66FF\u6362 / frozen \u4FDD\u7559 \u2014 ADR 0003 \u4E09\u6001\uFF09"
1483
1654
  ).action(async () => {
1484
1655
  try {
1485
1656
  const ide = detectIde();
@@ -1496,9 +1667,8 @@ var updateCommand = new Command2("update").description(
1496
1667
  logger.success(
1497
1668
  `Already at latest: ${result.packageName} (${result.variant} v${result.version})`
1498
1669
  );
1499
- logger.info(
1500
- " Regenerable files refreshed in case of manual edits."
1501
- );
1670
+ logger.info(" Regenerable files refreshed in case of manual edits.");
1671
+ printFrozenDrift(result.frozenDrift);
1502
1672
  return;
1503
1673
  }
1504
1674
  logger.success(
@@ -1507,17 +1677,41 @@ var updateCommand = new Command2("update").description(
1507
1677
  if (result.rewritten.length > 0) {
1508
1678
  logger.info(` Rewrote: ${result.rewritten.join(", ")}`);
1509
1679
  }
1680
+ if (result.managedReplaced.length > 0) {
1681
+ logger.info(
1682
+ ` Managed regions updated: ${result.managedReplaced.join(", ")}`
1683
+ );
1684
+ }
1510
1685
  if (result.preserved.length > 0) {
1511
1686
  logger.info(
1512
- ` Preserved: ${result.preserved.join(", ")} (frozen \u2014 your customizations kept)`
1687
+ ` Preserved: ${result.preserved.join(
1688
+ ", "
1689
+ )} (frozen \u2014 your customizations kept)`
1690
+ );
1691
+ }
1692
+ printFrozenDrift(result.frozenDrift);
1693
+ if (result.hintPath) {
1694
+ logger.info("");
1695
+ logger.info(`\u{1F4A1} token rename hint: ${result.hintPath}`);
1696
+ logger.info(
1697
+ ` \u68C0\u6D4B\u5230 ${result.renames.length} \u4E2A token rename\uFF0C\u5EFA\u8BAE\u8C03\u7528 \`teamix-evo-upgrade\` skill \u626B src/** \u7ED9 codemod \u5EFA\u8BAE\uFF08ADR 0019 \xA7D4\uFF09\u3002`
1513
1698
  );
1514
1699
  }
1515
1700
  } catch (err) {
1516
- logger.error(`Failed to update tokens: ${err.message}`);
1701
+ logger.error(`Failed to update tokens: ${getErrorMessage(err)}`);
1517
1702
  logger.debug(err.stack ?? "");
1518
1703
  process.exitCode = 1;
1519
1704
  }
1520
1705
  });
1706
+ function printFrozenDrift(drift) {
1707
+ if (drift.length === 0) return;
1708
+ logger.warn(
1709
+ ` \u26A0\uFE0F frozen drift\uFF08\u4E0A\u6E38\u6709\u53D8\u52A8\uFF0C\u4F46\u672C\u5730\u5C5E\u7528\u6237\u53D7\u63A7\u6587\u4EF6 \u2014 ADR 0019 \xA7D2\uFF09\uFF1A${drift.map((d) => d.target).join(", ")}`
1710
+ );
1711
+ logger.info(
1712
+ " \u5982\u9700\u5BF9\u7167\u4E0A\u6E38\u53D8\u52A8\uFF0C\u8BF7\u624B\u52A8 diff \u6216\u540E\u7EED\u4F7F\u7528 `--accept-upstream <target>`\uFF08\u5F85\u6279\u6B21 5 \u843D\u5730\uFF09\u3002"
1713
+ );
1714
+ }
1521
1715
 
1522
1716
  // src/commands/tokens/list.ts
1523
1717
  import { Command as Command3 } from "commander";
@@ -1528,7 +1722,9 @@ var listCommand = new Command3("list").description("\u5217\u51FA\u5DF2\u5B89\u88
1528
1722
  const config = await readProjectConfig(projectRoot);
1529
1723
  if (!config?.packages?.tokens) {
1530
1724
  logger.info("No tokens variant installed.");
1531
- logger.info('Run "npx teamix-evo@latest tokens init [variant]" to get started.');
1725
+ logger.info(
1726
+ 'Run "npx teamix-evo@latest tokens init [variant]" to get started.'
1727
+ );
1532
1728
  return;
1533
1729
  }
1534
1730
  const { variant, version: version2 } = config.packages.tokens;
@@ -1540,7 +1736,7 @@ var listCommand = new Command3("list").description("\u5217\u51FA\u5DF2\u5B89\u88
1540
1736
  const manifest = await readInstalledManifest(projectRoot);
1541
1737
  if (manifest) {
1542
1738
  const pkg = manifest.installed.find(
1543
- (p) => p.package === "@teamix-evo/tokens" && p.variant === variant
1739
+ (p2) => p2.package === "@teamix-evo/tokens" && p2.variant === variant
1544
1740
  );
1545
1741
  if (pkg) {
1546
1742
  logger.info(` Resources: ${pkg.resources.length} files`);
@@ -1550,7 +1746,7 @@ var listCommand = new Command3("list").description("\u5217\u51FA\u5DF2\u5B89\u88
1550
1746
  }
1551
1747
  }
1552
1748
  } catch (err) {
1553
- logger.error(`Failed to list: ${err.message}`);
1749
+ logger.error(`Failed to list: ${getErrorMessage(err)}`);
1554
1750
  process.exitCode = 1;
1555
1751
  }
1556
1752
  });
@@ -1580,9 +1776,11 @@ var listVariantsCommand = new Command4("list-variants").description("\u5217\u51F
1580
1776
  }
1581
1777
  logger.info("");
1582
1778
  }
1583
- logger.info("Install a variant: npx teamix-evo@latest tokens init <name>");
1779
+ logger.info(
1780
+ "Install a variant: npx teamix-evo@latest tokens init <name>"
1781
+ );
1584
1782
  } catch (err) {
1585
- logger.error(`Failed to list variants: ${err.message}`);
1783
+ logger.error(`Failed to list variants: ${getErrorMessage(err)}`);
1586
1784
  logger.debug(err.stack ?? "");
1587
1785
  process.exitCode = 1;
1588
1786
  }
@@ -1591,7 +1789,7 @@ var listVariantsCommand = new Command4("list-variants").description("\u5217\u51F
1591
1789
  // src/commands/tokens/uninstall.ts
1592
1790
  import { Command as Command5 } from "commander";
1593
1791
  import * as fs9 from "fs/promises";
1594
- import * as path12 from "path";
1792
+ import * as path13 from "path";
1595
1793
  import * as prompts from "@clack/prompts";
1596
1794
  var TOKENS_PACKAGE = "@teamix-evo/tokens";
1597
1795
  var uninstallCommand = new Command5("uninstall").description(
@@ -1613,7 +1811,7 @@ var uninstallCommand = new Command5("uninstall").description(
1613
1811
  }
1614
1812
  const installedManifest = await readInstalledManifest(projectRoot);
1615
1813
  const pkg = installedManifest?.installed.find(
1616
- (p) => p.package === TOKENS_PACKAGE
1814
+ (p2) => p2.package === TOKENS_PACKAGE
1617
1815
  );
1618
1816
  const resources = pkg?.resources ?? [];
1619
1817
  const removable = opts.keepFiles ? [] : opts.purge ? resources.filter((r) => r.strategy !== "managed") : resources.filter((r) => r.strategy === "regenerable");
@@ -1622,38 +1820,36 @@ var uninstallCommand = new Command5("uninstall").description(
1622
1820
  `Will remove ${removable.length} file(s); keep ${kept} managed file(s).`
1623
1821
  );
1624
1822
  if (!opts.yes) {
1625
- const confirm5 = await prompts.confirm({
1823
+ const confirm8 = await prompts.confirm({
1626
1824
  message: "\u786E\u8BA4\u5378\u8F7D tokens \u53D8\u4F53\uFF1F",
1627
1825
  initialValue: false
1628
1826
  });
1629
- if (prompts.isCancel(confirm5) || !confirm5) {
1827
+ if (prompts.isCancel(confirm8) || !confirm8) {
1630
1828
  logger.info("Cancelled.");
1631
1829
  return;
1632
1830
  }
1633
1831
  }
1634
1832
  let removed = 0;
1635
1833
  for (const r of removable) {
1636
- const target = path12.isAbsolute(r.target) ? r.target : path12.join(projectRoot, r.target);
1834
+ const target = path13.isAbsolute(r.target) ? r.target : path13.join(projectRoot, r.target);
1637
1835
  try {
1638
1836
  await fs9.unlink(target);
1639
1837
  removed++;
1640
1838
  } catch (err) {
1641
1839
  if (err.code !== "ENOENT") {
1642
- logger.warn(
1643
- `Failed to remove ${target}: ${err.message}`
1644
- );
1840
+ logger.warn(`Failed to remove ${target}: ${getErrorMessage(err)}`);
1645
1841
  }
1646
1842
  }
1647
1843
  }
1648
1844
  if (installedManifest) {
1649
1845
  installedManifest.installed = installedManifest.installed.filter(
1650
- (p) => p.package !== TOKENS_PACKAGE
1846
+ (p2) => p2.package !== TOKENS_PACKAGE
1651
1847
  );
1652
1848
  await writeInstalledManifest(projectRoot, installedManifest);
1653
1849
  }
1654
1850
  delete config.packages.tokens;
1655
1851
  await writeProjectConfig(projectRoot, config);
1656
- const lockPath = path12.join(
1852
+ const lockPath = path13.join(
1657
1853
  projectRoot,
1658
1854
  ".teamix-evo",
1659
1855
  "tokens-lock.json"
@@ -1663,18 +1859,18 @@ var uninstallCommand = new Command5("uninstall").description(
1663
1859
  } catch (err) {
1664
1860
  if (err.code !== "ENOENT") {
1665
1861
  logger.warn(
1666
- `Failed to remove tokens-lock.json: ${err.message}`
1862
+ `Failed to remove tokens-lock.json: ${getErrorMessage(err)}`
1667
1863
  );
1668
1864
  }
1669
1865
  }
1670
1866
  logger.success(`Uninstalled ${TOKENS_PACKAGE}`);
1671
1867
  logger.info(` Removed: ${removed} files`);
1672
1868
  if (kept > 0) {
1673
- const note = opts.purge ? "managed (you may delete manually)" : "frozen / managed (preserved \u2014 ADR 0003; --purge to force)";
1674
- logger.info(` Kept: ${kept} files \u2014 ${note}`);
1869
+ const note2 = opts.purge ? "managed (you may delete manually)" : "frozen / managed (preserved \u2014 ADR 0003; --purge to force)";
1870
+ logger.info(` Kept: ${kept} files \u2014 ${note2}`);
1675
1871
  }
1676
1872
  } catch (err) {
1677
- logger.error(`Failed to uninstall: ${err.message}`);
1873
+ logger.error(`Failed to uninstall: ${getErrorMessage(err)}`);
1678
1874
  logger.debug(err.stack ?? "");
1679
1875
  process.exitCode = 1;
1680
1876
  }
@@ -1695,16 +1891,24 @@ import { Command as Command14 } from "commander";
1695
1891
  import { Command as Command7 } from "commander";
1696
1892
  import * as prompts2 from "@clack/prompts";
1697
1893
 
1894
+ // src/utils/cancelled.ts
1895
+ var CancelledError = class extends Error {
1896
+ constructor(message = "Cancelled by user.") {
1897
+ super(message);
1898
+ this.name = "CancelledError";
1899
+ }
1900
+ };
1901
+
1698
1902
  // src/commands/skills/_parse.ts
1699
1903
  function parseIdeList(input) {
1700
1904
  const parts = input.split(",").map((s) => s.trim().toLowerCase()).filter(Boolean);
1701
1905
  const result = [];
1702
- for (const p of parts) {
1703
- if (p === "qoder" || p === "claude") {
1704
- if (!result.includes(p)) result.push(p);
1906
+ for (const p2 of parts) {
1907
+ if (p2 === "qoder" || p2 === "claude") {
1908
+ if (!result.includes(p2)) result.push(p2);
1705
1909
  } else {
1706
1910
  throw new Error(
1707
- `Unknown IDE: "${p}". Expected one of: ${ALL_IDE_KINDS.join(", ")}.`
1911
+ `Unknown IDE: "${p2}". Expected one of: ${ALL_IDE_KINDS.join(", ")}.`
1708
1912
  );
1709
1913
  }
1710
1914
  }
@@ -1736,7 +1940,9 @@ var initCommand2 = new Command7("init").description(
1736
1940
  return;
1737
1941
  }
1738
1942
  logger.info(
1739
- `Initializing skills (bulk): ides=[${ides.join(",")}], scope="${scope}"`
1943
+ `Initializing skills (bulk): ides=[${ides.join(
1944
+ ","
1945
+ )}], scope="${scope}"`
1740
1946
  );
1741
1947
  logger.debug(`Project root: ${projectRoot}`);
1742
1948
  const result = await runSkillsInit({
@@ -1764,10 +1970,16 @@ var initCommand2 = new Command7("init").description(
1764
1970
  }
1765
1971
  logger.info(` Files: ${result.fileCount}`);
1766
1972
  logger.info("");
1767
- logger.info('Run "npx teamix-evo@latest skills list" to see installed skills.');
1973
+ logger.info(
1974
+ 'Run "npx teamix-evo@latest skills list" to see installed skills.'
1975
+ );
1768
1976
  } catch (err) {
1769
- logger.error(`Failed to init skills: ${err.message}`);
1770
- logger.debug(err.stack ?? "");
1977
+ if (err instanceof CancelledError) {
1978
+ logger.info("skills init \u5DF2\u53D6\u6D88\u3002");
1979
+ return;
1980
+ }
1981
+ logger.error(`Failed to init skills: ${getErrorMessage(err)}`);
1982
+ logger.debug(err instanceof Error ? err.stack ?? "" : "");
1771
1983
  process.exitCode = 1;
1772
1984
  }
1773
1985
  });
@@ -1792,7 +2004,7 @@ async function resolveIdesAndScope(args) {
1792
2004
  required: true
1793
2005
  });
1794
2006
  if (prompts2.isCancel(idesAns)) {
1795
- throw new Error("Cancelled by user.");
2007
+ throw new CancelledError();
1796
2008
  }
1797
2009
  const scopeAns = await prompts2.select({
1798
2010
  message: "\u5B89\u88C5\u8303\u56F4\uFF1F",
@@ -1803,7 +2015,7 @@ async function resolveIdesAndScope(args) {
1803
2015
  initialValue: "project"
1804
2016
  });
1805
2017
  if (prompts2.isCancel(scopeAns)) {
1806
- throw new Error("Cancelled by user.");
2018
+ throw new CancelledError();
1807
2019
  }
1808
2020
  return { ides: idesAns, scope: scopeAns };
1809
2021
  }
@@ -1869,10 +2081,16 @@ var addCommand = new Command8("add").description(
1869
2081
  }
1870
2082
  logger.info(` Files: ${result.fileCount}`);
1871
2083
  logger.info("");
1872
- logger.info('Run "npx teamix-evo@latest skills list" to see installed skills.');
2084
+ logger.info(
2085
+ 'Run "npx teamix-evo@latest skills list" to see installed skills.'
2086
+ );
1873
2087
  } catch (err) {
1874
- logger.error(`Failed to add skills: ${err.message}`);
1875
- logger.debug(err.stack ?? "");
2088
+ if (err instanceof CancelledError) {
2089
+ logger.info("skills add \u5DF2\u53D6\u6D88\u3002");
2090
+ return;
2091
+ }
2092
+ logger.error(`Failed to add skills: ${getErrorMessage(err)}`);
2093
+ logger.debug(err instanceof Error ? err.stack ?? "" : "");
1876
2094
  process.exitCode = 1;
1877
2095
  }
1878
2096
  });
@@ -1909,7 +2127,7 @@ async function resolveIdesAndScope2(args) {
1909
2127
  required: true
1910
2128
  });
1911
2129
  if (prompts3.isCancel(idesAns)) {
1912
- throw new Error("Cancelled by user.");
2130
+ throw new CancelledError();
1913
2131
  }
1914
2132
  const scopeAns = await prompts3.select({
1915
2133
  message: "\u5B89\u88C5\u8303\u56F4\uFF1F",
@@ -1920,7 +2138,7 @@ async function resolveIdesAndScope2(args) {
1920
2138
  initialValue: "project"
1921
2139
  });
1922
2140
  if (prompts3.isCancel(scopeAns)) {
1923
- throw new Error("Cancelled by user.");
2141
+ throw new CancelledError();
1924
2142
  }
1925
2143
  return { ides: idesAns, scope: scopeAns };
1926
2144
  }
@@ -1941,7 +2159,7 @@ var listCommand2 = new Command9("list").alias("ls").description(
1941
2159
  const config = await readProjectConfig(projectRoot);
1942
2160
  const installedManifest = await readInstalledManifest(projectRoot);
1943
2161
  const pkg = installedManifest?.installed.find(
1944
- (p) => p.package === SKILLS_PACKAGE
2162
+ (p2) => p2.package === SKILLS_PACKAGE
1945
2163
  );
1946
2164
  const skillsLock = await readSkillsLock(projectRoot);
1947
2165
  const installedBySkill = /* @__PURE__ */ new Map();
@@ -1957,7 +2175,9 @@ var listCommand2 = new Command9("list").alias("ls").description(
1957
2175
  if (opts.installed) {
1958
2176
  if (!config?.packages?.skills || !pkg) {
1959
2177
  logger.info("No skills installed.");
1960
- logger.info('Run "npx teamix-evo@latest skills init" to get started.');
2178
+ logger.info(
2179
+ 'Run "npx teamix-evo@latest skills init" to get started.'
2180
+ );
1961
2181
  return;
1962
2182
  }
1963
2183
  printInstalledHeader(config.packages.skills, pkg.installedAt);
@@ -2008,7 +2228,7 @@ var listCommand2 = new Command9("list").alias("ls").description(
2008
2228
  ` Total: ${skills.length} skill(s) \u2014 ${installedCount} installed, ${skills.length - installedCount} available`
2009
2229
  );
2010
2230
  } catch (err) {
2011
- logger.error(`Failed to list: ${err.message}`);
2231
+ logger.error(`Failed to list: ${getErrorMessage(err)}`);
2012
2232
  process.exitCode = 1;
2013
2233
  }
2014
2234
  });
@@ -2023,7 +2243,7 @@ function printInstalledHeader(cfg, installedAt) {
2023
2243
 
2024
2244
  // src/commands/skills/update.ts
2025
2245
  import { Command as Command10 } from "commander";
2026
- import { createRequire as createRequire4 } from "module";
2246
+ import { createRequire as createRequire3 } from "module";
2027
2247
 
2028
2248
  // src/core/skills-update.ts
2029
2249
  var DEFAULT_SKILLS_PACKAGE3 = "@teamix-evo/skills";
@@ -2146,7 +2366,7 @@ async function runSkillsUpdate(options) {
2146
2366
  installed: []
2147
2367
  };
2148
2368
  const idx = installedManifest.installed.findIndex(
2149
- (p) => p.package === packageName
2369
+ (p2) => p2.package === packageName
2150
2370
  );
2151
2371
  const installedAt = (/* @__PURE__ */ new Date()).toISOString();
2152
2372
  const prior = idx >= 0 ? installedManifest.installed[idx].resources : [];
@@ -2196,15 +2416,18 @@ async function runSkillsUpdate(options) {
2196
2416
  }
2197
2417
 
2198
2418
  // src/commands/skills/update.ts
2199
- var require5 = createRequire4(import.meta.url);
2419
+ var require4 = createRequire3(import.meta.url);
2200
2420
  var SKILLS_PACKAGE2 = "@teamix-evo/skills";
2201
2421
  var updateCommand2 = new Command10("update").description(
2202
2422
  "\u66F4\u65B0\u5DF2\u5B89\u88C5\u7684 teamix-evo skills\uFF08\u4EC5\u5347\u7EA7 lock \u5DF2\u8BB0\u5F55\u4E14 scope \u5339\u914D\u7684 skill \u2014 ADR 0035\uFF09"
2203
- ).argument("[names...]", "\u53EF\u9009\uFF1A\u4EC5\u5347\u7EA7\u6307\u5B9A skill id;\u7701\u7565\u5219\u5347\u7EA7\u5168\u90E8\u5DF2\u88C5").option("--dry-run", "\u9884\u89C8\u53D8\u66F4\uFF0C\u4E0D\u5199\u76D8").action(async (names, opts) => {
2423
+ ).argument("[names...]", "\u53EF\u9009\uFF1A\u4EC5\u5347\u7EA7\u6307\u5B9A skill id\uFF1B\u7701\u7565\u5219\u5347\u7EA7\u5168\u90E8\u5DF2\u88C5").option("--dry-run", "\u9884\u89C8\u53D8\u66F4\uFF0C\u4E0D\u5199\u76D8").option(
2424
+ "--scope <scope>",
2425
+ "project | global\uFF08\u9ED8\u8BA4\u6309 cwd \u63A8\u65AD\uFF1Acwd \u662F\u9879\u76EE\u2192project\uFF1B\u5426\u5219 fallback \u5230\u5168\u5C40\uFF09"
2426
+ ).action(async (names, opts) => {
2204
2427
  try {
2205
2428
  const ide = detectIde();
2206
2429
  const cwd = ide.getProjectRoot();
2207
- const projectRoot = resolveSkillsMaintenanceRoot(cwd);
2430
+ const projectRoot = resolveUpdateRoot(cwd, opts.scope);
2208
2431
  if (projectRoot !== cwd) {
2209
2432
  logger.info(`Using global skills meta root: ${projectRoot}`);
2210
2433
  }
@@ -2249,13 +2472,13 @@ var updateCommand2 = new Command10("update").description(
2249
2472
  `Skills updated to v${result.version} (${result.updatedSkillIds.length} skill(s)).`
2250
2473
  );
2251
2474
  if (result.updatedSkillIds.length > 0) {
2252
- logger.info(
2253
- ` Updated: ${result.updatedSkillIds.join(", ")}`
2254
- );
2475
+ logger.info(` Updated: ${result.updatedSkillIds.join(", ")}`);
2255
2476
  }
2256
2477
  if (result.skippedSkillIds.length > 0) {
2257
2478
  logger.info(
2258
- ` Skipped: ${result.skippedSkillIds.join(", ")} (scope mismatch / removed upstream)`
2479
+ ` Skipped: ${result.skippedSkillIds.join(
2480
+ ", "
2481
+ )} (scope mismatch / removed upstream)`
2259
2482
  );
2260
2483
  }
2261
2484
  logger.info(` Created: ${summary.created}`);
@@ -2266,11 +2489,32 @@ var updateCommand2 = new Command10("update").description(
2266
2489
  }
2267
2490
  }
2268
2491
  } catch (err) {
2269
- logger.error(`Failed to update skills: ${err.message}`);
2492
+ logger.error(`Failed to update skills: ${getErrorMessage(err)}`);
2270
2493
  logger.debug(err.stack ?? "");
2271
2494
  process.exitCode = 1;
2272
2495
  }
2273
2496
  });
2497
+ function resolveUpdateRoot(cwd, scopeFlag) {
2498
+ if (scopeFlag === void 0) {
2499
+ return resolveSkillsMaintenanceRoot(cwd);
2500
+ }
2501
+ const scope = parseScope(scopeFlag);
2502
+ if (scope === "global") {
2503
+ const globalRoot = getGlobalMetaRoot();
2504
+ if (!isTeamixEvoProject(globalRoot)) {
2505
+ throw new Error(
2506
+ `No global skills installed at ${globalRoot}. Run "npx teamix-evo@latest skills add <id> --scope global" first.`
2507
+ );
2508
+ }
2509
+ return globalRoot;
2510
+ }
2511
+ if (!isTeamixEvoProject(cwd)) {
2512
+ throw new Error(
2513
+ `Current directory is not a teamix-evo project (.teamix-evo/config.json missing). Re-run without --scope, or use "--scope global".`
2514
+ );
2515
+ }
2516
+ return cwd;
2517
+ }
2274
2518
  function formatPlanItem(item) {
2275
2519
  const tag = item.action === "up-to-date" ? " =" : item.strategy === "frozen" ? " \u2298 " : item.strategy === "managed" ? " \u2295 " : " \u2192 ";
2276
2520
  const ver = item.action === "up-to-date" ? `v${item.current} (no change)` : `v${item.current} \u2192 v${item.next} [${item.strategy}]`;
@@ -2279,7 +2523,7 @@ function formatPlanItem(item) {
2279
2523
  async function printVersionBanner() {
2280
2524
  let cliVersion;
2281
2525
  try {
2282
- const pkg = require5("../package.json");
2526
+ const pkg = require4("../package.json");
2283
2527
  cliVersion = pkg.version;
2284
2528
  } catch {
2285
2529
  }
@@ -2299,15 +2543,12 @@ async function printVersionBanner() {
2299
2543
  // src/commands/skills/uninstall.ts
2300
2544
  import { Command as Command11 } from "commander";
2301
2545
  import * as prompts4 from "@clack/prompts";
2302
- import * as path13 from "path";
2546
+ import * as path14 from "path";
2303
2547
  import * as fs10 from "fs/promises";
2304
2548
  var SKILLS_PACKAGE3 = "@teamix-evo/skills";
2305
2549
  var uninstallCommand2 = new Command11("uninstall").description(
2306
2550
  "\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"
2307
- ).argument(
2308
- "[ids...]",
2309
- "\u53EF\u9009\uFF1A\u4EC5\u5378\u8F7D\u6307\u5B9A skill id\uFF1B\u7701\u7565\u5219\u5378\u8F7D\u6574\u4E2A skills \u5305"
2310
- ).option("-y, --yes", "\u8DF3\u8FC7\u786E\u8BA4").action(async (ids, opts) => {
2551
+ ).argument("[ids...]", "\u53EF\u9009\uFF1A\u4EC5\u5378\u8F7D\u6307\u5B9A skill id\uFF1B\u7701\u7565\u5219\u5378\u8F7D\u6574\u4E2A skills \u5305").option("-y, --yes", "\u8DF3\u8FC7\u786E\u8BA4").action(async (ids, opts) => {
2311
2552
  try {
2312
2553
  const ide = detectIde();
2313
2554
  const cwd = ide.getProjectRoot();
@@ -2322,7 +2563,7 @@ var uninstallCommand2 = new Command11("uninstall").description(
2322
2563
  }
2323
2564
  const installedManifest = await readInstalledManifest(projectRoot);
2324
2565
  const pkg = installedManifest?.installed.find(
2325
- (p) => p.package === SKILLS_PACKAGE3
2566
+ (p2) => p2.package === SKILLS_PACKAGE3
2326
2567
  );
2327
2568
  const resources = pkg?.resources ?? [];
2328
2569
  if (ids.length === 0) {
@@ -2345,7 +2586,7 @@ var uninstallCommand2 = new Command11("uninstall").description(
2345
2586
  opts
2346
2587
  });
2347
2588
  } catch (err) {
2348
- logger.error(`Failed to uninstall: ${err.message}`);
2589
+ logger.error(`Failed to uninstall: ${getErrorMessage(err)}`);
2349
2590
  logger.debug(err.stack ?? "");
2350
2591
  process.exitCode = 1;
2351
2592
  }
@@ -2356,11 +2597,11 @@ async function runFullUninstall(args) {
2356
2597
  `Will remove ${resources.length} skill file(s) installed by ${SKILLS_PACKAGE3}.`
2357
2598
  );
2358
2599
  if (!opts.yes) {
2359
- const confirm5 = await prompts4.confirm({
2600
+ const confirm8 = await prompts4.confirm({
2360
2601
  message: "\u786E\u8BA4\u5378\u8F7D\uFF1F\u6B64\u64CD\u4F5C\u4F1A\u5220\u9664\u4E0A\u8FF0\u6587\u4EF6\u3002",
2361
2602
  initialValue: false
2362
2603
  });
2363
- if (prompts4.isCancel(confirm5) || !confirm5) {
2604
+ if (prompts4.isCancel(confirm8) || !confirm8) {
2364
2605
  logger.info("Cancelled.");
2365
2606
  return;
2366
2607
  }
@@ -2373,9 +2614,7 @@ async function runFullUninstall(args) {
2373
2614
  await fs10.rm(skillsRoot, { recursive: true, force: true });
2374
2615
  logger.debug(`Removed source dir ${skillsRoot}`);
2375
2616
  } catch (err) {
2376
- logger.warn(
2377
- `Failed to remove ${skillsRoot}: ${err.message}`
2378
- );
2617
+ logger.warn(`Failed to remove ${skillsRoot}: ${getErrorMessage(err)}`);
2379
2618
  }
2380
2619
  const skillNames = collectSkillNames({
2381
2620
  fromSources: sourceSkillNames,
@@ -2390,7 +2629,7 @@ async function runFullUninstall(args) {
2390
2629
  );
2391
2630
  if (installedManifest && pkg) {
2392
2631
  installedManifest.installed = installedManifest.installed.filter(
2393
- (p) => p.package !== SKILLS_PACKAGE3
2632
+ (p2) => p2.package !== SKILLS_PACKAGE3
2394
2633
  );
2395
2634
  await writeInstalledManifest(projectRoot, installedManifest);
2396
2635
  }
@@ -2398,10 +2637,10 @@ async function runFullUninstall(args) {
2398
2637
  await writeProjectConfig(projectRoot, config);
2399
2638
  logger.success(`Uninstalled ${SKILLS_PACKAGE3}`);
2400
2639
  logger.info(` Removed: ${removed.length} files`);
2401
- logger.info(` Source: ${path13.relative(projectRoot, skillsRoot)} (cleaned)`);
2640
+ logger.info(` Source: ${path14.relative(projectRoot, skillsRoot)} (cleaned)`);
2402
2641
  if (cleanedMirrorDirs.length > 0) {
2403
2642
  logger.info(
2404
- ` Mirrors: ${cleanedMirrorDirs.map((d) => path13.relative(projectRoot, d)).join(", ")} (cleaned)`
2643
+ ` Mirrors: ${cleanedMirrorDirs.map((d) => path14.relative(projectRoot, d)).join(", ")} (cleaned)`
2405
2644
  );
2406
2645
  }
2407
2646
  }
@@ -2425,11 +2664,11 @@ async function runPartialUninstall(args) {
2425
2664
  `Will remove ${matched.length} skill(s): ${matched.join(", ")} (${toRemove.length} file(s)).`
2426
2665
  );
2427
2666
  if (!opts.yes) {
2428
- const confirm5 = await prompts4.confirm({
2667
+ const confirm8 = await prompts4.confirm({
2429
2668
  message: "\u786E\u8BA4\u5378\u8F7D\uFF1F\u6B64\u64CD\u4F5C\u4F1A\u5220\u9664\u4E0A\u8FF0\u6587\u4EF6\u3002",
2430
2669
  initialValue: false
2431
2670
  });
2432
- if (prompts4.isCancel(confirm5) || !confirm5) {
2671
+ if (prompts4.isCancel(confirm8) || !confirm8) {
2433
2672
  logger.info("Cancelled.");
2434
2673
  return;
2435
2674
  }
@@ -2442,7 +2681,7 @@ async function runPartialUninstall(args) {
2442
2681
  await fs10.rm(dir, { recursive: true, force: true });
2443
2682
  logger.debug(`Removed source dir ${dir}`);
2444
2683
  } catch (err) {
2445
- logger.warn(`Failed to remove ${dir}: ${err.message}`);
2684
+ logger.warn(`Failed to remove ${dir}: ${getErrorMessage(err)}`);
2446
2685
  }
2447
2686
  }
2448
2687
  const config = await readProjectConfig(projectRoot);
@@ -2461,13 +2700,11 @@ async function runPartialUninstall(args) {
2461
2700
  pkg.resources = resources.filter((r) => !matched.includes(skillIdOf(r)));
2462
2701
  await writeInstalledManifest(projectRoot, installedManifest);
2463
2702
  }
2464
- logger.success(
2465
- `Removed ${matched.length} skill(s): ${matched.join(", ")}`
2466
- );
2703
+ logger.success(`Removed ${matched.length} skill(s): ${matched.join(", ")}`);
2467
2704
  logger.info(` Files: ${removed.length}`);
2468
2705
  if (cleanedMirrorDirs.length > 0) {
2469
2706
  logger.info(
2470
- ` Mirrors: ${cleanedMirrorDirs.map((d) => path13.relative(projectRoot, d)).join(", ")} (cleaned)`
2707
+ ` Mirrors: ${cleanedMirrorDirs.map((d) => path14.relative(projectRoot, d)).join(", ")} (cleaned)`
2471
2708
  );
2472
2709
  }
2473
2710
  if (missing.length > 0) {
@@ -2510,11 +2747,11 @@ async function removeMirrorDirs(projectRoot, scope, ides, skillNames) {
2510
2747
  removed.push(dir);
2511
2748
  logger.debug(`Removed mirror dir ${dir}`);
2512
2749
  } catch (err) {
2513
- logger.warn(`Failed to remove ${dir}: ${err.message}`);
2750
+ logger.warn(`Failed to remove ${dir}: ${getErrorMessage(err)}`);
2514
2751
  }
2515
2752
  }
2516
2753
  if (removed.length > 0) {
2517
- const skillsParent = path13.dirname(
2754
+ const skillsParent = path14.dirname(
2518
2755
  adapter.getSkillTargetDir("placeholder", targetScope, projectRoot)
2519
2756
  );
2520
2757
  try {
@@ -2549,15 +2786,15 @@ function dedupe(values) {
2549
2786
  import { Command as Command12 } from "commander";
2550
2787
 
2551
2788
  // src/core/skills-sync.ts
2552
- import * as path14 from "path";
2789
+ import * as path15 from "path";
2553
2790
  import * as fs11 from "fs/promises";
2554
- import { createRequire as createRequire5 } from "module";
2791
+ import { createRequire as createRequire4 } from "module";
2555
2792
  import { loadSkillsPackageManifest as loadSkillsPackageManifest2 } from "@teamix-evo/registry";
2556
- var require6 = createRequire5(import.meta.url);
2793
+ var require5 = createRequire4(import.meta.url);
2557
2794
  async function readSkillMetaFromUpstream(skillId) {
2558
2795
  try {
2559
- const pkgJson = require6.resolve("@teamix-evo/skills/package.json");
2560
- const packageRoot = path14.dirname(pkgJson);
2796
+ const pkgJson = require5.resolve("@teamix-evo/skills/package.json");
2797
+ const packageRoot = path15.dirname(pkgJson);
2561
2798
  const manifest = await loadSkillsPackageManifest2(packageRoot);
2562
2799
  const entry = manifest.skills.find((s) => s.id === skillId);
2563
2800
  if (!entry) return null;
@@ -2635,10 +2872,10 @@ async function runSkillsSync(options) {
2635
2872
  missingSourceIds: missing
2636
2873
  };
2637
2874
  }
2638
- async function dirExists(p) {
2875
+ async function dirExists(p2) {
2639
2876
  try {
2640
- const stat4 = await fs11.stat(p);
2641
- return stat4.isDirectory();
2877
+ const stat7 = await fs11.stat(p2);
2878
+ return stat7.isDirectory();
2642
2879
  } catch {
2643
2880
  return false;
2644
2881
  }
@@ -2646,7 +2883,7 @@ async function dirExists(p) {
2646
2883
  async function refreshMirrorRecords(projectRoot, newMirrorRecords) {
2647
2884
  const installed = await readInstalledManifest(projectRoot);
2648
2885
  if (!installed) return;
2649
- const pkg = installed.installed.find((p) => p.package === SKILLS_PACKAGE_DEFAULT);
2886
+ const pkg = installed.installed.find((p2) => p2.package === SKILLS_PACKAGE_DEFAULT);
2650
2887
  if (!pkg) return;
2651
2888
  const sourceOnly = pkg.resources.filter((r) => r.ide === void 0);
2652
2889
  pkg.resources = [...sourceOnly, ...newMirrorRecords];
@@ -2663,10 +2900,7 @@ var syncCommand = new Command12("sync").description(
2663
2900
  ).option(
2664
2901
  "--ide <list>",
2665
2902
  "\u9017\u53F7\u5206\u9694\u7684 IDE \u5217\u8868\uFF08\u8986\u76D6 lock \u4E2D\u8BB0\u5F55\u7684 mirroredTo\uFF09"
2666
- ).option(
2667
- "--scope <scope>",
2668
- "project | global\uFF08\u8986\u76D6 lock \u4E2D\u8BB0\u5F55\u7684 scope\uFF09"
2669
- ).action(async (names, opts) => {
2903
+ ).option("--scope <scope>", "project | global\uFF08\u8986\u76D6 lock \u4E2D\u8BB0\u5F55\u7684 scope\uFF09").action(async (names, opts) => {
2670
2904
  try {
2671
2905
  const ide = detectIde();
2672
2906
  const cwd = ide.getProjectRoot();
@@ -2702,7 +2936,7 @@ var syncCommand = new Command12("sync").description(
2702
2936
  );
2703
2937
  }
2704
2938
  } catch (err) {
2705
- logger.error(`Failed to sync skills: ${err.message}`);
2939
+ logger.error(`Failed to sync skills: ${getErrorMessage(err)}`);
2706
2940
  logger.debug(err.stack ?? "");
2707
2941
  process.exitCode = 1;
2708
2942
  }
@@ -2712,7 +2946,7 @@ var syncCommand = new Command12("sync").description(
2712
2946
  import { Command as Command13 } from "commander";
2713
2947
 
2714
2948
  // src/core/skills-doctor.ts
2715
- import * as path15 from "path";
2949
+ import * as path16 from "path";
2716
2950
  import * as fs12 from "fs/promises";
2717
2951
  async function runSkillsDoctor(options) {
2718
2952
  const { projectRoot } = options;
@@ -2735,7 +2969,7 @@ async function runSkillsDoctor(options) {
2735
2969
  const sourceFiles = await walkDir(sourceDir);
2736
2970
  const sourceContents = /* @__PURE__ */ new Map();
2737
2971
  for (const f of sourceFiles) {
2738
- const rel2 = path15.relative(sourceDir, f);
2972
+ const rel2 = path16.relative(sourceDir, f);
2739
2973
  sourceContents.set(rel2, await fs12.readFile(f, "utf-8"));
2740
2974
  }
2741
2975
  for (const ide of entry.mirroredTo) {
@@ -2757,7 +2991,7 @@ async function runSkillsDoctor(options) {
2757
2991
  continue;
2758
2992
  }
2759
2993
  for (const [rel2, sourceContent] of sourceContents.entries()) {
2760
- const mirrorFile = path15.join(mirrorDir, rel2);
2994
+ const mirrorFile = path16.join(mirrorDir, rel2);
2761
2995
  if (!await fileExists(mirrorFile)) {
2762
2996
  findings.push({
2763
2997
  kind: "missing-mirror",
@@ -2788,19 +3022,17 @@ async function runSkillsDoctor(options) {
2788
3022
  findings
2789
3023
  };
2790
3024
  }
2791
- async function dirExists2(p) {
3025
+ async function dirExists2(p2) {
2792
3026
  try {
2793
- const stat4 = await fs12.stat(p);
2794
- return stat4.isDirectory();
3027
+ const stat7 = await fs12.stat(p2);
3028
+ return stat7.isDirectory();
2795
3029
  } catch {
2796
3030
  return false;
2797
3031
  }
2798
3032
  }
2799
3033
 
2800
3034
  // src/commands/skills/doctor.ts
2801
- var doctorCommand = new Command13("doctor").description(
2802
- "\u68C0\u67E5 .teamix-evo/skills/ \u6E90\u4E0E IDE \u955C\u50CF\u662F\u5426\u6F02\u79FB\uFF1B\u63D0\u793A\u5982\u4F55\u4FEE\u590D"
2803
- ).action(async () => {
3035
+ var doctorCommand = new Command13("doctor").description("\u68C0\u67E5 .teamix-evo/skills/ \u6E90\u4E0E IDE \u955C\u50CF\u662F\u5426\u6F02\u79FB\uFF1B\u63D0\u793A\u5982\u4F55\u4FEE\u590D").action(async () => {
2804
3036
  try {
2805
3037
  const ide = detectIde();
2806
3038
  const cwd = ide.getProjectRoot();
@@ -2830,7 +3062,7 @@ var doctorCommand = new Command13("doctor").description(
2830
3062
  }
2831
3063
  process.exitCode = 1;
2832
3064
  } catch (err) {
2833
- logger.error(`Failed to run doctor: ${err.message}`);
3065
+ logger.error(`Failed to run doctor: ${getErrorMessage(err)}`);
2834
3066
  logger.debug(err.stack ?? "");
2835
3067
  process.exitCode = 1;
2836
3068
  }
@@ -2849,7 +3081,7 @@ skillsCommand.addCommand(doctorCommand);
2849
3081
  skillsCommand.addCommand(uninstallCommand2);
2850
3082
 
2851
3083
  // src/commands/ui/index.ts
2852
- import { Command as Command18 } from "commander";
3084
+ import { Command as Command19 } from "commander";
2853
3085
 
2854
3086
  // src/commands/ui/init.ts
2855
3087
  import { Command as Command15 } from "commander";
@@ -2885,8 +3117,8 @@ async function runUiInit(options) {
2885
3117
  const tsx = options.tsx ?? true;
2886
3118
  const rsc = options.rsc ?? false;
2887
3119
  const config = existingConfig ?? {
2888
- $schema: "https://teamix-evo.dev/schema/config/v1.json",
2889
- schemaVersion: 1,
3120
+ $schema: "https://teamix-evo.dev/schema/config/v2.json",
3121
+ schemaVersion: 2,
2890
3122
  ide: ideIdent,
2891
3123
  packages: {}
2892
3124
  };
@@ -2952,8 +3184,12 @@ var initCommand3 = new Command15("init").description(
2952
3184
  logger.info("");
2953
3185
  logger.info("Next: `npx teamix-evo@latest ui add button`");
2954
3186
  } catch (err) {
2955
- logger.error(`Failed to initialize ui: ${err.message}`);
2956
- logger.debug(err.stack ?? "");
3187
+ if (err instanceof CancelledError) {
3188
+ logger.info("ui init \u5DF2\u53D6\u6D88\u3002");
3189
+ return;
3190
+ }
3191
+ logger.error(`Failed to initialize ui: ${getErrorMessage(err)}`);
3192
+ logger.debug(err instanceof Error ? err.stack ?? "" : "");
2957
3193
  process.exitCode = 1;
2958
3194
  }
2959
3195
  });
@@ -2978,37 +3214,37 @@ async function resolveConfig(opts) {
2978
3214
  message: "components \u8DEF\u5F84\uFF08\u6CE8\u5165\u6309\u94AE\u7B49\u7EC4\u4EF6\u6E90\u7801\u7684\u76EE\u5F55\uFF09",
2979
3215
  initialValue: opts.components ?? DEFAULT_UI_ALIASES.components
2980
3216
  });
2981
- if (prompts5.isCancel(components)) throw new Error("Cancelled by user.");
3217
+ if (prompts5.isCancel(components)) throw new CancelledError();
2982
3218
  const hooks = await prompts5.text({
2983
3219
  message: "hooks \u8DEF\u5F84",
2984
3220
  initialValue: opts.hooks ?? DEFAULT_UI_ALIASES.hooks
2985
3221
  });
2986
- if (prompts5.isCancel(hooks)) throw new Error("Cancelled by user.");
3222
+ if (prompts5.isCancel(hooks)) throw new CancelledError();
2987
3223
  const utils = await prompts5.text({
2988
3224
  message: "utils \u8DEF\u5F84\uFF08cn \u7B49\u5DE5\u5177\uFF09",
2989
3225
  initialValue: opts.utils ?? DEFAULT_UI_ALIASES.utils
2990
3226
  });
2991
- if (prompts5.isCancel(utils)) throw new Error("Cancelled by user.");
3227
+ if (prompts5.isCancel(utils)) throw new CancelledError();
2992
3228
  const lib = await prompts5.text({
2993
3229
  message: "lib \u8DEF\u5F84\uFF08\u5171\u4EAB\u4EE3\u7801\u6839\uFF09",
2994
3230
  initialValue: opts.lib ?? DEFAULT_UI_ALIASES.lib
2995
3231
  });
2996
- if (prompts5.isCancel(lib)) throw new Error("Cancelled by user.");
3232
+ if (prompts5.isCancel(lib)) throw new CancelledError();
2997
3233
  const iconLibrary = await prompts5.text({
2998
3234
  message: "icon \u5E93\uFF08\u58F0\u660E\u6027\uFF0C\u7EC4\u4EF6\u6E90\u7801\u5DF2 hardcode lucide-react\uFF09",
2999
3235
  initialValue: opts.iconLibrary ?? "lucide"
3000
3236
  });
3001
- if (prompts5.isCancel(iconLibrary)) throw new Error("Cancelled by user.");
3237
+ if (prompts5.isCancel(iconLibrary)) throw new CancelledError();
3002
3238
  const tsxAns = await prompts5.confirm({
3003
3239
  message: "\u4F7F\u7528 TSX\uFF1F",
3004
3240
  initialValue: opts.tsx ?? true
3005
3241
  });
3006
- if (prompts5.isCancel(tsxAns)) throw new Error("Cancelled by user.");
3242
+ if (prompts5.isCancel(tsxAns)) throw new CancelledError();
3007
3243
  const rscAns = await prompts5.confirm({
3008
3244
  message: "\u4F7F\u7528 React Server Components\uFF1F",
3009
3245
  initialValue: opts.rsc ?? false
3010
3246
  });
3011
- if (prompts5.isCancel(rscAns)) throw new Error("Cancelled by user.");
3247
+ if (prompts5.isCancel(rscAns)) throw new CancelledError();
3012
3248
  return {
3013
3249
  aliases: {
3014
3250
  components,
@@ -3028,21 +3264,21 @@ async function resolveConfig(opts) {
3028
3264
  import { Command as Command16 } from "commander";
3029
3265
 
3030
3266
  // src/core/ui-client.ts
3031
- import * as path16 from "path";
3267
+ import * as path17 from "path";
3032
3268
  import * as fs13 from "fs/promises";
3033
- import { createRequire as createRequire6 } from "module";
3269
+ import { createRequire as createRequire5 } from "module";
3034
3270
  import { loadUiPackageManifest } from "@teamix-evo/registry";
3035
- var require7 = createRequire6(import.meta.url);
3271
+ var require6 = createRequire5(import.meta.url);
3036
3272
  function resolvePackageRoot2(packageName) {
3037
- const pkgJsonPath = require7.resolve(`${packageName}/package.json`);
3038
- return path16.dirname(pkgJsonPath);
3273
+ const pkgJsonPath = require6.resolve(`${packageName}/package.json`);
3274
+ return path17.dirname(pkgJsonPath);
3039
3275
  }
3040
3276
  async function loadUiData(packageName) {
3041
3277
  const packageRoot = resolvePackageRoot2(packageName);
3042
3278
  logger.debug(`Resolved ui package root: ${packageRoot}`);
3043
3279
  const manifest = await loadUiPackageManifest(packageRoot);
3044
3280
  let data = {};
3045
- const dataPath = path16.join(packageRoot, "_data.json");
3281
+ const dataPath = path17.join(packageRoot, "_data.json");
3046
3282
  try {
3047
3283
  const raw = await fs13.readFile(dataPath, "utf-8");
3048
3284
  data = JSON.parse(raw);
@@ -3056,7 +3292,7 @@ async function loadUiData(packageName) {
3056
3292
  }
3057
3293
 
3058
3294
  // src/core/ui-installer.ts
3059
- import * as path17 from "path";
3295
+ import * as path18 from "path";
3060
3296
  import * as fs14 from "fs/promises";
3061
3297
  import { resolveUiEntryOrder } from "@teamix-evo/registry";
3062
3298
 
@@ -3130,7 +3366,7 @@ async function installUiEntries(options) {
3130
3366
  continue;
3131
3367
  }
3132
3368
  const rootForEntry = entryPackageRoot?.get(entry.id) ?? packageRoot;
3133
- const sourceAbs = path17.resolve(rootForEntry, file.source);
3369
+ const sourceAbs = path18.resolve(rootForEntry, file.source);
3134
3370
  const raw = await fs14.readFile(sourceAbs, "utf-8");
3135
3371
  const transformed = rewriteImports(raw, aliases);
3136
3372
  await writeFileSafe(targetAbs, transformed);
@@ -3159,10 +3395,10 @@ function resolveTargetPath(projectRoot, aliases, entry, file) {
3159
3395
  `Entry "${entry.id}" requires alias "${file.targetAlias}" but it is not configured.`
3160
3396
  );
3161
3397
  }
3162
- return path17.join(projectRoot, aliasDir, file.targetName);
3398
+ return path18.join(projectRoot, aliasDir, file.targetName);
3163
3399
  }
3164
3400
  function rel(projectRoot, abs) {
3165
- return path17.relative(projectRoot, abs);
3401
+ return path18.relative(projectRoot, abs);
3166
3402
  }
3167
3403
 
3168
3404
  // src/core/ui-add.ts
@@ -3213,7 +3449,7 @@ async function runUiAdd(options) {
3213
3449
  const installed = await readInstalledManifest(
3214
3450
  projectRoot
3215
3451
  ) ?? { schemaVersion: 1, installed: [] };
3216
- const idx = installed.installed.findIndex((p) => p.package === packageName);
3452
+ const idx = installed.installed.findIndex((p2) => p2.package === packageName);
3217
3453
  const prior = idx >= 0 ? installed.installed[idx] : null;
3218
3454
  const mergedResources = mergeResources(
3219
3455
  prior?.resources ?? [],
@@ -3281,7 +3517,7 @@ var addCommand2 = new Command16("add").description(
3281
3517
  logger.info(` # or: npm install ${installCmd}`);
3282
3518
  }
3283
3519
  } catch (err) {
3284
- const message = err.message;
3520
+ const message = getErrorMessage(err);
3285
3521
  if (message.startsWith("UI not initialized")) {
3286
3522
  logger.error(
3287
3523
  "UI not initialized. Run `npx teamix-evo ui init` first."
@@ -3307,7 +3543,7 @@ async function runUiList(options) {
3307
3543
  const installedManifest = await readInstalledManifest(projectRoot);
3308
3544
  const installedIds = /* @__PURE__ */ new Set();
3309
3545
  const uiPkg = installedManifest?.installed.find(
3310
- (p) => p.package === packageName
3546
+ (p2) => p2.package === packageName
3311
3547
  );
3312
3548
  for (const r of uiPkg?.resources ?? []) {
3313
3549
  const colon = r.id.indexOf(":");
@@ -3378,72 +3614,695 @@ var listCommand3 = new Command17("list").description("\u5217\u51FA @teamix-evo/u
3378
3614
  `Total: ${total} entr${total === 1 ? "y" : "ies"}, ${installedCount} installed.`
3379
3615
  );
3380
3616
  } catch (err) {
3381
- logger.error(`Failed to list ui entries: ${err.message}`);
3617
+ logger.error(`Failed to list ui entries: ${getErrorMessage(err)}`);
3382
3618
  logger.debug(err.stack ?? "");
3383
3619
  process.exitCode = 1;
3384
3620
  }
3385
3621
  }
3386
3622
  );
3387
3623
 
3388
- // src/commands/ui/index.ts
3389
- var uiCommand = new Command18("ui").description(
3390
- "\u7BA1\u7406 teamix-evo ui \u7EC4\u4EF6\uFF08\u6E90\u7801\u6CE8\u5165\u5F0F\u5B89\u88C5\uFF0Cshadcn \u98CE\u683C\uFF09"
3391
- );
3392
- uiCommand.addCommand(initCommand3);
3393
- uiCommand.addCommand(addCommand2);
3394
- uiCommand.addCommand(listCommand3);
3395
-
3396
- // src/commands/biz-ui/index.ts
3397
- import { Command as Command22 } from "commander";
3398
-
3399
- // src/commands/biz-ui/add.ts
3400
- import { Command as Command19 } from "commander";
3624
+ // src/commands/_upgrade-command-factory.ts
3625
+ import { Command as Command18 } from "commander";
3401
3626
 
3402
- // src/core/variant-ui-add.ts
3403
- import * as path18 from "path";
3404
- import { createRequire as createRequire7 } from "module";
3627
+ // src/core/ui-upgrade.ts
3628
+ import * as path21 from "path";
3629
+ import { createRequire as createRequire6 } from "module";
3405
3630
  import {
3406
3631
  loadUiPackageManifest as loadUiPackageManifest2,
3407
- loadVariantUiPackageCatalog,
3408
3632
  loadVariantUiPackageManifest
3409
3633
  } from "@teamix-evo/registry";
3410
- var require8 = createRequire7(import.meta.url);
3411
- function resolvePackageRoot3(packageName) {
3412
- const pkgJsonPath = require8.resolve(`${packageName}/package.json`);
3413
- return path18.dirname(pkgJsonPath);
3634
+
3635
+ // src/core/ui-upgrade-detector.ts
3636
+ import * as fs15 from "fs/promises";
3637
+ import * as path19 from "path";
3638
+ var PACKAGE_NAME = {
3639
+ ui: "@teamix-evo/ui",
3640
+ "biz-ui": "@teamix-evo/biz-ui"
3641
+ };
3642
+ var ALIAS_KEY = {
3643
+ ui: "components",
3644
+ "biz-ui": "business"
3645
+ };
3646
+ var COMPONENT_FILE_RE = /\.(tsx|ts)$/;
3647
+ var SKIP_FILENAMES = /* @__PURE__ */ new Set(["index.ts", "index.tsx"]);
3648
+ async function detectComponentLineage(options) {
3649
+ const { projectRoot, category } = options;
3650
+ const config = options.config ?? await readProjectConfig(projectRoot);
3651
+ const installed = options.installed ?? await readInstalledManifest(projectRoot);
3652
+ const installDir = resolveInstallDir(category, config);
3653
+ const installDirAbs = path19.join(projectRoot, installDir);
3654
+ const installDirExists = await directoryExists(installDirAbs);
3655
+ const hasComponentsJson = await fileExists(
3656
+ path19.join(projectRoot, "components.json")
3657
+ );
3658
+ const installedPkg = findInstalledPackage(installed, PACKAGE_NAME[category]);
3659
+ const registeredIds = installedPkg ? extractIds(installedPkg).sort() : [];
3660
+ const onDiskIds = installDirExists ? await listComponentIds(installDirAbs) : [];
3661
+ const registeredSet = new Set(registeredIds);
3662
+ const unregisteredIds = onDiskIds.filter((id) => !registeredSet.has(id)).sort();
3663
+ const lineage = classifyLineage({
3664
+ hasInstalled: installedPkg !== null,
3665
+ hasComponentsJson,
3666
+ onDiskIds,
3667
+ unregisteredIds
3668
+ });
3669
+ return {
3670
+ category,
3671
+ lineage,
3672
+ installDir,
3673
+ installDirExists,
3674
+ hasComponentsJson,
3675
+ registeredIds,
3676
+ unregisteredIds,
3677
+ installedVersion: installedPkg?.version ?? null,
3678
+ installedVariant: installedPkg?.variant ?? null
3679
+ };
3414
3680
  }
3415
- async function runVariantUiAdd(packageName, options) {
3416
- const { projectRoot, variant, ids, overwrite } = options;
3417
- const fullPackageName = options.packageName ?? `@teamix-evo/${packageName}`;
3418
- if (ids.length === 0) {
3419
- throw new Error("At least one entry id must be provided.");
3681
+ function resolveInstallDir(category, config) {
3682
+ const aliasMap = config?.packages?.ui?.aliases ?? config?.packages?.["biz-ui"]?.aliases ?? DEFAULT_UI_ALIASES;
3683
+ const key = ALIAS_KEY[category];
3684
+ return aliasMap[key] ?? DEFAULT_UI_ALIASES[key];
3685
+ }
3686
+ function extractIds(pkg) {
3687
+ const ids = /* @__PURE__ */ new Set();
3688
+ for (const r of pkg.resources) {
3689
+ const colon = r.id.indexOf(":");
3690
+ ids.add(colon >= 0 ? r.id.slice(0, colon) : r.id);
3420
3691
  }
3421
- const config = await readProjectConfig(projectRoot);
3422
- const uiCfg = config?.packages?.ui;
3423
- if (!config || !uiCfg?.aliases) {
3424
- throw new Error(
3425
- `UI not initialized. Run \`teamix-evo ui init\` first \u2014 \`${packageName} add\` writes into the same alias map (business / templates).`
3426
- );
3692
+ return [...ids];
3693
+ }
3694
+ async function directoryExists(p2) {
3695
+ try {
3696
+ const stat7 = await fs15.stat(p2);
3697
+ return stat7.isDirectory();
3698
+ } catch {
3699
+ return false;
3427
3700
  }
3428
- const packageRoot = options.packageRoot ?? resolvePackageRoot3(fullPackageName);
3429
- const catalog = await loadVariantUiPackageCatalog(packageRoot);
3430
- if (!catalog.variants.some((v) => v.name === variant)) {
3431
- const known = catalog.variants.map((v) => v.name).join(", ");
3432
- throw new Error(
3433
- `Variant "${variant}" not found in ${fullPackageName}. Known variants: ${known}. Hint: \`teamix-evo ${packageName} list-variants\` shows all.`
3434
- );
3701
+ }
3702
+ async function listComponentIds(installDirAbs) {
3703
+ const entries = await fs15.readdir(installDirAbs, { withFileTypes: true });
3704
+ const ids = [];
3705
+ for (const e of entries) {
3706
+ if (!e.isFile()) continue;
3707
+ if (SKIP_FILENAMES.has(e.name)) continue;
3708
+ if (!COMPONENT_FILE_RE.test(e.name)) continue;
3709
+ ids.push(e.name.replace(COMPONENT_FILE_RE, ""));
3710
+ }
3711
+ return ids.sort();
3712
+ }
3713
+ function classifyLineage(args) {
3714
+ const { hasInstalled, hasComponentsJson, onDiskIds, unregisteredIds } = args;
3715
+ if (hasInstalled) {
3716
+ return unregisteredIds.length === 0 ? "teamix-evo" : "mixed";
3435
3717
  }
3436
- const variantDir = path18.join(packageRoot, "variants", variant);
3437
- const variantManifest = await loadVariantUiPackageManifest(variantDir);
3438
- const knownIds = new Set(variantManifest.entries.map((e) => e.id));
3439
- const unknown = ids.filter((id) => !knownIds.has(id));
3718
+ if (onDiskIds.length === 0) return "absent";
3719
+ return hasComponentsJson ? "shadcn-native" : "custom-only";
3720
+ }
3721
+
3722
+ // src/core/ui-upgrade-staging.ts
3723
+ import * as path20 from "path";
3724
+ var TEAMIX_DIR4 = ".teamix-evo";
3725
+ var STAGING_DIR = ".upgrade-staging";
3726
+ var PACKAGE_NAME2 = {
3727
+ ui: "@teamix-evo/ui",
3728
+ "biz-ui": "@teamix-evo/biz-ui"
3729
+ };
3730
+ function isoToFsSafe2(iso) {
3731
+ return iso.replace(/[:.]/g, "-");
3732
+ }
3733
+ async function buildUiUpgradeStaging(options) {
3734
+ const { lineageReport, category } = options;
3735
+ if (lineageReport.lineage !== "teamix-evo" && lineageReport.lineage !== "mixed") {
3736
+ return null;
3737
+ }
3738
+ const installed = options.installed ?? await readInstalledManifest(options.projectRoot);
3739
+ const installedPkg = findInstalledPackage(installed, PACKAGE_NAME2[category]);
3740
+ if (!installedPkg) return null;
3741
+ const isoTs = options.isoTs ?? (/* @__PURE__ */ new Date()).toISOString();
3742
+ const fsTs = isoToFsSafe2(isoTs);
3743
+ const stagingDir = path20.join(
3744
+ options.projectRoot,
3745
+ TEAMIX_DIR4,
3746
+ STAGING_DIR,
3747
+ `${category}-${fsTs}`
3748
+ );
3749
+ const entryMap = new Map(
3750
+ options.manifest.entries.map((e) => [e.id, e])
3751
+ );
3752
+ const resByEntryId = collectResourcesByEntry(installedPkg.resources);
3753
+ const onlyIds = options.onlyIds && options.onlyIds.length > 0 ? new Set(options.onlyIds) : null;
3754
+ const entries = [];
3755
+ for (const id of lineageReport.registeredIds) {
3756
+ if (onlyIds && !onlyIds.has(id)) continue;
3757
+ const built = await processRegistered({
3758
+ id,
3759
+ entry: entryMap.get(id),
3760
+ resource: resByEntryId.get(id),
3761
+ packageRoot: options.packageRoot,
3762
+ entryPackageRoot: options.entryPackageRoot,
3763
+ aliases: options.aliases,
3764
+ stagingDir,
3765
+ projectRoot: options.projectRoot,
3766
+ category,
3767
+ sourceVersion: options.manifest.version
3768
+ });
3769
+ if (built) entries.push(built);
3770
+ }
3771
+ for (const id of lineageReport.unregisteredIds) {
3772
+ if (onlyIds && !onlyIds.has(id)) continue;
3773
+ const built = await processForeign({
3774
+ id,
3775
+ installDirAbs: path20.join(options.projectRoot, lineageReport.installDir),
3776
+ stagingDir,
3777
+ projectRoot: options.projectRoot,
3778
+ category
3779
+ });
3780
+ if (built) entries.push(built);
3781
+ }
3782
+ if (entries.length === 0) return null;
3783
+ const byRisk = aggregateByRisk(entries);
3784
+ const manifestOut = {
3785
+ schemaVersion: 1,
3786
+ ts: isoTs,
3787
+ package: category,
3788
+ trigger: options.trigger,
3789
+ variant: lineageReport.installedVariant ?? "_flat",
3790
+ fromVersion: lineageReport.installedVersion ?? "",
3791
+ toVersion: options.manifest.version,
3792
+ lineage: lineageReport.lineage,
3793
+ summary: { total: entries.length, byRisk },
3794
+ entries
3795
+ };
3796
+ await ensureDir(stagingDir);
3797
+ await writeFileSafe(
3798
+ path20.join(stagingDir, "meta.json"),
3799
+ JSON.stringify(manifestOut, null, 2) + "\n"
3800
+ );
3801
+ return { stagingDir, manifest: manifestOut };
3802
+ }
3803
+ async function processRegistered(args) {
3804
+ const { id, entry, resource, stagingDir, projectRoot, category } = args;
3805
+ if (!resource) return null;
3806
+ const currentSource = await readFileOrNull(resource.target);
3807
+ if (currentSource === null) {
3808
+ return buildBreakingEntry({
3809
+ id,
3810
+ category,
3811
+ resource,
3812
+ projectRoot,
3813
+ stagingDir,
3814
+ currentSource: "",
3815
+ hint: "installed file missing on disk"
3816
+ });
3817
+ }
3818
+ if (!entry) {
3819
+ return buildBreakingEntry({
3820
+ id,
3821
+ category,
3822
+ resource,
3823
+ projectRoot,
3824
+ stagingDir,
3825
+ currentSource,
3826
+ hint: "entry removed in upstream package"
3827
+ });
3828
+ }
3829
+ const file = entry.files[0];
3830
+ if (!file) return null;
3831
+ const rootForEntry = args.entryPackageRoot?.get(id) ?? args.packageRoot;
3832
+ const sourceAbs = path20.resolve(rootForEntry, file.source);
3833
+ const raw = await readFileOrNull(sourceAbs);
3834
+ if (raw === null) {
3835
+ return null;
3836
+ }
3837
+ const incomingTransformed = rewriteImports(raw, args.aliases);
3838
+ const incomingHash = computeHash(incomingTransformed);
3839
+ const currentExt = path20.extname(resource.target) || ".tsx";
3840
+ const incomingExt = path20.extname(file.targetName) || currentExt;
3841
+ const currentRel = `${id}/current${currentExt}`;
3842
+ const incomingRel = `${id}/incoming${incomingExt}`;
3843
+ await writeFileSafe(path20.join(stagingDir, currentRel), currentSource);
3844
+ await writeFileSafe(path20.join(stagingDir, incomingRel), incomingTransformed);
3845
+ const diff = classifyRisk({
3846
+ currentHash: resource.hash,
3847
+ incomingHash,
3848
+ currentSource,
3849
+ incomingSource: incomingTransformed,
3850
+ multiFile: entry.files.length > 1
3851
+ });
3852
+ return {
3853
+ id,
3854
+ category,
3855
+ current: {
3856
+ target: path20.relative(projectRoot, resource.target),
3857
+ hash: resource.hash,
3858
+ sourceLineage: "teamix-evo"
3859
+ },
3860
+ incoming: {
3861
+ sourceVersion: args.sourceVersion,
3862
+ hash: incomingHash,
3863
+ relPath: incomingRel
3864
+ },
3865
+ diff
3866
+ };
3867
+ }
3868
+ async function processForeign(args) {
3869
+ const { id, installDirAbs, stagingDir, projectRoot, category } = args;
3870
+ const tsx = path20.join(installDirAbs, `${id}.tsx`);
3871
+ const ts = path20.join(installDirAbs, `${id}.ts`);
3872
+ const target = await fileExists(tsx) ? tsx : await fileExists(ts) ? ts : null;
3873
+ if (!target) return null;
3874
+ const raw = await readFileOrNull(target);
3875
+ if (raw === null) return null;
3876
+ const ext = path20.extname(target);
3877
+ const currentRel = `${id}/current${ext}`;
3878
+ await writeFileSafe(path20.join(stagingDir, currentRel), raw);
3879
+ return {
3880
+ id,
3881
+ category,
3882
+ current: {
3883
+ target: path20.relative(projectRoot, target),
3884
+ hash: computeHash(raw),
3885
+ sourceLineage: "custom"
3886
+ },
3887
+ diff: {
3888
+ riskLevel: "foreign",
3889
+ hints: [
3890
+ "component is on disk but not registered in .teamix-evo/manifest.json",
3891
+ "AI should propose: (a) ignore, (b) re-register via teamix-evo ui add, or (c) remove"
3892
+ ],
3893
+ filesChangedCount: 0
3894
+ }
3895
+ };
3896
+ }
3897
+ async function buildBreakingEntry(args) {
3898
+ const ext = path20.extname(args.resource.target) || ".tsx";
3899
+ const currentRel = `${args.id}/current${ext}`;
3900
+ await writeFileSafe(
3901
+ path20.join(args.stagingDir, currentRel),
3902
+ args.currentSource
3903
+ );
3904
+ return {
3905
+ id: args.id,
3906
+ category: args.category,
3907
+ current: {
3908
+ target: path20.relative(args.projectRoot, args.resource.target),
3909
+ hash: args.resource.hash,
3910
+ sourceLineage: "teamix-evo"
3911
+ },
3912
+ diff: {
3913
+ riskLevel: "breaking",
3914
+ hints: [args.hint],
3915
+ filesChangedCount: 0
3916
+ }
3917
+ };
3918
+ }
3919
+ function classifyRisk(args) {
3920
+ if (args.currentHash === args.incomingHash) {
3921
+ return { riskLevel: "unchanged", hints: [], filesChangedCount: 0 };
3922
+ }
3923
+ const curExports = extractExportNames(args.currentSource);
3924
+ const newExports = extractExportNames(args.incomingSource);
3925
+ const removedExports = setDiff(curExports, newExports);
3926
+ const addedExports = setDiff(newExports, curExports);
3927
+ const curVariants = extractCvaVariantValues(args.currentSource);
3928
+ const newVariants = extractCvaVariantValues(args.incomingSource);
3929
+ const removedVariants = setDiff(curVariants, newVariants);
3930
+ const addedVariants = setDiff(newVariants, curVariants);
3931
+ const hints = [];
3932
+ for (const e of removedExports) hints.push(`removed export: ${e}`);
3933
+ for (const e of addedExports) hints.push(`new export: ${e}`);
3934
+ for (const v of removedVariants) hints.push(`removed cva variant: ${v}`);
3935
+ for (const v of addedVariants) hints.push(`new cva variant: ${v}`);
3936
+ if (args.multiFile) hints.push("multi-file entry; only first file staged");
3937
+ let riskLevel;
3938
+ if (removedExports.length > 0 || removedVariants.length > 0) {
3939
+ riskLevel = "risky";
3940
+ } else if (addedExports.length > 0 || addedVariants.length > 0 || args.multiFile) {
3941
+ riskLevel = "upgradable-medium";
3942
+ } else {
3943
+ riskLevel = "upgradable-low";
3944
+ }
3945
+ return { riskLevel, hints, filesChangedCount: 1 };
3946
+ }
3947
+ function extractExportNames(src) {
3948
+ const names = /* @__PURE__ */ new Set();
3949
+ const re = /^\s*export\s+(?:default\s+)?(?:async\s+)?(?:const|let|var|function|class|interface|type|enum)\s+(\w+)/gm;
3950
+ let m;
3951
+ while ((m = re.exec(src)) !== null) {
3952
+ if (m[1]) names.add(m[1]);
3953
+ }
3954
+ for (const dm of src.matchAll(/^\s*export\s+default\s+(\w+)\s*;/gm)) {
3955
+ if (dm[1]) names.add(dm[1]);
3956
+ }
3957
+ return [...names];
3958
+ }
3959
+ function extractCvaVariantValues(src) {
3960
+ const block = extractVariantsBlock(src);
3961
+ if (block === null) return [];
3962
+ const names = /* @__PURE__ */ new Set();
3963
+ for (const groupBody of extractGroupBodies(block)) {
3964
+ for (const km of groupBody.matchAll(/^\s*(?:['"]?)(\w+)(?:['"]?)\s*:/gm)) {
3965
+ if (km[1]) names.add(km[1]);
3966
+ }
3967
+ }
3968
+ return [...names];
3969
+ }
3970
+ function extractVariantsBlock(src) {
3971
+ const idx = src.search(/\bvariants\s*:\s*\{/);
3972
+ if (idx < 0) return null;
3973
+ const open = src.indexOf("{", idx);
3974
+ if (open < 0) return null;
3975
+ let depth = 0;
3976
+ for (let i = open; i < src.length; i++) {
3977
+ const c = src[i];
3978
+ if (c === "{") depth++;
3979
+ else if (c === "}") {
3980
+ depth--;
3981
+ if (depth === 0) return src.slice(open + 1, i);
3982
+ }
3983
+ }
3984
+ return null;
3985
+ }
3986
+ function* extractGroupBodies(block) {
3987
+ const re = /(\w+)\s*:\s*\{/g;
3988
+ let m;
3989
+ while ((m = re.exec(block)) !== null) {
3990
+ const open = block.indexOf("{", m.index);
3991
+ if (open < 0) continue;
3992
+ let depth = 0;
3993
+ for (let i = open; i < block.length; i++) {
3994
+ const c = block[i];
3995
+ if (c === "{") depth++;
3996
+ else if (c === "}") {
3997
+ depth--;
3998
+ if (depth === 0) {
3999
+ yield block.slice(open + 1, i);
4000
+ re.lastIndex = i + 1;
4001
+ break;
4002
+ }
4003
+ }
4004
+ }
4005
+ }
4006
+ }
4007
+ function setDiff(a, b) {
4008
+ const bset = new Set(b);
4009
+ return a.filter((x) => !bset.has(x)).sort();
4010
+ }
4011
+ function collectResourcesByEntry(resources) {
4012
+ const out = /* @__PURE__ */ new Map();
4013
+ for (const r of resources) {
4014
+ const colon = r.id.indexOf(":");
4015
+ const eid = colon >= 0 ? r.id.slice(0, colon) : r.id;
4016
+ if (!out.has(eid)) out.set(eid, r);
4017
+ }
4018
+ return out;
4019
+ }
4020
+ function aggregateByRisk(entries) {
4021
+ const out = {};
4022
+ for (const e of entries) {
4023
+ const k = e.diff.riskLevel;
4024
+ out[k] = (out[k] ?? 0) + 1;
4025
+ }
4026
+ return out;
4027
+ }
4028
+
4029
+ // src/core/ui-upgrade.ts
4030
+ var nodeRequire = createRequire6(import.meta.url);
4031
+ function resolvePackageRoot3(packageName) {
4032
+ const pkgJsonPath = nodeRequire.resolve(`${packageName}/package.json`);
4033
+ return path21.dirname(pkgJsonPath);
4034
+ }
4035
+ async function runUiUpgrade(options) {
4036
+ const { projectRoot, category, ids = [], trigger } = options;
4037
+ const config = await readProjectConfig(projectRoot);
4038
+ if (!config) return { status: "not-initialized" };
4039
+ const cfgKey = category === "ui" ? "ui" : "biz-ui";
4040
+ if (!config.packages?.[cfgKey]) {
4041
+ return { status: "not-installed", detail: `${category} not installed` };
4042
+ }
4043
+ const aliases = config.packages.ui?.aliases ?? config.packages["biz-ui"]?.aliases;
4044
+ if (!aliases) {
4045
+ return {
4046
+ status: "not-installed",
4047
+ detail: `${category} aliases not configured (run \`teamix-evo ui init\` first)`
4048
+ };
4049
+ }
4050
+ const installed = await readInstalledManifest(projectRoot);
4051
+ const lineageReport = await detectComponentLineage({
4052
+ projectRoot,
4053
+ category,
4054
+ config,
4055
+ installed
4056
+ });
4057
+ if (lineageReport.lineage !== "teamix-evo" && lineageReport.lineage !== "mixed") {
4058
+ return {
4059
+ status: "skipped",
4060
+ detail: `lineage=${lineageReport.lineage}; nothing to stage`,
4061
+ lineage: lineageReport.lineage
4062
+ };
4063
+ }
4064
+ if (ids.length > 0) {
4065
+ const knownIds = /* @__PURE__ */ new Set([
4066
+ ...lineageReport.registeredIds,
4067
+ ...lineageReport.unregisteredIds
4068
+ ]);
4069
+ const unknown = ids.filter((id) => !knownIds.has(id));
4070
+ if (unknown.length > 0) {
4071
+ throw new Error(
4072
+ `Unknown ${category} component id(s): ${unknown.map((s) => `"${s}"`).join(
4073
+ ", "
4074
+ )}. Hint: \`teamix-evo ${category} list\` shows available ids.`
4075
+ );
4076
+ }
4077
+ }
4078
+ const built = await buildStaging({
4079
+ category,
4080
+ projectRoot,
4081
+ aliases,
4082
+ lineageReport,
4083
+ trigger,
4084
+ onlyIds: ids,
4085
+ uiPackageRoot: options.uiPackageRoot,
4086
+ bizUiPackageRoot: options.bizUiPackageRoot
4087
+ });
4088
+ if (built === null) {
4089
+ return {
4090
+ status: "skipped",
4091
+ detail: "no entries to stage",
4092
+ lineage: lineageReport.lineage
4093
+ };
4094
+ }
4095
+ return {
4096
+ status: "staged",
4097
+ stagingDir: built.stagingDir,
4098
+ manifest: built.manifest
4099
+ };
4100
+ }
4101
+ async function buildStaging(args) {
4102
+ const { category, projectRoot, aliases, lineageReport, trigger, onlyIds } = args;
4103
+ if (category === "ui") {
4104
+ const root = args.uiPackageRoot ?? resolvePackageRoot3("@teamix-evo/ui");
4105
+ const manifest = await loadUiPackageManifest2(root);
4106
+ return buildUiUpgradeStaging({
4107
+ projectRoot,
4108
+ category,
4109
+ manifest,
4110
+ packageRoot: root,
4111
+ aliases,
4112
+ lineageReport,
4113
+ trigger,
4114
+ onlyIds
4115
+ });
4116
+ }
4117
+ const bizRoot = args.bizUiPackageRoot ?? resolvePackageRoot3("@teamix-evo/biz-ui");
4118
+ const variant = lineageReport.installedVariant ?? "_flat";
4119
+ const variantDir = path21.join(bizRoot, "variants", variant);
4120
+ const variantManifest = await loadVariantUiPackageManifest(variantDir);
4121
+ const uiRoot = args.uiPackageRoot ?? resolvePackageRoot3("@teamix-evo/ui");
4122
+ const uiManifest = await loadUiPackageManifest2(uiRoot);
4123
+ const entryPackageRoot = /* @__PURE__ */ new Map();
4124
+ const merged = [];
4125
+ for (const e of variantManifest.entries) {
4126
+ entryPackageRoot.set(e.id, variantDir);
4127
+ merged.push(e);
4128
+ }
4129
+ for (const e of uiManifest.entries) {
4130
+ if (entryPackageRoot.has(e.id)) continue;
4131
+ entryPackageRoot.set(e.id, uiRoot);
4132
+ merged.push(e);
4133
+ }
4134
+ const synthetic = {
4135
+ schemaVersion: 1,
4136
+ package: "ui",
4137
+ version: variantManifest.version,
4138
+ engines: variantManifest.engines,
4139
+ entries: merged
4140
+ };
4141
+ return buildUiUpgradeStaging({
4142
+ projectRoot,
4143
+ category,
4144
+ manifest: synthetic,
4145
+ packageRoot: variantDir,
4146
+ entryPackageRoot,
4147
+ aliases,
4148
+ lineageReport,
4149
+ trigger,
4150
+ onlyIds
4151
+ });
4152
+ }
4153
+
4154
+ // src/commands/_upgrade-command-factory.ts
4155
+ var META = {
4156
+ ui: {
4157
+ category: "ui",
4158
+ installDirHint: "src/components/ui",
4159
+ notInstalledHint: "npx teamix-evo ui init",
4160
+ trigger: "ui-upgrade"
4161
+ },
4162
+ "biz-ui": {
4163
+ category: "biz-ui",
4164
+ installDirHint: "src/components/business",
4165
+ notInstalledHint: "npx teamix-evo biz-ui add <id>",
4166
+ trigger: "biz-ui-upgrade"
4167
+ }
4168
+ };
4169
+ function makeUpgradeCommand(category) {
4170
+ const meta = META[category];
4171
+ return new Command18("upgrade").description(
4172
+ `\u4E3A ${category} \u7EC4\u4EF6\u751F\u6210\u5347\u7EA7 staging\uFF08\u4E0D\u5199 src\uFF0C\u9700\u901A\u8FC7 teamix-evo-upgrade skill \u5E94\u7528\uFF09`
4173
+ ).argument(
4174
+ "[ids...]",
4175
+ "\u53EF\u9009 entry id \u5217\u8868\uFF1B\u7701\u7565\u65F6\u5BF9\u5168\u90E8\u5DF2\u5B89\u88C5\u7EC4\u4EF6\u751F\u6210 staging"
4176
+ ).option(
4177
+ "--apply",
4178
+ `\uFF08\u5B88\u536B\u63D0\u793A\uFF09\u4FDD\u7559\u4F46\u62D2\u7EDD\u6267\u884C\uFF1ACLI \u4E0D\u5199 ${meta.installDirHint}\uFF0C\u8BF7\u901A\u8FC7 teamix-evo-upgrade skill \u5E94\u7528 staging`
4179
+ ).action(async (ids, opts) => {
4180
+ if (opts.apply) {
4181
+ logger.error(
4182
+ `CLI \u4E0D\u4F1A\u81EA\u52A8\u5199\u5165 ${meta.installDirHint}\uFF08ADR 0019 frozen \u8FB9\u754C\uFF09\u3002`
4183
+ );
4184
+ logger.error(
4185
+ `\u8BF7\u8FD0\u884C \`teamix-evo ${category} upgrade\` \u751F\u6210 staging\uFF0C\u518D\u8BA9 AI \u8C03\u7528 teamix-evo-upgrade skill \u9010\u6587\u4EF6\u5E94\u7528\u3002`
4186
+ );
4187
+ process.exitCode = 1;
4188
+ return;
4189
+ }
4190
+ try {
4191
+ const ide = detectIde();
4192
+ const projectRoot = ide.getProjectRoot();
4193
+ const result = await runUiUpgrade({
4194
+ projectRoot,
4195
+ category: meta.category,
4196
+ ids,
4197
+ trigger: meta.trigger
4198
+ });
4199
+ switch (result.status) {
4200
+ case "not-initialized":
4201
+ logger.error(
4202
+ "Project not initialized. Run `npx teamix-evo init` first."
4203
+ );
4204
+ process.exitCode = 1;
4205
+ return;
4206
+ case "not-installed":
4207
+ logger.error(`${category} not installed: ${result.detail}`);
4208
+ logger.error(`Run \`${meta.notInstalledHint}\` first.`);
4209
+ process.exitCode = 1;
4210
+ return;
4211
+ case "skipped":
4212
+ logger.info(
4213
+ `Nothing to stage (lineage=${result.lineage}): ${result.detail}`
4214
+ );
4215
+ return;
4216
+ case "staged": {
4217
+ const { manifest, stagingDir } = result;
4218
+ logger.success(
4219
+ `${category} staging written: ${manifest.summary.total} entries \u2192 ${stagingDir}`
4220
+ );
4221
+ const byRisk = manifest.summary.byRisk;
4222
+ const parts = Object.entries(byRisk).filter(([, n]) => (n ?? 0) > 0).map(([k, n]) => `${k}=${n}`);
4223
+ if (parts.length > 0) {
4224
+ logger.info(` risk: ${parts.join(", ")}`);
4225
+ }
4226
+ logger.info("");
4227
+ logger.info(
4228
+ "Next: \u8BA9 AI \u8C03\u7528 teamix-evo-upgrade skill\uFF0C\u6309 risk \u5206\u6279 review & apply\u3002"
4229
+ );
4230
+ return;
4231
+ }
4232
+ }
4233
+ } catch (err) {
4234
+ logger.error(
4235
+ `Failed to build ${category} staging: ${getErrorMessage(err)}`
4236
+ );
4237
+ logger.debug(err.stack ?? "");
4238
+ process.exitCode = 1;
4239
+ }
4240
+ });
4241
+ }
4242
+
4243
+ // src/commands/ui/upgrade.ts
4244
+ var upgradeCommand = makeUpgradeCommand("ui");
4245
+
4246
+ // src/commands/ui/index.ts
4247
+ var uiCommand = new Command19("ui").description(
4248
+ "\u7BA1\u7406 teamix-evo ui \u7EC4\u4EF6\uFF08\u6E90\u7801\u6CE8\u5165\u5F0F\u5B89\u88C5\uFF0Cshadcn \u98CE\u683C\uFF09"
4249
+ );
4250
+ uiCommand.addCommand(initCommand3);
4251
+ uiCommand.addCommand(addCommand2);
4252
+ uiCommand.addCommand(listCommand3);
4253
+ uiCommand.addCommand(upgradeCommand);
4254
+
4255
+ // src/commands/biz-ui/index.ts
4256
+ import { Command as Command23 } from "commander";
4257
+
4258
+ // src/commands/biz-ui/add.ts
4259
+ import { Command as Command20 } from "commander";
4260
+
4261
+ // src/core/variant-ui-add.ts
4262
+ import * as path22 from "path";
4263
+ import { createRequire as createRequire7 } from "module";
4264
+ import {
4265
+ loadUiPackageManifest as loadUiPackageManifest3,
4266
+ loadVariantUiPackageCatalog,
4267
+ loadVariantUiPackageManifest as loadVariantUiPackageManifest2
4268
+ } from "@teamix-evo/registry";
4269
+ var require7 = createRequire7(import.meta.url);
4270
+ function resolvePackageRoot4(packageName) {
4271
+ const pkgJsonPath = require7.resolve(`${packageName}/package.json`);
4272
+ return path22.dirname(pkgJsonPath);
4273
+ }
4274
+ async function runVariantUiAdd(packageName, options) {
4275
+ const { projectRoot, variant, ids, overwrite } = options;
4276
+ const fullPackageName = options.packageName ?? `@teamix-evo/${packageName}`;
4277
+ if (ids.length === 0) {
4278
+ throw new Error("At least one entry id must be provided.");
4279
+ }
4280
+ const config = await readProjectConfig(projectRoot);
4281
+ const uiCfg = config?.packages?.ui;
4282
+ if (!config || !uiCfg?.aliases) {
4283
+ throw new Error(
4284
+ `UI not initialized. Run \`teamix-evo ui init\` first \u2014 \`${packageName} add\` writes into the same alias map (business / templates).`
4285
+ );
4286
+ }
4287
+ const packageRoot = options.packageRoot ?? resolvePackageRoot4(fullPackageName);
4288
+ const catalog = await loadVariantUiPackageCatalog(packageRoot);
4289
+ if (!catalog.variants.some((v) => v.name === variant)) {
4290
+ const known = catalog.variants.map((v) => v.name).join(", ");
4291
+ throw new Error(
4292
+ `Variant "${variant}" not found in ${fullPackageName}. Known variants: ${known}. Hint: \`teamix-evo ${packageName} list-variants\` shows all.`
4293
+ );
4294
+ }
4295
+ const variantDir = path22.join(packageRoot, "variants", variant);
4296
+ const variantManifest = await loadVariantUiPackageManifest2(variantDir);
4297
+ const knownIds = new Set(variantManifest.entries.map((e) => e.id));
4298
+ const unknown = ids.filter((id) => !knownIds.has(id));
3440
4299
  if (unknown.length > 0) {
3441
4300
  throw new Error(
3442
4301
  `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.`
3443
4302
  );
3444
4303
  }
3445
- const uiPackageRoot = resolvePackageRoot3("@teamix-evo/ui");
3446
- const uiManifest = await loadUiPackageManifest2(uiPackageRoot);
4304
+ const uiPackageRoot = resolvePackageRoot4("@teamix-evo/ui");
4305
+ const uiManifest = await loadUiPackageManifest3(uiPackageRoot);
3447
4306
  const entryPackageRoot = /* @__PURE__ */ new Map();
3448
4307
  const mergedEntries = [];
3449
4308
  for (const e of variantManifest.entries) {
@@ -3477,7 +4336,7 @@ async function runVariantUiAdd(packageName, options) {
3477
4336
  projectRoot
3478
4337
  ) ?? { schemaVersion: 1, installed: [] };
3479
4338
  const idx = installed.installed.findIndex(
3480
- (p) => p.package === fullPackageName && p.variant === variant
4339
+ (p2) => p2.package === fullPackageName && p2.variant === variant
3481
4340
  );
3482
4341
  const prior = idx >= 0 ? installed.installed[idx] : null;
3483
4342
  const mergedResources = mergeResources2(
@@ -3518,7 +4377,7 @@ async function runTemplatesAdd(options) {
3518
4377
  }
3519
4378
  async function listVariantUi(packageName, packageRoot) {
3520
4379
  const fullPackageName = `@teamix-evo/${packageName}`;
3521
- const root = packageRoot ?? resolvePackageRoot3(fullPackageName);
4380
+ const root = packageRoot ?? resolvePackageRoot4(fullPackageName);
3522
4381
  const catalog = await loadVariantUiPackageCatalog(root);
3523
4382
  return {
3524
4383
  packageName: fullPackageName,
@@ -3538,7 +4397,7 @@ async function listTemplatesVariants(packageRoot) {
3538
4397
  }
3539
4398
  async function listVariantUiEntries(packageName, variant, packageRoot) {
3540
4399
  const fullPackageName = `@teamix-evo/${packageName}`;
3541
- const root = packageRoot ?? resolvePackageRoot3(fullPackageName);
4400
+ const root = packageRoot ?? resolvePackageRoot4(fullPackageName);
3542
4401
  const catalog = await loadVariantUiPackageCatalog(root);
3543
4402
  if (!catalog.variants.some((v) => v.name === variant)) {
3544
4403
  const known = catalog.variants.map((v) => v.name).join(", ");
@@ -3546,8 +4405,8 @@ async function listVariantUiEntries(packageName, variant, packageRoot) {
3546
4405
  `Variant "${variant}" not found in ${fullPackageName}. Known: ${known}.`
3547
4406
  );
3548
4407
  }
3549
- const variantDir = path18.join(root, "variants", variant);
3550
- const variantManifest = await loadVariantUiPackageManifest(variantDir);
4408
+ const variantDir = path22.join(root, "variants", variant);
4409
+ const variantManifest = await loadVariantUiPackageManifest2(variantDir);
3551
4410
  return {
3552
4411
  packageName: fullPackageName,
3553
4412
  variant,
@@ -3568,7 +4427,7 @@ async function listTemplatesEntries(variant, packageRoot) {
3568
4427
  }
3569
4428
 
3570
4429
  // src/commands/biz-ui/add.ts
3571
- var addCommand3 = new Command19("add").description(
4430
+ var addCommand3 = new Command20("add").description(
3572
4431
  "\u5B89\u88C5\u4E00\u4E2A\u6216\u591A\u4E2A\u4E1A\u52A1 UI \u7EC4\u4EF6(\u6309 id,\u81EA\u52A8\u5C55\u5F00 ui \u5305\u7684 registryDependencies)"
3573
4432
  ).argument("<ids...>", '\u7EC4\u4EF6 id \u5217\u8868,\u5982 "tenant-switcher" "org-picker"').option("--variant <name>", '\u53D8\u4F53 id(\u5FC5\u586B,\u5982 "opentrek"\u3001"uni-manager")').option("--overwrite", "\u5373\u4F7F\u76EE\u6807\u6587\u4EF6\u5DF2\u5B58\u5728\u4E5F\u8986\u76D6").action(
3574
4433
  async (ids, opts) => {
@@ -3587,7 +4446,9 @@ var addCommand3 = new Command19("add").description(
3587
4446
  );
3588
4447
  }
3589
4448
  logger.info(
3590
- `Installing biz-ui entries from variant "${opts.variant}": ${ids.join(", ")}`
4449
+ `Installing biz-ui entries from variant "${opts.variant}": ${ids.join(
4450
+ ", "
4451
+ )}`
3591
4452
  );
3592
4453
  const result = await runBizUiAdd({
3593
4454
  projectRoot,
@@ -3609,7 +4470,7 @@ var addCommand3 = new Command19("add").description(
3609
4470
  logger.info(` pnpm add ${installCmd}`);
3610
4471
  }
3611
4472
  } catch (err) {
3612
- logger.error(`Failed: ${err.message}`);
4473
+ logger.error(`Failed: ${getErrorMessage(err)}`);
3613
4474
  logger.debug(err.stack ?? "");
3614
4475
  process.exitCode = 1;
3615
4476
  }
@@ -3617,8 +4478,8 @@ var addCommand3 = new Command19("add").description(
3617
4478
  );
3618
4479
 
3619
4480
  // src/commands/biz-ui/list.ts
3620
- import { Command as Command20 } from "commander";
3621
- var listCommand4 = new Command20("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) => {
4481
+ import { Command as Command21 } from "commander";
4482
+ var listCommand4 = new Command21("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) => {
3622
4483
  try {
3623
4484
  const result = await listBizUiEntries(opts.variant);
3624
4485
  logger.info(`${result.packageName}#${result.variant} entries:`);
@@ -3637,14 +4498,14 @@ var listCommand4 = new Command20("list").description("\u5217\u51FA\u6307\u5B9A\u
3637
4498
  `Install: npx teamix-evo@latest biz-ui add <id> --variant ${result.variant}`
3638
4499
  );
3639
4500
  } catch (err) {
3640
- logger.error(`Failed: ${err.message}`);
4501
+ logger.error(`Failed: ${getErrorMessage(err)}`);
3641
4502
  process.exitCode = 1;
3642
4503
  }
3643
4504
  });
3644
4505
 
3645
4506
  // src/commands/biz-ui/list-variants.ts
3646
- import { Command as Command21 } from "commander";
3647
- var listVariantsCommand2 = new Command21("list-variants").description("\u5217\u51FA @teamix-evo/biz-ui \u5305\u5185\u63D0\u4F9B\u7684\u6240\u6709\u4E1A\u52A1\u53D8\u4F53").action(async () => {
4507
+ import { Command as Command22 } from "commander";
4508
+ var listVariantsCommand2 = new Command22("list-variants").description("\u5217\u51FA @teamix-evo/biz-ui \u5305\u5185\u63D0\u4F9B\u7684\u6240\u6709\u4E1A\u52A1\u53D8\u4F53").action(async () => {
3648
4509
  try {
3649
4510
  const result = await listBizUiVariants();
3650
4511
  logger.info(`Available biz-ui variants in ${result.packageName}:`);
@@ -3658,27 +4519,33 @@ var listVariantsCommand2 = new Command21("list-variants").description("\u5217\u5
3658
4519
  if (v.description) logger.info(` ${v.description}`);
3659
4520
  logger.info("");
3660
4521
  }
3661
- logger.info("Install from a variant: npx teamix-evo@latest biz-ui add <id> --variant <name>");
4522
+ logger.info(
4523
+ "Install from a variant: npx teamix-evo@latest biz-ui add <id> --variant <name>"
4524
+ );
3662
4525
  } catch (err) {
3663
- logger.error(`Failed: ${err.message}`);
4526
+ logger.error(`Failed: ${getErrorMessage(err)}`);
3664
4527
  process.exitCode = 1;
3665
4528
  }
3666
4529
  });
3667
4530
 
4531
+ // src/commands/biz-ui/upgrade.ts
4532
+ var upgradeCommand2 = makeUpgradeCommand("biz-ui");
4533
+
3668
4534
  // src/commands/biz-ui/index.ts
3669
- var bizUiCommand = new Command22("biz-ui").description(
4535
+ var bizUiCommand = new Command23("biz-ui").description(
3670
4536
  "\u7BA1\u7406\u4E1A\u52A1 UI \u7EC4\u4EF6(\u53D8\u4F53\u611F\u77E5 \u2014 \u4E0E design / templates \u540C\u53D8\u4F53\u540D\u7A7A\u95F4)"
3671
4537
  );
3672
4538
  bizUiCommand.addCommand(addCommand3);
3673
4539
  bizUiCommand.addCommand(listCommand4);
3674
4540
  bizUiCommand.addCommand(listVariantsCommand2);
4541
+ bizUiCommand.addCommand(upgradeCommand2);
3675
4542
 
3676
4543
  // src/commands/templates/index.ts
3677
- import { Command as Command26 } from "commander";
4544
+ import { Command as Command27 } from "commander";
3678
4545
 
3679
4546
  // src/commands/templates/add.ts
3680
- import { Command as Command23 } from "commander";
3681
- var addCommand4 = new Command23("add").description(
4547
+ import { Command as Command24 } from "commander";
4548
+ var addCommand4 = new Command24("add").description(
3682
4549
  "\u5B89\u88C5\u4E00\u4E2A\u6216\u591A\u4E2A\u9875\u9762\u6A21\u677F(\u6309 id,\u81EA\u52A8\u5C55\u5F00 ui \u5305\u7684 registryDependencies)"
3683
4550
  ).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(
3684
4551
  async (ids, opts) => {
@@ -3697,7 +4564,9 @@ var addCommand4 = new Command23("add").description(
3697
4564
  );
3698
4565
  }
3699
4566
  logger.info(
3700
- `Installing templates from variant "${opts.variant}": ${ids.join(", ")}`
4567
+ `Installing templates from variant "${opts.variant}": ${ids.join(
4568
+ ", "
4569
+ )}`
3701
4570
  );
3702
4571
  const result = await runTemplatesAdd({
3703
4572
  projectRoot,
@@ -3719,7 +4588,7 @@ var addCommand4 = new Command23("add").description(
3719
4588
  logger.info(` pnpm add ${installCmd}`);
3720
4589
  }
3721
4590
  } catch (err) {
3722
- logger.error(`Failed: ${err.message}`);
4591
+ logger.error(`Failed: ${getErrorMessage(err)}`);
3723
4592
  logger.debug(err.stack ?? "");
3724
4593
  process.exitCode = 1;
3725
4594
  }
@@ -3727,8 +4596,8 @@ var addCommand4 = new Command23("add").description(
3727
4596
  );
3728
4597
 
3729
4598
  // src/commands/templates/list.ts
3730
- import { Command as Command24 } from "commander";
3731
- var listCommand5 = new Command24("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) => {
4599
+ import { Command as Command25 } from "commander";
4600
+ var listCommand5 = new Command25("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) => {
3732
4601
  try {
3733
4602
  const result = await listTemplatesEntries(opts.variant);
3734
4603
  logger.info(`${result.packageName}#${result.variant} entries:`);
@@ -3747,14 +4616,14 @@ var listCommand5 = new Command24("list").description("\u5217\u51FA\u6307\u5B9A\u
3747
4616
  `Install: npx teamix-evo@latest templates add <id> --variant ${result.variant}`
3748
4617
  );
3749
4618
  } catch (err) {
3750
- logger.error(`Failed: ${err.message}`);
4619
+ logger.error(`Failed: ${getErrorMessage(err)}`);
3751
4620
  process.exitCode = 1;
3752
4621
  }
3753
4622
  });
3754
4623
 
3755
4624
  // src/commands/templates/list-variants.ts
3756
- import { Command as Command25 } from "commander";
3757
- var listVariantsCommand3 = new Command25("list-variants").description("\u5217\u51FA @teamix-evo/templates \u5305\u5185\u63D0\u4F9B\u7684\u6240\u6709\u9875\u9762\u6A21\u677F\u53D8\u4F53").action(async () => {
4625
+ import { Command as Command26 } from "commander";
4626
+ var listVariantsCommand3 = new Command26("list-variants").description("\u5217\u51FA @teamix-evo/templates \u5305\u5185\u63D0\u4F9B\u7684\u6240\u6709\u9875\u9762\u6A21\u677F\u53D8\u4F53").action(async () => {
3758
4627
  try {
3759
4628
  const result = await listTemplatesVariants();
3760
4629
  logger.info(`Available templates variants in ${result.packageName}:`);
@@ -3768,15 +4637,17 @@ var listVariantsCommand3 = new Command25("list-variants").description("\u5217\u5
3768
4637
  if (v.description) logger.info(` ${v.description}`);
3769
4638
  logger.info("");
3770
4639
  }
3771
- logger.info("Install from a variant: npx teamix-evo@latest templates add <id> --variant <name>");
4640
+ logger.info(
4641
+ "Install from a variant: npx teamix-evo@latest templates add <id> --variant <name>"
4642
+ );
3772
4643
  } catch (err) {
3773
- logger.error(`Failed: ${err.message}`);
4644
+ logger.error(`Failed: ${getErrorMessage(err)}`);
3774
4645
  process.exitCode = 1;
3775
4646
  }
3776
4647
  });
3777
4648
 
3778
4649
  // src/commands/templates/index.ts
3779
- var templatesCommand = new Command26("templates").description(
4650
+ var templatesCommand = new Command27("templates").description(
3780
4651
  "\u7BA1\u7406\u9875\u9762\u6A21\u677F(\u53D8\u4F53\u611F\u77E5 \u2014 \u4E0E design / biz-ui \u540C\u53D8\u4F53\u540D\u7A7A\u95F4)"
3781
4652
  );
3782
4653
  templatesCommand.addCommand(addCommand4);
@@ -3784,16 +4655,16 @@ templatesCommand.addCommand(listCommand5);
3784
4655
  templatesCommand.addCommand(listVariantsCommand3);
3785
4656
 
3786
4657
  // src/commands/logs/index.ts
3787
- import { Command as Command29 } from "commander";
4658
+ import { Command as Command30 } from "commander";
3788
4659
 
3789
4660
  // src/commands/logs/analyze.ts
3790
- import { Command as Command27 } from "commander";
4661
+ import { Command as Command28 } from "commander";
3791
4662
  import { existsSync as existsSync2 } from "fs";
3792
- import { resolve as resolve3, join as join18 } from "path";
4663
+ import { resolve as resolve4, join as join22 } from "path";
3793
4664
 
3794
4665
  // src/commands/logs/_io.ts
3795
4666
  import { readFileSync, readdirSync, statSync } from "fs";
3796
- import { join as join17 } from "path";
4667
+ import { join as join21 } from "path";
3797
4668
  var DATE_DIR_RE = /^\d{4}-\d{2}-\d{2}$/;
3798
4669
  function parseIntOrUndef(v) {
3799
4670
  if (v === void 0) return void 0;
@@ -3805,7 +4676,7 @@ function readRecords(baseDir, dayLimit) {
3805
4676
  const selected = dayLimit !== void 0 ? dayDirs.slice(0, dayLimit) : dayDirs;
3806
4677
  const records = [];
3807
4678
  for (const day of selected) {
3808
- const dayPath = join17(baseDir, day);
4679
+ const dayPath = join21(baseDir, day);
3809
4680
  let entries;
3810
4681
  try {
3811
4682
  entries = readdirSync(dayPath);
@@ -3814,7 +4685,7 @@ function readRecords(baseDir, dayLimit) {
3814
4685
  }
3815
4686
  for (const entry of entries) {
3816
4687
  if (!entry.endsWith(".jsonl")) continue;
3817
- const fp = join17(dayPath, entry);
4688
+ const fp = join21(dayPath, entry);
3818
4689
  try {
3819
4690
  if (!statSync(fp).isFile()) continue;
3820
4691
  } catch {
@@ -3834,14 +4705,14 @@ function readRecords(baseDir, dayLimit) {
3834
4705
  }
3835
4706
 
3836
4707
  // src/commands/logs/analyze.ts
3837
- var logsAnalyzeCommand = new Command27("analyze").description(
4708
+ var logsAnalyzeCommand = new Command28("analyze").description(
3838
4709
  "\u6C47\u603B vibe-logger \u8F93\u51FA (.teamix-evo/logs/ai/**/*.jsonl) \u2014 \u5DE5\u5177 / \u5305\u6807\u7B7E / MCP \u8C03\u7528\u9891\u7387,\u8F85\u52A9\u751F\u6001\u4F18\u5316"
3839
4710
  ).option("--dir <path>", "log \u76EE\u5F55 (\u9ED8\u8BA4 <project>/.teamix-evo/logs/ai)").option(
3840
4711
  "--days <n>",
3841
4712
  "\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)"
3842
4713
  ).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) => {
3843
- const baseDir = resolve3(
3844
- opts.dir ?? join18(process.cwd(), ".teamix-evo", "logs", "ai")
4714
+ const baseDir = resolve4(
4715
+ opts.dir ?? join22(process.cwd(), ".teamix-evo", "logs", "ai")
3845
4716
  );
3846
4717
  if (!existsSync2(baseDir)) {
3847
4718
  logger.warn(`No log directory at ${baseDir}.`);
@@ -3983,14 +4854,14 @@ function pad(n, width) {
3983
4854
  }
3984
4855
 
3985
4856
  // src/commands/logs/trace.ts
3986
- import { Command as Command28 } from "commander";
4857
+ import { Command as Command29 } from "commander";
3987
4858
  import { existsSync as existsSync3 } from "fs";
3988
- import { resolve as resolve4, join as join19 } from "path";
3989
- var logsTraceCommand = new Command28("trace").description(
4859
+ import { resolve as resolve5, join as join23 } from "path";
4860
+ var logsTraceCommand = new Command29("trace").description(
3990
4861
  "\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"
3991
4862
  ).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>/.teamix-evo/logs/ai)").option("--json", "\u4EE5 JSON \u8F93\u51FA (CI/\u5DE5\u5177\u53CB\u597D)").action((opts) => {
3992
- const baseDir = resolve4(
3993
- opts.dir ?? join19(process.cwd(), ".teamix-evo", "logs", "ai")
4863
+ const baseDir = resolve5(
4864
+ opts.dir ?? join23(process.cwd(), ".teamix-evo", "logs", "ai")
3994
4865
  );
3995
4866
  if (!existsSync3(baseDir)) {
3996
4867
  logger.warn(`No log directory at ${baseDir}.`);
@@ -4159,22 +5030,22 @@ function padLeft(s, n) {
4159
5030
  }
4160
5031
 
4161
5032
  // src/commands/logs/index.ts
4162
- var logsCommand = new Command29("logs").description(
5033
+ var logsCommand = new Command30("logs").description(
4163
5034
  "\u67E5\u8BE2 vibe-logger \u8F93\u51FA (.teamix-evo/logs/ai/**/*.jsonl) \u2014 AI \u8C03\u7528\u94FE\u5206\u6790"
4164
5035
  );
4165
5036
  logsCommand.addCommand(logsAnalyzeCommand);
4166
5037
  logsCommand.addCommand(logsTraceCommand);
4167
5038
 
4168
5039
  // src/commands/lint/index.ts
4169
- import { Command as Command31 } from "commander";
5040
+ import { Command as Command32 } from "commander";
4170
5041
 
4171
5042
  // src/commands/lint/init.ts
4172
- import { Command as Command30 } from "commander";
5043
+ import { Command as Command31 } from "commander";
4173
5044
  import * as prompts6 from "@clack/prompts";
4174
5045
 
4175
5046
  // src/core/lint-init.ts
4176
- import * as path19 from "path";
4177
- import * as fs15 from "fs";
5047
+ import * as path23 from "path";
5048
+ import * as fs16 from "fs";
4178
5049
  import { execa } from "execa";
4179
5050
  var ESLINT_CONFIG_CONTENT = `/**
4180
5051
  * teamix-evo consumer ESLint preset \u2014 9 token-discipline rules.
@@ -4202,8 +5073,8 @@ var ESLINT_DEPS = [
4202
5073
  var STYLELINT_DEPS = ["@teamix-evo/stylelint-config", "stylelint"];
4203
5074
  async function runLintInit(options) {
4204
5075
  const { projectRoot, skipInstall } = options;
4205
- const eslintConfigPath = path19.join(projectRoot, "eslint.config.js");
4206
- const stylelintConfigPath = path19.join(projectRoot, "stylelint.config.cjs");
5076
+ const eslintConfigPath = path23.join(projectRoot, "eslint.config.js");
5077
+ const stylelintConfigPath = path23.join(projectRoot, "stylelint.config.cjs");
4207
5078
  const eslintExists = await fileExists(eslintConfigPath);
4208
5079
  const stylelintExists = await fileExists(stylelintConfigPath);
4209
5080
  if (eslintExists && stylelintExists) {
@@ -4241,12 +5112,12 @@ async function runLintInit(options) {
4241
5112
  };
4242
5113
  }
4243
5114
  function detectPm(projectRoot) {
4244
- if (fs15.existsSync(path19.join(projectRoot, "pnpm-lock.yaml"))) return "pnpm";
4245
- if (fs15.existsSync(path19.join(projectRoot, "yarn.lock"))) return "yarn";
5115
+ if (fs16.existsSync(path23.join(projectRoot, "pnpm-lock.yaml"))) return "pnpm";
5116
+ if (fs16.existsSync(path23.join(projectRoot, "yarn.lock"))) return "yarn";
4246
5117
  return "npm";
4247
5118
  }
4248
5119
  async function patchPackageJsonScripts(projectRoot) {
4249
- const pkgPath = path19.join(projectRoot, "package.json");
5120
+ const pkgPath = path23.join(projectRoot, "package.json");
4250
5121
  const raw = await readFileOrNull(pkgPath);
4251
5122
  if (!raw) return;
4252
5123
  let pkg;
@@ -4273,7 +5144,7 @@ async function patchPackageJsonScripts(projectRoot) {
4273
5144
  }
4274
5145
 
4275
5146
  // src/commands/lint/init.ts
4276
- var initCommand4 = new Command30("init").description(
5147
+ var initCommand4 = new Command31("init").description(
4277
5148
  "\u521D\u59CB\u5316 ESLint + Stylelint \u5DE5\u7A0B\u89C4\u8303\uFF08\u5B89\u88C5\u4F9D\u8D56 + \u751F\u6210\u914D\u7F6E\u6587\u4EF6 + \u6CE8\u5165 scripts\uFF09"
4278
5149
  ).option("-y, --yes", "\u8DF3\u8FC7\u786E\u8BA4\uFF0C\u76F4\u63A5\u6267\u884C").action(async (opts) => {
4279
5150
  try {
@@ -4314,22 +5185,2273 @@ var initCommand4 = new Command30("init").description(
4314
5185
  logger.info('Run "npm run lint" to check JSX/TSX token discipline.');
4315
5186
  logger.info('Run "npm run lint:css" to check CSS token discipline.');
4316
5187
  } catch (err) {
4317
- logger.error(`Failed to initialize lint: ${err.message}`);
5188
+ logger.error(`Failed to initialize lint: ${getErrorMessage(err)}`);
4318
5189
  logger.debug(err.stack ?? "");
4319
5190
  process.exitCode = 1;
4320
5191
  }
4321
5192
  });
4322
5193
 
4323
5194
  // src/commands/lint/index.ts
4324
- var lintCommand = new Command31("lint").description(
5195
+ var lintCommand = new Command32("lint").description(
4325
5196
  "\u7BA1\u7406\u5DE5\u7A0B\u89C4\u8303\uFF08ESLint + Stylelint token-discipline \u89C4\u5219\u96C6\uFF09"
4326
5197
  );
4327
5198
  lintCommand.addCommand(initCommand4);
4328
5199
 
5200
+ // src/commands/init/index.ts
5201
+ import { Command as Command33 } from "commander";
5202
+ import * as path29 from "path";
5203
+
5204
+ // src/core/init-detect.ts
5205
+ import * as fs17 from "fs/promises";
5206
+ import * as path24 from "path";
5207
+ var IGNORED_TOP_LEVEL = /* @__PURE__ */ new Set([
5208
+ ".git",
5209
+ ".gitignore",
5210
+ ".gitattributes",
5211
+ ".gitkeep",
5212
+ ".DS_Store",
5213
+ "Thumbs.db",
5214
+ ".idea",
5215
+ ".vscode",
5216
+ ".qoder",
5217
+ ".claude",
5218
+ "README.md",
5219
+ "README",
5220
+ "README.txt",
5221
+ "LICENSE",
5222
+ "LICENSE.md",
5223
+ "LICENSE.txt"
5224
+ ]);
5225
+ async function detectProjectState(cwd) {
5226
+ const absCwd = path24.resolve(cwd);
5227
+ const teamixDir = getTeamixDir(absCwd);
5228
+ const hasTeamixDir = await fileExists(teamixDir);
5229
+ if (hasTeamixDir) {
5230
+ return {
5231
+ state: "teamix-evo-installed",
5232
+ cwd: absCwd,
5233
+ hasTeamixDir: true,
5234
+ hasPackageJson: await fileExists(path24.join(absCwd, "package.json")),
5235
+ significantEntries: []
5236
+ };
5237
+ }
5238
+ let entries;
5239
+ try {
5240
+ entries = await fs17.readdir(absCwd);
5241
+ } catch (err) {
5242
+ if (err.code === "ENOENT") {
5243
+ return {
5244
+ state: "empty",
5245
+ cwd: absCwd,
5246
+ hasTeamixDir: false,
5247
+ hasPackageJson: false,
5248
+ significantEntries: []
5249
+ };
5250
+ }
5251
+ throw err;
5252
+ }
5253
+ const significant = entries.filter((e) => !IGNORED_TOP_LEVEL.has(e));
5254
+ const hasPackageJson2 = entries.includes("package.json");
5255
+ if (significant.length === 0) {
5256
+ return {
5257
+ state: "empty",
5258
+ cwd: absCwd,
5259
+ hasTeamixDir: false,
5260
+ hasPackageJson: false,
5261
+ significantEntries: []
5262
+ };
5263
+ }
5264
+ return {
5265
+ state: "non-teamix-evo",
5266
+ cwd: absCwd,
5267
+ hasTeamixDir: false,
5268
+ hasPackageJson: hasPackageJson2,
5269
+ significantEntries: significant.slice(0, 20).sort()
5270
+ };
5271
+ }
5272
+
5273
+ // src/core/init-conflicts.ts
5274
+ import * as crypto from "crypto";
5275
+ import * as fs18 from "fs/promises";
5276
+ import * as path25 from "path";
5277
+ var TAILWIND_CONFIG_CANDIDATES = [
5278
+ "tailwind.config.ts",
5279
+ "tailwind.config.js",
5280
+ "tailwind.config.cjs",
5281
+ "tailwind.config.mjs"
5282
+ ];
5283
+ var TOKENS_FILE_CANDIDATES = [
5284
+ "src/design-tokens.css",
5285
+ "src/styles/design-tokens.css",
5286
+ "src/styles/tokens.css"
5287
+ ];
5288
+ var TOKENS_DIR_CANDIDATES = ["tokens"];
5289
+ var INDEX_CSS_CANDIDATES = [
5290
+ "src/index.css",
5291
+ "src/main.css",
5292
+ "src/app.css",
5293
+ "src/styles/index.css",
5294
+ "src/styles/globals.css"
5295
+ ];
5296
+ var SHADCN_FILE_CANDIDATES = ["src/lib/utils.ts"];
5297
+ var SHADCN_DIR_CANDIDATES = ["src/components/ui"];
5298
+ async function isDir(target) {
5299
+ try {
5300
+ const stat7 = await fs18.stat(target);
5301
+ return stat7.isDirectory();
5302
+ } catch {
5303
+ return false;
5304
+ }
5305
+ }
5306
+ async function dirHasContent(target) {
5307
+ try {
5308
+ const entries = await fs18.readdir(target);
5309
+ return entries.length > 0;
5310
+ } catch {
5311
+ return false;
5312
+ }
5313
+ }
5314
+ function fingerprint(parts) {
5315
+ const hash = crypto.createHash("sha256");
5316
+ for (const p2 of [...parts].sort()) hash.update(p2);
5317
+ return `sha256:${hash.digest("hex").slice(0, 16)}`;
5318
+ }
5319
+ async function readPackageJson(cwd) {
5320
+ const raw = await readFileOrNull(path25.join(cwd, "package.json"));
5321
+ if (raw === null) return null;
5322
+ try {
5323
+ return JSON.parse(raw);
5324
+ } catch {
5325
+ return null;
5326
+ }
5327
+ }
5328
+ function detectTailwindMajor(pkg) {
5329
+ if (!pkg) return null;
5330
+ const v = pkg.dependencies?.tailwindcss ?? pkg.devDependencies?.tailwindcss ?? null;
5331
+ if (!v) return null;
5332
+ const m = /(\d+)/.exec(v);
5333
+ if (!m) return null;
5334
+ const major = Number.parseInt(m[1], 10);
5335
+ if (major === 3) return 3;
5336
+ if (major === 4) return 4;
5337
+ return null;
5338
+ }
5339
+ async function detectAgentsMd(cwd) {
5340
+ const target = path25.join(cwd, "AGENTS.md");
5341
+ const content = await readFileOrNull(target);
5342
+ const exists = content !== null;
5343
+ return {
5344
+ key: "agents-md",
5345
+ exists,
5346
+ paths: exists ? ["AGENTS.md"] : [],
5347
+ fingerprint: exists ? fingerprint([content]) : void 0,
5348
+ recommendedStrategy: exists ? "merge-managed" : "overwrite",
5349
+ availableStrategies: ["merge-managed", "overwrite", "skip"]
5350
+ };
5351
+ }
5352
+ async function detectComponentsJson(cwd) {
5353
+ const target = path25.join(cwd, "components.json");
5354
+ const content = await readFileOrNull(target);
5355
+ const exists = content !== null;
5356
+ return {
5357
+ key: "components-json",
5358
+ exists,
5359
+ paths: exists ? ["components.json"] : [],
5360
+ fingerprint: exists ? fingerprint([content]) : void 0,
5361
+ recommendedStrategy: exists ? "diff-prompt" : "overwrite",
5362
+ availableStrategies: ["diff-prompt", "overwrite", "skip"]
5363
+ };
5364
+ }
5365
+ async function detectTailwindConfig(cwd) {
5366
+ const matched = [];
5367
+ const contents = [];
5368
+ for (const rel2 of TAILWIND_CONFIG_CANDIDATES) {
5369
+ const c = await readFileOrNull(path25.join(cwd, rel2));
5370
+ if (c !== null) {
5371
+ matched.push(rel2);
5372
+ contents.push(c);
5373
+ }
5374
+ }
5375
+ const pkg = await readPackageJson(cwd);
5376
+ const tailwindMajor = detectTailwindMajor(pkg);
5377
+ const exists = matched.length > 0;
5378
+ return {
5379
+ key: "tailwind-config",
5380
+ exists,
5381
+ paths: matched,
5382
+ fingerprint: exists ? fingerprint(contents) : void 0,
5383
+ recommendedStrategy: exists ? "backup-overwrite" : "overwrite",
5384
+ availableStrategies: ["backup-overwrite", "overwrite", "skip"],
5385
+ meta: { tailwindMajor }
5386
+ };
5387
+ }
5388
+ async function detectTokens(cwd) {
5389
+ const matched = [];
5390
+ const contents = [];
5391
+ for (const rel2 of TOKENS_FILE_CANDIDATES) {
5392
+ const c = await readFileOrNull(path25.join(cwd, rel2));
5393
+ if (c !== null) {
5394
+ matched.push(rel2);
5395
+ contents.push(c);
5396
+ }
5397
+ }
5398
+ for (const rel2 of TOKENS_DIR_CANDIDATES) {
5399
+ const abs = path25.join(cwd, rel2);
5400
+ if (await isDir(abs) && await dirHasContent(abs)) {
5401
+ matched.push(`${rel2}/`);
5402
+ }
5403
+ }
5404
+ const exists = matched.length > 0;
5405
+ return {
5406
+ key: "tokens",
5407
+ exists,
5408
+ paths: matched,
5409
+ fingerprint: contents.length > 0 ? fingerprint(contents) : void 0,
5410
+ recommendedStrategy: exists ? "migrate" : "overwrite",
5411
+ availableStrategies: ["migrate", "coexist", "overwrite", "skip"]
5412
+ };
5413
+ }
5414
+ async function detectIndexCss(cwd) {
5415
+ const matched = [];
5416
+ const contents = [];
5417
+ for (const rel2 of INDEX_CSS_CANDIDATES) {
5418
+ const c = await readFileOrNull(path25.join(cwd, rel2));
5419
+ if (c !== null) {
5420
+ matched.push(rel2);
5421
+ contents.push(c);
5422
+ }
5423
+ }
5424
+ const exists = matched.length > 0;
5425
+ return {
5426
+ key: "index-css",
5427
+ exists,
5428
+ paths: matched,
5429
+ fingerprint: exists ? fingerprint(contents) : void 0,
5430
+ recommendedStrategy: exists ? "append" : "overwrite",
5431
+ availableStrategies: ["append", "diff-prompt", "overwrite", "skip"]
5432
+ };
5433
+ }
5434
+ async function detectShadcnSource(cwd) {
5435
+ const matched = [];
5436
+ for (const rel2 of SHADCN_FILE_CANDIDATES) {
5437
+ if (await fileExists(path25.join(cwd, rel2))) matched.push(rel2);
5438
+ }
5439
+ for (const rel2 of SHADCN_DIR_CANDIDATES) {
5440
+ const abs = path25.join(cwd, rel2);
5441
+ if (await isDir(abs) && await dirHasContent(abs)) {
5442
+ matched.push(`${rel2}/`);
5443
+ }
5444
+ }
5445
+ let componentCount = 0;
5446
+ try {
5447
+ const uiDir = path25.join(cwd, "src/components/ui");
5448
+ if (await isDir(uiDir)) {
5449
+ const entries = await fs18.readdir(uiDir);
5450
+ componentCount = entries.filter(
5451
+ (e) => e.endsWith(".tsx") || e.endsWith(".ts")
5452
+ ).length;
5453
+ }
5454
+ } catch {
5455
+ }
5456
+ const exists = matched.length > 0;
5457
+ return {
5458
+ key: "shadcn-source",
5459
+ exists,
5460
+ paths: matched,
5461
+ recommendedStrategy: exists ? "skip-existing" : "overwrite",
5462
+ availableStrategies: ["skip-existing", "per-file-prompt", "overwrite"],
5463
+ meta: { componentCount }
5464
+ };
5465
+ }
5466
+ async function detectConflicts(cwd) {
5467
+ const absCwd = path25.resolve(cwd);
5468
+ const items = await Promise.all([
5469
+ detectAgentsMd(absCwd),
5470
+ detectComponentsJson(absCwd),
5471
+ detectTailwindConfig(absCwd),
5472
+ detectTokens(absCwd),
5473
+ detectIndexCss(absCwd),
5474
+ detectShadcnSource(absCwd)
5475
+ ]);
5476
+ return {
5477
+ cwd: absCwd,
5478
+ items,
5479
+ hasAnyConflict: items.some((i) => i.exists)
5480
+ };
5481
+ }
5482
+
5483
+ // src/core/legacy-tokens-migrate.ts
5484
+ import * as path26 from "path";
5485
+ import * as fs19 from "fs/promises";
5486
+ var CONSUMER_TOKENS_DIR2 = "tokens";
5487
+ var CONSUMER_OVERRIDES_FILE2 = "tokens.overrides.css";
5488
+ var EMPTY_OVERRIDES_TEMPLATE2 = `/* User-owned token overrides \u2014 frozen on subsequent installs. */
5489
+ /* See @teamix-evo/tokens variant theme.css for available CSS custom properties. */
5490
+ `;
5491
+ function buildMigrationBanner(legacyRel, isoTimestamp) {
5492
+ return `/* === Migrated from ${legacyRel} on ${isoTimestamp} === */
5493
+ `;
5494
+ }
5495
+ function computeBackupRel(legacyRel, isoTimestamp) {
5496
+ const tsSafe = isoTimestamp.replace(/[:.]/g, "-");
5497
+ return path26.posix.join(
5498
+ ".teamix-evo",
5499
+ ".backups",
5500
+ `${legacyRel}.${tsSafe}.bak`
5501
+ );
5502
+ }
5503
+ async function migrateLegacyTokens(options) {
5504
+ const { projectRoot, legacyPaths, dryRun = false } = options;
5505
+ const seen = /* @__PURE__ */ new Set();
5506
+ const uniqueLegacy = [];
5507
+ for (const raw of legacyPaths) {
5508
+ const norm = raw.split(path26.sep).join("/");
5509
+ if (!seen.has(norm)) {
5510
+ seen.add(norm);
5511
+ uniqueLegacy.push(norm);
5512
+ }
5513
+ }
5514
+ const overridesRel = path26.posix.join(
5515
+ CONSUMER_TOKENS_DIR2,
5516
+ CONSUMER_OVERRIDES_FILE2
5517
+ );
5518
+ const overridesAbs = path26.join(projectRoot, overridesRel);
5519
+ const existingOverrides = await readFileOrNull(overridesAbs);
5520
+ const overridesCreated = existingOverrides === null;
5521
+ const baseContent = existingOverrides === null ? EMPTY_OVERRIDES_TEMPLATE2 : existingOverrides;
5522
+ const migrated = [];
5523
+ const skipped = [];
5524
+ const isoTimestamp = (/* @__PURE__ */ new Date()).toISOString();
5525
+ let appendedPayload = "";
5526
+ for (const legacyRel of uniqueLegacy) {
5527
+ const legacyAbs = path26.join(projectRoot, legacyRel);
5528
+ const content = await readFileOrNull(legacyAbs);
5529
+ if (content === null) {
5530
+ skipped.push({ from: legacyRel, reason: "not-found" });
5531
+ continue;
5532
+ }
5533
+ if (content.trim() === "") {
5534
+ skipped.push({ from: legacyRel, reason: "empty" });
5535
+ continue;
5536
+ }
5537
+ const banner = buildMigrationBanner(legacyRel, isoTimestamp);
5538
+ const separator = appendedPayload === "" && baseContent.endsWith("\n\n") ? "" : "\n";
5539
+ const block = `${separator}${banner}${content}${content.endsWith("\n") ? "" : "\n"}`;
5540
+ appendedPayload += block;
5541
+ const backupRel = dryRun ? null : computeBackupRel(legacyRel, isoTimestamp);
5542
+ migrated.push({
5543
+ from: legacyRel,
5544
+ backupTo: backupRel,
5545
+ bytesAppended: Buffer.byteLength(block, "utf-8")
5546
+ });
5547
+ }
5548
+ if (dryRun || migrated.length === 0) {
5549
+ return {
5550
+ migrated,
5551
+ skipped,
5552
+ overridesPath: overridesRel,
5553
+ overridesCreated: dryRun ? overridesCreated : false,
5554
+ dryRun
5555
+ };
5556
+ }
5557
+ await ensureDir(path26.dirname(overridesAbs));
5558
+ const merged = baseContent + appendedPayload;
5559
+ const tmp = overridesAbs + ".tmp";
5560
+ await fs19.writeFile(tmp, merged, "utf-8");
5561
+ await fs19.rename(tmp, overridesAbs);
5562
+ for (const entry of migrated) {
5563
+ const legacyAbs = path26.join(projectRoot, entry.from);
5564
+ const backupAbs = path26.join(projectRoot, entry.backupTo);
5565
+ await ensureDir(path26.dirname(backupAbs));
5566
+ const srcContent = await readFileOrNull(legacyAbs);
5567
+ if (srcContent === null) {
5568
+ logger.debug(
5569
+ `legacy-tokens-migrate: source disappeared before backup: ${entry.from}`
5570
+ );
5571
+ continue;
5572
+ }
5573
+ await fs19.writeFile(backupAbs, srcContent, "utf-8");
5574
+ if (await fileExists(legacyAbs)) {
5575
+ await fs19.unlink(legacyAbs);
5576
+ }
5577
+ logger.debug(
5578
+ `legacy-tokens-migrate: ${entry.from} \u2192 ${overridesRel} (backup: ${entry.backupTo})`
5579
+ );
5580
+ }
5581
+ return {
5582
+ migrated,
5583
+ skipped,
5584
+ overridesPath: overridesRel,
5585
+ overridesCreated,
5586
+ dryRun: false
5587
+ };
5588
+ }
5589
+
5590
+ // src/core/agents-md.ts
5591
+ import * as fs20 from "fs/promises";
5592
+ import * as path27 from "path";
5593
+ async function runGenerateAgentsMd(options) {
5594
+ const { projectRoot, variant, skillIds } = options;
5595
+ const ordered = [...skillIds].sort(
5596
+ (a, b) => bucketRank(a) - bucketRank(b) || a.localeCompare(b)
5597
+ );
5598
+ const sections = [];
5599
+ const missingSkillIds = [];
5600
+ for (const id of ordered) {
5601
+ const { section, missing } = await renderSkillSection(projectRoot, id);
5602
+ sections.push(section);
5603
+ if (missing) missingSkillIds.push(id);
5604
+ }
5605
+ const body = renderAgentsMd({ variant, sections });
5606
+ const target = path27.join(projectRoot, "AGENTS.md");
5607
+ await fs20.writeFile(target, body, "utf8");
5608
+ return {
5609
+ path: target,
5610
+ skillCount: ordered.length,
5611
+ missingSkillIds
5612
+ };
5613
+ }
5614
+ function bucketRank(id) {
5615
+ if (id.startsWith("teamix-evo-design-")) return 0;
5616
+ if (id.startsWith("teamix-evo-code-")) return 1;
5617
+ return 2;
5618
+ }
5619
+ async function renderSkillSection(projectRoot, skillId) {
5620
+ const skillPath = path27.join(
5621
+ projectRoot,
5622
+ ".teamix-evo",
5623
+ "skills",
5624
+ skillId,
5625
+ "SKILL.md"
5626
+ );
5627
+ const lines = [];
5628
+ lines.push(`### ${skillId}`);
5629
+ let parts = null;
5630
+ let missing = false;
5631
+ try {
5632
+ const raw = await fs20.readFile(skillPath, "utf8");
5633
+ parts = extractDescriptionParts(raw);
5634
+ } catch {
5635
+ missing = true;
5636
+ }
5637
+ if (parts?.capability) {
5638
+ lines.push(`- ${parts.capability}`);
5639
+ }
5640
+ lines.push(
5641
+ `- **TRIGGER**: ${parts?.trigger ?? "\u672A\u914D\u7F6E\u89E6\u53D1\u6761\u4EF6\uFF0C\u9700\u624B\u52A8\u6FC0\u6D3B\u8BE5 skill\u3002"}`
5642
+ );
5643
+ lines.push(
5644
+ `- **SKIP**: ${parts?.skip ?? "\u672A\u914D\u7F6E\u8DF3\u8FC7\u6761\u4EF6\uFF0C\u6309 TRIGGER \u515C\u5E95\u5224\u5B9A\u3002"}`
5645
+ );
5646
+ if (parts?.coordinates) {
5647
+ lines.push(`- **Coordinates with**: ${parts.coordinates}`);
5648
+ }
5649
+ lines.push(`- **\u4F4D\u7F6E**: \`.teamix-evo/skills/${skillId}/SKILL.md\``);
5650
+ return { section: lines.join("\n"), missing };
5651
+ }
5652
+ function renderAgentsMd(args) {
5653
+ const { variant, sections } = args;
5654
+ const skillBlock = sections.length > 0 ? sections.join("\n\n") : "_\uFF08\u672C\u5DE5\u7A0B\u672A\u88C5\u914D\u5DE5\u7A0B\u7EA7 skill\u3002\uFF09_";
5655
+ return `# AGENTS.md
5656
+
5657
+ > \u672C\u5DE5\u7A0B\u5DF2\u88C5\u914D Teamix Evo AI skills\u3002AI \u52A9\u624B\u5728\u4EE5\u4E0B\u573A\u666F\u4E0B**\u5FC5\u987B\u5148\u8BFB\u5BF9\u5E94 skill** \u518D\u52A8\u624B\u3002
5658
+ > \u672C\u6587\u4EF6\u7531 \`teamix-evo init\` / \`create-teamix-evo\` \u81EA\u52A8\u751F\u6210\uFF08regenerable\uFF0C[ADR 0038](https://github.com/teamix-evo/teamix-evo/blob/main/docs/adr/0038-create-agents-md-skill-trigger-fallback.md)\uFF09\uFF0C\u5237\u65B0\u65B9\u5F0F\u89C1\u5E95\u90E8\u3002
5659
+
5660
+ ## \u5DF2\u88C5 Skills\uFF08variant: ${variant}\uFF09
5661
+
5662
+ ${skillBlock}
5663
+
5664
+ ## \u89E6\u53D1\u515C\u5E95\u89C4\u5219
5665
+
5666
+ - \u5199\u65B0 \`.tsx\` / \`.ts\` \u524D\uFF0C\u5BF9\u7167\u4E0A\u8FF0 TRIGGER \u5224\u5B9A\u662F\u5426\u547D\u4E2D
5667
+ - \u547D\u4E2D\u5219\u5148\u8BFB\u5BF9\u5E94 \`SKILL.md\`\uFF0C\u518D\u52A8\u624B\uFF1B\u4E8C\u8005\u540C\u65F6\u547D\u4E2D\u5219\u4E24\u4E2A\u90FD\u8BFB
5668
+ - \u6A21\u7CCA\u573A\u666F\uFF1A\u5148\u6309 SKIP \u53CD\u5411\u6392\u9664\uFF0C\u5269\u4F59\u552F\u4E00 skill \u5373\u4E3A\u5165\u53E3
5669
+ - \u751F\u547D\u5468\u671F\u547D\u4EE4\uFF08\`init\` / \`update\` / \`add\`\uFF09\u8D70 \`teamix-evo-manage\`\uFF08\u5168\u5C40 skill\uFF0C\u672C\u6587\u4EF6\u4E0D\u5217\uFF09
5670
+
5671
+ > \u5237\u65B0\u672C\u6587\u4EF6\uFF1A\`npx teamix-evo skills add\` \u6216\u91CD\u8DD1 \`npm create teamix-evo\` / \`teamix-evo init\`\u3002
5672
+ `;
5673
+ }
5674
+ function extractDescriptionParts(fileContent) {
5675
+ const description = extractDescriptionBlock(fileContent);
5676
+ if (description == null) return null;
5677
+ const allLines = description.split("\n").map((l) => l.trim()).filter(Boolean);
5678
+ let capability = "";
5679
+ for (const line of allLines) {
5680
+ if (/^(TRIGGER when:|SKIP:|Coordinates with:)/i.test(line)) break;
5681
+ capability = capability ? `${capability} ${line}` : line;
5682
+ }
5683
+ return {
5684
+ capability: capability.trim(),
5685
+ trigger: extractSection(description, "TRIGGER when:"),
5686
+ skip: extractSection(description, "SKIP:"),
5687
+ coordinates: extractSection(description, "Coordinates with:")
5688
+ };
5689
+ }
5690
+ function extractDescriptionBlock(fileContent) {
5691
+ const lines = fileContent.split("\n");
5692
+ if (lines[0]?.trim() !== "---") return null;
5693
+ let endIdx = -1;
5694
+ for (let i = 1; i < lines.length; i++) {
5695
+ if (lines[i]?.trim() === "---") {
5696
+ endIdx = i;
5697
+ break;
5698
+ }
5699
+ }
5700
+ if (endIdx === -1) return null;
5701
+ const fmLines = lines.slice(1, endIdx);
5702
+ let startIdx = -1;
5703
+ let inlineValue = null;
5704
+ let blockMode = "inline";
5705
+ for (let i = 0; i < fmLines.length; i++) {
5706
+ const m = fmLines[i]?.match(/^description:\s*(\|[+-]?|>[+-]?)?\s*(.*)$/);
5707
+ if (m) {
5708
+ startIdx = i;
5709
+ const indicator = (m[1] ?? "").trim();
5710
+ const rest = m[2] ?? "";
5711
+ if (indicator.startsWith("|")) blockMode = "literal";
5712
+ else if (indicator.startsWith(">")) blockMode = "folded";
5713
+ else {
5714
+ blockMode = "inline";
5715
+ inlineValue = rest;
5716
+ }
5717
+ break;
5718
+ }
5719
+ }
5720
+ if (startIdx === -1) return null;
5721
+ if (blockMode === "inline") {
5722
+ return inlineValue ?? "";
5723
+ }
5724
+ const body = [];
5725
+ let blockIndent = -1;
5726
+ for (let i = startIdx + 1; i < fmLines.length; i++) {
5727
+ const line = fmLines[i] ?? "";
5728
+ if (line.trim() === "") {
5729
+ body.push("");
5730
+ continue;
5731
+ }
5732
+ const indentMatch = line.match(/^(\s+)/);
5733
+ const indent = indentMatch ? indentMatch[1].length : 0;
5734
+ if (indent === 0) break;
5735
+ if (blockIndent === -1) blockIndent = indent;
5736
+ if (indent < blockIndent) break;
5737
+ body.push(line.slice(blockIndent));
5738
+ }
5739
+ while (body.length > 0 && body[body.length - 1] === "") body.pop();
5740
+ return body.join("\n");
5741
+ }
5742
+ function extractSection(description, marker) {
5743
+ const markers = ["TRIGGER when:", "SKIP:", "Coordinates with:"];
5744
+ const lines = description.split("\n");
5745
+ let inSection = false;
5746
+ const collected = [];
5747
+ for (const line of lines) {
5748
+ const trimmed = line.trim();
5749
+ const startsWithMarker = trimmed.toLowerCase().startsWith(marker.toLowerCase());
5750
+ if (!inSection && startsWithMarker) {
5751
+ inSection = true;
5752
+ collected.push(trimmed.slice(marker.length).trim());
5753
+ continue;
5754
+ }
5755
+ if (inSection) {
5756
+ const hitNextMarker = markers.some(
5757
+ (m) => m !== marker && trimmed.toLowerCase().startsWith(m.toLowerCase())
5758
+ );
5759
+ if (hitNextMarker) break;
5760
+ if (trimmed === "") {
5761
+ if (collected[collected.length - 1] === "") break;
5762
+ collected.push("");
5763
+ continue;
5764
+ }
5765
+ collected.push(trimmed);
5766
+ }
5767
+ }
5768
+ if (!inSection) return null;
5769
+ const joined = collected.filter((l) => l !== "").join(" ").replace(/\s+/g, " ").trim();
5770
+ return joined || null;
5771
+ }
5772
+
5773
+ // src/core/snapshot.ts
5774
+ import * as fs21 from "fs/promises";
5775
+ import * as path28 from "path";
5776
+ var TEAMIX_DIR5 = ".teamix-evo";
5777
+ var SNAPSHOTS_DIR = ".snapshots";
5778
+ var LOGS_DIR = "logs";
5779
+ var META_FILE = "_meta.json";
5780
+ var DEFAULT_KEEP = 5;
5781
+ function isoToFsSafe3(iso) {
5782
+ return iso.replace(/[:.]/g, "-");
5783
+ }
5784
+ function fsSafeToIso(safe) {
5785
+ return safe.replace(
5786
+ /^(\d{4}-\d{2}-\d{2})T(\d{2})-(\d{2})-(\d{2})-(\d{3})(Z)$/,
5787
+ "$1T$2:$3:$4.$5$6"
5788
+ );
5789
+ }
5790
+ async function createSnapshot(projectRoot, opts = {}) {
5791
+ const teamixDir = path28.join(projectRoot, TEAMIX_DIR5);
5792
+ try {
5793
+ const stat7 = await fs21.stat(teamixDir);
5794
+ if (!stat7.isDirectory()) return null;
5795
+ } catch (err) {
5796
+ if (err.code === "ENOENT") return null;
5797
+ throw err;
5798
+ }
5799
+ const isoTs = (/* @__PURE__ */ new Date()).toISOString();
5800
+ const ts = isoToFsSafe3(isoTs);
5801
+ const snapshotRoot = path28.join(teamixDir, SNAPSHOTS_DIR);
5802
+ const target = path28.join(snapshotRoot, ts);
5803
+ await fs21.mkdir(target, { recursive: true });
5804
+ const entries = await fs21.readdir(teamixDir, { withFileTypes: true });
5805
+ for (const entry of entries) {
5806
+ if (entry.name === SNAPSHOTS_DIR) continue;
5807
+ if (entry.name === LOGS_DIR) continue;
5808
+ const src = path28.join(teamixDir, entry.name);
5809
+ const dst = path28.join(target, entry.name);
5810
+ await fs21.cp(src, dst, { recursive: true });
5811
+ }
5812
+ const meta = {
5813
+ ts: isoTs,
5814
+ reason: opts.reason ?? "manual"
5815
+ };
5816
+ await fs21.writeFile(
5817
+ path28.join(target, META_FILE),
5818
+ JSON.stringify(meta, null, 2) + "\n",
5819
+ "utf-8"
5820
+ );
5821
+ logger.debug(
5822
+ `Snapshot created \u2192 ${path28.relative(projectRoot, target)} (${meta.reason})`
5823
+ );
5824
+ const keep = opts.keep ?? DEFAULT_KEEP;
5825
+ await pruneSnapshots(projectRoot, keep, { protectedTs: opts.protectedTs });
5826
+ return { ts, path: target };
5827
+ }
5828
+ async function listSnapshots(projectRoot) {
5829
+ const snapshotRoot = path28.join(projectRoot, TEAMIX_DIR5, SNAPSHOTS_DIR);
5830
+ let entries;
5831
+ try {
5832
+ entries = await fs21.readdir(snapshotRoot, { withFileTypes: true });
5833
+ } catch (err) {
5834
+ if (err.code === "ENOENT") return [];
5835
+ throw err;
5836
+ }
5837
+ const result = [];
5838
+ for (const entry of entries) {
5839
+ if (!entry.isDirectory()) continue;
5840
+ const dir = path28.join(snapshotRoot, entry.name);
5841
+ let isoTs = null;
5842
+ let reason = null;
5843
+ try {
5844
+ const raw = await fs21.readFile(path28.join(dir, META_FILE), "utf-8");
5845
+ const parsed = JSON.parse(raw);
5846
+ if (typeof parsed.ts === "string") isoTs = parsed.ts;
5847
+ if (typeof parsed.reason === "string" && ["init", "update", "switch", "restore", "manual"].includes(
5848
+ parsed.reason
5849
+ )) {
5850
+ reason = parsed.reason;
5851
+ }
5852
+ } catch {
5853
+ isoTs = fsSafeToIso(entry.name);
5854
+ }
5855
+ result.push({ ts: entry.name, isoTs, reason, path: dir });
5856
+ }
5857
+ result.sort((a, b) => a.ts < b.ts ? 1 : a.ts > b.ts ? -1 : 0);
5858
+ return result;
5859
+ }
5860
+ async function pruneSnapshots(projectRoot, keep = DEFAULT_KEEP, opts = {}) {
5861
+ if (keep < 0)
5862
+ throw new Error(`pruneSnapshots: keep must be >= 0, got ${keep}`);
5863
+ const snapshots = await listSnapshots(projectRoot);
5864
+ if (snapshots.length <= keep) return [];
5865
+ const tail = snapshots.slice(keep);
5866
+ const toRemove = opts.protectedTs ? tail.filter((s) => s.ts !== opts.protectedTs) : tail;
5867
+ const removed = [];
5868
+ for (const snap of toRemove) {
5869
+ await fs21.rm(snap.path, { recursive: true, force: true });
5870
+ removed.push(snap.ts);
5871
+ logger.debug(`Pruned snapshot ${snap.ts}`);
5872
+ }
5873
+ return removed.reverse();
5874
+ }
5875
+ async function restoreSnapshot(projectRoot, ts) {
5876
+ const snapshotRoot = path28.join(projectRoot, TEAMIX_DIR5, SNAPSHOTS_DIR);
5877
+ const target = path28.join(snapshotRoot, ts);
5878
+ try {
5879
+ const stat7 = await fs21.stat(target);
5880
+ if (!stat7.isDirectory()) {
5881
+ throw new Error(`Snapshot path is not a directory: ${target}`);
5882
+ }
5883
+ } catch (err) {
5884
+ if (err.code === "ENOENT") {
5885
+ throw new Error(
5886
+ `Snapshot not found: "${ts}". Run \`teamix-evo restore --list\` to see available timestamps.`
5887
+ );
5888
+ }
5889
+ throw err;
5890
+ }
5891
+ await createSnapshot(projectRoot, { reason: "restore", protectedTs: ts });
5892
+ const teamixDir = path28.join(projectRoot, TEAMIX_DIR5);
5893
+ const live = await fs21.readdir(teamixDir, { withFileTypes: true });
5894
+ for (const entry of live) {
5895
+ if (entry.name === SNAPSHOTS_DIR) continue;
5896
+ if (entry.name === LOGS_DIR) continue;
5897
+ await fs21.rm(path28.join(teamixDir, entry.name), {
5898
+ recursive: true,
5899
+ force: true
5900
+ });
5901
+ }
5902
+ const snapshotEntries = await fs21.readdir(target, { withFileTypes: true });
5903
+ for (const entry of snapshotEntries) {
5904
+ if (entry.name === META_FILE) continue;
5905
+ const src = path28.join(target, entry.name);
5906
+ const dst = path28.join(teamixDir, entry.name);
5907
+ await fs21.cp(src, dst, { recursive: true });
5908
+ }
5909
+ logger.debug(`Restored .teamix-evo/ from snapshot ${ts}`);
5910
+ }
5911
+
5912
+ // src/core/project-init.ts
5913
+ var BASELINE_UI_ENTRIES = [
5914
+ "button",
5915
+ "button-group",
5916
+ "input",
5917
+ "form",
5918
+ "card",
5919
+ "collapsible",
5920
+ "dialog",
5921
+ "dropdown-menu",
5922
+ "tabs",
5923
+ "table",
5924
+ "sidebar",
5925
+ "page-shell",
5926
+ "page-header"
5927
+ ];
5928
+ var CRITICAL_STEPS = /* @__PURE__ */ new Set([
5929
+ "tokens",
5930
+ "skills",
5931
+ "ui-init"
5932
+ ]);
5933
+ var IMPLEMENTED_STRATEGIES = {
5934
+ "agents-md": ["merge-managed", "overwrite", "skip"],
5935
+ tokens: ["migrate", "overwrite", "skip"],
5936
+ "components-json": ["overwrite", "skip"],
5937
+ "shadcn-source": ["overwrite", "skip-existing", "skip"],
5938
+ "tailwind-config": ["skip"],
5939
+ "index-css": ["skip"]
5940
+ };
5941
+ function pickIde(ides) {
5942
+ return ides[0] ?? "qoder";
5943
+ }
5944
+ function strategyImplemented(key, strategy) {
5945
+ return IMPLEMENTED_STRATEGIES[key]?.includes(strategy) ?? false;
5946
+ }
5947
+ async function resolveUiEntries(options) {
5948
+ if (options.uiEntries && options.uiEntries.length > 0) {
5949
+ return [...options.uiEntries];
5950
+ }
5951
+ if (options.answers.uiSelection === "all") {
5952
+ const { manifest } = await loadUiData("@teamix-evo/ui");
5953
+ return manifest.entries.map((e) => e.id);
5954
+ }
5955
+ return [...BASELINE_UI_ENTRIES];
5956
+ }
5957
+ async function runProjectInit(options) {
5958
+ const { projectRoot, answers, dryRun = false, onStep } = options;
5959
+ const ide = pickIde(answers.ides);
5960
+ const steps = [];
5961
+ const pending = [];
5962
+ let snapshot = null;
5963
+ let snapshotError;
5964
+ if (!dryRun) {
5965
+ try {
5966
+ snapshot = await createSnapshot(projectRoot, { reason: "init" });
5967
+ } catch (err) {
5968
+ snapshotError = getErrorMessage(err);
5969
+ }
5970
+ }
5971
+ let aborted = false;
5972
+ const firstFailure = {
5973
+ value: null
5974
+ };
5975
+ function record(step) {
5976
+ steps.push(step);
5977
+ onStep?.(step);
5978
+ }
5979
+ function recordPending(key) {
5980
+ const strategy = answers.conflictDecisions[key];
5981
+ if (!strategy) return;
5982
+ if (strategyImplemented(key, strategy)) return;
5983
+ pending.push({
5984
+ key,
5985
+ strategy,
5986
+ reason: `Strategy "${strategy}" requires the managed-region engine (batch 4); recorded for follow-up.`
5987
+ });
5988
+ }
5989
+ function recordFailure(name, err) {
5990
+ const message = getErrorMessage(err);
5991
+ record({ name, status: "fail", detail: message });
5992
+ if (!firstFailure.value)
5993
+ firstFailure.value = { step: name, error: message };
5994
+ if (CRITICAL_STEPS.has(name)) aborted = true;
5995
+ }
5996
+ const tokensDecision = answers.conflictDecisions.tokens;
5997
+ const legacyTokensPaths = options.legacyTokensPaths ?? [];
5998
+ const wantMigrate = tokensDecision === "migrate" && legacyTokensPaths.length > 0;
5999
+ if (tokensDecision === "skip") {
6000
+ record({
6001
+ name: "tokens",
6002
+ status: "skip",
6003
+ detail: "conflict strategy = skip"
6004
+ });
6005
+ } else if (dryRun) {
6006
+ const planDetail = wantMigrate ? `runTokensInit(variant=${answers.variant}); migrateLegacyTokens(${legacyTokensPaths.length} file${legacyTokensPaths.length === 1 ? "" : "s"})` : `runTokensInit(variant=${answers.variant})`;
6007
+ record({
6008
+ name: "tokens",
6009
+ status: "planned",
6010
+ detail: planDetail
6011
+ });
6012
+ } else {
6013
+ try {
6014
+ const result = await runTokensInit({
6015
+ projectRoot,
6016
+ variant: answers.variant,
6017
+ ide
6018
+ });
6019
+ let detail = result.status === "installed" ? `${result.packageName}@${result.version} (${result.count} files)` : result.status;
6020
+ if (wantMigrate) {
6021
+ try {
6022
+ const m = await migrateLegacyTokens({
6023
+ projectRoot,
6024
+ legacyPaths: legacyTokensPaths
6025
+ });
6026
+ detail += `; migrated ${m.migrated.length}/${legacyTokensPaths.length} legacy file${legacyTokensPaths.length === 1 ? "" : "s"} \u2192 ${m.overridesPath}`;
6027
+ if (m.skipped.length > 0) {
6028
+ detail += ` (skipped ${m.skipped.length})`;
6029
+ }
6030
+ } catch (err) {
6031
+ detail += `; migrate failed: ${getErrorMessage(err)}`;
6032
+ }
6033
+ }
6034
+ record({
6035
+ name: "tokens",
6036
+ status: "ok",
6037
+ detail
6038
+ });
6039
+ } catch (err) {
6040
+ recordFailure("tokens", err);
6041
+ }
6042
+ }
6043
+ recordPending("tokens");
6044
+ const codeSkillId = `teamix-evo-code-${answers.variant}`;
6045
+ if (dryRun) {
6046
+ record({
6047
+ name: "skills",
6048
+ status: "planned",
6049
+ detail: `runSkillsAdd(${codeSkillId})`
6050
+ });
6051
+ } else if (aborted) {
6052
+ record({
6053
+ name: "skills",
6054
+ status: "skip",
6055
+ detail: "aborted: earlier critical step failed"
6056
+ });
6057
+ } else {
6058
+ try {
6059
+ const result = await runSkillsAdd({
6060
+ projectRoot,
6061
+ names: [codeSkillId],
6062
+ ides: answers.ides,
6063
+ scope: answers.scope,
6064
+ ide
6065
+ });
6066
+ record({
6067
+ name: "skills",
6068
+ status: "ok",
6069
+ detail: result.status === "installed" ? `added: ${result.addedSkillIds.join(", ") || "none"}; existing: ${result.skippedSkillIds.join(", ") || "none"}` : result.status
6070
+ });
6071
+ } catch (err) {
6072
+ recordFailure("skills", err);
6073
+ }
6074
+ }
6075
+ const agentsMdDecision = answers.conflictDecisions["agents-md"];
6076
+ const agentsMdSkillIds = [
6077
+ `teamix-evo-design-${answers.variant}`,
6078
+ codeSkillId
6079
+ ];
6080
+ if (agentsMdDecision === "skip") {
6081
+ record({
6082
+ name: "agents-md",
6083
+ status: "skip",
6084
+ detail: "conflict strategy = skip"
6085
+ });
6086
+ } else if (dryRun) {
6087
+ record({
6088
+ name: "agents-md",
6089
+ status: "planned",
6090
+ detail: `runGenerateAgentsMd(${agentsMdSkillIds.length} skills)`
6091
+ });
6092
+ } else {
6093
+ try {
6094
+ const result = await runGenerateAgentsMd({
6095
+ projectRoot,
6096
+ variant: answers.variant,
6097
+ skillIds: agentsMdSkillIds
6098
+ });
6099
+ record({
6100
+ name: "agents-md",
6101
+ status: "ok",
6102
+ detail: `${result.skillCount} skill index${result.missingSkillIds.length > 0 ? ` (missing SKILL.md: ${result.missingSkillIds.join(", ")})` : ""}`
6103
+ });
6104
+ } catch (err) {
6105
+ recordFailure("agents-md", err);
6106
+ }
6107
+ }
6108
+ recordPending("agents-md");
6109
+ const componentsJsonDecision = answers.conflictDecisions["components-json"];
6110
+ const shadcnDecision = answers.conflictDecisions["shadcn-source"];
6111
+ const skipUiInit = !answers.withUi || componentsJsonDecision === "skip";
6112
+ if (skipUiInit) {
6113
+ record({
6114
+ name: "ui-init",
6115
+ status: "skip",
6116
+ detail: !answers.withUi ? "withUi = false" : "components.json conflict strategy = skip"
6117
+ });
6118
+ record({
6119
+ name: "ui-add",
6120
+ status: "skip",
6121
+ detail: !answers.withUi ? "withUi = false" : "components.json conflict strategy = skip"
6122
+ });
6123
+ } else if (dryRun) {
6124
+ record({
6125
+ name: "ui-init",
6126
+ status: "planned",
6127
+ detail: "runUiInit()"
6128
+ });
6129
+ const entries = await resolveUiEntries(options).catch(() => [
6130
+ ...BASELINE_UI_ENTRIES
6131
+ ]);
6132
+ record({
6133
+ name: "ui-add",
6134
+ status: "planned",
6135
+ detail: `runUiAdd(${entries.length} entries: ${entries.slice(0, 3).join(", ")}${entries.length > 3 ? "\u2026" : ""})`
6136
+ });
6137
+ } else {
6138
+ if (aborted) {
6139
+ record({
6140
+ name: "ui-init",
6141
+ status: "skip",
6142
+ detail: "aborted: earlier critical step failed"
6143
+ });
6144
+ record({
6145
+ name: "ui-add",
6146
+ status: "skip",
6147
+ detail: "aborted: earlier critical step failed"
6148
+ });
6149
+ } else {
6150
+ let uiInitOk = false;
6151
+ try {
6152
+ const initResult = await runUiInit({ projectRoot, ide });
6153
+ record({
6154
+ name: "ui-init",
6155
+ status: "ok",
6156
+ detail: initResult.status === "installed" ? "config.json packages.ui written" : initResult.status
6157
+ });
6158
+ uiInitOk = true;
6159
+ } catch (err) {
6160
+ recordFailure("ui-init", err);
6161
+ }
6162
+ if (!uiInitOk) {
6163
+ record({
6164
+ name: "ui-add",
6165
+ status: "skip",
6166
+ detail: "aborted: ui-init failed"
6167
+ });
6168
+ } else if (shadcnDecision === "skip") {
6169
+ record({
6170
+ name: "ui-add",
6171
+ status: "skip",
6172
+ detail: "shadcn-source conflict strategy = skip"
6173
+ });
6174
+ } else {
6175
+ try {
6176
+ const entries = await resolveUiEntries(options);
6177
+ const addResult = await runUiAdd({
6178
+ projectRoot,
6179
+ ids: entries,
6180
+ // 'overwrite' strategy → overwrite=true; everything else (incl.
6181
+ // 'skip-existing' which is the default) → overwrite=false.
6182
+ overwrite: shadcnDecision === "overwrite"
6183
+ });
6184
+ record({
6185
+ name: "ui-add",
6186
+ status: "ok",
6187
+ detail: `${addResult.orderedIds.length} entries (${addResult.written} written, ${addResult.skipped} skipped)`
6188
+ });
6189
+ } catch (err) {
6190
+ recordFailure("ui-add", err);
6191
+ }
6192
+ }
6193
+ }
6194
+ }
6195
+ recordPending("components-json");
6196
+ recordPending("shadcn-source");
6197
+ if (!answers.withLint) {
6198
+ record({
6199
+ name: "lint",
6200
+ status: "skip",
6201
+ detail: "withLint = false"
6202
+ });
6203
+ } else if (dryRun) {
6204
+ record({
6205
+ name: "lint",
6206
+ status: "planned",
6207
+ detail: "runLintInit()"
6208
+ });
6209
+ } else {
6210
+ try {
6211
+ const result = await runLintInit({
6212
+ projectRoot,
6213
+ skipInstall: options.skipInstall ?? false
6214
+ });
6215
+ record({
6216
+ name: "lint",
6217
+ status: "ok",
6218
+ detail: result.status === "installed" ? `eslint=${result.eslint}, stylelint=${result.stylelint}` : result.status
6219
+ });
6220
+ } catch (err) {
6221
+ recordFailure("lint", err);
6222
+ }
6223
+ }
6224
+ recordPending("tailwind-config");
6225
+ recordPending("index-css");
6226
+ const status = dryRun ? "dry-run" : steps.some((s) => s.status === "fail") ? "partial" : "installed";
6227
+ const out = {
6228
+ status,
6229
+ steps,
6230
+ pendingConflictWork: pending,
6231
+ snapshot
6232
+ };
6233
+ if (snapshotError) out.snapshotError = snapshotError;
6234
+ if (firstFailure.value) {
6235
+ out.resumeHint = {
6236
+ failedAt: firstFailure.value.step,
6237
+ completed: steps.filter((s) => s.status === "ok").map((s) => s.name),
6238
+ failed: steps.filter((s) => s.status === "fail").map((s) => s.name),
6239
+ error: firstFailure.value.error,
6240
+ // Every sub-step is idempotent (returns `already-initialized` when its
6241
+ // state file already exists), so a plain re-run resumes from the
6242
+ // failed step. A richer --resume model lands with batch 3 (lock
6243
+ // snapshot + restore).
6244
+ resumeCommand: "teamix-evo init"
6245
+ };
6246
+ }
6247
+ return out;
6248
+ }
6249
+
6250
+ // src/commands/init/wizard.ts
6251
+ import * as p from "@clack/prompts";
6252
+ var FRIENDLY_KEY = {
6253
+ "agents-md": "AGENTS.md",
6254
+ "components-json": "components.json",
6255
+ "tailwind-config": "tailwind.config.*",
6256
+ tokens: "tokens / src/design-tokens.css",
6257
+ "index-css": "src/index.css",
6258
+ "shadcn-source": "src/lib/utils.ts + src/components/ui/"
6259
+ };
6260
+ var STRATEGY_LABEL = {
6261
+ overwrite: "\u8986\u76D6\uFF08\u5199\u5165\u65B0\u6587\u4EF6\uFF09",
6262
+ "merge-managed": "\u5408\u5E76\u5230 <!-- teamix-evo:managed --> \u5757\uFF08\u63A8\u8350\uFF09",
6263
+ skip: "\u8DF3\u8FC7\uFF08\u4FDD\u7559\u73B0\u72B6\uFF09",
6264
+ "diff-prompt": "\u5C55\u793A diff \u540E\u9010\u9879\u51B3\u5B9A",
6265
+ "backup-overwrite": "\u5907\u4EFD\u540E\u91CD\u5199\uFF08\u63A8\u8350\uFF09",
6266
+ migrate: "\u8FC1\u79FB\u5230 tokens/ \u6807\u51C6\u4F4D\uFF08\u63A8\u8350\uFF09",
6267
+ coexist: "\u5171\u5B58\uFF08\u4EC5\u5199 overrides\uFF0C\u4E0D\u8986\u76D6\u73B0\u6709\uFF09",
6268
+ append: "\u8FFD\u52A0 @import \u884C + managed \u6807\u8BB0\uFF08\u63A8\u8350\uFF09",
6269
+ "skip-existing": "\u8DF3\u8FC7\u540C\u540D\u6587\u4EF6\uFF0C\u4EC5\u88C5\u65B0\u589E\uFF08\u63A8\u8350\uFF09",
6270
+ "per-file-prompt": "\u9010\u6587\u4EF6\u8BE2\u95EE"
6271
+ };
6272
+ function strategyLabel(s) {
6273
+ return STRATEGY_LABEL[s];
6274
+ }
6275
+ function defaultConflictDecisions(conflicts, override = {}) {
6276
+ const out = {};
6277
+ for (const item of conflicts.items) {
6278
+ out[item.key] = override[item.key] ?? (item.exists ? item.recommendedStrategy : "overwrite");
6279
+ }
6280
+ return out;
6281
+ }
6282
+ function ensureNotCancelled(value) {
6283
+ if (p.isCancel(value)) {
6284
+ throw new CancelledError();
6285
+ }
6286
+ return value;
6287
+ }
6288
+ async function listVariantOptions() {
6289
+ try {
6290
+ const catalog = await listTokenVariants();
6291
+ return catalog.variants.map((v) => ({
6292
+ value: v.name,
6293
+ label: `${v.displayName} (${v.name})`,
6294
+ hint: v.description
6295
+ }));
6296
+ } catch {
6297
+ return [
6298
+ { value: "opentrek", label: "OpenTrek (opentrek)" },
6299
+ { value: "uni-manager", label: "Uni-Manager (uni-manager)" }
6300
+ ];
6301
+ }
6302
+ }
6303
+ async function runInitWizard(options) {
6304
+ const { cwd, conflicts, seed = {} } = options;
6305
+ const interactive = !options.nonInteractive && Boolean(process.stdin.isTTY) && !options.acceptDefaults;
6306
+ let variant = seed.variant;
6307
+ if (!variant) {
6308
+ const variantOptions = await listVariantOptions();
6309
+ if (!interactive) {
6310
+ variant = variantOptions[0]?.value ?? "opentrek";
6311
+ } else {
6312
+ p.intro("teamix-evo init");
6313
+ p.note(`\u{1F4E6} \u63A5\u5165\u76EE\u5F55\uFF1A${cwd}`);
6314
+ const choice = await p.select({
6315
+ message: "Tokens variant",
6316
+ options: variantOptions,
6317
+ initialValue: variantOptions[0]?.value ?? "opentrek"
6318
+ });
6319
+ variant = ensureNotCancelled(choice);
6320
+ }
6321
+ }
6322
+ let ides = seed.ides;
6323
+ if (!ides || ides.length === 0) {
6324
+ if (!interactive) {
6325
+ ides = [...ALL_IDE_KINDS];
6326
+ } else {
6327
+ const choice = await p.multiselect({
6328
+ message: "\u6CE8\u5165\u6280\u80FD\u7684 AI IDE\uFF08\u81F3\u5C11\u4E00\u4E2A\uFF09",
6329
+ options: ALL_IDE_KINDS.map((k) => ({
6330
+ value: k,
6331
+ label: k === "qoder" ? "Qoder" : "Claude Code"
6332
+ })),
6333
+ initialValues: [...ALL_IDE_KINDS],
6334
+ required: true
6335
+ });
6336
+ ides = ensureNotCancelled(choice);
6337
+ }
6338
+ }
6339
+ const scope = seed.scope ?? "project";
6340
+ let withLint = seed.withLint;
6341
+ if (withLint === void 0) {
6342
+ if (!interactive) {
6343
+ withLint = true;
6344
+ } else {
6345
+ const ans = await p.confirm({
6346
+ message: "\u5B89\u88C5 ESLint + Stylelint token-discipline \u89C4\u5219\uFF1F",
6347
+ initialValue: true
6348
+ });
6349
+ withLint = ensureNotCancelled(ans);
6350
+ }
6351
+ }
6352
+ let withUi = seed.withUi;
6353
+ if (withUi === void 0) {
6354
+ if (!interactive) {
6355
+ withUi = true;
6356
+ } else {
6357
+ const ans = await p.confirm({
6358
+ message: "\u5B89\u88C5 @teamix-evo/ui \u7EC4\u4EF6\u6E90\u7801\uFF1F",
6359
+ initialValue: true
6360
+ });
6361
+ withUi = ensureNotCancelled(ans);
6362
+ }
6363
+ }
6364
+ let uiSelection = seed.uiSelection;
6365
+ if (withUi && !uiSelection) {
6366
+ if (!interactive) {
6367
+ uiSelection = "baseline";
6368
+ } else {
6369
+ const ans = await p.select({
6370
+ message: "UI \u7EC4\u4EF6\u6E05\u5355",
6371
+ options: [
6372
+ {
6373
+ value: "baseline",
6374
+ label: "Baseline\uFF08\u63A8\u8350\uFF1Abutton / input / dialog \u7B49\u6838\u5FC3 ~20 \u4E2A\uFF09"
6375
+ },
6376
+ { value: "all", label: "\u5168\u91CF\u88C5\u673A\uFF0880+ \u7EC4\u4EF6\u6E90\u7801\uFF09" }
6377
+ ],
6378
+ initialValue: "baseline"
6379
+ });
6380
+ uiSelection = ensureNotCancelled(ans);
6381
+ }
6382
+ }
6383
+ uiSelection ??= "baseline";
6384
+ let withBizUi = seed.withBizUi;
6385
+ if (withBizUi === void 0) {
6386
+ if (!interactive) {
6387
+ withBizUi = false;
6388
+ } else {
6389
+ const ans = await p.confirm({
6390
+ message: "\u5B89\u88C5 @teamix-evo/biz-ui \u4E1A\u52A1\u7EC4\u4EF6\uFF1F\uFF08\u53EF\u7A0D\u540E\u7528 teamix-evo biz-ui add \u589E\u88C5\uFF09",
6391
+ initialValue: false
6392
+ });
6393
+ withBizUi = ensureNotCancelled(ans);
6394
+ }
6395
+ }
6396
+ const conflictDecisions = defaultConflictDecisions(
6397
+ conflicts,
6398
+ seed.conflictDecisions
6399
+ );
6400
+ if (interactive && conflicts.hasAnyConflict) {
6401
+ p.note(
6402
+ "\u68C0\u6D4B\u5230\u4E0E teamix-evo \u5199\u5165\u8DEF\u5F84\u51B2\u7A81\u7684\u73B0\u6709\u6587\u4EF6\uFF0C\u8BF7\u9010\u9879\u786E\u8BA4\u5904\u7406\u7B56\u7565\u3002\n\u9ED8\u8BA4\u7B56\u7565\u5DF2\u6839\u636E\u5185\u5BB9\u542F\u53D1\u5F0F\u63A8\u8350\uFF08\u6700\u5B89\u5168\u7684\u65B9\u6848\uFF09\u3002",
6403
+ "\u26A0 \u63A5\u5165\u51B2\u7A81"
6404
+ );
6405
+ for (const item of conflicts.items) {
6406
+ if (!item.exists) continue;
6407
+ if (seed.conflictDecisions?.[item.key]) continue;
6408
+ const ans = await p.select({
6409
+ message: `${FRIENDLY_KEY[item.key]} ${describeMatched(item)}`,
6410
+ options: item.availableStrategies.map((s) => ({
6411
+ value: s,
6412
+ label: strategyLabel(s)
6413
+ })),
6414
+ initialValue: item.recommendedStrategy
6415
+ });
6416
+ conflictDecisions[item.key] = ensureNotCancelled(ans);
6417
+ }
6418
+ }
6419
+ if (interactive) {
6420
+ p.outro("\u51C6\u5907\u5C31\u7EEA\uFF0C\u5F00\u59CB\u88C5\u673A\u2026");
6421
+ }
6422
+ return {
6423
+ variant,
6424
+ ides,
6425
+ scope,
6426
+ withLint,
6427
+ withUi,
6428
+ uiSelection,
6429
+ withBizUi,
6430
+ conflictDecisions
6431
+ };
6432
+ }
6433
+ function describeMatched(item) {
6434
+ if (item.paths.length === 0) return "";
6435
+ const head = item.paths.slice(0, 2).join(", ");
6436
+ const more = item.paths.length > 2 ? ` +${item.paths.length - 2}` : "";
6437
+ return `\uFF08\u547D\u4E2D\uFF1A${head}${more}\uFF09`;
6438
+ }
6439
+
6440
+ // src/commands/_shared/step-icon.ts
6441
+ var STEP_ICON = {
6442
+ ok: "\u2714",
6443
+ skip: "\u2298",
6444
+ fail: "\u2716",
6445
+ planned: "\xB7"
6446
+ };
6447
+
6448
+ // src/commands/init/index.ts
6449
+ var initCommand5 = new Command33("init").description(
6450
+ "\u628A teamix-evo AI \u5957\u4EF6\u63A5\u5165\u5DF2\u6709\u5DE5\u7A0B\uFF08\u666E\u901A\u7248\u63A5\u5165\uFF1A\u68C0\u6D4B\u51B2\u7A81 \u2192 \u5F15\u5BFC wizard \u2192 \u9759\u9ED8\u843D\u5730\uFF09"
6451
+ ).option("--cwd <dir>", "\u6307\u5B9A\u5DE5\u7A0B\u6839\u76EE\u5F55\uFF08\u9ED8\u8BA4\uFF1A\u5F53\u524D\u76EE\u5F55\uFF09").option("-y, --yes", "\u8DF3\u8FC7\u4EA4\u4E92\uFF0C\u5168\u90E8\u4F7F\u7528\u63A8\u8350\u9ED8\u8BA4\u503C").option("--dry-run", "\u53EA\u8F93\u51FA\u6267\u884C\u8BA1\u5212\uFF0C\u4E0D\u5199\u4EFB\u4F55\u6587\u4EF6").option("--variant <name>", "tokens variant\uFF08\u8986\u76D6 wizard \u8BE2\u95EE\uFF09").action(async (opts) => {
6452
+ const cwd = path29.resolve(opts.cwd ?? process.cwd());
6453
+ try {
6454
+ const state = await detectProjectState(cwd);
6455
+ if (state.state === "empty") {
6456
+ logger.warn("\u5F53\u524D\u76EE\u5F55\u662F\u7A7A\u76EE\u5F55\u6216\u4EC5\u542B\u53EF\u5FFD\u7565\u6587\u4EF6\u3002");
6457
+ logger.info("");
6458
+ logger.info(
6459
+ "\u63A8\u8350\u4F7F\u7528\u811A\u624B\u67B6\u5B8C\u6574\u7248\uFF1A`npm create teamix-evo@latest`\uFF08\u63D0\u4F9B base \u6A21\u677F + variant overlay + \u9ED8\u8BA4\u9875\u9762\u9AA8\u67B6\uFF09\u3002"
6460
+ );
6461
+ logger.info(
6462
+ "\u5982\u786E\u9700\u5728\u7A7A\u76EE\u5F55\u4E0A\u8DD1 init\uFF0C\u8BF7\u5148 `npm init -y` \u751F\u6210 package.json\u3002"
6463
+ );
6464
+ return;
6465
+ }
6466
+ if (state.state === "teamix-evo-installed") {
6467
+ logger.warn(
6468
+ "\u5F53\u524D\u76EE\u5F55\u5DF2\u68C0\u6D4B\u5230 .teamix-evo/ \u2014 \u8BE5\u5DE5\u7A0B\u5DF2\u7ECF\u63A5\u5165\u8FC7 teamix-evo\u3002"
6469
+ );
6470
+ logger.info("");
6471
+ logger.info("\u8BF7\u4F7F\u7528\uFF1A");
6472
+ logger.info(" \u2022 `teamix-evo update` \u5347\u7EA7 / \u540C\u6B65\u8D44\u6E90\uFF08managed \u533A\u5757\uFF09");
6473
+ logger.info(" \u2022 `teamix-evo doctor` \u8BCA\u65AD\u5F53\u524D\u5B89\u88C5\u72B6\u6001");
6474
+ logger.info(" \u2022 `teamix-evo skills uninstall <id>` \u5378\u8F7D\u6307\u5B9A\u8D44\u6E90");
6475
+ return;
6476
+ }
6477
+ if (!state.hasPackageJson) {
6478
+ logger.error(
6479
+ "\u5F53\u524D\u76EE\u5F55\u975E teamix-evo \u5DE5\u7A0B\u4E14\u65E0 package.json \u2014 \u8BF7\u5728\u5DE5\u7A0B\u6839\u76EE\u5F55\u6267\u884C\uFF0C\u6216\u5148 `npm init -y`\u3002"
6480
+ );
6481
+ process.exitCode = 1;
6482
+ return;
6483
+ }
6484
+ const conflicts = await detectConflicts(cwd);
6485
+ const isInteractive = Boolean(process.stdin.isTTY);
6486
+ const answers = await runInitWizard({
6487
+ cwd,
6488
+ conflicts,
6489
+ seed: opts.variant ? { variant: opts.variant } : void 0,
6490
+ acceptDefaults: opts.yes ?? false,
6491
+ nonInteractive: !isInteractive
6492
+ });
6493
+ const dryRun = opts.dryRun ?? false;
6494
+ if (dryRun) {
6495
+ logger.info("");
6496
+ logger.info("\u{1F4CB} dry-run \u6A21\u5F0F\uFF1A\u4EE5\u4E0B\u8BA1\u5212\u4E0D\u4F1A\u5199\u5165\u4EFB\u4F55\u6587\u4EF6");
6497
+ }
6498
+ const legacyTokensPaths = (conflicts.items.find((it) => it.key === "tokens")?.paths ?? []).filter((p2) => !p2.startsWith("tokens/"));
6499
+ const result = await runProjectInit({
6500
+ projectRoot: cwd,
6501
+ answers,
6502
+ dryRun,
6503
+ legacyTokensPaths,
6504
+ onStep: (step) => {
6505
+ const icon = STEP_ICON[step.status];
6506
+ const detail = step.detail ? ` \u2014 ${step.detail}` : "";
6507
+ logger.info(` ${icon} ${step.name}${detail}`);
6508
+ }
6509
+ });
6510
+ logger.info("");
6511
+ if (result.status === "dry-run") {
6512
+ logger.success("dry-run \u5B8C\u6210\u3002\u79FB\u9664 --dry-run \u5373\u53EF\u5B9E\u9645\u6267\u884C\u3002");
6513
+ } else if (result.status === "partial") {
6514
+ logger.warn("init \u90E8\u5206\u5B8C\u6210\uFF08\u542B\u5931\u8D25\u6B65\u9AA4\uFF09\u3002\u8BE6\u89C1\u4E0A\u65B9\u6B65\u9AA4\u660E\u7EC6\u3002");
6515
+ if (result.resumeHint) {
6516
+ const { failedAt, completed, error, resumeCommand } = result.resumeHint;
6517
+ logger.info("");
6518
+ logger.info("\u6062\u590D\u6307\u5F15\uFF1A");
6519
+ logger.info(` \u2022 \u5931\u8D25\u6B65\u9AA4\uFF1A${failedAt}`);
6520
+ logger.info(` \u2022 \u9519\u8BEF\u4FE1\u606F\uFF1A${error}`);
6521
+ if (completed.length > 0) {
6522
+ logger.info(
6523
+ ` \u2022 \u5DF2\u5B8C\u6210\u6B65\u9AA4\uFF08\u518D\u6B21\u8FD0\u884C\u4F1A\u81EA\u52A8\u8DF3\u8FC7\uFF09\uFF1A${completed.join(", ")}`
6524
+ );
6525
+ }
6526
+ logger.info(
6527
+ ` \u2022 \u4FEE\u590D\u540E\u91CD\u8DD1\uFF1A\`${resumeCommand}\`\uFF08\u6BCF\u4E2A\u5B50\u6B65\u9AA4\u5E42\u7B49\uFF0C\u4F1A\u4ECE\u65AD\u70B9\u5904\u7EED\u63A5\uFF09`
6528
+ );
6529
+ if (result.snapshot) {
6530
+ logger.info(
6531
+ ` \u2022 \u5B8C\u5168\u56DE\u6EDA\uFF1A\`teamix-evo restore ${result.snapshot.ts}\`\uFF08\u64A4\u9500\u672C\u6B21 init \u5199\u5165 \u2014 ADR 0019 \xA72\uFF09`
6532
+ );
6533
+ }
6534
+ }
6535
+ process.exitCode = 1;
6536
+ } else {
6537
+ logger.success(`teamix-evo \u5DF2\u63A5\u5165 ${cwd}`);
6538
+ if (result.snapshot) {
6539
+ logger.info(
6540
+ `\u{1F4BE} \u5DF2\u6355\u83B7 snapshot\uFF1A${result.snapshot.ts}\uFF08\u5982\u9700\u5B8C\u5168\u64A4\u9500\uFF1A\`teamix-evo restore ${result.snapshot.ts}\`\uFF09`
6541
+ );
6542
+ } else if (result.snapshotError) {
6543
+ logger.debug(`snapshot \u6355\u83B7\u5931\u8D25\uFF1A${result.snapshotError}`);
6544
+ }
6545
+ }
6546
+ if (result.pendingConflictWork.length > 0) {
6547
+ logger.info("");
6548
+ logger.warn(
6549
+ `\u4EE5\u4E0B\u51B2\u7A81\u7B56\u7565\u9700\u8981 managed-region \u5F15\u64CE\uFF08\u6279\u6B21 4 \u843D\u5730\u540E\u53EF\u7528 \`teamix-evo conflict resolve\` \u5B8C\u6210\uFF09\uFF1A`
6550
+ );
6551
+ for (const w of result.pendingConflictWork) {
6552
+ logger.info(` \u2022 ${w.key} \u2192 ${w.strategy}`);
6553
+ }
6554
+ }
6555
+ if (result.status === "installed") {
6556
+ logger.info("");
6557
+ logger.info("Next steps:");
6558
+ logger.info(" \u2022 \u542F\u52A8 dev server \u9A8C\u8BC1\uFF1A`npm run dev`");
6559
+ if (answers.withLint) {
6560
+ logger.info(" \u2022 lint \u68C0\u67E5\uFF1A`npm run lint` / `npm run lint:css`");
6561
+ }
6562
+ }
6563
+ } catch (err) {
6564
+ if (err instanceof CancelledError) {
6565
+ logger.info("init \u5DF2\u53D6\u6D88\u3002");
6566
+ return;
6567
+ }
6568
+ const message = getErrorMessage(err);
6569
+ logger.error(`init \u5931\u8D25\uFF1A${message}`);
6570
+ logger.debug(err instanceof Error ? err.stack ?? "" : "");
6571
+ process.exitCode = 1;
6572
+ }
6573
+ });
6574
+
6575
+ // src/commands/update/index.ts
6576
+ import { Command as Command34 } from "commander";
6577
+ import * as path31 from "path";
6578
+
6579
+ // src/core/project-update.ts
6580
+ import * as path30 from "path";
6581
+ import {
6582
+ loadTokensPackageManifest as loadTokensPackageManifest3,
6583
+ getVariantEntry as getVariantEntry3
6584
+ } from "@teamix-evo/registry";
6585
+ var DEFAULT_TOKENS_PACKAGE3 = "@teamix-evo/tokens";
6586
+ var DEFAULT_SKILLS_PACKAGE4 = "@teamix-evo/skills";
6587
+ var CRITICAL_STEPS2 = /* @__PURE__ */ new Set(["tokens"]);
6588
+ async function runProjectUpdate(options) {
6589
+ const { projectRoot, dryRun = false, onStep } = options;
6590
+ const tokensPackage = options.tokensPackageName ?? DEFAULT_TOKENS_PACKAGE3;
6591
+ const skillsPackage = options.skillsPackageName ?? DEFAULT_SKILLS_PACKAGE4;
6592
+ const config = await readProjectConfig(projectRoot);
6593
+ if (!config) {
6594
+ return { status: "not-initialized" };
6595
+ }
6596
+ let snapshot = null;
6597
+ let snapshotError;
6598
+ if (!dryRun) {
6599
+ try {
6600
+ snapshot = await createSnapshot(projectRoot, { reason: "update" });
6601
+ } catch (err) {
6602
+ snapshotError = getErrorMessage(err);
6603
+ }
6604
+ }
6605
+ const steps = [];
6606
+ let aborted = false;
6607
+ const firstFailure = { value: null };
6608
+ function record(step) {
6609
+ steps.push(step);
6610
+ onStep?.(step);
6611
+ }
6612
+ function recordFailure(name, err) {
6613
+ const message = getErrorMessage(err);
6614
+ record({ name, status: "fail", detail: message });
6615
+ if (!firstFailure.value) {
6616
+ firstFailure.value = { step: name, error: message };
6617
+ }
6618
+ if (CRITICAL_STEPS2.has(name)) aborted = true;
6619
+ }
6620
+ let anyChanged = false;
6621
+ if (!config.packages?.tokens) {
6622
+ record({
6623
+ name: "tokens",
6624
+ status: "skip",
6625
+ detail: "tokens not installed"
6626
+ });
6627
+ } else if (dryRun) {
6628
+ try {
6629
+ const plan = await planTokensUpdate(tokensPackage, config);
6630
+ record({ name: "tokens", status: "planned", detail: plan });
6631
+ } catch (err) {
6632
+ recordFailure("tokens", err);
6633
+ }
6634
+ } else {
6635
+ try {
6636
+ const result = await runTokensUpdate({
6637
+ projectRoot,
6638
+ packageName: tokensPackage
6639
+ });
6640
+ if (result.status === "not-initialized") {
6641
+ record({
6642
+ name: "tokens",
6643
+ status: "skip",
6644
+ detail: "tokens not installed"
6645
+ });
6646
+ } else if (result.status === "up-to-date") {
6647
+ const driftSuffix = result.frozenDrift.length > 0 ? `, frozen drift: ${result.frozenDrift.length}` : "";
6648
+ record({
6649
+ name: "tokens",
6650
+ status: "ok",
6651
+ detail: `up-to-date (${result.variant} v${result.version}${driftSuffix})`
6652
+ });
6653
+ } else {
6654
+ anyChanged = true;
6655
+ const extras = [];
6656
+ if (result.managedReplaced.length > 0)
6657
+ extras.push(`managed: ${result.managedReplaced.length}`);
6658
+ if (result.frozenDrift.length > 0)
6659
+ extras.push(`frozen drift: ${result.frozenDrift.length}`);
6660
+ const suffix = extras.length > 0 ? ` [${extras.join(", ")}]` : "";
6661
+ record({
6662
+ name: "tokens",
6663
+ status: "ok",
6664
+ detail: `${result.variant} v${result.from} \u2192 v${result.to}${suffix}`
6665
+ });
6666
+ }
6667
+ } catch (err) {
6668
+ recordFailure("tokens", err);
6669
+ }
6670
+ }
6671
+ if (!config.packages?.skills) {
6672
+ record({
6673
+ name: "skills",
6674
+ status: "skip",
6675
+ detail: "skills not installed"
6676
+ });
6677
+ } else if (aborted) {
6678
+ record({
6679
+ name: "skills",
6680
+ status: "skip",
6681
+ detail: "aborted: earlier critical step failed"
6682
+ });
6683
+ } else {
6684
+ try {
6685
+ const result = await runSkillsUpdate({
6686
+ projectRoot,
6687
+ dryRun,
6688
+ packageName: skillsPackage
6689
+ });
6690
+ if (result.status === "no-skills") {
6691
+ record({
6692
+ name: "skills",
6693
+ status: "skip",
6694
+ detail: "no skills locked"
6695
+ });
6696
+ } else if (result.status === "no-changes") {
6697
+ record({
6698
+ name: "skills",
6699
+ status: "ok",
6700
+ detail: `up-to-date (${result.checkedSkillIds.length} skill(s) at v${result.version})`
6701
+ });
6702
+ } else if (result.status === "dry-run") {
6703
+ const bumps = result.plan.filter((p2) => p2.action === "version-bump");
6704
+ const detail = bumps.length === 0 ? `up-to-date (${result.plan.length} skill(s) checked at v${result.currentVersion})` : `${bumps.length} skill(s) would update: ${result.currentVersion} \u2192 ${result.availableVersion}`;
6705
+ record({ name: "skills", status: "planned", detail });
6706
+ } else {
6707
+ anyChanged = true;
6708
+ const summary = result.updatedSkillIds.length > 0 ? `updated: ${result.updatedSkillIds.join(", ")} (v${result.version})` : `refreshed at v${result.version}`;
6709
+ record({ name: "skills", status: "ok", detail: summary });
6710
+ }
6711
+ } catch (err) {
6712
+ recordFailure("skills", err);
6713
+ }
6714
+ }
6715
+ await runComponentSourceStep("ui", {
6716
+ projectRoot,
6717
+ config,
6718
+ dryRun,
6719
+ record,
6720
+ recordFailure
6721
+ });
6722
+ await runComponentSourceStep("biz-ui", {
6723
+ projectRoot,
6724
+ config,
6725
+ dryRun,
6726
+ record,
6727
+ recordFailure
6728
+ });
6729
+ const out = (() => {
6730
+ if (firstFailure.value) {
6731
+ return {
6732
+ status: "partial",
6733
+ steps,
6734
+ resumeHint: {
6735
+ failedAt: firstFailure.value.step,
6736
+ completed: steps.filter((s) => s.status === "ok").map((s) => s.name),
6737
+ failed: steps.filter((s) => s.status === "fail").map((s) => s.name),
6738
+ error: firstFailure.value.error,
6739
+ resumeCommand: "teamix-evo update"
6740
+ },
6741
+ snapshot,
6742
+ ...snapshotError ? { snapshotError } : {}
6743
+ };
6744
+ }
6745
+ if (dryRun) return { status: "dry-run", steps, snapshot: null };
6746
+ if (anyChanged)
6747
+ return {
6748
+ status: "updated",
6749
+ steps,
6750
+ snapshot,
6751
+ ...snapshotError ? { snapshotError } : {}
6752
+ };
6753
+ return {
6754
+ status: "up-to-date",
6755
+ steps,
6756
+ snapshot,
6757
+ ...snapshotError ? { snapshotError } : {}
6758
+ };
6759
+ })();
6760
+ return out;
6761
+ }
6762
+ async function runComponentSourceStep(category, args) {
6763
+ const { projectRoot, config, dryRun, record, recordFailure } = args;
6764
+ const cfgKey = category === "ui" ? "ui" : "biz-ui";
6765
+ if (!config.packages?.[cfgKey]) {
6766
+ record({
6767
+ name: category,
6768
+ status: "skip",
6769
+ detail: `${category} not installed`
6770
+ });
6771
+ return;
6772
+ }
6773
+ const aliases = config.packages.ui?.aliases ?? config.packages["biz-ui"]?.aliases;
6774
+ if (!aliases) {
6775
+ record({
6776
+ name: category,
6777
+ status: "skip",
6778
+ detail: `${category} aliases not configured`
6779
+ });
6780
+ return;
6781
+ }
6782
+ let report;
6783
+ try {
6784
+ report = await detectComponentLineage({
6785
+ projectRoot,
6786
+ category,
6787
+ config
6788
+ });
6789
+ } catch (err) {
6790
+ record({
6791
+ name: category,
6792
+ status: "skip",
6793
+ detail: `lineage detect failed: ${getErrorMessage(err)}`
6794
+ });
6795
+ return;
6796
+ }
6797
+ if (report.lineage !== "teamix-evo" && report.lineage !== "mixed") {
6798
+ record({
6799
+ name: category,
6800
+ status: "skip",
6801
+ detail: `lineage=${report.lineage}; nothing to stage`
6802
+ });
6803
+ return;
6804
+ }
6805
+ if (dryRun) {
6806
+ const total = report.registeredIds.length + report.unregisteredIds.length;
6807
+ record({
6808
+ name: category,
6809
+ status: "planned",
6810
+ detail: total === 0 ? "no components to stage" : `${total} component(s) to stage (lineage=${report.lineage})`
6811
+ });
6812
+ return;
6813
+ }
6814
+ try {
6815
+ const built = await buildStaging({
6816
+ category,
6817
+ projectRoot,
6818
+ aliases,
6819
+ lineageReport: report,
6820
+ trigger: "update",
6821
+ onlyIds: []
6822
+ });
6823
+ if (built === null) {
6824
+ record({
6825
+ name: category,
6826
+ status: "skip",
6827
+ detail: "no entries to stage"
6828
+ });
6829
+ return;
6830
+ }
6831
+ const stagingRel = path30.relative(projectRoot, built.stagingDir);
6832
+ const summary = summarizeStagingRisk(built.manifest.summary.byRisk);
6833
+ record({
6834
+ name: category,
6835
+ status: "ok",
6836
+ detail: `${built.manifest.summary.total} component(s) staged${summary ? ` (${summary})` : ""}, see ${stagingRel}`
6837
+ });
6838
+ } catch (err) {
6839
+ recordFailure(category, err);
6840
+ }
6841
+ }
6842
+ function summarizeStagingRisk(byRisk) {
6843
+ const order = [
6844
+ "risky",
6845
+ "breaking",
6846
+ "foreign",
6847
+ "upgradable-medium",
6848
+ "upgradable-low",
6849
+ "unchanged"
6850
+ ];
6851
+ const parts = [];
6852
+ for (const k of order) {
6853
+ const v = byRisk[k];
6854
+ if (v && v > 0) parts.push(`${v} ${k}`);
6855
+ }
6856
+ return parts.join(", ");
6857
+ }
6858
+ async function planTokensUpdate(tokensPackage, config) {
6859
+ const tokensCfg = config.packages?.tokens;
6860
+ if (!tokensCfg) return "tokens not installed";
6861
+ const packageRoot = resolveTokensPackageRoot(tokensPackage);
6862
+ const catalog = await loadTokensPackageManifest3(packageRoot);
6863
+ const variantEntry = getVariantEntry3(catalog, tokensCfg.variant);
6864
+ if (!variantEntry) {
6865
+ return `variant "${tokensCfg.variant}" no longer in ${tokensPackage}@${catalog.version} \u2014 uninstall + re-init to switch`;
6866
+ }
6867
+ if (variantEntry.version === tokensCfg.version) {
6868
+ return `up-to-date (${tokensCfg.variant} v${tokensCfg.version})`;
6869
+ }
6870
+ return `${tokensCfg.variant} v${tokensCfg.version} \u2192 v${variantEntry.version}`;
6871
+ }
6872
+
6873
+ // src/commands/update/index.ts
6874
+ var updateCommand3 = new Command34("update").description(
6875
+ "\u4E00\u952E\u5347\u7EA7 teamix-evo \u5DF2\u88C5\u8D44\u6E90\uFF08regenerable \u8986\u76D6\u3001frozen \u4FDD\u7559\u3001managed \u5408\u5E76 \u2014 ADR 0003 \u4E09\u6001\uFF09"
6876
+ ).option("--cwd <dir>", "\u6307\u5B9A\u5DE5\u7A0B\u6839\u76EE\u5F55\uFF08\u9ED8\u8BA4\uFF1A\u5F53\u524D\u76EE\u5F55\uFF09").option("--dry-run", "\u53EA\u8F93\u51FA\u5347\u7EA7\u8BA1\u5212\uFF0C\u4E0D\u5199\u4EFB\u4F55\u6587\u4EF6").action(async (opts) => {
6877
+ const cwd = path31.resolve(opts.cwd ?? process.cwd());
6878
+ const dryRun = opts.dryRun ?? false;
6879
+ try {
6880
+ if (dryRun) {
6881
+ logger.info("");
6882
+ logger.info("\u{1F4CB} dry-run \u6A21\u5F0F\uFF1A\u4EE5\u4E0B\u8BA1\u5212\u4E0D\u4F1A\u5199\u5165\u4EFB\u4F55\u6587\u4EF6");
6883
+ }
6884
+ const result = await runProjectUpdate({
6885
+ projectRoot: cwd,
6886
+ dryRun,
6887
+ onStep: (step) => {
6888
+ const icon = STEP_ICON[step.status];
6889
+ const detail = step.detail ? ` \u2014 ${step.detail}` : "";
6890
+ logger.info(` ${icon} ${step.name}${detail}`);
6891
+ }
6892
+ });
6893
+ logger.info("");
6894
+ if (result.status === "not-initialized") {
6895
+ logger.warn(
6896
+ "\u5F53\u524D\u76EE\u5F55\u672A\u68C0\u6D4B\u5230 .teamix-evo/ \u2014 \u8BE5\u5DE5\u7A0B\u5C1A\u672A\u63A5\u5165 teamix-evo\u3002"
6897
+ );
6898
+ logger.info("");
6899
+ logger.info("\u8BF7\u4F7F\u7528\uFF1A");
6900
+ logger.info(
6901
+ " \u2022 `npx teamix-evo@latest init` \u666E\u901A\u7248\u63A5\u5165\uFF08\u5DF2\u6709 npm \u5DE5\u7A0B\uFF09"
6902
+ );
6903
+ logger.info(
6904
+ " \u2022 `npm create teamix-evo@latest` \u5B8C\u6574\u7248\u811A\u624B\u67B6\uFF08\u7A7A\u76EE\u5F55\uFF09"
6905
+ );
6906
+ process.exitCode = 1;
6907
+ return;
6908
+ }
6909
+ if (result.status === "dry-run") {
6910
+ logger.success("dry-run \u5B8C\u6210\u3002\u79FB\u9664 --dry-run \u5373\u53EF\u5B9E\u9645\u6267\u884C\u3002");
6911
+ return;
6912
+ }
6913
+ if (result.status === "partial") {
6914
+ logger.warn("update \u90E8\u5206\u5B8C\u6210\uFF08\u542B\u5931\u8D25\u6B65\u9AA4\uFF09\u3002\u8BE6\u89C1\u4E0A\u65B9\u6B65\u9AA4\u660E\u7EC6\u3002");
6915
+ if (result.resumeHint) {
6916
+ const { failedAt, completed, error, resumeCommand } = result.resumeHint;
6917
+ logger.info("");
6918
+ logger.info("\u6062\u590D\u6307\u5F15\uFF1A");
6919
+ logger.info(` \u2022 \u5931\u8D25\u6B65\u9AA4\uFF1A${failedAt}`);
6920
+ logger.info(` \u2022 \u9519\u8BEF\u4FE1\u606F\uFF1A${error}`);
6921
+ if (completed.length > 0) {
6922
+ logger.info(
6923
+ ` \u2022 \u5DF2\u5B8C\u6210\u6B65\u9AA4\uFF08\u518D\u6B21\u8FD0\u884C\u4F1A\u81EA\u52A8\u8DF3\u8FC7\uFF09\uFF1A${completed.join(", ")}`
6924
+ );
6925
+ }
6926
+ logger.info(
6927
+ ` \u2022 \u4FEE\u590D\u540E\u91CD\u8DD1\uFF1A\`${resumeCommand}\`\uFF08\u6BCF\u4E2A\u5B50\u6B65\u9AA4\u5E42\u7B49\uFF0Cversion-diff \u77ED\u8DEF\uFF09`
6928
+ );
6929
+ if (result.snapshot) {
6930
+ logger.info(
6931
+ ` \u2022 \u5B8C\u5168\u56DE\u6EDA\uFF1A\`teamix-evo restore ${result.snapshot.ts}\`\uFF08\u64A4\u9500\u672C\u6B21 update \u5199\u5165 \u2014 ADR 0019 \xA72\uFF09`
6932
+ );
6933
+ }
6934
+ }
6935
+ process.exitCode = 1;
6936
+ return;
6937
+ }
6938
+ if (result.status === "up-to-date") {
6939
+ logger.success("\u5DF2\u662F\u6700\u65B0\u7248\u672C\uFF0C\u65E0\u9700\u5347\u7EA7\u3002");
6940
+ return;
6941
+ }
6942
+ logger.success("teamix-evo \u5DF2\u5347\u7EA7\u5230\u6700\u65B0\u7248\u672C\u3002");
6943
+ if (result.snapshot) {
6944
+ logger.info(
6945
+ `\u{1F4BE} \u5DF2\u6355\u83B7 snapshot\uFF1A${result.snapshot.ts}\uFF08\u5982\u9700\u5B8C\u5168\u64A4\u9500\uFF1A\`teamix-evo restore ${result.snapshot.ts}\`\uFF09`
6946
+ );
6947
+ } else if (result.snapshotError) {
6948
+ logger.debug(`snapshot \u6355\u83B7\u5931\u8D25\uFF1A${result.snapshotError}`);
6949
+ }
6950
+ } catch (err) {
6951
+ const message = getErrorMessage(err);
6952
+ logger.error(`update \u5931\u8D25\uFF1A${message}`);
6953
+ logger.debug(err instanceof Error ? err.stack ?? "" : "");
6954
+ process.exitCode = 1;
6955
+ }
6956
+ });
6957
+
6958
+ // src/commands/restore/index.ts
6959
+ import { Command as Command35 } from "commander";
6960
+ import * as path32 from "path";
6961
+ import * as prompts7 from "@clack/prompts";
6962
+ function createRestoreCommand() {
6963
+ return new Command35("restore").description(
6964
+ "\u56DE\u6EDA .teamix-evo/ \u5230\u6307\u5B9A snapshot\uFF08init/update \u5931\u8D25\u65F6\u4F7F\u7528 \u2014 ADR 0019 \xA72\uFF09"
6965
+ ).argument("[ts]", "\u76EE\u6807 snapshot \u65F6\u95F4\u6233\uFF08\u5982 2026-06-11T20-59-03-000Z\uFF09").option("--list", "\u5217\u51FA\u53EF\u7528 snapshot\uFF08\u4E0D\u505A\u5B9E\u9645\u56DE\u6EDA\uFF09").option("--cwd <dir>", "\u6307\u5B9A\u5DE5\u7A0B\u6839\u76EE\u5F55\uFF08\u9ED8\u8BA4\uFF1A\u5F53\u524D\u76EE\u5F55\uFF09").option("-y, --yes", "\u8DF3\u8FC7\u4E8C\u6B21\u786E\u8BA4\uFF08destructive\uFF0C\u8BF7\u786E\u8BA4\u65E0\u8BEF\uFF09").action(async (ts, opts) => {
6966
+ const cwd = path32.resolve(opts.cwd ?? process.cwd());
6967
+ try {
6968
+ const snapshots = await listSnapshots(cwd);
6969
+ if (opts.list) {
6970
+ printSnapshotTable(snapshots);
6971
+ return;
6972
+ }
6973
+ if (!ts) {
6974
+ logger.info("\u7528\u6CD5\uFF1A");
6975
+ logger.info(
6976
+ " teamix-evo restore --list \u5217\u51FA\u53EF\u7528 snapshot"
6977
+ );
6978
+ logger.info(
6979
+ " teamix-evo restore <ts> [-y] \u56DE\u6EDA\u5230\u6307\u5B9A snapshot"
6980
+ );
6981
+ logger.info("");
6982
+ if (snapshots.length === 0) {
6983
+ logger.warn(
6984
+ "\u5F53\u524D .teamix-evo/.snapshots/ \u4E3A\u7A7A\uFF08\u6216\u5DE5\u7A0B\u672A\u63A5\u5165 teamix-evo\uFF09\u3002"
6985
+ );
6986
+ } else {
6987
+ logger.info(`\u5F53\u524D\u5171 ${snapshots.length} \u4E2A\u53EF\u7528 snapshot\u3002`);
6988
+ }
6989
+ process.exitCode = 1;
6990
+ return;
6991
+ }
6992
+ const target = snapshots.find((s) => s.ts === ts);
6993
+ if (!target) {
6994
+ logger.error(`\u672A\u627E\u5230 snapshot\uFF1A${ts}`);
6995
+ logger.info("");
6996
+ logger.info(
6997
+ "\u8FD0\u884C `teamix-evo restore --list` \u67E5\u770B\u53EF\u7528 snapshot \u5217\u8868\u3002"
6998
+ );
6999
+ process.exitCode = 1;
7000
+ return;
7001
+ }
7002
+ const isInteractive = Boolean(process.stdin.isTTY);
7003
+ if (!opts.yes && isInteractive) {
7004
+ logger.info("");
7005
+ logger.info(`\u5373\u5C06\u56DE\u6EDA\u5230 snapshot\uFF1A${target.ts}`);
7006
+ if (target.reason) logger.info(` \u2022 \u539F\u56E0\uFF1A${target.reason}`);
7007
+ if (target.isoTs) logger.info(` \u2022 \u65F6\u95F4\uFF1A${target.isoTs}`);
7008
+ logger.info(
7009
+ "\u8BE5\u64CD\u4F5C\u4F1A\u8986\u76D6\u5F53\u524D .teamix-evo/ \u5185\u5BB9\uFF08\u9664 .snapshots/ \u4E0E logs/ \u5916\uFF09\u3002"
7010
+ );
7011
+ logger.info(
7012
+ '\u56DE\u6EDA\u524D\u4F1A\u81EA\u52A8\u518D\u505A\u4E00\u6B21 reason="restore" \u7684\u5B89\u5168 snapshot\uFF0C\u53EF\u4E8C\u6B21\u56DE\u6EDA\u3002'
7013
+ );
7014
+ const confirmed = await prompts7.confirm({
7015
+ message: "\u7EE7\u7EED\u56DE\u6EDA\uFF1F"
7016
+ });
7017
+ if (prompts7.isCancel(confirmed) || !confirmed) {
7018
+ logger.info("\u5DF2\u53D6\u6D88\u3002");
7019
+ return;
7020
+ }
7021
+ }
7022
+ await restoreSnapshot(cwd, ts);
7023
+ logger.success(`\u5DF2\u56DE\u6EDA\u5230 snapshot\uFF1A${ts}`);
7024
+ logger.info(
7025
+ '\u63D0\u793A\uFF1A\u672C\u6B21\u64CD\u4F5C\u524D\u7684\u72B6\u6001\u5DF2\u4FDD\u5B58\u4E3A\u65B0\u7684 reason="restore" snapshot\uFF0C\u5982\u9700\u518D\u6B21\u56DE\u6EDA\u53EF\u6267\u884C `teamix-evo restore --list` \u67E5\u770B\u3002'
7026
+ );
7027
+ } catch (err) {
7028
+ const message = getErrorMessage(err);
7029
+ logger.error(`restore \u5931\u8D25\uFF1A${message}`);
7030
+ logger.debug(err.stack ?? "");
7031
+ process.exitCode = 1;
7032
+ }
7033
+ });
7034
+ }
7035
+ var restoreCommand = createRestoreCommand();
7036
+ function printSnapshotTable(snapshots) {
7037
+ if (snapshots.length === 0) {
7038
+ logger.warn("\u672A\u627E\u5230\u53EF\u7528 snapshot\u3002");
7039
+ logger.info("");
7040
+ logger.info(
7041
+ "\u63D0\u793A\uFF1Asnapshot \u7531 `teamix-evo init` / `teamix-evo update` \u5728\u6267\u884C\u524D\u81EA\u52A8\u751F\u6210\uFF1B"
7042
+ );
7043
+ logger.info(
7044
+ "\u82E5\u5DE5\u7A0B\u521A\u63A5\u5165\u6216\u4ECE\u672A\u8FD0\u884C\u8FC7\u8FD9\u4E9B\u547D\u4EE4\uFF0C\u76EE\u5F55\u4F1A\u662F\u7A7A\u7684\uFF08\u8FD9\u662F\u6B63\u5E38\u72B6\u6001\uFF09\u3002"
7045
+ );
7046
+ return;
7047
+ }
7048
+ logger.info(`\u5171 ${snapshots.length} \u4E2A snapshot\uFF08\u6700\u65B0\u5728\u524D\uFF09\uFF1A`);
7049
+ logger.info("");
7050
+ for (let i = 0; i < snapshots.length; i++) {
7051
+ const s = snapshots[i];
7052
+ const reason = s.reason ?? "(unknown)";
7053
+ const iso = s.isoTs ?? "(meta missing)";
7054
+ logger.info(` [${i + 1}] ${s.ts}`);
7055
+ logger.info(` reason=${reason} iso=${iso}`);
7056
+ }
7057
+ logger.info("");
7058
+ logger.info("\u56DE\u6EDA\u547D\u4EE4\uFF1Ateamix-evo restore <ts>");
7059
+ }
7060
+
7061
+ // src/commands/switch/index.ts
7062
+ import { Command as Command36 } from "commander";
7063
+ import * as path34 from "path";
7064
+ import * as prompts8 from "@clack/prompts";
7065
+
7066
+ // src/core/variant-switch.ts
7067
+ import * as path33 from "path";
7068
+ import * as fs22 from "fs/promises";
7069
+ import {
7070
+ loadTokensPackageManifest as loadTokensPackageManifest4,
7071
+ getVariantEntry as getVariantEntry4
7072
+ } from "@teamix-evo/registry";
7073
+ var DEFAULT_TOKENS_PACKAGE4 = "@teamix-evo/tokens";
7074
+ var CONSUMER_BASENAME_BY_UPSTREAM2 = {
7075
+ "theme.css": "tokens.theme.css",
7076
+ "overrides.css": "tokens.overrides.css"
7077
+ };
7078
+ async function runVariantSwitch(options) {
7079
+ const { projectRoot, newVariant, apply = false } = options;
7080
+ const packageName = options.packageName ?? DEFAULT_TOKENS_PACKAGE4;
7081
+ const config = await readProjectConfig(projectRoot);
7082
+ if (!config?.packages?.tokens) {
7083
+ return { status: "not-initialized" };
7084
+ }
7085
+ const currentVariant = config.packages.tokens.variant;
7086
+ if (currentVariant === newVariant) {
7087
+ return { status: "already-on-variant", variant: currentVariant };
7088
+ }
7089
+ const packageRoot = options.packageRoot ?? resolveTokensPackageRoot(packageName);
7090
+ const catalog = await loadTokensPackageManifest4(packageRoot);
7091
+ const variantEntry = getVariantEntry4(catalog, newVariant);
7092
+ if (!variantEntry) {
7093
+ return {
7094
+ status: "variant-not-found",
7095
+ requested: newVariant,
7096
+ available: catalog.variants.map((v) => v.name)
7097
+ };
7098
+ }
7099
+ const upstreamByBasename = /* @__PURE__ */ new Map();
7100
+ for (const fileRel of variantEntry.files) {
7101
+ upstreamByBasename.set(
7102
+ path33.basename(fileRel),
7103
+ path33.join(packageRoot, fileRel)
7104
+ );
7105
+ }
7106
+ const prior = await readInstalledManifest(projectRoot) ?? {
7107
+ schemaVersion: 1,
7108
+ installed: []
7109
+ };
7110
+ const installedIdx = prior.installed.findIndex(
7111
+ (p2) => p2.package === packageName
7112
+ );
7113
+ const priorResources = installedIdx >= 0 ? prior.installed[installedIdx].resources : [];
7114
+ const priorVersion = installedIdx >= 0 ? prior.installed[installedIdx].version : "0.0.0";
7115
+ const changes = [];
7116
+ for (const resource of priorResources) {
7117
+ const consumerBasename = path33.basename(resource.target);
7118
+ const upstreamBasename = lookupUpstreamBasename2(consumerBasename);
7119
+ const upstreamAbs = upstreamBasename ? upstreamByBasename.get(upstreamBasename) : void 0;
7120
+ if (resource.strategy === "frozen") {
7121
+ changes.push({
7122
+ target: resource.target,
7123
+ kind: "preserve",
7124
+ strategy: "frozen",
7125
+ reason: "frozen \u2014 user-owned, never auto-overwritten"
7126
+ });
7127
+ continue;
7128
+ }
7129
+ if (!upstreamAbs) {
7130
+ changes.push({
7131
+ target: resource.target,
7132
+ kind: "preserve",
7133
+ strategy: resource.strategy,
7134
+ reason: `no upstream counterpart in variant "${newVariant}"`
7135
+ });
7136
+ continue;
7137
+ }
7138
+ const upstreamContent = await fs22.readFile(upstreamAbs, "utf-8");
7139
+ if (resource.strategy === "regenerable") {
7140
+ const newHash = computeHash(upstreamContent);
7141
+ if (newHash === resource.hash) {
7142
+ changes.push({
7143
+ target: resource.target,
7144
+ kind: "unchanged",
7145
+ strategy: "regenerable",
7146
+ reason: "identical bytes between old and new variant"
7147
+ });
7148
+ } else {
7149
+ changes.push({
7150
+ target: resource.target,
7151
+ kind: "rewrite",
7152
+ strategy: "regenerable",
7153
+ reason: `regenerable swap (${currentVariant} \u2192 ${newVariant})`
7154
+ });
7155
+ }
7156
+ continue;
7157
+ }
7158
+ changes.push({
7159
+ target: resource.target,
7160
+ kind: "managed-update",
7161
+ strategy: "managed",
7162
+ reason: "managed regions replayed; outside content preserved"
7163
+ });
7164
+ }
7165
+ if (!apply) {
7166
+ return {
7167
+ status: "dry-run",
7168
+ from: currentVariant,
7169
+ to: newVariant,
7170
+ toVersion: variantEntry.version,
7171
+ changes
7172
+ };
7173
+ }
7174
+ const now = (/* @__PURE__ */ new Date()).toISOString();
7175
+ let snapshot = null;
7176
+ let snapshotError;
7177
+ try {
7178
+ logger.debug(
7179
+ `[variant-switch] capturing snapshot (reason=switch, from=${currentVariant}, to=${newVariant})`
7180
+ );
7181
+ snapshot = await createSnapshot(projectRoot, { reason: "switch" });
7182
+ logger.debug(
7183
+ `[variant-switch] snapshot captured: ${snapshot?.ts ?? "(none)"}`
7184
+ );
7185
+ } catch (err) {
7186
+ snapshotError = getErrorMessage(err);
7187
+ logger.debug(`[variant-switch] snapshot capture failed: ${snapshotError}`);
7188
+ }
7189
+ const refreshedResources = [];
7190
+ for (const resource of priorResources) {
7191
+ const consumerAbs = path33.isAbsolute(resource.target) ? resource.target : path33.join(projectRoot, resource.target);
7192
+ const consumerBasename = path33.basename(resource.target);
7193
+ const upstreamBasename = lookupUpstreamBasename2(consumerBasename);
7194
+ const upstreamAbs = upstreamBasename ? upstreamByBasename.get(upstreamBasename) : void 0;
7195
+ if (resource.strategy === "regenerable" && upstreamAbs) {
7196
+ const upstreamContent = await fs22.readFile(upstreamAbs, "utf-8");
7197
+ await writeFileSafe(consumerAbs, upstreamContent);
7198
+ logger.debug(`[variant-switch] rewrite regenerable: ${resource.target}`);
7199
+ refreshedResources.push({
7200
+ ...resource,
7201
+ hash: computeHash(upstreamContent)
7202
+ });
7203
+ continue;
7204
+ }
7205
+ if (resource.strategy === "managed" && upstreamAbs && await fileExists(consumerAbs)) {
7206
+ const upstreamContent = await fs22.readFile(upstreamAbs, "utf-8");
7207
+ const consumerContent = await fs22.readFile(consumerAbs, "utf-8");
7208
+ const merged = mergeManagedRegions(upstreamContent, consumerContent);
7209
+ if (merged !== consumerContent) {
7210
+ await writeFileSafe(consumerAbs, merged);
7211
+ logger.debug(`[variant-switch] managed-update: ${resource.target}`);
7212
+ } else {
7213
+ logger.debug(
7214
+ `[variant-switch] managed-update no-op (bytes unchanged): ${resource.target}`
7215
+ );
7216
+ }
7217
+ refreshedResources.push({
7218
+ ...resource,
7219
+ hash: computeHash(merged)
7220
+ });
7221
+ continue;
7222
+ }
7223
+ refreshedResources.push(resource);
7224
+ }
7225
+ const lock = {
7226
+ schemaVersion: 1,
7227
+ variant: {
7228
+ name: variantEntry.name,
7229
+ displayName: variantEntry.displayName,
7230
+ version: variantEntry.version,
7231
+ from: packageName
7232
+ },
7233
+ packageVersion: catalog.version,
7234
+ linked: variantEntry.linked,
7235
+ installedAt: now
7236
+ };
7237
+ await writeFileSafe(
7238
+ path33.join(projectRoot, ".teamix-evo", "tokens-lock.json"),
7239
+ JSON.stringify(lock, null, 2) + "\n"
7240
+ );
7241
+ logger.debug(
7242
+ `[variant-switch] tokens-lock.json updated (variant=${newVariant}, version=${variantEntry.version})`
7243
+ );
7244
+ const renames = selectApplicableRenames(
7245
+ variantEntry.renames ?? [],
7246
+ "0.0.0",
7247
+ variantEntry.version
7248
+ );
7249
+ const renamesMap = {};
7250
+ for (const r of renames) renamesMap[r.from] = r.to;
7251
+ const switchTs = now;
7252
+ config.priorVariant = currentVariant;
7253
+ config.packages.tokens.variant = newVariant;
7254
+ config.packages.tokens.version = variantEntry.version;
7255
+ const history = config.tokenRenameHistory ?? [];
7256
+ history.push({
7257
+ ts: switchTs,
7258
+ fromVariant: currentVariant,
7259
+ toVariant: newVariant,
7260
+ renames: renamesMap
7261
+ });
7262
+ config.tokenRenameHistory = history;
7263
+ await writeProjectConfig(projectRoot, config);
7264
+ logger.debug(
7265
+ `[variant-switch] config.json updated (priorVariant=${currentVariant}, variant=${newVariant}, renames=${renames.length})`
7266
+ );
7267
+ if (installedIdx >= 0) {
7268
+ prior.installed[installedIdx] = {
7269
+ ...prior.installed[installedIdx],
7270
+ variant: newVariant,
7271
+ version: variantEntry.version,
7272
+ installedAt: now,
7273
+ resources: refreshedResources
7274
+ };
7275
+ await writeInstalledManifest(projectRoot, prior);
7276
+ logger.debug(
7277
+ `[variant-switch] installed manifest updated (resources=${refreshedResources.length})`
7278
+ );
7279
+ }
7280
+ let hintPath;
7281
+ if (renames.length > 0) {
7282
+ const hint = await writeTokensUpgradeHint({
7283
+ projectRoot,
7284
+ trigger: "switch",
7285
+ fromVariant: currentVariant,
7286
+ toVariant: newVariant,
7287
+ fromVersion: priorVersion,
7288
+ toVersion: variantEntry.version,
7289
+ renames,
7290
+ isoTs: switchTs
7291
+ });
7292
+ if (hint) hintPath = hint.path;
7293
+ }
7294
+ if (hintPath) {
7295
+ logger.debug(`[variant-switch] upgrade hint written: ${hintPath}`);
7296
+ }
7297
+ logger.debug(
7298
+ `[variant-switch] apply complete (from=${currentVariant}, to=${newVariant}, toVersion=${variantEntry.version})`
7299
+ );
7300
+ return {
7301
+ status: "switched",
7302
+ from: currentVariant,
7303
+ to: newVariant,
7304
+ toVersion: variantEntry.version,
7305
+ changes,
7306
+ snapshot,
7307
+ ...snapshotError ? { snapshotError } : {},
7308
+ renames,
7309
+ ...hintPath ? { hintPath } : {}
7310
+ };
7311
+ }
7312
+ function lookupUpstreamBasename2(consumerBasename) {
7313
+ for (const [upstream, consumer] of Object.entries(
7314
+ CONSUMER_BASENAME_BY_UPSTREAM2
7315
+ )) {
7316
+ if (consumer === consumerBasename) return upstream;
7317
+ }
7318
+ return consumerBasename;
7319
+ }
7320
+
7321
+ // src/commands/switch/index.ts
7322
+ var KIND_ICON = {
7323
+ rewrite: "\u270E",
7324
+ "managed-update": "\u2295",
7325
+ preserve: "\u2298",
7326
+ unchanged: "="
7327
+ };
7328
+ var KIND_LABEL = {
7329
+ rewrite: "rewrite",
7330
+ "managed-update": "managed-update",
7331
+ preserve: "preserve",
7332
+ unchanged: "unchanged"
7333
+ };
7334
+ function createSwitchCommand() {
7335
+ return new Command36("switch").description(
7336
+ "variant \u5207\u6362\uFF1A\u5148\u5C55\u793A file-level diff\uFF0C--apply \u624D\u771F\u5199\uFF08ADR 0019 \xA7D3\uFF09"
7337
+ ).argument("<new-variant>", "\u76EE\u6807 variant id\uFF08\u5982 opentrek / uni-manager\uFF09").option("--cwd <dir>", "\u6307\u5B9A\u5DE5\u7A0B\u6839\u76EE\u5F55\uFF08\u9ED8\u8BA4\uFF1A\u5F53\u524D\u76EE\u5F55\uFF09").option("--apply", "\u771F\u6B63\u6267\u884C\u5207\u6362\uFF08\u9ED8\u8BA4 dry-run\uFF0C\u4EC5\u5C55\u793A\u8BA1\u5212\uFF09").option("-y, --yes", "\u8DF3\u8FC7 --apply \u4E8C\u6B21\u786E\u8BA4\uFF08destructive\uFF0C\u8BF7\u786E\u8BA4\u65E0\u8BEF\uFF09").action(async (newVariant, opts) => {
7338
+ const cwd = path34.resolve(opts.cwd ?? process.cwd());
7339
+ const apply = opts.apply ?? false;
7340
+ try {
7341
+ const plan = await runVariantSwitch({
7342
+ projectRoot: cwd,
7343
+ newVariant,
7344
+ apply: false
7345
+ });
7346
+ if (plan.status === "not-initialized") {
7347
+ logger.warn(
7348
+ "\u5F53\u524D\u76EE\u5F55\u672A\u68C0\u6D4B\u5230 .teamix-evo/ \u2014 \u8BE5\u5DE5\u7A0B\u5C1A\u672A\u63A5\u5165 teamix-evo\u3002"
7349
+ );
7350
+ logger.info("\u8BF7\u5148\u8FD0\u884C `npx teamix-evo@latest init` \u63A5\u5165\u3002");
7351
+ process.exitCode = 1;
7352
+ return;
7353
+ }
7354
+ if (plan.status === "already-on-variant") {
7355
+ logger.success(`\u5F53\u524D variant \u5DF2\u662F "${plan.variant}"\uFF0C\u65E0\u9700\u5207\u6362\u3002`);
7356
+ return;
7357
+ }
7358
+ if (plan.status === "variant-not-found") {
7359
+ logger.error(`\u672A\u77E5 variant\uFF1A"${plan.requested}"`);
7360
+ logger.info(`\u53EF\u7528 variant\uFF1A${plan.available.join(", ") || "(none)"}`);
7361
+ logger.info(
7362
+ "\u8FD0\u884C `teamix-evo tokens list-variants` \u53EF\u67E5\u770B\u5B8C\u6574\u5217\u8868\u3002"
7363
+ );
7364
+ process.exitCode = 1;
7365
+ return;
7366
+ }
7367
+ printChangePlan(plan.from, plan.to, plan.toVersion, plan.changes);
7368
+ if (!apply) {
7369
+ logger.info("");
7370
+ logger.info(
7371
+ "\u4EE5\u4E0A\u4E3A dry-run \u8BA1\u5212\u3002\u5982\u9700\u771F\u6B63\u5207\u6362\uFF0C\u8BF7\u52A0 --apply\uFF08\u5EFA\u8BAE\u5148 commit \u5F53\u524D\u6539\u52A8\uFF09\u3002"
7372
+ );
7373
+ return;
7374
+ }
7375
+ const isInteractive = Boolean(process.stdin.isTTY);
7376
+ if (!opts.yes && isInteractive) {
7377
+ logger.info("");
7378
+ logger.info(
7379
+ "switch --apply \u4F1A\u91CD\u5199 regenerable \u6587\u4EF6\u5E76\u66F4\u65B0 .teamix-evo/ \u72B6\u6001\u3002"
7380
+ );
7381
+ logger.info(
7382
+ '\u6267\u884C\u524D\u4F1A\u81EA\u52A8\u6355\u83B7 reason="switch" snapshot\uFF0C\u53EF\u901A\u8FC7 `teamix-evo restore <ts>` \u56DE\u6EDA\u3002'
7383
+ );
7384
+ const confirmed = await prompts8.confirm({
7385
+ message: `\u7EE7\u7EED\u5207\u6362\u81F3 "${newVariant}"\uFF1F`
7386
+ });
7387
+ if (prompts8.isCancel(confirmed) || !confirmed) {
7388
+ logger.info("\u5DF2\u53D6\u6D88\u3002");
7389
+ return;
7390
+ }
7391
+ }
7392
+ const applied = await runVariantSwitch({
7393
+ projectRoot: cwd,
7394
+ newVariant,
7395
+ apply: true
7396
+ });
7397
+ if (applied.status !== "switched") {
7398
+ logger.error(`switch \u5F02\u5E38\uFF1A\u72B6\u6001 ${applied.status}`);
7399
+ process.exitCode = 1;
7400
+ return;
7401
+ }
7402
+ logger.success(
7403
+ `\u5DF2\u5207\u6362\u81F3 variant "${applied.to}" (v${applied.toVersion})\u3002`
7404
+ );
7405
+ if (applied.snapshot) {
7406
+ logger.info(
7407
+ `\u{1F4BE} snapshot\uFF1A${applied.snapshot.ts}\uFF08\u56DE\u6EDA\uFF1A\`teamix-evo restore ${applied.snapshot.ts}\`\uFF09`
7408
+ );
7409
+ } else if (applied.snapshotError) {
7410
+ logger.debug(`snapshot \u6355\u83B7\u5931\u8D25\uFF1A${applied.snapshotError}`);
7411
+ }
7412
+ if (applied.hintPath) {
7413
+ logger.info("");
7414
+ logger.info(`\u{1F4A1} token rename hint: ${applied.hintPath}`);
7415
+ logger.info(
7416
+ ` \u68C0\u6D4B\u5230 ${applied.renames.length} \u4E2A token rename\uFF0C\u5EFA\u8BAE\u8C03\u7528 \`teamix-evo-upgrade\` skill \u626B src/** \u7ED9 codemod \u5EFA\u8BAE\uFF08ADR 0019 \xA7D4\uFF09\u3002`
7417
+ );
7418
+ } else {
7419
+ logger.info(
7420
+ "\u63D0\u793A\uFF1A\u672C\u6B21\u5207\u6362\u672A\u68C0\u6D4B\u5230 token rename\uFF08\u65B0 variant \u672A\u58F0\u660E renames \u6216 sinceVersion \u5747\u5728\u8303\u56F4\u5916\uFF09\u3002"
7421
+ );
7422
+ }
7423
+ } catch (err) {
7424
+ const message = getErrorMessage(err);
7425
+ logger.error(`switch \u5931\u8D25\uFF1A${message}`);
7426
+ logger.debug(err.stack ?? "");
7427
+ process.exitCode = 1;
7428
+ }
7429
+ });
7430
+ }
7431
+ var switchCommand = createSwitchCommand();
7432
+ function printChangePlan(from, to, toVersion, changes) {
7433
+ logger.info("");
7434
+ logger.info(`variant \u5207\u6362\u8BA1\u5212\uFF1A${from} \u2192 ${to} (v${toVersion})`);
7435
+ logger.info("");
7436
+ if (changes.length === 0) {
7437
+ logger.warn(
7438
+ "\u6CA1\u6709\u53EF\u5904\u7406\u7684\u8D44\u6E90\uFF08installed manifest \u4E3A\u7A7A\uFF09\u3002\u8BF7\u5148\u8FD0\u884C `teamix-evo init`\u3002"
7439
+ );
7440
+ return;
7441
+ }
7442
+ for (const change of changes) {
7443
+ const icon = KIND_ICON[change.kind];
7444
+ const label = KIND_LABEL[change.kind];
7445
+ logger.info(
7446
+ ` ${icon} [${label}] ${change.target} (${change.strategy}) \u2014 ${change.reason}`
7447
+ );
7448
+ }
7449
+ }
7450
+
4329
7451
  // src/index.ts
4330
- var require9 = createRequire8(import.meta.url);
4331
- var { version } = require9("../package.json");
4332
- var program = new Command32();
7452
+ var require8 = createRequire8(import.meta.url);
7453
+ var { version } = require8("../package.json");
7454
+ var program = new Command37();
4333
7455
  program.name("teamix-evo").description("Where ideas evolve. \u2014 AI Coding \u5957\u4EF6").version(version);
4334
7456
  program.addCommand(tokensCommand);
4335
7457
  program.addCommand(skillsCommand);
@@ -4338,6 +7460,10 @@ program.addCommand(bizUiCommand);
4338
7460
  program.addCommand(templatesCommand);
4339
7461
  program.addCommand(logsCommand);
4340
7462
  program.addCommand(lintCommand);
7463
+ program.addCommand(initCommand5);
7464
+ program.addCommand(updateCommand3);
7465
+ program.addCommand(restoreCommand);
7466
+ program.addCommand(switchCommand);
4341
7467
  function enableHelpAfterError(cmd) {
4342
7468
  cmd.showHelpAfterError(true);
4343
7469
  for (const child of cmd.commands) enableHelpAfterError(child);