@docyrus/docyrus 0.0.29 → 0.0.31

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 (22) hide show
  1. package/main.js +3 -3
  2. package/main.js.map +1 -1
  3. package/package.json +3 -3
  4. package/resources/pi-agent/skills/docyrus-api-dev/SKILL.md +161 -0
  5. package/resources/pi-agent/skills/docyrus-api-dev/references/api-client.md +349 -0
  6. package/resources/pi-agent/skills/docyrus-api-dev/references/authentication.md +299 -0
  7. package/resources/pi-agent/skills/docyrus-api-dev/references/data-source-query-guide.md +2063 -0
  8. package/resources/pi-agent/skills/docyrus-api-dev/references/formula-design-guide-llm.md +312 -0
  9. package/resources/pi-agent/skills/docyrus-api-dev/references/query-and-formulas.md +592 -0
  10. package/resources/pi-agent/skills/docyrus-app-dev-react/SKILL.md +334 -0
  11. package/resources/pi-agent/skills/docyrus-app-dev-react/references/README.md +28 -0
  12. package/resources/pi-agent/skills/docyrus-app-dev-react/references/api-client-and-auth.md +326 -0
  13. package/resources/pi-agent/skills/docyrus-app-dev-react/references/collections-and-patterns.md +352 -0
  14. package/resources/pi-agent/skills/docyrus-app-dev-react/references/component-selection-guide.md +602 -0
  15. package/resources/pi-agent/skills/docyrus-app-dev-react/references/icon-usage-guide.md +463 -0
  16. package/resources/pi-agent/skills/docyrus-app-dev-react/references/preferred-components-catalog.md +242 -0
  17. package/resources/pi-agent/skills/docyrus-platform/SKILL.md +2 -2
  18. package/resources/pi-agent/skills/docyrus-platform/references/auth-and-multi-tenancy.md +9 -1
  19. package/resources/pi-agent/skills/docyrus-platform/references/developer-tools.md +3 -2
  20. package/server-loader.js +780 -56
  21. package/server-loader.js.map +4 -4
  22. package/resources/pi-agent/extensions/multi-edit.ts +0 -835
package/server-loader.js CHANGED
@@ -544,7 +544,7 @@ var init_dist = __esm({
544
544
  });
