@agentyx/core 0.1.1 → 0.3.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.
Files changed (60) hide show
  1. package/README.md +28 -47
  2. package/dist/index.d.mts +396 -155
  3. package/dist/index.d.mts.map +1 -1
  4. package/dist/index.mjs +777 -226
  5. package/dist/index.mjs.map +1 -1
  6. package/package.json +1 -1
  7. package/schema/agentyx.schema.json +11 -7
  8. package/skills/angular-architecture/SKILL.md +29 -0
  9. package/skills/angular-modern/SKILL.md +12 -30
  10. package/skills/angular-signals/SKILL.md +23 -0
  11. package/skills/angular-testing/SKILL.md +24 -0
  12. package/skills/api-design/SKILL.md +29 -0
  13. package/skills/api-documentation/SKILL.md +34 -0
  14. package/skills/aria-patterns/SKILL.md +36 -0
  15. package/skills/auth-patterns/SKILL.md +35 -0
  16. package/skills/brainstorming/SKILL.md +12 -0
  17. package/skills/branching-strategy/SKILL.md +34 -0
  18. package/skills/ci-pipelines/SKILL.md +34 -0
  19. package/skills/code-quality/SKILL.md +28 -0
  20. package/skills/code-review/SKILL.md +23 -0
  21. package/skills/commit-hygiene/SKILL.md +34 -0
  22. package/skills/concise-output/SKILL.md +24 -0
  23. package/skills/containerization/SKILL.md +38 -0
  24. package/skills/context-efficient-development/SKILL.md +24 -0
  25. package/skills/data-modeling/SKILL.md +34 -0
  26. package/skills/decision-records/SKILL.md +34 -0
  27. package/skills/dependency-hygiene/SKILL.md +34 -0
  28. package/skills/dependency-security/SKILL.md +35 -0
  29. package/skills/deployment-safety/SKILL.md +33 -0
  30. package/skills/e2e-testing/SKILL.md +34 -0
  31. package/skills/engineering-principles/SKILL.md +34 -0
  32. package/skills/flaky-tests/SKILL.md +29 -0
  33. package/skills/focused-verification/SKILL.md +22 -0
  34. package/skills/incident-response/SKILL.md +34 -0
  35. package/skills/infrastructure-as-code/SKILL.md +34 -0
  36. package/skills/keyboard-navigation/SKILL.md +34 -0
  37. package/skills/legacy-code/SKILL.md +34 -0
  38. package/skills/metrics-and-tracing/SKILL.md +33 -0
  39. package/skills/parallel-work/SKILL.md +12 -0
  40. package/skills/performance-profiling/SKILL.md +34 -0
  41. package/skills/pull-requests/SKILL.md +34 -0
  42. package/skills/query-performance/SKILL.md +34 -0
  43. package/skills/refactoring-safely/SKILL.md +34 -0
  44. package/skills/requesting-code-review/SKILL.md +13 -0
  45. package/skills/schema-migrations/SKILL.md +34 -0
  46. package/skills/secrets-handling/SKILL.md +34 -0
  47. package/skills/secure-coding/SKILL.md +33 -0
  48. package/skills/semantic-html/SKILL.md +34 -0
  49. package/skills/structured-logging/SKILL.md +34 -0
  50. package/skills/subagent-driven-development/SKILL.md +13 -0
  51. package/skills/targeted-exploration/SKILL.md +24 -0
  52. package/skills/technical-writing/SKILL.md +34 -0
  53. package/skills/test-doubles/SKILL.md +30 -0
  54. package/skills/test-strategy/SKILL.md +35 -0
  55. package/skills/transactions-and-consistency/SKILL.md +34 -0
  56. package/skills/typescript-modeling/SKILL.md +23 -0
  57. package/skills/typescript-modern/SKILL.md +15 -27
  58. package/skills/typescript-strict/SKILL.md +23 -0
  59. package/skills/web-vitals/SKILL.md +34 -0
  60. package/skills/worktree-workflow/SKILL.md +12 -0
package/dist/index.mjs CHANGED
@@ -3,6 +3,7 @@ import { access, readFile } from "node:fs/promises";
3
3
  import { join, resolve } from "node:path";
4
4
  import { readFileSync } from "node:fs";
5
5
  import { fileURLToPath } from "node:url";
6
+ import { createHash } from "node:crypto";
6
7
  //#region src/errors.ts
7
8
  /**
8
9
  * Base class for every failure Agentyx raises deliberately.
@@ -20,6 +21,27 @@ var AgentyxError = class extends Error {
20
21
  }
21
22
  };
22
23
  //#endregion
24
+ //#region src/issues.ts
25
+ /**
26
+ * Turns a Zod failure into locations a reader can find in the file.
27
+ *
28
+ * Shared by every document Agentyx validates, so a problem in `.agentyx.json`
29
+ * and a problem in `.agentyx.lock.json` are reported the same way.
30
+ */
31
+ function toAgentyxIssues(error) {
32
+ return error.issues.map((issue) => ({
33
+ path: formatIssuePath(issue.path),
34
+ message: issue.message
35
+ }));
36
+ }
37
+ function formatIssuePath(path) {
38
+ if (path.length === 0) return "(root)";
39
+ return path.reduce((formatted, segment) => {
40
+ if (typeof segment === "number") return `${formatted}[${segment}]`;
41
+ return formatted === "" ? String(segment) : `${formatted}.${String(segment)}`;
42
+ }, "");
43
+ }
44
+ //#endregion
23
45
  //#region src/config/errors.ts
24
46
  /** Raised when the supplied directory has no `.agentyx.json`. */
