@caplets/core 0.13.1 → 0.14.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/index.js +504 -104
  2. package/package.json +3 -2
package/dist/index.js CHANGED
@@ -3720,7 +3720,7 @@ const allowsEval = /* @__PURE__ */ cached(() => {
3720
3720
  return false;
3721
3721
  }
3722
3722
  });
3723
- function isPlainObject$7(o) {
3723
+ function isPlainObject$8(o) {
3724
3724
  if (isObject(o) === false) return false;
3725
3725
  const ctor = o.constructor;
3726
3726
  if (ctor === void 0) return true;
@@ -3731,7 +3731,7 @@ function isPlainObject$7(o) {
3731
3731
  return true;
3732
3732
  }
3733
3733
  function shallowClone(o) {
3734
- if (isPlainObject$7(o)) return { ...o };
3734
+ if (isPlainObject$8(o)) return { ...o };
3735
3735
  if (Array.isArray(o)) return [...o];
3736
3736
  if (o instanceof Map) return new Map(o);
3737
3737
  if (o instanceof Set) return new Set(o);
@@ -3814,7 +3814,7 @@ function omit(schema, mask) {
3814
3814
  }));
3815
3815
  }
3816
3816
  function extend(schema, shape) {
3817
- if (!isPlainObject$7(shape)) throw new Error("Invalid input to extend: expected a plain object");
3817
+ if (!isPlainObject$8(shape)) throw new Error("Invalid input to extend: expected a plain object");
3818
3818
  const checks = schema._zod.def.checks;
3819
3819
  if (checks && checks.length > 0) {
3820
3820
  const existingShape = schema._zod.def.shape;
@@ -3830,7 +3830,7 @@ function extend(schema, shape) {
3830
3830
  } }));
3831
3831
  }
