@agentyx/core 0.4.0 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.mts +412 -93
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +700 -244
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
- package/schema/agentyx.schema.json +168 -0
package/dist/index.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
-
import { access, readFile } from "node:fs/promises";
|
|
3
|
-
import { join, resolve } from "node:path";
|
|
2
|
+
import { access, readFile, readdir, realpath } from "node:fs/promises";
|
|
3
|
+
import { dirname, isAbsolute, join, relative, resolve, sep } from "node:path";
|
|
4
4
|
import { readFileSync } from "node:fs";
|
|
5
5
|
import { fileURLToPath } from "node:url";
|
|
6
6
|
import { createHash } from "node:crypto";
|
|
@@ -87,6 +87,39 @@ var UnknownEnabledCapabilityError = class extends AgentyxError {
|
|
|
87
87
|
this.knownCapabilities = knownCapabilities;
|
|
88
88
|
}
|
|
89
89
|
};
|
|
90
|
+
/** Raised when a configured local Skill directory cannot be loaded safely. */
|
|
91
|
+
var LocalSkillDirectoryError = class extends AgentyxError {
|
|
92
|
+
directory;
|
|
93
|
+
constructor(directory, reason, options) {
|
|
94
|
+
super("local_skill_directory_error", `Could not load local Skill directory "${directory}": ${reason}`, options);
|
|
95
|
+
this.name = "LocalSkillDirectoryError";
|
|
96
|
+
this.directory = directory;
|
|
97
|
+
}
|
|
98
|
+
};
|
|
99
|
+
//#endregion
|
|
100
|
+
//#region src/hook/schema.ts
|
|
101
|
+
const HOOK_ACTIVATION_LEVELS = ["default", "optional"];
|
|
102
|
+
const hookActivationLevelSchema = z.enum(HOOK_ACTIVATION_LEVELS);
|
|
103
|
+
const HOOK_EVENTS = ["SessionStart"];
|
|
104
|
+
const hookEventSchema = z.enum(HOOK_EVENTS);
|
|
105
|
+
const hookNameSchema = z.string().min(1, "Hook names must be non-empty strings.").regex(/^[a-z0-9]+(?:-[a-z0-9]+)*$/, "Hook names must be lowercase kebab-case, for example \"session-doctor-bootstrap\".");
|
|
106
|
+
const hookDefinitionSchema = z.strictObject({
|
|
107
|
+
name: hookNameSchema.describe("Unique hook identifier."),
|
|
108
|
+
description: z.string().trim().min(1, "Hook descriptions must be non-empty strings."),
|
|
109
|
+
event: hookEventSchema.describe("The provider lifecycle event this hook runs on."),
|
|
110
|
+
command: z.string().trim().min(1, "Hook command must be a non-empty string."),
|
|
111
|
+
args: z.array(z.string()).default([])
|
|
112
|
+
});
|
|
113
|
+
const hookReferenceSchema = z.union([hookNameSchema, z.strictObject({
|
|
114
|
+
name: hookNameSchema.describe("Hook identifier."),
|
|
115
|
+
activation: hookActivationLevelSchema.default("default")
|
|
116
|
+
})]).transform((value) => typeof value === "string" ? {
|
|
117
|
+
name: value,
|
|
118
|
+
activation: "default"
|
|
119
|
+
} : value).pipe(z.strictObject({
|
|
120
|
+
name: hookNameSchema.describe("Hook identifier."),
|
|
121
|
+
activation: hookActivationLevelSchema.describe("Whether this hook is active by default or explicit opt-in.")
|
|
122
|
+
}));
|
|
90
123
|
//#endregion
|
|
91
124
|
//#region src/mcp/schema.ts
|
|
92
125
|
const MCP_CAPABILITY_LEVELS = ["default", "optional"];
|
|
@@ -195,7 +228,41 @@ const packDefinitionSchema = z.strictObject({
|
|
|
195
228
|
description: z.string().min(1, "Pack descriptions must be non-empty when provided.").describe("Short human-readable summary of the pack.").optional(),
|
|
196
229
|
skills: z.array(skillNameSchema).describe("Skills this pack contributes, in declaration order.").default([]),
|
|
197
230
|
mcpServers: z.array(mcpServerReferenceSchema).describe("MCP servers this pack contributes, in declaration order.").default([]),
|
|
198
|
-
tools: z.array(toolReferenceSchema).describe("Local tools this pack contributes, in declaration order.").default([])
|
|
231
|
+
tools: z.array(toolReferenceSchema).describe("Local tools this pack contributes, in declaration order.").default([]),
|
|
232
|
+
hooks: z.array(hookReferenceSchema).describe("Provider lifecycle hooks this pack contributes, in declaration order.").default([])
|
|
233
|
+
});
|
|
234
|
+
//#endregion
|
|
235
|
+
//#region src/source/schema.ts
|
|
236
|
+
const trustedSourceNameSchema = z.string().min(1, "Trusted source names must be non-empty strings.").regex(/^[a-z0-9]+(?:-[a-z0-9]+)*$/, "Trusted source names must be lowercase kebab-case, for example \"superpowers\".");
|
|
237
|
+
const trustedSourcePathSchema = z.string().min(1, "Trusted source paths must be non-empty strings.").regex(/^(?!\/)(?![A-Za-z]:)(?!.*(?:^|\/)\.\.(?:\/|$))[^\\\0]+$/, "Trusted source paths must be portable project-relative paths without parent traversal.");
|
|
238
|
+
const trustedSourceReferenceSchema = z.strictObject({
|
|
239
|
+
name: trustedSourceNameSchema.describe("Known trusted source identifier."),
|
|
240
|
+
path: trustedSourcePathSchema.describe("Project-relative checkout path for this source."),
|
|
241
|
+
ref: z.string().trim().min(1, "Trusted source refs must be non-empty strings.").describe("Reviewed immutable commit, signed tag or pinned release identifier.")
|
|
242
|
+
});
|
|
243
|
+
const trustedSourceDefinitionSchema = z.strictObject({
|
|
244
|
+
name: trustedSourceNameSchema,
|
|
245
|
+
displayName: z.string().min(1),
|
|
246
|
+
repository: z.string().url(),
|
|
247
|
+
type: z.literal("codex-plugin"),
|
|
248
|
+
manifestPath: z.string().min(1),
|
|
249
|
+
recommendedPath: trustedSourcePathSchema,
|
|
250
|
+
description: z.string().min(1),
|
|
251
|
+
installStatus: z.enum(["metadata-only", "installable"]),
|
|
252
|
+
installNote: z.string().min(1)
|
|
253
|
+
});
|
|
254
|
+
const codexPluginManifestSchema = z.object({
|
|
255
|
+
name: trustedSourceNameSchema,
|
|
256
|
+
version: z.string().min(1),
|
|
257
|
+
description: z.string().min(1),
|
|
258
|
+
repository: z.string().url(),
|
|
259
|
+
license: z.string().min(1),
|
|
260
|
+
skills: z.string().min(1)
|
|
261
|
+
}).passthrough();
|
|
262
|
+
const trustedSourceSkillSummarySchema = z.strictObject({
|
|
263
|
+
name: skillNameSchema,
|
|
264
|
+
description: z.string().min(1),
|
|
265
|
+
hasResources: z.boolean()
|
|
199
266
|
});
|
|
200
267
|
//#endregion
|
|
201
268
|
//#region src/config/schema.ts
|
|
@@ -205,12 +272,21 @@ const packDefinitionSchema = z.strictObject({
|
|
|
205
272
|
*/
|
|
206
273
|
const agentyxTargetSchema = z.string().min(1, "Targets must be non-empty strings.");
|
|
207
274
|
const enabledCapabilityNameSchema = z.string().min(1, "Enabled capability names must be non-empty strings.").regex(/^[a-z0-9]+(?:-[a-z0-9]+)*$/, "Enabled capability names must be lowercase kebab-case, for example \"rtk\".");
|
|
275
|
+
/**
|
|
276
|
+
* Portable project-relative path to a directory whose direct children are Agent Skills.
|
|
277
|
+
* Backslashes, absolute paths and parent traversal are rejected so the same config is safe on
|
|
278
|
+
* every supported platform.
|
|
279
|
+
*/
|
|
280
|
+
const skillDirectorySchema = z.string().min(1, "Skill directories must be non-empty strings.").regex(/^(?!\/)(?![A-Za-z]:)(?!.*(?:^|\/)\.\.(?:\/|$))[^\\\0]+$/, "Skill directories must be portable project-relative paths without parent traversal.");
|
|
208
281
|
/** The `.agentyx.json` project configuration. */
|
|
209
282
|
const agentyxConfigSchema = z.strictObject({
|
|
210
283
|
$schema: z.string().min(1, "$schema must be a non-empty string.").describe("Optional path or URL to the Agentyx JSON Schema.").optional(),
|
|
211
284
|
packs: z.array(packNameSchema).describe("Capability packs this project selects.").default([]),
|
|
212
285
|
enable: z.array(enabledCapabilityNameSchema).describe("Optional capabilities to activate by explicit identifier.").default([]),
|
|
213
|
-
targets: z.array(agentyxTargetSchema).describe("Coding-agent providers this project targets.").default([])
|
|
286
|
+
targets: z.array(agentyxTargetSchema).describe("Coding-agent providers this project targets.").default([]),
|
|
287
|
+
skillDirectories: z.array(skillDirectorySchema).describe("Project-relative roots containing local Agent Skills.").optional(),
|
|
288
|
+
localPacks: z.array(packDefinitionSchema).describe("Project-owned packs that can reference built-in or local Skills.").optional(),
|
|
289
|
+
trustedSources: z.array(trustedSourceReferenceSchema).describe("Pinned local checkouts of known external skill/plugin sources.").optional()
|
|
214
290
|
});
|
|
215
291
|
//#endregion
|
|
216
292
|
//#region src/config/json-schema.ts
|
|
@@ -281,163 +357,6 @@ function isFileNotFound(cause) {
|
|
|
281
357
|
return cause instanceof Error && cause.code === "ENOENT";
|
|
282
358
|
}
|
|
283
359
|
//#endregion
|
|
284
|
-
//#region src/mcp/errors.ts
|
|
285
|
-
var UnknownMcpServerError = class extends AgentyxError {
|
|
286
|
-
serverName;
|
|
287
|
-
requiredBy;
|
|
288
|
-
knownServers;
|
|
289
|
-
constructor(serverName, requiredBy, knownServers) {
|
|
290
|
-
const origin = requiredBy === void 0 ? "" : ` (required by "${requiredBy}")`;
|
|
291
|
-
const known = knownServers.length > 0 ? [...knownServers].sort().join(", ") : "none";
|
|
292
|
-
super("unknown_mcp_server", `Unknown MCP server "${serverName}"${origin}. Known MCP servers: ${known}.`);
|
|
293
|
-
this.name = "UnknownMcpServerError";
|
|
294
|
-
this.serverName = serverName;
|
|
295
|
-
this.requiredBy = requiredBy;
|
|
296
|
-
this.knownServers = knownServers;
|
|
297
|
-
}
|
|
298
|
-
};
|
|
299
|
-
var DuplicateMcpServerError = class extends AgentyxError {
|
|
300
|
-
serverName;
|
|
301
|
-
constructor(serverName) {
|
|
302
|
-
super("duplicate_mcp_server", `Duplicate MCP server definition: "${serverName}".`);
|
|
303
|
-
this.name = "DuplicateMcpServerError";
|
|
304
|
-
this.serverName = serverName;
|
|
305
|
-
}
|
|
306
|
-
};
|
|
307
|
-
var InvalidMcpServerError = class extends AgentyxError {
|
|
308
|
-
origin;
|
|
309
|
-
reason;
|
|
310
|
-
constructor(origin, reason, options) {
|
|
311
|
-
const detail = typeof reason === "string" ? reason : formatIssues$1(reason);
|
|
312
|
-
super("invalid_mcp_server", `Invalid MCP server in ${origin}: ${detail}`, options);
|
|
313
|
-
this.name = "InvalidMcpServerError";
|
|
314
|
-
this.origin = origin;
|
|
315
|
-
this.reason = detail;
|
|
316
|
-
}
|
|
317
|
-
};
|
|
318
|
-
function formatIssues$1(error) {
|
|
319
|
-
return error.issues.map((issue) => {
|
|
320
|
-
const path = issue.path.map(String).join(".");
|
|
321
|
-
return path === "" ? issue.message : `${path}: ${issue.message}`;
|
|
322
|
-
}).join("; ");
|
|
323
|
-
}
|
|
324
|
-
//#endregion
|
|
325
|
-
//#region src/mcp/registry.ts
|
|
326
|
-
function createMcpServerRegistry(sources) {
|
|
327
|
-
const byName = /* @__PURE__ */ new Map();
|
|
328
|
-
for (const source of sources) {
|
|
329
|
-
const name = mcpServerNameSchema.safeParse(source.name);
|
|
330
|
-
if (!name.success) throw new InvalidMcpServerError(`MCP server source "${source.name}"`, name.error);
|
|
331
|
-
if (byName.has(name.data)) throw new DuplicateMcpServerError(name.data);
|
|
332
|
-
byName.set(name.data, source);
|
|
333
|
-
}
|
|
334
|
-
const names = [...byName.keys()];
|
|
335
|
-
const loaded = /* @__PURE__ */ new Map();
|
|
336
|
-
const get = (name) => {
|
|
337
|
-
const cached = loaded.get(name);
|
|
338
|
-
if (cached !== void 0) return cached;
|
|
339
|
-
const source = byName.get(name);
|
|
340
|
-
if (source === void 0) throw new UnknownMcpServerError(name, void 0, names);
|
|
341
|
-
const origin = `MCP server "${name}"`;
|
|
342
|
-
const server = mcpServerDefinitionSchema.safeParse(source.load());
|
|
343
|
-
if (!server.success) throw new InvalidMcpServerError(origin, server.error);
|
|
344
|
-
if (server.data.name !== name) throw new InvalidMcpServerError(origin, `it declares the name "${server.data.name}"`);
|
|
345
|
-
loaded.set(name, server.data);
|
|
346
|
-
return server.data;
|
|
347
|
-
};
|
|
348
|
-
return {
|
|
349
|
-
names,
|
|
350
|
-
has: (name) => byName.has(name),
|
|
351
|
-
listMetadata: () => names.map((name) => {
|
|
352
|
-
const server = get(name);
|
|
353
|
-
return {
|
|
354
|
-
name: server.name,
|
|
355
|
-
description: server.description,
|
|
356
|
-
transport: server.transport,
|
|
357
|
-
contextCost: server.contextCost
|
|
358
|
-
};
|
|
359
|
-
}),
|
|
360
|
-
get
|
|
361
|
-
};
|
|
362
|
-
}
|
|
363
|
-
const builtInMcpServerRegistry = createMcpServerRegistry([
|
|
364
|
-
{
|
|
365
|
-
name: "context7",
|
|
366
|
-
load: () => ({
|
|
367
|
-
name: "context7",
|
|
368
|
-
description: "Fetch up-to-date library documentation from Context7.",
|
|
369
|
-
transport: "http",
|
|
370
|
-
contextCost: "medium",
|
|
371
|
-
url: "https://mcp.context7.com/mcp"
|
|
372
|
-
})
|
|
373
|
-
},
|
|
374
|
-
{
|
|
375
|
-
name: "playwright",
|
|
376
|
-
load: () => ({
|
|
377
|
-
name: "playwright",
|
|
378
|
-
description: "Automate and inspect browsers through Playwright MCP.",
|
|
379
|
-
transport: "stdio",
|
|
380
|
-
contextCost: "high",
|
|
381
|
-
command: "npx",
|
|
382
|
-
args: ["@playwright/mcp@latest"]
|
|
383
|
-
})
|
|
384
|
-
},
|
|
385
|
-
{
|
|
386
|
-
name: "codebase-memory",
|
|
387
|
-
load: () => ({
|
|
388
|
-
name: "codebase-memory",
|
|
389
|
-
description: "Structural code-intelligence MCP backed by a persistent code knowledge graph.",
|
|
390
|
-
transport: "stdio",
|
|
391
|
-
contextCost: "high",
|
|
392
|
-
command: "codebase-memory-mcp"
|
|
393
|
-
})
|
|
394
|
-
},
|
|
395
|
-
{
|
|
396
|
-
name: "github",
|
|
397
|
-
load: () => ({
|
|
398
|
-
name: "github",
|
|
399
|
-
description: "Read repositories, issues, pull requests and workflow runs on GitHub.",
|
|
400
|
-
transport: "http",
|
|
401
|
-
contextCost: "high",
|
|
402
|
-
url: "https://api.githubcopilot.com/mcp/"
|
|
403
|
-
})
|
|
404
|
-
},
|
|
405
|
-
{
|
|
406
|
-
name: "sentry",
|
|
407
|
-
load: () => ({
|
|
408
|
-
name: "sentry",
|
|
409
|
-
description: "Inspect production issues, events and stack traces recorded by Sentry.",
|
|
410
|
-
transport: "http",
|
|
411
|
-
contextCost: "medium",
|
|
412
|
-
url: "https://mcp.sentry.dev/mcp"
|
|
413
|
-
})
|
|
414
|
-
},
|
|
415
|
-
{
|
|
416
|
-
name: "chrome-devtools",
|
|
417
|
-
load: () => ({
|
|
418
|
-
name: "chrome-devtools",
|
|
419
|
-
description: "Record performance traces and inspect pages through Chrome DevTools.",
|
|
420
|
-
transport: "stdio",
|
|
421
|
-
contextCost: "high",
|
|
422
|
-
command: "npx",
|
|
423
|
-
args: ["-y", "chrome-devtools-mcp@latest"]
|
|
424
|
-
})
|
|
425
|
-
},
|
|
426
|
-
{
|
|
427
|
-
name: "supabase",
|
|
428
|
-
load: () => ({
|
|
429
|
-
name: "supabase",
|
|
430
|
-
description: "Inspect and query Supabase project schemas, tables and logs.",
|
|
431
|
-
transport: "stdio",
|
|
432
|
-
contextCost: "high",
|
|
433
|
-
command: "npx",
|
|
434
|
-
args: ["-y", "@supabase/mcp-server-supabase@latest"],
|
|
435
|
-
env: { SUPABASE_ACCESS_TOKEN: { fromEnv: "SUPABASE_ACCESS_TOKEN" } }
|
|
436
|
-
})
|
|
437
|
-
}
|
|
438
|
-
]);
|
|
439
|
-
const builtInMcpServerNames = builtInMcpServerRegistry.names;
|
|
440
|
-
//#endregion
|
|
441
360
|
//#region src/pack/errors.ts
|
|
442
361
|
/** Raised when a requested pack is not present in the registry. */
|
|
443
362
|
var UnknownPackError = class extends AgentyxError {
|
|
@@ -540,6 +459,10 @@ const builtInPacks = [
|
|
|
540
459
|
tools: [{
|
|
541
460
|
name: "rtk",
|
|
542
461
|
activation: "optional"
|
|
462
|
+
}],
|
|
463
|
+
hooks: [{
|
|
464
|
+
name: "session-doctor-bootstrap",
|
|
465
|
+
activation: "default"
|
|
543
466
|
}]
|
|
544
467
|
},
|
|
545
468
|
{
|
|
@@ -688,54 +611,6 @@ const builtInPacks = [
|
|
|
688
611
|
/** The registry used by default when no explicit registry is supplied. */
|
|
689
612
|
const builtInPackRegistry = createPackRegistry(builtInPacks);
|
|
690
613
|
//#endregion
|
|
691
|
-
//#region src/pack/resolver.ts
|
|
692
|
-
/**
|
|
693
|
-
* Resolves selected packs without hidden inheritance.
|
|
694
|
-
*
|
|
695
|
-
* The result preserves configured order, removes duplicates by first
|
|
696
|
-
* occurrence, and validates every requested pack.
|
|
697
|
-
*
|
|
698
|
-
* @throws {UnknownPackError} when a requested pack is missing.
|
|
699
|
-
*/
|
|
700
|
-
function resolvePacks(requestedPacks, registry = builtInPackRegistry) {
|
|
701
|
-
const resolved = [];
|
|
702
|
-
const seen = /* @__PURE__ */ new Set();
|
|
703
|
-
for (const name of requestedPacks) {
|
|
704
|
-
if (seen.has(name)) continue;
|
|
705
|
-
if (!registry.has(name)) throw new UnknownPackError(name, void 0, [...registry.keys()]);
|
|
706
|
-
seen.add(name);
|
|
707
|
-
resolved.push(name);
|
|
708
|
-
}
|
|
709
|
-
return resolved;
|
|
710
|
-
}
|
|
711
|
-
//#endregion
|
|
712
|
-
//#region src/mcp/resolver.ts
|
|
713
|
-
function collectPackMcpServerReferences(resolvedPacks, packRegistry, mcpRegistry) {
|
|
714
|
-
const servers = [];
|
|
715
|
-
const seen = /* @__PURE__ */ new Set();
|
|
716
|
-
for (const packName of resolvedPacks) for (const server of packRegistry.get(packName)?.mcpServers ?? []) {
|
|
717
|
-
const serverName = server.name;
|
|
718
|
-
if (seen.has(serverName)) continue;
|
|
719
|
-
if (!mcpRegistry.has(serverName)) throw new UnknownMcpServerError(serverName, packName, mcpRegistry.names);
|
|
720
|
-
seen.add(serverName);
|
|
721
|
-
servers.push(server);
|
|
722
|
-
}
|
|
723
|
-
return servers;
|
|
724
|
-
}
|
|
725
|
-
function filterEffectiveMcpServers(servers, enabledCapabilities) {
|
|
726
|
-
const enabled = new Set(enabledCapabilities);
|
|
727
|
-
return servers.filter((server) => server.activation === "default" || enabled.has(server.name)).map((server) => server.name);
|
|
728
|
-
}
|
|
729
|
-
function collectPackMcpServers(resolvedPacks, packRegistry, mcpRegistry, enabledCapabilities = []) {
|
|
730
|
-
return filterEffectiveMcpServers(collectPackMcpServerReferences(resolvedPacks, packRegistry, mcpRegistry), enabledCapabilities);
|
|
731
|
-
}
|
|
732
|
-
function resolvePackMcpServerReferences(requestedPacks, packRegistry = builtInPackRegistry, mcpRegistry = builtInMcpServerRegistry) {
|
|
733
|
-
return collectPackMcpServerReferences(resolvePacks(requestedPacks, packRegistry), packRegistry, mcpRegistry);
|
|
734
|
-
}
|
|
735
|
-
function resolvePackMcpServers(requestedPacks, packRegistry = builtInPackRegistry, mcpRegistry = builtInMcpServerRegistry, enabledCapabilities = []) {
|
|
736
|
-
return collectPackMcpServers(resolvePacks(requestedPacks, packRegistry), packRegistry, mcpRegistry, enabledCapabilities);
|
|
737
|
-
}
|
|
738
|
-
//#endregion
|
|
739
614
|
//#region src/assets.ts
|
|
740
615
|
/**
|
|
741
616
|
* Location of the non-code assets that ship with `@agentyx/core`.
|
|
@@ -795,14 +670,14 @@ var InvalidSkillError = class extends AgentyxError {
|
|
|
795
670
|
origin;
|
|
796
671
|
reason;
|
|
797
672
|
constructor(origin, reason, options) {
|
|
798
|
-
const detail = typeof reason === "string" ? reason : formatIssues(reason);
|
|
673
|
+
const detail = typeof reason === "string" ? reason : formatIssues$2(reason);
|
|
799
674
|
super("invalid_skill", `Invalid skill in ${origin}: ${detail}`, options);
|
|
800
675
|
this.name = "InvalidSkillError";
|
|
801
676
|
this.origin = origin;
|
|
802
677
|
this.reason = detail;
|
|
803
678
|
}
|
|
804
679
|
};
|
|
805
|
-
function formatIssues(error) {
|
|
680
|
+
function formatIssues$2(error) {
|
|
806
681
|
return error.issues.map((issue) => {
|
|
807
682
|
const path = issue.path.map(String).join(".");
|
|
808
683
|
return path === "" ? issue.message : `${path}: ${issue.message}`;
|
|
@@ -1021,31 +896,491 @@ function loadBuiltInSkill(name) {
|
|
|
1021
896
|
return parseSkillMarkdown(markdown, filePath);
|
|
1022
897
|
}
|
|
1023
898
|
//#endregion
|
|
1024
|
-
//#region src/
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
* @throws {UnknownSkillError} when a pack references a skill the registry lacks.
|
|
1032
|
-
*/
|
|
1033
|
-
function collectPackSkills(resolvedPacks, packRegistry, skillRegistry) {
|
|
1034
|
-
const skills = [];
|
|
1035
|
-
const seen = /* @__PURE__ */ new Set();
|
|
1036
|
-
for (const packName of resolvedPacks) for (const skillName of packRegistry.get(packName)?.skills ?? []) {
|
|
1037
|
-
if (seen.has(skillName)) continue;
|
|
1038
|
-
if (!skillRegistry.has(skillName)) throw new UnknownSkillError(skillName, packName, skillRegistry.names);
|
|
1039
|
-
seen.add(skillName);
|
|
1040
|
-
skills.push(skillName);
|
|
899
|
+
//#region src/source/errors.ts
|
|
900
|
+
var DuplicateTrustedSourceError = class extends AgentyxError {
|
|
901
|
+
sourceName;
|
|
902
|
+
constructor(sourceName) {
|
|
903
|
+
super("duplicate_trusted_source", `Duplicate trusted source "${sourceName}".`);
|
|
904
|
+
this.name = "DuplicateTrustedSourceError";
|
|
905
|
+
this.sourceName = sourceName;
|
|
1041
906
|
}
|
|
1042
|
-
|
|
907
|
+
};
|
|
908
|
+
var UnknownTrustedSourceError = class extends AgentyxError {
|
|
909
|
+
sourceName;
|
|
910
|
+
knownSources;
|
|
911
|
+
constructor(sourceName, knownSources) {
|
|
912
|
+
const known = knownSources.length > 0 ? [...knownSources].sort().join(", ") : "none";
|
|
913
|
+
super("unknown_trusted_source", `Unknown trusted source "${sourceName}". Known trusted sources: ${known}.`);
|
|
914
|
+
this.name = "UnknownTrustedSourceError";
|
|
915
|
+
this.sourceName = sourceName;
|
|
916
|
+
this.knownSources = knownSources;
|
|
917
|
+
}
|
|
918
|
+
};
|
|
919
|
+
var TrustedSourceLoadError = class extends AgentyxError {
|
|
920
|
+
sourceName;
|
|
921
|
+
constructor(sourceName, reason, options) {
|
|
922
|
+
super("trusted_source_load_error", `Could not load trusted source "${sourceName}": ${reason}`, options);
|
|
923
|
+
this.name = "TrustedSourceLoadError";
|
|
924
|
+
this.sourceName = sourceName;
|
|
925
|
+
}
|
|
926
|
+
};
|
|
927
|
+
//#endregion
|
|
928
|
+
//#region src/source/registry.ts
|
|
929
|
+
const knownTrustedSources = [trustedSourceDefinitionSchema.parse({
|
|
930
|
+
name: "superpowers",
|
|
931
|
+
displayName: "Superpowers",
|
|
932
|
+
repository: "https://github.com/obra/superpowers",
|
|
933
|
+
type: "codex-plugin",
|
|
934
|
+
manifestPath: ".codex-plugin/plugin.json",
|
|
935
|
+
recommendedPath: ".agentyx/sources/superpowers",
|
|
936
|
+
description: "Agentic planning, TDD, debugging, code review and delivery workflows packaged as a Codex plugin.",
|
|
937
|
+
installStatus: "metadata-only",
|
|
938
|
+
installNote: "Agentyx can inspect and verify the local plugin checkout, but does not install its resource-bearing skills yet."
|
|
939
|
+
})];
|
|
940
|
+
const knownTrustedSourceRegistry = createTrustedSourceRegistry(knownTrustedSources);
|
|
941
|
+
function createTrustedSourceRegistry(definitions) {
|
|
942
|
+
const registry = /* @__PURE__ */ new Map();
|
|
943
|
+
for (const definition of definitions) {
|
|
944
|
+
const parsed = trustedSourceDefinitionSchema.parse(definition);
|
|
945
|
+
if (registry.has(parsed.name)) throw new DuplicateTrustedSourceError(parsed.name);
|
|
946
|
+
registry.set(parsed.name, parsed);
|
|
947
|
+
}
|
|
948
|
+
return registry;
|
|
949
|
+
}
|
|
950
|
+
function getTrustedSourceDefinition(source, registry = knownTrustedSourceRegistry) {
|
|
951
|
+
const name = typeof source === "string" ? source : source.name;
|
|
952
|
+
const definition = registry.get(name);
|
|
953
|
+
if (definition === void 0) throw new UnknownTrustedSourceError(name, [...registry.keys()]);
|
|
954
|
+
return definition;
|
|
1043
955
|
}
|
|
956
|
+
//#endregion
|
|
957
|
+
//#region src/config/project.ts
|
|
1044
958
|
/**
|
|
1045
|
-
*
|
|
959
|
+
* Loads `.agentyx.json` and any checked-in Agent Skills it declares.
|
|
1046
960
|
*
|
|
1047
|
-
*
|
|
1048
|
-
*
|
|
961
|
+
* A Skill root contains one directory per Skill, each with a `SKILL.md`. Roots must resolve inside
|
|
962
|
+
* the project even through symlinks. Loading is local-only: this function never clones a repository
|
|
963
|
+
* or accesses the network.
|
|
964
|
+
*/
|
|
965
|
+
async function loadAgentyxProject(projectPath = process.cwd()) {
|
|
966
|
+
const projectDir = resolve(projectPath);
|
|
967
|
+
const config = await loadAgentyxConfig(projectDir);
|
|
968
|
+
for (const source of config.trustedSources ?? []) getTrustedSourceDefinition(source);
|
|
969
|
+
const localSources = await loadLocalSkillSources(projectDir, config.skillDirectories ?? []);
|
|
970
|
+
const skillRegistry = createSkillRegistry([...builtInSkillRegistry.names.map((name) => ({
|
|
971
|
+
name,
|
|
972
|
+
load: () => builtInSkillRegistry.get(name)
|
|
973
|
+
})), ...localSources]);
|
|
974
|
+
return {
|
|
975
|
+
config,
|
|
976
|
+
packRegistry: config.localPacks === void 0 ? builtInPackRegistry : createPackRegistry([...builtInPacks, ...config.localPacks]),
|
|
977
|
+
skillRegistry
|
|
978
|
+
};
|
|
979
|
+
}
|
|
980
|
+
async function loadLocalSkillSources(projectDir, directories) {
|
|
981
|
+
const projectRealPath = await realpath(projectDir);
|
|
982
|
+
const sources = [];
|
|
983
|
+
for (const directory of directories) {
|
|
984
|
+
const root = resolve(projectDir, directory);
|
|
985
|
+
let rootRealPath;
|
|
986
|
+
try {
|
|
987
|
+
rootRealPath = await realpath(root);
|
|
988
|
+
} catch (cause) {
|
|
989
|
+
throw localDirectoryError(directory, "the directory does not exist or cannot be read", cause);
|
|
990
|
+
}
|
|
991
|
+
if (!isInside$1(rootRealPath, projectRealPath)) throw new LocalSkillDirectoryError(directory, "the resolved path is outside the project");
|
|
992
|
+
let entries;
|
|
993
|
+
try {
|
|
994
|
+
entries = await readdir(rootRealPath, { withFileTypes: true });
|
|
995
|
+
} catch (cause) {
|
|
996
|
+
throw localDirectoryError(directory, "the directory cannot be listed", cause);
|
|
997
|
+
}
|
|
998
|
+
for (const entry of entries.sort((left, right) => left.name.localeCompare(right.name))) {
|
|
999
|
+
if (!entry.isDirectory()) continue;
|
|
1000
|
+
const filePath = join(rootRealPath, entry.name, "SKILL.md");
|
|
1001
|
+
let realFilePath;
|
|
1002
|
+
try {
|
|
1003
|
+
realFilePath = await realpath(filePath);
|
|
1004
|
+
} catch (cause) {
|
|
1005
|
+
if (cause instanceof Error && cause.code === "ENOENT") continue;
|
|
1006
|
+
throw localDirectoryError(directory, `cannot read ${entry.name}/SKILL.md`, cause);
|
|
1007
|
+
}
|
|
1008
|
+
if (!isInside$1(realFilePath, rootRealPath)) throw new LocalSkillDirectoryError(directory, `${entry.name}/SKILL.md resolves outside the skill directory`);
|
|
1009
|
+
let markdown;
|
|
1010
|
+
try {
|
|
1011
|
+
markdown = await readFile(realFilePath, "utf8");
|
|
1012
|
+
} catch (cause) {
|
|
1013
|
+
throw localDirectoryError(directory, `cannot read ${entry.name}/SKILL.md`, cause);
|
|
1014
|
+
}
|
|
1015
|
+
const skill = parseSkillMarkdown(markdown, filePath);
|
|
1016
|
+
if (skill.name !== entry.name) throw new LocalSkillDirectoryError(directory, `${entry.name}/SKILL.md declares the name "${skill.name}"`);
|
|
1017
|
+
sources.push({
|
|
1018
|
+
name: skill.name,
|
|
1019
|
+
load: () => skill
|
|
1020
|
+
});
|
|
1021
|
+
}
|
|
1022
|
+
}
|
|
1023
|
+
return sources;
|
|
1024
|
+
}
|
|
1025
|
+
function isInside$1(path, root) {
|
|
1026
|
+
const pathFromRoot = relative(root, path);
|
|
1027
|
+
return pathFromRoot === "" || !pathFromRoot.startsWith(`..${sep}`) && pathFromRoot !== ".." && !isAbsolute(pathFromRoot);
|
|
1028
|
+
}
|
|
1029
|
+
function localDirectoryError(directory, reason, cause) {
|
|
1030
|
+
return new LocalSkillDirectoryError(directory, reason, cause instanceof Error ? { cause } : void 0);
|
|
1031
|
+
}
|
|
1032
|
+
//#endregion
|
|
1033
|
+
//#region src/hook/errors.ts
|
|
1034
|
+
var UnknownHookError = class extends AgentyxError {
|
|
1035
|
+
hookName;
|
|
1036
|
+
requiredBy;
|
|
1037
|
+
knownHooks;
|
|
1038
|
+
constructor(hookName, requiredBy, knownHooks) {
|
|
1039
|
+
const origin = requiredBy === void 0 ? "" : ` (required by "${requiredBy}")`;
|
|
1040
|
+
const known = knownHooks.length > 0 ? [...knownHooks].sort().join(", ") : "none";
|
|
1041
|
+
super("unknown_hook", `Unknown hook "${hookName}"${origin}. Known hooks: ${known}.`);
|
|
1042
|
+
this.name = "UnknownHookError";
|
|
1043
|
+
this.hookName = hookName;
|
|
1044
|
+
this.requiredBy = requiredBy;
|
|
1045
|
+
this.knownHooks = knownHooks;
|
|
1046
|
+
}
|
|
1047
|
+
};
|
|
1048
|
+
var DuplicateHookError = class extends AgentyxError {
|
|
1049
|
+
hookName;
|
|
1050
|
+
constructor(hookName) {
|
|
1051
|
+
super("duplicate_hook", `Duplicate hook definition: "${hookName}".`);
|
|
1052
|
+
this.name = "DuplicateHookError";
|
|
1053
|
+
this.hookName = hookName;
|
|
1054
|
+
}
|
|
1055
|
+
};
|
|
1056
|
+
var InvalidHookError = class extends AgentyxError {
|
|
1057
|
+
origin;
|
|
1058
|
+
reason;
|
|
1059
|
+
constructor(origin, reason, options) {
|
|
1060
|
+
const detail = typeof reason === "string" ? reason : formatIssues$1(reason);
|
|
1061
|
+
super("invalid_hook", `Invalid hook in ${origin}: ${detail}`, options);
|
|
1062
|
+
this.name = "InvalidHookError";
|
|
1063
|
+
this.origin = origin;
|
|
1064
|
+
this.reason = detail;
|
|
1065
|
+
}
|
|
1066
|
+
};
|
|
1067
|
+
function formatIssues$1(error) {
|
|
1068
|
+
return error.issues.map((issue) => {
|
|
1069
|
+
const path = issue.path.map(String).join(".");
|
|
1070
|
+
return path === "" ? issue.message : `${path}: ${issue.message}`;
|
|
1071
|
+
}).join("; ");
|
|
1072
|
+
}
|
|
1073
|
+
//#endregion
|
|
1074
|
+
//#region src/hook/registry.ts
|
|
1075
|
+
function createHookRegistry(sources) {
|
|
1076
|
+
const byName = /* @__PURE__ */ new Map();
|
|
1077
|
+
for (const source of sources) {
|
|
1078
|
+
const name = hookNameSchema.safeParse(source.name);
|
|
1079
|
+
if (!name.success) throw new InvalidHookError(`Hook source "${source.name}"`, name.error);
|
|
1080
|
+
if (byName.has(name.data)) throw new DuplicateHookError(name.data);
|
|
1081
|
+
byName.set(name.data, source);
|
|
1082
|
+
}
|
|
1083
|
+
const names = [...byName.keys()];
|
|
1084
|
+
const loaded = /* @__PURE__ */ new Map();
|
|
1085
|
+
const get = (name) => {
|
|
1086
|
+
const cached = loaded.get(name);
|
|
1087
|
+
if (cached !== void 0) return cached;
|
|
1088
|
+
const source = byName.get(name);
|
|
1089
|
+
if (source === void 0) throw new UnknownHookError(name, void 0, names);
|
|
1090
|
+
const origin = `Hook "${name}"`;
|
|
1091
|
+
const hook = hookDefinitionSchema.safeParse(source.load());
|
|
1092
|
+
if (!hook.success) throw new InvalidHookError(origin, hook.error);
|
|
1093
|
+
if (hook.data.name !== name) throw new InvalidHookError(origin, `it declares the name "${hook.data.name}"`);
|
|
1094
|
+
loaded.set(name, hook.data);
|
|
1095
|
+
return hook.data;
|
|
1096
|
+
};
|
|
1097
|
+
return {
|
|
1098
|
+
names,
|
|
1099
|
+
has: (name) => byName.has(name),
|
|
1100
|
+
listMetadata: () => names.map((name) => {
|
|
1101
|
+
const hook = get(name);
|
|
1102
|
+
return {
|
|
1103
|
+
name: hook.name,
|
|
1104
|
+
description: hook.description,
|
|
1105
|
+
event: hook.event
|
|
1106
|
+
};
|
|
1107
|
+
}),
|
|
1108
|
+
get
|
|
1109
|
+
};
|
|
1110
|
+
}
|
|
1111
|
+
const builtInHookRegistry = createHookRegistry([{
|
|
1112
|
+
name: "session-doctor-bootstrap",
|
|
1113
|
+
load: () => ({
|
|
1114
|
+
name: "session-doctor-bootstrap",
|
|
1115
|
+
description: "Reports Agentyx project health at session start; silent when healthy.",
|
|
1116
|
+
event: "SessionStart",
|
|
1117
|
+
command: "npx",
|
|
1118
|
+
args: [
|
|
1119
|
+
"agentyx",
|
|
1120
|
+
"doctor",
|
|
1121
|
+
"--hook"
|
|
1122
|
+
]
|
|
1123
|
+
})
|
|
1124
|
+
}]);
|
|
1125
|
+
const builtInHookNames = builtInHookRegistry.names;
|
|
1126
|
+
//#endregion
|
|
1127
|
+
//#region src/pack/resolver.ts
|
|
1128
|
+
/**
|
|
1129
|
+
* Resolves selected packs without hidden inheritance.
|
|
1130
|
+
*
|
|
1131
|
+
* The result preserves configured order, removes duplicates by first
|
|
1132
|
+
* occurrence, and validates every requested pack.
|
|
1133
|
+
*
|
|
1134
|
+
* @throws {UnknownPackError} when a requested pack is missing.
|
|
1135
|
+
*/
|
|
1136
|
+
function resolvePacks(requestedPacks, registry = builtInPackRegistry) {
|
|
1137
|
+
const resolved = [];
|
|
1138
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1139
|
+
for (const name of requestedPacks) {
|
|
1140
|
+
if (seen.has(name)) continue;
|
|
1141
|
+
if (!registry.has(name)) throw new UnknownPackError(name, void 0, [...registry.keys()]);
|
|
1142
|
+
seen.add(name);
|
|
1143
|
+
resolved.push(name);
|
|
1144
|
+
}
|
|
1145
|
+
return resolved;
|
|
1146
|
+
}
|
|
1147
|
+
//#endregion
|
|
1148
|
+
//#region src/hook/resolver.ts
|
|
1149
|
+
function collectPackHookReferences(resolvedPacks, packRegistry, hookRegistry) {
|
|
1150
|
+
const hooks = [];
|
|
1151
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1152
|
+
for (const packName of resolvedPacks) for (const hook of packRegistry.get(packName)?.hooks ?? []) {
|
|
1153
|
+
const hookName = hook.name;
|
|
1154
|
+
if (seen.has(hookName)) continue;
|
|
1155
|
+
if (!hookRegistry.has(hookName)) throw new UnknownHookError(hookName, packName, hookRegistry.names);
|
|
1156
|
+
seen.add(hookName);
|
|
1157
|
+
hooks.push(hook);
|
|
1158
|
+
}
|
|
1159
|
+
return hooks;
|
|
1160
|
+
}
|
|
1161
|
+
function filterEffectiveHooks(hooks, enabledCapabilities) {
|
|
1162
|
+
const enabled = new Set(enabledCapabilities);
|
|
1163
|
+
return hooks.filter((hook) => hook.activation === "default" || enabled.has(hook.name)).map((hook) => hook.name);
|
|
1164
|
+
}
|
|
1165
|
+
function collectPackHooks(resolvedPacks, packRegistry, hookRegistry, enabledCapabilities = []) {
|
|
1166
|
+
return filterEffectiveHooks(collectPackHookReferences(resolvedPacks, packRegistry, hookRegistry), enabledCapabilities);
|
|
1167
|
+
}
|
|
1168
|
+
function resolvePackHookReferences(requestedPacks, packRegistry = builtInPackRegistry, hookRegistry = builtInHookRegistry) {
|
|
1169
|
+
return collectPackHookReferences(resolvePacks(requestedPacks, packRegistry), packRegistry, hookRegistry);
|
|
1170
|
+
}
|
|
1171
|
+
function resolvePackHooks(requestedPacks, packRegistry = builtInPackRegistry, hookRegistry = builtInHookRegistry, enabledCapabilities = []) {
|
|
1172
|
+
return collectPackHooks(resolvePacks(requestedPacks, packRegistry), packRegistry, hookRegistry, enabledCapabilities);
|
|
1173
|
+
}
|
|
1174
|
+
//#endregion
|
|
1175
|
+
//#region src/mcp/errors.ts
|
|
1176
|
+
var UnknownMcpServerError = class extends AgentyxError {
|
|
1177
|
+
serverName;
|
|
1178
|
+
requiredBy;
|
|
1179
|
+
knownServers;
|
|
1180
|
+
constructor(serverName, requiredBy, knownServers) {
|
|
1181
|
+
const origin = requiredBy === void 0 ? "" : ` (required by "${requiredBy}")`;
|
|
1182
|
+
const known = knownServers.length > 0 ? [...knownServers].sort().join(", ") : "none";
|
|
1183
|
+
super("unknown_mcp_server", `Unknown MCP server "${serverName}"${origin}. Known MCP servers: ${known}.`);
|
|
1184
|
+
this.name = "UnknownMcpServerError";
|
|
1185
|
+
this.serverName = serverName;
|
|
1186
|
+
this.requiredBy = requiredBy;
|
|
1187
|
+
this.knownServers = knownServers;
|
|
1188
|
+
}
|
|
1189
|
+
};
|
|
1190
|
+
var DuplicateMcpServerError = class extends AgentyxError {
|
|
1191
|
+
serverName;
|
|
1192
|
+
constructor(serverName) {
|
|
1193
|
+
super("duplicate_mcp_server", `Duplicate MCP server definition: "${serverName}".`);
|
|
1194
|
+
this.name = "DuplicateMcpServerError";
|
|
1195
|
+
this.serverName = serverName;
|
|
1196
|
+
}
|
|
1197
|
+
};
|
|
1198
|
+
var InvalidMcpServerError = class extends AgentyxError {
|
|
1199
|
+
origin;
|
|
1200
|
+
reason;
|
|
1201
|
+
constructor(origin, reason, options) {
|
|
1202
|
+
const detail = typeof reason === "string" ? reason : formatIssues(reason);
|
|
1203
|
+
super("invalid_mcp_server", `Invalid MCP server in ${origin}: ${detail}`, options);
|
|
1204
|
+
this.name = "InvalidMcpServerError";
|
|
1205
|
+
this.origin = origin;
|
|
1206
|
+
this.reason = detail;
|
|
1207
|
+
}
|
|
1208
|
+
};
|
|
1209
|
+
function formatIssues(error) {
|
|
1210
|
+
return error.issues.map((issue) => {
|
|
1211
|
+
const path = issue.path.map(String).join(".");
|
|
1212
|
+
return path === "" ? issue.message : `${path}: ${issue.message}`;
|
|
1213
|
+
}).join("; ");
|
|
1214
|
+
}
|
|
1215
|
+
//#endregion
|
|
1216
|
+
//#region src/mcp/registry.ts
|
|
1217
|
+
function createMcpServerRegistry(sources) {
|
|
1218
|
+
const byName = /* @__PURE__ */ new Map();
|
|
1219
|
+
for (const source of sources) {
|
|
1220
|
+
const name = mcpServerNameSchema.safeParse(source.name);
|
|
1221
|
+
if (!name.success) throw new InvalidMcpServerError(`MCP server source "${source.name}"`, name.error);
|
|
1222
|
+
if (byName.has(name.data)) throw new DuplicateMcpServerError(name.data);
|
|
1223
|
+
byName.set(name.data, source);
|
|
1224
|
+
}
|
|
1225
|
+
const names = [...byName.keys()];
|
|
1226
|
+
const loaded = /* @__PURE__ */ new Map();
|
|
1227
|
+
const get = (name) => {
|
|
1228
|
+
const cached = loaded.get(name);
|
|
1229
|
+
if (cached !== void 0) return cached;
|
|
1230
|
+
const source = byName.get(name);
|
|
1231
|
+
if (source === void 0) throw new UnknownMcpServerError(name, void 0, names);
|
|
1232
|
+
const origin = `MCP server "${name}"`;
|
|
1233
|
+
const server = mcpServerDefinitionSchema.safeParse(source.load());
|
|
1234
|
+
if (!server.success) throw new InvalidMcpServerError(origin, server.error);
|
|
1235
|
+
if (server.data.name !== name) throw new InvalidMcpServerError(origin, `it declares the name "${server.data.name}"`);
|
|
1236
|
+
loaded.set(name, server.data);
|
|
1237
|
+
return server.data;
|
|
1238
|
+
};
|
|
1239
|
+
return {
|
|
1240
|
+
names,
|
|
1241
|
+
has: (name) => byName.has(name),
|
|
1242
|
+
listMetadata: () => names.map((name) => {
|
|
1243
|
+
const server = get(name);
|
|
1244
|
+
return {
|
|
1245
|
+
name: server.name,
|
|
1246
|
+
description: server.description,
|
|
1247
|
+
transport: server.transport,
|
|
1248
|
+
contextCost: server.contextCost
|
|
1249
|
+
};
|
|
1250
|
+
}),
|
|
1251
|
+
get
|
|
1252
|
+
};
|
|
1253
|
+
}
|
|
1254
|
+
const builtInMcpServerRegistry = createMcpServerRegistry([
|
|
1255
|
+
{
|
|
1256
|
+
name: "context7",
|
|
1257
|
+
load: () => ({
|
|
1258
|
+
name: "context7",
|
|
1259
|
+
description: "Fetch up-to-date library documentation from Context7.",
|
|
1260
|
+
transport: "http",
|
|
1261
|
+
contextCost: "medium",
|
|
1262
|
+
url: "https://mcp.context7.com/mcp"
|
|
1263
|
+
})
|
|
1264
|
+
},
|
|
1265
|
+
{
|
|
1266
|
+
name: "playwright",
|
|
1267
|
+
load: () => ({
|
|
1268
|
+
name: "playwright",
|
|
1269
|
+
description: "Automate and inspect browsers through Playwright MCP.",
|
|
1270
|
+
transport: "stdio",
|
|
1271
|
+
contextCost: "high",
|
|
1272
|
+
command: "npx",
|
|
1273
|
+
args: ["@playwright/mcp@latest"]
|
|
1274
|
+
})
|
|
1275
|
+
},
|
|
1276
|
+
{
|
|
1277
|
+
name: "codebase-memory",
|
|
1278
|
+
load: () => ({
|
|
1279
|
+
name: "codebase-memory",
|
|
1280
|
+
description: "Structural code-intelligence MCP backed by a persistent code knowledge graph.",
|
|
1281
|
+
transport: "stdio",
|
|
1282
|
+
contextCost: "high",
|
|
1283
|
+
command: "codebase-memory-mcp"
|
|
1284
|
+
})
|
|
1285
|
+
},
|
|
1286
|
+
{
|
|
1287
|
+
name: "github",
|
|
1288
|
+
load: () => ({
|
|
1289
|
+
name: "github",
|
|
1290
|
+
description: "Read repositories, issues, pull requests and workflow runs on GitHub.",
|
|
1291
|
+
transport: "http",
|
|
1292
|
+
contextCost: "high",
|
|
1293
|
+
url: "https://api.githubcopilot.com/mcp/"
|
|
1294
|
+
})
|
|
1295
|
+
},
|
|
1296
|
+
{
|
|
1297
|
+
name: "sentry",
|
|
1298
|
+
load: () => ({
|
|
1299
|
+
name: "sentry",
|
|
1300
|
+
description: "Inspect production issues, events and stack traces recorded by Sentry.",
|
|
1301
|
+
transport: "http",
|
|
1302
|
+
contextCost: "medium",
|
|
1303
|
+
url: "https://mcp.sentry.dev/mcp"
|
|
1304
|
+
})
|
|
1305
|
+
},
|
|
1306
|
+
{
|
|
1307
|
+
name: "chrome-devtools",
|
|
1308
|
+
load: () => ({
|
|
1309
|
+
name: "chrome-devtools",
|
|
1310
|
+
description: "Record performance traces and inspect pages through Chrome DevTools.",
|
|
1311
|
+
transport: "stdio",
|
|
1312
|
+
contextCost: "high",
|
|
1313
|
+
command: "npx",
|
|
1314
|
+
args: ["-y", "chrome-devtools-mcp@latest"]
|
|
1315
|
+
})
|
|
1316
|
+
},
|
|
1317
|
+
{
|
|
1318
|
+
name: "supabase",
|
|
1319
|
+
load: () => ({
|
|
1320
|
+
name: "supabase",
|
|
1321
|
+
description: "Inspect and query Supabase project schemas, tables and logs.",
|
|
1322
|
+
transport: "stdio",
|
|
1323
|
+
contextCost: "high",
|
|
1324
|
+
command: "npx",
|
|
1325
|
+
args: ["-y", "@supabase/mcp-server-supabase@latest"],
|
|
1326
|
+
env: { SUPABASE_ACCESS_TOKEN: { fromEnv: "SUPABASE_ACCESS_TOKEN" } }
|
|
1327
|
+
})
|
|
1328
|
+
}
|
|
1329
|
+
]);
|
|
1330
|
+
const builtInMcpServerNames = builtInMcpServerRegistry.names;
|
|
1331
|
+
//#endregion
|
|
1332
|
+
//#region src/mcp/resolver.ts
|
|
1333
|
+
function collectPackMcpServerReferences(resolvedPacks, packRegistry, mcpRegistry) {
|
|
1334
|
+
const servers = [];
|
|
1335
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1336
|
+
for (const packName of resolvedPacks) for (const server of packRegistry.get(packName)?.mcpServers ?? []) {
|
|
1337
|
+
const serverName = server.name;
|
|
1338
|
+
if (seen.has(serverName)) continue;
|
|
1339
|
+
if (!mcpRegistry.has(serverName)) throw new UnknownMcpServerError(serverName, packName, mcpRegistry.names);
|
|
1340
|
+
seen.add(serverName);
|
|
1341
|
+
servers.push(server);
|
|
1342
|
+
}
|
|
1343
|
+
return servers;
|
|
1344
|
+
}
|
|
1345
|
+
function filterEffectiveMcpServers(servers, enabledCapabilities) {
|
|
1346
|
+
const enabled = new Set(enabledCapabilities);
|
|
1347
|
+
return servers.filter((server) => server.activation === "default" || enabled.has(server.name)).map((server) => server.name);
|
|
1348
|
+
}
|
|
1349
|
+
function collectPackMcpServers(resolvedPacks, packRegistry, mcpRegistry, enabledCapabilities = []) {
|
|
1350
|
+
return filterEffectiveMcpServers(collectPackMcpServerReferences(resolvedPacks, packRegistry, mcpRegistry), enabledCapabilities);
|
|
1351
|
+
}
|
|
1352
|
+
function resolvePackMcpServerReferences(requestedPacks, packRegistry = builtInPackRegistry, mcpRegistry = builtInMcpServerRegistry) {
|
|
1353
|
+
return collectPackMcpServerReferences(resolvePacks(requestedPacks, packRegistry), packRegistry, mcpRegistry);
|
|
1354
|
+
}
|
|
1355
|
+
function resolvePackMcpServers(requestedPacks, packRegistry = builtInPackRegistry, mcpRegistry = builtInMcpServerRegistry, enabledCapabilities = []) {
|
|
1356
|
+
return collectPackMcpServers(resolvePacks(requestedPacks, packRegistry), packRegistry, mcpRegistry, enabledCapabilities);
|
|
1357
|
+
}
|
|
1358
|
+
//#endregion
|
|
1359
|
+
//#region src/skill/resolver.ts
|
|
1360
|
+
/**
|
|
1361
|
+
* Collects the skills contributed by an already-resolved pack chain.
|
|
1362
|
+
*
|
|
1363
|
+
* Only identifiers are produced — no skill body is read — so this stays cheap
|
|
1364
|
+
* enough to run on every `resolve`.
|
|
1365
|
+
*
|
|
1366
|
+
* @throws {UnknownSkillError} when a pack references a skill the registry lacks.
|
|
1367
|
+
*/
|
|
1368
|
+
function collectPackSkills(resolvedPacks, packRegistry, skillRegistry) {
|
|
1369
|
+
const skills = [];
|
|
1370
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1371
|
+
for (const packName of resolvedPacks) for (const skillName of packRegistry.get(packName)?.skills ?? []) {
|
|
1372
|
+
if (seen.has(skillName)) continue;
|
|
1373
|
+
if (!skillRegistry.has(skillName)) throw new UnknownSkillError(skillName, packName, skillRegistry.names);
|
|
1374
|
+
seen.add(skillName);
|
|
1375
|
+
skills.push(skillName);
|
|
1376
|
+
}
|
|
1377
|
+
return skills;
|
|
1378
|
+
}
|
|
1379
|
+
/**
|
|
1380
|
+
* Resolves the requested packs and returns the skills they contribute.
|
|
1381
|
+
*
|
|
1382
|
+
* The result follows the resolved pack order, then each pack's declaration
|
|
1383
|
+
* order, and is de-duplicated by first occurrence. Skills do not extend other
|
|
1049
1384
|
* skills, so there is nothing further to expand.
|
|
1050
1385
|
*
|
|
1051
1386
|
* @throws {UnknownPackError} when a requested pack is missing.
|
|
@@ -1167,15 +1502,19 @@ function resolvePackTools(requestedPacks, packRegistry = builtInPackRegistry, to
|
|
|
1167
1502
|
* The input is never mutated.
|
|
1168
1503
|
*
|
|
1169
1504
|
* @throws {UnknownPackError} when a configured pack is missing.
|
|
1170
|
-
* @throws {CircularPackDependencyError} when inheritance forms a cycle.
|
|
1171
1505
|
* @throws {UnknownSkillError} when a pack references an unknown skill.
|
|
1172
1506
|
*/
|
|
1173
|
-
function resolveAgentyxConfig(config, registry = builtInPackRegistry, skillRegistry = builtInSkillRegistry, mcpRegistry = builtInMcpServerRegistry, toolRegistry = builtInToolRegistry) {
|
|
1507
|
+
function resolveAgentyxConfig(config, registry = builtInPackRegistry, skillRegistry = builtInSkillRegistry, mcpRegistry = builtInMcpServerRegistry, toolRegistry = builtInToolRegistry, hookRegistry = builtInHookRegistry) {
|
|
1174
1508
|
const requestedPacks = [...config.packs];
|
|
1175
1509
|
const resolvedPacks = resolvePacks(requestedPacks, registry);
|
|
1176
1510
|
const declaredMcpServers = collectPackMcpServerReferences(resolvedPacks, registry, mcpRegistry);
|
|
1177
1511
|
const declaredTools = collectPackToolReferences(resolvedPacks, registry, toolRegistry);
|
|
1178
|
-
const
|
|
1512
|
+
const declaredHooks = collectPackHookReferences(resolvedPacks, registry, hookRegistry);
|
|
1513
|
+
const knownOptionalCapabilities = [
|
|
1514
|
+
...declaredMcpServers.filter((server) => server.activation === "optional").map((server) => server.name),
|
|
1515
|
+
...declaredTools.filter((tool) => tool.activation === "optional").map((tool) => tool.name),
|
|
1516
|
+
...declaredHooks.filter((hook) => hook.activation === "optional").map((hook) => hook.name)
|
|
1517
|
+
];
|
|
1179
1518
|
for (const capability of config.enable) if (!knownOptionalCapabilities.includes(capability)) throw new UnknownEnabledCapabilityError(capability, knownOptionalCapabilities);
|
|
1180
1519
|
return {
|
|
1181
1520
|
requestedPacks,
|
|
@@ -1185,6 +1524,8 @@ function resolveAgentyxConfig(config, registry = builtInPackRegistry, skillRegis
|
|
|
1185
1524
|
mcpServers: filterEffectiveMcpServers(declaredMcpServers, config.enable),
|
|
1186
1525
|
declaredTools,
|
|
1187
1526
|
tools: filterEffectiveTools(declaredTools, config.enable),
|
|
1527
|
+
declaredHooks,
|
|
1528
|
+
hooks: filterEffectiveHooks(declaredHooks, config.enable),
|
|
1188
1529
|
enabled: [...config.enable],
|
|
1189
1530
|
targets: [...config.targets]
|
|
1190
1531
|
};
|
|
@@ -1263,7 +1604,26 @@ const mcpManifestEntrySchema = z.strictObject({
|
|
|
1263
1604
|
hash: contentHashSchema,
|
|
1264
1605
|
created: z.boolean()
|
|
1265
1606
|
});
|
|
1266
|
-
|
|
1607
|
+
/**
|
|
1608
|
+
* A provider hook configuration file Agentyx contributed entries to.
|
|
1609
|
+
*
|
|
1610
|
+
* Shares the `mcp` entry's shape and rationale: the file is shared with the
|
|
1611
|
+
* user, so the entry records the hook names Agentyx added rather than
|
|
1612
|
+
* claiming the whole document.
|
|
1613
|
+
*/
|
|
1614
|
+
const hookManifestEntrySchema = z.strictObject({
|
|
1615
|
+
kind: z.literal("hook"),
|
|
1616
|
+
path: managedPathSchema,
|
|
1617
|
+
hooks: z.array(z.string().min(1, "Hook names must be non-empty strings.")),
|
|
1618
|
+
targets: targetsSchema,
|
|
1619
|
+
hash: contentHashSchema,
|
|
1620
|
+
created: z.boolean()
|
|
1621
|
+
});
|
|
1622
|
+
const installManifestEntrySchema = z.discriminatedUnion("kind", [
|
|
1623
|
+
skillManifestEntrySchema,
|
|
1624
|
+
mcpManifestEntrySchema,
|
|
1625
|
+
hookManifestEntrySchema
|
|
1626
|
+
]);
|
|
1267
1627
|
/** The `.agentyx.lock.json` record of everything Agentyx installed into a project. */
|
|
1268
1628
|
const installManifestSchema = z.strictObject({
|
|
1269
1629
|
version: z.literal(1),
|
|
@@ -1340,13 +1700,21 @@ async function loadInstallManifest(projectPath = process.cwd()) {
|
|
|
1340
1700
|
* touches the filesystem.
|
|
1341
1701
|
*/
|
|
1342
1702
|
function formatInstallManifest(manifest) {
|
|
1343
|
-
const entries = [...manifest.entries].map((entry) =>
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1703
|
+
const entries = [...manifest.entries].map((entry) => {
|
|
1704
|
+
if (entry.kind === "skill") return {
|
|
1705
|
+
...entry,
|
|
1706
|
+
targets: sorted(entry.targets)
|
|
1707
|
+
};
|
|
1708
|
+
if (entry.kind === "mcp") return {
|
|
1709
|
+
...entry,
|
|
1710
|
+
targets: sorted(entry.targets),
|
|
1711
|
+
servers: sorted(entry.servers)
|
|
1712
|
+
};
|
|
1713
|
+
return {
|
|
1714
|
+
...entry,
|
|
1715
|
+
targets: sorted(entry.targets),
|
|
1716
|
+
hooks: sorted(entry.hooks)
|
|
1717
|
+
};
|
|
1350
1718
|
}).sort((left, right) => left.path.localeCompare(right.path));
|
|
1351
1719
|
return `${JSON.stringify({
|
|
1352
1720
|
version: manifest.version,
|
|
@@ -1527,6 +1895,94 @@ function isNotFound(cause) {
|
|
|
1527
1895
|
return cause instanceof Error && cause.code === "ENOENT";
|
|
1528
1896
|
}
|
|
1529
1897
|
//#endregion
|
|
1530
|
-
|
|
1898
|
+
//#region src/source/inspection.ts
|
|
1899
|
+
async function inspectTrustedSource(projectPath, reference) {
|
|
1900
|
+
const definition = getTrustedSourceDefinition(reference);
|
|
1901
|
+
const projectDir = resolve(projectPath);
|
|
1902
|
+
const projectRealPath = await realpath(projectDir);
|
|
1903
|
+
const root = resolve(projectDir, reference.path);
|
|
1904
|
+
const rootRealPath = await realpathOrThrow(reference.name, root, "the checkout path cannot be read");
|
|
1905
|
+
if (!isInside(rootRealPath, projectRealPath)) throw new TrustedSourceLoadError(reference.name, "the checkout path resolves outside the project");
|
|
1906
|
+
const manifestPath = join(rootRealPath, definition.manifestPath);
|
|
1907
|
+
const manifest = await readManifest(reference.name, manifestPath);
|
|
1908
|
+
if (manifest.name !== definition.name) throw new TrustedSourceLoadError(reference.name, `${definition.manifestPath} declares the name "${manifest.name}"`);
|
|
1909
|
+
if (manifest.repository !== definition.repository) throw new TrustedSourceLoadError(reference.name, `${definition.manifestPath} declares repository "${manifest.repository}"`);
|
|
1910
|
+
const skillsPath = resolve(dirname(manifestPath), manifest.skills);
|
|
1911
|
+
if (!isInside(skillsPath, rootRealPath)) throw new TrustedSourceLoadError(reference.name, "the manifest skills path resolves outside the checkout");
|
|
1912
|
+
const skillsRealPath = await realpathOrThrow(reference.name, skillsPath, "the manifest skills path cannot be read");
|
|
1913
|
+
if (!isInside(skillsRealPath, rootRealPath)) throw new TrustedSourceLoadError(reference.name, "the manifest skills path resolves outside the checkout");
|
|
1914
|
+
const skills = await inspectSkillSummaries(reference.name, skillsRealPath);
|
|
1915
|
+
return {
|
|
1916
|
+
definition,
|
|
1917
|
+
reference,
|
|
1918
|
+
rootPath: rootRealPath,
|
|
1919
|
+
manifest,
|
|
1920
|
+
skillsPath: skillsRealPath,
|
|
1921
|
+
skills,
|
|
1922
|
+
installable: definition.installStatus === "installable" && skills.every((skill) => !skill.hasResources),
|
|
1923
|
+
installNote: definition.installNote
|
|
1924
|
+
};
|
|
1925
|
+
}
|
|
1926
|
+
async function readManifest(sourceName, path) {
|
|
1927
|
+
let raw;
|
|
1928
|
+
try {
|
|
1929
|
+
raw = await readFile(path, "utf8");
|
|
1930
|
+
} catch (cause) {
|
|
1931
|
+
throw trustedSourceLoadError(sourceName, "the plugin manifest cannot be read", cause);
|
|
1932
|
+
}
|
|
1933
|
+
try {
|
|
1934
|
+
return codexPluginManifestSchema.parse(JSON.parse(raw));
|
|
1935
|
+
} catch (cause) {
|
|
1936
|
+
throw trustedSourceLoadError(sourceName, "the plugin manifest is invalid", cause);
|
|
1937
|
+
}
|
|
1938
|
+
}
|
|
1939
|
+
async function inspectSkillSummaries(sourceName, skillsPath) {
|
|
1940
|
+
let entries;
|
|
1941
|
+
try {
|
|
1942
|
+
entries = await readdir(skillsPath, { withFileTypes: true });
|
|
1943
|
+
} catch (cause) {
|
|
1944
|
+
throw trustedSourceLoadError(sourceName, "the plugin skills directory cannot be listed", cause);
|
|
1945
|
+
}
|
|
1946
|
+
const skills = [];
|
|
1947
|
+
for (const entry of entries.sort((left, right) => left.name.localeCompare(right.name))) {
|
|
1948
|
+
if (!entry.isDirectory()) continue;
|
|
1949
|
+
const skillRoot = join(skillsPath, entry.name);
|
|
1950
|
+
const skillPath = join(skillRoot, "SKILL.md");
|
|
1951
|
+
let markdown;
|
|
1952
|
+
try {
|
|
1953
|
+
markdown = await readFile(skillPath, "utf8");
|
|
1954
|
+
} catch (cause) {
|
|
1955
|
+
if (cause instanceof Error && cause.code === "ENOENT") continue;
|
|
1956
|
+
throw trustedSourceLoadError(sourceName, `cannot read ${entry.name}/SKILL.md`, cause);
|
|
1957
|
+
}
|
|
1958
|
+
const skill = parseSkillMarkdown(markdown, skillPath);
|
|
1959
|
+
if (skill.name !== entry.name) throw new TrustedSourceLoadError(sourceName, `${entry.name}/SKILL.md declares the name "${skill.name}"`);
|
|
1960
|
+
skills.push({
|
|
1961
|
+
name: skill.name,
|
|
1962
|
+
description: skill.description,
|
|
1963
|
+
hasResources: await hasResources(skillRoot)
|
|
1964
|
+
});
|
|
1965
|
+
}
|
|
1966
|
+
return skills;
|
|
1967
|
+
}
|
|
1968
|
+
async function hasResources(skillRoot) {
|
|
1969
|
+
return (await readdir(skillRoot, { withFileTypes: true })).some((entry) => entry.name !== "SKILL.md");
|
|
1970
|
+
}
|
|
1971
|
+
async function realpathOrThrow(sourceName, path, reason) {
|
|
1972
|
+
try {
|
|
1973
|
+
return await realpath(path);
|
|
1974
|
+
} catch (cause) {
|
|
1975
|
+
throw trustedSourceLoadError(sourceName, reason, cause);
|
|
1976
|
+
}
|
|
1977
|
+
}
|
|
1978
|
+
function isInside(path, root) {
|
|
1979
|
+
const pathFromRoot = relative(root, path);
|
|
1980
|
+
return pathFromRoot === "" || !pathFromRoot.startsWith(`..${sep}`) && pathFromRoot !== ".." && !isAbsolute(pathFromRoot);
|
|
1981
|
+
}
|
|
1982
|
+
function trustedSourceLoadError(sourceName, reason, cause) {
|
|
1983
|
+
return new TrustedSourceLoadError(sourceName, reason, cause instanceof Error ? { cause } : void 0);
|
|
1984
|
+
}
|
|
1985
|
+
//#endregion
|
|
1986
|
+
export { AGENTYX_CONFIG_FILENAME, AGENTYX_CONFIG_SCHEMA_ID, AGENTYX_MANIFEST_FILENAME, AgentyxConfigNotFoundError, AgentyxConfigParseError, AgentyxConfigValidationError, AgentyxError, AgentyxManifestParseError, AgentyxManifestValidationError, CircularPackDependencyError, DuplicateHookError, DuplicateMcpServerError, DuplicatePackError, DuplicateSkillError, DuplicateToolError, DuplicateTrustedSourceError, HOOK_ACTIVATION_LEVELS, HOOK_EVENTS, INSTALL_MANIFEST_VERSION, InvalidHookError, InvalidMcpServerError, InvalidSkillError, InvalidToolError, LocalSkillDirectoryError, MCP_CAPABILITY_LEVELS, MCP_CONTEXT_COSTS, PACKAGE_MANAGERS, PACK_CATEGORIES, TOOL_ACTIVATION_LEVELS, TrustedSourceLoadError, UnknownEnabledCapabilityError, UnknownHookError, UnknownMcpServerError, UnknownPackError, UnknownSkillError, UnknownToolError, UnknownTrustedSourceError, agentyxConfigPath, agentyxConfigSchema, agentyxCoreName, agentyxManifestPath, agentyxTargetSchema, buildAgentyxConfig, buildAgentyxConfigJsonSchema, builtInHookNames, builtInHookRegistry, builtInMcpServerNames, builtInMcpServerRegistry, builtInPackRegistry, builtInPacks, builtInSkillNames, builtInSkillRegistry, builtInToolNames, builtInToolRegistry, codexPluginManifestSchema, collectPackHookReferences, collectPackHooks, collectPackMcpServerReferences, collectPackMcpServers, collectPackToolReferences, createHookRegistry, createMcpServerRegistry, createPackRegistry, createSkillRegistry, createToolRegistry, createTrustedSourceRegistry, detectProject, emptyInstallManifest, enabledCapabilityNameSchema, filterEffectiveHooks, filterEffectiveMcpServers, filterEffectiveTools, formatAgentyxConfig, formatInstallManifest, formatSkillMarkdown, getCoreStatus, getTrustedSourceDefinition, hashContent, hookActivationLevelSchema, hookDefinitionSchema, hookEventSchema, hookManifestEntrySchema, hookNameSchema, hookReferenceSchema, inspectTrustedSource, installManifestEntrySchema, installManifestSchema, knownTrustedSourceRegistry, knownTrustedSources, loadAgentyxConfig, loadAgentyxProject, loadInstallManifest, manifestEntriesByPath, mcpCapabilityLevelSchema, mcpContextCostSchema, mcpEnvReferenceSchema, mcpManifestEntrySchema, mcpServerDefinitionSchema, mcpServerReferenceSchema, packCategorySchema, packDefinitionSchema, packNameSchema, parseAgentyxConfig, parseInstallManifest, parseSkillMarkdown, resolveAgentyxConfig, resolvePackHookReferences, resolvePackHooks, resolvePackMcpServerReferences, resolvePackMcpServers, resolvePackSkills, resolvePackToolReferences, resolvePackTools, resolvePacks, skillDefinitionSchema, skillDirectorySchema, skillManifestEntrySchema, toolActivationLevelSchema, toolDefinitionSchema, toolNameSchema, toolReferenceSchema, trustedSourceDefinitionSchema, trustedSourceNameSchema, trustedSourcePathSchema, trustedSourceReferenceSchema, trustedSourceSkillSummarySchema };
|
|
1531
1987
|
|
|
1532
1988
|
//# sourceMappingURL=index.mjs.map
|