teamix-evo 0.3.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,11 +1,10 @@
1
- // src/core/design-init.ts
2
- import * as path9 from "path";
1
+ // src/core/tokens-init.ts
2
+ import * as path8 from "path";
3
3
  import * as fs6 from "fs/promises";
4
4
  import { createRequire as createRequire2 } from "module";
5
5
  import {
6
- loadDesignPack,
7
- loadDesignPackageManifest,
8
- mergeDefaultAndVariant
6
+ loadTokensPackageManifest,
7
+ getVariantEntry
9
8
  } from "@teamix-evo/registry";
10
9
 
11
10
  // src/utils/fs.ts
@@ -90,71 +89,22 @@ function computeHash(content) {
90
89
  return `sha256:${hash}`;
91
90
  }
92
91
 
93
- // src/core/design-pack-classify.ts
94
- import * as path2 from "path";
95
- var TEAMIX_DIR = ".teamix-evo/design";
96
- var TOKENS_DIR = ".teamix-evo/tokens";
97
- var TOKENS_PACK_PREFIX = "foundations/tokens/";
98
- var ROOT_MANAGED_FILES = {
99
- "DESIGN.md": { target: "DESIGN.md", managedRegions: ["core"] },
100
- "AGENTS.md": { target: "AGENTS.md", managedRegions: ["teamix-evo"] },
101
- "CLAUDE.md": { target: "CLAUDE.md", managedRegions: ["teamix-evo"] }
102
- };
103
- var FROZEN_FILES = /* @__PURE__ */ new Set([
104
- "foundations/tokens/tokens.overrides.css"
105
- ]);
106
- function classifyPackFile(relPath) {
107
- if (path2.basename(relPath) === "README.md") {
108
- return null;
109
- }
110
- const rootManaged = ROOT_MANAGED_FILES[relPath];
111
- if (rootManaged) {
112
- return {
113
- target: rootManaged.target,
114
- strategy: "managed",
115
- managedRegions: rootManaged.managedRegions,
116
- isFrozen: false
117
- };
118
- }
119
- if (relPath.startsWith(TOKENS_PACK_PREFIX)) {
120
- const rel2 = relPath.slice(TOKENS_PACK_PREFIX.length);
121
- const target = path2.posix.join(TOKENS_DIR, rel2);
122
- if (FROZEN_FILES.has(relPath)) {
123
- return { target, strategy: "frozen", isFrozen: true };
124
- }
125
- return { target, strategy: "regenerable", isFrozen: false };
126
- }
127
- if (FROZEN_FILES.has(relPath)) {
128
- return {
129
- target: path2.posix.join(TEAMIX_DIR, relPath),
130
- strategy: "frozen",
131
- isFrozen: true
132
- };
133
- }
134
- return {
135
- target: path2.posix.join(TEAMIX_DIR, relPath),
136
- strategy: "regenerable",
137
- isFrozen: false
138
- };
139
- }
140
-
141
92
  // src/core/state.ts
142
- import * as path3 from "path";
93
+ import * as path2 from "path";
143
94
  import {
144
95
  validateConfig,
145
96
  validateInstalled,
146
97
  validateSkillsLock,
147
- DesignPackLockSchema
98
+ TokensPackLockSchema
148
99
  } from "@teamix-evo/registry";
149
- var TEAMIX_DIR2 = ".teamix-evo";
100
+ var TEAMIX_DIR = ".teamix-evo";
150
101
  var CONFIG_FILE = "config.json";
151
102
  var MANIFEST_FILE = "manifest.json";
152
- var DESIGN_DIR = "design";
153
- var DESIGN_LOCK_FILE = "pack.lock.json";
103
+ var TOKENS_LOCK_FILE = "tokens-lock.json";
154
104
  var SKILLS_DIR = "skills";
155
105
  var SKILLS_LOCK_FILE = "manifest.lock.json";
156
106
  function getTeamixDir(projectRoot) {
157
- return path3.join(projectRoot, TEAMIX_DIR2);
107
+ return path2.join(projectRoot, TEAMIX_DIR);
158
108
  }
