teamix-evo 0.8.0 → 0.9.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 +2 -0
- package/dist/core/index.d.ts +155 -10
- package/dist/core/index.js +808 -105
- package/dist/core/index.js.map +1 -1
- package/dist/index.js +2431 -336
- package/dist/index.js.map +1 -1
- package/package.json +5 -5
package/dist/index.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
// src/index.ts
|
|
4
|
-
import { Command as
|
|
4
|
+
import { Command as Command39 } from "commander";
|
|
5
5
|
import { createRequire as createRequire8 } from "module";
|
|
6
6
|
|
|
7
7
|
// src/commands/tokens/index.ts
|
|
8
|
-
import { Command as
|
|
8
|
+
import { Command as Command7 } from "commander";
|
|
9
9
|
|
|
10
10
|
// src/commands/tokens/init.ts
|
|
11
11
|
import { Command } from "commander";
|
|
@@ -181,7 +181,8 @@ var TEAMIX_DIR = ".teamix-evo";
|
|
|
181
181
|
var CONFIG_FILE = "config.json";
|
|
182
182
|
var MANIFEST_FILE = "manifest.json";
|
|
183
183
|
var TOKENS_LOCK_FILE = "tokens-lock.json";
|
|
184
|
-
var SKILLS_DIR = "skills";
|
|
184
|
+
var SKILLS_DIR = "skills-source";
|
|
185
|
+
var LEGACY_SKILLS_DIR = "skills";
|
|
185
186
|
var SKILLS_LOCK_FILE = "manifest.lock.json";
|
|
186
187
|
function getTeamixDir(projectRoot) {
|
|
187
188
|
return path4.join(projectRoot, TEAMIX_DIR);
|
|
@@ -265,6 +266,9 @@ function getSkillsSourceDir(projectRoot, skillName) {
|
|
|
265
266
|
const base = path4.join(projectRoot, TEAMIX_DIR, SKILLS_DIR);
|
|
266
267
|
return skillName ? path4.join(base, skillName) : base;
|
|
267
268
|
}
|
|
269
|
+
function getLegacySkillsSourceDir(projectRoot) {
|
|
270
|
+
return path4.join(projectRoot, TEAMIX_DIR, LEGACY_SKILLS_DIR);
|
|
271
|
+
}
|
|
268
272
|
async function readSkillsLock(projectRoot) {
|
|
269
273
|
const lockPath = path4.join(
|
|
270
274
|
projectRoot,
|
|
@@ -397,6 +401,7 @@ function resolveTokensPackageRoot(packageName) {
|
|
|
397
401
|
|
|
398
402
|
// src/core/skills-installer.ts
|
|
399
403
|
async function installSkills(options) {
|
|
404
|
+
await migrateLegacySkillsSourceDir(options.projectRoot);
|
|
400
405
|
const { manifest, ides, scope, onlyIds } = options;
|
|
401
406
|
const installed = [];
|
|
402
407
|
const targets = manifest.skills.filter(
|
|
@@ -689,6 +694,7 @@ function escapeRegExp(str) {
|
|
|
689
694
|
return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
690
695
|
}
|
|
691
696
|
async function syncSkillsToIdes(options) {
|
|
697
|
+
await migrateLegacySkillsSourceDir(options.projectRoot);
|
|
692
698
|
const { projectRoot, skills, ides, scope, onlyIds } = options;
|
|
693
699
|
const out = [];
|
|
694
700
|
const targets = skills.filter((s) => !onlyIds || onlyIds.includes(s.id));
|
|
@@ -732,6 +738,118 @@ async function syncSkillsToIdes(options) {
|
|
|
732
738
|
}
|
|
733
739
|
return { resources: out, count: out.length };
|
|
734
740
|
}
|
|
741
|
+
async function migrateLegacySkillsSourceDir(projectRoot) {
|
|
742
|
+
const legacyDir = getLegacySkillsSourceDir(projectRoot);
|
|
743
|
+
const newDir = getSkillsSourceDir(projectRoot);
|
|
744
|
+
let legacyExists = false;
|
|
745
|
+
let newExists = false;
|
|
746
|
+
try {
|
|
747
|
+
legacyExists = (await fs5.stat(legacyDir)).isDirectory();
|
|
748
|
+
} catch {
|
|
749
|
+
legacyExists = false;
|
|
750
|
+
}
|
|
751
|
+
try {
|
|
752
|
+
newExists = (await fs5.stat(newDir)).isDirectory();
|
|
753
|
+
} catch {
|
|
754
|
+
newExists = false;
|
|
755
|
+
}
|
|
756
|
+
if (!legacyExists) return;
|
|
757
|
+
if (newExists) {
|
|
758
|
+
logger.warn(
|
|
759
|
+
`Detected stale legacy skills source dir at ${legacyDir} alongside ${newDir}; the new layout takes precedence \u2014 you can safely delete the legacy dir.`
|
|
760
|
+
);
|
|
761
|
+
return;
|
|
762
|
+
}
|
|
763
|
+
try {
|
|
764
|
+
await fs5.rename(legacyDir, newDir);
|
|
765
|
+
logger.info(
|
|
766
|
+
`Migrated skills source dir: \`.teamix-evo/${LEGACY_SKILLS_DIR}/\` \u2192 \`.teamix-evo/skills-source/\``
|
|
767
|
+
);
|
|
768
|
+
} catch (err) {
|
|
769
|
+
logger.warn(
|
|
770
|
+
`Failed to rename legacy skills source dir (${getErrorMessage(
|
|
771
|
+
err
|
|
772
|
+
)}); leaving as-is. New skills will install under the new layout.`
|
|
773
|
+
);
|
|
774
|
+
return;
|
|
775
|
+
}
|
|
776
|
+
try {
|
|
777
|
+
const manifest = await readInstalledManifest(projectRoot);
|
|
778
|
+
if (!manifest) return;
|
|
779
|
+
const legacyFragmentPosix = `/.teamix-evo/${LEGACY_SKILLS_DIR}/`;
|
|
780
|
+
const newFragmentPosix = `/.teamix-evo/skills-source/`;
|
|
781
|
+
const legacyFragmentNative = `${path7.sep}.teamix-evo${path7.sep}${LEGACY_SKILLS_DIR}${path7.sep}`;
|
|
782
|
+
const newFragmentNative = `${path7.sep}.teamix-evo${path7.sep}skills-source${path7.sep}`;
|
|
783
|
+
let touched = 0;
|
|
784
|
+
for (const pkg of manifest.installed) {
|
|
785
|
+
for (const r of pkg.resources) {
|
|
786
|
+
if (typeof r.target !== "string") continue;
|
|
787
|
+
const before = r.target;
|
|
788
|
+
let after = before.replace(legacyFragmentPosix, newFragmentPosix);
|
|
789
|
+
after = after.replace(legacyFragmentNative, newFragmentNative);
|
|
790
|
+
if (after !== before) {
|
|
791
|
+
r.target = after;
|
|
792
|
+
touched += 1;
|
|
793
|
+
}
|
|
794
|
+
}
|
|
795
|
+
}
|
|
796
|
+
if (touched > 0) {
|
|
797
|
+
await writeInstalledManifest(projectRoot, manifest);
|
|
798
|
+
logger.debug(
|
|
799
|
+
`Rewrote ${touched} manifest target(s) to the new skills-source path.`
|
|
800
|
+
);
|
|
801
|
+
}
|
|
802
|
+
} catch (err) {
|
|
803
|
+
logger.warn(
|
|
804
|
+
`Migrated skills source dir but failed to update manifest paths (${getErrorMessage(
|
|
805
|
+
err
|
|
806
|
+
)}); manifest may still reference legacy paths.`
|
|
807
|
+
);
|
|
808
|
+
}
|
|
809
|
+
}
|
|
810
|
+
async function pruneEmptyIdeSkillDirs(args) {
|
|
811
|
+
const removed = [];
|
|
812
|
+
for (const ide of args.ides) {
|
|
813
|
+
const adapter = getAdapter(ide);
|
|
814
|
+
const placeholderDir = adapter.getSkillTargetDir(
|
|
815
|
+
"__placeholder__",
|
|
816
|
+
args.scope,
|
|
817
|
+
args.projectRoot
|
|
818
|
+
);
|
|
819
|
+
const skillsRoot = path7.dirname(placeholderDir);
|
|
820
|
+
let entries;
|
|
821
|
+
try {
|
|
822
|
+
entries = await fs5.readdir(skillsRoot);
|
|
823
|
+
} catch {
|
|
824
|
+
continue;
|
|
825
|
+
}
|
|
826
|
+
for (const name of entries) {
|
|
827
|
+
const dir = path7.join(skillsRoot, name);
|
|
828
|
+
let stat7;
|
|
829
|
+
try {
|
|
830
|
+
stat7 = await fs5.stat(dir);
|
|
831
|
+
} catch {
|
|
832
|
+
continue;
|
|
833
|
+
}
|
|
834
|
+
if (!stat7.isDirectory()) continue;
|
|
835
|
+
let children;
|
|
836
|
+
try {
|
|
837
|
+
children = await fs5.readdir(dir);
|
|
838
|
+
} catch {
|
|
839
|
+
continue;
|
|
840
|
+
}
|
|
841
|
+
if (children.some((c) => c === "SKILL.md")) continue;
|
|
842
|
+
if (children.length !== 0) continue;
|
|
843
|
+
try {
|
|
844
|
+
await fs5.rmdir(dir);
|
|
845
|
+
removed.push(dir);
|
|
846
|
+
logger.debug(`Pruned empty IDE skill dir: ${dir}`);
|
|
847
|
+
} catch {
|
|
848
|
+
}
|
|
849
|
+
}
|
|
850
|
+
}
|
|
851
|
+
return removed;
|
|
852
|
+
}
|
|
735
853
|
async function removeSkillFiles(records) {
|
|
736
854
|
const removed = [];
|
|
737
855
|
for (const r of records) {
|
|
@@ -827,13 +945,29 @@ async function runSkillsInit(options) {
|
|
|
827
945
|
}
|
|
828
946
|
return true;
|
|
829
947
|
}).map((s) => s.id);
|
|
830
|
-
const skippedSkillIds =
|
|
831
|
-
|
|
948
|
+
const { onlyIds, skippedSkillIds, outdatedSkills } = partitionByVersion(
|
|
949
|
+
candidateIds,
|
|
950
|
+
manifest,
|
|
951
|
+
existing
|
|
832
952
|
);
|
|
833
|
-
|
|
834
|
-
if (existingSkillsCfg && onlyIds.length === 0) {
|
|
953
|
+
if (existingSkillsCfg && onlyIds.length === 0 && outdatedSkills.length === 0) {
|
|
835
954
|
return { status: "already-initialized" };
|
|
836
955
|
}
|
|
956
|
+
if (onlyIds.length === 0) {
|
|
957
|
+
return {
|
|
958
|
+
status: "installed",
|
|
959
|
+
packageName,
|
|
960
|
+
version: existingSkillsCfg?.version ?? manifest.version,
|
|
961
|
+
ides,
|
|
962
|
+
scope,
|
|
963
|
+
skillCount: 0,
|
|
964
|
+
fileCount: 0,
|
|
965
|
+
resources: [],
|
|
966
|
+
addedSkillIds: [],
|
|
967
|
+
skippedSkillIds,
|
|
968
|
+
outdatedSkills
|
|
969
|
+
};
|
|
970
|
+
}
|
|
837
971
|
return finalizeSkillsInstall({
|
|
838
972
|
projectRoot,
|
|
839
973
|
packageName,
|
|
@@ -845,6 +979,7 @@ async function runSkillsInit(options) {
|
|
|
845
979
|
scope,
|
|
846
980
|
onlyIds,
|
|
847
981
|
skippedSkillIds,
|
|
982
|
+
outdatedSkills,
|
|
848
983
|
existing,
|
|
849
984
|
existingConfig
|
|
850
985
|
});
|
|
@@ -885,10 +1020,11 @@ async function runSkillsAdd(options) {
|
|
|
885
1020
|
}
|
|
886
1021
|
}
|
|
887
1022
|
const existing = await readExistingState(projectRoot, packageName);
|
|
888
|
-
const skippedSkillIds =
|
|
889
|
-
|
|
1023
|
+
const { onlyIds, skippedSkillIds, outdatedSkills } = partitionByVersion(
|
|
1024
|
+
requestedNames,
|
|
1025
|
+
manifest,
|
|
1026
|
+
existing
|
|
890
1027
|
);
|
|
891
|
-
const onlyIds = requestedNames.filter((n) => !existing.skillIds.has(n));
|
|
892
1028
|
if (onlyIds.length === 0) {
|
|
893
1029
|
return {
|
|
894
1030
|
status: "installed",
|
|
@@ -900,7 +1036,8 @@ async function runSkillsAdd(options) {
|
|
|
900
1036
|
fileCount: 0,
|
|
901
1037
|
resources: [],
|
|
902
1038
|
addedSkillIds: [],
|
|
903
|
-
skippedSkillIds
|
|
1039
|
+
skippedSkillIds,
|
|
1040
|
+
outdatedSkills
|
|
904
1041
|
};
|
|
905
1042
|
}
|
|
906
1043
|
return finalizeSkillsInstall({
|
|
@@ -914,10 +1051,52 @@ async function runSkillsAdd(options) {
|
|
|
914
1051
|
scope,
|
|
915
1052
|
onlyIds,
|
|
916
1053
|
skippedSkillIds,
|
|
1054
|
+
outdatedSkills,
|
|
917
1055
|
existing,
|
|
918
1056
|
existingConfig
|
|
919
1057
|
});
|
|
920
1058
|
}
|
|
1059
|
+
function partitionByVersion(ids, manifest, existing) {
|
|
1060
|
+
const manifestById = new Map(manifest.skills.map((s) => [s.id, s]));
|
|
1061
|
+
const onlyIds = [];
|
|
1062
|
+
const skippedSkillIds = [];
|
|
1063
|
+
const outdatedSkills = [];
|
|
1064
|
+
for (const name of ids) {
|
|
1065
|
+
if (!existing.skillIds.has(name)) {
|
|
1066
|
+
onlyIds.push(name);
|
|
1067
|
+
continue;
|
|
1068
|
+
}
|
|
1069
|
+
const installedVer = existing.lock?.skills?.[name]?.version;
|
|
1070
|
+
const latestVer = manifestById.get(name)?.version;
|
|
1071
|
+
if (installedVer && latestVer && compareSemver(installedVer, latestVer) < 0) {
|
|
1072
|
+
outdatedSkills.push({
|
|
1073
|
+
id: name,
|
|
1074
|
+
installed: installedVer,
|
|
1075
|
+
latest: latestVer
|
|
1076
|
+
});
|
|
1077
|
+
} else {
|
|
1078
|
+
skippedSkillIds.push(name);
|
|
1079
|
+
}
|
|
1080
|
+
}
|
|
1081
|
+
return { onlyIds, skippedSkillIds, outdatedSkills };
|
|
1082
|
+
}
|
|
1083
|
+
function parseSemverTriple(v) {
|
|
1084
|
+
const m = /^(\d+)\.(\d+)\.(\d+)/.exec(v);
|
|
1085
|
+
if (!m) return null;
|
|
1086
|
+
return [Number(m[1]), Number(m[2]), Number(m[3])];
|
|
1087
|
+
}
|
|
1088
|
+
function compareSemver(a, b) {
|
|
1089
|
+
const pa = parseSemverTriple(a);
|
|
1090
|
+
const pb = parseSemverTriple(b);
|
|
1091
|
+
if (!pa || !pb) {
|
|
1092
|
+
if (a === b) return 0;
|
|
1093
|
+
return a < b ? -1 : 1;
|
|
1094
|
+
}
|
|
1095
|
+
for (let i = 0; i < 3; i++) {
|
|
1096
|
+
if (pa[i] !== pb[i]) return pa[i] < pb[i] ? -1 : 1;
|
|
1097
|
+
}
|
|
1098
|
+
return 0;
|
|
1099
|
+
}
|
|
921
1100
|
async function readExistingState(projectRoot, packageName) {
|
|
922
1101
|
const installed = await readInstalledManifest(projectRoot);
|
|
923
1102
|
const pkg = installed?.installed.find((p2) => p2.package === packageName);
|
|
@@ -942,6 +1121,7 @@ async function finalizeSkillsInstall(args) {
|
|
|
942
1121
|
scope,
|
|
943
1122
|
onlyIds,
|
|
944
1123
|
skippedSkillIds,
|
|
1124
|
+
outdatedSkills,
|
|
945
1125
|
existing,
|
|
946
1126
|
existingConfig
|
|
947
1127
|
} = args;
|
|
@@ -1007,6 +1187,10 @@ async function finalizeSkillsInstall(args) {
|
|
|
1007
1187
|
}
|
|
1008
1188
|
await writeSkillsLock(projectRoot, lock);
|
|
1009
1189
|
await ensureMcpJson(projectRoot);
|
|
1190
|
+
try {
|
|
1191
|
+
await pruneEmptyIdeSkillDirs({ projectRoot, ides, scope });
|
|
1192
|
+
} catch {
|
|
1193
|
+
}
|
|
1010
1194
|
return {
|
|
1011
1195
|
status: "installed",
|
|
1012
1196
|
packageName,
|
|
@@ -1017,7 +1201,8 @@ async function finalizeSkillsInstall(args) {
|
|
|
1017
1201
|
fileCount: result.count,
|
|
1018
1202
|
resources: result.resources,
|
|
1019
1203
|
addedSkillIds: onlyIds,
|
|
1020
|
-
skippedSkillIds
|
|
1204
|
+
skippedSkillIds,
|
|
1205
|
+
outdatedSkills: outdatedSkills ?? []
|
|
1021
1206
|
};
|
|
1022
1207
|
}
|
|
1023
1208
|
function mergeInstalledResources(existing, next) {
|
|
@@ -1225,6 +1410,9 @@ async function installVariantFile(fileRelToPackage, packageRoot, projectRoot) {
|
|
|
1225
1410
|
const targetRel = path9.posix.join(CONSUMER_TOKENS_DIR, CONSUMER_THEME_FILE);
|
|
1226
1411
|
const targetAbs = path9.join(projectRoot, targetRel);
|
|
1227
1412
|
const content = await fs6.readFile(sourceAbs, "utf-8");
|
|
1413
|
+
if (await fileExists(targetAbs)) {
|
|
1414
|
+
await backupFile(targetAbs, projectRoot);
|
|
1415
|
+
}
|
|
1228
1416
|
await writeFileSafe(targetAbs, content);
|
|
1229
1417
|
return {
|
|
1230
1418
|
id: `tokens:${CONSUMER_THEME_FILE}`,
|
|
@@ -1434,10 +1622,10 @@ async function writeTokensUpgradeHint(options) {
|
|
|
1434
1622
|
}
|
|
1435
1623
|
function selectApplicableRenames(renames, fromVersion, toVersion) {
|
|
1436
1624
|
return renames.filter(
|
|
1437
|
-
(r) =>
|
|
1438
|
-
).sort((a, b) =>
|
|
1625
|
+
(r) => compareSemver2(r.sinceVersion, fromVersion) > 0 && compareSemver2(r.sinceVersion, toVersion) <= 0
|
|
1626
|
+
).sort((a, b) => compareSemver2(a.sinceVersion, b.sinceVersion));
|
|
1439
1627
|
}
|
|
1440
|
-
function
|
|
1628
|
+
function compareSemver2(a, b) {
|
|
1441
1629
|
const [aMain = "", aRest = ""] = a.split("-", 2);
|
|
1442
1630
|
const [bMain = "", bRest = ""] = b.split("-", 2);
|
|
1443
1631
|
const aParts = aMain.split(".").map((n) => Number.parseInt(n, 10));
|
|
@@ -1876,19 +2064,243 @@ var uninstallCommand = new Command5("uninstall").description(
|
|
|
1876
2064
|
}
|
|
1877
2065
|
});
|
|
1878
2066
|
|
|
2067
|
+
// src/commands/tokens/audit.ts
|
|
2068
|
+
import { Command as Command6 } from "commander";
|
|
2069
|
+
|
|
2070
|
+
// src/core/tokens-audit.ts
|
|
2071
|
+
import * as fs10 from "fs/promises";
|
|
2072
|
+
import * as path14 from "path";
|
|
2073
|
+
var THEME_REL = "tokens/tokens.theme.css";
|
|
2074
|
+
var OVERRIDES_REL = "tokens/tokens.overrides.css";
|
|
2075
|
+
async function runTokensAudit(options) {
|
|
2076
|
+
const themePath = path14.join(options.projectRoot, THEME_REL);
|
|
2077
|
+
const overridesPath = path14.join(options.projectRoot, OVERRIDES_REL);
|
|
2078
|
+
const themeSrc = await safeRead(themePath);
|
|
2079
|
+
if (themeSrc === null) {
|
|
2080
|
+
return { status: "no-theme", themePath, overridesPath };
|
|
2081
|
+
}
|
|
2082
|
+
const overridesSrc = await safeRead(overridesPath);
|
|
2083
|
+
if (overridesSrc === null) {
|
|
2084
|
+
return { status: "no-overrides", themePath, overridesPath };
|
|
2085
|
+
}
|
|
2086
|
+
const themeTokens = parseCssVariables(themeSrc);
|
|
2087
|
+
const overrideTokens = parseCssVariables(overridesSrc);
|
|
2088
|
+
const themeIndex = /* @__PURE__ */ new Map();
|
|
2089
|
+
for (const tok of themeTokens) themeIndex.set(tok.name, tok);
|
|
2090
|
+
const entries = [];
|
|
2091
|
+
for (const tok of overrideTokens) {
|
|
2092
|
+
entries.push(classifyOverride(tok, themeIndex));
|
|
2093
|
+
}
|
|
2094
|
+
const summary = {
|
|
2095
|
+
redundant: 0,
|
|
2096
|
+
kept: 0,
|
|
2097
|
+
migrate: 0,
|
|
2098
|
+
custom: 0
|
|
2099
|
+
};
|
|
2100
|
+
for (const entry of entries) summary[entry.category] += 1;
|
|
2101
|
+
return {
|
|
2102
|
+
status: "audited",
|
|
2103
|
+
themePath,
|
|
2104
|
+
overridesPath,
|
|
2105
|
+
totalOverrides: entries.length,
|
|
2106
|
+
entries,
|
|
2107
|
+
summary
|
|
2108
|
+
};
|
|
2109
|
+
}
|
|
2110
|
+
async function safeRead(filePath) {
|
|
2111
|
+
try {
|
|
2112
|
+
return await fs10.readFile(filePath, "utf-8");
|
|
2113
|
+
} catch {
|
|
2114
|
+
return null;
|
|
2115
|
+
}
|
|
2116
|
+
}
|
|
2117
|
+
function parseCssVariables(source) {
|
|
2118
|
+
const stripped = source.replace(
|
|
2119
|
+
/\/\*[\s\S]*?\*\//g,
|
|
2120
|
+
(m) => m.replace(/[^\n]/g, " ")
|
|
2121
|
+
);
|
|
2122
|
+
const out = [];
|
|
2123
|
+
const lines = stripped.split("\n");
|
|
2124
|
+
for (let i = 0; i < lines.length; i += 1) {
|
|
2125
|
+
const lineRaw = lines[i] ?? "";
|
|
2126
|
+
const match = /^\s*(--[A-Za-z0-9_-]+)\s*:\s*([^;]+?)\s*;/.exec(lineRaw);
|
|
2127
|
+
if (!match) continue;
|
|
2128
|
+
const name = match[1];
|
|
2129
|
+
const value = match[2];
|
|
2130
|
+
if (!name || !value) continue;
|
|
2131
|
+
out.push({ name, value: value.trim(), line: i + 1 });
|
|
2132
|
+
}
|
|
2133
|
+
return out;
|
|
2134
|
+
}
|
|
2135
|
+
function classifyOverride(override, themeIndex) {
|
|
2136
|
+
const direct = themeIndex.get(override.name);
|
|
2137
|
+
if (direct) {
|
|
2138
|
+
const equal = areValuesSemanticallyEqual(override.value, direct.value);
|
|
2139
|
+
return {
|
|
2140
|
+
name: override.name,
|
|
2141
|
+
value: override.value,
|
|
2142
|
+
line: override.line,
|
|
2143
|
+
category: equal ? "redundant" : "kept",
|
|
2144
|
+
themeName: direct.name,
|
|
2145
|
+
themeValue: direct.value,
|
|
2146
|
+
reason: equal ? `theme already declares ${direct.name} with an equivalent value \u2014 safe to delete` : `genuinely differs from theme value \u2014 kept as user override`
|
|
2147
|
+
};
|
|
2148
|
+
}
|
|
2149
|
+
if (!override.name.startsWith("--color-")) {
|
|
2150
|
+
const v4Name = `--color-${override.name.slice(2)}`;
|
|
2151
|
+
const v4Match = themeIndex.get(v4Name);
|
|
2152
|
+
if (v4Match) {
|
|
2153
|
+
const wrapped = wrapAsHslIfBare(override.value);
|
|
2154
|
+
const equal = areValuesSemanticallyEqual(wrapped, v4Match.value);
|
|
2155
|
+
return {
|
|
2156
|
+
name: override.name,
|
|
2157
|
+
value: override.value,
|
|
2158
|
+
line: override.line,
|
|
2159
|
+
category: "migrate",
|
|
2160
|
+
themeName: v4Match.name,
|
|
2161
|
+
themeValue: v4Match.value,
|
|
2162
|
+
compareCategory: equal ? "redundant" : "kept",
|
|
2163
|
+
reason: equal ? `legacy v3 shape; theme has ${v4Match.name} with equivalent value \u2014 rewrite as v4 (or delete)` : `legacy v3 shape; rewrite as v4 \`${v4Name}: hsl(...)\` and keep override`
|
|
2164
|
+
};
|
|
2165
|
+
}
|
|
2166
|
+
}
|
|
2167
|
+
return {
|
|
2168
|
+
name: override.name,
|
|
2169
|
+
value: override.value,
|
|
2170
|
+
line: override.line,
|
|
2171
|
+
category: "custom",
|
|
2172
|
+
reason: `no matching theme variable \u2014 project-specific extension`
|
|
2173
|
+
};
|
|
2174
|
+
}
|
|
2175
|
+
function areValuesSemanticallyEqual(a, b) {
|
|
2176
|
+
return normaliseCssValue(a) === normaliseCssValue(b);
|
|
2177
|
+
}
|
|
2178
|
+
function normaliseCssValue(value) {
|
|
2179
|
+
let v = value.trim().toLowerCase();
|
|
2180
|
+
v = v.replace(/\s+/g, " ");
|
|
2181
|
+
v = v.replace(/\s*([(),/])\s*/g, "$1");
|
|
2182
|
+
v = v.replace(/(\d+\.\d*?)0+(?=[^0-9]|$)/g, "$1").replace(/\.(?=[^0-9]|$)/g, "");
|
|
2183
|
+
return v;
|
|
2184
|
+
}
|
|
2185
|
+
function wrapAsHslIfBare(value) {
|
|
2186
|
+
const trimmed = value.trim();
|
|
2187
|
+
if (/^[a-z-]+\s*\(/i.test(trimmed)) return trimmed;
|
|
2188
|
+
const triplet = /^-?\d+(?:\.\d+)?\s+\d+(?:\.\d+)?%\s+\d+(?:\.\d+)?%(?:\s*\/\s*\S+)?$/;
|
|
2189
|
+
if (triplet.test(trimmed)) {
|
|
2190
|
+
return `hsl(${trimmed})`;
|
|
2191
|
+
}
|
|
2192
|
+
return trimmed;
|
|
2193
|
+
}
|
|
2194
|
+
function formatAuditEntry(entry) {
|
|
2195
|
+
const head = ` ${entry.name}: ${entry.value};`;
|
|
2196
|
+
switch (entry.category) {
|
|
2197
|
+
case "redundant":
|
|
2198
|
+
return `${head}
|
|
2199
|
+
\u2192 REDUNDANT \u2014 ${entry.reason}`;
|
|
2200
|
+
case "kept":
|
|
2201
|
+
return `${head}
|
|
2202
|
+
\u2192 KEEP \u2014 ${entry.reason}`;
|
|
2203
|
+
case "migrate": {
|
|
2204
|
+
const sub = entry.compareCategory ? ` (currently ${entry.compareCategory})` : "";
|
|
2205
|
+
return `${head}
|
|
2206
|
+
\u2192 MIGRATE${sub} \u2014 ${entry.reason}`;
|
|
2207
|
+
}
|
|
2208
|
+
case "custom":
|
|
2209
|
+
return `${head}
|
|
2210
|
+
\u2192 CUSTOM \u2014 ${entry.reason}`;
|
|
2211
|
+
}
|
|
2212
|
+
}
|
|
2213
|
+
function renderAuditSummary(result) {
|
|
2214
|
+
const lines = [];
|
|
2215
|
+
lines.push(`tokens audit \xB7 ${result.totalOverrides} override(s) inspected`);
|
|
2216
|
+
lines.push(
|
|
2217
|
+
` redundant=${result.summary.redundant} kept=${result.summary.kept} migrate=${result.summary.migrate} custom=${result.summary.custom}`
|
|
2218
|
+
);
|
|
2219
|
+
for (const cat of ["redundant", "migrate", "custom", "kept"]) {
|
|
2220
|
+
const subset = result.entries.filter((e) => e.category === cat);
|
|
2221
|
+
if (subset.length === 0) continue;
|
|
2222
|
+
lines.push("");
|
|
2223
|
+
lines.push(`[${cat.toUpperCase()}] (${subset.length})`);
|
|
2224
|
+
for (const entry of subset) lines.push(formatAuditEntry(entry));
|
|
2225
|
+
}
|
|
2226
|
+
return lines.join("\n");
|
|
2227
|
+
}
|
|
2228
|
+
|
|
2229
|
+
// src/commands/tokens/audit.ts
|
|
2230
|
+
var auditCommand = new Command6("audit").description(
|
|
2231
|
+
"\u5BA1\u8BA1 tokens/tokens.overrides.css\uFF1A\u4E0E\u53D8\u4F53 theme.css \u5BF9\u7167\uFF0C\u5206\u7C7B\u8F93\u51FA\u5197\u4F59/\u4FDD\u7559/\u8FC1\u79FB/\u9879\u76EE\u7279\u6709"
|
|
2232
|
+
).option("--json", "\u4EE5 JSON \u683C\u5F0F\u8F93\u51FA\u7ED3\u679C\uFF08\u4FBF\u4E8E skill \u6D88\u8D39\uFF09").option(
|
|
2233
|
+
"--filter <category>",
|
|
2234
|
+
"\u4EC5\u663E\u793A\u6307\u5B9A bucket\uFF08redundant|kept|migrate|custom\uFF09\uFF0C\u53EF\u9017\u53F7\u5206\u9694"
|
|
2235
|
+
).action(async (opts) => {
|
|
2236
|
+
try {
|
|
2237
|
+
const ide = detectIde();
|
|
2238
|
+
const projectRoot = ide.getProjectRoot();
|
|
2239
|
+
const result = await runTokensAudit({ projectRoot });
|
|
2240
|
+
if (result.status === "no-theme") {
|
|
2241
|
+
logger.error(
|
|
2242
|
+
`No tokens.theme.css found at ${result.themePath}. Run \`tokens init <variant>\` first.`
|
|
2243
|
+
);
|
|
2244
|
+
process.exitCode = 1;
|
|
2245
|
+
return;
|
|
2246
|
+
}
|
|
2247
|
+
if (result.status === "no-overrides") {
|
|
2248
|
+
logger.info(
|
|
2249
|
+
`No tokens.overrides.css at ${result.overridesPath}. Nothing to audit.`
|
|
2250
|
+
);
|
|
2251
|
+
return;
|
|
2252
|
+
}
|
|
2253
|
+
if (result.status !== "audited") {
|
|
2254
|
+
return;
|
|
2255
|
+
}
|
|
2256
|
+
const filterSet = parseFilter(opts.filter);
|
|
2257
|
+
const filtered = filterSet ? {
|
|
2258
|
+
...result,
|
|
2259
|
+
entries: result.entries.filter((e) => filterSet.has(e.category))
|
|
2260
|
+
} : result;
|
|
2261
|
+
if (opts.json) {
|
|
2262
|
+
process.stdout.write(JSON.stringify(filtered, null, 2) + "\n");
|
|
2263
|
+
return;
|
|
2264
|
+
}
|
|
2265
|
+
logger.info(renderAuditSummary(filtered));
|
|
2266
|
+
logger.info("");
|
|
2267
|
+
logger.info(
|
|
2268
|
+
"\u63D0\u793A\uFF1A\u6B64\u547D\u4EE4\u53EA\u8BFB\uFF0C\u4E0D\u4F1A\u4FEE\u6539 overrides.css\uFF1B\u6309\u4E0A\u9762\u7684 REDUNDANT / MIGRATE \u5206\u7C7B\u81EA\u884C\u88C1\u526A\u3002"
|
|
2269
|
+
);
|
|
2270
|
+
} catch (err) {
|
|
2271
|
+
logger.error(`Failed to audit tokens: ${getErrorMessage(err)}`);
|
|
2272
|
+
process.exitCode = 1;
|
|
2273
|
+
}
|
|
2274
|
+
});
|
|
2275
|
+
function parseFilter(raw) {
|
|
2276
|
+
if (!raw) return null;
|
|
2277
|
+
const valid = ["redundant", "kept", "migrate", "custom"];
|
|
2278
|
+
const out = /* @__PURE__ */ new Set();
|
|
2279
|
+
for (const part of raw.split(",").map((s) => s.trim().toLowerCase())) {
|
|
2280
|
+
if (!part) continue;
|
|
2281
|
+
if (valid.includes(part)) {
|
|
2282
|
+
out.add(part);
|
|
2283
|
+
}
|
|
2284
|
+
}
|
|
2285
|
+
return out.size > 0 ? out : null;
|
|
2286
|
+
}
|
|
2287
|
+
|
|
1879
2288
|
// src/commands/tokens/index.ts
|
|
1880
|
-
var tokensCommand = new
|
|
2289
|
+
var tokensCommand = new Command7("tokens").description(
|
|
2290
|
+
"\u7BA1\u7406 design tokens(\u53D8\u4F53\u7EA7 theme.css \u7B49)"
|
|
2291
|
+
);
|
|
1881
2292
|
tokensCommand.addCommand(initCommand);
|
|
1882
2293
|
tokensCommand.addCommand(updateCommand);
|
|
1883
2294
|
tokensCommand.addCommand(listCommand);
|
|
1884
2295
|
tokensCommand.addCommand(listVariantsCommand);
|
|
1885
2296
|
tokensCommand.addCommand(uninstallCommand);
|
|
2297
|
+
tokensCommand.addCommand(auditCommand);
|
|
1886
2298
|
|
|
1887
2299
|
// src/commands/skills/index.ts
|
|
1888
|
-
import { Command as
|
|
2300
|
+
import { Command as Command15 } from "commander";
|
|
1889
2301
|
|
|
1890
2302
|
// src/commands/skills/init.ts
|
|
1891
|
-
import { Command as
|
|
2303
|
+
import { Command as Command8 } from "commander";
|
|
1892
2304
|
import * as prompts2 from "@clack/prompts";
|
|
1893
2305
|
|
|
1894
2306
|
// src/utils/cancelled.ts
|
|
@@ -1921,7 +2333,7 @@ function parseScope(input) {
|
|
|
1921
2333
|
}
|
|
1922
2334
|
|
|
1923
2335
|
// src/commands/skills/init.ts
|
|
1924
|
-
var initCommand2 = new
|
|
2336
|
+
var initCommand2 = new Command8("init").description(
|
|
1925
2337
|
"\u81EA\u4E3E teamix-evo skills\uFF08\u6309 tokens variant + scope \u5168\u88C5\u7B26\u5408\u6761\u4EF6\u7684 skill\uFF1Bscope \u4E3A global-only \u7684 entry skill \u81EA\u52A8\u8DF3\u8FC7 \u2014 ADR 0033\uFF09"
|
|
1926
2338
|
).option("--ide <list>", '\u9017\u53F7\u5206\u9694\u7684 IDE \u5217\u8868\uFF0C\u5982 "qoder,claude"').option("--scope <scope>", "project | global\uFF08\u9ED8\u8BA4 project\uFF09").option("-y, --yes", "\u4F7F\u7528\u9ED8\u8BA4\u503C\uFF0C\u8DF3\u8FC7\u4EA4\u4E92").action(async (opts) => {
|
|
1927
2339
|
try {
|
|
@@ -1969,6 +2381,13 @@ var initCommand2 = new Command7("init").description(
|
|
|
1969
2381
|
);
|
|
1970
2382
|
}
|
|
1971
2383
|
logger.info(` Files: ${result.fileCount}`);
|
|
2384
|
+
if (result.outdatedSkills.length > 0) {
|
|
2385
|
+
const outdatedDesc = result.outdatedSkills.map((o) => `${o.id}@${o.installed} \u2192 ${o.latest}`).join(", ");
|
|
2386
|
+
const outdatedIds = result.outdatedSkills.map((o) => o.id).join(" ");
|
|
2387
|
+
logger.warn(
|
|
2388
|
+
`\u90E8\u5206\u6280\u80FD\u5DF2\u88C5\u4F46\u6709\u53EF\u7528\u66F4\u65B0\uFF1A${outdatedDesc}\u3002\u8FD0\u884C "npx teamix-evo@latest skills update ${outdatedIds}" \u5347\u7EA7\u3002`
|
|
2389
|
+
);
|
|
2390
|
+
}
|
|
1972
2391
|
logger.info("");
|
|
1973
2392
|
logger.info(
|
|
1974
2393
|
'Run "npx teamix-evo@latest skills list" to see installed skills.'
|
|
@@ -2021,9 +2440,9 @@ async function resolveIdesAndScope(args) {
|
|
|
2021
2440
|
}
|
|
2022
2441
|
|
|
2023
2442
|
// src/commands/skills/add.ts
|
|
2024
|
-
import { Command as
|
|
2443
|
+
import { Command as Command9 } from "commander";
|
|
2025
2444
|
import * as prompts3 from "@clack/prompts";
|
|
2026
|
-
var addCommand = new
|
|
2445
|
+
var addCommand = new Command9("add").description(
|
|
2027
2446
|
"\u589E\u91CF\u6DFB\u52A0 teamix-evo skills\uFF08\u5FC5\u987B\u6307\u5B9A\u81F3\u5C11\u4E00\u4E2A skill id\uFF1B\u81EA\u4E3E\u8BF7\u7528 `skills init`\uFF09"
|
|
2028
2447
|
).argument("<names...>", "\u81F3\u5C11\u4E00\u4E2A skill id\uFF08\u589E\u91CF\u88C5\uFF09").option("--ide <list>", '\u9017\u53F7\u5206\u9694\u7684 IDE \u5217\u8868\uFF0C\u5982 "qoder,claude"').option(
|
|
2029
2448
|
"--scope <scope>",
|
|
@@ -2060,26 +2479,45 @@ var addCommand = new Command8("add").description(
|
|
|
2060
2479
|
ide: ide.name,
|
|
2061
2480
|
names
|
|
2062
2481
|
});
|
|
2063
|
-
|
|
2064
|
-
|
|
2065
|
-
|
|
2066
|
-
|
|
2067
|
-
|
|
2068
|
-
|
|
2482
|
+
const hasAdded = result.addedSkillIds.length > 0;
|
|
2483
|
+
const hasSkipped = result.skippedSkillIds.length > 0;
|
|
2484
|
+
const hasOutdated = result.outdatedSkills.length > 0;
|
|
2485
|
+
const outdatedDesc = result.outdatedSkills.map((o) => `${o.id}@${o.installed} \u2192 ${o.latest}`).join(", ");
|
|
2486
|
+
const outdatedIds = result.outdatedSkills.map((o) => o.id).join(" ");
|
|
2487
|
+
if (!hasAdded) {
|
|
2488
|
+
if (hasOutdated) {
|
|
2489
|
+
logger.warn(
|
|
2490
|
+
`\u5DF2\u5B89\u88C5\u4F46\u6709\u53EF\u7528\u66F4\u65B0\uFF1A${outdatedDesc}\u3002\u8FD0\u884C "npx teamix-evo@latest skills update ${outdatedIds}" \u5347\u7EA7\u3002`
|
|
2491
|
+
);
|
|
2492
|
+
}
|
|
2493
|
+
if (hasSkipped) {
|
|
2494
|
+
if (hasOutdated) {
|
|
2495
|
+
logger.info(`\u5176\u4F59\u5DF2\u662F\u6700\u65B0\uFF1A${result.skippedSkillIds.join(", ")}`);
|
|
2496
|
+
} else {
|
|
2497
|
+
logger.warn(
|
|
2498
|
+
`\u5DF2\u5B58\u5728\uFF0C\u65E0\u9700\u6DFB\u52A0\uFF1A${result.skippedSkillIds.join(
|
|
2499
|
+
", "
|
|
2500
|
+
)}\u3002\u5982\u9700\u5237\u65B0\u5185\u5BB9\u8BF7\u8FD0\u884C "npx teamix-evo@latest skills update"\u3002`
|
|
2501
|
+
);
|
|
2502
|
+
}
|
|
2503
|
+
}
|
|
2069
2504
|
return;
|
|
2070
2505
|
}
|
|
2071
2506
|
logger.success(`Skills added: ${result.skillCount} skill(s)`);
|
|
2072
2507
|
logger.info(` IDEs: ${result.ides.join(", ")}`);
|
|
2073
2508
|
logger.info(` Scope: ${result.scope}`);
|
|
2074
|
-
|
|
2075
|
-
|
|
2076
|
-
}
|
|
2077
|
-
if (result.skippedSkillIds.length > 0) {
|
|
2509
|
+
logger.info(` Added: ${result.addedSkillIds.join(", ")}`);
|
|
2510
|
+
if (hasSkipped) {
|
|
2078
2511
|
logger.info(
|
|
2079
2512
|
` Skipped: ${result.skippedSkillIds.join(", ")} (already added)`
|
|
2080
2513
|
);
|
|
2081
2514
|
}
|
|
2082
2515
|
logger.info(` Files: ${result.fileCount}`);
|
|
2516
|
+
if (hasOutdated) {
|
|
2517
|
+
logger.warn(
|
|
2518
|
+
`\u90E8\u5206\u6280\u80FD\u5DF2\u88C5\u4F46\u6709\u53EF\u7528\u66F4\u65B0\uFF1A${outdatedDesc}\u3002\u8FD0\u884C "npx teamix-evo@latest skills update ${outdatedIds}" \u5347\u7EA7\u3002`
|
|
2519
|
+
);
|
|
2520
|
+
}
|
|
2083
2521
|
logger.info("");
|
|
2084
2522
|
logger.info(
|
|
2085
2523
|
'Run "npx teamix-evo@latest skills list" to see installed skills.'
|
|
@@ -2144,9 +2582,9 @@ async function resolveIdesAndScope2(args) {
|
|
|
2144
2582
|
}
|
|
2145
2583
|
|
|
2146
2584
|
// src/commands/skills/list.ts
|
|
2147
|
-
import { Command as
|
|
2585
|
+
import { Command as Command10 } from "commander";
|
|
2148
2586
|
var SKILLS_PACKAGE = "@teamix-evo/skills";
|
|
2149
|
-
var listCommand2 = new
|
|
2587
|
+
var listCommand2 = new Command10("list").alias("ls").description(
|
|
2150
2588
|
"\u5217\u51FA teamix-evo skills\uFF08\u9ED8\u8BA4\u5C55\u793A\u5168\u90E8 skill \u5E76\u6807\u6CE8\u5DF2\u88C5/\u672A\u88C5\uFF1B--installed \u4EC5\u770B\u5DF2\u88C5\uFF09"
|
|
2151
2589
|
).option("--installed", "\u4EC5\u5C55\u793A\u5DF2\u5B89\u88C5\u7684 skill\uFF08\u9690\u85CF\u672A\u5B89\u88C5\u9879\uFF09").action(async (opts) => {
|
|
2152
2590
|
try {
|
|
@@ -2242,7 +2680,7 @@ function printInstalledHeader(cfg, installedAt) {
|
|
|
2242
2680
|
}
|
|
2243
2681
|
|
|
2244
2682
|
// src/commands/skills/update.ts
|
|
2245
|
-
import { Command as
|
|
2683
|
+
import { Command as Command11 } from "commander";
|
|
2246
2684
|
import { createRequire as createRequire3 } from "module";
|
|
2247
2685
|
|
|
2248
2686
|
// src/core/skills-update.ts
|
|
@@ -2418,7 +2856,7 @@ async function runSkillsUpdate(options) {
|
|
|
2418
2856
|
// src/commands/skills/update.ts
|
|
2419
2857
|
var require4 = createRequire3(import.meta.url);
|
|
2420
2858
|
var SKILLS_PACKAGE2 = "@teamix-evo/skills";
|
|
2421
|
-
var updateCommand2 = new
|
|
2859
|
+
var updateCommand2 = new Command11("update").description(
|
|
2422
2860
|
"\u66F4\u65B0\u5DF2\u5B89\u88C5\u7684 teamix-evo skills\uFF08\u4EC5\u5347\u7EA7 lock \u5DF2\u8BB0\u5F55\u4E14 scope \u5339\u914D\u7684 skill \u2014 ADR 0035\uFF09"
|
|
2423
2861
|
).argument("[names...]", "\u53EF\u9009\uFF1A\u4EC5\u5347\u7EA7\u6307\u5B9A skill id\uFF1B\u7701\u7565\u5219\u5347\u7EA7\u5168\u90E8\u5DF2\u88C5").option("--dry-run", "\u9884\u89C8\u53D8\u66F4\uFF0C\u4E0D\u5199\u76D8").option(
|
|
2424
2862
|
"--scope <scope>",
|
|
@@ -2541,12 +2979,12 @@ async function printVersionBanner() {
|
|
|
2541
2979
|
}
|
|
2542
2980
|
|
|
2543
2981
|
// src/commands/skills/uninstall.ts
|
|
2544
|
-
import { Command as
|
|
2982
|
+
import { Command as Command12 } from "commander";
|
|
2545
2983
|
import * as prompts4 from "@clack/prompts";
|
|
2546
|
-
import * as
|
|
2547
|
-
import * as
|
|
2984
|
+
import * as path15 from "path";
|
|
2985
|
+
import * as fs11 from "fs/promises";
|
|
2548
2986
|
var SKILLS_PACKAGE3 = "@teamix-evo/skills";
|
|
2549
|
-
var uninstallCommand2 = new
|
|
2987
|
+
var uninstallCommand2 = new Command12("uninstall").description(
|
|
2550
2988
|
"\u5378\u8F7D\u5DF2\u5B89\u88C5\u7684 teamix-evo skills\uFF1B\u4E0D\u4F20 ids \u5219\u5378\u8F7D\u6574\u5305\uFF0C\u4F20 ids \u5219\u6309 skill \u5220\u9664"
|
|
2551
2989
|
).argument("[ids...]", "\u53EF\u9009\uFF1A\u4EC5\u5378\u8F7D\u6307\u5B9A skill id\uFF1B\u7701\u7565\u5219\u5378\u8F7D\u6574\u4E2A skills \u5305").option("-y, --yes", "\u8DF3\u8FC7\u786E\u8BA4").action(async (ids, opts) => {
|
|
2552
2990
|
try {
|
|
@@ -2611,7 +3049,7 @@ async function runFullUninstall(args) {
|
|
|
2611
3049
|
const skillsRoot = getSkillsSourceDir(projectRoot);
|
|
2612
3050
|
const sourceSkillNames = await listSkillSourceNames(skillsRoot);
|
|
2613
3051
|
try {
|
|
2614
|
-
await
|
|
3052
|
+
await fs11.rm(skillsRoot, { recursive: true, force: true });
|
|
2615
3053
|
logger.debug(`Removed source dir ${skillsRoot}`);
|
|
2616
3054
|
} catch (err) {
|
|
2617
3055
|
logger.warn(`Failed to remove ${skillsRoot}: ${getErrorMessage(err)}`);
|
|
@@ -2637,10 +3075,10 @@ async function runFullUninstall(args) {
|
|
|
2637
3075
|
await writeProjectConfig(projectRoot, config);
|
|
2638
3076
|
logger.success(`Uninstalled ${SKILLS_PACKAGE3}`);
|
|
2639
3077
|
logger.info(` Removed: ${removed.length} files`);
|
|
2640
|
-
logger.info(` Source: ${
|
|
3078
|
+
logger.info(` Source: ${path15.relative(projectRoot, skillsRoot)} (cleaned)`);
|
|
2641
3079
|
if (cleanedMirrorDirs.length > 0) {
|
|
2642
3080
|
logger.info(
|
|
2643
|
-
` Mirrors: ${cleanedMirrorDirs.map((d) =>
|
|
3081
|
+
` Mirrors: ${cleanedMirrorDirs.map((d) => path15.relative(projectRoot, d)).join(", ")} (cleaned)`
|
|
2644
3082
|
);
|
|
2645
3083
|
}
|
|
2646
3084
|
}
|
|
@@ -2678,7 +3116,7 @@ async function runPartialUninstall(args) {
|
|
|
2678
3116
|
for (const id of matched) {
|
|
2679
3117
|
const dir = getSkillsSourceDir(projectRoot, id);
|
|
2680
3118
|
try {
|
|
2681
|
-
await
|
|
3119
|
+
await fs11.rm(dir, { recursive: true, force: true });
|
|
2682
3120
|
logger.debug(`Removed source dir ${dir}`);
|
|
2683
3121
|
} catch (err) {
|
|
2684
3122
|
logger.warn(`Failed to remove ${dir}: ${getErrorMessage(err)}`);
|
|
@@ -2704,7 +3142,7 @@ async function runPartialUninstall(args) {
|
|
|
2704
3142
|
logger.info(` Files: ${removed.length}`);
|
|
2705
3143
|
if (cleanedMirrorDirs.length > 0) {
|
|
2706
3144
|
logger.info(
|
|
2707
|
-
` Mirrors: ${cleanedMirrorDirs.map((d) =>
|
|
3145
|
+
` Mirrors: ${cleanedMirrorDirs.map((d) => path15.relative(projectRoot, d)).join(", ")} (cleaned)`
|
|
2708
3146
|
);
|
|
2709
3147
|
}
|
|
2710
3148
|
if (missing.length > 0) {
|
|
@@ -2713,7 +3151,7 @@ async function runPartialUninstall(args) {
|
|
|
2713
3151
|
}
|
|
2714
3152
|
async function listSkillSourceNames(skillsRoot) {
|
|
2715
3153
|
try {
|
|
2716
|
-
const entries = await
|
|
3154
|
+
const entries = await fs11.readdir(skillsRoot, { withFileTypes: true });
|
|
2717
3155
|
return entries.filter((e) => e.isDirectory()).map((e) => e.name);
|
|
2718
3156
|
} catch {
|
|
2719
3157
|
return [];
|
|
@@ -2737,13 +3175,13 @@ async function removeMirrorDirs(projectRoot, scope, ides, skillNames) {
|
|
|
2737
3175
|
const dir = adapter.getSkillTargetDir(name, targetScope, projectRoot);
|
|
2738
3176
|
let existed = true;
|
|
2739
3177
|
try {
|
|
2740
|
-
await
|
|
3178
|
+
await fs11.access(dir);
|
|
2741
3179
|
} catch {
|
|
2742
3180
|
existed = false;
|
|
2743
3181
|
}
|
|
2744
3182
|
if (!existed) continue;
|
|
2745
3183
|
try {
|
|
2746
|
-
await
|
|
3184
|
+
await fs11.rm(dir, { recursive: true, force: true });
|
|
2747
3185
|
removed.push(dir);
|
|
2748
3186
|
logger.debug(`Removed mirror dir ${dir}`);
|
|
2749
3187
|
} catch (err) {
|
|
@@ -2751,13 +3189,13 @@ async function removeMirrorDirs(projectRoot, scope, ides, skillNames) {
|
|
|
2751
3189
|
}
|
|
2752
3190
|
}
|
|
2753
3191
|
if (removed.length > 0) {
|
|
2754
|
-
const skillsParent =
|
|
3192
|
+
const skillsParent = path15.dirname(
|
|
2755
3193
|
adapter.getSkillTargetDir("placeholder", targetScope, projectRoot)
|
|
2756
3194
|
);
|
|
2757
3195
|
try {
|
|
2758
|
-
const remaining = await
|
|
3196
|
+
const remaining = await fs11.readdir(skillsParent);
|
|
2759
3197
|
if (remaining.length === 0) {
|
|
2760
|
-
await
|
|
3198
|
+
await fs11.rmdir(skillsParent);
|
|
2761
3199
|
}
|
|
2762
3200
|
} catch {
|
|
2763
3201
|
}
|
|
@@ -2783,18 +3221,18 @@ function dedupe(values) {
|
|
|
2783
3221
|
}
|
|
2784
3222
|
|
|
2785
3223
|
// src/commands/skills/sync.ts
|
|
2786
|
-
import { Command as
|
|
3224
|
+
import { Command as Command13 } from "commander";
|
|
2787
3225
|
|
|
2788
3226
|
// src/core/skills-sync.ts
|
|
2789
|
-
import * as
|
|
2790
|
-
import * as
|
|
3227
|
+
import * as path16 from "path";
|
|
3228
|
+
import * as fs12 from "fs/promises";
|
|
2791
3229
|
import { createRequire as createRequire4 } from "module";
|
|
2792
3230
|
import { loadSkillsPackageManifest as loadSkillsPackageManifest2 } from "@teamix-evo/registry";
|
|
2793
3231
|
var require5 = createRequire4(import.meta.url);
|
|
2794
3232
|
async function readSkillMetaFromUpstream(skillId) {
|
|
2795
3233
|
try {
|
|
2796
3234
|
const pkgJson = require5.resolve("@teamix-evo/skills/package.json");
|
|
2797
|
-
const packageRoot =
|
|
3235
|
+
const packageRoot = path16.dirname(pkgJson);
|
|
2798
3236
|
const manifest = await loadSkillsPackageManifest2(packageRoot);
|
|
2799
3237
|
const entry = manifest.skills.find((s) => s.id === skillId);
|
|
2800
3238
|
if (!entry) return null;
|
|
@@ -2874,7 +3312,7 @@ async function runSkillsSync(options) {
|
|
|
2874
3312
|
}
|
|
2875
3313
|
async function dirExists(p2) {
|
|
2876
3314
|
try {
|
|
2877
|
-
const stat7 = await
|
|
3315
|
+
const stat7 = await fs12.stat(p2);
|
|
2878
3316
|
return stat7.isDirectory();
|
|
2879
3317
|
} catch {
|
|
2880
3318
|
return false;
|
|
@@ -2892,8 +3330,8 @@ async function refreshMirrorRecords(projectRoot, newMirrorRecords) {
|
|
|
2892
3330
|
}
|
|
2893
3331
|
|
|
2894
3332
|
// src/commands/skills/sync.ts
|
|
2895
|
-
var syncCommand = new
|
|
2896
|
-
"\u628A .teamix-evo/skills/ \u4E0B\u7684\u6E90\u91CD\u65B0\u955C\u50CF\u5230 IDE \u8DEF\u5F84\uFF08.qoder / .claude\uFF09"
|
|
3333
|
+
var syncCommand = new Command13("sync").description(
|
|
3334
|
+
"\u628A .teamix-evo/skills-source/ \u4E0B\u7684\u6E90\u91CD\u65B0\u955C\u50CF\u5230 IDE \u8DEF\u5F84\uFF08.qoder / .claude\uFF09"
|
|
2897
3335
|
).argument(
|
|
2898
3336
|
"[names...]",
|
|
2899
3337
|
"\u53EF\u9009\uFF1A\u4EC5\u540C\u6B65\u6307\u5B9A skill id\uFF1B\u7701\u7565\u5219\u540C\u6B65\u5168\u90E8\u5DF2\u8BB0\u5F55\u5728 lock \u5185\u7684 skill"
|
|
@@ -2918,7 +3356,7 @@ var syncCommand = new Command12("sync").description(
|
|
|
2918
3356
|
});
|
|
2919
3357
|
if (result.status === "no-skills") {
|
|
2920
3358
|
logger.info(
|
|
2921
|
-
"No skills recorded in .teamix-evo/skills/manifest.lock.json. Nothing to sync."
|
|
3359
|
+
"No skills recorded in .teamix-evo/skills-source/manifest.lock.json. Nothing to sync."
|
|
2922
3360
|
);
|
|
2923
3361
|
return;
|
|
2924
3362
|
}
|
|
@@ -2943,11 +3381,11 @@ var syncCommand = new Command12("sync").description(
|
|
|
2943
3381
|
});
|
|
2944
3382
|
|
|
2945
3383
|
// src/commands/skills/doctor.ts
|
|
2946
|
-
import { Command as
|
|
3384
|
+
import { Command as Command14 } from "commander";
|
|
2947
3385
|
|
|
2948
3386
|
// src/core/skills-doctor.ts
|
|
2949
|
-
import * as
|
|
2950
|
-
import * as
|
|
3387
|
+
import * as path17 from "path";
|
|
3388
|
+
import * as fs13 from "fs/promises";
|
|
2951
3389
|
async function runSkillsDoctor(options) {
|
|
2952
3390
|
const { projectRoot } = options;
|
|
2953
3391
|
const lock = await readSkillsLock(projectRoot);
|
|
@@ -2969,8 +3407,8 @@ async function runSkillsDoctor(options) {
|
|
|
2969
3407
|
const sourceFiles = await walkDir(sourceDir);
|
|
2970
3408
|
const sourceContents = /* @__PURE__ */ new Map();
|
|
2971
3409
|
for (const f of sourceFiles) {
|
|
2972
|
-
const rel2 =
|
|
2973
|
-
sourceContents.set(rel2, await
|
|
3410
|
+
const rel2 = path17.relative(sourceDir, f);
|
|
3411
|
+
sourceContents.set(rel2, await fs13.readFile(f, "utf-8"));
|
|
2974
3412
|
}
|
|
2975
3413
|
for (const ide of entry.mirroredTo) {
|
|
2976
3414
|
const adapter = getAdapter(ide);
|
|
@@ -2991,7 +3429,7 @@ async function runSkillsDoctor(options) {
|
|
|
2991
3429
|
continue;
|
|
2992
3430
|
}
|
|
2993
3431
|
for (const [rel2, sourceContent] of sourceContents.entries()) {
|
|
2994
|
-
const mirrorFile =
|
|
3432
|
+
const mirrorFile = path17.join(mirrorDir, rel2);
|
|
2995
3433
|
if (!await fileExists(mirrorFile)) {
|
|
2996
3434
|
findings.push({
|
|
2997
3435
|
kind: "missing-mirror",
|
|
@@ -3003,7 +3441,7 @@ async function runSkillsDoctor(options) {
|
|
|
3003
3441
|
});
|
|
3004
3442
|
continue;
|
|
3005
3443
|
}
|
|
3006
|
-
const mirrorContent = await
|
|
3444
|
+
const mirrorContent = await fs13.readFile(mirrorFile, "utf-8");
|
|
3007
3445
|
if (computeHash(mirrorContent) !== computeHash(sourceContent)) {
|
|
3008
3446
|
findings.push({
|
|
3009
3447
|
kind: "mirror-drift",
|
|
@@ -3024,7 +3462,7 @@ async function runSkillsDoctor(options) {
|
|
|
3024
3462
|
}
|
|
3025
3463
|
async function dirExists2(p2) {
|
|
3026
3464
|
try {
|
|
3027
|
-
const stat7 = await
|
|
3465
|
+
const stat7 = await fs13.stat(p2);
|
|
3028
3466
|
return stat7.isDirectory();
|
|
3029
3467
|
} catch {
|
|
3030
3468
|
return false;
|
|
@@ -3032,7 +3470,9 @@ async function dirExists2(p2) {
|
|
|
3032
3470
|
}
|
|
3033
3471
|
|
|
3034
3472
|
// src/commands/skills/doctor.ts
|
|
3035
|
-
var doctorCommand = new
|
|
3473
|
+
var doctorCommand = new Command14("doctor").description(
|
|
3474
|
+
"\u68C0\u67E5 .teamix-evo/skills-source/ \u6E90\u4E0E IDE \u955C\u50CF\u662F\u5426\u6F02\u79FB\uFF1B\u63D0\u793A\u5982\u4F55\u4FEE\u590D"
|
|
3475
|
+
).action(async () => {
|
|
3036
3476
|
try {
|
|
3037
3477
|
const ide = detectIde();
|
|
3038
3478
|
const cwd = ide.getProjectRoot();
|
|
@@ -3069,7 +3509,7 @@ var doctorCommand = new Command13("doctor").description("\u68C0\u67E5 .teamix-ev
|
|
|
3069
3509
|
});
|
|
3070
3510
|
|
|
3071
3511
|
// src/commands/skills/index.ts
|
|
3072
|
-
var skillsCommand = new
|
|
3512
|
+
var skillsCommand = new Command15("skills").description(
|
|
3073
3513
|
"\u7BA1\u7406 teamix-evo skills\uFF08\u5411 AI IDE \u6CE8\u5165\u6280\u80FD\uFF1Bsource-mirror \u6A21\u578B\u89C1 ADR 0013\uFF09"
|
|
3074
3514
|
);
|
|
3075
3515
|
skillsCommand.addCommand(initCommand2);
|
|
@@ -3081,10 +3521,10 @@ skillsCommand.addCommand(doctorCommand);
|
|
|
3081
3521
|
skillsCommand.addCommand(uninstallCommand2);
|
|
3082
3522
|
|
|
3083
3523
|
// src/commands/ui/index.ts
|
|
3084
|
-
import { Command as
|
|
3524
|
+
import { Command as Command21 } from "commander";
|
|
3085
3525
|
|
|
3086
3526
|
// src/commands/ui/init.ts
|
|
3087
|
-
import { Command as
|
|
3527
|
+
import { Command as Command16 } from "commander";
|
|
3088
3528
|
import * as prompts5 from "@clack/prompts";
|
|
3089
3529
|
|
|
3090
3530
|
// src/core/ui-init.ts
|
|
@@ -3142,7 +3582,7 @@ async function runUiInit(options) {
|
|
|
3142
3582
|
}
|
|
3143
3583
|
|
|
3144
3584
|
// src/commands/ui/init.ts
|
|
3145
|
-
var initCommand3 = new
|
|
3585
|
+
var initCommand3 = new Command16("init").description(
|
|
3146
3586
|
"\u521D\u59CB\u5316 teamix-evo ui \u914D\u7F6E\uFF08\u8BE2\u95EE aliases / iconLibrary / tsx / rsc\uFF09"
|
|
3147
3587
|
).option("-y, --yes", "\u4F7F\u7528\u9ED8\u8BA4\u503C\uFF0C\u8DF3\u8FC7\u4EA4\u4E92").option(
|
|
3148
3588
|
"--components <path>",
|
|
@@ -3261,26 +3701,26 @@ async function resolveConfig(opts) {
|
|
|
3261
3701
|
}
|
|
3262
3702
|
|
|
3263
3703
|
// src/commands/ui/add.ts
|
|
3264
|
-
import { Command as
|
|
3704
|
+
import { Command as Command17 } from "commander";
|
|
3265
3705
|
|
|
3266
3706
|
// src/core/ui-client.ts
|
|
3267
|
-
import * as
|
|
3268
|
-
import * as
|
|
3707
|
+
import * as path18 from "path";
|
|
3708
|
+
import * as fs14 from "fs/promises";
|
|
3269
3709
|
import { createRequire as createRequire5 } from "module";
|
|
3270
3710
|
import { loadUiPackageManifest } from "@teamix-evo/registry";
|
|
3271
3711
|
var require6 = createRequire5(import.meta.url);
|
|
3272
3712
|
function resolvePackageRoot2(packageName) {
|
|
3273
3713
|
const pkgJsonPath = require6.resolve(`${packageName}/package.json`);
|
|
3274
|
-
return
|
|
3714
|
+
return path18.dirname(pkgJsonPath);
|
|
3275
3715
|
}
|
|
3276
3716
|
async function loadUiData(packageName) {
|
|
3277
3717
|
const packageRoot = resolvePackageRoot2(packageName);
|
|
3278
3718
|
logger.debug(`Resolved ui package root: ${packageRoot}`);
|
|
3279
3719
|
const manifest = await loadUiPackageManifest(packageRoot);
|
|
3280
3720
|
let data = {};
|
|
3281
|
-
const dataPath =
|
|
3721
|
+
const dataPath = path18.join(packageRoot, "_data.json");
|
|
3282
3722
|
try {
|
|
3283
|
-
const raw = await
|
|
3723
|
+
const raw = await fs14.readFile(dataPath, "utf-8");
|
|
3284
3724
|
data = JSON.parse(raw);
|
|
3285
3725
|
} catch (err) {
|
|
3286
3726
|
if (err.code !== "ENOENT") {
|
|
@@ -3292,8 +3732,8 @@ async function loadUiData(packageName) {
|
|
|
3292
3732
|
}
|
|
3293
3733
|
|
|
3294
3734
|
// src/core/ui-installer.ts
|
|
3295
|
-
import * as
|
|
3296
|
-
import * as
|
|
3735
|
+
import * as path19 from "path";
|
|
3736
|
+
import * as fs15 from "fs/promises";
|
|
3297
3737
|
import { resolveUiEntryOrder } from "@teamix-evo/registry";
|
|
3298
3738
|
|
|
3299
3739
|
// src/utils/transform-imports.ts
|
|
@@ -3366,9 +3806,12 @@ async function installUiEntries(options) {
|
|
|
3366
3806
|
continue;
|
|
3367
3807
|
}
|
|
3368
3808
|
const rootForEntry = entryPackageRoot?.get(entry.id) ?? packageRoot;
|
|
3369
|
-
const sourceAbs =
|
|
3370
|
-
const raw = await
|
|
3809
|
+
const sourceAbs = path19.resolve(rootForEntry, file.source);
|
|
3810
|
+
const raw = await fs15.readFile(sourceAbs, "utf-8");
|
|
3371
3811
|
const transformed = rewriteImports(raw, aliases);
|
|
3812
|
+
if (exists) {
|
|
3813
|
+
await backupFile(targetAbs, projectRoot);
|
|
3814
|
+
}
|
|
3372
3815
|
await writeFileSafe(targetAbs, transformed);
|
|
3373
3816
|
written++;
|
|
3374
3817
|
logger.info(` write: ${rel(projectRoot, targetAbs)}`);
|
|
@@ -3395,10 +3838,10 @@ function resolveTargetPath(projectRoot, aliases, entry, file) {
|
|
|
3395
3838
|
`Entry "${entry.id}" requires alias "${file.targetAlias}" but it is not configured.`
|
|
3396
3839
|
);
|
|
3397
3840
|
}
|
|
3398
|
-
return
|
|
3841
|
+
return path19.join(projectRoot, aliasDir, file.targetName);
|
|
3399
3842
|
}
|
|
3400
3843
|
function rel(projectRoot, abs) {
|
|
3401
|
-
return
|
|
3844
|
+
return path19.relative(projectRoot, abs);
|
|
3402
3845
|
}
|
|
3403
3846
|
|
|
3404
3847
|
// src/core/ui-add.ts
|
|
@@ -3485,30 +3928,271 @@ function mergeResources(prior, next) {
|
|
|
3485
3928
|
return Array.from(merged.values());
|
|
3486
3929
|
}
|
|
3487
3930
|
|
|
3488
|
-
// src/
|
|
3489
|
-
|
|
3490
|
-
|
|
3491
|
-
|
|
3492
|
-
|
|
3493
|
-
|
|
3494
|
-
|
|
3495
|
-
|
|
3496
|
-
|
|
3497
|
-
|
|
3498
|
-
|
|
3499
|
-
|
|
3500
|
-
|
|
3501
|
-
|
|
3502
|
-
|
|
3503
|
-
|
|
3504
|
-
|
|
3505
|
-
|
|
3506
|
-
|
|
3507
|
-
|
|
3508
|
-
|
|
3509
|
-
|
|
3510
|
-
|
|
3511
|
-
|
|
3931
|
+
// src/core/ui-adopt.ts
|
|
3932
|
+
import * as path20 from "path";
|
|
3933
|
+
import * as fs16 from "fs/promises";
|
|
3934
|
+
var DEFAULT_UI_PACKAGE2 = "@teamix-evo/ui";
|
|
3935
|
+
async function runUiAdopt(options) {
|
|
3936
|
+
const { projectRoot, dryRun = false } = options;
|
|
3937
|
+
const packageName = options.packageName ?? DEFAULT_UI_PACKAGE2;
|
|
3938
|
+
const config = await readProjectConfig(projectRoot);
|
|
3939
|
+
const uiCfg = config?.packages?.ui;
|
|
3940
|
+
if (!config || !uiCfg?.aliases) {
|
|
3941
|
+
throw new Error(
|
|
3942
|
+
"UI not initialized. Run `runUiInit` (or `teamix-evo ui init`) first."
|
|
3943
|
+
);
|
|
3944
|
+
}
|
|
3945
|
+
const { manifest, packageRoot } = await loadUiData(packageName);
|
|
3946
|
+
const aliases = uiCfg.aliases;
|
|
3947
|
+
const adopted = [];
|
|
3948
|
+
const foreign = [];
|
|
3949
|
+
const hooks = [];
|
|
3950
|
+
const utils = [];
|
|
3951
|
+
const types = [];
|
|
3952
|
+
const idToEntry = /* @__PURE__ */ new Map();
|
|
3953
|
+
for (const e of manifest.deprecatedEntries ?? []) idToEntry.set(e.id, e);
|
|
3954
|
+
for (const e of manifest.entries) idToEntry.set(e.id, e);
|
|
3955
|
+
const aliasRoots = [
|
|
3956
|
+
{ abs: path20.join(projectRoot, aliases.components), bucket: "components" },
|
|
3957
|
+
{ abs: path20.join(projectRoot, aliases.hooks), bucket: "hooks" },
|
|
3958
|
+
{ abs: path20.join(projectRoot, aliases.utils), bucket: "utils" },
|
|
3959
|
+
{ abs: path20.join(projectRoot, aliases.business), bucket: "components" }
|
|
3960
|
+
];
|
|
3961
|
+
if (aliases.lib && aliases.lib !== aliases.utils) {
|
|
3962
|
+
aliasRoots.push({
|
|
3963
|
+
abs: path20.join(projectRoot, aliases.lib),
|
|
3964
|
+
bucket: "utils"
|
|
3965
|
+
});
|
|
3966
|
+
}
|
|
3967
|
+
for (const root of aliasRoots) {
|
|
3968
|
+
const files = await listSourceFiles(root.abs);
|
|
3969
|
+
for (const abs of files) {
|
|
3970
|
+
const rel2 = toPosixRel(projectRoot, abs);
|
|
3971
|
+
const content = await fs16.readFile(abs, "utf-8");
|
|
3972
|
+
const fileType = classifyFile(abs, content);
|
|
3973
|
+
const id = inferEntryId(abs);
|
|
3974
|
+
if (fileType === "type") {
|
|
3975
|
+
types.push({ rel: rel2, fileType, target: abs, reason: "no-registry-match" });
|
|
3976
|
+
continue;
|
|
3977
|
+
}
|
|
3978
|
+
if (fileType === "hook" || root.bucket === "hooks") {
|
|
3979
|
+
hooks.push({
|
|
3980
|
+
rel: rel2,
|
|
3981
|
+
fileType: "hook",
|
|
3982
|
+
target: abs,
|
|
3983
|
+
reason: "no-registry-match"
|
|
3984
|
+
});
|
|
3985
|
+
continue;
|
|
3986
|
+
}
|
|
3987
|
+
if (fileType === "util" || root.bucket === "utils") {
|
|
3988
|
+
utils.push({
|
|
3989
|
+
rel: rel2,
|
|
3990
|
+
fileType: "util",
|
|
3991
|
+
target: abs,
|
|
3992
|
+
reason: "no-registry-match"
|
|
3993
|
+
});
|
|
3994
|
+
continue;
|
|
3995
|
+
}
|
|
3996
|
+
const entry = id ? idToEntry.get(id) : void 0;
|
|
3997
|
+
if (!entry) {
|
|
3998
|
+
foreign.push({
|
|
3999
|
+
rel: rel2,
|
|
4000
|
+
fileType: fileType === "unknown" ? "component" : fileType,
|
|
4001
|
+
target: abs,
|
|
4002
|
+
reason: "no-registry-match"
|
|
4003
|
+
});
|
|
4004
|
+
continue;
|
|
4005
|
+
}
|
|
4006
|
+
const upstream = await readUpstreamContent(
|
|
4007
|
+
packageRoot,
|
|
4008
|
+
entry,
|
|
4009
|
+
aliases
|
|
4010
|
+
).catch((err) => {
|
|
4011
|
+
logger.debug(
|
|
4012
|
+
`Failed to read upstream for ${entry.id}: ${err.message}`
|
|
4013
|
+
);
|
|
4014
|
+
return null;
|
|
4015
|
+
});
|
|
4016
|
+
const lineage = upstream === null ? "detected" : normalize(upstream) === normalize(content) ? "teamix-evo" : "custom";
|
|
4017
|
+
adopted.push({
|
|
4018
|
+
id: entry.id,
|
|
4019
|
+
target: abs,
|
|
4020
|
+
rel: rel2,
|
|
4021
|
+
fileType: fileType === "unknown" ? "component" : fileType,
|
|
4022
|
+
sourceLineage: lineage,
|
|
4023
|
+
hash: computeHash(content)
|
|
4024
|
+
});
|
|
4025
|
+
}
|
|
4026
|
+
}
|
|
4027
|
+
if (!dryRun && adopted.length > 0) {
|
|
4028
|
+
await mergeIntoInstalledManifest({
|
|
4029
|
+
projectRoot,
|
|
4030
|
+
packageName,
|
|
4031
|
+
manifest,
|
|
4032
|
+
adopted
|
|
4033
|
+
});
|
|
4034
|
+
}
|
|
4035
|
+
return {
|
|
4036
|
+
packageName,
|
|
4037
|
+
adopted,
|
|
4038
|
+
foreign,
|
|
4039
|
+
hooks,
|
|
4040
|
+
utils,
|
|
4041
|
+
types,
|
|
4042
|
+
dryRun
|
|
4043
|
+
};
|
|
4044
|
+
}
|
|
4045
|
+
async function listSourceFiles(dir) {
|
|
4046
|
+
const out = [];
|
|
4047
|
+
const stack = [dir];
|
|
4048
|
+
while (stack.length > 0) {
|
|
4049
|
+
const current = stack.pop();
|
|
4050
|
+
let entries;
|
|
4051
|
+
try {
|
|
4052
|
+
entries = await fs16.readdir(current, { withFileTypes: true });
|
|
4053
|
+
} catch (err) {
|
|
4054
|
+
if (err.code === "ENOENT") continue;
|
|
4055
|
+
throw err;
|
|
4056
|
+
}
|
|
4057
|
+
for (const e of entries) {
|
|
4058
|
+
const abs = path20.join(current, e.name);
|
|
4059
|
+
if (e.isDirectory()) {
|
|
4060
|
+
if (e.name === "node_modules" || e.name.startsWith(".")) continue;
|
|
4061
|
+
stack.push(abs);
|
|
4062
|
+
continue;
|
|
4063
|
+
}
|
|
4064
|
+
if (!e.isFile()) continue;
|
|
4065
|
+
if (/\.(ts|tsx)$/.test(e.name)) out.push(abs);
|
|
4066
|
+
}
|
|
4067
|
+
}
|
|
4068
|
+
return out;
|
|
4069
|
+
}
|
|
4070
|
+
function classifyFile(abs, content) {
|
|
4071
|
+
const base = path20.basename(abs);
|
|
4072
|
+
if (base.endsWith(".d.ts")) return "type";
|
|
4073
|
+
if (/^use-[a-z0-9-]+\.tsx?$/i.test(base)) return "hook";
|
|
4074
|
+
const hasJsx = /<[A-Za-z][^>]*?>/.test(content);
|
|
4075
|
+
const hasReactImport = /from ['"]react['"]/.test(content);
|
|
4076
|
+
const hasForwardRef = /forwardRef\s*[<(]/.test(content);
|
|
4077
|
+
const hasProvider = /\.Provider\b/.test(content) || /createContext\s*[<(]/.test(content);
|
|
4078
|
+
if (hasProvider && (hasJsx || hasReactImport)) return "provider";
|
|
4079
|
+
if (hasJsx || hasForwardRef) return "component";
|
|
4080
|
+
if (hasReactImport && /export\s+(const|function|default)/.test(content))
|
|
4081
|
+
return "component";
|
|
4082
|
+
const exportsValue = /export\s+(const|function|class|default|let|var)\b/.test(
|
|
4083
|
+
content
|
|
4084
|
+
);
|
|
4085
|
+
const exportsType = /export\s+(type|interface)\b/.test(content) || /^\s*type\s+/m.test(content);
|
|
4086
|
+
if (!exportsValue && exportsType) return "type";
|
|
4087
|
+
if (base.startsWith("use-")) return "hook";
|
|
4088
|
+
if (/\b(use[A-Z]\w+)\s*\(/.test(content) && !hasJsx) {
|
|
4089
|
+
return "util";
|
|
4090
|
+
}
|
|
4091
|
+
if (!hasJsx && !hasReactImport) return "util";
|
|
4092
|
+
return "unknown";
|
|
4093
|
+
}
|
|
4094
|
+
function inferEntryId(abs) {
|
|
4095
|
+
const base = path20.basename(abs).replace(/\.(tsx?|d\.ts)$/i, "");
|
|
4096
|
+
if (!base) return null;
|
|
4097
|
+
if (!/^[a-z0-9][a-z0-9-]*$/.test(base)) return null;
|
|
4098
|
+
if (base.startsWith("use-")) return null;
|
|
4099
|
+
return base;
|
|
4100
|
+
}
|
|
4101
|
+
async function readUpstreamContent(packageRoot, entry, aliases) {
|
|
4102
|
+
const file = entry.files[0];
|
|
4103
|
+
if (!file) return null;
|
|
4104
|
+
const sourceAbs = path20.resolve(packageRoot, file.source);
|
|
4105
|
+
let raw;
|
|
4106
|
+
try {
|
|
4107
|
+
raw = await fs16.readFile(sourceAbs, "utf-8");
|
|
4108
|
+
} catch {
|
|
4109
|
+
return null;
|
|
4110
|
+
}
|
|
4111
|
+
return rewriteImports(raw, aliases);
|
|
4112
|
+
}
|
|
4113
|
+
function normalize(s) {
|
|
4114
|
+
return s.replace(/\r\n/g, "\n").trim();
|
|
4115
|
+
}
|
|
4116
|
+
function toPosixRel(projectRoot, abs) {
|
|
4117
|
+
return path20.relative(projectRoot, abs).split(path20.sep).join("/");
|
|
4118
|
+
}
|
|
4119
|
+
async function mergeIntoInstalledManifest(args) {
|
|
4120
|
+
const { projectRoot, packageName, manifest, adopted } = args;
|
|
4121
|
+
const installed = await readInstalledManifest(
|
|
4122
|
+
projectRoot
|
|
4123
|
+
) ?? { schemaVersion: 1, installed: [] };
|
|
4124
|
+
const idx = installed.installed.findIndex((p2) => p2.package === packageName);
|
|
4125
|
+
const prior = idx >= 0 ? installed.installed[idx] : null;
|
|
4126
|
+
const newResources = adopted.map((a) => {
|
|
4127
|
+
const targetName = path20.basename(a.target);
|
|
4128
|
+
return {
|
|
4129
|
+
id: `${a.id}:${targetName}`,
|
|
4130
|
+
target: a.target,
|
|
4131
|
+
hash: a.hash,
|
|
4132
|
+
strategy: "frozen",
|
|
4133
|
+
sourceLineage: a.sourceLineage
|
|
4134
|
+
};
|
|
4135
|
+
});
|
|
4136
|
+
const merged = mergeResources2(prior?.resources ?? [], newResources);
|
|
4137
|
+
const entry = {
|
|
4138
|
+
package: packageName,
|
|
4139
|
+
variant: "_flat",
|
|
4140
|
+
version: manifest.version,
|
|
4141
|
+
installedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
4142
|
+
resources: merged
|
|
4143
|
+
};
|
|
4144
|
+
if (idx >= 0) installed.installed[idx] = entry;
|
|
4145
|
+
else installed.installed.push(entry);
|
|
4146
|
+
await writeInstalledManifest(projectRoot, installed);
|
|
4147
|
+
}
|
|
4148
|
+
function mergeResources2(prior, next) {
|
|
4149
|
+
const merged = /* @__PURE__ */ new Map();
|
|
4150
|
+
for (const r of prior) merged.set(r.id, r);
|
|
4151
|
+
for (const r of next) merged.set(r.id, r);
|
|
4152
|
+
return Array.from(merged.values());
|
|
4153
|
+
}
|
|
4154
|
+
|
|
4155
|
+
// src/commands/ui/add.ts
|
|
4156
|
+
var addCommand2 = new Command17("add").description(
|
|
4157
|
+
"\u5B89\u88C5\u4E00\u4E2A\u6216\u591A\u4E2A ui entry\uFF08\u6309 id\uFF0C\u81EA\u52A8\u5C55\u5F00 registryDependencies\uFF09"
|
|
4158
|
+
).argument("[ids...]", 'entry id \u5217\u8868\uFF0C\u5982 "button" "dialog"').option("--overwrite", "\u5373\u4F7F\u76EE\u6807\u6587\u4EF6\u5DF2\u5B58\u5728\u4E5F\u8986\u76D6\uFF08\u7ED5\u8FC7 frozen \u8DF3\u8FC7\uFF09").option(
|
|
4159
|
+
"--include-deprecated",
|
|
4160
|
+
"\u5141\u8BB8\u5B89\u88C5\u5DF2\u5F52\u6863\u7684 deprecated entry\uFF08\u4EC5\u8FC1\u79FB / \u5BA1\u8BA1\u573A\u666F\uFF0CADR 0028\uFF09"
|
|
4161
|
+
).option("--adopt", "\u626B\u63CF\u9879\u76EE\u73B0\u6709 UI \u6E90\u7801\u7EB3\u7BA1\u5230 manifest\uFF0C\u4E0D\u5199\u5165\u4EFB\u4F55\u6587\u4EF6\u5185\u5BB9").option(
|
|
4162
|
+
"--dry-run",
|
|
4163
|
+
"\u4EC5\u626B\u63CF + \u8F93\u51FA\u62A5\u544A\uFF0C\u4E0D\u4FEE\u6539 manifest\uFF08\u4EC5\u4E0E --adopt \u540C\u7528\uFF09"
|
|
4164
|
+
).action(
|
|
4165
|
+
async (ids, opts) => {
|
|
4166
|
+
try {
|
|
4167
|
+
const ide = detectIde();
|
|
4168
|
+
const projectRoot = ide.getProjectRoot();
|
|
4169
|
+
if (opts.adopt) {
|
|
4170
|
+
await runAdoptCli(projectRoot, opts.dryRun ?? false);
|
|
4171
|
+
return;
|
|
4172
|
+
}
|
|
4173
|
+
if (opts.dryRun) {
|
|
4174
|
+
logger.warn(
|
|
4175
|
+
"`--dry-run` only takes effect together with `--adopt`; ignoring for plain ui add."
|
|
4176
|
+
);
|
|
4177
|
+
}
|
|
4178
|
+
if (!ids || ids.length === 0) {
|
|
4179
|
+
throw new Error(
|
|
4180
|
+
"At least one entry id must be provided (or pass `--adopt`)."
|
|
4181
|
+
);
|
|
4182
|
+
}
|
|
4183
|
+
logger.info(`Installing entries: ${ids.join(", ")}`);
|
|
4184
|
+
const result = await runUiAdd({
|
|
4185
|
+
projectRoot,
|
|
4186
|
+
ids,
|
|
4187
|
+
overwrite: opts.overwrite,
|
|
4188
|
+
includeDeprecated: opts.includeDeprecated
|
|
4189
|
+
});
|
|
4190
|
+
logger.success(
|
|
4191
|
+
`UI add complete: ${result.written} written, ${result.skipped} skipped.`
|
|
4192
|
+
);
|
|
4193
|
+
logger.info("");
|
|
4194
|
+
logger.info(`Resolved order: ${result.orderedIds.join(" \u2192 ")}`);
|
|
4195
|
+
const npmDeps = Object.entries(result.npmDependencies);
|
|
3512
4196
|
if (npmDeps.length > 0) {
|
|
3513
4197
|
logger.info("");
|
|
3514
4198
|
logger.info("Install npm dependencies in your project:");
|
|
@@ -3530,15 +4214,51 @@ var addCommand2 = new Command16("add").description(
|
|
|
3530
4214
|
}
|
|
3531
4215
|
}
|
|
3532
4216
|
);
|
|
4217
|
+
async function runAdoptCli(projectRoot, dryRun) {
|
|
4218
|
+
logger.info(
|
|
4219
|
+
dryRun ? "Adopt scan (dry-run) \u2014 no manifest changes will be written." : "Adopting existing UI source files into manifest..."
|
|
4220
|
+
);
|
|
4221
|
+
const result = await runUiAdopt({ projectRoot, dryRun });
|
|
4222
|
+
logger.success(
|
|
4223
|
+
`UI adopt complete: ${result.adopted.length} adopted, ${result.foreign.length} foreign, ${result.hooks.length} hooks, ${result.utils.length} utils, ${result.types.length} types.`
|
|
4224
|
+
);
|
|
4225
|
+
if (result.adopted.length > 0) {
|
|
4226
|
+
logger.info("");
|
|
4227
|
+
logger.info("Adopted (matched registry id):");
|
|
4228
|
+
for (const a of result.adopted) {
|
|
4229
|
+
logger.info(` \u2022 ${a.id} \u2190 ${a.rel} [${a.sourceLineage}]`);
|
|
4230
|
+
}
|
|
4231
|
+
}
|
|
4232
|
+
if (result.foreign.length > 0) {
|
|
4233
|
+
logger.info("");
|
|
4234
|
+
logger.info("Foreign (no registry match \u2014 keep as-is):");
|
|
4235
|
+
for (const f of result.foreign) logger.info(` \u2022 ${f.rel}`);
|
|
4236
|
+
}
|
|
4237
|
+
if (result.hooks.length > 0) {
|
|
4238
|
+
logger.info("");
|
|
4239
|
+
logger.info("Hooks:");
|
|
4240
|
+
for (const h of result.hooks) logger.info(` \u2022 ${h.rel}`);
|
|
4241
|
+
}
|
|
4242
|
+
if (result.utils.length > 0) {
|
|
4243
|
+
logger.info("");
|
|
4244
|
+
logger.info("Utils:");
|
|
4245
|
+
for (const u of result.utils) logger.info(` \u2022 ${u.rel}`);
|
|
4246
|
+
}
|
|
4247
|
+
if (result.types.length > 0) {
|
|
4248
|
+
logger.info("");
|
|
4249
|
+
logger.info("Type-only modules:");
|
|
4250
|
+
for (const t of result.types) logger.info(` \u2022 ${t.rel}`);
|
|
4251
|
+
}
|
|
4252
|
+
}
|
|
3533
4253
|
|
|
3534
4254
|
// src/commands/ui/list.ts
|
|
3535
|
-
import { Command as
|
|
4255
|
+
import { Command as Command18 } from "commander";
|
|
3536
4256
|
|
|
3537
4257
|
// src/core/ui-list.ts
|
|
3538
|
-
var
|
|
4258
|
+
var DEFAULT_UI_PACKAGE3 = "@teamix-evo/ui";
|
|
3539
4259
|
async function runUiList(options) {
|
|
3540
4260
|
const { projectRoot, installedOnly, includeDeprecated } = options;
|
|
3541
|
-
const packageName = options.packageName ??
|
|
4261
|
+
const packageName = options.packageName ?? DEFAULT_UI_PACKAGE3;
|
|
3542
4262
|
const { manifest } = await loadUiData(packageName);
|
|
3543
4263
|
const installedManifest = await readInstalledManifest(projectRoot);
|
|
3544
4264
|
const installedIds = /* @__PURE__ */ new Set();
|
|
@@ -3570,7 +4290,7 @@ async function runUiList(options) {
|
|
|
3570
4290
|
}
|
|
3571
4291
|
|
|
3572
4292
|
// src/commands/ui/list.ts
|
|
3573
|
-
var listCommand3 = new
|
|
4293
|
+
var listCommand3 = new Command18("list").description("\u5217\u51FA @teamix-evo/ui \u7684\u6240\u6709 entry \u53CA\u5DF2\u5B89\u88C5\u72B6\u6001").option("--installed", "\u4EC5\u5C55\u793A\u5DF2\u5B89\u88C5\u7684 entry").option(
|
|
3574
4294
|
"--include-deprecated",
|
|
3575
4295
|
"\u540C\u65F6\u5217\u51FA\u5DF2\u5F52\u6863\u7684 deprecated entry\uFF08\u9ED8\u8BA4\u9690\u85CF\uFF0CADR 0028\uFF09"
|
|
3576
4296
|
).action(
|
|
@@ -3622,10 +4342,10 @@ var listCommand3 = new Command17("list").description("\u5217\u51FA @teamix-evo/u
|
|
|
3622
4342
|
);
|
|
3623
4343
|
|
|
3624
4344
|
// src/commands/_upgrade-command-factory.ts
|
|
3625
|
-
import { Command as
|
|
4345
|
+
import { Command as Command19 } from "commander";
|
|
3626
4346
|
|
|
3627
4347
|
// src/core/ui-upgrade.ts
|
|
3628
|
-
import * as
|
|
4348
|
+
import * as path23 from "path";
|
|
3629
4349
|
import { createRequire as createRequire6 } from "module";
|
|
3630
4350
|
import {
|
|
3631
4351
|
loadUiPackageManifest as loadUiPackageManifest2,
|
|
@@ -3633,8 +4353,8 @@ import {
|
|
|
3633
4353
|
} from "@teamix-evo/registry";
|
|
3634
4354
|
|
|
3635
4355
|
// src/core/ui-upgrade-detector.ts
|
|
3636
|
-
import * as
|
|
3637
|
-
import * as
|
|
4356
|
+
import * as fs17 from "fs/promises";
|
|
4357
|
+
import * as path21 from "path";
|
|
3638
4358
|
var PACKAGE_NAME = {
|
|
3639
4359
|
ui: "@teamix-evo/ui",
|
|
3640
4360
|
"biz-ui": "@teamix-evo/biz-ui"
|
|
@@ -3650,10 +4370,10 @@ async function detectComponentLineage(options) {
|
|
|
3650
4370
|
const config = options.config ?? await readProjectConfig(projectRoot);
|
|
3651
4371
|
const installed = options.installed ?? await readInstalledManifest(projectRoot);
|
|
3652
4372
|
const installDir = resolveInstallDir(category, config);
|
|
3653
|
-
const installDirAbs =
|
|
4373
|
+
const installDirAbs = path21.join(projectRoot, installDir);
|
|
3654
4374
|
const installDirExists = await directoryExists(installDirAbs);
|
|
3655
4375
|
const hasComponentsJson = await fileExists(
|
|
3656
|
-
|
|
4376
|
+
path21.join(projectRoot, "components.json")
|
|
3657
4377
|
);
|
|
3658
4378
|
const installedPkg = findInstalledPackage(installed, PACKAGE_NAME[category]);
|
|
3659
4379
|
const registeredIds = installedPkg ? extractIds(installedPkg).sort() : [];
|
|
@@ -3693,14 +4413,14 @@ function extractIds(pkg) {
|
|
|
3693
4413
|
}
|
|
3694
4414
|
async function directoryExists(p2) {
|
|
3695
4415
|
try {
|
|
3696
|
-
const stat7 = await
|
|
4416
|
+
const stat7 = await fs17.stat(p2);
|
|
3697
4417
|
return stat7.isDirectory();
|
|
3698
4418
|
} catch {
|
|
3699
4419
|
return false;
|
|
3700
4420
|
}
|
|
3701
4421
|
}
|
|
3702
4422
|
async function listComponentIds(installDirAbs) {
|
|
3703
|
-
const entries = await
|
|
4423
|
+
const entries = await fs17.readdir(installDirAbs, { withFileTypes: true });
|
|
3704
4424
|
const ids = [];
|
|
3705
4425
|
for (const e of entries) {
|
|
3706
4426
|
if (!e.isFile()) continue;
|
|
@@ -3720,7 +4440,7 @@ function classifyLineage(args) {
|
|
|
3720
4440
|
}
|
|
3721
4441
|
|
|
3722
4442
|
// src/core/ui-upgrade-staging.ts
|
|
3723
|
-
import * as
|
|
4443
|
+
import * as path22 from "path";
|
|
3724
4444
|
var TEAMIX_DIR4 = ".teamix-evo";
|
|
3725
4445
|
var STAGING_DIR = ".upgrade-staging";
|
|
3726
4446
|
var PACKAGE_NAME2 = {
|
|
@@ -3740,7 +4460,7 @@ async function buildUiUpgradeStaging(options) {
|
|
|
3740
4460
|
if (!installedPkg) return null;
|
|
3741
4461
|
const isoTs = options.isoTs ?? (/* @__PURE__ */ new Date()).toISOString();
|
|
3742
4462
|
const fsTs = isoToFsSafe2(isoTs);
|
|
3743
|
-
const stagingDir =
|
|
4463
|
+
const stagingDir = path22.join(
|
|
3744
4464
|
options.projectRoot,
|
|
3745
4465
|
TEAMIX_DIR4,
|
|
3746
4466
|
STAGING_DIR,
|
|
@@ -3772,7 +4492,7 @@ async function buildUiUpgradeStaging(options) {
|
|
|
3772
4492
|
if (onlyIds && !onlyIds.has(id)) continue;
|
|
3773
4493
|
const built = await processForeign({
|
|
3774
4494
|
id,
|
|
3775
|
-
installDirAbs:
|
|
4495
|
+
installDirAbs: path22.join(options.projectRoot, lineageReport.installDir),
|
|
3776
4496
|
stagingDir,
|
|
3777
4497
|
projectRoot: options.projectRoot,
|
|
3778
4498
|
category
|
|
@@ -3795,7 +4515,7 @@ async function buildUiUpgradeStaging(options) {
|
|
|
3795
4515
|
};
|
|
3796
4516
|
await ensureDir(stagingDir);
|
|
3797
4517
|
await writeFileSafe(
|
|
3798
|
-
|
|
4518
|
+
path22.join(stagingDir, "meta.json"),
|
|
3799
4519
|
JSON.stringify(manifestOut, null, 2) + "\n"
|
|
3800
4520
|
);
|
|
3801
4521
|
return { stagingDir, manifest: manifestOut };
|
|
@@ -3829,19 +4549,19 @@ async function processRegistered(args) {
|
|
|
3829
4549
|
const file = entry.files[0];
|
|
3830
4550
|
if (!file) return null;
|
|
3831
4551
|
const rootForEntry = args.entryPackageRoot?.get(id) ?? args.packageRoot;
|
|
3832
|
-
const sourceAbs =
|
|
4552
|
+
const sourceAbs = path22.resolve(rootForEntry, file.source);
|
|
3833
4553
|
const raw = await readFileOrNull(sourceAbs);
|
|
3834
4554
|
if (raw === null) {
|
|
3835
4555
|
return null;
|
|
3836
4556
|
}
|
|
3837
4557
|
const incomingTransformed = rewriteImports(raw, args.aliases);
|
|
3838
4558
|
const incomingHash = computeHash(incomingTransformed);
|
|
3839
|
-
const currentExt =
|
|
3840
|
-
const incomingExt =
|
|
4559
|
+
const currentExt = path22.extname(resource.target) || ".tsx";
|
|
4560
|
+
const incomingExt = path22.extname(file.targetName) || currentExt;
|
|
3841
4561
|
const currentRel = `${id}/current${currentExt}`;
|
|
3842
4562
|
const incomingRel = `${id}/incoming${incomingExt}`;
|
|
3843
|
-
await writeFileSafe(
|
|
3844
|
-
await writeFileSafe(
|
|
4563
|
+
await writeFileSafe(path22.join(stagingDir, currentRel), currentSource);
|
|
4564
|
+
await writeFileSafe(path22.join(stagingDir, incomingRel), incomingTransformed);
|
|
3845
4565
|
const diff = classifyRisk({
|
|
3846
4566
|
currentHash: resource.hash,
|
|
3847
4567
|
incomingHash,
|
|
@@ -3849,11 +4569,16 @@ async function processRegistered(args) {
|
|
|
3849
4569
|
incomingSource: incomingTransformed,
|
|
3850
4570
|
multiFile: entry.files.length > 1
|
|
3851
4571
|
});
|
|
4572
|
+
const promotion = derivePromotion({
|
|
4573
|
+
currentSource,
|
|
4574
|
+
incomingSource: incomingTransformed,
|
|
4575
|
+
targetName: entry.files[0]?.targetName ?? `${id}.tsx`
|
|
4576
|
+
});
|
|
3852
4577
|
return {
|
|
3853
4578
|
id,
|
|
3854
4579
|
category,
|
|
3855
4580
|
current: {
|
|
3856
|
-
target:
|
|
4581
|
+
target: path22.relative(projectRoot, resource.target),
|
|
3857
4582
|
hash: resource.hash,
|
|
3858
4583
|
sourceLineage: "teamix-evo"
|
|
3859
4584
|
},
|
|
@@ -3862,25 +4587,26 @@ async function processRegistered(args) {
|
|
|
3862
4587
|
hash: incomingHash,
|
|
3863
4588
|
relPath: incomingRel
|
|
3864
4589
|
},
|
|
3865
|
-
diff
|
|
4590
|
+
diff,
|
|
4591
|
+
promotion
|
|
3866
4592
|
};
|
|
3867
4593
|
}
|
|
3868
4594
|
async function processForeign(args) {
|
|
3869
4595
|
const { id, installDirAbs, stagingDir, projectRoot, category } = args;
|
|
3870
|
-
const tsx =
|
|
3871
|
-
const ts =
|
|
4596
|
+
const tsx = path22.join(installDirAbs, `${id}.tsx`);
|
|
4597
|
+
const ts = path22.join(installDirAbs, `${id}.ts`);
|
|
3872
4598
|
const target = await fileExists(tsx) ? tsx : await fileExists(ts) ? ts : null;
|
|
3873
4599
|
if (!target) return null;
|
|
3874
4600
|
const raw = await readFileOrNull(target);
|
|
3875
4601
|
if (raw === null) return null;
|
|
3876
|
-
const ext =
|
|
4602
|
+
const ext = path22.extname(target);
|
|
3877
4603
|
const currentRel = `${id}/current${ext}`;
|
|
3878
|
-
await writeFileSafe(
|
|
4604
|
+
await writeFileSafe(path22.join(stagingDir, currentRel), raw);
|
|
3879
4605
|
return {
|
|
3880
4606
|
id,
|
|
3881
4607
|
category,
|
|
3882
4608
|
current: {
|
|
3883
|
-
target:
|
|
4609
|
+
target: path22.relative(projectRoot, target),
|
|
3884
4610
|
hash: computeHash(raw),
|
|
3885
4611
|
sourceLineage: "custom"
|
|
3886
4612
|
},
|
|
@@ -3895,17 +4621,17 @@ async function processForeign(args) {
|
|
|
3895
4621
|
};
|
|
3896
4622
|
}
|
|
3897
4623
|
async function buildBreakingEntry(args) {
|
|
3898
|
-
const ext =
|
|
4624
|
+
const ext = path22.extname(args.resource.target) || ".tsx";
|
|
3899
4625
|
const currentRel = `${args.id}/current${ext}`;
|
|
3900
4626
|
await writeFileSafe(
|
|
3901
|
-
|
|
4627
|
+
path22.join(args.stagingDir, currentRel),
|
|
3902
4628
|
args.currentSource
|
|
3903
4629
|
);
|
|
3904
4630
|
return {
|
|
3905
4631
|
id: args.id,
|
|
3906
4632
|
category: args.category,
|
|
3907
4633
|
current: {
|
|
3908
|
-
target:
|
|
4634
|
+
target: path22.relative(args.projectRoot, args.resource.target),
|
|
3909
4635
|
hash: args.resource.hash,
|
|
3910
4636
|
sourceLineage: "teamix-evo"
|
|
3911
4637
|
},
|
|
@@ -4025,12 +4751,191 @@ function aggregateByRisk(entries) {
|
|
|
4025
4751
|
}
|
|
4026
4752
|
return out;
|
|
4027
4753
|
}
|
|
4754
|
+
function derivePromotion(args) {
|
|
4755
|
+
const fileType = classifyPromoteFileType(args.targetName, args.currentSource);
|
|
4756
|
+
const featureVector = buildFeatureVector(
|
|
4757
|
+
args.currentSource,
|
|
4758
|
+
args.incomingSource
|
|
4759
|
+
);
|
|
4760
|
+
const { recommendedModes, confidence, reasons } = scorePromotionModes(
|
|
4761
|
+
fileType,
|
|
4762
|
+
featureVector
|
|
4763
|
+
);
|
|
4764
|
+
return { fileType, featureVector, recommendedModes, confidence, reasons };
|
|
4765
|
+
}
|
|
4766
|
+
function classifyPromoteFileType(targetName, src) {
|
|
4767
|
+
if (targetName.endsWith(".d.ts")) return "type";
|
|
4768
|
+
if (/^use-[a-z0-9-]+\.tsx?$/i.test(targetName)) return "hook";
|
|
4769
|
+
const hasJsx = /<[A-Za-z][^>]*?>/.test(src);
|
|
4770
|
+
const hasReactImport = /from ['"]react['"]/.test(src);
|
|
4771
|
+
const hasProvider = /\.Provider\b/.test(src) || /createContext\s*[<(]/.test(src);
|
|
4772
|
+
if (hasProvider && (hasJsx || hasReactImport)) return "provider";
|
|
4773
|
+
if (hasJsx || /forwardRef\s*[<(]/.test(src)) return "component";
|
|
4774
|
+
if (!hasJsx && !hasReactImport) return "util";
|
|
4775
|
+
return "component";
|
|
4776
|
+
}
|
|
4777
|
+
function buildFeatureVector(current, incoming) {
|
|
4778
|
+
const curExports = extractExportNames(current);
|
|
4779
|
+
const newExports = extractExportNames(incoming);
|
|
4780
|
+
const apiAdded = setDiff(curExports, newExports);
|
|
4781
|
+
const apiRemoved = setDiff(newExports, curExports);
|
|
4782
|
+
const curVariants = extractCvaVariantValues(current);
|
|
4783
|
+
const newVariants = extractCvaVariantValues(incoming);
|
|
4784
|
+
const cvaAdded = setDiff(curVariants, newVariants);
|
|
4785
|
+
const cvaModified = [];
|
|
4786
|
+
const sharedVariants = curVariants.filter((v) => newVariants.includes(v));
|
|
4787
|
+
for (const v of sharedVariants) {
|
|
4788
|
+
if (extractVariantBody(current, v) !== extractVariantBody(incoming, v)) {
|
|
4789
|
+
cvaModified.push(v);
|
|
4790
|
+
}
|
|
4791
|
+
}
|
|
4792
|
+
const curClass = extractClassNameLiterals(current);
|
|
4793
|
+
const newClass = extractClassNameLiterals(incoming);
|
|
4794
|
+
const classNameDiff = curClass !== newClass;
|
|
4795
|
+
const curTokens = extractTokenRefs(current);
|
|
4796
|
+
const newTokens = extractTokenRefs(incoming);
|
|
4797
|
+
const tokenUsageDiff = curTokens.size !== newTokens.size || [...curTokens].some((t) => !newTokens.has(t));
|
|
4798
|
+
const hasState = /\buseState\s*[<(]/.test(current);
|
|
4799
|
+
const hasEffect = /\b(useEffect|useLayoutEffect|useMemo|useCallback)\s*[<(]/.test(current);
|
|
4800
|
+
const curImports = extractImportSources(current);
|
|
4801
|
+
const newImports = extractImportSources(incoming);
|
|
4802
|
+
const hasExtraImports = [...curImports].some((src) => !newImports.has(src));
|
|
4803
|
+
const tagSet = /* @__PURE__ */ new Set();
|
|
4804
|
+
for (const m of current.matchAll(/<([A-Z]\w+)[\s/>]/g)) {
|
|
4805
|
+
if (m[1]) tagSet.add(m[1]);
|
|
4806
|
+
}
|
|
4807
|
+
const atomicChildren = [...tagSet];
|
|
4808
|
+
const isComposition = atomicChildren.length > 2;
|
|
4809
|
+
const signatureChanged = extractDefaultParamList(current) !== extractDefaultParamList(incoming);
|
|
4810
|
+
return {
|
|
4811
|
+
apiDelta: { added: apiAdded, removed: apiRemoved, signatureChanged },
|
|
4812
|
+
styleDelta: { classNameDiff, tokenUsageDiff },
|
|
4813
|
+
logicDelta: { hasState, hasEffect, hasExtraImports },
|
|
4814
|
+
cvaDelta: { addedVariants: cvaAdded, modifiedVariants: cvaModified },
|
|
4815
|
+
structureDelta: { isComposition, atomicChildren }
|
|
4816
|
+
};
|
|
4817
|
+
}
|
|
4818
|
+
function scorePromotionModes(fileType, fv) {
|
|
4819
|
+
const reasons = [];
|
|
4820
|
+
if (fileType === "hook" || fileType === "util" || fileType === "type") {
|
|
4821
|
+
reasons.push(
|
|
4822
|
+
`fileType=${fileType} \u2014 not a component, deferred to ManualReview`
|
|
4823
|
+
);
|
|
4824
|
+
return { recommendedModes: ["ManualReview"], confidence: 0.5, reasons };
|
|
4825
|
+
}
|
|
4826
|
+
const apiNoChange = fv.apiDelta.added.length === 0 && fv.apiDelta.removed.length === 0 && !fv.apiDelta.signatureChanged;
|
|
4827
|
+
const logicMinimal = !fv.logicDelta.hasState && !fv.logicDelta.hasEffect && !fv.logicDelta.hasExtraImports;
|
|
4828
|
+
if (fv.apiDelta.removed.length > 0 || fv.apiDelta.signatureChanged) {
|
|
4829
|
+
reasons.push(
|
|
4830
|
+
"signature changed or props removed \u2014 Coexist preserves user version"
|
|
4831
|
+
);
|
|
4832
|
+
return { recommendedModes: ["Coexist"], confidence: 0.85, reasons };
|
|
4833
|
+
}
|
|
4834
|
+
if (apiNoChange && logicMinimal && (fv.styleDelta.classNameDiff || fv.styleDelta.tokenUsageDiff) && fv.cvaDelta.addedVariants.length === 0 && fv.cvaDelta.modifiedVariants.length === 0) {
|
|
4835
|
+
reasons.push(
|
|
4836
|
+
"only style / token differences \u2014 push to tokens.overrides.css"
|
|
4837
|
+
);
|
|
4838
|
+
return { recommendedModes: ["TokenOnly"], confidence: 0.8, reasons };
|
|
4839
|
+
}
|
|
4840
|
+
const modes = [];
|
|
4841
|
+
let score = 0;
|
|
4842
|
+
if (fv.cvaDelta.addedVariants.length > 0 || fv.cvaDelta.modifiedVariants.length > 0) {
|
|
4843
|
+
modes.push("Variant");
|
|
4844
|
+
reasons.push(
|
|
4845
|
+
`cva variants delta: +${fv.cvaDelta.addedVariants.length} ~${fv.cvaDelta.modifiedVariants.length}`
|
|
4846
|
+
);
|
|
4847
|
+
score = Math.max(score, 0.7);
|
|
4848
|
+
}
|
|
4849
|
+
if (fv.logicDelta.hasState || fv.logicDelta.hasEffect || fv.logicDelta.hasExtraImports || fv.apiDelta.added.length > 0) {
|
|
4850
|
+
modes.push("Wrapper");
|
|
4851
|
+
reasons.push("user added state / effect / imports / props");
|
|
4852
|
+
score = Math.max(score, 0.75);
|
|
4853
|
+
}
|
|
4854
|
+
if (apiNoChange && logicMinimal && !fv.styleDelta.classNameDiff && !fv.styleDelta.tokenUsageDiff && fv.cvaDelta.addedVariants.length === 0 && fv.cvaDelta.modifiedVariants.length === 0) {
|
|
4855
|
+
modes.push("Preset");
|
|
4856
|
+
reasons.push("no API/logic delta \u2014 Preset captures default-prop tweaks");
|
|
4857
|
+
score = Math.max(score, 0.6);
|
|
4858
|
+
}
|
|
4859
|
+
if (fv.structureDelta.isComposition) {
|
|
4860
|
+
modes.push("Compose");
|
|
4861
|
+
reasons.push(
|
|
4862
|
+
`composition of ${fv.structureDelta.atomicChildren.length} atomic children`
|
|
4863
|
+
);
|
|
4864
|
+
score = Math.max(score, 0.65);
|
|
4865
|
+
}
|
|
4866
|
+
if (modes.length === 0) {
|
|
4867
|
+
reasons.push("no axis crossed the 0.6 threshold");
|
|
4868
|
+
return { recommendedModes: ["ManualReview"], confidence: 0.4, reasons };
|
|
4869
|
+
}
|
|
4870
|
+
return { recommendedModes: modes, confidence: score, reasons };
|
|
4871
|
+
}
|
|
4872
|
+
function extractVariantBody(src, key) {
|
|
4873
|
+
const block = extractVariantsBlock(src);
|
|
4874
|
+
if (block === null) return "";
|
|
4875
|
+
const re = new RegExp(`(?:["']?${key}["']?)\\s*:\\s*\\{`);
|
|
4876
|
+
const idx = block.search(re);
|
|
4877
|
+
if (idx < 0) return "";
|
|
4878
|
+
const open = block.indexOf("{", idx);
|
|
4879
|
+
if (open < 0) return "";
|
|
4880
|
+
let depth = 0;
|
|
4881
|
+
for (let i = open; i < block.length; i++) {
|
|
4882
|
+
const c = block[i];
|
|
4883
|
+
if (c === "{") depth++;
|
|
4884
|
+
else if (c === "}") {
|
|
4885
|
+
depth--;
|
|
4886
|
+
if (depth === 0) return block.slice(open + 1, i);
|
|
4887
|
+
}
|
|
4888
|
+
}
|
|
4889
|
+
return "";
|
|
4890
|
+
}
|
|
4891
|
+
function extractClassNameLiterals(src) {
|
|
4892
|
+
const out = [];
|
|
4893
|
+
for (const m of src.matchAll(/className\s*=\s*["'`]([^"'`]*)["'`]/g)) {
|
|
4894
|
+
if (m[1]) out.push(m[1]);
|
|
4895
|
+
}
|
|
4896
|
+
for (const m of src.matchAll(/\b(?:cn|clsx|cva)\s*\(/g)) {
|
|
4897
|
+
const open = (m.index ?? 0) + m[0].length - 1;
|
|
4898
|
+
let depth = 1;
|
|
4899
|
+
let i = open + 1;
|
|
4900
|
+
for (; i < src.length && depth > 0; i++) {
|
|
4901
|
+
const c = src[i];
|
|
4902
|
+
if (c === "(") depth++;
|
|
4903
|
+
else if (c === ")") depth--;
|
|
4904
|
+
}
|
|
4905
|
+
const body = src.slice(open + 1, i - 1);
|
|
4906
|
+
for (const lit of body.matchAll(/["'`]([^"'`]*)["'`]/g)) {
|
|
4907
|
+
if (lit[1]) out.push(lit[1]);
|
|
4908
|
+
}
|
|
4909
|
+
}
|
|
4910
|
+
return out.sort().join("|");
|
|
4911
|
+
}
|
|
4912
|
+
function extractTokenRefs(src) {
|
|
4913
|
+
const out = /* @__PURE__ */ new Set();
|
|
4914
|
+
for (const m of src.matchAll(/var\(--([a-z0-9-]+)\)/g)) {
|
|
4915
|
+
if (m[1]) out.add(m[1]);
|
|
4916
|
+
}
|
|
4917
|
+
for (const m of src.matchAll(/--([a-z][a-z0-9-]*)\s*:/g)) {
|
|
4918
|
+
if (m[1]) out.add(m[1]);
|
|
4919
|
+
}
|
|
4920
|
+
return out;
|
|
4921
|
+
}
|
|
4922
|
+
function extractImportSources(src) {
|
|
4923
|
+
const out = /* @__PURE__ */ new Set();
|
|
4924
|
+
for (const m of src.matchAll(/^\s*import\b[^'"]*['"]([^'"]+)['"]/gm)) {
|
|
4925
|
+
if (m[1]) out.add(m[1]);
|
|
4926
|
+
}
|
|
4927
|
+
return out;
|
|
4928
|
+
}
|
|
4929
|
+
function extractDefaultParamList(src) {
|
|
4930
|
+
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);
|
|
4931
|
+
return m?.[1]?.replace(/\s+/g, " ").trim() ?? "";
|
|
4932
|
+
}
|
|
4028
4933
|
|
|
4029
4934
|
// src/core/ui-upgrade.ts
|
|
4030
4935
|
var nodeRequire = createRequire6(import.meta.url);
|
|
4031
4936
|
function resolvePackageRoot3(packageName) {
|
|
4032
4937
|
const pkgJsonPath = nodeRequire.resolve(`${packageName}/package.json`);
|
|
4033
|
-
return
|
|
4938
|
+
return path23.dirname(pkgJsonPath);
|
|
4034
4939
|
}
|
|
4035
4940
|
async function runUiUpgrade(options) {
|
|
4036
4941
|
const { projectRoot, category, ids = [], trigger } = options;
|
|
@@ -4116,7 +5021,7 @@ async function buildStaging(args) {
|
|
|
4116
5021
|
}
|
|
4117
5022
|
const bizRoot = args.bizUiPackageRoot ?? resolvePackageRoot3("@teamix-evo/biz-ui");
|
|
4118
5023
|
const variant = lineageReport.installedVariant ?? "_flat";
|
|
4119
|
-
const variantDir =
|
|
5024
|
+
const variantDir = path23.join(bizRoot, "variants", variant);
|
|
4120
5025
|
const variantManifest = await loadVariantUiPackageManifest(variantDir);
|
|
4121
5026
|
const uiRoot = args.uiPackageRoot ?? resolvePackageRoot3("@teamix-evo/ui");
|
|
4122
5027
|
const uiManifest = await loadUiPackageManifest2(uiRoot);
|
|
@@ -4168,7 +5073,7 @@ var META = {
|
|
|
4168
5073
|
};
|
|
4169
5074
|
function makeUpgradeCommand(category) {
|
|
4170
5075
|
const meta = META[category];
|
|
4171
|
-
return new
|
|
5076
|
+
return new Command19("upgrade").description(
|
|
4172
5077
|
`\u4E3A ${category} \u7EC4\u4EF6\u751F\u6210\u5347\u7EA7 staging\uFF08\u4E0D\u5199 src\uFF0C\u9700\u901A\u8FC7 teamix-evo-upgrade skill \u5E94\u7528\uFF09`
|
|
4173
5078
|
).argument(
|
|
4174
5079
|
"[ids...]",
|
|
@@ -4230,36 +5135,865 @@ function makeUpgradeCommand(category) {
|
|
|
4230
5135
|
return;
|
|
4231
5136
|
}
|
|
4232
5137
|
}
|
|
4233
|
-
} catch (err) {
|
|
5138
|
+
} catch (err) {
|
|
5139
|
+
logger.error(
|
|
5140
|
+
`Failed to build ${category} staging: ${getErrorMessage(err)}`
|
|
5141
|
+
);
|
|
5142
|
+
logger.debug(err.stack ?? "");
|
|
5143
|
+
process.exitCode = 1;
|
|
5144
|
+
}
|
|
5145
|
+
});
|
|
5146
|
+
}
|
|
5147
|
+
|
|
5148
|
+
// src/commands/ui/upgrade.ts
|
|
5149
|
+
var upgradeCommand = makeUpgradeCommand("ui");
|
|
5150
|
+
|
|
5151
|
+
// src/commands/ui/promote.ts
|
|
5152
|
+
import { Command as Command20 } from "commander";
|
|
5153
|
+
|
|
5154
|
+
// src/core/ui-promote.ts
|
|
5155
|
+
import * as fs19 from "fs/promises";
|
|
5156
|
+
import * as path25 from "path";
|
|
5157
|
+
|
|
5158
|
+
// src/core/ui-promote-imports.ts
|
|
5159
|
+
import * as fs18 from "fs/promises";
|
|
5160
|
+
import * as path24 from "path";
|
|
5161
|
+
var SOURCE_FILE_EXT = /\.(tsx?|jsx?|mts|cts)$/;
|
|
5162
|
+
var STATIC_IMPORT_FROM = /^(\s*(?:import|export)\s[\s\S]*?from\s+['"])([^'"\n]+)(['"])/;
|
|
5163
|
+
var SIDE_EFFECT_IMPORT = /^(\s*import\s+['"])([^'"\n]+)(['"])/;
|
|
5164
|
+
var DYNAMIC_IMPORT_STR = /\bimport\s*\(\s*['"]([^'"]+)['"]\s*\)/g;
|
|
5165
|
+
var DYNAMIC_IMPORT_TPL = /\bimport\s*\(\s*`/g;
|
|
5166
|
+
async function rewriteProjectImports(projectRoot, specs, options = {}) {
|
|
5167
|
+
if (specs.length === 0) return { rewritten: [], skipped: [] };
|
|
5168
|
+
const scanRoot = options.scanRoot ?? path24.join(projectRoot, "src");
|
|
5169
|
+
const dryRun = options.dryRun ?? false;
|
|
5170
|
+
const backup = options.backup ?? true;
|
|
5171
|
+
const files = await collectTsFiles(scanRoot);
|
|
5172
|
+
const rewritten = [];
|
|
5173
|
+
const skipped = [];
|
|
5174
|
+
for (const file of files) {
|
|
5175
|
+
const raw = await readFileOrNull(file);
|
|
5176
|
+
if (raw === null) continue;
|
|
5177
|
+
const rel2 = toRel(projectRoot, file);
|
|
5178
|
+
const lines = raw.split("\n");
|
|
5179
|
+
let changed = false;
|
|
5180
|
+
for (let i = 0; i < lines.length; i++) {
|
|
5181
|
+
const orig = lines[i] ?? "";
|
|
5182
|
+
const staticMatch = orig.match(STATIC_IMPORT_FROM);
|
|
5183
|
+
if (staticMatch) {
|
|
5184
|
+
const whole = staticMatch[0];
|
|
5185
|
+
const prefix = staticMatch[1] ?? "";
|
|
5186
|
+
const spec = staticMatch[2] ?? "";
|
|
5187
|
+
const quote2 = staticMatch[3] ?? "";
|
|
5188
|
+
const replaced = applySpecs(spec, specs);
|
|
5189
|
+
if (replaced !== null) {
|
|
5190
|
+
if (/^\s*export\s/.test(orig)) {
|
|
5191
|
+
skipped.push({
|
|
5192
|
+
file: rel2,
|
|
5193
|
+
line: i + 1,
|
|
5194
|
+
reason: "aliased-export",
|
|
5195
|
+
snippet: orig.trim()
|
|
5196
|
+
});
|
|
5197
|
+
continue;
|
|
5198
|
+
}
|
|
5199
|
+
lines[i] = `${prefix}${replaced}${quote2}${orig.slice(whole.length)}`;
|
|
5200
|
+
changed = true;
|
|
5201
|
+
}
|
|
5202
|
+
continue;
|
|
5203
|
+
}
|
|
5204
|
+
const sideMatch = orig.match(SIDE_EFFECT_IMPORT);
|
|
5205
|
+
if (sideMatch) {
|
|
5206
|
+
const whole = sideMatch[0];
|
|
5207
|
+
const prefix = sideMatch[1] ?? "";
|
|
5208
|
+
const spec = sideMatch[2] ?? "";
|
|
5209
|
+
const quote2 = sideMatch[3] ?? "";
|
|
5210
|
+
const replaced = applySpecs(spec, specs);
|
|
5211
|
+
if (replaced !== null) {
|
|
5212
|
+
lines[i] = `${prefix}${replaced}${quote2}${orig.slice(whole.length)}`;
|
|
5213
|
+
changed = true;
|
|
5214
|
+
}
|
|
5215
|
+
continue;
|
|
5216
|
+
}
|
|
5217
|
+
for (const m of orig.matchAll(DYNAMIC_IMPORT_STR)) {
|
|
5218
|
+
const target = m[1] ?? "";
|
|
5219
|
+
if (specs.some((s) => isAffected(target, s.oldPath))) {
|
|
5220
|
+
skipped.push({
|
|
5221
|
+
file: rel2,
|
|
5222
|
+
line: i + 1,
|
|
5223
|
+
reason: "dynamic-import",
|
|
5224
|
+
snippet: orig.trim()
|
|
5225
|
+
});
|
|
5226
|
+
}
|
|
5227
|
+
}
|
|
5228
|
+
if (DYNAMIC_IMPORT_TPL.test(orig)) {
|
|
5229
|
+
skipped.push({
|
|
5230
|
+
file: rel2,
|
|
5231
|
+
line: i + 1,
|
|
5232
|
+
reason: "string-template",
|
|
5233
|
+
snippet: orig.trim()
|
|
5234
|
+
});
|
|
5235
|
+
DYNAMIC_IMPORT_TPL.lastIndex = 0;
|
|
5236
|
+
}
|
|
5237
|
+
}
|
|
5238
|
+
if (changed) {
|
|
5239
|
+
const updated = lines.join("\n");
|
|
5240
|
+
if (!dryRun) {
|
|
5241
|
+
if (backup) await backupFile(file, projectRoot);
|
|
5242
|
+
await writeFileSafe(file, updated);
|
|
5243
|
+
}
|
|
5244
|
+
rewritten.push(rel2);
|
|
5245
|
+
}
|
|
5246
|
+
}
|
|
5247
|
+
return { rewritten, skipped };
|
|
5248
|
+
}
|
|
5249
|
+
function applySpecs(target, specs) {
|
|
5250
|
+
for (const s of specs) {
|
|
5251
|
+
if (target === s.oldPath) return s.newPath;
|
|
5252
|
+
if (target.startsWith(s.oldPath + "/")) {
|
|
5253
|
+
return s.newPath + target.slice(s.oldPath.length);
|
|
5254
|
+
}
|
|
5255
|
+
}
|
|
5256
|
+
return null;
|
|
5257
|
+
}
|
|
5258
|
+
function isAffected(target, oldPath) {
|
|
5259
|
+
return target === oldPath || target.startsWith(oldPath + "/");
|
|
5260
|
+
}
|
|
5261
|
+
async function collectTsFiles(root) {
|
|
5262
|
+
const out = [];
|
|
5263
|
+
async function walk(dir) {
|
|
5264
|
+
let entries;
|
|
5265
|
+
try {
|
|
5266
|
+
entries = await fs18.readdir(dir, { withFileTypes: true });
|
|
5267
|
+
} catch (err) {
|
|
5268
|
+
if (err.code === "ENOENT") return;
|
|
5269
|
+
throw err;
|
|
5270
|
+
}
|
|
5271
|
+
for (const e of entries) {
|
|
5272
|
+
if (e.name.startsWith(".") || e.name === "node_modules" || e.name === "dist") {
|
|
5273
|
+
continue;
|
|
5274
|
+
}
|
|
5275
|
+
const full = path24.join(dir, e.name);
|
|
5276
|
+
if (e.isDirectory()) {
|
|
5277
|
+
await walk(full);
|
|
5278
|
+
} else if (e.isFile() && SOURCE_FILE_EXT.test(e.name)) {
|
|
5279
|
+
out.push(full);
|
|
5280
|
+
}
|
|
5281
|
+
}
|
|
5282
|
+
}
|
|
5283
|
+
await walk(root);
|
|
5284
|
+
return out;
|
|
5285
|
+
}
|
|
5286
|
+
function toRel(projectRoot, abs) {
|
|
5287
|
+
return path24.relative(projectRoot, abs).split(path24.sep).join("/");
|
|
5288
|
+
}
|
|
5289
|
+
|
|
5290
|
+
// src/core/ui-promote.ts
|
|
5291
|
+
var TEAMIX_DIR5 = ".teamix-evo";
|
|
5292
|
+
var STAGING_DIR2 = ".upgrade-staging";
|
|
5293
|
+
async function runUiPromote(options) {
|
|
5294
|
+
const { projectRoot } = options;
|
|
5295
|
+
const dryRun = options.dryRun ?? false;
|
|
5296
|
+
const promotions = [];
|
|
5297
|
+
const fileChanges = [];
|
|
5298
|
+
const skipped = [];
|
|
5299
|
+
const rewritten = [];
|
|
5300
|
+
const config = await readProjectConfig(projectRoot);
|
|
5301
|
+
if (!config?.packages?.ui?.aliases) {
|
|
5302
|
+
return {
|
|
5303
|
+
status: "not-initialized",
|
|
5304
|
+
promotions,
|
|
5305
|
+
fileChanges,
|
|
5306
|
+
importRewrite: { rewritten, skipped }
|
|
5307
|
+
};
|
|
5308
|
+
}
|
|
5309
|
+
const aliases = config.packages.ui.aliases;
|
|
5310
|
+
const stagingDir = options.stagingDir ?? await findLatestUiStagingDir(projectRoot);
|
|
5311
|
+
if (!stagingDir) {
|
|
5312
|
+
return {
|
|
5313
|
+
status: "no-staging",
|
|
5314
|
+
promotions,
|
|
5315
|
+
fileChanges,
|
|
5316
|
+
importRewrite: { rewritten, skipped }
|
|
5317
|
+
};
|
|
5318
|
+
}
|
|
5319
|
+
const meta = await readStagingMeta(stagingDir);
|
|
5320
|
+
if (!meta || meta.entries.length === 0) {
|
|
5321
|
+
return {
|
|
5322
|
+
status: "no-entries",
|
|
5323
|
+
stagingDir,
|
|
5324
|
+
promotions,
|
|
5325
|
+
fileChanges,
|
|
5326
|
+
importRewrite: { rewritten, skipped }
|
|
5327
|
+
};
|
|
5328
|
+
}
|
|
5329
|
+
const onlyIds = options.ids && options.ids.length > 0 ? new Set(options.ids) : null;
|
|
5330
|
+
const targetEntries = onlyIds ? meta.entries.filter((e) => onlyIds.has(e.id)) : meta.entries;
|
|
5331
|
+
const newResources = [];
|
|
5332
|
+
for (const entry of targetEntries) {
|
|
5333
|
+
const decision = decideModes(entry, options.modesOverride);
|
|
5334
|
+
try {
|
|
5335
|
+
const outcome = await promoteEntry({
|
|
5336
|
+
projectRoot,
|
|
5337
|
+
stagingDir,
|
|
5338
|
+
entry,
|
|
5339
|
+
modes: decision,
|
|
5340
|
+
aliases,
|
|
5341
|
+
keepOriginalNames: options.keepOriginalNames ?? false,
|
|
5342
|
+
dryRun
|
|
5343
|
+
});
|
|
5344
|
+
promotions.push(outcome.promotion);
|
|
5345
|
+
fileChanges.push(...outcome.fileChanges);
|
|
5346
|
+
newResources.push(...outcome.newResources);
|
|
5347
|
+
if (decision.includes("Coexist") && options.migrateImports && outcome.coexistRewriteSpec) {
|
|
5348
|
+
const r = await rewriteProjectImports(
|
|
5349
|
+
projectRoot,
|
|
5350
|
+
[outcome.coexistRewriteSpec],
|
|
5351
|
+
{ dryRun, backup: true }
|
|
5352
|
+
);
|
|
5353
|
+
rewritten.push(...r.rewritten);
|
|
5354
|
+
skipped.push(...r.skipped);
|
|
5355
|
+
for (const rel2 of r.rewritten) {
|
|
5356
|
+
fileChanges.push({
|
|
5357
|
+
kind: "modified",
|
|
5358
|
+
path: rel2,
|
|
5359
|
+
step: "ui-promote-imports"
|
|
5360
|
+
});
|
|
5361
|
+
}
|
|
5362
|
+
}
|
|
5363
|
+
} catch (err) {
|
|
5364
|
+
logger.error(`Promotion failed for ${entry.id}: ${getErrorMessage(err)}`);
|
|
5365
|
+
promotions.push({
|
|
5366
|
+
id: entry.id,
|
|
5367
|
+
modes: decision,
|
|
5368
|
+
status: "skipped",
|
|
5369
|
+
outputs: [],
|
|
5370
|
+
reason: getErrorMessage(err)
|
|
5371
|
+
});
|
|
5372
|
+
}
|
|
5373
|
+
}
|
|
5374
|
+
if (!dryRun && newResources.length > 0) {
|
|
5375
|
+
await mergeIntoInstalledManifest2(projectRoot, newResources);
|
|
5376
|
+
}
|
|
5377
|
+
return {
|
|
5378
|
+
status: "promoted",
|
|
5379
|
+
stagingDir,
|
|
5380
|
+
promotions,
|
|
5381
|
+
fileChanges,
|
|
5382
|
+
importRewrite: { rewritten, skipped }
|
|
5383
|
+
};
|
|
5384
|
+
}
|
|
5385
|
+
async function promoteEntry(args) {
|
|
5386
|
+
const { entry, modes, projectRoot, stagingDir, aliases, dryRun } = args;
|
|
5387
|
+
const fileChanges = [];
|
|
5388
|
+
const newResources = [];
|
|
5389
|
+
let coexistRewriteSpec;
|
|
5390
|
+
if (modes.includes("ManualReview")) {
|
|
5391
|
+
return {
|
|
5392
|
+
promotion: {
|
|
5393
|
+
id: entry.id,
|
|
5394
|
+
modes,
|
|
5395
|
+
status: "manual-review",
|
|
5396
|
+
outputs: [],
|
|
5397
|
+
reason: "Confidence below threshold or fileType non-component \u2014 see staging meta.json"
|
|
5398
|
+
},
|
|
5399
|
+
fileChanges,
|
|
5400
|
+
newResources
|
|
5401
|
+
};
|
|
5402
|
+
}
|
|
5403
|
+
if (modes.includes("TokenOnly")) {
|
|
5404
|
+
return {
|
|
5405
|
+
promotion: {
|
|
5406
|
+
id: entry.id,
|
|
5407
|
+
modes,
|
|
5408
|
+
status: "token-only",
|
|
5409
|
+
outputs: [],
|
|
5410
|
+
reason: "\u5DEE\u5F02\u4EC5\u9650 className/token \u2014 \u7F16\u8F91 tokens/tokens.overrides.css \u800C\u975E\u521B\u5EFA biz-ui \u6587\u4EF6"
|
|
5411
|
+
},
|
|
5412
|
+
fileChanges,
|
|
5413
|
+
newResources
|
|
5414
|
+
};
|
|
5415
|
+
}
|
|
5416
|
+
const currentSrc = await readStagedFile(stagingDir, entry, "current");
|
|
5417
|
+
const incomingSrc = await readStagedFile(stagingDir, entry, "incoming");
|
|
5418
|
+
const businessDir = path25.join(projectRoot, aliases.business);
|
|
5419
|
+
const uiDir = path25.join(projectRoot, aliases.components);
|
|
5420
|
+
const businessRel = aliases.business;
|
|
5421
|
+
const uiRel = aliases.components;
|
|
5422
|
+
if (modes.includes("Coexist")) {
|
|
5423
|
+
if (!currentSrc) {
|
|
5424
|
+
throw new Error("Coexist requires a current.tsx in staging");
|
|
5425
|
+
}
|
|
5426
|
+
if (!incomingSrc) {
|
|
5427
|
+
throw new Error("Coexist requires an incoming.tsx in staging");
|
|
5428
|
+
}
|
|
5429
|
+
const legacyId = `legacy-${entry.id}`;
|
|
5430
|
+
const legacyAbs = path25.join(businessDir, `${legacyId}.tsx`);
|
|
5431
|
+
const uiAbs = path25.join(uiDir, `${entry.id}.tsx`);
|
|
5432
|
+
if (await fileExists(uiAbs)) {
|
|
5433
|
+
if (!dryRun) await backupFile(uiAbs, projectRoot);
|
|
5434
|
+
fileChanges.push({
|
|
5435
|
+
kind: "backed-up",
|
|
5436
|
+
path: posix2(path25.relative(projectRoot, uiAbs)),
|
|
5437
|
+
step: "ui-promote",
|
|
5438
|
+
detail: "pre-coexist user version"
|
|
5439
|
+
});
|
|
5440
|
+
}
|
|
5441
|
+
const renamed = args.keepOriginalNames ? currentSrc : prefixExportsLegacy(currentSrc, entry.id);
|
|
5442
|
+
if (!dryRun) {
|
|
5443
|
+
await ensureDir(businessDir);
|
|
5444
|
+
await writeFileSafe(legacyAbs, renamed);
|
|
5445
|
+
}
|
|
5446
|
+
fileChanges.push({
|
|
5447
|
+
kind: "created",
|
|
5448
|
+
path: posix2(path25.join(businessRel, `${legacyId}.tsx`)),
|
|
5449
|
+
step: "ui-promote",
|
|
5450
|
+
detail: "Coexist legacy snapshot"
|
|
5451
|
+
});
|
|
5452
|
+
newResources.push({
|
|
5453
|
+
id: `${legacyId}:${legacyId}.tsx`,
|
|
5454
|
+
target: legacyAbs,
|
|
5455
|
+
hash: computeHash(renamed),
|
|
5456
|
+
strategy: "frozen"
|
|
5457
|
+
});
|
|
5458
|
+
if (!dryRun) await writeFileSafe(uiAbs, incomingSrc);
|
|
5459
|
+
fileChanges.push({
|
|
5460
|
+
kind: "modified",
|
|
5461
|
+
path: posix2(path25.join(uiRel, `${entry.id}.tsx`)),
|
|
5462
|
+
step: "ui-promote",
|
|
5463
|
+
detail: "Coexist upstream restore"
|
|
5464
|
+
});
|
|
5465
|
+
coexistRewriteSpec = {
|
|
5466
|
+
oldPath: importPathFor(uiRel, entry.id),
|
|
5467
|
+
newPath: importPathFor(businessRel, legacyId)
|
|
5468
|
+
};
|
|
5469
|
+
return {
|
|
5470
|
+
promotion: {
|
|
5471
|
+
id: entry.id,
|
|
5472
|
+
modes,
|
|
5473
|
+
status: "promoted",
|
|
5474
|
+
outputs: [
|
|
5475
|
+
posix2(path25.join(businessRel, `${legacyId}.tsx`)),
|
|
5476
|
+
posix2(path25.join(uiRel, `${entry.id}.tsx`))
|
|
5477
|
+
],
|
|
5478
|
+
reason: "\u53CC\u8F68\uFF1Aupstream \u88C5\u56DE ui/\u3001\u7528\u6237\u7248\u6539\u540D\u8FDB business/legacy-"
|
|
5479
|
+
},
|
|
5480
|
+
fileChanges,
|
|
5481
|
+
newResources,
|
|
5482
|
+
coexistRewriteSpec
|
|
5483
|
+
};
|
|
5484
|
+
}
|
|
5485
|
+
if (modes.includes("Fork")) {
|
|
5486
|
+
if (!currentSrc) throw new Error("Fork requires a current.tsx in staging");
|
|
5487
|
+
const forkAbs = path25.join(businessDir, `${entry.id}.tsx`);
|
|
5488
|
+
if (await fileExists(forkAbs)) {
|
|
5489
|
+
if (!dryRun) await backupFile(forkAbs, projectRoot);
|
|
5490
|
+
fileChanges.push({
|
|
5491
|
+
kind: "backed-up",
|
|
5492
|
+
path: posix2(path25.relative(projectRoot, forkAbs)),
|
|
5493
|
+
step: "ui-promote",
|
|
5494
|
+
detail: "pre-fork existing business file"
|
|
5495
|
+
});
|
|
5496
|
+
}
|
|
5497
|
+
if (!dryRun) {
|
|
5498
|
+
await ensureDir(businessDir);
|
|
5499
|
+
await writeFileSafe(forkAbs, currentSrc);
|
|
5500
|
+
}
|
|
5501
|
+
fileChanges.push({
|
|
5502
|
+
kind: "created",
|
|
5503
|
+
path: posix2(path25.join(businessRel, `${entry.id}.tsx`)),
|
|
5504
|
+
step: "ui-promote",
|
|
5505
|
+
detail: "Fork \u2014 full custom copy"
|
|
5506
|
+
});
|
|
5507
|
+
newResources.push({
|
|
5508
|
+
id: `${entry.id}:${entry.id}.tsx`,
|
|
5509
|
+
target: forkAbs,
|
|
5510
|
+
hash: computeHash(currentSrc),
|
|
5511
|
+
strategy: "frozen"
|
|
5512
|
+
});
|
|
5513
|
+
return {
|
|
5514
|
+
promotion: {
|
|
5515
|
+
id: entry.id,
|
|
5516
|
+
modes,
|
|
5517
|
+
status: "promoted",
|
|
5518
|
+
outputs: [posix2(path25.join(businessRel, `${entry.id}.tsx`))],
|
|
5519
|
+
reason: "Fork\uFF1A\u5B8C\u6574\u4FDD\u7559\u7528\u6237\u5B9E\u73B0"
|
|
5520
|
+
},
|
|
5521
|
+
fileChanges,
|
|
5522
|
+
newResources
|
|
5523
|
+
};
|
|
5524
|
+
}
|
|
5525
|
+
const businessAbs = path25.join(businessDir, `${entry.id}.tsx`);
|
|
5526
|
+
const composedSource = composeBusinessFile({
|
|
5527
|
+
id: entry.id,
|
|
5528
|
+
modes,
|
|
5529
|
+
aliases,
|
|
5530
|
+
entry,
|
|
5531
|
+
incomingSource: incomingSrc
|
|
5532
|
+
});
|
|
5533
|
+
if (await fileExists(businessAbs)) {
|
|
5534
|
+
if (!dryRun) await backupFile(businessAbs, projectRoot);
|
|
5535
|
+
fileChanges.push({
|
|
5536
|
+
kind: "backed-up",
|
|
5537
|
+
path: posix2(path25.relative(projectRoot, businessAbs)),
|
|
5538
|
+
step: "ui-promote",
|
|
5539
|
+
detail: "pre-promote business file"
|
|
5540
|
+
});
|
|
5541
|
+
}
|
|
5542
|
+
if (!dryRun) {
|
|
5543
|
+
await ensureDir(businessDir);
|
|
5544
|
+
await writeFileSafe(businessAbs, composedSource);
|
|
5545
|
+
}
|
|
5546
|
+
fileChanges.push({
|
|
5547
|
+
kind: "created",
|
|
5548
|
+
path: posix2(path25.join(businessRel, `${entry.id}.tsx`)),
|
|
5549
|
+
step: "ui-promote",
|
|
5550
|
+
detail: `modes: ${modes.join("+")}`
|
|
5551
|
+
});
|
|
5552
|
+
newResources.push({
|
|
5553
|
+
id: `${entry.id}:${entry.id}.tsx`,
|
|
5554
|
+
target: businessAbs,
|
|
5555
|
+
hash: computeHash(composedSource),
|
|
5556
|
+
strategy: "frozen"
|
|
5557
|
+
});
|
|
5558
|
+
return {
|
|
5559
|
+
promotion: {
|
|
5560
|
+
id: entry.id,
|
|
5561
|
+
modes,
|
|
5562
|
+
status: "promoted",
|
|
5563
|
+
outputs: [posix2(path25.join(businessRel, `${entry.id}.tsx`))],
|
|
5564
|
+
reason: `modes: ${modes.join("+")} \u5DF2\u751F\u6210 business/${entry.id}.tsx`
|
|
5565
|
+
},
|
|
5566
|
+
fileChanges,
|
|
5567
|
+
newResources
|
|
5568
|
+
};
|
|
5569
|
+
}
|
|
5570
|
+
function composeBusinessFile(args) {
|
|
5571
|
+
const { id, modes, aliases, entry } = args;
|
|
5572
|
+
const componentName = pascalCase(id);
|
|
5573
|
+
const upstreamImport = importPathFor(aliases.components, id);
|
|
5574
|
+
const hasVariant = modes.includes("Variant");
|
|
5575
|
+
const hasWrapper = modes.includes("Wrapper");
|
|
5576
|
+
const hasPreset = modes.includes("Preset");
|
|
5577
|
+
const hasCompose = modes.includes("Compose");
|
|
5578
|
+
const banner = renderBanner({ id, modes, entry });
|
|
5579
|
+
const importLines = [
|
|
5580
|
+
`import * as React from 'react';`,
|
|
5581
|
+
`import { ${componentName} as Base${componentName}, type ${componentName}Props as Base${componentName}Props } from '${upstreamImport}';`
|
|
5582
|
+
];
|
|
5583
|
+
if (hasCompose && entry.promotion?.featureVector?.structureDelta?.atomicChildren) {
|
|
5584
|
+
const children = entry.promotion.featureVector.structureDelta.atomicChildren.filter((c) => c !== componentName).slice(0, 4);
|
|
5585
|
+
for (const child of children) {
|
|
5586
|
+
importLines.push(
|
|
5587
|
+
`// TODO: confirm import path for atomic child <${child} />`
|
|
5588
|
+
);
|
|
5589
|
+
}
|
|
5590
|
+
}
|
|
5591
|
+
const body = [];
|
|
5592
|
+
body.push(
|
|
5593
|
+
`export interface ${componentName}Props extends Base${componentName}Props {`
|
|
5594
|
+
);
|
|
5595
|
+
if (hasWrapper) {
|
|
5596
|
+
body.push(
|
|
5597
|
+
` // TODO(Wrapper): add business-only props (e.g. loading?: boolean, confirm?: () => void)`
|
|
5598
|
+
);
|
|
5599
|
+
}
|
|
5600
|
+
body.push(`}`);
|
|
5601
|
+
body.push("");
|
|
5602
|
+
if (hasVariant) {
|
|
5603
|
+
body.push(
|
|
5604
|
+
`// TODO(Variant): extend cva variants from upstream \u2014 when upstream`
|
|
5605
|
+
);
|
|
5606
|
+
body.push(
|
|
5607
|
+
`// exports \`${id}Variants\`, import + spread it here; when not, copy the`
|
|
5608
|
+
);
|
|
5609
|
+
body.push(
|
|
5610
|
+
`// configuration into this file and annotate \`SOURCE_OF_TRUTH\`.`
|
|
5611
|
+
);
|
|
5612
|
+
body.push("");
|
|
5613
|
+
}
|
|
5614
|
+
if (hasPreset) {
|
|
5615
|
+
body.push(
|
|
5616
|
+
`export const ${componentName}: React.FC<${componentName}Props> = ({`
|
|
5617
|
+
);
|
|
5618
|
+
body.push(
|
|
5619
|
+
` // TODO(Preset): fill defaults migrated from your previous user version,`
|
|
5620
|
+
);
|
|
5621
|
+
body.push(
|
|
5622
|
+
` // e.g. \`size = 'md'\`, \`variant = 'primary'\`.`
|
|
5623
|
+
);
|
|
5624
|
+
body.push(` ...props`);
|
|
5625
|
+
body.push(`}) => {`);
|
|
5626
|
+
} else {
|
|
5627
|
+
body.push(
|
|
5628
|
+
`export const ${componentName}: React.FC<${componentName}Props> = (props) => {`
|
|
5629
|
+
);
|
|
5630
|
+
}
|
|
5631
|
+
if (hasWrapper) {
|
|
5632
|
+
body.push(
|
|
5633
|
+
` // TODO(Wrapper): pull business state / effects out of props before rendering.`
|
|
5634
|
+
);
|
|
5635
|
+
}
|
|
5636
|
+
if (hasCompose) {
|
|
5637
|
+
body.push(
|
|
5638
|
+
` // TODO(Compose): assemble atomic children \u2014 ${entry.promotion?.featureVector?.structureDelta?.atomicChildren?.join(
|
|
5639
|
+
", "
|
|
5640
|
+
) ?? "..."}`
|
|
5641
|
+
);
|
|
5642
|
+
}
|
|
5643
|
+
body.push(` return <Base${componentName} {...props} />;`);
|
|
5644
|
+
body.push(`};`);
|
|
5645
|
+
return [banner, "", importLines.join("\n"), "", body.join("\n"), ""].join(
|
|
5646
|
+
"\n"
|
|
5647
|
+
);
|
|
5648
|
+
}
|
|
5649
|
+
function renderBanner(args) {
|
|
5650
|
+
const reasons = args.entry.promotion?.reasons ?? [];
|
|
5651
|
+
const lines = [
|
|
5652
|
+
`/**`,
|
|
5653
|
+
` * Generated by \`teamix-evo ui promote-to-biz\` (Init landing plan \xA7C.3).`,
|
|
5654
|
+
` *`,
|
|
5655
|
+
` * id : ${args.id}`,
|
|
5656
|
+
` * modes : ${args.modes.join(" + ")}`,
|
|
5657
|
+
` * note : \u4E1A\u52A1\u5B9A\u5236\u5C42\uFF1B\u4E0A\u6E38 ui/${args.id}.tsx \u4ECD\u662F SOURCE_OF_TRUTH\uFF0C\u8BF7\u907F\u514D\u5728\u6B64\u518D\u6B21\u590D\u5236\u5176\u5185\u90E8\u7EC6\u8282\u3002`
|
|
5658
|
+
];
|
|
5659
|
+
if (reasons.length > 0) {
|
|
5660
|
+
lines.push(" *");
|
|
5661
|
+
lines.push(" * \u63A8\u8350\u7406\u7531\uFF1A");
|
|
5662
|
+
for (const r of reasons) lines.push(` * \u2022 ${r}`);
|
|
5663
|
+
}
|
|
5664
|
+
lines.push(" */");
|
|
5665
|
+
return lines.join("\n");
|
|
5666
|
+
}
|
|
5667
|
+
async function findLatestUiStagingDir(projectRoot) {
|
|
5668
|
+
const base = path25.join(projectRoot, TEAMIX_DIR5, STAGING_DIR2);
|
|
5669
|
+
let entries;
|
|
5670
|
+
try {
|
|
5671
|
+
entries = await fs19.readdir(base, { withFileTypes: true });
|
|
5672
|
+
} catch (err) {
|
|
5673
|
+
if (err.code === "ENOENT") return null;
|
|
5674
|
+
throw err;
|
|
5675
|
+
}
|
|
5676
|
+
const candidates = entries.filter((e) => e.isDirectory() && e.name.startsWith("ui-")).map((e) => e.name).sort();
|
|
5677
|
+
const last = candidates[candidates.length - 1];
|
|
5678
|
+
return last ? path25.join(base, last) : null;
|
|
5679
|
+
}
|
|
5680
|
+
async function readStagingMeta(stagingDir) {
|
|
5681
|
+
const raw = await readFileOrNull(path25.join(stagingDir, "meta.json"));
|
|
5682
|
+
if (raw === null) return null;
|
|
5683
|
+
try {
|
|
5684
|
+
return JSON.parse(raw);
|
|
5685
|
+
} catch {
|
|
5686
|
+
return null;
|
|
5687
|
+
}
|
|
5688
|
+
}
|
|
5689
|
+
async function readStagedFile(stagingDir, entry, kind) {
|
|
5690
|
+
const ext = path25.extname(entry.current.target) || ".tsx";
|
|
5691
|
+
const direct = path25.join(stagingDir, entry.id, `${kind}${ext}`);
|
|
5692
|
+
const directRead = await readFileOrNull(direct);
|
|
5693
|
+
if (directRead !== null) return directRead;
|
|
5694
|
+
if (kind === "incoming" && entry.incoming?.relPath) {
|
|
5695
|
+
return readFileOrNull(path25.join(stagingDir, entry.incoming.relPath));
|
|
5696
|
+
}
|
|
5697
|
+
return null;
|
|
5698
|
+
}
|
|
5699
|
+
function decideModes(entry, override) {
|
|
5700
|
+
if (override && override.length > 0) return override;
|
|
5701
|
+
const recommended = entry.promotion?.recommendedModes;
|
|
5702
|
+
if (recommended && recommended.length > 0) return recommended;
|
|
5703
|
+
return ["ManualReview"];
|
|
5704
|
+
}
|
|
5705
|
+
async function mergeIntoInstalledManifest2(projectRoot, newResources) {
|
|
5706
|
+
const installed = await readInstalledManifest(
|
|
5707
|
+
projectRoot
|
|
5708
|
+
) ?? { schemaVersion: 1, installed: [] };
|
|
5709
|
+
const idx = installed.installed.findIndex(
|
|
5710
|
+
(p2) => p2.package === "@teamix-evo/biz-ui-promoted"
|
|
5711
|
+
);
|
|
5712
|
+
const prior = idx >= 0 ? installed.installed[idx] : void 0;
|
|
5713
|
+
const merged = /* @__PURE__ */ new Map();
|
|
5714
|
+
for (const r of prior?.resources ?? []) merged.set(r.id, r);
|
|
5715
|
+
for (const r of newResources) merged.set(r.id, r);
|
|
5716
|
+
const next = {
|
|
5717
|
+
package: "@teamix-evo/biz-ui-promoted",
|
|
5718
|
+
variant: "_promoted",
|
|
5719
|
+
version: "0.0.0",
|
|
5720
|
+
installedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
5721
|
+
resources: Array.from(merged.values())
|
|
5722
|
+
};
|
|
5723
|
+
if (idx >= 0) installed.installed[idx] = next;
|
|
5724
|
+
else installed.installed.push(next);
|
|
5725
|
+
await writeInstalledManifest(projectRoot, installed);
|
|
5726
|
+
}
|
|
5727
|
+
function prefixExportsLegacy(source, id) {
|
|
5728
|
+
const stem = pascalCase(id);
|
|
5729
|
+
const stemRe = new RegExp(`\\b${stem}([A-Z]\\w*)?\\b`, "g");
|
|
5730
|
+
let out = source;
|
|
5731
|
+
out = out.replace(
|
|
5732
|
+
new RegExp(
|
|
5733
|
+
`(\\bexport\\s+(?:const|let|var|function|class|interface|type|enum)\\s+)(${stem}[A-Z]?\\w*)\\b`,
|
|
5734
|
+
"g"
|
|
5735
|
+
),
|
|
5736
|
+
(_full, prefix, name) => `${prefix}Legacy${name}`
|
|
5737
|
+
);
|
|
5738
|
+
out = out.replace(/export\s*\{([^}]+)\}/g, (full, body) => {
|
|
5739
|
+
const replacedBody = body.replace(stemRe, (m) => `Legacy${m}`);
|
|
5740
|
+
return `export {${replacedBody}}`;
|
|
5741
|
+
});
|
|
5742
|
+
out = out.replace(
|
|
5743
|
+
new RegExp(`(\\bexport\\s+default\\s+)(${stem}[A-Z]?\\w*)(\\s*;)`, "g"),
|
|
5744
|
+
(_full, prefix, name, suffix) => `${prefix}Legacy${name}${suffix}`
|
|
5745
|
+
);
|
|
5746
|
+
out = out.replace(
|
|
5747
|
+
new RegExp(
|
|
5748
|
+
`(\\bexport\\s+default\\s+(?:function|class)\\s+)(${stem}[A-Z]?\\w*)\\b`,
|
|
5749
|
+
"g"
|
|
5750
|
+
),
|
|
5751
|
+
(_full, prefix, name) => `${prefix}Legacy${name}`
|
|
5752
|
+
);
|
|
5753
|
+
return out;
|
|
5754
|
+
}
|
|
5755
|
+
function pascalCase(id) {
|
|
5756
|
+
return id.split(/[-_]/g).filter(Boolean).map((p2) => p2[0].toUpperCase() + p2.slice(1)).join("");
|
|
5757
|
+
}
|
|
5758
|
+
function importPathFor(aliasDir, id) {
|
|
5759
|
+
const trimmed = aliasDir.replace(/^\.\//, "").replace(/\/$/, "");
|
|
5760
|
+
const root = trimmed.startsWith("src/") ? `@/${trimmed.slice("src/".length)}` : `@/${trimmed}`;
|
|
5761
|
+
return `${root}/${id}`;
|
|
5762
|
+
}
|
|
5763
|
+
function posix2(p2) {
|
|
5764
|
+
return p2.split(path25.sep).join("/");
|
|
5765
|
+
}
|
|
5766
|
+
|
|
5767
|
+
// src/core/file-changes.ts
|
|
5768
|
+
import * as fs20 from "fs/promises";
|
|
5769
|
+
import * as path26 from "path";
|
|
5770
|
+
function toRelativePosix(p2, projectRoot) {
|
|
5771
|
+
let rel2 = p2;
|
|
5772
|
+
if (path26.isAbsolute(p2)) {
|
|
5773
|
+
rel2 = path26.relative(projectRoot, p2);
|
|
5774
|
+
}
|
|
5775
|
+
return rel2.split(path26.sep).join("/");
|
|
5776
|
+
}
|
|
5777
|
+
async function listBackupOriginals(projectRoot) {
|
|
5778
|
+
const backupsDir = path26.join(projectRoot, ".teamix-evo", ".backups");
|
|
5779
|
+
const out = /* @__PURE__ */ new Set();
|
|
5780
|
+
const stack = [backupsDir];
|
|
5781
|
+
while (stack.length > 0) {
|
|
5782
|
+
const dir = stack.pop();
|
|
5783
|
+
let entries;
|
|
5784
|
+
try {
|
|
5785
|
+
entries = await fs20.readdir(dir, { withFileTypes: true });
|
|
5786
|
+
} catch (err) {
|
|
5787
|
+
if (err.code === "ENOENT") continue;
|
|
5788
|
+
throw err;
|
|
5789
|
+
}
|
|
5790
|
+
for (const e of entries) {
|
|
5791
|
+
const full = path26.join(dir, e.name);
|
|
5792
|
+
if (e.isDirectory()) {
|
|
5793
|
+
stack.push(full);
|
|
5794
|
+
} else if (e.isFile() && e.name.endsWith(".bak")) {
|
|
5795
|
+
const rel2 = path26.relative(backupsDir, full);
|
|
5796
|
+
const original = stripBackupSuffix(rel2);
|
|
5797
|
+
if (original) out.add(original.split(path26.sep).join("/"));
|
|
5798
|
+
}
|
|
5799
|
+
}
|
|
5800
|
+
}
|
|
5801
|
+
return out;
|
|
5802
|
+
}
|
|
5803
|
+
function stripBackupSuffix(rel2) {
|
|
5804
|
+
const m = rel2.match(
|
|
5805
|
+
/^(.+)\.\d{4}-\d{2}-\d{2}T\d{2}-\d{2}-\d{2}-\d{3}Z\.bak$/
|
|
5806
|
+
);
|
|
5807
|
+
return m?.[1] ?? null;
|
|
5808
|
+
}
|
|
5809
|
+
function diffBackupSet(before, after) {
|
|
5810
|
+
const out = /* @__PURE__ */ new Set();
|
|
5811
|
+
for (const p2 of after) {
|
|
5812
|
+
if (!before.has(p2)) out.add(p2);
|
|
5813
|
+
}
|
|
5814
|
+
return out;
|
|
5815
|
+
}
|
|
5816
|
+
function formatFileChangeSummary(changes) {
|
|
5817
|
+
if (changes.length === 0) return [];
|
|
5818
|
+
const buckets = {
|
|
5819
|
+
created: /* @__PURE__ */ new Map(),
|
|
5820
|
+
modified: /* @__PURE__ */ new Map(),
|
|
5821
|
+
"backed-up": /* @__PURE__ */ new Map(),
|
|
5822
|
+
deleted: /* @__PURE__ */ new Map()
|
|
5823
|
+
};
|
|
5824
|
+
for (const c of changes) {
|
|
5825
|
+
const existing = buckets[c.kind].get(c.path);
|
|
5826
|
+
if (!existing) buckets[c.kind].set(c.path, c);
|
|
5827
|
+
}
|
|
5828
|
+
const order = [
|
|
5829
|
+
["created", "\u{1F195} \u65B0\u5EFA"],
|
|
5830
|
+
["modified", "\u270F\uFE0F \u4FEE\u6539"],
|
|
5831
|
+
["backed-up", "\u{1F4BE} \u5DF2\u5907\u4EFD"],
|
|
5832
|
+
["deleted", "\u{1F5D1} \u5220\u9664"]
|
|
5833
|
+
];
|
|
5834
|
+
const lines = [];
|
|
5835
|
+
for (const [kind, label] of order) {
|
|
5836
|
+
const bucket = buckets[kind];
|
|
5837
|
+
if (bucket.size === 0) continue;
|
|
5838
|
+
lines.push(`${label}\uFF08${bucket.size}\uFF09\uFF1A`);
|
|
5839
|
+
const sorted = [...bucket.values()].sort(
|
|
5840
|
+
(a, b) => a.path.localeCompare(b.path)
|
|
5841
|
+
);
|
|
5842
|
+
for (const c of sorted) {
|
|
5843
|
+
const detail = c.detail ? ` \u2014 ${c.detail}` : "";
|
|
5844
|
+
lines.push(` \u2022 ${c.path} [${c.step}]${detail}`);
|
|
5845
|
+
}
|
|
5846
|
+
}
|
|
5847
|
+
return lines;
|
|
5848
|
+
}
|
|
5849
|
+
|
|
5850
|
+
// src/commands/ui/promote.ts
|
|
5851
|
+
var VALID_MODES = [
|
|
5852
|
+
"Coexist",
|
|
5853
|
+
"Preset",
|
|
5854
|
+
"Wrapper",
|
|
5855
|
+
"Compose",
|
|
5856
|
+
"Variant",
|
|
5857
|
+
"Fork",
|
|
5858
|
+
"TokenOnly",
|
|
5859
|
+
"ManualReview"
|
|
5860
|
+
];
|
|
5861
|
+
var promoteCommand = new Command20("promote-to-biz").description(
|
|
5862
|
+
"\u5C06 staging \u4E2D\u7684\u7EC4\u4EF6\u6309 8 \u6A21\u5F0F promote \u5230 src/components/business/\uFF08Init \u843D\u5730\u8BA1\u5212 \xA7C.3\uFF09"
|
|
5863
|
+
).argument("[ids...]", "\u53EF\u9009 entry id \u5217\u8868\uFF1B\u7701\u7565\u65F6\u5904\u7406 staging \u4E2D\u5168\u90E8\u6761\u76EE").option(
|
|
5864
|
+
"--modes <list>",
|
|
5865
|
+
'\u9017\u53F7\u5206\u9694\u7684\u6A21\u5F0F\u5217\u8868\uFF08\u8986\u76D6 staging \u63A8\u8350\uFF09\uFF0C\u5982 "Wrapper,Preset"'
|
|
5866
|
+
).option(
|
|
5867
|
+
"--migrate-imports",
|
|
5868
|
+
"Coexist \u6A21\u5F0F\u65F6\u540C\u6B65\u91CD\u5199 src/** \u4E2D ui/<id> import \u4E3A legacy-<id>"
|
|
5869
|
+
).option(
|
|
5870
|
+
"--keep-original-names",
|
|
5871
|
+
"Coexist \u6A21\u5F0F\u8DF3\u8FC7 Foo \u2192 LegacyFoo \u91CD\u547D\u540D\uFF08\u9ED8\u8BA4\u4F1A\u91CD\u547D\u540D\uFF09"
|
|
5872
|
+
).option("--dry-run", "\u53EA\u8F93\u51FA\u53D8\u66F4\u8BA1\u5212\uFF0C\u4E0D\u5199\u5165\u4EFB\u4F55\u6587\u4EF6").option(
|
|
5873
|
+
"--staging-dir <path>",
|
|
5874
|
+
"\u6307\u5B9A staging \u76EE\u5F55\uFF08\u9ED8\u8BA4\u4F7F\u7528 .teamix-evo/.upgrade-staging/ui-* \u4E2D\u6700\u65B0\u7684\uFF09"
|
|
5875
|
+
).action(async (ids, opts) => {
|
|
5876
|
+
try {
|
|
5877
|
+
const ide = detectIde();
|
|
5878
|
+
const projectRoot = ide.getProjectRoot();
|
|
5879
|
+
const modesOverride = parseModes(opts.modes);
|
|
5880
|
+
const result = await runUiPromote({
|
|
5881
|
+
projectRoot,
|
|
5882
|
+
ids: ids ?? [],
|
|
5883
|
+
modesOverride,
|
|
5884
|
+
migrateImports: opts.migrateImports ?? false,
|
|
5885
|
+
keepOriginalNames: opts.keepOriginalNames ?? false,
|
|
5886
|
+
dryRun: opts.dryRun ?? false,
|
|
5887
|
+
stagingDir: opts.stagingDir
|
|
5888
|
+
});
|
|
5889
|
+
renderResult(result, opts.dryRun ?? false);
|
|
5890
|
+
} catch (err) {
|
|
5891
|
+
logger.error(`promote-to-biz failed: ${getErrorMessage(err)}`);
|
|
5892
|
+
logger.debug(err instanceof Error ? err.stack ?? "" : "");
|
|
5893
|
+
process.exitCode = 1;
|
|
5894
|
+
}
|
|
5895
|
+
});
|
|
5896
|
+
function parseModes(raw) {
|
|
5897
|
+
if (!raw || raw.trim().length === 0) return void 0;
|
|
5898
|
+
const parts = raw.split(",").map((s) => s.trim()).filter(Boolean);
|
|
5899
|
+
const out = [];
|
|
5900
|
+
for (const p2 of parts) {
|
|
5901
|
+
const match = VALID_MODES.find((m) => m.toLowerCase() === p2.toLowerCase());
|
|
5902
|
+
if (!match) {
|
|
5903
|
+
throw new Error(`Unknown mode "${p2}". Valid: ${VALID_MODES.join(", ")}`);
|
|
5904
|
+
}
|
|
5905
|
+
out.push(match);
|
|
5906
|
+
}
|
|
5907
|
+
return out;
|
|
5908
|
+
}
|
|
5909
|
+
function renderResult(result, dryRun) {
|
|
5910
|
+
switch (result.status) {
|
|
5911
|
+
case "not-initialized":
|
|
5912
|
+
logger.error("UI not initialized. Run `npx teamix-evo ui init` first.");
|
|
5913
|
+
process.exitCode = 1;
|
|
5914
|
+
return;
|
|
5915
|
+
case "no-staging":
|
|
4234
5916
|
logger.error(
|
|
4235
|
-
`
|
|
5917
|
+
"No `ui-*` staging dir found under `.teamix-evo/.upgrade-staging/`. Run `teamix-evo ui upgrade` first."
|
|
4236
5918
|
);
|
|
4237
|
-
logger.debug(err.stack ?? "");
|
|
4238
5919
|
process.exitCode = 1;
|
|
5920
|
+
return;
|
|
5921
|
+
case "no-entries":
|
|
5922
|
+
logger.warn(`Staging dir is empty: ${result.stagingDir}`);
|
|
5923
|
+
return;
|
|
5924
|
+
case "promoted":
|
|
5925
|
+
break;
|
|
5926
|
+
}
|
|
5927
|
+
const promoted = result.promotions.filter((p2) => p2.status === "promoted");
|
|
5928
|
+
const manual = result.promotions.filter((p2) => p2.status === "manual-review");
|
|
5929
|
+
const tokenOnly = result.promotions.filter((p2) => p2.status === "token-only");
|
|
5930
|
+
const failed = result.promotions.filter((p2) => p2.status === "skipped");
|
|
5931
|
+
logger.success(
|
|
5932
|
+
`${dryRun ? "[dry-run] " : ""}promote-to-biz complete: ${promoted.length} promoted, ${manual.length} manual-review, ${tokenOnly.length} token-only, ${failed.length} skipped.`
|
|
5933
|
+
);
|
|
5934
|
+
logger.info("");
|
|
5935
|
+
if (promoted.length > 0) {
|
|
5936
|
+
logger.info("Promoted:");
|
|
5937
|
+
for (const p2 of promoted) {
|
|
5938
|
+
logger.info(` \u2022 ${p2.id} [${p2.modes.join("+")}]`);
|
|
5939
|
+
for (const out of p2.outputs) logger.info(` \u2192 ${out}`);
|
|
5940
|
+
if (p2.reason) logger.info(` ${p2.reason}`);
|
|
4239
5941
|
}
|
|
4240
|
-
|
|
5942
|
+
logger.info("");
|
|
5943
|
+
}
|
|
5944
|
+
if (manual.length > 0) {
|
|
5945
|
+
logger.info("Manual review needed (no file written):");
|
|
5946
|
+
for (const p2 of manual) logger.info(` \u2022 ${p2.id} \u2014 ${p2.reason ?? ""}`);
|
|
5947
|
+
logger.info("");
|
|
5948
|
+
}
|
|
5949
|
+
if (tokenOnly.length > 0) {
|
|
5950
|
+
logger.info("Token-only (edit tokens.overrides.css instead):");
|
|
5951
|
+
for (const p2 of tokenOnly) logger.info(` \u2022 ${p2.id}`);
|
|
5952
|
+
logger.info("");
|
|
5953
|
+
}
|
|
5954
|
+
if (failed.length > 0) {
|
|
5955
|
+
logger.warn("Failed:");
|
|
5956
|
+
for (const p2 of failed) logger.warn(` \u2022 ${p2.id} \u2014 ${p2.reason ?? ""}`);
|
|
5957
|
+
logger.info("");
|
|
5958
|
+
}
|
|
5959
|
+
if (result.importRewrite.rewritten.length > 0) {
|
|
5960
|
+
logger.info(
|
|
5961
|
+
`Import rewrite: ${result.importRewrite.rewritten.length} file(s) updated under src/**.`
|
|
5962
|
+
);
|
|
5963
|
+
}
|
|
5964
|
+
if (result.importRewrite.skipped.length > 0) {
|
|
5965
|
+
logger.warn("Manual import fix needed (could not be rewritten safely):");
|
|
5966
|
+
for (const s of result.importRewrite.skipped) {
|
|
5967
|
+
logger.warn(` \u2022 ${s.file}:${s.line} [${s.reason}] \u2014 ${s.snippet}`);
|
|
5968
|
+
}
|
|
5969
|
+
logger.info("");
|
|
5970
|
+
}
|
|
5971
|
+
if (result.fileChanges.length > 0) {
|
|
5972
|
+
logger.info("\u6587\u4EF6\u53D8\u66F4\uFF1A");
|
|
5973
|
+
for (const line of formatFileChangeSummary(result.fileChanges)) {
|
|
5974
|
+
logger.info(` ${line}`);
|
|
5975
|
+
}
|
|
5976
|
+
}
|
|
4241
5977
|
}
|
|
4242
5978
|
|
|
4243
|
-
// src/commands/ui/upgrade.ts
|
|
4244
|
-
var upgradeCommand = makeUpgradeCommand("ui");
|
|
4245
|
-
|
|
4246
5979
|
// src/commands/ui/index.ts
|
|
4247
|
-
var uiCommand = new
|
|
5980
|
+
var uiCommand = new Command21("ui").description(
|
|
4248
5981
|
"\u7BA1\u7406 teamix-evo ui \u7EC4\u4EF6\uFF08\u6E90\u7801\u6CE8\u5165\u5F0F\u5B89\u88C5\uFF0Cshadcn \u98CE\u683C\uFF09"
|
|
4249
5982
|
);
|
|
4250
5983
|
uiCommand.addCommand(initCommand3);
|
|
4251
5984
|
uiCommand.addCommand(addCommand2);
|
|
4252
5985
|
uiCommand.addCommand(listCommand3);
|
|
4253
5986
|
uiCommand.addCommand(upgradeCommand);
|
|
5987
|
+
uiCommand.addCommand(promoteCommand);
|
|
4254
5988
|
|
|
4255
5989
|
// src/commands/biz-ui/index.ts
|
|
4256
|
-
import { Command as
|
|
5990
|
+
import { Command as Command25 } from "commander";
|
|
4257
5991
|
|
|
4258
5992
|
// src/commands/biz-ui/add.ts
|
|
4259
|
-
import { Command as
|
|
5993
|
+
import { Command as Command22 } from "commander";
|
|
4260
5994
|
|
|
4261
5995
|
// src/core/variant-ui-add.ts
|
|
4262
|
-
import * as
|
|
5996
|
+
import * as path27 from "path";
|
|
4263
5997
|
import { createRequire as createRequire7 } from "module";
|
|
4264
5998
|
import {
|
|
4265
5999
|
loadUiPackageManifest as loadUiPackageManifest3,
|
|
@@ -4269,7 +6003,7 @@ import {
|
|
|
4269
6003
|
var require7 = createRequire7(import.meta.url);
|
|
4270
6004
|
function resolvePackageRoot4(packageName) {
|
|
4271
6005
|
const pkgJsonPath = require7.resolve(`${packageName}/package.json`);
|
|
4272
|
-
return
|
|
6006
|
+
return path27.dirname(pkgJsonPath);
|
|
4273
6007
|
}
|
|
4274
6008
|
async function runVariantUiAdd(packageName, options) {
|
|
4275
6009
|
const { projectRoot, variant, ids, overwrite } = options;
|
|
@@ -4292,7 +6026,7 @@ async function runVariantUiAdd(packageName, options) {
|
|
|
4292
6026
|
`Variant "${variant}" not found in ${fullPackageName}. Known variants: ${known}. Hint: \`teamix-evo ${packageName} list-variants\` shows all.`
|
|
4293
6027
|
);
|
|
4294
6028
|
}
|
|
4295
|
-
const variantDir =
|
|
6029
|
+
const variantDir = path27.join(packageRoot, "variants", variant);
|
|
4296
6030
|
const variantManifest = await loadVariantUiPackageManifest2(variantDir);
|
|
4297
6031
|
const knownIds = new Set(variantManifest.entries.map((e) => e.id));
|
|
4298
6032
|
const unknown = ids.filter((id) => !knownIds.has(id));
|
|
@@ -4339,7 +6073,7 @@ async function runVariantUiAdd(packageName, options) {
|
|
|
4339
6073
|
(p2) => p2.package === fullPackageName && p2.variant === variant
|
|
4340
6074
|
);
|
|
4341
6075
|
const prior = idx >= 0 ? installed.installed[idx] : null;
|
|
4342
|
-
const mergedResources =
|
|
6076
|
+
const mergedResources = mergeResources3(
|
|
4343
6077
|
prior?.resources ?? [],
|
|
4344
6078
|
result.resources
|
|
4345
6079
|
);
|
|
@@ -4363,7 +6097,7 @@ async function runVariantUiAdd(packageName, options) {
|
|
|
4363
6097
|
resources: result.resources
|
|
4364
6098
|
};
|
|
4365
6099
|
}
|
|
4366
|
-
function
|
|
6100
|
+
function mergeResources3(prior, next) {
|
|
4367
6101
|
const merged = /* @__PURE__ */ new Map();
|
|
4368
6102
|
for (const r of prior) merged.set(r.id, r);
|
|
4369
6103
|
for (const r of next) merged.set(r.id, r);
|
|
@@ -4405,7 +6139,7 @@ async function listVariantUiEntries(packageName, variant, packageRoot) {
|
|
|
4405
6139
|
`Variant "${variant}" not found in ${fullPackageName}. Known: ${known}.`
|
|
4406
6140
|
);
|
|
4407
6141
|
}
|
|
4408
|
-
const variantDir =
|
|
6142
|
+
const variantDir = path27.join(root, "variants", variant);
|
|
4409
6143
|
const variantManifest = await loadVariantUiPackageManifest2(variantDir);
|
|
4410
6144
|
return {
|
|
4411
6145
|
packageName: fullPackageName,
|
|
@@ -4427,7 +6161,7 @@ async function listTemplatesEntries(variant, packageRoot) {
|
|
|
4427
6161
|
}
|
|
4428
6162
|
|
|
4429
6163
|
// src/commands/biz-ui/add.ts
|
|
4430
|
-
var addCommand3 = new
|
|
6164
|
+
var addCommand3 = new Command22("add").description(
|
|
4431
6165
|
"\u5B89\u88C5\u4E00\u4E2A\u6216\u591A\u4E2A\u4E1A\u52A1 UI \u7EC4\u4EF6(\u6309 id,\u81EA\u52A8\u5C55\u5F00 ui \u5305\u7684 registryDependencies)"
|
|
4432
6166
|
).argument("<ids...>", '\u7EC4\u4EF6 id \u5217\u8868,\u5982 "tenant-switcher" "org-picker"').option("--variant <name>", '\u53D8\u4F53 id(\u5FC5\u586B,\u5982 "opentrek"\u3001"uni-manager")').option("--overwrite", "\u5373\u4F7F\u76EE\u6807\u6587\u4EF6\u5DF2\u5B58\u5728\u4E5F\u8986\u76D6").action(
|
|
4433
6167
|
async (ids, opts) => {
|
|
@@ -4478,8 +6212,8 @@ var addCommand3 = new Command20("add").description(
|
|
|
4478
6212
|
);
|
|
4479
6213
|
|
|
4480
6214
|
// src/commands/biz-ui/list.ts
|
|
4481
|
-
import { Command as
|
|
4482
|
-
var listCommand4 = new
|
|
6215
|
+
import { Command as Command23 } from "commander";
|
|
6216
|
+
var listCommand4 = new Command23("list").description("\u5217\u51FA\u6307\u5B9A\u53D8\u4F53\u4E0B\u7684 biz-ui entries").requiredOption("--variant <name>", "\u53D8\u4F53\u540D\uFF08\u5982 opentrek / uni-manager\uFF09").action(async (opts) => {
|
|
4483
6217
|
try {
|
|
4484
6218
|
const result = await listBizUiEntries(opts.variant);
|
|
4485
6219
|
logger.info(`${result.packageName}#${result.variant} entries:`);
|
|
@@ -4504,8 +6238,8 @@ var listCommand4 = new Command21("list").description("\u5217\u51FA\u6307\u5B9A\u
|
|
|
4504
6238
|
});
|
|
4505
6239
|
|
|
4506
6240
|
// src/commands/biz-ui/list-variants.ts
|
|
4507
|
-
import { Command as
|
|
4508
|
-
var listVariantsCommand2 = new
|
|
6241
|
+
import { Command as Command24 } from "commander";
|
|
6242
|
+
var listVariantsCommand2 = new Command24("list-variants").description("\u5217\u51FA @teamix-evo/biz-ui \u5305\u5185\u63D0\u4F9B\u7684\u6240\u6709\u4E1A\u52A1\u53D8\u4F53").action(async () => {
|
|
4509
6243
|
try {
|
|
4510
6244
|
const result = await listBizUiVariants();
|
|
4511
6245
|
logger.info(`Available biz-ui variants in ${result.packageName}:`);
|
|
@@ -4532,7 +6266,7 @@ var listVariantsCommand2 = new Command22("list-variants").description("\u5217\u5
|
|
|
4532
6266
|
var upgradeCommand2 = makeUpgradeCommand("biz-ui");
|
|
4533
6267
|
|
|
4534
6268
|
// src/commands/biz-ui/index.ts
|
|
4535
|
-
var bizUiCommand = new
|
|
6269
|
+
var bizUiCommand = new Command25("biz-ui").description(
|
|
4536
6270
|
"\u7BA1\u7406\u4E1A\u52A1 UI \u7EC4\u4EF6(\u53D8\u4F53\u611F\u77E5 \u2014 \u4E0E design / templates \u540C\u53D8\u4F53\u540D\u7A7A\u95F4)"
|
|
4537
6271
|
);
|
|
4538
6272
|
bizUiCommand.addCommand(addCommand3);
|
|
@@ -4541,11 +6275,11 @@ bizUiCommand.addCommand(listVariantsCommand2);
|
|
|
4541
6275
|
bizUiCommand.addCommand(upgradeCommand2);
|
|
4542
6276
|
|
|
4543
6277
|
// src/commands/templates/index.ts
|
|
4544
|
-
import { Command as
|
|
6278
|
+
import { Command as Command29 } from "commander";
|
|
4545
6279
|
|
|
4546
6280
|
// src/commands/templates/add.ts
|
|
4547
|
-
import { Command as
|
|
4548
|
-
var addCommand4 = new
|
|
6281
|
+
import { Command as Command26 } from "commander";
|
|
6282
|
+
var addCommand4 = new Command26("add").description(
|
|
4549
6283
|
"\u5B89\u88C5\u4E00\u4E2A\u6216\u591A\u4E2A\u9875\u9762\u6A21\u677F(\u6309 id,\u81EA\u52A8\u5C55\u5F00 ui \u5305\u7684 registryDependencies)"
|
|
4550
6284
|
).argument("<ids...>", '\u6A21\u677F id \u5217\u8868,\u5982 "list-detail-page"').option("--variant <name>", '\u53D8\u4F53 id(\u5FC5\u586B,\u5982 "opentrek"\u3001"uni-manager")').option("--overwrite", "\u5373\u4F7F\u76EE\u6807\u6587\u4EF6\u5DF2\u5B58\u5728\u4E5F\u8986\u76D6").action(
|
|
4551
6285
|
async (ids, opts) => {
|
|
@@ -4596,8 +6330,8 @@ var addCommand4 = new Command24("add").description(
|
|
|
4596
6330
|
);
|
|
4597
6331
|
|
|
4598
6332
|
// src/commands/templates/list.ts
|
|
4599
|
-
import { Command as
|
|
4600
|
-
var listCommand5 = new
|
|
6333
|
+
import { Command as Command27 } from "commander";
|
|
6334
|
+
var listCommand5 = new Command27("list").description("\u5217\u51FA\u6307\u5B9A\u53D8\u4F53\u4E0B\u7684 templates entries").requiredOption("--variant <name>", "\u53D8\u4F53\u540D\uFF08\u5982 opentrek / uni-manager\uFF09").action(async (opts) => {
|
|
4601
6335
|
try {
|
|
4602
6336
|
const result = await listTemplatesEntries(opts.variant);
|
|
4603
6337
|
logger.info(`${result.packageName}#${result.variant} entries:`);
|
|
@@ -4622,8 +6356,8 @@ var listCommand5 = new Command25("list").description("\u5217\u51FA\u6307\u5B9A\u
|
|
|
4622
6356
|
});
|
|
4623
6357
|
|
|
4624
6358
|
// src/commands/templates/list-variants.ts
|
|
4625
|
-
import { Command as
|
|
4626
|
-
var listVariantsCommand3 = new
|
|
6359
|
+
import { Command as Command28 } from "commander";
|
|
6360
|
+
var listVariantsCommand3 = new Command28("list-variants").description("\u5217\u51FA @teamix-evo/templates \u5305\u5185\u63D0\u4F9B\u7684\u6240\u6709\u9875\u9762\u6A21\u677F\u53D8\u4F53").action(async () => {
|
|
4627
6361
|
try {
|
|
4628
6362
|
const result = await listTemplatesVariants();
|
|
4629
6363
|
logger.info(`Available templates variants in ${result.packageName}:`);
|
|
@@ -4647,7 +6381,7 @@ var listVariantsCommand3 = new Command26("list-variants").description("\u5217\u5
|
|
|
4647
6381
|
});
|
|
4648
6382
|
|
|
4649
6383
|
// src/commands/templates/index.ts
|
|
4650
|
-
var templatesCommand = new
|
|
6384
|
+
var templatesCommand = new Command29("templates").description(
|
|
4651
6385
|
"\u7BA1\u7406\u9875\u9762\u6A21\u677F(\u53D8\u4F53\u611F\u77E5 \u2014 \u4E0E design / biz-ui \u540C\u53D8\u4F53\u540D\u7A7A\u95F4)"
|
|
4652
6386
|
);
|
|
4653
6387
|
templatesCommand.addCommand(addCommand4);
|
|
@@ -4655,16 +6389,16 @@ templatesCommand.addCommand(listCommand5);
|
|
|
4655
6389
|
templatesCommand.addCommand(listVariantsCommand3);
|
|
4656
6390
|
|
|
4657
6391
|
// src/commands/logs/index.ts
|
|
4658
|
-
import { Command as
|
|
6392
|
+
import { Command as Command32 } from "commander";
|
|
4659
6393
|
|
|
4660
6394
|
// src/commands/logs/analyze.ts
|
|
4661
|
-
import { Command as
|
|
6395
|
+
import { Command as Command30 } from "commander";
|
|
4662
6396
|
import { existsSync as existsSync2 } from "fs";
|
|
4663
|
-
import { resolve as
|
|
6397
|
+
import { resolve as resolve5, join as join27 } from "path";
|
|
4664
6398
|
|
|
4665
6399
|
// src/commands/logs/_io.ts
|
|
4666
6400
|
import { readFileSync, readdirSync, statSync } from "fs";
|
|
4667
|
-
import { join as
|
|
6401
|
+
import { join as join26 } from "path";
|
|
4668
6402
|
var DATE_DIR_RE = /^\d{4}-\d{2}-\d{2}$/;
|
|
4669
6403
|
function parseIntOrUndef(v) {
|
|
4670
6404
|
if (v === void 0) return void 0;
|
|
@@ -4676,7 +6410,7 @@ function readRecords(baseDir, dayLimit) {
|
|
|
4676
6410
|
const selected = dayLimit !== void 0 ? dayDirs.slice(0, dayLimit) : dayDirs;
|
|
4677
6411
|
const records = [];
|
|
4678
6412
|
for (const day of selected) {
|
|
4679
|
-
const dayPath =
|
|
6413
|
+
const dayPath = join26(baseDir, day);
|
|
4680
6414
|
let entries;
|
|
4681
6415
|
try {
|
|
4682
6416
|
entries = readdirSync(dayPath);
|
|
@@ -4685,7 +6419,7 @@ function readRecords(baseDir, dayLimit) {
|
|
|
4685
6419
|
}
|
|
4686
6420
|
for (const entry of entries) {
|
|
4687
6421
|
if (!entry.endsWith(".jsonl")) continue;
|
|
4688
|
-
const fp =
|
|
6422
|
+
const fp = join26(dayPath, entry);
|
|
4689
6423
|
try {
|
|
4690
6424
|
if (!statSync(fp).isFile()) continue;
|
|
4691
6425
|
} catch {
|
|
@@ -4705,14 +6439,14 @@ function readRecords(baseDir, dayLimit) {
|
|
|
4705
6439
|
}
|
|
4706
6440
|
|
|
4707
6441
|
// src/commands/logs/analyze.ts
|
|
4708
|
-
var logsAnalyzeCommand = new
|
|
6442
|
+
var logsAnalyzeCommand = new Command30("analyze").description(
|
|
4709
6443
|
"\u6C47\u603B vibe-logger \u8F93\u51FA (.teamix-evo/logs/ai/**/*.jsonl) \u2014 \u5DE5\u5177 / \u5305\u6807\u7B7E / MCP \u8C03\u7528\u9891\u7387,\u8F85\u52A9\u751F\u6001\u4F18\u5316"
|
|
4710
6444
|
).option("--dir <path>", "log \u76EE\u5F55 (\u9ED8\u8BA4 <project>/.teamix-evo/logs/ai)").option(
|
|
4711
6445
|
"--days <n>",
|
|
4712
6446
|
"\u53EA\u770B\u6700\u8FD1 N \u5929\u7684\u76EE\u5F55 (\u9ED8\u8BA4\u5168\u90E8;\u6309\u76EE\u5F55\u540D YYYY-MM-DD \u6BD4\u5BF9,\u4E0D\u89E3\u6790\u8BB0\u5F55 ts)"
|
|
4713
6447
|
).option("--top <n>", "\u6BCF\u4E2A\u6392\u884C\u5C55\u793A\u524D N \u9879 (\u9ED8\u8BA4 10)", "10").option("--json", "\u4EE5 JSON \u8F93\u51FA (CI/\u5DE5\u5177\u53CB\u597D)").action((opts) => {
|
|
4714
|
-
const baseDir =
|
|
4715
|
-
opts.dir ??
|
|
6448
|
+
const baseDir = resolve5(
|
|
6449
|
+
opts.dir ?? join27(process.cwd(), ".teamix-evo", "logs", "ai")
|
|
4716
6450
|
);
|
|
4717
6451
|
if (!existsSync2(baseDir)) {
|
|
4718
6452
|
logger.warn(`No log directory at ${baseDir}.`);
|
|
@@ -4854,14 +6588,14 @@ function pad(n, width) {
|
|
|
4854
6588
|
}
|
|
4855
6589
|
|
|
4856
6590
|
// src/commands/logs/trace.ts
|
|
4857
|
-
import { Command as
|
|
6591
|
+
import { Command as Command31 } from "commander";
|
|
4858
6592
|
import { existsSync as existsSync3 } from "fs";
|
|
4859
|
-
import { resolve as
|
|
4860
|
-
var logsTraceCommand = new
|
|
6593
|
+
import { resolve as resolve6, join as join28 } from "path";
|
|
6594
|
+
var logsTraceCommand = new Command31("trace").description(
|
|
4861
6595
|
"\u6309\u4F1A\u8BDD\u8FD8\u539F AI \u8C03\u7528\u94FE\u8DEF:\u4ECE\u7528\u6237 prompt \u8D77\u59CB,\u4E32\u8054\u540E\u7EED PreToolUse/PostToolUse \u76F4\u5230\u4E0B\u4E00\u4E2A prompt \u6216 Stop"
|
|
4862
6596
|
).option("--prompt <keyword>", "\u6309\u7528\u6237\u8F93\u5165\u5173\u952E\u5B57\u8FC7\u6EE4 (\u5B50\u4E32\u5339\u914D,\u4E0D\u533A\u5206\u5927\u5C0F\u5199)").option("--session <id>", "\u6307\u5B9A\u4F1A\u8BDD ID (\u524D\u7F00\u5339\u914D)").option("--days <n>", "\u53EA\u770B\u6700\u8FD1 N \u5929\u7684\u76EE\u5F55 (\u9ED8\u8BA4 7)", "7").option("--dir <path>", "log \u76EE\u5F55 (\u9ED8\u8BA4 <project>/.teamix-evo/logs/ai)").option("--json", "\u4EE5 JSON \u8F93\u51FA (CI/\u5DE5\u5177\u53CB\u597D)").action((opts) => {
|
|
4863
|
-
const baseDir =
|
|
4864
|
-
opts.dir ??
|
|
6597
|
+
const baseDir = resolve6(
|
|
6598
|
+
opts.dir ?? join28(process.cwd(), ".teamix-evo", "logs", "ai")
|
|
4865
6599
|
);
|
|
4866
6600
|
if (!existsSync3(baseDir)) {
|
|
4867
6601
|
logger.warn(`No log directory at ${baseDir}.`);
|
|
@@ -5030,22 +6764,22 @@ function padLeft(s, n) {
|
|
|
5030
6764
|
}
|
|
5031
6765
|
|
|
5032
6766
|
// src/commands/logs/index.ts
|
|
5033
|
-
var logsCommand = new
|
|
6767
|
+
var logsCommand = new Command32("logs").description(
|
|
5034
6768
|
"\u67E5\u8BE2 vibe-logger \u8F93\u51FA (.teamix-evo/logs/ai/**/*.jsonl) \u2014 AI \u8C03\u7528\u94FE\u5206\u6790"
|
|
5035
6769
|
);
|
|
5036
6770
|
logsCommand.addCommand(logsAnalyzeCommand);
|
|
5037
6771
|
logsCommand.addCommand(logsTraceCommand);
|
|
5038
6772
|
|
|
5039
6773
|
// src/commands/lint/index.ts
|
|
5040
|
-
import { Command as
|
|
6774
|
+
import { Command as Command34 } from "commander";
|
|
5041
6775
|
|
|
5042
6776
|
// src/commands/lint/init.ts
|
|
5043
|
-
import { Command as
|
|
6777
|
+
import { Command as Command33 } from "commander";
|
|
5044
6778
|
import * as prompts6 from "@clack/prompts";
|
|
5045
6779
|
|
|
5046
6780
|
// src/core/lint-init.ts
|
|
5047
|
-
import * as
|
|
5048
|
-
import * as
|
|
6781
|
+
import * as path28 from "path";
|
|
6782
|
+
import * as fs21 from "fs";
|
|
5049
6783
|
import { execa } from "execa";
|
|
5050
6784
|
var ESLINT_CONFIG_CONTENT = `/**
|
|
5051
6785
|
* teamix-evo consumer ESLint preset \u2014 9 token-discipline rules.
|
|
@@ -5072,18 +6806,29 @@ var ESLINT_DEPS = [
|
|
|
5072
6806
|
];
|
|
5073
6807
|
var STYLELINT_DEPS = ["@teamix-evo/stylelint-config", "stylelint"];
|
|
5074
6808
|
async function runLintInit(options) {
|
|
5075
|
-
const {
|
|
5076
|
-
|
|
5077
|
-
|
|
5078
|
-
|
|
5079
|
-
|
|
5080
|
-
|
|
6809
|
+
const {
|
|
6810
|
+
projectRoot,
|
|
6811
|
+
skipInstall,
|
|
6812
|
+
eslintStrategy = "overwrite",
|
|
6813
|
+
stylelintStrategy = "overwrite",
|
|
6814
|
+
eslintExistingPaths = [],
|
|
6815
|
+
stylelintExistingPaths = []
|
|
6816
|
+
} = options;
|
|
6817
|
+
const eslintConfigPath = path28.join(projectRoot, "eslint.config.js");
|
|
6818
|
+
const stylelintConfigPath = path28.join(projectRoot, "stylelint.config.cjs");
|
|
6819
|
+
const eslintTemplateExists = await fileExists(eslintConfigPath);
|
|
6820
|
+
const stylelintTemplateExists = await fileExists(stylelintConfigPath);
|
|
6821
|
+
const eslintSkipRequested = eslintStrategy === "skip" && eslintExistingPaths.length > 0;
|
|
6822
|
+
const stylelintSkipRequested = stylelintStrategy === "skip" && stylelintExistingPaths.length > 0;
|
|
6823
|
+
const eslintNeedsWrite = !eslintTemplateExists && !eslintSkipRequested;
|
|
6824
|
+
const stylelintNeedsWrite = !stylelintTemplateExists && !stylelintSkipRequested;
|
|
6825
|
+
if (!eslintNeedsWrite && !stylelintNeedsWrite) {
|
|
5081
6826
|
return { status: "already-initialized" };
|
|
5082
6827
|
}
|
|
5083
6828
|
if (!skipInstall) {
|
|
5084
6829
|
const depsToInstall = [
|
|
5085
|
-
...
|
|
5086
|
-
...
|
|
6830
|
+
...eslintNeedsWrite ? ESLINT_DEPS : [],
|
|
6831
|
+
...stylelintNeedsWrite ? STYLELINT_DEPS : []
|
|
5087
6832
|
];
|
|
5088
6833
|
if (depsToInstall.length > 0) {
|
|
5089
6834
|
const pm = detectPm(projectRoot);
|
|
@@ -5092,39 +6837,54 @@ async function runLintInit(options) {
|
|
|
5092
6837
|
await execa(pm, args, { cwd: projectRoot, stdio: "inherit" });
|
|
5093
6838
|
}
|
|
5094
6839
|
}
|
|
6840
|
+
if (eslintNeedsWrite && eslintExistingPaths.length > 0) {
|
|
6841
|
+
for (const rel2 of eslintExistingPaths) {
|
|
6842
|
+
await backupFile(path28.join(projectRoot, rel2), projectRoot);
|
|
6843
|
+
}
|
|
6844
|
+
}
|
|
6845
|
+
if (stylelintNeedsWrite && stylelintExistingPaths.length > 0) {
|
|
6846
|
+
for (const rel2 of stylelintExistingPaths) {
|
|
6847
|
+
await backupFile(path28.join(projectRoot, rel2), projectRoot);
|
|
6848
|
+
}
|
|
6849
|
+
}
|
|
5095
6850
|
let wroteEslint = false;
|
|
5096
6851
|
let wroteStylelint = false;
|
|
5097
|
-
if (
|
|
6852
|
+
if (eslintNeedsWrite) {
|
|
5098
6853
|
await writeFileSafe(eslintConfigPath, ESLINT_CONFIG_CONTENT);
|
|
5099
6854
|
logger.debug(`Wrote eslint.config.js \u2192 ${eslintConfigPath}`);
|
|
5100
6855
|
wroteEslint = true;
|
|
5101
6856
|
}
|
|
5102
|
-
if (
|
|
6857
|
+
if (stylelintNeedsWrite) {
|
|
5103
6858
|
await writeFileSafe(stylelintConfigPath, STYLELINT_CONFIG_CONTENT);
|
|
5104
6859
|
logger.debug(`Wrote stylelint.config.cjs \u2192 ${stylelintConfigPath}`);
|
|
5105
6860
|
wroteStylelint = true;
|
|
5106
6861
|
}
|
|
5107
|
-
await patchPackageJsonScripts(projectRoot);
|
|
6862
|
+
const packageJsonPatched = await patchPackageJsonScripts(projectRoot);
|
|
5108
6863
|
return {
|
|
5109
6864
|
status: "installed",
|
|
5110
6865
|
eslint: wroteEslint,
|
|
5111
|
-
stylelint: wroteStylelint
|
|
6866
|
+
stylelint: wroteStylelint,
|
|
6867
|
+
eslintMergeRequested: wroteEslint && eslintStrategy === "merge" && eslintExistingPaths.length > 0,
|
|
6868
|
+
stylelintMergeRequested: wroteStylelint && stylelintStrategy === "merge" && stylelintExistingPaths.length > 0,
|
|
6869
|
+
eslintSkipped: eslintSkipRequested,
|
|
6870
|
+
stylelintSkipped: stylelintSkipRequested,
|
|
6871
|
+
packageJsonPatched
|
|
5112
6872
|
};
|
|
5113
6873
|
}
|
|
5114
6874
|
function detectPm(projectRoot) {
|
|
5115
|
-
if (
|
|
5116
|
-
if (
|
|
6875
|
+
if (fs21.existsSync(path28.join(projectRoot, "pnpm-lock.yaml"))) return "pnpm";
|
|
6876
|
+
if (fs21.existsSync(path28.join(projectRoot, "yarn.lock"))) return "yarn";
|
|
5117
6877
|
return "npm";
|
|
5118
6878
|
}
|
|
5119
6879
|
async function patchPackageJsonScripts(projectRoot) {
|
|
5120
|
-
const pkgPath =
|
|
6880
|
+
const pkgPath = path28.join(projectRoot, "package.json");
|
|
5121
6881
|
const raw = await readFileOrNull(pkgPath);
|
|
5122
|
-
if (!raw) return;
|
|
6882
|
+
if (!raw) return false;
|
|
5123
6883
|
let pkg;
|
|
5124
6884
|
try {
|
|
5125
6885
|
pkg = JSON.parse(raw);
|
|
5126
6886
|
} catch {
|
|
5127
|
-
return;
|
|
6887
|
+
return false;
|
|
5128
6888
|
}
|
|
5129
6889
|
const scripts = pkg.scripts ?? {};
|
|
5130
6890
|
let changed = false;
|
|
@@ -5137,14 +6897,16 @@ async function patchPackageJsonScripts(projectRoot) {
|
|
|
5137
6897
|
changed = true;
|
|
5138
6898
|
}
|
|
5139
6899
|
if (changed) {
|
|
6900
|
+
await backupFile(pkgPath, projectRoot);
|
|
5140
6901
|
pkg.scripts = scripts;
|
|
5141
6902
|
await writeFileSafe(pkgPath, JSON.stringify(pkg, null, 2) + "\n");
|
|
5142
6903
|
logger.debug("Patched package.json scripts with lint / lint:css");
|
|
5143
6904
|
}
|
|
6905
|
+
return changed;
|
|
5144
6906
|
}
|
|
5145
6907
|
|
|
5146
6908
|
// src/commands/lint/init.ts
|
|
5147
|
-
var initCommand4 = new
|
|
6909
|
+
var initCommand4 = new Command33("init").description(
|
|
5148
6910
|
"\u521D\u59CB\u5316 ESLint + Stylelint \u5DE5\u7A0B\u89C4\u8303\uFF08\u5B89\u88C5\u4F9D\u8D56 + \u751F\u6210\u914D\u7F6E\u6587\u4EF6 + \u6CE8\u5165 scripts\uFF09"
|
|
5149
6911
|
).option("-y, --yes", "\u8DF3\u8FC7\u786E\u8BA4\uFF0C\u76F4\u63A5\u6267\u884C").action(async (opts) => {
|
|
5150
6912
|
try {
|
|
@@ -5192,18 +6954,18 @@ var initCommand4 = new Command31("init").description(
|
|
|
5192
6954
|
});
|
|
5193
6955
|
|
|
5194
6956
|
// src/commands/lint/index.ts
|
|
5195
|
-
var lintCommand = new
|
|
6957
|
+
var lintCommand = new Command34("lint").description(
|
|
5196
6958
|
"\u7BA1\u7406\u5DE5\u7A0B\u89C4\u8303\uFF08ESLint + Stylelint token-discipline \u89C4\u5219\u96C6\uFF09"
|
|
5197
6959
|
);
|
|
5198
6960
|
lintCommand.addCommand(initCommand4);
|
|
5199
6961
|
|
|
5200
6962
|
// src/commands/init/index.ts
|
|
5201
|
-
import { Command as
|
|
5202
|
-
import * as
|
|
6963
|
+
import { Command as Command35 } from "commander";
|
|
6964
|
+
import * as path35 from "path";
|
|
5203
6965
|
|
|
5204
6966
|
// src/core/init-detect.ts
|
|
5205
|
-
import * as
|
|
5206
|
-
import * as
|
|
6967
|
+
import * as fs22 from "fs/promises";
|
|
6968
|
+
import * as path29 from "path";
|
|
5207
6969
|
var IGNORED_TOP_LEVEL = /* @__PURE__ */ new Set([
|
|
5208
6970
|
".git",
|
|
5209
6971
|
".gitignore",
|
|
@@ -5223,7 +6985,7 @@ var IGNORED_TOP_LEVEL = /* @__PURE__ */ new Set([
|
|
|
5223
6985
|
"LICENSE.txt"
|
|
5224
6986
|
]);
|
|
5225
6987
|
async function detectProjectState(cwd) {
|
|
5226
|
-
const absCwd =
|
|
6988
|
+
const absCwd = path29.resolve(cwd);
|
|
5227
6989
|
const teamixDir = getTeamixDir(absCwd);
|
|
5228
6990
|
const hasTeamixDir = await fileExists(teamixDir);
|
|
5229
6991
|
if (hasTeamixDir) {
|
|
@@ -5231,13 +6993,13 @@ async function detectProjectState(cwd) {
|
|
|
5231
6993
|
state: "teamix-evo-installed",
|
|
5232
6994
|
cwd: absCwd,
|
|
5233
6995
|
hasTeamixDir: true,
|
|
5234
|
-
hasPackageJson: await fileExists(
|
|
6996
|
+
hasPackageJson: await fileExists(path29.join(absCwd, "package.json")),
|
|
5235
6997
|
significantEntries: []
|
|
5236
6998
|
};
|
|
5237
6999
|
}
|
|
5238
7000
|
let entries;
|
|
5239
7001
|
try {
|
|
5240
|
-
entries = await
|
|
7002
|
+
entries = await fs22.readdir(absCwd);
|
|
5241
7003
|
} catch (err) {
|
|
5242
7004
|
if (err.code === "ENOENT") {
|
|
5243
7005
|
return {
|
|
@@ -5272,8 +7034,8 @@ async function detectProjectState(cwd) {
|
|
|
5272
7034
|
|
|
5273
7035
|
// src/core/init-conflicts.ts
|
|
5274
7036
|
import * as crypto from "crypto";
|
|
5275
|
-
import * as
|
|
5276
|
-
import * as
|
|
7037
|
+
import * as fs23 from "fs/promises";
|
|
7038
|
+
import * as path30 from "path";
|
|
5277
7039
|
var TAILWIND_CONFIG_CANDIDATES = [
|
|
5278
7040
|
"tailwind.config.ts",
|
|
5279
7041
|
"tailwind.config.js",
|
|
@@ -5295,9 +7057,29 @@ var INDEX_CSS_CANDIDATES = [
|
|
|
5295
7057
|
];
|
|
5296
7058
|
var SHADCN_FILE_CANDIDATES = ["src/lib/utils.ts"];
|
|
5297
7059
|
var SHADCN_DIR_CANDIDATES = ["src/components/ui"];
|
|
7060
|
+
var ESLINT_CONFIG_CANDIDATES = [
|
|
7061
|
+
".eslintrc.cjs",
|
|
7062
|
+
".eslintrc.js",
|
|
7063
|
+
".eslintrc.json",
|
|
7064
|
+
".eslintrc.yml",
|
|
7065
|
+
"eslint.config.js",
|
|
7066
|
+
"eslint.config.cjs",
|
|
7067
|
+
"eslint.config.mjs",
|
|
7068
|
+
"eslint.config.ts"
|
|
7069
|
+
];
|
|
7070
|
+
var STYLELINT_CONFIG_CANDIDATES = [
|
|
7071
|
+
".stylelintrc.cjs",
|
|
7072
|
+
".stylelintrc.js",
|
|
7073
|
+
".stylelintrc.json",
|
|
7074
|
+
".stylelintrc.yml",
|
|
7075
|
+
"stylelint.config.cjs",
|
|
7076
|
+
"stylelint.config.js",
|
|
7077
|
+
"stylelint.config.mjs",
|
|
7078
|
+
"stylelint.config.ts"
|
|
7079
|
+
];
|
|
5298
7080
|
async function isDir(target) {
|
|
5299
7081
|
try {
|
|
5300
|
-
const stat7 = await
|
|
7082
|
+
const stat7 = await fs23.stat(target);
|
|
5301
7083
|
return stat7.isDirectory();
|
|
5302
7084
|
} catch {
|
|
5303
7085
|
return false;
|
|
@@ -5305,7 +7087,7 @@ async function isDir(target) {
|
|
|
5305
7087
|
}
|
|
5306
7088
|
async function dirHasContent(target) {
|
|
5307
7089
|
try {
|
|
5308
|
-
const entries = await
|
|
7090
|
+
const entries = await fs23.readdir(target);
|
|
5309
7091
|
return entries.length > 0;
|
|
5310
7092
|
} catch {
|
|
5311
7093
|
return false;
|
|
@@ -5317,7 +7099,7 @@ function fingerprint(parts) {
|
|
|
5317
7099
|
return `sha256:${hash.digest("hex").slice(0, 16)}`;
|
|
5318
7100
|
}
|
|
5319
7101
|
async function readPackageJson(cwd) {
|
|
5320
|
-
const raw = await readFileOrNull(
|
|
7102
|
+
const raw = await readFileOrNull(path30.join(cwd, "package.json"));
|
|
5321
7103
|
if (raw === null) return null;
|
|
5322
7104
|
try {
|
|
5323
7105
|
return JSON.parse(raw);
|
|
@@ -5337,7 +7119,7 @@ function detectTailwindMajor(pkg) {
|
|
|
5337
7119
|
return null;
|
|
5338
7120
|
}
|
|
5339
7121
|
async function detectAgentsMd(cwd) {
|
|
5340
|
-
const target =
|
|
7122
|
+
const target = path30.join(cwd, "AGENTS.md");
|
|
5341
7123
|
const content = await readFileOrNull(target);
|
|
5342
7124
|
const exists = content !== null;
|
|
5343
7125
|
return {
|
|
@@ -5350,7 +7132,7 @@ async function detectAgentsMd(cwd) {
|
|
|
5350
7132
|
};
|
|
5351
7133
|
}
|
|
5352
7134
|
async function detectComponentsJson(cwd) {
|
|
5353
|
-
const target =
|
|
7135
|
+
const target = path30.join(cwd, "components.json");
|
|
5354
7136
|
const content = await readFileOrNull(target);
|
|
5355
7137
|
const exists = content !== null;
|
|
5356
7138
|
return {
|
|
@@ -5366,7 +7148,7 @@ async function detectTailwindConfig(cwd) {
|
|
|
5366
7148
|
const matched = [];
|
|
5367
7149
|
const contents = [];
|
|
5368
7150
|
for (const rel2 of TAILWIND_CONFIG_CANDIDATES) {
|
|
5369
|
-
const c = await readFileOrNull(
|
|
7151
|
+
const c = await readFileOrNull(path30.join(cwd, rel2));
|
|
5370
7152
|
if (c !== null) {
|
|
5371
7153
|
matched.push(rel2);
|
|
5372
7154
|
contents.push(c);
|
|
@@ -5389,14 +7171,14 @@ async function detectTokens(cwd) {
|
|
|
5389
7171
|
const matched = [];
|
|
5390
7172
|
const contents = [];
|
|
5391
7173
|
for (const rel2 of TOKENS_FILE_CANDIDATES) {
|
|
5392
|
-
const c = await readFileOrNull(
|
|
7174
|
+
const c = await readFileOrNull(path30.join(cwd, rel2));
|
|
5393
7175
|
if (c !== null) {
|
|
5394
7176
|
matched.push(rel2);
|
|
5395
7177
|
contents.push(c);
|
|
5396
7178
|
}
|
|
5397
7179
|
}
|
|
5398
7180
|
for (const rel2 of TOKENS_DIR_CANDIDATES) {
|
|
5399
|
-
const abs =
|
|
7181
|
+
const abs = path30.join(cwd, rel2);
|
|
5400
7182
|
if (await isDir(abs) && await dirHasContent(abs)) {
|
|
5401
7183
|
matched.push(`${rel2}/`);
|
|
5402
7184
|
}
|
|
@@ -5415,7 +7197,7 @@ async function detectIndexCss(cwd) {
|
|
|
5415
7197
|
const matched = [];
|
|
5416
7198
|
const contents = [];
|
|
5417
7199
|
for (const rel2 of INDEX_CSS_CANDIDATES) {
|
|
5418
|
-
const c = await readFileOrNull(
|
|
7200
|
+
const c = await readFileOrNull(path30.join(cwd, rel2));
|
|
5419
7201
|
if (c !== null) {
|
|
5420
7202
|
matched.push(rel2);
|
|
5421
7203
|
contents.push(c);
|
|
@@ -5434,19 +7216,19 @@ async function detectIndexCss(cwd) {
|
|
|
5434
7216
|
async function detectShadcnSource(cwd) {
|
|
5435
7217
|
const matched = [];
|
|
5436
7218
|
for (const rel2 of SHADCN_FILE_CANDIDATES) {
|
|
5437
|
-
if (await fileExists(
|
|
7219
|
+
if (await fileExists(path30.join(cwd, rel2))) matched.push(rel2);
|
|
5438
7220
|
}
|
|
5439
7221
|
for (const rel2 of SHADCN_DIR_CANDIDATES) {
|
|
5440
|
-
const abs =
|
|
7222
|
+
const abs = path30.join(cwd, rel2);
|
|
5441
7223
|
if (await isDir(abs) && await dirHasContent(abs)) {
|
|
5442
7224
|
matched.push(`${rel2}/`);
|
|
5443
7225
|
}
|
|
5444
7226
|
}
|
|
5445
7227
|
let componentCount = 0;
|
|
5446
7228
|
try {
|
|
5447
|
-
const uiDir =
|
|
7229
|
+
const uiDir = path30.join(cwd, "src/components/ui");
|
|
5448
7230
|
if (await isDir(uiDir)) {
|
|
5449
|
-
const entries = await
|
|
7231
|
+
const entries = await fs23.readdir(uiDir);
|
|
5450
7232
|
componentCount = entries.filter(
|
|
5451
7233
|
(e) => e.endsWith(".tsx") || e.endsWith(".ts")
|
|
5452
7234
|
).length;
|
|
@@ -5464,14 +7246,16 @@ async function detectShadcnSource(cwd) {
|
|
|
5464
7246
|
};
|
|
5465
7247
|
}
|
|
5466
7248
|
async function detectConflicts(cwd) {
|
|
5467
|
-
const absCwd =
|
|
7249
|
+
const absCwd = path30.resolve(cwd);
|
|
5468
7250
|
const items = await Promise.all([
|
|
5469
7251
|
detectAgentsMd(absCwd),
|
|
5470
7252
|
detectComponentsJson(absCwd),
|
|
5471
7253
|
detectTailwindConfig(absCwd),
|
|
5472
7254
|
detectTokens(absCwd),
|
|
5473
7255
|
detectIndexCss(absCwd),
|
|
5474
|
-
detectShadcnSource(absCwd)
|
|
7256
|
+
detectShadcnSource(absCwd),
|
|
7257
|
+
detectEslintConfig(absCwd),
|
|
7258
|
+
detectStylelintConfig(absCwd)
|
|
5475
7259
|
]);
|
|
5476
7260
|
return {
|
|
5477
7261
|
cwd: absCwd,
|
|
@@ -5479,10 +7263,50 @@ async function detectConflicts(cwd) {
|
|
|
5479
7263
|
hasAnyConflict: items.some((i) => i.exists)
|
|
5480
7264
|
};
|
|
5481
7265
|
}
|
|
7266
|
+
async function detectEslintConfig(cwd) {
|
|
7267
|
+
const matched = [];
|
|
7268
|
+
const contents = [];
|
|
7269
|
+
for (const rel2 of ESLINT_CONFIG_CANDIDATES) {
|
|
7270
|
+
const c = await readFileOrNull(path30.join(cwd, rel2));
|
|
7271
|
+
if (c !== null) {
|
|
7272
|
+
matched.push(rel2);
|
|
7273
|
+
contents.push(c);
|
|
7274
|
+
}
|
|
7275
|
+
}
|
|
7276
|
+
const exists = matched.length > 0;
|
|
7277
|
+
return {
|
|
7278
|
+
key: "eslint-config",
|
|
7279
|
+
exists,
|
|
7280
|
+
paths: matched,
|
|
7281
|
+
fingerprint: exists ? fingerprint(contents) : void 0,
|
|
7282
|
+
recommendedStrategy: exists ? "merge" : "overwrite",
|
|
7283
|
+
availableStrategies: ["merge", "backup-overwrite", "overwrite", "skip"]
|
|
7284
|
+
};
|
|
7285
|
+
}
|
|
7286
|
+
async function detectStylelintConfig(cwd) {
|
|
7287
|
+
const matched = [];
|
|
7288
|
+
const contents = [];
|
|
7289
|
+
for (const rel2 of STYLELINT_CONFIG_CANDIDATES) {
|
|
7290
|
+
const c = await readFileOrNull(path30.join(cwd, rel2));
|
|
7291
|
+
if (c !== null) {
|
|
7292
|
+
matched.push(rel2);
|
|
7293
|
+
contents.push(c);
|
|
7294
|
+
}
|
|
7295
|
+
}
|
|
7296
|
+
const exists = matched.length > 0;
|
|
7297
|
+
return {
|
|
7298
|
+
key: "stylelint-config",
|
|
7299
|
+
exists,
|
|
7300
|
+
paths: matched,
|
|
7301
|
+
fingerprint: exists ? fingerprint(contents) : void 0,
|
|
7302
|
+
recommendedStrategy: exists ? "merge" : "overwrite",
|
|
7303
|
+
availableStrategies: ["merge", "backup-overwrite", "overwrite", "skip"]
|
|
7304
|
+
};
|
|
7305
|
+
}
|
|
5482
7306
|
|
|
5483
7307
|
// src/core/legacy-tokens-migrate.ts
|
|
5484
|
-
import * as
|
|
5485
|
-
import * as
|
|
7308
|
+
import * as path31 from "path";
|
|
7309
|
+
import * as fs24 from "fs/promises";
|
|
5486
7310
|
var CONSUMER_TOKENS_DIR2 = "tokens";
|
|
5487
7311
|
var CONSUMER_OVERRIDES_FILE2 = "tokens.overrides.css";
|
|
5488
7312
|
var EMPTY_OVERRIDES_TEMPLATE2 = `/* User-owned token overrides \u2014 frozen on subsequent installs. */
|
|
@@ -5494,7 +7318,7 @@ function buildMigrationBanner(legacyRel, isoTimestamp) {
|
|
|
5494
7318
|
}
|
|
5495
7319
|
function computeBackupRel(legacyRel, isoTimestamp) {
|
|
5496
7320
|
const tsSafe = isoTimestamp.replace(/[:.]/g, "-");
|
|
5497
|
-
return
|
|
7321
|
+
return path31.posix.join(
|
|
5498
7322
|
".teamix-evo",
|
|
5499
7323
|
".backups",
|
|
5500
7324
|
`${legacyRel}.${tsSafe}.bak`
|
|
@@ -5505,17 +7329,17 @@ async function migrateLegacyTokens(options) {
|
|
|
5505
7329
|
const seen = /* @__PURE__ */ new Set();
|
|
5506
7330
|
const uniqueLegacy = [];
|
|
5507
7331
|
for (const raw of legacyPaths) {
|
|
5508
|
-
const norm = raw.split(
|
|
7332
|
+
const norm = raw.split(path31.sep).join("/");
|
|
5509
7333
|
if (!seen.has(norm)) {
|
|
5510
7334
|
seen.add(norm);
|
|
5511
7335
|
uniqueLegacy.push(norm);
|
|
5512
7336
|
}
|
|
5513
7337
|
}
|
|
5514
|
-
const overridesRel =
|
|
7338
|
+
const overridesRel = path31.posix.join(
|
|
5515
7339
|
CONSUMER_TOKENS_DIR2,
|
|
5516
7340
|
CONSUMER_OVERRIDES_FILE2
|
|
5517
7341
|
);
|
|
5518
|
-
const overridesAbs =
|
|
7342
|
+
const overridesAbs = path31.join(projectRoot, overridesRel);
|
|
5519
7343
|
const existingOverrides = await readFileOrNull(overridesAbs);
|
|
5520
7344
|
const overridesCreated = existingOverrides === null;
|
|
5521
7345
|
const baseContent = existingOverrides === null ? EMPTY_OVERRIDES_TEMPLATE2 : existingOverrides;
|
|
@@ -5524,7 +7348,7 @@ async function migrateLegacyTokens(options) {
|
|
|
5524
7348
|
const isoTimestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
5525
7349
|
let appendedPayload = "";
|
|
5526
7350
|
for (const legacyRel of uniqueLegacy) {
|
|
5527
|
-
const legacyAbs =
|
|
7351
|
+
const legacyAbs = path31.join(projectRoot, legacyRel);
|
|
5528
7352
|
const content = await readFileOrNull(legacyAbs);
|
|
5529
7353
|
if (content === null) {
|
|
5530
7354
|
skipped.push({ from: legacyRel, reason: "not-found" });
|
|
@@ -5554,15 +7378,15 @@ async function migrateLegacyTokens(options) {
|
|
|
5554
7378
|
dryRun
|
|
5555
7379
|
};
|
|
5556
7380
|
}
|
|
5557
|
-
await ensureDir(
|
|
7381
|
+
await ensureDir(path31.dirname(overridesAbs));
|
|
5558
7382
|
const merged = baseContent + appendedPayload;
|
|
5559
7383
|
const tmp = overridesAbs + ".tmp";
|
|
5560
|
-
await
|
|
5561
|
-
await
|
|
7384
|
+
await fs24.writeFile(tmp, merged, "utf-8");
|
|
7385
|
+
await fs24.rename(tmp, overridesAbs);
|
|
5562
7386
|
for (const entry of migrated) {
|
|
5563
|
-
const legacyAbs =
|
|
5564
|
-
const backupAbs =
|
|
5565
|
-
await ensureDir(
|
|
7387
|
+
const legacyAbs = path31.join(projectRoot, entry.from);
|
|
7388
|
+
const backupAbs = path31.join(projectRoot, entry.backupTo);
|
|
7389
|
+
await ensureDir(path31.dirname(backupAbs));
|
|
5566
7390
|
const srcContent = await readFileOrNull(legacyAbs);
|
|
5567
7391
|
if (srcContent === null) {
|
|
5568
7392
|
logger.debug(
|
|
@@ -5570,9 +7394,9 @@ async function migrateLegacyTokens(options) {
|
|
|
5570
7394
|
);
|
|
5571
7395
|
continue;
|
|
5572
7396
|
}
|
|
5573
|
-
await
|
|
7397
|
+
await fs24.writeFile(backupAbs, srcContent, "utf-8");
|
|
5574
7398
|
if (await fileExists(legacyAbs)) {
|
|
5575
|
-
await
|
|
7399
|
+
await fs24.unlink(legacyAbs);
|
|
5576
7400
|
}
|
|
5577
7401
|
logger.debug(
|
|
5578
7402
|
`legacy-tokens-migrate: ${entry.from} \u2192 ${overridesRel} (backup: ${entry.backupTo})`
|
|
@@ -5588,10 +7412,13 @@ async function migrateLegacyTokens(options) {
|
|
|
5588
7412
|
}
|
|
5589
7413
|
|
|
5590
7414
|
// src/core/agents-md.ts
|
|
5591
|
-
import * as
|
|
5592
|
-
import * as
|
|
7415
|
+
import * as fs25 from "fs/promises";
|
|
7416
|
+
import * as path32 from "path";
|
|
7417
|
+
import { hasManagedRegion as hasManagedRegion3, replaceManagedRegion as replaceManagedRegion3 } from "@teamix-evo/registry";
|
|
7418
|
+
var AGENTS_MD_MANAGED_ID = "teamix-evo-skills";
|
|
5593
7419
|
async function runGenerateAgentsMd(options) {
|
|
5594
7420
|
const { projectRoot, variant, skillIds } = options;
|
|
7421
|
+
const mode = options.mode ?? "overwrite";
|
|
5595
7422
|
const ordered = [...skillIds].sort(
|
|
5596
7423
|
(a, b) => bucketRank(a) - bucketRank(b) || a.localeCompare(b)
|
|
5597
7424
|
);
|
|
@@ -5602,13 +7429,47 @@ async function runGenerateAgentsMd(options) {
|
|
|
5602
7429
|
sections.push(section);
|
|
5603
7430
|
if (missing) missingSkillIds.push(id);
|
|
5604
7431
|
}
|
|
5605
|
-
const
|
|
5606
|
-
const
|
|
5607
|
-
|
|
7432
|
+
const target = path32.join(projectRoot, "AGENTS.md");
|
|
7433
|
+
const targetExists = await fileExists(target);
|
|
7434
|
+
const fullTemplate = renderAgentsMd({ variant, sections });
|
|
7435
|
+
const managedBody = renderManagedBlockBody({ variant, sections });
|
|
7436
|
+
let outputContent;
|
|
7437
|
+
let merge;
|
|
7438
|
+
if (!targetExists) {
|
|
7439
|
+
outputContent = fullTemplate;
|
|
7440
|
+
merge = "created";
|
|
7441
|
+
} else {
|
|
7442
|
+
await backupFile(target, projectRoot);
|
|
7443
|
+
if (mode === "merge-managed") {
|
|
7444
|
+
const existing = await readFileOrNull(target) ?? "";
|
|
7445
|
+
if (hasManagedRegion3(existing, AGENTS_MD_MANAGED_ID)) {
|
|
7446
|
+
outputContent = replaceManagedRegion3(
|
|
7447
|
+
existing,
|
|
7448
|
+
AGENTS_MD_MANAGED_ID,
|
|
7449
|
+
managedBody
|
|
7450
|
+
);
|
|
7451
|
+
merge = "managed-replaced";
|
|
7452
|
+
} else {
|
|
7453
|
+
const wrapped = wrapManagedBlock(managedBody);
|
|
7454
|
+
outputContent = `${wrapped}
|
|
7455
|
+
|
|
7456
|
+
${PRECEDENCE_NOTICE}
|
|
7457
|
+
|
|
7458
|
+
${existing.trimStart()}`;
|
|
7459
|
+
merge = "managed-prepended";
|
|
7460
|
+
}
|
|
7461
|
+
} else {
|
|
7462
|
+
outputContent = fullTemplate;
|
|
7463
|
+
merge = "overwritten";
|
|
7464
|
+
}
|
|
7465
|
+
}
|
|
7466
|
+
await fs25.writeFile(target, outputContent, "utf8");
|
|
5608
7467
|
return {
|
|
5609
7468
|
path: target,
|
|
5610
7469
|
skillCount: ordered.length,
|
|
5611
|
-
missingSkillIds
|
|
7470
|
+
missingSkillIds,
|
|
7471
|
+
backedUp: targetExists,
|
|
7472
|
+
merge
|
|
5612
7473
|
};
|
|
5613
7474
|
}
|
|
5614
7475
|
function bucketRank(id) {
|
|
@@ -5617,11 +7478,8 @@ function bucketRank(id) {
|
|
|
5617
7478
|
return 2;
|
|
5618
7479
|
}
|
|
5619
7480
|
async function renderSkillSection(projectRoot, skillId) {
|
|
5620
|
-
const skillPath =
|
|
5621
|
-
projectRoot,
|
|
5622
|
-
".teamix-evo",
|
|
5623
|
-
"skills",
|
|
5624
|
-
skillId,
|
|
7481
|
+
const skillPath = path32.join(
|
|
7482
|
+
getSkillsSourceDir(projectRoot, skillId),
|
|
5625
7483
|
"SKILL.md"
|
|
5626
7484
|
);
|
|
5627
7485
|
const lines = [];
|
|
@@ -5629,7 +7487,7 @@ async function renderSkillSection(projectRoot, skillId) {
|
|
|
5629
7487
|
let parts = null;
|
|
5630
7488
|
let missing = false;
|
|
5631
7489
|
try {
|
|
5632
|
-
const raw = await
|
|
7490
|
+
const raw = await fs25.readFile(skillPath, "utf8");
|
|
5633
7491
|
parts = extractDescriptionParts(raw);
|
|
5634
7492
|
} catch {
|
|
5635
7493
|
missing = true;
|
|
@@ -5646,10 +7504,19 @@ async function renderSkillSection(projectRoot, skillId) {
|
|
|
5646
7504
|
if (parts?.coordinates) {
|
|
5647
7505
|
lines.push(`- **Coordinates with**: ${parts.coordinates}`);
|
|
5648
7506
|
}
|
|
5649
|
-
lines.push(`- **\u4F4D\u7F6E**: \`.teamix-evo/skills/${skillId}/SKILL.md\``);
|
|
7507
|
+
lines.push(`- **\u4F4D\u7F6E**: \`.teamix-evo/skills-source/${skillId}/SKILL.md\``);
|
|
5650
7508
|
return { section: lines.join("\n"), missing };
|
|
5651
7509
|
}
|
|
5652
7510
|
function renderAgentsMd(args) {
|
|
7511
|
+
const { variant, sections } = args;
|
|
7512
|
+
const managedBody = renderManagedBlockBody({ variant, sections });
|
|
7513
|
+
const wrapped = wrapManagedBlock(managedBody);
|
|
7514
|
+
return `${wrapped}
|
|
7515
|
+
|
|
7516
|
+
${PRECEDENCE_NOTICE}
|
|
7517
|
+
`;
|
|
7518
|
+
}
|
|
7519
|
+
function renderManagedBlockBody(args) {
|
|
5653
7520
|
const { variant, sections } = args;
|
|
5654
7521
|
const skillBlock = sections.length > 0 ? sections.join("\n\n") : "_\uFF08\u672C\u5DE5\u7A0B\u672A\u88C5\u914D\u5DE5\u7A0B\u7EA7 skill\u3002\uFF09_";
|
|
5655
7522
|
return `# AGENTS.md
|
|
@@ -5668,9 +7535,15 @@ ${skillBlock}
|
|
|
5668
7535
|
- \u6A21\u7CCA\u573A\u666F\uFF1A\u5148\u6309 SKIP \u53CD\u5411\u6392\u9664\uFF0C\u5269\u4F59\u552F\u4E00 skill \u5373\u4E3A\u5165\u53E3
|
|
5669
7536
|
- \u751F\u547D\u5468\u671F\u547D\u4EE4\uFF08\`init\` / \`update\` / \`add\`\uFF09\u8D70 \`teamix-evo-manage\`\uFF08\u5168\u5C40 skill\uFF0C\u672C\u6587\u4EF6\u4E0D\u5217\uFF09
|
|
5670
7537
|
|
|
5671
|
-
> \u5237\u65B0\u672C\u6587\u4EF6\uFF1A\`npx teamix-evo skills add\` \u6216\u91CD\u8DD1 \`npm create teamix-evo\` / \`teamix-evo init\`\u3002
|
|
5672
|
-
|
|
7538
|
+
> \u5237\u65B0\u672C\u6587\u4EF6\uFF1A\`npx teamix-evo skills add\` \u6216\u91CD\u8DD1 \`npm create teamix-evo\` / \`teamix-evo init\`\u3002`;
|
|
7539
|
+
}
|
|
7540
|
+
function wrapManagedBlock(body) {
|
|
7541
|
+
return `<!-- teamix-evo:managed:start id="${AGENTS_MD_MANAGED_ID}" -->
|
|
7542
|
+
${body}
|
|
7543
|
+
<!-- teamix-evo:managed:end id="${AGENTS_MD_MANAGED_ID}" -->`;
|
|
5673
7544
|
}
|
|
7545
|
+
var PRECEDENCE_NOTICE = `<!-- teamix-evo:precedence -->
|
|
7546
|
+
> \u51B2\u7A81\u4EE5\u4E0A\u65B9\u7684 **Skills** \u7D22\u5F15\u4E3A\u51C6\uFF08\u4E0A\u6E38\u8DEF\u5F84\u4E0E TRIGGER/SKIP \u5951\u7EA6\uFF09\uFF1B\u9879\u76EE\u7279\u6709\u7684\u4EBA\u5DE5\u7EC6\u5219\u8BF7\u5199\u5728\u672C\u5904\u4EE5\u4E0B\u3001\u4E0D\u8981\u8986\u76D6\u4E0A\u65B9 managed \u533A\u57DF\u3002`;
|
|
5674
7547
|
function extractDescriptionParts(fileContent) {
|
|
5675
7548
|
const description = extractDescriptionBlock(fileContent);
|
|
5676
7549
|
if (description == null) return null;
|
|
@@ -5771,9 +7644,9 @@ function extractSection(description, marker) {
|
|
|
5771
7644
|
}
|
|
5772
7645
|
|
|
5773
7646
|
// src/core/snapshot.ts
|
|
5774
|
-
import * as
|
|
5775
|
-
import * as
|
|
5776
|
-
var
|
|
7647
|
+
import * as fs26 from "fs/promises";
|
|
7648
|
+
import * as path33 from "path";
|
|
7649
|
+
var TEAMIX_DIR6 = ".teamix-evo";
|
|
5777
7650
|
var SNAPSHOTS_DIR = ".snapshots";
|
|
5778
7651
|
var LOGS_DIR = "logs";
|
|
5779
7652
|
var META_FILE = "_meta.json";
|
|
@@ -5788,9 +7661,9 @@ function fsSafeToIso(safe) {
|
|
|
5788
7661
|
);
|
|
5789
7662
|
}
|
|
5790
7663
|
async function createSnapshot(projectRoot, opts = {}) {
|
|
5791
|
-
const teamixDir =
|
|
7664
|
+
const teamixDir = path33.join(projectRoot, TEAMIX_DIR6);
|
|
5792
7665
|
try {
|
|
5793
|
-
const stat7 = await
|
|
7666
|
+
const stat7 = await fs26.stat(teamixDir);
|
|
5794
7667
|
if (!stat7.isDirectory()) return null;
|
|
5795
7668
|
} catch (err) {
|
|
5796
7669
|
if (err.code === "ENOENT") return null;
|
|
@@ -5798,38 +7671,38 @@ async function createSnapshot(projectRoot, opts = {}) {
|
|
|
5798
7671
|
}
|
|
5799
7672
|
const isoTs = (/* @__PURE__ */ new Date()).toISOString();
|
|
5800
7673
|
const ts = isoToFsSafe3(isoTs);
|
|
5801
|
-
const snapshotRoot =
|
|
5802
|
-
const target =
|
|
5803
|
-
await
|
|
5804
|
-
const entries = await
|
|
7674
|
+
const snapshotRoot = path33.join(teamixDir, SNAPSHOTS_DIR);
|
|
7675
|
+
const target = path33.join(snapshotRoot, ts);
|
|
7676
|
+
await fs26.mkdir(target, { recursive: true });
|
|
7677
|
+
const entries = await fs26.readdir(teamixDir, { withFileTypes: true });
|
|
5805
7678
|
for (const entry of entries) {
|
|
5806
7679
|
if (entry.name === SNAPSHOTS_DIR) continue;
|
|
5807
7680
|
if (entry.name === LOGS_DIR) continue;
|
|
5808
|
-
const src =
|
|
5809
|
-
const dst =
|
|
5810
|
-
await
|
|
7681
|
+
const src = path33.join(teamixDir, entry.name);
|
|
7682
|
+
const dst = path33.join(target, entry.name);
|
|
7683
|
+
await fs26.cp(src, dst, { recursive: true });
|
|
5811
7684
|
}
|
|
5812
7685
|
const meta = {
|
|
5813
7686
|
ts: isoTs,
|
|
5814
7687
|
reason: opts.reason ?? "manual"
|
|
5815
7688
|
};
|
|
5816
|
-
await
|
|
5817
|
-
|
|
7689
|
+
await fs26.writeFile(
|
|
7690
|
+
path33.join(target, META_FILE),
|
|
5818
7691
|
JSON.stringify(meta, null, 2) + "\n",
|
|
5819
7692
|
"utf-8"
|
|
5820
7693
|
);
|
|
5821
7694
|
logger.debug(
|
|
5822
|
-
`Snapshot created \u2192 ${
|
|
7695
|
+
`Snapshot created \u2192 ${path33.relative(projectRoot, target)} (${meta.reason})`
|
|
5823
7696
|
);
|
|
5824
7697
|
const keep = opts.keep ?? DEFAULT_KEEP;
|
|
5825
7698
|
await pruneSnapshots(projectRoot, keep, { protectedTs: opts.protectedTs });
|
|
5826
7699
|
return { ts, path: target };
|
|
5827
7700
|
}
|
|
5828
7701
|
async function listSnapshots(projectRoot) {
|
|
5829
|
-
const snapshotRoot =
|
|
7702
|
+
const snapshotRoot = path33.join(projectRoot, TEAMIX_DIR6, SNAPSHOTS_DIR);
|
|
5830
7703
|
let entries;
|
|
5831
7704
|
try {
|
|
5832
|
-
entries = await
|
|
7705
|
+
entries = await fs26.readdir(snapshotRoot, { withFileTypes: true });
|
|
5833
7706
|
} catch (err) {
|
|
5834
7707
|
if (err.code === "ENOENT") return [];
|
|
5835
7708
|
throw err;
|
|
@@ -5837,11 +7710,11 @@ async function listSnapshots(projectRoot) {
|
|
|
5837
7710
|
const result = [];
|
|
5838
7711
|
for (const entry of entries) {
|
|
5839
7712
|
if (!entry.isDirectory()) continue;
|
|
5840
|
-
const dir =
|
|
7713
|
+
const dir = path33.join(snapshotRoot, entry.name);
|
|
5841
7714
|
let isoTs = null;
|
|
5842
7715
|
let reason = null;
|
|
5843
7716
|
try {
|
|
5844
|
-
const raw = await
|
|
7717
|
+
const raw = await fs26.readFile(path33.join(dir, META_FILE), "utf-8");
|
|
5845
7718
|
const parsed = JSON.parse(raw);
|
|
5846
7719
|
if (typeof parsed.ts === "string") isoTs = parsed.ts;
|
|
5847
7720
|
if (typeof parsed.reason === "string" && ["init", "update", "switch", "restore", "manual"].includes(
|
|
@@ -5866,17 +7739,17 @@ async function pruneSnapshots(projectRoot, keep = DEFAULT_KEEP, opts = {}) {
|
|
|
5866
7739
|
const toRemove = opts.protectedTs ? tail.filter((s) => s.ts !== opts.protectedTs) : tail;
|
|
5867
7740
|
const removed = [];
|
|
5868
7741
|
for (const snap of toRemove) {
|
|
5869
|
-
await
|
|
7742
|
+
await fs26.rm(snap.path, { recursive: true, force: true });
|
|
5870
7743
|
removed.push(snap.ts);
|
|
5871
7744
|
logger.debug(`Pruned snapshot ${snap.ts}`);
|
|
5872
7745
|
}
|
|
5873
7746
|
return removed.reverse();
|
|
5874
7747
|
}
|
|
5875
7748
|
async function restoreSnapshot(projectRoot, ts) {
|
|
5876
|
-
const snapshotRoot =
|
|
5877
|
-
const target =
|
|
7749
|
+
const snapshotRoot = path33.join(projectRoot, TEAMIX_DIR6, SNAPSHOTS_DIR);
|
|
7750
|
+
const target = path33.join(snapshotRoot, ts);
|
|
5878
7751
|
try {
|
|
5879
|
-
const stat7 = await
|
|
7752
|
+
const stat7 = await fs26.stat(target);
|
|
5880
7753
|
if (!stat7.isDirectory()) {
|
|
5881
7754
|
throw new Error(`Snapshot path is not a directory: ${target}`);
|
|
5882
7755
|
}
|
|
@@ -5889,22 +7762,22 @@ async function restoreSnapshot(projectRoot, ts) {
|
|
|
5889
7762
|
throw err;
|
|
5890
7763
|
}
|
|
5891
7764
|
await createSnapshot(projectRoot, { reason: "restore", protectedTs: ts });
|
|
5892
|
-
const teamixDir =
|
|
5893
|
-
const live = await
|
|
7765
|
+
const teamixDir = path33.join(projectRoot, TEAMIX_DIR6);
|
|
7766
|
+
const live = await fs26.readdir(teamixDir, { withFileTypes: true });
|
|
5894
7767
|
for (const entry of live) {
|
|
5895
7768
|
if (entry.name === SNAPSHOTS_DIR) continue;
|
|
5896
7769
|
if (entry.name === LOGS_DIR) continue;
|
|
5897
|
-
await
|
|
7770
|
+
await fs26.rm(path33.join(teamixDir, entry.name), {
|
|
5898
7771
|
recursive: true,
|
|
5899
7772
|
force: true
|
|
5900
7773
|
});
|
|
5901
7774
|
}
|
|
5902
|
-
const snapshotEntries = await
|
|
7775
|
+
const snapshotEntries = await fs26.readdir(target, { withFileTypes: true });
|
|
5903
7776
|
for (const entry of snapshotEntries) {
|
|
5904
7777
|
if (entry.name === META_FILE) continue;
|
|
5905
|
-
const src =
|
|
5906
|
-
const dst =
|
|
5907
|
-
await
|
|
7778
|
+
const src = path33.join(target, entry.name);
|
|
7779
|
+
const dst = path33.join(teamixDir, entry.name);
|
|
7780
|
+
await fs26.cp(src, dst, { recursive: true });
|
|
5908
7781
|
}
|
|
5909
7782
|
logger.debug(`Restored .teamix-evo/ from snapshot ${ts}`);
|
|
5910
7783
|
}
|
|
@@ -5931,12 +7804,19 @@ var CRITICAL_STEPS = /* @__PURE__ */ new Set([
|
|
|
5931
7804
|
"ui-init"
|
|
5932
7805
|
]);
|
|
5933
7806
|
var IMPLEMENTED_STRATEGIES = {
|
|
7807
|
+
// 'agents-md': both 'merge-managed' (Phase 2.B — splice the
|
|
7808
|
+
// teamix-evo-skills managed region) and 'overwrite' (full rewrite) write
|
|
7809
|
+
// through `runGenerateAgentsMd`.
|
|
5934
7810
|
"agents-md": ["merge-managed", "overwrite", "skip"],
|
|
5935
7811
|
tokens: ["migrate", "overwrite", "skip"],
|
|
5936
7812
|
"components-json": ["overwrite", "skip"],
|
|
5937
7813
|
"shadcn-source": ["overwrite", "skip-existing", "skip"],
|
|
5938
7814
|
"tailwind-config": ["skip"],
|
|
5939
|
-
"index-css": ["skip"]
|
|
7815
|
+
"index-css": ["skip"],
|
|
7816
|
+
// Phase 3.E: lint conflict strategies are honored end-to-end by
|
|
7817
|
+
// `runLintInit` (backup user file + write template, optional AI-assist hint).
|
|
7818
|
+
"eslint-config": ["merge", "backup-overwrite", "skip", "overwrite"],
|
|
7819
|
+
"stylelint-config": ["merge", "backup-overwrite", "skip", "overwrite"]
|
|
5940
7820
|
};
|
|
5941
7821
|
function pickIde(ides) {
|
|
5942
7822
|
return ides[0] ?? "qoder";
|
|
@@ -5954,11 +7834,75 @@ async function resolveUiEntries(options) {
|
|
|
5954
7834
|
}
|
|
5955
7835
|
return [...BASELINE_UI_ENTRIES];
|
|
5956
7836
|
}
|
|
7837
|
+
function deriveTokensChanges(result, projectRoot) {
|
|
7838
|
+
if (result.status !== "installed") return [];
|
|
7839
|
+
return result.resources.map((r) => ({
|
|
7840
|
+
kind: "created",
|
|
7841
|
+
path: toRelativePosix(r.target, projectRoot),
|
|
7842
|
+
step: "tokens",
|
|
7843
|
+
detail: r.strategy
|
|
7844
|
+
}));
|
|
7845
|
+
}
|
|
7846
|
+
function deriveSkillsChanges(result, projectRoot) {
|
|
7847
|
+
if (result.status !== "installed") return [];
|
|
7848
|
+
return result.addedSkillIds.map((id) => ({
|
|
7849
|
+
kind: "created",
|
|
7850
|
+
path: `.teamix-evo/skills/${id}/SKILL.md`,
|
|
7851
|
+
step: "skills",
|
|
7852
|
+
detail: "skill installed (source mirror + IDE mirrors)"
|
|
7853
|
+
}));
|
|
7854
|
+
}
|
|
7855
|
+
function deriveUiAddChanges(result, projectRoot) {
|
|
7856
|
+
const out = [];
|
|
7857
|
+
let remaining = result.written;
|
|
7858
|
+
for (let i = result.resources.length - 1; i >= 0 && remaining > 0; i--) {
|
|
7859
|
+
const r = result.resources[i];
|
|
7860
|
+
out.unshift({
|
|
7861
|
+
kind: "created",
|
|
7862
|
+
path: toRelativePosix(r.target, projectRoot),
|
|
7863
|
+
step: "ui-add",
|
|
7864
|
+
detail: r.strategy
|
|
7865
|
+
});
|
|
7866
|
+
remaining--;
|
|
7867
|
+
}
|
|
7868
|
+
return out;
|
|
7869
|
+
}
|
|
7870
|
+
function deriveLintChanges(result) {
|
|
7871
|
+
if (result.status !== "installed") return [];
|
|
7872
|
+
const out = [];
|
|
7873
|
+
if (result.eslint) {
|
|
7874
|
+
out.push({
|
|
7875
|
+
kind: "created",
|
|
7876
|
+
path: "eslint.config.js",
|
|
7877
|
+
step: "lint",
|
|
7878
|
+
detail: "@teamix-evo/eslint-config consumer preset"
|
|
7879
|
+
});
|
|
7880
|
+
}
|
|
7881
|
+
if (result.stylelint) {
|
|
7882
|
+
out.push({
|
|
7883
|
+
kind: "created",
|
|
7884
|
+
path: "stylelint.config.cjs",
|
|
7885
|
+
step: "lint",
|
|
7886
|
+
detail: "@teamix-evo/stylelint-config consumer preset"
|
|
7887
|
+
});
|
|
7888
|
+
}
|
|
7889
|
+
if (result.packageJsonPatched) {
|
|
7890
|
+
out.push({
|
|
7891
|
+
kind: "created",
|
|
7892
|
+
path: "package.json",
|
|
7893
|
+
step: "lint",
|
|
7894
|
+
detail: 'scripts.lint / scripts["lint:css"]'
|
|
7895
|
+
});
|
|
7896
|
+
}
|
|
7897
|
+
return out;
|
|
7898
|
+
}
|
|
5957
7899
|
async function runProjectInit(options) {
|
|
5958
7900
|
const { projectRoot, answers, dryRun = false, onStep } = options;
|
|
5959
7901
|
const ide = pickIde(answers.ides);
|
|
5960
7902
|
const steps = [];
|
|
5961
7903
|
const pending = [];
|
|
7904
|
+
const allChanges = [];
|
|
7905
|
+
const backupsBefore = dryRun ? /* @__PURE__ */ new Set() : await listBackupOriginals(projectRoot).catch(() => /* @__PURE__ */ new Set());
|
|
5962
7906
|
let snapshot = null;
|
|
5963
7907
|
let snapshotError;
|
|
5964
7908
|
if (!dryRun) {
|
|
@@ -5975,6 +7919,9 @@ async function runProjectInit(options) {
|
|
|
5975
7919
|
function record(step) {
|
|
5976
7920
|
steps.push(step);
|
|
5977
7921
|
onStep?.(step);
|
|
7922
|
+
if (step.changes && step.changes.length > 0) {
|
|
7923
|
+
allChanges.push(...step.changes);
|
|
7924
|
+
}
|
|
5978
7925
|
}
|
|
5979
7926
|
function recordPending(key) {
|
|
5980
7927
|
const strategy = answers.conflictDecisions[key];
|
|
@@ -6034,7 +7981,8 @@ async function runProjectInit(options) {
|
|
|
6034
7981
|
record({
|
|
6035
7982
|
name: "tokens",
|
|
6036
7983
|
status: "ok",
|
|
6037
|
-
detail
|
|
7984
|
+
detail,
|
|
7985
|
+
changes: deriveTokensChanges(result, projectRoot)
|
|
6038
7986
|
});
|
|
6039
7987
|
} catch (err) {
|
|
6040
7988
|
recordFailure("tokens", err);
|
|
@@ -6066,7 +8014,8 @@ async function runProjectInit(options) {
|
|
|
6066
8014
|
record({
|
|
6067
8015
|
name: "skills",
|
|
6068
8016
|
status: "ok",
|
|
6069
|
-
detail: result.status === "installed" ? `added: ${result.addedSkillIds.join(", ") || "none"}; existing: ${result.skippedSkillIds.join(", ") || "none"}` : result.status
|
|
8017
|
+
detail: result.status === "installed" ? `added: ${result.addedSkillIds.join(", ") || "none"}; existing: ${result.skippedSkillIds.join(", ") || "none"}` : result.status,
|
|
8018
|
+
changes: deriveSkillsChanges(result, projectRoot)
|
|
6070
8019
|
});
|
|
6071
8020
|
} catch (err) {
|
|
6072
8021
|
recordFailure("skills", err);
|
|
@@ -6094,12 +8043,25 @@ async function runProjectInit(options) {
|
|
|
6094
8043
|
const result = await runGenerateAgentsMd({
|
|
6095
8044
|
projectRoot,
|
|
6096
8045
|
variant: answers.variant,
|
|
6097
|
-
skillIds: agentsMdSkillIds
|
|
8046
|
+
skillIds: agentsMdSkillIds,
|
|
8047
|
+
// Phase 2.B: when the user picked `merge-managed` on conflict, only
|
|
8048
|
+
// rewrite the `teamix-evo-skills` managed region so hand-written
|
|
8049
|
+
// sections survive the regenerate step. `overwrite` keeps the
|
|
8050
|
+
// historical full-rewrite default.
|
|
8051
|
+
mode: agentsMdDecision === "merge-managed" ? "merge-managed" : "overwrite"
|
|
6098
8052
|
});
|
|
6099
8053
|
record({
|
|
6100
8054
|
name: "agents-md",
|
|
6101
8055
|
status: "ok",
|
|
6102
|
-
detail: `${result.skillCount} skill index${result.missingSkillIds.length > 0 ? ` (missing SKILL.md: ${result.missingSkillIds.join(", ")})` : ""}
|
|
8056
|
+
detail: `${result.skillCount} skill index${result.missingSkillIds.length > 0 ? ` (missing SKILL.md: ${result.missingSkillIds.join(", ")})` : ""}`,
|
|
8057
|
+
changes: [
|
|
8058
|
+
{
|
|
8059
|
+
kind: result.backedUp ? "modified" : "created",
|
|
8060
|
+
path: toRelativePosix(result.path, projectRoot),
|
|
8061
|
+
step: "agents-md",
|
|
8062
|
+
detail: "skill-trigger fallback (ADR 0038)"
|
|
8063
|
+
}
|
|
8064
|
+
]
|
|
6103
8065
|
});
|
|
6104
8066
|
} catch (err) {
|
|
6105
8067
|
recordFailure("agents-md", err);
|
|
@@ -6184,7 +8146,8 @@ async function runProjectInit(options) {
|
|
|
6184
8146
|
record({
|
|
6185
8147
|
name: "ui-add",
|
|
6186
8148
|
status: "ok",
|
|
6187
|
-
detail: `${addResult.orderedIds.length} entries (${addResult.written} written, ${addResult.skipped} skipped)
|
|
8149
|
+
detail: `${addResult.orderedIds.length} entries (${addResult.written} written, ${addResult.skipped} skipped)`,
|
|
8150
|
+
changes: deriveUiAddChanges(addResult, projectRoot)
|
|
6188
8151
|
});
|
|
6189
8152
|
} catch (err) {
|
|
6190
8153
|
recordFailure("ui-add", err);
|
|
@@ -6208,14 +8171,39 @@ async function runProjectInit(options) {
|
|
|
6208
8171
|
});
|
|
6209
8172
|
} else {
|
|
6210
8173
|
try {
|
|
8174
|
+
const eslintStrategy = answers.conflictDecisions["eslint-config"];
|
|
8175
|
+
const stylelintStrategy = answers.conflictDecisions["stylelint-config"];
|
|
8176
|
+
const eslintExistingPaths = options.legacyEslintPaths ?? [];
|
|
8177
|
+
const stylelintExistingPaths = options.legacyStylelintPaths ?? [];
|
|
6211
8178
|
const result = await runLintInit({
|
|
6212
8179
|
projectRoot,
|
|
6213
|
-
skipInstall: options.skipInstall ?? false
|
|
8180
|
+
skipInstall: options.skipInstall ?? false,
|
|
8181
|
+
eslintStrategy: eslintStrategy === "merge" || eslintStrategy === "backup-overwrite" || eslintStrategy === "skip" || eslintStrategy === "overwrite" ? eslintStrategy : "overwrite",
|
|
8182
|
+
stylelintStrategy: stylelintStrategy === "merge" || stylelintStrategy === "backup-overwrite" || stylelintStrategy === "skip" || stylelintStrategy === "overwrite" ? stylelintStrategy : "overwrite",
|
|
8183
|
+
eslintExistingPaths,
|
|
8184
|
+
stylelintExistingPaths
|
|
6214
8185
|
});
|
|
8186
|
+
const detailParts = [];
|
|
8187
|
+
if (result.status === "installed") {
|
|
8188
|
+
detailParts.push(
|
|
8189
|
+
`eslint=${result.eslint}, stylelint=${result.stylelint}`
|
|
8190
|
+
);
|
|
8191
|
+
if (result.eslintMergeRequested) {
|
|
8192
|
+
detailParts.push("eslint:AI-merge-pending");
|
|
8193
|
+
}
|
|
8194
|
+
if (result.stylelintMergeRequested) {
|
|
8195
|
+
detailParts.push("stylelint:AI-merge-pending");
|
|
8196
|
+
}
|
|
8197
|
+
if (result.eslintSkipped) detailParts.push("eslint:skipped");
|
|
8198
|
+
if (result.stylelintSkipped) detailParts.push("stylelint:skipped");
|
|
8199
|
+
} else {
|
|
8200
|
+
detailParts.push(result.status);
|
|
8201
|
+
}
|
|
6215
8202
|
record({
|
|
6216
8203
|
name: "lint",
|
|
6217
8204
|
status: "ok",
|
|
6218
|
-
detail:
|
|
8205
|
+
detail: detailParts.join(" / "),
|
|
8206
|
+
changes: deriveLintChanges(result)
|
|
6219
8207
|
});
|
|
6220
8208
|
} catch (err) {
|
|
6221
8209
|
recordFailure("lint", err);
|
|
@@ -6223,11 +8211,34 @@ async function runProjectInit(options) {
|
|
|
6223
8211
|
}
|
|
6224
8212
|
recordPending("tailwind-config");
|
|
6225
8213
|
recordPending("index-css");
|
|
8214
|
+
if (!dryRun) {
|
|
8215
|
+
try {
|
|
8216
|
+
const backupsAfter = await listBackupOriginals(projectRoot);
|
|
8217
|
+
const newlyBackedUp = diffBackupSet(backupsBefore, backupsAfter);
|
|
8218
|
+
if (newlyBackedUp.size > 0) {
|
|
8219
|
+
for (const change of allChanges) {
|
|
8220
|
+
if (change.kind === "created" && newlyBackedUp.has(change.path)) {
|
|
8221
|
+
change.kind = "modified";
|
|
8222
|
+
}
|
|
8223
|
+
}
|
|
8224
|
+
for (const rel2 of newlyBackedUp) {
|
|
8225
|
+
allChanges.push({
|
|
8226
|
+
kind: "backed-up",
|
|
8227
|
+
path: rel2,
|
|
8228
|
+
step: "backup",
|
|
8229
|
+
detail: ".teamix-evo/.backups/<\u540C\u8DEF\u5F84>.<isoTs>.bak"
|
|
8230
|
+
});
|
|
8231
|
+
}
|
|
8232
|
+
}
|
|
8233
|
+
} catch {
|
|
8234
|
+
}
|
|
8235
|
+
}
|
|
6226
8236
|
const status = dryRun ? "dry-run" : steps.some((s) => s.status === "fail") ? "partial" : "installed";
|
|
6227
8237
|
const out = {
|
|
6228
8238
|
status,
|
|
6229
8239
|
steps,
|
|
6230
8240
|
pendingConflictWork: pending,
|
|
8241
|
+
changes: allChanges,
|
|
6231
8242
|
snapshot
|
|
6232
8243
|
};
|
|
6233
8244
|
if (snapshotError) out.snapshotError = snapshotError;
|
|
@@ -6247,6 +8258,72 @@ async function runProjectInit(options) {
|
|
|
6247
8258
|
return out;
|
|
6248
8259
|
}
|
|
6249
8260
|
|
|
8261
|
+
// src/core/post-init-guidance.ts
|
|
8262
|
+
import * as path34 from "path";
|
|
8263
|
+
var HEADING = "Next steps:";
|
|
8264
|
+
async function buildPostInitGuidance(options) {
|
|
8265
|
+
const { projectRoot, withLint, withUi } = options;
|
|
8266
|
+
let foreignComponents = null;
|
|
8267
|
+
if (withUi) {
|
|
8268
|
+
try {
|
|
8269
|
+
const result = await runUiAdopt({ projectRoot, dryRun: true });
|
|
8270
|
+
foreignComponents = result.foreign.length;
|
|
8271
|
+
} catch {
|
|
8272
|
+
foreignComponents = null;
|
|
8273
|
+
}
|
|
8274
|
+
}
|
|
8275
|
+
let overridesVariables = null;
|
|
8276
|
+
try {
|
|
8277
|
+
const overridesAbs = path34.join(
|
|
8278
|
+
projectRoot,
|
|
8279
|
+
"tokens",
|
|
8280
|
+
"tokens.overrides.css"
|
|
8281
|
+
);
|
|
8282
|
+
const raw = await readFileOrNull(overridesAbs);
|
|
8283
|
+
if (raw !== null) {
|
|
8284
|
+
overridesVariables = countCssCustomProperties(raw);
|
|
8285
|
+
}
|
|
8286
|
+
} catch {
|
|
8287
|
+
overridesVariables = null;
|
|
8288
|
+
}
|
|
8289
|
+
const lines = [];
|
|
8290
|
+
lines.push("\u2022 \u542F\u52A8 dev server \u9A8C\u8BC1\uFF1A`npm run dev`");
|
|
8291
|
+
if (withLint) {
|
|
8292
|
+
lines.push("\u2022 lint \u68C0\u67E5\uFF1A`npm run lint` / `npm run lint:css`");
|
|
8293
|
+
}
|
|
8294
|
+
if (withUi) {
|
|
8295
|
+
if (foreignComponents !== null && foreignComponents > 0) {
|
|
8296
|
+
lines.push(
|
|
8297
|
+
`\u2022 \u68C0\u6D4B\u5230 ${foreignComponents} \u4E2A foreign UI \u7EC4\u4EF6\uFF08\u7528\u6237\u81EA\u6709\uFF0C\u4E0D\u5728 registry\uFF09\u2014 \u53EF\u6267\u884C \`teamix-evo ui add --adopt\` \u590D\u6838\u7EB3\u7BA1\u72B6\u6001`
|
|
8298
|
+
);
|
|
8299
|
+
} else if (foreignComponents === 0) {
|
|
8300
|
+
lines.push(
|
|
8301
|
+
"\u2022 UI \u63A5\u7BA1\u5B8C\u6210\uFF1A\u5F53\u524D src/components/ui/ \u5DF2\u5168\u90E8\u6CE8\u518C\u5230 manifest"
|
|
8302
|
+
);
|
|
8303
|
+
}
|
|
8304
|
+
}
|
|
8305
|
+
if (overridesVariables !== null && overridesVariables > 0) {
|
|
8306
|
+
lines.push(
|
|
8307
|
+
`\u2022 tokens.overrides.css \u542B ${overridesVariables} \u4E2A CSS \u53D8\u91CF \u2014 \u540E\u7EED\u53EF\u901A\u8FC7 \`teamix-evo tokens audit\` \u5BA1\u8BA1\u5197\u4F59 / \u63A8\u8350\u8FC1\u79FB\uFF08Phase 4.C.5\uFF09`
|
|
8308
|
+
);
|
|
8309
|
+
}
|
|
8310
|
+
lines.push(
|
|
8311
|
+
"\u2022 \u8BA9 AI \u63A5\u529B\uFF1A\u5728 IDE \u5185\u89E6\u53D1 `teamix-evo-manage` skill\uFF0C\u6309 adopt \u2192 upgrade \u2192 tokens audit \u2192 AGENTS.md \u56DE\u586B \u2192 lint 5 \u6B65\u8D70\u5B8C\u95ED\u73AF"
|
|
8312
|
+
);
|
|
8313
|
+
return {
|
|
8314
|
+
stats: { foreignComponents, overridesVariables },
|
|
8315
|
+
lines
|
|
8316
|
+
};
|
|
8317
|
+
}
|
|
8318
|
+
var POST_INIT_HEADING = HEADING;
|
|
8319
|
+
function countCssCustomProperties(content) {
|
|
8320
|
+
const seen = /* @__PURE__ */ new Set();
|
|
8321
|
+
for (const m of content.matchAll(/^\s*(--[a-z][a-z0-9-]*)\s*:/gim)) {
|
|
8322
|
+
if (m[1]) seen.add(m[1]);
|
|
8323
|
+
}
|
|
8324
|
+
return seen.size;
|
|
8325
|
+
}
|
|
8326
|
+
|
|
6250
8327
|
// src/commands/init/wizard.ts
|
|
6251
8328
|
import * as p from "@clack/prompts";
|
|
6252
8329
|
var FRIENDLY_KEY = {
|
|
@@ -6255,7 +8332,9 @@ var FRIENDLY_KEY = {
|
|
|
6255
8332
|
"tailwind-config": "tailwind.config.*",
|
|
6256
8333
|
tokens: "tokens / src/design-tokens.css",
|
|
6257
8334
|
"index-css": "src/index.css",
|
|
6258
|
-
"shadcn-source": "src/lib/utils.ts + src/components/ui/"
|
|
8335
|
+
"shadcn-source": "src/lib/utils.ts + src/components/ui/",
|
|
8336
|
+
"eslint-config": "eslint.config.* / .eslintrc.*",
|
|
8337
|
+
"stylelint-config": "stylelint.config.* / .stylelintrc.*"
|
|
6259
8338
|
};
|
|
6260
8339
|
var STRATEGY_LABEL = {
|
|
6261
8340
|
overwrite: "\u8986\u76D6\uFF08\u5199\u5165\u65B0\u6587\u4EF6\uFF09",
|
|
@@ -6267,7 +8346,8 @@ var STRATEGY_LABEL = {
|
|
|
6267
8346
|
coexist: "\u5171\u5B58\uFF08\u4EC5\u5199 overrides\uFF0C\u4E0D\u8986\u76D6\u73B0\u6709\uFF09",
|
|
6268
8347
|
append: "\u8FFD\u52A0 @import \u884C + managed \u6807\u8BB0\uFF08\u63A8\u8350\uFF09",
|
|
6269
8348
|
"skip-existing": "\u8DF3\u8FC7\u540C\u540D\u6587\u4EF6\uFF0C\u4EC5\u88C5\u65B0\u589E\uFF08\u63A8\u8350\uFF09",
|
|
6270
|
-
"per-file-prompt": "\u9010\u6587\u4EF6\u8BE2\u95EE"
|
|
8349
|
+
"per-file-prompt": "\u9010\u6587\u4EF6\u8BE2\u95EE",
|
|
8350
|
+
merge: "\u5907\u4EFD + \u5199\u6A21\u677F\uFF0C\u8D77 AI \u52A9\u624B\u624B\u5408\u5E76\uFF08\u63A8\u8350\uFF09"
|
|
6271
8351
|
};
|
|
6272
8352
|
function strategyLabel(s) {
|
|
6273
8353
|
return STRATEGY_LABEL[s];
|
|
@@ -6446,10 +8526,10 @@ var STEP_ICON = {
|
|
|
6446
8526
|
};
|
|
6447
8527
|
|
|
6448
8528
|
// src/commands/init/index.ts
|
|
6449
|
-
var initCommand5 = new
|
|
8529
|
+
var initCommand5 = new Command35("init").description(
|
|
6450
8530
|
"\u628A teamix-evo AI \u5957\u4EF6\u63A5\u5165\u5DF2\u6709\u5DE5\u7A0B\uFF08\u666E\u901A\u7248\u63A5\u5165\uFF1A\u68C0\u6D4B\u51B2\u7A81 \u2192 \u5F15\u5BFC wizard \u2192 \u9759\u9ED8\u843D\u5730\uFF09"
|
|
6451
8531
|
).option("--cwd <dir>", "\u6307\u5B9A\u5DE5\u7A0B\u6839\u76EE\u5F55\uFF08\u9ED8\u8BA4\uFF1A\u5F53\u524D\u76EE\u5F55\uFF09").option("-y, --yes", "\u8DF3\u8FC7\u4EA4\u4E92\uFF0C\u5168\u90E8\u4F7F\u7528\u63A8\u8350\u9ED8\u8BA4\u503C").option("--dry-run", "\u53EA\u8F93\u51FA\u6267\u884C\u8BA1\u5212\uFF0C\u4E0D\u5199\u4EFB\u4F55\u6587\u4EF6").option("--variant <name>", "tokens variant\uFF08\u8986\u76D6 wizard \u8BE2\u95EE\uFF09").action(async (opts) => {
|
|
6452
|
-
const cwd =
|
|
8532
|
+
const cwd = path35.resolve(opts.cwd ?? process.cwd());
|
|
6453
8533
|
try {
|
|
6454
8534
|
const state = await detectProjectState(cwd);
|
|
6455
8535
|
if (state.state === "empty") {
|
|
@@ -6496,11 +8576,15 @@ var initCommand5 = new Command33("init").description(
|
|
|
6496
8576
|
logger.info("\u{1F4CB} dry-run \u6A21\u5F0F\uFF1A\u4EE5\u4E0B\u8BA1\u5212\u4E0D\u4F1A\u5199\u5165\u4EFB\u4F55\u6587\u4EF6");
|
|
6497
8577
|
}
|
|
6498
8578
|
const legacyTokensPaths = (conflicts.items.find((it) => it.key === "tokens")?.paths ?? []).filter((p2) => !p2.startsWith("tokens/"));
|
|
8579
|
+
const legacyEslintPaths = conflicts.items.find((it) => it.key === "eslint-config")?.paths ?? [];
|
|
8580
|
+
const legacyStylelintPaths = conflicts.items.find((it) => it.key === "stylelint-config")?.paths ?? [];
|
|
6499
8581
|
const result = await runProjectInit({
|
|
6500
8582
|
projectRoot: cwd,
|
|
6501
8583
|
answers,
|
|
6502
8584
|
dryRun,
|
|
6503
8585
|
legacyTokensPaths,
|
|
8586
|
+
legacyEslintPaths,
|
|
8587
|
+
legacyStylelintPaths,
|
|
6504
8588
|
onStep: (step) => {
|
|
6505
8589
|
const icon = STEP_ICON[step.status];
|
|
6506
8590
|
const detail = step.detail ? ` \u2014 ${step.detail}` : "";
|
|
@@ -6552,12 +8636,23 @@ var initCommand5 = new Command33("init").description(
|
|
|
6552
8636
|
logger.info(` \u2022 ${w.key} \u2192 ${w.strategy}`);
|
|
6553
8637
|
}
|
|
6554
8638
|
}
|
|
8639
|
+
if (result.status !== "dry-run" && result.changes.length > 0) {
|
|
8640
|
+
logger.info("");
|
|
8641
|
+
logger.info("\u672C\u6B21 init \u6587\u4EF6\u53D8\u66F4\u6E05\u5355\uFF1A");
|
|
8642
|
+
for (const line of formatFileChangeSummary(result.changes)) {
|
|
8643
|
+
logger.info(line);
|
|
8644
|
+
}
|
|
8645
|
+
}
|
|
6555
8646
|
if (result.status === "installed") {
|
|
8647
|
+
const guidance = await buildPostInitGuidance({
|
|
8648
|
+
projectRoot: cwd,
|
|
8649
|
+
withLint: answers.withLint,
|
|
8650
|
+
withUi: answers.withUi
|
|
8651
|
+
});
|
|
6556
8652
|
logger.info("");
|
|
6557
|
-
logger.info(
|
|
6558
|
-
|
|
6559
|
-
|
|
6560
|
-
logger.info(" \u2022 lint \u68C0\u67E5\uFF1A`npm run lint` / `npm run lint:css`");
|
|
8653
|
+
logger.info(POST_INIT_HEADING);
|
|
8654
|
+
for (const line of guidance.lines) {
|
|
8655
|
+
logger.info(` ${line}`);
|
|
6561
8656
|
}
|
|
6562
8657
|
}
|
|
6563
8658
|
} catch (err) {
|
|
@@ -6573,11 +8668,11 @@ var initCommand5 = new Command33("init").description(
|
|
|
6573
8668
|
});
|
|
6574
8669
|
|
|
6575
8670
|
// src/commands/update/index.ts
|
|
6576
|
-
import { Command as
|
|
6577
|
-
import * as
|
|
8671
|
+
import { Command as Command36 } from "commander";
|
|
8672
|
+
import * as path37 from "path";
|
|
6578
8673
|
|
|
6579
8674
|
// src/core/project-update.ts
|
|
6580
|
-
import * as
|
|
8675
|
+
import * as path36 from "path";
|
|
6581
8676
|
import {
|
|
6582
8677
|
loadTokensPackageManifest as loadTokensPackageManifest3,
|
|
6583
8678
|
getVariantEntry as getVariantEntry3
|
|
@@ -6828,7 +8923,7 @@ async function runComponentSourceStep(category, args) {
|
|
|
6828
8923
|
});
|
|
6829
8924
|
return;
|
|
6830
8925
|
}
|
|
6831
|
-
const stagingRel =
|
|
8926
|
+
const stagingRel = path36.relative(projectRoot, built.stagingDir);
|
|
6832
8927
|
const summary = summarizeStagingRisk(built.manifest.summary.byRisk);
|
|
6833
8928
|
record({
|
|
6834
8929
|
name: category,
|
|
@@ -6871,10 +8966,10 @@ async function planTokensUpdate(tokensPackage, config) {
|
|
|
6871
8966
|
}
|
|
6872
8967
|
|
|
6873
8968
|
// src/commands/update/index.ts
|
|
6874
|
-
var updateCommand3 = new
|
|
8969
|
+
var updateCommand3 = new Command36("update").description(
|
|
6875
8970
|
"\u4E00\u952E\u5347\u7EA7 teamix-evo \u5DF2\u88C5\u8D44\u6E90\uFF08regenerable \u8986\u76D6\u3001frozen \u4FDD\u7559\u3001managed \u5408\u5E76 \u2014 ADR 0003 \u4E09\u6001\uFF09"
|
|
6876
8971
|
).option("--cwd <dir>", "\u6307\u5B9A\u5DE5\u7A0B\u6839\u76EE\u5F55\uFF08\u9ED8\u8BA4\uFF1A\u5F53\u524D\u76EE\u5F55\uFF09").option("--dry-run", "\u53EA\u8F93\u51FA\u5347\u7EA7\u8BA1\u5212\uFF0C\u4E0D\u5199\u4EFB\u4F55\u6587\u4EF6").action(async (opts) => {
|
|
6877
|
-
const cwd =
|
|
8972
|
+
const cwd = path37.resolve(opts.cwd ?? process.cwd());
|
|
6878
8973
|
const dryRun = opts.dryRun ?? false;
|
|
6879
8974
|
try {
|
|
6880
8975
|
if (dryRun) {
|
|
@@ -6956,14 +9051,14 @@ var updateCommand3 = new Command34("update").description(
|
|
|
6956
9051
|
});
|
|
6957
9052
|
|
|
6958
9053
|
// src/commands/restore/index.ts
|
|
6959
|
-
import { Command as
|
|
6960
|
-
import * as
|
|
9054
|
+
import { Command as Command37 } from "commander";
|
|
9055
|
+
import * as path38 from "path";
|
|
6961
9056
|
import * as prompts7 from "@clack/prompts";
|
|
6962
9057
|
function createRestoreCommand() {
|
|
6963
|
-
return new
|
|
9058
|
+
return new Command37("restore").description(
|
|
6964
9059
|
"\u56DE\u6EDA .teamix-evo/ \u5230\u6307\u5B9A snapshot\uFF08init/update \u5931\u8D25\u65F6\u4F7F\u7528 \u2014 ADR 0019 \xA72\uFF09"
|
|
6965
9060
|
).argument("[ts]", "\u76EE\u6807 snapshot \u65F6\u95F4\u6233\uFF08\u5982 2026-06-11T20-59-03-000Z\uFF09").option("--list", "\u5217\u51FA\u53EF\u7528 snapshot\uFF08\u4E0D\u505A\u5B9E\u9645\u56DE\u6EDA\uFF09").option("--cwd <dir>", "\u6307\u5B9A\u5DE5\u7A0B\u6839\u76EE\u5F55\uFF08\u9ED8\u8BA4\uFF1A\u5F53\u524D\u76EE\u5F55\uFF09").option("-y, --yes", "\u8DF3\u8FC7\u4E8C\u6B21\u786E\u8BA4\uFF08destructive\uFF0C\u8BF7\u786E\u8BA4\u65E0\u8BEF\uFF09").action(async (ts, opts) => {
|
|
6966
|
-
const cwd =
|
|
9061
|
+
const cwd = path38.resolve(opts.cwd ?? process.cwd());
|
|
6967
9062
|
try {
|
|
6968
9063
|
const snapshots = await listSnapshots(cwd);
|
|
6969
9064
|
if (opts.list) {
|
|
@@ -7059,13 +9154,13 @@ function printSnapshotTable(snapshots) {
|
|
|
7059
9154
|
}
|
|
7060
9155
|
|
|
7061
9156
|
// src/commands/switch/index.ts
|
|
7062
|
-
import { Command as
|
|
7063
|
-
import * as
|
|
9157
|
+
import { Command as Command38 } from "commander";
|
|
9158
|
+
import * as path40 from "path";
|
|
7064
9159
|
import * as prompts8 from "@clack/prompts";
|
|
7065
9160
|
|
|
7066
9161
|
// src/core/variant-switch.ts
|
|
7067
|
-
import * as
|
|
7068
|
-
import * as
|
|
9162
|
+
import * as path39 from "path";
|
|
9163
|
+
import * as fs27 from "fs/promises";
|
|
7069
9164
|
import {
|
|
7070
9165
|
loadTokensPackageManifest as loadTokensPackageManifest4,
|
|
7071
9166
|
getVariantEntry as getVariantEntry4
|
|
@@ -7099,8 +9194,8 @@ async function runVariantSwitch(options) {
|
|
|
7099
9194
|
const upstreamByBasename = /* @__PURE__ */ new Map();
|
|
7100
9195
|
for (const fileRel of variantEntry.files) {
|
|
7101
9196
|
upstreamByBasename.set(
|
|
7102
|
-
|
|
7103
|
-
|
|
9197
|
+
path39.basename(fileRel),
|
|
9198
|
+
path39.join(packageRoot, fileRel)
|
|
7104
9199
|
);
|
|
7105
9200
|
}
|
|
7106
9201
|
const prior = await readInstalledManifest(projectRoot) ?? {
|
|
@@ -7114,7 +9209,7 @@ async function runVariantSwitch(options) {
|
|
|
7114
9209
|
const priorVersion = installedIdx >= 0 ? prior.installed[installedIdx].version : "0.0.0";
|
|
7115
9210
|
const changes = [];
|
|
7116
9211
|
for (const resource of priorResources) {
|
|
7117
|
-
const consumerBasename =
|
|
9212
|
+
const consumerBasename = path39.basename(resource.target);
|
|
7118
9213
|
const upstreamBasename = lookupUpstreamBasename2(consumerBasename);
|
|
7119
9214
|
const upstreamAbs = upstreamBasename ? upstreamByBasename.get(upstreamBasename) : void 0;
|
|
7120
9215
|
if (resource.strategy === "frozen") {
|
|
@@ -7135,7 +9230,7 @@ async function runVariantSwitch(options) {
|
|
|
7135
9230
|
});
|
|
7136
9231
|
continue;
|
|
7137
9232
|
}
|
|
7138
|
-
const upstreamContent = await
|
|
9233
|
+
const upstreamContent = await fs27.readFile(upstreamAbs, "utf-8");
|
|
7139
9234
|
if (resource.strategy === "regenerable") {
|
|
7140
9235
|
const newHash = computeHash(upstreamContent);
|
|
7141
9236
|
if (newHash === resource.hash) {
|
|
@@ -7188,12 +9283,12 @@ async function runVariantSwitch(options) {
|
|
|
7188
9283
|
}
|
|
7189
9284
|
const refreshedResources = [];
|
|
7190
9285
|
for (const resource of priorResources) {
|
|
7191
|
-
const consumerAbs =
|
|
7192
|
-
const consumerBasename =
|
|
9286
|
+
const consumerAbs = path39.isAbsolute(resource.target) ? resource.target : path39.join(projectRoot, resource.target);
|
|
9287
|
+
const consumerBasename = path39.basename(resource.target);
|
|
7193
9288
|
const upstreamBasename = lookupUpstreamBasename2(consumerBasename);
|
|
7194
9289
|
const upstreamAbs = upstreamBasename ? upstreamByBasename.get(upstreamBasename) : void 0;
|
|
7195
9290
|
if (resource.strategy === "regenerable" && upstreamAbs) {
|
|
7196
|
-
const upstreamContent = await
|
|
9291
|
+
const upstreamContent = await fs27.readFile(upstreamAbs, "utf-8");
|
|
7197
9292
|
await writeFileSafe(consumerAbs, upstreamContent);
|
|
7198
9293
|
logger.debug(`[variant-switch] rewrite regenerable: ${resource.target}`);
|
|
7199
9294
|
refreshedResources.push({
|
|
@@ -7203,8 +9298,8 @@ async function runVariantSwitch(options) {
|
|
|
7203
9298
|
continue;
|
|
7204
9299
|
}
|
|
7205
9300
|
if (resource.strategy === "managed" && upstreamAbs && await fileExists(consumerAbs)) {
|
|
7206
|
-
const upstreamContent = await
|
|
7207
|
-
const consumerContent = await
|
|
9301
|
+
const upstreamContent = await fs27.readFile(upstreamAbs, "utf-8");
|
|
9302
|
+
const consumerContent = await fs27.readFile(consumerAbs, "utf-8");
|
|
7208
9303
|
const merged = mergeManagedRegions(upstreamContent, consumerContent);
|
|
7209
9304
|
if (merged !== consumerContent) {
|
|
7210
9305
|
await writeFileSafe(consumerAbs, merged);
|
|
@@ -7235,7 +9330,7 @@ async function runVariantSwitch(options) {
|
|
|
7235
9330
|
installedAt: now
|
|
7236
9331
|
};
|
|
7237
9332
|
await writeFileSafe(
|
|
7238
|
-
|
|
9333
|
+
path39.join(projectRoot, ".teamix-evo", "tokens-lock.json"),
|
|
7239
9334
|
JSON.stringify(lock, null, 2) + "\n"
|
|
7240
9335
|
);
|
|
7241
9336
|
logger.debug(
|
|
@@ -7332,10 +9427,10 @@ var KIND_LABEL = {
|
|
|
7332
9427
|
unchanged: "unchanged"
|
|
7333
9428
|
};
|
|
7334
9429
|
function createSwitchCommand() {
|
|
7335
|
-
return new
|
|
9430
|
+
return new Command38("switch").description(
|
|
7336
9431
|
"variant \u5207\u6362\uFF1A\u5148\u5C55\u793A file-level diff\uFF0C--apply \u624D\u771F\u5199\uFF08ADR 0019 \xA7D3\uFF09"
|
|
7337
9432
|
).argument("<new-variant>", "\u76EE\u6807 variant id\uFF08\u5982 opentrek / uni-manager\uFF09").option("--cwd <dir>", "\u6307\u5B9A\u5DE5\u7A0B\u6839\u76EE\u5F55\uFF08\u9ED8\u8BA4\uFF1A\u5F53\u524D\u76EE\u5F55\uFF09").option("--apply", "\u771F\u6B63\u6267\u884C\u5207\u6362\uFF08\u9ED8\u8BA4 dry-run\uFF0C\u4EC5\u5C55\u793A\u8BA1\u5212\uFF09").option("-y, --yes", "\u8DF3\u8FC7 --apply \u4E8C\u6B21\u786E\u8BA4\uFF08destructive\uFF0C\u8BF7\u786E\u8BA4\u65E0\u8BEF\uFF09").action(async (newVariant, opts) => {
|
|
7338
|
-
const cwd =
|
|
9433
|
+
const cwd = path40.resolve(opts.cwd ?? process.cwd());
|
|
7339
9434
|
const apply = opts.apply ?? false;
|
|
7340
9435
|
try {
|
|
7341
9436
|
const plan = await runVariantSwitch({
|
|
@@ -7451,7 +9546,7 @@ function printChangePlan(from, to, toVersion, changes) {
|
|
|
7451
9546
|
// src/index.ts
|
|
7452
9547
|
var require8 = createRequire8(import.meta.url);
|
|
7453
9548
|
var { version } = require8("../package.json");
|
|
7454
|
-
var program = new
|
|
9549
|
+
var program = new Command39();
|
|
7455
9550
|
program.name("teamix-evo").description("Where ideas evolve. \u2014 AI Coding \u5957\u4EF6").version(version);
|
|
7456
9551
|
program.addCommand(tokensCommand);
|
|
7457
9552
|
program.addCommand(skillsCommand);
|