25
47
  var AgentyxConfigNotFoundError = class extends AgentyxError {
@@ -45,7 +67,7 @@ var AgentyxConfigValidationError = class extends AgentyxError {
45
67
  /** The file the configuration came from, when it was read from disk. */
46
68
  filePath;
47
69
  constructor(error, filePath) {
48
- const issues = toConfigIssues(error);
70
+ const issues = toAgentyxIssues(error);
49
71
  const location = filePath === void 0 ? "" : ` in ${filePath}`;
50
72
  const details = issues.map((issue) => ` - ${issue.path}: ${issue.message}`).join("\n");
51
73
  super("agentyx_config_invalid", `Invalid Agentyx configuration${location}:\n${details}`);
@@ -54,26 +76,20 @@ var AgentyxConfigValidationError = class extends AgentyxError {
54
76
  this.filePath = filePath;
55
77
  }
56
78
  };
57
- function toConfigIssues(error) {
58
- return error.issues.map((issue) => ({
59
- path: formatIssuePath(issue.path),
60
- message: issue.message
61
- }));
62
- }
63
- function formatIssuePath(path) {
64
- if (path.length === 0) return "(root)";
65
- return path.reduce((formatted, segment) => {
66
- if (typeof segment === "number") return `${formatted}[${segment}]`;
67
- return formatted === "" ? String(segment) : `${formatted}.${String(segment)}`;
68
- }, "");
69
- }
79
+ var UnknownEnabledCapabilityError = class extends AgentyxError {
80
+ capabilityName;
81
+ knownCapabilities;
82
+ constructor(capabilityName, knownCapabilities) {
83
+ const known = knownCapabilities.length > 0 ? [...knownCapabilities].sort().join(", ") : "none";
84
+ super("unknown_enabled_capability", `Unknown enabled capability "${capabilityName}". Known optional capabilities: ${known}.`);
85
+ this.name = "UnknownEnabledCapabilityError";
86
+ this.capabilityName = capabilityName;
87
+ this.knownCapabilities = knownCapabilities;
88
+ }
89
+ };
70
90
  //#endregion
71
91
  //#region src/mcp/schema.ts
72
- const MCP_CAPABILITY_LEVELS = [
73
- "essential",
74
- "recommended",
75
- "optional"
76
- ];
92
+ const MCP_CAPABILITY_LEVELS = ["default", "optional"];
77
93
  const mcpCapabilityLevelSchema = z.enum(MCP_CAPABILITY_LEVELS);
78
94
  const MCP_CONTEXT_COSTS = [
79
95
  "low",
@@ -91,14 +107,14 @@ const commonMcpServerDefinitionSchema = z.strictObject({
91
107
  });
92
108
  const explicitMcpServerReferenceSchema = z.strictObject({
93
109
  name: mcpServerNameSchema.describe("MCP server identifier."),
94
- level: mcpCapabilityLevelSchema.describe("How important this MCP capability is to the declaring stack.").default("recommended")
110
+ activation: mcpCapabilityLevelSchema.describe("Whether this MCP capability is active by default or explicit opt-in.").default("default")
95
111
  });
96
112
  const mcpServerReferenceSchema = z.union([mcpServerNameSchema, explicitMcpServerReferenceSchema]).transform((value) => typeof value === "string" ? {
97
113
  name: value,
98
- level: "recommended"
114
+ activation: "default"
99
115
  } : value).pipe(z.strictObject({
100
116
  name: mcpServerNameSchema.describe("MCP server identifier."),
101
- level: mcpCapabilityLevelSchema.describe("How important this MCP capability is to the declaring stack.")
117
+ activation: mcpCapabilityLevelSchema.describe("Whether this MCP capability is active by default or explicit opt-in.")
102
118
  }));
103
119
  const stdioMcpServerDefinitionSchema = commonMcpServerDefinitionSchema.extend({
104
120
  transport: z.literal("stdio"),
@@ -135,44 +151,65 @@ const skillMetadataSchema = z.strictObject({
135
151
  */
136
152
  const skillDefinitionSchema = skillMetadataSchema.extend({ content: z.string().trim().min(1, "Skill content must be non-empty.").describe("The Markdown instructions handed to an agent.") });
137
153
  //#endregion
138
- //#region src/stack/schema.ts
139
- /**
140
- * Stack names are plain identifiers. They are intentionally not constrained to
141
- * a closed list so that external registries can contribute stacks later.
142
- */
143
- const stackNameSchema = z.string().min(1, "Stack names must be non-empty strings.");
154
+ //#region src/tool/schema.ts
155
+ const TOOL_ACTIVATION_LEVELS = ["default", "optional"];
156
+ const toolActivationLevelSchema = z.enum(TOOL_ACTIVATION_LEVELS);
157
+ const toolNameSchema = z.string().min(1, "Tool names must be non-empty strings.").regex(/^[a-z0-9]+(?:-[a-z0-9]+)*$/, "Tool names must be lowercase kebab-case, for example \"rtk\".");
158
+ const toolReferenceSchema = z.union([toolNameSchema, z.strictObject({
159
+ name: toolNameSchema.describe("Tool identifier."),
160
+ activation: toolActivationLevelSchema.default("default")
161
+ })]).transform((value) => typeof value === "string" ? {
162
+ name: value,
163
+ activation: "default"
164
+ } : value).pipe(z.strictObject({
165
+ name: toolNameSchema.describe("Tool identifier."),
166
+ activation: toolActivationLevelSchema
167
+ }));
168
+ const toolDefinitionSchema = z.strictObject({
169
+ name: toolNameSchema.describe("Unique tool identifier."),
170
+ description: z.string().trim().min(1, "Tool descriptions must be non-empty strings."),
171
+ kind: z.literal("executable"),
172
+ command: z.string().trim().min(1, "Tool executable command must be a non-empty string."),
173
+ optional: z.boolean().default(true),
174
+ installHint: z.string().trim().min(1, "Tool install hints must be non-empty strings.").optional()
175
+ });
176
+ //#endregion
177
+ //#region src/pack/schema.ts
178
+ const PACK_CATEGORIES = [
179
+ "engineering",
180
+ "language",
181
+ "framework",
182
+ "efficiency",
183
+ "workflow"
184
+ ];
185
+ const packCategorySchema = z.enum(PACK_CATEGORIES);
144
186
  /**
145
- * A stack describes a development environment that can build on other stacks
146
- * and contribute provider-agnostic capabilities to them.
187
+ * Pack names are plain identifiers. They are intentionally not constrained to
188
+ * a closed list so that external registries can contribute packs later.
147
189
  */
148
- const stackDefinitionSchema = z.strictObject({
149
- name: stackNameSchema.describe("Unique stack identifier."),
150
- description: z.string().min(1, "Stack descriptions must be non-empty when provided.").describe("Short human-readable summary of the stack.").optional(),
151
- extends: z.array(stackNameSchema).describe("Stacks this stack builds on, in dependency-first order.").default([]),
152
- skills: z.array(skillNameSchema).describe("Skills this stack contributes, in declaration order.").default([]),
153
- mcpServers: z.array(mcpServerReferenceSchema).describe("MCP servers this stack contributes, in declaration order.").default([])
190
+ const packNameSchema = z.string().min(1, "Pack names must be non-empty strings.");
191
+ /** A pack is a composable provider-neutral capability bundle. */
192
+ const packDefinitionSchema = z.strictObject({
193
+ name: packNameSchema.describe("Unique pack identifier."),
194
+ category: packCategorySchema.describe("Discoverability category; does not affect resolution.").default("engineering"),
195
+ description: z.string().min(1, "Pack descriptions must be non-empty when provided.").describe("Short human-readable summary of the pack.").optional(),
196
+ skills: z.array(skillNameSchema).describe("Skills this pack contributes, in declaration order.").default([]),
197
+ 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([])
154
199
  });
155
200
  //#endregion
156
201
  //#region src/config/schema.ts
157
- /** How much autonomy the generated environment should grant an agent. */
158
- const AGENTYX_PROFILES = [
159
- "lean",
160
- "balanced",
161
- "autonomous"
162
- ];
163
- const agentyxProfileSchema = z.enum(AGENTYX_PROFILES, { error: `Profile must be one of: ${AGENTYX_PROFILES.join(", ")}.` });
164
- /** Applied when a configuration omits `profile`. */
165
- const DEFAULT_AGENTYX_PROFILE = "balanced";
166
202
  /**
167
203
  * Targets stay free-form strings on purpose: third-party adapters should be
168
204
  * able to register providers without a change to this schema.
169
205
  */
170
206
  const agentyxTargetSchema = z.string().min(1, "Targets must be non-empty strings.");
207
+ 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\".");
171
208
  /** The `.agentyx.json` project configuration. */
172
209
  const agentyxConfigSchema = z.strictObject({
173
210
  $schema: z.string().min(1, "$schema must be a non-empty string.").describe("Optional path or URL to the Agentyx JSON Schema.").optional(),
174
- extends: z.array(stackNameSchema).describe("Stacks this project builds on.").default([]),
175
- profile: agentyxProfileSchema.describe("How much autonomy the generated environment grants.").default(DEFAULT_AGENTYX_PROFILE),
211
+ packs: z.array(packNameSchema).describe("Capability packs this project selects.").default([]),
212
+ enable: z.array(enabledCapabilityNameSchema).describe("Optional capabilities to activate by explicit identifier.").default([]),
176
213
  targets: z.array(agentyxTargetSchema).describe("Coding-agent providers this project targets.").default([])
177
214
  });
178
215
  //#endregion
@@ -250,7 +287,7 @@ var UnknownMcpServerError = class extends AgentyxError {
250
287
  requiredBy;
251
288
  knownServers;
252
289
  constructor(serverName, requiredBy, knownServers) {
253
- const origin = requiredBy === void 0 ? "" : ` (required by stack "${requiredBy}")`;
290
+ const origin = requiredBy === void 0 ? "" : ` (required by "${requiredBy}")`;
254
291
  const known = knownServers.length > 0 ? [...knownServers].sort().join(", ") : "none";
255
292
  super("unknown_mcp_server", `Unknown MCP server "${serverName}"${origin}. Known MCP servers: ${known}.`);
256
293
  this.name = "UnknownMcpServerError";
@@ -323,208 +360,380 @@ function createMcpServerRegistry(sources) {
323
360
  get
324
361
  };
325
362
  }
326
- const builtInMcpServerRegistry = createMcpServerRegistry([{
327
- name: "context7",
328
- load: () => ({
363
+ const builtInMcpServerRegistry = createMcpServerRegistry([
364
+ {
329
365
  name: "context7",
330
- description: "Fetch up-to-date library documentation from Context7.",
331
- transport: "http",
332
- contextCost: "medium",
333
- url: "https://mcp.context7.com/mcp"
334
- })
335
- }, {
336
- name: "playwright",
337
- load: () => ({
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
+ {
338
375
  name: "playwright",
339
- description: "Automate and inspect browsers through Playwright MCP.",
340
- transport: "stdio",
341
- contextCost: "high",
342
- command: "npx",
343
- args: ["@playwright/mcp@latest"]
344
- })
345
- }]);
346
- const builtInMcpServerNames = builtInMcpServerRegistry.names;
347
- //#endregion
348
- //#region src/optimization/profile.ts
349
- const optimizationProfiles = [
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
+ },
350
385
  {
351
- name: "lean",
352
- goal: "Minimum context and tool overhead.",
353
- mcpLevels: ["essential"],
354
- notes: [
355
- "Installs all resolved Skills.",
356
- "Keeps MCP exposure to essential capabilities.",
357
- "Prefers local CLI or native tools when they cover the same workflow."
358
- ]
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
+ })
359
394
  },
360
395
  {
361
- name: "balanced",
362
- goal: "Good default developer experience with conservative autonomy.",
363
- mcpLevels: ["essential", "recommended"],
364
- notes: ["Installs all resolved Skills.", "Includes default and recommended MCP capabilities."]
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
+ })
365
404
  },
366
405
  {
367
- name: "autonomous",
368
- goal: "Maximum agent capability from declared stack capabilities.",
369
- mcpLevels: [
370
- "essential",
371
- "recommended",
372
- "optional"
373
- ],
374
- notes: ["Installs all resolved Skills.", "Includes optional MCP capabilities declared by the selected stacks."]
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
+ })
375
437
  }
376
- ];
377
- const optimizationProfileNames = optimizationProfiles.map((profile) => profile.name);
378
- function getOptimizationProfile(name) {
379
- const profile = optimizationProfiles.find((candidate) => candidate.name === name);
380
- if (profile === void 0) throw new Error(`Unknown optimization profile: ${name}`);
381
- return profile;
382
- }
383
- function isMcpLevelEnabled(profile, level) {
384
- return getOptimizationProfile(profile).mcpLevels.includes(level);
385
- }
438
+ ]);
439
+ const builtInMcpServerNames = builtInMcpServerRegistry.names;
386
440
  //#endregion
387
- //#region src/stack/errors.ts
388
- /**
389
- * Raised when a requested or inherited stack is not present in the registry.
390
- *
391
- * The offending name is exposed as `stackName`; `stack` stays the JS stack
392
- * trace inherited from `Error`.
393
- */
394
- var UnknownStackError = class extends AgentyxError {
395
- stackName;
441
+ //#region src/pack/errors.ts
442
+ /** Raised when a requested pack is not present in the registry. */
443
+ var UnknownPackError = class extends AgentyxError {
444
+ packName;
396
445
  requiredBy;
397
- knownStacks;
398
- constructor(stackName, requiredBy, knownStacks) {
446
+ knownPacks;
447
+ constructor(packName, requiredBy, knownPacks) {
399
448
  const origin = requiredBy === void 0 ? "" : ` (required by "${requiredBy}")`;
400
- const known = knownStacks.length > 0 ? [...knownStacks].sort().join(", ") : "none";
401
- super("unknown_stack", `Unknown stack "${stackName}"${origin}. Known stacks: ${known}.`);
402
- this.name = "UnknownStackError";
403
- this.stackName = stackName;
449
+ const known = knownPacks.length > 0 ? [...knownPacks].sort().join(", ") : "none";
450
+ super("unknown_pack", `Unknown pack "${packName}"${origin}. Known packs: ${known}.`);
451
+ this.name = "UnknownPackError";
452
+ this.packName = packName;
404
453
  this.requiredBy = requiredBy;
405
- this.knownStacks = knownStacks;
454
+ this.knownPacks = knownPacks;
406
455
  }
407
456
  };
408
- /** Raised when stack inheritance forms a cycle. */
409
- var CircularStackDependencyError = class extends AgentyxError {
410
- /** The cycle path, starting and ending with the same stack. */
457
+ /** @deprecated Packs no longer inherit from other packs. */
458
+ var CircularPackDependencyError = class extends AgentyxError {
459
+ /** The cycle path, starting and ending with the same pack. */
411
460
  cycle;
412
461
  constructor(cycle) {
413
- super("circular_stack_dependency", `Circular stack dependency: ${cycle.join(" -> ")}.`);
414
- this.name = "CircularStackDependencyError";
462
+ super("circular_pack_dependency", `Circular pack dependency: ${cycle.join(" -> ")}.`);
463
+ this.name = "CircularPackDependencyError";
415
464
  this.cycle = cycle;
416
465
  }
417
466
  };
418
- /** Raised when a registry is built from definitions that reuse a stack name. */
419
- var DuplicateStackError = class extends AgentyxError {
420
- stackName;
421
- constructor(stackName) {
422
- super("duplicate_stack", `Duplicate stack definition: "${stackName}".`);
423
- this.name = "DuplicateStackError";
424
- this.stackName = stackName;
467
+ /** Raised when a registry is built from definitions that reuse a pack name. */
468
+ var DuplicatePackError = class extends AgentyxError {
469
+ packName;
470
+ constructor(packName) {
471
+ super("duplicate_pack", `Duplicate pack definition: "${packName}".`);
472
+ this.name = "DuplicatePackError";
473
+ this.packName = packName;
425
474
  }
426
475
  };
427
476
  //#endregion
428
- //#region src/stack/registry.ts
477
+ //#region src/pack/registry.ts
429
478
  /** Validates definitions and indexes them by name. */
430
- function createStackRegistry(definitions) {
479
+ function createPackRegistry(definitions) {
431
480
  const registry = /* @__PURE__ */ new Map();
432
481
  for (const definition of definitions) {
433
- const stack = stackDefinitionSchema.parse(definition);
434
- if (registry.has(stack.name)) throw new DuplicateStackError(stack.name);
435
- registry.set(stack.name, stack);
482
+ const pack = packDefinitionSchema.parse(definition);
483
+ if (registry.has(pack.name)) throw new DuplicatePackError(pack.name);
484
+ registry.set(pack.name, pack);
436
485
  }
437
486
  return registry;
438
487
  }
439
488
  /**
440
- * The stacks Agentyx ships with, expressed as data. Relationships live here, not
489
+ * The packs Agentyx ships with, expressed as data. Relationships live here, not
441
490
  * in the resolver.
442
491
  */
443
- const builtInStacks = [
492
+ const builtInPacks = [
444
493
  {
445
- name: "core",
446
- description: "Baseline development environment shared by every Agentyx stack.",
494
+ name: "technical",
495
+ category: "engineering",
496
+ description: "General software-engineering quality independent of language or framework.",
447
497
  skills: [
448
- "planning",
449
- "systematic-debugging",
450
- "verification"
498
+ "engineering-principles",
499
+ "code-quality",
500
+ "api-design",
501
+ "code-review"
451
502
  ]
452
503
  },
453
504
  {
454
505
  name: "typescript",
455
- description: "TypeScript development environment.",
456
- extends: ["core"],
457
- skills: ["typescript-modern"]
506
+ category: "language",
507
+ description: "TypeScript language conventions for strict, modern code.",
508
+ skills: [
509
+ "typescript-strict",
510
+ "typescript-modeling",
511
+ "typescript-modern"
512
+ ]
458
513
  },
459
514
  {
460
515
  name: "angular",
516
+ category: "framework",
461
517
  description: "Modern Angular development environment.",
462
- extends: ["typescript"],
463
- skills: ["angular-modern"],
518
+ skills: [
519
+ "angular-modern",
520
+ "angular-signals",
521
+ "angular-architecture",
522
+ "angular-testing"
523
+ ],
524
+ mcpServers: ["context7"]
525
+ },
526
+ {
527
+ name: "efficiency",
528
+ category: "efficiency",
529
+ description: "Reduce context and tool overhead without weakening verification.",
530
+ skills: [
531
+ "context-efficient-development",
532
+ "concise-output",
533
+ "targeted-exploration",
534
+ "focused-verification"
535
+ ],
464
536
  mcpServers: [{
465
- name: "context7",
466
- level: "recommended"
537
+ name: "codebase-memory",
538
+ activation: "optional"
539
+ }],
540
+ tools: [{
541
+ name: "rtk",
542
+ activation: "optional"
543
+ }]
544
+ },
545
+ {
546
+ name: "agentic",
547
+ category: "workflow",
548
+ description: "Provider-neutral development workflows for substantial coding-agent work.",
549
+ skills: [
550
+ "brainstorming",
551
+ "planning",
552
+ "systematic-debugging",
553
+ "verification",
554
+ "parallel-work",
555
+ "worktree-workflow",
556
+ "subagent-driven-development",
557
+ "requesting-code-review"
558
+ ]
559
+ },
560
+ {
561
+ name: "testing",
562
+ category: "engineering",
563
+ description: "Test design, isolation and stability at every level of the pyramid.",
564
+ skills: [
565
+ "test-strategy",
566
+ "test-doubles",
567
+ "e2e-testing",
568
+ "flaky-tests"
569
+ ],
570
+ mcpServers: [{
571
+ name: "playwright",
572
+ activation: "optional"
573
+ }]
574
+ },
575
+ {
576
+ name: "security",
577
+ category: "engineering",
578
+ description: "Application security fundamentals for untrusted input, secrets and access.",
579
+ skills: [
580
+ "secure-coding",
581
+ "secrets-handling",
582
+ "dependency-security",
583
+ "auth-patterns"
584
+ ]
585
+ },
586
+ {
587
+ name: "performance",
588
+ category: "engineering",
589
+ description: "Measure before optimizing, across application, browser and database.",
590
+ skills: [
591
+ "performance-profiling",
592
+ "web-vitals",
593
+ "query-performance"
594
+ ],
595
+ mcpServers: [{
596
+ name: "chrome-devtools",
597
+ activation: "optional"
598
+ }]
599
+ },
600
+ {
601
+ name: "accessibility",
602
+ category: "engineering",
603
+ description: "Accessible markup, ARIA and keyboard interaction for web interfaces.",
604
+ skills: [
605
+ "semantic-html",
606
+ "aria-patterns",
607
+ "keyboard-navigation"
608
+ ],
609
+ mcpServers: [{
610
+ name: "chrome-devtools",
611
+ activation: "optional"
612
+ }]
613
+ },
614
+ {
615
+ name: "refactoring",
616
+ category: "engineering",
617
+ description: "Change structure safely, including in legacy code with weak tests.",
618
+ skills: [
619
+ "refactoring-safely",
620
+ "legacy-code",
621
+ "dependency-hygiene"
622
+ ]
623
+ },
624
+ {
625
+ name: "documentation",
626
+ category: "engineering",
627
+ description: "Documentation, interface reference and decision records that stay useful.",
628
+ skills: [
629
+ "technical-writing",
630
+ "api-documentation",
631
+ "decision-records"
632
+ ]
633
+ },
634
+ {
635
+ name: "observability",
636
+ category: "engineering",
637
+ description: "Logging, metrics, tracing and incident response for running systems.",
638
+ skills: [
639
+ "structured-logging",
640
+ "metrics-and-tracing",
641
+ "incident-response"
642
+ ],
643
+ mcpServers: [{
644
+ name: "sentry",
645
+ activation: "optional"
646
+ }]
647
+ },
648
+ {
649
+ name: "data",
650
+ category: "engineering",
651
+ description: "Schema design, migrations and transactional correctness.",
652
+ skills: [
653
+ "data-modeling",
654
+ "schema-migrations",
655
+ "transactions-and-consistency"
656
+ ],
657
+ mcpServers: [{
658
+ name: "supabase",
659
+ activation: "optional"
660
+ }]
661
+ },
662
+ {
663
+ name: "git",
664
+ category: "workflow",
665
+ description: "Commit, branch and pull-request practice for reviewable history.",
666
+ skills: [
667
+ "commit-hygiene",
668
+ "branching-strategy",
669
+ "pull-requests"
670
+ ],
671
+ mcpServers: [{
672
+ name: "github",
673
+ activation: "optional"
467
674
  }]
675
+ },
676
+ {
677
+ name: "devops",
678
+ category: "workflow",
679
+ description: "Continuous integration, containers, deployment safety and infrastructure.",
680
+ skills: [
681
+ "ci-pipelines",
682
+ "containerization",
683
+ "deployment-safety",
684
+ "infrastructure-as-code"
685
+ ]
468
686
  }
469
687
  ];
470
688
  /** The registry used by default when no explicit registry is supplied. */
471
- const builtInStackRegistry = createStackRegistry(builtInStacks);
689
+ const builtInPackRegistry = createPackRegistry(builtInPacks);
472
690
  //#endregion
473
- //#region src/stack/resolver.ts
691
+ //#region src/pack/resolver.ts
474
692
  /**
475
- * Expands the requested stacks into the full inheritance chain.
693
+ * Resolves selected packs without hidden inheritance.
476
694
  *
477
- * The result is dependency-first, de-duplicated and deterministic: stacks are
478
- * visited in the order they are requested, and each stack's parents are visited
479
- * in declaration order before the stack itself.
695
+ * The result preserves configured order, removes duplicates by first
696
+ * occurrence, and validates every requested pack.
480
697
  *
481
- * @throws {UnknownStackError} when a requested or inherited stack is missing.
482
- * @throws {CircularStackDependencyError} when inheritance forms a cycle.
698
+ * @throws {UnknownPackError} when a requested pack is missing.
483
699
  */
484
- function resolveStacks(requestedStacks, registry = builtInStackRegistry) {
700
+ function resolvePacks(requestedPacks, registry = builtInPackRegistry) {
485
701
  const resolved = [];
486
- const settled = /* @__PURE__ */ new Set();
487
- const path = [];
488
- const visit = (name, requiredBy) => {
489
- if (settled.has(name)) return;
490
- const cycleStart = path.indexOf(name);
491
- if (cycleStart !== -1) throw new CircularStackDependencyError([...path.slice(cycleStart), name]);
492
- const definition = registry.get(name);
493
- if (definition === void 0) throw new UnknownStackError(name, requiredBy, [...registry.keys()]);
494
- path.push(name);
495
- for (const parent of definition.extends) visit(parent, name);
496
- path.pop();
497
- settled.add(name);
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);
498
707
  resolved.push(name);
499
- };
500
- for (const name of requestedStacks) visit(name, void 0);
708
+ }
501
709
  return resolved;
502
710
  }
503
711
  //#endregion
504
712
  //#region src/mcp/resolver.ts
505
- function collectStackMcpServerReferences(resolvedStacks, stackRegistry, mcpRegistry) {
713
+ function collectPackMcpServerReferences(resolvedPacks, packRegistry, mcpRegistry) {
506
714
  const servers = [];
507
715
  const seen = /* @__PURE__ */ new Set();
508
- for (const stackName of resolvedStacks) for (const server of stackRegistry.get(stackName)?.mcpServers ?? []) {
716
+ for (const packName of resolvedPacks) for (const server of packRegistry.get(packName)?.mcpServers ?? []) {
509
717
  const serverName = server.name;
510
718
  if (seen.has(serverName)) continue;
511
- if (!mcpRegistry.has(serverName)) throw new UnknownMcpServerError(serverName, stackName, mcpRegistry.names);
719
+ if (!mcpRegistry.has(serverName)) throw new UnknownMcpServerError(serverName, packName, mcpRegistry.names);
512
720
  seen.add(serverName);
513
721
  servers.push(server);
514
722
  }
515
723
  return servers;
516
724
  }
517
- function filterEffectiveMcpServers(servers, profile) {
518
- return servers.filter((server) => isMcpLevelEnabled(profile, server.level)).map((server) => server.name);
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);
519
728
  }
520
- function collectStackMcpServers(resolvedStacks, stackRegistry, mcpRegistry, profile = DEFAULT_AGENTYX_PROFILE) {
521
- return filterEffectiveMcpServers(collectStackMcpServerReferences(resolvedStacks, stackRegistry, mcpRegistry), profile);
729
+ function collectPackMcpServers(resolvedPacks, packRegistry, mcpRegistry, enabledCapabilities = []) {
730
+ return filterEffectiveMcpServers(collectPackMcpServerReferences(resolvedPacks, packRegistry, mcpRegistry), enabledCapabilities);
522
731
  }
523
- function resolveStackMcpServerReferences(requestedStacks, stackRegistry = builtInStackRegistry, mcpRegistry = builtInMcpServerRegistry) {
524
- return collectStackMcpServerReferences(resolveStacks(requestedStacks, stackRegistry), stackRegistry, mcpRegistry);
732
+ function resolvePackMcpServerReferences(requestedPacks, packRegistry = builtInPackRegistry, mcpRegistry = builtInMcpServerRegistry) {
733
+ return collectPackMcpServerReferences(resolvePacks(requestedPacks, packRegistry), packRegistry, mcpRegistry);
525
734
  }
526
- function resolveStackMcpServers(requestedStacks, stackRegistry = builtInStackRegistry, mcpRegistry = builtInMcpServerRegistry, profile = DEFAULT_AGENTYX_PROFILE) {
527
- return collectStackMcpServers(resolveStacks(requestedStacks, stackRegistry), stackRegistry, mcpRegistry, profile);
735
+ function resolvePackMcpServers(requestedPacks, packRegistry = builtInPackRegistry, mcpRegistry = builtInMcpServerRegistry, enabledCapabilities = []) {
736
+ return collectPackMcpServers(resolvePacks(requestedPacks, packRegistry), packRegistry, mcpRegistry, enabledCapabilities);
528
737
  }
529
738
  //#endregion
530
739
  //#region src/assets.ts
@@ -545,18 +754,18 @@ const BUILT_IN_SKILLS_PATH = fileURLToPath(new URL("../skills", import.meta.url)
545
754
  //#endregion
546
755
  //#region src/skill/errors.ts
547
756
  /**
548
- * Raised when a stack references a skill the registry does not know.
757
+ * Raised when a pack references a skill the registry does not know.
549
758
  *
550
- * The offending name is exposed as `skillName`; `stack` stays the JS stack
759
+ * The offending name is exposed as `skillName`; `pack` stays the JS pack
551
760
  * trace inherited from `Error`.
552
761
  */
553
762
  var UnknownSkillError = class extends AgentyxError {
554
763
  skillName;
555
- /** The stack that declared the skill, when the failure came from resolution. */
764
+ /** The pack that declared the skill, when the failure came from resolution. */
556
765
  requiredBy;
557
766
  knownSkills;
558
767
  constructor(skillName, requiredBy, knownSkills) {
559
- const origin = requiredBy === void 0 ? "" : ` (required by stack "${requiredBy}")`;
768
+ const origin = requiredBy === void 0 ? "" : ` (required by pack "${requiredBy}")`;
560
769
  const known = knownSkills.length > 0 ? [...knownSkills].sort().join(", ") : "none";
561
770
  super("unknown_skill", `Unknown skill "${skillName}"${origin}. Known skills: ${known}.`);
562
771
  this.name = "UnknownSkillError";
@@ -724,14 +933,65 @@ function createSkillRegistry(sources) {
724
933
  /**
725
934
  * The skills Agentyx ships with. The names are the directory names under
726
935
  * `packages/core/skills`, and knowing them without touching the filesystem is
727
- * what lets stack resolution validate references without reading any body.
936
+ * what lets pack resolution validate references without reading any body.
728
937
  */
729
938
  const builtInSkillNames = [
939
+ "engineering-principles",
940
+ "code-quality",
941
+ "api-design",
942
+ "code-review",
730
943
  "planning",
731
944
  "systematic-debugging",
732
945
  "verification",
946
+ "brainstorming",
947
+ "parallel-work",
948
+ "worktree-workflow",
949
+ "subagent-driven-development",
950
+ "requesting-code-review",
951
+ "typescript-strict",
952
+ "typescript-modeling",
733
953
  "typescript-modern",
734
- "angular-modern"
954
+ "angular-modern",
955
+ "angular-signals",
956
+ "angular-architecture",
957
+ "angular-testing",
958
+ "context-efficient-development",
959
+ "concise-output",
960
+ "targeted-exploration",
961
+ "focused-verification",
962
+ "test-strategy",
963
+ "test-doubles",
964
+ "e2e-testing",
965
+ "flaky-tests",
966
+ "secure-coding",
967
+ "secrets-handling",
968
+ "dependency-security",
969
+ "auth-patterns",
970
+ "performance-profiling",
971
+ "web-vitals",
972
+ "query-performance",
973
+ "semantic-html",
974
+ "aria-patterns",
975
+ "keyboard-navigation",
976
+ "refactoring-safely",
977
+ "legacy-code",
978
+ "dependency-hygiene",
979
+ "technical-writing",
980
+ "api-documentation",
981
+ "decision-records",
982
+ "structured-logging",
983
+ "metrics-and-tracing",
984
+ "incident-response",
985
+ "data-modeling",
986
+ "schema-migrations",
987
+ "transactions-and-consistency",
988
+ "commit-hygiene",
989
+ "branching-strategy",
990
+ "pull-requests",
991
+ "ci-pipelines",
992
+ "containerization",
993
+ "deployment-safety",
994
+ "infrastructure-as-code"
735
995
  ];
736
996
  /** The absolute path of a built-in skill's `SKILL.md`. */
737
997
  function builtInSkillPath(name) {
@@ -763,63 +1023,349 @@ function loadBuiltInSkill(name) {
763
1023
  //#endregion
764
1024
  //#region src/skill/resolver.ts
765
1025
  /**
766
- * Collects the skills contributed by an already-resolved stack chain.
1026
+ * Collects the skills contributed by an already-resolved pack chain.
767
1027
  *
768
1028
  * Only identifiers are produced — no skill body is read — so this stays cheap
769
1029
  * enough to run on every `resolve`.
770
1030
  *
771
- * @throws {UnknownSkillError} when a stack references a skill the registry lacks.
1031
+ * @throws {UnknownSkillError} when a pack references a skill the registry lacks.
772
1032
  */
773
- function collectStackSkills(resolvedStacks, stackRegistry, skillRegistry) {
1033
+ function collectPackSkills(resolvedPacks, packRegistry, skillRegistry) {
774
1034
  const skills = [];
775
1035
  const seen = /* @__PURE__ */ new Set();
776
- for (const stackName of resolvedStacks) for (const skillName of stackRegistry.get(stackName)?.skills ?? []) {
1036
+ for (const packName of resolvedPacks) for (const skillName of packRegistry.get(packName)?.skills ?? []) {
777
1037
  if (seen.has(skillName)) continue;
778
- if (!skillRegistry.has(skillName)) throw new UnknownSkillError(skillName, stackName, skillRegistry.names);
1038
+ if (!skillRegistry.has(skillName)) throw new UnknownSkillError(skillName, packName, skillRegistry.names);
779
1039
  seen.add(skillName);
780
1040
  skills.push(skillName);
781
1041
  }
782
1042
  return skills;
783
1043
  }
784
1044
  /**
785
- * Expands the requested stacks and returns the skills they contribute.
1045
+ * Resolves the requested packs and returns the skills they contribute.
786
1046
  *
787
- * The result follows the resolved stack order, then each stack's declaration
1047
+ * The result follows the resolved pack order, then each pack's declaration
788
1048
  * order, and is de-duplicated by first occurrence. Skills do not extend other
789
1049
  * skills, so there is nothing further to expand.
790
1050
  *
791
- * @throws {UnknownStackError} when a requested or inherited stack is missing.
792
- * @throws {CircularStackDependencyError} when stack inheritance forms a cycle.
793
- * @throws {UnknownSkillError} when a stack references a skill the registry lacks.
1051
+ * @throws {UnknownPackError} when a requested pack is missing.
1052
+ * @throws {UnknownSkillError} when a pack references a skill the registry lacks.
794
1053
  */
795
- function resolveStackSkills(requestedStacks, stackRegistry = builtInStackRegistry, skillRegistry = builtInSkillRegistry) {
796
- return collectStackSkills(resolveStacks(requestedStacks, stackRegistry), stackRegistry, skillRegistry);
1054
+ function resolvePackSkills(requestedPacks, packRegistry = builtInPackRegistry, skillRegistry = builtInSkillRegistry) {
1055
+ return collectPackSkills(resolvePacks(requestedPacks, packRegistry), packRegistry, skillRegistry);
1056
+ }
1057
+ //#endregion
1058
+ //#region src/tool/errors.ts
1059
+ var UnknownToolError = class extends AgentyxError {
1060
+ toolName;
1061
+ requiredBy;
1062
+ knownTools;
1063
+ constructor(toolName, requiredBy, knownTools) {
1064
+ const origin = requiredBy === void 0 ? "" : ` (required by "${requiredBy}")`;
1065
+ const known = knownTools.length > 0 ? [...knownTools].sort().join(", ") : "none";
1066
+ super("unknown_tool", `Unknown tool "${toolName}"${origin}. Known tools: ${known}.`);
1067
+ this.name = "UnknownToolError";
1068
+ this.toolName = toolName;
1069
+ this.requiredBy = requiredBy;
1070
+ this.knownTools = knownTools;
1071
+ }
1072
+ };
1073
+ var DuplicateToolError = class extends AgentyxError {
1074
+ toolName;
1075
+ constructor(toolName) {
1076
+ super("duplicate_tool", `Duplicate tool definition: "${toolName}".`);
1077
+ this.name = "DuplicateToolError";
1078
+ this.toolName = toolName;
1079
+ }
1080
+ };
1081
+ var InvalidToolError = class extends AgentyxError {
1082
+ constructor(origin, reason, options) {
1083
+ const message = reason instanceof Error ? reason.message : String(reason);
1084
+ super("invalid_tool", `Invalid ${origin}: ${message}.`, options);
1085
+ this.name = "InvalidToolError";
1086
+ }
1087
+ };
1088
+ //#endregion
1089
+ //#region src/tool/registry.ts
1090
+ function createToolRegistry(sources) {
1091
+ const byName = /* @__PURE__ */ new Map();
1092
+ for (const source of sources) {
1093
+ const name = toolNameSchema.safeParse(source.name);
1094
+ if (!name.success) throw new InvalidToolError(`tool source "${source.name}"`, name.error);
1095
+ if (byName.has(name.data)) throw new DuplicateToolError(name.data);
1096
+ byName.set(name.data, source);
1097
+ }
1098
+ const names = [...byName.keys()];
1099
+ const loaded = /* @__PURE__ */ new Map();
1100
+ const get = (name) => {
1101
+ const cached = loaded.get(name);
1102
+ if (cached !== void 0) return cached;
1103
+ const source = byName.get(name);
1104
+ if (source === void 0) throw new UnknownToolError(name, void 0, names);
1105
+ const origin = `tool "${name}"`;
1106
+ const tool = toolDefinitionSchema.safeParse(source.load());
1107
+ if (!tool.success) throw new InvalidToolError(origin, tool.error);
1108
+ if (tool.data.name !== name) throw new InvalidToolError(origin, `it declares the name "${tool.data.name}"`);
1109
+ loaded.set(name, tool.data);
1110
+ return tool.data;
1111
+ };
1112
+ return {
1113
+ names,
1114
+ has: (name) => byName.has(name),
1115
+ listMetadata: () => names.map((name) => {
1116
+ const tool = get(name);
1117
+ return {
1118
+ name: tool.name,
1119
+ description: tool.description,
1120
+ kind: tool.kind,
1121
+ command: tool.command,
1122
+ optional: tool.optional
1123
+ };
1124
+ }),
1125
+ get
1126
+ };
1127
+ }
1128
+ const builtInToolRegistry = createToolRegistry([{
1129
+ name: "rtk",
1130
+ load: () => ({
1131
+ name: "rtk",
1132
+ description: "Rust Token Killer command-output proxy for reducing coding-agent context noise.",
1133
+ kind: "executable",
1134
+ command: "rtk",
1135
+ optional: true,
1136
+ installHint: "Install from the rtk-ai/rtk project if your local workflow benefits from it."
1137
+ })
1138
+ }]);
1139
+ const builtInToolNames = builtInToolRegistry.names;
1140
+ //#endregion
1141
+ //#region src/tool/resolver.ts
1142
+ function collectPackToolReferences(resolvedPacks, packRegistry, toolRegistry) {
1143
+ const tools = [];
1144
+ const seen = /* @__PURE__ */ new Set();
1145
+ for (const packName of resolvedPacks) for (const tool of packRegistry.get(packName)?.tools ?? []) {
1146
+ if (seen.has(tool.name)) continue;
1147
+ if (!toolRegistry.has(tool.name)) throw new UnknownToolError(tool.name, packName, toolRegistry.names);
1148
+ seen.add(tool.name);
1149
+ tools.push(tool);
1150
+ }
1151
+ return tools;
1152
+ }
1153
+ function filterEffectiveTools(tools, enabledCapabilities) {
1154
+ const enabled = new Set(enabledCapabilities);
1155
+ return tools.filter((tool) => tool.activation === "default" || enabled.has(tool.name)).map((tool) => tool.name);
1156
+ }
1157
+ function resolvePackToolReferences(requestedPacks, packRegistry = builtInPackRegistry, toolRegistry = builtInToolRegistry) {
1158
+ return collectPackToolReferences(resolvePacks(requestedPacks, packRegistry), packRegistry, toolRegistry);
1159
+ }
1160
+ function resolvePackTools(requestedPacks, packRegistry = builtInPackRegistry, toolRegistry = builtInToolRegistry, enabledCapabilities = []) {
1161
+ return filterEffectiveTools(resolvePackToolReferences(requestedPacks, packRegistry, toolRegistry), enabledCapabilities);
797
1162
  }
798
1163
  //#endregion
799
1164
  //#region src/config/resolver.ts
800
1165
  /**
801
- * Combines a validated project configuration with stack and skill resolution.
1166
+ * Combines a validated project configuration with pack and skill resolution.
802
1167
  * The input is never mutated.
803
1168
  *
804
- * @throws {UnknownStackError} when a configured stack is missing.
805
- * @throws {CircularStackDependencyError} when inheritance forms a cycle.
806
- * @throws {UnknownSkillError} when a stack references an unknown skill.
1169
+ * @throws {UnknownPackError} when a configured pack is missing.
1170
+ * @throws {CircularPackDependencyError} when inheritance forms a cycle.
1171
+ * @throws {UnknownSkillError} when a pack references an unknown skill.
807
1172
  */
808
- function resolveAgentyxConfig(config, registry = builtInStackRegistry, skillRegistry = builtInSkillRegistry, mcpRegistry = builtInMcpServerRegistry) {
809
- const requestedStacks = [...config.extends];
810
- const resolvedStacks = resolveStacks(requestedStacks, registry);
811
- const declaredMcpServers = collectStackMcpServerReferences(resolvedStacks, registry, mcpRegistry);
1173
+ function resolveAgentyxConfig(config, registry = builtInPackRegistry, skillRegistry = builtInSkillRegistry, mcpRegistry = builtInMcpServerRegistry, toolRegistry = builtInToolRegistry) {
1174
+ const requestedPacks = [...config.packs];
1175
+ const resolvedPacks = resolvePacks(requestedPacks, registry);
1176
+ const declaredMcpServers = collectPackMcpServerReferences(resolvedPacks, registry, mcpRegistry);
1177
+ 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)];
1179
+ for (const capability of config.enable) if (!knownOptionalCapabilities.includes(capability)) throw new UnknownEnabledCapabilityError(capability, knownOptionalCapabilities);
812
1180
  return {
813
- requestedStacks,
814
- resolvedStacks,
815
- skills: collectStackSkills(resolvedStacks, registry, skillRegistry),
1181
+ requestedPacks,
1182
+ resolvedPacks,
1183
+ skills: collectPackSkills(resolvedPacks, registry, skillRegistry),
816
1184
  declaredMcpServers,
817
- mcpServers: filterEffectiveMcpServers(declaredMcpServers, config.profile),
818
- profile: config.profile,
1185
+ mcpServers: filterEffectiveMcpServers(declaredMcpServers, config.enable),
1186
+ declaredTools,
1187
+ tools: filterEffectiveTools(declaredTools, config.enable),
1188
+ enabled: [...config.enable],
819
1189
  targets: [...config.targets]
820
1190
  };
821
1191
  }
822
1192
  //#endregion
1193
+ //#region src/manifest/errors.ts
1194
+ /** Raised when `.agentyx.lock.json` is not valid JSON. */
1195
+ var AgentyxManifestParseError = class extends AgentyxError {
1196
+ filePath;
1197
+ constructor(filePath, reason, options) {
1198
+ super("agentyx_manifest_parse_error", `${filePath} is not valid JSON: ${reason}`, options);
1199
+ this.name = "AgentyxManifestParseError";
1200
+ this.filePath = filePath;
1201
+ }
1202
+ };
1203
+ /**
1204
+ * Raised when the manifest parses as JSON but is not a manifest Agentyx
1205
+ * understands.
1206
+ *
1207
+ * This is never repaired. The manifest is what tells Agentyx which files it is
1208
+ * allowed to overwrite and delete, so guessing at a damaged one would be
1209
+ * guessing about destructive operations.
1210
+ */
1211
+ var AgentyxManifestValidationError = class extends AgentyxError {
1212
+ issues;
1213
+ filePath;
1214
+ constructor(error, filePath) {
1215
+ const issues = toAgentyxIssues(error);
1216
+ const location = filePath === void 0 ? "" : ` in ${filePath}`;
1217
+ const details = issues.map((issue) => ` - ${issue.path}: ${issue.message}`).join("\n");
1218
+ super("agentyx_manifest_invalid", `Invalid Agentyx install manifest${location}:\n${details}\nDelete the file to start from a clean install record.`);
1219
+ this.name = "AgentyxManifestValidationError";
1220
+ this.issues = issues;
1221
+ this.filePath = filePath;
1222
+ }
1223
+ };
1224
+ //#endregion
1225
+ //#region src/manifest/schema.ts
1226
+ /**
1227
+ * The manifest format version.
1228
+ *
1229
+ * A manifest written by a newer Agentyx fails validation instead of being
1230
+ * reinterpreted, because acting on a half-understood record of managed files is
1231
+ * how an installer deletes something it should not.
1232
+ */
1233
+ const INSTALL_MANIFEST_VERSION = 1;
1234
+ /**
1235
+ * A path Agentyx manages, relative to the project root and always written with
1236
+ * `/` separators so a manifest reads the same on every platform.
1237
+ */
1238
+ const managedPathSchema = z.string().min(1, "Managed paths must be non-empty strings.").refine((value) => !value.startsWith("/"), "Managed paths must be relative to the project root.").refine((value) => !value.includes("\\"), "Managed paths must use `/` separators.").refine((value) => !value.split("/").includes(".."), "Managed paths must not traverse outside the project root.");
1239
+ /** A SHA-256 digest of the exact bytes Agentyx last wrote to a managed path. */
1240
+ const contentHashSchema = z.string().regex(/^[0-9a-f]{64}$/, "Content hashes must be lowercase hexadecimal SHA-256 digests.");
1241
+ const targetsSchema = z.array(z.string().min(1, "Targets must be non-empty strings.")).min(1, "A manifest entry must record at least one target.");
1242
+ /** A `SKILL.md` file Agentyx wrote, owned entirely by Agentyx. */
1243
+ const skillManifestEntrySchema = z.strictObject({
1244
+ kind: z.literal("skill"),
1245
+ path: managedPathSchema,
1246
+ skill: z.string().min(1, "Skill names must be non-empty strings."),
1247
+ targets: targetsSchema,
1248
+ hash: contentHashSchema
1249
+ });
1250
+ /**
1251
+ * A provider MCP configuration file Agentyx contributed entries to.
1252
+ *
1253
+ * Unlike a skill file this one is shared with the user, so the entry records
1254
+ * the server keys Agentyx added rather than claiming the whole document.
1255
+ * `created` says whether the file existed before Agentyx first wrote it, which
1256
+ * is the only thing that makes removing the file itself safe.
1257
+ */
1258
+ const mcpManifestEntrySchema = z.strictObject({
1259
+ kind: z.literal("mcp"),
1260
+ path: managedPathSchema,
1261
+ servers: z.array(z.string().min(1, "MCP server names must be non-empty strings.")),
1262
+ targets: targetsSchema,
1263
+ hash: contentHashSchema,
1264
+ created: z.boolean()
1265
+ });
1266
+ const installManifestEntrySchema = z.discriminatedUnion("kind", [skillManifestEntrySchema, mcpManifestEntrySchema]);
1267
+ /** The `.agentyx.lock.json` record of everything Agentyx installed into a project. */
1268
+ const installManifestSchema = z.strictObject({
1269
+ version: z.literal(1),
1270
+ entries: z.array(installManifestEntrySchema).default([])
1271
+ });
1272
+ //#endregion
1273
+ //#region src/manifest/io.ts
1274
+ const AGENTYX_MANIFEST_FILENAME = ".agentyx.lock.json";
1275
+ /** The absolute path `.agentyx.lock.json` is read from for a given project. */
1276
+ function agentyxManifestPath(projectPath = process.cwd()) {
1277
+ return resolve(projectPath, AGENTYX_MANIFEST_FILENAME);
1278
+ }
1279
+ /** A manifest that claims nothing — what a project has before its first install. */
1280
+ function emptyInstallManifest() {
1281
+ return {
1282
+ version: 1,
1283
+ entries: []
1284
+ };
1285
+ }
1286
+ /**
1287
+ * The SHA-256 digest Agentyx records for managed content.
1288
+ *
1289
+ * Comparing this digest with the file on disk is what separates "Agentyx wrote
1290
+ * this and may replace it" from "someone else owns this file", so it is
1291
+ * computed over the exact bytes written, never over a normalised form.
1292
+ */
1293
+ function hashContent(content) {
1294
+ return createHash("sha256").update(content, "utf8").digest("hex");
1295
+ }
1296
+ /**
1297
+ * Validates an already-parsed manifest document.
1298
+ *
1299
+ * @throws {AgentyxManifestValidationError} when the document violates the schema.
1300
+ */
1301
+ function parseInstallManifest(value, filePath) {
1302
+ const result = installManifestSchema.safeParse(value);
1303
+ if (!result.success) throw new AgentyxManifestValidationError(result.error, filePath);
1304
+ return result.data;
1305
+ }
1306
+ /**
1307
+ * Reads `.agentyx.lock.json` from `projectPath`.
1308
+ *
1309
+ * A missing manifest is a legitimate state — the project has simply never been
1310
+ * installed into — and yields an empty manifest. A manifest that exists but
1311
+ * cannot be understood is a hard failure: it governs which files Agentyx may
1312
+ * overwrite and delete, and a damaged one is never guessed at.
1313
+ *
1314
+ * @throws {AgentyxManifestParseError} when the file is not valid JSON.
1315
+ * @throws {AgentyxManifestValidationError} when the document violates the schema.
1316
+ */
1317
+ async function loadInstallManifest(projectPath = process.cwd()) {
1318
+ const filePath = agentyxManifestPath(projectPath);
1319
+ let contents;
1320
+ try {
1321
+ contents = await readFile(filePath, "utf8");
1322
+ } catch (cause) {
1323
+ if (cause instanceof Error && cause.code === "ENOENT") return emptyInstallManifest();
1324
+ throw cause;
1325
+ }
1326
+ let document;
1327
+ try {
1328
+ document = JSON.parse(contents);
1329
+ } catch (cause) {
1330
+ throw new AgentyxManifestParseError(filePath, cause instanceof Error ? cause.message : String(cause), { cause });
1331
+ }
1332
+ return parseInstallManifest(document, filePath);
1333
+ }
1334
+ /**
1335
+ * Serialises a manifest for writing.
1336
+ *
1337
+ * Entries and their lists are sorted so that reinstalling an unchanged project
1338
+ * produces a byte-identical file and the manifest never shows up as noise in a
1339
+ * diff. Writing is deliberately not done here — only the install executor
1340
+ * touches the filesystem.
1341
+ */
1342
+ 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)
1350
+ }).sort((left, right) => left.path.localeCompare(right.path));
1351
+ return `${JSON.stringify({
1352
+ version: manifest.version,
1353
+ entries
1354
+ }, null, 2)}\n`;
1355
+ }
1356
+ /**
1357
+ * Indexes manifest entries by managed path.
1358
+ *
1359
+ * Every ownership question — may this file be overwritten, may it be deleted —
1360
+ * is a lookup by path, so callers do not scan the list themselves.
1361
+ */
1362
+ function manifestEntriesByPath(manifest) {
1363
+ return new Map(manifest.entries.map((entry) => [entry.path, entry]));
1364
+ }
1365
+ function sorted(values) {
1366
+ return [...values].sort();
1367
+ }
1368
+ //#endregion
823
1369
  //#region src/meta.ts
824
1370
  const agentyxCoreName = "agentyx-core";
825
1371
  function getCoreStatus() {
@@ -844,7 +1390,7 @@ async function detectProject(projectDir) {
844
1390
  const packageJsonPath = join(projectDir, "package.json");
845
1391
  const packageJson = await readPackageJson(packageJsonPath);
846
1392
  const packageManager = await detectPackageManager(projectDir, packageJson.data);
847
- const detectedStacks = await detectStacks(projectDir, packageJson.data);
1393
+ const detectedPacks = await detectPacks(projectDir, packageJson.data);
848
1394
  return {
849
1395
  projectDir,
850
1396
  packageJson: {
@@ -856,31 +1402,36 @@ async function detectProject(projectDir) {
856
1402
  error: packageJson.error
857
1403
  },
858
1404
  packageManager,
859
- detectedStacks,
860
- recommendedStack: recommendStack(detectedStacks)
1405
+ detectedPacks,
1406
+ recommendedPacks: recommendPacks(detectedPacks)
861
1407
  };
862
1408
  }
863
1409
  function buildAgentyxConfig(input) {
864
1410
  return {
865
- extends: [input.stack],
866
- profile: input.profile,
1411
+ packs: [...input.packs],
1412
+ enable: [...input.enable ?? []],
867
1413
  targets: [...input.targets]
868
1414
  };
869
1415
  }
870
1416
  function formatAgentyxConfig(config) {
871
1417
  return `${JSON.stringify(config, null, 2)}\n`;
872
1418
  }
873
- function recommendStack(detectedStacks) {
874
- if (detectedStacks.includes("angular")) return "angular";
875
- if (detectedStacks.includes("typescript")) return "typescript";
1419
+ function recommendPacks(detectedPacks) {
1420
+ if (detectedPacks.includes("angular")) return [
1421
+ "technical",
1422
+ "typescript",
1423
+ "angular"
1424
+ ];
1425
+ if (detectedPacks.includes("typescript")) return ["technical", "typescript"];
1426
+ return ["technical"];
876
1427
  }
877
- async function detectStacks(projectDir, packageJson) {
878
- const stacks = [];
1428
+ async function detectPacks(projectDir, packageJson) {
1429
+ const packs = [];
879
1430
  const dependencies = collectDependencies(packageJson);
880
1431
  const hasAngular = dependencies.has("@angular/core");
881
- if (dependencies.has("typescript") || await exists(join(projectDir, "tsconfig.json")) || hasAngular) stacks.push("typescript");
882
- if (hasAngular) stacks.push("angular");
883
- return stacks;
1432
+ if (dependencies.has("typescript") || await exists(join(projectDir, "tsconfig.json")) || hasAngular) packs.push("typescript");
1433
+ if (hasAngular) packs.push("angular");
1434
+ return packs;
884
1435
  }
885
1436
  async function detectPackageManager(projectDir, packageJson) {
886
1437
  const lockfiles = [];
@@ -976,6 +1527,6 @@ function isNotFound(cause) {
976
1527
  return cause instanceof Error && cause.code === "ENOENT";
977
1528
  }
978
1529
  //#endregion
979
- export { AGENTYX_CONFIG_FILENAME, AGENTYX_CONFIG_SCHEMA_ID, AGENTYX_PROFILES, AgentyxConfigNotFoundError, AgentyxConfigParseError, AgentyxConfigValidationError, AgentyxError, CircularStackDependencyError, DEFAULT_AGENTYX_PROFILE, DuplicateMcpServerError, DuplicateSkillError, DuplicateStackError, InvalidMcpServerError, InvalidSkillError, MCP_CAPABILITY_LEVELS, MCP_CONTEXT_COSTS, PACKAGE_MANAGERS, UnknownMcpServerError, UnknownSkillError, UnknownStackError, agentyxConfigPath, agentyxConfigSchema, agentyxCoreName, agentyxProfileSchema, buildAgentyxConfig, buildAgentyxConfigJsonSchema, builtInMcpServerNames, builtInMcpServerRegistry, builtInSkillNames, builtInSkillRegistry, builtInStackRegistry, builtInStacks, collectStackMcpServerReferences, collectStackMcpServers, createMcpServerRegistry, createSkillRegistry, createStackRegistry, detectProject, filterEffectiveMcpServers, formatAgentyxConfig, formatSkillMarkdown, getCoreStatus, getOptimizationProfile, isMcpLevelEnabled, loadAgentyxConfig, mcpCapabilityLevelSchema, mcpContextCostSchema, mcpEnvReferenceSchema, mcpServerDefinitionSchema, mcpServerReferenceSchema, optimizationProfileNames, optimizationProfiles, parseAgentyxConfig, parseSkillMarkdown, resolveAgentyxConfig, resolveStackMcpServerReferences, resolveStackMcpServers, resolveStackSkills, resolveStacks, skillDefinitionSchema, stackDefinitionSchema };
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 };
980
1531
 
981
1532
  //# sourceMappingURL=index.mjs.map