@foxian/esl 0.1.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.
Files changed (173) hide show
  1. package/LICENSE +21 -0
  2. package/dist/api-error.d.ts +13 -0
  3. package/dist/api-error.js +31 -0
  4. package/dist/bin/esl.d.ts +35 -0
  5. package/dist/bin/esl.js +1033 -0
  6. package/dist/builtin/builtin-packages.json +11 -0
  7. package/dist/builtin/esl-operator/SKILL.md +65 -0
  8. package/dist/builtin/esl-operator/references/agent-interaction.md +110 -0
  9. package/dist/builtin/esl-operator/references/author.md +98 -0
  10. package/dist/builtin/esl-operator/references/consumer.md +109 -0
  11. package/dist/builtin/esl-operator/references/setup.md +41 -0
  12. package/dist/builtin/esl-operator/skill.json +7 -0
  13. package/dist/builtin-dir.d.ts +1 -0
  14. package/dist/builtin-dir.js +11 -0
  15. package/dist/commands/adapt.d.ts +8 -0
  16. package/dist/commands/adapt.js +27 -0
  17. package/dist/commands/admin.d.ts +11 -0
  18. package/dist/commands/admin.js +96 -0
  19. package/dist/commands/clone.d.ts +10 -0
  20. package/dist/commands/clone.js +21 -0
  21. package/dist/commands/config.d.ts +8 -0
  22. package/dist/commands/config.js +19 -0
  23. package/dist/commands/delete.d.ts +13 -0
  24. package/dist/commands/delete.js +51 -0
  25. package/dist/commands/deprecate.d.ts +16 -0
  26. package/dist/commands/deprecate.js +29 -0
  27. package/dist/commands/import.d.ts +14 -0
  28. package/dist/commands/import.js +21 -0
  29. package/dist/commands/info.d.ts +28 -0
  30. package/dist/commands/info.js +69 -0
  31. package/dist/commands/init.d.ts +28 -0
  32. package/dist/commands/init.js +252 -0
  33. package/dist/commands/install.d.ts +17 -0
  34. package/dist/commands/install.js +435 -0
  35. package/dist/commands/link.d.ts +11 -0
  36. package/dist/commands/link.js +170 -0
  37. package/dist/commands/list.d.ts +6 -0
  38. package/dist/commands/list.js +7 -0
  39. package/dist/commands/login.d.ts +20 -0
  40. package/dist/commands/login.js +132 -0
  41. package/dist/commands/logout.d.ts +6 -0
  42. package/dist/commands/logout.js +18 -0
  43. package/dist/commands/network-options.d.ts +27 -0
  44. package/dist/commands/network-options.js +133 -0
  45. package/dist/commands/notes.d.ts +10 -0
  46. package/dist/commands/notes.js +24 -0
  47. package/dist/commands/publish.d.ts +19 -0
  48. package/dist/commands/publish.js +271 -0
  49. package/dist/commands/release-delete.d.ts +21 -0
  50. package/dist/commands/release-delete.js +34 -0
  51. package/dist/commands/release-manifest.d.ts +5 -0
  52. package/dist/commands/release-manifest.js +23 -0
  53. package/dist/commands/rename.d.ts +5 -0
  54. package/dist/commands/rename.js +25 -0
  55. package/dist/commands/repair-tag.d.ts +5 -0
  56. package/dist/commands/repair-tag.js +14 -0
  57. package/dist/commands/reset-source.d.ts +16 -0
  58. package/dist/commands/reset-source.js +71 -0
  59. package/dist/commands/resolve-unlink-identity.d.ts +14 -0
  60. package/dist/commands/resolve-unlink-identity.js +47 -0
  61. package/dist/commands/search.d.ts +19 -0
  62. package/dist/commands/search.js +33 -0
  63. package/dist/commands/share.d.ts +17 -0
  64. package/dist/commands/share.js +56 -0
  65. package/dist/commands/source.d.ts +10 -0
  66. package/dist/commands/source.js +34 -0
  67. package/dist/commands/status.d.ts +16 -0
  68. package/dist/commands/status.js +77 -0
  69. package/dist/commands/sync-builtin.d.ts +10 -0
  70. package/dist/commands/sync-builtin.js +68 -0
  71. package/dist/commands/tools.d.ts +19 -0
  72. package/dist/commands/tools.js +73 -0
  73. package/dist/commands/uninstall.d.ts +19 -0
  74. package/dist/commands/uninstall.js +75 -0
  75. package/dist/commands/unlink.d.ts +14 -0
  76. package/dist/commands/unlink.js +108 -0
  77. package/dist/commands/update.d.ts +18 -0
  78. package/dist/commands/update.js +176 -0
  79. package/dist/commands/upload.d.ts +27 -0
  80. package/dist/commands/upload.js +446 -0
  81. package/dist/commands/use.d.ts +9 -0
  82. package/dist/commands/use.js +51 -0
  83. package/dist/commands/validate.d.ts +4 -0
  84. package/dist/commands/validate.js +8 -0
  85. package/dist/commands/version.d.ts +16 -0
  86. package/dist/commands/version.js +147 -0
  87. package/dist/commands/whoami.d.ts +14 -0
  88. package/dist/commands/whoami.js +54 -0
  89. package/dist/index.d.ts +24 -0
  90. package/dist/index.js +24 -0
  91. package/dist/output.d.ts +1 -0
  92. package/dist/output.js +3 -0
  93. package/dist/postinstall.d.ts +1 -0
  94. package/dist/postinstall.js +8 -0
  95. package/dist/prompt.d.ts +9 -0
  96. package/dist/prompt.js +24 -0
  97. package/dist/vendor/core/adapt/adapt-engine.d.ts +24 -0
  98. package/dist/vendor/core/adapt/adapt-engine.js +99 -0
  99. package/dist/vendor/core/adapt/adapted-skill-copy.d.ts +2 -0
  100. package/dist/vendor/core/adapt/adapted-skill-copy.js +17 -0
  101. package/dist/vendor/core/adapt/claude-adapter.d.ts +8 -0
  102. package/dist/vendor/core/adapt/claude-adapter.js +19 -0
  103. package/dist/vendor/core/adapt/codex-adapter.d.ts +8 -0
  104. package/dist/vendor/core/adapt/codex-adapter.js +19 -0
  105. package/dist/vendor/core/adapt/index.d.ts +2 -0
  106. package/dist/vendor/core/adapt/index.js +2 -0
  107. package/dist/vendor/core/adapt/tool-adapter.d.ts +12 -0
  108. package/dist/vendor/core/adapt/tool-adapter.js +1 -0
  109. package/dist/vendor/core/adapt/trae-adapter.d.ts +8 -0
  110. package/dist/vendor/core/adapt/trae-adapter.js +19 -0
  111. package/dist/vendor/core/adapt/trae-cn-adapter.d.ts +8 -0
  112. package/dist/vendor/core/adapt/trae-cn-adapter.js +19 -0
  113. package/dist/vendor/core/agent-interaction.d.ts +58 -0
  114. package/dist/vendor/core/agent-interaction.js +58 -0
  115. package/dist/vendor/core/command-params.d.ts +5 -0
  116. package/dist/vendor/core/command-params.js +46 -0
  117. package/dist/vendor/core/index.d.ts +22 -0
  118. package/dist/vendor/core/index.js +22 -0
  119. package/dist/vendor/core/link/skill-links.d.ts +38 -0
  120. package/dist/vendor/core/link/skill-links.js +245 -0
  121. package/dist/vendor/core/link/tool-links.d.ts +83 -0
  122. package/dist/vendor/core/link/tool-links.js +376 -0
  123. package/dist/vendor/core/org/account-policy.d.ts +8 -0
  124. package/dist/vendor/core/org/account-policy.js +57 -0
  125. package/dist/vendor/core/org/org-name.d.ts +2 -0
  126. package/dist/vendor/core/org/org-name.js +22 -0
  127. package/dist/vendor/core/org/standing-teams.d.ts +10 -0
  128. package/dist/vendor/core/org/standing-teams.js +27 -0
  129. package/dist/vendor/core/schema/compatibility.d.ts +17 -0
  130. package/dist/vendor/core/schema/compatibility.js +45 -0
  131. package/dist/vendor/core/schema/release-manifest.d.ts +88 -0
  132. package/dist/vendor/core/schema/release-manifest.js +161 -0
  133. package/dist/vendor/core/schema/skill-json.d.ts +61 -0
  134. package/dist/vendor/core/schema/skill-json.js +47 -0
  135. package/dist/vendor/core/schema/validation-result.d.ts +7 -0
  136. package/dist/vendor/core/schema/validation-result.js +1 -0
  137. package/dist/vendor/core/skill/builtin-package.d.ts +32 -0
  138. package/dist/vendor/core/skill/builtin-package.js +143 -0
  139. package/dist/vendor/core/skill/directory-validator.d.ts +9 -0
  140. package/dist/vendor/core/skill/directory-validator.js +72 -0
  141. package/dist/vendor/core/skill/import-skill.d.ts +10 -0
  142. package/dist/vendor/core/skill/import-skill.js +68 -0
  143. package/dist/vendor/core/skill/published-package.d.ts +1 -0
  144. package/dist/vendor/core/skill/published-package.js +9 -0
  145. package/dist/vendor/core/skill/skill-md.d.ts +15 -0
  146. package/dist/vendor/core/skill/skill-md.js +32 -0
  147. package/dist/vendor/core/store/file-copy.d.ts +3 -0
  148. package/dist/vendor/core/store/file-copy.js +102 -0
  149. package/dist/vendor/core/store/local-store.d.ts +43 -0
  150. package/dist/vendor/core/store/local-store.js +104 -0
  151. package/dist/vendor/core/store/skill-store.d.ts +24 -0
  152. package/dist/vendor/core/store/skill-store.js +41 -0
  153. package/dist/vendor/core/store/skills-json.d.ts +34 -0
  154. package/dist/vendor/core/store/skills-json.js +146 -0
  155. package/dist/vendor/core/version/select-version.d.ts +16 -0
  156. package/dist/vendor/core/version/select-version.js +34 -0
  157. package/dist/vendor/core/version/version.d.ts +2 -0
  158. package/dist/vendor/core/version/version.js +25 -0
  159. package/dist/vendor/i18n/api-errors.d.ts +121 -0
  160. package/dist/vendor/i18n/api-errors.js +240 -0
  161. package/dist/vendor/i18n/i18n-runtime.d.ts +4 -0
  162. package/dist/vendor/i18n/i18n-runtime.js +16 -0
  163. package/dist/vendor/i18n/index.d.ts +5 -0
  164. package/dist/vendor/i18n/index.js +5 -0
  165. package/dist/vendor/i18n/locales.d.ts +9 -0
  166. package/dist/vendor/i18n/locales.js +33 -0
  167. package/dist/vendor/i18n/resources.d.ts +11 -0
  168. package/dist/vendor/i18n/resources.js +925 -0
  169. package/dist/vendor/i18n/translate-api-error.d.ts +8 -0
  170. package/dist/vendor/i18n/translate-api-error.js +12 -0
  171. package/dist/version.d.ts +1 -0
  172. package/dist/version.js +24 -0
  173. package/package.json +46 -0
