@agentskit/doc-bridge 1.4.0 → 1.4.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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,25 @@
1
1
  # Changelog
2
2
 
3
+ ## 1.4.3
4
+
5
+ ### Patch Changes
6
+
7
+ - Keep the release audit green by pinning the available `nanoid` fix and documenting
8
+ the upstream `extract-zip` advisory exception until its patched npm release exists.
9
+
10
+ ## 1.4.2
11
+
12
+ ### Patch Changes
13
+
14
+ - df61b17: Accept managed ecosystem products without a public repository by supporting `repo: null` and declaration-backed claims in the canonical ecosystem contract.
15
+
16
+ ## 1.4.1
17
+
18
+ ### Patch Changes
19
+
20
+ - ef543f6: Publish the portable handoff skill through an Agent Plugins v1 manifest for GitHub Copilot.
21
+ - 8b07f46: Package the portable handoff skill and credential-free MCP server as a Claude Code plugin.
22
+
3
23
  ## 1.4.0
4
24
 
5
25
  ### Minor Changes
package/README.md CHANGED
@@ -116,7 +116,7 @@ Using Cline? Follow the deterministic [`llms-install.md`](llms-install.md) setup
116
116
  |---------|------------|--------------------|
117
117
  | **CLI** | Inspect ownership, search docs, run gates, ask local questions | `ak-docs query`, `search`, `ask`, `doctor`, `gate` |
118
118
  | **MCP server** | Let Cursor, Claude Code, Codex-style agents resolve handoffs before editing | `ak-docs mcp`, `handoff.resolve` |
119
- | **GitHub Action / CI** | Fail stale indexes and broken human-doc links on PRs | `AgentsKit-io/doc-bridge@v1.3.0` |
119
+ | **GitHub Action / CI** | Fail stale indexes and broken human-doc links on PRs | `AgentsKit-io/doc-bridge@v1.4.0` |
120
120
  | **Documentation conformance** | Check the stable ecosystem standard with auditable evidence | `ak-docs conformance run documentation-standard-v1 --text` |
121
121
  | **Doc adapters** | Link human docs to agent docs | `fumadocs`, `docusaurus`, `vitepress`, `starlight`, `nextra`, `plain-markdown` |
122
122
  | **Monorepo routing** | Discover workspaces and checks | `pnpm-monorepo`, `nx` |
