caplets 0.17.1 → 0.17.3

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 (3) hide show
  1. package/README.md +19 -6
  2. package/dist/index.js +129 -36
  3. package/package.json +2 -2
package/README.md CHANGED
@@ -77,16 +77,18 @@ You can also invoke configured Caplets directly from the CLI for agent-friendly
77
77
  ```sh
78
78
  caplets get-caplet context7
79
79
  caplets list-tools context7
80
- caplets get-tool context7.resolve-library-id
81
- caplets call-tool context7.resolve-library-id --args '{"libraryName":"react"}'
82
- caplets call-tool context7.resolve-library-id --args '{"libraryName":"react"}' --field result.id --format json
80
+ caplets get-tool context7 resolve-library-id
81
+ caplets call-tool context7 resolve-library-id --args '{"libraryName":"react"}'
82
+ caplets call-tool context7 resolve-library-id --args '{"libraryName":"react"}' --field result.id --format json
83
83
  caplets list-resources docs
84
84
  caplets read-resource docs file:///repo/README.md
85
85
  caplets list-prompts linear
86
- caplets get-prompt linear.review_issue --args '{"issueId":"CAP-123"}'
86
+ caplets get-prompt linear review_issue --args '{"issueId":"CAP-123"}'
87
87
  caplets complete docs --resource-template 'file:///repo/{path}' --argument path --value src/
88
88
  ```
89
89
 
90
+ The older qualified form, such as `caplets call-tool context7.resolve-library-id` or `caplets get-prompt linear.review_issue`, remains supported for scripts and existing usage.
91
+
90
92
  Direct CLI operation commands print Markdown summaries by default. Add `--format plain` for plain text or `--format json` for machine-readable JSON (`md` is accepted as an alias for `markdown`). If a downstream tool returns `isError: true`, Caplets still exits with status code 1.
91
93
 
92
94
  ### Shell completions
@@ -117,7 +119,7 @@ caplets completion cmd > %USERPROFILE%\caplets-completion.cmd
117
119
  %USERPROFILE%\caplets-completion.cmd
