@enactprotocol/shared 2.3.4 → 2.3.7

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 (40) hide show
  1. package/README.md +1 -1
  2. package/dist/index.d.ts +2 -0
  3. package/dist/index.d.ts.map +1 -1
  4. package/dist/index.js +1 -0
  5. package/dist/index.js.map +1 -1
  6. package/dist/manifest/loader.d.ts +5 -5
  7. package/dist/manifest/loader.d.ts.map +1 -1
  8. package/dist/manifest/loader.js +15 -9
  9. package/dist/manifest/loader.js.map +1 -1
  10. package/dist/manifest/parser.d.ts +1 -1
  11. package/dist/manifest/parser.js +1 -1
  12. package/dist/manifest/validator.d.ts +4 -0
  13. package/dist/manifest/validator.d.ts.map +1 -1
  14. package/dist/manifest/validator.js +6 -0
  15. package/dist/manifest/validator.js.map +1 -1
  16. package/dist/resolver.d.ts +3 -2
  17. package/dist/resolver.d.ts.map +1 -1
  18. package/dist/resolver.js +5 -4
  19. package/dist/resolver.js.map +1 -1
  20. package/dist/types/manifest.d.ts +4 -4
  21. package/dist/types/manifest.d.ts.map +1 -1
  22. package/dist/types/manifest.js +4 -3
  23. package/dist/types/manifest.js.map +1 -1
  24. package/dist/types/organization.d.ts +49 -0
  25. package/dist/types/organization.d.ts.map +1 -0
  26. package/dist/types/organization.js +13 -0
  27. package/dist/types/organization.js.map +1 -0
  28. package/package.json +2 -2
  29. package/src/index.ts +10 -0
  30. package/src/manifest/loader.ts +15 -9
  31. package/src/manifest/parser.ts +1 -1
  32. package/src/manifest/validator.ts +7 -0
  33. package/src/resolver.ts +5 -4
  34. package/src/types/manifest.ts +5 -4
  35. package/src/types/organization.ts +55 -0
  36. package/tests/manifest/loader.test.ts +50 -9
  37. package/tests/manifest/parser.test.ts +4 -3
  38. package/tests/manifest-types.test.ts +5 -4
  39. package/tests/resolver.test.ts +5 -5
  40. package/tsconfig.tsbuildinfo +1 -1