@@ -129,6 +129,14 @@ See [docs/getting-started.md](docs/getting-started.md), [docs/mcp.md](docs/mcp.m
129
129
 
130
130
  This repository also contains a Cursor plugin that pairs the read-only Doc Bridge MCP server with a handoff skill. It resolves `startHere`, `readBeforeEditing`, `editRoots`, and `checks` before Cursor edits a routed repository. The plugin does not request credentials or write project files through MCP.
131
131
 
132
+ ### GitHub Copilot plugin
133
+
134
+ The root Agent Plugins manifest exposes the same portable handoff skill and read-only MCP server to GitHub Copilot CLI. Copilot discovers `skills/` and `.mcp.json` from the standard plugin layout, so the integration stays source-owned instead of copying prompts into another repository.
135
+
136
+ ```bash
137
+ copilot plugin install AgentsKit-io/doc-bridge
138
+ ```
139
+
132
140
  ### Portable Agent Skill
133
141
 
134
142
  [`skills/doc-bridge-handoff`](skills/doc-bridge-handoff) packages the same fail-closed routing contract in the open Agent Skills layout for OpenClaw-compatible clients, Hermes Agent, Pi, Cursor, and other runtimes that can execute a local skill script. The skill prefers the read-only MCP tool and falls back to a pinned, zero-credential CLI resolver. It never edits files, runs returned checks, or grants authority outside `editRoots`.
@@ -246,7 +254,7 @@ permissions:
246
254
 
247
255
  steps:
248
256
  - uses: actions/checkout@v4
249
- - uses: AgentsKit-io/doc-bridge@v1.3.0
257
+ - uses: AgentsKit-io/doc-bridge@v1.4.0
250
258
  with:
251
259
  config-path: doc-bridge.config.json
252
260
  ```
@@ -332,7 +340,7 @@ ak-docs memory promote --pr # opens draft PR via gh
332
340
 
333
341
  ## Status
334
342
 
335
- **v1.3.0 stable** — deterministic Documentation Standard v1 conformance, VitePress, Starlight, Nextra, and Nx discovery, verified release provenance, Marketplace Action, doctor + CI + skill, and full Tier A/B/C.
343
+ **v1.4.0 stable** — portable, fail-closed handoffs for Cursor, Pi, Hermes, and ClawHub-compatible clients; deterministic Documentation Standard v1 conformance; verified release provenance; Marketplace Action; doctor + CI + skill; and full Tier A/B/C.
336
344
 
337
345
  ```bash
338
346
  pnpm install && pnpm build && pnpm test
package/action.yml CHANGED
@@ -20,7 +20,7 @@ inputs:
20
20
  package-version:
21
21
  description: Exact @agentskit/doc-bridge npm version (kept in sync with this Action release)
22
22
  required: false
23
- default: '1.4.0'
23
+ default: '1.4.3'
24
24
 
25
25
  runs:
26
26
  using: composite
@@ -356,7 +356,9 @@ var IndexConfigSchema = z.object({
356
356
  llmsTxt: z.object({
357
357
  enabled: z.boolean().optional(),
358
358
  outFile: z.string().min(1).max(512).optional(),
359
- preamble: z.string().max(4e3).optional()
359
+ preamble: z.string().max(4e3).optional(),
360
+ urlPrefix: z.string().url().optional(),
361
+ pathPrefix: z.string().min(1).max(512).optional()
360
362
  }).strict().optional(),
361
363
  capabilities: z.object({
362
364
  enabled: z.boolean().optional(),
@@ -692,7 +694,7 @@ var ProductSchema = z2.object({
692
694
  role: NonEmptyStringSchema,
693
695
  promise: NonEmptyStringSchema,
694
696
  maturity: z2.enum(["planning", "alpha", "beta", "stable", "deprecated"]),
695
- repo: RepoSchema,
697
+ repo: RepoSchema.nullable(),
696
698
  accent: z2.string().regex(/^#[0-9A-Fa-f]{6}$/),
697
699
  surfaces: SurfaceSchema,
698
700
  navigation: z2.object({
@@ -707,7 +709,7 @@ var LegacyPropertySchema = z2.object({
707
709
  barLabel: NonEmptyStringSchema,
708
710
  domain: NonEmptyStringSchema,
709
711
  url: HttpsUrlSchema,
710
- repo: RepoSchema,
712
+ repo: RepoSchema.nullable(),
711
713
  tagline: NonEmptyStringSchema,
712
714
  kind: NonEmptyStringSchema,
713
715
  accent: z2.string().regex(/^#[0-9A-Fa-f]{6}$/),
@@ -744,7 +746,8 @@ var ClaimProductSchema = z2.object({
744
746
  productId: SlugSchema,
745
747
  source: z2.discriminatedUnion("type", [
746
748
  z2.object({ type: z2.literal("endpoint"), url: HttpsUrlSchema }).passthrough(),
747
- z2.object({ type: z2.literal("repository"), repo: RepoSchema }).passthrough()
749
+ z2.object({ type: z2.literal("repository"), repo: RepoSchema }).passthrough(),
750
+ z2.object({ type: z2.literal("declaration"), summary: NonEmptyStringSchema }).passthrough()
748
751
  ]),
749
752
  verification: z2.enum(["verified", "declared"]),
750
753
  claims: z2.array(ClaimSchema)
@@ -821,8 +824,10 @@ var parseCanonicalEcosystemContract = (manifestInput, claimsInput) => {
821
824
  if (!claimProduct) throw new Error(`Claims are missing product ${productId}.`);
822
825
  if (claimProduct.source.type === "endpoint") {
823
826
  if (claimProduct.source.url !== product.surfaces.stats) throw new Error(`Claims source for ${productId} must match stats.`);
824
- } else if (claimProduct.source.repo !== product.repo) {
827
+ } else if (claimProduct.source.type === "repository" && claimProduct.source.repo !== product.repo) {
825
828
  throw new Error(`Claims source for ${productId} must match repo.`);
829
+ } else if (claimProduct.source.type === "declaration" && product.repo !== null) {
830
+ throw new Error(`Declaration source for ${productId} requires a null repo.`);
826
831
  }
827
832
  if (claimProduct.verification === "declared" && claimProduct.claims.length > 0) {
828
833
  throw new Error(`Declared product ${productId} cannot publish claims.`);
@@ -924,6 +929,9 @@ var defaultChecksForTarget = (root, opts) => {
924
929
  return opts.strict ? ["npm test", "npm run lint"] : ["npm test"];
925
930
  };
926
931
 
932
+ // src/index-builder/scan-corpus.ts
933
+ import { minimatch } from "minimatch";
934
+
927
935
  // src/lib/bounded-text.ts
928
936
  import { readFileSync as readFileSync3, statSync } from "fs";
929
937
  var MAX_DOCUMENT_BYTES = 4 * 1024 * 1024;
@@ -1084,10 +1092,20 @@ var walkFiles = (root, opts) => {
1084
1092
  };
1085
1093
 
1086
1094
  // src/index-builder/scan-corpus.ts
1095
+ var configuredPathMatches = (relPath, include, exclude) => {
1096
+ const normalize = (pattern) => toPosix(pattern).replace(/^\.\//, "");
1097
+ const included = include?.filter(Boolean).map(normalize) ?? [];
1098
+ const excluded = exclude?.filter(Boolean).map(normalize) ?? [];
1099
+ if (excluded.some((pattern) => minimatch(relPath, pattern, { dot: true }))) return false;
1100
+ return included.length === 0 || included.some((pattern) => minimatch(relPath, pattern, { dot: true }));
1101
+ };
1087
1102
  var scanAgentCorpus = (root, config) => {
1088
1103
  const agentRoot = containedProjectPath(root, config.corpus.agent.root);
1089
1104
  if (!agentRoot) throw new Error("Agent corpus root escapes the project root.");
1090
- const files = walkFiles(agentRoot, { extensions: [".md", ".mdx"] });
1105
+ const files = walkFiles(agentRoot, { extensions: [".md", ".mdx"] }).filter((abs) => {
1106
+ const relToCorpus = toPosix(abs.replace(`${toPosix(agentRoot)}/`, ""));
1107
+ return configuredPathMatches(relToCorpus, config.corpus.agent.include, config.corpus.agent.exclude);
1108
+ });
1091
1109
  const corpusRelRoot = toPosix(config.corpus.agent.root);
1092
1110
  const budget = { used: 0 };
1093
1111
  return files.map((abs) => {
@@ -1350,7 +1368,7 @@ var buildLookup = (config, packages, corpus, indexOutFile, humanDocs = {}, root
1350
1368
  };
1351
1369
 
1352
1370
  // src/version.ts
1353
- var PACKAGE_VERSION = "1.4.0";
1371
+ var PACKAGE_VERSION = "1.4.3";
1354
1372
 
1355
1373
  // src/index-builder/capabilities.ts
1356
1374
  var renderCapabilitiesJson = (config, index, paths) => {
@@ -1401,6 +1419,18 @@ var sha256NormalizedV1 = (payload) => {
1401
1419
  };
1402
1420
 
1403
1421
  // src/index-builder/llms-txt.ts
1422
+ var routePath = (path, pathPrefix) => {
1423
+ const normalizedPath = path.replaceAll("\\", "/").replace(/\.(?:md|mdx)$/, "");
1424
+ const normalizedPrefix = pathPrefix?.replaceAll("\\", "/").replace(/\/$/, "");
1425
+ const relativePath2 = normalizedPrefix && normalizedPath.startsWith(`${normalizedPrefix}/`) ? normalizedPath.slice(normalizedPrefix.length + 1) : normalizedPath;
1426
+ return relativePath2.replace(/^\/+/, "");
1427
+ };
1428
+ var knowledgeUrl = (path, options = {}) => {
1429
+ const relativePath2 = routePath(path, options.pathPrefix);
1430
+ if (!options.urlPrefix) return path;
1431
+ const base = options.urlPrefix.endsWith("/") ? options.urlPrefix : `${options.urlPrefix}/`;
1432
+ return new URL(relativePath2, base).toString();
1433
+ };
1404
1434
  var renderLlmsTxt = (config, knowledge, projectName2) => {
1405
1435
  const preamble = config.index?.llmsTxt?.preamble ?? `# ${projectName2}
1406
1436
 
@@ -1408,7 +1438,8 @@ var renderLlmsTxt = (config, knowledge, projectName2) => {
1408
1438
  `;
1409
1439
  const lines = knowledge.slice(0, 500).map((entry) => {
1410
1440
  const desc = entry.description ? `: ${entry.description}` : "";
1411
- return `- [${entry.title}](${entry.path})${desc}`;
1441
+ const url = knowledgeUrl(entry.path, config.index?.llmsTxt);
1442
+ return `- [${entry.title}](${url})${desc}`;
1412
1443
  });
1413
1444
  return `${preamble.trim()}
1414
1445
 
@@ -1640,11 +1671,23 @@ var nextraAdapter = {
1640
1671
  };
1641
1672
 
1642
1673
  // src/index-builder/human-adapters/plain-markdown.ts
1674
+ import { minimatch as minimatch2 } from "minimatch";
1675
+ var stringPatterns = (value) => Array.isArray(value) ? value.filter((item) => typeof item === "string" && item.length > 0) : [];
1676
+ var matchesConfiguredPath = (relPath, options) => {
1677
+ const normalize = (pattern) => pattern.replaceAll("\\", "/").replace(/^\.\//, "");
1678
+ const include = stringPatterns(options?.include).map(normalize);
1679
+ const exclude = stringPatterns(options?.exclude).map(normalize);
1680
+ if (exclude.some((pattern) => minimatch2(relPath, pattern, { dot: true }))) return false;
1681
+ return include.length === 0 || include.some((pattern) => minimatch2(relPath, pattern, { dot: true }));
1682
+ };
1643
1683
  var plainMarkdownAdapter = {
1644
1684
  plugin: "plain-markdown",
1645
1685
  scan: ({ root, config }) => {
1646
1686
  const humanRoot = optionString(config.options, ["contentDir", "root", "docsDir"]) ?? "docs";
1647
- return scanMarkdownDocs(root, humanRoot, { urlPrefix: config.options?.urlPrefix });
1687
+ return scanMarkdownDocs(root, humanRoot, {
1688
+ urlPrefix: config.options?.urlPrefix,
1689
+ includeRelPath: (relPath) => matchesConfiguredPath(relPath, config.options)
1690
+ });
1648
1691
  }
1649
1692
  };
1650
1693
 
@@ -1673,7 +1716,7 @@ var starlightAdapter = {
1673
1716
  };
1674
1717
 
1675
1718
  // src/index-builder/human-adapters/vitepress.ts
1676
- import { minimatch } from "minimatch";
1719
+ import { minimatch as minimatch3 } from "minimatch";
1677
1720
  var isVitePressPage = (relPath) => !relPath.split("/").some((part) => part === ".vitepress" || part.startsWith("."));
1678
1721
  var srcExcludePatterns = (value) => {
1679
1722
  if (value === void 0) return [];
@@ -1687,7 +1730,7 @@ var srcExcludePatterns = (value) => {
1687
1730
  return pattern;
1688
1731
  });
1689
1732
  };
1690
- var isExcluded = (relPath, patterns) => patterns.some((pattern) => minimatch(relPath, pattern, { dot: true }));
1733
+ var isExcluded = (relPath, patterns) => patterns.some((pattern) => minimatch3(relPath, pattern, { dot: true }));
1691
1734
  var vitepressSlug = (relPath, cleanUrls) => {
1692
1735
  const slug2 = routeSlug(relPath);
1693
1736
  if (cleanUrls || /(?:^|\/)index\.mdx?$/.test(relPath)) return slug2;