@agentyx/core 0.6.0 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -1,7 +1,7 @@
1
1
  import { z } from "zod";
2
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";
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
@@ -130,13 +130,15 @@ const MCP_CONTEXT_COSTS = [
130
130
  "high"
131
131
  ];
132
132
  const mcpContextCostSchema = z.enum(MCP_CONTEXT_COSTS);
133
+ const mcpRuntimeSchema = z.enum(["local", "may-download"]);
133
134
  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
135
  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
136
  const commonMcpServerDefinitionSchema = z.strictObject({
136
137
  name: mcpServerNameSchema.describe("Unique MCP server identifier."),
137
138
  description: z.string().trim().min(1, "MCP server descriptions must be non-empty strings."),
138
139
  transport: z.enum(["stdio", "http"]),
139
- contextCost: mcpContextCostSchema.describe("Qualitative context/tool-schema overhead classification; not token accounting.").optional()
140
+ contextCost: mcpContextCostSchema.describe("Qualitative context/tool-schema overhead classification; not token accounting.").optional(),
141
+ runtime: mcpRuntimeSchema.describe("Whether launching this server can fetch a package on first use; see MCP_RUNTIMES.").optional()
140
142
  });
141
143
  const explicitMcpServerReferenceSchema = z.strictObject({
142
144
  name: mcpServerNameSchema.describe("MCP server identifier."),
@@ -1116,6 +1118,7 @@ const builtInHookRegistry = createHookRegistry([{
1116
1118
  event: "SessionStart",
1117
1119
  command: "npx",
1118
1120
  args: [
1121
+ "--no-install",
1119
1122
  "agentyx",
1120
1123
  "doctor",
1121
1124
  "--hook"
@@ -1245,7 +1248,8 @@ function createMcpServerRegistry(sources) {
1245
1248
  name: server.name,
1246
1249
  description: server.description,
1247
1250
  transport: server.transport,
1248
- contextCost: server.contextCost
1251
+ contextCost: server.contextCost,
1252
+ runtime: server.runtime
1249
1253
  };
1250
1254
  }),
1251
1255
  get
@@ -1259,6 +1263,7 @@ const builtInMcpServerRegistry = createMcpServerRegistry([
1259
1263
  description: "Fetch up-to-date library documentation from Context7.",
1260
1264
  transport: "http",
1261
1265
  contextCost: "medium",
1266
+ runtime: "local",
1262
1267
  url: "https://mcp.context7.com/mcp"
1263
1268
  })
1264
1269
  },
@@ -1269,8 +1274,9 @@ const builtInMcpServerRegistry = createMcpServerRegistry([
1269
1274
  description: "Automate and inspect browsers through Playwright MCP.",
1270
1275
  transport: "stdio",
1271
1276
  contextCost: "high",
1277
+ runtime: "may-download",
1272
1278
  command: "npx",
1273
- args: ["@playwright/mcp@latest"]
1279
+ args: ["@playwright/mcp@0.0.81"]
1274
1280
  })
1275
1281
  },
1276
1282
  {
@@ -1280,6 +1286,7 @@ const builtInMcpServerRegistry = createMcpServerRegistry([
1280
1286
  description: "Structural code-intelligence MCP backed by a persistent code knowledge graph.",
1281
1287
  transport: "stdio",
1282
1288
  contextCost: "high",
1289
+ runtime: "local",
1283
1290
  command: "codebase-memory-mcp"
1284
1291
  })
1285
1292
  },
@@ -1290,6 +1297,7 @@ const builtInMcpServerRegistry = createMcpServerRegistry([
1290
1297
  description: "Read repositories, issues, pull requests and workflow runs on GitHub.",
1291
1298
  transport: "http",
1292
1299
  contextCost: "high",
1300
+ runtime: "local",
1293
1301
  url: "https://api.githubcopilot.com/mcp/"
1294
1302
  })
1295
1303
  },
@@ -1300,6 +1308,7 @@ const builtInMcpServerRegistry = createMcpServerRegistry([
1300
1308
  description: "Inspect production issues, events and stack traces recorded by Sentry.",
1301
1309
  transport: "http",
1302
1310
  contextCost: "medium",
1311
+ runtime: "local",
1303
1312
  url: "https://mcp.sentry.dev/mcp"
1304
1313
  })
1305
1314
  },
