teamix-evo 0.14.0 → 0.14.2

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.
@@ -314,8 +314,14 @@ interface RunUiAddResult {
314
314
  packageName: string;
315
315
  /** Registered ids in install order (deps + requested). */
316
316
  orderedIds: string[];
317
- /** Number of files written. */
317
+ /** Number of files written (= created + overwritten). */
318
318
  written: number;
319
+ /** Number of files newly created. */
320
+ created: number;
321
+ /** Number of existing files overwritten. */
322
+ overwritten: number;
323
+ /** Number of overwritten files that had local modifications. */
324
+ userModified: number;
319
325
  /** Number of files skipped due to frozen + exists. */
320
326
  skipped: number;
321
327
  /** Aggregate npm dependencies of the installed entries. */
@@ -390,6 +396,9 @@ interface RunVariantUiAddResult {
390
396
  variant: string;
391
397
  orderedIds: string[];
392
398
  written: number;
399
+ created: number;
400
+ overwritten: number;
401
+ userModified: number;
393
402
  skipped: number;
394
403
  npmDependencies: Record<string, string>;
395
404
  resources: InstalledResource[];
@@ -500,9 +509,14 @@ declare function runLintInit(options: RunLintInitOptions): Promise<RunLintInitRe
500
509
  * skill activation conditions even when the user prompt does not directly
501
510
  * hit the description-based trigger.
502
511
  *
512
+ * Includes both project-scope content skills AND global-scope lifecycle skills
513
+ * (e.g. `teamix-evo-manage`): manage triggers are rendered into the project
514
+ * AGENTS.md so team members who clone the repo see the lifecycle entry points
515
+ * even before installing the global skill themselves. A "全局技能" note guides
516
+ * the AI to check availability and prompt installation when needed (ADR 0033).
517
+ *
503
518
  * Out of scope (per ADR 0038):
504
519
  * - Does NOT copy skill body / rules / patterns — those stay in the skill.
505
- * - Does NOT include `teamix-evo-manage` (entry skill, global scope, ADR 0033).
506
520
  *
507
521
  * Lifted from `create-teamix-evo` (v0.5) into `teamix-evo/core` so both
508
522
  * `npm create teamix-evo` (scaffold path) and `teamix-evo init` (existing-
@@ -515,11 +529,14 @@ interface RunGenerateAgentsMdOptions {
515
529
  projectRoot: string;
516
530
  /** Tokens / skills variant (e.g. "opentrek"). Used for header context. */
517
531
  variant: string;
532
+ /** Project-scope skill ids to index. */
533
+ skillIds: string[];
518
534
  /**
519
- * Skill ids to index. Caller is responsible for filtering out global-only
520
- * skills (e.g. `teamix-evo-manage`).
535
+ * Global-scope skill ids to include alongside project skills. Rendered with
536
+ * a "全局技能" installation note. TRIGGER / SKIP text is read from the
537
+ * global IDE mirror path at generation time.
521
538
  */
522
- skillIds: string[];
539
+ globalSkillIds?: string[];
523
540
  /**
524
541
  * Reconciliation mode (Phase 2.B):
525
542
  * - `'overwrite'` (default): always rewrite the full file. Pre-existing
@@ -883,6 +900,8 @@ interface UiInstallOptions {
883
900
  skipExisting?: boolean;
884
901
  /** When false, preserve directory structure in import paths (skip flattenRestPath). Defaults to true. */
885
902
  flatten?: boolean;
903
+ /** Prior manifest resources for the same package — used to detect user-modified files before overwrite. */
904
+ priorResources?: InstalledResource[];
886
905
  }
887
906
  interface UiInstallResult {
888
907
  /** Ordered list of entry ids that were processed (deps + requested) */
@@ -891,8 +910,14 @@ interface UiInstallResult {
891
910
  resources: InstalledResource[];
892
911
  /** Aggregate npm dependencies across the installed entries */
893
912
  npmDependencies: Record<string, string>;
894
- /** Number of files written */
913
+ /** Number of files written (= created + overwritten, kept for backwards compat) */
895
914
  written: number;
915
+ /** Number of files newly created (target did not exist) */
916
+ created: number;
917
+ /** Number of existing files overwritten */
918
+ overwritten: number;
919
+ /** Number of overwritten files that had local modifications vs manifest hash */
920
+ userModified: number;
896
921
  /** Number of files skipped because they already exist (frozen) */
897
922
  skipped: number;
898
923
  }
@@ -165,12 +165,40 @@ async function readInstalledManifest(projectRoot) {
165
165
  );
166
166
  }
167
167
  const result = validateInstalled(data);
168
- if (!result.success) {
169
- throw new Error(
170
- `Invalid manifest.json schema: ${result.error}. Fix the file manually or remove it to start fresh.`
168
+ if (result.success) {
169
+ return result.data;
170
+ }
171
+ const migrated = migrateManifest(data);
172
+ const retryResult = validateInstalled(migrated);
173
+ if (retryResult.success) {
174
+ logger.warn(
175
+ "Migrated legacy manifest.json \u2014 will be overwritten on next install."
171
176
  );
177
+ return retryResult.data;
172
178
  }
173
- return result.data;
179
+ throw new Error(
180
+ `Invalid manifest.json schema: ${result.error}. Fix the file manually or remove it to start fresh.`
181
+ );
182
+ }
183
+ function migrateManifest(data) {
184
+ if (typeof data !== "object" || data === null) return data;
185
+ const obj = data;
186
+ if (!("schemaVersion" in obj)) obj.schemaVersion = 1;
187
+ if (!Array.isArray(obj.installed)) obj.installed = [];
188
+ for (const pkg of obj.installed) {
189
+ if (typeof pkg !== "object" || pkg === null) continue;
190
+ if (!pkg.variant) pkg.variant = "_flat";
191
+ if (!pkg.installedAt) pkg.installedAt = "";
192
+ if (!pkg.version) pkg.version = "0.0.0";
193
+ if (Array.isArray(pkg.resources)) {
194
+ for (const res of pkg.resources) {
195
+ if (typeof res !== "object" || res === null) continue;
196
+ if (!res.hash) res.hash = "sha256:legacy-unknown";
197
+ if (!res.strategy) res.strategy = "frozen";
198
+ }
199
+ }
200
+ }
201
+ return obj;
174
202
  }
175
203
  async function writeInstalledManifest(projectRoot, manifest) {
176
204
  const manifestPath = path2.join(projectRoot, TEAMIX_DIR, MANIFEST_FILE);
@@ -948,9 +976,15 @@ function partitionByVersion(ids, manifest, existing) {
948
976
  onlyIds.push(name);
949
977
  continue;
950
978
  }
951
- const installedVer = existing.lock?.skills?.[name]?.version;
979
+ const installedVer = existing.lock?.skills?.[name]?.version ?? existing.pkg?.version;
952
980
  const latestVer = manifestById.get(name)?.version;
953
- if (installedVer && latestVer && compareSemver(installedVer, latestVer) < 0) {
981
+ if (!installedVer || !latestVer) {
982
+ if (latestVer) {
983
+ outdatedSkills.push({ id: name, installed: "0.0.0", latest: latestVer });
984
+ } else {
985
+ skippedSkillIds.push(name);
986
+ }
987
+ } else if (compareSemver(installedVer, latestVer) < 0) {
954
988
  outdatedSkills.push({
955
989
  id: name,
956
990
  installed: installedVer,
@@ -1429,18 +1463,24 @@ import { hasManagedRegion as hasManagedRegion2, replaceManagedRegion as replaceM
1429
1463
  var AGENTS_MD_MANAGED_ID = "teamix-evo-skills";
1430
1464
  async function runGenerateAgentsMd(options) {
1431
1465
  const { projectRoot, variant, skillIds } = options;
1466
+ const globalSkillIds = options.globalSkillIds ?? [];
1432
1467
  const mode = options.mode ?? "overwrite";
1433
1468
  const config = await readProjectConfig(projectRoot);
1434
1469
  const lock = await readSkillsLock(projectRoot);
1435
1470
  const ides = config?.packages?.skills?.ides ?? ["qoder", "claude"];
1436
1471
  const scope = config?.packages?.skills?.scope ?? lock?.skills[skillIds[0] ?? ""]?.scope ?? "project";
1437
- const ordered = [...skillIds].sort(
1472
+ const skillScopeMap = /* @__PURE__ */ new Map();
1473
+ for (const id of skillIds) skillScopeMap.set(id, scope);
1474
+ for (const id of globalSkillIds) skillScopeMap.set(id, "global");
1475
+ const allSkillIds = [...skillIds, ...globalSkillIds];
1476
+ const ordered = allSkillIds.sort(
1438
1477
  (a, b) => bucketRank(a) - bucketRank(b) || a.localeCompare(b)
1439
1478
  );
1440
1479
  const sections = [];
1441
1480
  const missingSkillIds = [];
1442
1481
  for (const id of ordered) {
1443
- const { section, missing } = await renderSkillSection(projectRoot, id, ides, scope);
1482
+ const skillScope = skillScopeMap.get(id) ?? scope;
1483
+ const { section, missing } = await renderSkillSection(projectRoot, id, ides, skillScope);
1444
1484
  sections.push(section);
1445
1485
  if (missing) missingSkillIds.push(id);
1446
1486
  }
@@ -1524,6 +1564,12 @@ async function renderSkillSection(projectRoot, skillId, ides, scope) {
1524
1564
  if (parts?.coordinates) {
1525
1565
  lines.push(`- **Coordinates with**: ${parts.coordinates}`);
1526
1566
  }
1567
+ if (scope === "global") {
1568
+ lines.push("");
1569
+ lines.push(
1570
+ "> **\u5168\u5C40\u6280\u80FD** \u2014 \u5B89\u88C5\u5728\u5168\u5C40\u800C\u975E\u9879\u76EE\u5185\uFF0C\u4E0D\u968F\u4EE3\u7801\u4ED3\u5E93\u5206\u53D1\u3002AI \u89E6\u53D1\u65F6\u5E94\u5148\u786E\u8BA4\u672C\u6280\u80FD\u53EF\u7528\uFF1B\u82E5\u4E0D\u53EF\u7528\uFF0C\u5F15\u5BFC\u7528\u6237\u6267\u884C\u5168\u5C40\u5B89\u88C5\u3002"
1571
+ );
1572
+ }
1527
1573
  return { section: lines.join("\n"), missing };
1528
1574
  }
1529
1575
  function renderAgentsMd(args) {
@@ -1669,13 +1715,34 @@ async function runSkillsUpdate(options) {
1669
1715
  }
1670
1716
  const ides = skillsCfg.ides ?? ["qoder", "claude"];
1671
1717
  const scope = skillsCfg.scope ?? "project";
1672
- const existingLock = await readSkillsLock(projectRoot);
1718
+ let existingLock = await readSkillsLock(projectRoot);
1673
1719
  if (!existingLock || Object.keys(existingLock.skills).length === 0) {
1674
- return { status: "no-skills" };
1720
+ const installedManifest2 = await readInstalledManifest(projectRoot);
1721
+ const pkg = installedManifest2?.installed.find(
1722
+ (p) => p.package === packageName
1723
+ );
1724
+ if (!pkg || pkg.resources.length === 0) {
1725
+ return { status: "no-skills" };
1726
+ }
1727
+ const derivedSkills = {};
1728
+ for (const r of pkg.resources) {
1729
+ const skillId = r.id.split(":")[0] ?? r.id;
1730
+ if (!derivedSkills[skillId]) {
1731
+ derivedSkills[skillId] = {
1732
+ version: pkg.version,
1733
+ from: packageName,
1734
+ installedAt: "",
1735
+ scope,
1736
+ mirroredTo: []
1737
+ };
1738
+ }
1739
+ }
1740
+ existingLock = { schemaVersion: 1, skills: derivedSkills };
1675
1741
  }
1742
+ const activeLock = existingLock;
1676
1743
  const { manifest, data, packageRoot } = await loadSkillsData(packageName);
1677
1744
  const manifestById = new Map(manifest.skills.map((s) => [s.id, s]));
1678
- const lockIds = Object.keys(existingLock.skills);
1745
+ const lockIds = Object.keys(activeLock.skills);
1679
1746
  const requestedSet = requestedNames ? new Set(requestedNames) : null;
1680
1747
  if (requestedSet) {
1681
1748
  const unknown = requestedNames.filter(
@@ -1712,7 +1779,7 @@ async function runSkillsUpdate(options) {
1712
1779
  targetIds.push(id);
1713
1780
  }
1714
1781
  const allSame = targetIds.every((id) => {
1715
- const lockVer = existingLock.skills[id].version;
1782
+ const lockVer = activeLock.skills[id].version;
1716
1783
  const manVer = manifestById.get(id).version;
1717
1784
  return lockVer === manVer;
1718
1785
  });
@@ -1726,7 +1793,7 @@ async function runSkillsUpdate(options) {
1726
1793
  }
1727
1794
  if (dryRun) {
1728
1795
  const plan = targetIds.map((id) => {
1729
- const lockVer = existingLock.skills[id].version;
1796
+ const lockVer = activeLock.skills[id].version;
1730
1797
  const entry2 = manifestById.get(id);
1731
1798
  const sameVersion = lockVer === entry2.version;
1732
1799
  return {
@@ -1798,7 +1865,7 @@ async function runSkillsUpdate(options) {
1798
1865
  await writeInstalledManifest(projectRoot, installedManifest);
1799
1866
  const lock = {
1800
1867
  schemaVersion: 1,
1801
- skills: { ...existingLock.skills }
1868
+ skills: { ...activeLock.skills }
1802
1869
  };
1803
1870
  for (const id of targetIds) {
1804
1871
  const skillDef = manifestById.get(id);
@@ -1818,10 +1885,12 @@ async function runSkillsUpdate(options) {
1818
1885
  const variant = await readTokensVariant(projectRoot);
1819
1886
  if (variant) {
1820
1887
  const projectSkillIds = Object.entries(lock.skills).filter(([, v]) => v.scope === "project").map(([id]) => id);
1888
+ const globalSkillIds = Object.entries(lock.skills).filter(([, v]) => v.scope === "global").map(([id]) => id);
1821
1889
  await runGenerateAgentsMd({
1822
1890
  projectRoot,
1823
1891
  variant,
1824
1892
  skillIds: projectSkillIds,
1893
+ globalSkillIds,
1825
1894
  mode: "merge-managed"
1826
1895
  });
1827
1896
  }
@@ -1911,6 +1980,10 @@ async function loadUiData(packageName) {
1911
1980
  const packageRoot = resolvePackageRoot2(packageName);
1912
1981
  logger.debug(`Resolved ui package root: ${packageRoot}`);
1913
1982
  const manifest = await loadUiPackageManifest(packageRoot);
1983
+ const pkgJson = JSON.parse(
1984
+ await fs9.readFile(path10.join(packageRoot, "package.json"), "utf-8")
1985
+ );
1986
+ manifest.version = pkgJson.version;
1914
1987
  let data = {};
1915
1988
  const dataPath = path10.join(packageRoot, "_data.json");
1916
1989
  try {
@@ -1984,8 +2057,14 @@ async function installUiEntries(options) {
1984
2057
  const idToEntry = new Map(manifest.entries.map((e) => [e.id, e]));
1985
2058
  const resources = [];
1986
2059
  const npmDeps = {};
1987
- let written = 0;
2060
+ let created = 0;
2061
+ let overwritten = 0;
2062
+ let userModified = 0;
1988
2063
  let skipped = 0;
2064
+ const { priorResources } = options;
2065
+ const priorByTarget = new Map(
2066
+ (priorResources ?? []).map((r) => [r.target, r])
2067
+ );
1989
2068
  for (const id of orderedIds) {
1990
2069
  const entry = idToEntry.get(id);
1991
2070
  if (!entry) continue;
@@ -2019,11 +2098,25 @@ async function installUiEntries(options) {
2019
2098
  });
2020
2099
  continue;
2021
2100
  }
2101
+ const relPath = path11.relative(projectRoot, targetAbs);
2102
+ const prior = priorByTarget.get(relPath);
2103
+ if (prior && prior.hash !== "sha256:legacy-unknown") {
2104
+ const onDiskHash = computeHash(current);
2105
+ if (onDiskHash !== prior.hash) {
2106
+ logger.warn(
2107
+ ` \u26A0 ${relPath} has local modifications (backed up)`
2108
+ );
2109
+ userModified++;
2110
+ }
2111
+ }
2022
2112
  await backupFile(targetAbs, projectRoot);
2113
+ overwritten++;
2114
+ logger.info(` overwrite: ${rel(projectRoot, targetAbs)}`);
2115
+ } else {
2116
+ created++;
2117
+ logger.info(` create: ${rel(projectRoot, targetAbs)}`);
2023
2118
  }
2024
2119
  await writeFileSafe(targetAbs, transformed);
2025
- written++;
2026
- logger.info(` write: ${rel(projectRoot, targetAbs)}`);
2027
2120
  resources.push({
2028
2121
  id: `${entry.id}:${file.targetName}`,
2029
2122
  target: path11.relative(projectRoot, targetAbs),
@@ -2036,7 +2129,10 @@ async function installUiEntries(options) {
2036
2129
  orderedIds,
2037
2130
  resources,
2038
2131
  npmDependencies: npmDeps,
2039
- written,
2132
+ written: created + overwritten,
2133
+ created,
2134
+ overwritten,
2135
+ userModified,
2040
2136
  skipped
2041
2137
  };
2042
2138
  }
@@ -2115,17 +2211,22 @@ async function runUiAdd(options) {
2115
2211
  `Unknown entry id(s): ${unknown.map((s) => `"${s}"`).join(", ")}. Run \`teamix-evo ui list\` to see options.`
2116
2212
  );
2117
2213
  }
2214
+ const existingManifest = await readInstalledManifest(
2215
+ projectRoot
2216
+ ) ?? { schemaVersion: 1, installed: [] };
2217
+ const priorPkg = existingManifest.installed.find(
2218
+ (p) => p.package === packageName
2219
+ );
2118
2220
  const result = await installUiEntries({
2119
2221
  projectRoot,
2120
2222
  manifest: effectiveManifest,
2121
2223
  packageRoot,
2122
2224
  aliases: uiCfg.aliases,
2123
2225
  requested: ids,
2124
- skipExisting: !overwrite
2226
+ skipExisting: !overwrite,
2227
+ priorResources: priorPkg?.resources
2125
2228
  });
2126
- const installed = await readInstalledManifest(
2127
- projectRoot
2128
- ) ?? { schemaVersion: 1, installed: [] };
2229
+ const installed = existingManifest;
2129
2230
  const idx = installed.installed.findIndex((p) => p.package === packageName);
2130
2231
  const prior = idx >= 0 ? installed.installed[idx] : null;
2131
2232
  const mergedResources = mergeResources(
@@ -2150,6 +2251,9 @@ async function runUiAdd(options) {
2150
2251
  packageName,
2151
2252
  orderedIds: result.orderedIds,
2152
2253
  written: result.written,
2254
+ created: result.created,
2255
+ overwritten: result.overwritten,
2256
+ userModified: result.userModified,
2153
2257
  skipped: result.skipped,
2154
2258
  npmDependencies: result.npmDependencies,
2155
2259
  resources: result.resources
@@ -2199,6 +2303,7 @@ async function runUiList(options) {
2199
2303
 
2200
2304
  // src/core/variant-ui-add.ts
2201
2305
  import * as path12 from "path";
2306
+ import * as fs11 from "fs/promises";
2202
2307
  import { createRequire as createRequire4 } from "module";
2203
2308
  import {
2204
2309
  loadUiPackageManifest as loadUiPackageManifest2,
@@ -2233,6 +2338,10 @@ async function runVariantUiAdd(packageName, options) {
2233
2338
  }
2234
2339
  const variantDir = path12.join(packageRoot, "variants", variant);
2235
2340
  const variantManifest = await loadVariantUiPackageManifest(variantDir);
2341
+ const pkgJson = JSON.parse(
2342
+ await fs11.readFile(path12.join(packageRoot, "package.json"), "utf-8")
2343
+ );
2344
+ const packageVersion = pkgJson.version;
2236
2345
  const knownIds = new Set(variantManifest.entries.map((e) => e.id));
2237
2346
  const unknown = ids.filter((id) => !knownIds.has(id));
2238
2347
  if (unknown.length > 0) {
@@ -2256,28 +2365,27 @@ async function runVariantUiAdd(packageName, options) {
2256
2365
  const adaptedManifest = {
2257
2366
  schemaVersion: 1,
2258
2367
  package: "ui",
2259
- version: variantManifest.version,
2368
+ version: packageVersion,
2260
2369
  engines: variantManifest.engines,
2261
2370
  entries: mergedEntries
2262
2371
  };
2372
+ const installed = await readInstalledManifest(
2373
+ projectRoot
2374
+ ) ?? { schemaVersion: 1, installed: [] };
2375
+ const idx = installed.installed.findIndex(
2376
+ (p) => p.package === fullPackageName && p.variant === variant
2377
+ );
2378
+ const prior = idx >= 0 ? installed.installed[idx] : null;
2263
2379
  const result = await installUiEntries({
2264
2380
  projectRoot,
2265
2381
  manifest: adaptedManifest,
2266
2382
  packageRoot: variantDir,
2267
- // default for variant entries
2268
2383
  entryPackageRoot,
2269
- // ui entries resolve from uiPackageRoot
2270
2384
  aliases: uiCfg.aliases,
2271
2385
  requested: ids,
2272
- skipExisting: !overwrite
2386
+ skipExisting: !overwrite,
2387
+ priorResources: prior?.resources
2273
2388
  });
2274
- const installed = await readInstalledManifest(
2275
- projectRoot
2276
- ) ?? { schemaVersion: 1, installed: [] };
2277
- const idx = installed.installed.findIndex(
2278
- (p) => p.package === fullPackageName && p.variant === variant
2279
- );
2280
- const prior = idx >= 0 ? installed.installed[idx] : null;
2281
2389
  const mergedResources = mergeResources2(
2282
2390
  prior?.resources ?? [],
2283
2391
  result.resources
@@ -2285,7 +2393,7 @@ async function runVariantUiAdd(packageName, options) {
2285
2393
  const entry = {
2286
2394
  package: fullPackageName,
2287
2395
  variant,
2288
- version: variantManifest.version,
2396
+ version: packageVersion,
2289
2397
  installedAt: (/* @__PURE__ */ new Date()).toISOString(),
2290
2398
  resources: mergedResources
2291
2399
  };
@@ -2297,6 +2405,9 @@ async function runVariantUiAdd(packageName, options) {
2297
2405
  variant,
2298
2406
  orderedIds: result.orderedIds,
2299
2407
  written: result.written,
2408
+ created: result.created,
2409
+ overwritten: result.overwritten,
2410
+ userModified: result.userModified,
2300
2411
  skipped: result.skipped,
2301
2412
  npmDependencies: result.npmDependencies,
2302
2413
  resources: result.resources
@@ -2358,7 +2469,7 @@ async function listBizUiEntries(variant, packageRoot) {
2358
2469
 
2359
2470
  // src/core/lint-init.ts
2360
2471
  import * as path13 from "path";
2361
- import * as fs11 from "fs";
2472
+ import * as fs12 from "fs";
2362
2473
  import { execa } from "execa";
2363
2474
  var ESLINT_CONFIG_CONTENT = `/**
2364
2475
  * teamix-evo consumer ESLint preset \u2014 9 token-discipline rules.
@@ -2450,7 +2561,7 @@ async function runLintInit(options) {
2450
2561
  let stylelintIgnoreFilesWarning = false;
2451
2562
  if (!stylelintNeedsWrite && stylelintTemplateExists) {
2452
2563
  try {
2453
- const existingContent = fs11.readFileSync(stylelintConfigPath, "utf-8");
2564
+ const existingContent = fs12.readFileSync(stylelintConfigPath, "utf-8");
2454
2565
  const usesTeamixPreset = existingContent.includes("@teamix-evo/stylelint-config/presets/") || existingContent.includes("@teamix-evo/stylelint-config/preset/");
2455
2566
  const hasTokenIgnore = existingContent.includes("tokens.theme.css") && existingContent.includes("tokens.overrides.css");
2456
2567
  if (!usesTeamixPreset && !hasTokenIgnore) {
@@ -2487,8 +2598,8 @@ async function runLintInit(options) {
2487
2598
  };
2488
2599
  }
2489
2600
  function detectPm(projectRoot) {
2490
- if (fs11.existsSync(path13.join(projectRoot, "pnpm-lock.yaml"))) return "pnpm";
2491
- if (fs11.existsSync(path13.join(projectRoot, "yarn.lock"))) return "yarn";
2601
+ if (fs12.existsSync(path13.join(projectRoot, "pnpm-lock.yaml"))) return "pnpm";
2602
+ if (fs12.existsSync(path13.join(projectRoot, "yarn.lock"))) return "yarn";
2492
2603
  return "npm";
2493
2604
  }
2494
2605
  async function patchPackageJsonScripts(projectRoot) {
@@ -2521,7 +2632,7 @@ async function patchPackageJsonScripts(projectRoot) {
2521
2632
  }
2522
2633
 
2523
2634
  // src/core/init-detect.ts
2524
- import * as fs12 from "fs/promises";
2635
+ import * as fs13 from "fs/promises";
2525
2636
  import * as path14 from "path";
2526
2637
  var IGNORED_TOP_LEVEL = /* @__PURE__ */ new Set([
2527
2638
  ".git",
@@ -2556,7 +2667,7 @@ async function detectProjectState(cwd) {
2556
2667
  }
2557
2668
  let entries;
2558
2669
  try {
2559
- entries = await fs12.readdir(absCwd);
2670
+ entries = await fs13.readdir(absCwd);
2560
2671
  } catch (err) {
2561
2672
  if (err.code === "ENOENT") {
2562
2673
  return {
@@ -2590,7 +2701,7 @@ async function detectProjectState(cwd) {
2590
2701
  }
2591
2702
 
2592
2703
  // src/core/project-state.ts
2593
- import * as fs13 from "fs/promises";
2704
+ import * as fs14 from "fs/promises";
2594
2705
  import * as path15 from "path";
2595
2706
  var IGNORED_TOP_LEVEL2 = /* @__PURE__ */ new Set([
2596
2707
  ".git",
@@ -2650,7 +2761,7 @@ async function detectProjectState2(cwd) {
2650
2761
  }
2651
2762
  let entries;
2652
2763
  try {
2653
- entries = await fs13.readdir(absCwd);
2764
+ entries = await fs14.readdir(absCwd);
2654
2765
  } catch (err) {
2655
2766
  if (err.code === "ENOENT") {
2656
2767
  return {
@@ -2733,7 +2844,7 @@ function assertCommandPrecondition(command, state) {
2733
2844
 
2734
2845
  // src/core/init-conflicts.ts
2735
2846
  import * as crypto from "crypto";
2736
- import * as fs14 from "fs/promises";
2847
+ import * as fs15 from "fs/promises";
2737
2848
  import * as path16 from "path";
2738
2849
  var TAILWIND_CONFIG_CANDIDATES = [
2739
2850
  "tailwind.config.ts",
@@ -2778,7 +2889,7 @@ var STYLELINT_CONFIG_CANDIDATES = [
2778
2889
  ];
2779
2890
  async function isDir(target) {
2780
2891
  try {
2781
- const stat3 = await fs14.stat(target);
2892
+ const stat3 = await fs15.stat(target);
2782
2893
  return stat3.isDirectory();
2783
2894
  } catch {
2784
2895
  return false;
@@ -2786,7 +2897,7 @@ async function isDir(target) {
2786
2897
  }
2787
2898
  async function dirHasContent(target) {
2788
2899
  try {
2789
- const entries = await fs14.readdir(target);
2900
+ const entries = await fs15.readdir(target);
2790
2901
  return entries.length > 0;
2791
2902
  } catch {
2792
2903
  return false;
@@ -2927,7 +3038,7 @@ async function detectShadcnSource(cwd) {
2927
3038
  try {
2928
3039
  const uiDir = path16.join(cwd, "src/components/ui");
2929
3040
  if (await isDir(uiDir)) {
2930
- const entries = await fs14.readdir(uiDir);
3041
+ const entries = await fs15.readdir(uiDir);
2931
3042
  componentCount = entries.filter(
2932
3043
  (e) => e.endsWith(".tsx") || e.endsWith(".ts")
2933
3044
  ).length;
@@ -3005,7 +3116,7 @@ async function detectStylelintConfig(cwd) {
3005
3116
 
3006
3117
  // src/core/meta-installer.ts
3007
3118
  import * as path17 from "path";
3008
- import * as fs15 from "fs/promises";
3119
+ import * as fs16 from "fs/promises";
3009
3120
  import { createRequire as createRequire5 } from "module";
3010
3121
  import {
3011
3122
  loadUiPackageManifest as loadUiPackageManifest3,
@@ -3049,7 +3160,7 @@ async function landManifestMeta(opts) {
3049
3160
  await ensureDir(metaDir);
3050
3161
  const manifestDest = path17.join(metaDir, "manifest.json");
3051
3162
  const manifestSrc = path17.join(packageRoot, "manifest.json");
3052
- await fs15.copyFile(manifestSrc, manifestDest);
3163
+ await fs16.copyFile(manifestSrc, manifestDest);
3053
3164
  logger.debug(`meta: copied manifest.json \u2192 ${manifestDest}`);
3054
3165
  let written = 0;
3055
3166
  let total = 0;
@@ -3059,7 +3170,7 @@ async function landManifestMeta(opts) {
3059
3170
  const srcPath = path17.join(packageRoot, entry.meta);
3060
3171
  const destPath = path17.join(metaDir, `${entry.id}.md`);
3061
3172
  try {
3062
- await fs15.copyFile(srcPath, destPath);
3173
+ await fs16.copyFile(srcPath, destPath);
3063
3174
  written++;
3064
3175
  logger.debug(`meta: ${entry.id} \u2192 ${destPath}`);
3065
3176
  } catch (err) {
@@ -3082,7 +3193,7 @@ async function landManifestMeta(opts) {
3082
3193
  }
3083
3194
 
3084
3195
  // src/core/deps-install.ts
3085
- import * as fs16 from "fs/promises";
3196
+ import * as fs17 from "fs/promises";
3086
3197
  import * as path18 from "path";
3087
3198
  import { exec } from "child_process";
3088
3199
  import { promisify } from "util";
@@ -3139,7 +3250,7 @@ async function installProjectDeps(options) {
3139
3250
  pkg.dependencies = Object.fromEntries(
3140
3251
  Object.entries(pkg.dependencies).sort(([a], [b]) => a.localeCompare(b))
3141
3252
  );
3142
- await fs16.writeFile(pkgPath, JSON.stringify(pkg, null, 2) + "\n", "utf-8");
3253
+ await fs17.writeFile(pkgPath, JSON.stringify(pkg, null, 2) + "\n", "utf-8");
3143
3254
  logger.info(
3144
3255
  ` patched package.json: +${Object.keys(added).length} dependencies`
3145
3256
  );
@@ -3201,7 +3312,7 @@ ${stepLines}
3201
3312
  }
3202
3313
 
3203
3314
  // src/core/file-changes.ts
3204
- import * as fs17 from "fs/promises";
3315
+ import * as fs18 from "fs/promises";
3205
3316
  import * as path19 from "path";
3206
3317
  function toRelativePosix(p, projectRoot) {
3207
3318
  let rel2 = p;
@@ -3330,6 +3441,7 @@ async function runProjectInit(options) {
3330
3441
  `teamix-evo-design-${variant}`,
3331
3442
  `teamix-evo-code-${variant}`
3332
3443
  ],
3444
+ globalSkillIds: ["teamix-evo-manage"],
3333
3445
  mode: "merge-managed"
3334
3446
  });
3335
3447
  record({
@@ -3660,7 +3772,7 @@ function deriveLintChanges(result) {
3660
3772
 
3661
3773
  // src/core/installer.ts
3662
3774
  import * as path21 from "path";
3663
- import * as fs18 from "fs/promises";
3775
+ import * as fs19 from "fs/promises";
3664
3776
  async function installResources(options) {
3665
3777
  const { projectRoot, manifest, data, variantDir, packageRoot } = options;
3666
3778
  const installedResources = [];
@@ -3703,7 +3815,7 @@ async function installSingleResource(resource, projectRoot, data, variantDir, pa
3703
3815
  const templateContent = await loadTemplateFile(sourcePath);
3704
3816
  content = renderTemplate(templateContent, data);
3705
3817
  } else {
3706
- content = await fs18.readFile(sourcePath, "utf-8");
3818
+ content = await fs19.readFile(sourcePath, "utf-8");
3707
3819
  }
3708
3820
  await writeFileSafe(targetPath, content);
3709
3821
  const hash = computeHash(content);
@@ -3736,7 +3848,7 @@ async function installRecursiveResource(resource, projectRoot, data, variantDir,
3736
3848
  const templateContent = await loadTemplateFile(entry);
3737
3849
  content = renderTemplate(templateContent, data);
3738
3850
  } else {
3739
- content = await fs18.readFile(entry, "utf-8");
3851
+ content = await fs19.readFile(entry, "utf-8");
3740
3852
  }
3741
3853
  await writeFileSafe(targetFile, content);
3742
3854
  const hash = computeHash(content);
@@ -3754,7 +3866,7 @@ async function installRecursiveResource(resource, projectRoot, data, variantDir,
3754
3866
 
3755
3867
  // src/core/registry-client.ts
3756
3868
  import * as path22 from "path";
3757
- import * as fs19 from "fs/promises";
3869
+ import * as fs20 from "fs/promises";
3758
3870
  import { createRequire as createRequire6 } from "module";
3759
3871
  import { loadVariantManifest } from "@teamix-evo/registry";
3760
3872
  var require7 = createRequire6(import.meta.url);
@@ -3771,7 +3883,7 @@ async function loadVariantData(packageName, variant) {
3771
3883
  let data = {};
3772
3884
  const dataPath = path22.join(variantDir, "_data.json");
3773
3885
  try {
3774
- const raw = await fs19.readFile(dataPath, "utf-8");
3886
+ const raw = await fs20.readFile(dataPath, "utf-8");
3775
3887
  data = JSON.parse(raw);
3776
3888
  } catch (err) {
3777
3889
  if (err.code !== "ENOENT") {