@teamix-evo/registry 0.1.0 → 0.2.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Teamix Evo Contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,123 @@
1
+ # @teamix-evo/registry
2
+
3
+ > Teamix Evo 协议包 — 提供 Schema、类型、加载器、校验器和策略决策引擎。
4
+
5
+ ## 定位
6
+
7
+ registry 是整个 Teamix Evo 架构的**协议层**,负责定义:
8
+
9
+ - 资源清单(VariantManifest)的结构约束
10
+ - 项目配置(ProjectConfig)的 Schema
11
+ - 安装状态(InstalledManifest)的追踪格式
12
+ - 三种更新策略(frozen / regenerable / managed)的决策逻辑
13
+ - Managed Regions 标记的解析与替换
14
+
15
+ CLI 包和 design 包都依赖本包的协议定义工作。
16
+
17
+ ## 目录结构
18
+
19
+ ```
20
+ packages/registry/
21
+ ├── src/
22
+ │ ├── schema/
23
+ │ │ ├── index.ts # Schema 统一导出
24
+ │ │ ├── manifest.ts # VariantManifest Zod Schema
25
+ │ │ ├── config.ts # ProjectConfig Zod Schema
26
+ │ │ └── installed.ts # InstalledManifest Zod Schema
27
+ │ ├── types.ts # 从 Schema 推导的 TypeScript 类型
28
+ │ ├── loader.ts # 从文件系统加载 manifest
29
+ │ ├── validator.ts # 校验封装 + 友好错误消息
30
+ │ ├── strategy.ts # updateStrategy 决策函数
31
+ │ ├── managed-regions.ts # HTML 标记区解析与替换
32
+ │ ├── index.ts # 公共导出入口
33
+ │ └── __tests__/ # 单元测试
34
+ ├── tsup.config.ts # 构建配置(ESM + CJS + DTS)
35
+ ├── tsconfig.json
36
+ └── package.json
37
+ ```
38
+
39
+ ## 研发流程
40
+
41
+ ### 1. 环境准备
42
+
43
+ ```bash
44
+ # 在仓库根目录
45
+ pnpm install
46
+ ```
47
+
48
+ ### 2. 开发
49
+
50
+ ```bash
51
+ # 监听模式构建(修改即时编译)
52
+ pnpm --filter @teamix-evo/registry dev
53
+ ```
54
+
55
+ ### 3. 新增 / 修改 Schema
56
+
57
+ 1. 在 `src/schema/` 中使用 Zod 定义或修改 Schema
58
+ 2. 在 `src/types.ts` 中通过 `z.infer<typeof XxxSchema>` 导出对应类型
59
+ 3. 在 `src/index.ts` 中确保新增的 Schema 和类型被导出
60
+ 4. 如需新增校验函数,在 `src/validator.ts` 中添加
61
+
62
+ ### 4. 修改策略逻辑
63
+
64
+ - `src/strategy.ts` — 修改 `shouldUpdate` 或 `getUpdateAction`
65
+ - `src/managed-regions.ts` — 修改标记解析或替换逻辑
66
+
67
+ ### 5. 测试
68
+
69
+ ```bash
70
+ # 运行全部测试
71
+ pnpm --filter @teamix-evo/registry test
72
+
73
+ # 监听模式
74
+ pnpm --filter @teamix-evo/registry test:watch
75
+ ```
76
+
77
+ 测试文件位于 `src/__tests__/`,覆盖:
78
+
79
+ - `manifest.test.ts` — VariantManifest Schema 校验
80
+ - `config.test.ts` — ProjectConfig Schema 校验
81
+ - `installed.test.ts` — InstalledManifest Schema 校验
82
+ - `validator.test.ts` — 友好错误消息格式化
83
+ - `strategy.test.ts` — 三种策略决策逻辑
84
+ - `managed-regions.test.ts` — 标记解析、替换、错误检测
85
+
86
+ ### 6. 类型检查
87
+
88
+ ```bash
89
+ pnpm --filter @teamix-evo/registry typecheck
90
+ ```
91
+
92
+ ### 7. 构建
93
+
94
+ ```bash
95
+ pnpm --filter @teamix-evo/registry build
96
+ ```
97
+
98
+ 产物输出到 `dist/`,包含:
99
+
100
+ - `index.js` — ESM 格式
101
+ - `index.cjs` — CommonJS 格式
102
+ - `index.d.ts` — TypeScript 声明
103
+
104
+ ### 8. 发布前检查
105
+
106
+ - 确保所有测试通过
107
+ - 确保类型检查通过
108
+ - 本包与 `teamix-evo`(CLI)使用 **linked** 版本策略,需一起发布
109
+
110
+ ## 关键约定
111
+
112
+ - 所有 Schema 使用 [Zod](https://zod.dev/) 定义,运行时校验 + 编译时类型推导一体
113
+ - `Result<T>` 类型用于校验返回,避免 throw(除 loader 和 managed-regions 外)
114
+ - managed regions 标记格式:`<!-- teamix-evo:managed:start id="xxx" -->...<!-- teamix-evo:managed:end id="xxx" -->`
115
+ - 同一文件中 region id 不得重复
116
+
117
+ ## 依赖关系
118
+
119
+ ```
120
+ 本包无内部包依赖
121
+ CLI 包 → 依赖本包
122
+ design 包 → devDependencies 引用本包(验证脚本)
123
+ ```
package/dist/index.cjs CHANGED
@@ -37,17 +37,34 @@ __export(index_exports, {
37
37
  ProjectConfigSchema: () => ProjectConfigSchema,
38
38
  ResourceSchema: () => ResourceSchema,
39
39
  ResourceTypeSchema: () => ResourceTypeSchema,
40
+ SkillEntrySchema: () => SkillEntrySchema,
41
+ SkillIdeSchema: () => SkillIdeSchema,
42
+ SkillScopeSchema: () => SkillScopeSchema,
43
+ SkillsPackageManifestSchema: () => SkillsPackageManifestSchema,
44
+ TailwindVersionSchema: () => TailwindVersionSchema,
45
+ UiAliasSchema: () => UiAliasSchema,
46
+ UiAliasesSchema: () => UiAliasesSchema,
47
+ UiEntryFileSchema: () => UiEntryFileSchema,
48
+ UiEntrySchema: () => UiEntrySchema,
49
+ UiEntryTypeSchema: () => UiEntryTypeSchema,
50
+ UiPackageManifestSchema: () => UiPackageManifestSchema,
40
51
  UpdateStrategySchema: () => UpdateStrategySchema,
41
52
  VariantManifestSchema: () => VariantManifestSchema,
42
53
  getUpdateAction: () => getUpdateAction,
43
54
  hasManagedRegion: () => hasManagedRegion,
55
+ loadSkillsPackageManifest: () => loadSkillsPackageManifest,
56
+ loadUiPackageManifest: () => loadUiPackageManifest,
44
57
  loadVariantManifest: () => loadVariantManifest,
45
58
  parseManagedRegions: () => parseManagedRegions,
46
59
  replaceManagedRegion: () => replaceManagedRegion,
60
+ resolveUiEntryOrder: () => resolveUiEntryOrder,
47
61
  shouldUpdate: () => shouldUpdate,
48
62
  validateConfig: () => validateConfig,
49
63
  validateInstalled: () => validateInstalled,
50
- validateManifest: () => validateManifest
64
+ validateManifest: () => validateManifest,
65
+ validateSkillsPackage: () => validateSkillsPackage,
66
+ validateUiDependencyGraph: () => validateUiDependencyGraph,
67
+ validateUiPackage: () => validateUiPackage
51
68
  });
52
69
  module.exports = __toCommonJS(index_exports);
53
70
 
@@ -62,8 +79,11 @@ var ResourceTypeSchema = import_zod.z.enum([
62
79
  "doc",
63
80
  "tokens",
64
81
  "ai-rules",
65
- "config"
82
+ "config",
83
+ "skill"
66
84
  ]);
85
+ var SkillIdeSchema = import_zod.z.enum(["qoder", "claude"]);
86
+ var SkillScopeSchema = import_zod.z.enum(["project", "global"]);
67
87
  var ResourceSchema = import_zod.z.object({
68
88
  /** Unique identifier for the resource within the manifest */
69
89
  id: import_zod.z.string().min(1),
@@ -71,7 +91,10 @@ var ResourceSchema = import_zod.z.object({
71
91
  type: ResourceTypeSchema,
72
92
  /** Source path (relative to variant root), typically a template */
73
93
  source: import_zod.z.string().min(1),
74
- /** Target path (relative to project root) where the resource is installed */
94
+ /**
95
+ * Target path (relative to project root) where the resource is installed.
96
+ * For type="skill" this is ignored: the path is computed from skill name + ide + scope.
97
+ */
75
98
  target: import_zod.z.string().min(1),
76
99
  /** How the resource is updated */
77
100
  updateStrategy: UpdateStrategySchema,
@@ -80,7 +103,11 @@ var ResourceSchema = import_zod.z.object({
80
103
  /** IDs of managed regions (only relevant when updateStrategy is "managed") */
81
104
  managedRegions: import_zod.z.array(import_zod.z.string()).optional(),
82
105
  /** Whether to recursively process directory contents */
83
- recursive: import_zod.z.boolean().optional()
106
+ recursive: import_zod.z.boolean().optional(),
107
+ /** Supported IDEs (only meaningful when type="skill"; defaults to all when omitted) */
108
+ ides: import_zod.z.array(SkillIdeSchema).optional(),
109
+ /** Default install scope (only meaningful when type="skill"; user can override at install time) */
110
+ scope: SkillScopeSchema.optional()
84
111
  });
85
112
  var VariantManifestSchema = import_zod.z.object({
86
113
  $schema: import_zod.z.string().optional(),
@@ -104,14 +131,160 @@ var VariantManifestSchema = import_zod.z.object({
104
131
  /** List of resources provided by this variant */
105
132
  resources: import_zod.z.array(ResourceSchema)
106
133
  });
134
+ var SkillEntrySchema = import_zod.z.object({
135
+ /** Skill identifier (matches name/folder) */
136
+ id: import_zod.z.string().min(1),
137
+ /** Skill name — must match the directory name and frontmatter `name` */
138
+ name: import_zod.z.string().min(1).regex(
139
+ /^[a-z0-9][a-z0-9-]*$/,
140
+ "Skill name must be lowercase letters, digits, hyphens (no leading hyphen)"
141
+ ),
142
+ /** One-line description for skill discovery */
143
+ description: import_zod.z.string().min(1),
144
+ /** Semver version */
145
+ version: import_zod.z.string().regex(/^\d+\.\d+\.\d+/, "Invalid semver version"),
146
+ /** Source path relative to package root — file or directory */
147
+ source: import_zod.z.string().min(1),
148
+ /** Supported IDEs (defaults to both when omitted) */
149
+ ides: import_zod.z.array(SkillIdeSchema).default(["qoder", "claude"]),
150
+ /** Update strategy (defaults to managed) */
151
+ updateStrategy: UpdateStrategySchema.default("managed"),
152
+ /** Managed region IDs to maintain on update */
153
+ managedRegions: import_zod.z.array(import_zod.z.string()).optional(),
154
+ /** Whether the source is a Handlebars template */
155
+ template: import_zod.z.boolean().optional()
156
+ });
157
+ var SkillsPackageManifestSchema = import_zod.z.object({
158
+ $schema: import_zod.z.string().optional(),
159
+ schemaVersion: import_zod.z.literal(1),
160
+ /** Always "skills" for this package */
161
+ package: import_zod.z.literal("skills"),
162
+ /** Semver version of the skills package */
163
+ version: import_zod.z.string().regex(/^\d+\.\d+\.\d+/, "Invalid semver version"),
164
+ /** Engine compatibility */
165
+ engines: import_zod.z.object({
166
+ "teamix-evo": import_zod.z.string().min(1)
167
+ }),
168
+ /** Flat list of skills shipped by this package */
169
+ skills: import_zod.z.array(SkillEntrySchema)
170
+ });
171
+ var UiEntryTypeSchema = import_zod.z.enum([
172
+ "component",
173
+ "hook",
174
+ "util",
175
+ "block"
176
+ ]);
177
+ var UiAliasSchema = import_zod.z.enum(["components", "hooks", "utils", "lib"]);
178
+ var UiEntryStatusSchema = import_zod.z.enum([
179
+ "stable",
180
+ "experimental",
181
+ "deprecated"
182
+ ]);
183
+ var UiEntryFileSchema = import_zod.z.object({
184
+ /** Source path relative to the ui package root (e.g. "src/components/button/button.tsx") */
185
+ source: import_zod.z.string().min(1),
186
+ /** Which alias this file resolves under in the consumer project */
187
+ targetAlias: UiAliasSchema,
188
+ /** Filename written under the alias directory (e.g. "button.tsx") */
189
+ targetName: import_zod.z.string().min(1)
190
+ });
191
+ var UiEntrySchema = import_zod.z.object({
192
+ /** Unique entry identifier within the ui package (e.g. "button") */
193
+ id: import_zod.z.string().min(1).regex(
194
+ /^[a-z0-9][a-z0-9-]*$/,
195
+ "UI entry id must be lowercase letters, digits, hyphens (no leading hyphen)"
196
+ ),
197
+ /** Display name (e.g. "Button") */
198
+ name: import_zod.z.string().min(1),
199
+ /** Entry type */
200
+ type: UiEntryTypeSchema,
201
+ /** One-line description for entry discovery and AI guidance */
202
+ description: import_zod.z.string().min(1),
203
+ /** Files this entry ships (typically 1) */
204
+ files: import_zod.z.array(UiEntryFileSchema).min(1),
205
+ /**
206
+ * Optional path to an AI-readable meta document (frontmatter + Markdown).
207
+ * When set, CLI install drops it at `.teamix-evo/design/components/<id>.meta.md`
208
+ * for design's ai-rules to consume.
209
+ */
210
+ meta: import_zod.z.string().optional(),
211
+ /** Other UI entries this one depends on (e.g. "button" depends on "cn") */
212
+ registryDependencies: import_zod.z.array(import_zod.z.string()).optional(),
213
+ /** npm dependencies required by this entry (name → semver range) */
214
+ dependencies: import_zod.z.record(import_zod.z.string(), import_zod.z.string()).optional(),
215
+ /**
216
+ * How CLI handles this entry on `ui upgrade`.
217
+ * Defaults to `frozen` — user-owned source code, untouched after first install.
218
+ * Upgrade is handled by AI + skill flow (no CLI rewrite). See PLAN §10.9.
219
+ */
220
+ updateStrategy: UpdateStrategySchema.default("frozen"),
221
+ /**
222
+ * Whether the source file should be passed through Handlebars before write.
223
+ * Most entries don't need templating (use import-rewrite transformer instead).
224
+ */
225
+ template: import_zod.z.boolean().optional(),
226
+ /**
227
+ * Maturity / lifecycle status of this entry. Defaults to `stable`.
228
+ * See {@link UiEntryStatusSchema} for semantics.
229
+ */
230
+ status: UiEntryStatusSchema.default("stable"),
231
+ /**
232
+ * Free-text rationale shown to consumers when `status` is `deprecated`.
233
+ * Should explain why the entry is going away and what replaces it.
234
+ */
235
+ deprecatedReason: import_zod.z.string().optional(),
236
+ /**
237
+ * If this entry is `deprecated`, the id of the entry that supersedes it.
238
+ * Consumers (and AI) should migrate to `replacedBy` rather than this entry.
239
+ */
240
+ replacedBy: import_zod.z.string().optional()
241
+ });
242
+ var UiPackageManifestSchema = import_zod.z.object({
243
+ $schema: import_zod.z.string().optional(),
244
+ schemaVersion: import_zod.z.literal(1),
245
+ /** Always "ui" for this package */
246
+ package: import_zod.z.literal("ui"),
247
+ /** Semver version of the ui package */
248
+ version: import_zod.z.string().regex(/^\d+\.\d+\.\d+/, "Invalid semver version"),
249
+ /** Engine compatibility */
250
+ engines: import_zod.z.object({
251
+ "teamix-evo": import_zod.z.string().min(1)
252
+ }),
253
+ /** Flat list of entries shipped by this package */
254
+ entries: import_zod.z.array(UiEntrySchema)
255
+ });
107
256
 
108
257
  // src/schema/config.ts
109
258
  var import_zod2 = require("zod");
259
+ var TailwindVersionSchema = import_zod2.z.enum(["v3", "v4"]);
260
+ var UiAliasesSchema = import_zod2.z.object({
261
+ components: import_zod2.z.string().min(1),
262
+ hooks: import_zod2.z.string().min(1),
263
+ utils: import_zod2.z.string().min(1),
264
+ lib: import_zod2.z.string().min(1)
265
+ });
110
266
  var PackageEntrySchema = import_zod2.z.object({
111
- /** Variant identifier (e.g. "opentrek") */
267
+ /** Variant identifier (e.g. "opentrek"; use "_flat" for skills/ui) */
112
268
  variant: import_zod2.z.string().min(1),
113
269
  /** Semver version string */
114
- version: import_zod2.z.string().min(1)
270
+ version: import_zod2.z.string().min(1),
271
+ /** Tailwind CSS version this project uses (only meaningful for design package). */
272
+ tailwind: TailwindVersionSchema.optional(),
273
+ /** IDEs this package was installed for (only meaningful for skills package). */
274
+ ides: import_zod2.z.array(SkillIdeSchema).optional(),
275
+ /** Install scope (only meaningful for skills package). */
276
+ scope: SkillScopeSchema.optional(),
277
+ /** Path aliases for ui entry installation (only meaningful for ui package). */
278
+ aliases: UiAliasesSchema.optional(),
279
+ /**
280
+ * Default icon library declared by the project (only meaningful for ui).
281
+ * Declarative only — does NOT trigger code rewrites; ui entries hardcode imports.
282
+ */
283
+ iconLibrary: import_zod2.z.string().min(1).optional(),
284
+ /** Whether the project uses TSX (true) or JSX (false). ui-specific. */
285
+ tsx: import_zod2.z.boolean().optional(),
286
+ /** Whether to emit React Server Components markers (`"use client"`). ui-specific. */
287
+ rsc: import_zod2.z.boolean().optional()
115
288
  });
116
289
  var ProjectConfigSchema = import_zod2.z.object({
117
290
  $schema: import_zod2.z.string().optional(),
@@ -127,17 +300,21 @@ var import_zod3 = require("zod");
127
300
  var InstalledResourceSchema = import_zod3.z.object({
128
301
  /** Resource identifier matching the variant manifest */
129
302
  id: import_zod3.z.string().min(1),
130
- /** Target path where the resource was installed */
303
+ /** Target path where the resource was installed (absolute or project-relative) */
131
304
  target: import_zod3.z.string().min(1),
132
305
  /** Content hash for change detection (e.g. "sha256:...") */
133
306
  hash: import_zod3.z.string().min(1),
134
307
  /** Update strategy that was applied */
135
- strategy: import_zod3.z.enum(["frozen", "regenerable", "managed"])
308
+ strategy: import_zod3.z.enum(["frozen", "regenerable", "managed"]),
309
+ /** IDE this resource was installed for (skill resources only) */
310
+ ide: SkillIdeSchema.optional(),
311
+ /** Install scope (skill resources only) */
312
+ scope: SkillScopeSchema.optional()
136
313
  });
137
314
  var InstalledPackageSchema = import_zod3.z.object({
138
315
  /** Full package name (e.g. "@teamix-evo/design") */
139
316
  package: import_zod3.z.string().min(1),
140
- /** Variant identifier */
317
+ /** Variant identifier (use "_flat" for non-variant packages such as skills) */
141
318
  variant: import_zod3.z.string().min(1),
142
319
  /** Installed version */
143
320
  version: import_zod3.z.string().min(1),
@@ -196,6 +373,115 @@ function validateInstalled(data) {
196
373
  ${formatZodError(result.error)}`
197
374
  };
198
375
  }
376
+ function validateSkillsPackage(data) {
377
+ const result = SkillsPackageManifestSchema.safeParse(data);
378
+ if (result.success) {
379
+ return { success: true, data: result.data };
380
+ }
381
+ return {
382
+ success: false,
383
+ error: `Invalid skills package manifest:
384
+ ${formatZodError(result.error)}`
385
+ };
386
+ }
387
+ function validateUiPackage(data) {
388
+ const parsed = UiPackageManifestSchema.safeParse(data);
389
+ if (!parsed.success) {
390
+ return {
391
+ success: false,
392
+ error: `Invalid ui package manifest:
393
+ ${formatZodError(parsed.error)}`
394
+ };
395
+ }
396
+ const graphIssues = validateUiDependencyGraph(parsed.data.entries);
397
+ if (graphIssues.length > 0) {
398
+ return {
399
+ success: false,
400
+ error: `Invalid ui package manifest dependency graph:
401
+ ${graphIssues.map((i) => ` - ${i}`).join("\n")}`
402
+ };
403
+ }
404
+ return { success: true, data: parsed.data };
405
+ }
406
+ function validateUiDependencyGraph(entries) {
407
+ const issues = [];
408
+ const idToEntry = /* @__PURE__ */ new Map();
409
+ for (const entry of entries) {
410
+ if (idToEntry.has(entry.id)) {
411
+ issues.push(`duplicate entry id: "${entry.id}"`);
412
+ } else {
413
+ idToEntry.set(entry.id, entry);
414
+ }
415
+ }
416
+ for (const entry of entries) {
417
+ const deps = entry.registryDependencies ?? [];
418
+ for (const dep of deps) {
419
+ if (!idToEntry.has(dep)) {
420
+ issues.push(
421
+ `entry "${entry.id}" depends on unknown entry "${dep}"`
422
+ );
423
+ }
424
+ }
425
+ }
426
+ const WHITE = 0;
427
+ const GRAY = 1;
428
+ const BLACK = 2;
429
+ const color = /* @__PURE__ */ new Map();
430
+ for (const id of idToEntry.keys()) color.set(id, WHITE);
431
+ const cycles = [];
432
+ function dfs(id, path2) {
433
+ color.set(id, GRAY);
434
+ path2.push(id);
435
+ const entry = idToEntry.get(id);
436
+ const deps = entry?.registryDependencies ?? [];
437
+ for (const dep of deps) {
438
+ if (!idToEntry.has(dep)) continue;
439
+ const c = color.get(dep);
440
+ if (c === GRAY) {
441
+ const startIdx = path2.indexOf(dep);
442
+ cycles.push([...path2.slice(startIdx), dep]);
443
+ } else if (c === WHITE) {
444
+ dfs(dep, path2);
445
+ }
446
+ }
447
+ path2.pop();
448
+ color.set(id, BLACK);
449
+ }
450
+ for (const id of idToEntry.keys()) {
451
+ if (color.get(id) === WHITE) dfs(id, []);
452
+ }
453
+ for (const cycle of cycles) {
454
+ issues.push(`cyclic registryDependencies: ${cycle.join(" \u2192 ")}`);
455
+ }
456
+ return issues;
457
+ }
458
+ function resolveUiEntryOrder(entries, requested) {
459
+ const idToEntry = new Map(entries.map((e) => [e.id, e]));
460
+ for (const id of requested) {
461
+ if (!idToEntry.has(id)) {
462
+ throw new Error(`Unknown ui entry: "${id}"`);
463
+ }
464
+ }
465
+ const graphIssues = validateUiDependencyGraph(entries);
466
+ if (graphIssues.length > 0) {
467
+ throw new Error(
468
+ `UI dependency graph has issues:
469
+ ${graphIssues.map((i) => ` - ${i}`).join("\n")}`
470
+ );
471
+ }
472
+ const visited = /* @__PURE__ */ new Set();
473
+ const order = [];
474
+ function visit(id) {
475
+ if (visited.has(id)) return;
476
+ visited.add(id);
477
+ const entry = idToEntry.get(id);
478
+ const deps = entry?.registryDependencies ?? [];
479
+ for (const dep of deps) visit(dep);
480
+ order.push(id);
481
+ }
482
+ for (const id of requested) visit(id);
483
+ return order;
484
+ }
199
485
 
200
486
  // src/loader.ts
201
487
  async function loadVariantManifest(packageDir) {
@@ -225,6 +511,60 @@ async function loadVariantManifest(packageDir) {
225
511
  }
226
512
  return result.data;
227
513
  }
514
+ async function loadSkillsPackageManifest(packageDir) {
515
+ const manifestPath = path.join(packageDir, "manifest.json");
516
+ let raw;
517
+ try {
518
+ raw = await fs.readFile(manifestPath, "utf-8");
519
+ } catch (err) {
520
+ const code = err.code;
521
+ if (code === "ENOENT") {
522
+ throw new Error(`Skills manifest not found: ${manifestPath}`);
523
+ }
524
+ throw new Error(
525
+ `Failed to read skills manifest at ${manifestPath}: ${err.message}`
526
+ );
527
+ }
528
+ let data;
529
+ try {
530
+ data = JSON.parse(raw);
531
+ } catch {
532
+ throw new Error(`Invalid JSON in skills manifest at ${manifestPath}`);
533
+ }
534
+ const result = validateSkillsPackage(data);
535
+ if (!result.success) {
536
+ throw new Error(`${result.error}
537
+ File: ${manifestPath}`);
538
+ }
539
+ return result.data;
540
+ }
541
+ async function loadUiPackageManifest(packageDir) {
542
+ const manifestPath = path.join(packageDir, "manifest.json");
543
+ let raw;
544
+ try {
545
+ raw = await fs.readFile(manifestPath, "utf-8");
546
+ } catch (err) {
547
+ const code = err.code;
548
+ if (code === "ENOENT") {
549
+ throw new Error(`UI manifest not found: ${manifestPath}`);
550
+ }
551
+ throw new Error(
552
+ `Failed to read ui manifest at ${manifestPath}: ${err.message}`
553
+ );
554
+ }
555
+ let data;
556
+ try {
557
+ data = JSON.parse(raw);
558
+ } catch {
559
+ throw new Error(`Invalid JSON in ui manifest at ${manifestPath}`);
560
+ }
561
+ const result = validateUiPackage(data);
562
+ if (!result.success) {
563
+ throw new Error(`${result.error}
564
+ File: ${manifestPath}`);
565
+ }
566
+ return result.data;
567
+ }
228
568
 
229
569
  // src/managed-regions.ts
230
570
  var REGION_PATTERN = /<!-- teamix-evo:managed:start id="([^"]+)" -->([\s\S]*?)<!-- teamix-evo:managed:end id="\1" -->/g;
@@ -233,15 +573,25 @@ var END_MARKER_PATTERN = /<!-- teamix-evo:managed:end id="([^"]+)" -->/g;
233
573
  function parseManagedRegions(content) {
234
574
  const startIds = /* @__PURE__ */ new Set();
235
575
  const endIds = /* @__PURE__ */ new Set();
576
+ const startCounts = /* @__PURE__ */ new Map();
236
577
  let match;
237
578
  const startRe = new RegExp(START_MARKER_PATTERN.source, "g");
238
579
  while ((match = startRe.exec(content)) !== null) {
239
- startIds.add(match[1]);
580
+ const id = match[1];
581
+ startIds.add(id);
582
+ startCounts.set(id, (startCounts.get(id) ?? 0) + 1);
240
583
  }
241
584
  const endRe = new RegExp(END_MARKER_PATTERN.source, "g");
242
585
  while ((match = endRe.exec(content)) !== null) {
243
586
  endIds.add(match[1]);
244
587
  }
588
+ for (const [id, count] of startCounts) {
589
+ if (count > 1) {
590
+ throw new Error(
591
+ `Duplicate managed region: found ${count} start markers for "${id}". Each id must be unique within a file.`
592
+ );
593
+ }
594
+ }
245
595
  for (const id of startIds) {
246
596
  if (!endIds.has(id)) {
247
597
  throw new Error(
@@ -274,7 +624,9 @@ function replaceManagedRegion(content, id, newContent) {
274
624
  const startMarker = `<!-- teamix-evo:managed:start id="${id}" -->`;
275
625
  const endMarker = `<!-- teamix-evo:managed:end id="${id}" -->`;
276
626
  const pattern = new RegExp(
277
- `<!-- teamix-evo:managed:start id="${escapeRegExp(id)}" -->[\\s\\S]*?<!-- teamix-evo:managed:end id="${escapeRegExp(id)}" -->`,
627
+ `<!-- teamix-evo:managed:start id="${escapeRegExp(
628
+ id
629
+ )}" -->[\\s\\S]*?<!-- teamix-evo:managed:end id="${escapeRegExp(id)}" -->`,
278
630
  "g"
279
631
  );
280
632
  if (!pattern.test(content)) {
@@ -290,7 +642,9 @@ ${endMarker}`
290
642
  }
291
643
  function hasManagedRegion(content, id) {
292
644
  const pattern = new RegExp(
293
- `<!-- teamix-evo:managed:start id="${escapeRegExp(id)}" -->[\\s\\S]*?<!-- teamix-evo:managed:end id="${escapeRegExp(id)}" -->`
645
+ `<!-- teamix-evo:managed:start id="${escapeRegExp(
646
+ id
647
+ )}" -->[\\s\\S]*?<!-- teamix-evo:managed:end id="${escapeRegExp(id)}" -->`
294
648
  );
295
649
  return pattern.test(content);
296
650
  }
@@ -341,16 +695,33 @@ function getUpdateAction(strategy, options) {
341
695
  ProjectConfigSchema,
342
696
  ResourceSchema,
343
697
  ResourceTypeSchema,
698
+ SkillEntrySchema,
699
+ SkillIdeSchema,
700
+ SkillScopeSchema,
701
+ SkillsPackageManifestSchema,
702
+ TailwindVersionSchema,
703
+ UiAliasSchema,
704
+ UiAliasesSchema,
705
+ UiEntryFileSchema,
706
+ UiEntrySchema,
707
+ UiEntryTypeSchema,
708
+ UiPackageManifestSchema,
344
709
  UpdateStrategySchema,
345
710
  VariantManifestSchema,
346
711
  getUpdateAction,
347
712
  hasManagedRegion,
713
+ loadSkillsPackageManifest,
714
+ loadUiPackageManifest,
348
715
  loadVariantManifest,
349
716
  parseManagedRegions,
350
717
  replaceManagedRegion,
718
+ resolveUiEntryOrder,
351
719
  shouldUpdate,
352
720
  validateConfig,
353
721
  validateInstalled,
354
- validateManifest
722
+ validateManifest,
723
+ validateSkillsPackage,
724
+ validateUiDependencyGraph,
725
+ validateUiPackage
355
726
  });
356
727
  //# sourceMappingURL=index.cjs.map