teamix-evo 0.13.4 → 0.14.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 +1 -4
- package/dist/core/index.d.ts +49 -68
- package/dist/core/index.js +837 -713
- package/dist/core/index.js.map +1 -1
- package/dist/index.js +1821 -2110
- package/dist/index.js.map +1 -1
- package/package.json +8 -7
package/dist/core/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// src/core/tokens-init.ts
|
|
2
|
-
import * as
|
|
3
|
-
import * as
|
|
2
|
+
import * as path8 from "path";
|
|
3
|
+
import * as fs7 from "fs/promises";
|
|
4
4
|
import {
|
|
5
5
|
loadTokensPackageManifest,
|
|
6
6
|
getVariantEntry
|
|
@@ -96,6 +96,7 @@ import {
|
|
|
96
96
|
validateSkillsLock,
|
|
97
97
|
TokensPackLockSchema
|
|
98
98
|
} from "@teamix-evo/registry";
|
|
99
|
+
import * as fs2 from "fs/promises";
|
|
99
100
|
|
|
100
101
|
// src/utils/error.ts
|
|
101
102
|
function getErrorMessage(err) {
|
|
@@ -113,9 +114,11 @@ var TEAMIX_DIR = ".teamix-evo";
|
|
|
113
114
|
var CONFIG_FILE = "config.json";
|
|
114
115
|
var MANIFEST_FILE = "manifest.json";
|
|
115
116
|
var TOKENS_LOCK_FILE = "tokens-lock.json";
|
|
116
|
-
var
|
|
117
|
-
var
|
|
118
|
-
|
|
117
|
+
var SKILLS_LOCK_FILE = "skills-lock.json";
|
|
118
|
+
var LEGACY_SKILLS_LOCK_PATHS = [
|
|
119
|
+
path2.join("skills-source", "manifest.lock.json"),
|
|
120
|
+
path2.join("skills", "manifest.lock.json")
|
|
121
|
+
];
|
|
119
122
|
function getTeamixDir(projectRoot) {
|
|
120
123
|
return path2.join(projectRoot, TEAMIX_DIR);
|
|
121
124
|
}
|
|
@@ -194,51 +197,61 @@ async function readTokensVariant(projectRoot) {
|
|
|
194
197
|
const lock = await readTokensLock(projectRoot);
|
|
195
198
|
return lock?.variant.name ?? null;
|
|
196
199
|
}
|
|
197
|
-
function getSkillsSourceDir(projectRoot, skillName) {
|
|
198
|
-
const base = path2.join(projectRoot, TEAMIX_DIR, SKILLS_DIR);
|
|
199
|
-
return skillName ? path2.join(base, skillName) : base;
|
|
200
|
-
}
|
|
201
|
-
function getLegacySkillsSourceDir(projectRoot) {
|
|
202
|
-
return path2.join(projectRoot, TEAMIX_DIR, LEGACY_SKILLS_DIR);
|
|
203
|
-
}
|
|
204
200
|
async function readSkillsLock(projectRoot) {
|
|
205
|
-
const lockPath = path2.join(
|
|
206
|
-
projectRoot,
|
|
207
|
-
TEAMIX_DIR,
|
|
208
|
-
SKILLS_DIR,
|
|
209
|
-
SKILLS_LOCK_FILE
|
|
210
|
-
);
|
|
201
|
+
const lockPath = path2.join(projectRoot, TEAMIX_DIR, SKILLS_LOCK_FILE);
|
|
211
202
|
const raw = await readFileOrNull(lockPath);
|
|
212
203
|
if (raw === null) return null;
|
|
213
204
|
try {
|
|
214
205
|
const data = JSON.parse(raw);
|
|
215
206
|
const result = validateSkillsLock(data);
|
|
216
207
|
if (!result.success) {
|
|
217
|
-
logger.warn(`Invalid skills
|
|
208
|
+
logger.warn(`Invalid skills-lock.json: ${result.error}`);
|
|
218
209
|
return null;
|
|
219
210
|
}
|
|
220
211
|
return result.data;
|
|
221
212
|
} catch (err) {
|
|
222
213
|
logger.warn(
|
|
223
|
-
`Failed to parse skills
|
|
214
|
+
`Failed to parse skills-lock.json: ${getErrorMessage(err)}`
|
|
224
215
|
);
|
|
225
216
|
return null;
|
|
226
217
|
}
|
|
227
218
|
}
|
|
228
219
|
async function writeSkillsLock(projectRoot, lock) {
|
|
229
|
-
const lockPath = path2.join(
|
|
230
|
-
projectRoot,
|
|
231
|
-
TEAMIX_DIR,
|
|
232
|
-
SKILLS_DIR,
|
|
233
|
-
SKILLS_LOCK_FILE
|
|
234
|
-
);
|
|
220
|
+
const lockPath = path2.join(projectRoot, TEAMIX_DIR, SKILLS_LOCK_FILE);
|
|
235
221
|
await writeFileSafe(lockPath, JSON.stringify(lock, null, 2) + "\n");
|
|
236
222
|
logger.debug(`Wrote skills lock \u2192 ${lockPath}`);
|
|
237
223
|
}
|
|
224
|
+
async function migrateSkillsLockLocation(projectRoot) {
|
|
225
|
+
const newPath = path2.join(projectRoot, TEAMIX_DIR, SKILLS_LOCK_FILE);
|
|
226
|
+
try {
|
|
227
|
+
await fs2.access(newPath);
|
|
228
|
+
} catch {
|
|
229
|
+
for (const legacyRel of LEGACY_SKILLS_LOCK_PATHS) {
|
|
230
|
+
const legacyPath = path2.join(projectRoot, TEAMIX_DIR, legacyRel);
|
|
231
|
+
try {
|
|
232
|
+
await fs2.access(legacyPath);
|
|
233
|
+
await ensureDir(path2.dirname(newPath));
|
|
234
|
+
await fs2.rename(legacyPath, newPath);
|
|
235
|
+
logger.debug(`Migrated skills lock: ${legacyRel} \u2192 ${SKILLS_LOCK_FILE}`);
|
|
236
|
+
break;
|
|
237
|
+
} catch {
|
|
238
|
+
continue;
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
for (const staleDir of ["skills-source", "skills"]) {
|
|
243
|
+
const abs = path2.join(projectRoot, TEAMIX_DIR, staleDir);
|
|
244
|
+
try {
|
|
245
|
+
await fs2.rm(abs, { recursive: true, force: true });
|
|
246
|
+
logger.debug(`Cleaned up stale dir: .teamix-evo/${staleDir}/`);
|
|
247
|
+
} catch {
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
}
|
|
238
251
|
|
|
239
252
|
// src/core/skills-client.ts
|
|
240
253
|
import * as path3 from "path";
|
|
241
|
-
import * as
|
|
254
|
+
import * as fs3 from "fs/promises";
|
|
242
255
|
import { createRequire } from "module";
|
|
243
256
|
import { loadSkillsPackageManifest } from "@teamix-evo/registry";
|
|
244
257
|
var require2 = createRequire(import.meta.url);
|
|
@@ -253,7 +266,7 @@ async function loadSkillsData(packageName) {
|
|
|
253
266
|
let data = {};
|
|
254
267
|
const dataPath = path3.join(packageRoot, "_data.json");
|
|
255
268
|
try {
|
|
256
|
-
const raw = await
|
|
269
|
+
const raw = await fs3.readFile(dataPath, "utf-8");
|
|
257
270
|
data = JSON.parse(raw);
|
|
258
271
|
} catch (err) {
|
|
259
272
|
if (err.code !== "ENOENT") {
|
|
@@ -266,7 +279,7 @@ async function loadSkillsData(packageName) {
|
|
|
266
279
|
|
|
267
280
|
// src/core/skills-installer.ts
|
|
268
281
|
import * as path7 from "path";
|
|
269
|
-
import * as
|
|
282
|
+
import * as fs6 from "fs/promises";
|
|
270
283
|
import {
|
|
271
284
|
replaceManagedRegion,
|
|
272
285
|
hasManagedRegion,
|
|
@@ -326,7 +339,7 @@ function getAdapter(kind) {
|
|
|
326
339
|
|
|
327
340
|
// src/utils/template.ts
|
|
328
341
|
import Handlebars from "handlebars";
|
|
329
|
-
import * as
|
|
342
|
+
import * as fs4 from "fs/promises";
|
|
330
343
|
Handlebars.registerHelper("lowercase", (str) => {
|
|
331
344
|
return typeof str === "string" ? str.toLowerCase() : String(str ?? "").toLowerCase();
|
|
332
345
|
});
|
|
@@ -349,12 +362,12 @@ function renderTemplate(templateContent, data) {
|
|
|
349
362
|
return compiled(data);
|
|
350
363
|
}
|
|
351
364
|
async function loadTemplateFile(filePath) {
|
|
352
|
-
return
|
|
365
|
+
return fs4.readFile(filePath, "utf-8");
|
|
353
366
|
}
|
|
354
367
|
|
|
355
368
|
// src/utils/path.ts
|
|
356
369
|
import * as path6 from "path";
|
|
357
|
-
import * as
|
|
370
|
+
import * as fs5 from "fs/promises";
|
|
358
371
|
import { createRequire as createRequire2 } from "module";
|
|
359
372
|
var require3 = createRequire2(import.meta.url);
|
|
360
373
|
function resolveSourcePath(source, variantDir, packageRoot) {
|
|
@@ -365,7 +378,7 @@ function resolveSourcePath(source, variantDir, packageRoot) {
|
|
|
365
378
|
}
|
|
366
379
|
async function walkDir(dir, skipDirs) {
|
|
367
380
|
const files = [];
|
|
368
|
-
const entries = await
|
|
381
|
+
const entries = await fs5.readdir(dir, { withFileTypes: true });
|
|
369
382
|
for (const entry of entries) {
|
|
370
383
|
const fullPath = path6.join(dir, entry.name);
|
|
371
384
|
if (entry.isDirectory()) {
|
|
@@ -381,10 +394,13 @@ function resolveTokensPackageRoot(packageName) {
|
|
|
381
394
|
const pkgJson = require3.resolve(`${packageName}/package.json`);
|
|
382
395
|
return path6.dirname(pkgJson);
|
|
383
396
|
}
|
|
397
|
+
function resolveResourceTarget(projectRoot, target) {
|
|
398
|
+
return path6.isAbsolute(target) ? target : path6.resolve(projectRoot, target);
|
|
399
|
+
}
|
|
384
400
|
|
|
385
401
|
// src/core/skills-installer.ts
|
|
386
402
|
async function installSkills(options) {
|
|
387
|
-
await
|
|
403
|
+
await migrateSkillsLockLocation(options.projectRoot);
|
|
388
404
|
const { manifest, ides, scope, onlyIds } = options;
|
|
389
405
|
const installed = [];
|
|
390
406
|
const targets = manifest.skills.filter(
|
|
@@ -400,75 +416,61 @@ async function installSkills(options) {
|
|
|
400
416
|
);
|
|
401
417
|
continue;
|
|
402
418
|
}
|
|
403
|
-
const
|
|
404
|
-
installed.push(...sourceRecords);
|
|
419
|
+
const rendered = await renderSkillFiles(skill, options);
|
|
405
420
|
for (const ide of skillIdes) {
|
|
406
|
-
const
|
|
421
|
+
const records = await writeRenderedToIde(
|
|
407
422
|
skill,
|
|
423
|
+
rendered,
|
|
408
424
|
ide,
|
|
409
425
|
scope,
|
|
410
426
|
options.projectRoot
|
|
411
427
|
);
|
|
412
|
-
installed.push(...
|
|
428
|
+
installed.push(...records);
|
|
413
429
|
}
|
|
414
430
|
}
|
|
415
431
|
return { resources: installed, count: installed.length };
|
|
416
432
|
}
|
|
417
|
-
async function
|
|
418
|
-
const { data, packageRoot
|
|
433
|
+
async function renderSkillFiles(skill, options) {
|
|
434
|
+
const { data, packageRoot } = options;
|
|
419
435
|
const sourceAbs = path7.resolve(packageRoot, skill.source);
|
|
420
|
-
const
|
|
421
|
-
const
|
|
422
|
-
const records = [];
|
|
436
|
+
const stat3 = await fs6.stat(sourceAbs);
|
|
437
|
+
const rendered = /* @__PURE__ */ new Map();
|
|
423
438
|
if (stat3.isFile()) {
|
|
424
|
-
const targetFile = path7.join(targetDir, "SKILL.md");
|
|
425
439
|
const content = await renderSkillContent(sourceAbs, skill, data);
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
logger.debug(` Wrote source: ${targetFile}`);
|
|
429
|
-
return records;
|
|
440
|
+
rendered.set("SKILL.md", content);
|
|
441
|
+
return rendered;
|
|
430
442
|
}
|
|
431
|
-
await ensureDir(targetDir);
|
|
432
443
|
const entries = await walkDir(sourceAbs);
|
|
433
444
|
for (const entry of entries) {
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
targetFile = targetFile.slice(0, -4);
|
|
445
|
+
let rel2 = path7.relative(sourceAbs, entry);
|
|
446
|
+
if (skill.template && rel2.endsWith(".hbs")) {
|
|
447
|
+
rel2 = rel2.slice(0, -4);
|
|
438
448
|
}
|
|
439
|
-
const content = skill.template && entry.endsWith(".hbs") ? renderTemplate(await loadTemplateFile(entry), { ...data, skill }) : await
|
|
440
|
-
|
|
441
|
-
const relWritten = path7.relative(targetDir, targetFile);
|
|
442
|
-
records.push(makeSourceRecord(skill, targetFile, content, relWritten));
|
|
443
|
-
logger.debug(` Wrote source: ${targetFile}`);
|
|
449
|
+
const content = skill.template && entry.endsWith(".hbs") ? renderTemplate(await loadTemplateFile(entry), { ...data, skill }) : await fs6.readFile(entry, "utf-8");
|
|
450
|
+
rendered.set(rel2, content);
|
|
444
451
|
}
|
|
445
|
-
return
|
|
452
|
+
return rendered;
|
|
446
453
|
}
|
|
447
|
-
async function
|
|
448
|
-
const sourceDir = getSkillsSourceDir(projectRoot, skill.name);
|
|
454
|
+
async function writeRenderedToIde(skill, rendered, ide, scope, projectRoot) {
|
|
449
455
|
const adapter = getAdapter(ide);
|
|
450
456
|
const targetDir = adapter.getSkillTargetDir(skill.name, scope, projectRoot);
|
|
451
457
|
const records = [];
|
|
452
|
-
const sourceFiles = await walkDir(sourceDir);
|
|
453
458
|
await ensureDir(targetDir);
|
|
454
|
-
for (const
|
|
455
|
-
const rel2 = path7.relative(sourceDir, src);
|
|
459
|
+
for (const [rel2, sourceContent] of rendered) {
|
|
456
460
|
const targetFile = path7.join(targetDir, rel2);
|
|
457
|
-
const sourceContent = await fs5.readFile(src, "utf-8");
|
|
458
461
|
const writtenContent = await writeMirrorContent(
|
|
459
462
|
targetFile,
|
|
460
463
|
sourceContent,
|
|
461
|
-
skill.managedRegions
|
|
462
|
-
src
|
|
464
|
+
skill.managedRegions
|
|
463
465
|
);
|
|
464
466
|
records.push(
|
|
465
|
-
makeMirrorRecord(skill, targetFile, writtenContent, ide, scope, rel2)
|
|
467
|
+
makeMirrorRecord(skill, projectRoot, targetFile, writtenContent, ide, scope, rel2)
|
|
466
468
|
);
|
|
467
|
-
logger.debug(`
|
|
469
|
+
logger.debug(` Wrote ${ide}:${scope}: ${targetFile}`);
|
|
468
470
|
}
|
|
469
471
|
return records;
|
|
470
472
|
}
|
|
471
|
-
async function writeMirrorContent(targetFile, sourceContent, managedRegions
|
|
473
|
+
async function writeMirrorContent(targetFile, sourceContent, managedRegions) {
|
|
472
474
|
const existing = await readFileOrNull(targetFile);
|
|
473
475
|
if (existing === null) {
|
|
474
476
|
await writeFileSafe(targetFile, sourceContent);
|
|
@@ -479,7 +481,7 @@ async function writeMirrorContent(targetFile, sourceContent, managedRegions, sou
|
|
|
479
481
|
if (matchedRegions.length === 0) {
|
|
480
482
|
if (existing !== sourceContent) {
|
|
481
483
|
logger.warn(
|
|
482
|
-
`
|
|
484
|
+
`Drift detected at ${targetFile} \u2014 overwriting from upstream. Re-run \`npx teamix-evo@latest skills sync\` after any manual edits.`
|
|
483
485
|
);
|
|
484
486
|
await writeFileSafe(targetFile, sourceContent);
|
|
485
487
|
return sourceContent;
|
|
@@ -521,22 +523,13 @@ async function renderSkillContent(sourceAbs, skill, data) {
|
|
|
521
523
|
const tpl = await loadTemplateFile(sourceAbs);
|
|
522
524
|
return renderTemplate(tpl, { ...data, skill });
|
|
523
525
|
}
|
|
524
|
-
return
|
|
525
|
-
}
|
|
526
|
-
function makeSourceRecord(skill, targetAbs, content, rel2) {
|
|
527
|
-
const id = rel2 ? `${skill.id}:source:${rel2}` : `${skill.id}:source`;
|
|
528
|
-
return {
|
|
529
|
-
id,
|
|
530
|
-
target: targetAbs,
|
|
531
|
-
hash: computeHash(content),
|
|
532
|
-
strategy: skill.updateStrategy
|
|
533
|
-
};
|
|
526
|
+
return fs6.readFile(sourceAbs, "utf-8");
|
|
534
527
|
}
|
|
535
|
-
function makeMirrorRecord(skill, targetAbs, content, ide, scope, rel2) {
|
|
528
|
+
function makeMirrorRecord(skill, projectRoot, targetAbs, content, ide, scope, rel2) {
|
|
536
529
|
const id = rel2 && rel2 !== "SKILL.md" ? `${skill.id}:${rel2}` : skill.id;
|
|
537
530
|
return {
|
|
538
531
|
id,
|
|
539
|
-
target: targetAbs,
|
|
532
|
+
target: path7.relative(projectRoot, targetAbs),
|
|
540
533
|
hash: computeHash(content),
|
|
541
534
|
strategy: skill.updateStrategy,
|
|
542
535
|
ide,
|
|
@@ -552,64 +545,41 @@ async function updateSkills(options) {
|
|
|
552
545
|
if (idFilter && !idFilter.has(skill.id)) continue;
|
|
553
546
|
const skillIdes = skill.ides.filter((i) => ides.includes(i));
|
|
554
547
|
if (skillIdes.length === 0) continue;
|
|
555
|
-
const
|
|
556
|
-
updated.push(...sourceRecords);
|
|
548
|
+
const rendered = await renderSkillFiles(skill, options);
|
|
557
549
|
for (const ide of skillIdes) {
|
|
558
|
-
const
|
|
550
|
+
const records = await updateRenderedInIde(
|
|
559
551
|
skill,
|
|
552
|
+
rendered,
|
|
560
553
|
ide,
|
|
561
554
|
scope,
|
|
562
|
-
projectRoot
|
|
555
|
+
projectRoot,
|
|
556
|
+
summary
|
|
563
557
|
);
|
|
564
|
-
updated.push(...
|
|
558
|
+
updated.push(...records);
|
|
565
559
|
}
|
|
566
560
|
}
|
|
567
561
|
return { resources: updated, summary };
|
|
568
562
|
}
|
|
569
|
-
async function
|
|
570
|
-
const
|
|
571
|
-
const
|
|
572
|
-
const
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
const
|
|
577
|
-
const
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
targetFile: targetFile2,
|
|
588
|
-
newContent: newContent2,
|
|
589
|
-
exists: exists2,
|
|
590
|
-
updateStrategy: skill.updateStrategy,
|
|
591
|
-
managedRegions: skill.managedRegions,
|
|
592
|
-
projectRoot,
|
|
593
|
-
summary
|
|
594
|
-
});
|
|
595
|
-
const relWritten = path7.relative(targetDir, targetFile2);
|
|
596
|
-
records.push(makeSourceRecord(skill, targetFile2, written2, relWritten));
|
|
597
|
-
}
|
|
598
|
-
return records;
|
|
563
|
+
async function updateRenderedInIde(skill, rendered, ide, scope, projectRoot, summary) {
|
|
564
|
+
const adapter = getAdapter(ide);
|
|
565
|
+
const targetDir = adapter.getSkillTargetDir(skill.name, scope, projectRoot);
|
|
566
|
+
const records = [];
|
|
567
|
+
await ensureDir(targetDir);
|
|
568
|
+
for (const [rel2, newContent] of rendered) {
|
|
569
|
+
const targetFile = path7.join(targetDir, rel2);
|
|
570
|
+
const exists = await fileExists(targetFile);
|
|
571
|
+
const written = await rewriteSingleFile({
|
|
572
|
+
targetFile,
|
|
573
|
+
newContent,
|
|
574
|
+
exists,
|
|
575
|
+
updateStrategy: skill.updateStrategy,
|
|
576
|
+
managedRegions: skill.managedRegions,
|
|
577
|
+
projectRoot,
|
|
578
|
+
summary
|
|
579
|
+
});
|
|
580
|
+
records.push(makeMirrorRecord(skill, projectRoot, targetFile, written, ide, scope, rel2));
|
|
599
581
|
}
|
|
600
|
-
|
|
601
|
-
const newContent = await renderSkillContent(sourceAbs, skill, data);
|
|
602
|
-
const exists = await fileExists(targetFile);
|
|
603
|
-
const written = await rewriteSingleFile({
|
|
604
|
-
targetFile,
|
|
605
|
-
newContent,
|
|
606
|
-
exists,
|
|
607
|
-
updateStrategy: skill.updateStrategy,
|
|
608
|
-
managedRegions: skill.managedRegions,
|
|
609
|
-
projectRoot,
|
|
610
|
-
summary
|
|
611
|
-
});
|
|
612
|
-
return [makeSourceRecord(skill, targetFile, written)];
|
|
582
|
+
return records;
|
|
613
583
|
}
|
|
614
584
|
async function rewriteSingleFile(args) {
|
|
615
585
|
const {
|
|
@@ -676,41 +646,30 @@ async function rewriteSingleFile(args) {
|
|
|
676
646
|
function escapeRegExp(str) {
|
|
677
647
|
return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
678
648
|
}
|
|
679
|
-
async function
|
|
680
|
-
|
|
681
|
-
const { projectRoot, skills, ides, scope, onlyIds } = options;
|
|
649
|
+
async function reinstallSkillsToIdes(options) {
|
|
650
|
+
const { projectRoot, skills, manifest, data, packageRoot, ides, scope } = options;
|
|
682
651
|
const out = [];
|
|
683
|
-
const
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
logger.warn(
|
|
688
|
-
`Skill "${skill.id}" has no source at ${sourceDir}; skipped.`
|
|
689
|
-
);
|
|
652
|
+
for (const skill of skills) {
|
|
653
|
+
const entry = manifest.skills.find((s) => s.id === skill.id);
|
|
654
|
+
if (!entry) {
|
|
655
|
+
logger.warn(`Skill "${skill.id}" not found in npm package manifest; skipped.`);
|
|
690
656
|
continue;
|
|
691
657
|
}
|
|
658
|
+
const rendered = await renderSkillFiles(entry, { data, packageRoot });
|
|
692
659
|
for (const ide of ides) {
|
|
693
660
|
const adapter = getAdapter(ide);
|
|
694
|
-
const targetDir = adapter.getSkillTargetDir(
|
|
695
|
-
skill.name,
|
|
696
|
-
scope,
|
|
697
|
-
projectRoot
|
|
698
|
-
);
|
|
661
|
+
const targetDir = adapter.getSkillTargetDir(skill.name, scope, projectRoot);
|
|
699
662
|
await ensureDir(targetDir);
|
|
700
|
-
const
|
|
701
|
-
for (const src of sourceFiles) {
|
|
702
|
-
const rel2 = path7.relative(sourceDir, src);
|
|
663
|
+
for (const [rel2, sourceContent] of rendered) {
|
|
703
664
|
const targetFile = path7.join(targetDir, rel2);
|
|
704
|
-
const sourceContent = await fs5.readFile(src, "utf-8");
|
|
705
665
|
const writtenContent = await writeMirrorContent(
|
|
706
666
|
targetFile,
|
|
707
667
|
sourceContent,
|
|
708
|
-
skill.managedRegions
|
|
709
|
-
src
|
|
668
|
+
skill.managedRegions
|
|
710
669
|
);
|
|
711
670
|
out.push({
|
|
712
671
|
id: rel2 === "SKILL.md" ? skill.id : `${skill.id}:${rel2}`,
|
|
713
|
-
target: targetFile,
|
|
672
|
+
target: path7.relative(projectRoot, targetFile),
|
|
714
673
|
hash: computeHash(writtenContent),
|
|
715
674
|
strategy: skill.updateStrategy,
|
|
716
675
|
ide,
|
|
@@ -721,75 +680,6 @@ async function syncSkillsToIdes(options) {
|
|
|
721
680
|
}
|
|
722
681
|
return { resources: out, count: out.length };
|
|
723
682
|
}
|
|
724
|
-
async function migrateLegacySkillsSourceDir(projectRoot) {
|
|
725
|
-
const legacyDir = getLegacySkillsSourceDir(projectRoot);
|
|
726
|
-
const newDir = getSkillsSourceDir(projectRoot);
|
|
727
|
-
let legacyExists = false;
|
|
728
|
-
let newExists = false;
|
|
729
|
-
try {
|
|
730
|
-
legacyExists = (await fs5.stat(legacyDir)).isDirectory();
|
|
731
|
-
} catch {
|
|
732
|
-
legacyExists = false;
|
|
733
|
-
}
|
|
734
|
-
try {
|
|
735
|
-
newExists = (await fs5.stat(newDir)).isDirectory();
|
|
736
|
-
} catch {
|
|
737
|
-
newExists = false;
|
|
738
|
-
}
|
|
739
|
-
if (!legacyExists) return;
|
|
740
|
-
if (newExists) {
|
|
741
|
-
logger.warn(
|
|
742
|
-
`Detected stale legacy skills source dir at ${legacyDir} alongside ${newDir}; the new layout takes precedence \u2014 you can safely delete the legacy dir.`
|
|
743
|
-
);
|
|
744
|
-
return;
|
|
745
|
-
}
|
|
746
|
-
try {
|
|
747
|
-
await fs5.rename(legacyDir, newDir);
|
|
748
|
-
logger.info(
|
|
749
|
-
`Migrated skills source dir: \`.teamix-evo/${LEGACY_SKILLS_DIR}/\` \u2192 \`.teamix-evo/skills-source/\``
|
|
750
|
-
);
|
|
751
|
-
} catch (err) {
|
|
752
|
-
logger.warn(
|
|
753
|
-
`Failed to rename legacy skills source dir (${getErrorMessage(
|
|
754
|
-
err
|
|
755
|
-
)}); leaving as-is. New skills will install under the new layout.`
|
|
756
|
-
);
|
|
757
|
-
return;
|
|
758
|
-
}
|
|
759
|
-
try {
|
|
760
|
-
const manifest = await readInstalledManifest(projectRoot);
|
|
761
|
-
if (!manifest) return;
|
|
762
|
-
const legacyFragmentPosix = `/.teamix-evo/${LEGACY_SKILLS_DIR}/`;
|
|
763
|
-
const newFragmentPosix = `/.teamix-evo/skills-source/`;
|
|
764
|
-
const legacyFragmentNative = `${path7.sep}.teamix-evo${path7.sep}${LEGACY_SKILLS_DIR}${path7.sep}`;
|
|
765
|
-
const newFragmentNative = `${path7.sep}.teamix-evo${path7.sep}skills-source${path7.sep}`;
|
|
766
|
-
let touched = 0;
|
|
767
|
-
for (const pkg of manifest.installed) {
|
|
768
|
-
for (const r of pkg.resources) {
|
|
769
|
-
if (typeof r.target !== "string") continue;
|
|
770
|
-
const before = r.target;
|
|
771
|
-
let after = before.replace(legacyFragmentPosix, newFragmentPosix);
|
|
772
|
-
after = after.replace(legacyFragmentNative, newFragmentNative);
|
|
773
|
-
if (after !== before) {
|
|
774
|
-
r.target = after;
|
|
775
|
-
touched += 1;
|
|
776
|
-
}
|
|
777
|
-
}
|
|
778
|
-
}
|
|
779
|
-
if (touched > 0) {
|
|
780
|
-
await writeInstalledManifest(projectRoot, manifest);
|
|
781
|
-
logger.debug(
|
|
782
|
-
`Rewrote ${touched} manifest target(s) to the new skills-source path.`
|
|
783
|
-
);
|
|
784
|
-
}
|
|
785
|
-
} catch (err) {
|
|
786
|
-
logger.warn(
|
|
787
|
-
`Migrated skills source dir but failed to update manifest paths (${getErrorMessage(
|
|
788
|
-
err
|
|
789
|
-
)}); manifest may still reference legacy paths.`
|
|
790
|
-
);
|
|
791
|
-
}
|
|
792
|
-
}
|
|
793
683
|
async function pruneEmptyIdeSkillDirs(args) {
|
|
794
684
|
const removed = [];
|
|
795
685
|
for (const ide of args.ides) {
|
|
@@ -802,7 +692,7 @@ async function pruneEmptyIdeSkillDirs(args) {
|
|
|
802
692
|
const skillsRoot = path7.dirname(placeholderDir);
|
|
803
693
|
let entries;
|
|
804
694
|
try {
|
|
805
|
-
entries = await
|
|
695
|
+
entries = await fs6.readdir(skillsRoot);
|
|
806
696
|
} catch {
|
|
807
697
|
continue;
|
|
808
698
|
}
|
|
@@ -810,21 +700,21 @@ async function pruneEmptyIdeSkillDirs(args) {
|
|
|
810
700
|
const dir = path7.join(skillsRoot, name);
|
|
811
701
|
let stat3;
|
|
812
702
|
try {
|
|
813
|
-
stat3 = await
|
|
703
|
+
stat3 = await fs6.stat(dir);
|
|
814
704
|
} catch {
|
|
815
705
|
continue;
|
|
816
706
|
}
|
|
817
707
|
if (!stat3.isDirectory()) continue;
|
|
818
708
|
let children;
|
|
819
709
|
try {
|
|
820
|
-
children = await
|
|
710
|
+
children = await fs6.readdir(dir);
|
|
821
711
|
} catch {
|
|
822
712
|
continue;
|
|
823
713
|
}
|
|
824
714
|
if (children.some((c) => c === "SKILL.md")) continue;
|
|
825
715
|
if (children.length !== 0) continue;
|
|
826
716
|
try {
|
|
827
|
-
await
|
|
717
|
+
await fs6.rmdir(dir);
|
|
828
718
|
removed.push(dir);
|
|
829
719
|
logger.debug(`Pruned empty IDE skill dir: ${dir}`);
|
|
830
720
|
} catch {
|
|
@@ -833,26 +723,29 @@ async function pruneEmptyIdeSkillDirs(args) {
|
|
|
833
723
|
}
|
|
834
724
|
return removed;
|
|
835
725
|
}
|
|
836
|
-
async function removeSkillFiles(records) {
|
|
726
|
+
async function removeSkillFiles(records, projectRoot) {
|
|
837
727
|
const removed = [];
|
|
838
728
|
for (const r of records) {
|
|
729
|
+
const abs = resolveResourceTarget(projectRoot, r.target);
|
|
839
730
|
try {
|
|
840
|
-
await
|
|
841
|
-
removed.push(
|
|
731
|
+
await fs6.unlink(abs);
|
|
732
|
+
removed.push(abs);
|
|
842
733
|
} catch (err) {
|
|
843
734
|
if (err.code !== "ENOENT") {
|
|
844
|
-
logger.warn(`Failed to remove ${
|
|
735
|
+
logger.warn(`Failed to remove ${abs}: ${getErrorMessage(err)}`);
|
|
845
736
|
}
|
|
846
737
|
}
|
|
847
738
|
}
|
|
848
|
-
const startDirs = new Set(
|
|
739
|
+
const startDirs = new Set(
|
|
740
|
+
records.map((r) => path7.dirname(resolveResourceTarget(projectRoot, r.target)))
|
|
741
|
+
);
|
|
849
742
|
for (const startDir of startDirs) {
|
|
850
743
|
let dir = startDir;
|
|
851
744
|
for (let depth = 0; depth < 8; depth++) {
|
|
852
745
|
try {
|
|
853
|
-
const entries = await
|
|
746
|
+
const entries = await fs6.readdir(dir);
|
|
854
747
|
if (entries.length !== 0) break;
|
|
855
|
-
await
|
|
748
|
+
await fs6.rmdir(dir);
|
|
856
749
|
} catch {
|
|
857
750
|
break;
|
|
858
751
|
}
|
|
@@ -862,32 +755,6 @@ async function removeSkillFiles(records) {
|
|
|
862
755
|
return removed;
|
|
863
756
|
}
|
|
864
757
|
|
|
865
|
-
// src/utils/mcp.ts
|
|
866
|
-
import * as path8 from "path";
|
|
867
|
-
var MCP_JSON_CONTENT = {
|
|
868
|
-
mcpServers: {
|
|
869
|
-
"teamix-evo": {
|
|
870
|
-
command: "node",
|
|
871
|
-
args: ["node_modules/@teamix-evo/mcp/dist/cli.js"]
|
|
872
|
-
}
|
|
873
|
-
}
|
|
874
|
-
};
|
|
875
|
-
async function ensureMcpJson(projectRoot) {
|
|
876
|
-
const mcpPath = path8.join(projectRoot, ".mcp.json");
|
|
877
|
-
if (await fileExists(mcpPath)) return "exists";
|
|
878
|
-
try {
|
|
879
|
-
await writeFileSafe(
|
|
880
|
-
mcpPath,
|
|
881
|
-
JSON.stringify(MCP_JSON_CONTENT, null, 2) + "\n"
|
|
882
|
-
);
|
|
883
|
-
logger.debug(`Wrote .mcp.json \u2192 ${mcpPath}`);
|
|
884
|
-
return "created";
|
|
885
|
-
} catch (err) {
|
|
886
|
-
logger.warn(`Failed to write .mcp.json: ${getErrorMessage(err)}`);
|
|
887
|
-
return "failed";
|
|
888
|
-
}
|
|
889
|
-
}
|
|
890
|
-
|
|
891
758
|
// src/core/skills-add.ts
|
|
892
759
|
var DEFAULT_SKILLS_PACKAGE = "@teamix-evo/skills";
|
|
893
760
|
var FLAT_VARIANT = "_flat";
|
|
@@ -1201,7 +1068,6 @@ async function finalizeSkillsInstall(args) {
|
|
|
1201
1068
|
};
|
|
1202
1069
|
}
|
|
1203
1070
|
await writeSkillsLock(projectRoot, lock);
|
|
1204
|
-
await ensureMcpJson(projectRoot);
|
|
1205
1071
|
try {
|
|
1206
1072
|
await pruneEmptyIdeSkillDirs({ projectRoot, ides, scope });
|
|
1207
1073
|
} catch {
|
|
@@ -1346,7 +1212,7 @@ Run \`npx teamix-evo@latest tokens list-variants\` to see all options.`
|
|
|
1346
1212
|
const result = await installVariantFile(fileRel, packageRoot, projectRoot);
|
|
1347
1213
|
if (result) installed.push(result);
|
|
1348
1214
|
}
|
|
1349
|
-
const overridesAbs =
|
|
1215
|
+
const overridesAbs = path8.join(
|
|
1350
1216
|
projectRoot,
|
|
1351
1217
|
CONSUMER_TOKENS_DIR,
|
|
1352
1218
|
CONSUMER_OVERRIDES_FILE
|
|
@@ -1356,10 +1222,10 @@ Run \`npx teamix-evo@latest tokens list-variants\` to see all options.`
|
|
|
1356
1222
|
}
|
|
1357
1223
|
const overridesId = `tokens:${CONSUMER_OVERRIDES_FILE}`;
|
|
1358
1224
|
if (!installed.some((r) => r.id === overridesId)) {
|
|
1359
|
-
const overridesContent = await
|
|
1225
|
+
const overridesContent = await fs7.readFile(overridesAbs, "utf-8");
|
|
1360
1226
|
installed.push({
|
|
1361
1227
|
id: overridesId,
|
|
1362
|
-
target:
|
|
1228
|
+
target: path8.posix.join(CONSUMER_TOKENS_DIR, CONSUMER_OVERRIDES_FILE),
|
|
1363
1229
|
hash: computeHash(overridesContent),
|
|
1364
1230
|
strategy: "frozen"
|
|
1365
1231
|
});
|
|
@@ -1377,7 +1243,7 @@ Run \`npx teamix-evo@latest tokens list-variants\` to see all options.`
|
|
|
1377
1243
|
installedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
1378
1244
|
};
|
|
1379
1245
|
await writeFileSafe(
|
|
1380
|
-
|
|
1246
|
+
path8.join(projectRoot, ".teamix-evo", "tokens-lock.json"),
|
|
1381
1247
|
JSON.stringify(lock, null, 2) + "\n"
|
|
1382
1248
|
);
|
|
1383
1249
|
const config = {
|
|
@@ -1409,7 +1275,6 @@ Run \`npx teamix-evo@latest tokens list-variants\` to see all options.`
|
|
|
1409
1275
|
if (tokensIdx >= 0) prior.installed[tokensIdx] = tokensEntry;
|
|
1410
1276
|
else prior.installed.push(tokensEntry);
|
|
1411
1277
|
await writeInstalledManifest(projectRoot, prior);
|
|
1412
|
-
await ensureMcpJson(projectRoot);
|
|
1413
1278
|
const skills = await tryAutoInstallVariantSkills({
|
|
1414
1279
|
projectRoot,
|
|
1415
1280
|
variant,
|
|
@@ -1498,12 +1363,12 @@ async function tryAutoInstallVariantSkills(args) {
|
|
|
1498
1363
|
}
|
|
1499
1364
|
}
|
|
1500
1365
|
async function installVariantFile(fileRelToPackage, packageRoot, projectRoot) {
|
|
1501
|
-
const sourceAbs =
|
|
1502
|
-
const base =
|
|
1366
|
+
const sourceAbs = path8.join(packageRoot, fileRelToPackage);
|
|
1367
|
+
const base = path8.basename(fileRelToPackage);
|
|
1503
1368
|
if (base === "theme.css") {
|
|
1504
|
-
const targetRel =
|
|
1505
|
-
const targetAbs =
|
|
1506
|
-
const content = await
|
|
1369
|
+
const targetRel = path8.posix.join(CONSUMER_TOKENS_DIR, CONSUMER_THEME_FILE);
|
|
1370
|
+
const targetAbs = path8.join(projectRoot, targetRel);
|
|
1371
|
+
const content = await fs7.readFile(sourceAbs, "utf-8");
|
|
1507
1372
|
if (await fileExists(targetAbs)) {
|
|
1508
1373
|
await backupFile(targetAbs, projectRoot);
|
|
1509
1374
|
}
|
|
@@ -1516,13 +1381,13 @@ async function installVariantFile(fileRelToPackage, packageRoot, projectRoot) {
|
|
|
1516
1381
|
};
|
|
1517
1382
|
}
|
|
1518
1383
|
if (base === "overrides.css" || base === "tokens.overrides.css") {
|
|
1519
|
-
const targetRel =
|
|
1384
|
+
const targetRel = path8.posix.join(
|
|
1520
1385
|
CONSUMER_TOKENS_DIR,
|
|
1521
1386
|
CONSUMER_OVERRIDES_FILE
|
|
1522
1387
|
);
|
|
1523
|
-
const targetAbs =
|
|
1388
|
+
const targetAbs = path8.join(projectRoot, targetRel);
|
|
1524
1389
|
if (await fileExists(targetAbs)) {
|
|
1525
|
-
const existing = await
|
|
1390
|
+
const existing = await fs7.readFile(targetAbs, "utf-8");
|
|
1526
1391
|
return {
|
|
1527
1392
|
id: `tokens:${CONSUMER_OVERRIDES_FILE}`,
|
|
1528
1393
|
target: targetRel,
|
|
@@ -1530,7 +1395,7 @@ async function installVariantFile(fileRelToPackage, packageRoot, projectRoot) {
|
|
|
1530
1395
|
strategy: "frozen"
|
|
1531
1396
|
};
|
|
1532
1397
|
}
|
|
1533
|
-
const content = await
|
|
1398
|
+
const content = await fs7.readFile(sourceAbs, "utf-8");
|
|
1534
1399
|
await writeFileSafe(targetAbs, content);
|
|
1535
1400
|
return {
|
|
1536
1401
|
id: `tokens:${CONSUMER_OVERRIDES_FILE}`,
|
|
@@ -1557,81 +1422,315 @@ async function listTokenVariants(packageName = DEFAULT_TOKENS_PACKAGE, packageRo
|
|
|
1557
1422
|
};
|
|
1558
1423
|
}
|
|
1559
1424
|
|
|
1560
|
-
// src/core/
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
1425
|
+
// src/core/agents-md.ts
|
|
1426
|
+
import * as fs8 from "fs/promises";
|
|
1427
|
+
import * as path9 from "path";
|
|
1428
|
+
import { hasManagedRegion as hasManagedRegion2, replaceManagedRegion as replaceManagedRegion2 } from "@teamix-evo/registry";
|
|
1429
|
+
var AGENTS_MD_MANAGED_ID = "teamix-evo-skills";
|
|
1430
|
+
async function runGenerateAgentsMd(options) {
|
|
1431
|
+
const { projectRoot, variant, skillIds } = options;
|
|
1432
|
+
const mode = options.mode ?? "overwrite";
|
|
1566
1433
|
const config = await readProjectConfig(projectRoot);
|
|
1567
|
-
const
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
const
|
|
1574
|
-
|
|
1575
|
-
|
|
1434
|
+
const lock = await readSkillsLock(projectRoot);
|
|
1435
|
+
const ides = config?.packages?.skills?.ides ?? ["qoder", "claude"];
|
|
1436
|
+
const scope = config?.packages?.skills?.scope ?? lock?.skills[skillIds[0] ?? ""]?.scope ?? "project";
|
|
1437
|
+
const ordered = [...skillIds].sort(
|
|
1438
|
+
(a, b) => bucketRank(a) - bucketRank(b) || a.localeCompare(b)
|
|
1439
|
+
);
|
|
1440
|
+
const sections = [];
|
|
1441
|
+
const missingSkillIds = [];
|
|
1442
|
+
for (const id of ordered) {
|
|
1443
|
+
const { section, missing } = await renderSkillSection(projectRoot, id, ides, scope);
|
|
1444
|
+
sections.push(section);
|
|
1445
|
+
if (missing) missingSkillIds.push(id);
|
|
1576
1446
|
}
|
|
1577
|
-
const
|
|
1578
|
-
const
|
|
1579
|
-
const
|
|
1580
|
-
const
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
)
|
|
1447
|
+
const target = path9.join(projectRoot, "AGENTS.md");
|
|
1448
|
+
const targetExists = await fileExists(target);
|
|
1449
|
+
const fullTemplate = renderAgentsMd({ variant, sections });
|
|
1450
|
+
const managedBody = renderManagedBlockBody({ variant, sections });
|
|
1451
|
+
let outputContent;
|
|
1452
|
+
let merge;
|
|
1453
|
+
if (!targetExists) {
|
|
1454
|
+
outputContent = fullTemplate;
|
|
1455
|
+
merge = "created";
|
|
1456
|
+
} else {
|
|
1457
|
+
await backupFile(target, projectRoot);
|
|
1458
|
+
if (mode === "merge-managed") {
|
|
1459
|
+
const existing = await readFileOrNull(target) ?? "";
|
|
1460
|
+
if (hasManagedRegion2(existing, AGENTS_MD_MANAGED_ID)) {
|
|
1461
|
+
outputContent = replaceManagedRegion2(
|
|
1462
|
+
existing,
|
|
1463
|
+
AGENTS_MD_MANAGED_ID,
|
|
1464
|
+
managedBody
|
|
1465
|
+
);
|
|
1466
|
+
merge = "managed-replaced";
|
|
1467
|
+
} else {
|
|
1468
|
+
const wrapped = wrapManagedBlock(managedBody);
|
|
1469
|
+
outputContent = `${wrapped}
|
|
1470
|
+
|
|
1471
|
+
${PRECEDENCE_NOTICE}
|
|
1472
|
+
|
|
1473
|
+
${existing.trimStart()}`;
|
|
1474
|
+
merge = "managed-prepended";
|
|
1475
|
+
}
|
|
1476
|
+
} else {
|
|
1477
|
+
outputContent = fullTemplate;
|
|
1478
|
+
merge = "overwritten";
|
|
1591
1479
|
}
|
|
1592
1480
|
}
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
|
|
1481
|
+
await fs8.writeFile(target, outputContent, "utf8");
|
|
1482
|
+
return {
|
|
1483
|
+
path: target,
|
|
1484
|
+
skillCount: ordered.length,
|
|
1485
|
+
missingSkillIds,
|
|
1486
|
+
backedUp: targetExists,
|
|
1487
|
+
merge
|
|
1488
|
+
};
|
|
1489
|
+
}
|
|
1490
|
+
function bucketRank(id) {
|
|
1491
|
+
if (id.startsWith("teamix-evo-design-")) return 0;
|
|
1492
|
+
if (id.startsWith("teamix-evo-code-")) return 1;
|
|
1493
|
+
return 2;
|
|
1494
|
+
}
|
|
1495
|
+
async function renderSkillSection(projectRoot, skillId, ides, scope) {
|
|
1496
|
+
const lines = [];
|
|
1497
|
+
lines.push(`### ${skillId}`);
|
|
1498
|
+
let parts = null;
|
|
1499
|
+
let missing = false;
|
|
1500
|
+
for (const ide of ides) {
|
|
1501
|
+
const adapter = getAdapter(ide);
|
|
1502
|
+
const skillPath = path9.join(
|
|
1503
|
+
adapter.getSkillTargetDir(skillId, scope, projectRoot),
|
|
1504
|
+
"SKILL.md"
|
|
1505
|
+
);
|
|
1506
|
+
try {
|
|
1507
|
+
const raw = await fs8.readFile(skillPath, "utf8");
|
|
1508
|
+
parts = extractDescriptionParts(raw);
|
|
1509
|
+
break;
|
|
1510
|
+
} catch {
|
|
1611
1511
|
continue;
|
|
1612
1512
|
}
|
|
1613
|
-
targetIds.push(id);
|
|
1614
1513
|
}
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
return lockVer === manVer;
|
|
1619
|
-
});
|
|
1620
|
-
if (targetIds.length > 0 && allSame && !dryRun) {
|
|
1621
|
-
return {
|
|
1622
|
-
status: "no-changes",
|
|
1623
|
-
packageName,
|
|
1624
|
-
version: manifest.version,
|
|
1625
|
-
checkedSkillIds: targetIds
|
|
1626
|
-
};
|
|
1514
|
+
if (!parts) missing = true;
|
|
1515
|
+
if (parts?.capability) {
|
|
1516
|
+
lines.push(`- ${parts.capability}`);
|
|
1627
1517
|
}
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
1518
|
+
lines.push(
|
|
1519
|
+
`- **TRIGGER**: ${parts?.trigger ?? "\u672A\u914D\u7F6E\u89E6\u53D1\u6761\u4EF6\uFF0C\u9700\u624B\u52A8\u6FC0\u6D3B\u8BE5 skill\u3002"}`
|
|
1520
|
+
);
|
|
1521
|
+
lines.push(
|
|
1522
|
+
`- **SKIP**: ${parts?.skip ?? "\u672A\u914D\u7F6E\u8DF3\u8FC7\u6761\u4EF6\uFF0C\u6309 TRIGGER \u515C\u5E95\u5224\u5B9A\u3002"}`
|
|
1523
|
+
);
|
|
1524
|
+
if (parts?.coordinates) {
|
|
1525
|
+
lines.push(`- **Coordinates with**: ${parts.coordinates}`);
|
|
1526
|
+
}
|
|
1527
|
+
return { section: lines.join("\n"), missing };
|
|
1528
|
+
}
|
|
1529
|
+
function renderAgentsMd(args) {
|
|
1530
|
+
const { variant, sections } = args;
|
|
1531
|
+
const managedBody = renderManagedBlockBody({ variant, sections });
|
|
1532
|
+
const wrapped = wrapManagedBlock(managedBody);
|
|
1533
|
+
return `${wrapped}
|
|
1534
|
+
|
|
1535
|
+
${PRECEDENCE_NOTICE}
|
|
1536
|
+
`;
|
|
1537
|
+
}
|
|
1538
|
+
function renderManagedBlockBody(args) {
|
|
1539
|
+
const { variant, sections } = args;
|
|
1540
|
+
const skillBlock = sections.length > 0 ? sections.join("\n\n") : "_\uFF08\u672C\u5DE5\u7A0B\u672A\u88C5\u914D\u5DE5\u7A0B\u7EA7 skill\u3002\uFF09_";
|
|
1541
|
+
return `# AGENTS.md
|
|
1542
|
+
|
|
1543
|
+
> AI \u52A9\u624B\uFF1A\u5199\u4EE3\u7801\u524D\u5148\u5BF9\u7167\u4E0B\u65B9 TRIGGER\uFF0C\u547D\u4E2D\u5219\u5FC5\u987B\u8BFB\u5BF9\u5E94 skill \u518D\u52A8\u624B\uFF1B\u4E0D\u786E\u5B9A\u65F6\u4ECD\u5EFA\u8BAE\u5FEB\u901F\u67E5\u9605\u76F8\u5173 skill \u4EE5\u786E\u8BA4\u662F\u5426\u6D89\u53CA\u89C4\u8303\u7EA6\u675F\u3002
|
|
1544
|
+
>
|
|
1545
|
+
> \u6FC0\u6D3B\u65B9\u5F0F\uFF1A\u5728\u5BF9\u8BDD\u4E2D\u76F4\u63A5\u63D0\u53CA skill \u540D\u79F0\uFF08\u5982 \`teamix-evo-design-${variant}\`\uFF09\uFF0CIDE \u4F1A\u81EA\u52A8\u52A0\u8F7D\u5176\u5B8C\u6574\u5185\u5BB9\u3002
|
|
1546
|
+
|
|
1547
|
+
## \u5DF2\u88C5 Skills\uFF08variant: ${variant}\uFF09
|
|
1548
|
+
|
|
1549
|
+
${skillBlock}
|
|
1550
|
+
|
|
1551
|
+
> \u5237\u65B0\u672C\u6587\u4EF6\uFF1A\`npx teamix-evo@latest skills update\``;
|
|
1552
|
+
}
|
|
1553
|
+
function wrapManagedBlock(body) {
|
|
1554
|
+
return `<!-- teamix-evo:managed:start id="${AGENTS_MD_MANAGED_ID}" -->
|
|
1555
|
+
${body}
|
|
1556
|
+
<!-- teamix-evo:managed:end id="${AGENTS_MD_MANAGED_ID}" -->`;
|
|
1557
|
+
}
|
|
1558
|
+
var PRECEDENCE_NOTICE = `<!-- teamix-evo:precedence -->
|
|
1559
|
+
> \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`;
|
|
1560
|
+
function extractDescriptionParts(fileContent) {
|
|
1561
|
+
const description = extractDescriptionBlock(fileContent);
|
|
1562
|
+
if (description == null) return null;
|
|
1563
|
+
const allLines = description.split("\n").map((l) => l.trim()).filter(Boolean);
|
|
1564
|
+
let capability = "";
|
|
1565
|
+
for (const line of allLines) {
|
|
1566
|
+
if (/^(TRIGGER when:|SKIP:|Coordinates with:)/i.test(line)) break;
|
|
1567
|
+
capability = capability ? `${capability} ${line}` : line;
|
|
1568
|
+
}
|
|
1569
|
+
return {
|
|
1570
|
+
capability: capability.trim(),
|
|
1571
|
+
trigger: extractSection(description, "TRIGGER when:"),
|
|
1572
|
+
skip: extractSection(description, "SKIP:"),
|
|
1573
|
+
coordinates: extractSection(description, "Coordinates with:")
|
|
1574
|
+
};
|
|
1575
|
+
}
|
|
1576
|
+
function extractDescriptionBlock(fileContent) {
|
|
1577
|
+
const lines = fileContent.split("\n");
|
|
1578
|
+
if (lines[0]?.trim() !== "---") return null;
|
|
1579
|
+
let endIdx = -1;
|
|
1580
|
+
for (let i = 1; i < lines.length; i++) {
|
|
1581
|
+
if (lines[i]?.trim() === "---") {
|
|
1582
|
+
endIdx = i;
|
|
1583
|
+
break;
|
|
1584
|
+
}
|
|
1585
|
+
}
|
|
1586
|
+
if (endIdx === -1) return null;
|
|
1587
|
+
const fmLines = lines.slice(1, endIdx);
|
|
1588
|
+
let startIdx = -1;
|
|
1589
|
+
let inlineValue = null;
|
|
1590
|
+
let blockMode = "inline";
|
|
1591
|
+
for (let i = 0; i < fmLines.length; i++) {
|
|
1592
|
+
const m = fmLines[i]?.match(/^description:\s*(\|[+-]?|>[+-]?)?\s*(.*)$/);
|
|
1593
|
+
if (m) {
|
|
1594
|
+
startIdx = i;
|
|
1595
|
+
const indicator = (m[1] ?? "").trim();
|
|
1596
|
+
const rest = m[2] ?? "";
|
|
1597
|
+
if (indicator.startsWith("|")) blockMode = "literal";
|
|
1598
|
+
else if (indicator.startsWith(">")) blockMode = "folded";
|
|
1599
|
+
else {
|
|
1600
|
+
blockMode = "inline";
|
|
1601
|
+
inlineValue = rest;
|
|
1602
|
+
}
|
|
1603
|
+
break;
|
|
1604
|
+
}
|
|
1605
|
+
}
|
|
1606
|
+
if (startIdx === -1) return null;
|
|
1607
|
+
if (blockMode === "inline") {
|
|
1608
|
+
return inlineValue ?? "";
|
|
1609
|
+
}
|
|
1610
|
+
const body = [];
|
|
1611
|
+
let blockIndent = -1;
|
|
1612
|
+
for (let i = startIdx + 1; i < fmLines.length; i++) {
|
|
1613
|
+
const line = fmLines[i] ?? "";
|
|
1614
|
+
if (line.trim() === "") {
|
|
1615
|
+
body.push("");
|
|
1616
|
+
continue;
|
|
1617
|
+
}
|
|
1618
|
+
const indentMatch = line.match(/^(\s+)/);
|
|
1619
|
+
const indent = indentMatch ? indentMatch[1].length : 0;
|
|
1620
|
+
if (indent === 0) break;
|
|
1621
|
+
if (blockIndent === -1) blockIndent = indent;
|
|
1622
|
+
if (indent < blockIndent) break;
|
|
1623
|
+
body.push(line.slice(blockIndent));
|
|
1624
|
+
}
|
|
1625
|
+
while (body.length > 0 && body[body.length - 1] === "") body.pop();
|
|
1626
|
+
return body.join("\n");
|
|
1627
|
+
}
|
|
1628
|
+
function extractSection(description, marker) {
|
|
1629
|
+
const markers = ["TRIGGER when:", "SKIP:", "Coordinates with:"];
|
|
1630
|
+
const lines = description.split("\n");
|
|
1631
|
+
let inSection = false;
|
|
1632
|
+
const collected = [];
|
|
1633
|
+
for (const line of lines) {
|
|
1634
|
+
const trimmed = line.trim();
|
|
1635
|
+
const startsWithMarker = trimmed.toLowerCase().startsWith(marker.toLowerCase());
|
|
1636
|
+
if (!inSection && startsWithMarker) {
|
|
1637
|
+
inSection = true;
|
|
1638
|
+
collected.push(trimmed.slice(marker.length).trim());
|
|
1639
|
+
continue;
|
|
1640
|
+
}
|
|
1641
|
+
if (inSection) {
|
|
1642
|
+
const hitNextMarker = markers.some(
|
|
1643
|
+
(m) => m !== marker && trimmed.toLowerCase().startsWith(m.toLowerCase())
|
|
1644
|
+
);
|
|
1645
|
+
if (hitNextMarker) break;
|
|
1646
|
+
if (trimmed === "") {
|
|
1647
|
+
if (collected[collected.length - 1] === "") break;
|
|
1648
|
+
collected.push("");
|
|
1649
|
+
continue;
|
|
1650
|
+
}
|
|
1651
|
+
collected.push(trimmed);
|
|
1652
|
+
}
|
|
1653
|
+
}
|
|
1654
|
+
if (!inSection) return null;
|
|
1655
|
+
const joined = collected.filter((l) => l !== "").join(" ").replace(/\s+/g, " ").trim();
|
|
1656
|
+
return joined || null;
|
|
1657
|
+
}
|
|
1658
|
+
|
|
1659
|
+
// src/core/skills-update.ts
|
|
1660
|
+
var DEFAULT_SKILLS_PACKAGE3 = "@teamix-evo/skills";
|
|
1661
|
+
var FLAT_VARIANT2 = "_flat";
|
|
1662
|
+
async function runSkillsUpdate(options) {
|
|
1663
|
+
const { projectRoot, names: requestedNames, dryRun } = options;
|
|
1664
|
+
const packageName = options.packageName ?? DEFAULT_SKILLS_PACKAGE3;
|
|
1665
|
+
const config = await readProjectConfig(projectRoot);
|
|
1666
|
+
const skillsCfg = config?.packages?.skills;
|
|
1667
|
+
if (!skillsCfg) {
|
|
1668
|
+
return { status: "no-skills" };
|
|
1669
|
+
}
|
|
1670
|
+
const ides = skillsCfg.ides ?? ["qoder", "claude"];
|
|
1671
|
+
const scope = skillsCfg.scope ?? "project";
|
|
1672
|
+
const existingLock = await readSkillsLock(projectRoot);
|
|
1673
|
+
if (!existingLock || Object.keys(existingLock.skills).length === 0) {
|
|
1674
|
+
return { status: "no-skills" };
|
|
1675
|
+
}
|
|
1676
|
+
const { manifest, data, packageRoot } = await loadSkillsData(packageName);
|
|
1677
|
+
const manifestById = new Map(manifest.skills.map((s) => [s.id, s]));
|
|
1678
|
+
const lockIds = Object.keys(existingLock.skills);
|
|
1679
|
+
const requestedSet = requestedNames ? new Set(requestedNames) : null;
|
|
1680
|
+
if (requestedSet) {
|
|
1681
|
+
const unknown = requestedNames.filter(
|
|
1682
|
+
(n) => !lockIds.includes(n) && !manifestById.has(n)
|
|
1683
|
+
);
|
|
1684
|
+
if (unknown.length > 0) {
|
|
1685
|
+
throw new Error(
|
|
1686
|
+
`Unknown skill id(s): ${unknown.join(
|
|
1687
|
+
", "
|
|
1688
|
+
)}. Available (installed): ${lockIds.join(", ") || "(none)"}.`
|
|
1689
|
+
);
|
|
1690
|
+
}
|
|
1691
|
+
}
|
|
1692
|
+
const targetIds = [];
|
|
1693
|
+
const skippedSkillIds = [];
|
|
1694
|
+
for (const id of lockIds) {
|
|
1695
|
+
if (requestedSet && !requestedSet.has(id)) continue;
|
|
1696
|
+
const entry2 = manifestById.get(id);
|
|
1697
|
+
if (!entry2) {
|
|
1698
|
+
logger.debug(
|
|
1699
|
+
`Skipping "${id}": no longer in upstream manifest. Use \`skills uninstall ${id}\` to remove.`
|
|
1700
|
+
);
|
|
1701
|
+
skippedSkillIds.push(id);
|
|
1702
|
+
continue;
|
|
1703
|
+
}
|
|
1704
|
+
const effectiveScope = entry2.scope ?? "project";
|
|
1705
|
+
if (effectiveScope !== scope) {
|
|
1706
|
+
logger.debug(
|
|
1707
|
+
`Skipping "${id}" (scope=${effectiveScope}): current install scope is "${scope}".`
|
|
1708
|
+
);
|
|
1709
|
+
skippedSkillIds.push(id);
|
|
1710
|
+
continue;
|
|
1711
|
+
}
|
|
1712
|
+
targetIds.push(id);
|
|
1713
|
+
}
|
|
1714
|
+
const allSame = targetIds.every((id) => {
|
|
1715
|
+
const lockVer = existingLock.skills[id].version;
|
|
1716
|
+
const manVer = manifestById.get(id).version;
|
|
1717
|
+
return lockVer === manVer;
|
|
1718
|
+
});
|
|
1719
|
+
if (targetIds.length > 0 && allSame && !dryRun) {
|
|
1720
|
+
return {
|
|
1721
|
+
status: "no-changes",
|
|
1722
|
+
packageName,
|
|
1723
|
+
version: manifest.version,
|
|
1724
|
+
checkedSkillIds: targetIds
|
|
1725
|
+
};
|
|
1726
|
+
}
|
|
1727
|
+
if (dryRun) {
|
|
1728
|
+
const plan = targetIds.map((id) => {
|
|
1729
|
+
const lockVer = existingLock.skills[id].version;
|
|
1730
|
+
const entry2 = manifestById.get(id);
|
|
1731
|
+
const sameVersion = lockVer === entry2.version;
|
|
1732
|
+
return {
|
|
1733
|
+
id,
|
|
1635
1734
|
current: lockVer,
|
|
1636
1735
|
next: entry2.version,
|
|
1637
1736
|
strategy: entry2.updateStrategy ?? "managed",
|
|
@@ -1714,6 +1813,24 @@ async function runSkillsUpdate(options) {
|
|
|
1714
1813
|
};
|
|
1715
1814
|
}
|
|
1716
1815
|
await writeSkillsLock(projectRoot, lock);
|
|
1816
|
+
if (scope === "project") {
|
|
1817
|
+
try {
|
|
1818
|
+
const variant = await readTokensVariant(projectRoot);
|
|
1819
|
+
if (variant) {
|
|
1820
|
+
const projectSkillIds = Object.entries(lock.skills).filter(([, v]) => v.scope === "project").map(([id]) => id);
|
|
1821
|
+
await runGenerateAgentsMd({
|
|
1822
|
+
projectRoot,
|
|
1823
|
+
variant,
|
|
1824
|
+
skillIds: projectSkillIds,
|
|
1825
|
+
mode: "merge-managed"
|
|
1826
|
+
});
|
|
1827
|
+
}
|
|
1828
|
+
} catch (err) {
|
|
1829
|
+
logger.warn(
|
|
1830
|
+
`AGENTS.md \u5237\u65B0\u5931\u8D25\uFF08\u975E\u5173\u952E\uFF09\uFF1A${err instanceof Error ? err.message : String(err)}`
|
|
1831
|
+
);
|
|
1832
|
+
}
|
|
1833
|
+
}
|
|
1717
1834
|
return {
|
|
1718
1835
|
status: "updated",
|
|
1719
1836
|
packageName,
|
|
@@ -1734,8 +1851,7 @@ var DEFAULT_UI_ALIASES = {
|
|
|
1734
1851
|
utils: "src/lib/utils",
|
|
1735
1852
|
lib: "src/lib",
|
|
1736
1853
|
business: "src/components/business",
|
|
1737
|
-
blocks: "src/blocks"
|
|
1738
|
-
templates: "src/templates"
|
|
1854
|
+
blocks: "src/blocks"
|
|
1739
1855
|
};
|
|
1740
1856
|
var DEFAULT_UI_ICON_LIBRARY = "lucide";
|
|
1741
1857
|
async function runUiInit(options) {
|
|
@@ -1752,8 +1868,7 @@ async function runUiInit(options) {
|
|
|
1752
1868
|
utils: options.aliases?.utils ?? DEFAULT_UI_ALIASES.utils,
|
|
1753
1869
|
lib: options.aliases?.lib ?? DEFAULT_UI_ALIASES.lib,
|
|
1754
1870
|
business: options.aliases?.business ?? DEFAULT_UI_ALIASES.business,
|
|
1755
|
-
blocks: options.aliases?.blocks ?? DEFAULT_UI_ALIASES.blocks
|
|
1756
|
-
templates: options.aliases?.templates ?? DEFAULT_UI_ALIASES.templates
|
|
1871
|
+
blocks: options.aliases?.blocks ?? DEFAULT_UI_ALIASES.blocks
|
|
1757
1872
|
};
|
|
1758
1873
|
const iconLibrary = options.iconLibrary ?? DEFAULT_UI_ICON_LIBRARY;
|
|
1759
1874
|
const tsx = options.tsx ?? true;
|
|
@@ -1773,7 +1888,6 @@ async function runUiInit(options) {
|
|
|
1773
1888
|
rsc
|
|
1774
1889
|
};
|
|
1775
1890
|
await writeProjectConfig(projectRoot, config);
|
|
1776
|
-
await ensureMcpJson(projectRoot);
|
|
1777
1891
|
return {
|
|
1778
1892
|
status: "installed",
|
|
1779
1893
|
aliases,
|
|
@@ -1785,7 +1899,7 @@ async function runUiInit(options) {
|
|
|
1785
1899
|
|
|
1786
1900
|
// src/core/ui-client.ts
|
|
1787
1901
|
import * as path10 from "path";
|
|
1788
|
-
import * as
|
|
1902
|
+
import * as fs9 from "fs/promises";
|
|
1789
1903
|
import { createRequire as createRequire3 } from "module";
|
|
1790
1904
|
import { loadUiPackageManifest } from "@teamix-evo/registry";
|
|
1791
1905
|
var require4 = createRequire3(import.meta.url);
|
|
@@ -1800,7 +1914,7 @@ async function loadUiData(packageName) {
|
|
|
1800
1914
|
let data = {};
|
|
1801
1915
|
const dataPath = path10.join(packageRoot, "_data.json");
|
|
1802
1916
|
try {
|
|
1803
|
-
const raw = await
|
|
1917
|
+
const raw = await fs9.readFile(dataPath, "utf-8");
|
|
1804
1918
|
data = JSON.parse(raw);
|
|
1805
1919
|
} catch (err) {
|
|
1806
1920
|
if (err.code !== "ENOENT") {
|
|
@@ -1813,7 +1927,7 @@ async function loadUiData(packageName) {
|
|
|
1813
1927
|
|
|
1814
1928
|
// src/core/ui-installer.ts
|
|
1815
1929
|
import * as path11 from "path";
|
|
1816
|
-
import * as
|
|
1930
|
+
import * as fs10 from "fs/promises";
|
|
1817
1931
|
import { resolveUiEntryOrder } from "@teamix-evo/registry";
|
|
1818
1932
|
|
|
1819
1933
|
// src/utils/transform-imports.ts
|
|
@@ -1890,9 +2004,21 @@ async function installUiEntries(options) {
|
|
|
1890
2004
|
}
|
|
1891
2005
|
const rootForEntry = entryPackageRoot?.get(entry.id) ?? packageRoot;
|
|
1892
2006
|
const sourceAbs = path11.resolve(rootForEntry, file.source);
|
|
1893
|
-
const raw = await
|
|
2007
|
+
const raw = await fs10.readFile(sourceAbs, "utf-8");
|
|
1894
2008
|
const transformed = rewriteImports(raw, aliases, { flatten });
|
|
1895
2009
|
if (exists) {
|
|
2010
|
+
const current = await fs10.readFile(targetAbs, "utf-8");
|
|
2011
|
+
if (current === transformed) {
|
|
2012
|
+
logger.info(` skip (identical): ${rel(projectRoot, targetAbs)}`);
|
|
2013
|
+
skipped++;
|
|
2014
|
+
resources.push({
|
|
2015
|
+
id: `${entry.id}:${file.targetName}`,
|
|
2016
|
+
target: path11.relative(projectRoot, targetAbs),
|
|
2017
|
+
hash: computeHash(transformed),
|
|
2018
|
+
strategy: entry.updateStrategy ?? "frozen"
|
|
2019
|
+
});
|
|
2020
|
+
continue;
|
|
2021
|
+
}
|
|
1896
2022
|
await backupFile(targetAbs, projectRoot);
|
|
1897
2023
|
}
|
|
1898
2024
|
await writeFileSafe(targetAbs, transformed);
|
|
@@ -1900,7 +2026,7 @@ async function installUiEntries(options) {
|
|
|
1900
2026
|
logger.info(` write: ${rel(projectRoot, targetAbs)}`);
|
|
1901
2027
|
resources.push({
|
|
1902
2028
|
id: `${entry.id}:${file.targetName}`,
|
|
1903
|
-
target: targetAbs,
|
|
2029
|
+
target: path11.relative(projectRoot, targetAbs),
|
|
1904
2030
|
hash: computeHash(transformed),
|
|
1905
2031
|
strategy: entry.updateStrategy ?? "frozen"
|
|
1906
2032
|
});
|
|
@@ -1926,23 +2052,26 @@ function resolveTargetPath(projectRoot, aliases, entry, file) {
|
|
|
1926
2052
|
function rel(projectRoot, abs) {
|
|
1927
2053
|
return path11.relative(projectRoot, abs);
|
|
1928
2054
|
}
|
|
1929
|
-
async function removeUiFiles(records) {
|
|
2055
|
+
async function removeUiFiles(records, projectRoot) {
|
|
1930
2056
|
const removed = [];
|
|
1931
2057
|
for (const r of records) {
|
|
2058
|
+
const abs = resolveResourceTarget(projectRoot, r.target);
|
|
1932
2059
|
try {
|
|
1933
|
-
await
|
|
1934
|
-
removed.push(
|
|
2060
|
+
await fs10.unlink(abs);
|
|
2061
|
+
removed.push(abs);
|
|
1935
2062
|
} catch (err) {
|
|
1936
2063
|
if (err.code !== "ENOENT") {
|
|
1937
|
-
logger.warn(`Failed to remove ${
|
|
2064
|
+
logger.warn(`Failed to remove ${abs}: ${getErrorMessage(err)}`);
|
|
1938
2065
|
}
|
|
1939
2066
|
}
|
|
1940
2067
|
}
|
|
1941
|
-
const parents = new Set(
|
|
2068
|
+
const parents = new Set(
|
|
2069
|
+
records.map((r) => path11.dirname(resolveResourceTarget(projectRoot, r.target)))
|
|
2070
|
+
);
|
|
1942
2071
|
for (const dir of parents) {
|
|
1943
2072
|
try {
|
|
1944
|
-
const entries = await
|
|
1945
|
-
if (entries.length === 0) await
|
|
2073
|
+
const entries = await fs10.readdir(dir);
|
|
2074
|
+
if (entries.length === 0) await fs10.rmdir(dir);
|
|
1946
2075
|
} catch {
|
|
1947
2076
|
}
|
|
1948
2077
|
}
|
|
@@ -2091,7 +2220,7 @@ async function runVariantUiAdd(packageName, options) {
|
|
|
2091
2220
|
const uiCfg = config?.packages?.ui;
|
|
2092
2221
|
if (!config || !uiCfg?.aliases) {
|
|
2093
2222
|
throw new Error(
|
|
2094
|
-
`UI not initialized. Run \`teamix-evo ui init\` first \u2014 \`${packageName} add\` writes into the same alias map (business /
|
|
2223
|
+
`UI not initialized. Run \`teamix-evo ui init\` first \u2014 \`${packageName} add\` writes into the same alias map (business / blocks).`
|
|
2095
2224
|
);
|
|
2096
2225
|
}
|
|
2097
2226
|
const packageRoot = options.packageRoot ?? resolvePackageRoot3(fullPackageName);
|
|
@@ -2182,9 +2311,6 @@ function mergeResources2(prior, next) {
|
|
|
2182
2311
|
async function runBizUiAdd(options) {
|
|
2183
2312
|
return runVariantUiAdd("biz-ui", options);
|
|
2184
2313
|
}
|
|
2185
|
-
async function runTemplatesAdd(options) {
|
|
2186
|
-
return runVariantUiAdd("templates", options);
|
|
2187
|
-
}
|
|
2188
2314
|
async function listVariantUi(packageName, packageRoot) {
|
|
2189
2315
|
const fullPackageName = `@teamix-evo/${packageName}`;
|
|
2190
2316
|
const root = packageRoot ?? resolvePackageRoot3(fullPackageName);
|
|
@@ -2202,9 +2328,6 @@ async function listVariantUi(packageName, packageRoot) {
|
|
|
2202
2328
|
async function listBizUiVariants(packageRoot) {
|
|
2203
2329
|
return listVariantUi("biz-ui", packageRoot);
|
|
2204
2330
|
}
|
|
2205
|
-
async function listTemplatesVariants(packageRoot) {
|
|
2206
|
-
return listVariantUi("templates", packageRoot);
|
|
2207
|
-
}
|
|
2208
2331
|
async function listVariantUiEntries(packageName, variant, packageRoot) {
|
|
2209
2332
|
const fullPackageName = `@teamix-evo/${packageName}`;
|
|
2210
2333
|
const root = packageRoot ?? resolvePackageRoot3(fullPackageName);
|
|
@@ -2232,13 +2355,10 @@ async function listVariantUiEntries(packageName, variant, packageRoot) {
|
|
|
2232
2355
|
async function listBizUiEntries(variant, packageRoot) {
|
|
2233
2356
|
return listVariantUiEntries("biz-ui", variant, packageRoot);
|
|
2234
2357
|
}
|
|
2235
|
-
async function listTemplatesEntries(variant, packageRoot) {
|
|
2236
|
-
return listVariantUiEntries("templates", variant, packageRoot);
|
|
2237
|
-
}
|
|
2238
2358
|
|
|
2239
2359
|
// src/core/lint-init.ts
|
|
2240
2360
|
import * as path13 from "path";
|
|
2241
|
-
import * as
|
|
2361
|
+
import * as fs11 from "fs";
|
|
2242
2362
|
import { execa } from "execa";
|
|
2243
2363
|
var ESLINT_CONFIG_CONTENT = `/**
|
|
2244
2364
|
* teamix-evo consumer ESLint preset \u2014 9 token-discipline rules.
|
|
@@ -2330,7 +2450,7 @@ async function runLintInit(options) {
|
|
|
2330
2450
|
let stylelintIgnoreFilesWarning = false;
|
|
2331
2451
|
if (!stylelintNeedsWrite && stylelintTemplateExists) {
|
|
2332
2452
|
try {
|
|
2333
|
-
const existingContent =
|
|
2453
|
+
const existingContent = fs11.readFileSync(stylelintConfigPath, "utf-8");
|
|
2334
2454
|
const usesTeamixPreset = existingContent.includes("@teamix-evo/stylelint-config/presets/") || existingContent.includes("@teamix-evo/stylelint-config/preset/");
|
|
2335
2455
|
const hasTokenIgnore = existingContent.includes("tokens.theme.css") && existingContent.includes("tokens.overrides.css");
|
|
2336
2456
|
if (!usesTeamixPreset && !hasTokenIgnore) {
|
|
@@ -2344,305 +2464,65 @@ async function runLintInit(options) {
|
|
|
2344
2464
|
" '**/tokens.theme.css',",
|
|
2345
2465
|
" '**/tokens.overrides.css',",
|
|
2346
2466
|
" ]",
|
|
2347
|
-
"",
|
|
2348
|
-
"\u6216\u5207\u6362\u5230 teamix-evo \u9884\u8BBE\u4EE5\u81EA\u52A8\u83B7\u5F97\u6392\u9664\u89C4\u5219:",
|
|
2349
|
-
"",
|
|
2350
|
-
" extends: ['@teamix-evo/stylelint-config/presets/consumer']"
|
|
2351
|
-
].join("\n")
|
|
2352
|
-
);
|
|
2353
|
-
}
|
|
2354
|
-
} catch {
|
|
2355
|
-
}
|
|
2356
|
-
}
|
|
2357
|
-
return {
|
|
2358
|
-
status: "installed",
|
|
2359
|
-
eslint: wroteEslint,
|
|
2360
|
-
stylelint: wroteStylelint,
|
|
2361
|
-
eslintMergeRequested: wroteEslint && eslintStrategy === "merge" && eslintExistingPaths.length > 0,
|
|
2362
|
-
stylelintMergeRequested: wroteStylelint && stylelintStrategy === "merge" && stylelintExistingPaths.length > 0,
|
|
2363
|
-
eslintSkipped: eslintSkipRequested,
|
|
2364
|
-
stylelintSkipped: stylelintSkipRequested,
|
|
2365
|
-
packageJsonPatched,
|
|
2366
|
-
stylelintIgnoreFilesWarning
|
|
2367
|
-
};
|
|
2368
|
-
}
|
|
2369
|
-
function detectPm(projectRoot) {
|
|
2370
|
-
if (fs9.existsSync(path13.join(projectRoot, "pnpm-lock.yaml"))) return "pnpm";
|
|
2371
|
-
if (fs9.existsSync(path13.join(projectRoot, "yarn.lock"))) return "yarn";
|
|
2372
|
-
return "npm";
|
|
2373
|
-
}
|
|
2374
|
-
async function patchPackageJsonScripts(projectRoot) {
|
|
2375
|
-
const pkgPath = path13.join(projectRoot, "package.json");
|
|
2376
|
-
const raw = await readFileOrNull(pkgPath);
|
|
2377
|
-
if (!raw) return false;
|
|
2378
|
-
let pkg;
|
|
2379
|
-
try {
|
|
2380
|
-
pkg = JSON.parse(raw);
|
|
2381
|
-
} catch {
|
|
2382
|
-
return false;
|
|
2383
|
-
}
|
|
2384
|
-
const scripts = pkg.scripts ?? {};
|
|
2385
|
-
let changed = false;
|
|
2386
|
-
if (!scripts.lint) {
|
|
2387
|
-
scripts.lint = "eslint src/";
|
|
2388
|
-
changed = true;
|
|
2389
|
-
}
|
|
2390
|
-
if (!scripts["lint:css"]) {
|
|
2391
|
-
scripts["lint:css"] = "stylelint 'src/**/*.css'";
|
|
2392
|
-
changed = true;
|
|
2393
|
-
}
|
|
2394
|
-
if (changed) {
|
|
2395
|
-
await backupFile(pkgPath, projectRoot);
|
|
2396
|
-
pkg.scripts = scripts;
|
|
2397
|
-
await writeFileSafe(pkgPath, JSON.stringify(pkg, null, 2) + "\n");
|
|
2398
|
-
logger.debug("Patched package.json scripts with lint / lint:css");
|
|
2399
|
-
}
|
|
2400
|
-
return changed;
|
|
2401
|
-
}
|
|
2402
|
-
|
|
2403
|
-
// src/core/agents-md.ts
|
|
2404
|
-
import * as fs10 from "fs/promises";
|
|
2405
|
-
import * as path14 from "path";
|
|
2406
|
-
import { hasManagedRegion as hasManagedRegion2, replaceManagedRegion as replaceManagedRegion2 } from "@teamix-evo/registry";
|
|
2407
|
-
var AGENTS_MD_MANAGED_ID = "teamix-evo-skills";
|
|
2408
|
-
async function runGenerateAgentsMd(options) {
|
|
2409
|
-
const { projectRoot, variant, skillIds } = options;
|
|
2410
|
-
const mode = options.mode ?? "overwrite";
|
|
2411
|
-
const ordered = [...skillIds].sort(
|
|
2412
|
-
(a, b) => bucketRank(a) - bucketRank(b) || a.localeCompare(b)
|
|
2413
|
-
);
|
|
2414
|
-
const sections = [];
|
|
2415
|
-
const missingSkillIds = [];
|
|
2416
|
-
for (const id of ordered) {
|
|
2417
|
-
const { section, missing } = await renderSkillSection(projectRoot, id);
|
|
2418
|
-
sections.push(section);
|
|
2419
|
-
if (missing) missingSkillIds.push(id);
|
|
2420
|
-
}
|
|
2421
|
-
const target = path14.join(projectRoot, "AGENTS.md");
|
|
2422
|
-
const targetExists = await fileExists(target);
|
|
2423
|
-
const fullTemplate = renderAgentsMd({ variant, sections });
|
|
2424
|
-
const managedBody = renderManagedBlockBody({ variant, sections });
|
|
2425
|
-
let outputContent;
|
|
2426
|
-
let merge;
|
|
2427
|
-
if (!targetExists) {
|
|
2428
|
-
outputContent = fullTemplate;
|
|
2429
|
-
merge = "created";
|
|
2430
|
-
} else {
|
|
2431
|
-
await backupFile(target, projectRoot);
|
|
2432
|
-
if (mode === "merge-managed") {
|
|
2433
|
-
const existing = await readFileOrNull(target) ?? "";
|
|
2434
|
-
if (hasManagedRegion2(existing, AGENTS_MD_MANAGED_ID)) {
|
|
2435
|
-
outputContent = replaceManagedRegion2(
|
|
2436
|
-
existing,
|
|
2437
|
-
AGENTS_MD_MANAGED_ID,
|
|
2438
|
-
managedBody
|
|
2439
|
-
);
|
|
2440
|
-
merge = "managed-replaced";
|
|
2441
|
-
} else {
|
|
2442
|
-
const wrapped = wrapManagedBlock(managedBody);
|
|
2443
|
-
outputContent = `${wrapped}
|
|
2444
|
-
|
|
2445
|
-
${PRECEDENCE_NOTICE}
|
|
2446
|
-
|
|
2447
|
-
${existing.trimStart()}`;
|
|
2448
|
-
merge = "managed-prepended";
|
|
2449
|
-
}
|
|
2450
|
-
} else {
|
|
2451
|
-
outputContent = fullTemplate;
|
|
2452
|
-
merge = "overwritten";
|
|
2453
|
-
}
|
|
2454
|
-
}
|
|
2455
|
-
await fs10.writeFile(target, outputContent, "utf8");
|
|
2456
|
-
return {
|
|
2457
|
-
path: target,
|
|
2458
|
-
skillCount: ordered.length,
|
|
2459
|
-
missingSkillIds,
|
|
2460
|
-
backedUp: targetExists,
|
|
2461
|
-
merge
|
|
2462
|
-
};
|
|
2463
|
-
}
|
|
2464
|
-
function bucketRank(id) {
|
|
2465
|
-
if (id.startsWith("teamix-evo-design-")) return 0;
|
|
2466
|
-
if (id.startsWith("teamix-evo-code-")) return 1;
|
|
2467
|
-
return 2;
|
|
2468
|
-
}
|
|
2469
|
-
async function renderSkillSection(projectRoot, skillId) {
|
|
2470
|
-
const skillPath = path14.join(
|
|
2471
|
-
getSkillsSourceDir(projectRoot, skillId),
|
|
2472
|
-
"SKILL.md"
|
|
2473
|
-
);
|
|
2474
|
-
const lines = [];
|
|
2475
|
-
lines.push(`### ${skillId}`);
|
|
2476
|
-
let parts = null;
|
|
2477
|
-
let missing = false;
|
|
2478
|
-
try {
|
|
2479
|
-
const raw = await fs10.readFile(skillPath, "utf8");
|
|
2480
|
-
parts = extractDescriptionParts(raw);
|
|
2481
|
-
} catch {
|
|
2482
|
-
missing = true;
|
|
2483
|
-
}
|
|
2484
|
-
if (parts?.capability) {
|
|
2485
|
-
lines.push(`- ${parts.capability}`);
|
|
2486
|
-
}
|
|
2487
|
-
lines.push(
|
|
2488
|
-
`- **TRIGGER**: ${parts?.trigger ?? "\u672A\u914D\u7F6E\u89E6\u53D1\u6761\u4EF6\uFF0C\u9700\u624B\u52A8\u6FC0\u6D3B\u8BE5 skill\u3002"}`
|
|
2489
|
-
);
|
|
2490
|
-
lines.push(
|
|
2491
|
-
`- **SKIP**: ${parts?.skip ?? "\u672A\u914D\u7F6E\u8DF3\u8FC7\u6761\u4EF6\uFF0C\u6309 TRIGGER \u515C\u5E95\u5224\u5B9A\u3002"}`
|
|
2492
|
-
);
|
|
2493
|
-
if (parts?.coordinates) {
|
|
2494
|
-
lines.push(`- **Coordinates with**: ${parts.coordinates}`);
|
|
2495
|
-
}
|
|
2496
|
-
lines.push(`- **\u4F4D\u7F6E**: \`.teamix-evo/skills-source/${skillId}/SKILL.md\``);
|
|
2497
|
-
return { section: lines.join("\n"), missing };
|
|
2498
|
-
}
|
|
2499
|
-
function renderAgentsMd(args) {
|
|
2500
|
-
const { variant, sections } = args;
|
|
2501
|
-
const managedBody = renderManagedBlockBody({ variant, sections });
|
|
2502
|
-
const wrapped = wrapManagedBlock(managedBody);
|
|
2503
|
-
return `${wrapped}
|
|
2504
|
-
|
|
2505
|
-
${PRECEDENCE_NOTICE}
|
|
2506
|
-
`;
|
|
2507
|
-
}
|
|
2508
|
-
function renderManagedBlockBody(args) {
|
|
2509
|
-
const { variant, sections } = args;
|
|
2510
|
-
const skillBlock = sections.length > 0 ? sections.join("\n\n") : "_\uFF08\u672C\u5DE5\u7A0B\u672A\u88C5\u914D\u5DE5\u7A0B\u7EA7 skill\u3002\uFF09_";
|
|
2511
|
-
return `# AGENTS.md
|
|
2512
|
-
|
|
2513
|
-
> \u672C\u5DE5\u7A0B\u5DF2\u88C5\u914D Teamix Evo AI skills\u3002AI \u52A9\u624B\u5728\u4EE5\u4E0B\u573A\u666F\u4E0B**\u5FC5\u987B\u5148\u8BFB\u5BF9\u5E94 skill** \u518D\u52A8\u624B\u3002
|
|
2514
|
-
> \u672C\u6587\u4EF6\u7531 \`teamix-evo init\` / \`create-teamix-evo\` \u81EA\u52A8\u751F\u6210\uFF08regenerable\uFF0C\u9075\u5FAA ADR 0038\uFF09\uFF0C\u5237\u65B0\u65B9\u5F0F\u89C1\u5E95\u90E8\u3002
|
|
2515
|
-
|
|
2516
|
-
## \u5DF2\u88C5 Skills\uFF08variant: ${variant}\uFF09
|
|
2517
|
-
|
|
2518
|
-
${skillBlock}
|
|
2519
|
-
|
|
2520
|
-
## \u89E6\u53D1\u515C\u5E95\u89C4\u5219
|
|
2521
|
-
|
|
2522
|
-
- \u5199\u65B0 \`.tsx\` / \`.ts\` \u524D\uFF0C\u5BF9\u7167\u4E0A\u8FF0 TRIGGER \u5224\u5B9A\u662F\u5426\u547D\u4E2D
|
|
2523
|
-
- \u547D\u4E2D\u5219\u5148\u8BFB\u5BF9\u5E94 \`SKILL.md\`\uFF0C\u518D\u52A8\u624B\uFF1B\u4E8C\u8005\u540C\u65F6\u547D\u4E2D\u5219\u4E24\u4E2A\u90FD\u8BFB
|
|
2524
|
-
- \u6A21\u7CCA\u573A\u666F\uFF1A\u5148\u6309 SKIP \u53CD\u5411\u6392\u9664\uFF0C\u5269\u4F59\u552F\u4E00 skill \u5373\u4E3A\u5165\u53E3
|
|
2525
|
-
- \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
|
|
2526
|
-
- \u8FC1\u79FB\u573A\u666F\uFF08"\u4EE3\u7801\u8FC1\u79FB" / "\u6267\u884C\u8FC1\u79FB" / "\u65E7\u9879\u76EE\u8FC1\u79FB" / "\u91CD\u5EFA\u8001\u5DE5\u7A0B"\uFF09\u540C\u6837\u8D70 \`teamix-evo-manage\` \u573A\u666F 6
|
|
2527
|
-
|
|
2528
|
-
## UI \u7EC4\u4EF6\u9886\u5730\uFF08init \u540E\u7EA6\u675F\uFF09
|
|
2529
|
-
|
|
2530
|
-
- \`src/components/ui/\` \u7531 teamix-evo \u63A5\u7BA1\uFF0C\u7981\u6B62\u624B\u5DE5\u6216\u901A\u8FC7 shadcn CLI \u6DFB\u52A0\u65B0\u7EC4\u4EF6
|
|
2531
|
-
- \u5982\u9700\u65B0\u589E UI \u7EC4\u4EF6\uFF0C\u4F7F\u7528 \`npx teamix-evo ui add <id>\`
|
|
2532
|
-
- \`src/components/shadcn-ui/\` \u662F init \u524D legacy \u7EC4\u4EF6\u5F52\u6863\uFF0C\u53EA\u8BFB\uFF1B\u65B0\u4EE3\u7801\u4E0D\u5E94\u518D import
|
|
2533
|
-
- \u5347\u7EA7 legacy \u7EC4\u4EF6\uFF1A\u89E6\u53D1 teamix-evo-upgrade skill
|
|
2534
|
-
|
|
2535
|
-
> \u5237\u65B0\u672C\u6587\u4EF6\uFF1A\`npx teamix-evo skills add\` \u6216\u91CD\u8DD1 \`npm create teamix-evo\` / \`teamix-evo init\`\u3002`;
|
|
2536
|
-
}
|
|
2537
|
-
function wrapManagedBlock(body) {
|
|
2538
|
-
return `<!-- teamix-evo:managed:start id="${AGENTS_MD_MANAGED_ID}" -->
|
|
2539
|
-
${body}
|
|
2540
|
-
<!-- teamix-evo:managed:end id="${AGENTS_MD_MANAGED_ID}" -->`;
|
|
2541
|
-
}
|
|
2542
|
-
var PRECEDENCE_NOTICE = `<!-- teamix-evo:precedence -->
|
|
2543
|
-
> \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`;
|
|
2544
|
-
function extractDescriptionParts(fileContent) {
|
|
2545
|
-
const description = extractDescriptionBlock(fileContent);
|
|
2546
|
-
if (description == null) return null;
|
|
2547
|
-
const allLines = description.split("\n").map((l) => l.trim()).filter(Boolean);
|
|
2548
|
-
let capability = "";
|
|
2549
|
-
for (const line of allLines) {
|
|
2550
|
-
if (/^(TRIGGER when:|SKIP:|Coordinates with:)/i.test(line)) break;
|
|
2551
|
-
capability = capability ? `${capability} ${line}` : line;
|
|
2552
|
-
}
|
|
2553
|
-
return {
|
|
2554
|
-
capability: capability.trim(),
|
|
2555
|
-
trigger: extractSection(description, "TRIGGER when:"),
|
|
2556
|
-
skip: extractSection(description, "SKIP:"),
|
|
2557
|
-
coordinates: extractSection(description, "Coordinates with:")
|
|
2558
|
-
};
|
|
2559
|
-
}
|
|
2560
|
-
function extractDescriptionBlock(fileContent) {
|
|
2561
|
-
const lines = fileContent.split("\n");
|
|
2562
|
-
if (lines[0]?.trim() !== "---") return null;
|
|
2563
|
-
let endIdx = -1;
|
|
2564
|
-
for (let i = 1; i < lines.length; i++) {
|
|
2565
|
-
if (lines[i]?.trim() === "---") {
|
|
2566
|
-
endIdx = i;
|
|
2567
|
-
break;
|
|
2568
|
-
}
|
|
2569
|
-
}
|
|
2570
|
-
if (endIdx === -1) return null;
|
|
2571
|
-
const fmLines = lines.slice(1, endIdx);
|
|
2572
|
-
let startIdx = -1;
|
|
2573
|
-
let inlineValue = null;
|
|
2574
|
-
let blockMode = "inline";
|
|
2575
|
-
for (let i = 0; i < fmLines.length; i++) {
|
|
2576
|
-
const m = fmLines[i]?.match(/^description:\s*(\|[+-]?|>[+-]?)?\s*(.*)$/);
|
|
2577
|
-
if (m) {
|
|
2578
|
-
startIdx = i;
|
|
2579
|
-
const indicator = (m[1] ?? "").trim();
|
|
2580
|
-
const rest = m[2] ?? "";
|
|
2581
|
-
if (indicator.startsWith("|")) blockMode = "literal";
|
|
2582
|
-
else if (indicator.startsWith(">")) blockMode = "folded";
|
|
2583
|
-
else {
|
|
2584
|
-
blockMode = "inline";
|
|
2585
|
-
inlineValue = rest;
|
|
2467
|
+
"",
|
|
2468
|
+
"\u6216\u5207\u6362\u5230 teamix-evo \u9884\u8BBE\u4EE5\u81EA\u52A8\u83B7\u5F97\u6392\u9664\u89C4\u5219:",
|
|
2469
|
+
"",
|
|
2470
|
+
" extends: ['@teamix-evo/stylelint-config/presets/consumer']"
|
|
2471
|
+
].join("\n")
|
|
2472
|
+
);
|
|
2586
2473
|
}
|
|
2587
|
-
|
|
2474
|
+
} catch {
|
|
2588
2475
|
}
|
|
2589
2476
|
}
|
|
2590
|
-
|
|
2591
|
-
|
|
2592
|
-
|
|
2477
|
+
return {
|
|
2478
|
+
status: "installed",
|
|
2479
|
+
eslint: wroteEslint,
|
|
2480
|
+
stylelint: wroteStylelint,
|
|
2481
|
+
eslintMergeRequested: wroteEslint && eslintStrategy === "merge" && eslintExistingPaths.length > 0,
|
|
2482
|
+
stylelintMergeRequested: wroteStylelint && stylelintStrategy === "merge" && stylelintExistingPaths.length > 0,
|
|
2483
|
+
eslintSkipped: eslintSkipRequested,
|
|
2484
|
+
stylelintSkipped: stylelintSkipRequested,
|
|
2485
|
+
packageJsonPatched,
|
|
2486
|
+
stylelintIgnoreFilesWarning
|
|
2487
|
+
};
|
|
2488
|
+
}
|
|
2489
|
+
function detectPm(projectRoot) {
|
|
2490
|
+
if (fs11.existsSync(path13.join(projectRoot, "pnpm-lock.yaml"))) return "pnpm";
|
|
2491
|
+
if (fs11.existsSync(path13.join(projectRoot, "yarn.lock"))) return "yarn";
|
|
2492
|
+
return "npm";
|
|
2493
|
+
}
|
|
2494
|
+
async function patchPackageJsonScripts(projectRoot) {
|
|
2495
|
+
const pkgPath = path13.join(projectRoot, "package.json");
|
|
2496
|
+
const raw = await readFileOrNull(pkgPath);
|
|
2497
|
+
if (!raw) return false;
|
|
2498
|
+
let pkg;
|
|
2499
|
+
try {
|
|
2500
|
+
pkg = JSON.parse(raw);
|
|
2501
|
+
} catch {
|
|
2502
|
+
return false;
|
|
2593
2503
|
}
|
|
2594
|
-
const
|
|
2595
|
-
let
|
|
2596
|
-
|
|
2597
|
-
|
|
2598
|
-
|
|
2599
|
-
body.push("");
|
|
2600
|
-
continue;
|
|
2601
|
-
}
|
|
2602
|
-
const indentMatch = line.match(/^(\s+)/);
|
|
2603
|
-
const indent = indentMatch ? indentMatch[1].length : 0;
|
|
2604
|
-
if (indent === 0) break;
|
|
2605
|
-
if (blockIndent === -1) blockIndent = indent;
|
|
2606
|
-
if (indent < blockIndent) break;
|
|
2607
|
-
body.push(line.slice(blockIndent));
|
|
2504
|
+
const scripts = pkg.scripts ?? {};
|
|
2505
|
+
let changed = false;
|
|
2506
|
+
if (!scripts.lint) {
|
|
2507
|
+
scripts.lint = "eslint src/";
|
|
2508
|
+
changed = true;
|
|
2608
2509
|
}
|
|
2609
|
-
|
|
2610
|
-
|
|
2611
|
-
|
|
2612
|
-
function extractSection(description, marker) {
|
|
2613
|
-
const markers = ["TRIGGER when:", "SKIP:", "Coordinates with:"];
|
|
2614
|
-
const lines = description.split("\n");
|
|
2615
|
-
let inSection = false;
|
|
2616
|
-
const collected = [];
|
|
2617
|
-
for (const line of lines) {
|
|
2618
|
-
const trimmed = line.trim();
|
|
2619
|
-
const startsWithMarker = trimmed.toLowerCase().startsWith(marker.toLowerCase());
|
|
2620
|
-
if (!inSection && startsWithMarker) {
|
|
2621
|
-
inSection = true;
|
|
2622
|
-
collected.push(trimmed.slice(marker.length).trim());
|
|
2623
|
-
continue;
|
|
2624
|
-
}
|
|
2625
|
-
if (inSection) {
|
|
2626
|
-
const hitNextMarker = markers.some(
|
|
2627
|
-
(m) => m !== marker && trimmed.toLowerCase().startsWith(m.toLowerCase())
|
|
2628
|
-
);
|
|
2629
|
-
if (hitNextMarker) break;
|
|
2630
|
-
if (trimmed === "") {
|
|
2631
|
-
if (collected[collected.length - 1] === "") break;
|
|
2632
|
-
collected.push("");
|
|
2633
|
-
continue;
|
|
2634
|
-
}
|
|
2635
|
-
collected.push(trimmed);
|
|
2636
|
-
}
|
|
2510
|
+
if (!scripts["lint:css"]) {
|
|
2511
|
+
scripts["lint:css"] = "stylelint 'src/**/*.css'";
|
|
2512
|
+
changed = true;
|
|
2637
2513
|
}
|
|
2638
|
-
if (
|
|
2639
|
-
|
|
2640
|
-
|
|
2514
|
+
if (changed) {
|
|
2515
|
+
await backupFile(pkgPath, projectRoot);
|
|
2516
|
+
pkg.scripts = scripts;
|
|
2517
|
+
await writeFileSafe(pkgPath, JSON.stringify(pkg, null, 2) + "\n");
|
|
2518
|
+
logger.debug("Patched package.json scripts with lint / lint:css");
|
|
2519
|
+
}
|
|
2520
|
+
return changed;
|
|
2641
2521
|
}
|
|
2642
2522
|
|
|
2643
2523
|
// src/core/init-detect.ts
|
|
2644
|
-
import * as
|
|
2645
|
-
import * as
|
|
2524
|
+
import * as fs12 from "fs/promises";
|
|
2525
|
+
import * as path14 from "path";
|
|
2646
2526
|
var IGNORED_TOP_LEVEL = /* @__PURE__ */ new Set([
|
|
2647
2527
|
".git",
|
|
2648
2528
|
".gitignore",
|
|
@@ -2662,7 +2542,7 @@ var IGNORED_TOP_LEVEL = /* @__PURE__ */ new Set([
|
|
|
2662
2542
|
"LICENSE.txt"
|
|
2663
2543
|
]);
|
|
2664
2544
|
async function detectProjectState(cwd) {
|
|
2665
|
-
const absCwd =
|
|
2545
|
+
const absCwd = path14.resolve(cwd);
|
|
2666
2546
|
const teamixDir = getTeamixDir(absCwd);
|
|
2667
2547
|
const hasTeamixDir = await fileExists(teamixDir);
|
|
2668
2548
|
if (hasTeamixDir) {
|
|
@@ -2670,13 +2550,13 @@ async function detectProjectState(cwd) {
|
|
|
2670
2550
|
state: "teamix-evo-installed",
|
|
2671
2551
|
cwd: absCwd,
|
|
2672
2552
|
hasTeamixDir: true,
|
|
2673
|
-
hasPackageJson: await fileExists(
|
|
2553
|
+
hasPackageJson: await fileExists(path14.join(absCwd, "package.json")),
|
|
2674
2554
|
significantEntries: []
|
|
2675
2555
|
};
|
|
2676
2556
|
}
|
|
2677
2557
|
let entries;
|
|
2678
2558
|
try {
|
|
2679
|
-
entries = await
|
|
2559
|
+
entries = await fs12.readdir(absCwd);
|
|
2680
2560
|
} catch (err) {
|
|
2681
2561
|
if (err.code === "ENOENT") {
|
|
2682
2562
|
return {
|
|
@@ -2709,9 +2589,151 @@ async function detectProjectState(cwd) {
|
|
|
2709
2589
|
};
|
|
2710
2590
|
}
|
|
2711
2591
|
|
|
2592
|
+
// src/core/project-state.ts
|
|
2593
|
+
import * as fs13 from "fs/promises";
|
|
2594
|
+
import * as path15 from "path";
|
|
2595
|
+
var IGNORED_TOP_LEVEL2 = /* @__PURE__ */ new Set([
|
|
2596
|
+
".git",
|
|
2597
|
+
".gitignore",
|
|
2598
|
+
".gitattributes",
|
|
2599
|
+
".gitkeep",
|
|
2600
|
+
".DS_Store",
|
|
2601
|
+
"Thumbs.db",
|
|
2602
|
+
".idea",
|
|
2603
|
+
".vscode",
|
|
2604
|
+
".qoder",
|
|
2605
|
+
".claude",
|
|
2606
|
+
"README.md",
|
|
2607
|
+
"README",
|
|
2608
|
+
"README.txt",
|
|
2609
|
+
"LICENSE",
|
|
2610
|
+
"LICENSE.md",
|
|
2611
|
+
"LICENSE.txt"
|
|
2612
|
+
]);
|
|
2613
|
+
var SHADCN_TRAIT_KEYS = ["$schema", "style", "rsc", "tsx", "aliases"];
|
|
2614
|
+
async function isShadcnComponentsJson(filePath) {
|
|
2615
|
+
const content = await readFileOrNull(filePath);
|
|
2616
|
+
if (!content) return false;
|
|
2617
|
+
try {
|
|
2618
|
+
const json = JSON.parse(content);
|
|
2619
|
+
return SHADCN_TRAIT_KEYS.some((key) => key in json);
|
|
2620
|
+
} catch {
|
|
2621
|
+
return false;
|
|
2622
|
+
}
|
|
2623
|
+
}
|
|
2624
|
+
async function detectProjectState2(cwd) {
|
|
2625
|
+
const absCwd = path15.resolve(cwd);
|
|
2626
|
+
const teamixDir = getTeamixDir(absCwd);
|
|
2627
|
+
const hasTeamixDir = await fileExists(teamixDir);
|
|
2628
|
+
if (hasTeamixDir) {
|
|
2629
|
+
return {
|
|
2630
|
+
state: "teamix-evo",
|
|
2631
|
+
cwd: absCwd,
|
|
2632
|
+
hasTeamixDir: true,
|
|
2633
|
+
hasPackageJson: await fileExists(path15.join(absCwd, "package.json")),
|
|
2634
|
+
hasComponentsJson: false,
|
|
2635
|
+
significantEntries: []
|
|
2636
|
+
};
|
|
2637
|
+
}
|
|
2638
|
+
const componentsJsonPath = path15.join(absCwd, "components.json");
|
|
2639
|
+
const hasPackageJson = await fileExists(path15.join(absCwd, "package.json"));
|
|
2640
|
+
const hasComponentsJson = await isShadcnComponentsJson(componentsJsonPath);
|
|
2641
|
+
if (hasPackageJson && hasComponentsJson) {
|
|
2642
|
+
return {
|
|
2643
|
+
state: "shadcn",
|
|
2644
|
+
cwd: absCwd,
|
|
2645
|
+
hasTeamixDir: false,
|
|
2646
|
+
hasPackageJson: true,
|
|
2647
|
+
hasComponentsJson: true,
|
|
2648
|
+
significantEntries: []
|
|
2649
|
+
};
|
|
2650
|
+
}
|
|
2651
|
+
let entries;
|
|
2652
|
+
try {
|
|
2653
|
+
entries = await fs13.readdir(absCwd);
|
|
2654
|
+
} catch (err) {
|
|
2655
|
+
if (err.code === "ENOENT") {
|
|
2656
|
+
return {
|
|
2657
|
+
state: "empty",
|
|
2658
|
+
cwd: absCwd,
|
|
2659
|
+
hasTeamixDir: false,
|
|
2660
|
+
hasPackageJson: false,
|
|
2661
|
+
hasComponentsJson: false,
|
|
2662
|
+
significantEntries: []
|
|
2663
|
+
};
|
|
2664
|
+
}
|
|
2665
|
+
throw err;
|
|
2666
|
+
}
|
|
2667
|
+
const significant = entries.filter((e) => !IGNORED_TOP_LEVEL2.has(e));
|
|
2668
|
+
if (significant.length === 0) {
|
|
2669
|
+
return {
|
|
2670
|
+
state: "empty",
|
|
2671
|
+
cwd: absCwd,
|
|
2672
|
+
hasTeamixDir: false,
|
|
2673
|
+
hasPackageJson: false,
|
|
2674
|
+
hasComponentsJson: false,
|
|
2675
|
+
significantEntries: []
|
|
2676
|
+
};
|
|
2677
|
+
}
|
|
2678
|
+
return {
|
|
2679
|
+
state: "other",
|
|
2680
|
+
cwd: absCwd,
|
|
2681
|
+
hasTeamixDir: false,
|
|
2682
|
+
hasPackageJson,
|
|
2683
|
+
hasComponentsJson,
|
|
2684
|
+
significantEntries: significant.slice(0, 20).sort()
|
|
2685
|
+
};
|
|
2686
|
+
}
|
|
2687
|
+
|
|
2688
|
+
// src/core/command-guard.ts
|
|
2689
|
+
var COMMAND_LABELS = {
|
|
2690
|
+
init: "\u521D\u59CB\u5316 Teamix Evo \u5957\u4EF6",
|
|
2691
|
+
migrate: "shadcn \u9879\u76EE\u8FC1\u79FB\u4E3A Teamix Evo \u5957\u4EF6",
|
|
2692
|
+
update: "Teamix Evo \u5957\u4EF6\u66F4\u65B0"
|
|
2693
|
+
};
|
|
2694
|
+
var STATE_LABELS = {
|
|
2695
|
+
empty: "\u7A7A\u76EE\u5F55",
|
|
2696
|
+
shadcn: "shadcn \u5DE5\u7A0B",
|
|
2697
|
+
"teamix-evo": "Teamix Evo \u5DE5\u7A0B",
|
|
2698
|
+
other: "\u666E\u901A npm \u5DE5\u7A0B"
|
|
2699
|
+
};
|
|
2700
|
+
var VALID_STATES = {
|
|
2701
|
+
init: "empty",
|
|
2702
|
+
migrate: "shadcn",
|
|
2703
|
+
update: "teamix-evo"
|
|
2704
|
+
};
|
|
2705
|
+
var STATE_TO_COMMAND = {
|
|
2706
|
+
empty: "init",
|
|
2707
|
+
shadcn: "migrate",
|
|
2708
|
+
"teamix-evo": "update",
|
|
2709
|
+
other: null
|
|
2710
|
+
};
|
|
2711
|
+
function assertCommandPrecondition(command, state) {
|
|
2712
|
+
const expected = VALID_STATES[command];
|
|
2713
|
+
if (state === expected) return;
|
|
2714
|
+
const stateLabel = STATE_LABELS[state];
|
|
2715
|
+
const commandLabel = COMMAND_LABELS[command];
|
|
2716
|
+
logger.error(
|
|
2717
|
+
`\u5F53\u524D\u9879\u76EE\u72B6\u6001\u4E3A\u300C${stateLabel}\u300D\uFF0C\u65E0\u6CD5\u6267\u884C\u300C${commandLabel}\u300D\u3002`
|
|
2718
|
+
);
|
|
2719
|
+
const suggestion = STATE_TO_COMMAND[state];
|
|
2720
|
+
if (suggestion) {
|
|
2721
|
+
const suggestionLabel = COMMAND_LABELS[suggestion];
|
|
2722
|
+
logger.info(`\u5EFA\u8BAE\u4F7F\u7528\uFF1Ateamix-evo ${suggestion}\uFF08${suggestionLabel}\uFF09`);
|
|
2723
|
+
} else {
|
|
2724
|
+
logger.info(
|
|
2725
|
+
"\u5F53\u524D\u9879\u76EE\u7C7B\u578B\u6682\u4E0D\u652F\u6301\u76F4\u63A5\u63A5\u5165\u3002"
|
|
2726
|
+
);
|
|
2727
|
+
logger.info(
|
|
2728
|
+
"\u5EFA\u8BAE\uFF1A\u5148\u7528 teamix-evo init \u521D\u59CB\u5316 Teamix Evo \u5957\u4EF6\uFF08\u7A7A\u76EE\u5F55\uFF09\uFF0C\u7136\u540E\u5BF9\u65E7\u9879\u76EE\u8FDB\u884C\u91CD\u6784\u3002"
|
|
2729
|
+
);
|
|
2730
|
+
}
|
|
2731
|
+
process.exitCode = 1;
|
|
2732
|
+
}
|
|
2733
|
+
|
|
2712
2734
|
// src/core/init-conflicts.ts
|
|
2713
2735
|
import * as crypto from "crypto";
|
|
2714
|
-
import * as
|
|
2736
|
+
import * as fs14 from "fs/promises";
|
|
2715
2737
|
import * as path16 from "path";
|
|
2716
2738
|
var TAILWIND_CONFIG_CANDIDATES = [
|
|
2717
2739
|
"tailwind.config.ts",
|
|
@@ -2756,7 +2778,7 @@ var STYLELINT_CONFIG_CANDIDATES = [
|
|
|
2756
2778
|
];
|
|
2757
2779
|
async function isDir(target) {
|
|
2758
2780
|
try {
|
|
2759
|
-
const stat3 = await
|
|
2781
|
+
const stat3 = await fs14.stat(target);
|
|
2760
2782
|
return stat3.isDirectory();
|
|
2761
2783
|
} catch {
|
|
2762
2784
|
return false;
|
|
@@ -2764,7 +2786,7 @@ async function isDir(target) {
|
|
|
2764
2786
|
}
|
|
2765
2787
|
async function dirHasContent(target) {
|
|
2766
2788
|
try {
|
|
2767
|
-
const entries = await
|
|
2789
|
+
const entries = await fs14.readdir(target);
|
|
2768
2790
|
return entries.length > 0;
|
|
2769
2791
|
} catch {
|
|
2770
2792
|
return false;
|
|
@@ -2905,7 +2927,7 @@ async function detectShadcnSource(cwd) {
|
|
|
2905
2927
|
try {
|
|
2906
2928
|
const uiDir = path16.join(cwd, "src/components/ui");
|
|
2907
2929
|
if (await isDir(uiDir)) {
|
|
2908
|
-
const entries = await
|
|
2930
|
+
const entries = await fs14.readdir(uiDir);
|
|
2909
2931
|
componentCount = entries.filter(
|
|
2910
2932
|
(e) => e.endsWith(".tsx") || e.endsWith(".ts")
|
|
2911
2933
|
).length;
|
|
@@ -2981,19 +3003,97 @@ async function detectStylelintConfig(cwd) {
|
|
|
2981
3003
|
};
|
|
2982
3004
|
}
|
|
2983
3005
|
|
|
2984
|
-
// src/core/
|
|
2985
|
-
import * as fs13 from "fs/promises";
|
|
3006
|
+
// src/core/meta-installer.ts
|
|
2986
3007
|
import * as path17 from "path";
|
|
3008
|
+
import * as fs15 from "fs/promises";
|
|
3009
|
+
import { createRequire as createRequire5 } from "module";
|
|
3010
|
+
import {
|
|
3011
|
+
loadUiPackageManifest as loadUiPackageManifest3,
|
|
3012
|
+
loadVariantUiPackageManifest as loadVariantUiPackageManifest2
|
|
3013
|
+
} from "@teamix-evo/registry";
|
|
3014
|
+
var require6 = createRequire5(import.meta.url);
|
|
3015
|
+
var META_DIR = "meta";
|
|
3016
|
+
function resolvePackageRoot4(packageName) {
|
|
3017
|
+
const pkgJsonPath = require6.resolve(`${packageName}/package.json`);
|
|
3018
|
+
return path17.dirname(pkgJsonPath);
|
|
3019
|
+
}
|
|
3020
|
+
function getMetaDir(projectRoot, category) {
|
|
3021
|
+
const base = path17.join(getTeamixDir(projectRoot), META_DIR);
|
|
3022
|
+
return category ? path17.join(base, category) : base;
|
|
3023
|
+
}
|
|
3024
|
+
async function landUiMeta(projectRoot) {
|
|
3025
|
+
const packageRoot = resolvePackageRoot4("@teamix-evo/ui");
|
|
3026
|
+
const manifest = await loadUiPackageManifest3(packageRoot);
|
|
3027
|
+
return landManifestMeta({
|
|
3028
|
+
projectRoot,
|
|
3029
|
+
packageRoot,
|
|
3030
|
+
manifest,
|
|
3031
|
+
category: "ui"
|
|
3032
|
+
});
|
|
3033
|
+
}
|
|
3034
|
+
async function landBizUiMeta(projectRoot, variant) {
|
|
3035
|
+
const packageRoot = resolvePackageRoot4("@teamix-evo/biz-ui");
|
|
3036
|
+
const variantRoot = path17.join(packageRoot, "variants", variant);
|
|
3037
|
+
const manifest = await loadVariantUiPackageManifest2(variantRoot);
|
|
3038
|
+
return landManifestMeta({
|
|
3039
|
+
projectRoot,
|
|
3040
|
+
packageRoot: variantRoot,
|
|
3041
|
+
manifest,
|
|
3042
|
+
category: "biz-ui"
|
|
3043
|
+
});
|
|
3044
|
+
}
|
|
3045
|
+
async function landManifestMeta(opts) {
|
|
3046
|
+
const { projectRoot, packageRoot, manifest, category } = opts;
|
|
3047
|
+
await ensureTeamixDir(projectRoot);
|
|
3048
|
+
const metaDir = getMetaDir(projectRoot, category);
|
|
3049
|
+
await ensureDir(metaDir);
|
|
3050
|
+
const manifestDest = path17.join(metaDir, "manifest.json");
|
|
3051
|
+
const manifestSrc = path17.join(packageRoot, "manifest.json");
|
|
3052
|
+
await fs15.copyFile(manifestSrc, manifestDest);
|
|
3053
|
+
logger.debug(`meta: copied manifest.json \u2192 ${manifestDest}`);
|
|
3054
|
+
let written = 0;
|
|
3055
|
+
let total = 0;
|
|
3056
|
+
for (const entry of manifest.entries) {
|
|
3057
|
+
if (!entry.meta) continue;
|
|
3058
|
+
total++;
|
|
3059
|
+
const srcPath = path17.join(packageRoot, entry.meta);
|
|
3060
|
+
const destPath = path17.join(metaDir, `${entry.id}.md`);
|
|
3061
|
+
try {
|
|
3062
|
+
await fs15.copyFile(srcPath, destPath);
|
|
3063
|
+
written++;
|
|
3064
|
+
logger.debug(`meta: ${entry.id} \u2192 ${destPath}`);
|
|
3065
|
+
} catch (err) {
|
|
3066
|
+
if (err.code === "ENOENT") {
|
|
3067
|
+
logger.warn(`meta: ${entry.id} \u2014 source not found: ${srcPath}`);
|
|
3068
|
+
} else {
|
|
3069
|
+
throw err;
|
|
3070
|
+
}
|
|
3071
|
+
}
|
|
3072
|
+
}
|
|
3073
|
+
logger.info(
|
|
3074
|
+
` meta/${category}: ${written}/${total} meta files, manifest.json`
|
|
3075
|
+
);
|
|
3076
|
+
return {
|
|
3077
|
+
category,
|
|
3078
|
+
manifestWritten: true,
|
|
3079
|
+
metaFilesWritten: written,
|
|
3080
|
+
metaFilesTotal: total
|
|
3081
|
+
};
|
|
3082
|
+
}
|
|
3083
|
+
|
|
3084
|
+
// src/core/deps-install.ts
|
|
3085
|
+
import * as fs16 from "fs/promises";
|
|
3086
|
+
import * as path18 from "path";
|
|
2987
3087
|
import { exec } from "child_process";
|
|
2988
3088
|
import { promisify } from "util";
|
|
2989
3089
|
var execAsync = promisify(exec);
|
|
2990
3090
|
async function detectPackageManager(projectRoot) {
|
|
2991
|
-
if (await fileExists(
|
|
2992
|
-
if (await fileExists(
|
|
3091
|
+
if (await fileExists(path18.join(projectRoot, "pnpm-lock.yaml"))) return "pnpm";
|
|
3092
|
+
if (await fileExists(path18.join(projectRoot, "pnpm-workspace.yaml")))
|
|
2993
3093
|
return "pnpm";
|
|
2994
|
-
if (await fileExists(
|
|
2995
|
-
if (await fileExists(
|
|
2996
|
-
if (await fileExists(
|
|
3094
|
+
if (await fileExists(path18.join(projectRoot, "bun.lockb"))) return "bun";
|
|
3095
|
+
if (await fileExists(path18.join(projectRoot, "bun.lock"))) return "bun";
|
|
3096
|
+
if (await fileExists(path18.join(projectRoot, "yarn.lock"))) return "yarn";
|
|
2997
3097
|
return "npm";
|
|
2998
3098
|
}
|
|
2999
3099
|
function getInstallCommand(pm) {
|
|
@@ -3010,7 +3110,7 @@ function getInstallCommand(pm) {
|
|
|
3010
3110
|
}
|
|
3011
3111
|
async function installProjectDeps(options) {
|
|
3012
3112
|
const { projectRoot, npmDependencies, skipInstall = false } = options;
|
|
3013
|
-
const pkgPath =
|
|
3113
|
+
const pkgPath = path18.join(projectRoot, "package.json");
|
|
3014
3114
|
const raw = await readFileOrNull(pkgPath);
|
|
3015
3115
|
if (!raw) {
|
|
3016
3116
|
throw new Error(
|
|
@@ -3039,7 +3139,7 @@ async function installProjectDeps(options) {
|
|
|
3039
3139
|
pkg.dependencies = Object.fromEntries(
|
|
3040
3140
|
Object.entries(pkg.dependencies).sort(([a], [b]) => a.localeCompare(b))
|
|
3041
3141
|
);
|
|
3042
|
-
await
|
|
3142
|
+
await fs16.writeFile(pkgPath, JSON.stringify(pkg, null, 2) + "\n", "utf-8");
|
|
3043
3143
|
logger.info(
|
|
3044
3144
|
` patched package.json: +${Object.keys(added).length} dependencies`
|
|
3045
3145
|
);
|
|
@@ -3101,19 +3201,19 @@ ${stepLines}
|
|
|
3101
3201
|
}
|
|
3102
3202
|
|
|
3103
3203
|
// src/core/file-changes.ts
|
|
3104
|
-
import * as
|
|
3105
|
-
import * as
|
|
3204
|
+
import * as fs17 from "fs/promises";
|
|
3205
|
+
import * as path19 from "path";
|
|
3106
3206
|
function toRelativePosix(p, projectRoot) {
|
|
3107
3207
|
let rel2 = p;
|
|
3108
|
-
if (
|
|
3109
|
-
rel2 =
|
|
3208
|
+
if (path19.isAbsolute(p)) {
|
|
3209
|
+
rel2 = path19.relative(projectRoot, p);
|
|
3110
3210
|
}
|
|
3111
|
-
return rel2.split(
|
|
3211
|
+
return rel2.split(path19.sep).join("/");
|
|
3112
3212
|
}
|
|
3113
3213
|
|
|
3114
3214
|
// src/core/project-init.ts
|
|
3115
3215
|
import * as fsNode from "fs/promises";
|
|
3116
|
-
import * as
|
|
3216
|
+
import * as path20 from "path";
|
|
3117
3217
|
var CRITICAL_STEPS = /* @__PURE__ */ new Set([
|
|
3118
3218
|
"tokens",
|
|
3119
3219
|
"skills",
|
|
@@ -3124,7 +3224,6 @@ var GITIGNORE_MARKER_END = "# <<< teamix-evo:managed <<<";
|
|
|
3124
3224
|
var GITIGNORE_RULES = [
|
|
3125
3225
|
"# Runtime artifacts (regenerable / archives)",
|
|
3126
3226
|
".teamix-evo/.snapshots/",
|
|
3127
|
-
".teamix-evo/logs/",
|
|
3128
3227
|
".teamix-evo/.backups/",
|
|
3129
3228
|
".teamix-evo/.upgrade-staging/",
|
|
3130
3229
|
".teamix-evo/.upgrade-hints/"
|
|
@@ -3206,7 +3305,7 @@ async function runProjectInit(options) {
|
|
|
3206
3305
|
detail: `${result.skillCount} skills, ${result.fileCount} files (added: ${result.addedSkillIds.join(", ") || "none"})`,
|
|
3207
3306
|
changes: result.addedSkillIds.map((id) => ({
|
|
3208
3307
|
kind: "created",
|
|
3209
|
-
path:
|
|
3308
|
+
path: `.${ides[0] ?? "qoder"}/skills/${id}/SKILL.md`,
|
|
3210
3309
|
step: "skills",
|
|
3211
3310
|
detail: "skill installed"
|
|
3212
3311
|
}))
|
|
@@ -3363,6 +3462,31 @@ async function runProjectInit(options) {
|
|
|
3363
3462
|
recordFailure("biz-ui-add", err);
|
|
3364
3463
|
}
|
|
3365
3464
|
}
|
|
3465
|
+
if (dryRun) {
|
|
3466
|
+
record({
|
|
3467
|
+
name: "meta-landing",
|
|
3468
|
+
status: "planned",
|
|
3469
|
+
detail: `landUiMeta() + landBizUiMeta(${variant})`
|
|
3470
|
+
});
|
|
3471
|
+
} else if (aborted) {
|
|
3472
|
+
record({
|
|
3473
|
+
name: "meta-landing",
|
|
3474
|
+
status: "skip",
|
|
3475
|
+
detail: "aborted: earlier critical step failed"
|
|
3476
|
+
});
|
|
3477
|
+
} else {
|
|
3478
|
+
try {
|
|
3479
|
+
const uiResult = await landUiMeta(projectRoot);
|
|
3480
|
+
const bizResult = await landBizUiMeta(projectRoot, variant);
|
|
3481
|
+
record({
|
|
3482
|
+
name: "meta-landing",
|
|
3483
|
+
status: "ok",
|
|
3484
|
+
detail: `ui: ${uiResult.metaFilesWritten}/${uiResult.metaFilesTotal}, biz-ui: ${bizResult.metaFilesWritten}/${bizResult.metaFilesTotal}`
|
|
3485
|
+
});
|
|
3486
|
+
} catch (err) {
|
|
3487
|
+
recordFailure("meta-landing", err);
|
|
3488
|
+
}
|
|
3489
|
+
}
|
|
3366
3490
|
if (!dryRun && !aborted && Object.keys(collectedNpmDeps).length > 0) {
|
|
3367
3491
|
try {
|
|
3368
3492
|
await installProjectDeps({
|
|
@@ -3430,7 +3554,7 @@ async function runProjectInit(options) {
|
|
|
3430
3554
|
detail: "projectRoot does not exist"
|
|
3431
3555
|
});
|
|
3432
3556
|
} else {
|
|
3433
|
-
const giPath =
|
|
3557
|
+
const giPath = path20.join(projectRoot, ".gitignore");
|
|
3434
3558
|
let giContent = "";
|
|
3435
3559
|
try {
|
|
3436
3560
|
giContent = await fsNode.readFile(giPath, "utf-8");
|
|
@@ -3486,15 +3610,16 @@ async function runProjectInit(options) {
|
|
|
3486
3610
|
resumeCommand: "teamix-evo init"
|
|
3487
3611
|
};
|
|
3488
3612
|
}
|
|
3489
|
-
|
|
3613
|
+
const hasFailures = steps.some((s) => s.status === "fail" || s.status === "skip");
|
|
3614
|
+
if (!dryRun && hasFailures) {
|
|
3490
3615
|
try {
|
|
3491
3616
|
const checklistContent = renderInitChecklist({ variant, status, steps });
|
|
3492
|
-
const checklistPath =
|
|
3617
|
+
const checklistPath = path20.join(
|
|
3493
3618
|
projectRoot,
|
|
3494
3619
|
".teamix-evo",
|
|
3495
3620
|
"init-checklist.md"
|
|
3496
3621
|
);
|
|
3497
|
-
await fsNode.mkdir(
|
|
3622
|
+
await fsNode.mkdir(path20.dirname(checklistPath), { recursive: true });
|
|
3498
3623
|
await fsNode.writeFile(checklistPath, checklistContent, "utf-8");
|
|
3499
3624
|
logger.info(" wrote .teamix-evo/init-checklist.md");
|
|
3500
3625
|
} catch {
|
|
@@ -3534,8 +3659,8 @@ function deriveLintChanges(result) {
|
|
|
3534
3659
|
}
|
|
3535
3660
|
|
|
3536
3661
|
// src/core/installer.ts
|
|
3537
|
-
import * as
|
|
3538
|
-
import * as
|
|
3662
|
+
import * as path21 from "path";
|
|
3663
|
+
import * as fs18 from "fs/promises";
|
|
3539
3664
|
async function installResources(options) {
|
|
3540
3665
|
const { projectRoot, manifest, data, variantDir, packageRoot } = options;
|
|
3541
3666
|
const installedResources = [];
|
|
@@ -3572,13 +3697,13 @@ async function installSingleResource(resource, projectRoot, data, variantDir, pa
|
|
|
3572
3697
|
variantDir,
|
|
3573
3698
|
packageRoot
|
|
3574
3699
|
);
|
|
3575
|
-
const targetPath =
|
|
3700
|
+
const targetPath = path21.join(projectRoot, resource.target);
|
|
3576
3701
|
let content;
|
|
3577
3702
|
if (resource.template) {
|
|
3578
3703
|
const templateContent = await loadTemplateFile(sourcePath);
|
|
3579
3704
|
content = renderTemplate(templateContent, data);
|
|
3580
3705
|
} else {
|
|
3581
|
-
content = await
|
|
3706
|
+
content = await fs18.readFile(sourcePath, "utf-8");
|
|
3582
3707
|
}
|
|
3583
3708
|
await writeFileSafe(targetPath, content);
|
|
3584
3709
|
const hash = computeHash(content);
|
|
@@ -3596,13 +3721,13 @@ async function installRecursiveResource(resource, projectRoot, data, variantDir,
|
|
|
3596
3721
|
variantDir,
|
|
3597
3722
|
packageRoot
|
|
3598
3723
|
);
|
|
3599
|
-
const targetDir =
|
|
3724
|
+
const targetDir = path21.join(projectRoot, resource.target);
|
|
3600
3725
|
const results = [];
|
|
3601
3726
|
await ensureDir(targetDir);
|
|
3602
3727
|
const entries = await walkDir(sourcePath);
|
|
3603
3728
|
for (const entry of entries) {
|
|
3604
|
-
const relPath =
|
|
3605
|
-
let targetFile =
|
|
3729
|
+
const relPath = path21.relative(sourcePath, entry);
|
|
3730
|
+
let targetFile = path21.join(targetDir, relPath);
|
|
3606
3731
|
if (resource.template && targetFile.endsWith(".hbs")) {
|
|
3607
3732
|
targetFile = targetFile.slice(0, -4);
|
|
3608
3733
|
}
|
|
@@ -3611,11 +3736,11 @@ async function installRecursiveResource(resource, projectRoot, data, variantDir,
|
|
|
3611
3736
|
const templateContent = await loadTemplateFile(entry);
|
|
3612
3737
|
content = renderTemplate(templateContent, data);
|
|
3613
3738
|
} else {
|
|
3614
|
-
content = await
|
|
3739
|
+
content = await fs18.readFile(entry, "utf-8");
|
|
3615
3740
|
}
|
|
3616
3741
|
await writeFileSafe(targetFile, content);
|
|
3617
3742
|
const hash = computeHash(content);
|
|
3618
|
-
const targetRel =
|
|
3743
|
+
const targetRel = path21.relative(projectRoot, targetFile);
|
|
3619
3744
|
results.push({
|
|
3620
3745
|
id: `${resource.id}:${relPath}`,
|
|
3621
3746
|
target: targetRel,
|
|
@@ -3628,25 +3753,25 @@ async function installRecursiveResource(resource, projectRoot, data, variantDir,
|
|
|
3628
3753
|
}
|
|
3629
3754
|
|
|
3630
3755
|
// src/core/registry-client.ts
|
|
3631
|
-
import * as
|
|
3632
|
-
import * as
|
|
3633
|
-
import { createRequire as
|
|
3756
|
+
import * as path22 from "path";
|
|
3757
|
+
import * as fs19 from "fs/promises";
|
|
3758
|
+
import { createRequire as createRequire6 } from "module";
|
|
3634
3759
|
import { loadVariantManifest } from "@teamix-evo/registry";
|
|
3635
|
-
var
|
|
3636
|
-
function
|
|
3637
|
-
const pkgJsonPath =
|
|
3638
|
-
return
|
|
3760
|
+
var require7 = createRequire6(import.meta.url);
|
|
3761
|
+
function resolvePackageRoot5(packageName) {
|
|
3762
|
+
const pkgJsonPath = require7.resolve(`${packageName}/package.json`);
|
|
3763
|
+
return path22.dirname(pkgJsonPath);
|
|
3639
3764
|
}
|
|
3640
3765
|
async function loadVariantData(packageName, variant) {
|
|
3641
|
-
const packageRoot =
|
|
3642
|
-
const variantDir =
|
|
3766
|
+
const packageRoot = resolvePackageRoot5(packageName);
|
|
3767
|
+
const variantDir = path22.join(packageRoot, "library", variant);
|
|
3643
3768
|
logger.debug(`Resolved variant dir: ${variantDir}`);
|
|
3644
3769
|
logger.debug(`Package root: ${packageRoot}`);
|
|
3645
3770
|
const manifest = await loadVariantManifest(variantDir);
|
|
3646
3771
|
let data = {};
|
|
3647
|
-
const dataPath =
|
|
3772
|
+
const dataPath = path22.join(variantDir, "_data.json");
|
|
3648
3773
|
try {
|
|
3649
|
-
const raw = await
|
|
3774
|
+
const raw = await fs19.readFile(dataPath, "utf-8");
|
|
3650
3775
|
data = JSON.parse(raw);
|
|
3651
3776
|
} catch (err) {
|
|
3652
3777
|
if (err.code !== "ENOENT") {
|
|
@@ -3659,8 +3784,10 @@ async function loadVariantData(packageName, variant) {
|
|
|
3659
3784
|
export {
|
|
3660
3785
|
DEFAULT_UI_ALIASES,
|
|
3661
3786
|
DEFAULT_UI_ICON_LIBRARY,
|
|
3787
|
+
assertCommandPrecondition,
|
|
3662
3788
|
detectConflicts,
|
|
3663
|
-
detectProjectState,
|
|
3789
|
+
detectProjectState2 as detectProjectState,
|
|
3790
|
+
detectProjectState as detectProjectStateLegacy,
|
|
3664
3791
|
ensureTeamixDir,
|
|
3665
3792
|
extractDescriptionParts,
|
|
3666
3793
|
getTeamixDir,
|
|
@@ -3669,14 +3796,13 @@ export {
|
|
|
3669
3796
|
installUiEntries,
|
|
3670
3797
|
listBizUiEntries,
|
|
3671
3798
|
listBizUiVariants,
|
|
3672
|
-
listTemplatesEntries,
|
|
3673
|
-
listTemplatesVariants,
|
|
3674
3799
|
listTokenVariants,
|
|
3675
3800
|
loadSkillsData,
|
|
3676
3801
|
loadUiData,
|
|
3677
3802
|
loadVariantData,
|
|
3678
3803
|
readInstalledManifest,
|
|
3679
3804
|
readProjectConfig,
|
|
3805
|
+
reinstallSkillsToIdes,
|
|
3680
3806
|
removeSkillFiles,
|
|
3681
3807
|
removeUiFiles,
|
|
3682
3808
|
runBizUiAdd,
|
|
@@ -3686,12 +3812,10 @@ export {
|
|
|
3686
3812
|
runSkillsAdd,
|
|
3687
3813
|
runSkillsInit,
|
|
3688
3814
|
runSkillsUpdate,
|
|
3689
|
-
runTemplatesAdd,
|
|
3690
3815
|
runTokensInit,
|
|
3691
3816
|
runUiAdd,
|
|
3692
3817
|
runUiInit,
|
|
3693
3818
|
runUiList,
|
|
3694
|
-
syncSkillsToIdes,
|
|
3695
3819
|
updateSkills,
|
|
3696
3820
|
writeInstalledManifest,
|
|
3697
3821
|
writeProjectConfig
|