teamix-evo 0.3.1 → 0.4.1
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 +32 -31
- package/dist/core/index.d.ts +71 -29
- package/dist/core/index.js +357 -255
- package/dist/core/index.js.map +1 -1
- package/dist/index.js +1394 -480
- package/dist/index.js.map +1 -1
- package/package.json +6 -6
package/dist/core/index.js
CHANGED
|
@@ -1,11 +1,10 @@
|
|
|
1
|
-
// src/core/
|
|
1
|
+
// src/core/tokens-init.ts
|
|
2
2
|
import * as path9 from "path";
|
|
3
3
|
import * as fs6 from "fs/promises";
|
|
4
4
|
import { createRequire as createRequire2 } from "module";
|
|
5
5
|
import {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
mergeDefaultAndVariant
|
|
6
|
+
loadTokensPackageManifest,
|
|
7
|
+
getVariantEntry
|
|
9
8
|
} from "@teamix-evo/registry";
|
|
10
9
|
|
|
11
10
|
// src/utils/fs.ts
|
|
@@ -90,71 +89,22 @@ function computeHash(content) {
|
|
|
90
89
|
return `sha256:${hash}`;
|
|
91
90
|
}
|
|
92
91
|
|
|
93
|
-
// src/core/design-pack-classify.ts
|
|
94
|
-
import * as path2 from "path";
|
|
95
|
-
var TEAMIX_DIR = ".teamix-evo/design";
|
|
96
|
-
var TOKENS_DIR = ".teamix-evo/tokens";
|
|
97
|
-
var TOKENS_PACK_PREFIX = "foundations/tokens/";
|
|
98
|
-
var ROOT_MANAGED_FILES = {
|
|
99
|
-
"DESIGN.md": { target: "DESIGN.md", managedRegions: ["core"] },
|
|
100
|
-
"AGENTS.md": { target: "AGENTS.md", managedRegions: ["teamix-evo"] },
|
|
101
|
-
"CLAUDE.md": { target: "CLAUDE.md", managedRegions: ["teamix-evo"] }
|
|
102
|
-
};
|
|
103
|
-
var FROZEN_FILES = /* @__PURE__ */ new Set([
|
|
104
|
-
"foundations/tokens/tokens.overrides.css"
|
|
105
|
-
]);
|
|
106
|
-
function classifyPackFile(relPath) {
|
|
107
|
-
if (path2.basename(relPath) === "README.md") {
|
|
108
|
-
return null;
|
|
109
|
-
}
|
|
110
|
-
const rootManaged = ROOT_MANAGED_FILES[relPath];
|
|
111
|
-
if (rootManaged) {
|
|
112
|
-
return {
|
|
113
|
-
target: rootManaged.target,
|
|
114
|
-
strategy: "managed",
|
|
115
|
-
managedRegions: rootManaged.managedRegions,
|
|
116
|
-
isFrozen: false
|
|
117
|
-
};
|
|
118
|
-
}
|
|
119
|
-
if (relPath.startsWith(TOKENS_PACK_PREFIX)) {
|
|
120
|
-
const rel2 = relPath.slice(TOKENS_PACK_PREFIX.length);
|
|
121
|
-
const target = path2.posix.join(TOKENS_DIR, rel2);
|
|
122
|
-
if (FROZEN_FILES.has(relPath)) {
|
|
123
|
-
return { target, strategy: "frozen", isFrozen: true };
|
|
124
|
-
}
|
|
125
|
-
return { target, strategy: "regenerable", isFrozen: false };
|
|
126
|
-
}
|
|
127
|
-
if (FROZEN_FILES.has(relPath)) {
|
|
128
|
-
return {
|
|
129
|
-
target: path2.posix.join(TEAMIX_DIR, relPath),
|
|
130
|
-
strategy: "frozen",
|
|
131
|
-
isFrozen: true
|
|
132
|
-
};
|
|
133
|
-
}
|
|
134
|
-
return {
|
|
135
|
-
target: path2.posix.join(TEAMIX_DIR, relPath),
|
|
136
|
-
strategy: "regenerable",
|
|
137
|
-
isFrozen: false
|
|
138
|
-
};
|
|
139
|
-
}
|
|
140
|
-
|
|
141
92
|
// src/core/state.ts
|
|
142
|
-
import * as
|
|
93
|
+
import * as path2 from "path";
|
|
143
94
|
import {
|
|
144
95
|
validateConfig,
|
|
145
96
|
validateInstalled,
|
|
146
97
|
validateSkillsLock,
|
|
147
|
-
|
|
98
|
+
TokensPackLockSchema
|
|
148
99
|
} from "@teamix-evo/registry";
|
|
149
|
-
var
|
|
100
|
+
var TEAMIX_DIR = ".teamix-evo";
|
|
150
101
|
var CONFIG_FILE = "config.json";
|
|
151
102
|
var MANIFEST_FILE = "manifest.json";
|
|
152
|
-
var
|
|
153
|
-
var DESIGN_LOCK_FILE = "pack.lock.json";
|
|
103
|
+
var TOKENS_LOCK_FILE = "tokens-lock.json";
|
|
154
104
|
var SKILLS_DIR = "skills";
|
|
155
105
|
var SKILLS_LOCK_FILE = "manifest.lock.json";
|
|
156
106
|
function getTeamixDir(projectRoot) {
|
|
157
|
-
return
|
|
107
|
+
return path2.join(projectRoot, TEAMIX_DIR);
|
|
158
108
|
}
|
|
159
109
|
async function ensureTeamixDir(projectRoot) {
|
|
160
110
|
const dir = getTeamixDir(projectRoot);
|
|
@@ -162,84 +112,85 @@ async function ensureTeamixDir(projectRoot) {
|
|
|
162
112
|
return dir;
|
|
163
113
|
}
|
|
164
114
|
async function readProjectConfig(projectRoot) {
|
|
165
|
-
const configPath =
|
|
115
|
+
const configPath = path2.join(projectRoot, TEAMIX_DIR, CONFIG_FILE);
|
|
166
116
|
const raw = await readFileOrNull(configPath);
|
|
167
117
|
if (raw === null) return null;
|
|
118
|
+
let data;
|
|
168
119
|
try {
|
|
169
|
-
|
|
170
|
-
const result = validateConfig(data);
|
|
171
|
-
if (!result.success) {
|
|
172
|
-
logger.warn(`Invalid config.json: ${result.error}`);
|
|
173
|
-
return null;
|
|
174
|
-
}
|
|
175
|
-
return result.data;
|
|
120
|
+
data = JSON.parse(raw);
|
|
176
121
|
} catch (err) {
|
|
177
|
-
|
|
178
|
-
|
|
122
|
+
throw new Error(
|
|
123
|
+
`Corrupted config.json (${err.message}). Fix the JSON manually or remove the file to start fresh; refusing to clobber prior config.`
|
|
124
|
+
);
|
|
179
125
|
}
|
|
126
|
+
const result = validateConfig(data);
|
|
127
|
+
if (!result.success) {
|
|
128
|
+
throw new Error(
|
|
129
|
+
`Invalid config.json schema: ${result.error}. Fix the file manually or remove it to start fresh.`
|
|
130
|
+
);
|
|
131
|
+
}
|
|
132
|
+
return result.data;
|
|
180
133
|
}
|
|
181
134
|
async function writeProjectConfig(projectRoot, config) {
|
|
182
|
-
const configPath =
|
|
135
|
+
const configPath = path2.join(projectRoot, TEAMIX_DIR, CONFIG_FILE);
|
|
183
136
|
await writeFileSafe(configPath, JSON.stringify(config, null, 2) + "\n");
|
|
184
137
|
logger.debug(`Wrote config \u2192 ${configPath}`);
|
|
185
138
|
}
|
|
186
139
|
async function readInstalledManifest(projectRoot) {
|
|
187
|
-
const manifestPath =
|
|
140
|
+
const manifestPath = path2.join(projectRoot, TEAMIX_DIR, MANIFEST_FILE);
|
|
188
141
|
const raw = await readFileOrNull(manifestPath);
|
|
189
142
|
if (raw === null) return null;
|
|
143
|
+
let data;
|
|
190
144
|
try {
|
|
191
|
-
|
|
192
|
-
const result = validateInstalled(data);
|
|
193
|
-
if (!result.success) {
|
|
194
|
-
logger.warn(`Invalid manifest.json: ${result.error}`);
|
|
195
|
-
return null;
|
|
196
|
-
}
|
|
197
|
-
return result.data;
|
|
145
|
+
data = JSON.parse(raw);
|
|
198
146
|
} catch (err) {
|
|
199
|
-
|
|
200
|
-
|
|
147
|
+
throw new Error(
|
|
148
|
+
`Corrupted manifest.json (${err.message}). Fix the JSON manually or remove the file to start fresh; refusing to clobber prior install records.`
|
|
149
|
+
);
|
|
201
150
|
}
|
|
151
|
+
const result = validateInstalled(data);
|
|
152
|
+
if (!result.success) {
|
|
153
|
+
throw new Error(
|
|
154
|
+
`Invalid manifest.json schema: ${result.error}. Fix the file manually or remove it to start fresh.`
|
|
155
|
+
);
|
|
156
|
+
}
|
|
157
|
+
return result.data;
|
|
202
158
|
}
|
|
203
159
|
async function writeInstalledManifest(projectRoot, manifest) {
|
|
204
|
-
const manifestPath =
|
|
160
|
+
const manifestPath = path2.join(projectRoot, TEAMIX_DIR, MANIFEST_FILE);
|
|
205
161
|
await writeFileSafe(manifestPath, JSON.stringify(manifest, null, 2) + "\n");
|
|
206
162
|
logger.debug(`Wrote manifest \u2192 ${manifestPath}`);
|
|
207
163
|
}
|
|
208
|
-
async function
|
|
209
|
-
const lockPath =
|
|
210
|
-
projectRoot,
|
|
211
|
-
TEAMIX_DIR2,
|
|
212
|
-
DESIGN_DIR,
|
|
213
|
-
DESIGN_LOCK_FILE
|
|
214
|
-
);
|
|
164
|
+
async function readTokensLock(projectRoot) {
|
|
165
|
+
const lockPath = path2.join(projectRoot, TEAMIX_DIR, TOKENS_LOCK_FILE);
|
|
215
166
|
const raw = await readFileOrNull(lockPath);
|
|
216
167
|
if (raw === null) return null;
|
|
217
168
|
try {
|
|
218
|
-
const parsed =
|
|
169
|
+
const parsed = TokensPackLockSchema.safeParse(JSON.parse(raw));
|
|
219
170
|
if (!parsed.success) {
|
|
220
|
-
logger.warn(`Invalid
|
|
171
|
+
logger.warn(`Invalid tokens-lock.json: ${parsed.error.message}`);
|
|
221
172
|
return null;
|
|
222
173
|
}
|
|
223
174
|
return parsed.data;
|
|
224
175
|
} catch (err) {
|
|
225
176
|
logger.warn(
|
|
226
|
-
`Failed to parse
|
|
177
|
+
`Failed to parse tokens-lock.json: ${err.message}`
|
|
227
178
|
);
|
|
228
179
|
return null;
|
|
229
180
|
}
|
|
230
181
|
}
|
|
231
|
-
async function
|
|
232
|
-
const lock = await
|
|
182
|
+
async function readTokensVariant(projectRoot) {
|
|
183
|
+
const lock = await readTokensLock(projectRoot);
|
|
233
184
|
return lock?.variant.name ?? null;
|
|
234
185
|
}
|
|
235
186
|
function getSkillsSourceDir(projectRoot, skillName) {
|
|
236
|
-
const base =
|
|
237
|
-
return skillName ?
|
|
187
|
+
const base = path2.join(projectRoot, TEAMIX_DIR, SKILLS_DIR);
|
|
188
|
+
return skillName ? path2.join(base, skillName) : base;
|
|
238
189
|
}
|
|
239
190
|
async function readSkillsLock(projectRoot) {
|
|
240
|
-
const lockPath =
|
|
191
|
+
const lockPath = path2.join(
|
|
241
192
|
projectRoot,
|
|
242
|
-
|
|
193
|
+
TEAMIX_DIR,
|
|
243
194
|
SKILLS_DIR,
|
|
244
195
|
SKILLS_LOCK_FILE
|
|
245
196
|
);
|
|
@@ -261,9 +212,9 @@ async function readSkillsLock(projectRoot) {
|
|
|
261
212
|
}
|
|
262
213
|
}
|
|
263
214
|
async function writeSkillsLock(projectRoot, lock) {
|
|
264
|
-
const lockPath =
|
|
215
|
+
const lockPath = path2.join(
|
|
265
216
|
projectRoot,
|
|
266
|
-
|
|
217
|
+
TEAMIX_DIR,
|
|
267
218
|
SKILLS_DIR,
|
|
268
219
|
SKILLS_LOCK_FILE
|
|
269
220
|
);
|
|
@@ -272,21 +223,21 @@ async function writeSkillsLock(projectRoot, lock) {
|
|
|
272
223
|
}
|
|
273
224
|
|
|
274
225
|
// src/core/skills-client.ts
|
|
275
|
-
import * as
|
|
226
|
+
import * as path3 from "path";
|
|
276
227
|
import * as fs2 from "fs/promises";
|
|
277
228
|
import { createRequire } from "module";
|
|
278
229
|
import { loadSkillsPackageManifest } from "@teamix-evo/registry";
|
|
279
230
|
var require2 = createRequire(import.meta.url);
|
|
280
231
|
function resolvePackageRoot(packageName) {
|
|
281
232
|
const pkgJsonPath = require2.resolve(`${packageName}/package.json`);
|
|
282
|
-
return
|
|
233
|
+
return path3.dirname(pkgJsonPath);
|
|
283
234
|
}
|
|
284
235
|
async function loadSkillsData(packageName) {
|
|
285
236
|
const packageRoot = resolvePackageRoot(packageName);
|
|
286
237
|
logger.debug(`Resolved skills package root: ${packageRoot}`);
|
|
287
238
|
const manifest = await loadSkillsPackageManifest(packageRoot);
|
|
288
239
|
let data = {};
|
|
289
|
-
const dataPath =
|
|
240
|
+
const dataPath = path3.join(packageRoot, "_data.json");
|
|
290
241
|
try {
|
|
291
242
|
const raw = await fs2.readFile(dataPath, "utf-8");
|
|
292
243
|
data = JSON.parse(raw);
|
|
@@ -300,13 +251,16 @@ async function loadSkillsData(packageName) {
|
|
|
300
251
|
}
|
|
301
252
|
|
|
302
253
|
// src/core/skills-installer.ts
|
|
303
|
-
import * as
|
|
254
|
+
import * as path7 from "path";
|
|
304
255
|
import * as fs5 from "fs/promises";
|
|
305
|
-
import {
|
|
256
|
+
import {
|
|
257
|
+
replaceManagedRegion,
|
|
258
|
+
hasManagedRegion
|
|
259
|
+
} from "@teamix-evo/registry";
|
|
306
260
|
|
|
307
261
|
// src/ide/QoderAdapter.ts
|
|
308
262
|
import * as os from "os";
|
|
309
|
-
import * as
|
|
263
|
+
import * as path4 from "path";
|
|
310
264
|
var QoderAdapter = class {
|
|
311
265
|
kind = "qoder";
|
|
312
266
|
name = "qoder";
|
|
@@ -317,14 +271,14 @@ var QoderAdapter = class {
|
|
|
317
271
|
return true;
|
|
318
272
|
}
|
|
319
273
|
getSkillTargetDir(skillName, scope, projectRoot) {
|
|
320
|
-
const base = scope === "global" ?
|
|
321
|
-
return
|
|
274
|
+
const base = scope === "global" ? path4.join(os.homedir(), ".qoder") : path4.join(projectRoot ?? this.getProjectRoot(), ".qoder");
|
|
275
|
+
return path4.join(base, "skills", skillName);
|
|
322
276
|
}
|
|
323
277
|
};
|
|
324
278
|
|
|
325
279
|
// src/ide/ClaudeAdapter.ts
|
|
326
280
|
import * as os2 from "os";
|
|
327
|
-
import * as
|
|
281
|
+
import * as path5 from "path";
|
|
328
282
|
var ClaudeAdapter = class {
|
|
329
283
|
kind = "claude";
|
|
330
284
|
name = "claude";
|
|
@@ -335,8 +289,8 @@ var ClaudeAdapter = class {
|
|
|
335
289
|
return Boolean(process.env.CLAUDECODE);
|
|
336
290
|
}
|
|
337
291
|
getSkillTargetDir(skillName, scope, projectRoot) {
|
|
338
|
-
const base = scope === "global" ?
|
|
339
|
-
return
|
|
292
|
+
const base = scope === "global" ? path5.join(os2.homedir(), ".claude") : path5.join(projectRoot ?? this.getProjectRoot(), ".claude");
|
|
293
|
+
return path5.join(base, "skills", skillName);
|
|
340
294
|
}
|
|
341
295
|
};
|
|
342
296
|
|
|
@@ -383,19 +337,19 @@ async function loadTemplateFile(filePath) {
|
|
|
383
337
|
}
|
|
384
338
|
|
|
385
339
|
// src/utils/path.ts
|
|
386
|
-
import * as
|
|
340
|
+
import * as path6 from "path";
|
|
387
341
|
import * as fs4 from "fs/promises";
|
|
388
342
|
function resolveSourcePath(source, variantDir, packageRoot) {
|
|
389
343
|
if (source.startsWith("_template/")) {
|
|
390
|
-
return
|
|
344
|
+
return path6.join(packageRoot, source);
|
|
391
345
|
}
|
|
392
|
-
return
|
|
346
|
+
return path6.join(variantDir, source);
|
|
393
347
|
}
|
|
394
348
|
async function walkDir(dir) {
|
|
395
349
|
const files = [];
|
|
396
350
|
const entries = await fs4.readdir(dir, { withFileTypes: true });
|
|
397
351
|
for (const entry of entries) {
|
|
398
|
-
const fullPath =
|
|
352
|
+
const fullPath = path6.join(dir, entry.name);
|
|
399
353
|
if (entry.isDirectory()) {
|
|
400
354
|
files.push(...await walkDir(fullPath));
|
|
401
355
|
} else if (entry.isFile()) {
|
|
@@ -438,12 +392,12 @@ async function installSkills(options) {
|
|
|
438
392
|
}
|
|
439
393
|
async function writeSkillSource(skill, options) {
|
|
440
394
|
const { data, packageRoot, projectRoot } = options;
|
|
441
|
-
const sourceAbs =
|
|
395
|
+
const sourceAbs = path7.resolve(packageRoot, skill.source);
|
|
442
396
|
const targetDir = getSkillsSourceDir(projectRoot, skill.name);
|
|
443
397
|
const stat2 = await fs5.stat(sourceAbs);
|
|
444
398
|
const records = [];
|
|
445
399
|
if (stat2.isFile()) {
|
|
446
|
-
const targetFile =
|
|
400
|
+
const targetFile = path7.join(targetDir, "SKILL.md");
|
|
447
401
|
const content = await renderSkillContent(sourceAbs, skill, data);
|
|
448
402
|
await writeFileSafe(targetFile, content);
|
|
449
403
|
records.push(makeSourceRecord(skill, targetFile, content));
|
|
@@ -453,14 +407,14 @@ async function writeSkillSource(skill, options) {
|
|
|
453
407
|
await ensureDir(targetDir);
|
|
454
408
|
const entries = await walkDir(sourceAbs);
|
|
455
409
|
for (const entry of entries) {
|
|
456
|
-
const rel2 =
|
|
457
|
-
let targetFile =
|
|
410
|
+
const rel2 = path7.relative(sourceAbs, entry);
|
|
411
|
+
let targetFile = path7.join(targetDir, rel2);
|
|
458
412
|
if (skill.template && targetFile.endsWith(".hbs")) {
|
|
459
413
|
targetFile = targetFile.slice(0, -4);
|
|
460
414
|
}
|
|
461
415
|
const content = skill.template && entry.endsWith(".hbs") ? renderTemplate(await loadTemplateFile(entry), { ...data, skill }) : await fs5.readFile(entry, "utf-8");
|
|
462
416
|
await writeFileSafe(targetFile, content);
|
|
463
|
-
const relWritten =
|
|
417
|
+
const relWritten = path7.relative(targetDir, targetFile);
|
|
464
418
|
records.push(makeSourceRecord(skill, targetFile, content, relWritten));
|
|
465
419
|
logger.debug(` Wrote source: ${targetFile}`);
|
|
466
420
|
}
|
|
@@ -474,15 +428,66 @@ async function mirrorSkillToIde(skill, ide, scope, projectRoot) {
|
|
|
474
428
|
const sourceFiles = await walkDir(sourceDir);
|
|
475
429
|
await ensureDir(targetDir);
|
|
476
430
|
for (const src of sourceFiles) {
|
|
477
|
-
const rel2 =
|
|
478
|
-
const targetFile =
|
|
479
|
-
const
|
|
480
|
-
await
|
|
481
|
-
|
|
431
|
+
const rel2 = path7.relative(sourceDir, src);
|
|
432
|
+
const targetFile = path7.join(targetDir, rel2);
|
|
433
|
+
const sourceContent = await fs5.readFile(src, "utf-8");
|
|
434
|
+
const writtenContent = await writeMirrorContent(
|
|
435
|
+
targetFile,
|
|
436
|
+
sourceContent,
|
|
437
|
+
skill.managedRegions,
|
|
438
|
+
src
|
|
439
|
+
);
|
|
440
|
+
records.push(
|
|
441
|
+
makeMirrorRecord(skill, targetFile, writtenContent, ide, scope, rel2)
|
|
442
|
+
);
|
|
482
443
|
logger.debug(` Mirrored ${ide}:${scope}: ${targetFile}`);
|
|
483
444
|
}
|
|
484
445
|
return records;
|
|
485
446
|
}
|
|
447
|
+
async function writeMirrorContent(targetFile, sourceContent, managedRegions, sourceFile) {
|
|
448
|
+
const existing = await readFileOrNull(targetFile);
|
|
449
|
+
if (existing === null) {
|
|
450
|
+
await writeFileSafe(targetFile, sourceContent);
|
|
451
|
+
return sourceContent;
|
|
452
|
+
}
|
|
453
|
+
const regions = managedRegions ?? [];
|
|
454
|
+
const matchedRegions = regions.filter((id) => hasManagedRegion(existing, id));
|
|
455
|
+
if (matchedRegions.length === 0) {
|
|
456
|
+
if (existing !== sourceContent) {
|
|
457
|
+
logger.warn(
|
|
458
|
+
`Mirror drift detected at ${targetFile} \u2014 overwriting from source. Edit ${sourceFile} (not the mirror) and re-run \`teamix-evo skills sync\`.`
|
|
459
|
+
);
|
|
460
|
+
await writeFileSafe(targetFile, sourceContent);
|
|
461
|
+
return sourceContent;
|
|
462
|
+
}
|
|
463
|
+
return existing;
|
|
464
|
+
}
|
|
465
|
+
let merged = existing;
|
|
466
|
+
for (const id of matchedRegions) {
|
|
467
|
+
const newRegion = extractRegionBody(sourceContent, id);
|
|
468
|
+
if (newRegion === null) continue;
|
|
469
|
+
try {
|
|
470
|
+
merged = replaceManagedRegion(merged, id, newRegion);
|
|
471
|
+
} catch {
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
if (merged !== existing) {
|
|
475
|
+
await writeFileSafe(targetFile, merged);
|
|
476
|
+
}
|
|
477
|
+
return merged;
|
|
478
|
+
}
|
|
479
|
+
function extractRegionBody(content, id) {
|
|
480
|
+
const re = new RegExp(
|
|
481
|
+
`<!-- teamix-evo:managed:start id="${escapeRegExp(
|
|
482
|
+
id
|
|
483
|
+
)}" -->([\\s\\S]*?)<!-- teamix-evo:managed:end id="${escapeRegExp(
|
|
484
|
+
id
|
|
485
|
+
)}" -->`
|
|
486
|
+
);
|
|
487
|
+
const m = content.match(re);
|
|
488
|
+
if (!m) return null;
|
|
489
|
+
return m[1].replace(/^\n/, "").replace(/\n$/, "");
|
|
490
|
+
}
|
|
486
491
|
async function renderSkillContent(sourceAbs, skill, data) {
|
|
487
492
|
if (skill.template ?? sourceAbs.endsWith(".hbs")) {
|
|
488
493
|
const tpl = await loadTemplateFile(sourceAbs);
|
|
@@ -537,7 +542,7 @@ async function updateSkills(options) {
|
|
|
537
542
|
}
|
|
538
543
|
async function rewriteSkillSource(skill, options, summary) {
|
|
539
544
|
const { data, packageRoot, projectRoot } = options;
|
|
540
|
-
const sourceAbs =
|
|
545
|
+
const sourceAbs = path7.resolve(packageRoot, skill.source);
|
|
541
546
|
const targetDir = getSkillsSourceDir(projectRoot, skill.name);
|
|
542
547
|
const stat2 = await fs5.stat(sourceAbs);
|
|
543
548
|
if (!stat2.isFile()) {
|
|
@@ -545,8 +550,8 @@ async function rewriteSkillSource(skill, options, summary) {
|
|
|
545
550
|
const entries = await walkDir(sourceAbs);
|
|
546
551
|
const records = [];
|
|
547
552
|
for (const entry of entries) {
|
|
548
|
-
const rel2 =
|
|
549
|
-
let targetFile2 =
|
|
553
|
+
const rel2 = path7.relative(sourceAbs, entry);
|
|
554
|
+
let targetFile2 = path7.join(targetDir, rel2);
|
|
550
555
|
if (skill.template && targetFile2.endsWith(".hbs")) {
|
|
551
556
|
targetFile2 = targetFile2.slice(0, -4);
|
|
552
557
|
}
|
|
@@ -559,12 +564,12 @@ async function rewriteSkillSource(skill, options, summary) {
|
|
|
559
564
|
summary.created++;
|
|
560
565
|
}
|
|
561
566
|
await writeFileSafe(targetFile2, content);
|
|
562
|
-
const relWritten =
|
|
567
|
+
const relWritten = path7.relative(targetDir, targetFile2);
|
|
563
568
|
records.push(makeSourceRecord(skill, targetFile2, content, relWritten));
|
|
564
569
|
}
|
|
565
570
|
return records;
|
|
566
571
|
}
|
|
567
|
-
const targetFile =
|
|
572
|
+
const targetFile = path7.join(targetDir, "SKILL.md");
|
|
568
573
|
const newContent = await renderSkillContent(sourceAbs, skill, data);
|
|
569
574
|
const exists = await fileExists(targetFile);
|
|
570
575
|
if (skill.updateStrategy === "frozen") {
|
|
@@ -639,14 +644,19 @@ async function syncSkillsToIdes(options) {
|
|
|
639
644
|
await ensureDir(targetDir);
|
|
640
645
|
const sourceFiles = await walkDir(sourceDir);
|
|
641
646
|
for (const src of sourceFiles) {
|
|
642
|
-
const rel2 =
|
|
643
|
-
const targetFile =
|
|
644
|
-
const
|
|
645
|
-
await
|
|
647
|
+
const rel2 = path7.relative(sourceDir, src);
|
|
648
|
+
const targetFile = path7.join(targetDir, rel2);
|
|
649
|
+
const sourceContent = await fs5.readFile(src, "utf-8");
|
|
650
|
+
const writtenContent = await writeMirrorContent(
|
|
651
|
+
targetFile,
|
|
652
|
+
sourceContent,
|
|
653
|
+
skill.managedRegions,
|
|
654
|
+
src
|
|
655
|
+
);
|
|
646
656
|
out.push({
|
|
647
657
|
id: rel2 === "SKILL.md" ? skill.id : `${skill.id}:${rel2}`,
|
|
648
658
|
target: targetFile,
|
|
649
|
-
hash: computeHash(
|
|
659
|
+
hash: computeHash(writtenContent),
|
|
650
660
|
strategy: skill.updateStrategy,
|
|
651
661
|
ide,
|
|
652
662
|
scope
|
|
@@ -668,17 +678,46 @@ async function removeSkillFiles(records) {
|
|
|
668
678
|
}
|
|
669
679
|
}
|
|
670
680
|
}
|
|
671
|
-
const
|
|
672
|
-
for (const
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
681
|
+
const startDirs = new Set(records.map((r) => path7.dirname(r.target)));
|
|
682
|
+
for (const startDir of startDirs) {
|
|
683
|
+
let dir = startDir;
|
|
684
|
+
for (let depth = 0; depth < 8; depth++) {
|
|
685
|
+
try {
|
|
686
|
+
const entries = await fs5.readdir(dir);
|
|
687
|
+
if (entries.length !== 0) break;
|
|
688
|
+
await fs5.rmdir(dir);
|
|
689
|
+
} catch {
|
|
690
|
+
break;
|
|
691
|
+
}
|
|
692
|
+
dir = path7.dirname(dir);
|
|
677
693
|
}
|
|
678
694
|
}
|
|
679
695
|
return removed;
|
|
680
696
|
}
|
|
681
697
|
|
|
698
|
+
// src/utils/mcp.ts
|
|
699
|
+
import * as path8 from "path";
|
|
700
|
+
var MCP_JSON_CONTENT = {
|
|
701
|
+
mcpServers: {
|
|
702
|
+
"teamix-evo": {
|
|
703
|
+
command: "npx",
|
|
704
|
+
args: ["-y", "@teamix-evo/mcp"]
|
|
705
|
+
}
|
|
706
|
+
}
|
|
707
|
+
};
|
|
708
|
+
async function ensureMcpJson(projectRoot) {
|
|
709
|
+
const mcpPath = path8.join(projectRoot, ".mcp.json");
|
|
710
|
+
if (await fileExists(mcpPath)) return "exists";
|
|
711
|
+
try {
|
|
712
|
+
await writeFileSafe(mcpPath, JSON.stringify(MCP_JSON_CONTENT, null, 2) + "\n");
|
|
713
|
+
logger.debug(`Wrote .mcp.json \u2192 ${mcpPath}`);
|
|
714
|
+
return "created";
|
|
715
|
+
} catch (err) {
|
|
716
|
+
logger.warn(`Failed to write .mcp.json: ${err.message}`);
|
|
717
|
+
return "failed";
|
|
718
|
+
}
|
|
719
|
+
}
|
|
720
|
+
|
|
682
721
|
// src/core/skills-add.ts
|
|
683
722
|
var DEFAULT_SKILLS_PACKAGE = "@teamix-evo/skills";
|
|
684
723
|
var FLAT_VARIANT = "_flat";
|
|
@@ -690,9 +729,6 @@ async function runSkillsAdd(options) {
|
|
|
690
729
|
await ensureTeamixDir(projectRoot);
|
|
691
730
|
const existingConfig = await readProjectConfig(projectRoot);
|
|
692
731
|
const existingSkillsCfg = existingConfig?.packages?.skills;
|
|
693
|
-
if (!isIncremental && existingSkillsCfg) {
|
|
694
|
-
return { status: "already-added" };
|
|
695
|
-
}
|
|
696
732
|
const ides = options.ides && options.ides.length > 0 ? [...options.ides] : existingSkillsCfg?.ides ? [...existingSkillsCfg.ides] : [];
|
|
697
733
|
const scope = options.scope ?? existingSkillsCfg?.scope;
|
|
698
734
|
if (ides.length === 0) {
|
|
@@ -702,7 +738,7 @@ async function runSkillsAdd(options) {
|
|
|
702
738
|
throw new Error("Scope must be specified (project | global).");
|
|
703
739
|
}
|
|
704
740
|
const { manifest, data, packageRoot } = await loadSkillsData(packageName);
|
|
705
|
-
const
|
|
741
|
+
const currentTokensVariant = await readTokensVariant(projectRoot);
|
|
706
742
|
if (isIncremental) {
|
|
707
743
|
const known = new Set(manifest.skills.map((s) => s.id));
|
|
708
744
|
const unknown = requestedNames.filter((n) => !known.has(n));
|
|
@@ -730,23 +766,27 @@ async function runSkillsAdd(options) {
|
|
|
730
766
|
skippedSkillIds = requestedNames.filter((n) => existingSkillIds.has(n));
|
|
731
767
|
onlyIds = requestedNames.filter((n) => !existingSkillIds.has(n));
|
|
732
768
|
} else {
|
|
733
|
-
|
|
734
|
-
onlyIds = manifest.skills.filter((s) => {
|
|
769
|
+
const candidateIds = manifest.skills.filter((s) => {
|
|
735
770
|
if (!s.variant) return true;
|
|
736
|
-
if (!
|
|
771
|
+
if (!currentTokensVariant) {
|
|
737
772
|
logger.debug(
|
|
738
|
-
`Skipping variant-bound skill "${s.id}" (variant=${s.variant}): no
|
|
773
|
+
`Skipping variant-bound skill "${s.id}" (variant=${s.variant}): no tokens variant installed; will be picked up when "tokens init" runs.`
|
|
739
774
|
);
|
|
740
775
|
return false;
|
|
741
776
|
}
|
|
742
|
-
if (s.variant !==
|
|
777
|
+
if (s.variant !== currentTokensVariant) {
|
|
743
778
|
logger.debug(
|
|
744
|
-
`Skipping variant-bound skill "${s.id}" (variant=${s.variant}): current
|
|
779
|
+
`Skipping variant-bound skill "${s.id}" (variant=${s.variant}): current tokens variant is "${currentTokensVariant}".`
|
|
745
780
|
);
|
|
746
781
|
return false;
|
|
747
782
|
}
|
|
748
783
|
return true;
|
|
749
784
|
}).map((s) => s.id);
|
|
785
|
+
skippedSkillIds = candidateIds.filter((id) => existingSkillIds.has(id));
|
|
786
|
+
onlyIds = candidateIds.filter((id) => !existingSkillIds.has(id));
|
|
787
|
+
}
|
|
788
|
+
if (!isIncremental && existingSkillsCfg && onlyIds.length === 0) {
|
|
789
|
+
return { status: "already-added" };
|
|
750
790
|
}
|
|
751
791
|
if (isIncremental && onlyIds.length === 0) {
|
|
752
792
|
return {
|
|
@@ -823,6 +863,7 @@ async function runSkillsAdd(options) {
|
|
|
823
863
|
};
|
|
824
864
|
}
|
|
825
865
|
await writeSkillsLock(projectRoot, lock);
|
|
866
|
+
await ensureMcpJson(projectRoot);
|
|
826
867
|
return {
|
|
827
868
|
status: "installed",
|
|
828
869
|
packageName,
|
|
@@ -844,85 +885,110 @@ function mergeInstalledResources(existing, next) {
|
|
|
844
885
|
return [...map.values()];
|
|
845
886
|
}
|
|
846
887
|
|
|
847
|
-
// src/core/
|
|
848
|
-
var BASELINE_DESIGN_RULES_SKILL = "teamix-evo-design-rules";
|
|
888
|
+
// src/core/tokens-init.ts
|
|
849
889
|
var DEFAULT_SKILLS_PACKAGE2 = "@teamix-evo/skills";
|
|
850
890
|
var DEFAULT_AUTO_SKILL_IDES = ["qoder", "claude"];
|
|
851
891
|
var DEFAULT_AUTO_SKILL_SCOPE = "project";
|
|
852
|
-
var
|
|
892
|
+
var DEFAULT_TOKENS_PACKAGE = "@teamix-evo/tokens";
|
|
893
|
+
var CONSUMER_TOKENS_DIR = "tokens";
|
|
894
|
+
var CONSUMER_THEME_FILE = "tokens.theme.css";
|
|
895
|
+
var CONSUMER_OVERRIDES_FILE = "tokens.overrides.css";
|
|
896
|
+
var EMPTY_OVERRIDES_TEMPLATE = `/* User-owned token overrides \u2014 frozen on subsequent installs. */
|
|
897
|
+
/* See @teamix-evo/tokens variant theme.css for available CSS custom properties. */
|
|
898
|
+
`;
|
|
853
899
|
var require3 = createRequire2(import.meta.url);
|
|
854
|
-
async function
|
|
900
|
+
async function runTokensInit(options) {
|
|
855
901
|
const { projectRoot, variant, ide } = options;
|
|
856
|
-
const packageName = options.packageName ??
|
|
902
|
+
const packageName = options.packageName ?? DEFAULT_TOKENS_PACKAGE;
|
|
857
903
|
await ensureTeamixDir(projectRoot);
|
|
858
|
-
const
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
status: "already-initialized",
|
|
862
|
-
existingVariant: existingConfig.packages.design.variant
|
|
863
|
-
};
|
|
864
|
-
}
|
|
865
|
-
const packageRoot = options.packageRoot ?? resolveDesignPackageRoot(packageName);
|
|
866
|
-
const catalog = await loadDesignPackageManifest(packageRoot);
|
|
867
|
-
const variantEntry = catalog.variants.find((v) => v.name === variant);
|
|
904
|
+
const packageRoot = options.packageRoot ?? resolveTokensPackageRoot(packageName);
|
|
905
|
+
const catalog = await loadTokensPackageManifest(packageRoot);
|
|
906
|
+
const variantEntry = getVariantEntry(catalog, variant);
|
|
868
907
|
if (!variantEntry) {
|
|
869
908
|
const known = catalog.variants.map((v) => v.name).join(", ");
|
|
870
909
|
throw new Error(
|
|
871
|
-
`
|
|
910
|
+
`Unknown variant "${variant}". Available variants: ${known || "(none)"}.
|
|
911
|
+
Run \`teamix-evo tokens list-variants\` to see all options.`
|
|
872
912
|
);
|
|
873
913
|
}
|
|
874
|
-
const
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
914
|
+
const existingConfig = await readProjectConfig(projectRoot);
|
|
915
|
+
if (existingConfig?.packages?.tokens) {
|
|
916
|
+
const existingVariant = existingConfig.packages.tokens.variant;
|
|
917
|
+
if (existingVariant === variant) {
|
|
918
|
+
return { status: "already-initialized", existingVariant };
|
|
919
|
+
}
|
|
920
|
+
return {
|
|
921
|
+
status: "variant-mismatch",
|
|
922
|
+
existingVariant,
|
|
923
|
+
requestedVariant: variant
|
|
924
|
+
};
|
|
925
|
+
}
|
|
879
926
|
const installed = [];
|
|
880
|
-
for (const
|
|
881
|
-
const result = await
|
|
927
|
+
for (const fileRel of variantEntry.files) {
|
|
928
|
+
const result = await installVariantFile(fileRel, packageRoot, projectRoot);
|
|
882
929
|
if (result) installed.push(result);
|
|
883
930
|
}
|
|
931
|
+
const overridesAbs = path9.join(
|
|
932
|
+
projectRoot,
|
|
933
|
+
CONSUMER_TOKENS_DIR,
|
|
934
|
+
CONSUMER_OVERRIDES_FILE
|
|
935
|
+
);
|
|
936
|
+
if (!await fileExists(overridesAbs)) {
|
|
937
|
+
await writeFileSafe(overridesAbs, EMPTY_OVERRIDES_TEMPLATE);
|
|
938
|
+
}
|
|
939
|
+
const overridesContent = await fs6.readFile(overridesAbs, "utf-8");
|
|
940
|
+
installed.push({
|
|
941
|
+
id: `tokens:${CONSUMER_OVERRIDES_FILE}`,
|
|
942
|
+
target: path9.posix.join(CONSUMER_TOKENS_DIR, CONSUMER_OVERRIDES_FILE),
|
|
943
|
+
hash: computeHash(overridesContent),
|
|
944
|
+
strategy: "frozen"
|
|
945
|
+
});
|
|
884
946
|
const lock = {
|
|
885
947
|
schemaVersion: 1,
|
|
886
|
-
default: { version: defaultPack.version, from: packageName },
|
|
887
948
|
variant: {
|
|
888
|
-
name:
|
|
889
|
-
displayName:
|
|
890
|
-
version:
|
|
949
|
+
name: variantEntry.name,
|
|
950
|
+
displayName: variantEntry.displayName,
|
|
951
|
+
version: variantEntry.version,
|
|
891
952
|
from: packageName
|
|
892
953
|
},
|
|
893
|
-
|
|
954
|
+
packageVersion: catalog.version,
|
|
955
|
+
linked: variantEntry.linked,
|
|
894
956
|
installedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
895
957
|
};
|
|
896
958
|
await writeFileSafe(
|
|
897
|
-
path9.join(projectRoot, ".teamix-evo", "
|
|
959
|
+
path9.join(projectRoot, ".teamix-evo", "tokens-lock.json"),
|
|
898
960
|
JSON.stringify(lock, null, 2) + "\n"
|
|
899
961
|
);
|
|
900
962
|
const config = {
|
|
901
963
|
$schema: "https://teamix-evo.dev/schema/config/v1.json",
|
|
902
964
|
schemaVersion: 1,
|
|
903
|
-
ide,
|
|
965
|
+
ide: existingConfig?.ide ?? ide,
|
|
904
966
|
packages: {
|
|
905
|
-
|
|
967
|
+
...existingConfig?.packages ?? {},
|
|
968
|
+
tokens: {
|
|
906
969
|
variant,
|
|
907
|
-
version:
|
|
970
|
+
version: variantEntry.version,
|
|
908
971
|
tailwind: "v4"
|
|
909
972
|
}
|
|
910
973
|
}
|
|
911
974
|
};
|
|
912
975
|
await writeProjectConfig(projectRoot, config);
|
|
913
|
-
const
|
|
976
|
+
const prior = await readInstalledManifest(projectRoot) ?? {
|
|
914
977
|
schemaVersion: 1,
|
|
915
|
-
installed: [
|
|
916
|
-
{
|
|
917
|
-
package: packageName,
|
|
918
|
-
variant,
|
|
919
|
-
version: variantPack.version,
|
|
920
|
-
installedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
921
|
-
resources: installed
|
|
922
|
-
}
|
|
923
|
-
]
|
|
978
|
+
installed: []
|
|
924
979
|
};
|
|
925
|
-
|
|
980
|
+
const tokensIdx = prior.installed.findIndex((p) => p.package === packageName);
|
|
981
|
+
const tokensEntry = {
|
|
982
|
+
package: packageName,
|
|
983
|
+
variant,
|
|
984
|
+
version: variantEntry.version,
|
|
985
|
+
installedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
986
|
+
resources: installed
|
|
987
|
+
};
|
|
988
|
+
if (tokensIdx >= 0) prior.installed[tokensIdx] = tokensEntry;
|
|
989
|
+
else prior.installed.push(tokensEntry);
|
|
990
|
+
await writeInstalledManifest(projectRoot, prior);
|
|
991
|
+
await ensureMcpJson(projectRoot);
|
|
926
992
|
const skills = await tryAutoInstallVariantSkills({
|
|
927
993
|
projectRoot,
|
|
928
994
|
variant,
|
|
@@ -932,21 +998,16 @@ async function runDesignInit(options) {
|
|
|
932
998
|
status: "installed",
|
|
933
999
|
packageName,
|
|
934
1000
|
variant,
|
|
935
|
-
version:
|
|
1001
|
+
version: variantEntry.version,
|
|
936
1002
|
count: installed.length,
|
|
937
1003
|
resources: installed,
|
|
938
|
-
merge: {
|
|
939
|
-
overrides: merge.overrides,
|
|
940
|
-
variantAdds: merge.variantAdds,
|
|
941
|
-
defaultPassThrough: merge.defaultPassThrough
|
|
942
|
-
},
|
|
943
1004
|
skills
|
|
944
1005
|
};
|
|
945
1006
|
}
|
|
946
1007
|
async function tryAutoInstallVariantSkills(args) {
|
|
947
1008
|
const { projectRoot, variant, ide } = args;
|
|
948
|
-
const variantSkillId =
|
|
949
|
-
const desired = [
|
|
1009
|
+
const variantSkillId = `teamix-evo-design-${variant}`;
|
|
1010
|
+
const desired = [variantSkillId];
|
|
950
1011
|
let manifestSkillIds;
|
|
951
1012
|
try {
|
|
952
1013
|
const { manifest } = await loadSkillsData(DEFAULT_SKILLS_PACKAGE2);
|
|
@@ -966,7 +1027,9 @@ async function tryAutoInstallVariantSkills(args) {
|
|
|
966
1027
|
const missing = desired.filter((id) => !manifestSkillIds.has(id));
|
|
967
1028
|
if (missing.length > 0) {
|
|
968
1029
|
logger.warn(
|
|
969
|
-
`Skills auto-install: not found in manifest, skipping: ${missing.join(
|
|
1030
|
+
`Skills auto-install: not found in manifest, skipping: ${missing.join(
|
|
1031
|
+
", "
|
|
1032
|
+
)}.`
|
|
970
1033
|
);
|
|
971
1034
|
}
|
|
972
1035
|
if (present.length === 0) {
|
|
@@ -1011,32 +1074,69 @@ async function tryAutoInstallVariantSkills(args) {
|
|
|
1011
1074
|
};
|
|
1012
1075
|
}
|
|
1013
1076
|
}
|
|
1014
|
-
async function
|
|
1015
|
-
const
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1077
|
+
async function installVariantFile(fileRelToPackage, packageRoot, projectRoot) {
|
|
1078
|
+
const sourceAbs = path9.join(packageRoot, fileRelToPackage);
|
|
1079
|
+
const base = path9.basename(fileRelToPackage);
|
|
1080
|
+
if (base === "theme.css") {
|
|
1081
|
+
const targetRel = path9.posix.join(
|
|
1082
|
+
CONSUMER_TOKENS_DIR,
|
|
1083
|
+
CONSUMER_THEME_FILE
|
|
1084
|
+
);
|
|
1085
|
+
const targetAbs = path9.join(projectRoot, targetRel);
|
|
1086
|
+
const content = await fs6.readFile(sourceAbs, "utf-8");
|
|
1087
|
+
await writeFileSafe(targetAbs, content);
|
|
1020
1088
|
return {
|
|
1021
|
-
id: `
|
|
1022
|
-
target:
|
|
1023
|
-
hash: computeHash(
|
|
1024
|
-
strategy:
|
|
1089
|
+
id: `tokens:${CONSUMER_THEME_FILE}`,
|
|
1090
|
+
target: targetRel,
|
|
1091
|
+
hash: computeHash(content),
|
|
1092
|
+
strategy: "regenerable"
|
|
1025
1093
|
};
|
|
1026
1094
|
}
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1095
|
+
if (base === "overrides.css" || base === "tokens.overrides.css") {
|
|
1096
|
+
const targetRel = path9.posix.join(
|
|
1097
|
+
CONSUMER_TOKENS_DIR,
|
|
1098
|
+
CONSUMER_OVERRIDES_FILE
|
|
1099
|
+
);
|
|
1100
|
+
const targetAbs = path9.join(projectRoot, targetRel);
|
|
1101
|
+
if (await fileExists(targetAbs)) {
|
|
1102
|
+
const existing = await fs6.readFile(targetAbs, "utf-8");
|
|
1103
|
+
return {
|
|
1104
|
+
id: `tokens:${CONSUMER_OVERRIDES_FILE}`,
|
|
1105
|
+
target: targetRel,
|
|
1106
|
+
hash: computeHash(existing),
|
|
1107
|
+
strategy: "frozen"
|
|
1108
|
+
};
|
|
1109
|
+
}
|
|
1110
|
+
const content = await fs6.readFile(sourceAbs, "utf-8");
|
|
1111
|
+
await writeFileSafe(targetAbs, content);
|
|
1112
|
+
return {
|
|
1113
|
+
id: `tokens:${CONSUMER_OVERRIDES_FILE}`,
|
|
1114
|
+
target: targetRel,
|
|
1115
|
+
hash: computeHash(content),
|
|
1116
|
+
strategy: "frozen"
|
|
1117
|
+
};
|
|
1118
|
+
}
|
|
1119
|
+
return null;
|
|
1035
1120
|
}
|
|
1036
|
-
function
|
|
1121
|
+
function resolveTokensPackageRoot(packageName) {
|
|
1037
1122
|
const pkgJson = require3.resolve(`${packageName}/package.json`);
|
|
1038
1123
|
return path9.dirname(pkgJson);
|
|
1039
1124
|
}
|
|
1125
|
+
async function listTokenVariants(packageName = DEFAULT_TOKENS_PACKAGE, packageRoot) {
|
|
1126
|
+
const root = packageRoot ?? resolveTokensPackageRoot(packageName);
|
|
1127
|
+
const catalog = await loadTokensPackageManifest(root);
|
|
1128
|
+
return {
|
|
1129
|
+
packageName,
|
|
1130
|
+
packageVersion: catalog.version,
|
|
1131
|
+
variants: catalog.variants.map((v) => ({
|
|
1132
|
+
name: v.name,
|
|
1133
|
+
displayName: v.displayName,
|
|
1134
|
+
version: v.version,
|
|
1135
|
+
description: v.description,
|
|
1136
|
+
linked: v.linked
|
|
1137
|
+
}))
|
|
1138
|
+
};
|
|
1139
|
+
}
|
|
1040
1140
|
|
|
1041
1141
|
// src/core/ui-init.ts
|
|
1042
1142
|
var DEFAULT_UI_ALIASES = {
|
|
@@ -1082,6 +1182,7 @@ async function runUiInit(options) {
|
|
|
1082
1182
|
rsc
|
|
1083
1183
|
};
|
|
1084
1184
|
await writeProjectConfig(projectRoot, config);
|
|
1185
|
+
await ensureMcpJson(projectRoot);
|
|
1085
1186
|
return {
|
|
1086
1187
|
status: "installed",
|
|
1087
1188
|
aliases,
|
|
@@ -1161,12 +1262,12 @@ function aliasToImportPath(alias) {
|
|
|
1161
1262
|
}
|
|
1162
1263
|
|
|
1163
1264
|
// src/core/ui-installer.ts
|
|
1164
|
-
var DESIGN_COMPONENTS_DIR = ".teamix-evo/design/components";
|
|
1165
1265
|
async function installUiEntries(options) {
|
|
1166
1266
|
const {
|
|
1167
1267
|
projectRoot,
|
|
1168
1268
|
manifest,
|
|
1169
1269
|
packageRoot,
|
|
1270
|
+
entryPackageRoot,
|
|
1170
1271
|
aliases,
|
|
1171
1272
|
requested,
|
|
1172
1273
|
skipExisting = true
|
|
@@ -1175,7 +1276,6 @@ async function installUiEntries(options) {
|
|
|
1175
1276
|
const idToEntry = new Map(manifest.entries.map((e) => [e.id, e]));
|
|
1176
1277
|
const resources = [];
|
|
1177
1278
|
const npmDeps = {};
|
|
1178
|
-
const metaFiles = [];
|
|
1179
1279
|
let written = 0;
|
|
1180
1280
|
let skipped = 0;
|
|
1181
1281
|
for (const id of orderedIds) {
|
|
@@ -1194,7 +1294,8 @@ async function installUiEntries(options) {
|
|
|
1194
1294
|
skipped++;
|
|
1195
1295
|
continue;
|
|
1196
1296
|
}
|
|
1197
|
-
const
|
|
1297
|
+
const rootForEntry = entryPackageRoot?.get(entry.id) ?? packageRoot;
|
|
1298
|
+
const sourceAbs = path11.resolve(rootForEntry, file.source);
|
|
1198
1299
|
const raw = await fs8.readFile(sourceAbs, "utf-8");
|
|
1199
1300
|
const transformed = rewriteImports(raw, aliases);
|
|
1200
1301
|
await writeFileSafe(targetAbs, transformed);
|
|
@@ -1207,32 +1308,13 @@ async function installUiEntries(options) {
|
|
|
1207
1308
|
strategy: entry.updateStrategy ?? "frozen"
|
|
1208
1309
|
});
|
|
1209
1310
|
}
|
|
1210
|
-
if (entry.meta) {
|
|
1211
|
-
const metaSourceAbs = path11.resolve(packageRoot, entry.meta);
|
|
1212
|
-
const metaContent = await fs8.readFile(metaSourceAbs, "utf-8");
|
|
1213
|
-
const metaTargetAbs = path11.join(
|
|
1214
|
-
projectRoot,
|
|
1215
|
-
DESIGN_COMPONENTS_DIR,
|
|
1216
|
-
`${entry.id}.meta.md`
|
|
1217
|
-
);
|
|
1218
|
-
await writeFileSafe(metaTargetAbs, metaContent);
|
|
1219
|
-
metaFiles.push(metaTargetAbs);
|
|
1220
|
-
resources.push({
|
|
1221
|
-
id: `${entry.id}:meta`,
|
|
1222
|
-
target: metaTargetAbs,
|
|
1223
|
-
hash: computeHash(metaContent),
|
|
1224
|
-
strategy: "regenerable"
|
|
1225
|
-
});
|
|
1226
|
-
logger.info(` meta: ${rel(projectRoot, metaTargetAbs)}`);
|
|
1227
|
-
}
|
|
1228
1311
|
}
|
|
1229
1312
|
return {
|
|
1230
1313
|
orderedIds,
|
|
1231
1314
|
resources,
|
|
1232
1315
|
npmDependencies: npmDeps,
|
|
1233
1316
|
written,
|
|
1234
|
-
skipped
|
|
1235
|
-
metaFiles
|
|
1317
|
+
skipped
|
|
1236
1318
|
};
|
|
1237
1319
|
}
|
|
1238
1320
|
function resolveTargetPath(projectRoot, aliases, entry, file) {
|
|
@@ -1273,7 +1355,7 @@ async function removeUiFiles(records) {
|
|
|
1273
1355
|
// src/core/ui-add.ts
|
|
1274
1356
|
var DEFAULT_UI_PACKAGE = "@teamix-evo/ui";
|
|
1275
1357
|
async function runUiAdd(options) {
|
|
1276
|
-
const { projectRoot, ids, overwrite } = options;
|
|
1358
|
+
const { projectRoot, ids, overwrite, includeDeprecated } = options;
|
|
1277
1359
|
const packageName = options.packageName ?? DEFAULT_UI_PACKAGE;
|
|
1278
1360
|
if (ids.length === 0) {
|
|
1279
1361
|
throw new Error("At least one entry id must be provided.");
|
|
@@ -1286,7 +1368,21 @@ async function runUiAdd(options) {
|
|
|
1286
1368
|
);
|
|
1287
1369
|
}
|
|
1288
1370
|
const { manifest, packageRoot } = await loadUiData(packageName);
|
|
1289
|
-
const
|
|
1371
|
+
const archived = manifest.deprecatedEntries ?? [];
|
|
1372
|
+
const archivedIds = new Set(archived.map((e) => e.id));
|
|
1373
|
+
const activeIds = new Set(manifest.entries.map((e) => e.id));
|
|
1374
|
+
const requestedDeprecated = ids.filter((id) => archivedIds.has(id));
|
|
1375
|
+
if (requestedDeprecated.length > 0 && !includeDeprecated) {
|
|
1376
|
+
const list = requestedDeprecated.map((s) => `"${s}"`).join(", ");
|
|
1377
|
+
throw new Error(
|
|
1378
|
+
`Refusing to install deprecated entr${requestedDeprecated.length === 1 ? "y" : "ies"} ${list}. These entries are archived and not part of the active distribution (ADR 0028). Pass \`--include-deprecated\` to override (e.g. for migration tooling).`
|
|
1379
|
+
);
|
|
1380
|
+
}
|
|
1381
|
+
const effectiveManifest = includeDeprecated ? { ...manifest, entries: [...manifest.entries, ...archived] } : manifest;
|
|
1382
|
+
const knownIds = /* @__PURE__ */ new Set([
|
|
1383
|
+
...activeIds,
|
|
1384
|
+
...includeDeprecated ? archivedIds : []
|
|
1385
|
+
]);
|
|
1290
1386
|
const unknown = ids.filter((id) => !knownIds.has(id));
|
|
1291
1387
|
if (unknown.length > 0) {
|
|
1292
1388
|
throw new Error(
|
|
@@ -1295,7 +1391,7 @@ async function runUiAdd(options) {
|
|
|
1295
1391
|
}
|
|
1296
1392
|
const result = await installUiEntries({
|
|
1297
1393
|
projectRoot,
|
|
1298
|
-
manifest,
|
|
1394
|
+
manifest: effectiveManifest,
|
|
1299
1395
|
packageRoot,
|
|
1300
1396
|
aliases: uiCfg.aliases,
|
|
1301
1397
|
requested: ids,
|
|
@@ -1329,7 +1425,6 @@ async function runUiAdd(options) {
|
|
|
1329
1425
|
orderedIds: result.orderedIds,
|
|
1330
1426
|
written: result.written,
|
|
1331
1427
|
skipped: result.skipped,
|
|
1332
|
-
metaFiles: result.metaFiles,
|
|
1333
1428
|
npmDependencies: result.npmDependencies,
|
|
1334
1429
|
resources: result.resources
|
|
1335
1430
|
};
|
|
@@ -1344,7 +1439,7 @@ function mergeResources(prior, next) {
|
|
|
1344
1439
|
// src/core/ui-list.ts
|
|
1345
1440
|
var DEFAULT_UI_PACKAGE2 = "@teamix-evo/ui";
|
|
1346
1441
|
async function runUiList(options) {
|
|
1347
|
-
const { projectRoot, installedOnly } = options;
|
|
1442
|
+
const { projectRoot, installedOnly, includeDeprecated } = options;
|
|
1348
1443
|
const packageName = options.packageName ?? DEFAULT_UI_PACKAGE2;
|
|
1349
1444
|
const { manifest } = await loadUiData(packageName);
|
|
1350
1445
|
const installedManifest = await readInstalledManifest(projectRoot);
|
|
@@ -1356,15 +1451,21 @@ async function runUiList(options) {
|
|
|
1356
1451
|
const colon = r.id.indexOf(":");
|
|
1357
1452
|
installedIds.add(colon >= 0 ? r.id.slice(0, colon) : r.id);
|
|
1358
1453
|
}
|
|
1359
|
-
const
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1454
|
+
const archived = manifest.deprecatedEntries ?? [];
|
|
1455
|
+
const pool = includeDeprecated ? [
|
|
1456
|
+
...manifest.entries.map((e) => ({ entry: e, deprecated: false })),
|
|
1457
|
+
...archived.map((e) => ({ entry: e, deprecated: true }))
|
|
1458
|
+
] : manifest.entries.map((e) => ({ entry: e, deprecated: false }));
|
|
1459
|
+
const entries = pool.filter(({ entry }) => !installedOnly || installedIds.has(entry.id)).map(({ entry, deprecated }) => ({
|
|
1460
|
+
id: entry.id,
|
|
1461
|
+
type: entry.type,
|
|
1462
|
+
description: entry.description,
|
|
1463
|
+
installed: installedIds.has(entry.id),
|
|
1464
|
+
deprecated
|
|
1364
1465
|
}));
|
|
1365
1466
|
return {
|
|
1366
1467
|
packageName,
|
|
1367
|
-
total: manifest.entries.length,
|
|
1468
|
+
total: manifest.entries.length + (includeDeprecated ? archived.length : 0),
|
|
1368
1469
|
installedCount: installedIds.size,
|
|
1369
1470
|
entries
|
|
1370
1471
|
};
|
|
@@ -1501,6 +1602,7 @@ export {
|
|
|
1501
1602
|
installResources,
|
|
1502
1603
|
installSkills,
|
|
1503
1604
|
installUiEntries,
|
|
1605
|
+
listTokenVariants,
|
|
1504
1606
|
loadSkillsData,
|
|
1505
1607
|
loadUiData,
|
|
1506
1608
|
loadVariantData,
|
|
@@ -1508,8 +1610,8 @@ export {
|
|
|
1508
1610
|
readProjectConfig,
|
|
1509
1611
|
removeSkillFiles,
|
|
1510
1612
|
removeUiFiles,
|
|
1511
|
-
runDesignInit,
|
|
1512
1613
|
runSkillsAdd,
|
|
1614
|
+
runTokensInit,
|
|
1513
1615
|
runUiAdd,
|
|
1514
1616
|
runUiInit,
|
|
1515
1617
|
runUiList,
|