@enactprotocol/shared 2.3.5 → 2.3.8

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 (58) hide show
  1. package/README.md +1 -1
  2. package/dist/config.js +1 -1
  3. package/dist/config.js.map +1 -1
  4. package/dist/index.d.ts +3 -1
  5. package/dist/index.d.ts.map +1 -1
  6. package/dist/index.js +2 -1
  7. package/dist/index.js.map +1 -1
  8. package/dist/manifest/loader.d.ts +5 -5
  9. package/dist/manifest/loader.d.ts.map +1 -1
  10. package/dist/manifest/loader.js +15 -9
  11. package/dist/manifest/loader.js.map +1 -1
  12. package/dist/manifest/parser.d.ts +1 -1
  13. package/dist/manifest/parser.js +1 -1
  14. package/dist/manifest/validator.d.ts +4 -0
  15. package/dist/manifest/validator.d.ts.map +1 -1
  16. package/dist/manifest/validator.js +6 -0
  17. package/dist/manifest/validator.js.map +1 -1
  18. package/dist/mcp-registry.js +1 -1
  19. package/dist/paths.d.ts +20 -7
  20. package/dist/paths.d.ts.map +1 -1
  21. package/dist/paths.js +47 -14
  22. package/dist/paths.js.map +1 -1
  23. package/dist/registry.d.ts +7 -6
  24. package/dist/registry.d.ts.map +1 -1
  25. package/dist/registry.js +16 -15
  26. package/dist/registry.js.map +1 -1
  27. package/dist/resolver.d.ts +6 -5
  28. package/dist/resolver.d.ts.map +1 -1
  29. package/dist/resolver.js +20 -19
  30. package/dist/resolver.js.map +1 -1
  31. package/dist/types/manifest.d.ts +4 -4
  32. package/dist/types/manifest.d.ts.map +1 -1
  33. package/dist/types/manifest.js +4 -3
  34. package/dist/types/manifest.js.map +1 -1
  35. package/dist/types/organization.d.ts +49 -0
  36. package/dist/types/organization.d.ts.map +1 -0
  37. package/dist/types/organization.js +13 -0
  38. package/dist/types/organization.js.map +1 -0
  39. package/package.json +2 -2
  40. package/src/config.ts +1 -1
  41. package/src/index.ts +12 -0
  42. package/src/manifest/loader.ts +15 -9
  43. package/src/manifest/parser.ts +1 -1
  44. package/src/manifest/validator.ts +7 -0
  45. package/src/mcp-registry.ts +1 -1
  46. package/src/paths.ts +51 -14
  47. package/src/registry.ts +16 -15
  48. package/src/resolver.ts +20 -19
  49. package/src/types/manifest.ts +5 -4
  50. package/src/types/organization.ts +55 -0
  51. package/tests/config.test.ts +1 -1
  52. package/tests/manifest/loader.test.ts +50 -9
  53. package/tests/manifest/parser.test.ts +4 -3
  54. package/tests/manifest-types.test.ts +5 -4
  55. package/tests/paths.test.ts +12 -12
  56. package/tests/registry.test.ts +30 -30
  57. package/tests/resolver.test.ts +11 -11
  58. package/tsconfig.tsbuildinfo +1 -1
package/src/paths.ts CHANGED
@@ -4,7 +4,7 @@
4
4
 
5
5
  import { existsSync } from "node:fs";
6
6
  import { homedir } from "node:os";
7
- import { join, resolve } from "node:path";
7
+ import { dirname, join, resolve } from "node:path";
8
8
 