545
545
  if (!chunk) {
546
546
  if (i === 1) {
547
- await new Promise((resolve2) => setTimeout(resolve2));
547
+ await new Promise((resolve3) => setTimeout(resolve3));
548
548
  maxReadCount = 3;
549
549
  continue;
550
550
  }
@@ -692,7 +692,7 @@ var init_dist = __esm({
692
692
  // src/server/server-loader.ts
693
693
  var import_node_fs = require("node:fs");
694
694
  var import_node_url = require("node:url");
695
- var import_node_path4 = require("node:path");
695
+ var import_node_path6 = require("node:path");
696
696
  var import_picocolors2 = __toESM(require_picocolors());
697
697
 
698
698
  // src/agent/envStore.ts
@@ -795,9 +795,9 @@ var AgentEnvStore = class {
795
795
 
796
796
  // src/server/agentServer.ts
797
797
  var import_node_child_process = require("node:child_process");
798
- var import_node_crypto2 = require("node:crypto");
799
- var import_promises3 = require("node:fs/promises");
800
- var import_node_path3 = require("node:path");
798
+ var import_node_crypto3 = require("node:crypto");
799
+ var import_promises5 = require("node:fs/promises");
800
+ var import_node_path5 = require("node:path");
801
801
 
802
802
  // ../../node_modules/.pnpm/hono@4.12.8/node_modules/hono/dist/compose.js
803
803
  var compose = (middleware, onError, onNotFound) => {
@@ -4050,8 +4050,8 @@ var OAuthFlowManager = class {
4050
4050
  if (state.currentStep) {
4051
4051
  return Promise.resolve(state.currentStep);
4052
4052
  }
4053
- return new Promise((resolve2) => {
4054
- state.waitingForStep = { resolve: resolve2 };
4053
+ return new Promise((resolve3) => {
4054
+ state.waitingForStep = { resolve: resolve3 };
4055
4055
  });
4056
4056
  }
4057
4057
  async start(params) {
@@ -4072,8 +4072,8 @@ var OAuthFlowManager = class {
4072
4072
  onPrompt: async (prompt) => {
4073
4073
  const step = buildPromptStep({ state, type: "prompt", prompt });
4074
4074
  this.emitStep(state, step);
4075
- return await new Promise((resolve2) => {
4076
- state.pendingInput = { type: "prompt", resolve: resolve2 };
4075
+ return await new Promise((resolve3) => {
4076
+ state.pendingInput = { type: "prompt", resolve: resolve3 };
4077
4077
  });
4078
4078
  },
4079
4079
  onManualCodeInput: async () => {
@@ -4085,8 +4085,8 @@ var OAuthFlowManager = class {
4085
4085
  }
4086
4086
  });
4087
4087
  this.emitStep(state, step);
4088
- return await new Promise((resolve2) => {
4089
- state.pendingInput = { type: "manual-code", resolve: resolve2 };
4088
+ return await new Promise((resolve3) => {
4089
+ state.pendingInput = { type: "manual-code", resolve: resolve3 };
4090
4090
  });
4091
4091
  },
4092
4092
  onProgress: (_message) => {
@@ -4128,6 +4128,535 @@ var OAuthFlowManager = class {
4128
4128
  }
4129
4129
  };
4130
4130
 
4131
+ // src/server/mcpConfigService.ts
4132
+ var import_node_crypto2 = require("node:crypto");
4133
+ var import_promises3 = require("node:fs/promises");
4134
+ var import_node_os = require("node:os");
4135
+ var import_node_path3 = require("node:path");
4136
+ var PROJECT_CONFIG_NAME = ".pi/mcp.json";
4137
+ var IMPORT_PATHS = {
4138
+ "cursor": (0, import_node_path3.join)((0, import_node_os.homedir)(), ".cursor", "mcp.json"),
4139
+ "claude-code": (0, import_node_path3.join)((0, import_node_os.homedir)(), ".claude", "claude_desktop_config.json"),
4140
+ "claude-desktop": (0, import_node_path3.join)((0, import_node_os.homedir)(), "Library", "Application Support", "Claude", "claude_desktop_config.json"),
4141
+ "codex": (0, import_node_path3.join)((0, import_node_os.homedir)(), ".codex", "config.json"),
4142
+ "windsurf": (0, import_node_path3.join)((0, import_node_os.homedir)(), ".windsurf", "mcp.json"),
4143
+ "vscode": ".vscode/mcp.json"
4144
+ };
4145
+ function getMcpConfigPath(agentDir) {
4146
+ return (0, import_node_path3.join)(agentDir, "mcp.json");
4147
+ }
4148
+ function getMcpCachePath(agentDir) {
4149
+ return (0, import_node_path3.join)(agentDir, "mcp-cache.json");
4150
+ }
4151
+ var SERVER_NAME_REGEX = /^[a-zA-Z0-9][a-zA-Z0-9_-]*$/;
4152
+ function validateServerName(name) {
4153
+ if (!name || typeof name !== "string") {
4154
+ return { ok: false, error: "Server name is required" };
4155
+ }
4156
+ if (!SERVER_NAME_REGEX.test(name)) {
4157
+ return { ok: false, error: "Server name must start with alphanumeric and contain only alphanumeric, hyphens, and underscores" };
4158
+ }
4159
+ return { ok: true };
4160
+ }
4161
+ function validateServerEntry(entry) {
4162
+ if (!entry || typeof entry !== "object" || Array.isArray(entry)) {
4163
+ return { ok: false, error: "Server entry must be an object" };
4164
+ }
4165
+ const obj = entry;
4166
+ const hasCommand = typeof obj.command === "string" && obj.command.length > 0;
4167
+ const hasUrl = typeof obj.url === "string" && obj.url.length > 0;
4168
+ if (!hasCommand && !hasUrl) {
4169
+ return { ok: false, error: "Server entry must have either 'command' (stdio) or 'url' (http)" };
4170
+ }
4171
+ if (obj.lifecycle !== void 0) {
4172
+ if (!["keep-alive", "lazy", "eager"].includes(obj.lifecycle)) {
4173
+ return { ok: false, error: "lifecycle must be 'keep-alive', 'lazy', or 'eager'" };
4174
+ }
4175
+ }
4176
+ if (obj.auth !== void 0) {
4177
+ if (!["oauth", "bearer"].includes(obj.auth)) {
4178
+ return { ok: false, error: "auth must be 'oauth' or 'bearer'" };
4179
+ }
4180
+ }
4181
+ if (obj.args !== void 0) {
4182
+ if (!Array.isArray(obj.args) || !obj.args.every((a) => typeof a === "string")) {
4183
+ return { ok: false, error: "args must be an array of strings" };
4184
+ }
4185
+ }
4186
+ if (obj.env !== void 0) {
4187
+ if (!isStringRecord(obj.env)) {
4188
+ return { ok: false, error: "env must be a Record<string, string>" };
4189
+ }
4190
+ }
4191
+ if (obj.headers !== void 0) {
4192
+ if (!isStringRecord(obj.headers)) {
4193
+ return { ok: false, error: "headers must be a Record<string, string>" };
4194
+ }
4195
+ }
4196
+ return { ok: true, value: obj };
4197
+ }
4198
+ function isStringRecord(value) {
4199
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
4200
+ return false;
4201
+ }
4202
+ return Object.values(value).every((v) => typeof v === "string");
4203
+ }
4204
+ function detectTransport(entry) {
4205
+ return entry.url ? "http" : "stdio";
4206
+ }
4207
+ function redactSensitiveFields(entry) {
4208
+ const copy = { ...entry };
4209
+ if (copy.bearerToken) {
4210
+ copy.bearerToken = copy.bearerToken.slice(0, 4) + "****";
4211
+ }
4212
+ if (copy.env) {
4213
+ const redactedEnv = { ...copy.env };
4214
+ for (const key of Object.keys(redactedEnv)) {
4215
+ const lower = key.toLowerCase();
4216
+ if (lower.includes("token") || lower.includes("secret") || lower.includes("key") || lower.includes("password")) {
4217
+ const val = redactedEnv[key];
4218
+ redactedEnv[key] = val.length > 4 ? val.slice(0, 4) + "****" : "****";
4219
+ }
4220
+ }
4221
+ copy.env = redactedEnv;
4222
+ }
4223
+ return copy;
4224
+ }
4225
+ function parseConfigFile(raw2) {
4226
+ if (!raw2 || typeof raw2 !== "object") {
4227
+ return { mcpServers: {} };
4228
+ }
4229
+ const obj = raw2;
4230
+ const servers = obj.mcpServers ?? obj["mcp-servers"] ?? {};
4231
+ if (typeof servers !== "object" || servers === null || Array.isArray(servers)) {
4232
+ return { mcpServers: {} };
4233
+ }
4234
+ return {
4235
+ mcpServers: servers,
4236
+ imports: Array.isArray(obj.imports) ? obj.imports : void 0,
4237
+ settings: obj.settings
4238
+ };
4239
+ }
4240
+ function extractServers(config, kind) {
4241
+ if (!config || typeof config !== "object") {
4242
+ return {};
4243
+ }
4244
+ const obj = config;
4245
+ let servers;
4246
+ switch (kind) {
4247
+ case "claude-desktop":
4248
+ case "claude-code":
4249
+ case "codex":
4250
+ servers = obj.mcpServers;
4251
+ break;
4252
+ case "cursor":
4253
+ case "windsurf":
4254
+ case "vscode":
4255
+ servers = obj.mcpServers ?? obj["mcp-servers"];
4256
+ break;
4257
+ default:
4258
+ return {};
4259
+ }
4260
+ if (!servers || typeof servers !== "object" || Array.isArray(servers)) {
4261
+ return {};
4262
+ }
4263
+ return servers;
4264
+ }
4265
+ async function readJsonFile(filePath) {
4266
+ try {
4267
+ const content = await (0, import_promises3.readFile)(filePath, "utf-8");
4268
+ return JSON.parse(content);
4269
+ } catch {
4270
+ return null;
4271
+ }
4272
+ }
4273
+ async function loadMcpServerConfig(agentDir, cwd) {
4274
+ const configPath = getMcpConfigPath(agentDir);
4275
+ let config = { mcpServers: {} };
4276
+ const raw2 = await readJsonFile(configPath);
4277
+ if (raw2) {
4278
+ config = parseConfigFile(raw2);
4279
+ }
4280
+ if (config.imports?.length) {
4281
+ for (const importKind of config.imports) {
4282
+ const importPath = IMPORT_PATHS[importKind];
4283
+ if (!importPath) {
4284
+ continue;
4285
+ }
4286
+ const fullPath = importPath.startsWith(".") ? (0, import_node_path3.resolve)(cwd, importPath) : importPath;
4287
+ const imported = await readJsonFile(fullPath);
4288
+ if (!imported) {
4289
+ continue;
4290
+ }
4291
+ const servers = extractServers(imported, importKind);
4292
+ for (const [name, def] of Object.entries(servers)) {
4293
+ if (!config.mcpServers[name]) {
4294
+ config.mcpServers[name] = def;
4295
+ }
4296
+ }
4297
+ }
4298
+ }
4299
+ const projectPath = (0, import_node_path3.resolve)(cwd, PROJECT_CONFIG_NAME);
4300
+ if (projectPath !== configPath) {
4301
+ const projectRaw = await readJsonFile(projectPath);
4302
+ if (projectRaw) {
4303
+ const validated = parseConfigFile(projectRaw);
4304
+ config.mcpServers = { ...config.mcpServers, ...validated.mcpServers };
4305
+ if (validated.settings) {
4306
+ config.settings = { ...config.settings, ...validated.settings };
4307
+ }
4308
+ }
4309
+ }
4310
+ return config;
4311
+ }
4312
+ var CACHE_VERSION = 1;
4313
+ var CACHE_MAX_AGE_MS = 7 * 24 * 60 * 60 * 1e3;
4314
+ async function loadMcpServerCache(agentDir) {
4315
+ const cachePath = getMcpCachePath(agentDir);
4316
+ const raw2 = await readJsonFile(cachePath);
4317
+ if (!raw2 || typeof raw2 !== "object") {
4318
+ return null;
4319
+ }
4320
+ const obj = raw2;
4321
+ if (obj.version !== CACHE_VERSION) {
4322
+ return null;
4323
+ }
4324
+ if (!obj.servers || typeof obj.servers !== "object") {
4325
+ return null;
4326
+ }
4327
+ return raw2;
4328
+ }
4329
+ function computeServerHash(definition) {
4330
+ const identity = {
4331
+ command: definition.command,
4332
+ args: definition.args,
4333
+ env: definition.env,
4334
+ cwd: definition.cwd,
4335
+ url: definition.url,
4336
+ headers: definition.headers,
4337
+ auth: definition.auth,
4338
+ bearerToken: definition.bearerToken,
4339
+ bearerTokenEnv: definition.bearerTokenEnv,
4340
+ exposeResources: definition.exposeResources
4341
+ };
4342
+ const normalized = stableStringify(identity);
4343
+ return (0, import_node_crypto2.createHash)("sha256").update(normalized).digest("hex");
4344
+ }
4345
+ function isServerCacheValid(entry, definition, maxAgeMs = CACHE_MAX_AGE_MS) {
4346
+ if (!entry || entry.configHash !== computeServerHash(definition)) {
4347
+ return false;
4348
+ }
4349
+ if (!entry.cachedAt || typeof entry.cachedAt !== "number") {
4350
+ return false;
4351
+ }
4352
+ if (maxAgeMs > 0 && Date.now() - entry.cachedAt > maxAgeMs) {
4353
+ return false;
4354
+ }
4355
+ return true;
4356
+ }
4357
+ function stableStringify(value) {
4358
+ if (value === null || value === void 0 || typeof value !== "object") {
4359
+ const serialized = JSON.stringify(value);
4360
+ return serialized === void 0 ? "undefined" : serialized;
4361
+ }
4362
+ if (Array.isArray(value)) {
4363
+ return `[${value.map((v) => stableStringify(v)).join(",")}]`;
4364
+ }
4365
+ const obj = value;
4366
+ const keys = Object.keys(obj).sort();
4367
+ return `{${keys.map((k) => `${JSON.stringify(k)}:${stableStringify(obj[k])}`).join(",")}}`;
4368
+ }
4369
+ async function getServerProvenance(agentDir, cwd) {
4370
+ const provenance = /* @__PURE__ */ new Map();
4371
+ const userPath = getMcpConfigPath(agentDir);
4372
+ const userRaw = await readJsonFile(userPath);
4373
+ const userConfig = userRaw ? parseConfigFile(userRaw) : { mcpServers: {} };
4374
+ for (const name of Object.keys(userConfig.mcpServers)) {
4375
+ provenance.set(name, { path: userPath, kind: "user" });
4376
+ }
4377
+ if (userConfig.imports?.length) {
4378
+ for (const importKind of userConfig.imports) {
4379
+ const importPath = IMPORT_PATHS[importKind];
4380
+ if (!importPath) {
4381
+ continue;
4382
+ }
4383
+ const fullPath = importPath.startsWith(".") ? (0, import_node_path3.resolve)(cwd, importPath) : importPath;
4384
+ const imported = await readJsonFile(fullPath);
4385
+ if (!imported) {
4386
+ continue;
4387
+ }
4388
+ const servers = extractServers(imported, importKind);
4389
+ for (const name of Object.keys(servers)) {
4390
+ if (!provenance.has(name)) {
4391
+ provenance.set(name, { path: userPath, kind: "import", importKind });
4392
+ }
4393
+ }
4394
+ }
4395
+ }
4396
+ const projectPath = (0, import_node_path3.resolve)(cwd, PROJECT_CONFIG_NAME);
4397
+ if (projectPath !== userPath) {
4398
+ const projectRaw = await readJsonFile(projectPath);
4399
+ if (projectRaw) {
4400
+ const projectConfig = parseConfigFile(projectRaw);
4401
+ for (const name of Object.keys(projectConfig.mcpServers)) {
4402
+ provenance.set(name, { path: projectPath, kind: "project" });
4403
+ }
4404
+ }
4405
+ }
4406
+ return provenance;
4407
+ }
4408
+ async function readUserConfig(agentDir) {
4409
+ const configPath = getMcpConfigPath(agentDir);
4410
+ let raw2 = {};
4411
+ const parsed = await readJsonFile(configPath);
4412
+ if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
4413
+ raw2 = parsed;
4414
+ }
4415
+ const config = parseConfigFile(raw2);
4416
+ return { raw: raw2, config };
4417
+ }
4418
+ async function writeUserConfig(agentDir, raw2) {
4419
+ const configPath = getMcpConfigPath(agentDir);
4420
+ await (0, import_promises3.mkdir)((0, import_node_path3.dirname)(configPath), { recursive: true });
4421
+ const tmpPath = `${configPath}.${process.pid}.tmp`;
4422
+ await (0, import_promises3.writeFile)(tmpPath, JSON.stringify(raw2, null, 2) + "\n", "utf-8");
4423
+ await (0, import_promises3.rename)(tmpPath, configPath);
4424
+ }
4425
+ async function addMcpServer(agentDir, name, entry) {
4426
+ const { raw: raw2, config } = await readUserConfig(agentDir);
4427
+ if (config.mcpServers[name]) {
4428
+ throw new Error(`Server "${name}" already exists`);
4429
+ }
4430
+ const servers = raw2.mcpServers ?? raw2["mcp-servers"] ?? {};
4431
+ const key = raw2["mcp-servers"] && !raw2.mcpServers ? "mcp-servers" : "mcpServers";
4432
+ servers[name] = entry;
4433
+ raw2[key] = servers;
4434
+ await writeUserConfig(agentDir, raw2);
4435
+ }
4436
+ async function updateMcpServer(agentDir, name, entry) {
4437
+ const { raw: raw2 } = await readUserConfig(agentDir);
4438
+ const servers = raw2.mcpServers ?? raw2["mcp-servers"] ?? {};
4439
+ const key = raw2["mcp-servers"] && !raw2.mcpServers ? "mcp-servers" : "mcpServers";
4440
+ if (!servers[name]) {
4441
+ throw new Error(`Server "${name}" not found in user config`);
4442
+ }
4443
+ servers[name] = entry;
4444
+ raw2[key] = servers;
4445
+ await writeUserConfig(agentDir, raw2);
4446
+ }
4447
+ async function removeMcpServer(agentDir, name) {
4448
+ const { raw: raw2 } = await readUserConfig(agentDir);
4449
+ const servers = raw2.mcpServers ?? raw2["mcp-servers"] ?? {};
4450
+ const key = raw2["mcp-servers"] && !raw2.mcpServers ? "mcp-servers" : "mcpServers";
4451
+ if (!servers[name]) {
4452
+ throw new Error(`Server "${name}" not found in user config`);
4453
+ }
4454
+ delete servers[name];
4455
+ raw2[key] = servers;
4456
+ await writeUserConfig(agentDir, raw2);
4457
+ }
4458
+ function getServerPrefix(serverName, mode) {
4459
+ if (mode === "none") {
4460
+ return "";
4461
+ }
4462
+ if (mode === "short") {
4463
+ let short = serverName.replace(/-?mcp$/i, "").replace(/-/g, "_");
4464
+ if (!short) {
4465
+ short = "mcp";
4466
+ }
4467
+ return short;
4468
+ }
4469
+ return serverName.replace(/-/g, "_");
4470
+ }
4471
+ function formatToolName(toolName, serverName, prefix) {
4472
+ const p = getServerPrefix(serverName, prefix);
4473
+ return p ? `${p}_${toolName}` : toolName;
4474
+ }
4475
+
4476
+ // src/server/skillsService.ts
4477
+ var import_promises4 = require("node:fs/promises");
4478
+ var import_node_path4 = require("node:path");
4479
+ function parseFrontmatter(content) {
4480
+ const frontmatter = {};
4481
+ if (!content.startsWith("---")) {
4482
+ return { frontmatter, body: content };
4483
+ }
4484
+ const endIndex = content.indexOf("\n---", 3);
4485
+ if (endIndex === -1) {
4486
+ return { frontmatter, body: content };
4487
+ }
4488
+ const fmBlock = content.slice(4, endIndex);
4489
+ for (const line of fmBlock.split("\n")) {
4490
+ const colonIndex = line.indexOf(":");
4491
+ if (colonIndex <= 0) {
4492
+ continue;
4493
+ }
4494
+ const key = line.slice(0, colonIndex).trim();
4495
+ const value = line.slice(colonIndex + 1).trim();
4496
+ if (key) {
4497
+ frontmatter[key] = value;
4498
+ }
4499
+ }
4500
+ const body = content.slice(endIndex + 4).trimStart();
4501
+ return { frontmatter, body };
4502
+ }
4503
+ async function dirExists(path) {
4504
+ try {
4505
+ const s = await (0, import_promises4.stat)(path);
4506
+ return s.isDirectory();
4507
+ } catch {
4508
+ return false;
4509
+ }
4510
+ }
4511
+ async function listSkills(agentDir) {
4512
+ const skillsDir = (0, import_node_path4.join)(agentDir, "skills");
4513
+ let entries;
4514
+ try {
4515
+ entries = await (0, import_promises4.readdir)(skillsDir, { withFileTypes: true });
4516
+ } catch {
4517
+ return [];
4518
+ }
4519
+ const skills = [];
4520
+ for (const entry of entries) {
4521
+ if (!entry.isDirectory()) {
4522
+ continue;
4523
+ }
4524
+ const skillDir = (0, import_node_path4.join)(skillsDir, entry.name);
4525
+ const skillMdPath = (0, import_node_path4.join)(skillDir, "SKILL.md");
4526
+ let content;
4527
+ try {
4528
+ content = await (0, import_promises4.readFile)(skillMdPath, "utf-8");
4529
+ } catch {
4530
+ continue;
4531
+ }
4532
+ const parsed = parseFrontmatter(content);
4533
+ const hasReferences = await dirExists((0, import_node_path4.join)(skillDir, "references"));
4534
+ skills.push({
4535
+ name: parsed.frontmatter.name || entry.name,
4536
+ description: parsed.frontmatter.description || null,
4537
+ directory: entry.name,
4538
+ hasReferences,
4539
+ userInvocable: parsed.frontmatter["user-invocable"] === "true",
4540
+ frontmatter: parsed.frontmatter
4541
+ });
4542
+ }
4543
+ return skills.sort((a, b) => a.name.localeCompare(b.name));
4544
+ }
4545
+ async function getSkillDetail(agentDir, skillName) {
4546
+ const skillsDir = (0, import_node_path4.join)(agentDir, "skills");
4547
+ let entries;
4548
+ try {
4549
+ entries = await (0, import_promises4.readdir)(skillsDir, { withFileTypes: true });
4550
+ } catch {
4551
+ return null;
4552
+ }
4553
+ for (const entry of entries) {
4554
+ if (!entry.isDirectory()) {
4555
+ continue;
4556
+ }
4557
+ const skillDir = (0, import_node_path4.join)(skillsDir, entry.name);
4558
+ const skillMdPath = (0, import_node_path4.join)(skillDir, "SKILL.md");
4559
+ let content;
4560
+ try {
4561
+ content = await (0, import_promises4.readFile)(skillMdPath, "utf-8");
4562
+ } catch {
4563
+ continue;
4564
+ }
4565
+ const parsed = parseFrontmatter(content);
4566
+ const fmName = parsed.frontmatter.name || entry.name;
4567
+ if (entry.name === skillName || fmName === skillName) {
4568
+ const hasReferences = await dirExists((0, import_node_path4.join)(skillDir, "references"));
4569
+ return {
4570
+ skill: {
4571
+ name: fmName,
4572
+ description: parsed.frontmatter.description || null,
4573
+ directory: entry.name,
4574
+ hasReferences,
4575
+ userInvocable: parsed.frontmatter["user-invocable"] === "true",
4576
+ frontmatter: parsed.frontmatter
4577
+ },
4578
+ content
4579
+ };
4580
+ }
4581
+ }
4582
+ return null;
4583
+ }
4584
+
4585
+ // src/server/toolsService.ts
4586
+ var BUILT_IN_TOOLS = {
4587
+ agent: [
4588
+ { name: "read", description: "Read file contents" },
4589
+ { name: "bash", description: "Execute shell commands" },
4590
+ { name: "grep", description: "Search file contents" },
4591
+ { name: "find", description: "Find files by name pattern" },
4592
+ { name: "ls", description: "List directory contents" }
4593
+ ],
4594
+ coder: [
4595
+ { name: "read", description: "Read file contents" },
4596
+ { name: "bash", description: "Execute shell commands" },
4597
+ { name: "edit", description: "Edit file contents" },
4598
+ { name: "write", description: "Write file contents" },
4599
+ { name: "grep", description: "Search file contents" },
4600
+ { name: "find", description: "Find files by name pattern" },
4601
+ { name: "ls", description: "List directory contents" }
4602
+ ]
4603
+ };
4604
+ async function listAllTools(params) {
4605
+ const { profile, agentDir, cwd } = params;
4606
+ const tools = [];
4607
+ const builtIn = BUILT_IN_TOOLS[profile] ?? BUILT_IN_TOOLS.coder;
4608
+ for (const tool of builtIn) {
4609
+ tools.push({
4610
+ name: tool.name,
4611
+ description: tool.description,
4612
+ source: "built-in"
4613
+ });
4614
+ }
4615
+ let config;
4616
+ let cache;
4617
+ try {
4618
+ [config, cache] = await Promise.all([
4619
+ loadMcpServerConfig(agentDir, cwd),
4620
+ loadMcpServerCache(agentDir)
4621
+ ]);
4622
+ } catch {
4623
+ return tools;
4624
+ }
4625
+ const serverNames = Object.keys(config.mcpServers);
4626
+ if (serverNames.length === 0) {
4627
+ return tools;
4628
+ }
4629
+ tools.push({
4630
+ name: "mcp",
4631
+ description: "Call an MCP server tool",
4632
+ source: "built-in"
4633
+ });
4634
+ if (!cache) {
4635
+ return tools;
4636
+ }
4637
+ const prefix = config.settings?.toolPrefix ?? "server";
4638
+ for (const serverName of serverNames) {
4639
+ const serverCache = cache.servers[serverName];
4640
+ if (!serverCache) {
4641
+ continue;
4642
+ }
4643
+ for (const tool of serverCache.tools ?? []) {
4644
+ if (!tool?.name) {
4645
+ continue;
4646
+ }
4647
+ tools.push({
4648
+ name: formatToolName(tool.name, serverName, prefix),
4649
+ description: tool.description ?? null,
4650
+ source: `mcp:${serverName}`,
4651
+ serverName,
4652
+ originalName: tool.name,
4653
+ inputSchema: tool.inputSchema
4654
+ });
4655
+ }
4656
+ }
4657
+ return tools;
4658
+ }
4659
+
4131
4660
  // src/server/agentServer.ts
4132
4661
  function generateMessageId() {
4133
4662
  return `msg_${Date.now()}_${Math.random().toString(36).slice(2, 10)}`;
@@ -4185,7 +4714,7 @@ async function waitForIdle(session, timeoutMs = 3e4) {
4185
4714
  if (!session.isStreaming) {
4186
4715
  return;
4187
4716
  }
4188
- return new Promise((resolve2, reject) => {
4717
+ return new Promise((resolve3, reject) => {
4189
4718
  const timer = setTimeout(() => {
4190
4719
  unsubscribe();
4191
4720
  reject(new Error("Timed out waiting for session to become idle"));
@@ -4194,13 +4723,13 @@ async function waitForIdle(session, timeoutMs = 3e4) {
4194
4723
  if (event.type === "agent_end") {
4195
4724
  clearTimeout(timer);
4196
4725
  unsubscribe();
4197
- resolve2();
4726
+ resolve3();
4198
4727
  }
4199
4728
  });
4200
4729
  if (!session.isStreaming) {
4201
4730
  clearTimeout(timer);
4202
4731
  unsubscribe();
4203
- resolve2();
4732
+ resolve3();
4204
4733
  }
4205
4734
  });
4206
4735
  }
@@ -4312,8 +4841,8 @@ var FS_IGNORE = /* @__PURE__ */ new Set([
4312
4841
  ".svelte-kit"
4313
4842
  ]);
4314
4843
  function resolveSafePath(cwd, requestPath) {
4315
- const resolved = (0, import_node_path3.resolve)(cwd, requestPath);
4316
- const normalizedCwd = cwd.endsWith(import_node_path3.sep) ? cwd : cwd + import_node_path3.sep;
4844
+ const resolved = (0, import_node_path5.resolve)(cwd, requestPath);
4845
+ const normalizedCwd = cwd.endsWith(import_node_path5.sep) ? cwd : cwd + import_node_path5.sep;
4317
4846
  if (resolved !== cwd && !resolved.startsWith(normalizedCwd)) {
4318
4847
  throw new Error("Path escapes working directory");
4319
4848
  }
@@ -4330,7 +4859,7 @@ async function buildTree(params) {
4330
4859
  }
4331
4860
  let entries;
4332
4861
  try {
4333
- entries = await (0, import_promises3.readdir)(dir, { withFileTypes: true });
4862
+ entries = await (0, import_promises5.readdir)(dir, { withFileTypes: true });
4334
4863
  } catch {
4335
4864
  return [];
4336
4865
  }
@@ -4350,8 +4879,8 @@ async function buildTree(params) {
4350
4879
  if (ignore.has(entry.name)) {
4351
4880
  continue;
4352
4881
  }
4353
- const fullPath = (0, import_node_path3.join)(dir, entry.name);
4354
- const relativePath = (0, import_node_path3.relative)(cwd, fullPath);
4882
+ const fullPath = (0, import_node_path5.join)(dir, entry.name);
4883
+ const relativePath = (0, import_node_path5.relative)(cwd, fullPath);
4355
4884
  if (entry.isDirectory()) {
4356
4885
  const children = await buildTree({
4357
4886
  dir: fullPath,
@@ -4377,7 +4906,7 @@ async function walkFiles(params) {
4377
4906
  }
4378
4907
  let entries;
4379
4908
  try {
4380
- entries = await (0, import_promises3.readdir)(dir, { withFileTypes: true });
4909
+ entries = await (0, import_promises5.readdir)(dir, { withFileTypes: true });
4381
4910
  } catch {
4382
4911
  return;
4383
4912
  }
@@ -4388,8 +4917,8 @@ async function walkFiles(params) {
4388
4917
  if (ignore.has(entry.name)) {
4389
4918
  continue;
4390
4919
  }
4391
- const fullPath = (0, import_node_path3.join)(dir, entry.name);
4392
- const relativePath = (0, import_node_path3.relative)(cwd, fullPath);
4920
+ const fullPath = (0, import_node_path5.join)(dir, entry.name);
4921
+ const relativePath = (0, import_node_path5.relative)(cwd, fullPath);
4393
4922
  if (entry.isDirectory()) {
4394
4923
  await walkFiles({ dir: fullPath, cwd, pattern, ignore, maxResults, results });
4395
4924
  } else if (entry.isFile() && pattern.test(relativePath)) {
@@ -4830,11 +5359,11 @@ async function createAgentServer(params) {
4830
5359
  }
4831
5360
  try {
4832
5361
  const resolved = resolveSafePath(context.cwd, filePath);
4833
- const fileStat = await (0, import_promises3.stat)(resolved);
5362
+ const fileStat = await (0, import_promises5.stat)(resolved);
4834
5363
  if (!fileStat.isFile()) {
4835
5364
  return c.json({ error: "Path is not a file" }, 400);
4836
5365
  }
4837
- const content = await (0, import_promises3.readFile)(resolved, "utf-8");
5366
+ const content = await (0, import_promises5.readFile)(resolved, "utf-8");
4838
5367
  return c.json({
4839
5368
  path: filePath,
4840
5369
  content,
@@ -4854,9 +5383,9 @@ async function createAgentServer(params) {
4854
5383
  }
4855
5384
  try {
4856
5385
  const resolved = resolveSafePath(context.cwd, body.path);
4857
- await (0, import_promises3.mkdir)((0, import_node_path3.join)(resolved, ".."), { recursive: true });
4858
- await (0, import_promises3.writeFile)(resolved, body.content, "utf-8");
4859
- const fileStat = await (0, import_promises3.stat)(resolved);
5386
+ await (0, import_promises5.mkdir)((0, import_node_path5.join)(resolved, ".."), { recursive: true });
5387
+ await (0, import_promises5.writeFile)(resolved, body.content, "utf-8");
5388
+ const fileStat = await (0, import_promises5.stat)(resolved);
4860
5389
  return c.json({ ok: true, path: body.path, size: fileStat.size });
4861
5390
  } catch (error) {
4862
5391
  const message = error instanceof Error ? error.message : String(error);
@@ -4870,7 +5399,7 @@ async function createAgentServer(params) {
4870
5399
  }
4871
5400
  try {
4872
5401
  const resolved = resolveSafePath(context.cwd, body.path);
4873
- await (0, import_promises3.mkdir)(resolved, { recursive: true });
5402
+ await (0, import_promises5.mkdir)(resolved, { recursive: true });
4874
5403
  return c.json({ ok: true, path: body.path });
4875
5404
  } catch (error) {
4876
5405
  const message = error instanceof Error ? error.message : String(error);
@@ -4885,8 +5414,8 @@ async function createAgentServer(params) {
4885
5414
  try {
4886
5415
  const resolvedFrom = resolveSafePath(context.cwd, body.from);
4887
5416
  const resolvedTo = resolveSafePath(context.cwd, body.to);
4888
- await (0, import_promises3.mkdir)((0, import_node_path3.join)(resolvedTo, ".."), { recursive: true });
4889
- await (0, import_promises3.rename)(resolvedFrom, resolvedTo);
5417
+ await (0, import_promises5.mkdir)((0, import_node_path5.join)(resolvedTo, ".."), { recursive: true });
5418
+ await (0, import_promises5.rename)(resolvedFrom, resolvedTo);
4890
5419
  return c.json({ ok: true, from: body.from, to: body.to });
4891
5420
  } catch (error) {
4892
5421
  const message = error instanceof Error ? error.message : String(error);
@@ -4904,8 +5433,8 @@ async function createAgentServer(params) {
4904
5433
  if (resolved === context.cwd) {
4905
5434
  return c.json({ error: "Cannot delete working directory" }, 400);
4906
5435
  }
4907
- const fileStat = await (0, import_promises3.stat)(resolved);
4908
- await (0, import_promises3.rm)(resolved, { recursive: fileStat.isDirectory() });
5436
+ const fileStat = await (0, import_promises5.stat)(resolved);
5437
+ await (0, import_promises5.rm)(resolved, { recursive: fileStat.isDirectory() });
4909
5438
  return c.json({ ok: true, path: filePath });
4910
5439
  } catch (error) {
4911
5440
  const message = error instanceof Error ? error.message : String(error);
@@ -4914,7 +5443,7 @@ async function createAgentServer(params) {
4914
5443
  }
4915
5444
  });
4916
5445
  const CLI_EXEC = process.env.DOCYRUS_CLI_EXECUTABLE || process.execPath;
4917
- const CLI_ENTRY = process.env.DOCYRUS_CLI_ENTRY || (0, import_node_path3.resolve)(process.argv[1] ? (0, import_node_path3.join)(process.argv[1], "..") : __dirname, "main.js");
5446
+ const CLI_ENTRY = process.env.DOCYRUS_CLI_ENTRY || (0, import_node_path5.resolve)(process.argv[1] ? (0, import_node_path5.join)(process.argv[1], "..") : __dirname, "main.js");
4918
5447
  const CLI_SCOPE = process.env.DOCYRUS_CLI_SCOPE;
4919
5448
  const CLI_TIMEOUT_MS = 3e4;
4920
5449
  function runCliCommand(args) {
@@ -4964,7 +5493,7 @@ async function createAgentServer(params) {
4964
5493
  }
4965
5494
  async function detectDevPort(cwd) {
4966
5495
  try {
4967
- const pkg = JSON.parse(await (0, import_promises3.readFile)((0, import_node_path3.join)(cwd, "package.json"), "utf-8"));
5496
+ const pkg = JSON.parse(await (0, import_promises5.readFile)((0, import_node_path5.join)(cwd, "package.json"), "utf-8"));
4968
5497
  const devScript = pkg.scripts?.dev;
4969
5498
  if (devScript) {
4970
5499
  const portFlag = devScript.match(/--port\s+(\d+)/);
@@ -4980,7 +5509,7 @@ async function createAgentServer(params) {
4980
5509
  }
4981
5510
  for (const name of ["vite.config.ts", "vite.config.mts", "vite.config.js"]) {
4982
5511
  try {
4983
- const content = await (0, import_promises3.readFile)((0, import_node_path3.join)(cwd, name), "utf-8");
5512
+ const content = await (0, import_promises5.readFile)((0, import_node_path5.join)(cwd, name), "utf-8");
4984
5513
  const portMatch = content.match(/port\s*:\s*(\d+)/);
4985
5514
  if (portMatch) {
4986
5515
  return Number(portMatch[1]);
@@ -4990,19 +5519,19 @@ async function createAgentServer(params) {
4990
5519
  }
4991
5520
  for (const name of ["next.config.ts", "next.config.mts", "next.config.js", "next.config.mjs"]) {
4992
5521
  try {
4993
- await (0, import_promises3.stat)((0, import_node_path3.join)(cwd, name));
5522
+ await (0, import_promises5.stat)((0, import_node_path5.join)(cwd, name));
4994
5523
  return 3e3;
4995
5524
  } catch {
4996
5525
  }
4997
5526
  }
4998
5527
  try {
4999
- await (0, import_promises3.stat)((0, import_node_path3.join)(cwd, "angular.json"));
5528
+ await (0, import_promises5.stat)((0, import_node_path5.join)(cwd, "angular.json"));
5000
5529
  return 4200;
5001
5530
  } catch {
5002
5531
  }
5003
5532
  for (const name of ["nuxt.config.ts", "nuxt.config.js"]) {
5004
5533
  try {
5005
- await (0, import_promises3.stat)((0, import_node_path3.join)(cwd, name));
5534
+ await (0, import_promises5.stat)((0, import_node_path5.join)(cwd, name));
5006
5535
  return 3e3;
5007
5536
  } catch {
5008
5537
  }
@@ -5032,14 +5561,14 @@ async function createAgentServer(params) {
5032
5561
  } catch {
5033
5562
  }
5034
5563
  try {
5035
- const pkg = JSON.parse(await (0, import_promises3.readFile)((0, import_node_path3.join)(cwd, "package.json"), "utf-8"));
5564
+ const pkg = JSON.parse(await (0, import_promises5.readFile)((0, import_node_path5.join)(cwd, "package.json"), "utf-8"));
5036
5565
  packageName = pkg.name ?? null;
5037
5566
  packageVersion = pkg.version ?? null;
5038
5567
  } catch {
5039
5568
  }
5040
5569
  cachedProjectInfo = {
5041
5570
  path: cwd,
5042
- folder: (0, import_node_path3.basename)(cwd),
5571
+ folder: (0, import_node_path5.basename)(cwd),
5043
5572
  repo,
5044
5573
  packageName,
5045
5574
  packageVersion
@@ -5250,8 +5779,8 @@ async function createAgentServer(params) {
5250
5779
  }
5251
5780
  if (entry.status !== "deleted") {
5252
5781
  try {
5253
- const resolved = (0, import_node_path3.resolve)(cwd, entry.path);
5254
- const buf = await (0, import_promises3.readFile)(resolved);
5782
+ const resolved = (0, import_node_path5.resolve)(cwd, entry.path);
5783
+ const buf = await (0, import_promises5.readFile)(resolved);
5255
5784
  if (buf.subarray(0, 8192).includes(0)) {
5256
5785
  return null;
5257
5786
  }
@@ -5288,6 +5817,185 @@ async function createAgentServer(params) {
5288
5817
  return c.json({ error: message }, 500);
5289
5818
  }
5290
5819
  });
5820
+ app.get("/api/mcp/servers", async (c) => {
5821
+ try {
5822
+ const [config, cache, provenance] = await Promise.all([
5823
+ loadMcpServerConfig(context.agentDir, context.cwd),
5824
+ loadMcpServerCache(context.agentDir),
5825
+ getServerProvenance(context.agentDir, context.cwd)
5826
+ ]);
5827
+ const servers = Object.entries(config.mcpServers).map(([name, entry]) => {
5828
+ const prov = provenance.get(name);
5829
+ const serverCache = cache?.servers[name] ?? null;
5830
+ return {
5831
+ name,
5832
+ transport: detectTransport(entry),
5833
+ config: redactSensitiveFields(entry),
5834
+ provenance: prov?.kind ?? "user",
5835
+ importSource: prov?.importKind ?? null,
5836
+ cache: serverCache ? {
5837
+ toolCount: serverCache.tools?.length ?? 0,
5838
+ resourceCount: serverCache.resources?.length ?? 0,
5839
+ cachedAt: new Date(serverCache.cachedAt).toISOString(),
5840
+ isValid: isServerCacheValid(serverCache, entry)
5841
+ } : null
5842
+ };
5843
+ });
5844
+ return c.json({
5845
+ servers,
5846
+ settings: {
5847
+ toolPrefix: config.settings?.toolPrefix ?? "server",
5848
+ idleTimeout: config.settings?.idleTimeout ?? 10,
5849
+ directTools: config.settings?.directTools ?? false
5850
+ },
5851
+ imports: config.imports ?? null
5852
+ });
5853
+ } catch (error) {
5854
+ const message = error instanceof Error ? error.message : String(error);
5855
+ return c.json({ error: message }, 500);
5856
+ }
5857
+ });
5858
+ app.get("/api/mcp/servers/:serverName", async (c) => {
5859
+ const serverName = c.req.param("serverName");
5860
+ try {
5861
+ const [config, cache, provenance] = await Promise.all([
5862
+ loadMcpServerConfig(context.agentDir, context.cwd),
5863
+ loadMcpServerCache(context.agentDir),
5864
+ getServerProvenance(context.agentDir, context.cwd)
5865
+ ]);
5866
+ const entry = config.mcpServers[serverName];
5867
+ if (!entry) {
5868
+ return c.json({ error: `Server "${serverName}" not found` }, 404);
5869
+ }
5870
+ const prov = provenance.get(serverName);
5871
+ const serverCache = cache?.servers[serverName] ?? null;
5872
+ return c.json({
5873
+ name: serverName,
5874
+ transport: detectTransport(entry),
5875
+ config: redactSensitiveFields(entry),
5876
+ provenance: prov?.kind ?? "user",
5877
+ tools: serverCache?.tools?.map((t) => ({
5878
+ name: t.name,
5879
+ description: t.description ?? null,
5880
+ inputSchema: t.inputSchema
5881
+ })) ?? [],
5882
+ resources: serverCache?.resources?.map((r) => ({
5883
+ uri: r.uri,
5884
+ name: r.name,
5885
+ description: r.description ?? null
5886
+ })) ?? [],
5887
+ cache: serverCache ? {
5888
+ configHash: serverCache.configHash,
5889
+ toolCount: serverCache.tools?.length ?? 0,
5890
+ resourceCount: serverCache.resources?.length ?? 0,
5891
+ cachedAt: new Date(serverCache.cachedAt).toISOString(),
5892
+ isValid: isServerCacheValid(serverCache, entry)
5893
+ } : null
5894
+ });
5895
+ } catch (error) {
5896
+ const message = error instanceof Error ? error.message : String(error);
5897
+ return c.json({ error: message }, 500);
5898
+ }
5899
+ });
5900
+ app.post("/api/mcp/servers", async (c) => {
5901
+ try {
5902
+ const body = await c.req.json();
5903
+ if (!body.name) {
5904
+ return c.json({ error: "Missing required field: name" }, 400);
5905
+ }
5906
+ const nameResult = validateServerName(body.name);
5907
+ if (!nameResult.ok) {
5908
+ return c.json({ error: nameResult.error }, 400);
5909
+ }
5910
+ const entryResult = validateServerEntry(body.config);
5911
+ if (!entryResult.ok) {
5912
+ return c.json({ error: entryResult.error }, 400);
5913
+ }
5914
+ await addMcpServer(context.agentDir, body.name, entryResult.value);
5915
+ return c.json({ ok: true, name: body.name, config: redactSensitiveFields(entryResult.value) });
5916
+ } catch (error) {
5917
+ const message = error instanceof Error ? error.message : String(error);
5918
+ const status = message.includes("already exists") ? 409 : 500;
5919
+ return c.json({ error: message }, status);
5920
+ }
5921
+ });
5922
+ app.put("/api/mcp/servers/:serverName", async (c) => {
5923
+ const serverName = c.req.param("serverName");
5924
+ try {
5925
+ const provenance = await getServerProvenance(context.agentDir, context.cwd);
5926
+ const prov = provenance.get(serverName);
5927
+ if (prov && prov.kind !== "user") {
5928
+ return c.json({ error: `Cannot modify ${prov.kind} server "${serverName}"` }, 403);
5929
+ }
5930
+ const body = await c.req.json();
5931
+ const entryResult = validateServerEntry(body.config);
5932
+ if (!entryResult.ok) {
5933
+ return c.json({ error: entryResult.error }, 400);
5934
+ }
5935
+ await updateMcpServer(context.agentDir, serverName, entryResult.value);
5936
+ return c.json({ ok: true, name: serverName, config: redactSensitiveFields(entryResult.value) });
5937
+ } catch (error) {
5938
+ const message = error instanceof Error ? error.message : String(error);
5939
+ const status = message.includes("not found") ? 404 : 500;
5940
+ return c.json({ error: message }, status);
5941
+ }
5942
+ });
5943
+ app.delete("/api/mcp/servers/:serverName", async (c) => {
5944
+ const serverName = c.req.param("serverName");
5945
+ try {
5946
+ const provenance = await getServerProvenance(context.agentDir, context.cwd);
5947
+ const prov = provenance.get(serverName);
5948
+ if (prov && prov.kind !== "user") {
5949
+ return c.json({ error: `Cannot remove ${prov.kind} server "${serverName}"` }, 403);
5950
+ }
5951
+ await removeMcpServer(context.agentDir, serverName);
5952
+ return c.json({ ok: true, name: serverName });
5953
+ } catch (error) {
5954
+ const message = error instanceof Error ? error.message : String(error);
5955
+ const status = message.includes("not found") ? 404 : 500;
5956
+ return c.json({ error: message }, status);
5957
+ }
5958
+ });
5959
+ app.get("/api/skills", async (c) => {
5960
+ try {
5961
+ const skills = await listSkills(context.agentDir);
5962
+ return c.json({ skills });
5963
+ } catch (error) {
5964
+ const message = error instanceof Error ? error.message : String(error);
5965
+ return c.json({ error: message }, 500);
5966
+ }
5967
+ });
5968
+ app.get("/api/skills/:skillName", async (c) => {
5969
+ const skillName = c.req.param("skillName");
5970
+ try {
5971
+ const result = await getSkillDetail(context.agentDir, skillName);
5972
+ if (!result) {
5973
+ return c.json({ error: `Skill "${skillName}" not found` }, 404);
5974
+ }
5975
+ return c.json({ ...result.skill, content: result.content });
5976
+ } catch (error) {
5977
+ const message = error instanceof Error ? error.message : String(error);
5978
+ return c.json({ error: message }, 500);
5979
+ }
5980
+ });
5981
+ app.get("/api/tools", async (c) => {
5982
+ try {
5983
+ const tools = await listAllTools({
5984
+ profile: context.profile,
5985
+ agentDir: context.agentDir,
5986
+ cwd: context.cwd
5987
+ });
5988
+ const builtInCount = tools.filter((t) => t.source === "built-in").length;
5989
+ const mcpCount = tools.filter((t) => t.source !== "built-in").length;
5990
+ return c.json({
5991
+ tools,
5992
+ summary: { builtIn: builtInCount, mcp: mcpCount, total: tools.length }
5993
+ });
5994
+ } catch (error) {
5995
+ const message = error instanceof Error ? error.message : String(error);
5996
+ return c.json({ error: message }, 500);
5997
+ }
5998
+ });
5291
5999
  const BOOLEAN_CLI_FLAGS = /* @__PURE__ */ new Set(["json", "verbose", "global", "noAuth", "expand", "i"]);
5292
6000
  function buildCliArgs(pathSegments, query, body) {
5293
6001
  const args = [...pathSegments, "--json"];
@@ -5438,6 +6146,22 @@ async function createAgentServer(params) {
5438
6146
  process.stderr.write(` POST /api/env/stop \u2014 stop dev server
5439
6147
  `);
5440
6148
  process.stderr.write(` GET /api/git/diff \u2014 uncommitted file diffs
6149
+ `);
6150
+ process.stderr.write(` GET /api/mcp/servers \u2014 list MCP servers
6151
+ `);
6152
+ process.stderr.write(` GET /api/mcp/servers/:name \u2014 MCP server details
6153
+ `);
6154
+ process.stderr.write(` POST /api/mcp/servers \u2014 add MCP server
6155
+ `);
6156
+ process.stderr.write(` PUT /api/mcp/servers/:name \u2014 update MCP server
6157
+ `);
6158
+ process.stderr.write(` DEL /api/mcp/servers/:name \u2014 remove MCP server
6159
+ `);
6160
+ process.stderr.write(` GET /api/skills \u2014 list skills
6161
+ `);
6162
+ process.stderr.write(` GET /api/skills/:name \u2014 skill details
6163
+ `);
6164
+ process.stderr.write(` GET /api/tools \u2014 list all tools
5441
6165
  `);
5442
6166
  process.stderr.write(` * /api/cli/** \u2014 proxy any docyrus CLI command
5443
6167
 
@@ -5487,7 +6211,7 @@ function extractBearerToken(authorizationHeader) {
5487
6211
  return token;
5488
6212
  }
5489
6213
  function hashToken(token) {
5490
- return (0, import_node_crypto2.createHash)("sha256").update(token).digest();
6214
+ return (0, import_node_crypto3.createHash)("sha256").update(token).digest();
5491
6215
  }
5492
6216
  function isBearerTokenAuthorized(actualToken, expectedToken) {
5493
6217
  if (!expectedToken) {
@@ -5498,7 +6222,7 @@ function isBearerTokenAuthorized(actualToken, expectedToken) {
5498
6222
  }
5499
6223
  const actualHash = hashToken(actualToken);
5500
6224
  const expectedHash = hashToken(expectedToken);
5501
- return (0, import_node_crypto2.timingSafeEqual)(actualHash, expectedHash);
6225
+ return (0, import_node_crypto3.timingSafeEqual)(actualHash, expectedHash);
5502
6226
  }
5503
6227
  function createUnauthorizedResponse() {
5504
6228
  return new Response(JSON.stringify({ error: "Unauthorized" }), {
@@ -5564,15 +6288,15 @@ function readLoaderRequest() {
5564
6288
  }
5565
6289
  async function loadPiExports() {
5566
6290
  const piPackageDir = readRequiredEnv("PI_PACKAGE_DIR");
5567
- const moduleUrl = (0, import_node_url.pathToFileURL)((0, import_node_path4.join)(piPackageDir, "dist", "index.js")).href;
6291
+ const moduleUrl = (0, import_node_url.pathToFileURL)((0, import_node_path6.join)(piPackageDir, "dist", "index.js")).href;
5568
6292
  return await import(moduleUrl);
5569
6293
  }
5570
6294
  function resolvePackagedPiResourceRoot() {
5571
6295
  const candidates = [
5572
- (0, import_node_path4.resolve)(process.cwd(), "apps/api-cli/resources/pi-agent"),
5573
- (0, import_node_path4.resolve)(__dirname, "../resources/pi-agent"),
5574
- (0, import_node_path4.resolve)(__dirname, "resources/pi-agent"),
5575
- (0, import_node_path4.resolve)(process.cwd(), "dist/apps/api-cli/resources/pi-agent")
6296
+ (0, import_node_path6.resolve)(process.cwd(), "apps/api-cli/resources/pi-agent"),
6297
+ (0, import_node_path6.resolve)(__dirname, "../resources/pi-agent"),
6298
+ (0, import_node_path6.resolve)(__dirname, "resources/pi-agent"),
6299
+ (0, import_node_path6.resolve)(process.cwd(), "dist/apps/api-cli/resources/pi-agent")
5576
6300
  ];
5577
6301
  const resolved = candidates.find((candidate) => (0, import_node_fs.existsSync)(candidate));
5578
6302
  if (!resolved) {
@@ -5581,13 +6305,13 @@ function resolvePackagedPiResourceRoot() {
5581
6305
  return resolved;
5582
6306
  }
5583
6307
  function resolvePackagedExtensionPaths(resourceRoot) {
5584
- const extensionsRoot = (0, import_node_path4.join)(resourceRoot, "extensions");
6308
+ const extensionsRoot = (0, import_node_path6.join)(resourceRoot, "extensions");
5585
6309
  if (!(0, import_node_fs.existsSync)(extensionsRoot)) {
5586
6310
  return [];
5587
6311
  }
5588
6312
  return (0, import_node_fs.readdirSync)(extensionsRoot, {
5589
6313
  withFileTypes: true
5590
- }).filter((entry) => entry.isDirectory() || entry.isFile() && (entry.name.endsWith(".ts") || entry.name.endsWith(".js"))).map((entry) => (0, import_node_path4.join)(extensionsRoot, entry.name)).sort((left, right) => left.localeCompare(right));
6314
+ }).filter((entry) => entry.isDirectory() || entry.isFile() && (entry.name.endsWith(".ts") || entry.name.endsWith(".js"))).map((entry) => (0, import_node_path6.join)(extensionsRoot, entry.name)).sort((left, right) => left.localeCompare(right));
5591
6315
  }
5592
6316
  function setProcessArgValue(flag, value) {
5593
6317
  const existingIndex = process.argv.indexOf(flag);
@@ -5701,16 +6425,16 @@ async function main() {
5701
6425
  const version = process.env.DOCYRUS_PI_VERSION || "dev";
5702
6426
  const resourceRoot = resolvePackagedPiResourceRoot();
5703
6427
  const packagedExtensionPaths = resolvePackagedExtensionPaths(resourceRoot);
5704
- const mcpConfigPath = (0, import_node_path4.join)(agentDir, "mcp.json");
6428
+ const mcpConfigPath = (0, import_node_path6.join)(agentDir, "mcp.json");
5705
6429
  const hasPackagedMcpAdapter = packagedExtensionPaths.some((extensionPath) => extensionPath.includes("pi-mcp-adapter"));
5706
- const envStore = new AgentEnvStore((0, import_node_path4.join)(agentDir, "env.json"));
6430
+ const envStore = new AgentEnvStore((0, import_node_path6.join)(agentDir, "env.json"));
5707
6431
  await envStore.hydrateProcessEnv(process.env);
5708
6432
  if (hasPackagedMcpAdapter) {
5709
6433
  setProcessArgValue("--mcp-config", mcpConfigPath);
5710
6434
  }
5711
- const authStorage = pi.AuthStorage.create((0, import_node_path4.join)(agentDir, "auth.json"));
6435
+ const authStorage = pi.AuthStorage.create((0, import_node_path6.join)(agentDir, "auth.json"));
5712
6436
  const settingsManager = pi.SettingsManager.create(cwd, agentDir);
5713
- const modelsJsonPath = (0, import_node_path4.join)(agentDir, "models.json");
6437
+ const modelsJsonPath = (0, import_node_path6.join)(agentDir, "models.json");
5714
6438
  const modelRegistry = new pi.ModelRegistry(authStorage, modelsJsonPath);
5715
6439
  const quietStartup = !request.verbose;
5716
6440
  if (quietStartup) {
@@ -5739,7 +6463,7 @@ async function main() {
5739
6463
  agentDir,
5740
6464
  settingsManager,
5741
6465
  additionalExtensionPaths: packagedExtensionPaths,
5742
- systemPrompt: (0, import_node_path4.join)(
6466
+ systemPrompt: (0, import_node_path6.join)(
5743
6467
  resourceRoot,
5744
6468
  "prompts",
5745
6469
  request.profile === "agent" ? "agent-system.md" : "coder-system.md"