teamix-evo 0.2.0 → 0.3.1
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/README.md +76 -50
- package/dist/core/index.d.ts +94 -16
- package/dist/core/index.js +727 -372
- package/dist/core/index.js.map +1 -1
- package/dist/index.js +1758 -997
- package/dist/index.js.map +1 -1
- package/package.json +11 -5
package/dist/index.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
// src/index.ts
|
|
4
|
-
import { Command as
|
|
5
|
-
import { createRequire as
|
|
4
|
+
import { Command as Command24 } from "commander";
|
|
5
|
+
import { createRequire as createRequire5 } from "module";
|
|
6
6
|
|
|
7
7
|
// src/commands/design/index.ts
|
|
8
|
-
import { Command as
|
|
8
|
+
import { Command as Command6 } from "commander";
|
|
9
9
|
|
|
10
10
|
// src/commands/design/init.ts
|
|
11
11
|
import { Command } from "commander";
|
|
@@ -66,11 +66,19 @@ function detectIde() {
|
|
|
66
66
|
return new QoderAdapter();
|
|
67
67
|
}
|
|
68
68
|
|
|
69
|
-
// src/core/
|
|
70
|
-
import * as
|
|
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
|
|
71
80
|
import * as fs from "fs/promises";
|
|
72
|
-
import
|
|
73
|
-
import { loadVariantManifest } from "@teamix-evo/registry";
|
|
81
|
+
import * as path3 from "path";
|
|
74
82
|
|
|
75
83
|
// src/utils/logger.ts
|
|
76
84
|
import { red, yellow, cyan, green, gray } from "kolorist";
|
|
@@ -95,52 +103,20 @@ var logger = {
|
|
|
95
103
|
}
|
|
96
104
|
};
|
|
97
105
|
|
|
98
|
-
// src/core/registry-client.ts
|
|
99
|
-
var require2 = createRequire(import.meta.url);
|
|
100
|
-
function resolvePackageRoot(packageName) {
|
|
101
|
-
const pkgJsonPath = require2.resolve(`${packageName}/package.json`);
|
|
102
|
-
return path3.dirname(pkgJsonPath);
|
|
103
|
-
}
|
|
104
|
-
async function loadVariantData(packageName, variant) {
|
|
105
|
-
const packageRoot = resolvePackageRoot(packageName);
|
|
106
|
-
const variantDir = path3.join(packageRoot, "library", variant);
|
|
107
|
-
logger.debug(`Resolved variant dir: ${variantDir}`);
|
|
108
|
-
logger.debug(`Package root: ${packageRoot}`);
|
|
109
|
-
const manifest = await loadVariantManifest(variantDir);
|
|
110
|
-
let data = {};
|
|
111
|
-
const dataPath = path3.join(variantDir, "_data.json");
|
|
112
|
-
try {
|
|
113
|
-
const raw = await fs.readFile(dataPath, "utf-8");
|
|
114
|
-
data = JSON.parse(raw);
|
|
115
|
-
} catch (err) {
|
|
116
|
-
if (err.code !== "ENOENT") {
|
|
117
|
-
throw err;
|
|
118
|
-
}
|
|
119
|
-
logger.debug(`No _data.json found at ${dataPath}, using empty data`);
|
|
120
|
-
}
|
|
121
|
-
return { manifest, data, variantDir, packageRoot };
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
// src/core/installer.ts
|
|
125
|
-
import * as path6 from "path";
|
|
126
|
-
import * as fs5 from "fs/promises";
|
|
127
|
-
|
|
128
106
|
// src/utils/fs.ts
|
|
129
|
-
import * as fs2 from "fs/promises";
|
|
130
|
-
import * as path4 from "path";
|
|
131
107
|
async function ensureDir(dir) {
|
|
132
|
-
await
|
|
108
|
+
await fs.mkdir(dir, { recursive: true });
|
|
133
109
|
}
|
|
134
110
|
async function writeFileSafe(filePath, content) {
|
|
135
|
-
const dir =
|
|
111
|
+
const dir = path3.dirname(filePath);
|
|
136
112
|
await ensureDir(dir);
|
|
137
113
|
const tmp = filePath + ".tmp";
|
|
138
|
-
await
|
|
139
|
-
await
|
|
114
|
+
await fs.writeFile(tmp, content, "utf-8");
|
|
115
|
+
await fs.rename(tmp, filePath);
|
|
140
116
|
}
|
|
141
117
|
async function readFileOrNull(filePath) {
|
|
142
118
|
try {
|
|
143
|
-
return await
|
|
119
|
+
return await fs.readFile(filePath, "utf-8");
|
|
144
120
|
} catch (err) {
|
|
145
121
|
if (err.code === "ENOENT") {
|
|
146
122
|
return null;
|
|
@@ -154,21 +130,21 @@ async function backupFile(filePath, projectRoot) {
|
|
|
154
130
|
logger.debug(`Skip backup: ${filePath} does not exist`);
|
|
155
131
|
return;
|
|
156
132
|
}
|
|
157
|
-
const rel2 =
|
|
133
|
+
const rel2 = path3.relative(projectRoot, filePath);
|
|
158
134
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
159
|
-
const backupPath =
|
|
135
|
+
const backupPath = path3.join(
|
|
160
136
|
projectRoot,
|
|
161
137
|
".teamix-evo",
|
|
162
138
|
".backups",
|
|
163
139
|
`${rel2}.${timestamp}.bak`
|
|
164
140
|
);
|
|
165
|
-
await ensureDir(
|
|
166
|
-
await
|
|
167
|
-
logger.debug(`Backed up ${rel2} \u2192 ${
|
|
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)}`);
|
|
168
144
|
}
|
|
169
145
|
async function fileExists(filePath) {
|
|
170
146
|
try {
|
|
171
|
-
await
|
|
147
|
+
await fs.access(filePath);
|
|
172
148
|
return true;
|
|
173
149
|
} catch {
|
|
174
150
|
return false;
|
|
@@ -182,157 +158,71 @@ function computeHash(content) {
|
|
|
182
158
|
return `sha256:${hash}`;
|
|
183
159
|
}
|
|
184
160
|
|
|
185
|
-
// src/
|
|
186
|
-
import
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
compiledCache.set(templateContent, compiled);
|
|
202
|
-
}
|
|
203
|
-
return compiled;
|
|
204
|
-
}
|
|
205
|
-
function renderTemplate(templateContent, data) {
|
|
206
|
-
const compiled = getCompiledTemplate(templateContent);
|
|
207
|
-
return compiled(data);
|
|
208
|
-
}
|
|
209
|
-
async function loadTemplateFile(filePath) {
|
|
210
|
-
return fs3.readFile(filePath, "utf-8");
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
// src/utils/path.ts
|
|
214
|
-
import * as path5 from "path";
|
|
215
|
-
import * as fs4 from "fs/promises";
|
|
216
|
-
function resolveSourcePath(source, variantDir, packageRoot) {
|
|
217
|
-
if (source.startsWith("_template/")) {
|
|
218
|
-
return path5.join(packageRoot, source);
|
|
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;
|
|
219
177
|
}
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
files.push(...await walkDir(fullPath));
|
|
229
|
-
} else if (entry.isFile()) {
|
|
230
|
-
files.push(fullPath);
|
|
231
|
-
}
|
|
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
|
+
};
|
|
232
186
|
}
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
const { projectRoot, manifest, data, variantDir, packageRoot } = options;
|
|
239
|
-
const installedResources = [];
|
|
240
|
-
for (const resource of manifest.resources) {
|
|
241
|
-
logger.debug(`Installing resource: ${resource.id} \u2192 ${resource.target}`);
|
|
242
|
-
if (resource.recursive) {
|
|
243
|
-
const results = await installRecursiveResource(
|
|
244
|
-
resource,
|
|
245
|
-
projectRoot,
|
|
246
|
-
data,
|
|
247
|
-
variantDir,
|
|
248
|
-
packageRoot
|
|
249
|
-
);
|
|
250
|
-
installedResources.push(...results);
|
|
251
|
-
} else {
|
|
252
|
-
const result = await installSingleResource(
|
|
253
|
-
resource,
|
|
254
|
-
projectRoot,
|
|
255
|
-
data,
|
|
256
|
-
variantDir,
|
|
257
|
-
packageRoot
|
|
258
|
-
);
|
|
259
|
-
installedResources.push(result);
|
|
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
|
}
|
|
193
|
+
return { target, strategy: "regenerable", isFrozen: false };
|
|
261
194
|
}
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
const sourcePath = resolveSourcePath(
|
|
269
|
-
resource.source,
|
|
270
|
-
variantDir,
|
|
271
|
-
packageRoot
|
|
272
|
-
);
|
|
273
|
-
const targetPath = path6.join(projectRoot, resource.target);
|
|
274
|
-
let content;
|
|
275
|
-
if (resource.template) {
|
|
276
|
-
const templateContent = await loadTemplateFile(sourcePath);
|
|
277
|
-
content = renderTemplate(templateContent, data);
|
|
278
|
-
} else {
|
|
279
|
-
content = await fs5.readFile(sourcePath, "utf-8");
|
|
195
|
+
if (FROZEN_FILES.has(relPath)) {
|
|
196
|
+
return {
|
|
197
|
+
target: path4.posix.join(TEAMIX_DIR, relPath),
|
|
198
|
+
strategy: "frozen",
|
|
199
|
+
isFrozen: true
|
|
200
|
+
};
|
|
280
201
|
}
|
|
281
|
-
await writeFileSafe(targetPath, content);
|
|
282
|
-
const hash = computeHash(content);
|
|
283
|
-
logger.debug(` Written: ${resource.target} (${resource.updateStrategy})`);
|
|
284
202
|
return {
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
strategy: resource.updateStrategy
|
|
203
|
+
target: path4.posix.join(TEAMIX_DIR, relPath),
|
|
204
|
+
strategy: "regenerable",
|
|
205
|
+
isFrozen: false
|
|
289
206
|
};
|
|
290
207
|
}
|
|
291
|
-
async function installRecursiveResource(resource, projectRoot, data, variantDir, packageRoot) {
|
|
292
|
-
const sourcePath = resolveSourcePath(
|
|
293
|
-
resource.source,
|
|
294
|
-
variantDir,
|
|
295
|
-
packageRoot
|
|
296
|
-
);
|
|
297
|
-
const targetDir = path6.join(projectRoot, resource.target);
|
|
298
|
-
const results = [];
|
|
299
|
-
await ensureDir(targetDir);
|
|
300
|
-
const entries = await walkDir(sourcePath);
|
|
301
|
-
for (const entry of entries) {
|
|
302
|
-
const relPath = path6.relative(sourcePath, entry);
|
|
303
|
-
let targetFile = path6.join(targetDir, relPath);
|
|
304
|
-
if (resource.template && targetFile.endsWith(".hbs")) {
|
|
305
|
-
targetFile = targetFile.slice(0, -4);
|
|
306
|
-
}
|
|
307
|
-
let content;
|
|
308
|
-
if (resource.template && entry.endsWith(".hbs")) {
|
|
309
|
-
const templateContent = await loadTemplateFile(entry);
|
|
310
|
-
content = renderTemplate(templateContent, data);
|
|
311
|
-
} else {
|
|
312
|
-
content = await fs5.readFile(entry, "utf-8");
|
|
313
|
-
}
|
|
314
|
-
await writeFileSafe(targetFile, content);
|
|
315
|
-
const hash = computeHash(content);
|
|
316
|
-
const targetRel = path6.relative(projectRoot, targetFile);
|
|
317
|
-
results.push({
|
|
318
|
-
id: `${resource.id}:${relPath}`,
|
|
319
|
-
target: targetRel,
|
|
320
|
-
hash,
|
|
321
|
-
strategy: resource.updateStrategy
|
|
322
|
-
});
|
|
323
|
-
logger.debug(` Written: ${targetRel}`);
|
|
324
|
-
}
|
|
325
|
-
return results;
|
|
326
|
-
}
|
|
327
208
|
|
|
328
209
|
// src/core/state.ts
|
|
329
|
-
import * as
|
|
330
|
-
import {
|
|
331
|
-
|
|
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";
|
|
332
218
|
var CONFIG_FILE = "config.json";
|
|
333
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";
|
|
334
224
|
function getTeamixDir(projectRoot) {
|
|
335
|
-
return
|
|
225
|
+
return path5.join(projectRoot, TEAMIX_DIR2);
|
|
336
226
|
}
|
|
337
227
|
async function ensureTeamixDir(projectRoot) {
|
|
338
228
|
const dir = getTeamixDir(projectRoot);
|
|
@@ -340,7 +230,7 @@ async function ensureTeamixDir(projectRoot) {
|
|
|
340
230
|
return dir;
|
|
341
231
|
}
|
|
342
232
|
async function readProjectConfig(projectRoot) {
|
|
343
|
-
const configPath =
|
|
233
|
+
const configPath = path5.join(projectRoot, TEAMIX_DIR2, CONFIG_FILE);
|
|
344
234
|
const raw = await readFileOrNull(configPath);
|
|
345
235
|
if (raw === null) return null;
|
|
346
236
|
try {
|
|
@@ -357,12 +247,12 @@ async function readProjectConfig(projectRoot) {
|
|
|
357
247
|
}
|
|
358
248
|
}
|
|
359
249
|
async function writeProjectConfig(projectRoot, config) {
|
|
360
|
-
const configPath =
|
|
250
|
+
const configPath = path5.join(projectRoot, TEAMIX_DIR2, CONFIG_FILE);
|
|
361
251
|
await writeFileSafe(configPath, JSON.stringify(config, null, 2) + "\n");
|
|
362
252
|
logger.debug(`Wrote config \u2192 ${configPath}`);
|
|
363
253
|
}
|
|
364
254
|
async function readInstalledManifest(projectRoot) {
|
|
365
|
-
const manifestPath =
|
|
255
|
+
const manifestPath = path5.join(projectRoot, TEAMIX_DIR2, MANIFEST_FILE);
|
|
366
256
|
const raw = await readFileOrNull(manifestPath);
|
|
367
257
|
if (raw === null) return null;
|
|
368
258
|
try {
|
|
@@ -379,559 +269,94 @@ async function readInstalledManifest(projectRoot) {
|
|
|
379
269
|
}
|
|
380
270
|
}
|
|
381
271
|
async function writeInstalledManifest(projectRoot, manifest) {
|
|
382
|
-
const manifestPath =
|
|
272
|
+
const manifestPath = path5.join(projectRoot, TEAMIX_DIR2, MANIFEST_FILE);
|
|
383
273
|
await writeFileSafe(manifestPath, JSON.stringify(manifest, null, 2) + "\n");
|
|
384
274
|
logger.debug(`Wrote manifest \u2192 ${manifestPath}`);
|
|
385
275
|
}
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
var DEFAULT_DESIGN_PACKAGE = "@teamix-evo/design";
|
|
389
|
-
async function runDesignInit(options) {
|
|
390
|
-
const { projectRoot, variant, tailwind, ide } = options;
|
|
391
|
-
const packageName = options.packageName ?? DEFAULT_DESIGN_PACKAGE;
|
|
392
|
-
await ensureTeamixDir(projectRoot);
|
|
393
|
-
const existingConfig = await readProjectConfig(projectRoot);
|
|
394
|
-
if (existingConfig?.packages?.design) {
|
|
395
|
-
return {
|
|
396
|
-
status: "already-initialized",
|
|
397
|
-
existingVariant: existingConfig.packages.design.variant
|
|
398
|
-
};
|
|
399
|
-
}
|
|
400
|
-
const { manifest, data, variantDir, packageRoot } = await loadVariantData(
|
|
401
|
-
packageName,
|
|
402
|
-
variant
|
|
403
|
-
);
|
|
404
|
-
const result = await installResources({
|
|
276
|
+
async function readDesignPackLock(projectRoot) {
|
|
277
|
+
const lockPath = path5.join(
|
|
405
278
|
projectRoot,
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
packageRoot
|
|
410
|
-
});
|
|
411
|
-
const config = {
|
|
412
|
-
$schema: "https://teamix-evo.dev/schema/config/v1.json",
|
|
413
|
-
schemaVersion: 1,
|
|
414
|
-
ide,
|
|
415
|
-
packages: {
|
|
416
|
-
design: {
|
|
417
|
-
variant,
|
|
418
|
-
version: manifest.version,
|
|
419
|
-
tailwind
|
|
420
|
-
}
|
|
421
|
-
}
|
|
422
|
-
};
|
|
423
|
-
await writeProjectConfig(projectRoot, config);
|
|
424
|
-
const installedManifest = {
|
|
425
|
-
schemaVersion: 1,
|
|
426
|
-
installed: [
|
|
427
|
-
{
|
|
428
|
-
package: packageName,
|
|
429
|
-
variant,
|
|
430
|
-
version: manifest.version,
|
|
431
|
-
installedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
432
|
-
resources: result.resources
|
|
433
|
-
}
|
|
434
|
-
]
|
|
435
|
-
};
|
|
436
|
-
await writeInstalledManifest(projectRoot, installedManifest);
|
|
437
|
-
return {
|
|
438
|
-
status: "installed",
|
|
439
|
-
packageName,
|
|
440
|
-
variant,
|
|
441
|
-
version: manifest.version,
|
|
442
|
-
tailwind,
|
|
443
|
-
count: result.count,
|
|
444
|
-
resources: result.resources
|
|
445
|
-
};
|
|
446
|
-
}
|
|
447
|
-
|
|
448
|
-
// src/commands/design/init.ts
|
|
449
|
-
var DEFAULT_VARIANT = "opentrek";
|
|
450
|
-
var DEFAULT_TAILWIND = "v4";
|
|
451
|
-
function normalizeTailwind(input) {
|
|
452
|
-
if (input === void 0) return DEFAULT_TAILWIND;
|
|
453
|
-
const lower = input.toLowerCase();
|
|
454
|
-
if (lower === "v3" || lower === "3") return "v3";
|
|
455
|
-
if (lower === "v4" || lower === "4") return "v4";
|
|
456
|
-
throw new Error(
|
|
457
|
-
`Invalid --tailwind value: "${input}". Expected "v3" or "v4".`
|
|
279
|
+
TEAMIX_DIR2,
|
|
280
|
+
DESIGN_DIR,
|
|
281
|
+
DESIGN_LOCK_FILE
|
|
458
282
|
);
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
"--tailwind <version>",
|
|
462
|
-
"\u9879\u76EE\u4F7F\u7528\u7684 Tailwind CSS \u4E3B\u7248\u672C\uFF08v3 | v4\uFF09",
|
|
463
|
-
DEFAULT_TAILWIND
|
|
464
|
-
).action(async (variant, opts) => {
|
|
283
|
+
const raw = await readFileOrNull(lockPath);
|
|
284
|
+
if (raw === null) return null;
|
|
465
285
|
try {
|
|
466
|
-
const
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
`Initializing design system: variant="${variant}", tailwind="${tailwind}"`
|
|
471
|
-
);
|
|
472
|
-
logger.debug(`Project root: ${projectRoot}`);
|
|
473
|
-
logger.debug(`IDE: ${ide.name}`);
|
|
474
|
-
logger.info(`Loading variant "${variant}"...`);
|
|
475
|
-
logger.info("Installing resources...");
|
|
476
|
-
const result = await runDesignInit({
|
|
477
|
-
projectRoot,
|
|
478
|
-
variant,
|
|
479
|
-
tailwind,
|
|
480
|
-
ide: ide.name
|
|
481
|
-
});
|
|
482
|
-
if (result.status === "already-initialized") {
|
|
483
|
-
logger.warn(
|
|
484
|
-
`Design system already initialized (variant: ${result.existingVariant}). Use "teamix-evo design update" to update.`
|
|
485
|
-
);
|
|
486
|
-
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;
|
|
487
290
|
}
|
|
488
|
-
|
|
489
|
-
`Design system initialized: ${result.packageName} v${result.version}`
|
|
490
|
-
);
|
|
491
|
-
logger.info(` Variant: ${result.variant}`);
|
|
492
|
-
logger.info(` Tailwind: ${result.tailwind}`);
|
|
493
|
-
logger.info(` Resources: ${result.count} files installed`);
|
|
494
|
-
logger.info("");
|
|
495
|
-
logger.info('Run "teamix-evo design update" to update resources later.');
|
|
291
|
+
return parsed.data;
|
|
496
292
|
} catch (err) {
|
|
497
|
-
logger.
|
|
498
|
-
|
|
499
|
-
|
|
293
|
+
logger.warn(
|
|
294
|
+
`Failed to parse design pack.lock.json: ${err.message}`
|
|
295
|
+
);
|
|
296
|
+
return null;
|
|
500
297
|
}
|
|
501
|
-
}
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
async function
|
|
511
|
-
const
|
|
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(
|
|
512
309
|
projectRoot,
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
packageRoot,
|
|
517
|
-
installedManifest,
|
|
518
|
-
packageName
|
|
519
|
-
} = options;
|
|
520
|
-
const updatedResources = [];
|
|
521
|
-
const summary = { overwritten: 0, managed: 0, skipped: 0, created: 0 };
|
|
522
|
-
const installedPkg = installedManifest.installed.find(
|
|
523
|
-
(p) => p.package === packageName && p.variant === manifest.variant
|
|
310
|
+
TEAMIX_DIR2,
|
|
311
|
+
SKILLS_DIR,
|
|
312
|
+
SKILLS_LOCK_FILE
|
|
524
313
|
);
|
|
525
|
-
const
|
|
526
|
-
if (
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
const results = await updateRecursiveResource(
|
|
534
|
-
resource,
|
|
535
|
-
projectRoot,
|
|
536
|
-
data,
|
|
537
|
-
variantDir,
|
|
538
|
-
packageRoot,
|
|
539
|
-
installedMap,
|
|
540
|
-
summary
|
|
541
|
-
);
|
|
542
|
-
updatedResources.push(...results);
|
|
543
|
-
} else {
|
|
544
|
-
const result = await updateSingleResource(
|
|
545
|
-
resource,
|
|
546
|
-
projectRoot,
|
|
547
|
-
data,
|
|
548
|
-
variantDir,
|
|
549
|
-
packageRoot,
|
|
550
|
-
installedMap,
|
|
551
|
-
summary
|
|
552
|
-
);
|
|
553
|
-
updatedResources.push(result);
|
|
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;
|
|
554
322
|
}
|
|
323
|
+
return result.data;
|
|
324
|
+
} catch (err) {
|
|
325
|
+
logger.warn(
|
|
326
|
+
`Failed to parse skills manifest.lock.json: ${err.message}`
|
|
327
|
+
);
|
|
328
|
+
return null;
|
|
555
329
|
}
|
|
556
|
-
return { resources: updatedResources, summary };
|
|
557
330
|
}
|
|
558
|
-
async function
|
|
559
|
-
const
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
variantDir,
|
|
565
|
-
packageRoot
|
|
331
|
+
async function writeSkillsLock(projectRoot, lock) {
|
|
332
|
+
const lockPath = path5.join(
|
|
333
|
+
projectRoot,
|
|
334
|
+
TEAMIX_DIR2,
|
|
335
|
+
SKILLS_DIR,
|
|
336
|
+
SKILLS_LOCK_FILE
|
|
566
337
|
);
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
const templateContent = await loadTemplateFile(sourcePath);
|
|
570
|
-
newContent = renderTemplate(templateContent, data);
|
|
571
|
-
} else {
|
|
572
|
-
newContent = await fs6.readFile(sourcePath, "utf-8");
|
|
573
|
-
}
|
|
574
|
-
const newHash = computeHash(newContent);
|
|
575
|
-
const action = getUpdateAction(resource.updateStrategy, {
|
|
576
|
-
exists,
|
|
577
|
-
hash: newHash,
|
|
578
|
-
currentHash: installed?.hash
|
|
579
|
-
});
|
|
580
|
-
switch (action) {
|
|
581
|
-
case "skip": {
|
|
582
|
-
logger.debug(` Skip: ${resource.target} (${resource.updateStrategy})`);
|
|
583
|
-
summary.skipped++;
|
|
584
|
-
return installed ?? {
|
|
585
|
-
id: resource.id,
|
|
586
|
-
target: resource.target,
|
|
587
|
-
hash: newHash,
|
|
588
|
-
strategy: resource.updateStrategy
|
|
589
|
-
};
|
|
590
|
-
}
|
|
591
|
-
case "overwrite": {
|
|
592
|
-
if (exists) {
|
|
593
|
-
await backupFile(targetPath, projectRoot);
|
|
594
|
-
summary.overwritten++;
|
|
595
|
-
} else {
|
|
596
|
-
summary.created++;
|
|
597
|
-
}
|
|
598
|
-
await writeFileSafe(targetPath, newContent);
|
|
599
|
-
logger.debug(` ${exists ? "Overwrite" : "Create"}: ${resource.target}`);
|
|
600
|
-
return {
|
|
601
|
-
id: resource.id,
|
|
602
|
-
target: resource.target,
|
|
603
|
-
hash: newHash,
|
|
604
|
-
strategy: resource.updateStrategy
|
|
605
|
-
};
|
|
606
|
-
}
|
|
607
|
-
case "managed-update": {
|
|
608
|
-
const currentContent = await readFileOrNull(targetPath);
|
|
609
|
-
if (currentContent === null) {
|
|
610
|
-
await writeFileSafe(targetPath, newContent);
|
|
611
|
-
summary.created++;
|
|
612
|
-
return {
|
|
613
|
-
id: resource.id,
|
|
614
|
-
target: resource.target,
|
|
615
|
-
hash: computeHash(newContent),
|
|
616
|
-
strategy: resource.updateStrategy
|
|
617
|
-
};
|
|
618
|
-
}
|
|
619
|
-
let updatedContent = currentContent;
|
|
620
|
-
const regionIds = resource.managedRegions ?? [];
|
|
621
|
-
for (const regionId of regionIds) {
|
|
622
|
-
const regionPattern = new RegExp(
|
|
623
|
-
`<!-- teamix-evo:managed:start id="${escapeRegExp(
|
|
624
|
-
regionId
|
|
625
|
-
)}" -->([\\s\\S]*?)<!-- teamix-evo:managed:end id="${escapeRegExp(
|
|
626
|
-
regionId
|
|
627
|
-
)}" -->`
|
|
628
|
-
);
|
|
629
|
-
const match = newContent.match(regionPattern);
|
|
630
|
-
if (match) {
|
|
631
|
-
const regionContent = match[1].replace(/^\n/, "").replace(/\n$/, "");
|
|
632
|
-
try {
|
|
633
|
-
updatedContent = replaceManagedRegion(
|
|
634
|
-
updatedContent,
|
|
635
|
-
regionId,
|
|
636
|
-
regionContent
|
|
637
|
-
);
|
|
638
|
-
} catch {
|
|
639
|
-
logger.warn(
|
|
640
|
-
`Managed region "${regionId}" not found in ${resource.target}. Skipping region.`
|
|
641
|
-
);
|
|
642
|
-
}
|
|
643
|
-
}
|
|
644
|
-
}
|
|
645
|
-
await backupFile(targetPath, projectRoot);
|
|
646
|
-
await writeFileSafe(targetPath, updatedContent);
|
|
647
|
-
summary.managed++;
|
|
648
|
-
return {
|
|
649
|
-
id: resource.id,
|
|
650
|
-
target: resource.target,
|
|
651
|
-
hash: computeHash(updatedContent),
|
|
652
|
-
strategy: resource.updateStrategy
|
|
653
|
-
};
|
|
654
|
-
}
|
|
655
|
-
default:
|
|
656
|
-
summary.skipped++;
|
|
657
|
-
return installed ?? {
|
|
658
|
-
id: resource.id,
|
|
659
|
-
target: resource.target,
|
|
660
|
-
hash: newHash,
|
|
661
|
-
strategy: resource.updateStrategy
|
|
662
|
-
};
|
|
663
|
-
}
|
|
338
|
+
await writeFileSafe(lockPath, JSON.stringify(lock, null, 2) + "\n");
|
|
339
|
+
logger.debug(`Wrote skills lock \u2192 ${lockPath}`);
|
|
664
340
|
}
|
|
665
|
-
async function updateRecursiveResource(resource, projectRoot, data, variantDir, packageRoot, installedMap, summary) {
|
|
666
|
-
const sourcePath = resolveSourcePath(
|
|
667
|
-
resource.source,
|
|
668
|
-
variantDir,
|
|
669
|
-
packageRoot
|
|
670
|
-
);
|
|
671
|
-
const targetDir = path8.join(projectRoot, resource.target);
|
|
672
|
-
const results = [];
|
|
673
|
-
if (resource.updateStrategy === "frozen") {
|
|
674
|
-
const anyInstalled = [...installedMap.keys()].some(
|
|
675
|
-
(k) => k.startsWith(`${resource.id}:`)
|
|
676
|
-
);
|
|
677
|
-
if (anyInstalled) {
|
|
678
|
-
summary.skipped++;
|
|
679
|
-
for (const [id, res] of installedMap) {
|
|
680
|
-
if (id.startsWith(`${resource.id}:`)) {
|
|
681
|
-
results.push(res);
|
|
682
|
-
}
|
|
683
|
-
}
|
|
684
|
-
return results;
|
|
685
|
-
}
|
|
686
|
-
}
|
|
687
|
-
await ensureDir(targetDir);
|
|
688
|
-
const entries = await walkDir(sourcePath);
|
|
689
|
-
for (const entry of entries) {
|
|
690
|
-
const relPath = path8.relative(sourcePath, entry);
|
|
691
|
-
let targetFile = path8.join(targetDir, relPath);
|
|
692
|
-
if (resource.template && targetFile.endsWith(".hbs")) {
|
|
693
|
-
targetFile = targetFile.slice(0, -4);
|
|
694
|
-
}
|
|
695
|
-
let content;
|
|
696
|
-
if (resource.template && entry.endsWith(".hbs")) {
|
|
697
|
-
const templateContent = await loadTemplateFile(entry);
|
|
698
|
-
content = renderTemplate(templateContent, data);
|
|
699
|
-
} else {
|
|
700
|
-
content = await fs6.readFile(entry, "utf-8");
|
|
701
|
-
}
|
|
702
|
-
await writeFileSafe(targetFile, content);
|
|
703
|
-
const hash = computeHash(content);
|
|
704
|
-
const targetRel = path8.relative(projectRoot, targetFile);
|
|
705
|
-
results.push({
|
|
706
|
-
id: `${resource.id}:${relPath}`,
|
|
707
|
-
target: targetRel,
|
|
708
|
-
hash,
|
|
709
|
-
strategy: resource.updateStrategy
|
|
710
|
-
});
|
|
711
|
-
summary.overwritten++;
|
|
712
|
-
}
|
|
713
|
-
return results;
|
|
714
|
-
}
|
|
715
|
-
function escapeRegExp(str) {
|
|
716
|
-
return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
717
|
-
}
|
|
718
|
-
|
|
719
|
-
// src/commands/design/update.ts
|
|
720
|
-
var DESIGN_PACKAGE = "@teamix-evo/design";
|
|
721
|
-
var updateCommand = new Command2("update").description("\u66F4\u65B0\u8BBE\u8BA1\u4F53\u7CFB\u8D44\u6E90").action(async () => {
|
|
722
|
-
try {
|
|
723
|
-
const ide = detectIde();
|
|
724
|
-
const projectRoot = ide.getProjectRoot();
|
|
725
|
-
logger.info("Updating design system resources...");
|
|
726
|
-
const config = await readProjectConfig(projectRoot);
|
|
727
|
-
if (!config?.packages?.design) {
|
|
728
|
-
logger.error(
|
|
729
|
-
'Design system not initialized. Run "teamix-evo design init" first.'
|
|
730
|
-
);
|
|
731
|
-
process.exitCode = 1;
|
|
732
|
-
return;
|
|
733
|
-
}
|
|
734
|
-
const { variant, version: currentVersion } = config.packages.design;
|
|
735
|
-
const installedManifest = await readInstalledManifest(projectRoot);
|
|
736
|
-
if (!installedManifest) {
|
|
737
|
-
logger.error(
|
|
738
|
-
'No installed manifest found. Try re-initializing with "teamix-evo design init".'
|
|
739
|
-
);
|
|
740
|
-
process.exitCode = 1;
|
|
741
|
-
return;
|
|
742
|
-
}
|
|
743
|
-
logger.info(`Loading variant "${variant}" from ${DESIGN_PACKAGE}...`);
|
|
744
|
-
const { manifest, data, variantDir, packageRoot } = await loadVariantData(DESIGN_PACKAGE, variant);
|
|
745
|
-
logger.info(
|
|
746
|
-
`Current: v${currentVersion} \u2192 Available: v${manifest.version}`
|
|
747
|
-
);
|
|
748
|
-
const result = await updateResources({
|
|
749
|
-
projectRoot,
|
|
750
|
-
manifest,
|
|
751
|
-
data,
|
|
752
|
-
variantDir,
|
|
753
|
-
packageRoot,
|
|
754
|
-
installedManifest,
|
|
755
|
-
packageName: DESIGN_PACKAGE
|
|
756
|
-
});
|
|
757
|
-
config.packages.design.version = manifest.version;
|
|
758
|
-
await writeProjectConfig(projectRoot, config);
|
|
759
|
-
const updatedManifest = { ...installedManifest };
|
|
760
|
-
const pkgIdx = updatedManifest.installed.findIndex(
|
|
761
|
-
(p) => p.package === DESIGN_PACKAGE && p.variant === variant
|
|
762
|
-
);
|
|
763
|
-
const pkgEntry = {
|
|
764
|
-
package: DESIGN_PACKAGE,
|
|
765
|
-
variant,
|
|
766
|
-
version: manifest.version,
|
|
767
|
-
installedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
768
|
-
resources: result.resources
|
|
769
|
-
};
|
|
770
|
-
if (pkgIdx >= 0) {
|
|
771
|
-
updatedManifest.installed[pkgIdx] = pkgEntry;
|
|
772
|
-
} else {
|
|
773
|
-
updatedManifest.installed.push(pkgEntry);
|
|
774
|
-
}
|
|
775
|
-
await writeInstalledManifest(projectRoot, updatedManifest);
|
|
776
|
-
const { summary } = result;
|
|
777
|
-
logger.success(
|
|
778
|
-
`Design system updated to v${manifest.version}`
|
|
779
|
-
);
|
|
780
|
-
logger.info(` Created: ${summary.created}`);
|
|
781
|
-
logger.info(` Overwritten: ${summary.overwritten}`);
|
|
782
|
-
logger.info(` Managed: ${summary.managed}`);
|
|
783
|
-
logger.info(` Skipped: ${summary.skipped}`);
|
|
784
|
-
} catch (err) {
|
|
785
|
-
logger.error(`Failed to update: ${err.message}`);
|
|
786
|
-
logger.debug(err.stack ?? "");
|
|
787
|
-
process.exitCode = 1;
|
|
788
|
-
}
|
|
789
|
-
});
|
|
790
|
-
|
|
791
|
-
// src/commands/design/list.ts
|
|
792
|
-
import { Command as Command3 } from "commander";
|
|
793
|
-
var listCommand = new Command3("list").description("\u5217\u51FA\u5DF2\u5B89\u88C5\u7684\u8BBE\u8BA1\u53D8\u4F53").action(async () => {
|
|
794
|
-
try {
|
|
795
|
-
const ide = detectIde();
|
|
796
|
-
const projectRoot = ide.getProjectRoot();
|
|
797
|
-
const config = await readProjectConfig(projectRoot);
|
|
798
|
-
if (!config?.packages?.design) {
|
|
799
|
-
logger.info("No design system installed.");
|
|
800
|
-
logger.info('Run "teamix-evo design init [variant]" to get started.');
|
|
801
|
-
return;
|
|
802
|
-
}
|
|
803
|
-
const { variant, version: version2 } = config.packages.design;
|
|
804
|
-
logger.info("Installed design system:");
|
|
805
|
-
logger.info(` Package: @teamix-evo/design`);
|
|
806
|
-
logger.info(` Variant: ${variant}`);
|
|
807
|
-
logger.info(` Version: ${version2}`);
|
|
808
|
-
logger.info(` IDE: ${config.ide}`);
|
|
809
|
-
const manifest = await readInstalledManifest(projectRoot);
|
|
810
|
-
if (manifest) {
|
|
811
|
-
const pkg = manifest.installed.find(
|
|
812
|
-
(p) => p.package === "@teamix-evo/design" && p.variant === variant
|
|
813
|
-
);
|
|
814
|
-
if (pkg) {
|
|
815
|
-
logger.info(` Resources: ${pkg.resources.length} files`);
|
|
816
|
-
logger.info(
|
|
817
|
-
` Installed: ${new Date(pkg.installedAt).toLocaleString()}`
|
|
818
|
-
);
|
|
819
|
-
}
|
|
820
|
-
}
|
|
821
|
-
} catch (err) {
|
|
822
|
-
logger.error(`Failed to list: ${err.message}`);
|
|
823
|
-
process.exitCode = 1;
|
|
824
|
-
}
|
|
825
|
-
});
|
|
826
|
-
|
|
827
|
-
// src/commands/design/uninstall.ts
|
|
828
|
-
import { Command as Command4 } from "commander";
|
|
829
|
-
import * as fs7 from "fs/promises";
|
|
830
|
-
import * as path9 from "path";
|
|
831
|
-
import * as prompts from "@clack/prompts";
|
|
832
|
-
var DESIGN_PACKAGE2 = "@teamix-evo/design";
|
|
833
|
-
var uninstallCommand = new Command4("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(
|
|
834
|
-
"--keep-files",
|
|
835
|
-
"\u4EC5\u6E05\u7406 .teamix-evo \u4E2D\u7684\u8BB0\u8D26\u4FE1\u606F\uFF0C\u4E0D\u5220\u9664\u5DF2\u843D\u5730\u8D44\u6E90\u6587\u4EF6"
|
|
836
|
-
).action(async (opts) => {
|
|
837
|
-
try {
|
|
838
|
-
const ide = detectIde();
|
|
839
|
-
const projectRoot = ide.getProjectRoot();
|
|
840
|
-
const config = await readProjectConfig(projectRoot);
|
|
841
|
-
if (!config?.packages?.design) {
|
|
842
|
-
logger.info("Design system is not installed. Nothing to do.");
|
|
843
|
-
return;
|
|
844
|
-
}
|
|
845
|
-
const installedManifest = await readInstalledManifest(projectRoot);
|
|
846
|
-
const pkg = installedManifest?.installed.find(
|
|
847
|
-
(p) => p.package === DESIGN_PACKAGE2
|
|
848
|
-
);
|
|
849
|
-
const resources = pkg?.resources ?? [];
|
|
850
|
-
const removable = opts.keepFiles ? [] : resources.filter((r) => r.strategy !== "managed");
|
|
851
|
-
const kept = resources.length - removable.length;
|
|
852
|
-
logger.info(
|
|
853
|
-
`Will remove ${removable.length} file(s); keep ${kept} managed file(s).`
|
|
854
|
-
);
|
|
855
|
-
if (!opts.yes) {
|
|
856
|
-
const confirm4 = await prompts.confirm({
|
|
857
|
-
message: "\u786E\u8BA4\u5378\u8F7D\u8BBE\u8BA1\u4F53\u7CFB\uFF1F",
|
|
858
|
-
initialValue: false
|
|
859
|
-
});
|
|
860
|
-
if (prompts.isCancel(confirm4) || !confirm4) {
|
|
861
|
-
logger.info("Cancelled.");
|
|
862
|
-
return;
|
|
863
|
-
}
|
|
864
|
-
}
|
|
865
|
-
let removed = 0;
|
|
866
|
-
for (const r of removable) {
|
|
867
|
-
const target = path9.isAbsolute(r.target) ? r.target : path9.join(projectRoot, r.target);
|
|
868
|
-
try {
|
|
869
|
-
await fs7.unlink(target);
|
|
870
|
-
removed++;
|
|
871
|
-
} catch (err) {
|
|
872
|
-
if (err.code !== "ENOENT") {
|
|
873
|
-
logger.warn(
|
|
874
|
-
`Failed to remove ${target}: ${err.message}`
|
|
875
|
-
);
|
|
876
|
-
}
|
|
877
|
-
}
|
|
878
|
-
}
|
|
879
|
-
if (installedManifest) {
|
|
880
|
-
installedManifest.installed = installedManifest.installed.filter(
|
|
881
|
-
(p) => p.package !== DESIGN_PACKAGE2
|
|
882
|
-
);
|
|
883
|
-
await writeInstalledManifest(projectRoot, installedManifest);
|
|
884
|
-
}
|
|
885
|
-
delete config.packages.design;
|
|
886
|
-
await writeProjectConfig(projectRoot, config);
|
|
887
|
-
logger.success(`Uninstalled ${DESIGN_PACKAGE2}`);
|
|
888
|
-
logger.info(` Removed: ${removed} files`);
|
|
889
|
-
if (kept > 0) {
|
|
890
|
-
logger.info(
|
|
891
|
-
` Kept: ${kept} managed files (you may delete manually)`
|
|
892
|
-
);
|
|
893
|
-
}
|
|
894
|
-
} catch (err) {
|
|
895
|
-
logger.error(`Failed to uninstall: ${err.message}`);
|
|
896
|
-
logger.debug(err.stack ?? "");
|
|
897
|
-
process.exitCode = 1;
|
|
898
|
-
}
|
|
899
|
-
});
|
|
900
|
-
|
|
901
|
-
// src/commands/design/index.ts
|
|
902
|
-
var designCommand = new Command5("design").description(
|
|
903
|
-
"\u7BA1\u7406\u8BBE\u8BA1\u4F53\u7CFB\u8D44\u6E90"
|
|
904
|
-
);
|
|
905
|
-
designCommand.addCommand(initCommand);
|
|
906
|
-
designCommand.addCommand(updateCommand);
|
|
907
|
-
designCommand.addCommand(listCommand);
|
|
908
|
-
designCommand.addCommand(uninstallCommand);
|
|
909
|
-
|
|
910
|
-
// src/commands/skills/index.ts
|
|
911
|
-
import { Command as Command10 } from "commander";
|
|
912
|
-
|
|
913
|
-
// src/commands/skills/add.ts
|
|
914
|
-
import { Command as Command6 } from "commander";
|
|
915
|
-
import * as prompts2 from "@clack/prompts";
|
|
916
341
|
|
|
917
342
|
// src/core/skills-client.ts
|
|
918
|
-
import * as
|
|
919
|
-
import * as
|
|
920
|
-
import { createRequire
|
|
343
|
+
import * as path6 from "path";
|
|
344
|
+
import * as fs2 from "fs/promises";
|
|
345
|
+
import { createRequire } from "module";
|
|
921
346
|
import { loadSkillsPackageManifest } from "@teamix-evo/registry";
|
|
922
|
-
var
|
|
923
|
-
function
|
|
924
|
-
const pkgJsonPath =
|
|
925
|
-
return
|
|
347
|
+
var require2 = createRequire(import.meta.url);
|
|
348
|
+
function resolvePackageRoot(packageName) {
|
|
349
|
+
const pkgJsonPath = require2.resolve(`${packageName}/package.json`);
|
|
350
|
+
return path6.dirname(pkgJsonPath);
|
|
926
351
|
}
|
|
927
352
|
async function loadSkillsData(packageName) {
|
|
928
|
-
const packageRoot =
|
|
353
|
+
const packageRoot = resolvePackageRoot(packageName);
|
|
929
354
|
logger.debug(`Resolved skills package root: ${packageRoot}`);
|
|
930
355
|
const manifest = await loadSkillsPackageManifest(packageRoot);
|
|
931
356
|
let data = {};
|
|
932
|
-
const dataPath =
|
|
357
|
+
const dataPath = path6.join(packageRoot, "_data.json");
|
|
933
358
|
try {
|
|
934
|
-
const raw = await
|
|
359
|
+
const raw = await fs2.readFile(dataPath, "utf-8");
|
|
935
360
|
data = JSON.parse(raw);
|
|
936
361
|
} catch (err) {
|
|
937
362
|
if (err.code !== "ENOENT") {
|
|
@@ -943,9 +368,56 @@ async function loadSkillsData(packageName) {
|
|
|
943
368
|
}
|
|
944
369
|
|
|
945
370
|
// src/core/skills-installer.ts
|
|
946
|
-
import * as
|
|
947
|
-
import * as
|
|
948
|
-
import { replaceManagedRegion
|
|
371
|
+
import * as path8 from "path";
|
|
372
|
+
import * as fs5 from "fs/promises";
|
|
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);
|
|
389
|
+
}
|
|
390
|
+
compiled = Handlebars.compile(templateContent, { noEscape: true });
|
|
391
|
+
compiledCache.set(templateContent, compiled);
|
|
392
|
+
}
|
|
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
|
+
}
|
|
416
|
+
}
|
|
417
|
+
return files;
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
// src/core/skills-installer.ts
|
|
949
421
|
async function installSkills(options) {
|
|
950
422
|
const { manifest, ides, scope, onlyIds } = options;
|
|
951
423
|
const installed = [];
|
|
@@ -962,60 +434,85 @@ async function installSkills(options) {
|
|
|
962
434
|
);
|
|
963
435
|
continue;
|
|
964
436
|
}
|
|
437
|
+
const sourceRecords = await writeSkillSource(skill, options);
|
|
438
|
+
installed.push(...sourceRecords);
|
|
965
439
|
for (const ide of skillIdes) {
|
|
966
|
-
const
|
|
967
|
-
|
|
440
|
+
const mirrorRecords = await mirrorSkillToIde(
|
|
441
|
+
skill,
|
|
442
|
+
ide,
|
|
443
|
+
scope,
|
|
444
|
+
options.projectRoot
|
|
445
|
+
);
|
|
446
|
+
installed.push(...mirrorRecords);
|
|
968
447
|
}
|
|
969
448
|
}
|
|
970
449
|
return { resources: installed, count: installed.length };
|
|
971
450
|
}
|
|
972
|
-
async function
|
|
451
|
+
async function writeSkillSource(skill, options) {
|
|
973
452
|
const { data, packageRoot, projectRoot } = options;
|
|
974
|
-
const
|
|
975
|
-
const targetDir =
|
|
976
|
-
const
|
|
977
|
-
const
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
const targetFile = path11.join(targetDir, "SKILL.md");
|
|
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");
|
|
981
459
|
const content = await renderSkillContent(sourceAbs, skill, data);
|
|
982
460
|
await writeFileSafe(targetFile, content);
|
|
983
|
-
|
|
984
|
-
logger.debug(` Wrote
|
|
985
|
-
return
|
|
461
|
+
records.push(makeSourceRecord(skill, targetFile, content));
|
|
462
|
+
logger.debug(` Wrote source: ${targetFile}`);
|
|
463
|
+
return records;
|
|
986
464
|
}
|
|
987
465
|
await ensureDir(targetDir);
|
|
988
466
|
const entries = await walkDir(sourceAbs);
|
|
989
467
|
for (const entry of entries) {
|
|
990
|
-
const rel2 =
|
|
991
|
-
let targetFile =
|
|
468
|
+
const rel2 = path8.relative(sourceAbs, entry);
|
|
469
|
+
let targetFile = path8.join(targetDir, rel2);
|
|
992
470
|
if (skill.template && targetFile.endsWith(".hbs")) {
|
|
993
471
|
targetFile = targetFile.slice(0, -4);
|
|
994
472
|
}
|
|
995
|
-
|
|
996
|
-
if (skill.template && entry.endsWith(".hbs")) {
|
|
997
|
-
const tpl = await loadTemplateFile(entry);
|
|
998
|
-
content = renderTemplate(tpl, { ...data, skill });
|
|
999
|
-
} else {
|
|
1000
|
-
content = await fs9.readFile(entry, "utf-8");
|
|
1001
|
-
}
|
|
473
|
+
const content = skill.template && entry.endsWith(".hbs") ? renderTemplate(await loadTemplateFile(entry), { ...data, skill }) : await fs5.readFile(entry, "utf-8");
|
|
1002
474
|
await writeFileSafe(targetFile, content);
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
);
|
|
1006
|
-
logger.debug(` Wrote ${ide}:${scope}: ${targetFile}`);
|
|
475
|
+
const relWritten = path8.relative(targetDir, targetFile);
|
|
476
|
+
records.push(makeSourceRecord(skill, targetFile, content, relWritten));
|
|
477
|
+
logger.debug(` Wrote source: ${targetFile}`);
|
|
1007
478
|
}
|
|
1008
|
-
return
|
|
479
|
+
return records;
|
|
1009
480
|
}
|
|
1010
|
-
async function
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
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
|
+
};
|
|
1016
513
|
}
|
|
1017
|
-
function
|
|
1018
|
-
const id = rel2 ? `${skill.id}:${rel2}` : skill.id;
|
|
514
|
+
function makeMirrorRecord(skill, targetAbs, content, ide, scope, rel2) {
|
|
515
|
+
const id = rel2 && rel2 !== "SKILL.md" ? `${skill.id}:${rel2}` : skill.id;
|
|
1019
516
|
return {
|
|
1020
517
|
id,
|
|
1021
518
|
target: targetAbs,
|
|
@@ -1026,55 +523,46 @@ function makeInstalledRecord(skill, targetAbs, content, ide, scope, rel2) {
|
|
|
1026
523
|
};
|
|
1027
524
|
}
|
|
1028
525
|
async function updateSkills(options) {
|
|
1029
|
-
const { manifest, ides, scope,
|
|
526
|
+
const { manifest, ides, scope, projectRoot } = options;
|
|
1030
527
|
const summary = { overwritten: 0, managed: 0, skipped: 0, created: 0 };
|
|
1031
528
|
const updated = [];
|
|
1032
|
-
const installedMap = /* @__PURE__ */ new Map();
|
|
1033
|
-
for (const r of installed) {
|
|
1034
|
-
installedMap.set(installedKey(r), r);
|
|
1035
|
-
}
|
|
1036
529
|
for (const skill of manifest.skills) {
|
|
1037
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);
|
|
1038
538
|
for (const ide of skillIdes) {
|
|
1039
|
-
const
|
|
539
|
+
const mirrorRecords = await mirrorSkillToIde(
|
|
1040
540
|
skill,
|
|
1041
541
|
ide,
|
|
1042
542
|
scope,
|
|
1043
|
-
|
|
1044
|
-
packageRoot,
|
|
1045
|
-
projectRoot,
|
|
1046
|
-
installedMap,
|
|
1047
|
-
summary
|
|
543
|
+
projectRoot
|
|
1048
544
|
);
|
|
1049
|
-
updated.push(...
|
|
545
|
+
updated.push(...mirrorRecords);
|
|
1050
546
|
}
|
|
1051
547
|
}
|
|
1052
548
|
return { resources: updated, summary };
|
|
1053
549
|
}
|
|
1054
|
-
async function
|
|
1055
|
-
const
|
|
1056
|
-
const
|
|
1057
|
-
const
|
|
1058
|
-
const
|
|
1059
|
-
|
|
1060
|
-
if (!stat2.isFile()) {
|
|
1061
|
-
const entries = await walkDir(sourceAbs);
|
|
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()) {
|
|
1062
556
|
await ensureDir(targetDir);
|
|
557
|
+
const entries = await walkDir(sourceAbs);
|
|
558
|
+
const records = [];
|
|
1063
559
|
for (const entry of entries) {
|
|
1064
|
-
const rel2 =
|
|
1065
|
-
let targetFile2 =
|
|
560
|
+
const rel2 = path8.relative(sourceAbs, entry);
|
|
561
|
+
let targetFile2 = path8.join(targetDir, rel2);
|
|
1066
562
|
if (skill.template && targetFile2.endsWith(".hbs")) {
|
|
1067
563
|
targetFile2 = targetFile2.slice(0, -4);
|
|
1068
564
|
}
|
|
1069
|
-
|
|
1070
|
-
if (skill.template && entry.endsWith(".hbs")) {
|
|
1071
|
-
content = renderTemplate(await loadTemplateFile(entry), {
|
|
1072
|
-
...data,
|
|
1073
|
-
skill
|
|
1074
|
-
});
|
|
1075
|
-
} else {
|
|
1076
|
-
content = await fs9.readFile(entry, "utf-8");
|
|
1077
|
-
}
|
|
565
|
+
const content = skill.template && entry.endsWith(".hbs") ? renderTemplate(await loadTemplateFile(entry), { ...data, skill }) : await fs5.readFile(entry, "utf-8");
|
|
1078
566
|
const exists2 = await fileExists(targetFile2);
|
|
1079
567
|
if (exists2) {
|
|
1080
568
|
await backupFile(targetFile2, projectRoot);
|
|
@@ -1083,30 +571,23 @@ async function updateSkillForIde(skill, ide, scope, data, packageRoot, projectRo
|
|
|
1083
571
|
summary.created++;
|
|
1084
572
|
}
|
|
1085
573
|
await writeFileSafe(targetFile2, content);
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
);
|
|
574
|
+
const relWritten = path8.relative(targetDir, targetFile2);
|
|
575
|
+
records.push(makeSourceRecord(skill, targetFile2, content, relWritten));
|
|
1089
576
|
}
|
|
1090
577
|
return records;
|
|
1091
578
|
}
|
|
1092
|
-
const targetFile =
|
|
579
|
+
const targetFile = path8.join(targetDir, "SKILL.md");
|
|
1093
580
|
const newContent = await renderSkillContent(sourceAbs, skill, data);
|
|
1094
581
|
const exists = await fileExists(targetFile);
|
|
1095
|
-
const installedKeyStr = `${skill.id}|${ide}|${scope}`;
|
|
1096
|
-
const prior = installedMap.get(installedKeyStr);
|
|
1097
582
|
if (skill.updateStrategy === "frozen") {
|
|
1098
583
|
if (exists) {
|
|
1099
584
|
summary.skipped++;
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
];
|
|
585
|
+
const current2 = await readFileOrNull(targetFile) ?? newContent;
|
|
586
|
+
return [makeSourceRecord(skill, targetFile, current2)];
|
|
1103
587
|
}
|
|
1104
588
|
await writeFileSafe(targetFile, newContent);
|
|
1105
589
|
summary.created++;
|
|
1106
|
-
|
|
1107
|
-
makeInstalledRecord(skill, targetFile, newContent, ide, scope)
|
|
1108
|
-
);
|
|
1109
|
-
return records;
|
|
590
|
+
return [makeSourceRecord(skill, targetFile, newContent)];
|
|
1110
591
|
}
|
|
1111
592
|
if (skill.updateStrategy === "regenerable" || !exists) {
|
|
1112
593
|
if (exists) {
|
|
@@ -1116,19 +597,15 @@ async function updateSkillForIde(skill, ide, scope, data, packageRoot, projectRo
|
|
|
1116
597
|
summary.created++;
|
|
1117
598
|
}
|
|
1118
599
|
await writeFileSafe(targetFile, newContent);
|
|
1119
|
-
|
|
1120
|
-
makeInstalledRecord(skill, targetFile, newContent, ide, scope)
|
|
1121
|
-
);
|
|
1122
|
-
return records;
|
|
600
|
+
return [makeSourceRecord(skill, targetFile, newContent)];
|
|
1123
601
|
}
|
|
1124
602
|
const current = await readFileOrNull(targetFile);
|
|
1125
|
-
let
|
|
1126
|
-
const
|
|
1127
|
-
for (const regionId of regionIds) {
|
|
603
|
+
let merged = current ?? newContent;
|
|
604
|
+
for (const regionId of skill.managedRegions ?? []) {
|
|
1128
605
|
const re = new RegExp(
|
|
1129
|
-
`<!-- teamix-evo:managed:start id="${
|
|
606
|
+
`<!-- teamix-evo:managed:start id="${escapeRegExp(
|
|
1130
607
|
regionId
|
|
1131
|
-
)}" -->([\\s\\S]*?)<!-- teamix-evo:managed:end id="${
|
|
608
|
+
)}" -->([\\s\\S]*?)<!-- teamix-evo:managed:end id="${escapeRegExp(
|
|
1132
609
|
regionId
|
|
1133
610
|
)}" -->`
|
|
1134
611
|
);
|
|
@@ -1136,7 +613,7 @@ async function updateSkillForIde(skill, ide, scope, data, packageRoot, projectRo
|
|
|
1136
613
|
if (match) {
|
|
1137
614
|
const region = match[1].replace(/^\n/, "").replace(/\n$/, "");
|
|
1138
615
|
try {
|
|
1139
|
-
|
|
616
|
+
merged = replaceManagedRegion(merged, regionId, region);
|
|
1140
617
|
} catch {
|
|
1141
618
|
logger.warn(
|
|
1142
619
|
`Managed region "${regionId}" not found in ${targetFile}. Skipped.`
|
|
@@ -1145,22 +622,57 @@ async function updateSkillForIde(skill, ide, scope, data, packageRoot, projectRo
|
|
|
1145
622
|
}
|
|
1146
623
|
}
|
|
1147
624
|
await backupFile(targetFile, projectRoot);
|
|
1148
|
-
await writeFileSafe(targetFile,
|
|
625
|
+
await writeFileSafe(targetFile, merged);
|
|
1149
626
|
summary.managed++;
|
|
1150
|
-
|
|
1151
|
-
return records;
|
|
1152
|
-
}
|
|
1153
|
-
function installedKey(r) {
|
|
1154
|
-
return `${r.id}|${r.ide ?? ""}|${r.scope ?? ""}`;
|
|
627
|
+
return [makeSourceRecord(skill, targetFile, merged)];
|
|
1155
628
|
}
|
|
1156
|
-
function
|
|
629
|
+
function escapeRegExp(str) {
|
|
1157
630
|
return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
1158
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
|
+
});
|
|
666
|
+
}
|
|
667
|
+
}
|
|
668
|
+
}
|
|
669
|
+
return { resources: out, count: out.length };
|
|
670
|
+
}
|
|
1159
671
|
async function removeSkillFiles(records) {
|
|
1160
672
|
const removed = [];
|
|
1161
673
|
for (const r of records) {
|
|
1162
674
|
try {
|
|
1163
|
-
await
|
|
675
|
+
await fs5.unlink(r.target);
|
|
1164
676
|
removed.push(r.target);
|
|
1165
677
|
} catch (err) {
|
|
1166
678
|
if (err.code !== "ENOENT") {
|
|
@@ -1168,11 +680,11 @@ async function removeSkillFiles(records) {
|
|
|
1168
680
|
}
|
|
1169
681
|
}
|
|
1170
682
|
}
|
|
1171
|
-
const parents = new Set(records.map((r) =>
|
|
683
|
+
const parents = new Set(records.map((r) => path8.dirname(r.target)));
|
|
1172
684
|
for (const dir of parents) {
|
|
1173
685
|
try {
|
|
1174
|
-
const entries = await
|
|
1175
|
-
if (entries.length === 0) await
|
|
686
|
+
const entries = await fs5.readdir(dir);
|
|
687
|
+
if (entries.length === 0) await fs5.rmdir(dir);
|
|
1176
688
|
} catch {
|
|
1177
689
|
}
|
|
1178
690
|
}
|
|
@@ -1202,6 +714,7 @@ async function runSkillsAdd(options) {
|
|
|
1202
714
|
throw new Error("Scope must be specified (project | global).");
|
|
1203
715
|
}
|
|
1204
716
|
const { manifest, data, packageRoot } = await loadSkillsData(packageName);
|
|
717
|
+
const currentDesignVariant = await readDesignVariant(projectRoot);
|
|
1205
718
|
if (isIncremental) {
|
|
1206
719
|
const known = new Set(manifest.skills.map((s) => s.id));
|
|
1207
720
|
const unknown = requestedNames.filter((n) => !known.has(n));
|
|
@@ -1216,9 +729,13 @@ async function runSkillsAdd(options) {
|
|
|
1216
729
|
const existingPkg = existingInstalled?.installed.find(
|
|
1217
730
|
(p) => p.package === packageName
|
|
1218
731
|
);
|
|
1219
|
-
const
|
|
1220
|
-
|
|
1221
|
-
|
|
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
|
+
]);
|
|
1222
739
|
let onlyIds;
|
|
1223
740
|
let skippedSkillIds;
|
|
1224
741
|
if (isIncremental) {
|
|
@@ -1226,7 +743,22 @@ async function runSkillsAdd(options) {
|
|
|
1226
743
|
onlyIds = requestedNames.filter((n) => !existingSkillIds.has(n));
|
|
1227
744
|
} else {
|
|
1228
745
|
skippedSkillIds = [];
|
|
1229
|
-
onlyIds = manifest.skills.
|
|
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);
|
|
1230
762
|
}
|
|
1231
763
|
if (isIncremental && onlyIds.length === 0) {
|
|
1232
764
|
return {
|
|
@@ -1242,72 +774,562 @@ async function runSkillsAdd(options) {
|
|
|
1242
774
|
skippedSkillIds
|
|
1243
775
|
};
|
|
1244
776
|
}
|
|
1245
|
-
const result = await installSkills({
|
|
1246
|
-
projectRoot,
|
|
1247
|
-
manifest,
|
|
1248
|
-
data,
|
|
1249
|
-
packageRoot,
|
|
1250
|
-
ides,
|
|
1251
|
-
scope,
|
|
1252
|
-
onlyIds
|
|
1253
|
-
});
|
|
1254
|
-
const config = existingConfig ?? {
|
|
1255
|
-
$schema: "https://teamix-evo.dev/schema/config/v1.json",
|
|
1256
|
-
schemaVersion: 1,
|
|
1257
|
-
ide: ideIdent,
|
|
1258
|
-
packages: {}
|
|
1259
|
-
};
|
|
1260
|
-
config.packages.skills = {
|
|
1261
|
-
variant: FLAT_VARIANT,
|
|
1262
|
-
version: manifest.version,
|
|
1263
|
-
ides,
|
|
1264
|
-
scope
|
|
1265
|
-
};
|
|
1266
|
-
await writeProjectConfig(projectRoot, config);
|
|
1267
|
-
const
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
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
|
+
};
|
|
850
|
+
}
|
|
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);
|
|
895
|
+
}
|
|
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
|
+
};
|
|
957
|
+
}
|
|
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
|
+
};
|
|
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
|
+
};
|
|
1024
|
+
}
|
|
1025
|
+
}
|
|
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
|
+
};
|
|
1066
|
+
}
|
|
1067
|
+
|
|
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) => {
|
|
1070
|
+
try {
|
|
1071
|
+
const ide = detectIde();
|
|
1072
|
+
const projectRoot = ide.getProjectRoot();
|
|
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.`
|
|
1086
|
+
);
|
|
1087
|
+
return;
|
|
1088
|
+
}
|
|
1089
|
+
logger.success(
|
|
1090
|
+
`Design system initialized: ${result.packageName} (${result.variant} v${result.version})`
|
|
1091
|
+
);
|
|
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`
|
|
1097
|
+
);
|
|
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
|
+
}
|
|
1115
|
+
}
|
|
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.'
|
|
1120
|
+
);
|
|
1121
|
+
} catch (err) {
|
|
1122
|
+
logger.error(`Failed to initialize: ${err.message}`);
|
|
1123
|
+
logger.debug(err.stack ?? "");
|
|
1124
|
+
process.exitCode = 1;
|
|
1125
|
+
}
|
|
1126
|
+
});
|
|
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
|
+
|
|
1143
|
+
// src/commands/design/list.ts
|
|
1144
|
+
import { Command as Command3 } from "commander";
|
|
1145
|
+
var listCommand = new Command3("list").description("\u5217\u51FA\u5DF2\u5B89\u88C5\u7684\u8BBE\u8BA1\u53D8\u4F53").action(async () => {
|
|
1146
|
+
try {
|
|
1147
|
+
const ide = detectIde();
|
|
1148
|
+
const projectRoot = ide.getProjectRoot();
|
|
1149
|
+
const config = await readProjectConfig(projectRoot);
|
|
1150
|
+
if (!config?.packages?.design) {
|
|
1151
|
+
logger.info("No design system installed.");
|
|
1152
|
+
logger.info('Run "teamix-evo design init [variant]" to get started.');
|
|
1153
|
+
return;
|
|
1154
|
+
}
|
|
1155
|
+
const { variant, version: version2 } = config.packages.design;
|
|
1156
|
+
logger.info("Installed design system:");
|
|
1157
|
+
logger.info(` Package: @teamix-evo/design`);
|
|
1158
|
+
logger.info(` Variant: ${variant}`);
|
|
1159
|
+
logger.info(` Version: ${version2}`);
|
|
1160
|
+
logger.info(` IDE: ${config.ide}`);
|
|
1161
|
+
const manifest = await readInstalledManifest(projectRoot);
|
|
1162
|
+
if (manifest) {
|
|
1163
|
+
const pkg = manifest.installed.find(
|
|
1164
|
+
(p) => p.package === "@teamix-evo/design" && p.variant === variant
|
|
1165
|
+
);
|
|
1166
|
+
if (pkg) {
|
|
1167
|
+
logger.info(` Resources: ${pkg.resources.length} files`);
|
|
1168
|
+
logger.info(
|
|
1169
|
+
` Installed: ${new Date(pkg.installedAt).toLocaleString()}`
|
|
1170
|
+
);
|
|
1171
|
+
}
|
|
1172
|
+
}
|
|
1173
|
+
} catch (err) {
|
|
1174
|
+
logger.error(`Failed to list: ${err.message}`);
|
|
1175
|
+
process.exitCode = 1;
|
|
1176
|
+
}
|
|
1177
|
+
});
|
|
1178
|
+
|
|
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);
|
|
1300
1315
|
}
|
|
1301
|
-
function
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
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
|
+
function resolveSkillsMaintenanceRoot(cwd) {
|
|
1325
|
+
if (isTeamixEvoProject(cwd)) return cwd;
|
|
1326
|
+
const globalRoot = getGlobalMetaRoot();
|
|
1327
|
+
if (isTeamixEvoProject(globalRoot)) return globalRoot;
|
|
1328
|
+
return cwd;
|
|
1307
1329
|
}
|
|
1308
1330
|
|
|
1309
1331
|
// src/commands/skills/add.ts
|
|
1310
|
-
var addCommand = new
|
|
1332
|
+
var addCommand = new Command7("add").description(
|
|
1311
1333
|
"\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"
|
|
1312
1334
|
).argument(
|
|
1313
1335
|
"[names...]",
|
|
@@ -1318,13 +1340,18 @@ var addCommand = new Command6("add").description(
|
|
|
1318
1340
|
).option("-y, --yes", "\u4F7F\u7528\u9ED8\u8BA4\u503C\uFF0C\u8DF3\u8FC7\u4EA4\u4E92").action(async (names, opts) => {
|
|
1319
1341
|
try {
|
|
1320
1342
|
const ide = detectIde();
|
|
1321
|
-
const
|
|
1343
|
+
const cwd = ide.getProjectRoot();
|
|
1322
1344
|
const isIncremental = names.length > 0;
|
|
1323
1345
|
const { ides, scope } = await resolveIdesAndScope({
|
|
1324
1346
|
opts,
|
|
1325
|
-
projectRoot,
|
|
1347
|
+
projectRoot: cwd,
|
|
1326
1348
|
isIncremental
|
|
1327
1349
|
});
|
|
1350
|
+
let projectRoot = cwd;
|
|
1351
|
+
if (scope === "global" && !isTeamixEvoProject(cwd)) {
|
|
1352
|
+
projectRoot = await ensureGlobalMetaRoot();
|
|
1353
|
+
logger.info(`Global skill install \u2014 meta root: ${projectRoot}`);
|
|
1354
|
+
}
|
|
1328
1355
|
logger.info(
|
|
1329
1356
|
isIncremental ? `Adding skills [${names.join(",")}]: ides=[${ides.join(
|
|
1330
1357
|
","
|
|
@@ -1439,14 +1466,18 @@ function parseScope(input) {
|
|
|
1439
1466
|
}
|
|
1440
1467
|
|
|
1441
1468
|
// src/commands/skills/list.ts
|
|
1442
|
-
import { Command as
|
|
1469
|
+
import { Command as Command8 } from "commander";
|
|
1443
1470
|
var SKILLS_PACKAGE = "@teamix-evo/skills";
|
|
1444
|
-
var listCommand2 = new
|
|
1471
|
+
var listCommand2 = new Command8("list").alias("ls").description(
|
|
1445
1472
|
"\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"
|
|
1446
1473
|
).option("--installed", "\u4EC5\u5C55\u793A\u5DF2\u5B89\u88C5\u7684 skill\uFF08\u9690\u85CF\u672A\u5B89\u88C5\u9879\uFF09").action(async (opts) => {
|
|
1447
1474
|
try {
|
|
1448
1475
|
const ide = detectIde();
|
|
1449
|
-
const
|
|
1476
|
+
const cwd = ide.getProjectRoot();
|
|
1477
|
+
const projectRoot = resolveSkillsMaintenanceRoot(cwd);
|
|
1478
|
+
if (projectRoot !== cwd) {
|
|
1479
|
+
logger.info(`Using global skills meta root: ${projectRoot}`);
|
|
1480
|
+
}
|
|
1450
1481
|
const config = await readProjectConfig(projectRoot);
|
|
1451
1482
|
const installedManifest = await readInstalledManifest(projectRoot);
|
|
1452
1483
|
const pkg = installedManifest?.installed.find(
|
|
@@ -1521,13 +1552,17 @@ function printInstalledHeader(cfg, installedAt) {
|
|
|
1521
1552
|
}
|
|
1522
1553
|
|
|
1523
1554
|
// src/commands/skills/update.ts
|
|
1524
|
-
import { Command as
|
|
1555
|
+
import { Command as Command9 } from "commander";
|
|
1525
1556
|
var SKILLS_PACKAGE2 = "@teamix-evo/skills";
|
|
1526
1557
|
var FLAT_VARIANT2 = "_flat";
|
|
1527
|
-
var updateCommand2 = new
|
|
1558
|
+
var updateCommand2 = new Command9("update").description("\u66F4\u65B0\u5DF2\u5B89\u88C5\u7684 teamix-evo skills").action(async () => {
|
|
1528
1559
|
try {
|
|
1529
1560
|
const ide = detectIde();
|
|
1530
|
-
const
|
|
1561
|
+
const cwd = ide.getProjectRoot();
|
|
1562
|
+
const projectRoot = resolveSkillsMaintenanceRoot(cwd);
|
|
1563
|
+
if (projectRoot !== cwd) {
|
|
1564
|
+
logger.info(`Using global skills meta root: ${projectRoot}`);
|
|
1565
|
+
}
|
|
1531
1566
|
const config = await readProjectConfig(projectRoot);
|
|
1532
1567
|
if (!config?.packages?.skills) {
|
|
1533
1568
|
logger.error('Skills not added. Run "teamix-evo skills add" first.');
|
|
@@ -1550,114 +1585,537 @@ var updateCommand2 = new Command8("update").description("\u66F4\u65B0\u5DF2\u5B8
|
|
|
1550
1585
|
const { manifest, data, packageRoot } = await loadSkillsData(
|
|
1551
1586
|
SKILLS_PACKAGE2
|
|
1552
1587
|
);
|
|
1553
|
-
const pkgInstalled = installedManifest.installed.find(
|
|
1554
|
-
(p) => p.package === SKILLS_PACKAGE2
|
|
1555
|
-
);
|
|
1556
|
-
const installedResources = pkgInstalled?.resources ?? [];
|
|
1557
1588
|
logger.info(
|
|
1558
1589
|
`Current: v${skillsEntry.version} \u2192 Available: v${manifest.version}`
|
|
1559
1590
|
);
|
|
1560
1591
|
const result = await updateSkills({
|
|
1561
1592
|
projectRoot,
|
|
1562
|
-
manifest,
|
|
1563
|
-
data,
|
|
1564
|
-
packageRoot,
|
|
1593
|
+
manifest,
|
|
1594
|
+
data,
|
|
1595
|
+
packageRoot,
|
|
1596
|
+
ides,
|
|
1597
|
+
scope
|
|
1598
|
+
});
|
|
1599
|
+
config.packages.skills.version = manifest.version;
|
|
1600
|
+
await writeProjectConfig(projectRoot, config);
|
|
1601
|
+
const installedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
1602
|
+
const idx = installedManifest.installed.findIndex(
|
|
1603
|
+
(p) => p.package === SKILLS_PACKAGE2
|
|
1604
|
+
);
|
|
1605
|
+
const entry = {
|
|
1606
|
+
package: SKILLS_PACKAGE2,
|
|
1607
|
+
variant: FLAT_VARIANT2,
|
|
1608
|
+
version: manifest.version,
|
|
1609
|
+
installedAt,
|
|
1610
|
+
resources: result.resources
|
|
1611
|
+
};
|
|
1612
|
+
if (idx >= 0) installedManifest.installed[idx] = entry;
|
|
1613
|
+
else installedManifest.installed.push(entry);
|
|
1614
|
+
await writeInstalledManifest(projectRoot, installedManifest);
|
|
1615
|
+
const existingLock = await readSkillsLock(projectRoot) ?? {
|
|
1616
|
+
schemaVersion: 1,
|
|
1617
|
+
skills: {}
|
|
1618
|
+
};
|
|
1619
|
+
const lock = {
|
|
1620
|
+
schemaVersion: 1,
|
|
1621
|
+
skills: { ...existingLock.skills }
|
|
1622
|
+
};
|
|
1623
|
+
for (const skill of manifest.skills) {
|
|
1624
|
+
const mirroredTo = skill.ides.filter((i) => ides.includes(i));
|
|
1625
|
+
if (mirroredTo.length === 0) continue;
|
|
1626
|
+
lock.skills[skill.id] = {
|
|
1627
|
+
version: skill.version,
|
|
1628
|
+
from: SKILLS_PACKAGE2,
|
|
1629
|
+
installedAt,
|
|
1630
|
+
scope,
|
|
1631
|
+
mirroredTo
|
|
1632
|
+
};
|
|
1633
|
+
}
|
|
1634
|
+
await writeSkillsLock(projectRoot, lock);
|
|
1635
|
+
const { summary } = result;
|
|
1636
|
+
logger.success(`Skills updated to v${manifest.version}`);
|
|
1637
|
+
logger.info(` Created: ${summary.created}`);
|
|
1638
|
+
logger.info(` Overwritten: ${summary.overwritten}`);
|
|
1639
|
+
logger.info(` Managed: ${summary.managed}`);
|
|
1640
|
+
logger.info(` Skipped: ${summary.skipped}`);
|
|
1641
|
+
} catch (err) {
|
|
1642
|
+
logger.error(`Failed to update skills: ${err.message}`);
|
|
1643
|
+
logger.debug(err.stack ?? "");
|
|
1644
|
+
process.exitCode = 1;
|
|
1645
|
+
}
|
|
1646
|
+
});
|
|
1647
|
+
|
|
1648
|
+
// src/commands/skills/uninstall.ts
|
|
1649
|
+
import { Command as Command10 } from "commander";
|
|
1650
|
+
import * as prompts3 from "@clack/prompts";
|
|
1651
|
+
import * as path12 from "path";
|
|
1652
|
+
import * as fs9 from "fs/promises";
|
|
1653
|
+
var SKILLS_PACKAGE3 = "@teamix-evo/skills";
|
|
1654
|
+
var uninstallCommand2 = new Command10("uninstall").description(
|
|
1655
|
+
"\u5378\u8F7D\u5DF2\u5B89\u88C5\u7684 teamix-evo skills\uFF1B\u4E0D\u4F20 ids \u5219\u5378\u8F7D\u6574\u5305\uFF0C\u4F20 ids \u5219\u6309 skill \u5220\u9664"
|
|
1656
|
+
).argument(
|
|
1657
|
+
"[ids...]",
|
|
1658
|
+
"\u53EF\u9009\uFF1A\u4EC5\u5378\u8F7D\u6307\u5B9A skill id\uFF1B\u7701\u7565\u5219\u5378\u8F7D\u6574\u4E2A skills \u5305"
|
|
1659
|
+
).option("-y, --yes", "\u8DF3\u8FC7\u786E\u8BA4").action(async (ids, opts) => {
|
|
1660
|
+
try {
|
|
1661
|
+
const ide = detectIde();
|
|
1662
|
+
const cwd = ide.getProjectRoot();
|
|
1663
|
+
const projectRoot = resolveSkillsMaintenanceRoot(cwd);
|
|
1664
|
+
if (projectRoot !== cwd) {
|
|
1665
|
+
logger.info(`Using global skills meta root: ${projectRoot}`);
|
|
1666
|
+
}
|
|
1667
|
+
const config = await readProjectConfig(projectRoot);
|
|
1668
|
+
if (!config?.packages?.skills) {
|
|
1669
|
+
logger.info("Skills are not installed. Nothing to do.");
|
|
1670
|
+
return;
|
|
1671
|
+
}
|
|
1672
|
+
const installedManifest = await readInstalledManifest(projectRoot);
|
|
1673
|
+
const pkg = installedManifest?.installed.find(
|
|
1674
|
+
(p) => p.package === SKILLS_PACKAGE3
|
|
1675
|
+
);
|
|
1676
|
+
const resources = pkg?.resources ?? [];
|
|
1677
|
+
if (ids.length === 0) {
|
|
1678
|
+
await runFullUninstall({
|
|
1679
|
+
projectRoot,
|
|
1680
|
+
config,
|
|
1681
|
+
installedManifest,
|
|
1682
|
+
pkg,
|
|
1683
|
+
resources,
|
|
1684
|
+
opts
|
|
1685
|
+
});
|
|
1686
|
+
return;
|
|
1687
|
+
}
|
|
1688
|
+
await runPartialUninstall({
|
|
1689
|
+
projectRoot,
|
|
1690
|
+
installedManifest,
|
|
1691
|
+
pkg,
|
|
1692
|
+
resources,
|
|
1693
|
+
ids,
|
|
1694
|
+
opts
|
|
1695
|
+
});
|
|
1696
|
+
} catch (err) {
|
|
1697
|
+
logger.error(`Failed to uninstall: ${err.message}`);
|
|
1698
|
+
logger.debug(err.stack ?? "");
|
|
1699
|
+
process.exitCode = 1;
|
|
1700
|
+
}
|
|
1701
|
+
});
|
|
1702
|
+
async function runFullUninstall(args) {
|
|
1703
|
+
const { projectRoot, config, installedManifest, pkg, resources, opts } = args;
|
|
1704
|
+
logger.info(
|
|
1705
|
+
`Will remove ${resources.length} skill file(s) installed by ${SKILLS_PACKAGE3}.`
|
|
1706
|
+
);
|
|
1707
|
+
if (!opts.yes) {
|
|
1708
|
+
const confirm4 = await prompts3.confirm({
|
|
1709
|
+
message: "\u786E\u8BA4\u5378\u8F7D\uFF1F\u6B64\u64CD\u4F5C\u4F1A\u5220\u9664\u4E0A\u8FF0\u6587\u4EF6\u3002",
|
|
1710
|
+
initialValue: false
|
|
1711
|
+
});
|
|
1712
|
+
if (prompts3.isCancel(confirm4) || !confirm4) {
|
|
1713
|
+
logger.info("Cancelled.");
|
|
1714
|
+
return;
|
|
1715
|
+
}
|
|
1716
|
+
}
|
|
1717
|
+
const removed = await removeSkillFiles(resources);
|
|
1718
|
+
logger.debug(`Removed ${removed.length} files`);
|
|
1719
|
+
const skillsRoot = getSkillsSourceDir(projectRoot);
|
|
1720
|
+
try {
|
|
1721
|
+
await fs9.rm(skillsRoot, { recursive: true, force: true });
|
|
1722
|
+
logger.debug(`Removed source dir ${skillsRoot}`);
|
|
1723
|
+
} catch (err) {
|
|
1724
|
+
logger.warn(
|
|
1725
|
+
`Failed to remove ${skillsRoot}: ${err.message}`
|
|
1726
|
+
);
|
|
1727
|
+
}
|
|
1728
|
+
if (installedManifest && pkg) {
|
|
1729
|
+
installedManifest.installed = installedManifest.installed.filter(
|
|
1730
|
+
(p) => p.package !== SKILLS_PACKAGE3
|
|
1731
|
+
);
|
|
1732
|
+
await writeInstalledManifest(projectRoot, installedManifest);
|
|
1733
|
+
}
|
|
1734
|
+
delete config.packages.skills;
|
|
1735
|
+
await writeProjectConfig(projectRoot, config);
|
|
1736
|
+
logger.success(`Uninstalled ${SKILLS_PACKAGE3}`);
|
|
1737
|
+
logger.info(` Removed: ${removed.length} files`);
|
|
1738
|
+
logger.info(` Source: ${path12.relative(projectRoot, skillsRoot)} (cleaned)`);
|
|
1739
|
+
}
|
|
1740
|
+
async function runPartialUninstall(args) {
|
|
1741
|
+
const { projectRoot, installedManifest, pkg, resources, ids, opts } = args;
|
|
1742
|
+
const grouped = groupBySkillId(resources);
|
|
1743
|
+
const requested = dedupe(ids);
|
|
1744
|
+
const matched = requested.filter((id) => grouped.has(id));
|
|
1745
|
+
const missing = requested.filter((id) => !grouped.has(id));
|
|
1746
|
+
if (missing.length > 0) {
|
|
1747
|
+
logger.warn(`Not installed (skipped): ${missing.join(", ")}`);
|
|
1748
|
+
}
|
|
1749
|
+
if (matched.length === 0) {
|
|
1750
|
+
logger.info("Nothing to remove.");
|
|
1751
|
+
return;
|
|
1752
|
+
}
|
|
1753
|
+
const toRemove = matched.flatMap(
|
|
1754
|
+
(id) => grouped.get(id) ?? []
|
|
1755
|
+
);
|
|
1756
|
+
logger.info(
|
|
1757
|
+
`Will remove ${matched.length} skill(s): ${matched.join(", ")} (${toRemove.length} file(s)).`
|
|
1758
|
+
);
|
|
1759
|
+
if (!opts.yes) {
|
|
1760
|
+
const confirm4 = await prompts3.confirm({
|
|
1761
|
+
message: "\u786E\u8BA4\u5378\u8F7D\uFF1F\u6B64\u64CD\u4F5C\u4F1A\u5220\u9664\u4E0A\u8FF0\u6587\u4EF6\u3002",
|
|
1762
|
+
initialValue: false
|
|
1763
|
+
});
|
|
1764
|
+
if (prompts3.isCancel(confirm4) || !confirm4) {
|
|
1765
|
+
logger.info("Cancelled.");
|
|
1766
|
+
return;
|
|
1767
|
+
}
|
|
1768
|
+
}
|
|
1769
|
+
const removed = await removeSkillFiles(toRemove);
|
|
1770
|
+
logger.debug(`Removed ${removed.length} files`);
|
|
1771
|
+
for (const id of matched) {
|
|
1772
|
+
const dir = getSkillsSourceDir(projectRoot, id);
|
|
1773
|
+
try {
|
|
1774
|
+
await fs9.rm(dir, { recursive: true, force: true });
|
|
1775
|
+
logger.debug(`Removed source dir ${dir}`);
|
|
1776
|
+
} catch (err) {
|
|
1777
|
+
logger.warn(`Failed to remove ${dir}: ${err.message}`);
|
|
1778
|
+
}
|
|
1779
|
+
}
|
|
1780
|
+
const lock = await readSkillsLock(projectRoot);
|
|
1781
|
+
if (lock) {
|
|
1782
|
+
for (const id of matched) delete lock.skills[id];
|
|
1783
|
+
await writeSkillsLock(projectRoot, lock);
|
|
1784
|
+
}
|
|
1785
|
+
if (installedManifest && pkg) {
|
|
1786
|
+
pkg.resources = resources.filter((r) => !matched.includes(skillIdOf(r)));
|
|
1787
|
+
await writeInstalledManifest(projectRoot, installedManifest);
|
|
1788
|
+
}
|
|
1789
|
+
logger.success(
|
|
1790
|
+
`Removed ${matched.length} skill(s): ${matched.join(", ")}`
|
|
1791
|
+
);
|
|
1792
|
+
logger.info(` Files: ${removed.length}`);
|
|
1793
|
+
if (missing.length > 0) {
|
|
1794
|
+
logger.info(` Skipped: ${missing.join(", ")} (not installed)`);
|
|
1795
|
+
}
|
|
1796
|
+
}
|
|
1797
|
+
function skillIdOf(r) {
|
|
1798
|
+
return r.id.split(":")[0];
|
|
1799
|
+
}
|
|
1800
|
+
function groupBySkillId(records) {
|
|
1801
|
+
const map = /* @__PURE__ */ new Map();
|
|
1802
|
+
for (const r of records) {
|
|
1803
|
+
const id = skillIdOf(r);
|
|
1804
|
+
const bucket = map.get(id);
|
|
1805
|
+
if (bucket) bucket.push(r);
|
|
1806
|
+
else map.set(id, [r]);
|
|
1807
|
+
}
|
|
1808
|
+
return map;
|
|
1809
|
+
}
|
|
1810
|
+
function dedupe(values) {
|
|
1811
|
+
return Array.from(new Set(values));
|
|
1812
|
+
}
|
|
1813
|
+
|
|
1814
|
+
// src/commands/skills/sync.ts
|
|
1815
|
+
import { Command as Command11 } from "commander";
|
|
1816
|
+
|
|
1817
|
+
// src/core/skills-sync.ts
|
|
1818
|
+
import * as fs10 from "fs/promises";
|
|
1819
|
+
var SKILLS_PACKAGE_DEFAULT = "@teamix-evo/skills";
|
|
1820
|
+
async function runSkillsSync(options) {
|
|
1821
|
+
const { projectRoot, names } = options;
|
|
1822
|
+
const lock = await readSkillsLock(projectRoot);
|
|
1823
|
+
if (!lock || Object.keys(lock.skills).length === 0) {
|
|
1824
|
+
return {
|
|
1825
|
+
status: "no-skills",
|
|
1826
|
+
syncedSkillIds: [],
|
|
1827
|
+
fileCount: 0,
|
|
1828
|
+
resources: [],
|
|
1829
|
+
missingSourceIds: []
|
|
1830
|
+
};
|
|
1831
|
+
}
|
|
1832
|
+
const skillIds = Object.keys(lock.skills);
|
|
1833
|
+
const targets = names ? skillIds.filter((id) => names.includes(id)) : skillIds;
|
|
1834
|
+
const allResources = [];
|
|
1835
|
+
const synced = [];
|
|
1836
|
+
const missing = [];
|
|
1837
|
+
for (const skillId of targets) {
|
|
1838
|
+
const lockEntry = lock.skills[skillId];
|
|
1839
|
+
if (!lockEntry) continue;
|
|
1840
|
+
const sourceDir = getSkillsSourceDir(projectRoot, skillId);
|
|
1841
|
+
if (!await dirExists(sourceDir)) {
|
|
1842
|
+
logger.warn(`Skill "${skillId}" has no source at ${sourceDir}; skipped.`);
|
|
1843
|
+
missing.push(skillId);
|
|
1844
|
+
continue;
|
|
1845
|
+
}
|
|
1846
|
+
const ides = options.ides ?? lockEntry.mirroredTo;
|
|
1847
|
+
const scope = options.scope ?? lockEntry.scope;
|
|
1848
|
+
if (ides.length === 0) {
|
|
1849
|
+
logger.warn(`Skill "${skillId}" has no IDE mirror targets; skipped.`);
|
|
1850
|
+
continue;
|
|
1851
|
+
}
|
|
1852
|
+
const result = await syncSkillsToIdes({
|
|
1853
|
+
projectRoot,
|
|
1854
|
+
skills: [
|
|
1855
|
+
{
|
|
1856
|
+
id: skillId,
|
|
1857
|
+
name: skillId,
|
|
1858
|
+
updateStrategy: "regenerable"
|
|
1859
|
+
}
|
|
1860
|
+
],
|
|
1861
|
+
ides,
|
|
1862
|
+
scope
|
|
1863
|
+
});
|
|
1864
|
+
allResources.push(...result.resources);
|
|
1865
|
+
synced.push(skillId);
|
|
1866
|
+
lock.skills[skillId] = {
|
|
1867
|
+
...lockEntry,
|
|
1868
|
+
mirroredTo: ides,
|
|
1869
|
+
scope,
|
|
1870
|
+
installedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
1871
|
+
};
|
|
1872
|
+
}
|
|
1873
|
+
await writeSkillsLock(projectRoot, lock);
|
|
1874
|
+
await refreshMirrorRecords(projectRoot, allResources);
|
|
1875
|
+
return {
|
|
1876
|
+
status: "synced",
|
|
1877
|
+
syncedSkillIds: synced,
|
|
1878
|
+
fileCount: allResources.length,
|
|
1879
|
+
resources: allResources,
|
|
1880
|
+
missingSourceIds: missing
|
|
1881
|
+
};
|
|
1882
|
+
}
|
|
1883
|
+
async function dirExists(p) {
|
|
1884
|
+
try {
|
|
1885
|
+
const stat4 = await fs10.stat(p);
|
|
1886
|
+
return stat4.isDirectory();
|
|
1887
|
+
} catch {
|
|
1888
|
+
return false;
|
|
1889
|
+
}
|
|
1890
|
+
}
|
|
1891
|
+
async function refreshMirrorRecords(projectRoot, newMirrorRecords) {
|
|
1892
|
+
const installed = await readInstalledManifest(projectRoot);
|
|
1893
|
+
if (!installed) return;
|
|
1894
|
+
const pkg = installed.installed.find((p) => p.package === SKILLS_PACKAGE_DEFAULT);
|
|
1895
|
+
if (!pkg) return;
|
|
1896
|
+
const sourceOnly = pkg.resources.filter((r) => r.ide === void 0);
|
|
1897
|
+
pkg.resources = [...sourceOnly, ...newMirrorRecords];
|
|
1898
|
+
pkg.installedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
1899
|
+
await writeInstalledManifest(projectRoot, installed);
|
|
1900
|
+
}
|
|
1901
|
+
|
|
1902
|
+
// src/commands/skills/sync.ts
|
|
1903
|
+
var syncCommand = new Command11("sync").description(
|
|
1904
|
+
"\u628A .teamix-evo/skills/ \u4E0B\u7684\u6E90\u91CD\u65B0\u955C\u50CF\u5230 IDE \u8DEF\u5F84\uFF08.qoder / .claude\uFF09"
|
|
1905
|
+
).argument(
|
|
1906
|
+
"[names...]",
|
|
1907
|
+
"\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"
|
|
1908
|
+
).option(
|
|
1909
|
+
"--ide <list>",
|
|
1910
|
+
"\u9017\u53F7\u5206\u9694\u7684 IDE \u5217\u8868\uFF08\u8986\u76D6 lock \u4E2D\u8BB0\u5F55\u7684 mirroredTo\uFF09"
|
|
1911
|
+
).option(
|
|
1912
|
+
"--scope <scope>",
|
|
1913
|
+
"project | global\uFF08\u8986\u76D6 lock \u4E2D\u8BB0\u5F55\u7684 scope\uFF09"
|
|
1914
|
+
).action(async (names, opts) => {
|
|
1915
|
+
try {
|
|
1916
|
+
const ide = detectIde();
|
|
1917
|
+
const cwd = ide.getProjectRoot();
|
|
1918
|
+
const projectRoot = resolveSkillsMaintenanceRoot(cwd);
|
|
1919
|
+
if (projectRoot !== cwd) {
|
|
1920
|
+
logger.info(`Using global skills meta root: ${projectRoot}`);
|
|
1921
|
+
}
|
|
1922
|
+
const ides = opts.ide ? parseIdeList2(opts.ide) : void 0;
|
|
1923
|
+
const scope = opts.scope ? parseScope2(opts.scope) : void 0;
|
|
1924
|
+
const result = await runSkillsSync({
|
|
1925
|
+
projectRoot,
|
|
1565
1926
|
ides,
|
|
1566
1927
|
scope,
|
|
1567
|
-
|
|
1928
|
+
names: names.length > 0 ? names : void 0
|
|
1568
1929
|
});
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1930
|
+
if (result.status === "no-skills") {
|
|
1931
|
+
logger.info(
|
|
1932
|
+
"No skills recorded in .teamix-evo/skills/manifest.lock.json. Nothing to sync."
|
|
1933
|
+
);
|
|
1934
|
+
return;
|
|
1935
|
+
}
|
|
1936
|
+
logger.success(
|
|
1937
|
+
`Synced ${result.syncedSkillIds.length} skill(s) \u2192 ${result.fileCount} file(s)`
|
|
1573
1938
|
);
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
const { summary } = result;
|
|
1585
|
-
logger.success(`Skills updated to v${manifest.version}`);
|
|
1586
|
-
logger.info(` Created: ${summary.created}`);
|
|
1587
|
-
logger.info(` Overwritten: ${summary.overwritten}`);
|
|
1588
|
-
logger.info(` Managed: ${summary.managed}`);
|
|
1589
|
-
logger.info(` Skipped: ${summary.skipped}`);
|
|
1939
|
+
if (result.syncedSkillIds.length > 0) {
|
|
1940
|
+
logger.info(` Skills: ${result.syncedSkillIds.join(", ")}`);
|
|
1941
|
+
}
|
|
1942
|
+
if (result.missingSourceIds.length > 0) {
|
|
1943
|
+
logger.warn(
|
|
1944
|
+
` Missing source: ${result.missingSourceIds.join(
|
|
1945
|
+
", "
|
|
1946
|
+
)} (run "skills add <id>" to (re)install)`
|
|
1947
|
+
);
|
|
1948
|
+
}
|
|
1590
1949
|
} catch (err) {
|
|
1591
|
-
logger.error(`Failed to
|
|
1950
|
+
logger.error(`Failed to sync skills: ${err.message}`);
|
|
1592
1951
|
logger.debug(err.stack ?? "");
|
|
1593
1952
|
process.exitCode = 1;
|
|
1594
1953
|
}
|
|
1595
1954
|
});
|
|
1955
|
+
function parseIdeList2(input) {
|
|
1956
|
+
const parts = input.split(",").map((s) => s.trim().toLowerCase()).filter(Boolean);
|
|
1957
|
+
const result = [];
|
|
1958
|
+
for (const p of parts) {
|
|
1959
|
+
if (p === "qoder" || p === "claude") {
|
|
1960
|
+
if (!result.includes(p)) result.push(p);
|
|
1961
|
+
} else {
|
|
1962
|
+
throw new Error(
|
|
1963
|
+
`Unknown IDE: "${p}". Expected one of: ${ALL_IDE_KINDS.join(", ")}.`
|
|
1964
|
+
);
|
|
1965
|
+
}
|
|
1966
|
+
}
|
|
1967
|
+
return result;
|
|
1968
|
+
}
|
|
1969
|
+
function parseScope2(input) {
|
|
1970
|
+
const v = input.toLowerCase();
|
|
1971
|
+
if (v === "project" || v === "global") return v;
|
|
1972
|
+
throw new Error(`Invalid --scope: "${input}". Expected project | global.`);
|
|
1973
|
+
}
|
|
1596
1974
|
|
|
1597
|
-
// src/commands/skills/
|
|
1598
|
-
import { Command as
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1975
|
+
// src/commands/skills/doctor.ts
|
|
1976
|
+
import { Command as Command12 } from "commander";
|
|
1977
|
+
|
|
1978
|
+
// src/core/skills-doctor.ts
|
|
1979
|
+
import * as path13 from "path";
|
|
1980
|
+
import * as fs11 from "fs/promises";
|
|
1981
|
+
async function runSkillsDoctor(options) {
|
|
1982
|
+
const { projectRoot } = options;
|
|
1983
|
+
const lock = await readSkillsLock(projectRoot);
|
|
1984
|
+
if (!lock || Object.keys(lock.skills).length === 0) {
|
|
1985
|
+
return { status: "no-skills", findings: [] };
|
|
1986
|
+
}
|
|
1987
|
+
const findings = [];
|
|
1988
|
+
for (const [skillId, entry] of Object.entries(lock.skills)) {
|
|
1989
|
+
const sourceDir = getSkillsSourceDir(projectRoot, skillId);
|
|
1990
|
+
if (!await dirExists2(sourceDir)) {
|
|
1991
|
+
findings.push({
|
|
1992
|
+
kind: "missing-source",
|
|
1993
|
+
skillId,
|
|
1994
|
+
path: sourceDir,
|
|
1995
|
+
detail: 'Run "teamix-evo skills add" to reinstall.'
|
|
1996
|
+
});
|
|
1997
|
+
continue;
|
|
1998
|
+
}
|
|
1999
|
+
const sourceFiles = await walkDir(sourceDir);
|
|
2000
|
+
const sourceContents = /* @__PURE__ */ new Map();
|
|
2001
|
+
for (const f of sourceFiles) {
|
|
2002
|
+
const rel2 = path13.relative(sourceDir, f);
|
|
2003
|
+
sourceContents.set(rel2, await fs11.readFile(f, "utf-8"));
|
|
2004
|
+
}
|
|
2005
|
+
for (const ide of entry.mirroredTo) {
|
|
2006
|
+
const adapter = getAdapter(ide);
|
|
2007
|
+
const mirrorDir = adapter.getSkillTargetDir(
|
|
2008
|
+
skillId,
|
|
2009
|
+
entry.scope,
|
|
2010
|
+
projectRoot
|
|
2011
|
+
);
|
|
2012
|
+
if (!await dirExists2(mirrorDir)) {
|
|
2013
|
+
findings.push({
|
|
2014
|
+
kind: "missing-mirror",
|
|
2015
|
+
skillId,
|
|
2016
|
+
ide,
|
|
2017
|
+
scope: entry.scope,
|
|
2018
|
+
path: mirrorDir,
|
|
2019
|
+
detail: 'Run "teamix-evo skills sync" to re-mirror.'
|
|
2020
|
+
});
|
|
2021
|
+
continue;
|
|
2022
|
+
}
|
|
2023
|
+
for (const [rel2, sourceContent] of sourceContents.entries()) {
|
|
2024
|
+
const mirrorFile = path13.join(mirrorDir, rel2);
|
|
2025
|
+
if (!await fileExists(mirrorFile)) {
|
|
2026
|
+
findings.push({
|
|
2027
|
+
kind: "missing-mirror",
|
|
2028
|
+
skillId,
|
|
2029
|
+
ide,
|
|
2030
|
+
scope: entry.scope,
|
|
2031
|
+
path: mirrorFile,
|
|
2032
|
+
detail: 'Run "teamix-evo skills sync" to re-mirror.'
|
|
2033
|
+
});
|
|
2034
|
+
continue;
|
|
2035
|
+
}
|
|
2036
|
+
const mirrorContent = await fs11.readFile(mirrorFile, "utf-8");
|
|
2037
|
+
if (computeHash(mirrorContent) !== computeHash(sourceContent)) {
|
|
2038
|
+
findings.push({
|
|
2039
|
+
kind: "mirror-drift",
|
|
2040
|
+
skillId,
|
|
2041
|
+
ide,
|
|
2042
|
+
scope: entry.scope,
|
|
2043
|
+
path: mirrorFile,
|
|
2044
|
+
detail: 'Mirror differs from source. Re-run "teamix-evo skills sync" to overwrite.'
|
|
2045
|
+
});
|
|
2046
|
+
}
|
|
2047
|
+
}
|
|
2048
|
+
}
|
|
2049
|
+
}
|
|
2050
|
+
return {
|
|
2051
|
+
status: findings.length === 0 ? "clean" : "drift",
|
|
2052
|
+
findings
|
|
2053
|
+
};
|
|
2054
|
+
}
|
|
2055
|
+
async function dirExists2(p) {
|
|
2056
|
+
try {
|
|
2057
|
+
const stat4 = await fs11.stat(p);
|
|
2058
|
+
return stat4.isDirectory();
|
|
2059
|
+
} catch {
|
|
2060
|
+
return false;
|
|
2061
|
+
}
|
|
2062
|
+
}
|
|
2063
|
+
|
|
2064
|
+
// src/commands/skills/doctor.ts
|
|
2065
|
+
var doctorCommand = new Command12("doctor").description(
|
|
2066
|
+
"\u68C0\u67E5 .teamix-evo/skills/ \u6E90\u4E0E IDE \u955C\u50CF\u662F\u5426\u6F02\u79FB\uFF1B\u63D0\u793A\u5982\u4F55\u4FEE\u590D"
|
|
2067
|
+
).action(async () => {
|
|
1602
2068
|
try {
|
|
1603
2069
|
const ide = detectIde();
|
|
1604
|
-
const
|
|
1605
|
-
const
|
|
1606
|
-
if (
|
|
1607
|
-
logger.info(
|
|
2070
|
+
const cwd = ide.getProjectRoot();
|
|
2071
|
+
const projectRoot = resolveSkillsMaintenanceRoot(cwd);
|
|
2072
|
+
if (projectRoot !== cwd) {
|
|
2073
|
+
logger.info(`Using global skills meta root: ${projectRoot}`);
|
|
2074
|
+
}
|
|
2075
|
+
const result = await runSkillsDoctor({ projectRoot });
|
|
2076
|
+
if (result.status === "no-skills") {
|
|
2077
|
+
logger.info(
|
|
2078
|
+
'No skills recorded. Run "teamix-evo skills add" first.'
|
|
2079
|
+
);
|
|
1608
2080
|
return;
|
|
1609
2081
|
}
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
);
|
|
1614
|
-
const resources = pkg?.resources ?? [];
|
|
1615
|
-
logger.info(
|
|
1616
|
-
`Will remove ${resources.length} skill file(s) installed by ${SKILLS_PACKAGE3}.`
|
|
1617
|
-
);
|
|
1618
|
-
if (!opts.yes) {
|
|
1619
|
-
const confirm4 = await prompts3.confirm({
|
|
1620
|
-
message: "\u786E\u8BA4\u5378\u8F7D\uFF1F\u6B64\u64CD\u4F5C\u4F1A\u5220\u9664\u4E0A\u8FF0\u6587\u4EF6\u3002",
|
|
1621
|
-
initialValue: false
|
|
1622
|
-
});
|
|
1623
|
-
if (prompts3.isCancel(confirm4) || !confirm4) {
|
|
1624
|
-
logger.info("Cancelled.");
|
|
1625
|
-
return;
|
|
1626
|
-
}
|
|
2082
|
+
if (result.status === "clean") {
|
|
2083
|
+
logger.success("Skills are in sync. No drift detected.");
|
|
2084
|
+
return;
|
|
1627
2085
|
}
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
);
|
|
1634
|
-
|
|
2086
|
+
logger.warn(
|
|
2087
|
+
`Found ${result.findings.length} drift issue(s). Run "teamix-evo skills sync" to repair mirrors.`
|
|
2088
|
+
);
|
|
2089
|
+
for (const f of result.findings) {
|
|
2090
|
+
const idePart = f.ide ? ` [${f.ide}]` : "";
|
|
2091
|
+
logger.info(` - ${f.kind}${idePart}: ${f.skillId}`);
|
|
2092
|
+
logger.info(` ${f.path}`);
|
|
2093
|
+
if (f.detail) logger.info(` ${f.detail}`);
|
|
1635
2094
|
}
|
|
1636
|
-
|
|
1637
|
-
await writeProjectConfig(projectRoot, config);
|
|
1638
|
-
logger.success(`Uninstalled ${SKILLS_PACKAGE3}`);
|
|
1639
|
-
logger.info(` Removed: ${removed.length} files`);
|
|
2095
|
+
process.exitCode = 1;
|
|
1640
2096
|
} catch (err) {
|
|
1641
|
-
logger.error(`Failed to
|
|
2097
|
+
logger.error(`Failed to run doctor: ${err.message}`);
|
|
1642
2098
|
logger.debug(err.stack ?? "");
|
|
1643
2099
|
process.exitCode = 1;
|
|
1644
2100
|
}
|
|
1645
2101
|
});
|
|
1646
2102
|
|
|
1647
2103
|
// src/commands/skills/index.ts
|
|
1648
|
-
var skillsCommand = new
|
|
1649
|
-
"\u7BA1\u7406 teamix-evo skills\uFF08\u5411 AI IDE \u6CE8\u5165\u6280\u80FD\uFF09"
|
|
2104
|
+
var skillsCommand = new Command13("skills").description(
|
|
2105
|
+
"\u7BA1\u7406 teamix-evo skills\uFF08\u5411 AI IDE \u6CE8\u5165\u6280\u80FD\uFF1Bsource-mirror \u6A21\u578B\u89C1 ADR 0013\uFF09"
|
|
1650
2106
|
);
|
|
1651
2107
|
skillsCommand.addCommand(addCommand);
|
|
1652
2108
|
skillsCommand.addCommand(listCommand2);
|
|
1653
2109
|
skillsCommand.addCommand(updateCommand2);
|
|
2110
|
+
skillsCommand.addCommand(syncCommand);
|
|
2111
|
+
skillsCommand.addCommand(doctorCommand);
|
|
1654
2112
|
skillsCommand.addCommand(uninstallCommand2);
|
|
1655
2113
|
|
|
1656
2114
|
// src/commands/ui/index.ts
|
|
1657
|
-
import { Command as
|
|
2115
|
+
import { Command as Command17 } from "commander";
|
|
1658
2116
|
|
|
1659
2117
|
// src/commands/ui/init.ts
|
|
1660
|
-
import { Command as
|
|
2118
|
+
import { Command as Command14 } from "commander";
|
|
1661
2119
|
import * as prompts4 from "@clack/prompts";
|
|
1662
2120
|
|
|
1663
2121
|
// src/core/ui-init.ts
|
|
@@ -1665,7 +2123,9 @@ var DEFAULT_UI_ALIASES = {
|
|
|
1665
2123
|
components: "src/components/ui",
|
|
1666
2124
|
hooks: "src/hooks",
|
|
1667
2125
|
utils: "src/lib/utils",
|
|
1668
|
-
lib: "src/lib"
|
|
2126
|
+
lib: "src/lib",
|
|
2127
|
+
business: "src/components/business",
|
|
2128
|
+
templates: "src/templates"
|
|
1669
2129
|
};
|
|
1670
2130
|
var DEFAULT_UI_ICON_LIBRARY = "lucide";
|
|
1671
2131
|
async function runUiInit(options) {
|
|
@@ -1680,7 +2140,9 @@ async function runUiInit(options) {
|
|
|
1680
2140
|
components: options.aliases?.components ?? DEFAULT_UI_ALIASES.components,
|
|
1681
2141
|
hooks: options.aliases?.hooks ?? DEFAULT_UI_ALIASES.hooks,
|
|
1682
2142
|
utils: options.aliases?.utils ?? DEFAULT_UI_ALIASES.utils,
|
|
1683
|
-
lib: options.aliases?.lib ?? DEFAULT_UI_ALIASES.lib
|
|
2143
|
+
lib: options.aliases?.lib ?? DEFAULT_UI_ALIASES.lib,
|
|
2144
|
+
business: options.aliases?.business ?? DEFAULT_UI_ALIASES.business,
|
|
2145
|
+
templates: options.aliases?.templates ?? DEFAULT_UI_ALIASES.templates
|
|
1684
2146
|
};
|
|
1685
2147
|
const iconLibrary = options.iconLibrary ?? DEFAULT_UI_ICON_LIBRARY;
|
|
1686
2148
|
const tsx = options.tsx ?? true;
|
|
@@ -1710,7 +2172,7 @@ async function runUiInit(options) {
|
|
|
1710
2172
|
}
|
|
1711
2173
|
|
|
1712
2174
|
// src/commands/ui/init.ts
|
|
1713
|
-
var initCommand2 = new
|
|
2175
|
+
var initCommand2 = new Command14("init").description(
|
|
1714
2176
|
"\u521D\u59CB\u5316 teamix-evo ui \u914D\u7F6E\uFF08\u8BE2\u95EE aliases / iconLibrary / tsx / rsc\uFF09"
|
|
1715
2177
|
).option("-y, --yes", "\u4F7F\u7528\u9ED8\u8BA4\u503C\uFF0C\u8DF3\u8FC7\u4EA4\u4E92").option(
|
|
1716
2178
|
"--components <path>",
|
|
@@ -1757,7 +2219,9 @@ async function resolveConfig(opts) {
|
|
|
1757
2219
|
components: opts.components ?? DEFAULT_UI_ALIASES.components,
|
|
1758
2220
|
hooks: opts.hooks ?? DEFAULT_UI_ALIASES.hooks,
|
|
1759
2221
|
utils: opts.utils ?? DEFAULT_UI_ALIASES.utils,
|
|
1760
|
-
lib: opts.lib ?? DEFAULT_UI_ALIASES.lib
|
|
2222
|
+
lib: opts.lib ?? DEFAULT_UI_ALIASES.lib,
|
|
2223
|
+
business: DEFAULT_UI_ALIASES.business,
|
|
2224
|
+
templates: DEFAULT_UI_ALIASES.templates
|
|
1761
2225
|
},
|
|
1762
2226
|
iconLibrary: opts.iconLibrary ?? "lucide",
|
|
1763
2227
|
tsx: opts.tsx ?? true,
|
|
@@ -1800,7 +2264,14 @@ async function resolveConfig(opts) {
|
|
|
1800
2264
|
});
|
|
1801
2265
|
if (prompts4.isCancel(rscAns)) throw new Error("Cancelled by user.");
|
|
1802
2266
|
return {
|
|
1803
|
-
aliases: {
|
|
2267
|
+
aliases: {
|
|
2268
|
+
components,
|
|
2269
|
+
hooks,
|
|
2270
|
+
utils,
|
|
2271
|
+
lib,
|
|
2272
|
+
business: DEFAULT_UI_ALIASES.business,
|
|
2273
|
+
templates: DEFAULT_UI_ALIASES.templates
|
|
2274
|
+
},
|
|
1804
2275
|
iconLibrary,
|
|
1805
2276
|
tsx: tsxAns,
|
|
1806
2277
|
rsc: rscAns
|
|
@@ -1808,26 +2279,26 @@ async function resolveConfig(opts) {
|
|
|
1808
2279
|
}
|
|
1809
2280
|
|
|
1810
2281
|
// src/commands/ui/add.ts
|
|
1811
|
-
import { Command as
|
|
2282
|
+
import { Command as Command15 } from "commander";
|
|
1812
2283
|
|
|
1813
2284
|
// src/core/ui-client.ts
|
|
1814
|
-
import * as
|
|
1815
|
-
import * as
|
|
2285
|
+
import * as path14 from "path";
|
|
2286
|
+
import * as fs12 from "fs/promises";
|
|
1816
2287
|
import { createRequire as createRequire3 } from "module";
|
|
1817
2288
|
import { loadUiPackageManifest } from "@teamix-evo/registry";
|
|
1818
2289
|
var require4 = createRequire3(import.meta.url);
|
|
1819
|
-
function
|
|
2290
|
+
function resolvePackageRoot2(packageName) {
|
|
1820
2291
|
const pkgJsonPath = require4.resolve(`${packageName}/package.json`);
|
|
1821
|
-
return
|
|
2292
|
+
return path14.dirname(pkgJsonPath);
|
|
1822
2293
|
}
|
|
1823
2294
|
async function loadUiData(packageName) {
|
|
1824
|
-
const packageRoot =
|
|
2295
|
+
const packageRoot = resolvePackageRoot2(packageName);
|
|
1825
2296
|
logger.debug(`Resolved ui package root: ${packageRoot}`);
|
|
1826
2297
|
const manifest = await loadUiPackageManifest(packageRoot);
|
|
1827
2298
|
let data = {};
|
|
1828
|
-
const dataPath =
|
|
2299
|
+
const dataPath = path14.join(packageRoot, "_data.json");
|
|
1829
2300
|
try {
|
|
1830
|
-
const raw = await
|
|
2301
|
+
const raw = await fs12.readFile(dataPath, "utf-8");
|
|
1831
2302
|
data = JSON.parse(raw);
|
|
1832
2303
|
} catch (err) {
|
|
1833
2304
|
if (err.code !== "ENOENT") {
|
|
@@ -1839,8 +2310,8 @@ async function loadUiData(packageName) {
|
|
|
1839
2310
|
}
|
|
1840
2311
|
|
|
1841
2312
|
// src/core/ui-installer.ts
|
|
1842
|
-
import * as
|
|
1843
|
-
import * as
|
|
2313
|
+
import * as path15 from "path";
|
|
2314
|
+
import * as fs13 from "fs/promises";
|
|
1844
2315
|
import { resolveUiEntryOrder } from "@teamix-evo/registry";
|
|
1845
2316
|
|
|
1846
2317
|
// src/utils/transform-imports.ts
|
|
@@ -1858,10 +2329,19 @@ function rewriteImports(source, aliases) {
|
|
|
1858
2329
|
if (!aliasKey) return full;
|
|
1859
2330
|
const alias = aliases[aliasKey];
|
|
1860
2331
|
const normalized = aliasToImportPath(alias);
|
|
1861
|
-
|
|
2332
|
+
const flatRest = flattenRestPath(rest);
|
|
2333
|
+
return `${quote}${normalized}${flatRest}${quote}`;
|
|
1862
2334
|
}
|
|
1863
2335
|
);
|
|
1864
2336
|
}
|
|
2337
|
+
function flattenRestPath(rest) {
|
|
2338
|
+
if (!rest) return "";
|
|
2339
|
+
const segments = rest.split("/");
|
|
2340
|
+
if (segments.length === 3) {
|
|
2341
|
+
return `/${segments[2]}`;
|
|
2342
|
+
}
|
|
2343
|
+
return rest;
|
|
2344
|
+
}
|
|
1865
2345
|
function aliasToImportPath(alias) {
|
|
1866
2346
|
const trimmed = alias.replace(/^\.\//, "").replace(/\/$/, "");
|
|
1867
2347
|
if (trimmed.startsWith("src/")) {
|
|
@@ -1904,8 +2384,8 @@ async function installUiEntries(options) {
|
|
|
1904
2384
|
skipped++;
|
|
1905
2385
|
continue;
|
|
1906
2386
|
}
|
|
1907
|
-
const sourceAbs =
|
|
1908
|
-
const raw = await
|
|
2387
|
+
const sourceAbs = path15.resolve(packageRoot, file.source);
|
|
2388
|
+
const raw = await fs13.readFile(sourceAbs, "utf-8");
|
|
1909
2389
|
const transformed = rewriteImports(raw, aliases);
|
|
1910
2390
|
await writeFileSafe(targetAbs, transformed);
|
|
1911
2391
|
written++;
|
|
@@ -1918,9 +2398,9 @@ async function installUiEntries(options) {
|
|
|
1918
2398
|
});
|
|
1919
2399
|
}
|
|
1920
2400
|
if (entry.meta) {
|
|
1921
|
-
const metaSourceAbs =
|
|
1922
|
-
const metaContent = await
|
|
1923
|
-
const metaTargetAbs =
|
|
2401
|
+
const metaSourceAbs = path15.resolve(packageRoot, entry.meta);
|
|
2402
|
+
const metaContent = await fs13.readFile(metaSourceAbs, "utf-8");
|
|
2403
|
+
const metaTargetAbs = path15.join(
|
|
1924
2404
|
projectRoot,
|
|
1925
2405
|
DESIGN_COMPONENTS_DIR,
|
|
1926
2406
|
`${entry.id}.meta.md`
|
|
@@ -1952,10 +2432,10 @@ function resolveTargetPath(projectRoot, aliases, entry, file) {
|
|
|
1952
2432
|
`Entry "${entry.id}" requires alias "${file.targetAlias}" but it is not configured.`
|
|
1953
2433
|
);
|
|
1954
2434
|
}
|
|
1955
|
-
return
|
|
2435
|
+
return path15.join(projectRoot, aliasDir, file.targetName);
|
|
1956
2436
|
}
|
|
1957
2437
|
function rel(projectRoot, abs) {
|
|
1958
|
-
return
|
|
2438
|
+
return path15.relative(projectRoot, abs);
|
|
1959
2439
|
}
|
|
1960
2440
|
|
|
1961
2441
|
// src/core/ui-add.ts
|
|
@@ -2030,7 +2510,7 @@ function mergeResources(prior, next) {
|
|
|
2030
2510
|
}
|
|
2031
2511
|
|
|
2032
2512
|
// src/commands/ui/add.ts
|
|
2033
|
-
var addCommand2 = new
|
|
2513
|
+
var addCommand2 = new Command15("add").description(
|
|
2034
2514
|
"\u5B89\u88C5\u4E00\u4E2A\u6216\u591A\u4E2A ui entry\uFF08\u6309 id\uFF0C\u81EA\u52A8\u5C55\u5F00 registryDependencies\uFF09"
|
|
2035
2515
|
).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) => {
|
|
2036
2516
|
try {
|
|
@@ -2074,7 +2554,7 @@ var addCommand2 = new Command12("add").description(
|
|
|
2074
2554
|
});
|
|
2075
2555
|
|
|
2076
2556
|
// src/commands/ui/list.ts
|
|
2077
|
-
import { Command as
|
|
2557
|
+
import { Command as Command16 } from "commander";
|
|
2078
2558
|
|
|
2079
2559
|
// src/core/ui-list.ts
|
|
2080
2560
|
var DEFAULT_UI_PACKAGE2 = "@teamix-evo/ui";
|
|
@@ -2106,7 +2586,7 @@ async function runUiList(options) {
|
|
|
2106
2586
|
}
|
|
2107
2587
|
|
|
2108
2588
|
// src/commands/ui/list.ts
|
|
2109
|
-
var listCommand3 = new
|
|
2589
|
+
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) => {
|
|
2110
2590
|
try {
|
|
2111
2591
|
const ide = detectIde();
|
|
2112
2592
|
const projectRoot = ide.getProjectRoot();
|
|
@@ -2152,20 +2632,301 @@ var listCommand3 = new Command13("list").description("\u5217\u51FA @teamix-evo/u
|
|
|
2152
2632
|
});
|
|
2153
2633
|
|
|
2154
2634
|
// src/commands/ui/index.ts
|
|
2155
|
-
var uiCommand = new
|
|
2635
|
+
var uiCommand = new Command17("ui").description(
|
|
2156
2636
|
"\u7BA1\u7406 teamix-evo ui \u7EC4\u4EF6\uFF08\u6E90\u7801\u6CE8\u5165\u5F0F\u5B89\u88C5\uFF0Cshadcn \u98CE\u683C\uFF09"
|
|
2157
2637
|
);
|
|
2158
2638
|
uiCommand.addCommand(initCommand2);
|
|
2159
2639
|
uiCommand.addCommand(addCommand2);
|
|
2160
2640
|
uiCommand.addCommand(listCommand3);
|
|
2161
2641
|
|
|
2162
|
-
// src/index.ts
|
|
2642
|
+
// src/commands/biz-ui/index.ts
|
|
2643
|
+
import { Command as Command20 } from "commander";
|
|
2644
|
+
|
|
2645
|
+
// src/commands/biz-ui/add.ts
|
|
2646
|
+
import { Command as Command18 } from "commander";
|
|
2647
|
+
|
|
2648
|
+
// src/core/variant-ui-add.ts
|
|
2649
|
+
import * as path16 from "path";
|
|
2650
|
+
import { createRequire as createRequire4 } from "module";
|
|
2651
|
+
import {
|
|
2652
|
+
loadVariantUiPackageCatalog,
|
|
2653
|
+
loadVariantUiPackageManifest
|
|
2654
|
+
} from "@teamix-evo/registry";
|
|
2163
2655
|
var require5 = createRequire4(import.meta.url);
|
|
2164
|
-
|
|
2165
|
-
|
|
2656
|
+
function resolvePackageRoot3(packageName) {
|
|
2657
|
+
const pkgJsonPath = require5.resolve(`${packageName}/package.json`);
|
|
2658
|
+
return path16.dirname(pkgJsonPath);
|
|
2659
|
+
}
|
|
2660
|
+
async function runVariantUiAdd(packageName, options) {
|
|
2661
|
+
const { projectRoot, variant, ids, overwrite } = options;
|
|
2662
|
+
const fullPackageName = options.packageName ?? `@teamix-evo/${packageName}`;
|
|
2663
|
+
if (ids.length === 0) {
|
|
2664
|
+
throw new Error("At least one entry id must be provided.");
|
|
2665
|
+
}
|
|
2666
|
+
const config = await readProjectConfig(projectRoot);
|
|
2667
|
+
const uiCfg = config?.packages?.ui;
|
|
2668
|
+
if (!config || !uiCfg?.aliases) {
|
|
2669
|
+
throw new Error(
|
|
2670
|
+
`UI not initialized. Run \`teamix-evo ui init\` first \u2014 \`${packageName} add\` writes into the same alias map (business / templates).`
|
|
2671
|
+
);
|
|
2672
|
+
}
|
|
2673
|
+
const packageRoot = options.packageRoot ?? resolvePackageRoot3(fullPackageName);
|
|
2674
|
+
const catalog = await loadVariantUiPackageCatalog(packageRoot);
|
|
2675
|
+
if (!catalog.variants.some((v) => v.name === variant)) {
|
|
2676
|
+
const known = catalog.variants.map((v) => v.name).join(", ");
|
|
2677
|
+
throw new Error(
|
|
2678
|
+
`Variant "${variant}" not found in ${fullPackageName}. Known variants: ${known}. Hint: \`teamix-evo ${packageName} list-variants\` shows all.`
|
|
2679
|
+
);
|
|
2680
|
+
}
|
|
2681
|
+
const variantDir = path16.join(packageRoot, "variants", variant);
|
|
2682
|
+
const variantManifest = await loadVariantUiPackageManifest(variantDir);
|
|
2683
|
+
const knownIds = new Set(variantManifest.entries.map((e) => e.id));
|
|
2684
|
+
const unknown = ids.filter((id) => !knownIds.has(id));
|
|
2685
|
+
if (unknown.length > 0) {
|
|
2686
|
+
throw new Error(
|
|
2687
|
+
`Unknown entry id(s) in ${packageName}#${variant}: ${unknown.map((s) => `"${s}"`).join(", ")}. Run \`teamix-evo ${packageName} list --variant ${variant}\` to see options.`
|
|
2688
|
+
);
|
|
2689
|
+
}
|
|
2690
|
+
const adaptedManifest = {
|
|
2691
|
+
schemaVersion: 1,
|
|
2692
|
+
package: "ui",
|
|
2693
|
+
version: variantManifest.version,
|
|
2694
|
+
engines: variantManifest.engines,
|
|
2695
|
+
entries: variantManifest.entries
|
|
2696
|
+
};
|
|
2697
|
+
const result = await installUiEntries({
|
|
2698
|
+
projectRoot,
|
|
2699
|
+
manifest: adaptedManifest,
|
|
2700
|
+
packageRoot: variantDir,
|
|
2701
|
+
// sources resolved relative to variant dir
|
|
2702
|
+
aliases: uiCfg.aliases,
|
|
2703
|
+
requested: ids,
|
|
2704
|
+
skipExisting: !overwrite
|
|
2705
|
+
});
|
|
2706
|
+
const installed = await readInstalledManifest(
|
|
2707
|
+
projectRoot
|
|
2708
|
+
) ?? { schemaVersion: 1, installed: [] };
|
|
2709
|
+
const idx = installed.installed.findIndex(
|
|
2710
|
+
(p) => p.package === fullPackageName && p.variant === variant
|
|
2711
|
+
);
|
|
2712
|
+
const prior = idx >= 0 ? installed.installed[idx] : null;
|
|
2713
|
+
const mergedResources = mergeResources2(
|
|
2714
|
+
prior?.resources ?? [],
|
|
2715
|
+
result.resources
|
|
2716
|
+
);
|
|
2717
|
+
const entry = {
|
|
2718
|
+
package: fullPackageName,
|
|
2719
|
+
variant,
|
|
2720
|
+
version: variantManifest.version,
|
|
2721
|
+
installedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2722
|
+
resources: mergedResources
|
|
2723
|
+
};
|
|
2724
|
+
if (idx >= 0) installed.installed[idx] = entry;
|
|
2725
|
+
else installed.installed.push(entry);
|
|
2726
|
+
await writeInstalledManifest(projectRoot, installed);
|
|
2727
|
+
return {
|
|
2728
|
+
packageName: fullPackageName,
|
|
2729
|
+
variant,
|
|
2730
|
+
orderedIds: result.orderedIds,
|
|
2731
|
+
written: result.written,
|
|
2732
|
+
skipped: result.skipped,
|
|
2733
|
+
metaFiles: result.metaFiles,
|
|
2734
|
+
npmDependencies: result.npmDependencies,
|
|
2735
|
+
resources: result.resources
|
|
2736
|
+
};
|
|
2737
|
+
}
|
|
2738
|
+
function mergeResources2(prior, next) {
|
|
2739
|
+
const merged = /* @__PURE__ */ new Map();
|
|
2740
|
+
for (const r of prior) merged.set(r.id, r);
|
|
2741
|
+
for (const r of next) merged.set(r.id, r);
|
|
2742
|
+
return Array.from(merged.values());
|
|
2743
|
+
}
|
|
2744
|
+
async function runBizUiAdd(options) {
|
|
2745
|
+
return runVariantUiAdd("biz-ui", options);
|
|
2746
|
+
}
|
|
2747
|
+
async function runTemplatesAdd(options) {
|
|
2748
|
+
return runVariantUiAdd("templates", options);
|
|
2749
|
+
}
|
|
2750
|
+
async function listVariantUi(packageName, packageRoot) {
|
|
2751
|
+
const fullPackageName = `@teamix-evo/${packageName}`;
|
|
2752
|
+
const root = packageRoot ?? resolvePackageRoot3(fullPackageName);
|
|
2753
|
+
const catalog = await loadVariantUiPackageCatalog(root);
|
|
2754
|
+
return {
|
|
2755
|
+
packageName: fullPackageName,
|
|
2756
|
+
variants: catalog.variants.map((v) => ({
|
|
2757
|
+
name: v.name,
|
|
2758
|
+
displayName: v.displayName,
|
|
2759
|
+
version: v.version,
|
|
2760
|
+
description: v.description
|
|
2761
|
+
}))
|
|
2762
|
+
};
|
|
2763
|
+
}
|
|
2764
|
+
async function listBizUiVariants(packageRoot) {
|
|
2765
|
+
return listVariantUi("biz-ui", packageRoot);
|
|
2766
|
+
}
|
|
2767
|
+
async function listTemplatesVariants(packageRoot) {
|
|
2768
|
+
return listVariantUi("templates", packageRoot);
|
|
2769
|
+
}
|
|
2770
|
+
|
|
2771
|
+
// src/commands/biz-ui/add.ts
|
|
2772
|
+
var addCommand3 = new Command18("add").description(
|
|
2773
|
+
"\u5B89\u88C5\u4E00\u4E2A\u6216\u591A\u4E2A\u4E1A\u52A1 UI \u7EC4\u4EF6(\u6309 id,\u81EA\u52A8\u5C55\u5F00 ui \u5305\u7684 registryDependencies)"
|
|
2774
|
+
).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(
|
|
2775
|
+
async (ids, opts) => {
|
|
2776
|
+
try {
|
|
2777
|
+
if (!opts.variant) {
|
|
2778
|
+
throw new Error(
|
|
2779
|
+
"--variant <name> is required. Run `teamix-evo biz-ui list-variants` to see available variants."
|
|
2780
|
+
);
|
|
2781
|
+
}
|
|
2782
|
+
const ide = detectIde();
|
|
2783
|
+
const projectRoot = ide.getProjectRoot();
|
|
2784
|
+
logger.info(
|
|
2785
|
+
`Installing biz-ui entries from variant "${opts.variant}": ${ids.join(", ")}`
|
|
2786
|
+
);
|
|
2787
|
+
const result = await runBizUiAdd({
|
|
2788
|
+
projectRoot,
|
|
2789
|
+
variant: opts.variant,
|
|
2790
|
+
ids,
|
|
2791
|
+
overwrite: opts.overwrite
|
|
2792
|
+
});
|
|
2793
|
+
logger.success(
|
|
2794
|
+
`biz-ui add complete: ${result.written} written, ${result.skipped} skipped, ${result.metaFiles.length} meta.`
|
|
2795
|
+
);
|
|
2796
|
+
logger.info("");
|
|
2797
|
+
logger.info(`Variant: ${result.variant}`);
|
|
2798
|
+
logger.info(`Resolved order: ${result.orderedIds.join(" \u2192 ")}`);
|
|
2799
|
+
const npmDeps = Object.entries(result.npmDependencies);
|
|
2800
|
+
if (npmDeps.length > 0) {
|
|
2801
|
+
logger.info("");
|
|
2802
|
+
logger.info("Install npm dependencies in your project:");
|
|
2803
|
+
const installCmd = npmDeps.map(([name, range]) => `${name}@${range}`).join(" ");
|
|
2804
|
+
logger.info(` pnpm add ${installCmd}`);
|
|
2805
|
+
}
|
|
2806
|
+
} catch (err) {
|
|
2807
|
+
logger.error(`Failed: ${err.message}`);
|
|
2808
|
+
logger.debug(err.stack ?? "");
|
|
2809
|
+
process.exitCode = 1;
|
|
2810
|
+
}
|
|
2811
|
+
}
|
|
2812
|
+
);
|
|
2813
|
+
|
|
2814
|
+
// src/commands/biz-ui/list-variants.ts
|
|
2815
|
+
import { Command as Command19 } from "commander";
|
|
2816
|
+
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 () => {
|
|
2817
|
+
try {
|
|
2818
|
+
const result = await listBizUiVariants();
|
|
2819
|
+
logger.info(`Available biz-ui variants in ${result.packageName}:`);
|
|
2820
|
+
logger.info("");
|
|
2821
|
+
if (result.variants.length === 0) {
|
|
2822
|
+
logger.info(" (no variants yet)");
|
|
2823
|
+
return;
|
|
2824
|
+
}
|
|
2825
|
+
for (const v of result.variants) {
|
|
2826
|
+
logger.info(` ${v.name} (${v.displayName}) \u2014 v${v.version}`);
|
|
2827
|
+
if (v.description) logger.info(` ${v.description}`);
|
|
2828
|
+
logger.info("");
|
|
2829
|
+
}
|
|
2830
|
+
logger.info("Install from a variant: teamix-evo biz-ui add <id> --variant <name>");
|
|
2831
|
+
} catch (err) {
|
|
2832
|
+
logger.error(`Failed: ${err.message}`);
|
|
2833
|
+
process.exitCode = 1;
|
|
2834
|
+
}
|
|
2835
|
+
});
|
|
2836
|
+
|
|
2837
|
+
// src/commands/biz-ui/index.ts
|
|
2838
|
+
var bizUiCommand = new Command20("biz-ui").description(
|
|
2839
|
+
"\u7BA1\u7406\u4E1A\u52A1 UI \u7EC4\u4EF6(\u53D8\u4F53\u611F\u77E5 \u2014 \u4E0E design / templates \u540C\u53D8\u4F53\u540D\u7A7A\u95F4)"
|
|
2840
|
+
);
|
|
2841
|
+
bizUiCommand.addCommand(addCommand3);
|
|
2842
|
+
bizUiCommand.addCommand(listVariantsCommand2);
|
|
2843
|
+
|
|
2844
|
+
// src/commands/templates/index.ts
|
|
2845
|
+
import { Command as Command23 } from "commander";
|
|
2846
|
+
|
|
2847
|
+
// src/commands/templates/add.ts
|
|
2848
|
+
import { Command as Command21 } from "commander";
|
|
2849
|
+
var addCommand4 = new Command21("add").description(
|
|
2850
|
+
"\u5B89\u88C5\u4E00\u4E2A\u6216\u591A\u4E2A\u9875\u9762\u6A21\u677F(\u6309 id,\u81EA\u52A8\u5C55\u5F00 ui \u5305\u7684 registryDependencies)"
|
|
2851
|
+
).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(
|
|
2852
|
+
async (ids, opts) => {
|
|
2853
|
+
try {
|
|
2854
|
+
if (!opts.variant) {
|
|
2855
|
+
throw new Error(
|
|
2856
|
+
"--variant <name> is required. Run `teamix-evo templates list-variants` to see available variants."
|
|
2857
|
+
);
|
|
2858
|
+
}
|
|
2859
|
+
const ide = detectIde();
|
|
2860
|
+
const projectRoot = ide.getProjectRoot();
|
|
2861
|
+
logger.info(
|
|
2862
|
+
`Installing templates from variant "${opts.variant}": ${ids.join(", ")}`
|
|
2863
|
+
);
|
|
2864
|
+
const result = await runTemplatesAdd({
|
|
2865
|
+
projectRoot,
|
|
2866
|
+
variant: opts.variant,
|
|
2867
|
+
ids,
|
|
2868
|
+
overwrite: opts.overwrite
|
|
2869
|
+
});
|
|
2870
|
+
logger.success(
|
|
2871
|
+
`templates add complete: ${result.written} written, ${result.skipped} skipped, ${result.metaFiles.length} meta.`
|
|
2872
|
+
);
|
|
2873
|
+
logger.info("");
|
|
2874
|
+
logger.info(`Variant: ${result.variant}`);
|
|
2875
|
+
logger.info(`Resolved order: ${result.orderedIds.join(" \u2192 ")}`);
|
|
2876
|
+
const npmDeps = Object.entries(result.npmDependencies);
|
|
2877
|
+
if (npmDeps.length > 0) {
|
|
2878
|
+
logger.info("");
|
|
2879
|
+
logger.info("Install npm dependencies in your project:");
|
|
2880
|
+
const installCmd = npmDeps.map(([name, range]) => `${name}@${range}`).join(" ");
|
|
2881
|
+
logger.info(` pnpm add ${installCmd}`);
|
|
2882
|
+
}
|
|
2883
|
+
} catch (err) {
|
|
2884
|
+
logger.error(`Failed: ${err.message}`);
|
|
2885
|
+
logger.debug(err.stack ?? "");
|
|
2886
|
+
process.exitCode = 1;
|
|
2887
|
+
}
|
|
2888
|
+
}
|
|
2889
|
+
);
|
|
2890
|
+
|
|
2891
|
+
// src/commands/templates/list-variants.ts
|
|
2892
|
+
import { Command as Command22 } from "commander";
|
|
2893
|
+
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 () => {
|
|
2894
|
+
try {
|
|
2895
|
+
const result = await listTemplatesVariants();
|
|
2896
|
+
logger.info(`Available templates variants in ${result.packageName}:`);
|
|
2897
|
+
logger.info("");
|
|
2898
|
+
if (result.variants.length === 0) {
|
|
2899
|
+
logger.info(" (no variants yet)");
|
|
2900
|
+
return;
|
|
2901
|
+
}
|
|
2902
|
+
for (const v of result.variants) {
|
|
2903
|
+
logger.info(` ${v.name} (${v.displayName}) \u2014 v${v.version}`);
|
|
2904
|
+
if (v.description) logger.info(` ${v.description}`);
|
|
2905
|
+
logger.info("");
|
|
2906
|
+
}
|
|
2907
|
+
logger.info("Install from a variant: teamix-evo templates add <id> --variant <name>");
|
|
2908
|
+
} catch (err) {
|
|
2909
|
+
logger.error(`Failed: ${err.message}`);
|
|
2910
|
+
process.exitCode = 1;
|
|
2911
|
+
}
|
|
2912
|
+
});
|
|
2913
|
+
|
|
2914
|
+
// src/commands/templates/index.ts
|
|
2915
|
+
var templatesCommand = new Command23("templates").description(
|
|
2916
|
+
"\u7BA1\u7406\u9875\u9762\u6A21\u677F(\u53D8\u4F53\u611F\u77E5 \u2014 \u4E0E design / biz-ui \u540C\u53D8\u4F53\u540D\u7A7A\u95F4)"
|
|
2917
|
+
);
|
|
2918
|
+
templatesCommand.addCommand(addCommand4);
|
|
2919
|
+
templatesCommand.addCommand(listVariantsCommand3);
|
|
2920
|
+
|
|
2921
|
+
// src/index.ts
|
|
2922
|
+
var require6 = createRequire5(import.meta.url);
|
|
2923
|
+
var { version } = require6("../package.json");
|
|
2924
|
+
var program = new Command24();
|
|
2166
2925
|
program.name("teamix-evo").description("Where ideas evolve. \u2014 AI Coding \u5957\u4EF6").version(version);
|
|
2167
2926
|
program.addCommand(designCommand);
|
|
2168
2927
|
program.addCommand(skillsCommand);
|
|
2169
2928
|
program.addCommand(uiCommand);
|
|
2929
|
+
program.addCommand(bizUiCommand);
|
|
2930
|
+
program.addCommand(templatesCommand);
|
|
2170
2931
|
program.parse();
|
|
2171
2932
|
//# sourceMappingURL=index.js.map
|