118
120
  ```
119
121
 
120
- Completions include command names, options, common enum values, configured Caplet IDs, and cache-backed downstream names for qualified targets such as `caplets call-tool repo.<TAB>`. Downstream discovery is bounded by the `completion` config timeouts and a platform-native cache directory. Generated shell scripts suppress completion stderr; run the underlying CLI command directly when debugging completion behavior.
122
+ Completions include command names, options, common enum values, configured Caplet IDs, and cache-backed downstream names for split targets such as `caplets call-tool repo <TAB>` and qualified targets such as `caplets call-tool repo.<TAB>`. Downstream discovery is bounded by the `completion` config timeouts and a platform-native cache directory. Generated shell scripts suppress completion stderr; run the underlying CLI command directly when debugging completion behavior.
121
123
 
122
124
  Backends that require OAuth or token auth may need `caplets auth login <server>` before live downstream completions can return richer results. Completion never starts interactive login flows.
123
125
 
@@ -546,7 +548,18 @@ This repository includes polished working examples under [`caplets/`](caplets/):
546
548
  - `linear`: Linear's hosted OAuth MCP endpoint.
547
549
  - `context7`: Context7 documentation lookup through `@upstash/context7-mcp`.
548
550
  - `repo-cli`: Read-oriented repository CLI workflows through `git` and package scripts.
549
- - `github-cli`: Read-oriented GitHub workflows through the `gh` CLI.
551
+ - `ast-grep`: Structural code search, scan, rewrite, rule testing, and scaffolding through the `ast-grep` CLI.
552
+ - `osv`: OSV.dev vulnerability lookups through explicit read-only HTTP actions.
553
+ - `npm`: npm registry operations through npm's published OpenAPI spec URL.
554
+ - `pypi`: PyPI project metadata, release metadata, and Simple API JSON through a curated OpenAPI spec.
555
+ - `deepwiki`: Repository-focused documentation and architecture context through DeepWiki MCP.
556
+ - `sourcegraph`: Cross-repository code search and navigation through Sourcegraph MCP.
557
+ - `playwright`: Headless browser automation for frontend inspection and testing through Playwright MCP.
558
+ - `coding-agent-toolkit`: A CapletSet that bundles high-value coding-agent examples; source children are symlinks to canonical top-level examples and installed copies are materialized as self-contained files/directories.
559
+ - `github-cli`: Pre-existing secondary read-oriented GitHub workflows through the `gh` CLI; prefer the canonical `github` MCP example for the polished GitHub integration.
560
+
561
+ GraphQL is intentionally skipped in this showcase batch so the examples can focus on HTTP,
562
+ OpenAPI, MCP, CLI, and CapletSet coverage without duplicating GitHub or GitLab surfaces.
550
563
 
551
564
  Install every example from a repo's `caplets/` directory into the current project's
552
565
  `./.caplets` directory:
package/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import { createRequire } from "node:module";
3
- import { accessSync, chmodSync, closeSync, constants, cpSync, existsSync, lstatSync, mkdirSync, mkdtempSync, openSync, readFileSync, readdirSync, renameSync, rmSync, statSync, watch, writeFileSync, writeSync } from "node:fs";
4
- import minpath, { basename, delimiter, dirname, extname, isAbsolute, join, parse, posix, relative, resolve, win32 } from "node:path";
3
+ import { accessSync, chmodSync, closeSync, constants, copyFileSync, cpSync, existsSync, lstatSync, mkdirSync, mkdtempSync, openSync, readFileSync, readdirSync, readlinkSync, realpathSync, renameSync, rmSync, statSync, watch, writeFileSync, writeSync } from "node:fs";
4
+ import minpath, { basename, delimiter, dirname, extname, isAbsolute, join, parse, posix, relative, resolve, sep, win32 } from "node:path";
5
5
  import { execFileSync, spawn } from "node:child_process";
6
6
  import process$1, { stdin, stdout } from "node:process";
7
7
  import { fileURLToPath } from "node:url";
@@ -4709,7 +4709,7 @@ function generatedToolInputJsonSchema() {
4709
4709
  return generatedToolInputJsonSchemaForCaplet({ backend: "tool" });
4710
4710
  }
4711
4711
  //#endregion
4712
- //#region ../core/dist/options-BqibJVxq.js
4712
+ //#region ../core/dist/options-j9p3L3r1.js
4713
4713
  var __create = Object.create;
4714
4714
  var __defProp = Object.defineProperty;
4715
4715
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
@@ -56439,8 +56439,21 @@ async function loadOpenApiSource(endpoint, authDir) {
56439
56439
  if (endpoint.specPath) return endpoint.specPath;
56440
56440
  if (!endpoint.specUrl) throw new CapletsError("CONFIG_INVALID", `${endpoint.server} is missing OpenAPI spec source`);
56441
56441
  if (!endpoint.baseUrl) throw new CapletsError("CONFIG_INVALID", `${endpoint.server} must configure baseUrl when using remote specUrl`);
56442
- const response = await fetchWithLimit(endpoint.specUrl, endpoint.requestTimeoutMs, shouldSendSpecAuth(endpoint) ? authHeaders(endpoint, authDir) : {});
56443
- return JSON.parse(response);
56442
+ return parseOpenApiSourceText(await fetchWithLimit(endpoint.specUrl, endpoint.requestTimeoutMs, shouldSendSpecAuth(endpoint) ? authHeaders(endpoint, authDir) : {}));
56443
+ }
56444
+ function parseOpenApiSourceText(source) {
56445
+ let parsed;
56446
+ try {
56447
+ parsed = JSON.parse(source);
56448
+ } catch (jsonError) {
56449
+ try {
56450
+ parsed = (0, import_dist$1.parse)(source);
56451
+ } catch {
56452
+ throw jsonError instanceof Error ? jsonError : new Error(String(jsonError));
56453
+ }
56454
+ }
56455
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) throw new Error("OpenAPI source must parse to an object");
56456
+ return parsed;
56444
56457
  }
56445
56458
  function extractOperations(endpoint, document) {
56446
56459
  const operations = [];
@@ -56458,6 +56471,7 @@ function extractOperations(endpoint, document) {
56458
56471
  const requestBody = requestBodyFor(operation);
56459
56472
  const outputSchema = outputSchemaFor(operation);
56460
56473
  const baseUrl = endpoint.baseUrl ?? firstServerUrl(document);
56474
+ const staticHeaders = staticHeaderDefaultsFor(endpoint, parameters);
56461
56475
  validateOperationBaseUrl(endpoint, baseUrl);
56462
56476
  operations.push({
56463
56477
  name,
@@ -56465,15 +56479,32 @@ function extractOperations(endpoint, document) {
56465
56479
  path,
56466
56480
  ...typeof operation.summary === "string" ? { summary: operation.summary } : {},
56467
56481
  ...typeof operation.description === "string" ? { description: operation.description } : {},
56468
- inputSchema: inputSchemaFor(parameters, requestBody),
56482
+ inputSchema: inputSchemaFor(parameters, requestBody, staticHeaders),
56469
56483
  ...outputSchema ? { outputSchema } : {},
56470
56484
  ...requestBody?.contentType ? { requestBodyContentType: requestBody.contentType } : {},
56471
- ...baseUrl ? { baseUrl } : {}
56485
+ ...baseUrl ? { baseUrl } : {},
56486
+ ...Object.keys(staticHeaders).length ? { staticHeaders } : {}
56472
56487
  });
56473
56488
  }
56474
56489
  }
56475
56490
  return operations.sort((left, right) => left.name.localeCompare(right.name));
56476
56491
  }
56492
+ function staticHeaderDefaultsFor(endpoint, parameters) {
56493
+ const configuredHeaderNames = configuredAuthHeaderNames(endpoint);
56494
+ const headers = {};
56495
+ for (const parameter of parameters) {
56496
+ if (parameter?.in !== "header" || typeof parameter.name !== "string") continue;
56497
+ const normalized = parameter.name.toLowerCase();
56498
+ if (configuredHeaderNames.has(normalized) || FORBIDDEN_ARGUMENT_HEADERS.has(normalized) && normalized !== "accept") continue;
56499
+ const defaultValue = parameter.schema?.default;
56500
+ if ([
56501
+ "string",
56502
+ "number",
56503
+ "boolean"
56504
+ ].includes(typeof defaultValue)) headers[parameter.name] = String(defaultValue);
56505
+ }
56506
+ return headers;
56507
+ }
56477
56508
  function requestBodyFor(operation) {
56478
56509
  const requestBody = operation.requestBody;
56479
56510
  if (!requestBody || typeof requestBody !== "object") return;
@@ -56534,19 +56565,20 @@ function structuredOutputSchema(bodySchema) {
56534
56565
  }
56535
56566
  };
56536
56567
  }
56537
- function inputSchemaFor(parameters, requestBody) {
56568
+ function inputSchemaFor(parameters, requestBody, staticHeaders = {}) {
56538
56569
  const schema = {
56539
56570
  type: "object",
56540
56571
  additionalProperties: false,
56541
56572
  properties: {}
56542
56573
  };
56543
56574
  const required = [];
56575
+ const protectedStaticHeaders = new Set(Object.keys(staticHeaders).map((key) => key.toLowerCase()).filter((key) => FORBIDDEN_ARGUMENT_HEADERS.has(key)));
56544
56576
  for (const location of [
56545
56577
  "path",
56546
56578
  "query",
56547
56579
  "header"
56548
56580
  ]) {
56549
- const locationParameters = parameters.filter((parameter) => parameter?.in === location);
56581
+ const locationParameters = parameters.filter((parameter) => parameter?.in === location && !(location === "header" && protectedStaticHeaders.has(parameter.name?.toLowerCase())));
56550
56582
  if (locationParameters.length === 0) continue;
56551
56583
  const nestedRequired = locationParameters.filter((parameter) => parameter.required === true || location === "path").map((parameter) => parameter.name);
56552
56584
  schema.properties[location] = {
@@ -56585,7 +56617,8 @@ function buildRequest(endpoint, operation, args, authDir) {
56585
56617
  for (const [key, value] of Object.entries(asRecord(args.query))) if (value !== void 0 && value !== null) url.searchParams.append(key, serializeHttpValue("query", key, value));
56586
56618
  const headers = new Headers();
56587
56619
  applyAuth(headers, endpoint, authDir);
56588
- const configuredHeaderNames = endpoint.auth.type === "headers" ? new Set(Object.keys(endpoint.auth.headers).map((key) => key.toLowerCase())) : /* @__PURE__ */ new Set();
56620
+ const configuredHeaderNames = configuredAuthHeaderNames(endpoint);
56621
+ for (const [key, value] of Object.entries(operation.staticHeaders ?? {})) if (!headers.has(key) && !configuredHeaderNames.has(key.toLowerCase())) headers.set(key, value);
56589
56622
  for (const [key, value] of Object.entries(asRecord(args.header))) if (value !== void 0 && value !== null) {
56590
56623
  const normalized = key.toLowerCase();
56591
56624
  if (FORBIDDEN_ARGUMENT_HEADERS.has(normalized) || configuredHeaderNames.has(normalized)) throw new CapletsError("REQUEST_INVALID", `Header ${key} cannot be supplied by arguments`);
@@ -56631,6 +56664,9 @@ function asRecord(value) {
56631
56664
  function applyAuth(headers, endpoint, authDir) {
56632
56665
  for (const [key, value] of Object.entries(authHeaders(endpoint, authDir))) headers.set(key, value);
56633
56666
  }
56667
+ function configuredAuthHeaderNames(endpoint) {
56668
+ return endpoint.auth.type === "headers" ? new Set(Object.keys(endpoint.auth.headers).map((key) => key.toLowerCase())) : /* @__PURE__ */ new Set();
56669
+ }
56634
56670
  function authHeaders(endpoint, authDir) {
56635
56671
  switch (endpoint.auth.type) {
56636
56672
  case "none": return {};
@@ -57625,7 +57661,7 @@ var CapletsEngine = class {
57625
57661
  }
57626
57662
  }
57627
57663
  async completeCliWords(words) {
57628
- const { completeCliWords } = await Promise.resolve().then(() => completion_dbB1hc97_exports).then((n) => n.r);
57664
+ const { completeCliWords } = await Promise.resolve().then(() => completion_Cq1z7ci2_exports).then((n) => n.r);
57629
57665
  return await completeCliWords(words, {
57630
57666
  config: this.registry.config,
57631
57667
  managers: {
@@ -57963,8 +57999,8 @@ function hasEnv$1(value) {
57963
57999
  return value !== void 0 && value.trim() !== "";
57964
58000
  }
57965
58001
  //#endregion
57966
- //#region ../core/dist/completion-dbB1hc97.js
57967
- var completion_dbB1hc97_exports = /* @__PURE__ */ __exportAll$1({
58002
+ //#region ../core/dist/completion-Cq1z7ci2.js
58003
+ var completion_Cq1z7ci2_exports = /* @__PURE__ */ __exportAll$1({
57968
58004
  a: () => formatCapletList,
57969
58005
  c: () => resolveCliConfigPaths,
57970
58006
  i: () => trailingSpaceCompletionToken,
@@ -58434,9 +58470,17 @@ async function completeCliWords(words, options = {}) {
58434
58470
  if (normalized.length === 1) return prefixFilter([...topLevelCommandNames], current);
58435
58471
  if (normalized.length === 2 && command in cliSubcommands) return prefixFilter(cliSubcommands[command], current);
58436
58472
  if (normalized.length === 2 && capletIdCommands.has(command)) return prefixFilter(promptResourceCommands.has(command) ? configuredCapletIds(options, { backend: "mcp" }) : configuredCapletIds(options), current);
58437
- if (normalized.length === 2 && (qualifiedToolCommands.has(command) || qualifiedPromptCommands.has(command))) {
58438
- if (current.includes(".")) return prefixFilter((await discoverCompletionCandidates(current.slice(0, current.indexOf(".")), qualifiedToolCommands.has(command) ? "tools" : "prompts", discoveryOptions(options))).map((candidate) => candidate.value), current);
58439
- return prefixFilter(configuredCapletIds(options, qualifiedPromptCommands.has(command) ? { backend: "mcp" } : void 0).map((id) => `${id}.`), current);
58473
+ if (qualifiedToolCommands.has(command) || qualifiedPromptCommands.has(command)) {
58474
+ const kind = qualifiedToolCommands.has(command) ? "tools" : "prompts";
58475
+ const idFilter = qualifiedPromptCommands.has(command) ? { backend: "mcp" } : void 0;
58476
+ if (normalized.length === 2) {
58477
+ if (current.includes(".")) return prefixFilter((await discoverCompletionCandidates(current.slice(0, current.indexOf(".")), kind, discoveryOptions(options))).map((candidate) => candidate.value), current);
58478
+ return prefixFilter(configuredCapletIds(options, idFilter), current);
58479
+ }
58480
+ if (normalized.length === 3 && subcommand && !subcommand.includes(".")) {
58481
+ if (current.startsWith("-")) return [];
58482
+ return prefixFilter((await discoverCompletionCandidates(subcommand, kind, discoveryOptions(options))).map((candidate) => candidate.value.replace(`${subcommand}.`, "")), current);
58483
+ }
58440
58484
  }
58441
58485
  if (command === cliCommands.readResource && normalized.length === 3) return prefixFilter((await discoverCompletionCandidates(subcommand, "resources", discoveryOptions(options))).map((candidate) => candidate.value), current);
58442
58486
  if (command === cliCommands.auth && ["login", "logout"].includes(subcommand) && normalized.length === 3) return prefixFilter(configuredCapletIds(options), current);
@@ -59819,7 +59863,7 @@ const EMPTY_COMPLETION_RESULT = { completion: {
59819
59863
  values: [],
59820
59864
  hasMore: false
59821
59865
  } };
59822
- var version$1 = "0.18.1";
59866
+ var version$1 = "0.18.3";
59823
59867
  var CapletsMcpSession = class {
59824
59868
  engine;
59825
59869
  server;
@@ -63618,12 +63662,14 @@ function rejectCrossKindDestinationCollision(plan, destinationRoot) {
63618
63662
  function installPlan(caplet, options) {
63619
63663
  const isDirectory = basename(caplet.path) === "CAPLET.md";
63620
63664
  const sourcePath = isDirectory ? dirname(caplet.path) : caplet.path;
63665
+ const sourceBoundary = dirname(sourcePath);
63621
63666
  const sourcePathRelative = relative(options.repoRoot, sourcePath);
63622
63667
  const destination = isDirectory ? join(options.destinationRoot, caplet.id) : join(options.destinationRoot, `${caplet.id}.md`);
63623
63668
  return {
63624
63669
  id: caplet.id,
63625
63670
  source: `${options.sourceId}#${sourcePathRelative}`,