@@ -0,0 +1,161 @@
1
+ import fs from 'node:fs/promises';
2
+ import path from 'node:path';
3
+ import { z } from 'zod';
4
+ import { SemVerSchema } from './skill-json.js';
5
+ import { validateSkillMd } from '../skill/skill-md.js';
6
+ const SpdxLicenseSchema = z.string().regex(/^(?:MIT|Apache-2\.0|BSD-[234]-Clause|ISC|MPL-2\.0|GPL-[23]\.0|LGPL-[23]\.0|AGPL-[23]\.0)(?:\s+(?:AND|OR)\s+(?:MIT|Apache-2\.0|BSD-[234]-Clause|ISC|MPL-2\.0|GPL-[23]\.0|LGPL-[23]\.0|AGPL-[23]\.0))*$/, { message: 'license must be an SPDX expression or LicenseRef-* identifier' });
7
+ const LicenseSchema = z.union([
8
+ SpdxLicenseSchema,
9
+ z.string().regex(/^LicenseRef-[A-Za-z0-9.-]+$/, {
10
+ message: 'license must be an SPDX expression or LicenseRef-* identifier'
11
+ })
12
+ ]);
13
+ // Release Manifest v4 的 name 是技能归属的唯一权威来源(ADR-0032):
14
+ // `@scope/skill-name` 或无 scope 的 `skill-name`(解析为上传者个人命名空间)。
15
+ const SKILL_IDENTITY_PATTERN = '^(@[a-z0-9-]+/)?[a-z0-9-]{1,64}$';
16
+ export const SkillIdentitySchema = z.string().regex(new RegExp(SKILL_IDENTITY_PATTERN), {
17
+ message: 'name must be "@scope/skill-name" or a bare "skill-name" (personal namespace)'
18
+ });
19
+ // 显示名长度上限(ADR-0048):trim 后按字符数计。
20
+ export const DISPLAY_NAME_MAX_LENGTH = 128;
21
+ // 显示名(ADR-0048):纯展示短标题,可为中文与空格,不参与身份/授权。空串与
22
+ // 全空白按「未设置」处理,trim 后长度上限 128。
23
+ const DisplayNameSchema = z
24
+ .string({ message: 'displayName must be a string' })
25
+ .optional()
26
+ .transform((value) => {
27
+ if (value === undefined)
28
+ return undefined;
29
+ const trimmed = value.trim();
30
+ return trimmed.length > 0 ? trimmed : undefined;
31
+ });
32
+ export const ReleaseManifestSchema = z
33
+ .object({
34
+ schemaVersion: z.union([z.literal(3), z.literal(4)]),
35
+ name: SkillIdentitySchema,
36
+ version: SemVerSchema,
37
+ license: LicenseSchema,
38
+ displayName: DisplayNameSchema,
39
+ keywords: z.array(z.string().min(1)),
40
+ compatibility: z.object({
41
+ tools: z.array(z.string().min(1)).optional(),
42
+ languages: z.array(z.string().min(1)).optional()
43
+ }),
44
+ dependencies: z.record(z.string().regex(/^@[a-z0-9-]+\/[a-z0-9-]+$/), z.string().min(1))
45
+ })
46
+ .strict()
47
+ .superRefine((data, ctx) => {
48
+ if (data.displayName === undefined)
49
+ return;
50
+ // 带 displayName 的清单必须是 v4(ADR-0048):字段随 v4 引入,v3 保持
51
+ // 旧契约不承载新字段,避免旧 CLI 遇未知键时既报错又无版本信号。
52
+ if (data.schemaVersion === 3) {
53
+ ctx.addIssue({
54
+ code: 'custom',
55
+ path: ['displayName'],
56
+ message: 'displayName requires schemaVersion 4 (the field was added in Release Manifest v4)'
57
+ });
58
+ }
59
+ // 长度上限按 trim 后计算(去空白)。
60
+ if (data.displayName.length > DISPLAY_NAME_MAX_LENGTH) {
61
+ ctx.addIssue({
62
+ code: 'custom',
63
+ path: ['displayName'],
64
+ message: `displayName must be at most ${DISPLAY_NAME_MAX_LENGTH} characters after trimming`
65
+ });
66
+ }
67
+ });
68
+ // Release Manifest v4 的 name 即 Skill Identity(ADR-0032):`@scope/skill-name`
69
+ // 或裸 `skill-name`(消费端按上传者用户名补全为个人命名空间)。解析失败返回 null。
70
+ export function parseSkillIdentity(name) {
71
+ const match = name.match(/^@([a-z0-9-]+)\/([a-z0-9-]{1,64})$/);
72
+ if (match) {
73
+ return { scope: match[1], shortName: match[2] };
74
+ }
75
+ if (/^[a-z0-9-]{1,64}$/.test(name)) {
76
+ return { scope: null, shortName: name };
77
+ }
78
+ return null;
79
+ }
80
+ // 显示名种子(ADR-0048):init 生成 v4 清单时把机器短名转成标题
81
+ // (markdown-master → Markdown Master)。运行时解析对外显示名不做此转换,
82
+ // 只在此处生成可编辑的初始值。
83
+ export function titleCaseDisplayName(name) {
84
+ const shortName = parseSkillIdentity(name)?.shortName ?? name;
85
+ return shortName
86
+ .split('-')
87
+ .filter((word) => word.length > 0)
88
+ .map((word) => word.charAt(0).toUpperCase() + word.slice(1))
89
+ .join(' ');
90
+ }
91
+ export function createMinimalReleaseManifest(name, license, displayName) {
92
+ return {
93
+ schemaVersion: 4,
94
+ name,
95
+ version: '0.1.0',
96
+ license,
97
+ ...(displayName ? { displayName } : {}),
98
+ keywords: [],
99
+ compatibility: {},
100
+ dependencies: {}
101
+ };
102
+ }
103
+ export function validateReleaseManifest(data) {
104
+ const result = ReleaseManifestSchema.safeParse(data);
105
+ if (result.success)
106
+ return { success: true, data: result.data };
107
+ const errors = result.error.issues.map((issue) => {
108
+ const field = issue.path.join('.') || 'release.json';
109
+ return `${field}: ${issue.message}`;
110
+ });
111
+ if (isPreVersionManifest(data)) {
112
+ errors.push('release.json: schemaVersion 1 is no longer supported; run `esl version <SemVer>` to record the version, then commit and push the source');
113
+ }
114
+ if (isV2Manifest(data)) {
115
+ errors.push('release.json: schemaVersion 2 is no longer supported; add the required "name" field ("@scope/skill-name" or a bare "skill-name") and set schemaVersion to 3 or 4');
116
+ }
117
+ return { success: false, errors };
118
+ }
119
+ function isPreVersionManifest(data) {
120
+ return (typeof data === 'object' &&
121
+ data !== null &&
122
+ data.schemaVersion === 1);
123
+ }
124
+ function isV2Manifest(data) {
125
+ return (typeof data === 'object' &&
126
+ data !== null &&
127
+ data.schemaVersion === 2);
128
+ }
129
+ export async function validateSkillSourceDirectory(directory) {
130
+ const resolved = path.resolve(directory);
131
+ const skillMdPath = path.join(resolved, 'SKILL.md');
132
+ const releaseManifestPath = path.join(resolved, 'release.json');
133
+ const errors = [];
134
+ let skillMd;
135
+ let releaseManifest;
136
+ try {
137
+ const validation = validateSkillMd(await fs.readFile(skillMdPath, 'utf8'));
138
+ if (validation.success)
139
+ skillMd = validation.data;
140
+ else
141
+ errors.push(...validation.errors);
142
+ }
143
+ catch {
144
+ errors.push('SKILL.md: file is required');
145
+ }
146
+ try {
147
+ const parsed = JSON.parse(await fs.readFile(releaseManifestPath, 'utf8'));
148
+ const validation = validateReleaseManifest(parsed);
149
+ if (validation.success)
150
+ releaseManifest = validation.data;
151
+ else
152
+ errors.push(...validation.errors);
153
+ }
154
+ catch {
155
+ errors.push('release.json: file is required and must contain valid JSON');
156
+ }
157
+ if (errors.length > 0 || !skillMd || !releaseManifest) {
158
+ return { success: false, errors };
159
+ }
160
+ return { success: true, data: { directory: resolved, skillMd, releaseManifest } };
161
+ }
@@ -0,0 +1,61 @@
1
+ import { z } from 'zod';
2
+ import type { ValidationResult } from './validation-result.js';
3
+ export declare const SkillNameSchema: z.ZodString;
4
+ export declare const SemVerSchema: z.ZodString;
5
+ export declare const SkillJsonSchema: z.ZodObject<{
6
+ name: z.ZodString;
7
+ version: z.ZodString;
8
+ description: z.ZodString;
9
+ author: z.ZodString;
10
+ license: z.ZodOptional<z.ZodString>;
11
+ keywords: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
12
+ compatibility: z.ZodOptional<z.ZodObject<{
13
+ tools: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
14
+ languages: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
15
+ }, "strip", z.ZodTypeAny, {
16
+ tools?: string[] | undefined;
17
+ languages?: string[] | undefined;
18
+ }, {
19
+ tools?: string[] | undefined;
20
+ languages?: string[] | undefined;
21
+ }>>;
22
+ dependencies: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
23
+ repository: z.ZodOptional<z.ZodString>;
24
+ }, "strip", z.ZodTypeAny, {
25
+ name: string;
26
+ version: string;
27
+ description: string;
28
+ author: string;
29
+ license?: string | undefined;
30
+ keywords?: string[] | undefined;
31
+ compatibility?: {
32
+ tools?: string[] | undefined;
33
+ languages?: string[] | undefined;
34
+ } | undefined;
35
+ dependencies?: Record<string, string> | undefined;
36
+ repository?: string | undefined;
37
+ }, {
38
+ name: string;
39
+ version: string;
40
+ description: string;
41
+ author: string;
42
+ license?: string | undefined;
43
+ keywords?: string[] | undefined;
44
+ compatibility?: {
45
+ tools?: string[] | undefined;
46
+ languages?: string[] | undefined;
47
+ } | undefined;
48
+ dependencies?: Record<string, string> | undefined;
49
+ repository?: string | undefined;
50
+ }>;
51
+ export type SkillJson = z.infer<typeof SkillJsonSchema>;
52
+ export declare function validateSkillJson(data: unknown): ValidationResult<SkillJson>;
53
+ export declare function parseSkillName(name: string): {
54
+ scope: string;
55
+ skillName: string;
56
+ };
57
+ export declare function createMinimalSkillManifest(input: {
58
+ name: string;
59
+ description: string;
60
+ author: string;
61
+ }): SkillJson;
@@ -0,0 +1,47 @@
1
+ import { z } from 'zod';
2
+ export const SkillNameSchema = z.string().regex(/^@[a-z0-9-]+\/[a-z0-9-]+$/, {
3
+ message: 'Skill name must follow @namespace/skill-name using lowercase letters, digits, and hyphens'
4
+ });
5
+ export const SemVerSchema = z.string().regex(/^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?$/, {
6
+ message: 'Version must be valid SemVer such as 1.0.0'
7
+ });
8
+ export const SkillJsonSchema = z.object({
9
+ name: SkillNameSchema,
10
+ version: SemVerSchema,
11
+ description: z.string().min(1).max(1024),
12
+ author: z.string().min(1),
13
+ license: z.string().optional(),
14
+ keywords: z.array(z.string().min(1)).optional(),
15
+ compatibility: z
16
+ .object({
17
+ tools: z.array(z.string().min(1)).optional(),
18
+ languages: z.array(z.string().min(1)).optional()
19
+ })
20
+ .optional(),
21
+ dependencies: z.record(SkillNameSchema, z.string().min(1)).optional(),
22
+ repository: z.string().min(1).optional()
23
+ });
24
+ export function validateSkillJson(data) {
25
+ const result = SkillJsonSchema.safeParse(data);
26
+ if (result.success) {
27
+ return { success: true, data: result.data };
28
+ }
29
+ return {
30
+ success: false,
31
+ errors: result.error.issues.map((issue) => {
32
+ const path = issue.path.join('.') || 'skill.json';
33
+ return `${path}: ${issue.message}`;
34
+ })
35
+ };
36
+ }
37
+ export function parseSkillName(name) {
38
+ const result = SkillNameSchema.safeParse(name);
39
+ if (!result.success) {
40
+ throw new Error('Skill name must follow @namespace/skill-name using lowercase letters, digits, and hyphens');
41
+ }
42
+ const [scope, skillName] = name.slice(1).split('/');
43
+ return { scope, skillName };
44
+ }
45
+ export function createMinimalSkillManifest(input) {
46
+ return { name: input.name, version: '0.1.0', description: input.description, author: input.author, keywords: [] };
47
+ }
@@ -0,0 +1,7 @@
1
+ export type ValidationResult<T> = {
2
+ success: true;
3
+ data: T;
4
+ } | {
5
+ success: false;
6
+ errors: string[];
7
+ };
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,32 @@
1
+ export interface BuiltinPackageManifestEntry {
2
+ name: string;
3
+ version: string;
4
+ checksum: string;
5
+ shortName: string;
6
+ }
7
+ export interface BuiltinPackageManifest {
8
+ version: 1;
9
+ packages: Record<string, BuiltinPackageManifestEntry>;
10
+ }
11
+ export interface BuiltinPackage {
12
+ name: string;
13
+ shortName: string;
14
+ version: string;
15
+ checksum: string;
16
+ directory: string;
17
+ }
18
+ export interface BuildBuiltinPackageOptions {
19
+ sourceDir: string;
20
+ outputRoot: string;
21
+ identity: string;
22
+ cliVersion: string;
23
+ author?: string;
24
+ }
25
+ export declare const BUILTIN_SCOPE = "builtin";
26
+ export declare const BUILTIN_IDENTITY = "@builtin/esl-operator";
27
+ export declare const BUILTIN_SPECIFIER_PREFIX = "builtin:";
28
+ export declare function isBuiltinIdentity(name: string): boolean;
29
+ export declare function buildBuiltinPackage(options: BuildBuiltinPackageOptions): Promise<BuiltinPackageManifestEntry>;
30
+ export declare function loadBuiltinPackage(builtinRoot: string, name: string): Promise<BuiltinPackage | null>;
31
+ export declare function loadBuiltinPackageOrThrow(builtinRoot: string, name: string): Promise<BuiltinPackage>;
32
+ export declare function computePackageChecksum(directory: string): Promise<string>;
@@ -0,0 +1,143 @@
1
+ import fs from 'node:fs/promises';
2
+ import path from 'node:path';
3
+ import crypto from 'node:crypto';
4
+ import { parseSkillName, SemVerSchema } from '../schema/skill-json.js';
5
+ import { validateSkillMd } from './skill-md.js';
6
+ const BUILTIN_MANIFEST = 'builtin-packages.json';
7
+ export const BUILTIN_SCOPE = 'builtin';
8
+ export const BUILTIN_IDENTITY = '@builtin/esl-operator';
9
+ export const BUILTIN_SPECIFIER_PREFIX = 'builtin:';
10
+ export function isBuiltinIdentity(name) {
11
+ try {
12
+ return parseSkillName(name).scope === BUILTIN_SCOPE;
13
+ }
14
+ catch {
15
+ return false;
16
+ }
17
+ }
18
+ export async function buildBuiltinPackage(options) {
19
+ const { scope, skillName } = parseSkillName(options.identity);
20
+ if (scope !== 'builtin') {
21
+ throw new Error(`Built-in skill identity must use the builtin scope: ${options.identity}`);
22
+ }
23
+ if (!SemVerSchema.safeParse(options.cliVersion).success) {
24
+ throw new Error(`Built-in skill version must be a valid SemVer matching @foxian/esl: ${options.cliVersion}`);
25
+ }
26
+ const skillMdContent = await fs.readFile(path.join(options.sourceDir, 'SKILL.md'), 'utf8');
27
+ const skillMdValidation = validateSkillMd(skillMdContent);
28
+ if (!skillMdValidation.success) {
29
+ throw new Error(`Invalid built-in SKILL.md: ${skillMdValidation.errors.join(', ')}`);
30
+ }
31
+ if (skillMdValidation.data.name !== skillName) {
32
+ throw new Error(`SKILL.md name "${skillMdValidation.data.name}" must match built-in short name "${skillName}"`);
33
+ }
34
+ const packageDir = path.join(options.outputRoot, skillName);
35
+ await fs.rm(packageDir, { recursive: true, force: true });
36
+ await fs.mkdir(packageDir, { recursive: true });
37
+ const skillJson = {
38
+ name: options.identity,
39
+ version: options.cliVersion,
40
+ description: skillMdValidation.data.description,
41
+ author: options.author ?? '@foxian/esl',
42
+ keywords: []
43
+ };
44
+ await fs.writeFile(path.join(packageDir, 'skill.json'), `${JSON.stringify(skillJson, null, 2)}\n`, 'utf8');
45
+ await copySourceFiles(options.sourceDir, packageDir);
46
+ const checksum = await computePackageChecksum(packageDir);
47
+ const manifest = await loadManifest(options.outputRoot);
48
+ manifest.packages[options.identity] = {
49
+ name: options.identity,
50
+ version: options.cliVersion,
51
+ checksum,
52
+ shortName: skillName
53
+ };
54
+ await saveManifest(options.outputRoot, manifest);
55
+ return {
56
+ name: options.identity,
57
+ version: options.cliVersion,
58
+ checksum,
59
+ shortName: skillName
60
+ };
61
+ }
62
+ export async function loadBuiltinPackage(builtinRoot, name) {
63
+ const manifest = await loadManifest(builtinRoot);
64
+ const entry = manifest.packages[name];
65
+ if (!entry) {
66
+ return null;
67
+ }
68
+ const directory = path.join(builtinRoot, entry.shortName);
69
+ const actualChecksum = await computePackageChecksum(directory);
70
+ if (actualChecksum !== entry.checksum) {
71
+ throw new Error(`Built-in skill package checksum does not match: expected ${entry.checksum}, got ${actualChecksum}`);
72
+ }
73
+ return {
74
+ name: entry.name,
75
+ shortName: entry.shortName,
76
+ version: entry.version,
77
+ checksum: entry.checksum,
78
+ directory
79
+ };
80
+ }
81
+ export async function loadBuiltinPackageOrThrow(builtinRoot, name) {
82
+ const builtin = await loadBuiltinPackage(builtinRoot, name);
83
+ if (!builtin) {
84
+ throw new Error(`Unknown built-in skill: ${name}`);
85
+ }
86
+ return builtin;
87
+ }
88
+ async function loadManifest(builtinRoot) {
89
+ try {
90
+ const raw = await fs.readFile(path.join(builtinRoot, BUILTIN_MANIFEST), 'utf8');
91
+ const parsed = JSON.parse(raw);
92
+ return { version: 1, packages: parsed.packages ?? {} };
93
+ }
94
+ catch {
95
+ return { version: 1, packages: {} };
96
+ }
97
+ }
98
+ async function saveManifest(builtinRoot, manifest) {
99
+ await fs.mkdir(builtinRoot, { recursive: true });
100
+ await fs.writeFile(path.join(builtinRoot, BUILTIN_MANIFEST), `${JSON.stringify(manifest, null, 2)}\n`, 'utf8');
101
+ }
102
+ async function copySourceFiles(sourceDir, packageDir) {
103
+ const entries = await fs.readdir(sourceDir, { withFileTypes: true });
104
+ for (const entry of entries) {
105
+ if (entry.name === '.git') {
106
+ continue;
107
+ }
108
+ const sourcePath = path.join(sourceDir, entry.name);
109
+ const targetPath = path.join(packageDir, entry.name);
110
+ if (entry.isDirectory()) {
111
+ await fs.cp(sourcePath, targetPath, { recursive: true });
112
+ }
113
+ else {
114
+ await fs.copyFile(sourcePath, targetPath);
115
+ }
116
+ }
117
+ }
118
+ export async function computePackageChecksum(directory) {
119
+ const files = [];
120
+ async function walk(current, rel) {
121
+ const entries = await fs.readdir(current, { withFileTypes: true });
122
+ for (const entry of entries) {
123
+ const entryRel = rel ? `${rel}/${entry.name}` : entry.name;
124
+ const fullPath = path.join(current, entry.name);
125
+ if (entry.isDirectory()) {
126
+ await walk(fullPath, entryRel);
127
+ }
128
+ else {
129
+ files.push(entryRel);
130
+ }
131
+ }
132
+ }
133
+ await walk(directory, '');
134
+ files.sort();
135
+ const hash = crypto.createHash('sha256');
136
+ for (const file of files) {
137
+ const content = await fs.readFile(path.join(directory, file));
138
+ hash.update(file);
139
+ hash.update('\0');
140
+ hash.update(content);
141
+ }
142
+ return `sha256-${hash.digest('hex')}`;
143
+ }
@@ -0,0 +1,9 @@
1
+ import { type SkillJson } from '../schema/skill-json.js';
2
+ import type { ValidationResult } from '../schema/validation-result.js';
3
+ import { type SkillMdMetadata } from './skill-md.js';
4
+ export interface SkillDirectory {
5
+ directory: string;
6
+ skillJson: SkillJson;
7
+ skillMd: SkillMdMetadata;
8
+ }
9
+ export declare function validateSkillDirectory(directory: string): Promise<ValidationResult<SkillDirectory>>;
@@ -0,0 +1,72 @@
1
+ import fs from 'node:fs/promises';
2
+ import path from 'node:path';
3
+ import { parseSkillName, validateSkillJson } from '../schema/skill-json.js';
4
+ import { validateSkillMd } from './skill-md.js';
5
+ async function exists(filePath) {
6
+ try {
7
+ await fs.access(filePath);
8
+ return true;
9
+ }
10
+ catch {
11
+ return false;
12
+ }
13
+ }
14
+ export async function validateSkillDirectory(directory) {
15
+ const errors = [];
16
+ const skillJsonPath = path.join(directory, 'skill.json');
17
+ const skillMdPath = path.join(directory, 'SKILL.md');
18
+ if (!(await exists(skillJsonPath))) {
19
+ errors.push('skill.json: file is required');
20
+ }
21
+ if (!(await exists(skillMdPath))) {
22
+ errors.push('SKILL.md: file is required');
23
+ }
24
+ if (await exists(path.join(directory, 'resources'))) {
25
+ errors.push('resources/: deprecated; use assets/ for runtime assets');
26
+ }
27
+ let skillJson;
28
+ let skillMd;
29
+ if (await exists(skillJsonPath)) {
30
+ try {
31
+ const raw = await fs.readFile(skillJsonPath, 'utf8');
32
+ const validation = validateSkillJson(JSON.parse(raw));
33
+ if (validation.success) {
34
+ skillJson = validation.data;
35
+ }
36
+ else {
37
+ errors.push(...validation.errors);
38
+ }
39
+ }
40
+ catch (error) {
41
+ errors.push(`skill.json: ${error.message}`);
42
+ }
43
+ }
44
+ if (await exists(skillMdPath)) {
45
+ try {
46
+ const validation = validateSkillMd(await fs.readFile(skillMdPath, 'utf8'));
47
+ if (validation.success) {
48
+ skillMd = validation.data;
49
+ }
50
+ else {
51
+ errors.push(...validation.errors);
52
+ }
53
+ }
54
+ catch (error) {
55
+ errors.push(`SKILL.md: ${error.message}`);
56
+ }
57
+ }
58
+ if (skillJson && skillMd) {
59
+ const { scope, skillName } = parseSkillName(skillJson.name);
60
+ if (skillMd.name !== skillName) {
61
+ errors.push(`SKILL.md name must match skill.json name suffix "${skillName}"`);
62
+ }
63
+ const directoryName = path.basename(path.resolve(directory));
64
+ if (skillMd.name !== directoryName && `${scope}_${skillName}` !== directoryName) {
65
+ errors.push(`SKILL.md name must match directory name "${directoryName}"`);
66
+ }
67
+ }
68
+ if (errors.length > 0 || !skillJson || !skillMd) {
69
+ return { success: false, errors };
70
+ }
71
+ return { success: true, data: { directory, skillJson, skillMd } };
72
+ }
@@ -0,0 +1,10 @@
1
+ export interface PrepareSkillImportOptions {
2
+ namespace?: string;
3
+ author?: string;
4
+ }
5
+ export interface PrepareSkillImportResult {
6
+ directory: string;
7
+ skillName: string;
8
+ createdSkillJson: boolean;
9
+ }
10
+ export declare function prepareSkillImport(directory: string, options?: PrepareSkillImportOptions): Promise<PrepareSkillImportResult>;
@@ -0,0 +1,68 @@
1
+ import fs from 'node:fs/promises';
2
+ import path from 'node:path';
3
+ import { parseSkillName, validateSkillJson } from '../schema/skill-json.js';
4
+ import { validateSkillDirectory } from './directory-validator.js';
5
+ import { validateSkillMd } from './skill-md.js';
6
+ const NamespacePattern = /^[a-z0-9-]+$/;
7
+ async function fileExists(filePath) {
8
+ try {
9
+ const stat = await fs.stat(filePath);
10
+ return stat.isFile();
11
+ }
12
+ catch {
13
+ return false;
14
+ }
15
+ }
16
+ async function readExistingSkillJson(skillJsonPath) {
17
+ const raw = await fs.readFile(skillJsonPath, 'utf8');
18
+ const validation = validateSkillJson(JSON.parse(raw));
19
+ if (!validation.success) {
20
+ throw new Error(validation.errors.join(', '));
21
+ }
22
+ return validation.data;
23
+ }
24
+ export async function prepareSkillImport(directory, options = {}) {
25
+ const resolved = path.resolve(directory);
26
+ const stat = await fs.stat(resolved).catch(() => null);
27
+ if (!stat?.isDirectory()) {
28
+ throw new Error(`${resolved} is not a directory`);
29
+ }
30
+ const skillMdPath = path.join(resolved, 'SKILL.md');
31
+ const skillMdValidation = validateSkillMd(await fs.readFile(skillMdPath, 'utf8'));
32
+ if (!skillMdValidation.success) {
33
+ throw new Error(skillMdValidation.errors.join(', '));
34
+ }
35
+ const namespace = options.namespace ?? 'local';
36
+ if (!NamespacePattern.test(namespace)) {
37
+ throw new Error('Namespace must use lowercase letters, digits, and hyphens');
38
+ }
39
+ const skillName = `@${namespace}/${skillMdValidation.data.name}`;
40
+ parseSkillName(skillName);
41
+ const skillJsonPath = path.join(resolved, 'skill.json');
42
+ const createdSkillJson = !(await fileExists(skillJsonPath));
43
+ if (createdSkillJson) {
44
+ const skillJson = {
45
+ name: skillName,
46
+ version: '0.1.0',
47
+ description: skillMdValidation.data.description,
48
+ author: options.author ?? process.env.USER ?? process.env.USERNAME ?? 'anonymous',
49
+ keywords: []
50
+ };
51
+ await fs.writeFile(skillJsonPath, `${JSON.stringify(skillJson, null, 2)}\n`, 'utf8');
52
+ }
53
+ else {
54
+ const existing = await readExistingSkillJson(skillJsonPath);
55
+ if (existing.name !== skillName) {
56
+ throw new Error(`skill.json name "${existing.name}" must match import name "${skillName}"`);
57
+ }
58
+ }
59
+ const validation = await validateSkillDirectory(resolved);
60
+ if (!validation.success) {
61
+ throw new Error(`Invalid skill package at ${resolved}: ${validation.errors.join(', ')}`);
62
+ }
63
+ return {
64
+ directory: resolved,
65
+ skillName,
66
+ createdSkillJson
67
+ };
68
+ }
@@ -0,0 +1 @@
1
+ export declare function preparePublishedSkillPackage(directory: string, identity: string): Promise<void>;
@@ -0,0 +1,9 @@
1
+ import fs from 'node:fs/promises';
2
+ import { parseSkillName } from '../schema/skill-json.js';
3
+ export async function preparePublishedSkillPackage(directory, identity) {
4
+ const { scope, skillName } = parseSkillName(identity);
5
+ const skillMdPath = `${directory}/SKILL.md`;
6
+ const content = await fs.readFile(skillMdPath, 'utf8');
7
+ const updated = content.replace(/^(---\r?\n)([\s\S]*?)(\r?\n---)/, (_match, open, frontmatter, close) => `${open}${frontmatter.replace(/(^name:\s*)[^\r\n]+/m, `$1${scope}:${skillName}`)}${close}`);
8
+ await fs.writeFile(skillMdPath, updated, 'utf8');
9
+ }
@@ -0,0 +1,15 @@
1
+ import { z } from 'zod';
2
+ import type { ValidationResult } from '../schema/validation-result.js';
3
+ declare const SkillMdMetadataSchema: z.ZodObject<{
4
+ name: z.ZodString;
5
+ description: z.ZodString;
6
+ }, "strip", z.ZodTypeAny, {
7
+ name: string;
8
+ description: string;
9
+ }, {
10
+ name: string;
11
+ description: string;
12
+ }>;
13
+ export type SkillMdMetadata = z.infer<typeof SkillMdMetadataSchema>;
14
+ export declare function validateSkillMd(content: string): ValidationResult<SkillMdMetadata>;
15
+ export {};