9
9
  /**
10
10
  * Scope for tool directories
@@ -48,44 +48,81 @@ export function getProjectEnactDir(startDir?: string): string | null {
48
48
  return null;
49
49
  }
50
50
 
51
+ /**
52
+ * Get the project-level agents directory
53
+ * Searches up from current working directory to find agents/
54
+ * @param startDir - Directory to start searching from (defaults to cwd)
55
+ * @returns Absolute path to agents/ or null if not found
56
+ */
57
+ export function getProjectAgentsDir(startDir?: string): string | null {
58
+ let currentDir = resolve(startDir ?? process.cwd());
59
+ const root = resolve("/");
60
+
61
+ while (currentDir !== root) {
62
+ const agentsDir = join(currentDir, "agents");
63
+ if (existsSync(agentsDir)) {
64
+ return agentsDir;
65
+ }
66
+ const parentDir = resolve(currentDir, "..");
67
+ if (parentDir === currentDir) {
68
+ break;
69
+ }
70
+ currentDir = parentDir;
71
+ }
72
+
73
+ return null;
74
+ }
75
+
76
+ /**
77
+ * Get the project root directory by searching for agents/ directory
78
+ * @param startDir - Directory to start searching from (defaults to cwd)
79
+ * @returns Absolute path to the project root or the startDir/cwd as fallback
80
+ */
81
+ export function getProjectRoot(startDir?: string): string {
82
+ const agentsDir = getProjectAgentsDir(startDir);
83
+ if (agentsDir) {
84
+ return dirname(agentsDir);
85
+ }
86
+ return resolve(startDir ?? process.cwd());
87
+ }
88
+
51
89
  /**
52
90
  * Get the tools directory for specified scope
53
91
  *
54
92
  * NOTE: For global scope ("user"), this is DEPRECATED.
55
- * Global tools are now tracked in ~/.enact/tools.json and stored in cache.
93
+ * Global tools are now tracked in ~/.enact/tools.json and stored in cache (~/.agents/skills/).
56
94
  * Use getToolsJsonPath("global") and getToolCachePath() from ./registry instead.
57
95
  *
58
- * For project scope, this returns .enact/tools/ where project tools are copied.
96
+ * For project scope, this returns agents/skills/ where project tools are installed.
59
97
  *
60
- * @param scope - 'user' for ~/.enact/tools/ (deprecated) or 'project' for .enact/tools/
98
+ * @param scope - 'user' for ~/.agents/skills/ or 'project' for agents/skills/
61
99
  * @param startDir - For project scope, directory to start searching from
62
100
  * @returns Absolute path to tools directory or null if project scope and not found
63
101
  * @deprecated Use registry.ts functions for global tools
64
102
  */
65
103
  export function getToolsDir(scope: ToolScope, startDir?: string): string | null {
66
104
  if (scope === "user") {
67
- // DEPRECATED: Global tools now use tools.json + cache
68
- // This path is kept for backward compatibility during migration
69
- return join(getEnactHome(), "tools");
105
+ // Global tools use ~/.agents/skills/
106
+ return getSkillsDir();
70
107
  }
71
108
 
72
- const projectDir = getProjectEnactDir(startDir);
73
- return projectDir ? join(projectDir, "tools") : null;
109
+ const agentsDir = getProjectAgentsDir(startDir);
110
+ return agentsDir ? join(agentsDir, "skills") : null;
74
111
  }
75
112
 
76
113
  /**
77
- * Get the skills directory (~/.agent/skills/)
114
+ * Get the skills directory (~/.agents/skills/)
78
115
  * This is the standard Agent Skills location for installed skills.
79
- * @returns Absolute path to ~/.agent/skills/
116
+ * @returns Absolute path to ~/.agents/skills/
80
117
  */
81
118
  export function getSkillsDir(): string {
82
- return join(homedir(), ".agent", "skills");
119
+ return join(homedir(), ".agents", "skills");
83
120
  }
84
121
 
85
122
  /**
86
- * Get the cache directory (~/.agent/skills/)
123
+ * Get the cache directory (~/.agents/skills/)
87
124
  * @deprecated Use getSkillsDir() instead
88
- * @returns Absolute path to ~/.agent/skills/
125
+ * @returns Absolute path to ~/.agents/skills/
89
126
  */