63626
63671
  sourcePath,
63672
+ sourceBoundary,
63627
63673
  destination,
63628
63674
  kind: isDirectory ? "directory" : "file"
63629
63675
  };
@@ -63685,16 +63731,40 @@ function removeInstallPath(path, label, force) {
63685
63731
  }
63686
63732
  function copyInstallPath(plan) {
63687
63733
  try {
63734
+ if (plan.kind === "directory") {
63735
+ copyDirectoryCaplet(plan.sourcePath, plan.destination, realpathSync(plan.sourceBoundary));
63736
+ return;
63737
+ }
63688
63738
  cpSync(plan.sourcePath, plan.destination, {
63689
- recursive: plan.kind === "directory",
63739
+ recursive: false,
63690
63740
  force: false,
63691
63741
  errorOnExist: true
63692
63742
  });
63693
63743
  } catch (error) {
63744
+ if (error instanceof CapletsError) throw error;
63694
63745
  if (isFsError(error, "EEXIST") || isFsError(error, "EISDIR")) throw new CapletsError("CONFIG_EXISTS", `Caplet ${plan.id} already exists at ${plan.destination}; pass --force to overwrite it`, toSafeError(error));
63695
63746
  throw new CapletsError("CONFIG_INVALID", `Could not install Caplet ${plan.id} to ${plan.destination}`, toSafeError(error));
63696
63747
  }
63697
63748
  }
