teamix-evo 0.5.0 → 0.6.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 +5 -3
- package/dist/core/index.d.ts +150 -24
- package/dist/core/index.js +416 -67
- package/dist/core/index.js.map +1 -1
- package/dist/index.js +780 -253
- package/dist/index.js.map +1 -1
- package/package.json +8 -7
package/dist/core/index.js
CHANGED
|
@@ -517,9 +517,11 @@ function makeMirrorRecord(skill, targetAbs, content, ide, scope, rel2) {
|
|
|
517
517
|
}
|
|
518
518
|
async function updateSkills(options) {
|
|
519
519
|
const { manifest, ides, scope, projectRoot } = options;
|
|
520
|
+
const idFilter = options.onlyIds ? new Set(options.onlyIds) : null;
|
|
520
521
|
const summary = { overwritten: 0, managed: 0, skipped: 0, created: 0 };
|
|
521
522
|
const updated = [];
|
|
522
523
|
for (const skill of manifest.skills) {
|
|
524
|
+
if (idFilter && !idFilter.has(skill.id)) continue;
|
|
523
525
|
const skillIdes = skill.ides.filter((i) => ides.includes(i));
|
|
524
526
|
if (skillIdes.length === 0) continue;
|
|
525
527
|
const sourceRecords = await rewriteSkillSource(
|
|
@@ -721,11 +723,73 @@ async function ensureMcpJson(projectRoot) {
|
|
|
721
723
|
// src/core/skills-add.ts
|
|
722
724
|
var DEFAULT_SKILLS_PACKAGE = "@teamix-evo/skills";
|
|
723
725
|
var FLAT_VARIANT = "_flat";
|
|
726
|
+
async function runSkillsInit(options) {
|
|
727
|
+
const { projectRoot } = options;
|
|
728
|
+
const packageName = options.packageName ?? DEFAULT_SKILLS_PACKAGE;
|
|
729
|
+
const ides = [...options.ides];
|
|
730
|
+
const scope = options.scope;
|
|
731
|
+
if (ides.length === 0) {
|
|
732
|
+
throw new Error("At least one IDE must be selected.");
|
|
733
|
+
}
|
|
734
|
+
await ensureTeamixDir(projectRoot);
|
|
735
|
+
const existingConfig = await readProjectConfig(projectRoot);
|
|
736
|
+
const existingSkillsCfg = existingConfig?.packages?.skills;
|
|
737
|
+
const { manifest, data, packageRoot } = await loadSkillsData(packageName);
|
|
738
|
+
const currentTokensVariant = await readTokensVariant(projectRoot);
|
|
739
|
+
const existing = await readExistingState(projectRoot, packageName);
|
|
740
|
+
const candidateIds = manifest.skills.filter((s) => {
|
|
741
|
+
const effectiveScope = s.scope ?? "project";
|
|
742
|
+
if (effectiveScope !== scope) {
|
|
743
|
+
logger.debug(
|
|
744
|
+
`Skipping skill "${s.id}" (scope=${effectiveScope}): current install scope is "${scope}". Use \`skills add ${s.id} --scope ${effectiveScope}\` to install.`
|
|
745
|
+
);
|
|
746
|
+
return false;
|
|
747
|
+
}
|
|
748
|
+
if (!s.variant) return true;
|
|
749
|
+
if (!currentTokensVariant) {
|
|
750
|
+
logger.debug(
|
|
751
|
+
`Skipping variant-bound skill "${s.id}" (variant=${s.variant}): no tokens variant installed; will be picked up when "tokens init" runs.`
|
|
752
|
+
);
|
|
753
|
+
return false;
|
|
754
|
+
}
|
|
755
|
+
if (s.variant !== currentTokensVariant) {
|
|
756
|
+
logger.debug(
|
|
757
|
+
`Skipping variant-bound skill "${s.id}" (variant=${s.variant}): current tokens variant is "${currentTokensVariant}".`
|
|
758
|
+
);
|
|
759
|
+
return false;
|
|
760
|
+
}
|
|
761
|
+
return true;
|
|
762
|
+
}).map((s) => s.id);
|
|
763
|
+
const skippedSkillIds = candidateIds.filter(
|
|
764
|
+
(id) => existing.skillIds.has(id)
|
|
765
|
+
);
|
|
766
|
+
const onlyIds = candidateIds.filter((id) => !existing.skillIds.has(id));
|
|
767
|
+
if (existingSkillsCfg && onlyIds.length === 0) {
|
|
768
|
+
return { status: "already-initialized" };
|
|
769
|
+
}
|
|
770
|
+
return finalizeSkillsInstall({
|
|
771
|
+
projectRoot,
|
|
772
|
+
packageName,
|
|
773
|
+
ideIdent: options.ide ?? "qoder",
|
|
774
|
+
manifest,
|
|
775
|
+
data,
|
|
776
|
+
packageRoot,
|
|
777
|
+
ides,
|
|
778
|
+
scope,
|
|
779
|
+
onlyIds,
|
|
780
|
+
skippedSkillIds,
|
|
781
|
+
existing,
|
|
782
|
+
existingConfig
|
|
783
|
+
});
|
|
784
|
+
}
|
|
724
785
|
async function runSkillsAdd(options) {
|
|
786
|
+
if (!options.names || options.names.length === 0) {
|
|
787
|
+
throw new Error(
|
|
788
|
+
"runSkillsAdd requires at least one skill id. Use runSkillsInit() for bulk install."
|
|
789
|
+
);
|
|
790
|
+
}
|
|
725
791
|
const { projectRoot, names: requestedNames } = options;
|
|
726
792
|
const packageName = options.packageName ?? DEFAULT_SKILLS_PACKAGE;
|
|
727
|
-
const ideIdent = options.ide ?? "qoder";
|
|
728
|
-
const isIncremental = !!requestedNames && requestedNames.length > 0;
|
|
729
793
|
await ensureTeamixDir(projectRoot);
|
|
730
794
|
const existingConfig = await readProjectConfig(projectRoot);
|
|
731
795
|
const existingSkillsCfg = existingConfig?.packages?.skills;
|
|
@@ -738,57 +802,27 @@ async function runSkillsAdd(options) {
|
|
|
738
802
|
throw new Error("Scope must be specified (project | global).");
|
|
739
803
|
}
|
|
740
804
|
const { manifest, data, packageRoot } = await loadSkillsData(packageName);
|
|
741
|
-
const
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
const
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
805
|
+
const known = new Set(manifest.skills.map((s) => s.id));
|
|
806
|
+
const unknown = requestedNames.filter((n) => !known.has(n));
|
|
807
|
+
if (unknown.length > 0) {
|
|
808
|
+
const available = [...known].join(", ");
|
|
809
|
+
throw new Error(
|
|
810
|
+
`Unknown skill id(s): ${unknown.join(", ")}. Available: ${available || "(none)"}.`
|
|
811
|
+
);
|
|
812
|
+
}
|
|
813
|
+
for (const s of manifest.skills) {
|
|
814
|
+
if (requestedNames.includes(s.id) && s.scope && s.scope !== scope) {
|
|
815
|
+
logger.warn(
|
|
816
|
+
`"${s.id}" \u63A8\u8350 ${s.scope} scope \u5B89\u88C5\u3002\u5F53\u524D\u4EE5 ${scope} scope \u5F3A\u5236\u5B89\u88C5,\u53EF\u80FD\u4E0E\u53E6\u4E00 scope \u7684\u526F\u672C\u51B2\u7A81\u3002\u5EFA\u8BAE\u6539\u7528 \`skills add ${s.id} --scope ${s.scope}\`\u3002`
|
|
749
817
|
);
|
|
750
818
|
}
|
|
751
819
|
}
|
|
752
|
-
const
|
|
753
|
-
const
|
|
754
|
-
(
|
|
820
|
+
const existing = await readExistingState(projectRoot, packageName);
|
|
821
|
+
const skippedSkillIds = requestedNames.filter(
|
|
822
|
+
(n) => existing.skillIds.has(n)
|
|
755
823
|
);
|
|
756
|
-
const
|
|
757
|
-
|
|
758
|
-
...Object.keys(existingLock?.skills ?? {}),
|
|
759
|
-
// Legacy fallback: pre-ADR-0013 installs only had manifest.json. Derive
|
|
760
|
-
// skill ids by stripping the trailing :source / :sub-file suffix.
|
|
761
|
-
...(existingPkg?.resources ?? []).map((r) => r.id.split(":")[0])
|
|
762
|
-
]);
|
|
763
|
-
let onlyIds;
|
|
764
|
-
let skippedSkillIds;
|
|
765
|
-
if (isIncremental) {
|
|
766
|
-
skippedSkillIds = requestedNames.filter((n) => existingSkillIds.has(n));
|
|
767
|
-
onlyIds = requestedNames.filter((n) => !existingSkillIds.has(n));
|
|
768
|
-
} else {
|
|
769
|
-
const candidateIds = manifest.skills.filter((s) => {
|
|
770
|
-
if (!s.variant) return true;
|
|
771
|
-
if (!currentTokensVariant) {
|
|
772
|
-
logger.debug(
|
|
773
|
-
`Skipping variant-bound skill "${s.id}" (variant=${s.variant}): no tokens variant installed; will be picked up when "tokens init" runs.`
|
|
774
|
-
);
|
|
775
|
-
return false;
|
|
776
|
-
}
|
|
777
|
-
if (s.variant !== currentTokensVariant) {
|
|
778
|
-
logger.debug(
|
|
779
|
-
`Skipping variant-bound skill "${s.id}" (variant=${s.variant}): current tokens variant is "${currentTokensVariant}".`
|
|
780
|
-
);
|
|
781
|
-
return false;
|
|
782
|
-
}
|
|
783
|
-
return true;
|
|
784
|
-
}).map((s) => s.id);
|
|
785
|
-
skippedSkillIds = candidateIds.filter((id) => existingSkillIds.has(id));
|
|
786
|
-
onlyIds = candidateIds.filter((id) => !existingSkillIds.has(id));
|
|
787
|
-
}
|
|
788
|
-
if (!isIncremental && existingSkillsCfg && onlyIds.length === 0) {
|
|
789
|
-
return { status: "already-added" };
|
|
790
|
-
}
|
|
791
|
-
if (isIncremental && onlyIds.length === 0) {
|
|
824
|
+
const onlyIds = requestedNames.filter((n) => !existing.skillIds.has(n));
|
|
825
|
+
if (onlyIds.length === 0) {
|
|
792
826
|
return {
|
|
793
827
|
status: "installed",
|
|
794
828
|
packageName,
|
|
@@ -802,6 +836,48 @@ async function runSkillsAdd(options) {
|
|
|
802
836
|
skippedSkillIds
|
|
803
837
|
};
|
|
804
838
|
}
|
|
839
|
+
return finalizeSkillsInstall({
|
|
840
|
+
projectRoot,
|
|
841
|
+
packageName,
|
|
842
|
+
ideIdent: options.ide ?? "qoder",
|
|
843
|
+
manifest,
|
|
844
|
+
data,
|
|
845
|
+
packageRoot,
|
|
846
|
+
ides,
|
|
847
|
+
scope,
|
|
848
|
+
onlyIds,
|
|
849
|
+
skippedSkillIds,
|
|
850
|
+
existing,
|
|
851
|
+
existingConfig
|
|
852
|
+
});
|
|
853
|
+
}
|
|
854
|
+
async function readExistingState(projectRoot, packageName) {
|
|
855
|
+
const installed = await readInstalledManifest(projectRoot);
|
|
856
|
+
const pkg = installed?.installed.find((p) => p.package === packageName);
|
|
857
|
+
const lock = await readSkillsLock(projectRoot);
|
|
858
|
+
const skillIds = /* @__PURE__ */ new Set([
|
|
859
|
+
...Object.keys(lock?.skills ?? {}),
|
|
860
|
+
// Legacy fallback: pre-ADR-0013 installs only had manifest.json. Derive
|
|
861
|
+
// skill ids by stripping the trailing :source / :sub-file suffix.
|
|
862
|
+
...(pkg?.resources ?? []).map((r) => r.id.split(":")[0] ?? r.id)
|
|
863
|
+
]);
|
|
864
|
+
return { installed, pkg, lock, skillIds };
|
|
865
|
+
}
|
|
866
|
+
async function finalizeSkillsInstall(args) {
|
|
867
|
+
const {
|
|
868
|
+
projectRoot,
|
|
869
|
+
packageName,
|
|
870
|
+
ideIdent,
|
|
871
|
+
manifest,
|
|
872
|
+
data,
|
|
873
|
+
packageRoot,
|
|
874
|
+
ides,
|
|
875
|
+
scope,
|
|
876
|
+
onlyIds,
|
|
877
|
+
skippedSkillIds,
|
|
878
|
+
existing,
|
|
879
|
+
existingConfig
|
|
880
|
+
} = args;
|
|
805
881
|
const result = await installSkills({
|
|
806
882
|
projectRoot,
|
|
807
883
|
manifest,
|
|
@@ -825,7 +901,7 @@ async function runSkillsAdd(options) {
|
|
|
825
901
|
};
|
|
826
902
|
await writeProjectConfig(projectRoot, config);
|
|
827
903
|
const installedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
828
|
-
const installedManifest =
|
|
904
|
+
const installedManifest = existing.installed ?? {
|
|
829
905
|
schemaVersion: 1,
|
|
830
906
|
installed: []
|
|
831
907
|
};
|
|
@@ -833,7 +909,7 @@ async function runSkillsAdd(options) {
|
|
|
833
909
|
(p) => p.package === packageName
|
|
834
910
|
);
|
|
835
911
|
const mergedResources = mergeInstalledResources(
|
|
836
|
-
|
|
912
|
+
existing.pkg?.resources ?? [],
|
|
837
913
|
result.resources
|
|
838
914
|
);
|
|
839
915
|
const entry = {
|
|
@@ -846,7 +922,7 @@ async function runSkillsAdd(options) {
|
|
|
846
922
|
if (idx >= 0) installedManifest.installed[idx] = entry;
|
|
847
923
|
else installedManifest.installed.push(entry);
|
|
848
924
|
await writeInstalledManifest(projectRoot, installedManifest);
|
|
849
|
-
const lock =
|
|
925
|
+
const lock = existing.lock ?? {
|
|
850
926
|
schemaVersion: 1,
|
|
851
927
|
skills: {}
|
|
852
928
|
};
|
|
@@ -1138,6 +1214,176 @@ async function listTokenVariants(packageName = DEFAULT_TOKENS_PACKAGE, packageRo
|
|
|
1138
1214
|
};
|
|
1139
1215
|
}
|
|
1140
1216
|
|
|
1217
|
+
// src/core/skills-update.ts
|
|
1218
|
+
var DEFAULT_SKILLS_PACKAGE3 = "@teamix-evo/skills";
|
|
1219
|
+
var FLAT_VARIANT2 = "_flat";
|
|
1220
|
+
async function runSkillsUpdate(options) {
|
|
1221
|
+
const { projectRoot, names: requestedNames, dryRun } = options;
|
|
1222
|
+
const packageName = options.packageName ?? DEFAULT_SKILLS_PACKAGE3;
|
|
1223
|
+
const config = await readProjectConfig(projectRoot);
|
|
1224
|
+
const skillsCfg = config?.packages?.skills;
|
|
1225
|
+
if (!skillsCfg) {
|
|
1226
|
+
return { status: "no-skills" };
|
|
1227
|
+
}
|
|
1228
|
+
const ides = skillsCfg.ides ?? ["qoder", "claude"];
|
|
1229
|
+
const scope = skillsCfg.scope ?? "project";
|
|
1230
|
+
const existingLock = await readSkillsLock(projectRoot);
|
|
1231
|
+
if (!existingLock || Object.keys(existingLock.skills).length === 0) {
|
|
1232
|
+
return { status: "no-skills" };
|
|
1233
|
+
}
|
|
1234
|
+
const { manifest, data, packageRoot } = await loadSkillsData(packageName);
|
|
1235
|
+
const manifestById = new Map(manifest.skills.map((s) => [s.id, s]));
|
|
1236
|
+
const lockIds = Object.keys(existingLock.skills);
|
|
1237
|
+
const requestedSet = requestedNames ? new Set(requestedNames) : null;
|
|
1238
|
+
if (requestedSet) {
|
|
1239
|
+
const unknown = requestedNames.filter(
|
|
1240
|
+
(n) => !lockIds.includes(n) && !manifestById.has(n)
|
|
1241
|
+
);
|
|
1242
|
+
if (unknown.length > 0) {
|
|
1243
|
+
throw new Error(
|
|
1244
|
+
`Unknown skill id(s): ${unknown.join(
|
|
1245
|
+
", "
|
|
1246
|
+
)}. Available (installed): ${lockIds.join(", ") || "(none)"}.`
|
|
1247
|
+
);
|
|
1248
|
+
}
|
|
1249
|
+
}
|
|
1250
|
+
const targetIds = [];
|
|
1251
|
+
const skippedSkillIds = [];
|
|
1252
|
+
for (const id of lockIds) {
|
|
1253
|
+
if (requestedSet && !requestedSet.has(id)) continue;
|
|
1254
|
+
const entry2 = manifestById.get(id);
|
|
1255
|
+
if (!entry2) {
|
|
1256
|
+
logger.debug(
|
|
1257
|
+
`Skipping "${id}": no longer in upstream manifest. Use \`skills uninstall ${id}\` to remove.`
|
|
1258
|
+
);
|
|
1259
|
+
skippedSkillIds.push(id);
|
|
1260
|
+
continue;
|
|
1261
|
+
}
|
|
1262
|
+
const effectiveScope = entry2.scope ?? "project";
|
|
1263
|
+
if (effectiveScope !== scope) {
|
|
1264
|
+
logger.debug(
|
|
1265
|
+
`Skipping "${id}" (scope=${effectiveScope}): current install scope is "${scope}".`
|
|
1266
|
+
);
|
|
1267
|
+
skippedSkillIds.push(id);
|
|
1268
|
+
continue;
|
|
1269
|
+
}
|
|
1270
|
+
targetIds.push(id);
|
|
1271
|
+
}
|
|
1272
|
+
const allSame = targetIds.every((id) => {
|
|
1273
|
+
const lockVer = existingLock.skills[id].version;
|
|
1274
|
+
const manVer = manifestById.get(id).version;
|
|
1275
|
+
return lockVer === manVer;
|
|
1276
|
+
});
|
|
1277
|
+
if (targetIds.length > 0 && allSame && !dryRun) {
|
|
1278
|
+
return {
|
|
1279
|
+
status: "no-changes",
|
|
1280
|
+
packageName,
|
|
1281
|
+
version: manifest.version,
|
|
1282
|
+
checkedSkillIds: targetIds
|
|
1283
|
+
};
|
|
1284
|
+
}
|
|
1285
|
+
if (dryRun) {
|
|
1286
|
+
const plan = targetIds.map((id) => {
|
|
1287
|
+
const lockVer = existingLock.skills[id].version;
|
|
1288
|
+
const entry2 = manifestById.get(id);
|
|
1289
|
+
const sameVersion = lockVer === entry2.version;
|
|
1290
|
+
return {
|
|
1291
|
+
id,
|
|
1292
|
+
current: lockVer,
|
|
1293
|
+
next: entry2.version,
|
|
1294
|
+
strategy: entry2.updateStrategy ?? "managed",
|
|
1295
|
+
action: sameVersion ? "up-to-date" : "version-bump"
|
|
1296
|
+
};
|
|
1297
|
+
});
|
|
1298
|
+
return {
|
|
1299
|
+
status: "dry-run",
|
|
1300
|
+
packageName,
|
|
1301
|
+
currentVersion: skillsCfg.version,
|
|
1302
|
+
availableVersion: manifest.version,
|
|
1303
|
+
plan
|
|
1304
|
+
};
|
|
1305
|
+
}
|
|
1306
|
+
if (targetIds.length === 0) {
|
|
1307
|
+
return {
|
|
1308
|
+
status: "updated",
|
|
1309
|
+
packageName,
|
|
1310
|
+
version: manifest.version,
|
|
1311
|
+
ides,
|
|
1312
|
+
scope,
|
|
1313
|
+
updatedSkillIds: [],
|
|
1314
|
+
skippedSkillIds,
|
|
1315
|
+
summary: { overwritten: 0, managed: 0, skipped: 0, created: 0 },
|
|
1316
|
+
resources: []
|
|
1317
|
+
};
|
|
1318
|
+
}
|
|
1319
|
+
const result = await updateSkills({
|
|
1320
|
+
projectRoot,
|
|
1321
|
+
manifest,
|
|
1322
|
+
data,
|
|
1323
|
+
packageRoot,
|
|
1324
|
+
ides,
|
|
1325
|
+
scope,
|
|
1326
|
+
onlyIds: targetIds
|
|
1327
|
+
});
|
|
1328
|
+
config.packages.skills = {
|
|
1329
|
+
...skillsCfg,
|
|
1330
|
+
version: manifest.version
|
|
1331
|
+
};
|
|
1332
|
+
await writeProjectConfig(projectRoot, config);
|
|
1333
|
+
const installedManifest = await readInstalledManifest(projectRoot) ?? {
|
|
1334
|
+
schemaVersion: 1,
|
|
1335
|
+
installed: []
|
|
1336
|
+
};
|
|
1337
|
+
const idx = installedManifest.installed.findIndex(
|
|
1338
|
+
(p) => p.package === packageName
|
|
1339
|
+
);
|
|
1340
|
+
const installedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
1341
|
+
const prior = idx >= 0 ? installedManifest.installed[idx].resources : [];
|
|
1342
|
+
const targetSet = new Set(targetIds);
|
|
1343
|
+
const preserved = prior.filter((r) => {
|
|
1344
|
+
const skillId = r.id.split(":")[0];
|
|
1345
|
+
return skillId ? !targetSet.has(skillId) : true;
|
|
1346
|
+
});
|
|
1347
|
+
const entry = {
|
|
1348
|
+
package: packageName,
|
|
1349
|
+
variant: FLAT_VARIANT2,
|
|
1350
|
+
version: manifest.version,
|
|
1351
|
+
installedAt,
|
|
1352
|
+
resources: [...preserved, ...result.resources]
|
|
1353
|
+
};
|
|
1354
|
+
if (idx >= 0) installedManifest.installed[idx] = entry;
|
|
1355
|
+
else installedManifest.installed.push(entry);
|
|
1356
|
+
await writeInstalledManifest(projectRoot, installedManifest);
|
|
1357
|
+
const lock = {
|
|
1358
|
+
schemaVersion: 1,
|
|
1359
|
+
skills: { ...existingLock.skills }
|
|
1360
|
+
};
|
|
1361
|
+
for (const id of targetIds) {
|
|
1362
|
+
const skillDef = manifestById.get(id);
|
|
1363
|
+
if (!skillDef) continue;
|
|
1364
|
+
const mirroredTo = skillDef.ides.filter((i) => ides.includes(i));
|
|
1365
|
+
lock.skills[id] = {
|
|
1366
|
+
version: skillDef.version,
|
|
1367
|
+
from: packageName,
|
|
1368
|
+
installedAt,
|
|
1369
|
+
scope,
|
|
1370
|
+
mirroredTo
|
|
1371
|
+
};
|
|
1372
|
+
}
|
|
1373
|
+
await writeSkillsLock(projectRoot, lock);
|
|
1374
|
+
return {
|
|
1375
|
+
status: "updated",
|
|
1376
|
+
packageName,
|
|
1377
|
+
version: manifest.version,
|
|
1378
|
+
ides,
|
|
1379
|
+
scope,
|
|
1380
|
+
updatedSkillIds: targetIds,
|
|
1381
|
+
skippedSkillIds,
|
|
1382
|
+
summary: result.summary,
|
|
1383
|
+
resources: result.resources
|
|
1384
|
+
};
|
|
1385
|
+
}
|
|
1386
|
+
|
|
1141
1387
|
// src/core/ui-init.ts
|
|
1142
1388
|
var DEFAULT_UI_ALIASES = {
|
|
1143
1389
|
components: "src/components/ui",
|
|
@@ -1471,9 +1717,109 @@ async function runUiList(options) {
|
|
|
1471
1717
|
};
|
|
1472
1718
|
}
|
|
1473
1719
|
|
|
1474
|
-
// src/core/
|
|
1720
|
+
// src/core/lint-init.ts
|
|
1475
1721
|
import * as path12 from "path";
|
|
1476
|
-
import * as fs9 from "fs
|
|
1722
|
+
import * as fs9 from "fs";
|
|
1723
|
+
import { execa } from "execa";
|
|
1724
|
+
var ESLINT_CONFIG_CONTENT = `/**
|
|
1725
|
+
* teamix-evo consumer ESLint preset \u2014 9 token-discipline rules.
|
|
1726
|
+
* - Repo-wide: no-color-literal / no-arbitrary-tw-value / no-raw-color-scale /
|
|
1727
|
+
* no-large-radius / prefer-gap-over-space / no-manual-dark-classnames /
|
|
1728
|
+
* dialog-must-have-title (all error)
|
|
1729
|
+
* - src/components/ui/** only: no-relative-ui-import / icon-from-lucide (error)
|
|
1730
|
+
*
|
|
1731
|
+
* See ADR 0008 / docs/principles.md \xA7P4.
|
|
1732
|
+
*/
|
|
1733
|
+
import consumerPreset from '@teamix-evo/eslint-config/presets/consumer';
|
|
1734
|
+
|
|
1735
|
+
export default [...consumerPreset];
|
|
1736
|
+
`;
|
|
1737
|
+
var STYLELINT_CONFIG_CONTENT = `/** @type {import('stylelint').Config} */
|
|
1738
|
+
module.exports = {
|
|
1739
|
+
extends: ['@teamix-evo/stylelint-config/presets/consumer'],
|
|
1740
|
+
};
|
|
1741
|
+
`;
|
|
1742
|
+
var ESLINT_DEPS = [
|
|
1743
|
+
"@teamix-evo/eslint-config",
|
|
1744
|
+
"eslint",
|
|
1745
|
+
"@typescript-eslint/parser"
|
|
1746
|
+
];
|
|
1747
|
+
var STYLELINT_DEPS = ["@teamix-evo/stylelint-config", "stylelint"];
|
|
1748
|
+
async function runLintInit(options) {
|
|
1749
|
+
const { projectRoot, skipInstall } = options;
|
|
1750
|
+
const eslintConfigPath = path12.join(projectRoot, "eslint.config.js");
|
|
1751
|
+
const stylelintConfigPath = path12.join(projectRoot, "stylelint.config.cjs");
|
|
1752
|
+
const eslintExists = await fileExists(eslintConfigPath);
|
|
1753
|
+
const stylelintExists = await fileExists(stylelintConfigPath);
|
|
1754
|
+
if (eslintExists && stylelintExists) {
|
|
1755
|
+
return { status: "already-initialized" };
|
|
1756
|
+
}
|
|
1757
|
+
if (!skipInstall) {
|
|
1758
|
+
const depsToInstall = [
|
|
1759
|
+
...eslintExists ? [] : ESLINT_DEPS,
|
|
1760
|
+
...stylelintExists ? [] : STYLELINT_DEPS
|
|
1761
|
+
];
|
|
1762
|
+
if (depsToInstall.length > 0) {
|
|
1763
|
+
const pm = detectPm(projectRoot);
|
|
1764
|
+
const args = pm === "yarn" ? ["add", "--dev", ...depsToInstall] : pm === "pnpm" ? ["add", "-D", ...depsToInstall] : ["install", "--save-dev", ...depsToInstall];
|
|
1765
|
+
logger.info(`Installing lint deps via ${pm}...`);
|
|
1766
|
+
await execa(pm, args, { cwd: projectRoot, stdio: "inherit" });
|
|
1767
|
+
}
|
|
1768
|
+
}
|
|
1769
|
+
let wroteEslint = false;
|
|
1770
|
+
let wroteStylelint = false;
|
|
1771
|
+
if (!eslintExists) {
|
|
1772
|
+
await writeFileSafe(eslintConfigPath, ESLINT_CONFIG_CONTENT);
|
|
1773
|
+
logger.debug(`Wrote eslint.config.js \u2192 ${eslintConfigPath}`);
|
|
1774
|
+
wroteEslint = true;
|
|
1775
|
+
}
|
|
1776
|
+
if (!stylelintExists) {
|
|
1777
|
+
await writeFileSafe(stylelintConfigPath, STYLELINT_CONFIG_CONTENT);
|
|
1778
|
+
logger.debug(`Wrote stylelint.config.cjs \u2192 ${stylelintConfigPath}`);
|
|
1779
|
+
wroteStylelint = true;
|
|
1780
|
+
}
|
|
1781
|
+
await patchPackageJsonScripts(projectRoot);
|
|
1782
|
+
return {
|
|
1783
|
+
status: "installed",
|
|
1784
|
+
eslint: wroteEslint,
|
|
1785
|
+
stylelint: wroteStylelint
|
|
1786
|
+
};
|
|
1787
|
+
}
|
|
1788
|
+
function detectPm(projectRoot) {
|
|
1789
|
+
if (fs9.existsSync(path12.join(projectRoot, "pnpm-lock.yaml"))) return "pnpm";
|
|
1790
|
+
if (fs9.existsSync(path12.join(projectRoot, "yarn.lock"))) return "yarn";
|
|
1791
|
+
return "npm";
|
|
1792
|
+
}
|
|
1793
|
+
async function patchPackageJsonScripts(projectRoot) {
|
|
1794
|
+
const pkgPath = path12.join(projectRoot, "package.json");
|
|
1795
|
+
const raw = await readFileOrNull(pkgPath);
|
|
1796
|
+
if (!raw) return;
|
|
1797
|
+
let pkg;
|
|
1798
|
+
try {
|
|
1799
|
+
pkg = JSON.parse(raw);
|
|
1800
|
+
} catch {
|
|
1801
|
+
return;
|
|
1802
|
+
}
|
|
1803
|
+
const scripts = pkg.scripts ?? {};
|
|
1804
|
+
let changed = false;
|
|
1805
|
+
if (!scripts.lint) {
|
|
1806
|
+
scripts.lint = "eslint src/";
|
|
1807
|
+
changed = true;
|
|
1808
|
+
}
|
|
1809
|
+
if (!scripts["lint:css"]) {
|
|
1810
|
+
scripts["lint:css"] = "stylelint 'src/**/*.css'";
|
|
1811
|
+
changed = true;
|
|
1812
|
+
}
|
|
1813
|
+
if (changed) {
|
|
1814
|
+
pkg.scripts = scripts;
|
|
1815
|
+
await writeFileSafe(pkgPath, JSON.stringify(pkg, null, 2) + "\n");
|
|
1816
|
+
logger.debug("Patched package.json scripts with lint / lint:css");
|
|
1817
|
+
}
|
|
1818
|
+
}
|
|
1819
|
+
|
|
1820
|
+
// src/core/installer.ts
|
|
1821
|
+
import * as path13 from "path";
|
|
1822
|
+
import * as fs10 from "fs/promises";
|
|
1477
1823
|
async function installResources(options) {
|
|
1478
1824
|
const { projectRoot, manifest, data, variantDir, packageRoot } = options;
|
|
1479
1825
|
const installedResources = [];
|
|
@@ -1510,13 +1856,13 @@ async function installSingleResource(resource, projectRoot, data, variantDir, pa
|
|
|
1510
1856
|
variantDir,
|
|
1511
1857
|
packageRoot
|
|
1512
1858
|
);
|
|
1513
|
-
const targetPath =
|
|
1859
|
+
const targetPath = path13.join(projectRoot, resource.target);
|
|
1514
1860
|
let content;
|
|
1515
1861
|
if (resource.template) {
|
|
1516
1862
|
const templateContent = await loadTemplateFile(sourcePath);
|
|
1517
1863
|
content = renderTemplate(templateContent, data);
|
|
1518
1864
|
} else {
|
|
1519
|
-
content = await
|
|
1865
|
+
content = await fs10.readFile(sourcePath, "utf-8");
|
|
1520
1866
|
}
|
|
1521
1867
|
await writeFileSafe(targetPath, content);
|
|
1522
1868
|
const hash = computeHash(content);
|
|
@@ -1534,13 +1880,13 @@ async function installRecursiveResource(resource, projectRoot, data, variantDir,
|
|
|
1534
1880
|
variantDir,
|
|
1535
1881
|
packageRoot
|
|
1536
1882
|
);
|
|
1537
|
-
const targetDir =
|
|
1883
|
+
const targetDir = path13.join(projectRoot, resource.target);
|
|
1538
1884
|
const results = [];
|
|
1539
1885
|
await ensureDir(targetDir);
|
|
1540
1886
|
const entries = await walkDir(sourcePath);
|
|
1541
1887
|
for (const entry of entries) {
|
|
1542
|
-
const relPath =
|
|
1543
|
-
let targetFile =
|
|
1888
|
+
const relPath = path13.relative(sourcePath, entry);
|
|
1889
|
+
let targetFile = path13.join(targetDir, relPath);
|
|
1544
1890
|
if (resource.template && targetFile.endsWith(".hbs")) {
|
|
1545
1891
|
targetFile = targetFile.slice(0, -4);
|
|
1546
1892
|
}
|
|
@@ -1549,11 +1895,11 @@ async function installRecursiveResource(resource, projectRoot, data, variantDir,
|
|
|
1549
1895
|
const templateContent = await loadTemplateFile(entry);
|
|
1550
1896
|
content = renderTemplate(templateContent, data);
|
|
1551
1897
|
} else {
|
|
1552
|
-
content = await
|
|
1898
|
+
content = await fs10.readFile(entry, "utf-8");
|
|
1553
1899
|
}
|
|
1554
1900
|
await writeFileSafe(targetFile, content);
|
|
1555
1901
|
const hash = computeHash(content);
|
|
1556
|
-
const targetRel =
|
|
1902
|
+
const targetRel = path13.relative(projectRoot, targetFile);
|
|
1557
1903
|
results.push({
|
|
1558
1904
|
id: `${resource.id}:${relPath}`,
|
|
1559
1905
|
target: targetRel,
|
|
@@ -1566,25 +1912,25 @@ async function installRecursiveResource(resource, projectRoot, data, variantDir,
|
|
|
1566
1912
|
}
|
|
1567
1913
|
|
|
1568
1914
|
// src/core/registry-client.ts
|
|
1569
|
-
import * as
|
|
1570
|
-
import * as
|
|
1915
|
+
import * as path14 from "path";
|
|
1916
|
+
import * as fs11 from "fs/promises";
|
|
1571
1917
|
import { createRequire as createRequire4 } from "module";
|
|
1572
1918
|
import { loadVariantManifest } from "@teamix-evo/registry";
|
|
1573
1919
|
var require5 = createRequire4(import.meta.url);
|
|
1574
1920
|
function resolvePackageRoot3(packageName) {
|
|
1575
1921
|
const pkgJsonPath = require5.resolve(`${packageName}/package.json`);
|
|
1576
|
-
return
|
|
1922
|
+
return path14.dirname(pkgJsonPath);
|
|
1577
1923
|
}
|
|
1578
1924
|
async function loadVariantData(packageName, variant) {
|
|
1579
1925
|
const packageRoot = resolvePackageRoot3(packageName);
|
|
1580
|
-
const variantDir =
|
|
1926
|
+
const variantDir = path14.join(packageRoot, "library", variant);
|
|
1581
1927
|
logger.debug(`Resolved variant dir: ${variantDir}`);
|
|
1582
1928
|
logger.debug(`Package root: ${packageRoot}`);
|
|
1583
1929
|
const manifest = await loadVariantManifest(variantDir);
|
|
1584
1930
|
let data = {};
|
|
1585
|
-
const dataPath =
|
|
1931
|
+
const dataPath = path14.join(variantDir, "_data.json");
|
|
1586
1932
|
try {
|
|
1587
|
-
const raw = await
|
|
1933
|
+
const raw = await fs11.readFile(dataPath, "utf-8");
|
|
1588
1934
|
data = JSON.parse(raw);
|
|
1589
1935
|
} catch (err) {
|
|
1590
1936
|
if (err.code !== "ENOENT") {
|
|
@@ -1610,7 +1956,10 @@ export {
|
|
|
1610
1956
|
readProjectConfig,
|
|
1611
1957
|
removeSkillFiles,
|
|
1612
1958
|
removeUiFiles,
|
|
1959
|
+
runLintInit,
|
|
1613
1960
|
runSkillsAdd,
|
|
1961
|
+
runSkillsInit,
|
|
1962
|
+
runSkillsUpdate,
|
|
1614
1963
|
runTokensInit,
|
|
1615
1964
|
runUiAdd,
|
|
1616
1965
|
runUiInit,
|