@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.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/skill/resolver.ts
1025
- /**
1026
- * Collects the skills contributed by an already-resolved pack chain.
1027
- *
1028
- * Only identifiers are produced — no skill body is read — so this stays cheap
1029
- * enough to run on every `resolve`.
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
- return skills;
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
- * Resolves the requested packs and returns the skills they contribute.
959
+ * Loads `.agentyx.json` and any checked-in Agent Skills it declares.
1046
960
  *
1047
- * The result follows the resolved pack order, then each pack's declaration
1048
- * order, and is de-duplicated by first occurrence. Skills do not extend other
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 knownOptionalCapabilities = [...declaredMcpServers.filter((server) => server.activation === "optional").map((server) => server.name), ...declaredTools.filter((tool) => tool.activation === "optional").map((tool) => tool.name)];
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
- const installManifestEntrySchema = z.discriminatedUnion("kind", [skillManifestEntrySchema, mcpManifestEntrySchema]);
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) => entry.kind === "skill" ? {
1344
- ...entry,
1345
- targets: sorted(entry.targets)
1346
- } : {
1347
- ...entry,
1348
- targets: sorted(entry.targets),
1349
- servers: sorted(entry.servers)
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
- export { AGENTYX_CONFIG_FILENAME, AGENTYX_CONFIG_SCHEMA_ID, AGENTYX_MANIFEST_FILENAME, AgentyxConfigNotFoundError, AgentyxConfigParseError, AgentyxConfigValidationError, AgentyxError, AgentyxManifestParseError, AgentyxManifestValidationError, CircularPackDependencyError, DuplicateMcpServerError, DuplicatePackError, DuplicateSkillError, DuplicateToolError, INSTALL_MANIFEST_VERSION, InvalidMcpServerError, InvalidSkillError, InvalidToolError, MCP_CAPABILITY_LEVELS, MCP_CONTEXT_COSTS, PACKAGE_MANAGERS, PACK_CATEGORIES, TOOL_ACTIVATION_LEVELS, UnknownEnabledCapabilityError, UnknownMcpServerError, UnknownPackError, UnknownSkillError, UnknownToolError, agentyxConfigPath, agentyxConfigSchema, agentyxCoreName, agentyxManifestPath, agentyxTargetSchema, buildAgentyxConfig, buildAgentyxConfigJsonSchema, builtInMcpServerNames, builtInMcpServerRegistry, builtInPackRegistry, builtInPacks, builtInSkillNames, builtInSkillRegistry, builtInToolNames, builtInToolRegistry, collectPackMcpServerReferences, collectPackMcpServers, collectPackToolReferences, createMcpServerRegistry, createPackRegistry, createSkillRegistry, createToolRegistry, detectProject, emptyInstallManifest, enabledCapabilityNameSchema, filterEffectiveMcpServers, filterEffectiveTools, formatAgentyxConfig, formatInstallManifest, formatSkillMarkdown, getCoreStatus, hashContent, installManifestEntrySchema, installManifestSchema, loadAgentyxConfig, loadInstallManifest, manifestEntriesByPath, mcpCapabilityLevelSchema, mcpContextCostSchema, mcpEnvReferenceSchema, mcpManifestEntrySchema, mcpServerDefinitionSchema, mcpServerReferenceSchema, packCategorySchema, packDefinitionSchema, packNameSchema, parseAgentyxConfig, parseInstallManifest, parseSkillMarkdown, resolveAgentyxConfig, resolvePackMcpServerReferences, resolvePackMcpServers, resolvePackSkills, resolvePackToolReferences, resolvePackTools, resolvePacks, skillDefinitionSchema, skillManifestEntrySchema, toolActivationLevelSchema, toolDefinitionSchema, toolNameSchema, toolReferenceSchema };
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