63749
+ function copyDirectoryCaplet(source, destination, sourceBoundary, seenDirectories = /* @__PURE__ */ new Set()) {
63750
+ const resolvedSource = lstatSync(source).isSymbolicLink() ? resolveDirectoryCapletSymlink(source, sourceBoundary) : source;
63751
+ if (statSync(resolvedSource).isDirectory()) {
63752
+ const realDirectory = realpathSync(resolvedSource);
63753
+ if (seenDirectories.has(realDirectory)) throw new CapletsError("CONFIG_INVALID", `Directory Caplet symlink ${source} creates a copy cycle`);
63754
+ const childSeenDirectories = new Set(seenDirectories);
63755
+ childSeenDirectories.add(realDirectory);
63756
+ mkdirSync(destination);
63757
+ for (const entry of readdirSync(resolvedSource)) copyDirectoryCaplet(join(resolvedSource, entry), join(destination, entry), sourceBoundary, childSeenDirectories);
63758
+ return;
63759
+ }
63760
+ copyFileSync(resolvedSource, destination);
63761
+ }
63762
+ function resolveDirectoryCapletSymlink(source, sourceBoundary) {
63763
+ const target = readlinkSync(source);
63764
+ const resolvedTarget = realpathSync(isAbsolute(target) ? target : resolve(dirname(source), target));
63765
+ if (resolvedTarget !== sourceBoundary && !resolvedTarget.startsWith(`${sourceBoundary}${sep}`)) throw new CapletsError("CONFIG_INVALID", `Directory Caplet symlink ${source} resolves outside source Caplets boundary`);
63766
+ return resolvedTarget;
63767
+ }
63698
63768
  function isFsError(error, code) {
63699
63769
  return typeof error === "object" && error !== null && "code" in error && error.code === code;
63700
63770
  }