159
109
  async function ensureTeamixDir(projectRoot) {
160
110
  const dir = getTeamixDir(projectRoot);
@@ -162,84 +112,85 @@ async function ensureTeamixDir(projectRoot) {
162
112
  return dir;
163
113
  }
164
114
  async function readProjectConfig(projectRoot) {
165
- const configPath = path3.join(projectRoot, TEAMIX_DIR2, CONFIG_FILE);
115
+ const configPath = path2.join(projectRoot, TEAMIX_DIR, CONFIG_FILE);
166
116
  const raw = await readFileOrNull(configPath);
167
117
  if (raw === null) return null;
118
+ let data;
168
119
  try {
169
- const data = JSON.parse(raw);
170
- const result = validateConfig(data);
171
- if (!result.success) {
172
- logger.warn(`Invalid config.json: ${result.error}`);
173
- return null;
174
- }
175
- return result.data;
120
+ data = JSON.parse(raw);
176
121
  } catch (err) {
177
- logger.warn(`Failed to parse config.json: ${err.message}`);
178
- return null;
122
+ throw new Error(
123
+ `Corrupted config.json (${err.message}). Fix the JSON manually or remove the file to start fresh; refusing to clobber prior config.`
124
+ );
125
+ }
126
+ const result = validateConfig(data);
127
+ if (!result.success) {
128
+ throw new Error(
129
+ `Invalid config.json schema: ${result.error}. Fix the file manually or remove it to start fresh.`
130
+ );
179
131
  }
132
+ return result.data;
180
133
  }
181
134
  async function writeProjectConfig(projectRoot, config) {
182
- const configPath = path3.join(projectRoot, TEAMIX_DIR2, CONFIG_FILE);
135
+ const configPath = path2.join(projectRoot, TEAMIX_DIR, CONFIG_FILE);
183
136
  await writeFileSafe(configPath, JSON.stringify(config, null, 2) + "\n");
184
137
  logger.debug(`Wrote config \u2192 ${configPath}`);
185
138
  }
186
139
  async function readInstalledManifest(projectRoot) {
187
- const manifestPath = path3.join(projectRoot, TEAMIX_DIR2, MANIFEST_FILE);
140
+ const manifestPath = path2.join(projectRoot, TEAMIX_DIR, MANIFEST_FILE);
188
141
  const raw = await readFileOrNull(manifestPath);
189
142
  if (raw === null) return null;
143
+ let data;
190
144
  try {
191
- const data = JSON.parse(raw);
192
- const result = validateInstalled(data);
193
- if (!result.success) {
194
- logger.warn(`Invalid manifest.json: ${result.error}`);
195
- return null;
196
- }
197
- return result.data;
145
+ data = JSON.parse(raw);
198
146
  } catch (err) {
199
- logger.warn(`Failed to parse manifest.json: ${err.message}`);
200
- return null;
147
+ throw new Error(
148
+ `Corrupted manifest.json (${err.message}). Fix the JSON manually or remove the file to start fresh; refusing to clobber prior install records.`
149
+ );
201
150
  }
151
+ const result = validateInstalled(data);
152
+ if (!result.success) {
153
+ throw new Error(
154
+ `Invalid manifest.json schema: ${result.error}. Fix the file manually or remove it to start fresh.`
155
+ );
156
+ }
157
+ return result.data;
202
158
  }
203
159
  async function writeInstalledManifest(projectRoot, manifest) {
204
- const manifestPath = path3.join(projectRoot, TEAMIX_DIR2, MANIFEST_FILE);
160
+ const manifestPath = path2.join(projectRoot, TEAMIX_DIR, MANIFEST_FILE);
205
161
  await writeFileSafe(manifestPath, JSON.stringify(manifest, null, 2) + "\n");
206
162
  logger.debug(`Wrote manifest \u2192 ${manifestPath}`);
207
163
  }
208
- async function readDesignPackLock(projectRoot) {
209
- const lockPath = path3.join(
210
- projectRoot,
211
- TEAMIX_DIR2,
212
- DESIGN_DIR,
213
- DESIGN_LOCK_FILE
214
- );
164
+ async function readTokensLock(projectRoot) {
165
+ const lockPath = path2.join(projectRoot, TEAMIX_DIR, TOKENS_LOCK_FILE);
215
166
  const raw = await readFileOrNull(lockPath);
216
167
  if (raw === null) return null;
217
168
  try {
218
- const parsed = DesignPackLockSchema.safeParse(JSON.parse(raw));
169
+ const parsed = TokensPackLockSchema.safeParse(JSON.parse(raw));
219
170
  if (!parsed.success) {
220
- logger.warn(`Invalid design pack.lock.json: ${parsed.error.message}`);
171
+ logger.warn(`Invalid tokens-lock.json: ${parsed.error.message}`);
221
172
  return null;
222
173
  }
223
174
  return parsed.data;
224
175
  } catch (err) {
225
176
  logger.warn(
226
- `Failed to parse design pack.lock.json: ${err.message}`
177
+ `Failed to parse tokens-lock.json: ${err.message}`
227
178
  );
228
179
  return null;
229
180
  }
230
181
  }
231
- async function readDesignVariant(projectRoot) {
232
- const lock = await readDesignPackLock(projectRoot);
182
+ async function readTokensVariant(projectRoot) {
183
+ const lock = await readTokensLock(projectRoot);
233
184
  return lock?.variant.name ?? null;
234
185
  }
235
186
  function getSkillsSourceDir(projectRoot, skillName) {
236
- const base = path3.join(projectRoot, TEAMIX_DIR2, SKILLS_DIR);
237
- return skillName ? path3.join(base, skillName) : base;
187
+ const base = path2.join(projectRoot, TEAMIX_DIR, SKILLS_DIR);
188
+ return skillName ? path2.join(base, skillName) : base;
238
189
  }
239
190
  async function readSkillsLock(projectRoot) {
240
- const lockPath = path3.join(
191
+ const lockPath = path2.join(
241
192
  projectRoot,
242
- TEAMIX_DIR2,
193
+ TEAMIX_DIR,
243
194
  SKILLS_DIR,
244
195
  SKILLS_LOCK_FILE
245
196
  );
@@ -261,9 +212,9 @@ async function readSkillsLock(projectRoot) {
261
212
  }
262
213
  }
263
214
  async function writeSkillsLock(projectRoot, lock) {
264
- const lockPath = path3.join(
215
+ const lockPath = path2.join(
265
216
  projectRoot,
266
- TEAMIX_DIR2,
217
+ TEAMIX_DIR,
267
218
  SKILLS_DIR,
268
219
  SKILLS_LOCK_FILE
269
220
  );
@@ -272,21 +223,21 @@ async function writeSkillsLock(projectRoot, lock) {
272
223
  }
273
224
 
274
225
  // src/core/skills-client.ts
275
- import * as path4 from "path";
226
+ import * as path3 from "path";
276
227
  import * as fs2 from "fs/promises";
277
228
  import { createRequire } from "module";
278
229
  import { loadSkillsPackageManifest } from "@teamix-evo/registry";
279
230
  var require2 = createRequire(import.meta.url);
280
231
  function resolvePackageRoot(packageName) {
281
232
  const pkgJsonPath = require2.resolve(`${packageName}/package.json`);
282
- return path4.dirname(pkgJsonPath);
233
+ return path3.dirname(pkgJsonPath);
283
234
  }
284
235
  async function loadSkillsData(packageName) {
285
236
  const packageRoot = resolvePackageRoot(packageName);
286
237
  logger.debug(`Resolved skills package root: ${packageRoot}`);
287
238
  const manifest = await loadSkillsPackageManifest(packageRoot);
288
239
  let data = {};
289
- const dataPath = path4.join(packageRoot, "_data.json");
240
+ const dataPath = path3.join(packageRoot, "_data.json");
290
241
  try {
291
242
  const raw = await fs2.readFile(dataPath, "utf-8");
292
243
  data = JSON.parse(raw);
@@ -300,13 +251,13 @@ async function loadSkillsData(packageName) {
300
251
  }
301
252
 
302
253
  // src/core/skills-installer.ts
303
- import * as path8 from "path";
254
+ import * as path7 from "path";
304
255
  import * as fs5 from "fs/promises";
305
256
  import { replaceManagedRegion } from "@teamix-evo/registry";
306
257
 
307
258
  // src/ide/QoderAdapter.ts
308
259
  import * as os from "os";
309
- import * as path5 from "path";
260
+ import * as path4 from "path";
310
261
  var QoderAdapter = class {
311
262
  kind = "qoder";
312
263
  name = "qoder";
@@ -317,14 +268,14 @@ var QoderAdapter = class {
317
268
  return true;
318
269
  }
319
270
  getSkillTargetDir(skillName, scope, projectRoot) {
320
- const base = scope === "global" ? path5.join(os.homedir(), ".qoder") : path5.join(projectRoot ?? this.getProjectRoot(), ".qoder");
321
- return path5.join(base, "skills", skillName);
271
+ const base = scope === "global" ? path4.join(os.homedir(), ".qoder") : path4.join(projectRoot ?? this.getProjectRoot(), ".qoder");
272
+ return path4.join(base, "skills", skillName);
322
273
  }
323
274
  };
324
275
 
325
276
  // src/ide/ClaudeAdapter.ts
326
277
  import * as os2 from "os";
327
- import * as path6 from "path";
278
+ import * as path5 from "path";
328
279
  var ClaudeAdapter = class {
329
280
  kind = "claude";
330
281
  name = "claude";
@@ -335,8 +286,8 @@ var ClaudeAdapter = class {
335
286
  return Boolean(process.env.CLAUDECODE);
336
287
  }
337
288
  getSkillTargetDir(skillName, scope, projectRoot) {
338
- const base = scope === "global" ? path6.join(os2.homedir(), ".claude") : path6.join(projectRoot ?? this.getProjectRoot(), ".claude");
339
- return path6.join(base, "skills", skillName);
289
+ const base = scope === "global" ? path5.join(os2.homedir(), ".claude") : path5.join(projectRoot ?? this.getProjectRoot(), ".claude");
290
+ return path5.join(base, "skills", skillName);
340
291
  }
341
292
  };
342
293
 
@@ -383,19 +334,19 @@ async function loadTemplateFile(filePath) {
383
334
  }
384
335
 
385
336
  // src/utils/path.ts
386
- import * as path7 from "path";
337
+ import * as path6 from "path";
387
338
  import * as fs4 from "fs/promises";
388
339
  function resolveSourcePath(source, variantDir, packageRoot) {
389
340
  if (source.startsWith("_template/")) {
390
- return path7.join(packageRoot, source);
341
+ return path6.join(packageRoot, source);
391
342
  }
392
- return path7.join(variantDir, source);
343
+ return path6.join(variantDir, source);
393
344
  }
394
345
  async function walkDir(dir) {
395
346
  const files = [];
396
347
  const entries = await fs4.readdir(dir, { withFileTypes: true });
397
348
  for (const entry of entries) {
398
- const fullPath = path7.join(dir, entry.name);
349
+ const fullPath = path6.join(dir, entry.name);
399
350
  if (entry.isDirectory()) {
400
351
  files.push(...await walkDir(fullPath));
401
352
  } else if (entry.isFile()) {
@@ -438,12 +389,12 @@ async function installSkills(options) {
438
389
  }
439
390
  async function writeSkillSource(skill, options) {
440
391
  const { data, packageRoot, projectRoot } = options;
441
- const sourceAbs = path8.resolve(packageRoot, skill.source);
392
+ const sourceAbs = path7.resolve(packageRoot, skill.source);
442
393
  const targetDir = getSkillsSourceDir(projectRoot, skill.name);
443
394
  const stat2 = await fs5.stat(sourceAbs);
444
395
  const records = [];
445
396
  if (stat2.isFile()) {
446
- const targetFile = path8.join(targetDir, "SKILL.md");
397
+ const targetFile = path7.join(targetDir, "SKILL.md");
447
398
  const content = await renderSkillContent(sourceAbs, skill, data);
448
399
  await writeFileSafe(targetFile, content);
449
400
  records.push(makeSourceRecord(skill, targetFile, content));
@@ -453,14 +404,14 @@ async function writeSkillSource(skill, options) {
453
404
  await ensureDir(targetDir);
454
405
  const entries = await walkDir(sourceAbs);
455
406
  for (const entry of entries) {
456
- const rel2 = path8.relative(sourceAbs, entry);
457
- let targetFile = path8.join(targetDir, rel2);
407
+ const rel2 = path7.relative(sourceAbs, entry);
408
+ let targetFile = path7.join(targetDir, rel2);
458
409
  if (skill.template && targetFile.endsWith(".hbs")) {
459
410
  targetFile = targetFile.slice(0, -4);
460
411
  }
461
412
  const content = skill.template && entry.endsWith(".hbs") ? renderTemplate(await loadTemplateFile(entry), { ...data, skill }) : await fs5.readFile(entry, "utf-8");
462
413
  await writeFileSafe(targetFile, content);
463
- const relWritten = path8.relative(targetDir, targetFile);
414
+ const relWritten = path7.relative(targetDir, targetFile);
464
415
  records.push(makeSourceRecord(skill, targetFile, content, relWritten));
465
416
  logger.debug(` Wrote source: ${targetFile}`);
466
417
  }
@@ -474,11 +425,19 @@ async function mirrorSkillToIde(skill, ide, scope, projectRoot) {
474
425
  const sourceFiles = await walkDir(sourceDir);
475
426
  await ensureDir(targetDir);
476
427
  for (const src of sourceFiles) {
477
- const rel2 = path8.relative(sourceDir, src);
478
- const targetFile = path8.join(targetDir, rel2);
479
- const content = await fs5.readFile(src, "utf-8");
480
- await writeFileSafe(targetFile, content);
481
- records.push(makeMirrorRecord(skill, targetFile, content, ide, scope, rel2));
428
+ const rel2 = path7.relative(sourceDir, src);
429
+ const targetFile = path7.join(targetDir, rel2);
430
+ const sourceContent = await fs5.readFile(src, "utf-8");
431
+ const existing = await readFileOrNull(targetFile);
432
+ if (existing !== null && existing !== sourceContent) {
433
+ logger.warn(
434
+ `Mirror drift detected at ${targetFile} \u2014 overwriting from source. Edit ${src} (not the mirror) and re-run \`teamix-evo skills sync\`.`
435
+ );
436
+ }
437
+ await writeFileSafe(targetFile, sourceContent);
438
+ records.push(
439
+ makeMirrorRecord(skill, targetFile, sourceContent, ide, scope, rel2)
440
+ );
482
441
  logger.debug(` Mirrored ${ide}:${scope}: ${targetFile}`);
483
442
  }
484
443
  return records;
@@ -537,7 +496,7 @@ async function updateSkills(options) {
537
496
  }
538
497
  async function rewriteSkillSource(skill, options, summary) {
539
498
  const { data, packageRoot, projectRoot } = options;
540
- const sourceAbs = path8.resolve(packageRoot, skill.source);
499
+ const sourceAbs = path7.resolve(packageRoot, skill.source);
541
500
  const targetDir = getSkillsSourceDir(projectRoot, skill.name);
542
501
  const stat2 = await fs5.stat(sourceAbs);
543
502
  if (!stat2.isFile()) {
@@ -545,8 +504,8 @@ async function rewriteSkillSource(skill, options, summary) {
545
504
  const entries = await walkDir(sourceAbs);
546
505
  const records = [];
547
506
  for (const entry of entries) {
548
- const rel2 = path8.relative(sourceAbs, entry);
549
- let targetFile2 = path8.join(targetDir, rel2);
507
+ const rel2 = path7.relative(sourceAbs, entry);
508
+ let targetFile2 = path7.join(targetDir, rel2);
550
509
  if (skill.template && targetFile2.endsWith(".hbs")) {
551
510
  targetFile2 = targetFile2.slice(0, -4);
552
511
  }
@@ -559,12 +518,12 @@ async function rewriteSkillSource(skill, options, summary) {
559
518
  summary.created++;
560
519
  }
561
520
  await writeFileSafe(targetFile2, content);
562
- const relWritten = path8.relative(targetDir, targetFile2);
521
+ const relWritten = path7.relative(targetDir, targetFile2);
563
522
  records.push(makeSourceRecord(skill, targetFile2, content, relWritten));
564
523
  }
565
524
  return records;
566
525
  }
567
- const targetFile = path8.join(targetDir, "SKILL.md");
526
+ const targetFile = path7.join(targetDir, "SKILL.md");
568
527
  const newContent = await renderSkillContent(sourceAbs, skill, data);
569
528
  const exists = await fileExists(targetFile);
570
529
  if (skill.updateStrategy === "frozen") {
@@ -639,14 +598,20 @@ async function syncSkillsToIdes(options) {
639
598
  await ensureDir(targetDir);
640
599
  const sourceFiles = await walkDir(sourceDir);
641
600
  for (const src of sourceFiles) {
642
- const rel2 = path8.relative(sourceDir, src);
643
- const targetFile = path8.join(targetDir, rel2);
644
- const content = await fs5.readFile(src, "utf-8");
645
- await writeFileSafe(targetFile, content);
601
+ const rel2 = path7.relative(sourceDir, src);
602
+ const targetFile = path7.join(targetDir, rel2);
603
+ const sourceContent = await fs5.readFile(src, "utf-8");
604
+ const existing = await readFileOrNull(targetFile);
605
+ if (existing !== null && existing !== sourceContent) {
606
+ logger.warn(
607
+ `Mirror drift detected at ${targetFile} \u2014 overwriting from source. Edit ${src} (not the mirror) and re-run \`teamix-evo skills sync\`.`
608
+ );
609
+ }
610
+ await writeFileSafe(targetFile, sourceContent);
646
611
  out.push({
647
612
  id: rel2 === "SKILL.md" ? skill.id : `${skill.id}:${rel2}`,
648
613
  target: targetFile,
649
- hash: computeHash(content),
614
+ hash: computeHash(sourceContent),
650
615
  strategy: skill.updateStrategy,
651
616
  ide,
652
617
  scope
@@ -668,12 +633,18 @@ async function removeSkillFiles(records) {
668
633
  }
669
634
  }
670
635
  }
671
- const parents = new Set(records.map((r) => path8.dirname(r.target)));
672
- for (const dir of parents) {
673
- try {
674
- const entries = await fs5.readdir(dir);
675
- if (entries.length === 0) await fs5.rmdir(dir);
676
- } catch {
636
+ const startDirs = new Set(records.map((r) => path7.dirname(r.target)));
637
+ for (const startDir of startDirs) {
638
+ let dir = startDir;
639
+ for (let depth = 0; depth < 8; depth++) {
640
+ try {
641
+ const entries = await fs5.readdir(dir);
642
+ if (entries.length !== 0) break;
643
+ await fs5.rmdir(dir);
644
+ } catch {
645
+ break;
646
+ }
647
+ dir = path7.dirname(dir);
677
648
  }
678
649
  }
679
650
  return removed;
@@ -702,7 +673,7 @@ async function runSkillsAdd(options) {
702
673
  throw new Error("Scope must be specified (project | global).");
703
674
  }
704
675
  const { manifest, data, packageRoot } = await loadSkillsData(packageName);
705
- const currentDesignVariant = await readDesignVariant(projectRoot);
676
+ const currentTokensVariant = await readTokensVariant(projectRoot);
706
677
  if (isIncremental) {
707
678
  const known = new Set(manifest.skills.map((s) => s.id));
708
679
  const unknown = requestedNames.filter((n) => !known.has(n));
@@ -733,15 +704,15 @@ async function runSkillsAdd(options) {
733
704
  skippedSkillIds = [];
734
705
  onlyIds = manifest.skills.filter((s) => {
735
706
  if (!s.variant) return true;
736
- if (!currentDesignVariant) {
707
+ if (!currentTokensVariant) {
737
708
  logger.debug(
738
- `Skipping variant-bound skill "${s.id}" (variant=${s.variant}): no design pack installed; will be picked up when "design init" runs.`
709
+ `Skipping variant-bound skill "${s.id}" (variant=${s.variant}): no tokens variant installed; will be picked up when "tokens init" runs.`
739
710
  );
740
711
  return false;
741
712
  }
742
- if (s.variant !== currentDesignVariant) {
713
+ if (s.variant !== currentTokensVariant) {
743
714
  logger.debug(
744
- `Skipping variant-bound skill "${s.id}" (variant=${s.variant}): current design variant is "${currentDesignVariant}".`
715
+ `Skipping variant-bound skill "${s.id}" (variant=${s.variant}): current tokens variant is "${currentTokensVariant}".`
745
716
  );
746
717
  return false;
747
718
  }
@@ -844,85 +815,108 @@ function mergeInstalledResources(existing, next) {
844
815
  return [...map.values()];
845
816
  }
846
817
 
847
- // src/core/design-init.ts
848
- var BASELINE_DESIGN_RULES_SKILL = "teamix-evo-design-rules";
818
+ // src/core/tokens-init.ts
849
819
  var DEFAULT_SKILLS_PACKAGE2 = "@teamix-evo/skills";
850
820
  var DEFAULT_AUTO_SKILL_IDES = ["qoder", "claude"];
851
821
  var DEFAULT_AUTO_SKILL_SCOPE = "project";
852
- var DEFAULT_DESIGN_PACKAGE = "@teamix-evo/design";
822
+ var DEFAULT_TOKENS_PACKAGE = "@teamix-evo/tokens";
823
+ var CONSUMER_TOKENS_DIR = "tokens";
824
+ var CONSUMER_THEME_FILE = "tokens.theme.css";
825
+ var CONSUMER_OVERRIDES_FILE = "tokens.overrides.css";
826
+ var EMPTY_OVERRIDES_TEMPLATE = `/* User-owned token overrides \u2014 frozen on subsequent installs. */
827
+ /* See @teamix-evo/tokens variant theme.css for available CSS custom properties. */
828
+ `;
853
829
  var require3 = createRequire2(import.meta.url);
854
- async function runDesignInit(options) {
830
+ async function runTokensInit(options) {
855
831
  const { projectRoot, variant, ide } = options;
856
- const packageName = options.packageName ?? DEFAULT_DESIGN_PACKAGE;
832
+ const packageName = options.packageName ?? DEFAULT_TOKENS_PACKAGE;
857
833
  await ensureTeamixDir(projectRoot);
858
834
  const existingConfig = await readProjectConfig(projectRoot);
859
- if (existingConfig?.packages?.design) {
835
+ if (existingConfig?.packages?.tokens) {
836
+ const existingVariant = existingConfig.packages.tokens.variant;
837
+ if (existingVariant === variant) {
838
+ return { status: "already-initialized", existingVariant };
839
+ }
860
840
  return {
861
- status: "already-initialized",
862
- existingVariant: existingConfig.packages.design.variant
841
+ status: "variant-mismatch",
842
+ existingVariant,
843
+ requestedVariant: variant
863
844
  };
864
845
  }
865
- const packageRoot = options.packageRoot ?? resolveDesignPackageRoot(packageName);
866
- const catalog = await loadDesignPackageManifest(packageRoot);
867
- const variantEntry = catalog.variants.find((v) => v.name === variant);
846
+ const packageRoot = options.packageRoot ?? resolveTokensPackageRoot(packageName);
847
+ const catalog = await loadTokensPackageManifest(packageRoot);
848
+ const variantEntry = getVariantEntry(catalog, variant);
868
849
  if (!variantEntry) {
869
850
  const known = catalog.variants.map((v) => v.name).join(", ");
870
851
  throw new Error(
871
- `Design variant not found: "${variant}". Known variants: ${known || "(none)"}. Hint: run "teamix-evo design list-variants" to see all.`
852
+ `Tokens variant not found: "${variant}". Known variants: ${known || "(none)"}. Hint: run "teamix-evo tokens list-variants" to see all.`
872
853
  );
873
854
  }
874
- const defaultDir = path9.join(packageRoot, "default");
875
- const variantDir = path9.join(packageRoot, "variants", variant);
876
- const defaultPack = await loadDesignPack(defaultDir);
877
- const variantPack = await loadDesignPack(variantDir);
878
- const merge = await mergeDefaultAndVariant(defaultDir, variantDir);
879
855
  const installed = [];
880
- for (const file of merge.files) {
881
- const result = await installPackFile(file, projectRoot);
856
+ for (const fileRel of variantEntry.files) {
857
+ const result = await installVariantFile(fileRel, packageRoot, projectRoot);
882
858
  if (result) installed.push(result);
883
859
  }
860
+ const overridesAbs = path8.join(
861
+ projectRoot,
862
+ CONSUMER_TOKENS_DIR,
863
+ CONSUMER_OVERRIDES_FILE
864
+ );
865
+ if (!await fileExists(overridesAbs)) {
866
+ await writeFileSafe(overridesAbs, EMPTY_OVERRIDES_TEMPLATE);
867
+ }
868
+ const overridesContent = await fs6.readFile(overridesAbs, "utf-8");
869
+ installed.push({
870
+ id: `tokens:${CONSUMER_OVERRIDES_FILE}`,
871
+ target: path8.posix.join(CONSUMER_TOKENS_DIR, CONSUMER_OVERRIDES_FILE),
872
+ hash: computeHash(overridesContent),
873
+ strategy: "frozen"
874
+ });
884
875
  const lock = {
885
876
  schemaVersion: 1,
886
- default: { version: defaultPack.version, from: packageName },
887
877
  variant: {
888
- name: variantPack.name,
889
- displayName: variantPack.displayName,
890
- version: variantPack.version,
878
+ name: variantEntry.name,
879
+ displayName: variantEntry.displayName,
880
+ version: variantEntry.version,
891
881
  from: packageName
892
882
  },
893
- linked: variantPack.linked,
883
+ packageVersion: catalog.version,
884
+ linked: variantEntry.linked,
894
885
  installedAt: (/* @__PURE__ */ new Date()).toISOString()
895
886
  };
896
887
  await writeFileSafe(
897
- path9.join(projectRoot, ".teamix-evo", "design", "pack.lock.json"),
888
+ path8.join(projectRoot, ".teamix-evo", "tokens-lock.json"),
898
889
  JSON.stringify(lock, null, 2) + "\n"
899
890
  );
900
891
  const config = {
901
892
  $schema: "https://teamix-evo.dev/schema/config/v1.json",
902
893
  schemaVersion: 1,
903
- ide,
894
+ ide: existingConfig?.ide ?? ide,
904
895
  packages: {
905
- design: {
896
+ ...existingConfig?.packages ?? {},
897
+ tokens: {
906
898
  variant,
907
- version: variantPack.version,
899
+ version: variantEntry.version,
908
900
  tailwind: "v4"
909
901
  }
910
902
  }
911
903
  };
912
904
  await writeProjectConfig(projectRoot, config);
913
- const installedManifest = {
905
+ const prior = await readInstalledManifest(projectRoot) ?? {
914
906
  schemaVersion: 1,
915
- installed: [
916
- {
917
- package: packageName,
918
- variant,
919
- version: variantPack.version,
920
- installedAt: (/* @__PURE__ */ new Date()).toISOString(),
921
- resources: installed
922
- }
923
- ]
907
+ installed: []
924
908
  };
925
- await writeInstalledManifest(projectRoot, installedManifest);
909
+ const tokensIdx = prior.installed.findIndex((p) => p.package === packageName);
910
+ const tokensEntry = {
911
+ package: packageName,
912
+ variant,
913
+ version: variantEntry.version,
914
+ installedAt: (/* @__PURE__ */ new Date()).toISOString(),
915
+ resources: installed
916
+ };
917
+ if (tokensIdx >= 0) prior.installed[tokensIdx] = tokensEntry;
918
+ else prior.installed.push(tokensEntry);
919
+ await writeInstalledManifest(projectRoot, prior);
926
920
  const skills = await tryAutoInstallVariantSkills({
927
921
  projectRoot,
928
922
  variant,
@@ -932,21 +926,16 @@ async function runDesignInit(options) {
932
926
  status: "installed",
933
927
  packageName,
934
928
  variant,
935
- version: variantPack.version,
929
+ version: variantEntry.version,
936
930
  count: installed.length,
937
931
  resources: installed,
938
- merge: {
939
- overrides: merge.overrides,
940
- variantAdds: merge.variantAdds,
941
- defaultPassThrough: merge.defaultPassThrough
942
- },
943
932
  skills
944
933
  };
945
934
  }
946
935
  async function tryAutoInstallVariantSkills(args) {
947
936
  const { projectRoot, variant, ide } = args;
948
- const variantSkillId = `${BASELINE_DESIGN_RULES_SKILL}-${variant}`;
949
- const desired = [BASELINE_DESIGN_RULES_SKILL, variantSkillId];
937
+ const variantSkillId = `teamix-evo-design-${variant}`;
938
+ const desired = [variantSkillId];
950
939
  let manifestSkillIds;
951
940
  try {
952
941
  const { manifest } = await loadSkillsData(DEFAULT_SKILLS_PACKAGE2);
@@ -966,7 +955,9 @@ async function tryAutoInstallVariantSkills(args) {
966
955
  const missing = desired.filter((id) => !manifestSkillIds.has(id));
967
956
  if (missing.length > 0) {
968
957
  logger.warn(
969
- `Skills auto-install: not found in manifest, skipping: ${missing.join(", ")}.`
958
+ `Skills auto-install: not found in manifest, skipping: ${missing.join(
959
+ ", "
960
+ )}.`
970
961
  );
971
962
  }
972
963
  if (present.length === 0) {
@@ -1011,34 +1002,100 @@ async function tryAutoInstallVariantSkills(args) {
1011
1002
  };
1012
1003
  }
1013
1004
  }
1014
- async function installPackFile(file, projectRoot) {
1015
- const cls = classifyPackFile(file.relPath);
1016
- if (cls === null) return null;
1017
- const targetAbs = path9.join(projectRoot, cls.target);
1018
- if (cls.isFrozen && await fileExists(targetAbs)) {
1019
- const existing = await fs6.readFile(targetAbs, "utf-8");
1005
+ async function installVariantFile(fileRelToPackage, packageRoot, projectRoot) {
1006
+ const sourceAbs = path8.join(packageRoot, fileRelToPackage);
1007
+ const base = path8.basename(fileRelToPackage);
1008
+ if (base === "theme.css") {
1009
+ const targetRel = path8.posix.join(
1010
+ CONSUMER_TOKENS_DIR,
1011
+ CONSUMER_THEME_FILE
1012
+ );
1013
+ const targetAbs = path8.join(projectRoot, targetRel);
1014
+ const content = await fs6.readFile(sourceAbs, "utf-8");
1015
+ await writeFileSafe(targetAbs, content);
1020
1016
  return {
1021
- id: `pack:${file.relPath}`,
1022
- target: cls.target,
1023
- hash: computeHash(existing),
1024
- strategy: cls.strategy
1017
+ id: `tokens:${CONSUMER_THEME_FILE}`,
1018
+ target: targetRel,
1019
+ hash: computeHash(content),
1020
+ strategy: "regenerable"
1025
1021
  };
1026
1022
  }
1027
- const content = await fs6.readFile(file.sourcePath, "utf-8");
1028
- await writeFileSafe(targetAbs, content);
1029
- return {
1030
- id: `pack:${file.relPath}`,
1031
- target: cls.target,
1032
- hash: computeHash(content),
1033
- strategy: cls.strategy
1034
- };
1023
+ if (base === "overrides.css" || base === "tokens.overrides.css") {
1024
+ const targetRel = path8.posix.join(
1025
+ CONSUMER_TOKENS_DIR,
1026
+ CONSUMER_OVERRIDES_FILE
1027
+ );
1028
+ const targetAbs = path8.join(projectRoot, targetRel);
1029
+ if (await fileExists(targetAbs)) {
1030
+ const existing = await fs6.readFile(targetAbs, "utf-8");
1031
+ return {
1032
+ id: `tokens:${CONSUMER_OVERRIDES_FILE}`,
1033
+ target: targetRel,
1034
+ hash: computeHash(existing),
1035
+ strategy: "frozen"
1036
+ };
1037
+ }
1038
+ const content = await fs6.readFile(sourceAbs, "utf-8");
1039
+ await writeFileSafe(targetAbs, content);
1040
+ return {
1041
+ id: `tokens:${CONSUMER_OVERRIDES_FILE}`,
1042
+ target: targetRel,
1043
+ hash: computeHash(content),
1044
+ strategy: "frozen"
1045
+ };
1046
+ }
1047
+ return null;
1035
1048
  }
1036
- function resolveDesignPackageRoot(packageName) {
1049
+ function resolveTokensPackageRoot(packageName) {
1037
1050
  const pkgJson = require3.resolve(`${packageName}/package.json`);
1038
- return path9.dirname(pkgJson);
1051
+ return path8.dirname(pkgJson);
1052
+ }
1053
+ async function listTokenVariants(packageName = DEFAULT_TOKENS_PACKAGE, packageRoot) {
1054
+ const root = packageRoot ?? resolveTokensPackageRoot(packageName);
1055
+ const catalog = await loadTokensPackageManifest(root);
1056
+ return {
1057
+ packageName,
1058
+ packageVersion: catalog.version,
1059
+ variants: catalog.variants.map((v) => ({
1060
+ name: v.name,
1061
+ displayName: v.displayName,
1062
+ version: v.version,
1063
+ description: v.description,
1064
+ linked: v.linked
1065
+ }))
1066
+ };
1039
1067
  }
1040
1068
 
1041
1069
  // src/core/ui-init.ts
1070
+ import * as fs7 from "fs/promises";
1071
+ import { createRequire as createRequire3 } from "module";
1072
+ import * as path9 from "path";
1073
+ var require4 = createRequire3(import.meta.url);
1074
+ async function deployPreferencesCss(projectRoot) {
1075
+ const targetDir = path9.join(projectRoot, "src");
1076
+ const targetPath = path9.join(targetDir, "preferences.css");
1077
+ try {
1078
+ await fs7.access(targetPath);
1079
+ logger.debug(`preferences.css already exists at ${targetPath}, skipping`);
1080
+ return "skipped";
1081
+ } catch {
1082
+ }
1083
+ let sourcePath;
1084
+ try {
1085
+ const uiPkgJson = require4.resolve("@teamix-evo/ui/package.json");
1086
+ sourcePath = path9.join(path9.dirname(uiPkgJson), "src", "preferences.css");
1087
+ await fs7.access(sourcePath);
1088
+ } catch {
1089
+ logger.debug(
1090
+ "Could not resolve @teamix-evo/ui/src/preferences.css; skipping deploy"
1091
+ );
1092
+ return "source-missing";
1093
+ }
1094
+ await fs7.mkdir(targetDir, { recursive: true });
1095
+ const content = await fs7.readFile(sourcePath, "utf-8");
1096
+ await fs7.writeFile(targetPath, content, "utf-8");
1097
+ return "deployed";
1098
+ }
1042
1099
  var DEFAULT_UI_ALIASES = {
1043
1100
  components: "src/components/ui",
1044
1101
  hooks: "src/hooks",
@@ -1082,23 +1139,25 @@ async function runUiInit(options) {
1082
1139
  rsc
1083
1140
  };
1084
1141
  await writeProjectConfig(projectRoot, config);
1142
+ const preferencesCss = await deployPreferencesCss(projectRoot);
1085
1143
  return {
1086
1144
  status: "installed",
1087
1145
  aliases,
1088
1146
  iconLibrary,
1089
1147
  tsx,
1090
- rsc
1148
+ rsc,
1149
+ preferencesCss
1091
1150
  };
1092
1151
  }
1093
1152
 
1094
1153
  // src/core/ui-client.ts
1095
1154
  import * as path10 from "path";
1096
- import * as fs7 from "fs/promises";
1097
- import { createRequire as createRequire3 } from "module";
1155
+ import * as fs8 from "fs/promises";
1156
+ import { createRequire as createRequire4 } from "module";
1098
1157
  import { loadUiPackageManifest } from "@teamix-evo/registry";
1099
- var require4 = createRequire3(import.meta.url);
1158
+ var require5 = createRequire4(import.meta.url);
1100
1159
  function resolvePackageRoot2(packageName) {
1101
- const pkgJsonPath = require4.resolve(`${packageName}/package.json`);
1160
+ const pkgJsonPath = require5.resolve(`${packageName}/package.json`);
1102
1161
  return path10.dirname(pkgJsonPath);
1103
1162
  }
1104
1163
  async function loadUiData(packageName) {
@@ -1108,7 +1167,7 @@ async function loadUiData(packageName) {
1108
1167
  let data = {};
1109
1168
  const dataPath = path10.join(packageRoot, "_data.json");
1110
1169
  try {
1111
- const raw = await fs7.readFile(dataPath, "utf-8");
1170
+ const raw = await fs8.readFile(dataPath, "utf-8");
1112
1171
  data = JSON.parse(raw);
1113
1172
  } catch (err) {
1114
1173
  if (err.code !== "ENOENT") {
@@ -1121,7 +1180,7 @@ async function loadUiData(packageName) {
1121
1180
 
1122
1181
  // src/core/ui-installer.ts
1123
1182
  import * as path11 from "path";
1124
- import * as fs8 from "fs/promises";
1183
+ import * as fs9 from "fs/promises";
1125
1184
  import { resolveUiEntryOrder } from "@teamix-evo/registry";
1126
1185
 
1127
1186
  // src/utils/transform-imports.ts
@@ -1161,12 +1220,12 @@ function aliasToImportPath(alias) {
1161
1220
  }
1162
1221
 
1163
1222
  // src/core/ui-installer.ts
1164
- var DESIGN_COMPONENTS_DIR = ".teamix-evo/design/components";
1165
1223
  async function installUiEntries(options) {
1166
1224
  const {
1167
1225
  projectRoot,
1168
1226
  manifest,
1169
1227
  packageRoot,
1228
+ entryPackageRoot,
1170
1229
  aliases,
1171
1230
  requested,
1172
1231
  skipExisting = true
@@ -1175,7 +1234,6 @@ async function installUiEntries(options) {
1175
1234
  const idToEntry = new Map(manifest.entries.map((e) => [e.id, e]));
1176
1235
  const resources = [];
1177
1236
  const npmDeps = {};
1178
- const metaFiles = [];
1179
1237
  let written = 0;
1180
1238
  let skipped = 0;
1181
1239
  for (const id of orderedIds) {
@@ -1194,8 +1252,9 @@ async function installUiEntries(options) {
1194
1252
  skipped++;
1195
1253
  continue;
1196
1254
  }
1197
- const sourceAbs = path11.resolve(packageRoot, file.source);
1198
- const raw = await fs8.readFile(sourceAbs, "utf-8");
1255
+ const rootForEntry = entryPackageRoot?.get(entry.id) ?? packageRoot;
1256
+ const sourceAbs = path11.resolve(rootForEntry, file.source);
1257
+ const raw = await fs9.readFile(sourceAbs, "utf-8");
1199
1258
  const transformed = rewriteImports(raw, aliases);
1200
1259
  await writeFileSafe(targetAbs, transformed);
1201
1260
  written++;
@@ -1207,32 +1266,13 @@ async function installUiEntries(options) {
1207
1266
  strategy: entry.updateStrategy ?? "frozen"
1208
1267
  });
1209
1268
  }
1210
- if (entry.meta) {
1211
- const metaSourceAbs = path11.resolve(packageRoot, entry.meta);
1212
- const metaContent = await fs8.readFile(metaSourceAbs, "utf-8");
1213
- const metaTargetAbs = path11.join(
1214
- projectRoot,
1215
- DESIGN_COMPONENTS_DIR,
1216
- `${entry.id}.meta.md`
1217
- );
1218
- await writeFileSafe(metaTargetAbs, metaContent);
1219
- metaFiles.push(metaTargetAbs);
1220
- resources.push({
1221
- id: `${entry.id}:meta`,
1222
- target: metaTargetAbs,
1223
- hash: computeHash(metaContent),
1224
- strategy: "regenerable"
1225
- });
1226
- logger.info(` meta: ${rel(projectRoot, metaTargetAbs)}`);
1227
- }
1228
1269
  }
1229
1270
  return {
1230
1271
  orderedIds,
1231
1272
  resources,
1232
1273
  npmDependencies: npmDeps,
1233
1274
  written,
1234
- skipped,
1235
- metaFiles
1275
+ skipped
1236
1276
  };
1237
1277
  }
1238
1278
  function resolveTargetPath(projectRoot, aliases, entry, file) {
@@ -1251,7 +1291,7 @@ async function removeUiFiles(records) {
1251
1291
  const removed = [];
1252
1292
  for (const r of records) {
1253
1293
  try {
1254
- await fs8.unlink(r.target);
1294
+ await fs9.unlink(r.target);
1255
1295
  removed.push(r.target);
1256
1296
  } catch (err) {
1257
1297
  if (err.code !== "ENOENT") {
@@ -1262,8 +1302,8 @@ async function removeUiFiles(records) {
1262
1302
  const parents = new Set(records.map((r) => path11.dirname(r.target)));
1263
1303
  for (const dir of parents) {
1264
1304
  try {
1265
- const entries = await fs8.readdir(dir);
1266
- if (entries.length === 0) await fs8.rmdir(dir);
1305
+ const entries = await fs9.readdir(dir);
1306
+ if (entries.length === 0) await fs9.rmdir(dir);
1267
1307
  } catch {
1268
1308
  }
1269
1309
  }
@@ -1329,7 +1369,6 @@ async function runUiAdd(options) {
1329
1369
  orderedIds: result.orderedIds,
1330
1370
  written: result.written,
1331
1371
  skipped: result.skipped,
1332
- metaFiles: result.metaFiles,
1333
1372
  npmDependencies: result.npmDependencies,
1334
1373
  resources: result.resources
1335
1374
  };
@@ -1372,7 +1411,7 @@ async function runUiList(options) {
1372
1411
 
1373
1412
  // src/core/installer.ts
1374
1413
  import * as path12 from "path";
1375
- import * as fs9 from "fs/promises";
1414
+ import * as fs10 from "fs/promises";
1376
1415
  async function installResources(options) {
1377
1416
  const { projectRoot, manifest, data, variantDir, packageRoot } = options;
1378
1417
  const installedResources = [];
@@ -1415,7 +1454,7 @@ async function installSingleResource(resource, projectRoot, data, variantDir, pa
1415
1454
  const templateContent = await loadTemplateFile(sourcePath);
1416
1455
  content = renderTemplate(templateContent, data);
1417
1456
  } else {
1418
- content = await fs9.readFile(sourcePath, "utf-8");
1457
+ content = await fs10.readFile(sourcePath, "utf-8");
1419
1458
  }
1420
1459
  await writeFileSafe(targetPath, content);
1421
1460
  const hash = computeHash(content);
@@ -1448,7 +1487,7 @@ async function installRecursiveResource(resource, projectRoot, data, variantDir,
1448
1487
  const templateContent = await loadTemplateFile(entry);
1449
1488
  content = renderTemplate(templateContent, data);
1450
1489
  } else {
1451
- content = await fs9.readFile(entry, "utf-8");
1490
+ content = await fs10.readFile(entry, "utf-8");
1452
1491
  }
1453
1492
  await writeFileSafe(targetFile, content);
1454
1493
  const hash = computeHash(content);
@@ -1466,12 +1505,12 @@ async function installRecursiveResource(resource, projectRoot, data, variantDir,
1466
1505
 
1467
1506
  // src/core/registry-client.ts
1468
1507
  import * as path13 from "path";
1469
- import * as fs10 from "fs/promises";
1470
- import { createRequire as createRequire4 } from "module";
1508
+ import * as fs11 from "fs/promises";
1509
+ import { createRequire as createRequire5 } from "module";
1471
1510
  import { loadVariantManifest } from "@teamix-evo/registry";
1472
- var require5 = createRequire4(import.meta.url);
1511
+ var require6 = createRequire5(import.meta.url);
1473
1512
  function resolvePackageRoot3(packageName) {
1474
- const pkgJsonPath = require5.resolve(`${packageName}/package.json`);
1513
+ const pkgJsonPath = require6.resolve(`${packageName}/package.json`);
1475
1514
  return path13.dirname(pkgJsonPath);
1476
1515
  }
1477
1516
  async function loadVariantData(packageName, variant) {
@@ -1483,7 +1522,7 @@ async function loadVariantData(packageName, variant) {
1483
1522
  let data = {};
1484
1523
  const dataPath = path13.join(variantDir, "_data.json");
1485
1524
  try {
1486
- const raw = await fs10.readFile(dataPath, "utf-8");
1525
+ const raw = await fs11.readFile(dataPath, "utf-8");
1487
1526
  data = JSON.parse(raw);
1488
1527
  } catch (err) {
1489
1528
  if (err.code !== "ENOENT") {
@@ -1501,6 +1540,7 @@ export {
1501
1540
  installResources,
1502
1541
  installSkills,
1503
1542
  installUiEntries,
1543
+ listTokenVariants,
1504
1544
  loadSkillsData,
1505
1545
  loadUiData,
1506
1546
  loadVariantData,
@@ -1508,8 +1548,8 @@ export {
1508
1548
  readProjectConfig,
1509
1549
  removeSkillFiles,
1510
1550
  removeUiFiles,
1511
- runDesignInit,
1512
1551
  runSkillsAdd,
1552
+ runTokensInit,
1513
1553
  runUiAdd,
1514
1554
  runUiInit,
1515
1555
  runUiList,