90
127
  export function getCacheDir(): string {
91
128
  return getSkillsDir();
package/src/registry.ts CHANGED
@@ -1,17 +1,16 @@
1
1
  /**
2
2
  * Local tool registry management
3
3
  *
4
- * Manages tools.json files for tracking installed tools:
4
+ * Manages skills.json files for tracking installed tools:
5
5
  * - Global: ~/.enact/tools.json (installed with -g)
6
- * - Project: .enact/tools.json (project dependencies)
6
+ * - Project: agents/skills.json (project dependencies)
7
7
  *
8
- * Tools are stored in cache and referenced by version in tools.json.
9
- * This eliminates the need for a separate ~/.enact/tools/ directory.
8
+ * Tools are stored in cache and referenced by version in the registry.
10
9
  */
11
10
 
12
11
  import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
13
- import { dirname, join } from "node:path";
14
- import { getCacheDir, getEnactHome, getProjectEnactDir } from "./paths";
12
+ import { dirname, join, resolve } from "node:path";
13
+ import { getCacheDir, getEnactHome, getProjectAgentsDir } from "./paths";
15
14
 
16
15
  /**
17
16
  * Structure of tools.json file
@@ -39,15 +38,17 @@ export interface InstalledToolInfo {
39
38
  }
40
39
 
41
40
  /**
42
- * Get the path to tools.json for the specified scope
41
+ * Get the path to the registry file for the specified scope
42
+ * - Global: ~/.enact/tools.json
43
+ * - Project: agents/skills.json
43
44
  */
44
45
  export function getToolsJsonPath(scope: RegistryScope, startDir?: string): string | null {
45
46
  if (scope === "global") {
46
47
  return join(getEnactHome(), "tools.json");
47
48
  }
48
49
 
49
- const projectDir = getProjectEnactDir(startDir);
50
- return projectDir ? join(projectDir, "tools.json") : null;
50
+ const agentsDir = getProjectAgentsDir(startDir);
51
+ return agentsDir ? join(agentsDir, "skills.json") : null;
51
52
  }
52
53
 
53
54
  /**
@@ -84,12 +85,12 @@ export function saveToolsRegistry(
84
85
  ): void {
85
86
  let registryPath = getToolsJsonPath(scope, startDir);
86
87
 
87
- // For project scope, create .enact/ directory if it doesn't exist
88
+ // For project scope, create agents/ directory if it doesn't exist
88
89
  if (!registryPath && scope === "project") {
89
- const projectRoot = startDir ?? process.cwd();
90
- const enactDir = join(projectRoot, ".enact");
91
- mkdirSync(enactDir, { recursive: true });
92
- registryPath = join(enactDir, "tools.json");
90
+ const projectRoot = resolve(startDir ?? process.cwd());
91
+ const agentsDir = join(projectRoot, "agents");
92
+ mkdirSync(agentsDir, { recursive: true });
93
+ registryPath = join(agentsDir, "skills.json");
93
94
  }
94
95
 
95
96
  if (!registryPath) {
@@ -166,7 +167,7 @@ export function getInstalledVersion(
166
167
 
167
168
  /**
168
169
  * Get the install path for a skill
169
- * Skills are stored at ~/.agent/skills/{name}/ (flat, no version subdirectory)
170
+ * Skills are stored at ~/.agents/skills/{name}/ (flat, no version subdirectory)
170
171
  */
171
172
  export function getToolCachePath(toolName: string, _version: string): string {
172
173
  const skillsDir = getCacheDir();
package/src/resolver.ts CHANGED
@@ -3,9 +3,9 @@
3
3
  *
4
4
  * Resolution order:
5
5
  * 1. Direct file path (if provided path exists)
6
- * 2. Project tools (.enact/tools/{name}/)
7
- * 3. Global tools (via ~/.enact/tools.json → ~/.agent/skills/)
8
- * 4. Skills directory (~/.agent/skills/{name}/)
6
+ * 2. Project skills (agents/skills/{name}/)
7
+ * 3. Global tools (via ~/.enact/tools.json → ~/.agents/skills/)
8
+ * 4. Skills directory (~/.agents/skills/{name}/)
9
9
  */
10
10
 
11
11
  import { existsSync } from "node:fs";
@@ -18,7 +18,7 @@ import {
18
18
  loadManifestFromDir,
19
19
  } from "./manifest/loader";
20
20
  import { manifestScriptsToActionsManifest } from "./manifest/scripts";
21
- import { getProjectEnactDir, getSkillsDir } from "./paths";
21
+ import { getProjectAgentsDir, getSkillsDir } from "./paths";
22
22
  import { getInstalledVersion, getToolCachePath, resolveAlias } from "./registry";
23
23
  import type { ToolLocation, ToolResolution } from "./types/manifest";
24
24
 
@@ -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
@@ -335,12 +336,12 @@ export function resolveTool(toolName: string, options: ResolveOptions = {}): Too
335
336
  }
336
337
  }
337
338
 