@@ -67491,7 +67561,7 @@ function createHttpServeApp(options, engine, io = {}) {
67491
67561
  }
67492
67562
  });
67493
67563
  }
67494
- return c.json(await dispatchRemoteCliRequest(request, controlContext(io, writeErr, authFlowStore, c.req.url, paths.control, options.trustProxy, (name) => c.req.header(name))));
67564
+ return c.json(await dispatchRemoteCliRequest(request, controlContext(io, writeErr, authFlowStore, c.req.url, paths.control, options.publicOrigin, options.trustProxy, (name) => c.req.header(name))));
67495
67565
  });
67496
67566
  app.get(routePath(paths.control, "auth/callback/:flowId"), async (c) => {
67497
67567
  const flowId = c.req.param("flowId");
@@ -67501,7 +67571,7 @@ function createHttpServeApp(options, engine, io = {}) {
67501
67571
  flowId,
67502
67572
  callbackUrl: c.req.url
67503
67573
  }
67504
- }, controlContext(io, writeErr, authFlowStore, c.req.url, paths.control, options.trustProxy, (name) => c.req.header(name)));
67574
+ }, controlContext(io, writeErr, authFlowStore, c.req.url, paths.control, options.publicOrigin, options.trustProxy, (name) => c.req.header(name)));
67505
67575
  if (!result.ok) writeErr(`Caplets authentication failed for flow ${flowId}: ${result.error.message}\n`);