@@ -1310,8 +1319,9 @@ const builtInMcpServerRegistry = createMcpServerRegistry([
1310
1319
  description: "Record performance traces and inspect pages through Chrome DevTools.",
1311
1320
  transport: "stdio",
1312
1321
  contextCost: "high",
1322
+ runtime: "may-download",
1313
1323
  command: "npx",
1314
- args: ["-y", "chrome-devtools-mcp@latest"]
1324
+ args: ["-y", "chrome-devtools-mcp@1.9.0"]
1315
1325
  })
1316
1326
  },
1317
1327
  {
@@ -1321,8 +1331,9 @@ const builtInMcpServerRegistry = createMcpServerRegistry([
1321
1331
  description: "Inspect and query Supabase project schemas, tables and logs.",
1322
1332
  transport: "stdio",
1323
1333
  contextCost: "high",
1334
+ runtime: "may-download",
1324
1335
  command: "npx",
1325
- args: ["-y", "@supabase/mcp-server-supabase@latest"],
1336
+ args: ["-y", "@supabase/mcp-server-supabase@0.13.0"],
1326
1337
  env: { SUPABASE_ACCESS_TOKEN: { fromEnv: "SUPABASE_ACCESS_TOKEN" } }
1327
1338
  })
1328
1339
  }
@@ -1740,13 +1751,92 @@ function getCoreStatus() {
1740
1751
  return "ready";
1741
1752
  }
1742
1753
  //#endregion
1743
- //#region src/project/detector.ts
1754
+ //#region src/project/executable.ts
1755
+ /** Whether `command` resolves to an executable file, via `PATH` unless it already contains a `/`. */
1756
+ async function isExecutableOnPath(command) {
1757
+ if (command.includes("/")) return isFileExecutable(command);
1758
+ for (const directory of (process.env.PATH ?? "").split(delimiter)) {
1759
+ if (directory.length === 0) continue;
1760
+ if (await isFileExecutable(`${directory}/${command}`)) return true;
1761
+ }
1762
+ return false;
1763
+ }
1764
+ async function isFileExecutable(path) {
1765
+ try {
1766
+ await access(path, constants.X_OK);
1767
+ return true;
1768
+ } catch {
1769
+ return false;
1770
+ }
1771
+ }
1772
+ //#endregion
1773
+ //#region src/project/signals.ts
1744
1774
  const PACKAGE_MANAGERS = [
1745
1775
  "pnpm",
1746
1776
  "npm",
1747
1777
  "yarn",
1748
1778
  "bun"
1749
1779
  ];
