teamix-evo 0.1.0 → 0.3.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,67 +1,122 @@
1
1
  #!/usr/bin/env node
2
- var __defProp = Object.defineProperty;
3
- var __getOwnPropNames = Object.getOwnPropertyNames;
4
- var __esm = (fn, res) => function __init() {
5
- return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
2
+
3
+ // src/index.ts
4
+ import { Command as Command24 } from "commander";
5
+ import { createRequire as createRequire5 } from "module";
6
+
7
+ // src/commands/design/index.ts
8
+ import { Command as Command6 } from "commander";
9
+
10
+ // src/commands/design/init.ts
11
+ import { Command } from "commander";
12
+
13
+ // src/ide/QoderAdapter.ts
14
+ import * as os from "os";
15
+ import * as path from "path";
16
+ var QoderAdapter = class {
17
+ kind = "qoder";
18
+ name = "qoder";
19
+ getProjectRoot() {
20
+ return process.cwd();
21
+ }
22
+ detectIde() {
23
+ return true;
24
+ }
25
+ getSkillTargetDir(skillName, scope, projectRoot) {
26
+ const base = scope === "global" ? path.join(os.homedir(), ".qoder") : path.join(projectRoot ?? this.getProjectRoot(), ".qoder");
27
+ return path.join(base, "skills", skillName);
28
+ }
6
29
  };
7
- var __export = (target, all) => {
8
- for (var name in all)
9
- __defProp(target, name, { get: all[name], enumerable: true });
30
+
31
+ // src/ide/ClaudeAdapter.ts
32
+ import * as os2 from "os";
33
+ import * as path2 from "path";
34
+ var ClaudeAdapter = class {
35
+ kind = "claude";
36
+ name = "claude";
37
+ getProjectRoot() {
38
+ return process.cwd();
39
+ }
40
+ detectIde() {
41
+ return Boolean(process.env.CLAUDECODE);
42
+ }
43
+ getSkillTargetDir(skillName, scope, projectRoot) {
44
+ const base = scope === "global" ? path2.join(os2.homedir(), ".claude") : path2.join(projectRoot ?? this.getProjectRoot(), ".claude");
45
+ return path2.join(base, "skills", skillName);
46
+ }
10
47
  };
11
48
 
49
+ // src/ide/index.ts
50
+ var ALL_IDE_KINDS = ["qoder", "claude"];
51
+ function getAdapter(kind) {
52
+ switch (kind) {
53
+ case "qoder":
54
+ return new QoderAdapter();
55
+ case "claude":
56
+ return new ClaudeAdapter();
57
+ default: {
58
+ const _exhaustive = kind;
59
+ throw new Error(`Unsupported IDE kind: ${_exhaustive}`);
60
+ }
61
+ }
62
+ }
63
+ function detectIde() {
64
+ const claude = new ClaudeAdapter();
65
+ if (claude.detectIde()) return claude;
66
+ return new QoderAdapter();
67
+ }
68
+
69
+ // src/core/design-init.ts
70
+ import * as path9 from "path";
71
+ import * as fs6 from "fs/promises";
72
+ import { createRequire as createRequire2 } from "module";
73
+ import {
74
+ loadDesignPack,
75
+ loadDesignPackageManifest,
76
+ mergeDefaultAndVariant
77
+ } from "@teamix-evo/registry";
78
+
79
+ // src/utils/fs.ts
80
+ import * as fs from "fs/promises";
81
+ import * as path3 from "path";
82
+
12
83
  // src/utils/logger.ts
13
84
  import { red, yellow, cyan, green, gray } from "kolorist";
14
- var isDebug, logger;
15
- var init_logger = __esm({
16
- "src/utils/logger.ts"() {
17
- "use strict";
18
- isDebug = process.env.TEAMIX_DEBUG === "1";
19
- logger = {
20
- info(msg) {
21
- console.log(cyan("\u2139"), msg);
22
- },
23
- warn(msg) {
24
- console.warn(yellow("\u26A0"), msg);
25
- },
26
- error(msg) {
27
- console.error(red("\u2716"), msg);
28
- },
29
- success(msg) {
30
- console.log(green("\u2714"), msg);
31
- },
32
- debug(msg) {
33
- if (isDebug) {
34
- console.log(gray("\u22A1"), gray(msg));
35
- }
36
- }
37
- };
85
+ var isDebug = process.env.TEAMIX_DEBUG === "1";
86
+ var logger = {
87
+ info(msg) {
88
+ console.log(cyan("\u2139"), msg);
89
+ },
90
+ warn(msg) {
91
+ console.warn(yellow("\u26A0"), msg);
92
+ },
93
+ error(msg) {
94
+ console.error(red("\u2716"), msg);
95
+ },
96
+ success(msg) {
97
+ console.log(green("\u2714"), msg);
98
+ },
99
+ debug(msg) {
100
+ if (isDebug) {
101
+ console.log(gray("\u22A1"), gray(msg));
102
+ }
38
103
  }
39
- });
104
+ };
40
105
 
41
106
  // src/utils/fs.ts
42
- var fs_exports = {};
43
- __export(fs_exports, {
44
- backupFile: () => backupFile,
45
- ensureDir: () => ensureDir,
46
- fileExists: () => fileExists,
47
- readFileOrNull: () => readFileOrNull,
48
- writeFileSafe: () => writeFileSafe
49
- });
50
- import * as fs2 from "fs/promises";
51
- import * as path2 from "path";
52
107
  async function ensureDir(dir) {
53
- await fs2.mkdir(dir, { recursive: true });
108
+ await fs.mkdir(dir, { recursive: true });
54
109
  }
55
110
  async function writeFileSafe(filePath, content) {
56
- const dir = path2.dirname(filePath);
111
+ const dir = path3.dirname(filePath);
57
112
  await ensureDir(dir);
58
113
  const tmp = filePath + ".tmp";
59
- await fs2.writeFile(tmp, content, "utf-8");
60
- await fs2.rename(tmp, filePath);
114
+ await fs.writeFile(tmp, content, "utf-8");
115
+ await fs.rename(tmp, filePath);
61
116
  }
62
117
  async function readFileOrNull(filePath) {
63
118
  try {
64
- return await fs2.readFile(filePath, "utf-8");
119
+ return await fs.readFile(filePath, "utf-8");
65
120
  } catch (err) {
66
121
  if (err.code === "ENOENT") {
67
122
  return null;
@@ -75,96 +130,26 @@ async function backupFile(filePath, projectRoot) {
75
130
  logger.debug(`Skip backup: ${filePath} does not exist`);
76
131
  return;
77
132
  }
78
- const rel = path2.relative(projectRoot, filePath);
133
+ const rel2 = path3.relative(projectRoot, filePath);
79
134
  const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
80
- const backupPath = path2.join(
135
+ const backupPath = path3.join(
81
136
  projectRoot,
82
137
  ".teamix-evo",
83
138
  ".backups",
84
- `${rel}.${timestamp}.bak`
139
+ `${rel2}.${timestamp}.bak`
85
140
  );
86
- await ensureDir(path2.dirname(backupPath));
87
- await fs2.writeFile(backupPath, content, "utf-8");
88
- logger.debug(`Backed up ${rel} \u2192 ${path2.relative(projectRoot, backupPath)}`);
141
+ await ensureDir(path3.dirname(backupPath));
142
+ await fs.writeFile(backupPath, content, "utf-8");
143
+ logger.debug(`Backed up ${rel2} \u2192 ${path3.relative(projectRoot, backupPath)}`);
89
144
  }
90
145
  async function fileExists(filePath) {
91
146
  try {
92
- await fs2.access(filePath);
147
+ await fs.access(filePath);
93
148
  return true;
94
149
  } catch {
95
150
  return false;
96
151
  }
97
152
  }
98
- var init_fs = __esm({
99
- "src/utils/fs.ts"() {
100
- "use strict";
101
- init_logger();
102
- }
103
- });
104
-
105
- // src/index.ts
106
- import { Command as Command5 } from "commander";
107
-
108
- // src/commands/design/index.ts
109
- import { Command as Command4 } from "commander";
110
-
111
- // src/commands/design/init.ts
112
- import { Command } from "commander";
113
-
114
- // src/ide/QoderAdapter.ts
115
- var QoderAdapter = class {
116
- name = "qoder";
117
- getProjectRoot() {
118
- return process.cwd();
119
- }
120
- detectIde() {
121
- return true;
122
- }
123
- };
124
-
125
- // src/ide/index.ts
126
- function detectIde() {
127
- return new QoderAdapter();
128
- }
129
-
130
- // src/core/registry-client.ts
131
- init_logger();
132
- import * as path from "path";
133
- import * as fs from "fs/promises";
134
- import { createRequire } from "module";
135
- import { loadVariantManifest } from "@teamix-evo/registry";
136
- function resolveVariantPackage(packageName, variant) {
137
- const require2 = createRequire(import.meta.url);
138
- const pkgJsonPath = require2.resolve(`${packageName}/package.json`);
139
- const pkgRoot = path.dirname(pkgJsonPath);
140
- return path.join(pkgRoot, "library", variant);
141
- }
142
- async function loadVariantData(packageName, variant) {
143
- const variantDir = resolveVariantPackage(packageName, variant);
144
- const require2 = createRequire(import.meta.url);
145
- const pkgJsonPath = require2.resolve(`${packageName}/package.json`);
146
- const packageRoot = path.dirname(pkgJsonPath);
147
- logger.debug(`Resolved variant dir: ${variantDir}`);
148
- logger.debug(`Package root: ${packageRoot}`);
149
- const manifest = await loadVariantManifest(variantDir);
150
- let data = {};
151
- const dataPath = path.join(variantDir, "_data.json");
152
- try {
153
- const raw = await fs.readFile(dataPath, "utf-8");
154
- data = JSON.parse(raw);
155
- } catch (err) {
156
- if (err.code !== "ENOENT") {
157
- throw err;
158
- }
159
- logger.debug(`No _data.json found at ${dataPath}, using empty data`);
160
- }
161
- return { manifest, data, variantDir, packageRoot };
162
- }
163
-
164
- // src/core/installer.ts
165
- init_fs();
166
- import * as path3 from "path";
167
- import * as fs4 from "fs/promises";
168
153
 
169
154
  // src/utils/hash.ts
170
155
  import { createHash } from "crypto";
@@ -173,134 +158,71 @@ function computeHash(content) {
173
158
  return `sha256:${hash}`;
174
159
  }
175
160
 
176
- // src/utils/template.ts
177
- import Handlebars from "handlebars";
178
- import * as fs3 from "fs/promises";
179
- Handlebars.registerHelper("lowercase", (str) => {
180
- return typeof str === "string" ? str.toLowerCase() : String(str ?? "").toLowerCase();
181
- });
182
- function renderTemplate(templateContent, data) {
183
- const compiled = Handlebars.compile(templateContent, { noEscape: true });
184
- return compiled(data);
185
- }
186
- async function loadTemplateFile(filePath) {
187
- return fs3.readFile(filePath, "utf-8");
188
- }
189
-
190
- // src/core/installer.ts
191
- init_logger();
192
- async function installResources(options) {
193
- const { projectRoot, manifest, data, variantDir, packageRoot } = options;
194
- const installedResources = [];
195
- for (const resource of manifest.resources) {
196
- logger.debug(`Installing resource: ${resource.id} \u2192 ${resource.target}`);
197
- if (resource.recursive) {
198
- const results = await installRecursiveResource(
199
- resource,
200
- projectRoot,
201
- data,
202
- variantDir,
203
- packageRoot
204
- );
205
- installedResources.push(...results);
206
- } else {
207
- const result = await installSingleResource(
208
- resource,
209
- projectRoot,
210
- data,
211
- variantDir,
212
- packageRoot
213
- );
214
- installedResources.push(result);
215
- }
161
+ // src/core/design-pack-classify.ts
162
+ import * as path4 from "path";
163
+ var TEAMIX_DIR = ".teamix-evo/design";
164
+ var TOKENS_DIR = ".teamix-evo/tokens";
165
+ var TOKENS_PACK_PREFIX = "foundations/tokens/";
166
+ var ROOT_MANAGED_FILES = {
167
+ "DESIGN.md": { target: "DESIGN.md", managedRegions: ["core"] },
168
+ "AGENTS.md": { target: "AGENTS.md", managedRegions: ["teamix-evo"] },
169
+ "CLAUDE.md": { target: "CLAUDE.md", managedRegions: ["teamix-evo"] }
170
+ };
171
+ var FROZEN_FILES = /* @__PURE__ */ new Set([
172
+ "foundations/tokens/tokens.overrides.css"
173
+ ]);
174
+ function classifyPackFile(relPath) {
175
+ if (path4.basename(relPath) === "README.md") {
176
+ return null;
216
177
  }
217
- return {
218
- resources: installedResources,
219
- count: installedResources.length
220
- };
221
- }
222
- async function installSingleResource(resource, projectRoot, data, variantDir, packageRoot) {
223
- const sourcePath = resolveSourcePath(resource.source, variantDir, packageRoot);
224
- const targetPath = path3.join(projectRoot, resource.target);
225
- let content;
226
- if (resource.template) {
227
- const templateContent = await loadTemplateFile(sourcePath);
228
- content = renderTemplate(templateContent, data);
229
- } else {
230
- content = await fs4.readFile(sourcePath, "utf-8");
178
+ const rootManaged = ROOT_MANAGED_FILES[relPath];
179
+ if (rootManaged) {
180
+ return {
181
+ target: rootManaged.target,
182
+ strategy: "managed",
183
+ managedRegions: rootManaged.managedRegions,
184
+ isFrozen: false
185
+ };
231
186
  }
232
- await writeFileSafe(targetPath, content);
233
- const hash = computeHash(content);
234
- logger.debug(` Written: ${resource.target} (${resource.updateStrategy})`);
235
- return {
236
- id: resource.id,
237
- target: resource.target,
238
- hash,
239
- strategy: resource.updateStrategy
240
- };
241
- }
242
- async function installRecursiveResource(resource, projectRoot, data, variantDir, packageRoot) {
243
- const sourcePath = resolveSourcePath(resource.source, variantDir, packageRoot);
244
- const targetDir = path3.join(projectRoot, resource.target);
245
- const results = [];
246
- await ensureDir(targetDir);
247
- const entries = await walkDir(sourcePath);
248
- for (const entry of entries) {
249
- const relPath = path3.relative(sourcePath, entry);
250
- let targetFile = path3.join(targetDir, relPath);
251
- if (resource.template && targetFile.endsWith(".hbs")) {
252
- targetFile = targetFile.slice(0, -4);
253
- }
254
- let content;
255
- if (resource.template && entry.endsWith(".hbs")) {
256
- const templateContent = await loadTemplateFile(entry);
257
- content = renderTemplate(templateContent, data);
258
- } else {
259
- content = await fs4.readFile(entry, "utf-8");
187
+ if (relPath.startsWith(TOKENS_PACK_PREFIX)) {
188
+ const rel2 = relPath.slice(TOKENS_PACK_PREFIX.length);
189
+ const target = path4.posix.join(TOKENS_DIR, rel2);
190
+ if (FROZEN_FILES.has(relPath)) {
191
+ return { target, strategy: "frozen", isFrozen: true };
260
192
  }
261
- await writeFileSafe(targetFile, content);
262
- const hash = computeHash(content);
263
- const targetRel = path3.relative(projectRoot, targetFile);
264
- results.push({
265
- id: `${resource.id}:${relPath}`,
266
- target: targetRel,
267
- hash,
268
- strategy: resource.updateStrategy
269
- });
270
- logger.debug(` Written: ${targetRel}`);
271
- }
272
- return results;
273
- }
274
- function resolveSourcePath(source, variantDir, packageRoot) {
275
- if (source.startsWith("_template/")) {
276
- return path3.join(packageRoot, source);
193
+ return { target, strategy: "regenerable", isFrozen: false };
277
194
  }
278
- return path3.join(variantDir, source);
279
- }
280
- async function walkDir(dir) {
281
- const files = [];
282
- const entries = await fs4.readdir(dir, { withFileTypes: true });
283
- for (const entry of entries) {
284
- const fullPath = path3.join(dir, entry.name);
285
- if (entry.isDirectory()) {
286
- files.push(...await walkDir(fullPath));
287
- } else if (entry.isFile()) {
288
- files.push(fullPath);
289
- }
195
+ if (FROZEN_FILES.has(relPath)) {
196
+ return {
197
+ target: path4.posix.join(TEAMIX_DIR, relPath),
198
+ strategy: "frozen",
199
+ isFrozen: true
200
+ };
290
201
  }
291
- return files;
202
+ return {
203
+ target: path4.posix.join(TEAMIX_DIR, relPath),
204
+ strategy: "regenerable",
205
+ isFrozen: false
206
+ };
292
207
  }
293
208
 
294
209
  // src/core/state.ts
295
- init_fs();
296
- init_logger();
297
- import * as path4 from "path";
298
- import { validateConfig, validateInstalled } from "@teamix-evo/registry";
299
- var TEAMIX_DIR = ".teamix-evo";
210
+ import * as path5 from "path";
211
+ import {
212
+ validateConfig,
213
+ validateInstalled,
214
+ validateSkillsLock,
215
+ DesignPackLockSchema
216
+ } from "@teamix-evo/registry";
217
+ var TEAMIX_DIR2 = ".teamix-evo";
300
218
  var CONFIG_FILE = "config.json";
301
219
  var MANIFEST_FILE = "manifest.json";
220
+ var DESIGN_DIR = "design";
221
+ var DESIGN_LOCK_FILE = "pack.lock.json";
222
+ var SKILLS_DIR = "skills";
223
+ var SKILLS_LOCK_FILE = "manifest.lock.json";
302
224
  function getTeamixDir(projectRoot) {
303
- return path4.join(projectRoot, TEAMIX_DIR);
225
+ return path5.join(projectRoot, TEAMIX_DIR2);
304
226
  }
305
227
  async function ensureTeamixDir(projectRoot) {
306
228
  const dir = getTeamixDir(projectRoot);
@@ -308,7 +230,7 @@ async function ensureTeamixDir(projectRoot) {
308
230
  return dir;
309
231
  }
310
232
  async function readProjectConfig(projectRoot) {
311
- const configPath = path4.join(projectRoot, TEAMIX_DIR, CONFIG_FILE);
233
+ const configPath = path5.join(projectRoot, TEAMIX_DIR2, CONFIG_FILE);
312
234
  const raw = await readFileOrNull(configPath);
313
235
  if (raw === null) return null;
314
236
  try {
@@ -325,12 +247,12 @@ async function readProjectConfig(projectRoot) {
325
247
  }
326
248
  }
327
249
  async function writeProjectConfig(projectRoot, config) {
328
- const configPath = path4.join(projectRoot, TEAMIX_DIR, CONFIG_FILE);
250
+ const configPath = path5.join(projectRoot, TEAMIX_DIR2, CONFIG_FILE);
329
251
  await writeFileSafe(configPath, JSON.stringify(config, null, 2) + "\n");
330
252
  logger.debug(`Wrote config \u2192 ${configPath}`);
331
253
  }
332
254
  async function readInstalledManifest(projectRoot) {
333
- const manifestPath = path4.join(projectRoot, TEAMIX_DIR, MANIFEST_FILE);
255
+ const manifestPath = path5.join(projectRoot, TEAMIX_DIR2, MANIFEST_FILE);
334
256
  const raw = await readFileOrNull(manifestPath);
335
257
  if (raw === null) return null;
336
258
  try {
@@ -347,388 +269,879 @@ async function readInstalledManifest(projectRoot) {
347
269
  }
348
270
  }
349
271
  async function writeInstalledManifest(projectRoot, manifest) {
350
- const manifestPath = path4.join(projectRoot, TEAMIX_DIR, MANIFEST_FILE);
272
+ const manifestPath = path5.join(projectRoot, TEAMIX_DIR2, MANIFEST_FILE);
351
273
  await writeFileSafe(manifestPath, JSON.stringify(manifest, null, 2) + "\n");
352
274
  logger.debug(`Wrote manifest \u2192 ${manifestPath}`);
353
275
  }
354
-
355
- // src/commands/design/init.ts
356
- init_logger();
357
- var DEFAULT_VARIANT = "opentrek";
358
- var DESIGN_PACKAGE = "@teamix-evo/design";
359
- var initCommand = new Command("init").description("\u521D\u59CB\u5316\u8BBE\u8BA1\u4F53\u7CFB\u8D44\u6E90").argument("[variant]", "\u8BBE\u8BA1\u53D8\u4F53\u540D\u79F0", DEFAULT_VARIANT).action(async (variant) => {
276
+ async function readDesignPackLock(projectRoot) {
277
+ const lockPath = path5.join(
278
+ projectRoot,
279
+ TEAMIX_DIR2,
280
+ DESIGN_DIR,
281
+ DESIGN_LOCK_FILE
282
+ );
283
+ const raw = await readFileOrNull(lockPath);
284
+ if (raw === null) return null;
360
285
  try {
361
- const ide = detectIde();
362
- const projectRoot = ide.getProjectRoot();
363
- logger.info(`Initializing design system: variant="${variant}"`);
364
- logger.debug(`Project root: ${projectRoot}`);
365
- logger.debug(`IDE: ${ide.name}`);
366
- await ensureTeamixDir(projectRoot);
367
- const existingConfig = await readProjectConfig(projectRoot);
368
- if (existingConfig?.packages?.design) {
369
- logger.warn(
370
- `Design system already initialized (variant: ${existingConfig.packages.design.variant}). Use "teamix-evo design update" to update.`
371
- );
372
- return;
286
+ const parsed = DesignPackLockSchema.safeParse(JSON.parse(raw));
287
+ if (!parsed.success) {
288
+ logger.warn(`Invalid design pack.lock.json: ${parsed.error.message}`);
289
+ return null;
373
290
  }
374
- logger.info(`Loading variant "${variant}" from ${DESIGN_PACKAGE}...`);
375
- const { manifest, data, variantDir, packageRoot } = await loadVariantData(DESIGN_PACKAGE, variant);
376
- logger.debug(
377
- `Loaded manifest: ${manifest.displayName} v${manifest.version}`
378
- );
379
- logger.debug(`Resources: ${manifest.resources.length}`);
380
- logger.info("Installing resources...");
381
- const result = await installResources({
382
- projectRoot,
383
- manifest,
384
- data,
385
- variantDir,
386
- packageRoot
387
- });
388
- const config = {
389
- $schema: "https://teamix-evo.dev/schema/config/v1.json",
390
- schemaVersion: 1,
391
- ide: ide.name,
392
- packages: {
393
- design: {
394
- variant,
395
- version: manifest.version
396
- }
397
- }
398
- };
399
- await writeProjectConfig(projectRoot, config);
400
- const installedManifest = {
401
- schemaVersion: 1,
402
- installed: [
403
- {
404
- package: DESIGN_PACKAGE,
405
- variant,
406
- version: manifest.version,
407
- installedAt: (/* @__PURE__ */ new Date()).toISOString(),
408
- resources: result.resources
409
- }
410
- ]
411
- };
412
- await writeInstalledManifest(projectRoot, installedManifest);
413
- logger.success(
414
- `Design system initialized: ${manifest.displayName} v${manifest.version}`
291
+ return parsed.data;
292
+ } catch (err) {
293
+ logger.warn(
294
+ `Failed to parse design pack.lock.json: ${err.message}`
415
295
  );
416
- logger.info(` Variant: ${variant}`);
417
- logger.info(` Resources: ${result.count} files installed`);
418
- logger.info("");
419
- logger.info('Run "teamix-evo design update" to update resources later.');
296
+ return null;
297
+ }
298
+ }
299
+ async function readDesignVariant(projectRoot) {
300
+ const lock = await readDesignPackLock(projectRoot);
301
+ return lock?.variant.name ?? null;
302
+ }
303
+ function getSkillsSourceDir(projectRoot, skillName) {
304
+ const base = path5.join(projectRoot, TEAMIX_DIR2, SKILLS_DIR);
305
+ return skillName ? path5.join(base, skillName) : base;
306
+ }
307
+ async function readSkillsLock(projectRoot) {
308
+ const lockPath = path5.join(
309
+ projectRoot,
310
+ TEAMIX_DIR2,
311
+ SKILLS_DIR,
312
+ SKILLS_LOCK_FILE
313
+ );
314
+ const raw = await readFileOrNull(lockPath);
315
+ if (raw === null) return null;
316
+ try {
317
+ const data = JSON.parse(raw);
318
+ const result = validateSkillsLock(data);
319
+ if (!result.success) {
320
+ logger.warn(`Invalid skills manifest.lock.json: ${result.error}`);
321
+ return null;
322
+ }
323
+ return result.data;
420
324
  } catch (err) {
421
- logger.error(`Failed to initialize: ${err.message}`);
422
- logger.debug(err.stack ?? "");
423
- process.exitCode = 1;
325
+ logger.warn(
326
+ `Failed to parse skills manifest.lock.json: ${err.message}`
327
+ );
328
+ return null;
424
329
  }
425
- });
330
+ }
331
+ async function writeSkillsLock(projectRoot, lock) {
332
+ const lockPath = path5.join(
333
+ projectRoot,
334
+ TEAMIX_DIR2,
335
+ SKILLS_DIR,
336
+ SKILLS_LOCK_FILE
337
+ );
338
+ await writeFileSafe(lockPath, JSON.stringify(lock, null, 2) + "\n");
339
+ logger.debug(`Wrote skills lock \u2192 ${lockPath}`);
340
+ }
426
341
 
427
- // src/commands/design/update.ts
428
- import { Command as Command2 } from "commander";
342
+ // src/core/skills-client.ts
343
+ import * as path6 from "path";
344
+ import * as fs2 from "fs/promises";
345
+ import { createRequire } from "module";
346
+ import { loadSkillsPackageManifest } from "@teamix-evo/registry";
347
+ var require2 = createRequire(import.meta.url);
348
+ function resolvePackageRoot(packageName) {
349
+ const pkgJsonPath = require2.resolve(`${packageName}/package.json`);
350
+ return path6.dirname(pkgJsonPath);
351
+ }
352
+ async function loadSkillsData(packageName) {
353
+ const packageRoot = resolvePackageRoot(packageName);
354
+ logger.debug(`Resolved skills package root: ${packageRoot}`);
355
+ const manifest = await loadSkillsPackageManifest(packageRoot);
356
+ let data = {};
357
+ const dataPath = path6.join(packageRoot, "_data.json");
358
+ try {
359
+ const raw = await fs2.readFile(dataPath, "utf-8");
360
+ data = JSON.parse(raw);
361
+ } catch (err) {
362
+ if (err.code !== "ENOENT") {
363
+ throw err;
364
+ }
365
+ logger.debug(`No _data.json found at ${dataPath}, using empty data`);
366
+ }
367
+ return { manifest, data, packageRoot };
368
+ }
429
369
 
430
- // src/core/updater.ts
431
- init_fs();
432
- import * as path5 from "path";
370
+ // src/core/skills-installer.ts
371
+ import * as path8 from "path";
433
372
  import * as fs5 from "fs/promises";
434
- import {
435
- getUpdateAction,
436
- replaceManagedRegion
437
- } from "@teamix-evo/registry";
438
- init_logger();
439
- async function updateResources(options) {
440
- const {
441
- projectRoot,
442
- manifest,
443
- data,
444
- variantDir,
445
- packageRoot,
446
- installedManifest,
447
- packageName
448
- } = options;
449
- const updatedResources = [];
450
- const summary = { overwritten: 0, managed: 0, skipped: 0, created: 0 };
451
- const installedPkg = installedManifest.installed.find(
452
- (p) => p.package === packageName && p.variant === manifest.variant
453
- );
454
- const installedMap = /* @__PURE__ */ new Map();
455
- if (installedPkg) {
456
- for (const res of installedPkg.resources) {
457
- installedMap.set(res.id, res);
373
+ import { replaceManagedRegion } from "@teamix-evo/registry";
374
+
375
+ // src/utils/template.ts
376
+ import Handlebars from "handlebars";
377
+ import * as fs3 from "fs/promises";
378
+ Handlebars.registerHelper("lowercase", (str) => {
379
+ return typeof str === "string" ? str.toLowerCase() : String(str ?? "").toLowerCase();
380
+ });
381
+ var compiledCache = /* @__PURE__ */ new Map();
382
+ var MAX_CACHE_SIZE = 64;
383
+ function getCompiledTemplate(templateContent) {
384
+ let compiled = compiledCache.get(templateContent);
385
+ if (!compiled) {
386
+ if (compiledCache.size >= MAX_CACHE_SIZE) {
387
+ const firstKey = compiledCache.keys().next().value;
388
+ compiledCache.delete(firstKey);
458
389
  }
390
+ compiled = Handlebars.compile(templateContent, { noEscape: true });
391
+ compiledCache.set(templateContent, compiled);
459
392
  }
460
- for (const resource of manifest.resources) {
461
- if (resource.recursive) {
462
- const results = await updateRecursiveResource(
463
- resource,
464
- projectRoot,
465
- data,
466
- variantDir,
467
- packageRoot,
468
- installedMap,
469
- summary
470
- );
471
- updatedResources.push(...results);
472
- } else {
473
- const result = await updateSingleResource(
474
- resource,
475
- projectRoot,
476
- data,
477
- variantDir,
478
- packageRoot,
479
- installedMap,
480
- summary
481
- );
482
- updatedResources.push(result);
483
- }
484
- }
485
- return { resources: updatedResources, summary };
486
- }
487
- async function updateSingleResource(resource, projectRoot, data, variantDir, packageRoot, installedMap, summary) {
488
- const targetPath = path5.join(projectRoot, resource.target);
489
- const exists = await fileExists(targetPath);
490
- const installed = installedMap.get(resource.id);
491
- const sourcePath = resolveSourcePath2(resource.source, variantDir, packageRoot);
492
- let newContent;
493
- if (resource.template) {
494
- const templateContent = await loadTemplateFile(sourcePath);
495
- newContent = renderTemplate(templateContent, data);
496
- } else {
497
- newContent = await fs5.readFile(sourcePath, "utf-8");
393
+ return compiled;
394
+ }
395
+ function renderTemplate(templateContent, data) {
396
+ const compiled = getCompiledTemplate(templateContent);
397
+ return compiled(data);
398
+ }
399
+ async function loadTemplateFile(filePath) {
400
+ return fs3.readFile(filePath, "utf-8");
401
+ }
402
+
403
+ // src/utils/path.ts
404
+ import * as path7 from "path";
405
+ import * as fs4 from "fs/promises";
406
+ async function walkDir(dir) {
407
+ const files = [];
408
+ const entries = await fs4.readdir(dir, { withFileTypes: true });
409
+ for (const entry of entries) {
410
+ const fullPath = path7.join(dir, entry.name);
411
+ if (entry.isDirectory()) {
412
+ files.push(...await walkDir(fullPath));
413
+ } else if (entry.isFile()) {
414
+ files.push(fullPath);
415
+ }
498
416
  }
499
- const newHash = computeHash(newContent);
500
- const action = getUpdateAction(resource.updateStrategy, {
501
- exists,
502
- hash: newHash,
503
- currentHash: installed?.hash
504
- });
505
- switch (action) {
506
- case "skip": {
507
- logger.debug(` Skip: ${resource.target} (${resource.updateStrategy})`);
508
- summary.skipped++;
509
- return installed ?? {
510
- id: resource.id,
511
- target: resource.target,
512
- hash: newHash,
513
- strategy: resource.updateStrategy
514
- };
417
+ return files;
418
+ }
419
+
420
+ // src/core/skills-installer.ts
421
+ async function installSkills(options) {
422
+ const { manifest, ides, scope, onlyIds } = options;
423
+ const installed = [];
424
+ const targets = manifest.skills.filter(
425
+ (s) => !onlyIds || onlyIds.includes(s.id)
426
+ );
427
+ for (const skill of targets) {
428
+ const skillIdes = skill.ides.filter((i) => ides.includes(i));
429
+ if (skillIdes.length === 0) {
430
+ logger.warn(
431
+ `Skill "${skill.name}" supports [${skill.ides.join(
432
+ ","
433
+ )}], no overlap with [${ides.join(",")}]; skipped.`
434
+ );
435
+ continue;
436
+ }
437
+ const sourceRecords = await writeSkillSource(skill, options);
438
+ installed.push(...sourceRecords);
439
+ for (const ide of skillIdes) {
440
+ const mirrorRecords = await mirrorSkillToIde(
441
+ skill,
442
+ ide,
443
+ scope,
444
+ options.projectRoot
445
+ );
446
+ installed.push(...mirrorRecords);
515
447
  }
516
- case "overwrite": {
517
- if (exists) {
518
- await backupFile(targetPath, projectRoot);
448
+ }
449
+ return { resources: installed, count: installed.length };
450
+ }
451
+ async function writeSkillSource(skill, options) {
452
+ const { data, packageRoot, projectRoot } = options;
453
+ const sourceAbs = path8.resolve(packageRoot, skill.source);
454
+ const targetDir = getSkillsSourceDir(projectRoot, skill.name);
455
+ const stat4 = await fs5.stat(sourceAbs);
456
+ const records = [];
457
+ if (stat4.isFile()) {
458
+ const targetFile = path8.join(targetDir, "SKILL.md");
459
+ const content = await renderSkillContent(sourceAbs, skill, data);
460
+ await writeFileSafe(targetFile, content);
461
+ records.push(makeSourceRecord(skill, targetFile, content));
462
+ logger.debug(` Wrote source: ${targetFile}`);
463
+ return records;
464
+ }
465
+ await ensureDir(targetDir);
466
+ const entries = await walkDir(sourceAbs);
467
+ for (const entry of entries) {
468
+ const rel2 = path8.relative(sourceAbs, entry);
469
+ let targetFile = path8.join(targetDir, rel2);
470
+ if (skill.template && targetFile.endsWith(".hbs")) {
471
+ targetFile = targetFile.slice(0, -4);
472
+ }
473
+ const content = skill.template && entry.endsWith(".hbs") ? renderTemplate(await loadTemplateFile(entry), { ...data, skill }) : await fs5.readFile(entry, "utf-8");
474
+ await writeFileSafe(targetFile, content);
475
+ const relWritten = path8.relative(targetDir, targetFile);
476
+ records.push(makeSourceRecord(skill, targetFile, content, relWritten));
477
+ logger.debug(` Wrote source: ${targetFile}`);
478
+ }
479
+ return records;
480
+ }
481
+ async function mirrorSkillToIde(skill, ide, scope, projectRoot) {
482
+ const sourceDir = getSkillsSourceDir(projectRoot, skill.name);
483
+ const adapter = getAdapter(ide);
484
+ const targetDir = adapter.getSkillTargetDir(skill.name, scope, projectRoot);
485
+ const records = [];
486
+ const sourceFiles = await walkDir(sourceDir);
487
+ await ensureDir(targetDir);
488
+ for (const src of sourceFiles) {
489
+ const rel2 = path8.relative(sourceDir, src);
490
+ const targetFile = path8.join(targetDir, rel2);
491
+ const content = await fs5.readFile(src, "utf-8");
492
+ await writeFileSafe(targetFile, content);
493
+ records.push(makeMirrorRecord(skill, targetFile, content, ide, scope, rel2));
494
+ logger.debug(` Mirrored ${ide}:${scope}: ${targetFile}`);
495
+ }
496
+ return records;
497
+ }
498
+ async function renderSkillContent(sourceAbs, skill, data) {
499
+ if (skill.template ?? sourceAbs.endsWith(".hbs")) {
500
+ const tpl = await loadTemplateFile(sourceAbs);
501
+ return renderTemplate(tpl, { ...data, skill });
502
+ }
503
+ return fs5.readFile(sourceAbs, "utf-8");
504
+ }
505
+ function makeSourceRecord(skill, targetAbs, content, rel2) {
506
+ const id = rel2 ? `${skill.id}:source:${rel2}` : `${skill.id}:source`;
507
+ return {
508
+ id,
509
+ target: targetAbs,
510
+ hash: computeHash(content),
511
+ strategy: skill.updateStrategy
512
+ };
513
+ }
514
+ function makeMirrorRecord(skill, targetAbs, content, ide, scope, rel2) {
515
+ const id = rel2 && rel2 !== "SKILL.md" ? `${skill.id}:${rel2}` : skill.id;
516
+ return {
517
+ id,
518
+ target: targetAbs,
519
+ hash: computeHash(content),
520
+ strategy: skill.updateStrategy,
521
+ ide,
522
+ scope
523
+ };
524
+ }
525
+ async function updateSkills(options) {
526
+ const { manifest, ides, scope, projectRoot } = options;
527
+ const summary = { overwritten: 0, managed: 0, skipped: 0, created: 0 };
528
+ const updated = [];
529
+ for (const skill of manifest.skills) {
530
+ const skillIdes = skill.ides.filter((i) => ides.includes(i));
531
+ if (skillIdes.length === 0) continue;
532
+ const sourceRecords = await rewriteSkillSource(
533
+ skill,
534
+ options,
535
+ summary
536
+ );
537
+ updated.push(...sourceRecords);
538
+ for (const ide of skillIdes) {
539
+ const mirrorRecords = await mirrorSkillToIde(
540
+ skill,
541
+ ide,
542
+ scope,
543
+ projectRoot
544
+ );
545
+ updated.push(...mirrorRecords);
546
+ }
547
+ }
548
+ return { resources: updated, summary };
549
+ }
550
+ async function rewriteSkillSource(skill, options, summary) {
551
+ const { data, packageRoot, projectRoot } = options;
552
+ const sourceAbs = path8.resolve(packageRoot, skill.source);
553
+ const targetDir = getSkillsSourceDir(projectRoot, skill.name);
554
+ const stat4 = await fs5.stat(sourceAbs);
555
+ if (!stat4.isFile()) {
556
+ await ensureDir(targetDir);
557
+ const entries = await walkDir(sourceAbs);
558
+ const records = [];
559
+ for (const entry of entries) {
560
+ const rel2 = path8.relative(sourceAbs, entry);
561
+ let targetFile2 = path8.join(targetDir, rel2);
562
+ if (skill.template && targetFile2.endsWith(".hbs")) {
563
+ targetFile2 = targetFile2.slice(0, -4);
564
+ }
565
+ const content = skill.template && entry.endsWith(".hbs") ? renderTemplate(await loadTemplateFile(entry), { ...data, skill }) : await fs5.readFile(entry, "utf-8");
566
+ const exists2 = await fileExists(targetFile2);
567
+ if (exists2) {
568
+ await backupFile(targetFile2, projectRoot);
519
569
  summary.overwritten++;
520
570
  } else {
521
571
  summary.created++;
522
572
  }
523
- await writeFileSafe(targetPath, newContent);
524
- logger.debug(` ${exists ? "Overwrite" : "Create"}: ${resource.target}`);
525
- return {
526
- id: resource.id,
527
- target: resource.target,
528
- hash: newHash,
529
- strategy: resource.updateStrategy
530
- };
573
+ await writeFileSafe(targetFile2, content);
574
+ const relWritten = path8.relative(targetDir, targetFile2);
575
+ records.push(makeSourceRecord(skill, targetFile2, content, relWritten));
531
576
  }
532
- case "managed-update": {
533
- const currentContent = await readFileOrNull(targetPath);
534
- if (currentContent === null) {
535
- await writeFileSafe(targetPath, newContent);
536
- summary.created++;
537
- return {
538
- id: resource.id,
539
- target: resource.target,
540
- hash: computeHash(newContent),
541
- strategy: resource.updateStrategy
542
- };
543
- }
544
- let updatedContent = currentContent;
545
- const regionIds = resource.managedRegions ?? [];
546
- for (const regionId of regionIds) {
547
- const regionPattern = new RegExp(
548
- `<!-- teamix-evo:managed:start id="${escapeRegExp(regionId)}" -->([\\s\\S]*?)<!-- teamix-evo:managed:end id="${escapeRegExp(regionId)}" -->`
577
+ return records;
578
+ }
579
+ const targetFile = path8.join(targetDir, "SKILL.md");
580
+ const newContent = await renderSkillContent(sourceAbs, skill, data);
581
+ const exists = await fileExists(targetFile);
582
+ if (skill.updateStrategy === "frozen") {
583
+ if (exists) {
584
+ summary.skipped++;
585
+ const current2 = await readFileOrNull(targetFile) ?? newContent;
586
+ return [makeSourceRecord(skill, targetFile, current2)];
587
+ }
588
+ await writeFileSafe(targetFile, newContent);
589
+ summary.created++;
590
+ return [makeSourceRecord(skill, targetFile, newContent)];
591
+ }
592
+ if (skill.updateStrategy === "regenerable" || !exists) {
593
+ if (exists) {
594
+ await backupFile(targetFile, projectRoot);
595
+ summary.overwritten++;
596
+ } else {
597
+ summary.created++;
598
+ }
599
+ await writeFileSafe(targetFile, newContent);
600
+ return [makeSourceRecord(skill, targetFile, newContent)];
601
+ }
602
+ const current = await readFileOrNull(targetFile);
603
+ let merged = current ?? newContent;
604
+ for (const regionId of skill.managedRegions ?? []) {
605
+ const re = new RegExp(
606
+ `<!-- teamix-evo:managed:start id="${escapeRegExp(
607
+ regionId
608
+ )}" -->([\\s\\S]*?)<!-- teamix-evo:managed:end id="${escapeRegExp(
609
+ regionId
610
+ )}" -->`
611
+ );
612
+ const match = newContent.match(re);
613
+ if (match) {
614
+ const region = match[1].replace(/^\n/, "").replace(/\n$/, "");
615
+ try {
616
+ merged = replaceManagedRegion(merged, regionId, region);
617
+ } catch {
618
+ logger.warn(
619
+ `Managed region "${regionId}" not found in ${targetFile}. Skipped.`
549
620
  );
550
- const match = newContent.match(regionPattern);
551
- if (match) {
552
- const regionContent = match[1].replace(/^\n/, "").replace(/\n$/, "");
553
- try {
554
- updatedContent = replaceManagedRegion(
555
- updatedContent,
556
- regionId,
557
- regionContent
558
- );
559
- } catch {
560
- logger.warn(
561
- `Managed region "${regionId}" not found in ${resource.target}. Skipping region.`
562
- );
563
- }
564
- }
565
621
  }
566
- await backupFile(targetPath, projectRoot);
567
- await writeFileSafe(targetPath, updatedContent);
568
- summary.managed++;
569
- return {
570
- id: resource.id,
571
- target: resource.target,
572
- hash: computeHash(updatedContent),
573
- strategy: resource.updateStrategy
574
- };
575
622
  }
576
- default:
577
- summary.skipped++;
578
- return installed ?? {
579
- id: resource.id,
580
- target: resource.target,
581
- hash: newHash,
582
- strategy: resource.updateStrategy
583
- };
584
623
  }
624
+ await backupFile(targetFile, projectRoot);
625
+ await writeFileSafe(targetFile, merged);
626
+ summary.managed++;
627
+ return [makeSourceRecord(skill, targetFile, merged)];
585
628
  }
586
- async function updateRecursiveResource(resource, projectRoot, data, variantDir, packageRoot, installedMap, summary) {
587
- const sourcePath = resolveSourcePath2(resource.source, variantDir, packageRoot);
588
- const targetDir = path5.join(projectRoot, resource.target);
589
- const results = [];
590
- if (resource.updateStrategy === "frozen") {
591
- const anyInstalled = [...installedMap.keys()].some(
592
- (k) => k.startsWith(`${resource.id}:`)
593
- );
594
- if (anyInstalled) {
595
- summary.skipped++;
596
- for (const [id, res] of installedMap) {
597
- if (id.startsWith(`${resource.id}:`)) {
598
- results.push(res);
599
- }
629
+ function escapeRegExp(str) {
630
+ return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
631
+ }
632
+ async function syncSkillsToIdes(options) {
633
+ const { projectRoot, skills, ides, scope, onlyIds } = options;
634
+ const out = [];
635
+ const targets = skills.filter((s) => !onlyIds || onlyIds.includes(s.id));
636
+ for (const skill of targets) {
637
+ const sourceDir = getSkillsSourceDir(projectRoot, skill.name);
638
+ if (!await fileExists(sourceDir)) {
639
+ logger.warn(
640
+ `Skill "${skill.id}" has no source at ${sourceDir}; skipped.`
641
+ );
642
+ continue;
643
+ }
644
+ for (const ide of ides) {
645
+ const adapter = getAdapter(ide);
646
+ const targetDir = adapter.getSkillTargetDir(
647
+ skill.name,
648
+ scope,
649
+ projectRoot
650
+ );
651
+ await ensureDir(targetDir);
652
+ const sourceFiles = await walkDir(sourceDir);
653
+ for (const src of sourceFiles) {
654
+ const rel2 = path8.relative(sourceDir, src);
655
+ const targetFile = path8.join(targetDir, rel2);
656
+ const content = await fs5.readFile(src, "utf-8");
657
+ await writeFileSafe(targetFile, content);
658
+ out.push({
659
+ id: rel2 === "SKILL.md" ? skill.id : `${skill.id}:${rel2}`,
660
+ target: targetFile,
661
+ hash: computeHash(content),
662
+ strategy: skill.updateStrategy,
663
+ ide,
664
+ scope
665
+ });
600
666
  }
601
- return results;
602
667
  }
603
668
  }
604
- const { ensureDir: ensureDir2 } = await Promise.resolve().then(() => (init_fs(), fs_exports));
605
- await ensureDir2(targetDir);
606
- const entries = await walkDir2(sourcePath);
607
- for (const entry of entries) {
608
- const relPath = path5.relative(sourcePath, entry);
609
- let targetFile = path5.join(targetDir, relPath);
610
- if (resource.template && targetFile.endsWith(".hbs")) {
611
- targetFile = targetFile.slice(0, -4);
669
+ return { resources: out, count: out.length };
670
+ }
671
+ async function removeSkillFiles(records) {
672
+ const removed = [];
673
+ for (const r of records) {
674
+ try {
675
+ await fs5.unlink(r.target);
676
+ removed.push(r.target);
677
+ } catch (err) {
678
+ if (err.code !== "ENOENT") {
679
+ logger.warn(`Failed to remove ${r.target}: ${err.message}`);
680
+ }
612
681
  }
613
- let content;
614
- if (resource.template && entry.endsWith(".hbs")) {
615
- const templateContent = await loadTemplateFile(entry);
616
- content = renderTemplate(templateContent, data);
617
- } else {
618
- content = await fs5.readFile(entry, "utf-8");
682
+ }
683
+ const parents = new Set(records.map((r) => path8.dirname(r.target)));
684
+ for (const dir of parents) {
685
+ try {
686
+ const entries = await fs5.readdir(dir);
687
+ if (entries.length === 0) await fs5.rmdir(dir);
688
+ } catch {
619
689
  }
620
- await writeFileSafe(targetFile, content);
621
- const hash = computeHash(content);
622
- const targetRel = path5.relative(projectRoot, targetFile);
623
- results.push({
624
- id: `${resource.id}:${relPath}`,
625
- target: targetRel,
626
- hash,
627
- strategy: resource.updateStrategy
628
- });
629
- summary.overwritten++;
630
690
  }
631
- return results;
691
+ return removed;
692
+ }
693
+
694
+ // src/core/skills-add.ts
695
+ var DEFAULT_SKILLS_PACKAGE = "@teamix-evo/skills";
696
+ var FLAT_VARIANT = "_flat";
697
+ async function runSkillsAdd(options) {
698
+ const { projectRoot, names: requestedNames } = options;
699
+ const packageName = options.packageName ?? DEFAULT_SKILLS_PACKAGE;
700
+ const ideIdent = options.ide ?? "qoder";
701
+ const isIncremental = !!requestedNames && requestedNames.length > 0;
702
+ await ensureTeamixDir(projectRoot);
703
+ const existingConfig = await readProjectConfig(projectRoot);
704
+ const existingSkillsCfg = existingConfig?.packages?.skills;
705
+ if (!isIncremental && existingSkillsCfg) {
706
+ return { status: "already-added" };
707
+ }
708
+ const ides = options.ides && options.ides.length > 0 ? [...options.ides] : existingSkillsCfg?.ides ? [...existingSkillsCfg.ides] : [];
709
+ const scope = options.scope ?? existingSkillsCfg?.scope;
710
+ if (ides.length === 0) {
711
+ throw new Error("At least one IDE must be selected.");
712
+ }
713
+ if (!scope) {
714
+ throw new Error("Scope must be specified (project | global).");
715
+ }
716
+ const { manifest, data, packageRoot } = await loadSkillsData(packageName);
717
+ const currentDesignVariant = await readDesignVariant(projectRoot);
718
+ if (isIncremental) {
719
+ const known = new Set(manifest.skills.map((s) => s.id));
720
+ const unknown = requestedNames.filter((n) => !known.has(n));
721
+ if (unknown.length > 0) {
722
+ const available = [...known].join(", ");
723
+ throw new Error(
724
+ `Unknown skill id(s): ${unknown.join(", ")}. Available: ${available || "(none)"}.`
725
+ );
726
+ }
727
+ }
728
+ const existingInstalled = await readInstalledManifest(projectRoot);
729
+ const existingPkg = existingInstalled?.installed.find(
730
+ (p) => p.package === packageName
731
+ );
732
+ const existingLock = await readSkillsLock(projectRoot);
733
+ const existingSkillIds = /* @__PURE__ */ new Set([
734
+ ...Object.keys(existingLock?.skills ?? {}),
735
+ // Legacy fallback: pre-ADR-0013 installs only had manifest.json. Derive
736
+ // skill ids by stripping the trailing :source / :sub-file suffix.
737
+ ...(existingPkg?.resources ?? []).map((r) => r.id.split(":")[0])
738
+ ]);
739
+ let onlyIds;
740
+ let skippedSkillIds;
741
+ if (isIncremental) {
742
+ skippedSkillIds = requestedNames.filter((n) => existingSkillIds.has(n));
743
+ onlyIds = requestedNames.filter((n) => !existingSkillIds.has(n));
744
+ } else {
745
+ skippedSkillIds = [];
746
+ onlyIds = manifest.skills.filter((s) => {
747
+ if (!s.variant) return true;
748
+ if (!currentDesignVariant) {
749
+ logger.debug(
750
+ `Skipping variant-bound skill "${s.id}" (variant=${s.variant}): no design pack installed; will be picked up when "design init" runs.`
751
+ );
752
+ return false;
753
+ }
754
+ if (s.variant !== currentDesignVariant) {
755
+ logger.debug(
756
+ `Skipping variant-bound skill "${s.id}" (variant=${s.variant}): current design variant is "${currentDesignVariant}".`
757
+ );
758
+ return false;
759
+ }
760
+ return true;
761
+ }).map((s) => s.id);
762
+ }
763
+ if (isIncremental && onlyIds.length === 0) {
764
+ return {
765
+ status: "installed",
766
+ packageName,
767
+ version: existingSkillsCfg?.version ?? manifest.version,
768
+ ides,
769
+ scope,
770
+ skillCount: 0,
771
+ fileCount: 0,
772
+ resources: [],
773
+ addedSkillIds: [],
774
+ skippedSkillIds
775
+ };
776
+ }
777
+ const result = await installSkills({
778
+ projectRoot,
779
+ manifest,
780
+ data,
781
+ packageRoot,
782
+ ides,
783
+ scope,
784
+ onlyIds
785
+ });
786
+ const config = existingConfig ?? {
787
+ $schema: "https://teamix-evo.dev/schema/config/v1.json",
788
+ schemaVersion: 1,
789
+ ide: ideIdent,
790
+ packages: {}
791
+ };
792
+ config.packages.skills = {
793
+ variant: FLAT_VARIANT,
794
+ version: manifest.version,
795
+ ides,
796
+ scope
797
+ };
798
+ await writeProjectConfig(projectRoot, config);
799
+ const installedAt = (/* @__PURE__ */ new Date()).toISOString();
800
+ const installedManifest = existingInstalled ?? {
801
+ schemaVersion: 1,
802
+ installed: []
803
+ };
804
+ const idx = installedManifest.installed.findIndex(
805
+ (p) => p.package === packageName
806
+ );
807
+ const mergedResources = mergeInstalledResources(
808
+ existingPkg?.resources ?? [],
809
+ result.resources
810
+ );
811
+ const entry = {
812
+ package: packageName,
813
+ variant: FLAT_VARIANT,
814
+ version: manifest.version,
815
+ installedAt,
816
+ resources: mergedResources
817
+ };
818
+ if (idx >= 0) installedManifest.installed[idx] = entry;
819
+ else installedManifest.installed.push(entry);
820
+ await writeInstalledManifest(projectRoot, installedManifest);
821
+ const lock = existingLock ?? {
822
+ schemaVersion: 1,
823
+ skills: {}
824
+ };
825
+ for (const skillId of onlyIds) {
826
+ const skillDef = manifest.skills.find((s) => s.id === skillId);
827
+ if (!skillDef) continue;
828
+ const mirroredTo = skillDef.ides.filter((i) => ides.includes(i));
829
+ lock.skills[skillId] = {
830
+ version: skillDef.version,
831
+ from: packageName,
832
+ installedAt,
833
+ scope,
834
+ mirroredTo
835
+ };
836
+ }
837
+ await writeSkillsLock(projectRoot, lock);
838
+ return {
839
+ status: "installed",
840
+ packageName,
841
+ version: manifest.version,
842
+ ides,
843
+ scope,
844
+ skillCount: onlyIds.length,
845
+ fileCount: result.count,
846
+ resources: result.resources,
847
+ addedSkillIds: onlyIds,
848
+ skippedSkillIds
849
+ };
632
850
  }
633
- function resolveSourcePath2(source, variantDir, packageRoot) {
634
- if (source.startsWith("_template/")) {
635
- return path5.join(packageRoot, source);
851
+ function mergeInstalledResources(existing, next) {
852
+ const map = /* @__PURE__ */ new Map();
853
+ const key = (r) => `${r.id}|${r.ide ?? ""}|${r.scope ?? ""}`;
854
+ for (const r of existing) map.set(key(r), r);
855
+ for (const r of next) map.set(key(r), r);
856
+ return [...map.values()];
857
+ }
858
+
859
+ // src/core/design-init.ts
860
+ var BASELINE_DESIGN_RULES_SKILL = "teamix-evo-design-rules";
861
+ var DEFAULT_SKILLS_PACKAGE2 = "@teamix-evo/skills";
862
+ var DEFAULT_AUTO_SKILL_IDES = ["qoder", "claude"];
863
+ var DEFAULT_AUTO_SKILL_SCOPE = "project";
864
+ var DEFAULT_DESIGN_PACKAGE = "@teamix-evo/design";
865
+ var require3 = createRequire2(import.meta.url);
866
+ async function runDesignInit(options) {
867
+ const { projectRoot, variant, ide } = options;
868
+ const packageName = options.packageName ?? DEFAULT_DESIGN_PACKAGE;
869
+ await ensureTeamixDir(projectRoot);
870
+ const existingConfig = await readProjectConfig(projectRoot);
871
+ if (existingConfig?.packages?.design) {
872
+ return {
873
+ status: "already-initialized",
874
+ existingVariant: existingConfig.packages.design.variant
875
+ };
876
+ }
877
+ const packageRoot = options.packageRoot ?? resolveDesignPackageRoot(packageName);
878
+ const catalog = await loadDesignPackageManifest(packageRoot);
879
+ const variantEntry = catalog.variants.find((v) => v.name === variant);
880
+ if (!variantEntry) {
881
+ const known = catalog.variants.map((v) => v.name).join(", ");
882
+ throw new Error(
883
+ `Design variant not found: "${variant}". Known variants: ${known || "(none)"}. Hint: run "teamix-evo design list-variants" to see all.`
884
+ );
885
+ }
886
+ const defaultDir = path9.join(packageRoot, "default");
887
+ const variantDir = path9.join(packageRoot, "variants", variant);
888
+ const defaultPack = await loadDesignPack(defaultDir);
889
+ const variantPack = await loadDesignPack(variantDir);
890
+ const merge = await mergeDefaultAndVariant(defaultDir, variantDir);
891
+ const installed = [];
892
+ for (const file of merge.files) {
893
+ const result = await installPackFile(file, projectRoot);
894
+ if (result) installed.push(result);
636
895
  }
637
- return path5.join(variantDir, source);
896
+ const lock = {
897
+ schemaVersion: 1,
898
+ default: { version: defaultPack.version, from: packageName },
899
+ variant: {
900
+ name: variantPack.name,
901
+ displayName: variantPack.displayName,
902
+ version: variantPack.version,
903
+ from: packageName
904
+ },
905
+ linked: variantPack.linked,
906
+ installedAt: (/* @__PURE__ */ new Date()).toISOString()
907
+ };
908
+ await writeFileSafe(
909
+ path9.join(projectRoot, ".teamix-evo", "design", "pack.lock.json"),
910
+ JSON.stringify(lock, null, 2) + "\n"
911
+ );
912
+ const config = {
913
+ $schema: "https://teamix-evo.dev/schema/config/v1.json",
914
+ schemaVersion: 1,
915
+ ide,
916
+ packages: {
917
+ design: {
918
+ variant,
919
+ version: variantPack.version,
920
+ tailwind: "v4"
921
+ }
922
+ }
923
+ };
924
+ await writeProjectConfig(projectRoot, config);
925
+ const installedManifest = {
926
+ schemaVersion: 1,
927
+ installed: [
928
+ {
929
+ package: packageName,
930
+ variant,
931
+ version: variantPack.version,
932
+ installedAt: (/* @__PURE__ */ new Date()).toISOString(),
933
+ resources: installed
934
+ }
935
+ ]
936
+ };
937
+ await writeInstalledManifest(projectRoot, installedManifest);
938
+ const skills = await tryAutoInstallVariantSkills({
939
+ projectRoot,
940
+ variant,
941
+ ide
942
+ });
943
+ return {
944
+ status: "installed",
945
+ packageName,
946
+ variant,
947
+ version: variantPack.version,
948
+ count: installed.length,
949
+ resources: installed,
950
+ merge: {
951
+ overrides: merge.overrides,
952
+ variantAdds: merge.variantAdds,
953
+ defaultPassThrough: merge.defaultPassThrough
954
+ },
955
+ skills
956
+ };
638
957
  }
639
- async function walkDir2(dir) {
640
- const files = [];
641
- const entries = await fs5.readdir(dir, { withFileTypes: true });
642
- for (const entry of entries) {
643
- const fullPath = path5.join(dir, entry.name);
644
- if (entry.isDirectory()) {
645
- files.push(...await walkDir2(fullPath));
646
- } else if (entry.isFile()) {
647
- files.push(fullPath);
958
+ async function tryAutoInstallVariantSkills(args) {
959
+ const { projectRoot, variant, ide } = args;
960
+ const variantSkillId = `${BASELINE_DESIGN_RULES_SKILL}-${variant}`;
961
+ const desired = [BASELINE_DESIGN_RULES_SKILL, variantSkillId];
962
+ let manifestSkillIds;
963
+ try {
964
+ const { manifest } = await loadSkillsData(DEFAULT_SKILLS_PACKAGE2);
965
+ manifestSkillIds = new Set(manifest.skills.map((s) => s.id));
966
+ } catch (err) {
967
+ logger.warn(
968
+ `Skipping skills auto-install: could not load skills manifest (${err.message}).`
969
+ );
970
+ return {
971
+ attempted: [],
972
+ addedSkillIds: [],
973
+ skippedSkillIds: [],
974
+ missing: desired
975
+ };
976
+ }
977
+ const present = desired.filter((id) => manifestSkillIds.has(id));
978
+ const missing = desired.filter((id) => !manifestSkillIds.has(id));
979
+ if (missing.length > 0) {
980
+ logger.warn(
981
+ `Skills auto-install: not found in manifest, skipping: ${missing.join(", ")}.`
982
+ );
983
+ }
984
+ if (present.length === 0) {
985
+ return {
986
+ attempted: desired,
987
+ addedSkillIds: [],
988
+ skippedSkillIds: [],
989
+ missing
990
+ };
991
+ }
992
+ try {
993
+ const result = await runSkillsAdd({
994
+ projectRoot,
995
+ names: present,
996
+ ides: DEFAULT_AUTO_SKILL_IDES,
997
+ scope: DEFAULT_AUTO_SKILL_SCOPE,
998
+ ide
999
+ });
1000
+ if (result.status !== "installed") {
1001
+ return {
1002
+ attempted: desired,
1003
+ addedSkillIds: [],
1004
+ skippedSkillIds: present,
1005
+ missing
1006
+ };
648
1007
  }
1008
+ return {
1009
+ attempted: desired,
1010
+ addedSkillIds: result.addedSkillIds,
1011
+ skippedSkillIds: result.skippedSkillIds,
1012
+ missing
1013
+ };
1014
+ } catch (err) {
1015
+ logger.warn(
1016
+ `Skills auto-install failed (continuing): ${err.message}`
1017
+ );
1018
+ return {
1019
+ attempted: desired,
1020
+ addedSkillIds: [],
1021
+ skippedSkillIds: [],
1022
+ missing
1023
+ };
649
1024
  }
650
- return files;
651
1025
  }
652
- function escapeRegExp(str) {
653
- return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
1026
+ async function installPackFile(file, projectRoot) {
1027
+ const cls = classifyPackFile(file.relPath);
1028
+ if (cls === null) return null;
1029
+ const targetAbs = path9.join(projectRoot, cls.target);
1030
+ if (cls.isFrozen && await fileExists(targetAbs)) {
1031
+ const existing = await fs6.readFile(targetAbs, "utf-8");
1032
+ return {
1033
+ id: `pack:${file.relPath}`,
1034
+ target: cls.target,
1035
+ hash: computeHash(existing),
1036
+ strategy: cls.strategy
1037
+ };
1038
+ }
1039
+ const content = await fs6.readFile(file.sourcePath, "utf-8");
1040
+ await writeFileSafe(targetAbs, content);
1041
+ return {
1042
+ id: `pack:${file.relPath}`,
1043
+ target: cls.target,
1044
+ hash: computeHash(content),
1045
+ strategy: cls.strategy
1046
+ };
1047
+ }
1048
+ function resolveDesignPackageRoot(packageName) {
1049
+ const pkgJson = require3.resolve(`${packageName}/package.json`);
1050
+ return path9.dirname(pkgJson);
1051
+ }
1052
+ async function listDesignVariants(packageName = DEFAULT_DESIGN_PACKAGE, packageRoot) {
1053
+ const root = packageRoot ?? resolveDesignPackageRoot(packageName);
1054
+ const catalog = await loadDesignPackageManifest(root);
1055
+ return {
1056
+ packageName,
1057
+ defaultDescription: catalog.default.description,
1058
+ variants: catalog.variants.map((v) => ({
1059
+ name: v.name,
1060
+ displayName: v.displayName,
1061
+ version: v.version,
1062
+ description: v.description,
1063
+ linked: v.linked
1064
+ }))
1065
+ };
654
1066
  }
655
1067
 
656
- // src/commands/design/update.ts
657
- init_logger();
658
- var DESIGN_PACKAGE2 = "@teamix-evo/design";
659
- var updateCommand = new Command2("update").description("\u66F4\u65B0\u8BBE\u8BA1\u4F53\u7CFB\u8D44\u6E90").action(async () => {
1068
+ // src/commands/design/init.ts
1069
+ var initCommand = new Command("init").description("\u521D\u59CB\u5316\u8BBE\u8BA1\u4F53\u7CFB\u8D44\u6E90\uFF08\u5FC5\u987B\u663E\u5F0F\u6307\u5B9A\u4E1A\u52A1\u53D8\u4F53\uFF09").argument("<variant>", '\u4E1A\u52A1\u53D8\u4F53\u540D\u79F0\uFF08\u5982 "opentrek"\u3001"uni-manager"\uFF09').action(async (variant) => {
660
1070
  try {
661
1071
  const ide = detectIde();
662
1072
  const projectRoot = ide.getProjectRoot();
663
- logger.info("Updating design system resources...");
664
- const config = await readProjectConfig(projectRoot);
665
- if (!config?.packages?.design) {
666
- logger.error(
667
- 'Design system not initialized. Run "teamix-evo design init" first.'
668
- );
669
- process.exitCode = 1;
670
- return;
671
- }
672
- const { variant, version: currentVersion } = config.packages.design;
673
- const installedManifest = await readInstalledManifest(projectRoot);
674
- if (!installedManifest) {
675
- logger.error(
676
- 'No installed manifest found. Try re-initializing with "teamix-evo design init".'
1073
+ logger.info(`Initializing design system: variant="${variant}"`);
1074
+ logger.debug(`Project root: ${projectRoot}`);
1075
+ logger.debug(`IDE: ${ide.name}`);
1076
+ logger.info(`Loading variant "${variant}"...`);
1077
+ logger.info("Installing resources...");
1078
+ const result = await runDesignInit({
1079
+ projectRoot,
1080
+ variant,
1081
+ ide: ide.name
1082
+ });
1083
+ if (result.status === "already-initialized") {
1084
+ logger.warn(
1085
+ `Design system already initialized (variant: ${result.existingVariant}). Use "teamix-evo design update" to update.`
677
1086
  );
678
- process.exitCode = 1;
679
1087
  return;
680
1088
  }
681
- logger.info(`Loading variant "${variant}" from ${DESIGN_PACKAGE2}...`);
682
- const { manifest, data, variantDir, packageRoot } = await loadVariantData(DESIGN_PACKAGE2, variant);
683
- logger.info(
684
- `Current: v${currentVersion} \u2192 Available: v${manifest.version}`
1089
+ logger.success(
1090
+ `Design system initialized: ${result.packageName} (${result.variant} v${result.version})`
685
1091
  );
686
- const result = await updateResources({
687
- projectRoot,
688
- manifest,
689
- data,
690
- variantDir,
691
- packageRoot,
692
- installedManifest,
693
- packageName: DESIGN_PACKAGE2
694
- });
695
- config.packages.design.version = manifest.version;
696
- await writeProjectConfig(projectRoot, config);
697
- const updatedManifest = { ...installedManifest };
698
- const pkgIdx = updatedManifest.installed.findIndex(
699
- (p) => p.package === DESIGN_PACKAGE2 && p.variant === variant
1092
+ logger.info(` Variant: ${result.variant}`);
1093
+ logger.info(` Tailwind: v4`);
1094
+ logger.info(` Resources: ${result.count} files installed`);
1095
+ logger.info(
1096
+ ` Merge: ${result.merge.overrides.length} overrides / ${result.merge.variantAdds.length} variant-only / ${result.merge.defaultPassThrough.length} default-passthrough`
700
1097
  );
701
- const pkgEntry = {
702
- package: DESIGN_PACKAGE2,
703
- variant,
704
- version: manifest.version,
705
- installedAt: (/* @__PURE__ */ new Date()).toISOString(),
706
- resources: result.resources
707
- };
708
- if (pkgIdx >= 0) {
709
- updatedManifest.installed[pkgIdx] = pkgEntry;
710
- } else {
711
- updatedManifest.installed.push(pkgEntry);
1098
+ if (result.skills) {
1099
+ const { addedSkillIds, skippedSkillIds, missing } = result.skills;
1100
+ if (addedSkillIds.length > 0) {
1101
+ logger.info(
1102
+ ` Skills: auto-installed ${addedSkillIds.join(", ")}`
1103
+ );
1104
+ }
1105
+ if (skippedSkillIds.length > 0) {
1106
+ logger.info(
1107
+ ` Skills: already installed (skipped) ${skippedSkillIds.join(", ")}`
1108
+ );
1109
+ }
1110
+ if (missing.length > 0) {
1111
+ logger.info(
1112
+ ` Skills: not in manifest (skipped) ${missing.join(", ")}`
1113
+ );
1114
+ }
712
1115
  }
713
- await writeInstalledManifest(projectRoot, updatedManifest);
714
- const { summary } = result;
715
- logger.success(
716
- `Design system updated to v${manifest.version}`
1116
+ logger.info("");
1117
+ logger.info('Run "teamix-evo design update" to update resources later.');
1118
+ logger.info(
1119
+ 'Run "teamix-evo design list-variants" to see all available variants.'
717
1120
  );
718
- logger.info(` Created: ${summary.created}`);
719
- logger.info(` Overwritten: ${summary.overwritten}`);
720
- logger.info(` Managed: ${summary.managed}`);
721
- logger.info(` Skipped: ${summary.skipped}`);
722
1121
  } catch (err) {
723
- logger.error(`Failed to update: ${err.message}`);
1122
+ logger.error(`Failed to initialize: ${err.message}`);
724
1123
  logger.debug(err.stack ?? "");
725
1124
  process.exitCode = 1;
726
1125
  }
727
1126
  });
728
1127
 
1128
+ // src/commands/design/update.ts
1129
+ import { Command as Command2 } from "commander";
1130
+ var updateCommand = new Command2("update").description("(v0.7) \u66F4\u65B0\u8BBE\u8BA1\u4F53\u7CFB\u8D44\u6E90 \u2014 \u5F53\u524D\u672A\u5B9E\u73B0,\u89C1 ADR 0010 / PLAN \xA712.6").action(() => {
1131
+ logger.warn(
1132
+ "design update is not yet implemented for the design pack model (default + variants/, ADR 0010)."
1133
+ );
1134
+ logger.info(
1135
+ "Workaround: clean `.teamix-evo/design/` and re-run `teamix-evo design init <variant>`."
1136
+ );
1137
+ logger.info(
1138
+ "Tracking: PLAN \xA712.6 v0.7 \u2014 semantic upgrade flow + tokens.overrides.css preservation."
1139
+ );
1140
+ process.exitCode = 0;
1141
+ });
1142
+
729
1143
  // src/commands/design/list.ts
730
1144
  import { Command as Command3 } from "commander";
731
- init_logger();
732
1145
  var listCommand = new Command3("list").description("\u5217\u51FA\u5DF2\u5B89\u88C5\u7684\u8BBE\u8BA1\u53D8\u4F53").action(async () => {
733
1146
  try {
734
1147
  const ide = detectIde();
@@ -739,11 +1152,11 @@ var listCommand = new Command3("list").description("\u5217\u51FA\u5DF2\u5B89\u88
739
1152
  logger.info('Run "teamix-evo design init [variant]" to get started.');
740
1153
  return;
741
1154
  }
742
- const { variant, version } = config.packages.design;
1155
+ const { variant, version: version2 } = config.packages.design;
743
1156
  logger.info("Installed design system:");
744
1157
  logger.info(` Package: @teamix-evo/design`);
745
1158
  logger.info(` Variant: ${variant}`);
746
- logger.info(` Version: ${version}`);
1159
+ logger.info(` Version: ${version2}`);
747
1160
  logger.info(` IDE: ${config.ide}`);
748
1161
  const manifest = await readInstalledManifest(projectRoot);
749
1162
  if (manifest) {
@@ -763,15 +1176,1631 @@ var listCommand = new Command3("list").description("\u5217\u51FA\u5DF2\u5B89\u88
763
1176
  }
764
1177
  });
765
1178
 
766
- // src/commands/design/index.ts
767
- var designCommand = new Command4("design").description("\u7BA1\u7406\u8BBE\u8BA1\u4F53\u7CFB\u8D44\u6E90");
768
- designCommand.addCommand(initCommand);
769
- designCommand.addCommand(updateCommand);
770
- designCommand.addCommand(listCommand);
1179
+ // src/commands/design/list-variants.ts
1180
+ import { Command as Command4 } from "commander";
1181
+ var listVariantsCommand = new Command4("list-variants").description("\u5217\u51FA @teamix-evo/design \u5305\u5185\u63D0\u4F9B\u7684\u6240\u6709\u53D8\u4F53").action(async () => {
1182
+ try {
1183
+ const result = await listDesignVariants();
1184
+ logger.info(`Available design variants in ${result.packageName}:`);
1185
+ logger.info("");
1186
+ logger.info(" default \u2014 B \u7AEF\u901A\u7528\u57FA\u7EBF(\u59CB\u7EC8\u5185\u7F6E,\u65E0\u9700\u9009\u62E9)");
1187
+ if (result.defaultDescription) {
1188
+ logger.info(` ${result.defaultDescription}`);
1189
+ }
1190
+ logger.info("");
1191
+ if (result.variants.length === 0) {
1192
+ logger.info(" (no variants beyond default)");
1193
+ return;
1194
+ }
1195
+ for (const v of result.variants) {
1196
+ logger.info(` ${v.name} (${v.displayName}) \u2014 v${v.version}`);
1197
+ if (v.description) logger.info(` ${v.description}`);
1198
+ if (v.linked) {
1199
+ const links = [];
1200
+ if (v.linked["biz-ui"]) links.push(`biz-ui: ${v.linked["biz-ui"]}`);
1201
+ if (v.linked.templates) links.push(`templates: ${v.linked.templates}`);
1202
+ if (links.length) logger.info(` linked: ${links.join(" / ")}`);
1203
+ }
1204
+ logger.info("");
1205
+ }
1206
+ logger.info("Install a variant: teamix-evo design init <name>");
1207
+ } catch (err) {
1208
+ logger.error(`Failed to list variants: ${err.message}`);
1209
+ logger.debug(err.stack ?? "");
1210
+ process.exitCode = 1;
1211
+ }
1212
+ });
1213
+
1214
+ // src/commands/design/uninstall.ts
1215
+ import { Command as Command5 } from "commander";
1216
+ import * as fs7 from "fs/promises";
1217
+ import * as path10 from "path";
1218
+ import * as prompts from "@clack/prompts";
1219
+ var DESIGN_PACKAGE = "@teamix-evo/design";
1220
+ var uninstallCommand = new Command5("uninstall").description("\u5378\u8F7D\u8BBE\u8BA1\u4F53\u7CFB\u8D44\u6E90\uFF08\u9ED8\u8BA4\u4F1A\u5220\u9664 frozen / regenerable \u8D44\u6E90\u6587\u4EF6\uFF09").option("-y, --yes", "\u8DF3\u8FC7\u786E\u8BA4").option(
1221
+ "--keep-files",
1222
+ "\u4EC5\u6E05\u7406 .teamix-evo \u4E2D\u7684\u8BB0\u8D26\u4FE1\u606F\uFF0C\u4E0D\u5220\u9664\u5DF2\u843D\u5730\u8D44\u6E90\u6587\u4EF6"
1223
+ ).action(async (opts) => {
1224
+ try {
1225
+ const ide = detectIde();
1226
+ const projectRoot = ide.getProjectRoot();
1227
+ const config = await readProjectConfig(projectRoot);
1228
+ if (!config?.packages?.design) {
1229
+ logger.info("Design system is not installed. Nothing to do.");
1230
+ return;
1231
+ }
1232
+ const installedManifest = await readInstalledManifest(projectRoot);
1233
+ const pkg = installedManifest?.installed.find(
1234
+ (p) => p.package === DESIGN_PACKAGE
1235
+ );
1236
+ const resources = pkg?.resources ?? [];
1237
+ const removable = opts.keepFiles ? [] : resources.filter((r) => r.strategy !== "managed");
1238
+ const kept = resources.length - removable.length;
1239
+ logger.info(
1240
+ `Will remove ${removable.length} file(s); keep ${kept} managed file(s).`
1241
+ );
1242
+ if (!opts.yes) {
1243
+ const confirm4 = await prompts.confirm({
1244
+ message: "\u786E\u8BA4\u5378\u8F7D\u8BBE\u8BA1\u4F53\u7CFB\uFF1F",
1245
+ initialValue: false
1246
+ });
1247
+ if (prompts.isCancel(confirm4) || !confirm4) {
1248
+ logger.info("Cancelled.");
1249
+ return;
1250
+ }
1251
+ }
1252
+ let removed = 0;
1253
+ for (const r of removable) {
1254
+ const target = path10.isAbsolute(r.target) ? r.target : path10.join(projectRoot, r.target);
1255
+ try {
1256
+ await fs7.unlink(target);
1257
+ removed++;
1258
+ } catch (err) {
1259
+ if (err.code !== "ENOENT") {
1260
+ logger.warn(
1261
+ `Failed to remove ${target}: ${err.message}`
1262
+ );
1263
+ }
1264
+ }
1265
+ }
1266
+ if (installedManifest) {
1267
+ installedManifest.installed = installedManifest.installed.filter(
1268
+ (p) => p.package !== DESIGN_PACKAGE
1269
+ );
1270
+ await writeInstalledManifest(projectRoot, installedManifest);
1271
+ }
1272
+ delete config.packages.design;
1273
+ await writeProjectConfig(projectRoot, config);
1274
+ logger.success(`Uninstalled ${DESIGN_PACKAGE}`);
1275
+ logger.info(` Removed: ${removed} files`);
1276
+ if (kept > 0) {
1277
+ logger.info(
1278
+ ` Kept: ${kept} managed files (you may delete manually)`
1279
+ );
1280
+ }
1281
+ } catch (err) {
1282
+ logger.error(`Failed to uninstall: ${err.message}`);
1283
+ logger.debug(err.stack ?? "");
1284
+ process.exitCode = 1;
1285
+ }
1286
+ });
1287
+
1288
+ // src/commands/design/index.ts
1289
+ var designCommand = new Command6("design").description(
1290
+ "\u7BA1\u7406\u8BBE\u8BA1\u4F53\u7CFB\u8D44\u6E90"
1291
+ );
1292
+ designCommand.addCommand(initCommand);
1293
+ designCommand.addCommand(updateCommand);
1294
+ designCommand.addCommand(listCommand);
1295
+ designCommand.addCommand(listVariantsCommand);
1296
+ designCommand.addCommand(uninstallCommand);
1297
+
1298
+ // src/commands/skills/index.ts
1299
+ import { Command as Command13 } from "commander";
1300
+
1301
+ // src/commands/skills/add.ts
1302
+ import { Command as Command7 } from "commander";
1303
+ import * as prompts2 from "@clack/prompts";
1304
+
1305
+ // src/utils/global-root.ts
1306
+ import { existsSync } from "fs";
1307
+ import * as fs8 from "fs/promises";
1308
+ import * as os3 from "os";
1309
+ import * as path11 from "path";
1310
+ var GLOBAL_META_DIR = ".teamix-evo-global";
1311
+ var TEAMIX_DIR3 = ".teamix-evo";
1312
+ var CONFIG_FILE2 = "config.json";
1313
+ function getGlobalMetaRoot() {
1314
+ return path11.join(os3.homedir(), GLOBAL_META_DIR);
1315
+ }
1316
+ function isTeamixEvoProject(dir) {
1317
+ return existsSync(path11.join(dir, TEAMIX_DIR3, CONFIG_FILE2));
1318
+ }
1319
+ async function ensureGlobalMetaRoot() {
1320
+ const root = getGlobalMetaRoot();
1321
+ await fs8.mkdir(root, { recursive: true });
1322
+ return root;
1323
+ }
1324
+
1325
+ // src/commands/skills/add.ts
1326
+ var addCommand = new Command7("add").description(
1327
+ "\u5411\u9879\u76EE\uFF08\u6216\u5168\u5C40 IDE \u914D\u7F6E\uFF09\u6DFB\u52A0 teamix-evo skills\uFF1B\u4E0D\u4F20 names \u5219\u6DFB\u52A0 manifest \u5185\u5168\u90E8 skill"
1328
+ ).argument(
1329
+ "[names...]",
1330
+ "\u53EF\u9009\uFF1A\u4EC5\u6DFB\u52A0\u6307\u5B9A skill id\uFF08\u589E\u91CF\u6A21\u5F0F\uFF09\uFF1B\u7701\u7565\u5219\u6DFB\u52A0\u5168\u90E8"
1331
+ ).option("--ide <list>", '\u9017\u53F7\u5206\u9694\u7684 IDE \u5217\u8868\uFF0C\u5982 "qoder,claude"').option(
1332
+ "--scope <scope>",
1333
+ "project | global\uFF08\u9ED8\u8BA4 project\uFF1B\u589E\u91CF\u6A21\u5F0F\u4E0B\u9ED8\u8BA4\u590D\u7528\u5DF2\u6709\u914D\u7F6E\uFF09"
1334
+ ).option("-y, --yes", "\u4F7F\u7528\u9ED8\u8BA4\u503C\uFF0C\u8DF3\u8FC7\u4EA4\u4E92").action(async (names, opts) => {
1335
+ try {
1336
+ const ide = detectIde();
1337
+ const cwd = ide.getProjectRoot();
1338
+ const isIncremental = names.length > 0;
1339
+ const { ides, scope } = await resolveIdesAndScope({
1340
+ opts,
1341
+ projectRoot: cwd,
1342
+ isIncremental
1343
+ });
1344
+ let projectRoot = cwd;
1345
+ if (scope === "global" && !isTeamixEvoProject(cwd)) {
1346
+ projectRoot = await ensureGlobalMetaRoot();
1347
+ logger.info(`Global skill install \u2014 meta root: ${projectRoot}`);
1348
+ }
1349
+ logger.info(
1350
+ isIncremental ? `Adding skills [${names.join(",")}]: ides=[${ides.join(
1351
+ ","
1352
+ )}], scope="${scope}"` : `Adding skills (all): ides=[${ides.join(",")}], scope="${scope}"`
1353
+ );
1354
+ logger.debug(`Project root: ${projectRoot}`);
1355
+ const result = await runSkillsAdd({
1356
+ projectRoot,
1357
+ ides,
1358
+ scope,
1359
+ ide: ide.name,
1360
+ names: isIncremental ? names : void 0
1361
+ });
1362
+ if (result.status === "already-added") {
1363
+ logger.warn(
1364
+ `Skills already added. Use "teamix-evo skills add <name>" to add specific skills, "teamix-evo skills update" to refresh, or "teamix-evo skills uninstall" to remove.`
1365
+ );
1366
+ return;
1367
+ }
1368
+ if (result.addedSkillIds.length === 0 && result.skippedSkillIds.length > 0) {
1369
+ logger.warn(
1370
+ `\u5DF2\u5B58\u5728\uFF0C\u65E0\u9700\u6DFB\u52A0\uFF1A${result.skippedSkillIds.join(
1371
+ ", "
1372
+ )}\u3002\u5982\u9700\u5237\u65B0\u5185\u5BB9\u8BF7\u8FD0\u884C "teamix-evo skills update"\u3002`
1373
+ );
1374
+ return;
1375
+ }
1376
+ logger.success(`Skills added: ${result.skillCount} skill(s)`);
1377
+ logger.info(` IDEs: ${result.ides.join(", ")}`);
1378
+ logger.info(` Scope: ${result.scope}`);
1379
+ if (result.addedSkillIds.length > 0) {
1380
+ logger.info(` Added: ${result.addedSkillIds.join(", ")}`);
1381
+ }
1382
+ if (result.skippedSkillIds.length > 0) {
1383
+ logger.info(
1384
+ ` Skipped: ${result.skippedSkillIds.join(", ")} (already added)`
1385
+ );
1386
+ }
1387
+ logger.info(` Files: ${result.fileCount}`);
1388
+ logger.info("");
1389
+ logger.info('Run "teamix-evo skills list" to see installed skills.');
1390
+ } catch (err) {
1391
+ logger.error(`Failed to add skills: ${err.message}`);
1392
+ logger.debug(err.stack ?? "");
1393
+ process.exitCode = 1;
1394
+ }
1395
+ });
1396
+ async function resolveIdesAndScope(args) {
1397
+ const { opts, projectRoot, isIncremental } = args;
1398
+ if (isIncremental && !opts.ide && !opts.scope && !opts.yes) {
1399
+ const existing = await readProjectConfig(projectRoot);
1400
+ const cfg = existing?.packages?.skills;
1401
+ if (cfg && cfg.ides && cfg.ides.length > 0 && cfg.scope) {
1402
+ logger.debug(
1403
+ `Reusing existing skills config: ides=[${cfg.ides.join(",")}], scope="${cfg.scope}"`
1404
+ );
1405
+ return {
1406
+ ides: [...cfg.ides],
1407
+ scope: cfg.scope
1408
+ };
1409
+ }
1410
+ }
1411
+ if (opts.ide || opts.yes) {
1412
+ const ides = opts.ide ? parseIdeList(opts.ide) : [...ALL_IDE_KINDS];
1413
+ const scope = parseScope(opts.scope);
1414
+ if (ides.length === 0) {
1415
+ throw new Error("At least one IDE must be selected.");
1416
+ }
1417
+ return { ides, scope };
1418
+ }
1419
+ const idesAns = await prompts2.multiselect({
1420
+ message: "\u9009\u62E9\u8981\u6CE8\u5165\u6280\u80FD\u7684 AI IDE\uFF08\u81F3\u5C11\u4E00\u4E2A\uFF09",
1421
+ options: ALL_IDE_KINDS.map((k) => ({
1422
+ value: k,
1423
+ label: k === "qoder" ? "Qoder" : "Claude Code"
1424
+ })),
1425
+ initialValues: [...ALL_IDE_KINDS],
1426
+ required: true
1427
+ });
1428
+ if (prompts2.isCancel(idesAns)) {
1429
+ throw new Error("Cancelled by user.");
1430
+ }
1431
+ const scopeAns = await prompts2.select({
1432
+ message: "\u5B89\u88C5\u8303\u56F4\uFF1F",
1433
+ options: [
1434
+ { value: "project", label: "\u9879\u76EE\u7EA7\uFF08.qoder/.claude \u5728\u5F53\u524D\u9879\u76EE\uFF09" },
1435
+ { value: "global", label: "\u5168\u5C40\uFF08~/.qoder/~/.claude\uFF09" }
1436
+ ],
1437
+ initialValue: "project"
1438
+ });
1439
+ if (prompts2.isCancel(scopeAns)) {
1440
+ throw new Error("Cancelled by user.");
1441
+ }
1442
+ return { ides: idesAns, scope: scopeAns };
1443
+ }
1444
+ function parseIdeList(input) {
1445
+ const parts = input.split(",").map((s) => s.trim().toLowerCase()).filter(Boolean);
1446
+ const result = [];
1447
+ for (const p of parts) {
1448
+ if (p === "qoder" || p === "claude") {
1449
+ if (!result.includes(p)) result.push(p);
1450
+ } else {
1451
+ throw new Error(`Unknown IDE: "${p}". Expected qoder | claude.`);
1452
+ }
1453
+ }
1454
+ return result;
1455
+ }
1456
+ function parseScope(input) {
1457
+ const v = (input ?? "project").toLowerCase();
1458
+ if (v === "project" || v === "global") return v;
1459
+ throw new Error(`Invalid --scope: "${input}". Expected project | global.`);
1460
+ }
1461
+
1462
+ // src/commands/skills/list.ts
1463
+ import { Command as Command8 } from "commander";
1464
+ var SKILLS_PACKAGE = "@teamix-evo/skills";
1465
+ var listCommand2 = new Command8("list").alias("ls").description(
1466
+ "\u5217\u51FA teamix-evo skills\uFF08\u9ED8\u8BA4\u5C55\u793A\u5168\u90E8 skill \u5E76\u6807\u6CE8\u5DF2\u88C5/\u672A\u88C5\uFF1B--installed \u4EC5\u770B\u5DF2\u88C5\uFF09"
1467
+ ).option("--installed", "\u4EC5\u5C55\u793A\u5DF2\u5B89\u88C5\u7684 skill\uFF08\u9690\u85CF\u672A\u5B89\u88C5\u9879\uFF09").action(async (opts) => {
1468
+ try {
1469
+ const ide = detectIde();
1470
+ const projectRoot = ide.getProjectRoot();
1471
+ const config = await readProjectConfig(projectRoot);
1472
+ const installedManifest = await readInstalledManifest(projectRoot);
1473
+ const pkg = installedManifest?.installed.find(
1474
+ (p) => p.package === SKILLS_PACKAGE
1475
+ );
1476
+ const installedBySkill = /* @__PURE__ */ new Map();
1477
+ for (const r of pkg?.resources ?? []) {
1478
+ const skillId = r.id.split(":")[0];
1479
+ installedBySkill.set(skillId, (installedBySkill.get(skillId) ?? 0) + 1);
1480
+ }
1481
+ if (opts.installed) {
1482
+ if (!config?.packages?.skills || !pkg) {
1483
+ logger.info("No skills installed.");
1484
+ logger.info('Run "teamix-evo skills add" to get started.');
1485
+ return;
1486
+ }
1487
+ printInstalledHeader(config.packages.skills, pkg.installedAt);
1488
+ logger.info("");
1489
+ logger.info("Installed skills:");
1490
+ for (const [skillId, count] of installedBySkill) {
1491
+ logger.info(` \u2713 ${skillId} (${count} file${count > 1 ? "s" : ""})`);
1492
+ }
1493
+ logger.info("");
1494
+ logger.info(
1495
+ ` Total: ${pkg.resources.length} files (${installedBySkill.size} skills \xD7 ides \xD7 scope)`
1496
+ );
1497
+ return;
1498
+ }
1499
+ const { manifest } = await loadSkillsData(SKILLS_PACKAGE);
1500
+ const skills = [...manifest.skills].sort(
1501
+ (a, b) => a.id.localeCompare(b.id)
1502
+ );
1503
+ const isInstalled = !!config?.packages?.skills && !!pkg;
1504
+ if (isInstalled) {
1505
+ printInstalledHeader(config.packages.skills, pkg.installedAt);
1506
+ } else {
1507
+ logger.info("Skills package not yet added.");
1508
+ logger.info(
1509
+ 'Run "teamix-evo skills add" to add all, or "teamix-evo skills add <id>" for specific skills.'
1510
+ );
1511
+ }
1512
+ logger.info("");
1513
+ logger.info(`Available skills (${SKILLS_PACKAGE}@${manifest.version}):`);
1514
+ let installedCount = 0;
1515
+ for (const s of skills) {
1516
+ const fileCount = installedBySkill.get(s.id);
1517
+ const installed = fileCount !== void 0;
1518
+ if (installed) installedCount++;
1519
+ const mark = installed ? "\u2713" : "\u25CB";
1520
+ const tail = installed ? `[installed, ${fileCount} file${fileCount > 1 ? "s" : ""}]` : `[not installed \u2014 run "teamix-evo skills add ${s.id}"]`;
1521
+ logger.info(` ${mark} ${s.id}@${s.version} ${tail}`);
1522
+ if (s.description) {
1523
+ logger.info(` ${s.description}`);
1524
+ }
1525
+ }
1526
+ logger.info("");
1527
+ logger.info(
1528
+ ` Total: ${skills.length} skill(s) \u2014 ${installedCount} installed, ${skills.length - installedCount} available`
1529
+ );
1530
+ } catch (err) {
1531
+ logger.error(`Failed to list: ${err.message}`);
1532
+ process.exitCode = 1;
1533
+ }
1534
+ });
1535
+ function printInstalledHeader(cfg, installedAt) {
1536
+ logger.info("Installed skills package:");
1537
+ logger.info(` Package: ${SKILLS_PACKAGE}`);
1538
+ logger.info(` Version: ${cfg.version ?? "(unknown)"}`);
1539
+ logger.info(` IDEs: ${(cfg.ides ?? []).join(", ") || "(unknown)"}`);
1540
+ logger.info(` Scope: ${cfg.scope ?? "(unknown)"}`);
1541
+ logger.info(` Installed: ${new Date(installedAt).toLocaleString()}`);
1542
+ }
1543
+
1544
+ // src/commands/skills/update.ts
1545
+ import { Command as Command9 } from "commander";
1546
+ var SKILLS_PACKAGE2 = "@teamix-evo/skills";
1547
+ var FLAT_VARIANT2 = "_flat";
1548
+ var updateCommand2 = new Command9("update").description("\u66F4\u65B0\u5DF2\u5B89\u88C5\u7684 teamix-evo skills").action(async () => {
1549
+ try {
1550
+ const ide = detectIde();
1551
+ const projectRoot = ide.getProjectRoot();
1552
+ const config = await readProjectConfig(projectRoot);
1553
+ if (!config?.packages?.skills) {
1554
+ logger.error('Skills not added. Run "teamix-evo skills add" first.');
1555
+ process.exitCode = 1;
1556
+ return;
1557
+ }
1558
+ const installedManifest = await readInstalledManifest(projectRoot);
1559
+ if (!installedManifest) {
1560
+ logger.error("No installed manifest found.");
1561
+ process.exitCode = 1;
1562
+ return;
1563
+ }
1564
+ const skillsEntry = config.packages.skills;
1565
+ const ides = skillsEntry.ides ?? [
1566
+ "qoder",
1567
+ "claude"
1568
+ ];
1569
+ const scope = skillsEntry.scope ?? "project";
1570
+ logger.info(`Updating skills (ides=[${ides.join(",")}], scope=${scope})`);
1571
+ const { manifest, data, packageRoot } = await loadSkillsData(
1572
+ SKILLS_PACKAGE2
1573
+ );
1574
+ logger.info(
1575
+ `Current: v${skillsEntry.version} \u2192 Available: v${manifest.version}`
1576
+ );
1577
+ const result = await updateSkills({
1578
+ projectRoot,
1579
+ manifest,
1580
+ data,
1581
+ packageRoot,
1582
+ ides,
1583
+ scope
1584
+ });
1585
+ config.packages.skills.version = manifest.version;
1586
+ await writeProjectConfig(projectRoot, config);
1587
+ const installedAt = (/* @__PURE__ */ new Date()).toISOString();
1588
+ const idx = installedManifest.installed.findIndex(
1589
+ (p) => p.package === SKILLS_PACKAGE2
1590
+ );
1591
+ const entry = {
1592
+ package: SKILLS_PACKAGE2,
1593
+ variant: FLAT_VARIANT2,
1594
+ version: manifest.version,
1595
+ installedAt,
1596
+ resources: result.resources
1597
+ };
1598
+ if (idx >= 0) installedManifest.installed[idx] = entry;
1599
+ else installedManifest.installed.push(entry);
1600
+ await writeInstalledManifest(projectRoot, installedManifest);
1601
+ const existingLock = await readSkillsLock(projectRoot) ?? {
1602
+ schemaVersion: 1,
1603
+ skills: {}
1604
+ };
1605
+ const lock = {
1606
+ schemaVersion: 1,
1607
+ skills: { ...existingLock.skills }
1608
+ };
1609
+ for (const skill of manifest.skills) {
1610
+ const mirroredTo = skill.ides.filter((i) => ides.includes(i));
1611
+ if (mirroredTo.length === 0) continue;
1612
+ lock.skills[skill.id] = {
1613
+ version: skill.version,
1614
+ from: SKILLS_PACKAGE2,
1615
+ installedAt,
1616
+ scope,
1617
+ mirroredTo
1618
+ };
1619
+ }
1620
+ await writeSkillsLock(projectRoot, lock);
1621
+ const { summary } = result;
1622
+ logger.success(`Skills updated to v${manifest.version}`);
1623
+ logger.info(` Created: ${summary.created}`);
1624
+ logger.info(` Overwritten: ${summary.overwritten}`);
1625
+ logger.info(` Managed: ${summary.managed}`);
1626
+ logger.info(` Skipped: ${summary.skipped}`);
1627
+ } catch (err) {
1628
+ logger.error(`Failed to update skills: ${err.message}`);
1629
+ logger.debug(err.stack ?? "");
1630
+ process.exitCode = 1;
1631
+ }
1632
+ });
1633
+
1634
+ // src/commands/skills/uninstall.ts
1635
+ import { Command as Command10 } from "commander";
1636
+ import * as prompts3 from "@clack/prompts";
1637
+ import * as path12 from "path";
1638
+ import * as fs9 from "fs/promises";
1639
+ var SKILLS_PACKAGE3 = "@teamix-evo/skills";
1640
+ var uninstallCommand2 = new Command10("uninstall").description("\u5378\u8F7D\u5DF2\u5B89\u88C5\u7684 teamix-evo skills\uFF08\u5220\u9664\u6CE8\u5165\u5230 IDE \u7684\u6280\u80FD\u6587\u4EF6\uFF09").option("-y, --yes", "\u8DF3\u8FC7\u786E\u8BA4").action(async (opts) => {
1641
+ try {
1642
+ const ide = detectIde();
1643
+ const projectRoot = ide.getProjectRoot();
1644
+ const config = await readProjectConfig(projectRoot);
1645
+ if (!config?.packages?.skills) {
1646
+ logger.info("Skills are not installed. Nothing to do.");
1647
+ return;
1648
+ }
1649
+ const installedManifest = await readInstalledManifest(projectRoot);
1650
+ const pkg = installedManifest?.installed.find(
1651
+ (p) => p.package === SKILLS_PACKAGE3
1652
+ );
1653
+ const resources = pkg?.resources ?? [];
1654
+ logger.info(
1655
+ `Will remove ${resources.length} skill file(s) installed by ${SKILLS_PACKAGE3}.`
1656
+ );
1657
+ if (!opts.yes) {
1658
+ const confirm4 = await prompts3.confirm({
1659
+ message: "\u786E\u8BA4\u5378\u8F7D\uFF1F\u6B64\u64CD\u4F5C\u4F1A\u5220\u9664\u4E0A\u8FF0\u6587\u4EF6\u3002",
1660
+ initialValue: false
1661
+ });
1662
+ if (prompts3.isCancel(confirm4) || !confirm4) {
1663
+ logger.info("Cancelled.");
1664
+ return;
1665
+ }
1666
+ }
1667
+ const removed = await removeSkillFiles(resources);
1668
+ logger.debug(`Removed ${removed.length} files`);
1669
+ const skillsRoot = getSkillsSourceDir(projectRoot);
1670
+ try {
1671
+ await fs9.rm(skillsRoot, { recursive: true, force: true });
1672
+ logger.debug(`Removed source dir ${skillsRoot}`);
1673
+ } catch (err) {
1674
+ logger.warn(
1675
+ `Failed to remove ${skillsRoot}: ${err.message}`
1676
+ );
1677
+ }
1678
+ if (installedManifest && pkg) {
1679
+ installedManifest.installed = installedManifest.installed.filter(
1680
+ (p) => p.package !== SKILLS_PACKAGE3
1681
+ );
1682
+ await writeInstalledManifest(projectRoot, installedManifest);
1683
+ }
1684
+ delete config.packages.skills;
1685
+ await writeProjectConfig(projectRoot, config);
1686
+ logger.success(`Uninstalled ${SKILLS_PACKAGE3}`);
1687
+ logger.info(` Removed: ${removed.length} files`);
1688
+ logger.info(` Source: ${path12.relative(projectRoot, skillsRoot)} (cleaned)`);
1689
+ } catch (err) {
1690
+ logger.error(`Failed to uninstall: ${err.message}`);
1691
+ logger.debug(err.stack ?? "");
1692
+ process.exitCode = 1;
1693
+ }
1694
+ });
1695
+
1696
+ // src/commands/skills/sync.ts
1697
+ import { Command as Command11 } from "commander";
1698
+
1699
+ // src/core/skills-sync.ts
1700
+ import * as fs10 from "fs/promises";
1701
+ var SKILLS_PACKAGE_DEFAULT = "@teamix-evo/skills";
1702
+ async function runSkillsSync(options) {
1703
+ const { projectRoot, names } = options;
1704
+ const lock = await readSkillsLock(projectRoot);
1705
+ if (!lock || Object.keys(lock.skills).length === 0) {
1706
+ return {
1707
+ status: "no-skills",
1708
+ syncedSkillIds: [],
1709
+ fileCount: 0,
1710
+ resources: [],
1711
+ missingSourceIds: []
1712
+ };
1713
+ }
1714
+ const skillIds = Object.keys(lock.skills);
1715
+ const targets = names ? skillIds.filter((id) => names.includes(id)) : skillIds;
1716
+ const allResources = [];
1717
+ const synced = [];
1718
+ const missing = [];
1719
+ for (const skillId of targets) {
1720
+ const lockEntry = lock.skills[skillId];
1721
+ if (!lockEntry) continue;
1722
+ const sourceDir = getSkillsSourceDir(projectRoot, skillId);
1723
+ if (!await dirExists(sourceDir)) {
1724
+ logger.warn(`Skill "${skillId}" has no source at ${sourceDir}; skipped.`);
1725
+ missing.push(skillId);
1726
+ continue;
1727
+ }
1728
+ const ides = options.ides ?? lockEntry.mirroredTo;
1729
+ const scope = options.scope ?? lockEntry.scope;
1730
+ if (ides.length === 0) {
1731
+ logger.warn(`Skill "${skillId}" has no IDE mirror targets; skipped.`);
1732
+ continue;
1733
+ }
1734
+ const result = await syncSkillsToIdes({
1735
+ projectRoot,
1736
+ skills: [
1737
+ {
1738
+ id: skillId,
1739
+ name: skillId,
1740
+ updateStrategy: "regenerable"
1741
+ }
1742
+ ],
1743
+ ides,
1744
+ scope
1745
+ });
1746
+ allResources.push(...result.resources);
1747
+ synced.push(skillId);
1748
+ lock.skills[skillId] = {
1749
+ ...lockEntry,
1750
+ mirroredTo: ides,
1751
+ scope,
1752
+ installedAt: (/* @__PURE__ */ new Date()).toISOString()
1753
+ };
1754
+ }
1755
+ await writeSkillsLock(projectRoot, lock);
1756
+ await refreshMirrorRecords(projectRoot, allResources);
1757
+ return {
1758
+ status: "synced",
1759
+ syncedSkillIds: synced,
1760
+ fileCount: allResources.length,
1761
+ resources: allResources,
1762
+ missingSourceIds: missing
1763
+ };
1764
+ }
1765
+ async function dirExists(p) {
1766
+ try {
1767
+ const stat4 = await fs10.stat(p);
1768
+ return stat4.isDirectory();
1769
+ } catch {
1770
+ return false;
1771
+ }
1772
+ }
1773
+ async function refreshMirrorRecords(projectRoot, newMirrorRecords) {
1774
+ const installed = await readInstalledManifest(projectRoot);
1775
+ if (!installed) return;
1776
+ const pkg = installed.installed.find((p) => p.package === SKILLS_PACKAGE_DEFAULT);
1777
+ if (!pkg) return;
1778
+ const sourceOnly = pkg.resources.filter((r) => r.ide === void 0);
1779
+ pkg.resources = [...sourceOnly, ...newMirrorRecords];
1780
+ pkg.installedAt = (/* @__PURE__ */ new Date()).toISOString();
1781
+ await writeInstalledManifest(projectRoot, installed);
1782
+ }
1783
+
1784
+ // src/commands/skills/sync.ts
1785
+ var syncCommand = new Command11("sync").description(
1786
+ "\u628A .teamix-evo/skills/ \u4E0B\u7684\u6E90\u91CD\u65B0\u955C\u50CF\u5230 IDE \u8DEF\u5F84\uFF08.qoder / .claude\uFF09"
1787
+ ).argument(
1788
+ "[names...]",
1789
+ "\u53EF\u9009\uFF1A\u4EC5\u540C\u6B65\u6307\u5B9A skill id\uFF1B\u7701\u7565\u5219\u540C\u6B65\u5168\u90E8\u5DF2\u8BB0\u5F55\u5728 lock \u5185\u7684 skill"
1790
+ ).option(
1791
+ "--ide <list>",
1792
+ "\u9017\u53F7\u5206\u9694\u7684 IDE \u5217\u8868\uFF08\u8986\u76D6 lock \u4E2D\u8BB0\u5F55\u7684 mirroredTo\uFF09"
1793
+ ).option(
1794
+ "--scope <scope>",
1795
+ "project | global\uFF08\u8986\u76D6 lock \u4E2D\u8BB0\u5F55\u7684 scope\uFF09"
1796
+ ).action(async (names, opts) => {
1797
+ try {
1798
+ const ide = detectIde();
1799
+ const projectRoot = ide.getProjectRoot();
1800
+ const ides = opts.ide ? parseIdeList2(opts.ide) : void 0;
1801
+ const scope = opts.scope ? parseScope2(opts.scope) : void 0;
1802
+ const result = await runSkillsSync({
1803
+ projectRoot,
1804
+ ides,
1805
+ scope,
1806
+ names: names.length > 0 ? names : void 0
1807
+ });
1808
+ if (result.status === "no-skills") {
1809
+ logger.info(
1810
+ "No skills recorded in .teamix-evo/skills/manifest.lock.json. Nothing to sync."
1811
+ );
1812
+ return;
1813
+ }
1814
+ logger.success(
1815
+ `Synced ${result.syncedSkillIds.length} skill(s) \u2192 ${result.fileCount} file(s)`
1816
+ );
1817
+ if (result.syncedSkillIds.length > 0) {
1818
+ logger.info(` Skills: ${result.syncedSkillIds.join(", ")}`);
1819
+ }
1820
+ if (result.missingSourceIds.length > 0) {
1821
+ logger.warn(
1822
+ ` Missing source: ${result.missingSourceIds.join(
1823
+ ", "
1824
+ )} (run "skills add <id>" to (re)install)`
1825
+ );
1826
+ }
1827
+ } catch (err) {
1828
+ logger.error(`Failed to sync skills: ${err.message}`);
1829
+ logger.debug(err.stack ?? "");
1830
+ process.exitCode = 1;
1831
+ }
1832
+ });
1833
+ function parseIdeList2(input) {
1834
+ const parts = input.split(",").map((s) => s.trim().toLowerCase()).filter(Boolean);
1835
+ const result = [];
1836
+ for (const p of parts) {
1837
+ if (p === "qoder" || p === "claude") {
1838
+ if (!result.includes(p)) result.push(p);
1839
+ } else {
1840
+ throw new Error(
1841
+ `Unknown IDE: "${p}". Expected one of: ${ALL_IDE_KINDS.join(", ")}.`
1842
+ );
1843
+ }
1844
+ }
1845
+ return result;
1846
+ }
1847
+ function parseScope2(input) {
1848
+ const v = input.toLowerCase();
1849
+ if (v === "project" || v === "global") return v;
1850
+ throw new Error(`Invalid --scope: "${input}". Expected project | global.`);
1851
+ }
1852
+
1853
+ // src/commands/skills/doctor.ts
1854
+ import { Command as Command12 } from "commander";
1855
+
1856
+ // src/core/skills-doctor.ts
1857
+ import * as path13 from "path";
1858
+ import * as fs11 from "fs/promises";
1859
+ async function runSkillsDoctor(options) {
1860
+ const { projectRoot } = options;
1861
+ const lock = await readSkillsLock(projectRoot);
1862
+ if (!lock || Object.keys(lock.skills).length === 0) {
1863
+ return { status: "no-skills", findings: [] };
1864
+ }
1865
+ const findings = [];
1866
+ for (const [skillId, entry] of Object.entries(lock.skills)) {
1867
+ const sourceDir = getSkillsSourceDir(projectRoot, skillId);
1868
+ if (!await dirExists2(sourceDir)) {
1869
+ findings.push({
1870
+ kind: "missing-source",
1871
+ skillId,
1872
+ path: sourceDir,
1873
+ detail: 'Run "teamix-evo skills add" to reinstall.'
1874
+ });
1875
+ continue;
1876
+ }
1877
+ const sourceFiles = await walkDir(sourceDir);
1878
+ const sourceContents = /* @__PURE__ */ new Map();
1879
+ for (const f of sourceFiles) {
1880
+ const rel2 = path13.relative(sourceDir, f);
1881
+ sourceContents.set(rel2, await fs11.readFile(f, "utf-8"));
1882
+ }
1883
+ for (const ide of entry.mirroredTo) {
1884
+ const adapter = getAdapter(ide);
1885
+ const mirrorDir = adapter.getSkillTargetDir(
1886
+ skillId,
1887
+ entry.scope,
1888
+ projectRoot
1889
+ );
1890
+ if (!await dirExists2(mirrorDir)) {
1891
+ findings.push({
1892
+ kind: "missing-mirror",
1893
+ skillId,
1894
+ ide,
1895
+ scope: entry.scope,
1896
+ path: mirrorDir,
1897
+ detail: 'Run "teamix-evo skills sync" to re-mirror.'
1898
+ });
1899
+ continue;
1900
+ }
1901
+ for (const [rel2, sourceContent] of sourceContents.entries()) {
1902
+ const mirrorFile = path13.join(mirrorDir, rel2);
1903
+ if (!await fileExists(mirrorFile)) {
1904
+ findings.push({
1905
+ kind: "missing-mirror",
1906
+ skillId,
1907
+ ide,
1908
+ scope: entry.scope,
1909
+ path: mirrorFile,
1910
+ detail: 'Run "teamix-evo skills sync" to re-mirror.'
1911
+ });
1912
+ continue;
1913
+ }
1914
+ const mirrorContent = await fs11.readFile(mirrorFile, "utf-8");
1915
+ if (computeHash(mirrorContent) !== computeHash(sourceContent)) {
1916
+ findings.push({
1917
+ kind: "mirror-drift",
1918
+ skillId,
1919
+ ide,
1920
+ scope: entry.scope,
1921
+ path: mirrorFile,
1922
+ detail: 'Mirror differs from source. Re-run "teamix-evo skills sync" to overwrite.'
1923
+ });
1924
+ }
1925
+ }
1926
+ }
1927
+ }
1928
+ return {
1929
+ status: findings.length === 0 ? "clean" : "drift",
1930
+ findings
1931
+ };
1932
+ }
1933
+ async function dirExists2(p) {
1934
+ try {
1935
+ const stat4 = await fs11.stat(p);
1936
+ return stat4.isDirectory();
1937
+ } catch {
1938
+ return false;
1939
+ }
1940
+ }
1941
+
1942
+ // src/commands/skills/doctor.ts
1943
+ var doctorCommand = new Command12("doctor").description(
1944
+ "\u68C0\u67E5 .teamix-evo/skills/ \u6E90\u4E0E IDE \u955C\u50CF\u662F\u5426\u6F02\u79FB\uFF1B\u63D0\u793A\u5982\u4F55\u4FEE\u590D"
1945
+ ).action(async () => {
1946
+ try {
1947
+ const ide = detectIde();
1948
+ const projectRoot = ide.getProjectRoot();
1949
+ const result = await runSkillsDoctor({ projectRoot });
1950
+ if (result.status === "no-skills") {
1951
+ logger.info(
1952
+ 'No skills recorded. Run "teamix-evo skills add" first.'
1953
+ );
1954
+ return;
1955
+ }
1956
+ if (result.status === "clean") {
1957
+ logger.success("Skills are in sync. No drift detected.");
1958
+ return;
1959
+ }
1960
+ logger.warn(
1961
+ `Found ${result.findings.length} drift issue(s). Run "teamix-evo skills sync" to repair mirrors.`
1962
+ );
1963
+ for (const f of result.findings) {
1964
+ const idePart = f.ide ? ` [${f.ide}]` : "";
1965
+ logger.info(` - ${f.kind}${idePart}: ${f.skillId}`);
1966
+ logger.info(` ${f.path}`);
1967
+ if (f.detail) logger.info(` ${f.detail}`);
1968
+ }
1969
+ process.exitCode = 1;
1970
+ } catch (err) {
1971
+ logger.error(`Failed to run doctor: ${err.message}`);
1972
+ logger.debug(err.stack ?? "");
1973
+ process.exitCode = 1;
1974
+ }
1975
+ });
1976
+
1977
+ // src/commands/skills/index.ts
1978
+ var skillsCommand = new Command13("skills").description(
1979
+ "\u7BA1\u7406 teamix-evo skills\uFF08\u5411 AI IDE \u6CE8\u5165\u6280\u80FD\uFF1Bsource-mirror \u6A21\u578B\u89C1 ADR 0013\uFF09"
1980
+ );
1981
+ skillsCommand.addCommand(addCommand);
1982
+ skillsCommand.addCommand(listCommand2);
1983
+ skillsCommand.addCommand(updateCommand2);
1984
+ skillsCommand.addCommand(syncCommand);
1985
+ skillsCommand.addCommand(doctorCommand);
1986
+ skillsCommand.addCommand(uninstallCommand2);
1987
+
1988
+ // src/commands/ui/index.ts
1989
+ import { Command as Command17 } from "commander";
1990
+
1991
+ // src/commands/ui/init.ts
1992
+ import { Command as Command14 } from "commander";
1993
+ import * as prompts4 from "@clack/prompts";
1994
+
1995
+ // src/core/ui-init.ts
1996
+ var DEFAULT_UI_ALIASES = {
1997
+ components: "src/components/ui",
1998
+ hooks: "src/hooks",
1999
+ utils: "src/lib/utils",
2000
+ lib: "src/lib",
2001
+ business: "src/components/business",
2002
+ templates: "src/templates"
2003
+ };
2004
+ var DEFAULT_UI_ICON_LIBRARY = "lucide";
2005
+ async function runUiInit(options) {
2006
+ const { projectRoot } = options;
2007
+ const ideIdent = options.ide ?? "qoder";
2008
+ await ensureTeamixDir(projectRoot);
2009
+ const existingConfig = await readProjectConfig(projectRoot);
2010
+ if (existingConfig?.packages?.ui) {
2011
+ return { status: "already-initialized" };
2012
+ }
2013
+ const aliases = {
2014
+ components: options.aliases?.components ?? DEFAULT_UI_ALIASES.components,
2015
+ hooks: options.aliases?.hooks ?? DEFAULT_UI_ALIASES.hooks,
2016
+ utils: options.aliases?.utils ?? DEFAULT_UI_ALIASES.utils,
2017
+ lib: options.aliases?.lib ?? DEFAULT_UI_ALIASES.lib,
2018
+ business: options.aliases?.business ?? DEFAULT_UI_ALIASES.business,
2019
+ templates: options.aliases?.templates ?? DEFAULT_UI_ALIASES.templates
2020
+ };
2021
+ const iconLibrary = options.iconLibrary ?? DEFAULT_UI_ICON_LIBRARY;
2022
+ const tsx = options.tsx ?? true;
2023
+ const rsc = options.rsc ?? false;
2024
+ const config = existingConfig ?? {
2025
+ $schema: "https://teamix-evo.dev/schema/config/v1.json",
2026
+ schemaVersion: 1,
2027
+ ide: ideIdent,
2028
+ packages: {}
2029
+ };
2030
+ config.packages.ui = {
2031
+ variant: "_flat",
2032
+ version: "0.0.0",
2033
+ aliases,
2034
+ iconLibrary,
2035
+ tsx,
2036
+ rsc
2037
+ };
2038
+ await writeProjectConfig(projectRoot, config);
2039
+ return {
2040
+ status: "installed",
2041
+ aliases,
2042
+ iconLibrary,
2043
+ tsx,
2044
+ rsc
2045
+ };
2046
+ }
2047
+
2048
+ // src/commands/ui/init.ts
2049
+ var initCommand2 = new Command14("init").description(
2050
+ "\u521D\u59CB\u5316 teamix-evo ui \u914D\u7F6E\uFF08\u8BE2\u95EE aliases / iconLibrary / tsx / rsc\uFF09"
2051
+ ).option("-y, --yes", "\u4F7F\u7528\u9ED8\u8BA4\u503C\uFF0C\u8DF3\u8FC7\u4EA4\u4E92").option(
2052
+ "--components <path>",
2053
+ "\u7EC4\u4EF6 alias \u8DEF\u5F84",
2054
+ DEFAULT_UI_ALIASES.components
2055
+ ).option("--hooks <path>", "hooks alias \u8DEF\u5F84", DEFAULT_UI_ALIASES.hooks).option("--utils <path>", "utils alias \u8DEF\u5F84", DEFAULT_UI_ALIASES.utils).option("--lib <path>", "lib alias \u8DEF\u5F84", DEFAULT_UI_ALIASES.lib).option("--icon-library <name>", "\u9ED8\u8BA4 icon \u5E93\uFF08\u58F0\u660E\u6027\uFF09", "lucide").option("--tsx", "\u4F7F\u7528 TSX", true).option("--rsc", "\u4F7F\u7528 React Server Components").action(async (opts) => {
2056
+ try {
2057
+ const ide = detectIde();
2058
+ const projectRoot = ide.getProjectRoot();
2059
+ const cfg = await resolveConfig(opts);
2060
+ const result = await runUiInit({
2061
+ projectRoot,
2062
+ aliases: cfg.aliases,
2063
+ iconLibrary: cfg.iconLibrary,
2064
+ tsx: cfg.tsx,
2065
+ rsc: cfg.rsc,
2066
+ ide: ide.name
2067
+ });
2068
+ if (result.status === "already-initialized") {
2069
+ logger.warn(
2070
+ "UI already initialized. Edit `.teamix-evo/config.json` directly to change aliases, or run `teamix-evo ui list`."
2071
+ );
2072
+ return;
2073
+ }
2074
+ logger.success("UI initialized.");
2075
+ logger.info(` components: ${result.aliases.components}`);
2076
+ logger.info(` hooks: ${result.aliases.hooks}`);
2077
+ logger.info(` utils: ${result.aliases.utils}`);
2078
+ logger.info(` lib: ${result.aliases.lib}`);
2079
+ logger.info(` iconLibrary: ${result.iconLibrary}`);
2080
+ logger.info(` tsx: ${result.tsx}, rsc: ${result.rsc}`);
2081
+ logger.info("");
2082
+ logger.info("Next: `npx teamix-evo ui add button`");
2083
+ } catch (err) {
2084
+ logger.error(`Failed to initialize ui: ${err.message}`);
2085
+ logger.debug(err.stack ?? "");
2086
+ process.exitCode = 1;
2087
+ }
2088
+ });
2089
+ async function resolveConfig(opts) {
2090
+ if (opts.yes) {
2091
+ return {
2092
+ aliases: {
2093
+ components: opts.components ?? DEFAULT_UI_ALIASES.components,
2094
+ hooks: opts.hooks ?? DEFAULT_UI_ALIASES.hooks,
2095
+ utils: opts.utils ?? DEFAULT_UI_ALIASES.utils,
2096
+ lib: opts.lib ?? DEFAULT_UI_ALIASES.lib,
2097
+ business: DEFAULT_UI_ALIASES.business,
2098
+ templates: DEFAULT_UI_ALIASES.templates
2099
+ },
2100
+ iconLibrary: opts.iconLibrary ?? "lucide",
2101
+ tsx: opts.tsx ?? true,
2102
+ rsc: opts.rsc ?? false
2103
+ };
2104
+ }
2105
+ const components = await prompts4.text({
2106
+ message: "components \u8DEF\u5F84\uFF08\u6CE8\u5165\u6309\u94AE\u7B49\u7EC4\u4EF6\u6E90\u7801\u7684\u76EE\u5F55\uFF09",
2107
+ initialValue: opts.components ?? DEFAULT_UI_ALIASES.components
2108
+ });
2109
+ if (prompts4.isCancel(components)) throw new Error("Cancelled by user.");
2110
+ const hooks = await prompts4.text({
2111
+ message: "hooks \u8DEF\u5F84",
2112
+ initialValue: opts.hooks ?? DEFAULT_UI_ALIASES.hooks
2113
+ });
2114
+ if (prompts4.isCancel(hooks)) throw new Error("Cancelled by user.");
2115
+ const utils = await prompts4.text({
2116
+ message: "utils \u8DEF\u5F84\uFF08cn \u7B49\u5DE5\u5177\uFF09",
2117
+ initialValue: opts.utils ?? DEFAULT_UI_ALIASES.utils
2118
+ });
2119
+ if (prompts4.isCancel(utils)) throw new Error("Cancelled by user.");
2120
+ const lib = await prompts4.text({
2121
+ message: "lib \u8DEF\u5F84\uFF08\u5171\u4EAB\u4EE3\u7801\u6839\uFF09",
2122
+ initialValue: opts.lib ?? DEFAULT_UI_ALIASES.lib
2123
+ });
2124
+ if (prompts4.isCancel(lib)) throw new Error("Cancelled by user.");
2125
+ const iconLibrary = await prompts4.text({
2126
+ message: "icon \u5E93\uFF08\u58F0\u660E\u6027\uFF0C\u7EC4\u4EF6\u6E90\u7801\u5DF2 hardcode lucide-react\uFF09",
2127
+ initialValue: opts.iconLibrary ?? "lucide"
2128
+ });
2129
+ if (prompts4.isCancel(iconLibrary)) throw new Error("Cancelled by user.");
2130
+ const tsxAns = await prompts4.confirm({
2131
+ message: "\u4F7F\u7528 TSX\uFF1F",
2132
+ initialValue: opts.tsx ?? true
2133
+ });
2134
+ if (prompts4.isCancel(tsxAns)) throw new Error("Cancelled by user.");
2135
+ const rscAns = await prompts4.confirm({
2136
+ message: "\u4F7F\u7528 React Server Components\uFF1F",
2137
+ initialValue: opts.rsc ?? false
2138
+ });
2139
+ if (prompts4.isCancel(rscAns)) throw new Error("Cancelled by user.");
2140
+ return {
2141
+ aliases: {
2142
+ components,
2143
+ hooks,
2144
+ utils,
2145
+ lib,
2146
+ business: DEFAULT_UI_ALIASES.business,
2147
+ templates: DEFAULT_UI_ALIASES.templates
2148
+ },
2149
+ iconLibrary,
2150
+ tsx: tsxAns,
2151
+ rsc: rscAns
2152
+ };
2153
+ }
2154
+
2155
+ // src/commands/ui/add.ts
2156
+ import { Command as Command15 } from "commander";
2157
+
2158
+ // src/core/ui-client.ts
2159
+ import * as path14 from "path";
2160
+ import * as fs12 from "fs/promises";
2161
+ import { createRequire as createRequire3 } from "module";
2162
+ import { loadUiPackageManifest } from "@teamix-evo/registry";
2163
+ var require4 = createRequire3(import.meta.url);
2164
+ function resolvePackageRoot2(packageName) {
2165
+ const pkgJsonPath = require4.resolve(`${packageName}/package.json`);
2166
+ return path14.dirname(pkgJsonPath);
2167
+ }
2168
+ async function loadUiData(packageName) {
2169
+ const packageRoot = resolvePackageRoot2(packageName);
2170
+ logger.debug(`Resolved ui package root: ${packageRoot}`);
2171
+ const manifest = await loadUiPackageManifest(packageRoot);
2172
+ let data = {};
2173
+ const dataPath = path14.join(packageRoot, "_data.json");
2174
+ try {
2175
+ const raw = await fs12.readFile(dataPath, "utf-8");
2176
+ data = JSON.parse(raw);
2177
+ } catch (err) {
2178
+ if (err.code !== "ENOENT") {
2179
+ throw err;
2180
+ }
2181
+ logger.debug(`No _data.json found at ${dataPath}, using empty data`);
2182
+ }
2183
+ return { manifest, data, packageRoot };
2184
+ }
2185
+
2186
+ // src/core/ui-installer.ts
2187
+ import * as path15 from "path";
2188
+ import * as fs13 from "fs/promises";
2189
+ import { resolveUiEntryOrder } from "@teamix-evo/registry";
2190
+
2191
+ // src/utils/transform-imports.ts
2192
+ var SOURCE_ROOT_TO_ALIAS_KEY = {
2193
+ components: "components",
2194
+ hooks: "hooks",
2195
+ utils: "utils",
2196
+ lib: "lib"
2197
+ };
2198
+ function rewriteImports(source, aliases) {
2199
+ return source.replace(
2200
+ /(['"])@\/([a-z][a-z0-9-]*)(\/[^'"]*)?\1/g,
2201
+ (full, quote, root, rest) => {
2202
+ const aliasKey = SOURCE_ROOT_TO_ALIAS_KEY[root];
2203
+ if (!aliasKey) return full;
2204
+ const alias = aliases[aliasKey];
2205
+ const normalized = aliasToImportPath(alias);
2206
+ const flatRest = flattenRestPath(rest);
2207
+ return `${quote}${normalized}${flatRest}${quote}`;
2208
+ }
2209
+ );
2210
+ }
2211
+ function flattenRestPath(rest) {
2212
+ if (!rest) return "";
2213
+ const segments = rest.split("/");
2214
+ if (segments.length === 3) {
2215
+ return `/${segments[2]}`;
2216
+ }
2217
+ return rest;
2218
+ }
2219
+ function aliasToImportPath(alias) {
2220
+ const trimmed = alias.replace(/^\.\//, "").replace(/\/$/, "");
2221
+ if (trimmed.startsWith("src/")) {
2222
+ return `@/${trimmed.slice("src/".length)}`;
2223
+ }
2224
+ return `@/${trimmed}`;
2225
+ }
2226
+
2227
+ // src/core/ui-installer.ts
2228
+ var DESIGN_COMPONENTS_DIR = ".teamix-evo/design/components";
2229
+ async function installUiEntries(options) {
2230
+ const {
2231
+ projectRoot,
2232
+ manifest,
2233
+ packageRoot,
2234
+ aliases,
2235
+ requested,
2236
+ skipExisting = true
2237
+ } = options;
2238
+ const orderedIds = resolveUiEntryOrder(manifest.entries, requested);
2239
+ const idToEntry = new Map(manifest.entries.map((e) => [e.id, e]));
2240
+ const resources = [];
2241
+ const npmDeps = {};
2242
+ const metaFiles = [];
2243
+ let written = 0;
2244
+ let skipped = 0;
2245
+ for (const id of orderedIds) {
2246
+ const entry = idToEntry.get(id);
2247
+ if (!entry) continue;
2248
+ if (entry.dependencies) {
2249
+ for (const [name, range] of Object.entries(entry.dependencies)) {
2250
+ npmDeps[name] = range;
2251
+ }
2252
+ }
2253
+ for (const file of entry.files) {
2254
+ const targetAbs = resolveTargetPath(projectRoot, aliases, entry, file);
2255
+ const exists = await fileExists(targetAbs);
2256
+ if (exists && skipExisting && (entry.updateStrategy ?? "frozen") === "frozen") {
2257
+ logger.info(` skip (frozen, exists): ${rel(projectRoot, targetAbs)}`);
2258
+ skipped++;
2259
+ continue;
2260
+ }
2261
+ const sourceAbs = path15.resolve(packageRoot, file.source);
2262
+ const raw = await fs13.readFile(sourceAbs, "utf-8");
2263
+ const transformed = rewriteImports(raw, aliases);
2264
+ await writeFileSafe(targetAbs, transformed);
2265
+ written++;
2266
+ logger.info(` write: ${rel(projectRoot, targetAbs)}`);
2267
+ resources.push({
2268
+ id: `${entry.id}:${file.targetName}`,
2269
+ target: targetAbs,
2270
+ hash: computeHash(transformed),
2271
+ strategy: entry.updateStrategy ?? "frozen"
2272
+ });
2273
+ }
2274
+ if (entry.meta) {
2275
+ const metaSourceAbs = path15.resolve(packageRoot, entry.meta);
2276
+ const metaContent = await fs13.readFile(metaSourceAbs, "utf-8");
2277
+ const metaTargetAbs = path15.join(
2278
+ projectRoot,
2279
+ DESIGN_COMPONENTS_DIR,
2280
+ `${entry.id}.meta.md`
2281
+ );
2282
+ await writeFileSafe(metaTargetAbs, metaContent);
2283
+ metaFiles.push(metaTargetAbs);
2284
+ resources.push({
2285
+ id: `${entry.id}:meta`,
2286
+ target: metaTargetAbs,
2287
+ hash: computeHash(metaContent),
2288
+ strategy: "regenerable"
2289
+ });
2290
+ logger.info(` meta: ${rel(projectRoot, metaTargetAbs)}`);
2291
+ }
2292
+ }
2293
+ return {
2294
+ orderedIds,
2295
+ resources,
2296
+ npmDependencies: npmDeps,
2297
+ written,
2298
+ skipped,
2299
+ metaFiles
2300
+ };
2301
+ }
2302
+ function resolveTargetPath(projectRoot, aliases, entry, file) {
2303
+ const aliasDir = aliases[file.targetAlias];
2304
+ if (!aliasDir) {
2305
+ throw new Error(
2306
+ `Entry "${entry.id}" requires alias "${file.targetAlias}" but it is not configured.`
2307
+ );
2308
+ }
2309
+ return path15.join(projectRoot, aliasDir, file.targetName);
2310
+ }
2311
+ function rel(projectRoot, abs) {
2312
+ return path15.relative(projectRoot, abs);
2313
+ }
2314
+
2315
+ // src/core/ui-add.ts
2316
+ var DEFAULT_UI_PACKAGE = "@teamix-evo/ui";
2317
+ async function runUiAdd(options) {
2318
+ const { projectRoot, ids, overwrite } = options;
2319
+ const packageName = options.packageName ?? DEFAULT_UI_PACKAGE;
2320
+ if (ids.length === 0) {
2321
+ throw new Error("At least one entry id must be provided.");
2322
+ }
2323
+ const config = await readProjectConfig(projectRoot);
2324
+ const uiCfg = config?.packages?.ui;
2325
+ if (!config || !uiCfg?.aliases) {
2326
+ throw new Error(
2327
+ "UI not initialized. Run `runUiInit` (or `teamix-evo ui init`) first."
2328
+ );
2329
+ }
2330
+ const { manifest, packageRoot } = await loadUiData(packageName);
2331
+ const knownIds = new Set(manifest.entries.map((e) => e.id));
2332
+ const unknown = ids.filter((id) => !knownIds.has(id));
2333
+ if (unknown.length > 0) {
2334
+ throw new Error(
2335
+ `Unknown entry id(s): ${unknown.map((s) => `"${s}"`).join(", ")}. Run \`teamix-evo ui list\` to see options.`
2336
+ );
2337
+ }
2338
+ const result = await installUiEntries({
2339
+ projectRoot,
2340
+ manifest,
2341
+ packageRoot,
2342
+ aliases: uiCfg.aliases,
2343
+ requested: ids,
2344
+ skipExisting: !overwrite
2345
+ });
2346
+ const installed = await readInstalledManifest(
2347
+ projectRoot
2348
+ ) ?? { schemaVersion: 1, installed: [] };
2349
+ const idx = installed.installed.findIndex((p) => p.package === packageName);
2350
+ const prior = idx >= 0 ? installed.installed[idx] : null;
2351
+ const mergedResources = mergeResources(
2352
+ prior?.resources ?? [],
2353
+ result.resources
2354
+ );
2355
+ const entry = {
2356
+ package: packageName,
2357
+ variant: "_flat",
2358
+ version: manifest.version,
2359
+ installedAt: (/* @__PURE__ */ new Date()).toISOString(),
2360
+ resources: mergedResources
2361
+ };
2362
+ if (idx >= 0) installed.installed[idx] = entry;
2363
+ else installed.installed.push(entry);
2364
+ await writeInstalledManifest(projectRoot, installed);
2365
+ if (uiCfg.version !== manifest.version) {
2366
+ uiCfg.version = manifest.version;
2367
+ await writeProjectConfig(projectRoot, config);
2368
+ }
2369
+ return {
2370
+ packageName,
2371
+ orderedIds: result.orderedIds,
2372
+ written: result.written,
2373
+ skipped: result.skipped,
2374
+ metaFiles: result.metaFiles,
2375
+ npmDependencies: result.npmDependencies,
2376
+ resources: result.resources
2377
+ };
2378
+ }
2379
+ function mergeResources(prior, next) {
2380
+ const merged = /* @__PURE__ */ new Map();
2381
+ for (const r of prior) merged.set(r.id, r);
2382
+ for (const r of next) merged.set(r.id, r);
2383
+ return Array.from(merged.values());
2384
+ }
2385
+
2386
+ // src/commands/ui/add.ts
2387
+ var addCommand2 = new Command15("add").description(
2388
+ "\u5B89\u88C5\u4E00\u4E2A\u6216\u591A\u4E2A ui entry\uFF08\u6309 id\uFF0C\u81EA\u52A8\u5C55\u5F00 registryDependencies\uFF09"
2389
+ ).argument("<ids...>", 'entry id \u5217\u8868\uFF0C\u5982 "button" "dialog"').option("--overwrite", "\u5373\u4F7F\u76EE\u6807\u6587\u4EF6\u5DF2\u5B58\u5728\u4E5F\u8986\u76D6\uFF08\u7ED5\u8FC7 frozen \u8DF3\u8FC7\uFF09").action(async (ids, opts) => {
2390
+ try {
2391
+ const ide = detectIde();
2392
+ const projectRoot = ide.getProjectRoot();
2393
+ logger.info(`Installing entries: ${ids.join(", ")}`);
2394
+ const result = await runUiAdd({
2395
+ projectRoot,
2396
+ ids,
2397
+ overwrite: opts.overwrite
2398
+ });
2399
+ logger.success(
2400
+ `UI add complete: ${result.written} written, ${result.skipped} skipped, ${result.metaFiles.length} meta.`
2401
+ );
2402
+ logger.info("");
2403
+ logger.info(`Resolved order: ${result.orderedIds.join(" \u2192 ")}`);
2404
+ const npmDeps = Object.entries(result.npmDependencies);
2405
+ if (npmDeps.length > 0) {
2406
+ logger.info("");
2407
+ logger.info("Install npm dependencies in your project:");
2408
+ const installCmd = npmDeps.map(([name, range]) => `${name}@${range}`).join(" ");
2409
+ logger.info(` pnpm add ${installCmd}`);
2410
+ logger.info(` # or: npm install ${installCmd}`);
2411
+ }
2412
+ if (result.metaFiles.length > 0) {
2413
+ logger.info("");
2414
+ logger.info(
2415
+ "Component meta dropped under .teamix-evo/design/components/ (AI-readable)."
2416
+ );
2417
+ }
2418
+ } catch (err) {
2419
+ const message = err.message;
2420
+ if (message.startsWith("UI not initialized")) {
2421
+ logger.error("UI not initialized. Run `npx teamix-evo ui init` first.");
2422
+ } else {
2423
+ logger.error(`Failed to add ui entries: ${message}`);
2424
+ }
2425
+ logger.debug(err.stack ?? "");
2426
+ process.exitCode = 1;
2427
+ }
2428
+ });
2429
+
2430
+ // src/commands/ui/list.ts
2431
+ import { Command as Command16 } from "commander";
2432
+
2433
+ // src/core/ui-list.ts
2434
+ var DEFAULT_UI_PACKAGE2 = "@teamix-evo/ui";
2435
+ async function runUiList(options) {
2436
+ const { projectRoot, installedOnly } = options;
2437
+ const packageName = options.packageName ?? DEFAULT_UI_PACKAGE2;
2438
+ const { manifest } = await loadUiData(packageName);
2439
+ const installedManifest = await readInstalledManifest(projectRoot);
2440
+ const installedIds = /* @__PURE__ */ new Set();
2441
+ const uiPkg = installedManifest?.installed.find(
2442
+ (p) => p.package === packageName
2443
+ );
2444
+ for (const r of uiPkg?.resources ?? []) {
2445
+ const colon = r.id.indexOf(":");
2446
+ installedIds.add(colon >= 0 ? r.id.slice(0, colon) : r.id);
2447
+ }
2448
+ const entries = manifest.entries.filter((e) => !installedOnly || installedIds.has(e.id)).map((e) => ({
2449
+ id: e.id,
2450
+ type: e.type,
2451
+ description: e.description,
2452
+ installed: installedIds.has(e.id)
2453
+ }));
2454
+ return {
2455
+ packageName,
2456
+ total: manifest.entries.length,
2457
+ installedCount: installedIds.size,
2458
+ entries
2459
+ };
2460
+ }
2461
+
2462
+ // src/commands/ui/list.ts
2463
+ var listCommand3 = new Command16("list").description("\u5217\u51FA @teamix-evo/ui \u7684\u6240\u6709 entry \u53CA\u5DF2\u5B89\u88C5\u72B6\u6001").option("--installed", "\u4EC5\u5C55\u793A\u5DF2\u5B89\u88C5\u7684 entry").action(async (opts) => {
2464
+ try {
2465
+ const ide = detectIde();
2466
+ const projectRoot = ide.getProjectRoot();
2467
+ const { entries, total, installedCount } = await runUiList({
2468
+ projectRoot,
2469
+ installedOnly: opts.installed
2470
+ });
2471
+ if (entries.length === 0) {
2472
+ logger.info(
2473
+ opts.installed ? "No ui entries installed." : "No ui entries available."
2474
+ );
2475
+ return;
2476
+ }
2477
+ const idWidth = Math.max(...entries.map((e) => e.id.length), 4);
2478
+ const typeWidth = Math.max(...entries.map((e) => e.type.length), 4);
2479
+ logger.info(
2480
+ `${"ID".padEnd(idWidth)} ${"TYPE".padEnd(
2481
+ typeWidth
2482
+ )} STATUS DESCRIPTION`
2483
+ );
2484
+ logger.info(
2485
+ `${"\u2500".repeat(idWidth)} ${"\u2500".repeat(
2486
+ typeWidth
2487
+ )} \u2500\u2500\u2500\u2500\u2500\u2500\u2500 \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500`
2488
+ );
2489
+ for (const e of entries) {
2490
+ const status = e.installed ? "INSTALLED" : "\u2013";
2491
+ logger.info(
2492
+ `${e.id.padEnd(idWidth)} ${e.type.padEnd(
2493
+ typeWidth
2494
+ )} ${status.padEnd(7)} ${e.description}`
2495
+ );
2496
+ }
2497
+ logger.info("");
2498
+ logger.info(
2499
+ `Total: ${total} entr${total === 1 ? "y" : "ies"}, ${installedCount} installed.`
2500
+ );
2501
+ } catch (err) {
2502
+ logger.error(`Failed to list ui entries: ${err.message}`);
2503
+ logger.debug(err.stack ?? "");
2504
+ process.exitCode = 1;
2505
+ }
2506
+ });
2507
+
2508
+ // src/commands/ui/index.ts
2509
+ var uiCommand = new Command17("ui").description(
2510
+ "\u7BA1\u7406 teamix-evo ui \u7EC4\u4EF6\uFF08\u6E90\u7801\u6CE8\u5165\u5F0F\u5B89\u88C5\uFF0Cshadcn \u98CE\u683C\uFF09"
2511
+ );
2512
+ uiCommand.addCommand(initCommand2);
2513
+ uiCommand.addCommand(addCommand2);
2514
+ uiCommand.addCommand(listCommand3);
2515
+
2516
+ // src/commands/biz-ui/index.ts
2517
+ import { Command as Command20 } from "commander";
2518
+
2519
+ // src/commands/biz-ui/add.ts
2520
+ import { Command as Command18 } from "commander";
2521
+
2522
+ // src/core/variant-ui-add.ts
2523
+ import * as path16 from "path";
2524
+ import { createRequire as createRequire4 } from "module";
2525
+ import {
2526
+ loadVariantUiPackageCatalog,
2527
+ loadVariantUiPackageManifest
2528
+ } from "@teamix-evo/registry";
2529
+ var require5 = createRequire4(import.meta.url);
2530
+ function resolvePackageRoot3(packageName) {
2531
+ const pkgJsonPath = require5.resolve(`${packageName}/package.json`);
2532
+ return path16.dirname(pkgJsonPath);
2533
+ }
2534
+ async function runVariantUiAdd(packageName, options) {
2535
+ const { projectRoot, variant, ids, overwrite } = options;
2536
+ const fullPackageName = options.packageName ?? `@teamix-evo/${packageName}`;
2537
+ if (ids.length === 0) {
2538
+ throw new Error("At least one entry id must be provided.");
2539
+ }
2540
+ const config = await readProjectConfig(projectRoot);
2541
+ const uiCfg = config?.packages?.ui;
2542
+ if (!config || !uiCfg?.aliases) {
2543
+ throw new Error(
2544
+ `UI not initialized. Run \`teamix-evo ui init\` first \u2014 \`${packageName} add\` writes into the same alias map (business / templates).`
2545
+ );
2546
+ }
2547
+ const packageRoot = options.packageRoot ?? resolvePackageRoot3(fullPackageName);
2548
+ const catalog = await loadVariantUiPackageCatalog(packageRoot);
2549
+ if (!catalog.variants.some((v) => v.name === variant)) {
2550
+ const known = catalog.variants.map((v) => v.name).join(", ");
2551
+ throw new Error(
2552
+ `Variant "${variant}" not found in ${fullPackageName}. Known variants: ${known}. Hint: \`teamix-evo ${packageName} list-variants\` shows all.`
2553
+ );
2554
+ }
2555
+ const variantDir = path16.join(packageRoot, "variants", variant);
2556
+ const variantManifest = await loadVariantUiPackageManifest(variantDir);
2557
+ const knownIds = new Set(variantManifest.entries.map((e) => e.id));
2558
+ const unknown = ids.filter((id) => !knownIds.has(id));
2559
+ if (unknown.length > 0) {
2560
+ throw new Error(
2561
+ `Unknown entry id(s) in ${packageName}#${variant}: ${unknown.map((s) => `"${s}"`).join(", ")}. Run \`teamix-evo ${packageName} list --variant ${variant}\` to see options.`
2562
+ );
2563
+ }
2564
+ const adaptedManifest = {
2565
+ schemaVersion: 1,
2566
+ package: "ui",
2567
+ version: variantManifest.version,
2568
+ engines: variantManifest.engines,
2569
+ entries: variantManifest.entries
2570
+ };
2571
+ const result = await installUiEntries({
2572
+ projectRoot,
2573
+ manifest: adaptedManifest,
2574
+ packageRoot: variantDir,
2575
+ // sources resolved relative to variant dir
2576
+ aliases: uiCfg.aliases,
2577
+ requested: ids,
2578
+ skipExisting: !overwrite
2579
+ });
2580
+ const installed = await readInstalledManifest(
2581
+ projectRoot
2582
+ ) ?? { schemaVersion: 1, installed: [] };
2583
+ const idx = installed.installed.findIndex(
2584
+ (p) => p.package === fullPackageName && p.variant === variant
2585
+ );
2586
+ const prior = idx >= 0 ? installed.installed[idx] : null;
2587
+ const mergedResources = mergeResources2(
2588
+ prior?.resources ?? [],
2589
+ result.resources
2590
+ );
2591
+ const entry = {
2592
+ package: fullPackageName,
2593
+ variant,
2594
+ version: variantManifest.version,
2595
+ installedAt: (/* @__PURE__ */ new Date()).toISOString(),
2596
+ resources: mergedResources
2597
+ };
2598
+ if (idx >= 0) installed.installed[idx] = entry;
2599
+ else installed.installed.push(entry);
2600
+ await writeInstalledManifest(projectRoot, installed);
2601
+ return {
2602
+ packageName: fullPackageName,
2603
+ variant,
2604
+ orderedIds: result.orderedIds,
2605
+ written: result.written,
2606
+ skipped: result.skipped,
2607
+ metaFiles: result.metaFiles,
2608
+ npmDependencies: result.npmDependencies,
2609
+ resources: result.resources
2610
+ };
2611
+ }
2612
+ function mergeResources2(prior, next) {
2613
+ const merged = /* @__PURE__ */ new Map();
2614
+ for (const r of prior) merged.set(r.id, r);
2615
+ for (const r of next) merged.set(r.id, r);
2616
+ return Array.from(merged.values());
2617
+ }
2618
+ async function runBizUiAdd(options) {
2619
+ return runVariantUiAdd("biz-ui", options);
2620
+ }
2621
+ async function runTemplatesAdd(options) {
2622
+ return runVariantUiAdd("templates", options);
2623
+ }
2624
+ async function listVariantUi(packageName, packageRoot) {
2625
+ const fullPackageName = `@teamix-evo/${packageName}`;
2626
+ const root = packageRoot ?? resolvePackageRoot3(fullPackageName);
2627
+ const catalog = await loadVariantUiPackageCatalog(root);
2628
+ return {
2629
+ packageName: fullPackageName,
2630
+ variants: catalog.variants.map((v) => ({
2631
+ name: v.name,
2632
+ displayName: v.displayName,
2633
+ version: v.version,
2634
+ description: v.description
2635
+ }))
2636
+ };
2637
+ }
2638
+ async function listBizUiVariants(packageRoot) {
2639
+ return listVariantUi("biz-ui", packageRoot);
2640
+ }
2641
+ async function listTemplatesVariants(packageRoot) {
2642
+ return listVariantUi("templates", packageRoot);
2643
+ }
2644
+
2645
+ // src/commands/biz-ui/add.ts
2646
+ var addCommand3 = new Command18("add").description(
2647
+ "\u5B89\u88C5\u4E00\u4E2A\u6216\u591A\u4E2A\u4E1A\u52A1 UI \u7EC4\u4EF6(\u6309 id,\u81EA\u52A8\u5C55\u5F00 ui \u5305\u7684 registryDependencies)"
2648
+ ).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(
2649
+ async (ids, opts) => {
2650
+ try {
2651
+ if (!opts.variant) {
2652
+ throw new Error(
2653
+ "--variant <name> is required. Run `teamix-evo biz-ui list-variants` to see available variants."
2654
+ );
2655
+ }
2656
+ const ide = detectIde();
2657
+ const projectRoot = ide.getProjectRoot();
2658
+ logger.info(
2659
+ `Installing biz-ui entries from variant "${opts.variant}": ${ids.join(", ")}`
2660
+ );
2661
+ const result = await runBizUiAdd({
2662
+ projectRoot,
2663
+ variant: opts.variant,
2664
+ ids,
2665
+ overwrite: opts.overwrite
2666
+ });
2667
+ logger.success(
2668
+ `biz-ui add complete: ${result.written} written, ${result.skipped} skipped, ${result.metaFiles.length} meta.`
2669
+ );
2670
+ logger.info("");
2671
+ logger.info(`Variant: ${result.variant}`);
2672
+ logger.info(`Resolved order: ${result.orderedIds.join(" \u2192 ")}`);
2673
+ const npmDeps = Object.entries(result.npmDependencies);
2674
+ if (npmDeps.length > 0) {
2675
+ logger.info("");
2676
+ logger.info("Install npm dependencies in your project:");
2677
+ const installCmd = npmDeps.map(([name, range]) => `${name}@${range}`).join(" ");
2678
+ logger.info(` pnpm add ${installCmd}`);
2679
+ }
2680
+ } catch (err) {
2681
+ logger.error(`Failed: ${err.message}`);
2682
+ logger.debug(err.stack ?? "");
2683
+ process.exitCode = 1;
2684
+ }
2685
+ }
2686
+ );
2687
+
2688
+ // src/commands/biz-ui/list-variants.ts
2689
+ import { Command as Command19 } from "commander";
2690
+ var listVariantsCommand2 = new Command19("list-variants").description("\u5217\u51FA @teamix-evo/biz-ui \u5305\u5185\u63D0\u4F9B\u7684\u6240\u6709\u4E1A\u52A1\u53D8\u4F53").action(async () => {
2691
+ try {
2692
+ const result = await listBizUiVariants();
2693
+ logger.info(`Available biz-ui variants in ${result.packageName}:`);
2694
+ logger.info("");
2695
+ if (result.variants.length === 0) {
2696
+ logger.info(" (no variants yet)");
2697
+ return;
2698
+ }
2699
+ for (const v of result.variants) {
2700
+ logger.info(` ${v.name} (${v.displayName}) \u2014 v${v.version}`);
2701
+ if (v.description) logger.info(` ${v.description}`);
2702
+ logger.info("");
2703
+ }
2704
+ logger.info("Install from a variant: teamix-evo biz-ui add <id> --variant <name>");
2705
+ } catch (err) {
2706
+ logger.error(`Failed: ${err.message}`);
2707
+ process.exitCode = 1;
2708
+ }
2709
+ });
2710
+
2711
+ // src/commands/biz-ui/index.ts
2712
+ var bizUiCommand = new Command20("biz-ui").description(
2713
+ "\u7BA1\u7406\u4E1A\u52A1 UI \u7EC4\u4EF6(\u53D8\u4F53\u611F\u77E5 \u2014 \u4E0E design / templates \u540C\u53D8\u4F53\u540D\u7A7A\u95F4)"
2714
+ );
2715
+ bizUiCommand.addCommand(addCommand3);
2716
+ bizUiCommand.addCommand(listVariantsCommand2);
2717
+
2718
+ // src/commands/templates/index.ts
2719
+ import { Command as Command23 } from "commander";
2720
+
2721
+ // src/commands/templates/add.ts
2722
+ import { Command as Command21 } from "commander";
2723
+ var addCommand4 = new Command21("add").description(
2724
+ "\u5B89\u88C5\u4E00\u4E2A\u6216\u591A\u4E2A\u9875\u9762\u6A21\u677F(\u6309 id,\u81EA\u52A8\u5C55\u5F00 ui \u5305\u7684 registryDependencies)"
2725
+ ).argument("<ids...>", '\u6A21\u677F id \u5217\u8868,\u5982 "list-detail-page"').option("--variant <name>", '\u53D8\u4F53 id(\u5FC5\u586B,\u5982 "opentrek"\u3001"uni-manager")').option("--overwrite", "\u5373\u4F7F\u76EE\u6807\u6587\u4EF6\u5DF2\u5B58\u5728\u4E5F\u8986\u76D6").action(
2726
+ async (ids, opts) => {
2727
+ try {
2728
+ if (!opts.variant) {
2729
+ throw new Error(
2730
+ "--variant <name> is required. Run `teamix-evo templates list-variants` to see available variants."
2731
+ );
2732
+ }
2733
+ const ide = detectIde();
2734
+ const projectRoot = ide.getProjectRoot();
2735
+ logger.info(
2736
+ `Installing templates from variant "${opts.variant}": ${ids.join(", ")}`
2737
+ );
2738
+ const result = await runTemplatesAdd({
2739
+ projectRoot,
2740
+ variant: opts.variant,
2741
+ ids,
2742
+ overwrite: opts.overwrite
2743
+ });
2744
+ logger.success(
2745
+ `templates add complete: ${result.written} written, ${result.skipped} skipped, ${result.metaFiles.length} meta.`
2746
+ );
2747
+ logger.info("");
2748
+ logger.info(`Variant: ${result.variant}`);
2749
+ logger.info(`Resolved order: ${result.orderedIds.join(" \u2192 ")}`);
2750
+ const npmDeps = Object.entries(result.npmDependencies);
2751
+ if (npmDeps.length > 0) {
2752
+ logger.info("");
2753
+ logger.info("Install npm dependencies in your project:");
2754
+ const installCmd = npmDeps.map(([name, range]) => `${name}@${range}`).join(" ");
2755
+ logger.info(` pnpm add ${installCmd}`);
2756
+ }
2757
+ } catch (err) {
2758
+ logger.error(`Failed: ${err.message}`);
2759
+ logger.debug(err.stack ?? "");
2760
+ process.exitCode = 1;
2761
+ }
2762
+ }
2763
+ );
2764
+
2765
+ // src/commands/templates/list-variants.ts
2766
+ import { Command as Command22 } from "commander";
2767
+ var listVariantsCommand3 = new Command22("list-variants").description("\u5217\u51FA @teamix-evo/templates \u5305\u5185\u63D0\u4F9B\u7684\u6240\u6709\u9875\u9762\u6A21\u677F\u53D8\u4F53").action(async () => {
2768
+ try {
2769
+ const result = await listTemplatesVariants();
2770
+ logger.info(`Available templates variants in ${result.packageName}:`);
2771
+ logger.info("");
2772
+ if (result.variants.length === 0) {
2773
+ logger.info(" (no variants yet)");
2774
+ return;
2775
+ }
2776
+ for (const v of result.variants) {
2777
+ logger.info(` ${v.name} (${v.displayName}) \u2014 v${v.version}`);
2778
+ if (v.description) logger.info(` ${v.description}`);
2779
+ logger.info("");
2780
+ }
2781
+ logger.info("Install from a variant: teamix-evo templates add <id> --variant <name>");
2782
+ } catch (err) {
2783
+ logger.error(`Failed: ${err.message}`);
2784
+ process.exitCode = 1;
2785
+ }
2786
+ });
2787
+
2788
+ // src/commands/templates/index.ts
2789
+ var templatesCommand = new Command23("templates").description(
2790
+ "\u7BA1\u7406\u9875\u9762\u6A21\u677F(\u53D8\u4F53\u611F\u77E5 \u2014 \u4E0E design / biz-ui \u540C\u53D8\u4F53\u540D\u7A7A\u95F4)"
2791
+ );
2792
+ templatesCommand.addCommand(addCommand4);
2793
+ templatesCommand.addCommand(listVariantsCommand3);
771
2794
 
772
2795
  // src/index.ts
773
- var program = new Command5();
774
- program.name("teamix-evo").description("Where ideas evolve. \u2014 AI Coding \u5957\u4EF6").version("0.0.0");
2796
+ var require6 = createRequire5(import.meta.url);
2797
+ var { version } = require6("../package.json");
2798
+ var program = new Command24();
2799
+ program.name("teamix-evo").description("Where ideas evolve. \u2014 AI Coding \u5957\u4EF6").version(version);
775
2800
  program.addCommand(designCommand);
2801
+ program.addCommand(skillsCommand);
2802
+ program.addCommand(uiCommand);
2803
+ program.addCommand(bizUiCommand);
2804
+ program.addCommand(templatesCommand);
776
2805
  program.parse();
777
2806
  //# sourceMappingURL=index.js.map