67506
67576
  return result.ok ? c.text("Caplets authentication complete. You can return to your terminal.") : c.text("Caplets authentication failed. Check server logs for details.", 400);
67507
67577
  });
@@ -67515,12 +67585,12 @@ function createHttpServeApp(options, engine, io = {}) {
67515
67585
  if (options.warnUnauthenticatedNetwork) writeErr(`Warning: Caplets MCP HTTP server is listening on ${options.host} without authentication.\n`);
67516
67586
  return app;
67517
67587
  }
67518
- function controlContext(io, writeErr, authFlowStore, requestUrl, controlPath, trustProxy, header) {
67588
+ function controlContext(io, writeErr, authFlowStore, requestUrl, controlPath, publicOrigin, trustProxy, header) {
67519
67589
  return {
67520
67590
  ...io.control,
67521
67591
  projectCapletsRoot: io.control?.projectCapletsRoot ?? resolveProjectCapletsRoot(),
67522
67592
  authFlowStore,
67523
- controlCallbackBaseUrl: new URL(controlPath, publicRequestOrigin(requestUrl, trustProxy, header)).toString(),
67593
+ controlCallbackBaseUrl: new URL(controlPath, publicOrigin ?? publicRequestOrigin(requestUrl, trustProxy, header)).toString(),
67524
67594
  writeErr
67525
67595
  };
67526
67596
  }
@@ -67685,6 +67755,7 @@ function resolveServeOptions(raw, env = process.env) {
67685
67755
  host,
67686
67756
  port,
67687
67757
  path,
67758
+ ...serverUrl ? { publicOrigin: serverUrl.origin } : {},
67688
67759
  auth,
67689
67760
  warnUnauthenticatedNetwork: !loopback && !auth.enabled,
67690
67761
  loopback,
@@ -67874,9 +67945,12 @@ function createProgram(io = {}) {
67874
67945
  suggestions = remote ? await remote.request("complete_cli", {
67875
67946
  shell,
67876
67947
  words: completionWords
67877
- }) : await completeCliWords(completionWords, configPath ? { configPath } : {});
67948
+ }) : await completeCliWordsLocally(completionWords, {
67949
+ ...configPath ? { configPath } : {},
67950
+ ...io.authDir ? { authDir: io.authDir } : {}
67951
+ });
67878
67952
  } catch {
67879
- suggestions = [];
67953
+ suggestions = remote ? [] : await completeCliWords(completionWords, configPath ? { configPath } : {});
67880
67954
  }