1780
+ /** Package name patterns matched exactly, or as a `@scope/*` prefix. */
1781
+ const TEST_FRAMEWORK_PATTERNS = [
1782
+ "vitest",
1783
+ "jest",
1784
+ "@jest/*",
1785
+ "@playwright/test",
1786
+ "cypress",
1787
+ "karma"
1788
+ ];
1789
+ const BROWSER_TESTING_PATTERNS = ["@playwright/test", "cypress"];
1790
+ const OBSERVABILITY_PATTERNS = ["@sentry/*", "@opentelemetry/*"];
1791
+ const DATA_TOOLING_PATTERNS = [
1792
+ "@supabase/*",
1793
+ "drizzle-orm",
1794
+ "prisma",
1795
+ "@prisma/client",
1796
+ "typeorm",
1797
+ "sequelize"
1798
+ ];
1799
+ const ACCESSIBILITY_PATTERNS = [
1800
+ "axe-core",
1801
+ "@axe-core/*",
1802
+ "pa11y"
1803
+ ];
1804
+ const DEPENDENCY_FIELDS = [
1805
+ "dependencies",
1806
+ "devDependencies",
1807
+ "peerDependencies",
1808
+ "optionalDependencies"
1809
+ ];
1810
+ const CONTAINER_IGNORED = /* @__PURE__ */ new Set([
1811
+ "node_modules",
1812
+ ".git",
1813
+ "dist",
1814
+ "build",
1815
+ "coverage"
1816
+ ]);
1817
+ /** Extensions counted as source files by the bounded repository-size scan. */
1818
+ const SOURCE_FILE_EXTENSIONS = /* @__PURE__ */ new Set([
1819
+ ".ts",
1820
+ ".tsx",
1821
+ ".js",
1822
+ ".jsx",
1823
+ ".mjs",
1824
+ ".cjs",
1825
+ ".vue",
1826
+ ".svelte",
1827
+ ".py",
1828
+ ".go",
1829
+ ".rs",
1830
+ ".java",
1831
+ ".kt",
1832
+ ".rb",
1833
+ ".php",
1834
+ ".css",
1835
+ ".scss",
1836
+ ".html"
1837
+ ]);
1838
+ /** Maximum directory entries visited by `scanRepositorySize`, regardless of match. */
1839
+ const REPOSITORY_SCAN_ENTRY_LIMIT = 5e3;
1750
1840
  const lockfileManagers = /* @__PURE__ */ new Map([
1751
1841
  ["pnpm-lock.yaml", "pnpm"],
1752
1842
  ["package-lock.json", "npm"],
@@ -1754,52 +1844,141 @@ const lockfileManagers = /* @__PURE__ */ new Map([
1754
1844
  ["bun.lock", "bun"],
1755
1845
  ["bun.lockb", "bun"]
1756
1846
  ]);
1757
- async function detectProject(projectDir) {
1847
+ /** Gathers every project signal with a bounded, cheap set of reads. */
1848
+ async function collectProjectSignals(projectDir) {
1758
1849
  const packageJsonPath = join(projectDir, "package.json");
1759
- const packageJson = await readPackageJson(packageJsonPath);
1760
- const packageManager = await detectPackageManager(projectDir, packageJson.data);
1761
- const detectedPacks = await detectPacks(projectDir, packageJson.data);
1850
+ const packageJsonResult = await readPackageJson(packageJsonPath);
1851
+ const packageJson = packageJsonResult.data;
1852
+ const [packageManager, containers, ci, monorepo, repositorySize, tsconfig, rtkAvailable] = await Promise.all([
1853
+ detectPackageManager(projectDir, packageJson),
1854
+ detectContainerFiles(projectDir),
1855
+ detectCiFiles(projectDir),
1856
+ detectMonorepo(projectDir, packageJson),
1857
+ scanRepositorySize(projectDir),
1858
+ exists(join(projectDir, "tsconfig.json")),
1859
+ isExecutableOnPath(builtInToolRegistry.get("rtk").command)
1860
+ ]);
1762
1861
  return {
1763
1862
  projectDir,
1764
1863
  packageJson: {
1765
- present: packageJson.present,
1766
- valid: packageJson.valid,
1864
+ present: packageJsonResult.present,
1865
+ valid: packageJsonResult.valid,
1767
1866
  path: packageJsonPath,
1768
- name: getString(packageJson.data?.name),
1769
- packageManager: getString(packageJson.data?.packageManager),
1770
- error: packageJson.error
1867
+ name: getString(packageJson?.name),
1868
+ packageManager: getString(packageJson?.packageManager),
1869
+ error: packageJsonResult.error
1771
1870
  },
1772
1871
  packageManager,
1773
- detectedPacks,
1774
- recommendedPacks: recommendPacks(detectedPacks)
1872
+ typescript: {
1873
+ dependency: findDependency(packageJson, "typescript"),
1874
+ tsconfig
1875
+ },
1876
+ angular: findDependency(packageJson, "@angular/core"),
1877
+ testFrameworks: findAnyDependency(packageJson, TEST_FRAMEWORK_PATTERNS),
1878
+ browserTesting: findAnyDependency(packageJson, BROWSER_TESTING_PATTERNS),
1879
+ observability: findAnyDependency(packageJson, OBSERVABILITY_PATTERNS),
1880
+ dataTooling: findAnyDependency(packageJson, DATA_TOOLING_PATTERNS),
1881
+ accessibilityTooling: findAnyDependency(packageJson, ACCESSIBILITY_PATTERNS),
1882
+ containers,
1883
+ ci,
1884
+ monorepo,
1885
+ repositorySize,
1886
+ localTools: { rtkAvailable }
1775
1887
  };
1776
1888
  }
1777
- function buildAgentyxConfig(input) {
1889
+ /**
1890
+ * Counts source-like files under a bounded traversal, ignoring dependency, VCS and build output
1891
+ * directories. Stops after visiting `entryLimit` directory entries — cheap even on a monorepo — and
1892
+ * reports `capped` so callers never mistake a partial scan for a complete one.
1893
+ */
1894
+ async function scanRepositorySize(projectDir, options = {}) {
1895
+ const entryLimit = options.entryLimit ?? 5e3;
1896
+ const directories = [projectDir];
1897
+ let filesScanned = 0;
1898
+ let entriesVisited = 0;
1899
+ let capped = false;
1900
+ while (directories.length > 0 && !capped) {
1901
+ const directory = directories.shift();
1902
+ if (directory === void 0) break;
1903
+ let entries;
1904
+ try {
1905
+ entries = await readdir(directory, { withFileTypes: true });
1906
+ } catch {
1907
+ continue;
1908
+ }
1909
+ for (const entry of entries) {
1910
+ if (entriesVisited >= entryLimit) {
1911
+ capped = true;
1912
+ break;
1913
+ }
1914
+ entriesVisited += 1;
1915
+ if (entry.isDirectory()) {
1916
+ if (entry.name.startsWith(".") || CONTAINER_IGNORED.has(entry.name)) continue;
1917
+ directories.push(join(directory, entry.name));
1918
+ } else if (entry.isFile() && SOURCE_FILE_EXTENSIONS.has(extname(entry.name))) filesScanned += 1;
1919
+ }
1920
+ }
1778
1921
  return {
1779
- packs: [...input.packs],
1780
- enable: [...input.enable ?? []],
1781
- targets: [...input.targets]
1922
+ filesScanned,
1923
+ capped
1782
1924
  };
1783
1925
  }
1784
- function formatAgentyxConfig(config) {
1785
- return `${JSON.stringify(config, null, 2)}\n`;
1926
+ function findDependency(packageJson, pattern) {
1927
+ for (const field of DEPENDENCY_FIELDS) {
1928
+ const value = packageJson?.[field];
1929
+ if (!isRecord(value)) continue;
1930
+ if (pattern.endsWith("/*")) {
1931
+ const prefix = pattern.slice(0, -1);
1932
+ const name = Object.keys(value).find((key) => key.startsWith(prefix));
1933
+ if (name !== void 0) return {
1934
+ dependency: name,
1935
+ field
1936
+ };
1937
+ continue;
1938
+ }
1939
+ if (pattern in value) return {
1940
+ dependency: pattern,
1941
+ field
1942
+ };
1943
+ }
1786
1944
  }
1787
- function recommendPacks(detectedPacks) {
1788
- if (detectedPacks.includes("angular")) return [
1789
- "technical",
1790
- "typescript",
1791
- "angular"
1792
- ];
1793
- if (detectedPacks.includes("typescript")) return ["technical", "typescript"];
1794
- return ["technical"];
1945
+ function findAnyDependency(packageJson, patterns) {
1946
+ const matches = [];
1947
+ const seen = /* @__PURE__ */ new Set();
1948
+ for (const pattern of patterns) {
1949
+ const match = findDependency(packageJson, pattern);
1950
+ if (match !== void 0 && !seen.has(match.dependency)) {
1951
+ seen.add(match.dependency);
1952
+ matches.push(match);
1953
+ }
1954
+ }
1955
+ return matches;
1956
+ }
1957
+ async function detectContainerFiles(projectDir) {
1958
+ const [dockerfile, dockerCompose, compose] = await Promise.all([
1959
+ exists(join(projectDir, "Dockerfile")),
1960
+ exists(join(projectDir, "docker-compose.yml")),
1961
+ exists(join(projectDir, "compose.yml"))
1962
+ ]);
1963
+ return {
1964
+ dockerfile,
1965
+ compose: dockerCompose || compose
1966
+ };
1795
1967
  }
1796
- async function detectPacks(projectDir, packageJson) {
1797
- const packs = [];
1798
- const dependencies = collectDependencies(packageJson);
1799
- const hasAngular = dependencies.has("@angular/core");
1800
- if (dependencies.has("typescript") || await exists(join(projectDir, "tsconfig.json")) || hasAngular) packs.push("typescript");
1801
- if (hasAngular) packs.push("angular");
1802
- return packs;
1968
+ async function detectCiFiles(projectDir) {
1969
+ return { githubActions: await exists(join(projectDir, ".github", "workflows")) };
1970
+ }
1971
+ async function detectMonorepo(projectDir, packageJson) {
1972
+ const markers = [];
1973
+ if (await exists(join(projectDir, "pnpm-workspace.yaml"))) markers.push("pnpm-workspace.yaml");
1974
+ if (await exists(join(projectDir, "turbo.json"))) markers.push("turbo.json");
1975
+ if (await exists(join(projectDir, "nx.json"))) markers.push("nx.json");
1976
+ const workspaces = packageJson?.workspaces;
1977
+ if (Array.isArray(workspaces) || isRecord(workspaces)) markers.push("package.json workspaces");
1978
+ return {
1979
+ detected: markers.length > 0,
1980
+ markers
1981
+ };
1803
1982
  }
1804
1983
  async function detectPackageManager(projectDir, packageJson) {
1805
1984
  const lockfiles = [];
@@ -1862,16 +2041,6 @@ async function readPackageJson(path) {
1862
2041
  };
1863
2042
  }
1864
2043
  }
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
2044
  function parsePackageManagerName(value) {
1876
2045
  const name = value?.split("@")[0];
1877
2046
  return PACKAGE_MANAGERS.includes(name) ? name : void 0;
@@ -1895,6 +2064,128 @@ function isNotFound(cause) {
1895
2064
  return cause instanceof Error && cause.code === "ENOENT";
1896
2065
  }
1897
2066
  //#endregion
2067
+ //#region src/project/detector.ts
2068
+ /**
2069
+ * Factual project detection only. It gathers `ProjectSignals` and never decides which packs or
2070
+ * capabilities fit — that policy lives in `recommendCapabilities`, kept separate so each half stays
2071
+ * independently testable.
2072
+ */
2073
+ async function detectProject(projectDir) {
2074
+ const signals = await collectProjectSignals(projectDir);
2075
+ return {
2076
+ projectDir,
2077
+ packageJson: signals.packageJson,
2078
+ packageManager: signals.packageManager,
2079
+ signals
2080
+ };
2081
+ }
2082
+ function buildAgentyxConfig(input) {
2083
+ return {
2084
+ packs: [...input.packs],
2085
+ enable: [...input.enable ?? []],
2086
+ targets: [...input.targets]
2087
+ };
2088
+ }
2089
+ function formatAgentyxConfig(config) {
2090
+ return `${JSON.stringify(config, null, 2)}\n`;
2091
+ }
2092
+ //#endregion
2093
+ //#region src/project/recommend.ts
2094
+ const RECOMMENDATION_CONFIDENCES = ["high", "medium"];
2095
+ /**
2096
+ * Heuristic thresholds for the `codebase-memory` capability suggestion, in scanned source files.
2097
+ * These are approximate signals, not exact token accounting, and a monorepo is treated as already
2098
+ * complex enough to lower the bar.
2099
+ */
2100
+ const CODEBASE_MEMORY_FILE_THRESHOLD = 2e3;
2101
+ const CODEBASE_MEMORY_MONOREPO_FILE_THRESHOLD = 800;
2102
+ /**
2103
+ * Applies conservative, deterministic policy on top of `ProjectSignals`. This is the only place
2104
+ * that turns an observed fact into a product suggestion — `ProjectSignals` stays fact-only so it
2105
+ * can be tested independently of pack names or capability policy.
2106
+ *
2107
+ * Never enables anything and never touches the filesystem: the caller decides what, if anything,
2108
+ * to do with the result.
2109
+ */
2110
+ function recommendCapabilities(signals, options = {}) {
2111
+ const packs = recommendPacks(signals);
2112
+ const configuredPacks = options.configuredPacks ?? [];
2113
+ return {
2114
+ packs,
2115
+ capabilities: recommendOptionalCapabilities(signals, /* @__PURE__ */ new Set([...packs.map((pack) => pack.name), ...configuredPacks]), configuredPacks)
2116
+ };
2117
+ }
2118
+ function recommendPacks(signals) {
2119
+ const packs = [];
2120
+ if (signals.packageJson.present) packs.push(packRecommendation("technical", "high", ["package.json detected — baseline engineering practices apply to any software project."]));
2121
+ const typescriptReasons = [...signals.typescript.dependency !== void 0 ? [dependencyReason("TypeScript", signals.typescript.dependency)] : [], ...signals.typescript.tsconfig ? ["tsconfig.json detected."] : []];
2122
+ if (typescriptReasons.length > 0) packs.push(packRecommendation("typescript", "high", typescriptReasons));
2123
+ if (signals.angular !== void 0) packs.push(packRecommendation("angular", "high", [dependencyReason("@angular/core", signals.angular)]));
2124
+ const testingMatches = dedupeMatches([...signals.testFrameworks, ...signals.browserTesting]);
2125
+ if (testingMatches.length > 0) packs.push(packRecommendation("testing", "high", testingMatches.map((match) => dependencyReason(match.dependency, match))));
2126
+ const devopsReasons = [
2127
+ ...signals.containers.dockerfile ? ["Dockerfile detected."] : [],
2128
+ ...signals.containers.compose ? ["Container compose file detected."] : [],
2129
+ ...signals.ci.githubActions ? ["GitHub Actions workflow detected (.github/workflows)."] : []
2130
+ ];
2131
+ if (devopsReasons.length > 0) packs.push(packRecommendation("devops", "high", devopsReasons));
2132
+ if (signals.observability.length > 0) packs.push(packRecommendation("observability", "high", signals.observability.map((match) => dependencyReason(match.dependency, match))));
2133
+ if (signals.dataTooling.length > 0) packs.push(packRecommendation("data", "high", signals.dataTooling.map((match) => dependencyReason(match.dependency, match))));
2134
+ if (signals.accessibilityTooling.length > 0) packs.push(packRecommendation("accessibility", "high", signals.accessibilityTooling.map((match) => dependencyReason(match.dependency, match))));
2135
+ return packs;
2136
+ }
2137
+ function recommendOptionalCapabilities(signals, activePacks, configuredPacks) {
2138
+ const capabilities = [];
2139
+ const playwrightMatch = signals.browserTesting.find((match) => match.dependency === "@playwright/test");
2140
+ 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."]));
2141
+ if (signals.observability.find((match) => match.dependency.startsWith("@sentry/")) !== void 0) capabilities.push(capabilityRecommendation("sentry", "high", ["Sentry is already configured as a dependency."]));
2142
+ if (signals.dataTooling.find((match) => match.dependency.startsWith("@supabase/")) !== void 0) capabilities.push(capabilityRecommendation("supabase", "high", ["Supabase is already configured as a dependency."]));
2143
+ if (configuredPacks.includes("efficiency") && signals.localTools.rtkAvailable) capabilities.push(capabilityRecommendation("rtk", "high", ["RTK is already installed locally."]));
2144
+ const sizeThreshold = signals.monorepo.detected ? 800 : CODEBASE_MEMORY_FILE_THRESHOLD;
2145
+ if (signals.repositorySize.filesScanned >= sizeThreshold || signals.repositorySize.capped) capabilities.push(capabilityRecommendation("codebase-memory", "medium", [repositorySizeReason(signals)]));
2146
+ return capabilities;
2147
+ }
2148
+ function repositorySizeReason(signals) {
2149
+ const { filesScanned, capped } = signals.repositorySize;
2150
+ const scope = signals.monorepo.detected ? " across this monorepo" : "";
2151
+ 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.`;
2152
+ }
2153
+ function packRecommendation(name, confidence, reasons) {
2154
+ return {
2155
+ kind: "pack",
2156
+ name,
2157
+ confidence,
2158
+ reasons
2159
+ };
2160
+ }
2161
+ /** Provenance comes from the pack registry itself — never a second, hand-maintained capability map. */
2162
+ function capabilityRecommendation(name, confidence, reasons) {
2163
+ return {
2164
+ kind: "capability",
2165
+ name,
2166
+ confidence,
2167
+ reasons,
2168
+ contributedBy: packsDeclaringOptionalCapability(name)
2169
+ };
2170
+ }
2171
+ function packsDeclaringOptionalCapability(name) {
2172
+ const packs = [];
2173
+ 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);
2174
+ return packs;
2175
+ }
2176
+ function dependencyReason(label, match) {
2177
+ return label === match.dependency ? `Detected ${match.dependency} in ${match.field}.` : `Detected ${label} (${match.dependency}) in ${match.field}.`;
2178
+ }
2179
+ function dedupeMatches(matches) {
2180
+ const seen = /* @__PURE__ */ new Set();
2181
+ const result = [];
2182
+ for (const match of matches) if (!seen.has(match.dependency)) {
2183
+ seen.add(match.dependency);
2184
+ result.push(match);
2185
+ }
2186
+ return result;
2187
+ }
2188
+ //#endregion
1898
2189
  //#region src/source/inspection.ts
1899
2190
  async function inspectTrustedSource(projectPath, reference) {
1900
2191
  const definition = getTrustedSourceDefinition(reference);
@@ -1983,6 +2274,6 @@ function trustedSourceLoadError(sourceName, reason, cause) {
1983
2274
  return new TrustedSourceLoadError(sourceName, reason, cause instanceof Error ? { cause } : void 0);
1984
2275
  }
1985
2276
  //#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 };
2277
+ 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, 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, createSkillRegistry, createToolRegistry, createTrustedSourceRegistry, detectProject, emptyInstallManifest, enabledCapabilityNameSchema, filterEffectiveHooks, filterEffectiveMcpServers, filterEffectiveTools, 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, recommendCapabilities, resolveAgentyxConfig, resolvePackHookReferences, resolvePackHooks, resolvePackMcpServerReferences, resolvePackMcpServers, resolvePackSkills, resolvePackToolReferences, resolvePackTools, resolvePacks, scanRepositorySize, skillDefinitionSchema, skillDirectorySchema, skillManifestEntrySchema, toolActivationLevelSchema, toolDefinitionSchema, toolNameSchema, toolReferenceSchema, trustedSourceDefinitionSchema, trustedSourceNameSchema, trustedSourcePathSchema, trustedSourceReferenceSchema, trustedSourceSkillSummarySchema };
1987
2278
 
1988
2279
  //# sourceMappingURL=index.mjs.map