@enactprotocol/shared 2.2.2 → 2.3.1
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 -18
- package/dist/config.d.ts +12 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +32 -6
- package/dist/config.js.map +1 -1
- package/dist/execution/action-command.d.ts +131 -0
- package/dist/execution/action-command.d.ts.map +1 -0
- package/dist/execution/action-command.js +300 -0
- package/dist/execution/action-command.js.map +1 -0
- package/dist/execution/command.d.ts +8 -8
- package/dist/execution/command.js +6 -6
- package/dist/execution/index.d.ts +1 -0
- package/dist/execution/index.d.ts.map +1 -1
- package/dist/execution/index.js +2 -0
- package/dist/execution/index.js.map +1 -1
- package/dist/execution/types.d.ts +5 -2
- package/dist/execution/types.d.ts.map +1 -1
- package/dist/execution/types.js.map +1 -1
- package/dist/index.d.ts +9 -7
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +14 -5
- package/dist/index.js.map +1 -1
- package/dist/manifest/actions-loader.d.ts +29 -0
- package/dist/manifest/actions-loader.d.ts.map +1 -0
- package/dist/manifest/actions-loader.js +34 -0
- package/dist/manifest/actions-loader.js.map +1 -0
- package/dist/manifest/actions-parser.d.ts +69 -0
- package/dist/manifest/actions-parser.d.ts.map +1 -0
- package/dist/manifest/actions-parser.js +265 -0
- package/dist/manifest/actions-parser.js.map +1 -0
- package/dist/manifest/index.d.ts +2 -0
- package/dist/manifest/index.d.ts.map +1 -1
- package/dist/manifest/index.js +4 -0
- package/dist/manifest/index.js.map +1 -1
- package/dist/manifest/loader.d.ts +7 -2
- package/dist/manifest/loader.d.ts.map +1 -1
- package/dist/manifest/loader.js +71 -4
- package/dist/manifest/loader.js.map +1 -1
- package/dist/manifest/parser.d.ts +1 -0
- package/dist/manifest/parser.d.ts.map +1 -1
- package/dist/manifest/parser.js +1 -0
- package/dist/manifest/parser.js.map +1 -1
- package/dist/manifest/scripts.d.ts +19 -0
- package/dist/manifest/scripts.d.ts.map +1 -0
- package/dist/manifest/scripts.js +102 -0
- package/dist/manifest/scripts.js.map +1 -0
- package/dist/manifest/validator.d.ts +1 -8
- package/dist/manifest/validator.d.ts.map +1 -1
- package/dist/manifest/validator.js +14 -13
- package/dist/manifest/validator.js.map +1 -1
- package/dist/mcp-registry.js +5 -5
- package/dist/mcp-registry.js.map +1 -1
- package/dist/paths.d.ts +9 -2
- package/dist/paths.d.ts.map +1 -1
- package/dist/paths.js +12 -3
- package/dist/paths.js.map +1 -1
- package/dist/registry.d.ts +47 -2
- package/dist/registry.d.ts.map +1 -1
- package/dist/registry.js +100 -7
- package/dist/registry.js.map +1 -1
- package/dist/resolver.d.ts +55 -4
- package/dist/resolver.d.ts.map +1 -1
- package/dist/resolver.js +144 -77
- package/dist/resolver.js.map +1 -1
- package/dist/types/actions.d.ts +194 -0
- package/dist/types/actions.d.ts.map +1 -0
- package/dist/types/actions.js +32 -0
- package/dist/types/actions.js.map +1 -0
- package/dist/types/index.d.ts +3 -1
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/index.js +1 -0
- package/dist/types/index.js.map +1 -1
- package/dist/types/manifest.d.ts +50 -5
- package/dist/types/manifest.d.ts.map +1 -1
- package/dist/types/manifest.js +10 -2
- package/dist/types/manifest.js.map +1 -1
- package/package.json +2 -2
- package/src/config.ts +48 -6
- package/src/execution/action-command.ts +417 -0
- package/src/execution/command.ts +8 -8
- package/src/execution/index.ts +17 -0
- package/src/execution/types.ts +13 -2
- package/src/index.ts +43 -0
- package/src/manifest/actions-loader.ts +49 -0
- package/src/manifest/index.ts +12 -0
- package/src/manifest/loader.ts +77 -4
- package/src/manifest/parser.ts +1 -0
- package/src/manifest/scripts.ts +116 -0
- package/src/manifest/validator.ts +15 -14
- package/src/mcp-registry.ts +5 -5
- package/src/paths.ts +13 -3
- package/src/registry.ts +136 -7
- package/src/resolver.ts +185 -79
- package/src/types/actions.ts +223 -0
- package/src/types/index.ts +11 -0
- package/src/types/manifest.ts +67 -6
- package/tests/action-command.test.ts +249 -0
- package/tests/config-normalization.test.ts +279 -0
- package/tests/config.test.ts +4 -1
- package/tests/effective-input-schema.test.ts +86 -0
- package/tests/fixtures/valid-tool.md +5 -12
- package/tests/fixtures/valid-tool.yaml +3 -10
- package/tests/hooks.test.ts +177 -0
- package/tests/manifest/loader.test.ts +34 -20
- package/tests/manifest/parser.test.ts +11 -15
- package/tests/manifest/validator.test.ts +7 -17
- package/tests/manifest-types.test.ts +9 -11
- package/tests/paths.test.ts +11 -4
- package/tests/registry.test.ts +204 -8
- package/tests/resolver.test.ts +90 -6
- package/tsconfig.tsbuildinfo +1 -1
|
@@ -34,7 +34,7 @@ describe("manifest loader", () => {
|
|
|
34
34
|
const filePath = join(FIXTURES_DIR, "valid-tool.yaml");
|
|
35
35
|
const result = loadManifest(filePath);
|
|
36
36
|
|
|
37
|
-
expect(result.manifest.name).toBe("acme/
|
|
37
|
+
expect(result.manifest.name).toBe("acme/greeter");
|
|
38
38
|
expect(result.manifest.description).toBe("Greets users by name");
|
|
39
39
|
expect(result.manifest.version).toBe("1.0.0");
|
|
40
40
|
expect(result.format).toBe("yaml");
|
|
@@ -46,7 +46,7 @@ describe("manifest loader", () => {
|
|
|
46
46
|
const filePath = join(FIXTURES_DIR, "valid-tool.md");
|
|
47
47
|
const result = loadManifest(filePath);
|
|
48
48
|
|
|
49
|
-
expect(result.manifest.name).toBe("acme/
|
|
49
|
+
expect(result.manifest.name).toBe("acme/analyzer");
|
|
50
50
|
expect(result.manifest.description).toBe("Analyzes documentation for completeness");
|
|
51
51
|
expect(result.format).toBe("md");
|
|
52
52
|
expect(result.body).toBeDefined();
|
|
@@ -104,11 +104,11 @@ description: A minimal tool
|
|
|
104
104
|
});
|
|
105
105
|
|
|
106
106
|
describe("loadManifestFromDir", () => {
|
|
107
|
-
test("loads manifest from directory with
|
|
107
|
+
test("loads manifest from directory with skill.yaml", () => {
|
|
108
108
|
const testDir = join(TEST_DIR, "yaml-dir");
|
|
109
109
|
mkdirSync(testDir, { recursive: true });
|
|
110
110
|
writeFileSync(
|
|
111
|
-
join(testDir, "
|
|
111
|
+
join(testDir, "skill.yaml"),
|
|
112
112
|
`
|
|
113
113
|
name: test/yaml
|
|
114
114
|
description: Test YAML manifest
|
|
@@ -120,6 +120,22 @@ description: Test YAML manifest
|
|
|
120
120
|
expect(result.manifest.name).toBe("test/yaml");
|
|
121
121
|
});
|
|
122
122
|
|
|
123
|
+
test("loads manifest from directory with legacy enact.yaml", () => {
|
|
124
|
+
const testDir = join(TEST_DIR, "legacy-yaml-dir");
|
|
125
|
+
mkdirSync(testDir, { recursive: true });
|
|
126
|
+
writeFileSync(
|
|
127
|
+
join(testDir, "enact.yaml"),
|
|
128
|
+
`
|
|
129
|
+
name: test/legacy-yaml
|
|
130
|
+
description: Test legacy YAML manifest
|
|
131
|
+
`,
|
|
132
|
+
"utf-8"
|
|
133
|
+
);
|
|
134
|
+
|
|
135
|
+
const result = loadManifestFromDir(testDir);
|
|
136
|
+
expect(result.manifest.name).toBe("test/legacy-yaml");
|
|
137
|
+
});
|
|
138
|
+
|
|
123
139
|
test("loads manifest from directory with enact.md", () => {
|
|
124
140
|
const testDir = join(TEST_DIR, "md-dir");
|
|
125
141
|
mkdirSync(testDir, { recursive: true });
|
|
@@ -140,31 +156,29 @@ description: Test Markdown manifest
|
|
|
140
156
|
expect(result.format).toBe("md");
|
|
141
157
|
});
|
|
142
158
|
|
|
143
|
-
test("prefers
|
|
159
|
+
test("prefers skill.yaml over legacy enact.yaml", () => {
|
|
144
160
|
const testDir = join(TEST_DIR, "both-dir");
|
|
145
161
|
mkdirSync(testDir, { recursive: true });
|
|
146
162
|
|
|
147
|
-
// Create both files
|
|
148
163
|
writeFileSync(
|
|
149
|
-
join(testDir, "
|
|
150
|
-
|
|
151
|
-
name: test/
|
|
152
|
-
description:
|
|
153
|
-
---
|
|
164
|
+
join(testDir, "skill.yaml"),
|
|
165
|
+
`
|
|
166
|
+
name: test/skill-preferred
|
|
167
|
+
description: Skill version
|
|
154
168
|
`,
|
|
155
169
|
"utf-8"
|
|
156
170
|
);
|
|
157
171
|
writeFileSync(
|
|
158
172
|
join(testDir, "enact.yaml"),
|
|
159
173
|
`
|
|
160
|
-
name: test/
|
|
161
|
-
description:
|
|
174
|
+
name: test/enact-version
|
|
175
|
+
description: Legacy version
|
|
162
176
|
`,
|
|
163
177
|
"utf-8"
|
|
164
178
|
);
|
|
165
179
|
|
|
166
180
|
const result = loadManifestFromDir(testDir);
|
|
167
|
-
expect(result.manifest.name).toBe("test/
|
|
181
|
+
expect(result.manifest.name).toBe("test/skill-preferred");
|
|
168
182
|
});
|
|
169
183
|
|
|
170
184
|
test("throws for directory without manifest", () => {
|
|
@@ -177,13 +191,13 @@ description: YAML version
|
|
|
177
191
|
});
|
|
178
192
|
|
|
179
193
|
describe("findManifestFile", () => {
|
|
180
|
-
test("finds
|
|
194
|
+
test("finds skill.yaml", () => {
|
|
181
195
|
const testDir = join(TEST_DIR, "find-yaml");
|
|
182
196
|
mkdirSync(testDir, { recursive: true });
|
|
183
|
-
writeFileSync(join(testDir, "
|
|
197
|
+
writeFileSync(join(testDir, "skill.yaml"), "name: test/find", "utf-8");
|
|
184
198
|
|
|
185
199
|
const result = findManifestFile(testDir);
|
|
186
|
-
expect(result).toBe(join(testDir, "
|
|
200
|
+
expect(result).toBe(join(testDir, "skill.yaml"));
|
|
187
201
|
});
|
|
188
202
|
|
|
189
203
|
test("finds enact.md", () => {
|
|
@@ -208,7 +222,7 @@ description: YAML version
|
|
|
208
222
|
test("returns true when manifest exists", () => {
|
|
209
223
|
const testDir = join(TEST_DIR, "has-manifest");
|
|
210
224
|
mkdirSync(testDir, { recursive: true });
|
|
211
|
-
writeFileSync(join(testDir, "
|
|
225
|
+
writeFileSync(join(testDir, "skill.yaml"), "name: test/has", "utf-8");
|
|
212
226
|
|
|
213
227
|
expect(hasManifest(testDir)).toBe(true);
|
|
214
228
|
});
|
|
@@ -227,7 +241,7 @@ description: YAML version
|
|
|
227
241
|
const result = tryLoadManifest(filePath);
|
|
228
242
|
|
|
229
243
|
expect(result).not.toBeNull();
|
|
230
|
-
expect(result?.manifest.name).toBe("acme/
|
|
244
|
+
expect(result?.manifest.name).toBe("acme/greeter");
|
|
231
245
|
});
|
|
232
246
|
|
|
233
247
|
test("returns null on failure", () => {
|
|
@@ -250,7 +264,7 @@ description: YAML version
|
|
|
250
264
|
const testDir = join(TEST_DIR, "try-success");
|
|
251
265
|
mkdirSync(testDir, { recursive: true });
|
|
252
266
|
writeFileSync(
|
|
253
|
-
join(testDir, "
|
|
267
|
+
join(testDir, "skill.yaml"),
|
|
254
268
|
`
|
|
255
269
|
name: test/try
|
|
256
270
|
description: Test try loading
|
|
@@ -25,15 +25,15 @@ version: "1.0.0"
|
|
|
25
25
|
test("parses nested YAML", () => {
|
|
26
26
|
const yaml = `
|
|
27
27
|
name: org/tool
|
|
28
|
-
|
|
28
|
+
outputSchema:
|
|
29
29
|
type: object
|
|
30
30
|
properties:
|
|
31
31
|
name:
|
|
32
32
|
type: string
|
|
33
33
|
`;
|
|
34
34
|
const result = parseYaml(yaml);
|
|
35
|
-
expect(result.
|
|
36
|
-
const schema = result.
|
|
35
|
+
expect(result.outputSchema).toBeDefined();
|
|
36
|
+
const schema = result.outputSchema as Record<string, unknown>;
|
|
37
37
|
expect(schema.type).toBe("object");
|
|
38
38
|
});
|
|
39
39
|
|
|
@@ -169,27 +169,21 @@ description: A test tool
|
|
|
169
169
|
test("parses complete YAML manifest", () => {
|
|
170
170
|
const yaml = `
|
|
171
171
|
enact: "2.0.0"
|
|
172
|
-
name: acme/
|
|
172
|
+
name: acme/greeter
|
|
173
173
|
description: Greets users
|
|
174
174
|
version: "1.0.0"
|
|
175
175
|
from: node:18-alpine
|
|
176
|
-
command: "echo 'Hello \${name}'"
|
|
177
176
|
timeout: 30s
|
|
178
177
|
license: MIT
|
|
179
178
|
tags:
|
|
180
179
|
- greeting
|
|
181
180
|
- utility
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
properties:
|
|
185
|
-
name:
|
|
186
|
-
type: string
|
|
187
|
-
required:
|
|
188
|
-
- name
|
|
181
|
+
scripts:
|
|
182
|
+
greet: "echo 'Hello {{name}}'"
|
|
189
183
|
`;
|
|
190
184
|
const result = parseManifest(yaml, "yaml");
|
|
191
185
|
expect(result.manifest.enact).toBe("2.0.0");
|
|
192
|
-
expect(result.manifest.name).toBe("acme/
|
|
186
|
+
expect(result.manifest.name).toBe("acme/greeter");
|
|
193
187
|
expect(result.manifest.from).toBe("node:18-alpine");
|
|
194
188
|
expect(result.manifest.tags).toContain("greeting");
|
|
195
189
|
});
|
|
@@ -266,12 +260,14 @@ Body here.
|
|
|
266
260
|
|
|
267
261
|
describe("detectFormat", () => {
|
|
268
262
|
test("detects .yaml extension", () => {
|
|
263
|
+
expect(detectFormat("skill.yaml")).toBe("yaml");
|
|
269
264
|
expect(detectFormat("enact.yaml")).toBe("yaml");
|
|
270
265
|
expect(detectFormat("tool.yaml")).toBe("yaml");
|
|
271
|
-
expect(detectFormat("/path/to/
|
|
266
|
+
expect(detectFormat("/path/to/skill.yaml")).toBe("yaml");
|
|
272
267
|
});
|
|
273
268
|
|
|
274
269
|
test("detects .yml extension", () => {
|
|
270
|
+
expect(detectFormat("skill.yml")).toBe("yaml");
|
|
275
271
|
expect(detectFormat("enact.yml")).toBe("yaml");
|
|
276
272
|
expect(detectFormat("/path/to/tool.yml")).toBe("yaml");
|
|
277
273
|
});
|
|
@@ -302,7 +298,7 @@ Body here.
|
|
|
302
298
|
name: org/tool
|
|
303
299
|
description: Test
|
|
304
300
|
`;
|
|
305
|
-
const result = parseManifestAuto(yaml, "
|
|
301
|
+
const result = parseManifestAuto(yaml, "skill.yaml");
|
|
306
302
|
expect(result.manifest.name).toBe("org/tool");
|
|
307
303
|
expect(result.format).toBe("yaml");
|
|
308
304
|
});
|
|
@@ -24,7 +24,7 @@ describe("manifest validator", () => {
|
|
|
24
24
|
test("validates complete manifest", () => {
|
|
25
25
|
const manifest = {
|
|
26
26
|
enact: "2.0.0",
|
|
27
|
-
name: "acme/
|
|
27
|
+
name: "acme/greeter",
|
|
28
28
|
description: "Greets users by name",
|
|
29
29
|
version: "1.0.0",
|
|
30
30
|
from: "node:18-alpine",
|
|
@@ -32,12 +32,8 @@ describe("manifest validator", () => {
|
|
|
32
32
|
timeout: "30s",
|
|
33
33
|
license: "MIT",
|
|
34
34
|
tags: ["greeting", "utility"],
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
properties: {
|
|
38
|
-
name: { type: "string" },
|
|
39
|
-
},
|
|
40
|
-
required: ["name"],
|
|
35
|
+
scripts: {
|
|
36
|
+
greet: "echo 'Hello {{name}}'",
|
|
41
37
|
},
|
|
42
38
|
outputSchema: {
|
|
43
39
|
type: "object",
|
|
@@ -222,13 +218,7 @@ describe("manifest validator", () => {
|
|
|
222
218
|
});
|
|
223
219
|
|
|
224
220
|
test("accepts valid tool name formats", () => {
|
|
225
|
-
const names = [
|
|
226
|
-
"org/tool",
|
|
227
|
-
"acme/utils/greeter",
|
|
228
|
-
"my-org/category/sub-category/tool-name",
|
|
229
|
-
"a/b",
|
|
230
|
-
"org_name/tool_name",
|
|
231
|
-
];
|
|
221
|
+
const names = ["org/tool", "acme/greeter", "@my-org/tool-name", "a/b", "org_name/tool_name"];
|
|
232
222
|
|
|
233
223
|
for (const name of names) {
|
|
234
224
|
const manifest = { name, description: "Test" };
|
|
@@ -405,7 +395,7 @@ describe("manifest validator", () => {
|
|
|
405
395
|
describe("isValidToolName", () => {
|
|
406
396
|
test("returns true for valid names", () => {
|
|
407
397
|
expect(isValidToolName("org/tool")).toBe(true);
|
|
408
|
-
expect(isValidToolName("acme/
|
|
398
|
+
expect(isValidToolName("@acme/greeter")).toBe(true);
|
|
409
399
|
expect(isValidToolName("my-org/my-tool")).toBe(true);
|
|
410
400
|
expect(isValidToolName("org_name/tool_name")).toBe(true);
|
|
411
401
|
});
|
|
@@ -425,9 +415,9 @@ describe("manifest validator", () => {
|
|
|
425
415
|
expect(isValidLocalToolName("simple")).toBe(true);
|
|
426
416
|
});
|
|
427
417
|
|
|
428
|
-
test("returns true for
|
|
418
|
+
test("returns true for scoped names", () => {
|
|
429
419
|
expect(isValidLocalToolName("org/tool")).toBe(true);
|
|
430
|
-
expect(isValidLocalToolName("acme/
|
|
420
|
+
expect(isValidLocalToolName("@acme/greeter")).toBe(true);
|
|
431
421
|
});
|
|
432
422
|
|
|
433
423
|
test("returns false for invalid names", () => {
|
|
@@ -29,7 +29,7 @@ describe("manifest types", () => {
|
|
|
29
29
|
|
|
30
30
|
test("accepts full manifest with all fields", () => {
|
|
31
31
|
const manifest: ToolManifest = {
|
|
32
|
-
name: "acme/
|
|
32
|
+
name: "acme/greeter",
|
|
33
33
|
description: "Greets users by name",
|
|
34
34
|
enact: "2.0.0",
|
|
35
35
|
version: "1.0.0",
|
|
@@ -38,12 +38,8 @@ describe("manifest types", () => {
|
|
|
38
38
|
timeout: "30s",
|
|
39
39
|
license: "MIT",
|
|
40
40
|
tags: ["greeting", "utility"],
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
properties: {
|
|
44
|
-
name: { type: "string" },
|
|
45
|
-
},
|
|
46
|
-
required: ["name"],
|
|
41
|
+
scripts: {
|
|
42
|
+
greet: "echo 'Hello {{name}}'",
|
|
47
43
|
},
|
|
48
44
|
outputSchema: {
|
|
49
45
|
type: "object",
|
|
@@ -90,7 +86,7 @@ describe("manifest types", () => {
|
|
|
90
86
|
"x-custom-field": "custom value",
|
|
91
87
|
};
|
|
92
88
|
|
|
93
|
-
expect(manifest.name).toBe("acme/
|
|
89
|
+
expect(manifest.name).toBe("acme/greeter");
|
|
94
90
|
expect(manifest.enact).toBe("2.0.0");
|
|
95
91
|
expect(manifest.env?.API_KEY?.secret).toBe(true);
|
|
96
92
|
expect(manifest.annotations?.readOnlyHint).toBe(true);
|
|
@@ -314,7 +310,7 @@ describe("manifest types", () => {
|
|
|
314
310
|
},
|
|
315
311
|
sourceDir: "/home/user/.enact/tools/org/tool",
|
|
316
312
|
location: "user",
|
|
317
|
-
manifestPath: "/home/user/.enact/tools/org/tool/
|
|
313
|
+
manifestPath: "/home/user/.enact/tools/org/tool/skill.yaml",
|
|
318
314
|
version: "1.0.0",
|
|
319
315
|
};
|
|
320
316
|
|
|
@@ -336,7 +332,7 @@ describe("manifest types", () => {
|
|
|
336
332
|
manifest: { name: "test", description: "test" },
|
|
337
333
|
sourceDir: "/test",
|
|
338
334
|
location: loc,
|
|
339
|
-
manifestPath: "/test/
|
|
335
|
+
manifestPath: "/test/skill.yaml",
|
|
340
336
|
};
|
|
341
337
|
expect(resolution.location).toBe(loc);
|
|
342
338
|
}
|
|
@@ -346,10 +342,12 @@ describe("manifest types", () => {
|
|
|
346
342
|
describe("constants", () => {
|
|
347
343
|
test("MANIFEST_FILES contains expected files", () => {
|
|
348
344
|
expect(MANIFEST_FILES).toContain("SKILL.md");
|
|
345
|
+
expect(MANIFEST_FILES).toContain("skill.yaml");
|
|
346
|
+
expect(MANIFEST_FILES).toContain("skill.yml");
|
|
349
347
|
expect(MANIFEST_FILES).toContain("enact.md");
|
|
350
348
|
expect(MANIFEST_FILES).toContain("enact.yaml");
|
|
351
349
|
expect(MANIFEST_FILES).toContain("enact.yml");
|
|
352
|
-
expect(MANIFEST_FILES.length).toBe(
|
|
350
|
+
expect(MANIFEST_FILES.length).toBe(6);
|
|
353
351
|
});
|
|
354
352
|
|
|
355
353
|
test("PACKAGE_MANIFEST_FILE is correct", () => {
|
package/tests/paths.test.ts
CHANGED
|
@@ -9,6 +9,7 @@ import {
|
|
|
9
9
|
getGlobalEnvPath,
|
|
10
10
|
getProjectEnactDir,
|
|
11
11
|
getProjectEnvPath,
|
|
12
|
+
getSkillsDir,
|
|
12
13
|
getToolsDir,
|
|
13
14
|
} from "../src/paths";
|
|
14
15
|
|
|
@@ -106,14 +107,20 @@ describe("path utilities", () => {
|
|
|
106
107
|
});
|
|
107
108
|
});
|
|
108
109
|
|
|
109
|
-
describe("
|
|
110
|
-
test("returns ~/.
|
|
111
|
-
const result =
|
|
112
|
-
const expected = join(homedir(), ".
|
|
110
|
+
describe("getSkillsDir", () => {
|
|
111
|
+
test("returns ~/.agent/skills/ path", () => {
|
|
112
|
+
const result = getSkillsDir();
|
|
113
|
+
const expected = join(homedir(), ".agent", "skills");
|
|
113
114
|
expect(result).toBe(expected);
|
|
114
115
|
});
|
|
115
116
|
});
|
|
116
117
|
|
|
118
|
+
describe("getCacheDir (deprecated)", () => {
|
|
119
|
+
test("returns same path as getSkillsDir", () => {
|
|
120
|
+
expect(getCacheDir()).toBe(getSkillsDir());
|
|
121
|
+
});
|
|
122
|
+
});
|
|
123
|
+
|
|
117
124
|
describe("getConfigPath", () => {
|
|
118
125
|
test("returns ~/.enact/config.yaml path", () => {
|
|
119
126
|
const result = getConfigPath();
|
package/tests/registry.test.ts
CHANGED
|
@@ -6,14 +6,19 @@ import { afterAll, beforeAll, describe, expect, test } from "bun:test";
|
|
|
6
6
|
import { existsSync, mkdirSync, rmSync, writeFileSync } from "node:fs";
|
|
7
7
|
import { join } from "node:path";
|
|
8
8
|
import {
|
|
9
|
+
addAlias,
|
|
9
10
|
addToolToRegistry,
|
|
11
|
+
getAliasesForTool,
|
|
10
12
|
getInstalledVersion,
|
|
11
13
|
getToolCachePath,
|
|
12
14
|
getToolsJsonPath,
|
|
13
15
|
isToolInstalled,
|
|
14
16
|
listInstalledTools,
|
|
15
17
|
loadToolsRegistry,
|
|
18
|
+
removeAlias,
|
|
19
|
+
removeAliasesForTool,
|
|
16
20
|
removeToolFromRegistry,
|
|
21
|
+
resolveAlias,
|
|
17
22
|
saveToolsRegistry,
|
|
18
23
|
} from "../src/registry";
|
|
19
24
|
|
|
@@ -194,18 +199,19 @@ describe("registry", () => {
|
|
|
194
199
|
});
|
|
195
200
|
|
|
196
201
|
describe("getToolCachePath", () => {
|
|
197
|
-
test("returns
|
|
202
|
+
test("returns skill path under ~/.agent/skills/", () => {
|
|
198
203
|
const path = getToolCachePath("org/tool", "1.0.0");
|
|
199
|
-
expect(path).toContain(".
|
|
200
|
-
expect(path).toContain("
|
|
204
|
+
expect(path).toContain(".agent");
|
|
205
|
+
expect(path).toContain("skills");
|
|
201
206
|
expect(path).toContain("org/tool");
|
|
202
|
-
|
|
207
|
+
// No version subdirectory in new layout
|
|
208
|
+
expect(path).not.toContain("v1.0.0");
|
|
203
209
|
});
|
|
204
210
|
|
|
205
|
-
test("
|
|
206
|
-
const
|
|
207
|
-
const
|
|
208
|
-
expect(
|
|
211
|
+
test("returns same path regardless of version", () => {
|
|
212
|
+
const path1 = getToolCachePath("org/tool", "1.0.0");
|
|
213
|
+
const path2 = getToolCachePath("org/tool", "2.0.0");
|
|
214
|
+
expect(path1).toBe(path2);
|
|
209
215
|
});
|
|
210
216
|
});
|
|
211
217
|
|
|
@@ -228,4 +234,194 @@ describe("registry", () => {
|
|
|
228
234
|
expect(tools.length).toBe(0);
|
|
229
235
|
});
|
|
230
236
|
});
|
|
237
|
+
|
|
238
|
+
describe("addAlias", () => {
|
|
239
|
+
test("adds alias to registry", () => {
|
|
240
|
+
addToolToRegistry("test/aliased-tool", "1.0.0", "project", PROJECT_DIR);
|
|
241
|
+
addAlias("mytool", "test/aliased-tool", "project", PROJECT_DIR);
|
|
242
|
+
|
|
243
|
+
const registry = loadToolsRegistry("project", PROJECT_DIR);
|
|
244
|
+
expect(registry.aliases?.mytool).toBe("test/aliased-tool");
|
|
245
|
+
|
|
246
|
+
// Clean up
|
|
247
|
+
rmSync(join(PROJECT_ENACT_DIR, "tools.json"));
|
|
248
|
+
});
|
|
249
|
+
|
|
250
|
+
test("throws error when alias already exists for different tool", () => {
|
|
251
|
+
addToolToRegistry("test/tool1", "1.0.0", "project", PROJECT_DIR);
|
|
252
|
+
addToolToRegistry("test/tool2", "1.0.0", "project", PROJECT_DIR);
|
|
253
|
+
addAlias("shared", "test/tool1", "project", PROJECT_DIR);
|
|
254
|
+
|
|
255
|
+
expect(() => {
|
|
256
|
+
addAlias("shared", "test/tool2", "project", PROJECT_DIR);
|
|
257
|
+
}).toThrow('Alias "shared" already exists for tool "test/tool1"');
|
|
258
|
+
|
|
259
|
+
// Clean up
|
|
260
|
+
rmSync(join(PROJECT_ENACT_DIR, "tools.json"));
|
|
261
|
+
});
|
|
262
|
+
|
|
263
|
+
test("allows adding same alias for same tool (idempotent)", () => {
|
|
264
|
+
addToolToRegistry("test/same-tool", "1.0.0", "project", PROJECT_DIR);
|
|
265
|
+
addAlias("same", "test/same-tool", "project", PROJECT_DIR);
|
|
266
|
+
|
|
267
|
+
// Should not throw
|
|
268
|
+
expect(() => {
|
|
269
|
+
addAlias("same", "test/same-tool", "project", PROJECT_DIR);
|
|
270
|
+
}).not.toThrow();
|
|
271
|
+
|
|
272
|
+
// Clean up
|
|
273
|
+
rmSync(join(PROJECT_ENACT_DIR, "tools.json"));
|
|
274
|
+
});
|
|
275
|
+
});
|
|
276
|
+
|
|
277
|
+
describe("removeAlias", () => {
|
|
278
|
+
test("removes existing alias", () => {
|
|
279
|
+
addToolToRegistry("test/removable", "1.0.0", "project", PROJECT_DIR);
|
|
280
|
+
addAlias("removeme", "test/removable", "project", PROJECT_DIR);
|
|
281
|
+
|
|
282
|
+
const removed = removeAlias("removeme", "project", PROJECT_DIR);
|
|
283
|
+
expect(removed).toBe(true);
|
|
284
|
+
|
|
285
|
+
const registry = loadToolsRegistry("project", PROJECT_DIR);
|
|
286
|
+
expect(registry.aliases?.removeme).toBeUndefined();
|
|
287
|
+
|
|
288
|
+
// Clean up
|
|
289
|
+
rmSync(join(PROJECT_ENACT_DIR, "tools.json"));
|
|
290
|
+
});
|
|
291
|
+
|
|
292
|
+
test("returns false for non-existent alias", () => {
|
|
293
|
+
const removed = removeAlias("nonexistent", "project", PROJECT_DIR);
|
|
294
|
+
expect(removed).toBe(false);
|
|
295
|
+
});
|
|
296
|
+
});
|
|
297
|
+
|
|
298
|
+
describe("resolveAlias", () => {
|
|
299
|
+
test("resolves existing alias to tool name", () => {
|
|
300
|
+
addToolToRegistry("org/full-name", "1.0.0", "project", PROJECT_DIR);
|
|
301
|
+
addAlias("short", "org/full-name", "project", PROJECT_DIR);
|
|
302
|
+
|
|
303
|
+
const resolved = resolveAlias("short", "project", PROJECT_DIR);
|
|
304
|
+
expect(resolved).toBe("org/full-name");
|
|
305
|
+
|
|
306
|
+
// Clean up
|
|
307
|
+
rmSync(join(PROJECT_ENACT_DIR, "tools.json"));
|
|
308
|
+
});
|
|
309
|
+
|
|
310
|
+
test("returns null for non-existent alias", () => {
|
|
311
|
+
const resolved = resolveAlias("unknown", "project", PROJECT_DIR);
|
|
312
|
+
expect(resolved).toBeNull();
|
|
313
|
+
});
|
|
314
|
+
});
|
|
315
|
+
|
|
316
|
+
describe("getAliasesForTool", () => {
|
|
317
|
+
test("returns all aliases for a tool", () => {
|
|
318
|
+
addToolToRegistry("test/multi-alias", "1.0.0", "project", PROJECT_DIR);
|
|
319
|
+
addAlias("alias1", "test/multi-alias", "project", PROJECT_DIR);
|
|
320
|
+
addAlias("alias2", "test/multi-alias", "project", PROJECT_DIR);
|
|
321
|
+
|
|
322
|
+
const aliases = getAliasesForTool("test/multi-alias", "project", PROJECT_DIR);
|
|
323
|
+
expect(aliases).toContain("alias1");
|
|
324
|
+
expect(aliases).toContain("alias2");
|
|
325
|
+
expect(aliases.length).toBe(2);
|
|
326
|
+
|
|
327
|
+
// Clean up
|
|
328
|
+
rmSync(join(PROJECT_ENACT_DIR, "tools.json"));
|
|
329
|
+
});
|
|
330
|
+
|
|
331
|
+
test("returns empty array for tool without aliases", () => {
|
|
332
|
+
addToolToRegistry("test/no-alias", "1.0.0", "project", PROJECT_DIR);
|
|
333
|
+
|
|
334
|
+
const aliases = getAliasesForTool("test/no-alias", "project", PROJECT_DIR);
|
|
335
|
+
expect(aliases).toEqual([]);
|
|
336
|
+
|
|
337
|
+
// Clean up
|
|
338
|
+
rmSync(join(PROJECT_ENACT_DIR, "tools.json"));
|
|
339
|
+
});
|
|
340
|
+
});
|
|
341
|
+
|
|
342
|
+
describe("removeAliasesForTool", () => {
|
|
343
|
+
test("removes all aliases for a tool", () => {
|
|
344
|
+
addToolToRegistry("test/cleanup", "1.0.0", "project", PROJECT_DIR);
|
|
345
|
+
addAlias("cleanup1", "test/cleanup", "project", PROJECT_DIR);
|
|
346
|
+
addAlias("cleanup2", "test/cleanup", "project", PROJECT_DIR);
|
|
347
|
+
|
|
348
|
+
const removed = removeAliasesForTool("test/cleanup", "project", PROJECT_DIR);
|
|
349
|
+
expect(removed).toBe(2);
|
|
350
|
+
|
|
351
|
+
const registry = loadToolsRegistry("project", PROJECT_DIR);
|
|
352
|
+
expect(registry.aliases?.cleanup1).toBeUndefined();
|
|
353
|
+
expect(registry.aliases?.cleanup2).toBeUndefined();
|
|
354
|
+
|
|
355
|
+
// Clean up
|
|
356
|
+
rmSync(join(PROJECT_ENACT_DIR, "tools.json"));
|
|
357
|
+
});
|
|
358
|
+
|
|
359
|
+
test("returns 0 for tool without aliases", () => {
|
|
360
|
+
addToolToRegistry("test/no-aliases-to-remove", "1.0.0", "project", PROJECT_DIR);
|
|
361
|
+
|
|
362
|
+
const removed = removeAliasesForTool("test/no-aliases-to-remove", "project", PROJECT_DIR);
|
|
363
|
+
expect(removed).toBe(0);
|
|
364
|
+
|
|
365
|
+
// Clean up
|
|
366
|
+
rmSync(join(PROJECT_ENACT_DIR, "tools.json"));
|
|
367
|
+
});
|
|
368
|
+
|
|
369
|
+
test("does not remove aliases for other tools", () => {
|
|
370
|
+
addToolToRegistry("test/keep", "1.0.0", "project", PROJECT_DIR);
|
|
371
|
+
addToolToRegistry("test/remove", "1.0.0", "project", PROJECT_DIR);
|
|
372
|
+
addAlias("keepme", "test/keep", "project", PROJECT_DIR);
|
|
373
|
+
addAlias("removeme", "test/remove", "project", PROJECT_DIR);
|
|
374
|
+
|
|
375
|
+
removeAliasesForTool("test/remove", "project", PROJECT_DIR);
|
|
376
|
+
|
|
377
|
+
const registry = loadToolsRegistry("project", PROJECT_DIR);
|
|
378
|
+
expect(registry.aliases?.keepme).toBe("test/keep");
|
|
379
|
+
expect(registry.aliases?.removeme).toBeUndefined();
|
|
380
|
+
|
|
381
|
+
// Clean up
|
|
382
|
+
rmSync(join(PROJECT_ENACT_DIR, "tools.json"));
|
|
383
|
+
});
|
|
384
|
+
});
|
|
385
|
+
|
|
386
|
+
describe("loadToolsRegistry with aliases", () => {
|
|
387
|
+
test("loads existing registry with aliases", () => {
|
|
388
|
+
const registryPath = join(PROJECT_ENACT_DIR, "tools.json");
|
|
389
|
+
writeFileSync(
|
|
390
|
+
registryPath,
|
|
391
|
+
JSON.stringify({
|
|
392
|
+
tools: {
|
|
393
|
+
"test/tool": "1.0.0",
|
|
394
|
+
},
|
|
395
|
+
aliases: {
|
|
396
|
+
t: "test/tool",
|
|
397
|
+
},
|
|
398
|
+
})
|
|
399
|
+
);
|
|
400
|
+
|
|
401
|
+
const registry = loadToolsRegistry("project", PROJECT_DIR);
|
|
402
|
+
expect(registry.tools["test/tool"]).toBe("1.0.0");
|
|
403
|
+
expect(registry.aliases?.t).toBe("test/tool");
|
|
404
|
+
|
|
405
|
+
// Clean up
|
|
406
|
+
rmSync(registryPath);
|
|
407
|
+
});
|
|
408
|
+
|
|
409
|
+
test("returns empty aliases when not present in file", () => {
|
|
410
|
+
const registryPath = join(PROJECT_ENACT_DIR, "tools.json");
|
|
411
|
+
writeFileSync(
|
|
412
|
+
registryPath,
|
|
413
|
+
JSON.stringify({
|
|
414
|
+
tools: {
|
|
415
|
+
"test/tool": "1.0.0",
|
|
416
|
+
},
|
|
417
|
+
})
|
|
418
|
+
);
|
|
419
|
+
|
|
420
|
+
const registry = loadToolsRegistry("project", PROJECT_DIR);
|
|
421
|
+
expect(registry.aliases).toEqual({});
|
|
422
|
+
|
|
423
|
+
// Clean up
|
|
424
|
+
rmSync(registryPath);
|
|
425
|
+
});
|
|
426
|
+
});
|
|
231
427
|
});
|