338
- // 1. Try project tools (.enact/tools/{name}/)
339
+ // 1. Try project skills (agents/skills/{name}/)
339
340
  if (!options.skipProject) {
340
- const projectDir = getProjectEnactDir(options.startDir);
341
- if (projectDir) {
342
- const projectToolsDir = join(projectDir, "tools");
343
- const toolDir = getToolPath(projectToolsDir, normalizedName);
341
+ const agentsDir = getProjectAgentsDir(options.startDir);
342
+ if (agentsDir) {
343
+ const projectSkillsDir = join(agentsDir, "skills");
344
+ const toolDir = getToolPath(projectSkillsDir, normalizedName);
344
345
  searchedLocations.push(toolDir);
345
346
 
346
347
  const result = tryLoadFromDir(toolDir, "project");
@@ -350,7 +351,7 @@ export function resolveTool(toolName: string, options: ResolveOptions = {}): Too
350
351
  }
351
352
  }
352
353
 
353
- // 2. Try global tools (via ~/.enact/tools.json → ~/.agent/skills/)
354
+ // 2. Try global tools (via ~/.enact/tools.json → ~/.agents/skills/)
354
355
  if (!options.skipUser) {
355
356
  const globalVersion = getInstalledVersion(normalizedName, "global");
356
357
  if (globalVersion) {
@@ -364,7 +365,7 @@ export function resolveTool(toolName: string, options: ResolveOptions = {}): Too
364
365
  }
365
366
  }
366
367
 
367
- // 3. Try skills directory (~/.agent/skills/{name}/)
368
+ // 3. Try skills directory (~/.agents/skills/{name}/)
368
369
  if (!options.skipCache) {
369
370
  const skillsDir = getSkillsDir();
370
371
  const skillDir = getToolPath(skillsDir, normalizedName);
@@ -560,11 +561,11 @@ export function getToolSearchPaths(toolName: string, options: ResolveOptions = {
560
561
  const normalizedName = normalizeToolName(toolName);
561
562
  const paths: string[] = [];
562
563
 
563
- // Project tools
564
+ // Project skills
564
565
  if (!options.skipProject) {
565
- const projectDir = getProjectEnactDir(options.startDir);
566
- if (projectDir) {
567
- paths.push(join(projectDir, "tools", toolNameToPath(normalizedName)));
566
+ const agentsDir = getProjectAgentsDir(options.startDir);
567
+ if (agentsDir) {
568
+ paths.push(join(agentsDir, "skills", toolNameToPath(normalizedName)));
568
569
  }
569
570
  }
570
571
 
@@ -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
+ }
@@ -534,7 +534,7 @@ describe("configuration manager", () => {
534
534
  expect(existsSync(enactHome)).toBe(true);
535
535
  });
536
536
 
537
- test("creates ~/.agent/skills/ directory", () => {
537
+ test("creates ~/.agents/skills/ directory", () => {
538
538
  const enactHome = getEnactHome();
539
539
  const cacheDir = getCacheDir();
540
540
 
@@ -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", () => {
@@ -21,6 +21,7 @@ describe("path utilities", () => {
21
21
  // Create test directory structure
22
22
  mkdirSync(NESTED_DIR, { recursive: true });
23
23
  mkdirSync(join(TEST_DIR, ".enact"), { recursive: true });
24
+ mkdirSync(join(TEST_DIR, "agents"), { recursive: true });
24
25
  });
25
26
 
26
27
  afterAll(() => {
@@ -81,36 +82,35 @@ describe("path utilities", () => {
81
82
  });
82
83
 
83
84
  describe("getToolsDir", () => {
84
- test("returns ~/.enact/tools/ for user scope", () => {
85
+ test("returns ~/.agents/skills/ for user scope", () => {
85
86
  const result = getToolsDir("user");
86
- const expected = join(homedir(), ".enact", "tools");
87
+ const expected = join(homedir(), ".agents", "skills");
87
88
  expect(result).toBe(expected);
88
89
  });
89
90
 
90
- test("returns .enact/tools/ for project scope", () => {
91
+ test("returns agents/skills/ for project scope", () => {
91
92
  const result = getToolsDir("project", TEST_DIR);
92
- expect(result).toBe(join(TEST_DIR, ".enact", "tools"));
93
+ expect(result).toBe(join(TEST_DIR, "agents", "skills"));
93
94
  });
94
95
 
95
96
  test("finds project tools in parent directory", () => {
96
97
  const result = getToolsDir("project", NESTED_DIR);
97
- expect(result).toBe(join(TEST_DIR, ".enact", "tools"));
98
+ expect(result).toBe(join(TEST_DIR, "agents", "skills"));
98
99
  });
99
100
 
100
- test("returns null for project scope when .enact not found", () => {
101
- // Similar to above - may find ~/.enact/ if it exists
102
- const result = getToolsDir("project", "/tmp/no-enact-unlikely-path");
103
- // Result will be null or a valid tools directory
101
+ test("returns null for project scope when agents/ not found", () => {
102
+ const result = getToolsDir("project", "/tmp/no-agents-unlikely-path");
103
+ // Result will be null or a valid skills directory
104
104
  if (result !== null) {
105
- expect(result.endsWith("tools")).toBe(true);
105
+ expect(result.endsWith("skills")).toBe(true);
106
106
  }
107
107
  });
108
108
  });
109
109
 
110
110
  describe("getSkillsDir", () => {
111
- test("returns ~/.agent/skills/ path", () => {
111
+ test("returns ~/.agents/skills/ path", () => {
112
112
  const result = getSkillsDir();
113
- const expected = join(homedir(), ".agent", "skills");
113
+ const expected = join(homedir(), ".agents", "skills");
114
114
  expect(result).toBe(expected);
115
115
  });
116
116
  });