@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.
- package/README.md +1 -1
- package/dist/config.js +1 -1
- package/dist/config.js.map +1 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/dist/manifest/loader.d.ts +5 -5
- package/dist/manifest/loader.d.ts.map +1 -1
- package/dist/manifest/loader.js +15 -9
- package/dist/manifest/loader.js.map +1 -1
- package/dist/manifest/parser.d.ts +1 -1
- package/dist/manifest/parser.js +1 -1
- package/dist/manifest/validator.d.ts +4 -0
- package/dist/manifest/validator.d.ts.map +1 -1
- package/dist/manifest/validator.js +6 -0
- package/dist/manifest/validator.js.map +1 -1
- package/dist/mcp-registry.js +1 -1
- package/dist/paths.d.ts +20 -7
- package/dist/paths.d.ts.map +1 -1
- package/dist/paths.js +47 -14
- package/dist/paths.js.map +1 -1
- package/dist/registry.d.ts +7 -6
- package/dist/registry.d.ts.map +1 -1
- package/dist/registry.js +16 -15
- package/dist/registry.js.map +1 -1
- package/dist/resolver.d.ts +6 -5
- package/dist/resolver.d.ts.map +1 -1
- package/dist/resolver.js +20 -19
- package/dist/resolver.js.map +1 -1
- package/dist/types/manifest.d.ts +4 -4
- package/dist/types/manifest.d.ts.map +1 -1
- package/dist/types/manifest.js +4 -3
- package/dist/types/manifest.js.map +1 -1
- package/dist/types/organization.d.ts +49 -0
- package/dist/types/organization.d.ts.map +1 -0
- package/dist/types/organization.js +13 -0
- package/dist/types/organization.js.map +1 -0
- package/package.json +2 -2
- package/src/config.ts +1 -1
- package/src/index.ts +12 -0
- package/src/manifest/loader.ts +15 -9
- package/src/manifest/parser.ts +1 -1
- package/src/manifest/validator.ts +7 -0
- package/src/mcp-registry.ts +1 -1
- package/src/paths.ts +51 -14
- package/src/registry.ts +16 -15
- package/src/resolver.ts +20 -19
- package/src/types/manifest.ts +5 -4
- package/src/types/organization.ts +55 -0
- package/tests/config.test.ts +1 -1
- package/tests/manifest/loader.test.ts +50 -9
- package/tests/manifest/parser.test.ts +4 -3
- package/tests/manifest-types.test.ts +5 -4
- package/tests/paths.test.ts +12 -12
- package/tests/registry.test.ts +30 -30
- package/tests/resolver.test.ts +11 -11
- 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
|
|
96
|
+
* For project scope, this returns agents/skills/ where project tools are installed.
|
|
59
97
|
*
|
|
60
|
-
* @param scope - 'user' for ~/.
|
|
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
|
-
//
|
|
68
|
-
|
|
69
|
-
return join(getEnactHome(), "tools");
|
|
105
|
+
// Global tools use ~/.agents/skills/
|
|
106
|
+
return getSkillsDir();
|
|
70
107
|
}
|
|
71
108
|
|
|
72
|
-
const
|
|
73
|
-
return
|
|
109
|
+
const agentsDir = getProjectAgentsDir(startDir);
|
|
110
|
+
return agentsDir ? join(agentsDir, "skills") : null;
|
|
74
111
|
}
|
|
75
112
|
|
|
76
113
|
/**
|
|
77
|
-
* Get the skills directory (~/.
|
|
114
|
+
* Get the skills directory (~/.agents/skills/)
|
|
78
115
|
* This is the standard Agent Skills location for installed skills.
|
|
79
|
-
* @returns Absolute path to ~/.
|
|
116
|
+
* @returns Absolute path to ~/.agents/skills/
|
|
80
117
|
*/
|
|
81
118
|
export function getSkillsDir(): string {
|
|
82
|
-
return join(homedir(), ".
|
|
119
|
+
return join(homedir(), ".agents", "skills");
|
|
83
120
|
}
|
|
84
121
|
|
|
85
122
|
/**
|
|
86
|
-
* Get the cache directory (~/.
|
|
123
|
+
* Get the cache directory (~/.agents/skills/)
|
|
87
124
|
* @deprecated Use getSkillsDir() instead
|
|
88
|
-
* @returns Absolute path to ~/.
|
|
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
|
|
4
|
+
* Manages skills.json files for tracking installed tools:
|
|
5
5
|
* - Global: ~/.enact/tools.json (installed with -g)
|
|
6
|
-
* - Project:
|
|
6
|
+
* - Project: agents/skills.json (project dependencies)
|
|
7
7
|
*
|
|
8
|
-
* Tools are stored in cache and referenced by version in
|
|
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,
|
|
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
|
|
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
|
|
50
|
-
return
|
|
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
|
|
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
|
|
91
|
-
mkdirSync(
|
|
92
|
-
registryPath = join(
|
|
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 ~/.
|
|
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
|
|
7
|
-
* 3. Global tools (via ~/.enact/tools.json → ~/.
|
|
8
|
-
* 4. Skills directory (~/.
|
|
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 {
|
|
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
|
-
*
|
|
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(
|
|
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.
|
|
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
|
|
339
|
+
// 1. Try project skills (agents/skills/{name}/)
|
|
339
340
|
if (!options.skipProject) {
|
|
340
|
-
const
|
|
341
|
-
if (
|
|
342
|
-
const
|
|
343
|
-
const toolDir = getToolPath(
|
|
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 → ~/.
|
|
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 (~/.
|
|
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
|
|
564
|
+
// Project skills
|
|
564
565
|
if (!options.skipProject) {
|
|
565
|
-
const
|
|
566
|
-
if (
|
|
567
|
-
paths.push(join(
|
|
566
|
+
const agentsDir = getProjectAgentsDir(options.startDir);
|
|
567
|
+
if (agentsDir) {
|
|
568
|
+
paths.push(join(agentsDir, "skills", toolNameToPath(normalizedName)));
|
|
568
569
|
}
|
|
569
570
|
}
|
|
570
571
|
|
package/src/types/manifest.ts
CHANGED
|
@@ -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.
|
|
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.
|
|
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.
|
|
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
|
+
}
|
package/tests/config.test.ts
CHANGED
|
@@ -534,7 +534,7 @@ describe("configuration manager", () => {
|
|
|
534
534
|
expect(existsSync(enactHome)).toBe(true);
|
|
535
535
|
});
|
|
536
536
|
|
|
537
|
-
test("creates ~/.
|
|
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.
|
|
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.
|
|
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("
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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(
|
|
351
|
+
expect(MANIFEST_FILES.length).toBe(7);
|
|
351
352
|
});
|
|
352
353
|
|
|
353
354
|
test("PACKAGE_MANIFEST_FILE is correct", () => {
|
package/tests/paths.test.ts
CHANGED
|
@@ -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 ~/.
|
|
85
|
+
test("returns ~/.agents/skills/ for user scope", () => {
|
|
85
86
|
const result = getToolsDir("user");
|
|
86
|
-
const expected = join(homedir(), ".
|
|
87
|
+
const expected = join(homedir(), ".agents", "skills");
|
|
87
88
|
expect(result).toBe(expected);
|
|
88
89
|
});
|
|
89
90
|
|
|
90
|
-
test("returns
|
|
91
|
+
test("returns agents/skills/ for project scope", () => {
|
|
91
92
|
const result = getToolsDir("project", TEST_DIR);
|
|
92
|
-
expect(result).toBe(join(TEST_DIR, "
|
|
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, "
|
|
98
|
+
expect(result).toBe(join(TEST_DIR, "agents", "skills"));
|
|
98
99
|
});
|
|
99
100
|
|
|
100
|
-
test("returns null for project scope when
|
|
101
|
-
|
|
102
|
-
|
|
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("
|
|
105
|
+
expect(result.endsWith("skills")).toBe(true);
|
|
106
106
|
}
|
|
107
107
|
});
|
|
108
108
|
});
|
|
109
109
|
|
|
110
110
|
describe("getSkillsDir", () => {
|
|
111
|
-
test("returns ~/.
|
|
111
|
+
test("returns ~/.agents/skills/ path", () => {
|
|
112
112
|
const result = getSkillsDir();
|
|
113
|
-
const expected = join(homedir(), ".
|
|
113
|
+
const expected = join(homedir(), ".agents", "skills");
|
|
114
114
|
expect(result).toBe(expected);
|
|
115
115
|
});
|
|
116
116
|
});
|