3832
3832
  function safeExtend(schema, shape) {
3833
- if (!isPlainObject$7(shape)) throw new Error("Invalid input to safeExtend: expected a plain object");
3833
+ if (!isPlainObject$8(shape)) throw new Error("Invalid input to safeExtend: expected a plain object");
3834
3834
  return clone(schema, mergeDefs(schema._zod.def, { get shape() {
3835
3835
  const _shape = {
3836
3836
  ...schema._zod.def.shape,
@@ -5444,7 +5444,7 @@ function mergeValues(a, b) {
5444
5444
  valid: true,
5445
5445
  data: a
5446
5446
  };
5447
- if (isPlainObject$7(a) && isPlainObject$7(b)) {
5447
+ if (isPlainObject$8(a) && isPlainObject$8(b)) {
5448
5448
  const bKeys = Object.keys(b);
5449
5449
  const sharedKeys = Object.keys(a).filter((key) => bKeys.indexOf(key) !== -1);
5450
5450
  const newObj = {
@@ -5520,7 +5520,7 @@ const $ZodRecord = /* @__PURE__ */ $constructor("$ZodRecord", (inst, def) => {
5520
5520
  $ZodType.init(inst, def);
5521
5521
  inst._zod.parse = (payload, ctx) => {
5522
5522
  const input = payload.value;
5523
- if (!isPlainObject$7(input)) {
5523
+ if (!isPlainObject$8(input)) {
5524
5524
  payload.issues.push({
5525
5525
  expected: "record",
5526
5526
  code: "invalid_type",
@@ -12186,7 +12186,7 @@ var Protocol = class {
12186
12186
  };
12187
12187
  }
12188
12188
  };
12189
- function isPlainObject$6(value) {
12189
+ function isPlainObject$7(value) {
12190
12190
  return value !== null && typeof value === "object" && !Array.isArray(value);
12191
12191
  }
12192
12192
  function mergeCapabilities(base, additional) {
@@ -12196,7 +12196,7 @@ function mergeCapabilities(base, additional) {
12196
12196
  const addValue = additional[k];
12197
12197
  if (addValue === void 0) continue;
12198
12198
  const baseValue = result[k];
12199
- if (isPlainObject$6(baseValue) && isPlainObject$6(addValue)) result[k] = {
12199
+ if (isPlainObject$7(baseValue) && isPlainObject$7(addValue)) result[k] = {
12200
12200
  ...baseValue,
12201
12201
  ...addValue
12202
12202
  };
@@ -19848,7 +19848,7 @@ const EMPTY_COMPLETION_RESULT = { completion: {
19848
19848
  } };
19849
19849
  //#endregion
19850
19850
  //#region package.json
19851
- var version = "0.13.1";
19851
+ var version = "0.14.0";
19852
19852
  //#endregion
19853
19853
  //#region ../../node_modules/.pnpm/unist-util-stringify-position@4.0.0/node_modules/unist-util-stringify-position/lib/index.js
19854
19854
  /**
@@ -27795,19 +27795,19 @@ function loadCapletFilesWithPaths(root) {
27795
27795
  if (servers[candidate.id] || openapiEndpoints[candidate.id] || graphqlEndpoints[candidate.id] || httpApis[candidate.id] || cliTools[candidate.id] || capletSets[candidate.id]) throw new CapletsError("CONFIG_INVALID", `Duplicate Caplet ID ${candidate.id} under ${root}`);
27796
27796
  paths[candidate.id] = candidate.path;
27797
27797
  const config = readCapletFile(candidate.path);
27798
- if (isPlainObject$5(config) && config.backend === "openapi") {
27798
+ if (isPlainObject$6(config) && config.backend === "openapi") {
27799
27799
  const { backend: _backend, ...endpoint } = config;
27800
27800
  openapiEndpoints[candidate.id] = endpoint;
27801
- } else if (isPlainObject$5(config) && config.backend === "graphql") {
27801
+ } else if (isPlainObject$6(config) && config.backend === "graphql") {
27802
27802
  const { backend: _backend, ...endpoint } = config;
27803
27803
  graphqlEndpoints[candidate.id] = endpoint;
27804
- } else if (isPlainObject$5(config) && config.backend === "http") {
27804
+ } else if (isPlainObject$6(config) && config.backend === "http") {
27805
27805
  const { backend: _backend, ...endpoint } = config;
27806
27806
  httpApis[candidate.id] = endpoint;
27807
- } else if (isPlainObject$5(config) && config.backend === "cli") {
27807
+ } else if (isPlainObject$6(config) && config.backend === "cli") {
27808
27808
  const { backend: _backend, ...endpoint } = config;
27809
27809
  cliTools[candidate.id] = endpoint;
27810
- } else if (isPlainObject$5(config) && config.backend === "caplets") {
27810
+ } else if (isPlainObject$6(config) && config.backend === "caplets") {
27811
27811
  const { backend: _backend, ...endpoint } = config;
27812
27812
  capletSets[candidate.id] = endpoint;
27813
27813
  } else servers[candidate.id] = config;
@@ -27961,7 +27961,7 @@ function parseFrontmatter(text, path) {
27961
27961
  value: text
27962
27962
  });
27963
27963
  matter(file, { strip: true });
27964
- if (!isPlainObject$5(file.data.matter) || Object.keys(file.data.matter).length === 0) throw new Error("empty frontmatter");
27964
+ if (!isPlainObject$6(file.data.matter) || Object.keys(file.data.matter).length === 0) throw new Error("empty frontmatter");
27965
27965
  return {
27966
27966
  frontmatter: file.data.matter,
27967
27967
  body: String(file)
@@ -27970,7 +27970,7 @@ function parseFrontmatter(text, path) {
27970
27970
  throw new CapletsError("CONFIG_INVALID", `Caplet file at ${path} has invalid YAML frontmatter`, redactSecrets(error));
27971
27971
  }
27972
27972
  }
27973
- function isPlainObject$5(value) {
27973
+ function isPlainObject$6(value) {
27974
27974
  return Boolean(value) && typeof value === "object" && !Array.isArray(value);
27975
27975
  }
27976
27976
  function validateCapletId(id, path) {
@@ -28591,7 +28591,7 @@ function normalizeLocalPaths(input, baseDir) {
28591
28591
  }
28592
28592
  function normalizeEndpointPaths(endpoints, baseDir, normalize) {
28593
28593
  if (!endpoints) return;
28594
- return Object.fromEntries(Object.entries(endpoints).map(([id, endpoint]) => [id, isPlainObject$4(endpoint) ? normalize(endpoint, baseDir) : endpoint]));
28594
+ return Object.fromEntries(Object.entries(endpoints).map(([id, endpoint]) => [id, isPlainObject$5(endpoint) ? normalize(endpoint, baseDir) : endpoint]));
28595
28595
  }
28596
28596
  function normalizeOpenApiPath(endpoint, baseDir) {
28597
28597
  return {
@@ -28600,7 +28600,7 @@ function normalizeOpenApiPath(endpoint, baseDir) {
28600
28600
  };
28601
28601
  }
28602
28602
  function normalizeGraphQlPath(endpoint, baseDir) {
28603
- const operations = isPlainObject$4(endpoint.operations) ? Object.fromEntries(Object.entries(endpoint.operations).map(([name, operation]) => [name, isPlainObject$4(operation) ? {
28603
+ const operations = isPlainObject$5(endpoint.operations) ? Object.fromEntries(Object.entries(endpoint.operations).map(([name, operation]) => [name, isPlainObject$5(operation) ? {
28604
28604
  ...operation,
28605
28605
  documentPath: normalizeLocalPath(operation.documentPath, baseDir)
28606
28606
  } : operation])) : endpoint.operations;
@@ -28611,7 +28611,7 @@ function normalizeGraphQlPath(endpoint, baseDir) {
28611
28611
  };
28612
28612
  }
28613
28613
  function normalizeCliToolsPaths(endpoint, baseDir) {
28614
- const actions = isPlainObject$4(endpoint.actions) ? Object.fromEntries(Object.entries(endpoint.actions).map(([name, action]) => [name, isPlainObject$4(action) ? {
28614
+ const actions = isPlainObject$5(endpoint.actions) ? Object.fromEntries(Object.entries(endpoint.actions).map(([name, action]) => [name, isPlainObject$5(action) ? {
28615
28615
  ...action,
28616
28616
  cwd: normalizeLocalPath(action.cwd, baseDir)
28617
28617
  } : action])) : endpoint.actions;
@@ -28814,7 +28814,7 @@ function isPublicMetadataPath(path) {
28814
28814
  if (path.length < 3 || path[0] !== "mcpServers" && path[0] !== "openapiEndpoints" && path[0] !== "graphqlEndpoints" && path[0] !== "httpApis" && path[0] !== "cliTools" && path[0] !== "capletSets") return false;
28815
28815
  return NON_INTERPOLATED_SERVER_FIELDS.has(path[2] ?? "");
28816
28816
  }
28817
- function isPlainObject$4(value) {
28817
+ function isPlainObject$5(value) {
28818
28818
  return Boolean(value) && typeof value === "object" && !Array.isArray(value);
28819
28819
  }
28820
28820
  function hasEnvReference(value) {
@@ -28984,9 +28984,9 @@ function validateInput(action, input) {
28984
28984
  if (!schema) return;
28985
28985
  const required = Array.isArray(schema.required) ? schema.required : [];
28986
28986
  for (const key of required) if (typeof key === "string" && (input[key] === void 0 || input[key] === null)) throw new CapletsError("REQUEST_INVALID", `CLI tool ${action.name} requires input ${key}`);
28987
- const properties = isPlainObject$3(schema.properties) ? schema.properties : {};
28987
+ const properties = isPlainObject$4(schema.properties) ? schema.properties : {};
28988
28988
  for (const [key, property] of Object.entries(properties)) {
28989
- if (input[key] === void 0 || !isPlainObject$3(property) || typeof property.type !== "string") continue;
28989
+ if (input[key] === void 0 || !isPlainObject$4(property) || typeof property.type !== "string") continue;
28990
28990
  if (!matchesJsonType(input[key], property.type)) throw new CapletsError("REQUEST_INVALID", `CLI tool ${action.name} input ${key} must be ${property.type}`);
28991
28991
  }
28992
28992
  }
@@ -28996,7 +28996,7 @@ function matchesJsonType(value, type) {
28996
28996
  case "number":
28997
28997
  case "integer": return typeof value === "number" && (type === "number" || Number.isInteger(value));
28998
28998
  case "boolean": return typeof value === "boolean";
28999
- case "object": return isPlainObject$3(value);
28999
+ case "object": return isPlainObject$4(value);
29000
29000
  case "array": return Array.isArray(value);
29001
29001
  case "null": return value === null;
29002
29002
  default: return true;
@@ -29087,7 +29087,7 @@ function isExecutable(path) {
29087
29087
  function isAbortError$1(error) {
29088
29088
  return error instanceof Error && error.name === "AbortError";
29089
29089
  }
29090
- function isPlainObject$3(value) {
29090
+ function isPlainObject$4(value) {
29091
29091
  return value !== null && typeof value === "object" && !Array.isArray(value);
29092
29092
  }
29093
29093
  //#endregion
@@ -48370,7 +48370,7 @@ function resolveMapping(mapping, input) {
48370
48370
  function resolveMappingToRecord(mapping, input, name) {
48371
48371
  if (mapping === void 0) return {};
48372
48372
  const resolved = resolveMapping(mapping, input);
48373
- if (!isPlainObject$2(resolved)) throw new CapletsError("REQUEST_INVALID", `HTTP action ${name} mapping must resolve to an object`);
48373
+ if (!isPlainObject$3(resolved)) throw new CapletsError("REQUEST_INVALID", `HTTP action ${name} mapping must resolve to an object`);
48374
48374
  return resolved;
48375
48375
  }
48376
48376
  function valueAtPath(input, path) {
@@ -48465,9 +48465,9 @@ function buildActionUrl(base, actionPath, options = {}) {
48465
48465
  return baseUrl;
48466
48466
  }
48467
48467
  function asRecord$1(value) {
48468
- return isPlainObject$2(value) ? value : {};
48468
+ return isPlainObject$3(value) ? value : {};
48469
48469
  }
48470
- function isPlainObject$2(value) {
48470
+ function isPlainObject$3(value) {
48471
48471
  return value !== null && typeof value === "object" && !Array.isArray(value);
48472
48472
  }
48473
48473
  //#endregion
@@ -58400,14 +58400,12 @@ function openApiCacheKey(endpoint) {
58400
58400
  //#endregion
58401
58401
  //#region src/capability-description.ts
58402
58402
  function capabilityDescription(server) {
58403
- const backendName = server.backend === "mcp" ? "MCP server" : server.backend === "openapi" ? "OpenAPI endpoint" : server.backend === "graphql" ? "GraphQL endpoint" : server.backend === "http" ? "HTTP API" : server.backend === "cli" ? "CLI tools" : server.backend === "caplets" ? "nested Caplets" : "backend";
58404
- const checkOperation = server.backend === "mcp" ? "check_mcp_server" : "check_backend";
58405
58403
  const hint = [
58406
- `Use this Caplet to inspect and call tools from its ${backendName} backend.`,
58404
+ `Use this Caplet to inspect and call tools from its ${server.backend === "mcp" ? "MCP server" : server.backend === "openapi" ? "OpenAPI endpoint" : server.backend === "graphql" ? "GraphQL endpoint" : server.backend === "http" ? "HTTP API" : server.backend === "cli" ? "CLI tools" : server.backend === "caplets" ? "nested Caplets" : "backend"} backend.`,
58407
58405
  "",
58408
58406
  "Recommended flow:",
58409
58407
  "- Read the full Caplet card: {\"operation\":\"get_caplet\"}",
58410
- `- Check the backend: {"operation":"${checkOperation}"}`,
58408
+ "- Check the backend: {\"operation\":\"check_backend\"}",
58411
58409
  "- Discover tools: {\"operation\":\"list_tools\"} or {\"operation\":\"search_tools\",\"query\":\"<what you need>\"}",
58412
58410
  "- Read one tool schema: {\"operation\":\"get_tool\",\"tool\":\"<tool name>\"}",
58413
58411
  "- Invoke one downstream tool: {\"operation\":\"call_tool\",\"tool\":\"<tool name>\",\"arguments\":{...}}",
@@ -58544,7 +58542,6 @@ function graphQlSource(server) {
58544
58542
  const operations = [
58545
58543
  "get_caplet",
58546
58544
  "check_backend",
58547
- "check_mcp_server",
58548
58545
  "list_tools",
58549
58546
  "search_tools",
58550
58547
  "get_tool",
@@ -58553,7 +58550,7 @@ const operations = [
58553
58550
  const generatedToolInputDescriptions = {
58554
58551
  operation: [
58555
58552
  "Caplets wrapper operation to perform for this configured Caplet backend.",
58556
- "Use get_caplet to read the full Caplet card, check_backend to check any backend, check_mcp_server to check an MCP backend, list_tools or search_tools to discover downstream tools, get_tool to read a downstream input schema, and call_tool to run one downstream tool, operation, action, CLI command, or child Caplet.",
58553
+ "Use get_caplet to read the full Caplet card, check_backend to check backend availability, list_tools or search_tools to discover downstream tools, get_tool to read a downstream input schema, and call_tool to run one downstream tool, operation, action, CLI command, or child Caplet.",
58557
58554
  "For call_tool, pass downstream inputs only inside the top-level \"arguments\" object."
58558
58555
  ].join(" "),
58559
58556
  query: "Required only for search_tools. Example: {\"operation\":\"search_tools\",\"query\":\"web search\",\"limit\":5}. Do not use query for call_tool; put downstream query values under arguments.query.",
@@ -58606,7 +58603,7 @@ function generatedToolInputJsonSchema() {
58606
58603
  //#region src/field-selection.ts
58607
58604
  function projectStructuredContent(value, outputSchema, fields) {
58608
58605
  validateFieldSelection(outputSchema, fields);
58609
- if (!isPlainObject$1(value)) throwInvalid("Field selection requires object structured content");
58606
+ if (!isPlainObject$2(value)) throwInvalid("Field selection requires object structured content");
58610
58607
  const result = createJsonObject();
58611
58608
  for (const field of fields) {
58612
58609
  const projected = projectPath(value, outputSchema, field.split("."));
@@ -58615,7 +58612,7 @@ function projectStructuredContent(value, outputSchema, fields) {
58615
58612
  return result;
58616
58613
  }
58617
58614
  function validateFieldSelection(outputSchema, fields) {
58618
- if (!isPlainObject$1(outputSchema)) throwInvalid("Field selection requires an output schema");
58615
+ if (!isPlainObject$2(outputSchema)) throwInvalid("Field selection requires an output schema");
58619
58616
  if (!Array.isArray(fields) || fields.some((field) => typeof field !== "string")) throwInvalid("Field selection requires an array of field paths");
58620
58617
  for (const field of fields) validateSchemaPath(outputSchema, field.split("."), field);
58621
58618
  }
@@ -58639,7 +58636,7 @@ function projectPath(value, schema, path) {
58639
58636
  return value.map((item) => projectPath(item, itemSchema, path) ?? {});
58640
58637
  }
58641
58638
  const segment = path[0];
58642
- if (!isPlainObject$1(value) || !Object.prototype.hasOwnProperty.call(value, segment)) return;
58639
+ if (!isPlainObject$2(value) || !Object.prototype.hasOwnProperty.call(value, segment)) return;
58643
58640
  const rest = path.slice(1);
58644
58641
  const propertySchema = getSchemaProperty(schema, segment);
58645
58642
  const projected = projectPath(value[segment], propertySchema, rest);
@@ -58651,24 +58648,24 @@ function pruneToSchema(value, schema) {
58651
58648
  const itemSchema = arrayItemSchema(schema);
58652
58649
  return value.map((item) => pruneToSchema(item, itemSchema));
58653
58650
  }
58654
- if (!isPlainObject$1(value)) return cloneJsonValue(value);
58655
- const properties = isPlainObject$1(schema) ? schema.properties : void 0;
58656
- if (!isPlainObject$1(properties)) return cloneJsonValue(value);
58651
+ if (!isPlainObject$2(value)) return cloneJsonValue(value);
58652
+ const properties = isPlainObject$2(schema) ? schema.properties : void 0;
58653
+ if (!isPlainObject$2(properties)) return cloneJsonValue(value);
58657
58654
  const result = createJsonObject();
58658
58655
  for (const [key, nestedSchema] of Object.entries(properties)) if (isSupportedSegment(key) && Object.prototype.hasOwnProperty.call(value, key)) result[key] = pruneToSchema(value[key], nestedSchema);
58659
58656
  return result;
58660
58657
  }
58661
58658
  function getSchemaProperty(schema, segment) {
58662
- const properties = isPlainObject$1(schema) ? schema.properties : void 0;
58659
+ const properties = isPlainObject$2(schema) ? schema.properties : void 0;
58663
58660
  if (!properties || !Object.prototype.hasOwnProperty.call(properties, segment)) return;
58664
58661
  return properties[segment];
58665
58662
  }
58666
58663
  function arrayItemSchema(schema) {
58667
- if (!isPlainObject$1(schema) || Array.isArray(schema.items)) return;
58664
+ if (!isPlainObject$2(schema) || Array.isArray(schema.items)) return;
58668
58665
  return schema.items;
58669
58666
  }
58670
58667
  function mergeValue(target, value) {
58671
- if (!isPlainObject$1(value)) return;
58668
+ if (!isPlainObject$2(value)) return;
58672
58669
  for (const [key, nested] of Object.entries(value)) {
58673
58670
  if (!isSupportedSegment(key)) continue;
58674
58671
  target[key] = mergeNested(target[key], nested);
@@ -58677,7 +58674,7 @@ function mergeValue(target, value) {
58677
58674
  function mergeNested(existing, next) {
58678
58675
  if (next === void 0) return existing;
58679
58676
  if (Array.isArray(existing) && Array.isArray(next)) return Array.from({ length: Math.max(existing.length, next.length) }, (_, index) => mergeNested(existing[index], next[index]));
58680
- if (isPlainObject$1(existing) && isPlainObject$1(next)) {
58677
+ if (isPlainObject$2(existing) && isPlainObject$2(next)) {
58681
58678
  const merged = Object.assign(createJsonObject(), existing);
58682
58679
  mergeValue(merged, next);
58683
58680
  return merged;
@@ -58687,7 +58684,7 @@ function mergeNested(existing, next) {
58687
58684
  function isSupportedSegment(segment) {
58688
58685
  return segment !== "" && segment !== "*" && segment !== "__proto__" && segment !== "prototype" && segment !== "constructor" && !/^\d+$/.test(segment);
58689
58686
  }
58690
- function isPlainObject$1(value) {
58687
+ function isPlainObject$2(value) {
58691
58688
  return Boolean(value) && typeof value === "object" && !Array.isArray(value);
58692
58689
  }
58693
58690
  function createJsonObject() {
@@ -58695,7 +58692,7 @@ function createJsonObject() {
58695
58692
  }
58696
58693
  function cloneJsonValue(value) {
58697
58694
  if (Array.isArray(value)) return value.map(cloneJsonValue);
58698
- if (isPlainObject$1(value)) {
58695
+ if (isPlainObject$2(value)) {
58699
58696
  const result = createJsonObject();
58700
58697
  for (const [key, nested] of Object.entries(value)) if (isSupportedSegment(key)) result[key] = cloneJsonValue(nested);
58701
58698
  return result;
@@ -58718,9 +58715,6 @@ async function handleServerTool(server, request, registry, downstream, openapi,
58718
58715
  switch (parsed.operation) {
58719
58716
  case "get_caplet": return jsonResult(registry.detail(server));
58720
58717
  case "check_backend": return jsonResult(await backendFor(server, downstream, openapi, graphql, http, cli, caplets).check(server));
58721
- case "check_mcp_server":
58722
- if (server.backend !== "mcp") throw new CapletsError("REQUEST_INVALID", "check_mcp_server is only valid for MCP-backed Caplets; use check_backend");
58723
- return jsonResult(await downstream.checkServer(server));
58724
58718
  case "list_tools": {
58725
58719
  const backend = backendFor(server, downstream, openapi, graphql, http, cli, caplets);
58726
58720
  const tools = await backend.listTools(server);
@@ -58771,7 +58765,6 @@ function validateOperationRequest(request, maxSearchLimit) {
58771
58765
  switch (value.operation) {
58772
58766
  case "get_caplet":
58773
58767
  case "check_backend":
58774
- case "check_mcp_server":
58775
58768
  case "list_tools":
58776
58769
  allowed([]);
58777
58770
  return { operation: value.operation };
@@ -58801,7 +58794,7 @@ function validateOperationRequest(request, maxSearchLimit) {
58801
58794
  "fields"
58802
58795
  ]);
58803
58796
  if (!value.tool) throw new CapletsError("REQUEST_INVALID", "call_tool requires tool");
58804
- if (!isPlainObject(value.arguments)) throw new CapletsError("REQUEST_INVALID", "call_tool.arguments must be a JSON object");
58797
+ if (!isPlainObject$1(value.arguments)) throw new CapletsError("REQUEST_INVALID", "call_tool.arguments must be a JSON object");
58805
58798
  return value.fields === void 0 ? {
58806
58799
  operation: "call_tool",
58807
58800
  tool: value.tool,
@@ -58822,7 +58815,7 @@ function jsonResult(value) {
58822
58815
  return {
58823
58816
  content: [{
58824
58817
  type: "text",
58825
- text: JSON.stringify(value, null, 2)
58818
+ text: "Result available in structuredContent.result."
58826
58819
  }],
58827
58820
  structuredContent: { result: value }
58828
58821
  };
@@ -58830,7 +58823,7 @@ function jsonResult(value) {
58830
58823
  function projectCallToolResult(result, outputSchema, fields) {
58831
58824
  if (result.isError === true) return result;
58832
58825
  const structuredContent = result.structuredContent;
58833
- if (!isPlainObject(structuredContent)) throw new CapletsError("DOWNSTREAM_PROTOCOL_ERROR", "Field selection requires the downstream tool to return object structuredContent");
58826
+ if (!isPlainObject$1(structuredContent)) throw new CapletsError("DOWNSTREAM_PROTOCOL_ERROR", "Field selection requires the downstream tool to return object structuredContent");
58834
58827
  const projected = projectStructuredContent(structuredContent, outputSchema, fields);
58835
58828
  return {
58836
58829
  ...result,
@@ -58841,7 +58834,7 @@ function projectCallToolResult(result, outputSchema, fields) {
58841
58834
  structuredContent: projected
58842
58835
  };
58843
58836
  }
58844
- function isPlainObject(value) {
58837
+ function isPlainObject$1(value) {
58845
58838
  return Boolean(value) && typeof value === "object" && !Array.isArray(value);
58846
58839
  }
58847
58840
  function backendFor(server, downstream, openapi, graphql, http, cli, caplets) {
@@ -62937,7 +62930,7 @@ async function loginAuth(serverId, options) {
62937
62930
  };
62938
62931
  if (server.backend === "mcp") await runOAuthFlow(server, flowOptions);
62939
62932
  else await runGenericOAuthFlow(server, flowOptions);
62940
- options.writeOut(`Authenticated ${serverId}\n`);
62933
+ options.writeOut(`Authenticated \`${serverId}\`.\n`);
62941
62934
  } catch (error) {
62942
62935
  options.writeErr(`${JSON.stringify(toSafeError(error, "AUTH_FAILED"), null, 2)}\n`);
62943
62936
  process.exitCode = 1;
@@ -62945,25 +62938,47 @@ async function loginAuth(serverId, options) {
62945
62938
  }
62946
62939
  function logoutAuth(serverId, options) {
62947
62940
  assertLoginTarget(findAuthTarget(serverId, loadConfig(options.configPath)), serverId);
62948
- if (deleteTokenBundle(serverId, options.authDir)) options.writeOut(`Deleted OAuth credentials for ${serverId}\n`);
62949
- else options.writeOut(`No OAuth credentials found for ${serverId}\n`);
62941
+ if (deleteTokenBundle(serverId, options.authDir)) options.writeOut(`Deleted OAuth credentials for \`${serverId}\`.\n`);
62942
+ else options.writeOut(`No OAuth credentials found for \`${serverId}\`.\n`);
62950
62943
  }
62951
62944
  function listAuth(options) {
62952
62945
  const servers = authTargets(loadConfig(options.configPath)).sort((left, right) => left.server.localeCompare(right.server));
62946
+ const format = options.format ?? "plain";
62947
+ if (format === "json") {
62948
+ const rows = servers.map((server) => {
62949
+ const bundle = readTokenBundle(server.server, options.authDir);
62950
+ const status = !bundle ? "missing" : isTokenBundleExpired(bundle) ? "expired" : "authenticated";
62951
+ return {
62952
+ server: server.server,
62953
+ status,
62954
+ ...bundle?.expiresAt ? { expiresAt: bundle.expiresAt } : {},
62955
+ ...bundle?.scope ? { scope: bundle.scope } : {}
62956
+ };
62957
+ });
62958
+ options.writeOut(`${JSON.stringify(rows, null, 2)}\n`);
62959
+ return;
62960
+ }
62953
62961
  if (servers.length === 0) {
62954
- options.writeOut("No configured remote OAuth servers found.\n");
62962
+ options.writeOut(format === "markdown" ? "## OAuth credentials\n\nNo configured remote OAuth servers found.\n" : "No configured remote OAuth servers found.\n");
62955
62963
  return;
62956
62964
  }
62965
+ if (format === "markdown") options.writeOut("## OAuth credentials\n\n");
62966
+ else options.writeOut("OAuth credentials\n\n");
62957
62967
  for (const server of servers) {
62958
62968
  const bundle = readTokenBundle(server.server, options.authDir);
62959
62969
  const status = !bundle ? "missing" : isTokenBundleExpired(bundle) ? "expired" : "authenticated";
62970
+ const details = [bundle?.expiresAt ? `expires ${bundle.expiresAt}` : void 0, bundle?.scope ? `scope ${bundle.scope}` : void 0].filter(Boolean).join("; ");
62971
+ if (format === "markdown") {
62972
+ options.writeOut(`- \`${server.server}\` — ${status}${details ? ` (${details})` : ""}\n`);
62973
+ continue;
62974
+ }
62960
62975
  options.writeOut([
62961
62976
  server.server,
62962
- status,
62963
- bundle?.expiresAt ? `expires ${bundle.expiresAt}` : void 0,
62964
- bundle?.scope ? `scope ${bundle.scope}` : void 0
62965
- ].filter(Boolean).join(" "));
62966
- options.writeOut("\n");
62977
+ ` Status: ${status}`,
62978
+ ...bundle?.expiresAt ? [` Expires: ${bundle.expiresAt}`] : [],
62979
+ ...bundle?.scope ? [` Scope: ${bundle.scope}`] : []
62980
+ ].join("\n"));
62981
+ options.writeOut("\n\n");
62967
62982
  }
62968
62983
  }
62969
62984
  function findAuthTarget(serverId, config = loadConfig()) {
@@ -63063,24 +63078,49 @@ function allCaplets(config) {
63063
63078
  ...Object.values(config.cliTools)
63064
63079
  ];
63065
63080
  }
63066
- function formatCapletList(rows) {
63081
+ function formatCapletList(rows, format = "plain") {
63082
+ return format === "markdown" ? formatCapletListMarkdown(rows) : formatCapletListPlain(rows);
63083
+ }
63084
+ function formatCapletListMarkdown(rows) {
63085
+ if (rows.length === 0) return "## Configured Caplets\n\nNo configured Caplets found.\n";
63086
+ const heading = [
63087
+ "## Configured Caplets",
63088
+ "",
63089
+ `${rows.length} ${rows.length === 1 ? "Caplet" : "Caplets"} shown.`,
63090
+ ""
63091
+ ];
63092
+ const entries = rows.flatMap((row) => [
63093
+ `- \`${row.server}\` — ${row.name}`,
63094
+ ` - Backend: ${row.backend}`,
63095
+ ` - Status: ${row.status}`,
63096
+ ` - Source: ${row.source}`,
63097
+ ...row.disabled ? [" - Disabled: true"] : [],
63098
+ ...row.path ? [` - Path: ${row.path}`] : []
63099
+ ]);
63100
+ const warnings = rows.flatMap((row) => row.shadows.map((shadow) => `Warning: ${formatSourceKind(row.source)} Caplet ${row.server} shadows ${formatSourceKind(shadow.kind)} Caplet at ${shadow.path}`));
63101
+ if (warnings.length === 0) return `${[...heading, ...entries].join("\n")}\n`;
63102
+ return `${[
63103
+ ...heading,
63104
+ ...entries,
63105
+ "",
63106
+ "Warnings:",
63107
+ ...warnings.map((warning) => `- ${warning}`)
63108
+ ].join("\n")}\n`;
63109
+ }
63110
+ function formatCapletListPlain(rows) {
63067
63111
  if (rows.length === 0) return "No configured Caplets found.\n";
63068
- const table = formatTable([[
63069
- "server",
63070
- "backend",
63071
- "status",
63072
- "source",
63073
- "name"
63074
- ], ...rows.map((row) => [
63112
+ const entries = rows.map((row) => [
63075
63113
  row.server,
63076
- row.backend,
63077
- row.status,
63078
- row.source,
63079
- row.name
63080
- ])]);
63114
+ ` Name: ${row.name}`,
63115
+ ` Backend: ${row.backend}`,
63116
+ ` Status: ${row.status}`,
63117
+ ` Source: ${row.source}`,
63118
+ ...row.disabled ? [" Disabled: true"] : [],
63119
+ ...row.path ? [` Path: ${row.path}`] : []
63120
+ ].join("\n")).join("\n\n");
63081
63121
  const warnings = rows.flatMap((row) => row.shadows.map((shadow) => `Warning: ${formatSourceKind(row.source)} Caplet ${row.server} shadows ${formatSourceKind(shadow.kind)} Caplet at ${shadow.path}`));
63082
- if (warnings.length === 0) return `${table}\n`;
63083
- return `${table}\n${warnings.join("\n")}\n`;
63122
+ if (warnings.length === 0) return `Configured Caplets (${rows.length})\n\n${entries}\n`;
63123
+ return `Configured Caplets (${rows.length})\n\n${entries}\n\n${warnings.join("\n")}\n`;
63084
63124
  }
63085
63125
  function formatSourceKind(kind) {
63086
63126
  if (kind.startsWith("project")) return "project";
@@ -63100,28 +63140,35 @@ function resolveCliConfigPaths(envConfigPath, authDir) {
63100
63140
  envConfig: envConfigPath ?? null
63101
63141
  };
63102
63142
  }
63103
- function formatConfigPaths(paths) {
63143
+ function formatConfigPaths(paths, format = "plain") {
63144
+ if (format === "markdown") return formatConfigPathsMarkdown(paths);
63145
+ return formatConfigPathsPlain(paths);
63146
+ }
63147
+ function formatConfigPathsMarkdown(paths) {
63104
63148
  return [
63105
- `userConfig: ${paths.userConfig}`,
63106
- `projectConfig: ${paths.projectConfig}`,
63107
- `userRoot: ${paths.userRoot}`,
63108
- `stateRoot: ${paths.stateRoot}`,
63109
- `projectRoot: ${paths.projectRoot}`,
63110
- `authDir: ${paths.authDir}`,
63111
- `envConfig: ${paths.envConfig ?? "unset"}`
63149
+ "## Caplets paths",
63150
+ "",
63151
+ `- User config: ${paths.userConfig}`,
63152
+ `- Project config: ${paths.projectConfig}`,
63153
+ `- User Caplets root: ${paths.userRoot}`,
63154
+ `- State root: ${paths.stateRoot}`,
63155
+ `- Project Caplets root: ${paths.projectRoot}`,
63156
+ `- Auth directory: ${paths.authDir}`,
63157
+ `- CAPLETS_CONFIG: ${paths.envConfig ?? "unset"}`
63112
63158
  ].join("\n") + "\n";
63113
63159
  }
63114
- function formatTable(rows) {
63115
- const firstRow = rows[0];
63116
- if (!firstRow) return "";
63117
- const widths = firstRow.map((_, column) => Math.max(...rows.map((row) => row[column]?.length ?? 0)));
63118
- return rows.map((row) => formatTableRow(row, widths)).join("\n");
63119
- }
63120
- function formatTableRow(row, widths) {
63121
- return row.map((value, column) => {
63122
- if (column === row.length - 1) return value;
63123
- return value.padEnd((widths[column] ?? 0) + 2);
63124
- }).join("").trimEnd();
63160
+ function formatConfigPathsPlain(paths) {
63161
+ return [
63162
+ "Caplets paths",
63163
+ "",
63164
+ `User config: ${paths.userConfig}`,
63165
+ `Project config: ${paths.projectConfig}`,
63166
+ `User root: ${paths.userRoot}`,
63167
+ `State root: ${paths.stateRoot}`,
63168
+ `Project root: ${paths.projectRoot}`,
63169
+ `Auth directory: ${paths.authDir}`,
63170
+ `CAPLETS_CONFIG: ${paths.envConfig ?? "unset"}`
63171
+ ].join("\n") + "\n";
63125
63172
  }
63126
63173
  //#endregion
63127
63174
  //#region src/cli/install.ts
@@ -63370,7 +63417,7 @@ async function runCli(args, io = {}) {
63370
63417
  ]);
63371
63418
  } catch (error) {
63372
63419
  if (error instanceof CommanderError) {
63373
- if (error.code === "commander.helpDisplayed" || error.code === "commander.version") return;
63420
+ if (error.code === "commander.helpDisplayed" || error.code === "commander.version" || error.message === "(outputHelp)") return;
63374
63421
  throw new CapletsError("REQUEST_INVALID", error.message);
63375
63422
  }
63376
63423
  throw error;
@@ -63379,6 +63426,9 @@ async function runCli(args, io = {}) {
63379
63426
  function createProgram(io = {}) {
63380
63427
  const writeOut = io.writeOut ?? ((value) => process.stdout.write(value));
63381
63428
  const writeErr = io.writeErr ?? ((value) => process.stderr.write(value));
63429
+ const setExitCode = io.setExitCode ?? ((code) => {
63430
+ process.exitCode = code;
63431
+ });
63382
63432
  const program = new Command();
63383
63433
  program.name("caplets").description("Progressive-disclosure gateway for MCP servers.").version(io.version ?? version).exitOverride().configureOutput({
63384
63434
  writeOut,
@@ -63392,13 +63442,13 @@ function createProgram(io = {}) {
63392
63442
  force: Boolean(options.force)
63393
63443
  })}\n`);
63394
63444
  });
63395
- program.command("list").description("List configured Caplets.").option("--all", "include disabled Caplets").option("--json", "print JSON output").action((options) => {
63445
+ program.command("list").description("List configured Caplets.").option("--all", "include disabled Caplets").option("--json", "print JSON output").option("--format <format>", "output format: plain, markdown, md, or json", parseOutputFormat).action((options) => {
63396
63446
  const rows = listCaplets(loadConfigWithSources(envConfigPath()), { includeDisabled: Boolean(options.all) });
63397
- if (options.json) {
63447
+ if (options.json || options.format === "json") {
63398
63448
  writeOut(`${JSON.stringify(rows, null, 2)}\n`);
63399
63449
  return;
63400
63450
  }
63401
- writeOut(formatCapletList(rows));
63451
+ writeOut(formatCapletList(rows, options.format ?? "plain"));
63402
63452
  });
63403
63453
  program.command("install").description("Install Caplets from a repo's caplets directory.").argument("<repo>", "local repo path, Git URL, or GitHub owner/repo").argument("[caplets...]", "optional Caplet IDs to install").option("-g, --global", "install to the user Caplets root").option("--force", "overwrite installed Caplets").action((repo, capletIds, options) => {
63404
63454
  const result = installCaplets(repo, {
@@ -63444,17 +63494,88 @@ function createProgram(io = {}) {
63444
63494
  destinationRoot: addDestinationRoot(options)
63445
63495
  }));
63446
63496
  });
63497
+ program.command("get-caplet").description("Print a configured Caplet card.").argument("<caplet>", "configured Caplet ID").option("--format <format>", "output format: markdown, md, plain, or json", parseOutputFormat).action(async (caplet, options) => {
63498
+ await executeOperation(caplet, { operation: "get_caplet" }, {
63499
+ writeOut,
63500
+ writeErr,
63501
+ setExitCode,
63502
+ authDir: io.authDir,
63503
+ format: options.format
63504
+ });
63505
+ });
63506
+ program.command("check-backend").description("Check backend availability for a configured Caplet.").argument("<caplet>", "configured Caplet ID").option("--format <format>", "output format: markdown, md, plain, or json", parseOutputFormat).action(async (caplet, options) => {
63507
+ await executeOperation(caplet, { operation: "check_backend" }, {
63508
+ writeOut,
63509
+ writeErr,
63510
+ setExitCode,
63511
+ authDir: io.authDir,
63512
+ format: options.format
63513
+ });
63514
+ });
63515
+ program.command("list-tools").description("List downstream tools for a configured Caplet.").argument("<caplet>", "configured Caplet ID").option("--format <format>", "output format: markdown, md, plain, or json", parseOutputFormat).action(async (caplet, options) => {
63516
+ await executeOperation(caplet, { operation: "list_tools" }, {
63517
+ writeOut,
63518
+ writeErr,
63519
+ setExitCode,
63520
+ authDir: io.authDir,
63521
+ format: options.format
63522
+ });
63523
+ });
63524
+ program.command("search-tools").description("Search downstream tools for a configured Caplet.").argument("<caplet>", "configured Caplet ID").argument("<query>", "search query").option("--limit <n>", "maximum number of tools to return", parsePositiveInteger).option("--format <format>", "output format: markdown, md, plain, or json", parseOutputFormat).action(async (caplet, query, options) => {
63525
+ await executeOperation(caplet, options.limit === void 0 ? {
63526
+ operation: "search_tools",
63527
+ query
63528
+ } : {
63529
+ operation: "search_tools",
63530
+ query,
63531
+ limit: options.limit
63532
+ }, {
63533
+ writeOut,
63534
+ writeErr,
63535
+ setExitCode,
63536
+ authDir: io.authDir,
63537
+ format: options.format
63538
+ });
63539
+ });
63540
+ program.command("get-tool").description("Print one downstream tool schema.").argument("<caplet.tool>", "qualified target, split on the first dot").option("--format <format>", "output format: markdown, md, plain, or json", parseOutputFormat).action(async (target, options) => {
63541
+ const { caplet, tool } = parseQualifiedTarget(target);
63542
+ await executeOperation(caplet, {
63543
+ operation: "get_tool",
63544
+ tool
63545
+ }, {
63546
+ writeOut,
63547
+ writeErr,
63548
+ setExitCode,
63549
+ authDir: io.authDir,
63550
+ format: options.format
63551
+ });
63552
+ });
63553
+ program.command("call-tool").description("Call one downstream tool.").argument("<caplet.tool>", "qualified target, split on the first dot").option("--args <json-object>", "JSON object of downstream tool arguments").option("--field <path>", "project a field from structured output", collect, []).option("--format <format>", "output format: markdown, md, plain, or json", parseOutputFormat).action(async (target, options) => {
63554
+ const { caplet, tool } = parseQualifiedTarget(target);
63555
+ await executeOperation(caplet, {
63556
+ operation: "call_tool",
63557
+ tool,
63558
+ arguments: parseCallToolArgs(options.args),
63559
+ ...options.field && options.field.length > 0 ? { fields: options.field } : {}
63560
+ }, {
63561
+ writeOut,
63562
+ writeErr,
63563
+ setExitCode,
63564
+ authDir: io.authDir,
63565
+ format: options.format
63566
+ });
63567
+ });
63447
63568
  const config = program.command("config").description("Inspect Caplets config locations.");
63448
63569
  config.command("path").description("Print the effective user config path.").action(() => {
63449
63570
  writeOut(`${resolveConfigPath(envConfigPath())}\n`);
63450
63571
  });
63451
- config.command("paths").description("Print resolved Caplets config, root, and auth paths.").option("--json", "print JSON output").action((options) => {
63572
+ config.command("paths").description("Print resolved Caplets config, root, and auth paths.").option("--json", "print JSON output").option("--format <format>", "output format: plain, markdown, md, or json", parseOutputFormat).action((options) => {
63452
63573
  const paths = resolveCliConfigPaths(envConfigPath(), io.authDir);
63453
- if (options.json) {
63574
+ if (options.json || options.format === "json") {
63454
63575
  writeOut(`${JSON.stringify(paths, null, 2)}\n`);
63455
63576
  return;
63456
63577
  }
63457
- writeOut(formatConfigPaths(paths));
63578
+ writeOut(formatConfigPaths(paths, options.format ?? "plain"));
63458
63579
  });
63459
63580
  const auth = program.command("auth").description("Manage OAuth credentials for remote servers.");
63460
63581
  auth.command("login").description("Authenticate a configured remote OAuth server.").argument("<server>", "configured server ID").option("--no-open", "print the authorization URL without opening a browser").action(async (serverId, options) => {
@@ -63475,10 +63596,11 @@ function createProgram(io = {}) {
63475
63596
  ...io.authDir ? { authDir: io.authDir } : {}
63476
63597
  });
63477
63598
  });
63478
- auth.command("list").description("List servers with stored OAuth credentials.").action(() => {
63599
+ auth.command("list").description("List servers with stored OAuth credentials.").option("--json", "print JSON output").option("--format <format>", "output format: plain, markdown, md, or json", parseOutputFormat).action((options) => {
63479
63600
  const configPath = envConfigPath();
63480
63601
  listAuth({
63481
63602
  writeOut,
63603
+ format: options.json || options.format === "json" ? "json" : options.format ?? "plain",
63482
63604
  ...configPath ? { configPath } : {},
63483
63605
  ...io.authDir ? { authDir: io.authDir } : {}
63484
63606
  });
@@ -63492,6 +63614,284 @@ function collect(value, previous) {
63492
63614
  previous.push(value);
63493
63615
  return previous;
63494
63616
  }
63617
+ function parsePositiveInteger(value) {
63618
+ const parsed = Number(value);
63619
+ if (!Number.isInteger(parsed) || parsed <= 0) throw new CapletsError("REQUEST_INVALID", `Expected a positive integer, got ${value}`);
63620
+ return parsed;
63621
+ }
63622
+ function parseOutputFormat(value) {
63623
+ switch (value.toLocaleLowerCase()) {
63624
+ case "markdown":
63625
+ case "md": return "markdown";
63626
+ case "plain": return "plain";
63627
+ case "json": return "json";
63628
+ default: throw new CapletsError("REQUEST_INVALID", `Expected output format markdown, md, plain, or json; got ${value}`);
63629
+ }
63630
+ }
63631
+ function parseQualifiedTarget(target) {
63632
+ const dot = target.indexOf(".");
63633
+ if (dot <= 0 || dot === target.length - 1) throw new CapletsError("REQUEST_INVALID", "Expected qualified target in the form <caplet>.<tool>");
63634
+ return {
63635
+ caplet: target.slice(0, dot),
63636
+ tool: target.slice(dot + 1)
63637
+ };
63638
+ }
63639
+ function parseCallToolArgs(value) {
63640
+ if (value === void 0) return {};
63641
+ let parsed;
63642
+ try {
63643
+ parsed = JSON.parse(value);
63644
+ } catch (error) {
63645
+ throw new CapletsError("REQUEST_INVALID", "call-tool --args must be valid JSON", error);
63646
+ }
63647
+ if (!isPlainObject(parsed)) throw new CapletsError("REQUEST_INVALID", "call-tool --args must be a JSON object");
63648
+ return parsed;
63649
+ }
63650
+ function isPlainObject(value) {
63651
+ return value !== null && typeof value === "object" && !Array.isArray(value);
63652
+ }
63653
+ async function executeOperation(caplet, request, io) {
63654
+ const configPath = envConfigPath();
63655
+ const engine = new CapletsEngine({
63656
+ ...configPath ? { configPath } : {},
63657
+ ...io.authDir ? { authDir: io.authDir } : {},
63658
+ watch: false,
63659
+ writeErr: io.writeErr
63660
+ });
63661
+ try {
63662
+ const result = await engine.execute(caplet, request);
63663
+ const output = cliOutputForOperation(result, {
63664
+ ...request,
63665
+ caplet
63666
+ }, io.format ?? "markdown");
63667
+ io.writeOut(typeof output === "string" ? `${output}\n` : `${JSON.stringify(output, null, 2)}\n`);
63668
+ if (isPlainObject(result) && result.isError === true) io.setExitCode(1);
63669
+ } finally {
63670
+ await engine.close();
63671
+ }
63672
+ }
63673
+ function cliOutputForOperation(result, request, format) {
63674
+ if (format === "json" || !isPlainObject(result)) return jsonPayloadForOperation(result, request.operation);
63675
+ return format === "markdown" ? markdownSummaryForOperation(result, request) : plainSummaryForOperation(result, request);
63676
+ }
63677
+ function jsonPayloadForOperation(result, operation) {
63678
+ if (operation === "call_tool" || !isPlainObject(result)) return result;
63679
+ const structuredContent = result.structuredContent;
63680
+ if (!isPlainObject(structuredContent) || !("result" in structuredContent)) return result;
63681
+ return structuredContent.result;
63682
+ }
63683
+ function markdownSummaryForOperation(result, request) {
63684
+ const operation = request.operation;
63685
+ const payload = jsonPayloadForOperation(result, operation);
63686
+ if (!isPlainObject(payload)) return String(payload);
63687
+ switch (operation) {
63688
+ case "get_caplet": return [
63689
+ `## Caplet \`${String(payload.caplet ?? "unknown")}\``,
63690
+ "",
63691
+ `**Name:** ${String(payload.name ?? "Unnamed")}`,
63692
+ `**Description:** ${String(payload.description ?? "No description.")}`,
63693
+ payload.backend ? `**Backend:** ${backendType(payload.backend)}` : void 0,
63694
+ "",
63695
+ "Next:",
63696
+ `- List tools: \`caplets list-tools ${String(payload.caplet ?? "<caplet>")}\``,
63697
+ `- Search tools: \`caplets search-tools ${String(payload.caplet ?? "<caplet>")} <query>\``
63698
+ ].filter((line) => line !== void 0).join("\n");
63699
+ case "check_backend": return [
63700
+ `## Backend \`${String(payload.server ?? "caplet")}\``,
63701
+ "",
63702
+ `- Status: ${String(payload.status ?? "unknown")}`,
63703
+ typeof payload.toolCount === "number" ? `- Tools: ${payload.toolCount}` : void 0,
63704
+ typeof payload.elapsedMs === "number" ? `- Elapsed: ${payload.elapsedMs}ms` : void 0,
63705
+ "",
63706
+ "Next:",
63707
+ `- List tools: \`caplets list-tools ${String(payload.server ?? "<caplet>")}\``
63708
+ ].filter((line) => line !== void 0).join("\n");
63709
+ case "list_tools": {
63710
+ const tools = Array.isArray(payload.tools) ? payload.tools : [];
63711
+ return [
63712
+ `## Tools for \`${String(payload.server ?? "caplet")}\``,
63713
+ "",
63714
+ `${tools.length} ${tools.length === 1 ? "tool" : "tools"} found.`,
63715
+ "",
63716
+ ...formatToolLines(tools, "markdown"),
63717
+ "",
63718
+ "Next:",
63719
+ `- Inspect a tool: \`caplets get-tool ${String(payload.server ?? "<caplet>")}.<tool>\``,
63720
+ `- Call a tool: \`caplets call-tool ${String(payload.server ?? "<caplet>")}.<tool> --args '{...}'\``,
63721
+ "- Machine output: add `--format json`"
63722
+ ].join("\n");
63723
+ }
63724
+ case "search_tools": {
63725
+ const tools = Array.isArray(payload.tools) ? payload.tools : [];
63726
+ return [
63727
+ `## Matches for ${JSON.stringify(String(payload.query ?? ""))} in \`${String(payload.server ?? "caplet")}\``,
63728
+ "",
63729
+ `${tools.length} ${tools.length === 1 ? "match" : "matches"} found.`,
63730
+ "",
63731
+ ...formatToolLines(tools, "markdown"),
63732
+ "",
63733
+ "Next:",
63734
+ tools.length > 0 ? `- Inspect the first match: \`caplets get-tool ${String(payload.server ?? "<caplet>")}.${firstToolName(tools) ?? "<tool>"}\`` : `- Try a broader query or list tools: \`caplets list-tools ${String(payload.server ?? "<caplet>")}\``
63735
+ ].join("\n");
63736
+ }
63737
+ case "get_tool": {
63738
+ const tool = isPlainObject(payload.tool) ? payload.tool : {};
63739
+ const target = `${String(payload.server ?? "<caplet>")}.${String(tool.name ?? "<tool>")}`;
63740
+ return [
63741
+ `## Tool \`${target}\``,
63742
+ "",
63743
+ tool.description ? compactDescription(String(tool.description)) : void 0,
63744
+ "",
63745
+ "Input:",
63746
+ `- ${schemaSummary(tool.inputSchema)}`,
63747
+ "",
63748
+ "Output:",
63749
+ `- ${tool.outputSchema ? schemaSummary(tool.outputSchema) : "not declared"}`,
63750
+ "",
63751
+ "Next:",
63752
+ `- Call: \`caplets call-tool ${target} --args '{...}'\``,
63753
+ "- Full schema: add `--format json`"
63754
+ ].filter((line) => line !== void 0).join("\n");
63755
+ }
63756
+ case "call_tool": return [
63757
+ `## Call \`${`${String(request.caplet ?? "<caplet>")}.${String(request.tool ?? "unknown")}`}\``,
63758
+ "",
63759
+ `- Status: ${payload.isError === true ? "failed" : "succeeded"}`,
63760
+ callStatusLine(payload) ? `- ${callStatusLine(payload)}` : void 0,
63761
+ `- Result: ${summarizeCallResult(payload)}`,
63762
+ "",
63763
+ "Use `--format json` to inspect the full structured result."
63764
+ ].filter((line) => line !== void 0).join("\n");
63765
+ default: return JSON.stringify(payload, null, 2);
63766
+ }
63767
+ }
63768
+ function plainSummaryForOperation(result, request) {
63769
+ const operation = request.operation;
63770
+ const payload = jsonPayloadForOperation(result, operation);
63771
+ if (!isPlainObject(payload)) return String(payload);
63772
+ switch (operation) {
63773
+ case "get_caplet": return [
63774
+ `Caplet: ${String(payload.caplet ?? "unknown")}`,
63775
+ `Name: ${String(payload.name ?? "Unnamed")}`,
63776
+ `Description: ${String(payload.description ?? "No description.")}`,
63777
+ payload.backend ? `Backend: ${backendType(payload.backend)}` : void 0,
63778
+ `Next: caplets list-tools ${String(payload.caplet ?? "<caplet>")} or caplets search-tools ${String(payload.caplet ?? "<caplet>")} <query>`
63779
+ ].filter((line) => Boolean(line)).join("\n");
63780
+ case "check_backend": return [
63781
+ `Backend: ${String(payload.server ?? "caplet")} is ${String(payload.status ?? "unknown")}`,
63782
+ typeof payload.toolCount === "number" ? `Tools: ${payload.toolCount}` : void 0,
63783
+ typeof payload.elapsedMs === "number" ? `Elapsed: ${payload.elapsedMs}ms` : void 0,
63784
+ `Next: caplets list-tools ${String(payload.server ?? "<caplet>")}`
63785
+ ].filter((line) => Boolean(line)).join("\n");
63786
+ case "list_tools": {
63787
+ const tools = Array.isArray(payload.tools) ? payload.tools : [];
63788
+ return [
63789
+ `Tools for ${String(payload.server ?? "caplet")} (${tools.length}):`,
63790
+ ...formatToolLines(tools, "plain"),
63791
+ `Next: caplets get-tool ${String(payload.server ?? "<caplet>")}.<tool> or caplets call-tool ${String(payload.server ?? "<caplet>")}.<tool> --args '{...}'`
63792
+ ].join("\n");
63793
+ }
63794
+ case "search_tools": {
63795
+ const tools = Array.isArray(payload.tools) ? payload.tools : [];
63796
+ return [
63797
+ `Matches for ${JSON.stringify(String(payload.query ?? ""))} in ${String(payload.server ?? "caplet")} (${tools.length}):`,
63798
+ ...formatToolLines(tools, "plain"),
63799
+ tools.length > 0 ? `Next: caplets get-tool ${String(payload.server ?? "<caplet>")}.${firstToolName(tools) ?? "<tool>"}` : `Next: try caplets list-tools ${String(payload.server ?? "<caplet>")} or a broader query.`
63800
+ ].join("\n");
63801
+ }
63802
+ case "get_tool": {
63803
+ const tool = isPlainObject(payload.tool) ? payload.tool : {};
63804
+ const target = `${String(payload.server ?? "<caplet>")}.${String(tool.name ?? "<tool>")}`;
63805
+ return [
63806
+ `Tool: ${target}`,
63807
+ tool.description ? `Description: ${compactDescription(String(tool.description))}` : void 0,
63808
+ `Input: ${schemaSummary(tool.inputSchema)}`,
63809
+ `Output: ${tool.outputSchema ? schemaSummary(tool.outputSchema) : "not declared"}`,
63810
+ `Next: caplets call-tool ${target} --args '{...}'`,
63811
+ "Use --format json to inspect full schemas and descriptions."
63812
+ ].filter((line) => Boolean(line)).join("\n");
63813
+ }
63814
+ case "call_tool": return [
63815
+ `Call ${`${String(request.caplet ?? "<caplet>")}.${String(request.tool ?? "unknown")}`} ${payload.isError === true ? "failed" : "succeeded"}.`,
63816
+ callStatusLine(payload),
63817
+ `Result: ${summarizeCallResult(payload)}`,
63818
+ "Use --format json to inspect the full structured result."
63819
+ ].filter((line) => Boolean(line)).join("\n");
63820
+ default: return JSON.stringify(payload, null, 2);
63821
+ }
63822
+ }
63823
+ function formatToolLines(tools, format) {
63824
+ if (tools.length === 0) return ["- none"];
63825
+ return tools.map((tool) => {
63826
+ if (!isPlainObject(tool)) return `- ${String(tool)}`;
63827
+ const name = String(tool.tool ?? tool.name ?? "unknown");
63828
+ const displayName = format === "markdown" ? `\`${name}\`` : name;
63829
+ const flags = [tool.hasInputSchema ? "input" : void 0, tool.hasOutputSchema ? "output" : void 0].filter(Boolean).join(", ");
63830
+ return `- ${displayName}${flags ? ` (${flags})` : ""}${tool.description ? ` — ${compactDescription(String(tool.description))}` : ""}`;
63831
+ });
63832
+ }
63833
+ function compactDescription(value) {
63834
+ const firstParagraph = value.trim().split(/\n\s*\n/u)[0] ?? "";
63835
+ const collapsed = (firstParagraph.match(/^.*?(?:[.!?](?=\s|$)|$)/u)?.[0] ?? firstParagraph).replace(/\s+/gu, " ").trim();
63836
+ return collapsed.length > 140 ? `${collapsed.slice(0, 137).trimEnd()}...` : collapsed;
63837
+ }
63838
+ function firstToolName(tools) {
63839
+ const first = tools[0];
63840
+ return isPlainObject(first) && typeof first.tool === "string" ? first.tool : void 0;
63841
+ }
63842
+ function backendType(value) {
63843
+ return isPlainObject(value) && typeof value.type === "string" ? value.type : "unknown";
63844
+ }
63845
+ function callStatusLine(payload) {
63846
+ const structured = isPlainObject(payload.structuredContent) ? payload.structuredContent : payload;
63847
+ return typeof structured.exitCode === "number" ? `Exit code: ${structured.exitCode}` : void 0;
63848
+ }
63849
+ function summarizeCallResult(payload) {
63850
+ const structured = isPlainObject(payload.structuredContent) ? payload.structuredContent : payload;
63851
+ const preview = previewValue(preferredPreviewValue(structured));
63852
+ if (preview) return preview;
63853
+ const keys = Object.keys(structured).filter((key) => key !== "elapsedMs");
63854
+ return keys.length > 0 ? `structured keys: ${keys.join(", ")}` : "no structured content";
63855
+ }
63856
+ function preferredPreviewValue(value) {
63857
+ if (!isPlainObject(value)) return value;
63858
+ if ("result" in value) return value.result;
63859
+ if ("json" in value) return value.json;
63860
+ if (typeof value.text === "string") return value.text;
63861
+ if (typeof value.stdout === "string" && value.stdout.trim()) return value.stdout.trim();
63862
+ return value;
63863
+ }
63864
+ function previewValue(value) {
63865
+ if (typeof value === "string") return truncatePreview(value);
63866
+ if (typeof value === "number" || typeof value === "boolean" || value === null) return String(value);
63867
+ if (Array.isArray(value)) return truncatePreview(JSON.stringify(value));
63868
+ if (isPlainObject(value)) {
63869
+ const entries = Object.entries(value).slice(0, 4);
63870
+ if (entries.length === 0) return "empty object";
63871
+ return truncatePreview(entries.map(([key, entryValue]) => `${key}: ${previewScalar(entryValue)}`).join(", "));
63872
+ }
63873
+ }
63874
+ function previewScalar(value) {
63875
+ if (typeof value === "string") return JSON.stringify(truncatePreview(value, 80));
63876
+ if (typeof value === "number" || typeof value === "boolean" || value === null) return String(value);
63877
+ if (Array.isArray(value)) return `[${value.length} item${value.length === 1 ? "" : "s"}]`;
63878
+ if (isPlainObject(value)) return `{${Object.keys(value).slice(0, 3).join(", ")}${Object.keys(value).length > 3 ? ", ..." : ""}}`;
63879
+ return typeof value;
63880
+ }
63881
+ function truncatePreview(value, maxLength = 180) {
63882
+ const collapsed = value.replace(/\s+/gu, " ").trim();
63883
+ return collapsed.length > maxLength ? `${collapsed.slice(0, maxLength - 3).trimEnd()}...` : collapsed;
63884
+ }
63885
+ function schemaSummary(schema) {
63886
+ if (!isPlainObject(schema)) return "not declared";
63887
+ const properties = isPlainObject(schema.properties) ? Object.keys(schema.properties) : [];
63888
+ const required = Array.isArray(schema.required) ? schema.required.filter((value) => typeof value === "string") : [];
63889
+ return [
63890
+ typeof schema.type === "string" ? `type ${schema.type}` : void 0,
63891
+ properties.length > 0 ? `properties ${properties.join(", ")}` : "no declared properties",
63892
+ required.length > 0 ? `required ${required.join(", ")}` : "no required fields"
63893
+ ].filter((part) => Boolean(part)).join("; ");
63894
+ }
63495
63895
  function addDestinationRoot(options) {
63496
63896
  return options.global ? resolveCapletsRoot(resolveConfigPath(envConfigPath())) : resolveProjectCapletsRoot();
63497
63897
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@caplets/core",
3
- "version": "0.13.1",
3
+ "version": "0.14.0",
4
4
  "description": "Core runtime library for Caplets progressive disclosure gateways.",
5
5
  "keywords": [
6
6
  "caplets",
@@ -44,6 +44,7 @@
44
44
  },
45
45
  "devDependencies": {
46
46
  "@types/node": "^25.7.0",
47
+ "@typescript/native-preview": "7.0.0-dev.20260515.1",
47
48
  "rolldown": "^1.0.0",
48
49
  "typescript": "^6.0.3",
49
50
  "vitest": "^4.1.6"
@@ -54,7 +55,7 @@
54
55
  "scripts": {
55
56
  "build": "rm -rf dist && rolldown -c",
56
57
  "build:watch": "rolldown -c --watch",
57
- "typecheck": "tsc --noEmit",
58
+ "typecheck": "tsgo --noEmit",
58
59
  "test": "vitest run"
59
60
  }
60
61
  }