teamix-evo 0.9.0 → 0.11.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 +35 -30
- package/dist/core/index.d.ts +44 -221
- package/dist/core/index.js +636 -1873
- package/dist/core/index.js.map +1 -1
- package/dist/index.js +6936 -6106
- package/dist/index.js.map +1 -1
- package/package.json +17 -17
- package/LICENSE +0 -21
package/dist/core/index.js
CHANGED
|
@@ -235,12 +235,6 @@ async function writeSkillsLock(projectRoot, lock) {
|
|
|
235
235
|
await writeFileSafe(lockPath, JSON.stringify(lock, null, 2) + "\n");
|
|
236
236
|
logger.debug(`Wrote skills lock \u2192 ${lockPath}`);
|
|
237
237
|
}
|
|
238
|
-
function findInstalledPackage(installed, packageName) {
|
|
239
|
-
if (!installed) return null;
|
|
240
|
-
const matches = installed.installed.filter((p) => p.package === packageName);
|
|
241
|
-
if (matches.length === 0) return null;
|
|
242
|
-
return matches[matches.length - 1] ?? null;
|
|
243
|
-
}
|
|
244
238
|
|
|
245
239
|
// src/core/skills-client.ts
|
|
246
240
|
import * as path3 from "path";
|
|
@@ -369,13 +363,14 @@ function resolveSourcePath(source, variantDir, packageRoot) {
|
|
|
369
363
|
}
|
|
370
364
|
return path6.join(variantDir, source);
|
|
371
365
|
}
|
|
372
|
-
async function walkDir(dir) {
|
|
366
|
+
async function walkDir(dir, skipDirs) {
|
|
373
367
|
const files = [];
|
|
374
368
|
const entries = await fs4.readdir(dir, { withFileTypes: true });
|
|
375
369
|
for (const entry of entries) {
|
|
376
370
|
const fullPath = path6.join(dir, entry.name);
|
|
377
371
|
if (entry.isDirectory()) {
|
|
378
|
-
|
|
372
|
+
if (skipDirs && skipDirs.has(entry.name)) continue;
|
|
373
|
+
files.push(...await walkDir(fullPath, skipDirs));
|
|
379
374
|
} else if (entry.isFile()) {
|
|
380
375
|
files.push(fullPath);
|
|
381
376
|
}
|
|
@@ -423,9 +418,9 @@ async function writeSkillSource(skill, options) {
|
|
|
423
418
|
const { data, packageRoot, projectRoot } = options;
|
|
424
419
|
const sourceAbs = path7.resolve(packageRoot, skill.source);
|
|
425
420
|
const targetDir = getSkillsSourceDir(projectRoot, skill.name);
|
|
426
|
-
const
|
|
421
|
+
const stat3 = await fs5.stat(sourceAbs);
|
|
427
422
|
const records = [];
|
|
428
|
-
if (
|
|
423
|
+
if (stat3.isFile()) {
|
|
429
424
|
const targetFile = path7.join(targetDir, "SKILL.md");
|
|
430
425
|
const content = await renderSkillContent(sourceAbs, skill, data);
|
|
431
426
|
await writeFileSafe(targetFile, content);
|
|
@@ -575,8 +570,8 @@ async function rewriteSkillSource(skill, options, summary) {
|
|
|
575
570
|
const { data, packageRoot, projectRoot } = options;
|
|
576
571
|
const sourceAbs = path7.resolve(packageRoot, skill.source);
|
|
577
572
|
const targetDir = getSkillsSourceDir(projectRoot, skill.name);
|
|
578
|
-
const
|
|
579
|
-
if (!
|
|
573
|
+
const stat3 = await fs5.stat(sourceAbs);
|
|
574
|
+
if (!stat3.isFile()) {
|
|
580
575
|
await ensureDir(targetDir);
|
|
581
576
|
const entries = await walkDir(sourceAbs);
|
|
582
577
|
const records = [];
|
|
@@ -813,13 +808,13 @@ async function pruneEmptyIdeSkillDirs(args) {
|
|
|
813
808
|
}
|
|
814
809
|
for (const name of entries) {
|
|
815
810
|
const dir = path7.join(skillsRoot, name);
|
|
816
|
-
let
|
|
811
|
+
let stat3;
|
|
817
812
|
try {
|
|
818
|
-
|
|
813
|
+
stat3 = await fs5.stat(dir);
|
|
819
814
|
} catch {
|
|
820
815
|
continue;
|
|
821
816
|
}
|
|
822
|
-
if (!
|
|
817
|
+
if (!stat3.isDirectory()) continue;
|
|
823
818
|
let children;
|
|
824
819
|
try {
|
|
825
820
|
children = await fs5.readdir(dir);
|
|
@@ -942,10 +937,25 @@ async function runSkillsInit(options) {
|
|
|
942
937
|
return { status: "already-initialized" };
|
|
943
938
|
}
|
|
944
939
|
if (onlyIds.length === 0) {
|
|
940
|
+
let autoUpdatedSkillIds = [];
|
|
941
|
+
if (outdatedSkills.length > 0) {
|
|
942
|
+
autoUpdatedSkillIds = await autoUpgradeOutdatedSkills({
|
|
943
|
+
projectRoot,
|
|
944
|
+
packageName,
|
|
945
|
+
manifest,
|
|
946
|
+
data,
|
|
947
|
+
packageRoot,
|
|
948
|
+
ides,
|
|
949
|
+
scope,
|
|
950
|
+
outdatedSkills,
|
|
951
|
+
existing,
|
|
952
|
+
existingConfig
|
|
953
|
+
});
|
|
954
|
+
}
|
|
945
955
|
return {
|
|
946
956
|
status: "installed",
|
|
947
957
|
packageName,
|
|
948
|
-
version:
|
|
958
|
+
version: manifest.version,
|
|
949
959
|
ides,
|
|
950
960
|
scope,
|
|
951
961
|
skillCount: 0,
|
|
@@ -953,7 +963,8 @@ async function runSkillsInit(options) {
|
|
|
953
963
|
resources: [],
|
|
954
964
|
addedSkillIds: [],
|
|
955
965
|
skippedSkillIds,
|
|
956
|
-
|
|
966
|
+
autoUpdatedSkillIds,
|
|
967
|
+
outdatedSkills: []
|
|
957
968
|
};
|
|
958
969
|
}
|
|
959
970
|
return finalizeSkillsInstall({
|
|
@@ -1014,10 +1025,25 @@ async function runSkillsAdd(options) {
|
|
|
1014
1025
|
existing
|
|
1015
1026
|
);
|
|
1016
1027
|
if (onlyIds.length === 0) {
|
|
1028
|
+
let autoUpdatedSkillIds = [];
|
|
1029
|
+
if (outdatedSkills.length > 0) {
|
|
1030
|
+
autoUpdatedSkillIds = await autoUpgradeOutdatedSkills({
|
|
1031
|
+
projectRoot,
|
|
1032
|
+
packageName,
|
|
1033
|
+
manifest,
|
|
1034
|
+
data,
|
|
1035
|
+
packageRoot,
|
|
1036
|
+
ides,
|
|
1037
|
+
scope,
|
|
1038
|
+
outdatedSkills,
|
|
1039
|
+
existing,
|
|
1040
|
+
existingConfig
|
|
1041
|
+
});
|
|
1042
|
+
}
|
|
1017
1043
|
return {
|
|
1018
1044
|
status: "installed",
|
|
1019
1045
|
packageName,
|
|
1020
|
-
version:
|
|
1046
|
+
version: manifest.version,
|
|
1021
1047
|
ides,
|
|
1022
1048
|
scope,
|
|
1023
1049
|
skillCount: 0,
|
|
@@ -1025,7 +1051,8 @@ async function runSkillsAdd(options) {
|
|
|
1025
1051
|
resources: [],
|
|
1026
1052
|
addedSkillIds: [],
|
|
1027
1053
|
skippedSkillIds,
|
|
1028
|
-
|
|
1054
|
+
autoUpdatedSkillIds,
|
|
1055
|
+
outdatedSkills: []
|
|
1029
1056
|
};
|
|
1030
1057
|
}
|
|
1031
1058
|
return finalizeSkillsInstall({
|
|
@@ -1179,6 +1206,21 @@ async function finalizeSkillsInstall(args) {
|
|
|
1179
1206
|
await pruneEmptyIdeSkillDirs({ projectRoot, ides, scope });
|
|
1180
1207
|
} catch {
|
|
1181
1208
|
}
|
|
1209
|
+
let autoUpdatedSkillIds = [];
|
|
1210
|
+
if (outdatedSkills && outdatedSkills.length > 0) {
|
|
1211
|
+
autoUpdatedSkillIds = await autoUpgradeOutdatedSkills({
|
|
1212
|
+
projectRoot,
|
|
1213
|
+
packageName,
|
|
1214
|
+
manifest,
|
|
1215
|
+
data,
|
|
1216
|
+
packageRoot,
|
|
1217
|
+
ides,
|
|
1218
|
+
scope,
|
|
1219
|
+
outdatedSkills,
|
|
1220
|
+
existing,
|
|
1221
|
+
existingConfig: existingConfig ?? config
|
|
1222
|
+
});
|
|
1223
|
+
}
|
|
1182
1224
|
return {
|
|
1183
1225
|
status: "installed",
|
|
1184
1226
|
packageName,
|
|
@@ -1190,7 +1232,8 @@ async function finalizeSkillsInstall(args) {
|
|
|
1190
1232
|
resources: result.resources,
|
|
1191
1233
|
addedSkillIds: onlyIds,
|
|
1192
1234
|
skippedSkillIds,
|
|
1193
|
-
|
|
1235
|
+
autoUpdatedSkillIds,
|
|
1236
|
+
outdatedSkills: []
|
|
1194
1237
|
};
|
|
1195
1238
|
}
|
|
1196
1239
|
function mergeInstalledResources(existing, next) {
|
|
@@ -1200,6 +1243,66 @@ function mergeInstalledResources(existing, next) {
|
|
|
1200
1243
|
for (const r of next) map.set(key(r), r);
|
|
1201
1244
|
return [...map.values()];
|
|
1202
1245
|
}
|
|
1246
|
+
async function autoUpgradeOutdatedSkills(args) {
|
|
1247
|
+
const {
|
|
1248
|
+
projectRoot,
|
|
1249
|
+
packageName,
|
|
1250
|
+
manifest,
|
|
1251
|
+
data,
|
|
1252
|
+
packageRoot,
|
|
1253
|
+
ides,
|
|
1254
|
+
scope,
|
|
1255
|
+
outdatedSkills,
|
|
1256
|
+
existing
|
|
1257
|
+
} = args;
|
|
1258
|
+
const targetIds = outdatedSkills.map((o) => o.id);
|
|
1259
|
+
await updateSkills({
|
|
1260
|
+
projectRoot,
|
|
1261
|
+
manifest,
|
|
1262
|
+
data,
|
|
1263
|
+
packageRoot,
|
|
1264
|
+
ides,
|
|
1265
|
+
scope,
|
|
1266
|
+
onlyIds: targetIds
|
|
1267
|
+
});
|
|
1268
|
+
const lock = existing.lock ?? {
|
|
1269
|
+
schemaVersion: 1,
|
|
1270
|
+
skills: {}
|
|
1271
|
+
};
|
|
1272
|
+
const installedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
1273
|
+
const manifestById = new Map(manifest.skills.map((s) => [s.id, s]));
|
|
1274
|
+
for (const id of targetIds) {
|
|
1275
|
+
const skillDef = manifestById.get(id);
|
|
1276
|
+
if (!skillDef) continue;
|
|
1277
|
+
const mirroredTo = skillDef.ides.filter((i) => ides.includes(i));
|
|
1278
|
+
lock.skills[id] = {
|
|
1279
|
+
version: skillDef.version,
|
|
1280
|
+
from: packageName,
|
|
1281
|
+
installedAt,
|
|
1282
|
+
scope,
|
|
1283
|
+
mirroredTo
|
|
1284
|
+
};
|
|
1285
|
+
}
|
|
1286
|
+
await writeSkillsLock(projectRoot, lock);
|
|
1287
|
+
const installedManifest = await readInstalledManifest(projectRoot) ?? {
|
|
1288
|
+
schemaVersion: 1,
|
|
1289
|
+
installed: []
|
|
1290
|
+
};
|
|
1291
|
+
const idx = installedManifest.installed.findIndex(
|
|
1292
|
+
(p) => p.package === packageName
|
|
1293
|
+
);
|
|
1294
|
+
if (idx >= 0) {
|
|
1295
|
+
installedManifest.installed[idx].version = manifest.version;
|
|
1296
|
+
installedManifest.installed[idx].installedAt = installedAt;
|
|
1297
|
+
}
|
|
1298
|
+
await writeInstalledManifest(projectRoot, installedManifest);
|
|
1299
|
+
logger.debug(
|
|
1300
|
+
`Auto-upgraded ${targetIds.length} outdated skill(s): ${targetIds.join(
|
|
1301
|
+
", "
|
|
1302
|
+
)}`
|
|
1303
|
+
);
|
|
1304
|
+
return targetIds;
|
|
1305
|
+
}
|
|
1203
1306
|
|
|
1204
1307
|
// src/core/tokens-init.ts
|
|
1205
1308
|
var DEFAULT_SKILLS_PACKAGE2 = "@teamix-evo/skills";
|
|
@@ -1251,13 +1354,16 @@ Run \`npx teamix-evo@latest tokens list-variants\` to see all options.`
|
|
|
1251
1354
|
if (!await fileExists(overridesAbs)) {
|
|
1252
1355
|
await writeFileSafe(overridesAbs, EMPTY_OVERRIDES_TEMPLATE);
|
|
1253
1356
|
}
|
|
1254
|
-
const
|
|
1255
|
-
installed.
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1357
|
+
const overridesId = `tokens:${CONSUMER_OVERRIDES_FILE}`;
|
|
1358
|
+
if (!installed.some((r) => r.id === overridesId)) {
|
|
1359
|
+
const overridesContent = await fs6.readFile(overridesAbs, "utf-8");
|
|
1360
|
+
installed.push({
|
|
1361
|
+
id: overridesId,
|
|
1362
|
+
target: path9.posix.join(CONSUMER_TOKENS_DIR, CONSUMER_OVERRIDES_FILE),
|
|
1363
|
+
hash: computeHash(overridesContent),
|
|
1364
|
+
strategy: "frozen"
|
|
1365
|
+
});
|
|
1366
|
+
}
|
|
1261
1367
|
const lock = {
|
|
1262
1368
|
schemaVersion: 1,
|
|
1263
1369
|
variant: {
|
|
@@ -1628,6 +1734,7 @@ var DEFAULT_UI_ALIASES = {
|
|
|
1628
1734
|
utils: "src/lib/utils",
|
|
1629
1735
|
lib: "src/lib",
|
|
1630
1736
|
business: "src/components/business",
|
|
1737
|
+
blocks: "src/blocks",
|
|
1631
1738
|
templates: "src/templates"
|
|
1632
1739
|
};
|
|
1633
1740
|
var DEFAULT_UI_ICON_LIBRARY = "lucide";
|
|
@@ -1645,6 +1752,7 @@ async function runUiInit(options) {
|
|
|
1645
1752
|
utils: options.aliases?.utils ?? DEFAULT_UI_ALIASES.utils,
|
|
1646
1753
|
lib: options.aliases?.lib ?? DEFAULT_UI_ALIASES.lib,
|
|
1647
1754
|
business: options.aliases?.business ?? DEFAULT_UI_ALIASES.business,
|
|
1755
|
+
blocks: options.aliases?.blocks ?? DEFAULT_UI_ALIASES.blocks,
|
|
1648
1756
|
templates: options.aliases?.templates ?? DEFAULT_UI_ALIASES.templates
|
|
1649
1757
|
};
|
|
1650
1758
|
const iconLibrary = options.iconLibrary ?? DEFAULT_UI_ICON_LIBRARY;
|
|
@@ -1713,9 +1821,11 @@ var SOURCE_ROOT_TO_ALIAS_KEY = {
|
|
|
1713
1821
|
components: "components",
|
|
1714
1822
|
hooks: "hooks",
|
|
1715
1823
|
utils: "utils",
|
|
1716
|
-
lib: "lib"
|
|
1824
|
+
lib: "lib",
|
|
1825
|
+
blocks: "blocks"
|
|
1717
1826
|
};
|
|
1718
|
-
function rewriteImports(source, aliases) {
|
|
1827
|
+
function rewriteImports(source, aliases, opts) {
|
|
1828
|
+
const shouldFlatten = opts?.flatten !== false;
|
|
1719
1829
|
return source.replace(
|
|
1720
1830
|
/(['"])@\/([a-z][a-z0-9-]*)(\/[^'"]*)?\1/g,
|
|
1721
1831
|
(full, quote, root, rest) => {
|
|
@@ -1723,8 +1833,8 @@ function rewriteImports(source, aliases) {
|
|
|
1723
1833
|
if (!aliasKey) return full;
|
|
1724
1834
|
const alias = aliases[aliasKey];
|
|
1725
1835
|
const normalized = aliasToImportPath(alias);
|
|
1726
|
-
const
|
|
1727
|
-
return `${quote}${normalized}${
|
|
1836
|
+
const resolvedRest = shouldFlatten ? flattenRestPath(rest) : rest ?? "";
|
|
1837
|
+
return `${quote}${normalized}${resolvedRest}${quote}`;
|
|
1728
1838
|
}
|
|
1729
1839
|
);
|
|
1730
1840
|
}
|
|
@@ -1753,7 +1863,8 @@ async function installUiEntries(options) {
|
|
|
1753
1863
|
entryPackageRoot,
|
|
1754
1864
|
aliases,
|
|
1755
1865
|
requested,
|
|
1756
|
-
skipExisting = true
|
|
1866
|
+
skipExisting = true,
|
|
1867
|
+
flatten = true
|
|
1757
1868
|
} = options;
|
|
1758
1869
|
const orderedIds = resolveUiEntryOrder(manifest.entries, requested);
|
|
1759
1870
|
const idToEntry = new Map(manifest.entries.map((e) => [e.id, e]));
|
|
@@ -1780,7 +1891,7 @@ async function installUiEntries(options) {
|
|
|
1780
1891
|
const rootForEntry = entryPackageRoot?.get(entry.id) ?? packageRoot;
|
|
1781
1892
|
const sourceAbs = path11.resolve(rootForEntry, file.source);
|
|
1782
1893
|
const raw = await fs8.readFile(sourceAbs, "utf-8");
|
|
1783
|
-
const transformed = rewriteImports(raw, aliases);
|
|
1894
|
+
const transformed = rewriteImports(raw, aliases, { flatten });
|
|
1784
1895
|
if (exists) {
|
|
1785
1896
|
await backupFile(targetAbs, projectRoot);
|
|
1786
1897
|
}
|
|
@@ -2208,6 +2319,33 @@ async function runLintInit(options) {
|
|
|
2208
2319
|
wroteStylelint = true;
|
|
2209
2320
|
}
|
|
2210
2321
|
const packageJsonPatched = await patchPackageJsonScripts(projectRoot);
|
|
2322
|
+
let stylelintIgnoreFilesWarning = false;
|
|
2323
|
+
if (!stylelintNeedsWrite && stylelintTemplateExists) {
|
|
2324
|
+
try {
|
|
2325
|
+
const existingContent = fs9.readFileSync(stylelintConfigPath, "utf-8");
|
|
2326
|
+
const usesTeamixPreset = existingContent.includes("@teamix-evo/stylelint-config/presets/") || existingContent.includes("@teamix-evo/stylelint-config/preset/");
|
|
2327
|
+
const hasTokenIgnore = existingContent.includes("tokens.theme.css") && existingContent.includes("tokens.overrides.css");
|
|
2328
|
+
if (!usesTeamixPreset && !hasTokenIgnore) {
|
|
2329
|
+
stylelintIgnoreFilesWarning = true;
|
|
2330
|
+
logger.warn(
|
|
2331
|
+
[
|
|
2332
|
+
"\u68C0\u6D4B\u5230\u73B0\u6709 stylelint \u914D\u7F6E\u672A\u6392\u9664 token \u5B9A\u4E49\u6587\u4EF6\u3002",
|
|
2333
|
+
"\u5EFA\u8BAE\u5728 stylelint.config.cjs \u4E2D\u6DFB\u52A0 ignoreFiles:",
|
|
2334
|
+
"",
|
|
2335
|
+
" ignoreFiles: [",
|
|
2336
|
+
" '**/tokens.theme.css',",
|
|
2337
|
+
" '**/tokens.overrides.css',",
|
|
2338
|
+
" ]",
|
|
2339
|
+
"",
|
|
2340
|
+
"\u6216\u5207\u6362\u5230 teamix-evo \u9884\u8BBE\u4EE5\u81EA\u52A8\u83B7\u5F97\u6392\u9664\u89C4\u5219:",
|
|
2341
|
+
"",
|
|
2342
|
+
" extends: ['@teamix-evo/stylelint-config/presets/consumer']"
|
|
2343
|
+
].join("\n")
|
|
2344
|
+
);
|
|
2345
|
+
}
|
|
2346
|
+
} catch {
|
|
2347
|
+
}
|
|
2348
|
+
}
|
|
2211
2349
|
return {
|
|
2212
2350
|
status: "installed",
|
|
2213
2351
|
eslint: wroteEslint,
|
|
@@ -2216,7 +2354,8 @@ async function runLintInit(options) {
|
|
|
2216
2354
|
stylelintMergeRequested: wroteStylelint && stylelintStrategy === "merge" && stylelintExistingPaths.length > 0,
|
|
2217
2355
|
eslintSkipped: eslintSkipRequested,
|
|
2218
2356
|
stylelintSkipped: stylelintSkipRequested,
|
|
2219
|
-
packageJsonPatched
|
|
2357
|
+
packageJsonPatched,
|
|
2358
|
+
stylelintIgnoreFilesWarning
|
|
2220
2359
|
};
|
|
2221
2360
|
}
|
|
2222
2361
|
function detectPm(projectRoot) {
|
|
@@ -2364,7 +2503,7 @@ function renderManagedBlockBody(args) {
|
|
|
2364
2503
|
return `# AGENTS.md
|
|
2365
2504
|
|
|
2366
2505
|
> \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
|
|
2367
|
-
> \u672C\u6587\u4EF6\u7531 \`teamix-evo init\` / \`create-teamix-evo\` \u81EA\u52A8\u751F\u6210\uFF08regenerable\uFF0C
|
|
2506
|
+
> \u672C\u6587\u4EF6\u7531 \`teamix-evo init\` / \`create-teamix-evo\` \u81EA\u52A8\u751F\u6210\uFF08regenerable\uFF0C\u9075\u5FAA ADR 0038\uFF09\uFF0C\u5237\u65B0\u65B9\u5F0F\u89C1\u5E95\u90E8\u3002
|
|
2368
2507
|
|
|
2369
2508
|
## \u5DF2\u88C5 Skills\uFF08variant: ${variant}\uFF09
|
|
2370
2509
|
|
|
@@ -2377,6 +2516,13 @@ ${skillBlock}
|
|
|
2377
2516
|
- \u6A21\u7CCA\u573A\u666F\uFF1A\u5148\u6309 SKIP \u53CD\u5411\u6392\u9664\uFF0C\u5269\u4F59\u552F\u4E00 skill \u5373\u4E3A\u5165\u53E3
|
|
2378
2517
|
- \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
|
|
2379
2518
|
|
|
2519
|
+
## UI \u7EC4\u4EF6\u9886\u5730\uFF08init \u540E\u7EA6\u675F\uFF09
|
|
2520
|
+
|
|
2521
|
+
- \`src/components/ui/\` \u7531 teamix-evo \u63A5\u7BA1\uFF0C\u7981\u6B62\u624B\u5DE5\u6216\u901A\u8FC7 shadcn CLI \u6DFB\u52A0\u65B0\u7EC4\u4EF6
|
|
2522
|
+
- \u5982\u9700\u65B0\u589E UI \u7EC4\u4EF6\uFF0C\u4F7F\u7528 \`npx teamix-evo ui add <id>\`
|
|
2523
|
+
- \`src/components/shadcn-ui/\` \u662F init \u524D legacy \u7EC4\u4EF6\u5F52\u6863\uFF0C\u53EA\u8BFB\uFF1B\u65B0\u4EE3\u7801\u4E0D\u5E94\u518D import
|
|
2524
|
+
- \u5347\u7EA7 legacy \u7EC4\u4EF6\uFF1A\u89E6\u53D1 teamix-evo-upgrade skill
|
|
2525
|
+
|
|
2380
2526
|
> \u5237\u65B0\u672C\u6587\u4EF6\uFF1A\`npx teamix-evo skills add\` \u6216\u91CD\u8DD1 \`npm create teamix-evo\` / \`teamix-evo init\`\u3002`;
|
|
2381
2527
|
}
|
|
2382
2528
|
function wrapManagedBlock(body) {
|
|
@@ -2601,8 +2747,8 @@ var STYLELINT_CONFIG_CANDIDATES = [
|
|
|
2601
2747
|
];
|
|
2602
2748
|
async function isDir(target) {
|
|
2603
2749
|
try {
|
|
2604
|
-
const
|
|
2605
|
-
return
|
|
2750
|
+
const stat3 = await fs12.stat(target);
|
|
2751
|
+
return stat3.isDirectory();
|
|
2606
2752
|
} catch {
|
|
2607
2753
|
return false;
|
|
2608
2754
|
}
|
|
@@ -2826,400 +2972,172 @@ async function detectStylelintConfig(cwd) {
|
|
|
2826
2972
|
};
|
|
2827
2973
|
}
|
|
2828
2974
|
|
|
2829
|
-
// src/core/
|
|
2830
|
-
import * as path17 from "path";
|
|
2975
|
+
// src/core/deps-install.ts
|
|
2831
2976
|
import * as fs13 from "fs/promises";
|
|
2832
|
-
|
|
2833
|
-
|
|
2834
|
-
|
|
2835
|
-
|
|
2836
|
-
|
|
2837
|
-
|
|
2838
|
-
|
|
2839
|
-
|
|
2840
|
-
|
|
2841
|
-
|
|
2842
|
-
|
|
2843
|
-
return
|
|
2844
|
-
".teamix-evo",
|
|
2845
|
-
".backups",
|
|
2846
|
-
`${legacyRel}.${tsSafe}.bak`
|
|
2847
|
-
);
|
|
2977
|
+
import * as path17 from "path";
|
|
2978
|
+
import { exec } from "child_process";
|
|
2979
|
+
import { promisify } from "util";
|
|
2980
|
+
var execAsync = promisify(exec);
|
|
2981
|
+
async function detectPackageManager(projectRoot) {
|
|
2982
|
+
if (await fileExists(path17.join(projectRoot, "pnpm-lock.yaml"))) return "pnpm";
|
|
2983
|
+
if (await fileExists(path17.join(projectRoot, "pnpm-workspace.yaml")))
|
|
2984
|
+
return "pnpm";
|
|
2985
|
+
if (await fileExists(path17.join(projectRoot, "bun.lockb"))) return "bun";
|
|
2986
|
+
if (await fileExists(path17.join(projectRoot, "bun.lock"))) return "bun";
|
|
2987
|
+
if (await fileExists(path17.join(projectRoot, "yarn.lock"))) return "yarn";
|
|
2988
|
+
return "npm";
|
|
2848
2989
|
}
|
|
2849
|
-
|
|
2850
|
-
|
|
2851
|
-
|
|
2852
|
-
|
|
2853
|
-
|
|
2854
|
-
|
|
2855
|
-
|
|
2856
|
-
|
|
2857
|
-
|
|
2858
|
-
|
|
2990
|
+
function getInstallCommand(pm) {
|
|
2991
|
+
switch (pm) {
|
|
2992
|
+
case "pnpm":
|
|
2993
|
+
return "pnpm install";
|
|
2994
|
+
case "yarn":
|
|
2995
|
+
return "yarn install";
|
|
2996
|
+
case "bun":
|
|
2997
|
+
return "bun install";
|
|
2998
|
+
case "npm":
|
|
2999
|
+
return "npm install";
|
|
3000
|
+
}
|
|
3001
|
+
}
|
|
3002
|
+
async function installProjectDeps(options) {
|
|
3003
|
+
const { projectRoot, npmDependencies, skipInstall = false } = options;
|
|
3004
|
+
const pkgPath = path17.join(projectRoot, "package.json");
|
|
3005
|
+
const raw = await readFileOrNull(pkgPath);
|
|
3006
|
+
if (!raw) {
|
|
3007
|
+
throw new Error(
|
|
3008
|
+
`package.json not found at ${projectRoot}. Cannot install dependencies.`
|
|
3009
|
+
);
|
|
2859
3010
|
}
|
|
2860
|
-
const
|
|
2861
|
-
|
|
2862
|
-
|
|
2863
|
-
|
|
2864
|
-
|
|
2865
|
-
const
|
|
2866
|
-
const
|
|
2867
|
-
const
|
|
2868
|
-
|
|
2869
|
-
|
|
2870
|
-
|
|
2871
|
-
|
|
2872
|
-
for (const legacyRel of uniqueLegacy) {
|
|
2873
|
-
const legacyAbs = path17.join(projectRoot, legacyRel);
|
|
2874
|
-
const content = await readFileOrNull(legacyAbs);
|
|
2875
|
-
if (content === null) {
|
|
2876
|
-
skipped.push({ from: legacyRel, reason: "not-found" });
|
|
2877
|
-
continue;
|
|
2878
|
-
}
|
|
2879
|
-
if (content.trim() === "") {
|
|
2880
|
-
skipped.push({ from: legacyRel, reason: "empty" });
|
|
2881
|
-
continue;
|
|
3011
|
+
const pkg = JSON.parse(raw);
|
|
3012
|
+
const existingDeps = {
|
|
3013
|
+
...pkg.dependencies,
|
|
3014
|
+
...pkg.devDependencies
|
|
3015
|
+
};
|
|
3016
|
+
const added = {};
|
|
3017
|
+
const existed = {};
|
|
3018
|
+
for (const [name, range] of Object.entries(npmDependencies)) {
|
|
3019
|
+
if (existingDeps[name]) {
|
|
3020
|
+
existed[name] = existingDeps[name];
|
|
3021
|
+
} else {
|
|
3022
|
+
added[name] = range;
|
|
2882
3023
|
}
|
|
2883
|
-
const banner = buildMigrationBanner(legacyRel, isoTimestamp);
|
|
2884
|
-
const separator = appendedPayload === "" && baseContent.endsWith("\n\n") ? "" : "\n";
|
|
2885
|
-
const block = `${separator}${banner}${content}${content.endsWith("\n") ? "" : "\n"}`;
|
|
2886
|
-
appendedPayload += block;
|
|
2887
|
-
const backupRel = dryRun ? null : computeBackupRel(legacyRel, isoTimestamp);
|
|
2888
|
-
migrated.push({
|
|
2889
|
-
from: legacyRel,
|
|
2890
|
-
backupTo: backupRel,
|
|
2891
|
-
bytesAppended: Buffer.byteLength(block, "utf-8")
|
|
2892
|
-
});
|
|
2893
3024
|
}
|
|
2894
|
-
if (
|
|
2895
|
-
|
|
2896
|
-
|
|
2897
|
-
|
|
2898
|
-
overridesPath: overridesRel,
|
|
2899
|
-
overridesCreated: dryRun ? overridesCreated : false,
|
|
2900
|
-
dryRun
|
|
2901
|
-
};
|
|
2902
|
-
}
|
|
2903
|
-
await ensureDir(path17.dirname(overridesAbs));
|
|
2904
|
-
const merged = baseContent + appendedPayload;
|
|
2905
|
-
const tmp = overridesAbs + ".tmp";
|
|
2906
|
-
await fs13.writeFile(tmp, merged, "utf-8");
|
|
2907
|
-
await fs13.rename(tmp, overridesAbs);
|
|
2908
|
-
for (const entry of migrated) {
|
|
2909
|
-
const legacyAbs = path17.join(projectRoot, entry.from);
|
|
2910
|
-
const backupAbs = path17.join(projectRoot, entry.backupTo);
|
|
2911
|
-
await ensureDir(path17.dirname(backupAbs));
|
|
2912
|
-
const srcContent = await readFileOrNull(legacyAbs);
|
|
2913
|
-
if (srcContent === null) {
|
|
2914
|
-
logger.debug(
|
|
2915
|
-
`legacy-tokens-migrate: source disappeared before backup: ${entry.from}`
|
|
2916
|
-
);
|
|
2917
|
-
continue;
|
|
3025
|
+
if (Object.keys(added).length > 0) {
|
|
3026
|
+
if (!pkg.dependencies) pkg.dependencies = {};
|
|
3027
|
+
for (const [name, range] of Object.entries(added)) {
|
|
3028
|
+
pkg.dependencies[name] = range;
|
|
2918
3029
|
}
|
|
2919
|
-
|
|
2920
|
-
|
|
2921
|
-
|
|
2922
|
-
|
|
2923
|
-
logger.
|
|
2924
|
-
`
|
|
3030
|
+
pkg.dependencies = Object.fromEntries(
|
|
3031
|
+
Object.entries(pkg.dependencies).sort(([a], [b]) => a.localeCompare(b))
|
|
3032
|
+
);
|
|
3033
|
+
await fs13.writeFile(pkgPath, JSON.stringify(pkg, null, 2) + "\n", "utf-8");
|
|
3034
|
+
logger.info(
|
|
3035
|
+
` patched package.json: +${Object.keys(added).length} dependencies`
|
|
2925
3036
|
);
|
|
2926
3037
|
}
|
|
2927
|
-
|
|
2928
|
-
|
|
2929
|
-
|
|
2930
|
-
|
|
2931
|
-
|
|
2932
|
-
dryRun: false
|
|
2933
|
-
};
|
|
2934
|
-
}
|
|
2935
|
-
|
|
2936
|
-
// src/core/snapshot.ts
|
|
2937
|
-
import * as fs14 from "fs/promises";
|
|
2938
|
-
import * as path18 from "path";
|
|
2939
|
-
var TEAMIX_DIR2 = ".teamix-evo";
|
|
2940
|
-
var SNAPSHOTS_DIR = ".snapshots";
|
|
2941
|
-
var LOGS_DIR = "logs";
|
|
2942
|
-
var META_FILE = "_meta.json";
|
|
2943
|
-
var DEFAULT_KEEP = 5;
|
|
2944
|
-
function isoToFsSafe(iso) {
|
|
2945
|
-
return iso.replace(/[:.]/g, "-");
|
|
2946
|
-
}
|
|
2947
|
-
function fsSafeToIso(safe) {
|
|
2948
|
-
return safe.replace(
|
|
2949
|
-
/^(\d{4}-\d{2}-\d{2})T(\d{2})-(\d{2})-(\d{2})-(\d{3})(Z)$/,
|
|
2950
|
-
"$1T$2:$3:$4.$5$6"
|
|
2951
|
-
);
|
|
2952
|
-
}
|
|
2953
|
-
async function createSnapshot(projectRoot, opts = {}) {
|
|
2954
|
-
const teamixDir = path18.join(projectRoot, TEAMIX_DIR2);
|
|
2955
|
-
try {
|
|
2956
|
-
const stat5 = await fs14.stat(teamixDir);
|
|
2957
|
-
if (!stat5.isDirectory()) return null;
|
|
2958
|
-
} catch (err) {
|
|
2959
|
-
if (err.code === "ENOENT") return null;
|
|
2960
|
-
throw err;
|
|
2961
|
-
}
|
|
2962
|
-
const isoTs = (/* @__PURE__ */ new Date()).toISOString();
|
|
2963
|
-
const ts = isoToFsSafe(isoTs);
|
|
2964
|
-
const snapshotRoot = path18.join(teamixDir, SNAPSHOTS_DIR);
|
|
2965
|
-
const target = path18.join(snapshotRoot, ts);
|
|
2966
|
-
await fs14.mkdir(target, { recursive: true });
|
|
2967
|
-
const entries = await fs14.readdir(teamixDir, { withFileTypes: true });
|
|
2968
|
-
for (const entry of entries) {
|
|
2969
|
-
if (entry.name === SNAPSHOTS_DIR) continue;
|
|
2970
|
-
if (entry.name === LOGS_DIR) continue;
|
|
2971
|
-
const src = path18.join(teamixDir, entry.name);
|
|
2972
|
-
const dst = path18.join(target, entry.name);
|
|
2973
|
-
await fs14.cp(src, dst, { recursive: true });
|
|
2974
|
-
}
|
|
2975
|
-
const meta = {
|
|
2976
|
-
ts: isoTs,
|
|
2977
|
-
reason: opts.reason ?? "manual"
|
|
2978
|
-
};
|
|
2979
|
-
await fs14.writeFile(
|
|
2980
|
-
path18.join(target, META_FILE),
|
|
2981
|
-
JSON.stringify(meta, null, 2) + "\n",
|
|
2982
|
-
"utf-8"
|
|
2983
|
-
);
|
|
2984
|
-
logger.debug(
|
|
2985
|
-
`Snapshot created \u2192 ${path18.relative(projectRoot, target)} (${meta.reason})`
|
|
2986
|
-
);
|
|
2987
|
-
const keep = opts.keep ?? DEFAULT_KEEP;
|
|
2988
|
-
await pruneSnapshots(projectRoot, keep, { protectedTs: opts.protectedTs });
|
|
2989
|
-
return { ts, path: target };
|
|
2990
|
-
}
|
|
2991
|
-
async function listSnapshots(projectRoot) {
|
|
2992
|
-
const snapshotRoot = path18.join(projectRoot, TEAMIX_DIR2, SNAPSHOTS_DIR);
|
|
2993
|
-
let entries;
|
|
2994
|
-
try {
|
|
2995
|
-
entries = await fs14.readdir(snapshotRoot, { withFileTypes: true });
|
|
2996
|
-
} catch (err) {
|
|
2997
|
-
if (err.code === "ENOENT") return [];
|
|
2998
|
-
throw err;
|
|
2999
|
-
}
|
|
3000
|
-
const result = [];
|
|
3001
|
-
for (const entry of entries) {
|
|
3002
|
-
if (!entry.isDirectory()) continue;
|
|
3003
|
-
const dir = path18.join(snapshotRoot, entry.name);
|
|
3004
|
-
let isoTs = null;
|
|
3005
|
-
let reason = null;
|
|
3038
|
+
const packageManager = await detectPackageManager(projectRoot);
|
|
3039
|
+
let installed = false;
|
|
3040
|
+
if (!skipInstall && Object.keys(added).length > 0) {
|
|
3041
|
+
const cmd = getInstallCommand(packageManager);
|
|
3042
|
+
logger.info(` running ${cmd}...`);
|
|
3006
3043
|
try {
|
|
3007
|
-
|
|
3008
|
-
|
|
3009
|
-
|
|
3010
|
-
|
|
3011
|
-
|
|
3012
|
-
|
|
3013
|
-
|
|
3014
|
-
|
|
3015
|
-
} catch {
|
|
3016
|
-
isoTs = fsSafeToIso(entry.name);
|
|
3044
|
+
await execAsync(cmd, { cwd: projectRoot, timeout: 12e4 });
|
|
3045
|
+
installed = true;
|
|
3046
|
+
logger.info(" install complete");
|
|
3047
|
+
} catch (err) {
|
|
3048
|
+
logger.warn(
|
|
3049
|
+
` install failed: ${err instanceof Error ? err.message : String(err)}`
|
|
3050
|
+
);
|
|
3051
|
+
logger.warn(" please run install manually");
|
|
3017
3052
|
}
|
|
3018
|
-
result.push({ ts: entry.name, isoTs, reason, path: dir });
|
|
3019
3053
|
}
|
|
3020
|
-
|
|
3021
|
-
return result;
|
|
3054
|
+
return { added, existed, installed, packageManager };
|
|
3022
3055
|
}
|
|
3023
|
-
|
|
3024
|
-
|
|
3025
|
-
|
|
3026
|
-
|
|
3027
|
-
|
|
3028
|
-
|
|
3029
|
-
|
|
3030
|
-
|
|
3031
|
-
|
|
3032
|
-
|
|
3033
|
-
|
|
3034
|
-
|
|
3035
|
-
|
|
3036
|
-
|
|
3056
|
+
|
|
3057
|
+
// src/core/init-checklist-template.ts
|
|
3058
|
+
var STEP_LABELS = {
|
|
3059
|
+
tokens: "tokens \u2014 design tokens \u5B89\u88C5",
|
|
3060
|
+
skills: "skills \u2014 AI skills \u6279\u91CF\u81EA\u4E3E",
|
|
3061
|
+
"agents-md": "agents-md \u2014 AGENTS.md \u751F\u6210",
|
|
3062
|
+
"ui-init": "ui-init \u2014 UI \u914D\u7F6E\u521D\u59CB\u5316",
|
|
3063
|
+
"ui-add": "ui-add \u2014 UI \u7EC4\u4EF6\u5168\u91CF\u5B89\u88C5",
|
|
3064
|
+
"biz-ui-add": "biz-ui-add \u2014 \u4E1A\u52A1\u7EC4\u4EF6\u5168\u91CF\u5B89\u88C5",
|
|
3065
|
+
lint: "lint \u2014 ESLint + Stylelint \u914D\u7F6E",
|
|
3066
|
+
gitignore: "gitignore \u2014 .gitignore \u8FFD\u52A0"
|
|
3067
|
+
};
|
|
3068
|
+
function renderInitChecklist(args) {
|
|
3069
|
+
const { variant, status, steps } = args;
|
|
3070
|
+
const ts = args.timestamp ?? (/* @__PURE__ */ new Date()).toISOString().replace("T", " ").slice(0, 19);
|
|
3071
|
+
const stepLines = steps.map((s) => {
|
|
3072
|
+
const label = STEP_LABELS[s.name] ?? s.name;
|
|
3073
|
+
const checked = s.status === "ok" ? "x" : " ";
|
|
3074
|
+
const suffix = s.status === "ok" ? "" : s.status === "skip" ? `\uFF08\u8DF3\u8FC7${s.detail ? "\uFF1A" + s.detail : ""}\uFF09` : s.status === "fail" ? `\uFF08\u5931\u8D25\uFF1A${s.detail ?? "unknown"}\uFF09` : "\uFF08\u8BA1\u5212\u4E2D\uFF09";
|
|
3075
|
+
return `- [${checked}] ${label}${suffix}`;
|
|
3076
|
+
}).join("\n");
|
|
3077
|
+
return `# teamix-evo init \u5B89\u88C5\u6458\u8981
|
|
3078
|
+
|
|
3079
|
+
> \u7531 \`teamix-evo init\` \u81EA\u52A8\u751F\u6210 \xB7 ${ts}
|
|
3080
|
+
> variant: ${variant} \xB7 status: ${status}
|
|
3081
|
+
|
|
3082
|
+
## \u5B89\u88C5\u6B65\u9AA4
|
|
3083
|
+
|
|
3084
|
+
${stepLines}
|
|
3085
|
+
|
|
3086
|
+
## \u5907\u6CE8
|
|
3087
|
+
|
|
3088
|
+
- \u5931\u8D25\u6216\u8DF3\u8FC7\u7684\u6B65\u9AA4\u4E0D\u5F71\u54CD\u540E\u7EED\u6B65\u9AA4\u72EC\u7ACB\u6267\u884C
|
|
3089
|
+
- \u4FEE\u590D\u540E\u91CD\u8DD1 \`teamix-evo init\`\uFF08\u6BCF\u6B65\u5E42\u7B49\uFF0C\u81EA\u52A8\u8DF3\u8FC7\u5DF2\u5B8C\u6210\u9879\uFF09
|
|
3090
|
+
- \u5982\u9700\u5168\u91CF\u8986\u76D6\uFF1A\`teamix-evo init --force\`
|
|
3091
|
+
`;
|
|
3037
3092
|
}
|
|
3038
3093
|
|
|
3039
3094
|
// src/core/file-changes.ts
|
|
3040
|
-
import * as
|
|
3041
|
-
import * as
|
|
3095
|
+
import * as fs14 from "fs/promises";
|
|
3096
|
+
import * as path18 from "path";
|
|
3042
3097
|
function toRelativePosix(p, projectRoot) {
|
|
3043
3098
|
let rel2 = p;
|
|
3044
|
-
if (
|
|
3045
|
-
rel2 =
|
|
3046
|
-
}
|
|
3047
|
-
return rel2.split(path19.sep).join("/");
|
|
3048
|
-
}
|
|
3049
|
-
async function listBackupOriginals(projectRoot) {
|
|
3050
|
-
const backupsDir = path19.join(projectRoot, ".teamix-evo", ".backups");
|
|
3051
|
-
const out = /* @__PURE__ */ new Set();
|
|
3052
|
-
const stack = [backupsDir];
|
|
3053
|
-
while (stack.length > 0) {
|
|
3054
|
-
const dir = stack.pop();
|
|
3055
|
-
let entries;
|
|
3056
|
-
try {
|
|
3057
|
-
entries = await fs15.readdir(dir, { withFileTypes: true });
|
|
3058
|
-
} catch (err) {
|
|
3059
|
-
if (err.code === "ENOENT") continue;
|
|
3060
|
-
throw err;
|
|
3061
|
-
}
|
|
3062
|
-
for (const e of entries) {
|
|
3063
|
-
const full = path19.join(dir, e.name);
|
|
3064
|
-
if (e.isDirectory()) {
|
|
3065
|
-
stack.push(full);
|
|
3066
|
-
} else if (e.isFile() && e.name.endsWith(".bak")) {
|
|
3067
|
-
const rel2 = path19.relative(backupsDir, full);
|
|
3068
|
-
const original = stripBackupSuffix(rel2);
|
|
3069
|
-
if (original) out.add(original.split(path19.sep).join("/"));
|
|
3070
|
-
}
|
|
3071
|
-
}
|
|
3072
|
-
}
|
|
3073
|
-
return out;
|
|
3074
|
-
}
|
|
3075
|
-
function stripBackupSuffix(rel2) {
|
|
3076
|
-
const m = rel2.match(
|
|
3077
|
-
/^(.+)\.\d{4}-\d{2}-\d{2}T\d{2}-\d{2}-\d{2}-\d{3}Z\.bak$/
|
|
3078
|
-
);
|
|
3079
|
-
return m?.[1] ?? null;
|
|
3080
|
-
}
|
|
3081
|
-
function diffBackupSet(before, after) {
|
|
3082
|
-
const out = /* @__PURE__ */ new Set();
|
|
3083
|
-
for (const p of after) {
|
|
3084
|
-
if (!before.has(p)) out.add(p);
|
|
3099
|
+
if (path18.isAbsolute(p)) {
|
|
3100
|
+
rel2 = path18.relative(projectRoot, p);
|
|
3085
3101
|
}
|
|
3086
|
-
return
|
|
3102
|
+
return rel2.split(path18.sep).join("/");
|
|
3087
3103
|
}
|
|
3088
3104
|
|
|
3089
3105
|
// src/core/project-init.ts
|
|
3090
|
-
|
|
3091
|
-
|
|
3092
|
-
"button-group",
|
|
3093
|
-
"input",
|
|
3094
|
-
"form",
|
|
3095
|
-
"card",
|
|
3096
|
-
"collapsible",
|
|
3097
|
-
"dialog",
|
|
3098
|
-
"dropdown-menu",
|
|
3099
|
-
"tabs",
|
|
3100
|
-
"table",
|
|
3101
|
-
"sidebar",
|
|
3102
|
-
"page-shell",
|
|
3103
|
-
"page-header"
|
|
3104
|
-
];
|
|
3106
|
+
import * as fsNode from "fs/promises";
|
|
3107
|
+
import * as path19 from "path";
|
|
3105
3108
|
var CRITICAL_STEPS = /* @__PURE__ */ new Set([
|
|
3106
3109
|
"tokens",
|
|
3107
3110
|
"skills",
|
|
3108
3111
|
"ui-init"
|
|
3109
3112
|
]);
|
|
3110
|
-
var
|
|
3111
|
-
|
|
3112
|
-
|
|
3113
|
-
|
|
3114
|
-
"
|
|
3115
|
-
|
|
3116
|
-
"
|
|
3117
|
-
"
|
|
3118
|
-
"
|
|
3119
|
-
"
|
|
3120
|
-
|
|
3121
|
-
|
|
3122
|
-
"
|
|
3123
|
-
|
|
3124
|
-
};
|
|
3125
|
-
function pickIde(ides) {
|
|
3126
|
-
return ides[0] ?? "qoder";
|
|
3127
|
-
}
|
|
3128
|
-
function strategyImplemented(key, strategy) {
|
|
3129
|
-
return IMPLEMENTED_STRATEGIES[key]?.includes(strategy) ?? false;
|
|
3130
|
-
}
|
|
3131
|
-
async function resolveUiEntries(options) {
|
|
3132
|
-
if (options.uiEntries && options.uiEntries.length > 0) {
|
|
3133
|
-
return [...options.uiEntries];
|
|
3134
|
-
}
|
|
3135
|
-
if (options.answers.uiSelection === "all") {
|
|
3136
|
-
const { manifest } = await loadUiData("@teamix-evo/ui");
|
|
3137
|
-
return manifest.entries.map((e) => e.id);
|
|
3138
|
-
}
|
|
3139
|
-
return [...BASELINE_UI_ENTRIES];
|
|
3140
|
-
}
|
|
3141
|
-
function deriveTokensChanges(result, projectRoot) {
|
|
3142
|
-
if (result.status !== "installed") return [];
|
|
3143
|
-
return result.resources.map((r) => ({
|
|
3144
|
-
kind: "created",
|
|
3145
|
-
path: toRelativePosix(r.target, projectRoot),
|
|
3146
|
-
step: "tokens",
|
|
3147
|
-
detail: r.strategy
|
|
3148
|
-
}));
|
|
3149
|
-
}
|
|
3150
|
-
function deriveSkillsChanges(result, projectRoot) {
|
|
3151
|
-
if (result.status !== "installed") return [];
|
|
3152
|
-
return result.addedSkillIds.map((id) => ({
|
|
3153
|
-
kind: "created",
|
|
3154
|
-
path: `.teamix-evo/skills/${id}/SKILL.md`,
|
|
3155
|
-
step: "skills",
|
|
3156
|
-
detail: "skill installed (source mirror + IDE mirrors)"
|
|
3157
|
-
}));
|
|
3158
|
-
}
|
|
3159
|
-
function deriveUiAddChanges(result, projectRoot) {
|
|
3160
|
-
const out = [];
|
|
3161
|
-
let remaining = result.written;
|
|
3162
|
-
for (let i = result.resources.length - 1; i >= 0 && remaining > 0; i--) {
|
|
3163
|
-
const r = result.resources[i];
|
|
3164
|
-
out.unshift({
|
|
3165
|
-
kind: "created",
|
|
3166
|
-
path: toRelativePosix(r.target, projectRoot),
|
|
3167
|
-
step: "ui-add",
|
|
3168
|
-
detail: r.strategy
|
|
3169
|
-
});
|
|
3170
|
-
remaining--;
|
|
3171
|
-
}
|
|
3172
|
-
return out;
|
|
3173
|
-
}
|
|
3174
|
-
function deriveLintChanges(result) {
|
|
3175
|
-
if (result.status !== "installed") return [];
|
|
3176
|
-
const out = [];
|
|
3177
|
-
if (result.eslint) {
|
|
3178
|
-
out.push({
|
|
3179
|
-
kind: "created",
|
|
3180
|
-
path: "eslint.config.js",
|
|
3181
|
-
step: "lint",
|
|
3182
|
-
detail: "@teamix-evo/eslint-config consumer preset"
|
|
3183
|
-
});
|
|
3184
|
-
}
|
|
3185
|
-
if (result.stylelint) {
|
|
3186
|
-
out.push({
|
|
3187
|
-
kind: "created",
|
|
3188
|
-
path: "stylelint.config.cjs",
|
|
3189
|
-
step: "lint",
|
|
3190
|
-
detail: "@teamix-evo/stylelint-config consumer preset"
|
|
3191
|
-
});
|
|
3192
|
-
}
|
|
3193
|
-
if (result.packageJsonPatched) {
|
|
3194
|
-
out.push({
|
|
3195
|
-
kind: "created",
|
|
3196
|
-
path: "package.json",
|
|
3197
|
-
step: "lint",
|
|
3198
|
-
detail: 'scripts.lint / scripts["lint:css"]'
|
|
3199
|
-
});
|
|
3200
|
-
}
|
|
3201
|
-
return out;
|
|
3202
|
-
}
|
|
3113
|
+
var GITIGNORE_MARKER_START = "# >>> teamix-evo:managed >>>";
|
|
3114
|
+
var GITIGNORE_MARKER_END = "# <<< teamix-evo:managed <<<";
|
|
3115
|
+
var GITIGNORE_RULES = [
|
|
3116
|
+
"# IDE skill mirrors (regenerable via `teamix-evo skills sync`; ADR 0013)",
|
|
3117
|
+
".qoder/skills/",
|
|
3118
|
+
".claude/skills/",
|
|
3119
|
+
"",
|
|
3120
|
+
"# Runtime artifacts (regenerable / archives)",
|
|
3121
|
+
".teamix-evo/.snapshots/",
|
|
3122
|
+
".teamix-evo/logs/",
|
|
3123
|
+
".teamix-evo/.backups/",
|
|
3124
|
+
".teamix-evo/.upgrade-staging/",
|
|
3125
|
+
".teamix-evo/.upgrade-hints/"
|
|
3126
|
+
];
|
|
3203
3127
|
async function runProjectInit(options) {
|
|
3204
|
-
const {
|
|
3205
|
-
|
|
3128
|
+
const {
|
|
3129
|
+
projectRoot,
|
|
3130
|
+
variant,
|
|
3131
|
+
ides,
|
|
3132
|
+
scope = "project",
|
|
3133
|
+
dryRun = false,
|
|
3134
|
+
onStep
|
|
3135
|
+
} = options;
|
|
3136
|
+
const ide = ides[0] ?? "qoder";
|
|
3206
3137
|
const steps = [];
|
|
3207
|
-
const pending = [];
|
|
3208
3138
|
const allChanges = [];
|
|
3209
|
-
const backupsBefore = dryRun ? /* @__PURE__ */ new Set() : await listBackupOriginals(projectRoot).catch(() => /* @__PURE__ */ new Set());
|
|
3210
|
-
let snapshot = null;
|
|
3211
|
-
let snapshotError;
|
|
3212
|
-
if (!dryRun) {
|
|
3213
|
-
try {
|
|
3214
|
-
snapshot = await createSnapshot(projectRoot, { reason: "init" });
|
|
3215
|
-
} catch (err) {
|
|
3216
|
-
snapshotError = getErrorMessage(err);
|
|
3217
|
-
}
|
|
3218
|
-
}
|
|
3219
3139
|
let aborted = false;
|
|
3220
|
-
const firstFailure = {
|
|
3221
|
-
value: null
|
|
3222
|
-
};
|
|
3140
|
+
const firstFailure = { value: null };
|
|
3223
3141
|
function record(step) {
|
|
3224
3142
|
steps.push(step);
|
|
3225
3143
|
onStep?.(step);
|
|
@@ -3227,16 +3145,6 @@ async function runProjectInit(options) {
|
|
|
3227
3145
|
allChanges.push(...step.changes);
|
|
3228
3146
|
}
|
|
3229
3147
|
}
|
|
3230
|
-
function recordPending(key) {
|
|
3231
|
-
const strategy = answers.conflictDecisions[key];
|
|
3232
|
-
if (!strategy) return;
|
|
3233
|
-
if (strategyImplemented(key, strategy)) return;
|
|
3234
|
-
pending.push({
|
|
3235
|
-
key,
|
|
3236
|
-
strategy,
|
|
3237
|
-
reason: `Strategy "${strategy}" requires the managed-region engine (batch 4); recorded for follow-up.`
|
|
3238
|
-
});
|
|
3239
|
-
}
|
|
3240
3148
|
function recordFailure(name, err) {
|
|
3241
3149
|
const message = getErrorMessage(err);
|
|
3242
3150
|
record({ name, status: "fail", detail: message });
|
|
@@ -3244,61 +3152,36 @@ async function runProjectInit(options) {
|
|
|
3244
3152
|
firstFailure.value = { step: name, error: message };
|
|
3245
3153
|
if (CRITICAL_STEPS.has(name)) aborted = true;
|
|
3246
3154
|
}
|
|
3247
|
-
|
|
3248
|
-
const legacyTokensPaths = options.legacyTokensPaths ?? [];
|
|
3249
|
-
const wantMigrate = tokensDecision === "migrate" && legacyTokensPaths.length > 0;
|
|
3250
|
-
if (tokensDecision === "skip") {
|
|
3251
|
-
record({
|
|
3252
|
-
name: "tokens",
|
|
3253
|
-
status: "skip",
|
|
3254
|
-
detail: "conflict strategy = skip"
|
|
3255
|
-
});
|
|
3256
|
-
} else if (dryRun) {
|
|
3257
|
-
const planDetail = wantMigrate ? `runTokensInit(variant=${answers.variant}); migrateLegacyTokens(${legacyTokensPaths.length} file${legacyTokensPaths.length === 1 ? "" : "s"})` : `runTokensInit(variant=${answers.variant})`;
|
|
3155
|
+
if (dryRun) {
|
|
3258
3156
|
record({
|
|
3259
3157
|
name: "tokens",
|
|
3260
3158
|
status: "planned",
|
|
3261
|
-
detail:
|
|
3159
|
+
detail: `runTokensInit(variant=${variant})`
|
|
3262
3160
|
});
|
|
3263
3161
|
} else {
|
|
3264
3162
|
try {
|
|
3265
|
-
const result = await runTokensInit({
|
|
3266
|
-
|
|
3267
|
-
variant: answers.variant,
|
|
3268
|
-
ide
|
|
3269
|
-
});
|
|
3270
|
-
let detail = result.status === "installed" ? `${result.packageName}@${result.version} (${result.count} files)` : result.status;
|
|
3271
|
-
if (wantMigrate) {
|
|
3272
|
-
try {
|
|
3273
|
-
const m = await migrateLegacyTokens({
|
|
3274
|
-
projectRoot,
|
|
3275
|
-
legacyPaths: legacyTokensPaths
|
|
3276
|
-
});
|
|
3277
|
-
detail += `; migrated ${m.migrated.length}/${legacyTokensPaths.length} legacy file${legacyTokensPaths.length === 1 ? "" : "s"} \u2192 ${m.overridesPath}`;
|
|
3278
|
-
if (m.skipped.length > 0) {
|
|
3279
|
-
detail += ` (skipped ${m.skipped.length})`;
|
|
3280
|
-
}
|
|
3281
|
-
} catch (err) {
|
|
3282
|
-
detail += `; migrate failed: ${getErrorMessage(err)}`;
|
|
3283
|
-
}
|
|
3284
|
-
}
|
|
3163
|
+
const result = await runTokensInit({ projectRoot, variant, ide });
|
|
3164
|
+
const detail = result.status === "installed" ? `${result.packageName}@${result.version} (${result.count} files)` : result.status;
|
|
3285
3165
|
record({
|
|
3286
3166
|
name: "tokens",
|
|
3287
3167
|
status: "ok",
|
|
3288
3168
|
detail,
|
|
3289
|
-
changes:
|
|
3169
|
+
changes: result.status === "installed" ? result.resources.map((r) => ({
|
|
3170
|
+
kind: "created",
|
|
3171
|
+
path: toRelativePosix(r.target, projectRoot),
|
|
3172
|
+
step: "tokens",
|
|
3173
|
+
detail: r.strategy
|
|
3174
|
+
})) : []
|
|
3290
3175
|
});
|
|
3291
3176
|
} catch (err) {
|
|
3292
3177
|
recordFailure("tokens", err);
|
|
3293
3178
|
}
|
|
3294
3179
|
}
|
|
3295
|
-
recordPending("tokens");
|
|
3296
|
-
const codeSkillId = `teamix-evo-code-${answers.variant}`;
|
|
3297
3180
|
if (dryRun) {
|
|
3298
3181
|
record({
|
|
3299
3182
|
name: "skills",
|
|
3300
3183
|
status: "planned",
|
|
3301
|
-
detail: `
|
|
3184
|
+
detail: `runSkillsInit(scope=${scope})`
|
|
3302
3185
|
});
|
|
3303
3186
|
} else if (aborted) {
|
|
3304
3187
|
record({
|
|
@@ -3308,1465 +3191,346 @@ async function runProjectInit(options) {
|
|
|
3308
3191
|
});
|
|
3309
3192
|
} else {
|
|
3310
3193
|
try {
|
|
3311
|
-
const result = await
|
|
3312
|
-
|
|
3313
|
-
|
|
3314
|
-
|
|
3315
|
-
|
|
3316
|
-
|
|
3317
|
-
|
|
3318
|
-
|
|
3319
|
-
|
|
3320
|
-
|
|
3321
|
-
|
|
3322
|
-
|
|
3323
|
-
|
|
3324
|
-
|
|
3325
|
-
|
|
3326
|
-
|
|
3327
|
-
}
|
|
3328
|
-
const agentsMdDecision = answers.conflictDecisions["agents-md"];
|
|
3329
|
-
const agentsMdSkillIds = [
|
|
3330
|
-
`teamix-evo-design-${answers.variant}`,
|
|
3331
|
-
codeSkillId
|
|
3332
|
-
];
|
|
3333
|
-
if (agentsMdDecision === "skip") {
|
|
3334
|
-
record({
|
|
3335
|
-
name: "agents-md",
|
|
3336
|
-
status: "skip",
|
|
3337
|
-
detail: "conflict strategy = skip"
|
|
3338
|
-
});
|
|
3339
|
-
} else if (dryRun) {
|
|
3340
|
-
record({
|
|
3341
|
-
name: "agents-md",
|
|
3342
|
-
status: "planned",
|
|
3343
|
-
detail: `runGenerateAgentsMd(${agentsMdSkillIds.length} skills)`
|
|
3344
|
-
});
|
|
3345
|
-
} else {
|
|
3346
|
-
try {
|
|
3347
|
-
const result = await runGenerateAgentsMd({
|
|
3348
|
-
projectRoot,
|
|
3349
|
-
variant: answers.variant,
|
|
3350
|
-
skillIds: agentsMdSkillIds,
|
|
3351
|
-
// Phase 2.B: when the user picked `merge-managed` on conflict, only
|
|
3352
|
-
// rewrite the `teamix-evo-skills` managed region so hand-written
|
|
3353
|
-
// sections survive the regenerate step. `overwrite` keeps the
|
|
3354
|
-
// historical full-rewrite default.
|
|
3355
|
-
mode: agentsMdDecision === "merge-managed" ? "merge-managed" : "overwrite"
|
|
3356
|
-
});
|
|
3357
|
-
record({
|
|
3358
|
-
name: "agents-md",
|
|
3359
|
-
status: "ok",
|
|
3360
|
-
detail: `${result.skillCount} skill index${result.missingSkillIds.length > 0 ? ` (missing SKILL.md: ${result.missingSkillIds.join(", ")})` : ""}`,
|
|
3361
|
-
changes: [
|
|
3362
|
-
{
|
|
3363
|
-
kind: result.backedUp ? "modified" : "created",
|
|
3364
|
-
path: toRelativePosix(result.path, projectRoot),
|
|
3365
|
-
step: "agents-md",
|
|
3366
|
-
detail: "skill-trigger fallback (ADR 0038)"
|
|
3367
|
-
}
|
|
3368
|
-
]
|
|
3369
|
-
});
|
|
3370
|
-
} catch (err) {
|
|
3371
|
-
recordFailure("agents-md", err);
|
|
3372
|
-
}
|
|
3373
|
-
}
|
|
3374
|
-
recordPending("agents-md");
|
|
3375
|
-
const componentsJsonDecision = answers.conflictDecisions["components-json"];
|
|
3376
|
-
const shadcnDecision = answers.conflictDecisions["shadcn-source"];
|
|
3377
|
-
const skipUiInit = !answers.withUi || componentsJsonDecision === "skip";
|
|
3378
|
-
if (skipUiInit) {
|
|
3379
|
-
record({
|
|
3380
|
-
name: "ui-init",
|
|
3381
|
-
status: "skip",
|
|
3382
|
-
detail: !answers.withUi ? "withUi = false" : "components.json conflict strategy = skip"
|
|
3383
|
-
});
|
|
3384
|
-
record({
|
|
3385
|
-
name: "ui-add",
|
|
3386
|
-
status: "skip",
|
|
3387
|
-
detail: !answers.withUi ? "withUi = false" : "components.json conflict strategy = skip"
|
|
3388
|
-
});
|
|
3389
|
-
} else if (dryRun) {
|
|
3390
|
-
record({
|
|
3391
|
-
name: "ui-init",
|
|
3392
|
-
status: "planned",
|
|
3393
|
-
detail: "runUiInit()"
|
|
3394
|
-
});
|
|
3395
|
-
const entries = await resolveUiEntries(options).catch(() => [
|
|
3396
|
-
...BASELINE_UI_ENTRIES
|
|
3397
|
-
]);
|
|
3398
|
-
record({
|
|
3399
|
-
name: "ui-add",
|
|
3400
|
-
status: "planned",
|
|
3401
|
-
detail: `runUiAdd(${entries.length} entries: ${entries.slice(0, 3).join(", ")}${entries.length > 3 ? "\u2026" : ""})`
|
|
3402
|
-
});
|
|
3403
|
-
} else {
|
|
3404
|
-
if (aborted) {
|
|
3405
|
-
record({
|
|
3406
|
-
name: "ui-init",
|
|
3407
|
-
status: "skip",
|
|
3408
|
-
detail: "aborted: earlier critical step failed"
|
|
3409
|
-
});
|
|
3410
|
-
record({
|
|
3411
|
-
name: "ui-add",
|
|
3412
|
-
status: "skip",
|
|
3413
|
-
detail: "aborted: earlier critical step failed"
|
|
3414
|
-
});
|
|
3415
|
-
} else {
|
|
3416
|
-
let uiInitOk = false;
|
|
3417
|
-
try {
|
|
3418
|
-
const initResult = await runUiInit({ projectRoot, ide });
|
|
3419
|
-
record({
|
|
3420
|
-
name: "ui-init",
|
|
3421
|
-
status: "ok",
|
|
3422
|
-
detail: initResult.status === "installed" ? "config.json packages.ui written" : initResult.status
|
|
3423
|
-
});
|
|
3424
|
-
uiInitOk = true;
|
|
3425
|
-
} catch (err) {
|
|
3426
|
-
recordFailure("ui-init", err);
|
|
3427
|
-
}
|
|
3428
|
-
if (!uiInitOk) {
|
|
3429
|
-
record({
|
|
3430
|
-
name: "ui-add",
|
|
3431
|
-
status: "skip",
|
|
3432
|
-
detail: "aborted: ui-init failed"
|
|
3433
|
-
});
|
|
3434
|
-
} else if (shadcnDecision === "skip") {
|
|
3435
|
-
record({
|
|
3436
|
-
name: "ui-add",
|
|
3437
|
-
status: "skip",
|
|
3438
|
-
detail: "shadcn-source conflict strategy = skip"
|
|
3439
|
-
});
|
|
3440
|
-
} else {
|
|
3441
|
-
try {
|
|
3442
|
-
const entries = await resolveUiEntries(options);
|
|
3443
|
-
const addResult = await runUiAdd({
|
|
3444
|
-
projectRoot,
|
|
3445
|
-
ids: entries,
|
|
3446
|
-
// 'overwrite' strategy → overwrite=true; everything else (incl.
|
|
3447
|
-
// 'skip-existing' which is the default) → overwrite=false.
|
|
3448
|
-
overwrite: shadcnDecision === "overwrite"
|
|
3449
|
-
});
|
|
3450
|
-
record({
|
|
3451
|
-
name: "ui-add",
|
|
3452
|
-
status: "ok",
|
|
3453
|
-
detail: `${addResult.orderedIds.length} entries (${addResult.written} written, ${addResult.skipped} skipped)`,
|
|
3454
|
-
changes: deriveUiAddChanges(addResult, projectRoot)
|
|
3455
|
-
});
|
|
3456
|
-
} catch (err) {
|
|
3457
|
-
recordFailure("ui-add", err);
|
|
3458
|
-
}
|
|
3459
|
-
}
|
|
3460
|
-
}
|
|
3461
|
-
}
|
|
3462
|
-
recordPending("components-json");
|
|
3463
|
-
recordPending("shadcn-source");
|
|
3464
|
-
if (!answers.withLint) {
|
|
3465
|
-
record({
|
|
3466
|
-
name: "lint",
|
|
3467
|
-
status: "skip",
|
|
3468
|
-
detail: "withLint = false"
|
|
3469
|
-
});
|
|
3470
|
-
} else if (dryRun) {
|
|
3471
|
-
record({
|
|
3472
|
-
name: "lint",
|
|
3473
|
-
status: "planned",
|
|
3474
|
-
detail: "runLintInit()"
|
|
3475
|
-
});
|
|
3476
|
-
} else {
|
|
3477
|
-
try {
|
|
3478
|
-
const eslintStrategy = answers.conflictDecisions["eslint-config"];
|
|
3479
|
-
const stylelintStrategy = answers.conflictDecisions["stylelint-config"];
|
|
3480
|
-
const eslintExistingPaths = options.legacyEslintPaths ?? [];
|
|
3481
|
-
const stylelintExistingPaths = options.legacyStylelintPaths ?? [];
|
|
3482
|
-
const result = await runLintInit({
|
|
3483
|
-
projectRoot,
|
|
3484
|
-
skipInstall: options.skipInstall ?? false,
|
|
3485
|
-
eslintStrategy: eslintStrategy === "merge" || eslintStrategy === "backup-overwrite" || eslintStrategy === "skip" || eslintStrategy === "overwrite" ? eslintStrategy : "overwrite",
|
|
3486
|
-
stylelintStrategy: stylelintStrategy === "merge" || stylelintStrategy === "backup-overwrite" || stylelintStrategy === "skip" || stylelintStrategy === "overwrite" ? stylelintStrategy : "overwrite",
|
|
3487
|
-
eslintExistingPaths,
|
|
3488
|
-
stylelintExistingPaths
|
|
3489
|
-
});
|
|
3490
|
-
const detailParts = [];
|
|
3491
|
-
if (result.status === "installed") {
|
|
3492
|
-
detailParts.push(
|
|
3493
|
-
`eslint=${result.eslint}, stylelint=${result.stylelint}`
|
|
3494
|
-
);
|
|
3495
|
-
if (result.eslintMergeRequested) {
|
|
3496
|
-
detailParts.push("eslint:AI-merge-pending");
|
|
3497
|
-
}
|
|
3498
|
-
if (result.stylelintMergeRequested) {
|
|
3499
|
-
detailParts.push("stylelint:AI-merge-pending");
|
|
3500
|
-
}
|
|
3501
|
-
if (result.eslintSkipped) detailParts.push("eslint:skipped");
|
|
3502
|
-
if (result.stylelintSkipped) detailParts.push("stylelint:skipped");
|
|
3503
|
-
} else {
|
|
3504
|
-
detailParts.push(result.status);
|
|
3505
|
-
}
|
|
3506
|
-
record({
|
|
3507
|
-
name: "lint",
|
|
3508
|
-
status: "ok",
|
|
3509
|
-
detail: detailParts.join(" / "),
|
|
3510
|
-
changes: deriveLintChanges(result)
|
|
3511
|
-
});
|
|
3512
|
-
} catch (err) {
|
|
3513
|
-
recordFailure("lint", err);
|
|
3514
|
-
}
|
|
3515
|
-
}
|
|
3516
|
-
recordPending("tailwind-config");
|
|
3517
|
-
recordPending("index-css");
|
|
3518
|
-
if (!dryRun) {
|
|
3519
|
-
try {
|
|
3520
|
-
const backupsAfter = await listBackupOriginals(projectRoot);
|
|
3521
|
-
const newlyBackedUp = diffBackupSet(backupsBefore, backupsAfter);
|
|
3522
|
-
if (newlyBackedUp.size > 0) {
|
|
3523
|
-
for (const change of allChanges) {
|
|
3524
|
-
if (change.kind === "created" && newlyBackedUp.has(change.path)) {
|
|
3525
|
-
change.kind = "modified";
|
|
3526
|
-
}
|
|
3527
|
-
}
|
|
3528
|
-
for (const rel2 of newlyBackedUp) {
|
|
3529
|
-
allChanges.push({
|
|
3530
|
-
kind: "backed-up",
|
|
3531
|
-
path: rel2,
|
|
3532
|
-
step: "backup",
|
|
3533
|
-
detail: ".teamix-evo/.backups/<\u540C\u8DEF\u5F84>.<isoTs>.bak"
|
|
3534
|
-
});
|
|
3535
|
-
}
|
|
3536
|
-
}
|
|
3537
|
-
} catch {
|
|
3538
|
-
}
|
|
3539
|
-
}
|
|
3540
|
-
const status = dryRun ? "dry-run" : steps.some((s) => s.status === "fail") ? "partial" : "installed";
|
|
3541
|
-
const out = {
|
|
3542
|
-
status,
|
|
3543
|
-
steps,
|
|
3544
|
-
pendingConflictWork: pending,
|
|
3545
|
-
changes: allChanges,
|
|
3546
|
-
snapshot
|
|
3547
|
-
};
|
|
3548
|
-
if (snapshotError) out.snapshotError = snapshotError;
|
|
3549
|
-
if (firstFailure.value) {
|
|
3550
|
-
out.resumeHint = {
|
|
3551
|
-
failedAt: firstFailure.value.step,
|
|
3552
|
-
completed: steps.filter((s) => s.status === "ok").map((s) => s.name),
|
|
3553
|
-
failed: steps.filter((s) => s.status === "fail").map((s) => s.name),
|
|
3554
|
-
error: firstFailure.value.error,
|
|
3555
|
-
// Every sub-step is idempotent (returns `already-initialized` when its
|
|
3556
|
-
// state file already exists), so a plain re-run resumes from the
|
|
3557
|
-
// failed step. A richer --resume model lands with batch 3 (lock
|
|
3558
|
-
// snapshot + restore).
|
|
3559
|
-
resumeCommand: "teamix-evo init"
|
|
3560
|
-
};
|
|
3561
|
-
}
|
|
3562
|
-
return out;
|
|
3563
|
-
}
|
|
3564
|
-
|
|
3565
|
-
// src/core/project-update.ts
|
|
3566
|
-
import * as path25 from "path";
|
|
3567
|
-
import {
|
|
3568
|
-
loadTokensPackageManifest as loadTokensPackageManifest3,
|
|
3569
|
-
getVariantEntry as getVariantEntry3
|
|
3570
|
-
} from "@teamix-evo/registry";
|
|
3571
|
-
|
|
3572
|
-
// src/core/tokens-update.ts
|
|
3573
|
-
import * as path21 from "path";
|
|
3574
|
-
import * as fs16 from "fs/promises";
|
|
3575
|
-
import {
|
|
3576
|
-
loadTokensPackageManifest as loadTokensPackageManifest2,
|
|
3577
|
-
getVariantEntry as getVariantEntry2
|
|
3578
|
-
} from "@teamix-evo/registry";
|
|
3579
|
-
|
|
3580
|
-
// src/core/upgrade-hints.ts
|
|
3581
|
-
import * as path20 from "path";
|
|
3582
|
-
var TEAMIX_DIR3 = ".teamix-evo";
|
|
3583
|
-
var HINTS_DIR = ".upgrade-hints";
|
|
3584
|
-
function isoToFsSafe2(iso) {
|
|
3585
|
-
return iso.replace(/[:.]/g, "-");
|
|
3586
|
-
}
|
|
3587
|
-
async function writeTokensUpgradeHint(options) {
|
|
3588
|
-
if (options.renames.length === 0) return null;
|
|
3589
|
-
const isoTs = options.isoTs ?? (/* @__PURE__ */ new Date()).toISOString();
|
|
3590
|
-
const fsTs = isoToFsSafe2(isoTs);
|
|
3591
|
-
const filename = `tokens-${fsTs}.json`;
|
|
3592
|
-
const target = path20.join(
|
|
3593
|
-
options.projectRoot,
|
|
3594
|
-
TEAMIX_DIR3,
|
|
3595
|
-
HINTS_DIR,
|
|
3596
|
-
filename
|
|
3597
|
-
);
|
|
3598
|
-
const payload = {
|
|
3599
|
-
schemaVersion: 1,
|
|
3600
|
-
ts: isoTs,
|
|
3601
|
-
package: "tokens",
|
|
3602
|
-
trigger: options.trigger,
|
|
3603
|
-
fromVariant: options.fromVariant,
|
|
3604
|
-
toVariant: options.toVariant,
|
|
3605
|
-
fromVersion: options.fromVersion,
|
|
3606
|
-
toVersion: options.toVersion,
|
|
3607
|
-
renames: options.renames
|
|
3608
|
-
};
|
|
3609
|
-
await writeFileSafe(target, JSON.stringify(payload, null, 2) + "\n");
|
|
3610
|
-
return {
|
|
3611
|
-
path: target,
|
|
3612
|
-
ts: fsTs,
|
|
3613
|
-
renameCount: options.renames.length
|
|
3614
|
-
};
|
|
3615
|
-
}
|
|
3616
|
-
function selectApplicableRenames(renames, fromVersion, toVersion) {
|
|
3617
|
-
return renames.filter(
|
|
3618
|
-
(r) => compareSemver2(r.sinceVersion, fromVersion) > 0 && compareSemver2(r.sinceVersion, toVersion) <= 0
|
|
3619
|
-
).sort((a, b) => compareSemver2(a.sinceVersion, b.sinceVersion));
|
|
3620
|
-
}
|
|
3621
|
-
function compareSemver2(a, b) {
|
|
3622
|
-
const [aMain = "", aRest = ""] = a.split("-", 2);
|
|
3623
|
-
const [bMain = "", bRest = ""] = b.split("-", 2);
|
|
3624
|
-
const aParts = aMain.split(".").map((n) => Number.parseInt(n, 10));
|
|
3625
|
-
const bParts = bMain.split(".").map((n) => Number.parseInt(n, 10));
|
|
3626
|
-
for (let i = 0; i < Math.max(aParts.length, bParts.length); i++) {
|
|
3627
|
-
const ai = aParts[i] ?? 0;
|
|
3628
|
-
const bi = bParts[i] ?? 0;
|
|
3629
|
-
if (ai !== bi) return ai - bi;
|
|
3630
|
-
}
|
|
3631
|
-
if (aRest === "" && bRest !== "") return 1;
|
|
3632
|
-
if (aRest !== "" && bRest === "") return -1;
|
|
3633
|
-
return aRest.localeCompare(bRest, void 0, { numeric: true });
|
|
3634
|
-
}
|
|
3635
|
-
|
|
3636
|
-
// src/core/managed-merge.ts
|
|
3637
|
-
import { hasManagedRegion as hasManagedRegion3, replaceManagedRegion as replaceManagedRegion3 } from "@teamix-evo/registry";
|
|
3638
|
-
function mergeManagedRegions(upstreamContent, consumerContent) {
|
|
3639
|
-
let updated = consumerContent;
|
|
3640
|
-
const re = /<!-- teamix-evo:managed:start id="([^"]+)" -->([\s\S]*?)<!-- teamix-evo:managed:end(?: id="\1")? -->/g;
|
|
3641
|
-
let match;
|
|
3642
|
-
while ((match = re.exec(upstreamContent)) !== null) {
|
|
3643
|
-
const id = match[1];
|
|
3644
|
-
const body = match[2].replace(/^\n/, "").replace(/\n$/, "");
|
|
3645
|
-
if (!hasManagedRegion3(updated, id)) {
|
|
3646
|
-
throw new Error(
|
|
3647
|
-
`Managed region "${id}" missing from consumer file \u2014 refusing to silently rewrite (ADR 0003).`
|
|
3648
|
-
);
|
|
3649
|
-
}
|
|
3650
|
-
updated = replaceManagedRegion3(updated, id, body);
|
|
3651
|
-
}
|
|
3652
|
-
return updated;
|
|
3653
|
-
}
|
|
3654
|
-
|
|
3655
|
-
// src/core/tokens-update.ts
|
|
3656
|
-
var DEFAULT_TOKENS_PACKAGE2 = "@teamix-evo/tokens";
|
|
3657
|
-
var CONSUMER_BASENAME_BY_UPSTREAM = {
|
|
3658
|
-
"theme.css": "tokens.theme.css",
|
|
3659
|
-
"overrides.css": "tokens.overrides.css"
|
|
3660
|
-
};
|
|
3661
|
-
async function runTokensUpdate(options) {
|
|
3662
|
-
const { projectRoot } = options;
|
|
3663
|
-
const packageName = options.packageName ?? DEFAULT_TOKENS_PACKAGE2;
|
|
3664
|
-
const config = await readProjectConfig(projectRoot);
|
|
3665
|
-
if (!config?.packages?.tokens) {
|
|
3666
|
-
return { status: "not-initialized" };
|
|
3667
|
-
}
|
|
3668
|
-
const currentVariant = config.packages.tokens.variant;
|
|
3669
|
-
const currentVersion = config.packages.tokens.version;
|
|
3670
|
-
const packageRoot = options.packageRoot ?? resolveTokensPackageRoot(packageName);
|
|
3671
|
-
const catalog = await loadTokensPackageManifest2(packageRoot);
|
|
3672
|
-
const variantEntry = getVariantEntry2(catalog, currentVariant);
|
|
3673
|
-
if (!variantEntry) {
|
|
3674
|
-
throw new Error(
|
|
3675
|
-
`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.`
|
|
3676
|
-
);
|
|
3677
|
-
}
|
|
3678
|
-
const upstreamByBasename = /* @__PURE__ */ new Map();
|
|
3679
|
-
for (const fileRel of variantEntry.files) {
|
|
3680
|
-
upstreamByBasename.set(
|
|
3681
|
-
path21.basename(fileRel),
|
|
3682
|
-
path21.join(packageRoot, fileRel)
|
|
3683
|
-
);
|
|
3684
|
-
}
|
|
3685
|
-
const prior = await readInstalledManifest(projectRoot) ?? {
|
|
3686
|
-
schemaVersion: 1,
|
|
3687
|
-
installed: []
|
|
3688
|
-
};
|
|
3689
|
-
const installedIdx = prior.installed.findIndex(
|
|
3690
|
-
(p) => p.package === packageName
|
|
3691
|
-
);
|
|
3692
|
-
const priorResources = installedIdx >= 0 ? prior.installed[installedIdx].resources : [];
|
|
3693
|
-
const rewritten = [];
|
|
3694
|
-
const managedReplaced = [];
|
|
3695
|
-
const preserved = [];
|
|
3696
|
-
const frozenDrift = [];
|
|
3697
|
-
const refreshedResources = [];
|
|
3698
|
-
for (const resource of priorResources) {
|
|
3699
|
-
const consumerAbs = path21.isAbsolute(resource.target) ? resource.target : path21.join(projectRoot, resource.target);
|
|
3700
|
-
const consumerBasename = path21.basename(resource.target);
|
|
3701
|
-
const upstreamBasename = lookupUpstreamBasename(consumerBasename);
|
|
3702
|
-
const upstreamAbs = upstreamBasename ? upstreamByBasename.get(upstreamBasename) : void 0;
|
|
3703
|
-
if (resource.strategy === "regenerable") {
|
|
3704
|
-
if (!upstreamAbs) {
|
|
3705
|
-
refreshedResources.push(resource);
|
|
3706
|
-
continue;
|
|
3707
|
-
}
|
|
3708
|
-
const content = await fs16.readFile(upstreamAbs, "utf-8");
|
|
3709
|
-
await writeFileSafe(consumerAbs, content);
|
|
3710
|
-
rewritten.push(resource.target);
|
|
3711
|
-
refreshedResources.push({
|
|
3712
|
-
...resource,
|
|
3713
|
-
hash: computeHash(content)
|
|
3714
|
-
});
|
|
3715
|
-
continue;
|
|
3716
|
-
}
|
|
3717
|
-
if (resource.strategy === "managed") {
|
|
3718
|
-
if (!upstreamAbs || !await fileExists(consumerAbs)) {
|
|
3719
|
-
refreshedResources.push(resource);
|
|
3720
|
-
continue;
|
|
3721
|
-
}
|
|
3722
|
-
const upstreamContent = await fs16.readFile(upstreamAbs, "utf-8");
|
|
3723
|
-
const consumerContent = await fs16.readFile(consumerAbs, "utf-8");
|
|
3724
|
-
const merged = mergeManagedRegions(upstreamContent, consumerContent);
|
|
3725
|
-
if (merged !== consumerContent) {
|
|
3726
|
-
await writeFileSafe(consumerAbs, merged);
|
|
3727
|
-
managedReplaced.push(resource.target);
|
|
3728
|
-
}
|
|
3729
|
-
refreshedResources.push({
|
|
3730
|
-
...resource,
|
|
3731
|
-
hash: computeHash(merged)
|
|
3732
|
-
});
|
|
3733
|
-
continue;
|
|
3734
|
-
}
|
|
3735
|
-
if (await fileExists(consumerAbs)) preserved.push(resource.target);
|
|
3736
|
-
if (upstreamAbs) {
|
|
3737
|
-
const upstreamContent = await fs16.readFile(upstreamAbs, "utf-8");
|
|
3738
|
-
const upstreamHash = computeHash(upstreamContent);
|
|
3739
|
-
if (resource.hash && upstreamHash !== resource.hash) {
|
|
3740
|
-
frozenDrift.push({
|
|
3741
|
-
target: resource.target,
|
|
3742
|
-
reason: "upstream-changed"
|
|
3743
|
-
});
|
|
3744
|
-
}
|
|
3745
|
-
}
|
|
3746
|
-
refreshedResources.push(resource);
|
|
3747
|
-
}
|
|
3748
|
-
if (variantEntry.version === currentVersion) {
|
|
3749
|
-
if (installedIdx >= 0) {
|
|
3750
|
-
prior.installed[installedIdx] = {
|
|
3751
|
-
...prior.installed[installedIdx],
|
|
3752
|
-
resources: refreshedResources
|
|
3753
|
-
};
|
|
3754
|
-
await writeInstalledManifest(projectRoot, prior);
|
|
3755
|
-
}
|
|
3756
|
-
return {
|
|
3757
|
-
status: "up-to-date",
|
|
3758
|
-
packageName,
|
|
3759
|
-
variant: currentVariant,
|
|
3760
|
-
version: currentVersion,
|
|
3761
|
-
frozenDrift
|
|
3762
|
-
};
|
|
3763
|
-
}
|
|
3764
|
-
const lock = {
|
|
3765
|
-
schemaVersion: 1,
|
|
3766
|
-
variant: {
|
|
3767
|
-
name: variantEntry.name,
|
|
3768
|
-
displayName: variantEntry.displayName,
|
|
3769
|
-
version: variantEntry.version,
|
|
3770
|
-
from: packageName
|
|
3771
|
-
},
|
|
3772
|
-
packageVersion: catalog.version,
|
|
3773
|
-
linked: variantEntry.linked,
|
|
3774
|
-
installedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
3775
|
-
};
|
|
3776
|
-
await writeFileSafe(
|
|
3777
|
-
path21.join(projectRoot, ".teamix-evo", "tokens-lock.json"),
|
|
3778
|
-
JSON.stringify(lock, null, 2) + "\n"
|
|
3779
|
-
);
|
|
3780
|
-
config.packages.tokens.version = variantEntry.version;
|
|
3781
|
-
await writeProjectConfig(projectRoot, config);
|
|
3782
|
-
if (installedIdx >= 0) {
|
|
3783
|
-
prior.installed[installedIdx] = {
|
|
3784
|
-
...prior.installed[installedIdx],
|
|
3785
|
-
version: variantEntry.version,
|
|
3786
|
-
installedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
3787
|
-
resources: refreshedResources
|
|
3788
|
-
};
|
|
3789
|
-
await writeInstalledManifest(projectRoot, prior);
|
|
3790
|
-
}
|
|
3791
|
-
const renames = selectApplicableRenames(
|
|
3792
|
-
variantEntry.renames ?? [],
|
|
3793
|
-
currentVersion,
|
|
3794
|
-
variantEntry.version
|
|
3795
|
-
);
|
|
3796
|
-
let hintPath;
|
|
3797
|
-
if (renames.length > 0) {
|
|
3798
|
-
const hint = await writeTokensUpgradeHint({
|
|
3799
|
-
projectRoot,
|
|
3800
|
-
trigger: "update",
|
|
3801
|
-
fromVariant: currentVariant,
|
|
3802
|
-
toVariant: currentVariant,
|
|
3803
|
-
fromVersion: currentVersion,
|
|
3804
|
-
toVersion: variantEntry.version,
|
|
3805
|
-
renames
|
|
3806
|
-
});
|
|
3807
|
-
if (hint) hintPath = hint.path;
|
|
3808
|
-
}
|
|
3809
|
-
return {
|
|
3810
|
-
status: "updated",
|
|
3811
|
-
packageName,
|
|
3812
|
-
variant: currentVariant,
|
|
3813
|
-
from: currentVersion,
|
|
3814
|
-
to: variantEntry.version,
|
|
3815
|
-
rewritten,
|
|
3816
|
-
managedReplaced,
|
|
3817
|
-
preserved,
|
|
3818
|
-
frozenDrift,
|
|
3819
|
-
renames,
|
|
3820
|
-
...hintPath ? { hintPath } : {}
|
|
3821
|
-
};
|
|
3822
|
-
}
|
|
3823
|
-
function lookupUpstreamBasename(consumerBasename) {
|
|
3824
|
-
for (const [upstream, consumer] of Object.entries(
|
|
3825
|
-
CONSUMER_BASENAME_BY_UPSTREAM
|
|
3826
|
-
)) {
|
|
3827
|
-
if (consumer === consumerBasename) return upstream;
|
|
3828
|
-
}
|
|
3829
|
-
return consumerBasename;
|
|
3830
|
-
}
|
|
3831
|
-
|
|
3832
|
-
// src/core/ui-upgrade-detector.ts
|
|
3833
|
-
import * as fs17 from "fs/promises";
|
|
3834
|
-
import * as path22 from "path";
|
|
3835
|
-
var PACKAGE_NAME = {
|
|
3836
|
-
ui: "@teamix-evo/ui",
|
|
3837
|
-
"biz-ui": "@teamix-evo/biz-ui"
|
|
3838
|
-
};
|
|
3839
|
-
var ALIAS_KEY = {
|
|
3840
|
-
ui: "components",
|
|
3841
|
-
"biz-ui": "business"
|
|
3842
|
-
};
|
|
3843
|
-
var COMPONENT_FILE_RE = /\.(tsx|ts)$/;
|
|
3844
|
-
var SKIP_FILENAMES = /* @__PURE__ */ new Set(["index.ts", "index.tsx"]);
|
|
3845
|
-
async function detectComponentLineage(options) {
|
|
3846
|
-
const { projectRoot, category } = options;
|
|
3847
|
-
const config = options.config ?? await readProjectConfig(projectRoot);
|
|
3848
|
-
const installed = options.installed ?? await readInstalledManifest(projectRoot);
|
|
3849
|
-
const installDir = resolveInstallDir(category, config);
|
|
3850
|
-
const installDirAbs = path22.join(projectRoot, installDir);
|
|
3851
|
-
const installDirExists = await directoryExists(installDirAbs);
|
|
3852
|
-
const hasComponentsJson = await fileExists(
|
|
3853
|
-
path22.join(projectRoot, "components.json")
|
|
3854
|
-
);
|
|
3855
|
-
const installedPkg = findInstalledPackage(installed, PACKAGE_NAME[category]);
|
|
3856
|
-
const registeredIds = installedPkg ? extractIds(installedPkg).sort() : [];
|
|
3857
|
-
const onDiskIds = installDirExists ? await listComponentIds(installDirAbs) : [];
|
|
3858
|
-
const registeredSet = new Set(registeredIds);
|
|
3859
|
-
const unregisteredIds = onDiskIds.filter((id) => !registeredSet.has(id)).sort();
|
|
3860
|
-
const lineage = classifyLineage({
|
|
3861
|
-
hasInstalled: installedPkg !== null,
|
|
3862
|
-
hasComponentsJson,
|
|
3863
|
-
onDiskIds,
|
|
3864
|
-
unregisteredIds
|
|
3865
|
-
});
|
|
3866
|
-
return {
|
|
3867
|
-
category,
|
|
3868
|
-
lineage,
|
|
3869
|
-
installDir,
|
|
3870
|
-
installDirExists,
|
|
3871
|
-
hasComponentsJson,
|
|
3872
|
-
registeredIds,
|
|
3873
|
-
unregisteredIds,
|
|
3874
|
-
installedVersion: installedPkg?.version ?? null,
|
|
3875
|
-
installedVariant: installedPkg?.variant ?? null
|
|
3876
|
-
};
|
|
3877
|
-
}
|
|
3878
|
-
function resolveInstallDir(category, config) {
|
|
3879
|
-
const aliasMap = config?.packages?.ui?.aliases ?? config?.packages?.["biz-ui"]?.aliases ?? DEFAULT_UI_ALIASES;
|
|
3880
|
-
const key = ALIAS_KEY[category];
|
|
3881
|
-
return aliasMap[key] ?? DEFAULT_UI_ALIASES[key];
|
|
3882
|
-
}
|
|
3883
|
-
function extractIds(pkg) {
|
|
3884
|
-
const ids = /* @__PURE__ */ new Set();
|
|
3885
|
-
for (const r of pkg.resources) {
|
|
3886
|
-
const colon = r.id.indexOf(":");
|
|
3887
|
-
ids.add(colon >= 0 ? r.id.slice(0, colon) : r.id);
|
|
3888
|
-
}
|
|
3889
|
-
return [...ids];
|
|
3890
|
-
}
|
|
3891
|
-
async function directoryExists(p) {
|
|
3892
|
-
try {
|
|
3893
|
-
const stat5 = await fs17.stat(p);
|
|
3894
|
-
return stat5.isDirectory();
|
|
3895
|
-
} catch {
|
|
3896
|
-
return false;
|
|
3897
|
-
}
|
|
3898
|
-
}
|
|
3899
|
-
async function listComponentIds(installDirAbs) {
|
|
3900
|
-
const entries = await fs17.readdir(installDirAbs, { withFileTypes: true });
|
|
3901
|
-
const ids = [];
|
|
3902
|
-
for (const e of entries) {
|
|
3903
|
-
if (!e.isFile()) continue;
|
|
3904
|
-
if (SKIP_FILENAMES.has(e.name)) continue;
|
|
3905
|
-
if (!COMPONENT_FILE_RE.test(e.name)) continue;
|
|
3906
|
-
ids.push(e.name.replace(COMPONENT_FILE_RE, ""));
|
|
3907
|
-
}
|
|
3908
|
-
return ids.sort();
|
|
3909
|
-
}
|
|
3910
|
-
function classifyLineage(args) {
|
|
3911
|
-
const { hasInstalled, hasComponentsJson, onDiskIds, unregisteredIds } = args;
|
|
3912
|
-
if (hasInstalled) {
|
|
3913
|
-
return unregisteredIds.length === 0 ? "teamix-evo" : "mixed";
|
|
3914
|
-
}
|
|
3915
|
-
if (onDiskIds.length === 0) return "absent";
|
|
3916
|
-
return hasComponentsJson ? "shadcn-native" : "custom-only";
|
|
3917
|
-
}
|
|
3918
|
-
|
|
3919
|
-
// src/core/ui-upgrade.ts
|
|
3920
|
-
import * as path24 from "path";
|
|
3921
|
-
import { createRequire as createRequire5 } from "module";
|
|
3922
|
-
import {
|
|
3923
|
-
loadUiPackageManifest as loadUiPackageManifest3,
|
|
3924
|
-
loadVariantUiPackageManifest as loadVariantUiPackageManifest2
|
|
3925
|
-
} from "@teamix-evo/registry";
|
|
3926
|
-
|
|
3927
|
-
// src/core/ui-upgrade-staging.ts
|
|
3928
|
-
import * as path23 from "path";
|
|
3929
|
-
var TEAMIX_DIR4 = ".teamix-evo";
|
|
3930
|
-
var STAGING_DIR = ".upgrade-staging";
|
|
3931
|
-
var PACKAGE_NAME2 = {
|
|
3932
|
-
ui: "@teamix-evo/ui",
|
|
3933
|
-
"biz-ui": "@teamix-evo/biz-ui"
|
|
3934
|
-
};
|
|
3935
|
-
function isoToFsSafe3(iso) {
|
|
3936
|
-
return iso.replace(/[:.]/g, "-");
|
|
3937
|
-
}
|
|
3938
|
-
async function buildUiUpgradeStaging(options) {
|
|
3939
|
-
const { lineageReport, category } = options;
|
|
3940
|
-
if (lineageReport.lineage !== "teamix-evo" && lineageReport.lineage !== "mixed") {
|
|
3941
|
-
return null;
|
|
3942
|
-
}
|
|
3943
|
-
const installed = options.installed ?? await readInstalledManifest(options.projectRoot);
|
|
3944
|
-
const installedPkg = findInstalledPackage(installed, PACKAGE_NAME2[category]);
|
|
3945
|
-
if (!installedPkg) return null;
|
|
3946
|
-
const isoTs = options.isoTs ?? (/* @__PURE__ */ new Date()).toISOString();
|
|
3947
|
-
const fsTs = isoToFsSafe3(isoTs);
|
|
3948
|
-
const stagingDir = path23.join(
|
|
3949
|
-
options.projectRoot,
|
|
3950
|
-
TEAMIX_DIR4,
|
|
3951
|
-
STAGING_DIR,
|
|
3952
|
-
`${category}-${fsTs}`
|
|
3953
|
-
);
|
|
3954
|
-
const entryMap = new Map(
|
|
3955
|
-
options.manifest.entries.map((e) => [e.id, e])
|
|
3956
|
-
);
|
|
3957
|
-
const resByEntryId = collectResourcesByEntry(installedPkg.resources);
|
|
3958
|
-
const onlyIds = options.onlyIds && options.onlyIds.length > 0 ? new Set(options.onlyIds) : null;
|
|
3959
|
-
const entries = [];
|
|
3960
|
-
for (const id of lineageReport.registeredIds) {
|
|
3961
|
-
if (onlyIds && !onlyIds.has(id)) continue;
|
|
3962
|
-
const built = await processRegistered({
|
|
3963
|
-
id,
|
|
3964
|
-
entry: entryMap.get(id),
|
|
3965
|
-
resource: resByEntryId.get(id),
|
|
3966
|
-
packageRoot: options.packageRoot,
|
|
3967
|
-
entryPackageRoot: options.entryPackageRoot,
|
|
3968
|
-
aliases: options.aliases,
|
|
3969
|
-
stagingDir,
|
|
3970
|
-
projectRoot: options.projectRoot,
|
|
3971
|
-
category,
|
|
3972
|
-
sourceVersion: options.manifest.version
|
|
3973
|
-
});
|
|
3974
|
-
if (built) entries.push(built);
|
|
3975
|
-
}
|
|
3976
|
-
for (const id of lineageReport.unregisteredIds) {
|
|
3977
|
-
if (onlyIds && !onlyIds.has(id)) continue;
|
|
3978
|
-
const built = await processForeign({
|
|
3979
|
-
id,
|
|
3980
|
-
installDirAbs: path23.join(options.projectRoot, lineageReport.installDir),
|
|
3981
|
-
stagingDir,
|
|
3982
|
-
projectRoot: options.projectRoot,
|
|
3983
|
-
category
|
|
3984
|
-
});
|
|
3985
|
-
if (built) entries.push(built);
|
|
3986
|
-
}
|
|
3987
|
-
if (entries.length === 0) return null;
|
|
3988
|
-
const byRisk = aggregateByRisk(entries);
|
|
3989
|
-
const manifestOut = {
|
|
3990
|
-
schemaVersion: 1,
|
|
3991
|
-
ts: isoTs,
|
|
3992
|
-
package: category,
|
|
3993
|
-
trigger: options.trigger,
|
|
3994
|
-
variant: lineageReport.installedVariant ?? "_flat",
|
|
3995
|
-
fromVersion: lineageReport.installedVersion ?? "",
|
|
3996
|
-
toVersion: options.manifest.version,
|
|
3997
|
-
lineage: lineageReport.lineage,
|
|
3998
|
-
summary: { total: entries.length, byRisk },
|
|
3999
|
-
entries
|
|
4000
|
-
};
|
|
4001
|
-
await ensureDir(stagingDir);
|
|
4002
|
-
await writeFileSafe(
|
|
4003
|
-
path23.join(stagingDir, "meta.json"),
|
|
4004
|
-
JSON.stringify(manifestOut, null, 2) + "\n"
|
|
4005
|
-
);
|
|
4006
|
-
return { stagingDir, manifest: manifestOut };
|
|
4007
|
-
}
|
|
4008
|
-
async function processRegistered(args) {
|
|
4009
|
-
const { id, entry, resource, stagingDir, projectRoot, category } = args;
|
|
4010
|
-
if (!resource) return null;
|
|
4011
|
-
const currentSource = await readFileOrNull(resource.target);
|
|
4012
|
-
if (currentSource === null) {
|
|
4013
|
-
return buildBreakingEntry({
|
|
4014
|
-
id,
|
|
4015
|
-
category,
|
|
4016
|
-
resource,
|
|
4017
|
-
projectRoot,
|
|
4018
|
-
stagingDir,
|
|
4019
|
-
currentSource: "",
|
|
4020
|
-
hint: "installed file missing on disk"
|
|
4021
|
-
});
|
|
4022
|
-
}
|
|
4023
|
-
if (!entry) {
|
|
4024
|
-
return buildBreakingEntry({
|
|
4025
|
-
id,
|
|
4026
|
-
category,
|
|
4027
|
-
resource,
|
|
4028
|
-
projectRoot,
|
|
4029
|
-
stagingDir,
|
|
4030
|
-
currentSource,
|
|
4031
|
-
hint: "entry removed in upstream package"
|
|
4032
|
-
});
|
|
4033
|
-
}
|
|
4034
|
-
const file = entry.files[0];
|
|
4035
|
-
if (!file) return null;
|
|
4036
|
-
const rootForEntry = args.entryPackageRoot?.get(id) ?? args.packageRoot;
|
|
4037
|
-
const sourceAbs = path23.resolve(rootForEntry, file.source);
|
|
4038
|
-
const raw = await readFileOrNull(sourceAbs);
|
|
4039
|
-
if (raw === null) {
|
|
4040
|
-
return null;
|
|
4041
|
-
}
|
|
4042
|
-
const incomingTransformed = rewriteImports(raw, args.aliases);
|
|
4043
|
-
const incomingHash = computeHash(incomingTransformed);
|
|
4044
|
-
const currentExt = path23.extname(resource.target) || ".tsx";
|
|
4045
|
-
const incomingExt = path23.extname(file.targetName) || currentExt;
|
|
4046
|
-
const currentRel = `${id}/current${currentExt}`;
|
|
4047
|
-
const incomingRel = `${id}/incoming${incomingExt}`;
|
|
4048
|
-
await writeFileSafe(path23.join(stagingDir, currentRel), currentSource);
|
|
4049
|
-
await writeFileSafe(path23.join(stagingDir, incomingRel), incomingTransformed);
|
|
4050
|
-
const diff = classifyRisk({
|
|
4051
|
-
currentHash: resource.hash,
|
|
4052
|
-
incomingHash,
|
|
4053
|
-
currentSource,
|
|
4054
|
-
incomingSource: incomingTransformed,
|
|
4055
|
-
multiFile: entry.files.length > 1
|
|
4056
|
-
});
|
|
4057
|
-
const promotion = derivePromotion({
|
|
4058
|
-
currentSource,
|
|
4059
|
-
incomingSource: incomingTransformed,
|
|
4060
|
-
targetName: entry.files[0]?.targetName ?? `${id}.tsx`
|
|
4061
|
-
});
|
|
4062
|
-
return {
|
|
4063
|
-
id,
|
|
4064
|
-
category,
|
|
4065
|
-
current: {
|
|
4066
|
-
target: path23.relative(projectRoot, resource.target),
|
|
4067
|
-
hash: resource.hash,
|
|
4068
|
-
sourceLineage: "teamix-evo"
|
|
4069
|
-
},
|
|
4070
|
-
incoming: {
|
|
4071
|
-
sourceVersion: args.sourceVersion,
|
|
4072
|
-
hash: incomingHash,
|
|
4073
|
-
relPath: incomingRel
|
|
4074
|
-
},
|
|
4075
|
-
diff,
|
|
4076
|
-
promotion
|
|
4077
|
-
};
|
|
4078
|
-
}
|
|
4079
|
-
async function processForeign(args) {
|
|
4080
|
-
const { id, installDirAbs, stagingDir, projectRoot, category } = args;
|
|
4081
|
-
const tsx = path23.join(installDirAbs, `${id}.tsx`);
|
|
4082
|
-
const ts = path23.join(installDirAbs, `${id}.ts`);
|
|
4083
|
-
const target = await fileExists(tsx) ? tsx : await fileExists(ts) ? ts : null;
|
|
4084
|
-
if (!target) return null;
|
|
4085
|
-
const raw = await readFileOrNull(target);
|
|
4086
|
-
if (raw === null) return null;
|
|
4087
|
-
const ext = path23.extname(target);
|
|
4088
|
-
const currentRel = `${id}/current${ext}`;
|
|
4089
|
-
await writeFileSafe(path23.join(stagingDir, currentRel), raw);
|
|
4090
|
-
return {
|
|
4091
|
-
id,
|
|
4092
|
-
category,
|
|
4093
|
-
current: {
|
|
4094
|
-
target: path23.relative(projectRoot, target),
|
|
4095
|
-
hash: computeHash(raw),
|
|
4096
|
-
sourceLineage: "custom"
|
|
4097
|
-
},
|
|
4098
|
-
diff: {
|
|
4099
|
-
riskLevel: "foreign",
|
|
4100
|
-
hints: [
|
|
4101
|
-
"component is on disk but not registered in .teamix-evo/manifest.json",
|
|
4102
|
-
"AI should propose: (a) ignore, (b) re-register via teamix-evo ui add, or (c) remove"
|
|
4103
|
-
],
|
|
4104
|
-
filesChangedCount: 0
|
|
4105
|
-
}
|
|
4106
|
-
};
|
|
4107
|
-
}
|
|
4108
|
-
async function buildBreakingEntry(args) {
|
|
4109
|
-
const ext = path23.extname(args.resource.target) || ".tsx";
|
|
4110
|
-
const currentRel = `${args.id}/current${ext}`;
|
|
4111
|
-
await writeFileSafe(
|
|
4112
|
-
path23.join(args.stagingDir, currentRel),
|
|
4113
|
-
args.currentSource
|
|
4114
|
-
);
|
|
4115
|
-
return {
|
|
4116
|
-
id: args.id,
|
|
4117
|
-
category: args.category,
|
|
4118
|
-
current: {
|
|
4119
|
-
target: path23.relative(args.projectRoot, args.resource.target),
|
|
4120
|
-
hash: args.resource.hash,
|
|
4121
|
-
sourceLineage: "teamix-evo"
|
|
4122
|
-
},
|
|
4123
|
-
diff: {
|
|
4124
|
-
riskLevel: "breaking",
|
|
4125
|
-
hints: [args.hint],
|
|
4126
|
-
filesChangedCount: 0
|
|
4127
|
-
}
|
|
4128
|
-
};
|
|
4129
|
-
}
|
|
4130
|
-
function classifyRisk(args) {
|
|
4131
|
-
if (args.currentHash === args.incomingHash) {
|
|
4132
|
-
return { riskLevel: "unchanged", hints: [], filesChangedCount: 0 };
|
|
4133
|
-
}
|
|
4134
|
-
const curExports = extractExportNames(args.currentSource);
|
|
4135
|
-
const newExports = extractExportNames(args.incomingSource);
|
|
4136
|
-
const removedExports = setDiff(curExports, newExports);
|
|
4137
|
-
const addedExports = setDiff(newExports, curExports);
|
|
4138
|
-
const curVariants = extractCvaVariantValues(args.currentSource);
|
|
4139
|
-
const newVariants = extractCvaVariantValues(args.incomingSource);
|
|
4140
|
-
const removedVariants = setDiff(curVariants, newVariants);
|
|
4141
|
-
const addedVariants = setDiff(newVariants, curVariants);
|
|
4142
|
-
const hints = [];
|
|
4143
|
-
for (const e of removedExports) hints.push(`removed export: ${e}`);
|
|
4144
|
-
for (const e of addedExports) hints.push(`new export: ${e}`);
|
|
4145
|
-
for (const v of removedVariants) hints.push(`removed cva variant: ${v}`);
|
|
4146
|
-
for (const v of addedVariants) hints.push(`new cva variant: ${v}`);
|
|
4147
|
-
if (args.multiFile) hints.push("multi-file entry; only first file staged");
|
|
4148
|
-
let riskLevel;
|
|
4149
|
-
if (removedExports.length > 0 || removedVariants.length > 0) {
|
|
4150
|
-
riskLevel = "risky";
|
|
4151
|
-
} else if (addedExports.length > 0 || addedVariants.length > 0 || args.multiFile) {
|
|
4152
|
-
riskLevel = "upgradable-medium";
|
|
4153
|
-
} else {
|
|
4154
|
-
riskLevel = "upgradable-low";
|
|
4155
|
-
}
|
|
4156
|
-
return { riskLevel, hints, filesChangedCount: 1 };
|
|
4157
|
-
}
|
|
4158
|
-
function extractExportNames(src) {
|
|
4159
|
-
const names = /* @__PURE__ */ new Set();
|
|
4160
|
-
const re = /^\s*export\s+(?:default\s+)?(?:async\s+)?(?:const|let|var|function|class|interface|type|enum)\s+(\w+)/gm;
|
|
4161
|
-
let m;
|
|
4162
|
-
while ((m = re.exec(src)) !== null) {
|
|
4163
|
-
if (m[1]) names.add(m[1]);
|
|
4164
|
-
}
|
|
4165
|
-
for (const dm of src.matchAll(/^\s*export\s+default\s+(\w+)\s*;/gm)) {
|
|
4166
|
-
if (dm[1]) names.add(dm[1]);
|
|
4167
|
-
}
|
|
4168
|
-
return [...names];
|
|
4169
|
-
}
|
|
4170
|
-
function extractCvaVariantValues(src) {
|
|
4171
|
-
const block = extractVariantsBlock(src);
|
|
4172
|
-
if (block === null) return [];
|
|
4173
|
-
const names = /* @__PURE__ */ new Set();
|
|
4174
|
-
for (const groupBody of extractGroupBodies(block)) {
|
|
4175
|
-
for (const km of groupBody.matchAll(/^\s*(?:['"]?)(\w+)(?:['"]?)\s*:/gm)) {
|
|
4176
|
-
if (km[1]) names.add(km[1]);
|
|
4177
|
-
}
|
|
4178
|
-
}
|
|
4179
|
-
return [...names];
|
|
4180
|
-
}
|
|
4181
|
-
function extractVariantsBlock(src) {
|
|
4182
|
-
const idx = src.search(/\bvariants\s*:\s*\{/);
|
|
4183
|
-
if (idx < 0) return null;
|
|
4184
|
-
const open = src.indexOf("{", idx);
|
|
4185
|
-
if (open < 0) return null;
|
|
4186
|
-
let depth = 0;
|
|
4187
|
-
for (let i = open; i < src.length; i++) {
|
|
4188
|
-
const c = src[i];
|
|
4189
|
-
if (c === "{") depth++;
|
|
4190
|
-
else if (c === "}") {
|
|
4191
|
-
depth--;
|
|
4192
|
-
if (depth === 0) return src.slice(open + 1, i);
|
|
4193
|
-
}
|
|
4194
|
-
}
|
|
4195
|
-
return null;
|
|
4196
|
-
}
|
|
4197
|
-
function* extractGroupBodies(block) {
|
|
4198
|
-
const re = /(\w+)\s*:\s*\{/g;
|
|
4199
|
-
let m;
|
|
4200
|
-
while ((m = re.exec(block)) !== null) {
|
|
4201
|
-
const open = block.indexOf("{", m.index);
|
|
4202
|
-
if (open < 0) continue;
|
|
4203
|
-
let depth = 0;
|
|
4204
|
-
for (let i = open; i < block.length; i++) {
|
|
4205
|
-
const c = block[i];
|
|
4206
|
-
if (c === "{") depth++;
|
|
4207
|
-
else if (c === "}") {
|
|
4208
|
-
depth--;
|
|
4209
|
-
if (depth === 0) {
|
|
4210
|
-
yield block.slice(open + 1, i);
|
|
4211
|
-
re.lastIndex = i + 1;
|
|
4212
|
-
break;
|
|
4213
|
-
}
|
|
4214
|
-
}
|
|
4215
|
-
}
|
|
4216
|
-
}
|
|
4217
|
-
}
|
|
4218
|
-
function setDiff(a, b) {
|
|
4219
|
-
const bset = new Set(b);
|
|
4220
|
-
return a.filter((x) => !bset.has(x)).sort();
|
|
4221
|
-
}
|
|
4222
|
-
function collectResourcesByEntry(resources) {
|
|
4223
|
-
const out = /* @__PURE__ */ new Map();
|
|
4224
|
-
for (const r of resources) {
|
|
4225
|
-
const colon = r.id.indexOf(":");
|
|
4226
|
-
const eid = colon >= 0 ? r.id.slice(0, colon) : r.id;
|
|
4227
|
-
if (!out.has(eid)) out.set(eid, r);
|
|
4228
|
-
}
|
|
4229
|
-
return out;
|
|
4230
|
-
}
|
|
4231
|
-
function aggregateByRisk(entries) {
|
|
4232
|
-
const out = {};
|
|
4233
|
-
for (const e of entries) {
|
|
4234
|
-
const k = e.diff.riskLevel;
|
|
4235
|
-
out[k] = (out[k] ?? 0) + 1;
|
|
4236
|
-
}
|
|
4237
|
-
return out;
|
|
4238
|
-
}
|
|
4239
|
-
function derivePromotion(args) {
|
|
4240
|
-
const fileType = classifyPromoteFileType(args.targetName, args.currentSource);
|
|
4241
|
-
const featureVector = buildFeatureVector(
|
|
4242
|
-
args.currentSource,
|
|
4243
|
-
args.incomingSource
|
|
4244
|
-
);
|
|
4245
|
-
const { recommendedModes, confidence, reasons } = scorePromotionModes(
|
|
4246
|
-
fileType,
|
|
4247
|
-
featureVector
|
|
4248
|
-
);
|
|
4249
|
-
return { fileType, featureVector, recommendedModes, confidence, reasons };
|
|
4250
|
-
}
|
|
4251
|
-
function classifyPromoteFileType(targetName, src) {
|
|
4252
|
-
if (targetName.endsWith(".d.ts")) return "type";
|
|
4253
|
-
if (/^use-[a-z0-9-]+\.tsx?$/i.test(targetName)) return "hook";
|
|
4254
|
-
const hasJsx = /<[A-Za-z][^>]*?>/.test(src);
|
|
4255
|
-
const hasReactImport = /from ['"]react['"]/.test(src);
|
|
4256
|
-
const hasProvider = /\.Provider\b/.test(src) || /createContext\s*[<(]/.test(src);
|
|
4257
|
-
if (hasProvider && (hasJsx || hasReactImport)) return "provider";
|
|
4258
|
-
if (hasJsx || /forwardRef\s*[<(]/.test(src)) return "component";
|
|
4259
|
-
if (!hasJsx && !hasReactImport) return "util";
|
|
4260
|
-
return "component";
|
|
4261
|
-
}
|
|
4262
|
-
function buildFeatureVector(current, incoming) {
|
|
4263
|
-
const curExports = extractExportNames(current);
|
|
4264
|
-
const newExports = extractExportNames(incoming);
|
|
4265
|
-
const apiAdded = setDiff(curExports, newExports);
|
|
4266
|
-
const apiRemoved = setDiff(newExports, curExports);
|
|
4267
|
-
const curVariants = extractCvaVariantValues(current);
|
|
4268
|
-
const newVariants = extractCvaVariantValues(incoming);
|
|
4269
|
-
const cvaAdded = setDiff(curVariants, newVariants);
|
|
4270
|
-
const cvaModified = [];
|
|
4271
|
-
const sharedVariants = curVariants.filter((v) => newVariants.includes(v));
|
|
4272
|
-
for (const v of sharedVariants) {
|
|
4273
|
-
if (extractVariantBody(current, v) !== extractVariantBody(incoming, v)) {
|
|
4274
|
-
cvaModified.push(v);
|
|
4275
|
-
}
|
|
4276
|
-
}
|
|
4277
|
-
const curClass = extractClassNameLiterals(current);
|
|
4278
|
-
const newClass = extractClassNameLiterals(incoming);
|
|
4279
|
-
const classNameDiff = curClass !== newClass;
|
|
4280
|
-
const curTokens = extractTokenRefs(current);
|
|
4281
|
-
const newTokens = extractTokenRefs(incoming);
|
|
4282
|
-
const tokenUsageDiff = curTokens.size !== newTokens.size || [...curTokens].some((t) => !newTokens.has(t));
|
|
4283
|
-
const hasState = /\buseState\s*[<(]/.test(current);
|
|
4284
|
-
const hasEffect = /\b(useEffect|useLayoutEffect|useMemo|useCallback)\s*[<(]/.test(current);
|
|
4285
|
-
const curImports = extractImportSources(current);
|
|
4286
|
-
const newImports = extractImportSources(incoming);
|
|
4287
|
-
const hasExtraImports = [...curImports].some((src) => !newImports.has(src));
|
|
4288
|
-
const tagSet = /* @__PURE__ */ new Set();
|
|
4289
|
-
for (const m of current.matchAll(/<([A-Z]\w+)[\s/>]/g)) {
|
|
4290
|
-
if (m[1]) tagSet.add(m[1]);
|
|
4291
|
-
}
|
|
4292
|
-
const atomicChildren = [...tagSet];
|
|
4293
|
-
const isComposition = atomicChildren.length > 2;
|
|
4294
|
-
const signatureChanged = extractDefaultParamList(current) !== extractDefaultParamList(incoming);
|
|
4295
|
-
return {
|
|
4296
|
-
apiDelta: { added: apiAdded, removed: apiRemoved, signatureChanged },
|
|
4297
|
-
styleDelta: { classNameDiff, tokenUsageDiff },
|
|
4298
|
-
logicDelta: { hasState, hasEffect, hasExtraImports },
|
|
4299
|
-
cvaDelta: { addedVariants: cvaAdded, modifiedVariants: cvaModified },
|
|
4300
|
-
structureDelta: { isComposition, atomicChildren }
|
|
4301
|
-
};
|
|
4302
|
-
}
|
|
4303
|
-
function scorePromotionModes(fileType, fv) {
|
|
4304
|
-
const reasons = [];
|
|
4305
|
-
if (fileType === "hook" || fileType === "util" || fileType === "type") {
|
|
4306
|
-
reasons.push(
|
|
4307
|
-
`fileType=${fileType} \u2014 not a component, deferred to ManualReview`
|
|
4308
|
-
);
|
|
4309
|
-
return { recommendedModes: ["ManualReview"], confidence: 0.5, reasons };
|
|
4310
|
-
}
|
|
4311
|
-
const apiNoChange = fv.apiDelta.added.length === 0 && fv.apiDelta.removed.length === 0 && !fv.apiDelta.signatureChanged;
|
|
4312
|
-
const logicMinimal = !fv.logicDelta.hasState && !fv.logicDelta.hasEffect && !fv.logicDelta.hasExtraImports;
|
|
4313
|
-
if (fv.apiDelta.removed.length > 0 || fv.apiDelta.signatureChanged) {
|
|
4314
|
-
reasons.push(
|
|
4315
|
-
"signature changed or props removed \u2014 Coexist preserves user version"
|
|
4316
|
-
);
|
|
4317
|
-
return { recommendedModes: ["Coexist"], confidence: 0.85, reasons };
|
|
4318
|
-
}
|
|
4319
|
-
if (apiNoChange && logicMinimal && (fv.styleDelta.classNameDiff || fv.styleDelta.tokenUsageDiff) && fv.cvaDelta.addedVariants.length === 0 && fv.cvaDelta.modifiedVariants.length === 0) {
|
|
4320
|
-
reasons.push(
|
|
4321
|
-
"only style / token differences \u2014 push to tokens.overrides.css"
|
|
4322
|
-
);
|
|
4323
|
-
return { recommendedModes: ["TokenOnly"], confidence: 0.8, reasons };
|
|
4324
|
-
}
|
|
4325
|
-
const modes = [];
|
|
4326
|
-
let score = 0;
|
|
4327
|
-
if (fv.cvaDelta.addedVariants.length > 0 || fv.cvaDelta.modifiedVariants.length > 0) {
|
|
4328
|
-
modes.push("Variant");
|
|
4329
|
-
reasons.push(
|
|
4330
|
-
`cva variants delta: +${fv.cvaDelta.addedVariants.length} ~${fv.cvaDelta.modifiedVariants.length}`
|
|
4331
|
-
);
|
|
4332
|
-
score = Math.max(score, 0.7);
|
|
4333
|
-
}
|
|
4334
|
-
if (fv.logicDelta.hasState || fv.logicDelta.hasEffect || fv.logicDelta.hasExtraImports || fv.apiDelta.added.length > 0) {
|
|
4335
|
-
modes.push("Wrapper");
|
|
4336
|
-
reasons.push("user added state / effect / imports / props");
|
|
4337
|
-
score = Math.max(score, 0.75);
|
|
4338
|
-
}
|
|
4339
|
-
if (apiNoChange && logicMinimal && !fv.styleDelta.classNameDiff && !fv.styleDelta.tokenUsageDiff && fv.cvaDelta.addedVariants.length === 0 && fv.cvaDelta.modifiedVariants.length === 0) {
|
|
4340
|
-
modes.push("Preset");
|
|
4341
|
-
reasons.push("no API/logic delta \u2014 Preset captures default-prop tweaks");
|
|
4342
|
-
score = Math.max(score, 0.6);
|
|
4343
|
-
}
|
|
4344
|
-
if (fv.structureDelta.isComposition) {
|
|
4345
|
-
modes.push("Compose");
|
|
4346
|
-
reasons.push(
|
|
4347
|
-
`composition of ${fv.structureDelta.atomicChildren.length} atomic children`
|
|
4348
|
-
);
|
|
4349
|
-
score = Math.max(score, 0.65);
|
|
4350
|
-
}
|
|
4351
|
-
if (modes.length === 0) {
|
|
4352
|
-
reasons.push("no axis crossed the 0.6 threshold");
|
|
4353
|
-
return { recommendedModes: ["ManualReview"], confidence: 0.4, reasons };
|
|
4354
|
-
}
|
|
4355
|
-
return { recommendedModes: modes, confidence: score, reasons };
|
|
4356
|
-
}
|
|
4357
|
-
function extractVariantBody(src, key) {
|
|
4358
|
-
const block = extractVariantsBlock(src);
|
|
4359
|
-
if (block === null) return "";
|
|
4360
|
-
const re = new RegExp(`(?:["']?${key}["']?)\\s*:\\s*\\{`);
|
|
4361
|
-
const idx = block.search(re);
|
|
4362
|
-
if (idx < 0) return "";
|
|
4363
|
-
const open = block.indexOf("{", idx);
|
|
4364
|
-
if (open < 0) return "";
|
|
4365
|
-
let depth = 0;
|
|
4366
|
-
for (let i = open; i < block.length; i++) {
|
|
4367
|
-
const c = block[i];
|
|
4368
|
-
if (c === "{") depth++;
|
|
4369
|
-
else if (c === "}") {
|
|
4370
|
-
depth--;
|
|
4371
|
-
if (depth === 0) return block.slice(open + 1, i);
|
|
4372
|
-
}
|
|
4373
|
-
}
|
|
4374
|
-
return "";
|
|
4375
|
-
}
|
|
4376
|
-
function extractClassNameLiterals(src) {
|
|
4377
|
-
const out = [];
|
|
4378
|
-
for (const m of src.matchAll(/className\s*=\s*["'`]([^"'`]*)["'`]/g)) {
|
|
4379
|
-
if (m[1]) out.push(m[1]);
|
|
4380
|
-
}
|
|
4381
|
-
for (const m of src.matchAll(/\b(?:cn|clsx|cva)\s*\(/g)) {
|
|
4382
|
-
const open = (m.index ?? 0) + m[0].length - 1;
|
|
4383
|
-
let depth = 1;
|
|
4384
|
-
let i = open + 1;
|
|
4385
|
-
for (; i < src.length && depth > 0; i++) {
|
|
4386
|
-
const c = src[i];
|
|
4387
|
-
if (c === "(") depth++;
|
|
4388
|
-
else if (c === ")") depth--;
|
|
4389
|
-
}
|
|
4390
|
-
const body = src.slice(open + 1, i - 1);
|
|
4391
|
-
for (const lit of body.matchAll(/["'`]([^"'`]*)["'`]/g)) {
|
|
4392
|
-
if (lit[1]) out.push(lit[1]);
|
|
4393
|
-
}
|
|
4394
|
-
}
|
|
4395
|
-
return out.sort().join("|");
|
|
4396
|
-
}
|
|
4397
|
-
function extractTokenRefs(src) {
|
|
4398
|
-
const out = /* @__PURE__ */ new Set();
|
|
4399
|
-
for (const m of src.matchAll(/var\(--([a-z0-9-]+)\)/g)) {
|
|
4400
|
-
if (m[1]) out.add(m[1]);
|
|
4401
|
-
}
|
|
4402
|
-
for (const m of src.matchAll(/--([a-z][a-z0-9-]*)\s*:/g)) {
|
|
4403
|
-
if (m[1]) out.add(m[1]);
|
|
4404
|
-
}
|
|
4405
|
-
return out;
|
|
4406
|
-
}
|
|
4407
|
-
function extractImportSources(src) {
|
|
4408
|
-
const out = /* @__PURE__ */ new Set();
|
|
4409
|
-
for (const m of src.matchAll(/^\s*import\b[^'"]*['"]([^'"]+)['"]/gm)) {
|
|
4410
|
-
if (m[1]) out.add(m[1]);
|
|
4411
|
-
}
|
|
4412
|
-
return out;
|
|
4413
|
-
}
|
|
4414
|
-
function extractDefaultParamList(src) {
|
|
4415
|
-
const m = /export\s+default\s+(?:async\s+)?function\s+\w*\s*\(([^)]*)\)/.exec(src) ?? /export\s+default\s+(?:\([^)]*\)|\w+)\s*=>/.exec(src) ?? /(?:const|function)\s+\w+\s*=?\s*(?:\(([^)]*)\)|\w+)\s*(?:=>|\{)/.exec(src);
|
|
4416
|
-
return m?.[1]?.replace(/\s+/g, " ").trim() ?? "";
|
|
4417
|
-
}
|
|
4418
|
-
|
|
4419
|
-
// src/core/ui-upgrade.ts
|
|
4420
|
-
var nodeRequire = createRequire5(import.meta.url);
|
|
4421
|
-
function resolvePackageRoot4(packageName) {
|
|
4422
|
-
const pkgJsonPath = nodeRequire.resolve(`${packageName}/package.json`);
|
|
4423
|
-
return path24.dirname(pkgJsonPath);
|
|
4424
|
-
}
|
|
4425
|
-
async function buildStaging(args) {
|
|
4426
|
-
const { category, projectRoot, aliases, lineageReport, trigger, onlyIds } = args;
|
|
4427
|
-
if (category === "ui") {
|
|
4428
|
-
const root = args.uiPackageRoot ?? resolvePackageRoot4("@teamix-evo/ui");
|
|
4429
|
-
const manifest = await loadUiPackageManifest3(root);
|
|
4430
|
-
return buildUiUpgradeStaging({
|
|
4431
|
-
projectRoot,
|
|
4432
|
-
category,
|
|
4433
|
-
manifest,
|
|
4434
|
-
packageRoot: root,
|
|
4435
|
-
aliases,
|
|
4436
|
-
lineageReport,
|
|
4437
|
-
trigger,
|
|
4438
|
-
onlyIds
|
|
4439
|
-
});
|
|
4440
|
-
}
|
|
4441
|
-
const bizRoot = args.bizUiPackageRoot ?? resolvePackageRoot4("@teamix-evo/biz-ui");
|
|
4442
|
-
const variant = lineageReport.installedVariant ?? "_flat";
|
|
4443
|
-
const variantDir = path24.join(bizRoot, "variants", variant);
|
|
4444
|
-
const variantManifest = await loadVariantUiPackageManifest2(variantDir);
|
|
4445
|
-
const uiRoot = args.uiPackageRoot ?? resolvePackageRoot4("@teamix-evo/ui");
|
|
4446
|
-
const uiManifest = await loadUiPackageManifest3(uiRoot);
|
|
4447
|
-
const entryPackageRoot = /* @__PURE__ */ new Map();
|
|
4448
|
-
const merged = [];
|
|
4449
|
-
for (const e of variantManifest.entries) {
|
|
4450
|
-
entryPackageRoot.set(e.id, variantDir);
|
|
4451
|
-
merged.push(e);
|
|
4452
|
-
}
|
|
4453
|
-
for (const e of uiManifest.entries) {
|
|
4454
|
-
if (entryPackageRoot.has(e.id)) continue;
|
|
4455
|
-
entryPackageRoot.set(e.id, uiRoot);
|
|
4456
|
-
merged.push(e);
|
|
4457
|
-
}
|
|
4458
|
-
const synthetic = {
|
|
4459
|
-
schemaVersion: 1,
|
|
4460
|
-
package: "ui",
|
|
4461
|
-
version: variantManifest.version,
|
|
4462
|
-
engines: variantManifest.engines,
|
|
4463
|
-
entries: merged
|
|
4464
|
-
};
|
|
4465
|
-
return buildUiUpgradeStaging({
|
|
4466
|
-
projectRoot,
|
|
4467
|
-
category,
|
|
4468
|
-
manifest: synthetic,
|
|
4469
|
-
packageRoot: variantDir,
|
|
4470
|
-
entryPackageRoot,
|
|
4471
|
-
aliases,
|
|
4472
|
-
lineageReport,
|
|
4473
|
-
trigger,
|
|
4474
|
-
onlyIds
|
|
4475
|
-
});
|
|
4476
|
-
}
|
|
4477
|
-
|
|
4478
|
-
// src/core/project-update.ts
|
|
4479
|
-
var DEFAULT_TOKENS_PACKAGE3 = "@teamix-evo/tokens";
|
|
4480
|
-
var DEFAULT_SKILLS_PACKAGE4 = "@teamix-evo/skills";
|
|
4481
|
-
var CRITICAL_STEPS2 = /* @__PURE__ */ new Set(["tokens"]);
|
|
4482
|
-
async function runProjectUpdate(options) {
|
|
4483
|
-
const { projectRoot, dryRun = false, onStep } = options;
|
|
4484
|
-
const tokensPackage = options.tokensPackageName ?? DEFAULT_TOKENS_PACKAGE3;
|
|
4485
|
-
const skillsPackage = options.skillsPackageName ?? DEFAULT_SKILLS_PACKAGE4;
|
|
4486
|
-
const config = await readProjectConfig(projectRoot);
|
|
4487
|
-
if (!config) {
|
|
4488
|
-
return { status: "not-initialized" };
|
|
4489
|
-
}
|
|
4490
|
-
let snapshot = null;
|
|
4491
|
-
let snapshotError;
|
|
4492
|
-
if (!dryRun) {
|
|
4493
|
-
try {
|
|
4494
|
-
snapshot = await createSnapshot(projectRoot, { reason: "update" });
|
|
3194
|
+
const result = await runSkillsInit({ projectRoot, ides, scope, ide });
|
|
3195
|
+
if (result.status === "already-initialized") {
|
|
3196
|
+
record({ name: "skills", status: "ok", detail: "already-initialized" });
|
|
3197
|
+
} else {
|
|
3198
|
+
record({
|
|
3199
|
+
name: "skills",
|
|
3200
|
+
status: "ok",
|
|
3201
|
+
detail: `${result.skillCount} skills, ${result.fileCount} files (added: ${result.addedSkillIds.join(", ") || "none"})`,
|
|
3202
|
+
changes: result.addedSkillIds.map((id) => ({
|
|
3203
|
+
kind: "created",
|
|
3204
|
+
path: `.teamix-evo/skills/${id}/SKILL.md`,
|
|
3205
|
+
step: "skills",
|
|
3206
|
+
detail: "skill installed"
|
|
3207
|
+
}))
|
|
3208
|
+
});
|
|
3209
|
+
}
|
|
4495
3210
|
} catch (err) {
|
|
4496
|
-
|
|
4497
|
-
}
|
|
4498
|
-
}
|
|
4499
|
-
const steps = [];
|
|
4500
|
-
let aborted = false;
|
|
4501
|
-
const firstFailure = { value: null };
|
|
4502
|
-
function record(step) {
|
|
4503
|
-
steps.push(step);
|
|
4504
|
-
onStep?.(step);
|
|
4505
|
-
}
|
|
4506
|
-
function recordFailure(name, err) {
|
|
4507
|
-
const message = getErrorMessage(err);
|
|
4508
|
-
record({ name, status: "fail", detail: message });
|
|
4509
|
-
if (!firstFailure.value) {
|
|
4510
|
-
firstFailure.value = { step: name, error: message };
|
|
3211
|
+
recordFailure("skills", err);
|
|
4511
3212
|
}
|
|
4512
|
-
if (CRITICAL_STEPS2.has(name)) aborted = true;
|
|
4513
3213
|
}
|
|
4514
|
-
|
|
4515
|
-
if (!config.packages?.tokens) {
|
|
3214
|
+
if (dryRun) {
|
|
4516
3215
|
record({
|
|
4517
|
-
name: "
|
|
4518
|
-
status: "
|
|
4519
|
-
detail: "
|
|
3216
|
+
name: "agents-md",
|
|
3217
|
+
status: "planned",
|
|
3218
|
+
detail: "runGenerateAgentsMd()"
|
|
4520
3219
|
});
|
|
4521
|
-
} else
|
|
3220
|
+
} else {
|
|
4522
3221
|
try {
|
|
4523
|
-
const
|
|
4524
|
-
|
|
3222
|
+
const result = await runGenerateAgentsMd({
|
|
3223
|
+
projectRoot,
|
|
3224
|
+
variant,
|
|
3225
|
+
skillIds: [
|
|
3226
|
+
`teamix-evo-design-${variant}`,
|
|
3227
|
+
`teamix-evo-code-${variant}`
|
|
3228
|
+
],
|
|
3229
|
+
mode: "merge-managed"
|
|
3230
|
+
});
|
|
3231
|
+
record({
|
|
3232
|
+
name: "agents-md",
|
|
3233
|
+
status: "ok",
|
|
3234
|
+
detail: `${result.skillCount} skill index`,
|
|
3235
|
+
changes: [
|
|
3236
|
+
{
|
|
3237
|
+
kind: result.backedUp ? "modified" : "created",
|
|
3238
|
+
path: toRelativePosix(result.path, projectRoot),
|
|
3239
|
+
step: "agents-md",
|
|
3240
|
+
detail: "skill-trigger fallback (ADR 0038)"
|
|
3241
|
+
}
|
|
3242
|
+
]
|
|
3243
|
+
});
|
|
4525
3244
|
} catch (err) {
|
|
4526
|
-
recordFailure("
|
|
3245
|
+
recordFailure("agents-md", err);
|
|
4527
3246
|
}
|
|
3247
|
+
}
|
|
3248
|
+
if (dryRun) {
|
|
3249
|
+
record({ name: "ui-init", status: "planned", detail: "runUiInit()" });
|
|
3250
|
+
} else if (aborted) {
|
|
3251
|
+
record({
|
|
3252
|
+
name: "ui-init",
|
|
3253
|
+
status: "skip",
|
|
3254
|
+
detail: "aborted: earlier critical step failed"
|
|
3255
|
+
});
|
|
4528
3256
|
} else {
|
|
4529
3257
|
try {
|
|
4530
|
-
const
|
|
4531
|
-
|
|
4532
|
-
|
|
3258
|
+
const initResult = await runUiInit({ projectRoot, ide });
|
|
3259
|
+
record({
|
|
3260
|
+
name: "ui-init",
|
|
3261
|
+
status: "ok",
|
|
3262
|
+
detail: initResult.status === "installed" ? "config.json packages.ui written" : initResult.status
|
|
4533
3263
|
});
|
|
4534
|
-
if (result.status === "not-initialized") {
|
|
4535
|
-
record({
|
|
4536
|
-
name: "tokens",
|
|
4537
|
-
status: "skip",
|
|
4538
|
-
detail: "tokens not installed"
|
|
4539
|
-
});
|
|
4540
|
-
} else if (result.status === "up-to-date") {
|
|
4541
|
-
const driftSuffix = result.frozenDrift.length > 0 ? `, frozen drift: ${result.frozenDrift.length}` : "";
|
|
4542
|
-
record({
|
|
4543
|
-
name: "tokens",
|
|
4544
|
-
status: "ok",
|
|
4545
|
-
detail: `up-to-date (${result.variant} v${result.version}${driftSuffix})`
|
|
4546
|
-
});
|
|
4547
|
-
} else {
|
|
4548
|
-
anyChanged = true;
|
|
4549
|
-
const extras = [];
|
|
4550
|
-
if (result.managedReplaced.length > 0)
|
|
4551
|
-
extras.push(`managed: ${result.managedReplaced.length}`);
|
|
4552
|
-
if (result.frozenDrift.length > 0)
|
|
4553
|
-
extras.push(`frozen drift: ${result.frozenDrift.length}`);
|
|
4554
|
-
const suffix = extras.length > 0 ? ` [${extras.join(", ")}]` : "";
|
|
4555
|
-
record({
|
|
4556
|
-
name: "tokens",
|
|
4557
|
-
status: "ok",
|
|
4558
|
-
detail: `${result.variant} v${result.from} \u2192 v${result.to}${suffix}`
|
|
4559
|
-
});
|
|
4560
|
-
}
|
|
4561
3264
|
} catch (err) {
|
|
4562
|
-
recordFailure("
|
|
3265
|
+
recordFailure("ui-init", err);
|
|
4563
3266
|
}
|
|
4564
3267
|
}
|
|
4565
|
-
|
|
3268
|
+
const collectedNpmDeps = {};
|
|
3269
|
+
if (dryRun) {
|
|
3270
|
+
const { manifest } = await loadUiData("@teamix-evo/ui").catch(() => ({
|
|
3271
|
+
manifest: { entries: [] }
|
|
3272
|
+
}));
|
|
4566
3273
|
record({
|
|
4567
|
-
name: "
|
|
4568
|
-
status: "
|
|
4569
|
-
detail:
|
|
3274
|
+
name: "ui-add",
|
|
3275
|
+
status: "planned",
|
|
3276
|
+
detail: `runUiAdd(${manifest.entries.length} entries, --all)`
|
|
4570
3277
|
});
|
|
4571
3278
|
} else if (aborted) {
|
|
4572
3279
|
record({
|
|
4573
|
-
name: "
|
|
3280
|
+
name: "ui-add",
|
|
4574
3281
|
status: "skip",
|
|
4575
3282
|
detail: "aborted: earlier critical step failed"
|
|
4576
3283
|
});
|
|
4577
3284
|
} else {
|
|
4578
3285
|
try {
|
|
4579
|
-
const
|
|
3286
|
+
const { manifest } = await loadUiData("@teamix-evo/ui");
|
|
3287
|
+
const allIds = manifest.entries.map((e) => e.id);
|
|
3288
|
+
const addResult = await runUiAdd({
|
|
4580
3289
|
projectRoot,
|
|
4581
|
-
|
|
4582
|
-
|
|
3290
|
+
ids: allIds,
|
|
3291
|
+
overwrite: true
|
|
3292
|
+
});
|
|
3293
|
+
const writtenResources = addResult.written > 0 ? addResult.resources.slice(-addResult.written) : [];
|
|
3294
|
+
record({
|
|
3295
|
+
name: "ui-add",
|
|
3296
|
+
status: "ok",
|
|
3297
|
+
detail: `${addResult.orderedIds.length} entries (${addResult.written} written, ${addResult.skipped} skipped)`,
|
|
3298
|
+
changes: writtenResources.map((r) => ({
|
|
3299
|
+
kind: "created",
|
|
3300
|
+
path: toRelativePosix(r.target, projectRoot),
|
|
3301
|
+
step: "ui-add",
|
|
3302
|
+
detail: r.strategy
|
|
3303
|
+
}))
|
|
3304
|
+
});
|
|
3305
|
+
Object.assign(collectedNpmDeps, addResult.npmDependencies);
|
|
3306
|
+
} catch (err) {
|
|
3307
|
+
recordFailure("ui-add", err);
|
|
3308
|
+
}
|
|
3309
|
+
}
|
|
3310
|
+
if (dryRun) {
|
|
3311
|
+
try {
|
|
3312
|
+
const listing = await listBizUiEntries(variant);
|
|
3313
|
+
record({
|
|
3314
|
+
name: "biz-ui-add",
|
|
3315
|
+
status: "planned",
|
|
3316
|
+
detail: `runBizUiAdd(variant=${variant}, ${listing.entries.length} entries)`
|
|
3317
|
+
});
|
|
3318
|
+
} catch {
|
|
3319
|
+
record({
|
|
3320
|
+
name: "biz-ui-add",
|
|
3321
|
+
status: "planned",
|
|
3322
|
+
detail: `runBizUiAdd(variant=${variant})`
|
|
4583
3323
|
});
|
|
4584
|
-
|
|
3324
|
+
}
|
|
3325
|
+
} else if (aborted) {
|
|
3326
|
+
record({
|
|
3327
|
+
name: "biz-ui-add",
|
|
3328
|
+
status: "skip",
|
|
3329
|
+
detail: "aborted: earlier critical step failed"
|
|
3330
|
+
});
|
|
3331
|
+
} else {
|
|
3332
|
+
try {
|
|
3333
|
+
const listing = await listBizUiEntries(variant);
|
|
3334
|
+
if (listing.entries.length === 0) {
|
|
4585
3335
|
record({
|
|
4586
|
-
name: "
|
|
3336
|
+
name: "biz-ui-add",
|
|
4587
3337
|
status: "skip",
|
|
4588
|
-
detail:
|
|
3338
|
+
detail: `no biz-ui entries for variant "${variant}"`
|
|
3339
|
+
});
|
|
3340
|
+
} else {
|
|
3341
|
+
const allIds = listing.entries.map((e) => e.id);
|
|
3342
|
+
const result = await runBizUiAdd({
|
|
3343
|
+
projectRoot,
|
|
3344
|
+
variant,
|
|
3345
|
+
ids: allIds,
|
|
3346
|
+
overwrite: true
|
|
4589
3347
|
});
|
|
4590
|
-
} else if (result.status === "no-changes") {
|
|
4591
3348
|
record({
|
|
4592
|
-
name: "
|
|
3349
|
+
name: "biz-ui-add",
|
|
4593
3350
|
status: "ok",
|
|
4594
|
-
detail:
|
|
3351
|
+
detail: `${result.orderedIds.length} entries (${result.written} written, ${result.skipped} skipped)`
|
|
4595
3352
|
});
|
|
4596
|
-
|
|
4597
|
-
|
|
4598
|
-
|
|
4599
|
-
record({ name: "skills", status: "planned", detail });
|
|
4600
|
-
} else {
|
|
4601
|
-
anyChanged = true;
|
|
4602
|
-
const summary = result.updatedSkillIds.length > 0 ? `updated: ${result.updatedSkillIds.join(", ")} (v${result.version})` : `refreshed at v${result.version}`;
|
|
4603
|
-
record({ name: "skills", status: "ok", detail: summary });
|
|
3353
|
+
if (result.npmDependencies) {
|
|
3354
|
+
Object.assign(collectedNpmDeps, result.npmDependencies);
|
|
3355
|
+
}
|
|
4604
3356
|
}
|
|
4605
3357
|
} catch (err) {
|
|
4606
|
-
recordFailure("
|
|
3358
|
+
recordFailure("biz-ui-add", err);
|
|
4607
3359
|
}
|
|
4608
3360
|
}
|
|
4609
|
-
|
|
4610
|
-
|
|
4611
|
-
|
|
4612
|
-
|
|
4613
|
-
|
|
4614
|
-
|
|
4615
|
-
|
|
4616
|
-
|
|
4617
|
-
|
|
4618
|
-
|
|
4619
|
-
|
|
4620
|
-
|
|
4621
|
-
|
|
4622
|
-
});
|
|
4623
|
-
const out = (() => {
|
|
4624
|
-
if (firstFailure.value) {
|
|
4625
|
-
return {
|
|
4626
|
-
status: "partial",
|
|
4627
|
-
steps,
|
|
4628
|
-
resumeHint: {
|
|
4629
|
-
failedAt: firstFailure.value.step,
|
|
4630
|
-
completed: steps.filter((s) => s.status === "ok").map((s) => s.name),
|
|
4631
|
-
failed: steps.filter((s) => s.status === "fail").map((s) => s.name),
|
|
4632
|
-
error: firstFailure.value.error,
|
|
4633
|
-
resumeCommand: "teamix-evo update"
|
|
4634
|
-
},
|
|
4635
|
-
snapshot,
|
|
4636
|
-
...snapshotError ? { snapshotError } : {}
|
|
4637
|
-
};
|
|
3361
|
+
if (!dryRun && !aborted && Object.keys(collectedNpmDeps).length > 0) {
|
|
3362
|
+
try {
|
|
3363
|
+
await installProjectDeps({
|
|
3364
|
+
projectRoot,
|
|
3365
|
+
npmDependencies: collectedNpmDeps,
|
|
3366
|
+
skipInstall: options.skipInstall ?? false
|
|
3367
|
+
});
|
|
3368
|
+
} catch (err) {
|
|
3369
|
+
logger.warn(
|
|
3370
|
+
`\u5B89\u88C5 ui/biz-ui \u4F20\u9012\u4F9D\u8D56\u5931\u8D25\uFF1A${getErrorMessage(
|
|
3371
|
+
err
|
|
3372
|
+
)}\uFF08\u53EF\u624B\u52A8\u8FD0\u884C \`npm install\` \u91CD\u8BD5\uFF09`
|
|
3373
|
+
);
|
|
4638
3374
|
}
|
|
4639
|
-
if (dryRun) return { status: "dry-run", steps, snapshot: null };
|
|
4640
|
-
if (anyChanged)
|
|
4641
|
-
return {
|
|
4642
|
-
status: "updated",
|
|
4643
|
-
steps,
|
|
4644
|
-
snapshot,
|
|
4645
|
-
...snapshotError ? { snapshotError } : {}
|
|
4646
|
-
};
|
|
4647
|
-
return {
|
|
4648
|
-
status: "up-to-date",
|
|
4649
|
-
steps,
|
|
4650
|
-
snapshot,
|
|
4651
|
-
...snapshotError ? { snapshotError } : {}
|
|
4652
|
-
};
|
|
4653
|
-
})();
|
|
4654
|
-
return out;
|
|
4655
|
-
}
|
|
4656
|
-
async function runComponentSourceStep(category, args) {
|
|
4657
|
-
const { projectRoot, config, dryRun, record, recordFailure } = args;
|
|
4658
|
-
const cfgKey = category === "ui" ? "ui" : "biz-ui";
|
|
4659
|
-
if (!config.packages?.[cfgKey]) {
|
|
4660
|
-
record({
|
|
4661
|
-
name: category,
|
|
4662
|
-
status: "skip",
|
|
4663
|
-
detail: `${category} not installed`
|
|
4664
|
-
});
|
|
4665
|
-
return;
|
|
4666
|
-
}
|
|
4667
|
-
const aliases = config.packages.ui?.aliases ?? config.packages["biz-ui"]?.aliases;
|
|
4668
|
-
if (!aliases) {
|
|
4669
|
-
record({
|
|
4670
|
-
name: category,
|
|
4671
|
-
status: "skip",
|
|
4672
|
-
detail: `${category} aliases not configured`
|
|
4673
|
-
});
|
|
4674
|
-
return;
|
|
4675
|
-
}
|
|
4676
|
-
let report;
|
|
4677
|
-
try {
|
|
4678
|
-
report = await detectComponentLineage({
|
|
4679
|
-
projectRoot,
|
|
4680
|
-
category,
|
|
4681
|
-
config
|
|
4682
|
-
});
|
|
4683
|
-
} catch (err) {
|
|
4684
|
-
record({
|
|
4685
|
-
name: category,
|
|
4686
|
-
status: "skip",
|
|
4687
|
-
detail: `lineage detect failed: ${getErrorMessage(err)}`
|
|
4688
|
-
});
|
|
4689
|
-
return;
|
|
4690
3375
|
}
|
|
4691
|
-
if (
|
|
4692
|
-
record({
|
|
4693
|
-
|
|
4694
|
-
|
|
4695
|
-
|
|
4696
|
-
|
|
4697
|
-
|
|
3376
|
+
if (dryRun) {
|
|
3377
|
+
record({ name: "lint", status: "planned", detail: "runLintInit()" });
|
|
3378
|
+
} else {
|
|
3379
|
+
try {
|
|
3380
|
+
const result = await runLintInit({
|
|
3381
|
+
projectRoot,
|
|
3382
|
+
skipInstall: options.skipInstall ?? false,
|
|
3383
|
+
eslintStrategy: "overwrite",
|
|
3384
|
+
stylelintStrategy: "overwrite",
|
|
3385
|
+
eslintExistingPaths: [],
|
|
3386
|
+
stylelintExistingPaths: []
|
|
3387
|
+
});
|
|
3388
|
+
const detailParts = [];
|
|
3389
|
+
if (result.status === "installed") {
|
|
3390
|
+
detailParts.push(
|
|
3391
|
+
`eslint=${result.eslint}, stylelint=${result.stylelint}`
|
|
3392
|
+
);
|
|
3393
|
+
if (result.packageJsonPatched) detailParts.push("package.json patched");
|
|
3394
|
+
} else {
|
|
3395
|
+
detailParts.push(result.status);
|
|
3396
|
+
}
|
|
3397
|
+
record({
|
|
3398
|
+
name: "lint",
|
|
3399
|
+
status: "ok",
|
|
3400
|
+
detail: detailParts.join(" / "),
|
|
3401
|
+
changes: deriveLintChanges(result)
|
|
3402
|
+
});
|
|
3403
|
+
} catch (err) {
|
|
3404
|
+
recordFailure("lint", err);
|
|
3405
|
+
}
|
|
4698
3406
|
}
|
|
4699
3407
|
if (dryRun) {
|
|
4700
|
-
const total = report.registeredIds.length + report.unregisteredIds.length;
|
|
4701
3408
|
record({
|
|
4702
|
-
name:
|
|
3409
|
+
name: "gitignore",
|
|
4703
3410
|
status: "planned",
|
|
4704
|
-
detail:
|
|
3411
|
+
detail: "append teamix-evo runtime rules"
|
|
4705
3412
|
});
|
|
4706
|
-
|
|
3413
|
+
} else {
|
|
3414
|
+
try {
|
|
3415
|
+
let projectRootExists = true;
|
|
3416
|
+
try {
|
|
3417
|
+
await fsNode.access(projectRoot);
|
|
3418
|
+
} catch {
|
|
3419
|
+
projectRootExists = false;
|
|
3420
|
+
}
|
|
3421
|
+
if (!projectRootExists) {
|
|
3422
|
+
record({
|
|
3423
|
+
name: "gitignore",
|
|
3424
|
+
status: "skip",
|
|
3425
|
+
detail: "projectRoot does not exist"
|
|
3426
|
+
});
|
|
3427
|
+
} else {
|
|
3428
|
+
const giPath = path19.join(projectRoot, ".gitignore");
|
|
3429
|
+
let giContent = "";
|
|
3430
|
+
try {
|
|
3431
|
+
giContent = await fsNode.readFile(giPath, "utf-8");
|
|
3432
|
+
} catch {
|
|
3433
|
+
}
|
|
3434
|
+
if (giContent.includes(GITIGNORE_MARKER_START)) {
|
|
3435
|
+
record({
|
|
3436
|
+
name: "gitignore",
|
|
3437
|
+
status: "skip",
|
|
3438
|
+
detail: "teamix-evo markers already present"
|
|
3439
|
+
});
|
|
3440
|
+
} else {
|
|
3441
|
+
const block = [
|
|
3442
|
+
"",
|
|
3443
|
+
GITIGNORE_MARKER_START,
|
|
3444
|
+
...GITIGNORE_RULES,
|
|
3445
|
+
GITIGNORE_MARKER_END,
|
|
3446
|
+
""
|
|
3447
|
+
].join("\n");
|
|
3448
|
+
const separator = giContent.length > 0 && !giContent.endsWith("\n") ? "\n" : "";
|
|
3449
|
+
await fsNode.writeFile(
|
|
3450
|
+
giPath,
|
|
3451
|
+
giContent + separator + block,
|
|
3452
|
+
"utf-8"
|
|
3453
|
+
);
|
|
3454
|
+
record({
|
|
3455
|
+
name: "gitignore",
|
|
3456
|
+
status: "ok",
|
|
3457
|
+
detail: `${GITIGNORE_RULES.filter((r) => r && !r.startsWith("#")).length} rules appended`,
|
|
3458
|
+
changes: [
|
|
3459
|
+
{
|
|
3460
|
+
kind: "modified",
|
|
3461
|
+
path: ".gitignore",
|
|
3462
|
+
step: "gitignore",
|
|
3463
|
+
detail: "teamix-evo runtime artifact rules"
|
|
3464
|
+
}
|
|
3465
|
+
]
|
|
3466
|
+
});
|
|
3467
|
+
}
|
|
3468
|
+
}
|
|
3469
|
+
} catch (err) {
|
|
3470
|
+
recordFailure("gitignore", err);
|
|
3471
|
+
}
|
|
4707
3472
|
}
|
|
4708
|
-
|
|
4709
|
-
|
|
4710
|
-
|
|
4711
|
-
|
|
4712
|
-
|
|
4713
|
-
|
|
4714
|
-
|
|
4715
|
-
|
|
4716
|
-
|
|
4717
|
-
|
|
4718
|
-
|
|
4719
|
-
|
|
4720
|
-
|
|
4721
|
-
|
|
4722
|
-
|
|
4723
|
-
|
|
3473
|
+
const status = dryRun ? "dry-run" : steps.some((s) => s.status === "fail") ? "partial" : "installed";
|
|
3474
|
+
const out = { status, steps, changes: allChanges };
|
|
3475
|
+
if (firstFailure.value) {
|
|
3476
|
+
out.resumeHint = {
|
|
3477
|
+
failedAt: firstFailure.value.step,
|
|
3478
|
+
completed: steps.filter((s) => s.status === "ok").map((s) => s.name),
|
|
3479
|
+
failed: steps.filter((s) => s.status === "fail").map((s) => s.name),
|
|
3480
|
+
error: firstFailure.value.error,
|
|
3481
|
+
resumeCommand: "teamix-evo init"
|
|
3482
|
+
};
|
|
3483
|
+
}
|
|
3484
|
+
if (!dryRun) {
|
|
3485
|
+
try {
|
|
3486
|
+
const checklistContent = renderInitChecklist({ variant, status, steps });
|
|
3487
|
+
const checklistPath = path19.join(
|
|
3488
|
+
projectRoot,
|
|
3489
|
+
".teamix-evo",
|
|
3490
|
+
"init-checklist.md"
|
|
3491
|
+
);
|
|
3492
|
+
await fsNode.mkdir(path19.dirname(checklistPath), { recursive: true });
|
|
3493
|
+
await fsNode.writeFile(checklistPath, checklistContent, "utf-8");
|
|
3494
|
+
logger.info(" wrote .teamix-evo/init-checklist.md");
|
|
3495
|
+
} catch {
|
|
3496
|
+
logger.warn(" failed to write init-checklist.md (non-fatal)");
|
|
4724
3497
|
}
|
|
4725
|
-
const stagingRel = path25.relative(projectRoot, built.stagingDir);
|
|
4726
|
-
const summary = summarizeStagingRisk(built.manifest.summary.byRisk);
|
|
4727
|
-
record({
|
|
4728
|
-
name: category,
|
|
4729
|
-
status: "ok",
|
|
4730
|
-
detail: `${built.manifest.summary.total} component(s) staged${summary ? ` (${summary})` : ""}, see ${stagingRel}`
|
|
4731
|
-
});
|
|
4732
|
-
} catch (err) {
|
|
4733
|
-
recordFailure(category, err);
|
|
4734
3498
|
}
|
|
3499
|
+
return out;
|
|
4735
3500
|
}
|
|
4736
|
-
function
|
|
4737
|
-
|
|
4738
|
-
|
|
4739
|
-
|
|
4740
|
-
|
|
4741
|
-
|
|
4742
|
-
|
|
4743
|
-
|
|
4744
|
-
|
|
4745
|
-
|
|
4746
|
-
|
|
4747
|
-
|
|
4748
|
-
|
|
4749
|
-
|
|
4750
|
-
|
|
4751
|
-
|
|
4752
|
-
|
|
4753
|
-
|
|
4754
|
-
if (!tokensCfg) return "tokens not installed";
|
|
4755
|
-
const packageRoot = resolveTokensPackageRoot(tokensPackage);
|
|
4756
|
-
const catalog = await loadTokensPackageManifest3(packageRoot);
|
|
4757
|
-
const variantEntry = getVariantEntry3(catalog, tokensCfg.variant);
|
|
4758
|
-
if (!variantEntry) {
|
|
4759
|
-
return `variant "${tokensCfg.variant}" no longer in ${tokensPackage}@${catalog.version} \u2014 uninstall + re-init to switch`;
|
|
3501
|
+
function deriveLintChanges(result) {
|
|
3502
|
+
if (result.status !== "installed") return [];
|
|
3503
|
+
const out = [];
|
|
3504
|
+
if (result.eslint) {
|
|
3505
|
+
out.push({
|
|
3506
|
+
kind: "created",
|
|
3507
|
+
path: "eslint.config.js",
|
|
3508
|
+
step: "lint",
|
|
3509
|
+
detail: "@teamix-evo/eslint-config"
|
|
3510
|
+
});
|
|
3511
|
+
}
|
|
3512
|
+
if (result.stylelint) {
|
|
3513
|
+
out.push({
|
|
3514
|
+
kind: "created",
|
|
3515
|
+
path: "stylelint.config.cjs",
|
|
3516
|
+
step: "lint",
|
|
3517
|
+
detail: "@teamix-evo/stylelint-config"
|
|
3518
|
+
});
|
|
4760
3519
|
}
|
|
4761
|
-
if (
|
|
4762
|
-
|
|
3520
|
+
if (result.packageJsonPatched) {
|
|
3521
|
+
out.push({
|
|
3522
|
+
kind: "created",
|
|
3523
|
+
path: "package.json",
|
|
3524
|
+
step: "lint",
|
|
3525
|
+
detail: 'scripts.lint / scripts["lint:css"]'
|
|
3526
|
+
});
|
|
4763
3527
|
}
|
|
4764
|
-
return
|
|
3528
|
+
return out;
|
|
4765
3529
|
}
|
|
4766
3530
|
|
|
4767
3531
|
// src/core/installer.ts
|
|
4768
|
-
import * as
|
|
4769
|
-
import * as
|
|
3532
|
+
import * as path20 from "path";
|
|
3533
|
+
import * as fs15 from "fs/promises";
|
|
4770
3534
|
async function installResources(options) {
|
|
4771
3535
|
const { projectRoot, manifest, data, variantDir, packageRoot } = options;
|
|
4772
3536
|
const installedResources = [];
|
|
@@ -4803,13 +3567,13 @@ async function installSingleResource(resource, projectRoot, data, variantDir, pa
|
|
|
4803
3567
|
variantDir,
|
|
4804
3568
|
packageRoot
|
|
4805
3569
|
);
|
|
4806
|
-
const targetPath =
|
|
3570
|
+
const targetPath = path20.join(projectRoot, resource.target);
|
|
4807
3571
|
let content;
|
|
4808
3572
|
if (resource.template) {
|
|
4809
3573
|
const templateContent = await loadTemplateFile(sourcePath);
|
|
4810
3574
|
content = renderTemplate(templateContent, data);
|
|
4811
3575
|
} else {
|
|
4812
|
-
content = await
|
|
3576
|
+
content = await fs15.readFile(sourcePath, "utf-8");
|
|
4813
3577
|
}
|
|
4814
3578
|
await writeFileSafe(targetPath, content);
|
|
4815
3579
|
const hash = computeHash(content);
|
|
@@ -4827,13 +3591,13 @@ async function installRecursiveResource(resource, projectRoot, data, variantDir,
|
|
|
4827
3591
|
variantDir,
|
|
4828
3592
|
packageRoot
|
|
4829
3593
|
);
|
|
4830
|
-
const targetDir =
|
|
3594
|
+
const targetDir = path20.join(projectRoot, resource.target);
|
|
4831
3595
|
const results = [];
|
|
4832
3596
|
await ensureDir(targetDir);
|
|
4833
3597
|
const entries = await walkDir(sourcePath);
|
|
4834
3598
|
for (const entry of entries) {
|
|
4835
|
-
const relPath =
|
|
4836
|
-
let targetFile =
|
|
3599
|
+
const relPath = path20.relative(sourcePath, entry);
|
|
3600
|
+
let targetFile = path20.join(targetDir, relPath);
|
|
4837
3601
|
if (resource.template && targetFile.endsWith(".hbs")) {
|
|
4838
3602
|
targetFile = targetFile.slice(0, -4);
|
|
4839
3603
|
}
|
|
@@ -4842,11 +3606,11 @@ async function installRecursiveResource(resource, projectRoot, data, variantDir,
|
|
|
4842
3606
|
const templateContent = await loadTemplateFile(entry);
|
|
4843
3607
|
content = renderTemplate(templateContent, data);
|
|
4844
3608
|
} else {
|
|
4845
|
-
content = await
|
|
3609
|
+
content = await fs15.readFile(entry, "utf-8");
|
|
4846
3610
|
}
|
|
4847
3611
|
await writeFileSafe(targetFile, content);
|
|
4848
3612
|
const hash = computeHash(content);
|
|
4849
|
-
const targetRel =
|
|
3613
|
+
const targetRel = path20.relative(projectRoot, targetFile);
|
|
4850
3614
|
results.push({
|
|
4851
3615
|
id: `${resource.id}:${relPath}`,
|
|
4852
3616
|
target: targetRel,
|
|
@@ -4859,25 +3623,25 @@ async function installRecursiveResource(resource, projectRoot, data, variantDir,
|
|
|
4859
3623
|
}
|
|
4860
3624
|
|
|
4861
3625
|
// src/core/registry-client.ts
|
|
4862
|
-
import * as
|
|
4863
|
-
import * as
|
|
4864
|
-
import { createRequire as
|
|
3626
|
+
import * as path21 from "path";
|
|
3627
|
+
import * as fs16 from "fs/promises";
|
|
3628
|
+
import { createRequire as createRequire5 } from "module";
|
|
4865
3629
|
import { loadVariantManifest } from "@teamix-evo/registry";
|
|
4866
|
-
var require6 =
|
|
4867
|
-
function
|
|
3630
|
+
var require6 = createRequire5(import.meta.url);
|
|
3631
|
+
function resolvePackageRoot4(packageName) {
|
|
4868
3632
|
const pkgJsonPath = require6.resolve(`${packageName}/package.json`);
|
|
4869
|
-
return
|
|
3633
|
+
return path21.dirname(pkgJsonPath);
|
|
4870
3634
|
}
|
|
4871
3635
|
async function loadVariantData(packageName, variant) {
|
|
4872
|
-
const packageRoot =
|
|
4873
|
-
const variantDir =
|
|
3636
|
+
const packageRoot = resolvePackageRoot4(packageName);
|
|
3637
|
+
const variantDir = path21.join(packageRoot, "library", variant);
|
|
4874
3638
|
logger.debug(`Resolved variant dir: ${variantDir}`);
|
|
4875
3639
|
logger.debug(`Package root: ${packageRoot}`);
|
|
4876
3640
|
const manifest = await loadVariantManifest(variantDir);
|
|
4877
3641
|
let data = {};
|
|
4878
|
-
const dataPath =
|
|
3642
|
+
const dataPath = path21.join(variantDir, "_data.json");
|
|
4879
3643
|
try {
|
|
4880
|
-
const raw = await
|
|
3644
|
+
const raw = await fs16.readFile(dataPath, "utf-8");
|
|
4881
3645
|
data = JSON.parse(raw);
|
|
4882
3646
|
} catch (err) {
|
|
4883
3647
|
if (err.code !== "ENOENT") {
|
|
@@ -4914,7 +3678,6 @@ export {
|
|
|
4914
3678
|
runGenerateAgentsMd,
|
|
4915
3679
|
runLintInit,
|
|
4916
3680
|
runProjectInit,
|
|
4917
|
-
runProjectUpdate,
|
|
4918
3681
|
runSkillsAdd,
|
|
4919
3682
|
runSkillsInit,
|
|
4920
3683
|
runSkillsUpdate,
|