67881
67955
  if (suggestions.length > 0) writeOut(`${suggestions.join("\n")}\n`);
67882
67956
  });
@@ -68070,8 +68144,8 @@ function createProgram(io = {}) {
68070
68144
  format: options.format
68071
68145
  });
68072
68146
  });
68073
- program.command(cliCommands.getTool).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) => {
68074
- const { caplet, tool } = parseQualifiedTarget(target);
68147
+ program.command(cliCommands.getTool).description("Print one downstream tool schema.").argument("<caplet-or-target>", "Caplet ID or qualified <caplet.tool> target").argument("[tool]", "downstream tool name when caplet is provided separately").option("--format <format>", "output format: markdown, md, plain, or json", parseOutputFormat).action(async (capletOrTarget, toolArgument, options) => {
68148
+ const { caplet, tool } = parseQualifiedTarget(capletOrTarget, toolArgument);
68075
68149
  await executeOperation(caplet, {
68076
68150
  operation: "get_tool",
68077
68151
  tool
@@ -68085,8 +68159,8 @@ function createProgram(io = {}) {
68085
68159
  format: options.format
68086
68160
  });
68087
68161
  });
68088
- program.command(cliCommands.callTool).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) => {
68089
- const { caplet, tool } = parseQualifiedTarget(target);
68162
+ program.command(cliCommands.callTool).description("Call one downstream tool.").argument("<caplet-or-target>", "Caplet ID or qualified <caplet.tool> target").argument("[tool]", "downstream tool name when caplet is provided separately").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 (capletOrTarget, toolArgument, options) => {
68163
+ const { caplet, tool } = parseQualifiedTarget(capletOrTarget, toolArgument);
68090
68164
  await executeOperation(caplet, {
68091
68165
  operation: "call_tool",
68092
68166
  tool,
@@ -68182,8 +68256,8 @@ function createProgram(io = {}) {
68182
68256
  remote: remoteClientForCli(io),
68183
68257
  format: options.format
68184
68258
  }));
68185
- program.command(cliCommands.getPrompt).description("Get one MCP prompt by name.").argument("<caplet.prompt>", "qualified target, split on the first dot").option("--args <json-object>", "JSON object of prompt arguments").option("--format <format>", "output format: markdown, md, plain, or json", parseOutputFormat).action(async (target, options) => {
68186
- const { caplet, tool: prompt } = parseQualifiedTarget(target);
68259
+ program.command(cliCommands.getPrompt).description("Get one MCP prompt by name.").argument("<caplet-or-target>", "MCP Caplet ID or qualified <caplet.prompt> target").argument("[prompt]", "prompt name when caplet is provided separately").option("--args <json-object>", "JSON object of prompt arguments").option("--format <format>", "output format: markdown, md, plain, or json", parseOutputFormat).action(async (capletOrTarget, promptArgument, options) => {
68260
+ const { caplet, tool: prompt } = parseQualifiedTarget(capletOrTarget, promptArgument);
68187
68261
  await executeOperation(caplet, {
68188
68262
  operation: "get_prompt",
68189
68263
  prompt,
@@ -68348,14 +68422,33 @@ function parseOutputFormat(value) {
68348
68422
  default: throw new CapletsError("REQUEST_INVALID", `Expected output format markdown, md, plain, or json; got ${value}`);
68349
68423
  }
68350
68424
  }
68351
- function parseQualifiedTarget(target) {
68352
- const dot = target.indexOf(".");
68353
- if (dot <= 0 || dot === target.length - 1) throw new CapletsError("REQUEST_INVALID", "Expected qualified target in the form <caplet>.<tool>");
68425
+ function parseQualifiedTarget(capletOrTarget, toolArgument) {
68426
+ if (toolArgument !== void 0) {
68427
+ if (capletOrTarget.length === 0 || toolArgument.length === 0) throw new CapletsError("REQUEST_INVALID", "Expected target in the form <caplet> <tool> or <caplet>.<tool>");
68428
+ return {
68429
+ caplet: capletOrTarget,
68430
+ tool: toolArgument
68431
+ };
68432
+ }
68433
+ const dot = capletOrTarget.indexOf(".");
68434
+ if (dot <= 0 || dot === capletOrTarget.length - 1) throw new CapletsError("REQUEST_INVALID", "Expected target in the form <caplet> <tool> or <caplet>.<tool>");
68354
68435
  return {
68355
- caplet: target.slice(0, dot),
68356
- tool: target.slice(dot + 1)
68436
+ caplet: capletOrTarget.slice(0, dot),
68437
+ tool: capletOrTarget.slice(dot + 1)
68357
68438
  };
68358
68439
  }
68440
+ async function completeCliWordsLocally(words, options) {
68441
+ const engine = new CapletsEngine({
68442
+ ...options.configPath ? { configPath: options.configPath } : {},
68443
+ ...options.authDir ? { authDir: options.authDir } : {},
68444
+ watch: false
68445
+ });
68446
+ try {
68447
+ return await engine.completeCliWords(words);
68448
+ } finally {
68449
+ await engine.close();
68450
+ }
68451
+ }
68359
68452
  function parseCallToolArgs(value) {
68360
68453
  if (value === void 0) return {};
68361
68454
  let parsed;
@@ -68780,7 +68873,7 @@ function writeAddResult(writeOut, label, result) {
68780
68873
  }
68781
68874
  //#endregion
68782
68875
  //#region package.json
68783
- var version = "0.17.1";
68876
+ var version = "0.17.3";
68784
68877
  //#endregion
68785
68878
  //#region src/index.ts
68786
68879
  async function main() {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "caplets",
3
- "version": "0.17.1",
3
+ "version": "0.17.3",
4
4
  "description": "Progressive disclosure gateway CLI for MCP servers and native Caplets adapters.",
5
5
  "keywords": [
6
6
  "caplets",
@@ -34,7 +34,7 @@
34
34
  },
35
35
  "dependencies": {
36
36
  "@modelcontextprotocol/sdk": "^1.29.0",
37
- "@caplets/core": "0.18.1"
37
+ "@caplets/core": "0.18.3"
38
38
  },
39
39
  "devDependencies": {
40
40
  "@types/node": "^25.9.1",