@agentyx/core 0.6.0 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.mts +255 -5
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +601 -75
- package/dist/index.mjs.map +1 -1
- package/package.json +3 -2
- package/schema/agentyx.schema.json +187 -0
- package/templates/project-context.md +9 -0
package/dist/index.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
-
import { access, readFile, readdir, realpath } from "node:fs/promises";
|
|
3
|
-
import { dirname, isAbsolute, join, relative, resolve, sep } from "node:path";
|
|
4
|
-
import { readFileSync } from "node:fs";
|
|
2
|
+
import { access, readFile, readdir, realpath, stat } from "node:fs/promises";
|
|
3
|
+
import { delimiter, dirname, extname, isAbsolute, join, relative, resolve, sep } from "node:path";
|
|
4
|
+
import { constants, readFileSync } from "node:fs";
|
|
5
5
|
import { fileURLToPath } from "node:url";
|
|
6
6
|
import { createHash } from "node:crypto";
|
|
7
7
|
//#region src/errors.ts
|
|
@@ -96,6 +96,15 @@ var LocalSkillDirectoryError = class extends AgentyxError {
|
|
|
96
96
|
this.directory = directory;
|
|
97
97
|
}
|
|
98
98
|
};
|
|
99
|
+
/** Raised when a configured project-context path is missing or escapes the project. */
|
|
100
|
+
var ProjectContextPathError = class extends AgentyxError {
|
|
101
|
+
path;
|
|
102
|
+
constructor(path, reason, options) {
|
|
103
|
+
super("project_context_path_error", `Invalid project context path "${path}": ${reason}.`, options);
|
|
104
|
+
this.name = "ProjectContextPathError";
|
|
105
|
+
this.path = path;
|
|
106
|
+
}
|
|
107
|
+
};
|
|
99
108
|
//#endregion
|
|
100
109
|
//#region src/hook/schema.ts
|
|
101
110
|
const HOOK_ACTIVATION_LEVELS = ["default", "optional"];
|
|
@@ -130,13 +139,15 @@ const MCP_CONTEXT_COSTS = [
|
|
|
130
139
|
"high"
|
|
131
140
|
];
|
|
132
141
|
const mcpContextCostSchema = z.enum(MCP_CONTEXT_COSTS);
|
|
142
|
+
const mcpRuntimeSchema = z.enum(["local", "may-download"]);
|
|
133
143
|
const mcpServerNameSchema = z.string().min(1, "MCP server names must be non-empty strings.").regex(/^[a-z0-9]+(?:-[a-z0-9]+)*$/, "MCP server names must be lowercase kebab-case, for example \"context7\".");
|
|
134
144
|
const mcpEnvReferenceSchema = z.strictObject({ fromEnv: z.string().trim().min(1, "Environment variable references must be non-empty strings.").regex(/^[A-Za-z_][A-Za-z0-9_]*$/, "Environment variable names must be valid shell names.") });
|
|
135
145
|
const commonMcpServerDefinitionSchema = z.strictObject({
|
|
136
146
|
name: mcpServerNameSchema.describe("Unique MCP server identifier."),
|
|
137
147
|
description: z.string().trim().min(1, "MCP server descriptions must be non-empty strings."),
|
|
138
148
|
transport: z.enum(["stdio", "http"]),
|
|
139
|
-
contextCost: mcpContextCostSchema.describe("Qualitative context/tool-schema overhead classification; not token accounting.").optional()
|
|
149
|
+
contextCost: mcpContextCostSchema.describe("Qualitative context/tool-schema overhead classification; not token accounting.").optional(),
|
|
150
|
+
runtime: mcpRuntimeSchema.describe("Whether launching this server can fetch a package on first use; see MCP_RUNTIMES.").optional()
|
|
140
151
|
});
|
|
141
152
|
const explicitMcpServerReferenceSchema = z.strictObject({
|
|
142
153
|
name: mcpServerNameSchema.describe("MCP server identifier."),
|
|
@@ -278,6 +289,53 @@ const enabledCapabilityNameSchema = z.string().min(1, "Enabled capability names
|
|
|
278
289
|
* every supported platform.
|
|
279
290
|
*/
|
|
280
291
|
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.");
|
|
292
|
+
const projectIdSchema = z.string().regex(/^[a-z0-9]+(?:-[a-z0-9]+)*$/, "Project ids must be lowercase kebab-case identifiers.");
|
|
293
|
+
const projectTextSchema = z.string().min(1, "Must not be blank.").refine((value) => value.trim().length > 0, "Must not be blank.").refine((value) => Array.from(value).every((character) => {
|
|
294
|
+
const codePoint = character.codePointAt(0) ?? 0;
|
|
295
|
+
return codePoint > 31 && (codePoint < 127 || codePoint > 159);
|
|
296
|
+
}), "Must be single-line text without control characters.");
|
|
297
|
+
const projectUrlSchema = z.string().url("Must be a valid URL.");
|
|
298
|
+
const projectResourceNames = z.array(projectTextSchema).refine((items) => new Set(items).size === items.length, "Entries must be unique.");
|
|
299
|
+
const projectDocsSchema = z.array(projectUrlSchema).refine((items) => new Set(items).size === items.length, "Documentation URLs must be unique.");
|
|
300
|
+
const projectOwnsSchema = z.array(projectIdSchema).refine((items) => new Set(items).size === items.length, "Ownership entries must be unique.");
|
|
301
|
+
const projectMetadataSchema = z.strictObject({
|
|
302
|
+
id: projectIdSchema.describe("Stable, human-authored project identifier in kebab-case."),
|
|
303
|
+
name: projectTextSchema.describe("Human-readable project name."),
|
|
304
|
+
description: projectTextSchema.optional().describe("Short description of this project."),
|
|
305
|
+
repository: projectUrlSchema.optional().describe("Repository URL; descriptive and never fetched."),
|
|
306
|
+
docs: projectDocsSchema.default([]).describe("Documentation URLs; descriptive only."),
|
|
307
|
+
mcp: projectResourceNames.default([]).describe("MCP server identifiers available to consult."),
|
|
308
|
+
owns: projectOwnsSchema.default([]).describe("Architectural capabilities owned by this project.")
|
|
309
|
+
});
|
|
310
|
+
const projectRelationTypeSchema = z.enum([
|
|
311
|
+
"uses",
|
|
312
|
+
"integrates",
|
|
313
|
+
"consumes",
|
|
314
|
+
"extends"
|
|
315
|
+
]);
|
|
316
|
+
const projectRelationSchema = z.strictObject({
|
|
317
|
+
id: projectIdSchema.describe("Stable identifier for the related project."),
|
|
318
|
+
name: projectTextSchema.describe("Human-readable related project name."),
|
|
319
|
+
type: projectRelationTypeSchema.describe("How the current project relates to this project."),
|
|
320
|
+
role: projectIdSchema.describe("Architectural role of the related project."),
|
|
321
|
+
repository: projectUrlSchema.optional().describe("Repository URL; descriptive and never fetched."),
|
|
322
|
+
docs: projectDocsSchema.default([]).describe("Documentation URLs; descriptive only."),
|
|
323
|
+
mcp: projectResourceNames.default([]).describe("MCP server identifiers available to consult."),
|
|
324
|
+
owns: projectOwnsSchema.default([]).describe("Architectural capabilities owned by this project."),
|
|
325
|
+
guidance: z.array(projectTextSchema).default([]).describe("Human-authored guidance for agents.")
|
|
326
|
+
});
|
|
327
|
+
const projectCommandSchema = z.strictObject({
|
|
328
|
+
command: projectTextSchema.describe("Suggested command; Agentyx never executes it."),
|
|
329
|
+
cwd: z.string().min(1).default(".").describe("Project-relative working directory.")
|
|
330
|
+
});
|
|
331
|
+
const projectContextSchema = z.strictObject({
|
|
332
|
+
commands: z.record(z.string().min(1).refine((name) => name.trim().length > 0, "Command names must not be blank."), projectCommandSchema).default({}),
|
|
333
|
+
areas: z.array(z.strictObject({
|
|
334
|
+
path: z.string().min(1),
|
|
335
|
+
purpose: projectTextSchema
|
|
336
|
+
})).default([]),
|
|
337
|
+
constraints: z.array(projectTextSchema).default([])
|
|
338
|
+
}).describe("Explicit repository-local facts for the generated project context Skill.");
|
|
281
339
|
/** The `.agentyx.json` project configuration. */
|
|
282
340
|
const agentyxConfigSchema = z.strictObject({
|
|
283
341
|
$schema: z.string().min(1, "$schema must be a non-empty string.").describe("Optional path or URL to the Agentyx JSON Schema.").optional(),
|
|
@@ -286,7 +344,45 @@ const agentyxConfigSchema = z.strictObject({
|
|
|
286
344
|
targets: z.array(agentyxTargetSchema).describe("Coding-agent providers this project targets.").default([]),
|
|
287
345
|
skillDirectories: z.array(skillDirectorySchema).describe("Project-relative roots containing local Agent Skills.").optional(),
|
|
288
346
|
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()
|
|
347
|
+
trustedSources: z.array(trustedSourceReferenceSchema).describe("Pinned local checkouts of known external skill/plugin sources.").optional(),
|
|
348
|
+
project: projectMetadataSchema.optional().describe("Identity and ownership for this project."),
|
|
349
|
+
relations: z.array(projectRelationSchema).optional().describe("Direct architectural relationships."),
|
|
350
|
+
context: projectContextSchema.optional()
|
|
351
|
+
}).superRefine((config, context) => {
|
|
352
|
+
const relations = config.relations ?? [];
|
|
353
|
+
if (relations.length > 0 && config.project === void 0) context.addIssue({
|
|
354
|
+
code: "custom",
|
|
355
|
+
path: ["project"],
|
|
356
|
+
message: "Project metadata is required when relations are configured."
|
|
357
|
+
});
|
|
358
|
+
const ids = /* @__PURE__ */ new Set();
|
|
359
|
+
for (const [index, relation] of relations.entries()) {
|
|
360
|
+
if (config.project?.id === relation.id) context.addIssue({
|
|
361
|
+
code: "custom",
|
|
362
|
+
path: [
|
|
363
|
+
"relations",
|
|
364
|
+
index,
|
|
365
|
+
"id"
|
|
366
|
+
],
|
|
367
|
+
message: "A project cannot relate to itself."
|
|
368
|
+
});
|
|
369
|
+
if (ids.has(relation.id)) context.addIssue({
|
|
370
|
+
code: "custom",
|
|
371
|
+
path: [
|
|
372
|
+
"relations",
|
|
373
|
+
index,
|
|
374
|
+
"id"
|
|
375
|
+
],
|
|
376
|
+
message: "Related project ids must be unique."
|
|
377
|
+
});
|
|
378
|
+
ids.add(relation.id);
|
|
379
|
+
}
|
|
380
|
+
const areaPaths = config.context?.areas.map((area) => area.path) ?? [];
|
|
381
|
+
if (new Set(areaPaths).size !== areaPaths.length) context.addIssue({
|
|
382
|
+
code: "custom",
|
|
383
|
+
path: ["context", "areas"],
|
|
384
|
+
message: "Area paths must be unique."
|
|
385
|
+
});
|
|
290
386
|
});
|
|
291
387
|
//#endregion
|
|
292
388
|
//#region src/config/json-schema.ts
|
|
@@ -357,6 +453,23 @@ function isFileNotFound(cause) {
|
|
|
357
453
|
return cause instanceof Error && cause.code === "ENOENT";
|
|
358
454
|
}
|
|
359
455
|
//#endregion
|
|
456
|
+
//#region src/assets.ts
|
|
457
|
+
/**
|
|
458
|
+
* Location of the non-code assets that ship with `@agentyx/core`.
|
|
459
|
+
*
|
|
460
|
+
* **This module must stay directly under `src/`.** The path is derived from
|
|
461
|
+
* `import.meta.url` rather than the working directory, and `src/assets.ts` and
|
|
462
|
+
* the bundled `dist/index.mjs` sit at the same depth below the package root, so
|
|
463
|
+
* one relative path is correct both in development and in the published
|
|
464
|
+
* package. Moving this file into a subdirectory breaks built-in skill loading;
|
|
465
|
+
* `packages/core/test/built-in-skills.test.ts` guards against that.
|
|
466
|
+
*
|
|
467
|
+
* `skills/` is listed in the package's `files`, so it is published alongside
|
|
468
|
+
* `dist/`.
|
|
469
|
+
*/
|
|
470
|
+
const BUILT_IN_SKILLS_PATH = fileURLToPath(new URL("../skills", import.meta.url));
|
|
471
|
+
const PROJECT_CONTEXT_TEMPLATE_PATH = fileURLToPath(new URL("../templates/project-context.md", import.meta.url));
|
|
472
|
+
//#endregion
|
|
360
473
|
//#region src/pack/errors.ts
|
|
361
474
|
/** Raised when a requested pack is not present in the registry. */
|
|
362
475
|
var UnknownPackError = class extends AgentyxError {
|
|
@@ -611,22 +724,6 @@ const builtInPacks = [
|
|
|
611
724
|
/** The registry used by default when no explicit registry is supplied. */
|
|
612
725
|
const builtInPackRegistry = createPackRegistry(builtInPacks);
|
|
613
726
|
//#endregion
|
|
614
|
-
//#region src/assets.ts
|
|
615
|
-
/**
|
|
616
|
-
* Location of the non-code assets that ship with `@agentyx/core`.
|
|
617
|
-
*
|
|
618
|
-
* **This module must stay directly under `src/`.** The path is derived from
|
|
619
|
-
* `import.meta.url` rather than the working directory, and `src/assets.ts` and
|
|
620
|
-
* the bundled `dist/index.mjs` sit at the same depth below the package root, so
|
|
621
|
-
* one relative path is correct both in development and in the published
|
|
622
|
-
* package. Moving this file into a subdirectory breaks built-in skill loading;
|
|
623
|
-
* `packages/core/test/built-in-skills.test.ts` guards against that.
|
|
624
|
-
*
|
|
625
|
-
* `skills/` is listed in the package's `files`, so it is published alongside
|
|
626
|
-
* `dist/`.
|
|
627
|
-
*/
|
|
628
|
-
const BUILT_IN_SKILLS_PATH = fileURLToPath(new URL("../skills", import.meta.url));
|
|
629
|
-
//#endregion
|
|
630
727
|
//#region src/skill/errors.ts
|
|
631
728
|
/**
|
|
632
729
|
* Raised when a pack references a skill the registry does not know.
|
|
@@ -965,18 +1062,70 @@ function getTrustedSourceDefinition(source, registry = knownTrustedSourceRegistr
|
|
|
965
1062
|
async function loadAgentyxProject(projectPath = process.cwd()) {
|
|
966
1063
|
const projectDir = resolve(projectPath);
|
|
967
1064
|
const config = await loadAgentyxConfig(projectDir);
|
|
1065
|
+
await validateContextPaths(projectDir, config.context);
|
|
968
1066
|
for (const source of config.trustedSources ?? []) getTrustedSourceDefinition(source);
|
|
969
1067
|
const localSources = await loadLocalSkillSources(projectDir, config.skillDirectories ?? []);
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
1068
|
+
if (localSources.some((source) => source.name === "agentyx-project-context")) throw new DuplicateSkillError("agentyx-project-context");
|
|
1069
|
+
const generatedSources = config.project !== void 0 || config.context !== void 0 && hasContext(config.context) ? [createProjectContextSkill(config.project, config.relations ?? [], config.context)] : [];
|
|
1070
|
+
const skillRegistry = createSkillRegistry([
|
|
1071
|
+
...builtInSkillRegistry.names.map((name) => ({
|
|
1072
|
+
name,
|
|
1073
|
+
load: () => builtInSkillRegistry.get(name)
|
|
1074
|
+
})),
|
|
1075
|
+
...localSources,
|
|
1076
|
+
...generatedSources
|
|
1077
|
+
]);
|
|
974
1078
|
return {
|
|
975
1079
|
config,
|
|
976
1080
|
packRegistry: config.localPacks === void 0 ? builtInPackRegistry : createPackRegistry([...builtInPacks, ...config.localPacks]),
|
|
977
|
-
skillRegistry
|
|
1081
|
+
skillRegistry,
|
|
1082
|
+
graph: config.project === void 0 ? void 0 : {
|
|
1083
|
+
project: config.project,
|
|
1084
|
+
relations: config.relations ?? []
|
|
1085
|
+
}
|
|
978
1086
|
};
|
|
979
1087
|
}
|
|
1088
|
+
function createProjectContextSkill(project, relations, context) {
|
|
1089
|
+
return {
|
|
1090
|
+
name: "agentyx-project-context",
|
|
1091
|
+
load: () => {
|
|
1092
|
+
const template = readFileSync(PROJECT_CONTEXT_TEMPLATE_PATH, "utf8").trim();
|
|
1093
|
+
const data = JSON.stringify({
|
|
1094
|
+
project,
|
|
1095
|
+
relations,
|
|
1096
|
+
context
|
|
1097
|
+
}, null, 2);
|
|
1098
|
+
const longestFence = Math.max(0, ...Array.from(data.matchAll(/`+/g), (match) => match[0].length));
|
|
1099
|
+
const fence = "`".repeat(Math.max(3, longestFence + 1));
|
|
1100
|
+
return {
|
|
1101
|
+
name: "agentyx-project-context",
|
|
1102
|
+
description: "Repository facts and direct architectural relationships.",
|
|
1103
|
+
content: template.replace("{{PROJECT_FACTS}}", `${fence}json\n${data}\n${fence}`)
|
|
1104
|
+
};
|
|
1105
|
+
}
|
|
1106
|
+
};
|
|
1107
|
+
}
|
|
1108
|
+
function hasContext(context) {
|
|
1109
|
+
return Object.keys(context.commands).length > 0 || context.areas.length > 0 || context.constraints.length > 0;
|
|
1110
|
+
}
|
|
1111
|
+
async function validateContextPaths(projectDir, context) {
|
|
1112
|
+
if (context === void 0) return;
|
|
1113
|
+
const paths = [...Object.values(context.commands).map(({ cwd }) => cwd), ...context.areas.map(({ path }) => path)];
|
|
1114
|
+
const projectRealPath = await realpath(projectDir);
|
|
1115
|
+
for (const path of paths) {
|
|
1116
|
+
if (/^(?:\/|[A-Za-z]:)|\\|(?:^|\/)\.\.(?:\/|$)/.test(path)) throw new ProjectContextPathError(path, "it must be project-relative and cannot traverse parents");
|
|
1117
|
+
const resolvedPath = resolve(projectDir, path);
|
|
1118
|
+
let resolvedRealPath;
|
|
1119
|
+
try {
|
|
1120
|
+
resolvedRealPath = await realpath(resolvedPath);
|
|
1121
|
+
if (!(await stat(resolvedRealPath)).isDirectory()) throw new ProjectContextPathError(path, "it must name a directory");
|
|
1122
|
+
} catch (cause) {
|
|
1123
|
+
if (cause instanceof ProjectContextPathError) throw cause;
|
|
1124
|
+
throw new ProjectContextPathError(path, "it does not exist or cannot be read", cause instanceof Error ? { cause } : void 0);
|
|
1125
|
+
}
|
|
1126
|
+
if (!isInside$1(resolvedRealPath, projectRealPath)) throw new ProjectContextPathError(path, "it resolves outside the project");
|
|
1127
|
+
}
|
|
1128
|
+
}
|
|
980
1129
|
async function loadLocalSkillSources(projectDir, directories) {
|
|
981
1130
|
const projectRealPath = await realpath(projectDir);
|
|
982
1131
|
const sources = [];
|
|
@@ -1116,6 +1265,7 @@ const builtInHookRegistry = createHookRegistry([{
|
|
|
1116
1265
|
event: "SessionStart",
|
|
1117
1266
|
command: "npx",
|
|
1118
1267
|
args: [
|
|
1268
|
+
"--no-install",
|
|
1119
1269
|
"agentyx",
|
|
1120
1270
|
"doctor",
|
|
1121
1271
|
"--hook"
|
|
@@ -1245,7 +1395,8 @@ function createMcpServerRegistry(sources) {
|
|
|
1245
1395
|
name: server.name,
|
|
1246
1396
|
description: server.description,
|
|
1247
1397
|
transport: server.transport,
|
|
1248
|
-
contextCost: server.contextCost
|
|
1398
|
+
contextCost: server.contextCost,
|
|
1399
|
+
runtime: server.runtime
|
|
1249
1400
|
};
|
|
1250
1401
|
}),
|
|
1251
1402
|
get
|
|
@@ -1259,6 +1410,7 @@ const builtInMcpServerRegistry = createMcpServerRegistry([
|
|
|
1259
1410
|
description: "Fetch up-to-date library documentation from Context7.",
|
|
1260
1411
|
transport: "http",
|
|
1261
1412
|
contextCost: "medium",
|
|
1413
|
+
runtime: "local",
|
|
1262
1414
|
url: "https://mcp.context7.com/mcp"
|
|
1263
1415
|
})
|
|
1264
1416
|
},
|
|
@@ -1269,8 +1421,9 @@ const builtInMcpServerRegistry = createMcpServerRegistry([
|
|
|
1269
1421
|
description: "Automate and inspect browsers through Playwright MCP.",
|
|
1270
1422
|
transport: "stdio",
|
|
1271
1423
|
contextCost: "high",
|
|
1424
|
+
runtime: "may-download",
|
|
1272
1425
|
command: "npx",
|
|
1273
|
-
args: ["@playwright/mcp@
|
|
1426
|
+
args: ["@playwright/mcp@0.0.81"]
|
|
1274
1427
|
})
|
|
1275
1428
|
},
|
|
1276
1429
|
{
|
|
@@ -1280,6 +1433,7 @@ const builtInMcpServerRegistry = createMcpServerRegistry([
|
|
|
1280
1433
|
description: "Structural code-intelligence MCP backed by a persistent code knowledge graph.",
|
|
1281
1434
|
transport: "stdio",
|
|
1282
1435
|
contextCost: "high",
|
|
1436
|
+
runtime: "local",
|
|
1283
1437
|
command: "codebase-memory-mcp"
|
|
1284
1438
|
})
|
|
1285
1439
|
},
|
|
@@ -1290,6 +1444,7 @@ const builtInMcpServerRegistry = createMcpServerRegistry([
|
|
|
1290
1444
|
description: "Read repositories, issues, pull requests and workflow runs on GitHub.",
|
|
1291
1445
|
transport: "http",
|
|
1292
1446
|
contextCost: "high",
|
|
1447
|
+
runtime: "local",
|
|
1293
1448
|
url: "https://api.githubcopilot.com/mcp/"
|
|
1294
1449
|
})
|
|
1295
1450
|
},
|
|
@@ -1300,6 +1455,7 @@ const builtInMcpServerRegistry = createMcpServerRegistry([
|
|
|
1300
1455
|
description: "Inspect production issues, events and stack traces recorded by Sentry.",
|
|
1301
1456
|
transport: "http",
|
|
1302
1457
|
contextCost: "medium",
|
|
1458
|
+
runtime: "local",
|
|
1303
1459
|
url: "https://mcp.sentry.dev/mcp"
|
|
1304
1460
|
})
|
|
1305
1461
|
},
|
|
@@ -1310,8 +1466,9 @@ const builtInMcpServerRegistry = createMcpServerRegistry([
|
|
|
1310
1466
|
description: "Record performance traces and inspect pages through Chrome DevTools.",
|
|
1311
1467
|
transport: "stdio",
|
|
1312
1468
|
contextCost: "high",
|
|
1469
|
+
runtime: "may-download",
|
|
1313
1470
|
command: "npx",
|
|
1314
|
-
args: ["-y", "chrome-devtools-mcp@
|
|
1471
|
+
args: ["-y", "chrome-devtools-mcp@1.9.0"]
|
|
1315
1472
|
})
|
|
1316
1473
|
},
|
|
1317
1474
|
{
|
|
@@ -1321,8 +1478,9 @@ const builtInMcpServerRegistry = createMcpServerRegistry([
|
|
|
1321
1478
|
description: "Inspect and query Supabase project schemas, tables and logs.",
|
|
1322
1479
|
transport: "stdio",
|
|
1323
1480
|
contextCost: "high",
|
|
1481
|
+
runtime: "may-download",
|
|
1324
1482
|
command: "npx",
|
|
1325
|
-
args: ["-y", "@supabase/mcp-server-supabase@
|
|
1483
|
+
args: ["-y", "@supabase/mcp-server-supabase@0.13.0"],
|
|
1326
1484
|
env: { SUPABASE_ACCESS_TOKEN: { fromEnv: "SUPABASE_ACCESS_TOKEN" } }
|
|
1327
1485
|
})
|
|
1328
1486
|
}
|
|
@@ -1519,7 +1677,7 @@ function resolveAgentyxConfig(config, registry = builtInPackRegistry, skillRegis
|
|
|
1519
1677
|
return {
|
|
1520
1678
|
requestedPacks,
|
|
1521
1679
|
resolvedPacks,
|
|
1522
|
-
skills: collectPackSkills(resolvedPacks, registry, skillRegistry),
|
|
1680
|
+
skills: unique([...collectPackSkills(resolvedPacks, registry, skillRegistry), ...config.project !== void 0 || (config.relations?.length ?? 0) > 0 || hasProjectContext(config.context) ? ["agentyx-project-context"] : []]),
|
|
1523
1681
|
declaredMcpServers,
|
|
1524
1682
|
mcpServers: filterEffectiveMcpServers(declaredMcpServers, config.enable),
|
|
1525
1683
|
declaredTools,
|
|
@@ -1530,6 +1688,12 @@ function resolveAgentyxConfig(config, registry = builtInPackRegistry, skillRegis
|
|
|
1530
1688
|
targets: [...config.targets]
|
|
1531
1689
|
};
|
|
1532
1690
|
}
|
|
1691
|
+
function hasProjectContext(context) {
|
|
1692
|
+
return context !== void 0 && (Object.keys(context.commands).length > 0 || context.areas.length > 0 || context.constraints.length > 0);
|
|
1693
|
+
}
|
|
1694
|
+
function unique(values) {
|
|
1695
|
+
return [...new Set(values)];
|
|
1696
|
+
}
|
|
1533
1697
|
//#endregion
|
|
1534
1698
|
//#region src/manifest/errors.ts
|
|
1535
1699
|
/** Raised when `.agentyx.lock.json` is not valid JSON. */
|
|
@@ -1740,13 +1904,92 @@ function getCoreStatus() {
|
|
|
1740
1904
|
return "ready";
|
|
1741
1905
|
}
|
|
1742
1906
|
//#endregion
|
|
1743
|
-
//#region src/project/
|
|
1907
|
+
//#region src/project/executable.ts
|
|
1908
|
+
/** Whether `command` resolves to an executable file, via `PATH` unless it already contains a `/`. */
|
|
1909
|
+
async function isExecutableOnPath(command) {
|
|
1910
|
+
if (command.includes("/")) return isFileExecutable(command);
|
|
1911
|
+
for (const directory of (process.env.PATH ?? "").split(delimiter)) {
|
|
1912
|
+
if (directory.length === 0) continue;
|
|
1913
|
+
if (await isFileExecutable(`${directory}/${command}`)) return true;
|
|
1914
|
+
}
|
|
1915
|
+
return false;
|
|
1916
|
+
}
|
|
1917
|
+
async function isFileExecutable(path) {
|
|
1918
|
+
try {
|
|
1919
|
+
await access(path, constants.X_OK);
|
|
1920
|
+
return true;
|
|
1921
|
+
} catch {
|
|
1922
|
+
return false;
|
|
1923
|
+
}
|
|
1924
|
+
}
|
|
1925
|
+
//#endregion
|
|
1926
|
+
//#region src/project/signals.ts
|
|
1744
1927
|
const PACKAGE_MANAGERS = [
|
|
1745
1928
|
"pnpm",
|
|
1746
1929
|
"npm",
|
|
1747
1930
|
"yarn",
|
|
1748
1931
|
"bun"
|
|
1749
1932
|
];
|
|
1933
|
+
/** Package name patterns matched exactly, or as a `@scope/*` prefix. */
|
|
1934
|
+
const TEST_FRAMEWORK_PATTERNS = [
|
|
1935
|
+
"vitest",
|
|
1936
|
+
"jest",
|
|
1937
|
+
"@jest/*",
|
|
1938
|
+
"@playwright/test",
|
|
1939
|
+
"cypress",
|
|
1940
|
+
"karma"
|
|
1941
|
+
];
|
|
1942
|
+
const BROWSER_TESTING_PATTERNS = ["@playwright/test", "cypress"];
|
|
1943
|
+
const OBSERVABILITY_PATTERNS = ["@sentry/*", "@opentelemetry/*"];
|
|
1944
|
+
const DATA_TOOLING_PATTERNS = [
|
|
1945
|
+
"@supabase/*",
|
|
1946
|
+
"drizzle-orm",
|
|
1947
|
+
"prisma",
|
|
1948
|
+
"@prisma/client",
|
|
1949
|
+
"typeorm",
|
|
1950
|
+
"sequelize"
|
|
1951
|
+
];
|
|
1952
|
+
const ACCESSIBILITY_PATTERNS = [
|
|
1953
|
+
"axe-core",
|
|
1954
|
+
"@axe-core/*",
|
|
1955
|
+
"pa11y"
|
|
1956
|
+
];
|
|
1957
|
+
const DEPENDENCY_FIELDS = [
|
|
1958
|
+
"dependencies",
|
|
1959
|
+
"devDependencies",
|
|
1960
|
+
"peerDependencies",
|
|
1961
|
+
"optionalDependencies"
|
|
1962
|
+
];
|
|
1963
|
+
const CONTAINER_IGNORED = /* @__PURE__ */ new Set([
|
|
1964
|
+
"node_modules",
|
|
1965
|
+
".git",
|
|
1966
|
+
"dist",
|
|
1967
|
+
"build",
|
|
1968
|
+
"coverage"
|
|
1969
|
+
]);
|
|
1970
|
+
/** Extensions counted as source files by the bounded repository-size scan. */
|
|
1971
|
+
const SOURCE_FILE_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
1972
|
+
".ts",
|
|
1973
|
+
".tsx",
|
|
1974
|
+
".js",
|
|
1975
|
+
".jsx",
|
|
1976
|
+
".mjs",
|
|
1977
|
+
".cjs",
|
|
1978
|
+
".vue",
|
|
1979
|
+
".svelte",
|
|
1980
|
+
".py",
|
|
1981
|
+
".go",
|
|
1982
|
+
".rs",
|
|
1983
|
+
".java",
|
|
1984
|
+
".kt",
|
|
1985
|
+
".rb",
|
|
1986
|
+
".php",
|
|
1987
|
+
".css",
|
|
1988
|
+
".scss",
|
|
1989
|
+
".html"
|
|
1990
|
+
]);
|
|
1991
|
+
/** Maximum directory entries visited by `scanRepositorySize`, regardless of match. */
|
|
1992
|
+
const REPOSITORY_SCAN_ENTRY_LIMIT = 5e3;
|
|
1750
1993
|
const lockfileManagers = /* @__PURE__ */ new Map([
|
|
1751
1994
|
["pnpm-lock.yaml", "pnpm"],
|
|
1752
1995
|
["package-lock.json", "npm"],
|
|
@@ -1754,52 +1997,142 @@ const lockfileManagers = /* @__PURE__ */ new Map([
|
|
|
1754
1997
|
["bun.lock", "bun"],
|
|
1755
1998
|
["bun.lockb", "bun"]
|
|
1756
1999
|
]);
|
|
1757
|
-
|
|
2000
|
+
/** Gathers every project signal with a bounded, cheap set of reads. */
|
|
2001
|
+
async function collectProjectSignals(projectDir) {
|
|
1758
2002
|
const packageJsonPath = join(projectDir, "package.json");
|
|
1759
|
-
const
|
|
1760
|
-
const
|
|
1761
|
-
const
|
|
2003
|
+
const packageJsonResult = await readPackageJson(packageJsonPath);
|
|
2004
|
+
const packageJson = packageJsonResult.data;
|
|
2005
|
+
const [packageManager, containers, ci, monorepo, repositorySize, tsconfig, rtkAvailable] = await Promise.all([
|
|
2006
|
+
detectPackageManager(projectDir, packageJson),
|
|
2007
|
+
detectContainerFiles(projectDir),
|
|
2008
|
+
detectCiFiles(projectDir),
|
|
2009
|
+
detectMonorepo(projectDir, packageJson),
|
|
2010
|
+
scanRepositorySize(projectDir),
|
|
2011
|
+
exists(join(projectDir, "tsconfig.json")),
|
|
2012
|
+
isExecutableOnPath(builtInToolRegistry.get("rtk").command)
|
|
2013
|
+
]);
|
|
1762
2014
|
return {
|
|
1763
2015
|
projectDir,
|
|
1764
2016
|
packageJson: {
|
|
1765
|
-
present:
|
|
1766
|
-
valid:
|
|
2017
|
+
present: packageJsonResult.present,
|
|
2018
|
+
valid: packageJsonResult.valid,
|
|
1767
2019
|
path: packageJsonPath,
|
|
1768
|
-
name: getString(packageJson
|
|
1769
|
-
packageManager: getString(packageJson
|
|
1770
|
-
|
|
2020
|
+
name: getString(packageJson?.name),
|
|
2021
|
+
packageManager: getString(packageJson?.packageManager),
|
|
2022
|
+
scripts: getStringRecord(packageJson?.scripts),
|
|
2023
|
+
error: packageJsonResult.error
|
|
1771
2024
|
},
|
|
1772
2025
|
packageManager,
|
|
1773
|
-
|
|
1774
|
-
|
|
2026
|
+
typescript: {
|
|
2027
|
+
dependency: findDependency(packageJson, "typescript"),
|
|
2028
|
+
tsconfig
|
|
2029
|
+
},
|
|
2030
|
+
angular: findDependency(packageJson, "@angular/core"),
|
|
2031
|
+
testFrameworks: findAnyDependency(packageJson, TEST_FRAMEWORK_PATTERNS),
|
|
2032
|
+
browserTesting: findAnyDependency(packageJson, BROWSER_TESTING_PATTERNS),
|
|
2033
|
+
observability: findAnyDependency(packageJson, OBSERVABILITY_PATTERNS),
|
|
2034
|
+
dataTooling: findAnyDependency(packageJson, DATA_TOOLING_PATTERNS),
|
|
2035
|
+
accessibilityTooling: findAnyDependency(packageJson, ACCESSIBILITY_PATTERNS),
|
|
2036
|
+
containers,
|
|
2037
|
+
ci,
|
|
2038
|
+
monorepo,
|
|
2039
|
+
repositorySize,
|
|
2040
|
+
localTools: { rtkAvailable }
|
|
1775
2041
|
};
|
|
1776
2042
|
}
|
|
1777
|
-
|
|
2043
|
+
/**
|
|
2044
|
+
* Counts source-like files under a bounded traversal, ignoring dependency, VCS and build output
|
|
2045
|
+
* directories. Stops after visiting `entryLimit` directory entries — cheap even on a monorepo — and
|
|
2046
|
+
* reports `capped` so callers never mistake a partial scan for a complete one.
|
|
2047
|
+
*/
|
|
2048
|
+
async function scanRepositorySize(projectDir, options = {}) {
|
|
2049
|
+
const entryLimit = options.entryLimit ?? 5e3;
|
|
2050
|
+
const directories = [projectDir];
|
|
2051
|
+
let filesScanned = 0;
|
|
2052
|
+
let entriesVisited = 0;
|
|
2053
|
+
let capped = false;
|
|
2054
|
+
while (directories.length > 0 && !capped) {
|
|
2055
|
+
const directory = directories.shift();
|
|
2056
|
+
if (directory === void 0) break;
|
|
2057
|
+
let entries;
|
|
2058
|
+
try {
|
|
2059
|
+
entries = await readdir(directory, { withFileTypes: true });
|
|
2060
|
+
} catch {
|
|
2061
|
+
continue;
|
|
2062
|
+
}
|
|
2063
|
+
for (const entry of entries) {
|
|
2064
|
+
if (entriesVisited >= entryLimit) {
|
|
2065
|
+
capped = true;
|
|
2066
|
+
break;
|
|
2067
|
+
}
|
|
2068
|
+
entriesVisited += 1;
|
|
2069
|
+
if (entry.isDirectory()) {
|
|
2070
|
+
if (entry.name.startsWith(".") || CONTAINER_IGNORED.has(entry.name)) continue;
|
|
2071
|
+
directories.push(join(directory, entry.name));
|
|
2072
|
+
} else if (entry.isFile() && SOURCE_FILE_EXTENSIONS.has(extname(entry.name))) filesScanned += 1;
|
|
2073
|
+
}
|
|
2074
|
+
}
|
|
1778
2075
|
return {
|
|
1779
|
-
|
|
1780
|
-
|
|
1781
|
-
targets: [...input.targets]
|
|
2076
|
+
filesScanned,
|
|
2077
|
+
capped
|
|
1782
2078
|
};
|
|
1783
2079
|
}
|
|
1784
|
-
function
|
|
1785
|
-
|
|
2080
|
+
function findDependency(packageJson, pattern) {
|
|
2081
|
+
for (const field of DEPENDENCY_FIELDS) {
|
|
2082
|
+
const value = packageJson?.[field];
|
|
2083
|
+
if (!isRecord(value)) continue;
|
|
2084
|
+
if (pattern.endsWith("/*")) {
|
|
2085
|
+
const prefix = pattern.slice(0, -1);
|
|
2086
|
+
const name = Object.keys(value).find((key) => key.startsWith(prefix));
|
|
2087
|
+
if (name !== void 0) return {
|
|
2088
|
+
dependency: name,
|
|
2089
|
+
field
|
|
2090
|
+
};
|
|
2091
|
+
continue;
|
|
2092
|
+
}
|
|
2093
|
+
if (pattern in value) return {
|
|
2094
|
+
dependency: pattern,
|
|
2095
|
+
field
|
|
2096
|
+
};
|
|
2097
|
+
}
|
|
1786
2098
|
}
|
|
1787
|
-
function
|
|
1788
|
-
|
|
1789
|
-
|
|
1790
|
-
|
|
1791
|
-
|
|
1792
|
-
|
|
1793
|
-
|
|
1794
|
-
|
|
2099
|
+
function findAnyDependency(packageJson, patterns) {
|
|
2100
|
+
const matches = [];
|
|
2101
|
+
const seen = /* @__PURE__ */ new Set();
|
|
2102
|
+
for (const pattern of patterns) {
|
|
2103
|
+
const match = findDependency(packageJson, pattern);
|
|
2104
|
+
if (match !== void 0 && !seen.has(match.dependency)) {
|
|
2105
|
+
seen.add(match.dependency);
|
|
2106
|
+
matches.push(match);
|
|
2107
|
+
}
|
|
2108
|
+
}
|
|
2109
|
+
return matches;
|
|
2110
|
+
}
|
|
2111
|
+
async function detectContainerFiles(projectDir) {
|
|
2112
|
+
const [dockerfile, dockerCompose, compose] = await Promise.all([
|
|
2113
|
+
exists(join(projectDir, "Dockerfile")),
|
|
2114
|
+
exists(join(projectDir, "docker-compose.yml")),
|
|
2115
|
+
exists(join(projectDir, "compose.yml"))
|
|
2116
|
+
]);
|
|
2117
|
+
return {
|
|
2118
|
+
dockerfile,
|
|
2119
|
+
compose: dockerCompose || compose
|
|
2120
|
+
};
|
|
1795
2121
|
}
|
|
1796
|
-
async function
|
|
1797
|
-
|
|
1798
|
-
|
|
1799
|
-
|
|
1800
|
-
|
|
1801
|
-
if (
|
|
1802
|
-
|
|
2122
|
+
async function detectCiFiles(projectDir) {
|
|
2123
|
+
return { githubActions: await exists(join(projectDir, ".github", "workflows")) };
|
|
2124
|
+
}
|
|
2125
|
+
async function detectMonorepo(projectDir, packageJson) {
|
|
2126
|
+
const markers = [];
|
|
2127
|
+
if (await exists(join(projectDir, "pnpm-workspace.yaml"))) markers.push("pnpm-workspace.yaml");
|
|
2128
|
+
if (await exists(join(projectDir, "turbo.json"))) markers.push("turbo.json");
|
|
2129
|
+
if (await exists(join(projectDir, "nx.json"))) markers.push("nx.json");
|
|
2130
|
+
const workspaces = packageJson?.workspaces;
|
|
2131
|
+
if (Array.isArray(workspaces) || isRecord(workspaces)) markers.push("package.json workspaces");
|
|
2132
|
+
return {
|
|
2133
|
+
detected: markers.length > 0,
|
|
2134
|
+
markers
|
|
2135
|
+
};
|
|
1803
2136
|
}
|
|
1804
2137
|
async function detectPackageManager(projectDir, packageJson) {
|
|
1805
2138
|
const lockfiles = [];
|
|
@@ -1862,16 +2195,6 @@ async function readPackageJson(path) {
|
|
|
1862
2195
|
};
|
|
1863
2196
|
}
|
|
1864
2197
|
}
|
|
1865
|
-
function collectDependencies(packageJson) {
|
|
1866
|
-
const dependencies = /* @__PURE__ */ new Set();
|
|
1867
|
-
for (const field of [
|
|
1868
|
-
packageJson?.dependencies,
|
|
1869
|
-
packageJson?.devDependencies,
|
|
1870
|
-
packageJson?.peerDependencies,
|
|
1871
|
-
packageJson?.optionalDependencies
|
|
1872
|
-
]) if (isRecord(field)) for (const name of Object.keys(field)) dependencies.add(name);
|
|
1873
|
-
return dependencies;
|
|
1874
|
-
}
|
|
1875
2198
|
function parsePackageManagerName(value) {
|
|
1876
2199
|
const name = value?.split("@")[0];
|
|
1877
2200
|
return PACKAGE_MANAGERS.includes(name) ? name : void 0;
|
|
@@ -1879,6 +2202,10 @@ function parsePackageManagerName(value) {
|
|
|
1879
2202
|
function getString(value) {
|
|
1880
2203
|
return typeof value === "string" ? value : void 0;
|
|
1881
2204
|
}
|
|
2205
|
+
function getStringRecord(value) {
|
|
2206
|
+
if (!isRecord(value)) return {};
|
|
2207
|
+
return Object.fromEntries(Object.entries(value).filter((entry) => typeof entry[1] === "string"));
|
|
2208
|
+
}
|
|
1882
2209
|
function isRecord(value) {
|
|
1883
2210
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
1884
2211
|
}
|
|
@@ -1895,6 +2222,205 @@ function isNotFound(cause) {
|
|
|
1895
2222
|
return cause instanceof Error && cause.code === "ENOENT";
|
|
1896
2223
|
}
|
|
1897
2224
|
//#endregion
|
|
2225
|
+
//#region src/project/detector.ts
|
|
2226
|
+
/**
|
|
2227
|
+
* Factual project detection only. It gathers `ProjectSignals` and never decides which packs or
|
|
2228
|
+
* capabilities fit — that policy lives in `recommendCapabilities`, kept separate so each half stays
|
|
2229
|
+
* independently testable.
|
|
2230
|
+
*/
|
|
2231
|
+
async function detectProject(projectDir) {
|
|
2232
|
+
const signals = await collectProjectSignals(projectDir);
|
|
2233
|
+
return {
|
|
2234
|
+
projectDir,
|
|
2235
|
+
packageJson: signals.packageJson,
|
|
2236
|
+
packageManager: signals.packageManager,
|
|
2237
|
+
signals
|
|
2238
|
+
};
|
|
2239
|
+
}
|
|
2240
|
+
function buildAgentyxConfig(input) {
|
|
2241
|
+
return {
|
|
2242
|
+
packs: [...input.packs],
|
|
2243
|
+
enable: [...input.enable ?? []],
|
|
2244
|
+
targets: [...input.targets]
|
|
2245
|
+
};
|
|
2246
|
+
}
|
|
2247
|
+
function formatAgentyxConfig(config) {
|
|
2248
|
+
return `${JSON.stringify(config, null, 2)}\n`;
|
|
2249
|
+
}
|
|
2250
|
+
//#endregion
|
|
2251
|
+
//#region src/project/recommend.ts
|
|
2252
|
+
const RECOMMENDATION_CONFIDENCES = ["high", "medium"];
|
|
2253
|
+
/**
|
|
2254
|
+
* Heuristic thresholds for the `codebase-memory` capability suggestion, in scanned source files.
|
|
2255
|
+
* These are approximate signals, not exact token accounting, and a monorepo is treated as already
|
|
2256
|
+
* complex enough to lower the bar.
|
|
2257
|
+
*/
|
|
2258
|
+
const CODEBASE_MEMORY_FILE_THRESHOLD = 2e3;
|
|
2259
|
+
const CODEBASE_MEMORY_MONOREPO_FILE_THRESHOLD = 800;
|
|
2260
|
+
/**
|
|
2261
|
+
* Applies conservative, deterministic policy on top of `ProjectSignals`. This is the only place
|
|
2262
|
+
* that turns an observed fact into a product suggestion — `ProjectSignals` stays fact-only so it
|
|
2263
|
+
* can be tested independently of pack names or capability policy.
|
|
2264
|
+
*
|
|
2265
|
+
* Never enables anything and never touches the filesystem: the caller decides what, if anything,
|
|
2266
|
+
* to do with the result.
|
|
2267
|
+
*/
|
|
2268
|
+
function recommendCapabilities(signals, options = {}) {
|
|
2269
|
+
const packs = recommendPacks(signals);
|
|
2270
|
+
const configuredPacks = options.configuredPacks ?? [];
|
|
2271
|
+
return {
|
|
2272
|
+
packs,
|
|
2273
|
+
capabilities: recommendOptionalCapabilities(signals, /* @__PURE__ */ new Set([...packs.map((pack) => pack.name), ...configuredPacks]), configuredPacks)
|
|
2274
|
+
};
|
|
2275
|
+
}
|
|
2276
|
+
function recommendPacks(signals) {
|
|
2277
|
+
const packs = [];
|
|
2278
|
+
if (signals.packageJson.present) packs.push(packRecommendation("technical", "high", ["package.json detected — baseline engineering practices apply to any software project."]));
|
|
2279
|
+
const typescriptReasons = [...signals.typescript.dependency !== void 0 ? [dependencyReason("TypeScript", signals.typescript.dependency)] : [], ...signals.typescript.tsconfig ? ["tsconfig.json detected."] : []];
|
|
2280
|
+
if (typescriptReasons.length > 0) packs.push(packRecommendation("typescript", "high", typescriptReasons));
|
|
2281
|
+
if (signals.angular !== void 0) packs.push(packRecommendation("angular", "high", [dependencyReason("@angular/core", signals.angular)]));
|
|
2282
|
+
const testingMatches = dedupeMatches([...signals.testFrameworks, ...signals.browserTesting]);
|
|
2283
|
+
if (testingMatches.length > 0) packs.push(packRecommendation("testing", "high", testingMatches.map((match) => dependencyReason(match.dependency, match))));
|
|
2284
|
+
const devopsReasons = [
|
|
2285
|
+
...signals.containers.dockerfile ? ["Dockerfile detected."] : [],
|
|
2286
|
+
...signals.containers.compose ? ["Container compose file detected."] : [],
|
|
2287
|
+
...signals.ci.githubActions ? ["GitHub Actions workflow detected (.github/workflows)."] : []
|
|
2288
|
+
];
|
|
2289
|
+
if (devopsReasons.length > 0) packs.push(packRecommendation("devops", "high", devopsReasons));
|
|
2290
|
+
if (signals.observability.length > 0) packs.push(packRecommendation("observability", "high", signals.observability.map((match) => dependencyReason(match.dependency, match))));
|
|
2291
|
+
if (signals.dataTooling.length > 0) packs.push(packRecommendation("data", "high", signals.dataTooling.map((match) => dependencyReason(match.dependency, match))));
|
|
2292
|
+
if (signals.accessibilityTooling.length > 0) packs.push(packRecommendation("accessibility", "high", signals.accessibilityTooling.map((match) => dependencyReason(match.dependency, match))));
|
|
2293
|
+
return packs;
|
|
2294
|
+
}
|
|
2295
|
+
function recommendOptionalCapabilities(signals, activePacks, configuredPacks) {
|
|
2296
|
+
const capabilities = [];
|
|
2297
|
+
const playwrightMatch = signals.browserTesting.find((match) => match.dependency === "@playwright/test");
|
|
2298
|
+
if (activePacks.has("testing") && playwrightMatch !== void 0) capabilities.push(capabilityRecommendation("playwright", "high", ["Playwright is already part of this project; browser MCP may be useful for interactive browser work."]));
|
|
2299
|
+
if (signals.observability.find((match) => match.dependency.startsWith("@sentry/")) !== void 0) capabilities.push(capabilityRecommendation("sentry", "high", ["Sentry is already configured as a dependency."]));
|
|
2300
|
+
if (signals.dataTooling.find((match) => match.dependency.startsWith("@supabase/")) !== void 0) capabilities.push(capabilityRecommendation("supabase", "high", ["Supabase is already configured as a dependency."]));
|
|
2301
|
+
if (configuredPacks.includes("efficiency") && signals.localTools.rtkAvailable) capabilities.push(capabilityRecommendation("rtk", "high", ["RTK is already installed locally."]));
|
|
2302
|
+
const sizeThreshold = signals.monorepo.detected ? 800 : CODEBASE_MEMORY_FILE_THRESHOLD;
|
|
2303
|
+
if (signals.repositorySize.filesScanned >= sizeThreshold || signals.repositorySize.capped) capabilities.push(capabilityRecommendation("codebase-memory", "medium", [repositorySizeReason(signals)]));
|
|
2304
|
+
return capabilities;
|
|
2305
|
+
}
|
|
2306
|
+
function repositorySizeReason(signals) {
|
|
2307
|
+
const { filesScanned, capped } = signals.repositorySize;
|
|
2308
|
+
const scope = signals.monorepo.detected ? " across this monorepo" : "";
|
|
2309
|
+
return `Heuristic: found ${capped ? `at least ${filesScanned} source files (the scan was capped before finishing)` : `${filesScanned} source files`}${scope}. A persistent codebase-memory MCP may reduce repeated full-tree reads on a repository this size — this is an approximate signal, not an exact token measurement.`;
|
|
2310
|
+
}
|
|
2311
|
+
function packRecommendation(name, confidence, reasons) {
|
|
2312
|
+
return {
|
|
2313
|
+
kind: "pack",
|
|
2314
|
+
name,
|
|
2315
|
+
confidence,
|
|
2316
|
+
reasons
|
|
2317
|
+
};
|
|
2318
|
+
}
|
|
2319
|
+
/** Provenance comes from the pack registry itself — never a second, hand-maintained capability map. */
|
|
2320
|
+
function capabilityRecommendation(name, confidence, reasons) {
|
|
2321
|
+
return {
|
|
2322
|
+
kind: "capability",
|
|
2323
|
+
name,
|
|
2324
|
+
confidence,
|
|
2325
|
+
reasons,
|
|
2326
|
+
contributedBy: packsDeclaringOptionalCapability(name)
|
|
2327
|
+
};
|
|
2328
|
+
}
|
|
2329
|
+
function packsDeclaringOptionalCapability(name) {
|
|
2330
|
+
const packs = [];
|
|
2331
|
+
for (const pack of builtInPackRegistry.values()) if (pack.mcpServers.some((server) => server.name === name && server.activation === "optional") || pack.tools.some((tool) => tool.name === name && tool.activation === "optional") || pack.hooks.some((hook) => hook.name === name && hook.activation === "optional")) packs.push(pack.name);
|
|
2332
|
+
return packs;
|
|
2333
|
+
}
|
|
2334
|
+
function dependencyReason(label, match) {
|
|
2335
|
+
return label === match.dependency ? `Detected ${match.dependency} in ${match.field}.` : `Detected ${label} (${match.dependency}) in ${match.field}.`;
|
|
2336
|
+
}
|
|
2337
|
+
function dedupeMatches(matches) {
|
|
2338
|
+
const seen = /* @__PURE__ */ new Set();
|
|
2339
|
+
const result = [];
|
|
2340
|
+
for (const match of matches) if (!seen.has(match.dependency)) {
|
|
2341
|
+
seen.add(match.dependency);
|
|
2342
|
+
result.push(match);
|
|
2343
|
+
}
|
|
2344
|
+
return result;
|
|
2345
|
+
}
|
|
2346
|
+
//#endregion
|
|
2347
|
+
//#region src/project-graph.ts
|
|
2348
|
+
var ProjectGraphNotConfiguredError = class extends AgentyxError {
|
|
2349
|
+
constructor() {
|
|
2350
|
+
super("project_graph_not_configured", "Project Graph requires a project object in .agentyx.json.");
|
|
2351
|
+
this.name = "ProjectGraphNotConfiguredError";
|
|
2352
|
+
}
|
|
2353
|
+
};
|
|
2354
|
+
function createProjectGraph(project, relations) {
|
|
2355
|
+
return {
|
|
2356
|
+
project,
|
|
2357
|
+
relations: [...relations]
|
|
2358
|
+
};
|
|
2359
|
+
}
|
|
2360
|
+
function renderProjectGraphText(graph) {
|
|
2361
|
+
const rows = graph.relations.map((relation, index) => {
|
|
2362
|
+
return `${index === graph.relations.length - 1 ? "└──" : "├──"} ${relation.type} [${relation.role}] → ${relation.name}`;
|
|
2363
|
+
});
|
|
2364
|
+
return [graph.project.name, ...rows].join("\n");
|
|
2365
|
+
}
|
|
2366
|
+
function renderProjectGraphJson(graph) {
|
|
2367
|
+
return `${JSON.stringify(graph, null, 2)}\n`;
|
|
2368
|
+
}
|
|
2369
|
+
function renderProjectGraphMermaid(graph) {
|
|
2370
|
+
const current = mermaidId(graph.project.id);
|
|
2371
|
+
return ["graph LR", ...graph.relations.map((relation) => ` ${current}["${mermaidText(graph.project.name)}"] -->|${mermaidText(`${relation.type}: ${relation.role}`)}| ${mermaidId(relation.id)}["${mermaidText(relation.name)}"]`)].join("\n");
|
|
2372
|
+
}
|
|
2373
|
+
function renderProjectGraphSvg(graph) {
|
|
2374
|
+
const width = 720;
|
|
2375
|
+
const rowHeight = 92;
|
|
2376
|
+
const height = Math.max(180, 100 + graph.relations.length * rowHeight);
|
|
2377
|
+
const centerX = 360;
|
|
2378
|
+
const centerY = height / 2;
|
|
2379
|
+
const svg = [
|
|
2380
|
+
`<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 ${width} ${height}" role="img" aria-labelledby="title desc">`,
|
|
2381
|
+
`<title id="title">${xml(graph.project.name)} project graph</title>`,
|
|
2382
|
+
`<desc id="desc">Direct architectural relationships from ${xml(graph.project.name)}.</desc>`,
|
|
2383
|
+
"<defs><marker id=\"arrow\" markerWidth=\"10\" markerHeight=\"8\" refX=\"9\" refY=\"4\" orient=\"auto\"><path d=\"M0,0 L10,4 L0,8 z\" fill=\"#526579\"/></marker></defs>"
|
|
2384
|
+
];
|
|
2385
|
+
graph.relations.forEach((relation, index) => {
|
|
2386
|
+
const y = 54 + index * rowHeight;
|
|
2387
|
+
const right = index % 2 === 0;
|
|
2388
|
+
const x = right ? 500 : 0;
|
|
2389
|
+
const edgeStart = right ? 488 : 232;
|
|
2390
|
+
const edgeEnd = right ? x : x + 220;
|
|
2391
|
+
svg.push(`<line x1="${edgeStart}" y1="${centerY}" x2="${edgeEnd}" y2="${y + 24}" stroke="#526579" stroke-width="2" marker-end="url(#arrow)"/>`);
|
|
2392
|
+
svg.push(`<text x="${right ? 494 : 226}" y="${Math.round((centerY + y + 24) / 2) - 7}" class="edge">${xml(truncate(`${relation.type} · ${relation.role}`, 34))}</text>`);
|
|
2393
|
+
svg.push(`<rect x="${x}" y="${y}" width="220" height="48" rx="10" class="node"/>`);
|
|
2394
|
+
svg.push(`<text x="${x + 110}" y="${y + 30}" class="label">${xml(truncate(relation.name, 22))}</text>`);
|
|
2395
|
+
});
|
|
2396
|
+
svg.push(`<rect x="232" y="${centerY - 34}" width="256" height="68" rx="14" class="current"/>`);
|
|
2397
|
+
svg.push(`<text x="${centerX}" y="${centerY + 6}" class="label">${xml(truncate(graph.project.name, 30))}</text>`);
|
|
2398
|
+
svg.push("<style>text{font-family:system-ui,sans-serif}.node{fill:#fff;stroke:#8da0b3;stroke-width:2}.current{fill:#e8f2ff;stroke:#2563a5;stroke-width:3}.label{text-anchor:middle;font-size:16px;font-weight:600;fill:#172536}.edge{text-anchor:middle;font-size:12px;fill:#34495e;paint-order:stroke;stroke:#fff;stroke-width:5px;stroke-linejoin:round}</style>");
|
|
2399
|
+
svg.push("</svg>");
|
|
2400
|
+
return `${svg.join("\n")}\n`;
|
|
2401
|
+
}
|
|
2402
|
+
function findProjectCapabilityOwners(graph, capability) {
|
|
2403
|
+
const owners = graph.relations.filter((relation) => relation.owns.includes(capability));
|
|
2404
|
+
return {
|
|
2405
|
+
capability,
|
|
2406
|
+
currentProject: graph.project.owns.includes(capability) ? graph.project : void 0,
|
|
2407
|
+
relatedProjects: owners
|
|
2408
|
+
};
|
|
2409
|
+
}
|
|
2410
|
+
function xml(value) {
|
|
2411
|
+
return value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll("\"", """).replaceAll("'", "'");
|
|
2412
|
+
}
|
|
2413
|
+
function mermaidText(value) {
|
|
2414
|
+
return value.replaceAll("&", "&").replaceAll("|", "|").replaceAll("[", "[").replaceAll("]", "]").replaceAll("\"", """).replaceAll("<", "<").replaceAll(">", ">");
|
|
2415
|
+
}
|
|
2416
|
+
function mermaidId(value) {
|
|
2417
|
+
return `node_${Array.from(value).map((char) => char.codePointAt(0)?.toString(16) ?? "0").join("")}`;
|
|
2418
|
+
}
|
|
2419
|
+
function truncate(value, limit) {
|
|
2420
|
+
const characters = Array.from(value);
|
|
2421
|
+
return characters.length > limit ? `${characters.slice(0, limit - 1).join("")}…` : value;
|
|
2422
|
+
}
|
|
2423
|
+
//#endregion
|
|
1898
2424
|
//#region src/source/inspection.ts
|
|
1899
2425
|
async function inspectTrustedSource(projectPath, reference) {
|
|
1900
2426
|
const definition = getTrustedSourceDefinition(reference);
|
|
@@ -1983,6 +2509,6 @@ function trustedSourceLoadError(sourceName, reason, cause) {
|
|
|
1983
2509
|
return new TrustedSourceLoadError(sourceName, reason, cause instanceof Error ? { cause } : void 0);
|
|
1984
2510
|
}
|
|
1985
2511
|
//#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 };
|
|
2512
|
+
export { AGENTYX_CONFIG_FILENAME, AGENTYX_CONFIG_SCHEMA_ID, AGENTYX_MANIFEST_FILENAME, AgentyxConfigNotFoundError, AgentyxConfigParseError, AgentyxConfigValidationError, AgentyxError, AgentyxManifestParseError, AgentyxManifestValidationError, CODEBASE_MEMORY_FILE_THRESHOLD, CODEBASE_MEMORY_MONOREPO_FILE_THRESHOLD, 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, ProjectContextPathError, ProjectGraphNotConfiguredError, RECOMMENDATION_CONFIDENCES, REPOSITORY_SCAN_ENTRY_LIMIT, 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, collectProjectSignals, createHookRegistry, createMcpServerRegistry, createPackRegistry, createProjectGraph, createSkillRegistry, createToolRegistry, createTrustedSourceRegistry, detectProject, emptyInstallManifest, enabledCapabilityNameSchema, filterEffectiveHooks, filterEffectiveMcpServers, filterEffectiveTools, findProjectCapabilityOwners, formatAgentyxConfig, formatInstallManifest, formatSkillMarkdown, getCoreStatus, getTrustedSourceDefinition, hashContent, hookActivationLevelSchema, hookDefinitionSchema, hookEventSchema, hookManifestEntrySchema, hookNameSchema, hookReferenceSchema, inspectTrustedSource, installManifestEntrySchema, installManifestSchema, isExecutableOnPath, knownTrustedSourceRegistry, knownTrustedSources, loadAgentyxConfig, loadAgentyxProject, loadInstallManifest, manifestEntriesByPath, mcpCapabilityLevelSchema, mcpContextCostSchema, mcpEnvReferenceSchema, mcpManifestEntrySchema, mcpServerDefinitionSchema, mcpServerReferenceSchema, packCategorySchema, packDefinitionSchema, packNameSchema, parseAgentyxConfig, parseInstallManifest, parseSkillMarkdown, projectContextSchema, projectMetadataSchema, projectRelationSchema, projectRelationTypeSchema, recommendCapabilities, renderProjectGraphJson, renderProjectGraphMermaid, renderProjectGraphSvg, renderProjectGraphText, resolveAgentyxConfig, resolvePackHookReferences, resolvePackHooks, resolvePackMcpServerReferences, resolvePackMcpServers, resolvePackSkills, resolvePackToolReferences, resolvePackTools, resolvePacks, scanRepositorySize, skillDefinitionSchema, skillDirectorySchema, skillManifestEntrySchema, toolActivationLevelSchema, toolDefinitionSchema, toolNameSchema, toolReferenceSchema, trustedSourceDefinitionSchema, trustedSourceNameSchema, trustedSourcePathSchema, trustedSourceReferenceSchema, trustedSourceSkillSummarySchema };
|
|
1987
2513
|
|
|
1988
2514
|
//# sourceMappingURL=index.mjs.map
|