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