@@ -52,7 +52,7 @@ export interface LoadManifestOptions extends ValidateManifestOptions {
52
52
  /**
53
53
  * Load a manifest from a file path
54
54
  *
55
- * @param filePath - Path to the manifest file (SKILL.md, skill.yaml, skill.yml, enact.md, enact.yaml, or enact.yml)
55
+ * @param filePath - Path to the manifest file (SKILL.md, skill.package.yml, skill.yml, enact.md, enact.yaml, or enact.yml)
56
56
  * @param options - Options for loading and validation
57
57
  * @returns LoadedManifest with validated manifest and metadata
58
58
  * @throws ManifestLoadError if file doesn't exist, parse fails, or validation fails
@@ -120,7 +120,7 @@ export function loadManifest(filePath: string, options: LoadManifestOptions = {}
120
120
  /**
121
121
  * Load SKILL.md for its body content and frontmatter fields.
122
122
  * Does NOT validate as a full ToolManifest — used only to extract
123
- * agent-facing documentation in two-file mode (skill.yaml + SKILL.md).
123
+ * agent-facing documentation in two-file mode (skill.package.yml + SKILL.md).
124
124
  */
125
125
  function loadSkillDoc(
126
126
  filePath: string
@@ -142,10 +142,10 @@ function loadSkillDoc(
142
142
  * Find and load a manifest from a directory
143
143
  *
144
144
  * Supports two modes:
145
- * 1. **Two-file model**: If both `skill.yaml` and `SKILL.md` exist, `skill.yaml` is
146
- * the package manifest and `SKILL.md` provides agent-facing documentation (body → `doc`).
147
- * Also supports legacy `enact.yaml` in place of `skill.yaml`.
148
- * 2. **Single-file fallback**: Searches for SKILL.md, skill.yaml, skill.yml, enact.md, enact.yaml, or enact.yml
145
+ * 1. **Two-file model**: If both a package manifest (`skill.package.yaml`, `skill.package.yml`, etc.) and `SKILL.md` exist,
146
+ * the package manifest provides execution metadata and `SKILL.md` provides agent-facing documentation (body → `doc`).
147
+ * Also supports legacy `enact.yaml` in place of `skill.package.yml`.
148
+ * 2. **Single-file fallback**: Searches for SKILL.md, skill.package.yaml, skill.package.yml, skill.yml, enact.md, enact.yaml, or enact.yml
149
149
  * and uses the first match as the complete manifest.
150
150
  *
151
151
  * @param dir - Directory to search for manifest
@@ -160,9 +160,15 @@ export function loadManifestFromDir(
160
160
  const skillMdPath = join(dir, "SKILL.md");
161
161
  const hasSkillMd = existsSync(skillMdPath);
162
162
 
163
- // Find config file (skill.yaml, skill.yml, or legacy enact.yaml/enact.yml)
163
+ // Find config file (skill.package.yaml, skill.package.yml, skill.yml, or legacy enact.yaml/enact.yml)
164
164
  let enactConfigPath: string | null = null;
165
- for (const f of ["skill.yaml", "skill.yml", "enact.yaml", "enact.yml"]) {
165
+ for (const f of [
166
+ "skill.package.yaml",
167
+ "skill.package.yml",
168
+ "skill.yml",
169
+ "enact.yaml",
170
+ "enact.yml",
171
+ ]) {
166
172
  const p = join(dir, f);
167
173
  if (existsSync(p)) {
168
174
  enactConfigPath = p;
@@ -170,7 +176,7 @@ export function loadManifestFromDir(
170
176
  }
171
177
  }
172
178
 
173
- // Two-file model: skill.yaml + SKILL.md
179
+ // Two-file model: skill.package.yaml/skill.package.yml + SKILL.md
174
180
  if (enactConfigPath && hasSkillMd) {
175
181
  const loaded = loadManifest(enactConfigPath, options);
176
182
  const skillDoc = loadSkillDoc(skillMdPath);
@@ -3,7 +3,7 @@
3
3
  *
4
4
  * Handles parsing of:
5
5
  * - SKILL.md files (YAML frontmatter + Markdown body) - primary format
6
- * - skill.yaml/yml files (pure YAML) - package manifest
6
+ * - skill.package.yml/yml files (pure YAML) - package manifest
7
7
  * - enact.yaml/yml files (pure YAML) - legacy format
8
8
  * - enact.md files (YAML frontmatter + Markdown body) - legacy format
9
9
  */
@@ -359,5 +359,12 @@ export function isValidTimeout(timeout: string): boolean {
359
359
  return GO_DURATION_REGEX.test(timeout);
360
360
  }
361
361
 
362
+ /**
363
+ * Check if a tool name is org-scoped (starts with @)
364
+ */
365
+ export function isOrgScoped(name: string): boolean {
366
+ return name.startsWith("@");
367
+ }
368
+
362
369
  // Export the schema for external use
363
370
  export { ToolManifestSchema };
package/src/resolver.ts CHANGED
@@ -127,11 +127,12 @@ export function parseActionSpecifier(specifier: string): ParsedActionSpecifier {
127
127
  }
128
128
 
129
129
  /**
130
- * Convert tool name to directory path
131
- * Strips the @ prefix for disk paths: "@org/my-skill" -> "org/my-skill"
130
+ * Convert tool name to directory path.
131
+ * Preserves @ prefix for npm-style layout: "@org/my-skill" -> "@org/my-skill"
132
+ * User tools unchanged: "user/my-skill" -> "user/my-skill"
132
133
  */
133
134
  export function toolNameToPath(name: string): string {
134
- return name.replace(/^@/, "").replace(/\\/g, "/");
135
+ return name.replace(/\\/g, "/");
135
136
  }
136
137
 
137
138
  /**
@@ -151,7 +152,7 @@ export function getToolPath(toolsDir: string, toolName: string): string {
151
152
  /**
152
153
  * Try to load a tool from a specific directory
153
154
  *
154
- * Supports two-file model (skill.yaml + SKILL.md) via loadManifestFromDir().
155
+ * Supports two-file model (skill.package.yml + SKILL.md) via loadManifestFromDir().
155
156
  *
156
157
  * @param dir - Directory to check
157
158
  * @param location - The location type for metadata
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * TypeScript types for Enact tool manifests
3
- * These types define the structure of SKILL.md (and skill.yaml/legacy enact.yaml/enact.md) frontmatter
3
+ * These types define the structure of SKILL.md (and skill.package.yml/legacy enact.yaml/enact.md) frontmatter
4
4
  */
5
5
 
6
6
  import type { JSONSchema7 } from "json-schema";
@@ -104,7 +104,7 @@ export interface ToolExample {
104
104
 
105
105
  /**
106
106
  * Complete tool manifest structure
107
- * This represents the YAML frontmatter in SKILL.md (or skill.yaml/legacy enact.md/enact.yaml)
107
+ * This represents the YAML frontmatter in SKILL.md (or skill.package.yml/legacy enact.md/enact.yaml)
108
108
  */
109
109
  export interface ToolManifest {
110
110
  // ==================== Required Fields ====================
@@ -310,12 +310,13 @@ export interface ToolResolution {
310
310
  /**
311
311
  * Supported manifest file names
312
312
  * SKILL.md is the primary format (aligned with Anthropic Agent Skills)
313
- * skill.yaml/yml is the package manifest
313
+ * skill.package.yml/yml is the package manifest
314
314
  * enact.md/yaml/yml are supported for backwards compatibility
315
315
  */
316
316
  export const MANIFEST_FILES = [
317
317
  "SKILL.md",
318
- "skill.yaml",
318
+ "skill.package.yaml",
319
+ "skill.package.yml",
319
320
  "skill.yml",
320
321
  "enact.md",
321
322
  "enact.yaml",
@@ -0,0 +1,55 @@
1
+ /**
2
+ * Organization types for org-scoped tool namespaces.
3
+ *
4
+ * Organizations allow businesses to reserve @org scopes (e.g., @anthropic/tool-name)
5
+ * and manage team publishing permissions.
6
+ */
7
+
8
+ /**
9
+ * Organization membership roles.
10
+ * - owner: Full control (manage org, members, tools, delete org)
11
+ * - admin: Publish tools + manage members
12
+ * - member: Publish tools only
13
+ */
14
+ export type OrgRole = "owner" | "admin" | "member";
15
+
16
+ /**
17
+ * Organization entity
18
+ */
19
+ export interface Organization {
20
+ id: string;
21
+ /** Org name without @ prefix (e.g., "anthropic") */
22
+ name: string;
23
+ display_name: string | null;
24
+ description: string | null;
25
+ avatar_url: string | null;
26
+ created_by: string;
27
+ created_at: string;
28
+ }
29
+
30
+ /**
31
+ * Organization member
32
+ */
33
+ export interface OrgMember {
34
+ org_id: string;
35
+ user_id: string;
36
+ username: string;
37
+ role: OrgRole;
38
+ added_at: string;
39
+ added_by: string | null;
40
+ }
41
+
42
+ /**
43
+ * Full org info with counts (for API responses)
44
+ */
45
+ export interface OrgInfo extends Organization {
46
+ member_count: number;
47
+ tool_count: number;
48
+ }
49
+
50
+ /**
51
+ * Check if a tool name is org-scoped (starts with @)
52
+ */
53
+ export function isOrgScoped(name: string): boolean {
54
+ return name.startsWith("@");
55
+ }
@@ -104,11 +104,11 @@ description: A minimal tool
104
104
  });
105
105
 
106
106
  describe("loadManifestFromDir", () => {
107
- test("loads manifest from directory with skill.yaml", () => {
107
+ test("loads manifest from directory with skill.package.yml", () => {
108
108
  const testDir = join(TEST_DIR, "yaml-dir");
109
109
  mkdirSync(testDir, { recursive: true });
110
110
  writeFileSync(
111
- join(testDir, "skill.yaml"),
111
+ join(testDir, "skill.package.yml"),
112
112
  `
113
113
  name: test/yaml
114
114
  description: Test YAML manifest
@@ -156,12 +156,53 @@ description: Test Markdown manifest
156
156
  expect(result.format).toBe("md");
157
157
  });
158
158
 
159
- test("prefers skill.yaml over legacy enact.yaml", () => {
159
+ test("loads manifest from directory with skill.package.yaml", () => {
160
+ const testDir = join(TEST_DIR, "package-yaml-dir");
161
+ mkdirSync(testDir, { recursive: true });
162
+ writeFileSync(
163
+ join(testDir, "skill.package.yaml"),
164
+ `
165
+ name: test/package-yaml
166
+ description: Test package YAML manifest
167
+ `,
168
+ "utf-8"
169
+ );
170
+
171
+ const result = loadManifestFromDir(testDir);
172
+ expect(result.manifest.name).toBe("test/package-yaml");
173
+ });
174
+
175
+ test("prefers skill.package.yaml over skill.package.yml", () => {
176
+ const testDir = join(TEST_DIR, "package-vs-skill-dir");
177
+ mkdirSync(testDir, { recursive: true });
178
+
179
+ writeFileSync(
180
+ join(testDir, "skill.package.yaml"),
181
+ `
182
+ name: test/package-preferred
183
+ description: Package version
184
+ `,
185
+ "utf-8"
186
+ );
187
+ writeFileSync(
188
+ join(testDir, "skill.package.yml"),
189
+ `
190
+ name: test/skill-version
191
+ description: Skill version
192
+ `,
193
+ "utf-8"
194
+ );
195
+
196
+ const result = loadManifestFromDir(testDir);
197
+ expect(result.manifest.name).toBe("test/package-preferred");
198
+ });
199
+
200
+ test("prefers skill.package.yml over legacy enact.yaml", () => {
160
201
  const testDir = join(TEST_DIR, "both-dir");
161
202
  mkdirSync(testDir, { recursive: true });
162
203
 
163
204
  writeFileSync(
164
- join(testDir, "skill.yaml"),
205
+ join(testDir, "skill.package.yml"),
165
206
  `
166
207
  name: test/skill-preferred
167
208
  description: Skill version
@@ -191,13 +232,13 @@ description: Legacy version
191
232
  });
192
233
 
193
234
  describe("findManifestFile", () => {
194
- test("finds skill.yaml", () => {
235
+ test("finds skill.package.yml", () => {
195
236
  const testDir = join(TEST_DIR, "find-yaml");
196
237
  mkdirSync(testDir, { recursive: true });
197
- writeFileSync(join(testDir, "skill.yaml"), "name: test/find", "utf-8");
238
+ writeFileSync(join(testDir, "skill.package.yml"), "name: test/find", "utf-8");
198
239
 
199
240
  const result = findManifestFile(testDir);
200
- expect(result).toBe(join(testDir, "skill.yaml"));
241
+ expect(result).toBe(join(testDir, "skill.package.yml"));
201
242
  });
202
243
 
203
244
  test("finds enact.md", () => {
@@ -222,7 +263,7 @@ description: Legacy version
222
263
  test("returns true when manifest exists", () => {
223
264
  const testDir = join(TEST_DIR, "has-manifest");
224
265
  mkdirSync(testDir, { recursive: true });
225
- writeFileSync(join(testDir, "skill.yaml"), "name: test/has", "utf-8");
266
+ writeFileSync(join(testDir, "skill.package.yml"), "name: test/has", "utf-8");
226
267
 
227
268
  expect(hasManifest(testDir)).toBe(true);
228
269
  });
@@ -264,7 +305,7 @@ description: Legacy version
264
305
  const testDir = join(TEST_DIR, "try-success");
265
306
  mkdirSync(testDir, { recursive: true });
266
307
  writeFileSync(
267
- join(testDir, "skill.yaml"),
308
+ join(testDir, "skill.package.yml"),
268
309
  `
269
310
  name: test/try
270
311
  description: Test try loading
@@ -260,10 +260,11 @@ Body here.
260
260
 
261
261
  describe("detectFormat", () => {
262
262
  test("detects .yaml extension", () => {
263
- expect(detectFormat("skill.yaml")).toBe("yaml");
263
+ expect(detectFormat("skill.package.yaml")).toBe("yaml");
264
+ expect(detectFormat("skill.package.yml")).toBe("yaml");
264
265
  expect(detectFormat("enact.yaml")).toBe("yaml");
265
266
  expect(detectFormat("tool.yaml")).toBe("yaml");
266
- expect(detectFormat("/path/to/skill.yaml")).toBe("yaml");
267
+ expect(detectFormat("/path/to/skill.package.yml")).toBe("yaml");
267
268
  });
268
269
 
269
270
  test("detects .yml extension", () => {
@@ -298,7 +299,7 @@ Body here.
298
299
  name: org/tool
299
300
  description: Test
300
301
  `;
301
- const result = parseManifestAuto(yaml, "skill.yaml");
302
+ const result = parseManifestAuto(yaml, "skill.package.yml");
302
303
  expect(result.manifest.name).toBe("org/tool");
303
304
  expect(result.format).toBe("yaml");
304
305
  });
@@ -310,7 +310,7 @@ describe("manifest types", () => {
310
310
  },
311
311
  sourceDir: "/home/user/.enact/tools/org/tool",
312
312
  location: "user",
313
- manifestPath: "/home/user/.enact/tools/org/tool/skill.yaml",
313
+ manifestPath: "/home/user/.enact/tools/org/tool/skill.package.yml",
314
314
  version: "1.0.0",
315
315
  };
316
316
 
@@ -332,7 +332,7 @@ describe("manifest types", () => {
332
332
  manifest: { name: "test", description: "test" },
333
333
  sourceDir: "/test",
334
334
  location: loc,
335
- manifestPath: "/test/skill.yaml",
335
+ manifestPath: "/test/skill.package.yml",
336
336
  };
337
337
  expect(resolution.location).toBe(loc);
338
338
  }
@@ -342,12 +342,13 @@ describe("manifest types", () => {
342
342
  describe("constants", () => {
343
343
  test("MANIFEST_FILES contains expected files", () => {
344
344
  expect(MANIFEST_FILES).toContain("SKILL.md");
345
- expect(MANIFEST_FILES).toContain("skill.yaml");
345
+ expect(MANIFEST_FILES).toContain("skill.package.yaml");
346
+ expect(MANIFEST_FILES).toContain("skill.package.yml");
346
347
  expect(MANIFEST_FILES).toContain("skill.yml");
347
348
  expect(MANIFEST_FILES).toContain("enact.md");
348
349
  expect(MANIFEST_FILES).toContain("enact.yaml");
349
350
  expect(MANIFEST_FILES).toContain("enact.yml");
350
- expect(MANIFEST_FILES.length).toBe(6);
351
+ expect(MANIFEST_FILES.length).toBe(7);
351
352
  });
352
353
 
353
354
  test("PACKAGE_MANIFEST_FILE is correct", () => {
@@ -25,7 +25,7 @@ describe("tool resolver", () => {
25
25
 
26
26
  // Create a project-level tool
27
27
  writeFileSync(
28
- join(PROJECT_ENACT_DIR, "tools", "test", "project-tool", "skill.yaml"),
28
+ join(PROJECT_ENACT_DIR, "tools", "test", "project-tool", "skill.package.yml"),
29
29
  `
30
30
  name: test/project-tool
31
31
  description: A project-level test tool
@@ -37,7 +37,7 @@ version: "1.0.0"
37
37
  // Create a direct tool directory for path-based resolution
38
38
  mkdirSync(join(TEST_DIR, "direct-tool"), { recursive: true });
39
39
  writeFileSync(
40
- join(TEST_DIR, "direct-tool", "skill.yaml"),
40
+ join(TEST_DIR, "direct-tool", "skill.package.yml"),
41
41
  `
42
42
  name: test/direct-tool
43
43
  description: A directly referenced tool
@@ -91,8 +91,8 @@ Documentation here.
91
91
  expect(toolNameToPath("acme/greeter")).toBe("acme/greeter");
92
92
  });
93
93
 
94
- test("strips @ prefix for disk paths", () => {
95
- expect(toolNameToPath("@acme/greeter")).toBe("acme/greeter");
94
+ test("preserves @ prefix for npm-style disk layout", () => {
95
+ expect(toolNameToPath("@acme/greeter")).toBe("@acme/greeter");
96
96
  });
97
97
 
98
98
  test("normalizes backslashes", () => {
@@ -146,7 +146,7 @@ Documentation here.
146
146
  });
147
147
 
148
148
  test("resolves tool from manifest file directly", () => {
149
- const manifestPath = join(TEST_DIR, "direct-tool", "skill.yaml");
149
+ const manifestPath = join(TEST_DIR, "direct-tool", "skill.package.yml");
150
150
  const result = resolveToolFromPath(manifestPath);
151
151
 
152
152
  expect(result.manifest.name).toBe("test/direct-tool");