@anokye-labs/kbexplorer-engine 0.1.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.
@@ -0,0 +1,1396 @@
1
+ import { KBGraph, KBNode, Cluster, KnownEdgeType, EdgeType, KBEdge, KBConfig, SourceConfig, DisplayMode, Connection, NodeSourceFile, KBAccessLabel, ExternalProviderConfig } from '@anokye-labs/kbexplorer-core';
2
+ import { C as ContentModelSchema, D as Diagnostic, K as KindConvention, a as ContentModelSource, V as VocabularyOverlay, N as NodeLayer, G as GHIssue, E as EngineEnv, b as GHTreeItem, c as GHRelease, R as RepoSource, d as RepoData } from './repo-data-0JvFdLGv.js';
3
+ export { e as CompositeLeg, f as Conventions, g as DerivedRule, h as DiagnosticLevel, i as EdgeRule, j as EdgesSpec, F as FkFlavor, k as GHCommit, J as JsonLdContext, L as Lifecycle, l as LifecycleBand, m as NodeTypeDefinition, O as OrgDef, T as TeamOps, n as Vocabulary, o as getRegisteredTypes, p as hasType, r as registerBuiltInNodeTypes, q as registerType, s as resetNodeTypeRegistry, t as resolveNodeLayer, u as resolveType, v as resolveTypeCluster } from './repo-data-0JvFdLGv.js';
4
+ import { C as CacheStore, R as RepoManifest } from './build-manifest-BT0sY84J.js';
5
+ export { B as BuildManifestOptions, G as GHFileContent, b as buildManifest, f as fetchCommits, a as fetchFile, c as fetchFiles, d as fetchIssues, e as fetchPullRequests, g as fetchReleases, h as fetchTree } from './build-manifest-BT0sY84J.js';
6
+ import { G as GraphProvider, P as ProviderResult, a as ProviderRegistry, T as TransformContext, b as GraphTransform, S as SqliteByteStore } from './sqlite-runtime-CysPjjzX.js';
7
+ export { D as DEFAULT_TRANSFORMS, c as applyTransforms, i as issueDirectoryLinkTransform, d as issueSplitTransform, r as readmeTransform } from './sqlite-runtime-CysPjjzX.js';
8
+ import { IngestedNode } from '@anokye-labs/kbexplorer-provider-rich-markdown/lib';
9
+ import 'sql.js';
10
+
11
+ /**
12
+ * Graph engine: computes the knowledge graph from parsed nodes.
13
+ * Builds edges, clusters, related nodes, and layout positions.
14
+ */
15
+
16
+ /**
17
+ * Build the full knowledge graph from a list of nodes and cluster definitions.
18
+ *
19
+ * Access render-gate (#445): nodes whose access label marks them
20
+ * restricted/confidential (or explicitly unknown, or `visibility: private`)
21
+ * are withheld here — the single assembly choke point — so they never reach
22
+ * the network render, reading views, search index, or exports. Edges to a
23
+ * withheld node drop with it (`buildEdges` only emits edges whose target is
24
+ * in the node map). Unlabeled nodes are untouched.
25
+ */
26
+ declare function buildGraph(nodes: KBNode[], clusters: Cluster[]): KBGraph;
27
+ /** Get the degree (connection count) of each node. */
28
+ declare function getNodeDegrees(graph: KBGraph): Map<string, number>;
29
+ /** Find the hub node — prefer 'home', then 'readme', then 'overview', then most-connected. */
30
+ declare function getHubNodeId(graph: KBGraph): string | null;
31
+ /** Find the edge description between two nodes. */
32
+ declare function getEdgeDescription(graph: KBGraph, from: string, to: string): string | undefined;
33
+ /** Hard visibility limits for the rendered graph. */
34
+ declare const MAX_VISIBLE_NODES = 40;
35
+ declare const MAX_VISIBLE_EDGES = 80;
36
+ interface TrimResult {
37
+ graph: KBGraph;
38
+ trimmed: boolean;
39
+ totalNodes: number;
40
+ totalEdges: number;
41
+ }
42
+ /**
43
+ * Cap graph to MAX_VISIBLE_NODES / MAX_VISIBLE_EDGES.
44
+ * Selection strategy:
45
+ * 1. Always keep the hub node and current node
46
+ * 2. Reserve 1-hop neighbors of the current node
47
+ * 3. Ensure at least 1 node per cluster (cluster floor)
48
+ * 4. Fill remaining slots by degree (most connected first)
49
+ * 5. After node trim, cap edges — prefer current-node edges, then by weight
50
+ */
51
+ declare function trimGraphToLimits(graph: KBGraph, currentNodeId?: string | null, maxNodes?: number, maxEdges?: number): TrimResult;
52
+
53
+ /**
54
+ * Edge-type weight table + lookup — governs relevance ranking for
55
+ * `computeRelated` (in `./graph`) and default edge weights when a connection
56
+ * doesn't specify its own.
57
+ *
58
+ * Provenance note (slice 1/5, anokye-labs/kbexplorer-template#472): in
59
+ * kbexplorer-template this pure data + lookup function live inside
60
+ * `src/representation/styles.ts` alongside DOM-touching style helpers (one
61
+ * reads the live DOM's computed style), so that whole module can't move
62
+ * into this runtime-agnostic engine package. These two exports are
63
+ * extracted here **verbatim** (byte-identical values/logic, no behavior
64
+ * change) since `graph.ts` — a slice-1 module — depends on them at runtime.
65
+ * `representation/styles.ts` itself stays in template for now (its
66
+ * DOM-dependent parts are out of scope for every slice of this migration so
67
+ * far); flagged upstream in case the template side wants to re-export from
68
+ * here instead of keeping a duplicate copy.
69
+ */
70
+ declare const EDGE_TYPE_WEIGHTS: Record<KnownEdgeType, number>;
71
+ /** Resolve the layout weight for an edge type (open-safe). */
72
+ declare function getEdgeWeight(type: EdgeType | undefined): number;
73
+
74
+ /**
75
+ * Scriptable graph-query helpers (anokye-labs/kbexplorer-template#475).
76
+ *
77
+ * A small, pure, runtime-agnostic read API over a computed {@link KBGraph}.
78
+ * These helpers are the scripting surface: given a graph (from
79
+ * {@link loadKnowledgeBase} or {@link buildGraph}) they answer the common
80
+ * questions — "get this node", "find nodes matching X", "who are its
81
+ * neighbors", "what's related", "give me a neighborhood subgraph", "what's the
82
+ * shortest path" — without any DOM, environment, or provider dependency.
83
+ *
84
+ * They deliberately *reuse* the graph's existing indices rather than
85
+ * recomputing structure: {@link related} reads the precomputed
86
+ * `graph.related` map, and callers who need node degrees should use
87
+ * {@link getNodeDegrees} from `./graph` (this module never reimplements it).
88
+ * The only structure built here is a lightweight, per-call adjacency map for
89
+ * neighbor/path traversal, which the graph does not otherwise expose.
90
+ */
91
+
92
+ /** Traversal direction over the directed edge set. */
93
+ type Direction = 'out' | 'in' | 'both';
94
+ /** Options for {@link neighbors}. */
95
+ interface NeighborOptions {
96
+ /** Which edge directions to follow. Default `'both'`. */
97
+ direction?: Direction;
98
+ /** Restrict to one or more edge types (e.g. `'contains'`). Default: any type. */
99
+ edgeType?: EdgeType | EdgeType[];
100
+ }
101
+ /** Options for {@link subgraph}. */
102
+ interface SubgraphOptions {
103
+ /** How many hops to expand out from the seed(s). Default `1`. */
104
+ radius?: number;
105
+ /** Which edge directions to follow while expanding. Default `'both'`. */
106
+ direction?: Direction;
107
+ }
108
+ /** Options for {@link shortestPath}. */
109
+ interface ShortestPathOptions {
110
+ /** Which edge directions to follow. Default `'both'`. */
111
+ direction?: Direction;
112
+ }
113
+ /** Look up a single node by id. Returns `undefined` when absent. */
114
+ declare function getNode(graph: KBGraph, id: string): KBNode | undefined;
115
+ /**
116
+ * Return every node the predicate accepts, in graph order. The predicate
117
+ * receives each {@link KBNode} and returns whether to keep it.
118
+ */
119
+ declare function findNodes(graph: KBGraph, predicate: (node: KBNode) => boolean): KBNode[];
120
+ /**
121
+ * Directly-connected neighbor nodes of `id`. Honors edge direction and an
122
+ * optional edge-type filter. Unknown ids yield `[]`. Neighbor ids are
123
+ * de-duplicated and returned in first-seen edge order; ids without a resolvable
124
+ * node (e.g. dangling edge targets) are skipped.
125
+ */
126
+ declare function neighbors(graph: KBGraph, id: string, options?: NeighborOptions): KBNode[];
127
+ /**
128
+ * Related nodes for `id`, using the graph's precomputed `related` index
129
+ * (weight-ranked at build time). Ids in the index that no longer resolve to a
130
+ * node are skipped. Unknown ids yield `[]`.
131
+ */
132
+ declare function related(graph: KBGraph, id: string): KBNode[];
133
+ /**
134
+ * Extract the neighborhood {@link KBGraph} around one or more seed nodes,
135
+ * expanded `radius` hops. The result is a well-formed graph: `nodes` are the
136
+ * reachable set, `edges` are only those whose endpoints are both kept,
137
+ * `clusters` are filtered to those actually used, and `related` is rebuilt to
138
+ * reference only kept nodes. Unknown seeds contribute nothing.
139
+ */
140
+ declare function subgraph(graph: KBGraph, seeds: string | string[], options?: SubgraphOptions): KBGraph;
141
+ /**
142
+ * Breadth-first shortest path between two node ids, returned as the inclusive
143
+ * list of node ids from `from` to `to` (length 1 when `from === to`). Returns
144
+ * `null` when either endpoint is unknown or no path exists. `direction`
145
+ * controls how edges may be traversed.
146
+ */
147
+ declare function shortestPath(graph: KBGraph, from: string, to: string, options?: ShortestPathOptions): string[] | null;
148
+
149
+ /** Canonical schema-file locations relative to the content-model root. */
150
+ declare const SCHEMA_PATHS: {
151
+ readonly teamops: "teamops.yaml";
152
+ readonly conventions: "schema/conventions.yaml";
153
+ readonly edges: "schema/edges.yaml";
154
+ readonly lifecycle: "schema/lifecycle.yaml";
155
+ readonly context: "index/context.jsonld";
156
+ /**
157
+ * Optional cross-repo vocabulary / synonym overlay (#153): a JSON-LD
158
+ * `@context` mapping per-repo alias terms → a canonical kind. Absent in most
159
+ * repos, in which case the synonym layer is a safe no-op.
160
+ */
161
+ readonly vocabulary: "index/vocabulary.jsonld";
162
+ };
163
+ /**
164
+ * A content-model source is "present" iff the identity anchor (`teamops.yaml`)
165
+ * and the URN context (`index/context.jsonld`) both exist. When absent the
166
+ * provider must be a no-op so existing graphs are unchanged.
167
+ */
168
+ declare function hasContentModelSource(source: ContentModelSource | null | undefined): boolean;
169
+ /**
170
+ * Resolve an entity's declared term (`@type`) to its canonical kind via the
171
+ * cross-repo vocabulary. Returns the term unchanged when no alias is declared,
172
+ * so the layer is a **safe no-op** (output byte-identical to a build without it).
173
+ *
174
+ * Resolution is a single hop: a canonical target is expected to itself be a
175
+ * declared kind/CURIE prefix (not another alias).
176
+ */
177
+ declare function canonicalKind(schema: ContentModelSchema, term: string): string;
178
+ /**
179
+ * Read and parse all schema files from a content-model source.
180
+ * Always returns a schema (best-effort) plus any diagnostics encountered.
181
+ *
182
+ * An optional cross-repo {@link VocabularyOverlay} (#153) is merged on top of
183
+ * the repo's own `index/vocabulary.jsonld`. The overlay is the **shared layer
184
+ * supplied independently of any single repo's context**; when its terms collide
185
+ * with the repo-local file the overlay wins. With neither present the vocabulary
186
+ * is empty and the synonym layer is a safe no-op.
187
+ */
188
+ declare function readContentModelSchema(source: ContentModelSource, overlay?: VocabularyOverlay): {
189
+ schema: ContentModelSchema;
190
+ diagnostics: Diagnostic[];
191
+ };
192
+ /** Whether a kind is org-scoped (carries an `/{org}` URN segment). */
193
+ declare function isOrgScoped(schema: ContentModelSchema, kind: string): boolean;
194
+ /**
195
+ * Derive the local/display node id for a canonical content-model URN.
196
+ *
197
+ * Content-model nodes carry TWO distinct identifiers (#445 / AF-003):
198
+ * - `identity` — the canonical URN minted by {@link buildUrn} from the JSON-LD
199
+ * context (e.g. `kg://xbox.com/people/ada`), the cross-provider merge key;
200
+ * - `id` — this provider-local display key, derived deterministically from the
201
+ * URN by stripping its `<scheme>://` prefix (e.g. `xbox.com/people/ada`).
202
+ *
203
+ * The mapping is a pure 1:1 function of the URN, so any consumer (viewers,
204
+ * link resolution) can recover a node's graph id from a resolved URN without
205
+ * access to the node set. Delegates to core's `stripScheme` (idempotent when
206
+ * no scheme is present).
207
+ */
208
+ declare function urnLocalId(urn: string): string;
209
+ /**
210
+ * Build a canonical URN for an entity.
211
+ *
212
+ * The URN **base** is read from the JSON-LD context (never hardcoded). For
213
+ * org-scoped kinds the org segment is spliced in after the base, defaulting to
214
+ * the home org from `teamops.yaml`:
215
+ *
216
+ * org-scoped: `{base}{org}/{id}` → `kg://xbox.com/squads/personalization/game-assist`
217
+ * authority-scoped: `{base}{id}` → `kg://xbox.com/people/ada`
218
+ *
219
+ * Returns `null` (and pushes a diagnostic) when the kind has no context prefix.
220
+ */
221
+ declare function buildUrn(schema: ContentModelSchema, kind: string, id: string, org?: string, diagnostics?: Diagnostic[]): string | null;
222
+ /**
223
+ * Resolve a CURIE (`prefix:local`) to a URN. Already-expanded URNs (containing
224
+ * `://`) are returned unchanged. Org-scoped prefixes use the optional `org`
225
+ * (defaulting to the home org).
226
+ *
227
+ * @example resolveCurie(schema, 'squad:game-assist') // kg://xbox.com/squads/personalization/game-assist
228
+ */
229
+ declare function resolveCurie(schema: ContentModelSchema, curie: string, opts?: {
230
+ org?: string;
231
+ diagnostics?: Diagnostic[];
232
+ }): string | null;
233
+ /** Look up the lifecycle band a kind belongs to (e.g. `mission` → `per-cycle`). */
234
+ declare function lifecycleBand(schema: ContentModelSchema, kind: string): string | undefined;
235
+ /** Get the storage convention for a kind. */
236
+ declare function getConvention(schema: ContentModelSchema, kind: string): KindConvention | undefined;
237
+
238
+ /** Provider id under which content-model nodes are emitted. */
239
+ declare const CONTENT_MODEL_PROVIDER = "content-model";
240
+ /** Result of a content-model build. */
241
+ interface ContentModelGraph {
242
+ nodes: KBNode[];
243
+ edges: KBEdge[];
244
+ diagnostics: Diagnostic[];
245
+ }
246
+ /**
247
+ * Build the content-model graph from a source. Returns empty results (a safe
248
+ * no-op) when no content-model source is present.
249
+ *
250
+ * An optional cross-repo {@link VocabularyOverlay} (#153) — a shared synonym
251
+ * layer supplied independently of any single repo's context — is merged on top
252
+ * of the source's own `index/vocabulary.jsonld` so repos using different words
253
+ * for the same concept unify to one canonical kind.
254
+ */
255
+ declare function buildContentModel(source: ContentModelSource | null | undefined, vocabularyOverlay?: VocabularyOverlay): ContentModelGraph;
256
+
257
+ /**
258
+ * Content-model registration hook (F2 / T2.5 + T2.6 — issues #164, #165).
259
+ *
260
+ * Registers the spine node types (Person, Squad, Workstream, Mission, Priority,
261
+ * Cycle, Org) in the node-type registry and binds each to its bespoke viewer in
262
+ * the viewer registry. Both registries are open seams, so this adds the kinds
263
+ * without touching any core union or render switch.
264
+ *
265
+ * Idempotent: registering the same id twice replaces the prior entry.
266
+ */
267
+
268
+ interface SpineKind {
269
+ id: string;
270
+ label: string;
271
+ layer: NodeLayer;
272
+ relations: string[];
273
+ viewer: string;
274
+ description: string;
275
+ }
276
+ /** The content-model spine kinds and the viewer each resolves to. */
277
+ declare const CONTENT_MODEL_KINDS: SpineKind[];
278
+ /** Register every spine node type + its bespoke viewer name. Idempotent. */
279
+ declare function registerContentModelTypes(): void;
280
+
281
+ declare function parseMarkdownFile(path: string, raw: string): KBNode;
282
+ /** Load authored content from a content directory in the repo. */
283
+ declare function loadAuthoredContent(source: SourceConfig, contentPath: string, env?: EngineEnv, cache?: CacheStore): Promise<KBNode[]>;
284
+ /** Extract issue cross-references (#N) from body text. */
285
+ declare function extractIssueRefs(body: string | null): number[];
286
+ /**
287
+ * Options for {@link issueToNode}. When `knownNumbers` is provided, only `#N`
288
+ * references that resolve to an existing issue or pull request emit an edge —
289
+ * this kills the phantom cross-reference edges that otherwise inflate the
290
+ * graph by hundreds of dangling targets (#NNN refs to PRs/issues that don't
291
+ * exist in this manifest).
292
+ */
293
+ interface IssueToNodeOptions {
294
+ /** Set of valid issue numbers in this repo (for filtering #N cross-refs). */
295
+ knownIssueNumbers?: Set<number>;
296
+ /** Set of valid PR numbers in this repo (for filtering #N cross-refs). */
297
+ knownPrNumbers?: Set<number>;
298
+ /** Repository node id — issue is linked to this with a `tracked-in` edge. */
299
+ repoNodeId?: string;
300
+ }
301
+ declare function issueToNode(issue: GHIssue, options?: IssueToNodeOptions): KBNode;
302
+ /** Split a markdown file into parent + section nodes at ## headings. */
303
+ declare function splitIntoSections(parentId: string, parentTitle: string, rawContent: string, cluster: string, emoji: string, source: KBNode['source'], allNodes: KBNode[]): KBNode[];
304
+ /** Build nodes from the file tree: repo root + directories + key files. */
305
+ declare function treeToNodes(tree: GHTreeItem[], repoName: string, excludePaths?: string[]): KBNode[];
306
+ /** Load repo-aware content: issues, README, and directory structure. */
307
+ declare function loadRepoContent(source: SourceConfig, env?: EngineEnv, cache?: CacheStore): Promise<KBNode[]>;
308
+ /** Extract cluster definitions from nodes + config. */
309
+ declare function extractClusters(nodes: KBNode[], config: KBConfig): Cluster[];
310
+ /** Try to load config.yaml from the repo. Falls back to DEFAULT_CONFIG. */
311
+ declare function loadConfig(source: SourceConfig, env?: EngineEnv, cache?: CacheStore): Promise<KBConfig>;
312
+
313
+ /**
314
+ * Minimal, Node-safe default {@link KBConfig} used as `loadConfig`'s fallback
315
+ * when a repo has no `config.yaml` (or it fails to load/parse).
316
+ *
317
+ * This is kept field-for-field identical to kbexplorer-template's
318
+ * `DEFAULT_CONFIG` (`src/types/index.ts`), with exactly one disclosed,
319
+ * intentional difference: template's `title` and `source` fields are backed
320
+ * by a Vite build-time env-injection read (`VITE_KB_TITLE`/`VITE_KB_OWNER`/
321
+ * `VITE_KB_REPO`/`VITE_KB_BRANCH`/`VITE_KB_PATH`, via `resolveDefaultSource`)
322
+ * that this package's `tests/boundary.test.ts` forbids and that this
323
+ * runtime-agnostic engine package must not depend on regardless (it should
324
+ * run under Node as well as a Vite-bundled browser app). Both fields here use
325
+ * exactly the same *fallback* values template's own functions already fall
326
+ * back to when those env vars are unset, so a repo with no Vite env
327
+ * configured gets byte-identical behavior either way. Callers that need
328
+ * environment-driven overrides (e.g. a `VITE_KB_*`-aware host app) should
329
+ * apply them on top of this before/after `loadConfig` merges its own
330
+ * `config.yaml` values in.
331
+ *
332
+ * `src/__tests__/default-config.test.ts` asserts this stays in sync with
333
+ * template's `DEFAULT_CONFIG` (minus the one disclosed exception above) so
334
+ * future drift between the two repos is caught by CI instead of silently
335
+ * propagating once slice 4 wires `loadConfig` to this fallback.
336
+ */
337
+ declare const DEFAULT_CONFIG: KBConfig;
338
+
339
+ /**
340
+ * nodemap.yaml parser — reads a nodemap definition and produces KBNode[].
341
+ *
342
+ * The nodemap defines how repository files, directories, and globs map
343
+ * to knowledge-graph nodes. This module is standalone: callers provide
344
+ * I/O callbacks so it works with both GitHub API and local filesystem.
345
+ */
346
+
347
+ /** A single entry in nodemap.yaml */
348
+ interface NodeMapEntry {
349
+ id: string;
350
+ title?: string;
351
+ emoji?: string;
352
+ cluster?: string;
353
+ display?: DisplayMode;
354
+ connections?: 'imports' | 'references' | Connection[];
355
+ exclude?: string[];
356
+ file?: string;
357
+ files?: string[];
358
+ glob?: string;
359
+ directory?: string;
360
+ split?: 'headings';
361
+ each?: 'file';
362
+ titleFrom?: 'filename' | 'heading';
363
+ }
364
+ /** Parsed nodemap.yaml */
365
+ interface NodeMap {
366
+ nodes: NodeMapEntry[];
367
+ }
368
+ /** Resolve a relative import path against the importing file's location. */
369
+ declare function resolveImportPath(importPath: string, fromFile: string): string;
370
+ /** Extract relative import/require paths from source code. */
371
+ declare function extractImportPaths(content: string, fromFile: string): string[];
372
+ /**
373
+ * Load and process a nodemap.yaml file.
374
+ * Returns KBNode[] for all mapped entries.
375
+ */
376
+ declare function loadNodeMap(nodemapRaw: string, readFile: (path: string) => Promise<string | null>, listFiles?: (pattern: string) => Promise<string[]>, listDirectory?: (dir: string) => Promise<{
377
+ path: string;
378
+ type: 'blob' | 'tree';
379
+ size?: number;
380
+ }[]>): Promise<KBNode[]>;
381
+
382
+ /**
383
+ * Identity URN helpers — canonical identifiers that link node
384
+ * representations across providers and layers.
385
+ *
386
+ * This module is the template's ONE identity-construction mechanism
387
+ * (issue #445 / AF-003): every `urn:` identity string is minted by
388
+ * {@link urnIdentity}, and {@link assignIdentity} is the single entry point
389
+ * that decides a node's identity from its {@link NodeSource}.
390
+ *
391
+ * ## Relationship to core's addressing library (`buildAddress` — AF-025)
392
+ *
393
+ * kbexplorer-core v0.3.0 ships an addressing library (`buildAddress` /
394
+ * `buildPersonAddress`) whose canonical form is `<scheme>://[<authority>/]<body>`
395
+ * — the separator is always `://`. The template's documented identity scheme
396
+ * (content/multi-layer-identity.md, ratified in #47) is the single-colon form
397
+ * `urn:<namespace>:<body>` (e.g. `urn:file:src/engine/graph.ts`). Core's
398
+ * `IdentityAddressingConfig` cannot reproduce that shape:
399
+ * `buildAddress('src/engine/graph.ts', { scheme: 'urn', authority: 'file' })`
400
+ * → `urn://file/src/engine/graph.ts` ≠ `urn:file:src/engine/graph.ts`.
401
+ * Migrating the documented values would break every persisted identity
402
+ * (nodemap `file:` links, goldens, downstream consumers), so — per the
403
+ * reconciliation contract recorded on #445 — the `urn:` shapes are preserved
404
+ * and ALL construction routes through the shared local minting helper below.
405
+ * Schema-minted addresses (content-model `kg://` URNs from `context.jsonld`)
406
+ * are reused verbatim via the JSON-LD `@id` (the core contract: an identity
407
+ * address is ALWAYS reused as a node's `@id`).
408
+ */
409
+
410
+ /**
411
+ * Mint a template-scheme identity URN: `urn:<namespace>:<body>`.
412
+ *
413
+ * The single `urn:` construction point (see module header). Namespaces in use:
414
+ * `file`, `content`, `issue`, `pr`, `commit`, `release`, `person`,
415
+ * `structural`, `structured`, `external`.
416
+ */
417
+ declare function urnIdentity(namespace: string, body: string | number): string;
418
+ /**
419
+ * Join multiple parts into an unambiguous (injective) URN body.
420
+ *
421
+ * Each part is percent-encoded before being joined with `:`, so the separator
422
+ * we insert is the ONLY literal colon in the result and `%` can only originate
423
+ * from the encoding. Distinct part tuples therefore always produce distinct
424
+ * bodies. This guards composite identities such as
425
+ * `urn:external:<provider>:<id>` against collision when a `provider` or `id`
426
+ * itself contains a `:` (or `%`): without encoding, `('a', 'b:c')` and
427
+ * `('a:b', 'c')` both collapse to `a:b:c`, silently conflating two distinct
428
+ * real-world entities once the cross-provider merge machinery runs.
429
+ *
430
+ * `encodeURIComponent` leaves the unreserved set (`A–Z a–z 0–9 - _ . ! ~ * ' ( )`)
431
+ * untouched, so non-pathological slugs (`wikipedia-reference`, `org-ceo`,
432
+ * `wiki-knowledge-graph`) are unchanged and existing valid identities do not
433
+ * churn.
434
+ */
435
+ declare function urnBody(...parts: Array<string | number>): string;
436
+ /**
437
+ * Generate a canonical identity URN for a node based on its source.
438
+ *
439
+ * Coverage notes:
440
+ * - `authored` — the frontmatter id doubles as the content key. Rich-Markdown
441
+ * authored docs (AuthoredRichMarkdownProvider) also carry an `authored`
442
+ * source, so they resolve here identically to plain authored docs and merge
443
+ * with other representations of the same content.
444
+ * - `structured` — registry-driven nodes. Schema-minted nodes (content-model
445
+ * entities) carry their canonical address as the JSON-LD `@id`; it is reused
446
+ * verbatim so the schema's addressing (`buildUrn` / `context.jsonld`) and
447
+ * this mechanism cannot drift. Structured nodes without an LD address get no
448
+ * identity from the source alone (their namespace is producer-scoped —
449
+ * `urn:structural:` vs `urn:structured:` — so producers mint it via
450
+ * {@link urnIdentity} before or instead of calling this).
451
+ * - `external` — provider-scoped: `urn:external:<provider>:<node id>`.
452
+ * Deterministic (provider ids derive from config). The `provider` and node
453
+ * `id` parts are percent-encoded via {@link urnBody} before joining, so the
454
+ * composition is injective: two distinct external (provider, id) pairs can
455
+ * never collide even when a part contains the `:` separator (which would
456
+ * otherwise let `('a','b:c')` and `('a:b','c')` conflate two real entities).
457
+ * - `person` — the stable, source-agnostic alias when present, else the
458
+ * GitHub login (back-compat: existing values used `login`).
459
+ * - `section` / `branch` / `repository` / `derived` — no identity: these are
460
+ * either sub-node projections or provider-local structural artifacts with
461
+ * no cross-provider counterpart to merge with.
462
+ */
463
+ declare function assignIdentity(node: KBNode): string | undefined;
464
+ /** Check if two nodes share an identity. */
465
+ declare function shareIdentity(a: KBNode, b: KBNode): boolean;
466
+ /**
467
+ * Build an identity index — maps identity URNs to all node IDs that share them.
468
+ * Used by the view system to merge representations.
469
+ */
470
+ declare function buildIdentityIndex(nodes: KBNode[]): Map<string, string[]>;
471
+
472
+ type StructuredFormat = 'json' | 'yaml';
473
+ /** A structured file handed to the mapper. */
474
+ interface StructuredFile {
475
+ path: string;
476
+ content: string;
477
+ }
478
+ /** An edge the produced node should carry (mapped onto a `Connection`). */
479
+ interface NodeMapEdgeRule {
480
+ /** Target node id. */
481
+ to: string;
482
+ /** Taxonomy relation (e.g. `structural`). */
483
+ relation?: string;
484
+ /** Structural edge type (defaults to `references` downstream). */
485
+ type?: string;
486
+ description?: string;
487
+ }
488
+ /** A single declarative mapping rule. */
489
+ interface NodeMapRule {
490
+ /** Informational rule id. */
491
+ id?: string;
492
+ /** Glob(s) the file path must match (any-of). Omit to match every path. */
493
+ glob?: string | string[];
494
+ /** Top-level keys the parsed object must all contain (shape match). */
495
+ shape?: string[];
496
+ /** JSON-LD `@type` assigned to matched files. */
497
+ type: string;
498
+ /** Registry `entityType`; defaults to a slug of `type`. */
499
+ entityType?: string;
500
+ cluster?: string;
501
+ emoji?: string;
502
+ /** Dot-path into the parsed data used as the node title. */
503
+ titleFrom?: string;
504
+ /**
505
+ * Promote selected parsed values into the JSON-LD envelope:
506
+ * `{ outputProp: 'dot.path.in.data' }`. The full parsed object is always
507
+ * retained on `node.data` regardless, so mapping stays reversible.
508
+ */
509
+ fields?: Record<string, string>;
510
+ /** Edges emitted from the produced node. */
511
+ edges?: NodeMapEdgeRule[];
512
+ }
513
+ /** Parsed `structured-node-map.yaml`. */
514
+ interface StructuredNodeMap {
515
+ rules: NodeMapRule[];
516
+ }
517
+ /** Options controlling node identity/cluster when applying a map. */
518
+ interface ApplyOptions {
519
+ /** Explicit node id (overrides the path-derived default). */
520
+ id?: string;
521
+ /** Prefix for the path-derived id (default `cfg`). */
522
+ idPrefix?: string;
523
+ /** Fallback cluster when neither rule nor heuristic provides one. */
524
+ cluster?: string;
525
+ }
526
+ /** Deterministic, url-safe slug for ids. */
527
+ declare function slugify(value: string): string;
528
+ /**
529
+ * Parse a structured file's content into `{ format, data }`. Returns `null`
530
+ * when the content is not structured object/array data (e.g. plain prose,
531
+ * binary, or an empty file) — such files are not this module's concern.
532
+ */
533
+ declare function parseStructuredContent(file: StructuredFile): {
534
+ format: StructuredFormat;
535
+ data: Record<string, unknown> | unknown[];
536
+ } | null;
537
+ /**
538
+ * Heuristic fallback for an UNMAPPED structured file: parse → infer a sensible
539
+ * `@type` from its shape → produce a typed node whose `data` retains the full
540
+ * parsed object (so it stays reversible). Returns `null` for non-structured
541
+ * content.
542
+ */
543
+ declare function inferStructuredNode(file: StructuredFile, parsed?: {
544
+ format: StructuredFormat;
545
+ data: Record<string, unknown> | unknown[];
546
+ } | null, options?: ApplyOptions): KBNode | null;
547
+ /**
548
+ * Map a structured file to a typed node. A matching declarative rule wins;
549
+ * otherwise the heuristic fallback runs. Returns `null` only when the file is
550
+ * not structured object/array data.
551
+ */
552
+ declare function applyStructuredNodeMap(file: StructuredFile, map: StructuredNodeMap | null | undefined, options?: ApplyOptions): KBNode | null;
553
+ /** Parse a `structured-node-map.yaml` file into a normalised {@link StructuredNodeMap}. */
554
+ declare function parseStructuredNodeMap(raw: string | null | undefined): StructuredNodeMap;
555
+ /**
556
+ * Re-serialise the original source content from a node produced by this module.
557
+ * Reversibility is *semantic*: re-parsing the output yields the same object that
558
+ * was stored on `node.data`. The format is inferred from the node's
559
+ * `source.ref` extension unless overridden.
560
+ */
561
+ declare function reconstructSource(node: Pick<KBNode, 'data' | 'source'>, formatOverride?: StructuredFormat): string;
562
+
563
+ declare const DEFAULT_STRUCTURED_CONTENT_PATH = "content-model";
564
+ interface StructuredContentConfig {
565
+ path?: string;
566
+ }
567
+ type EnvLike = EngineEnv;
568
+ declare function normalizeRepoRelativeDir(raw: unknown): string | null;
569
+ declare function resolveStructuredContentPath(config: KBConfig, env?: EnvLike): string;
570
+ declare function hasExplicitStructuredContentPath(config: KBConfig, env?: EnvLike): boolean;
571
+
572
+ /** GitHub repository coordinates needed to build deep links. */
573
+ interface RepoCoords {
574
+ owner: string;
575
+ repo: string;
576
+ branch: string;
577
+ }
578
+ /** Resolve repo coordinates from the app config (branch defaults to `main`). */
579
+ declare function repoCoordsFromConfig(config: Pick<KBConfig, 'source'>): RepoCoords;
580
+ /**
581
+ * Whether a node exposes an editable source-of-truth file. This is the single
582
+ * gate for the editor affordance: nodes without a resolvable writable file
583
+ * (README, derived, structural, unresolved stubs…) return `false`, so the
584
+ * editor simply never appears for them — a safe no-op.
585
+ */
586
+ declare function canEditSource(node: Pick<KBNode, 'sourceFile'>): boolean;
587
+ /** Return a node's source-of-truth file pointer, or `null` when it has none. */
588
+ declare function resolveSourceFile(node: Pick<KBNode, 'sourceFile'>): NodeSourceFile | null;
589
+ /** Result of validating edited source content against its declared format. */
590
+ type ValidationResult = {
591
+ ok: true;
592
+ } | {
593
+ ok: false;
594
+ error: string;
595
+ };
596
+ /**
597
+ * Normalise newlines to `\n`. Files checked out on Windows carry CRLF while a
598
+ * browser `<textarea>` always emits LF, so comparisons and diffs must be
599
+ * newline-agnostic — otherwise a one-line edit looks like a whole-file rewrite.
600
+ */
601
+ declare function normalizeNewlines(text: string): string;
602
+ /**
603
+ * Validate that edited content parses as its declared format **before** any
604
+ * handoff, so a user never opens a PR carrying invalid YAML/JSON. Empty content
605
+ * is rejected (an entity file must contain content). `markdown` is accepted
606
+ * as always-valid (there is nothing to parse) — `canEditSource` currently only
607
+ * surfaces the editor for `yaml`/`json`, but accepting the full
608
+ * `NodeSourceFile['format']` union keeps this forward-compatible and total.
609
+ */
610
+ declare function validateSourceContent(raw: string, format: NodeSourceFile['format']): ValidationResult;
611
+ /** Encode a repo-relative path for use inside a URL path (segment-wise). */
612
+ declare function encodeRepoPath(path: string): string;
613
+ /**
614
+ * GitHub web-editor URL for an **existing** file. Opening it shows GitHub's
615
+ * authenticated editor for the current file; the user pastes the edited content
616
+ * and commits it to a new branch + PR.
617
+ */
618
+ declare function buildEditUrl(coords: RepoCoords, path: string): string;
619
+ /**
620
+ * GitHub create-file URL for a **new** file, pre-filled with the target path and
621
+ * the edited content via the `filename` + `value` query params. GitHub commits
622
+ * it to a new branch and offers to open a PR.
623
+ */
624
+ declare function buildNewFileUrl(coords: RepoCoords, path: string, content: string): string;
625
+ /**
626
+ * Pick the correct GitHub deep link for the handoff: the create-file URL
627
+ * (content pre-filled) when the file is new, otherwise the web-editor URL for
628
+ * the existing file.
629
+ */
630
+ declare function buildHandoffUrl(coords: RepoCoords, path: string, content: string, exists: boolean): string;
631
+ /**
632
+ * Build a git-style unified diff for the change, suitable for downloading as a
633
+ * `.patch`. Returns an empty string when nothing changed.
634
+ *
635
+ * Pass `isNew` for a file that does not yet exist in the repo so the patch uses
636
+ * the git new-file headers (`new file mode` + `--- /dev/null`); a patch with
637
+ * `--- a/<path>` for a non-existent file would fail to apply.
638
+ */
639
+ declare function buildUnifiedDiff(path: string, oldText: string, newText: string, context?: number, isNew?: boolean): string;
640
+ /** Suggested filename for a downloaded `.patch` (basename of the source file). */
641
+ declare function patchFilename(path: string): string;
642
+ /** Everything the UI needs to hand a source edit off to GitHub. */
643
+ interface SourceEditHandoff {
644
+ /** Whether the edited content differs from the original. */
645
+ changed: boolean;
646
+ /** Whether the source file already exists in the repo. */
647
+ exists: boolean;
648
+ /** Primary GitHub deep link to open the change as a PR. */
649
+ url: string;
650
+ /** GitHub web-editor URL for the existing file. */
651
+ editUrl: string;
652
+ /** GitHub create-file URL pre-filled with the edited content. */
653
+ newFileUrl: string;
654
+ /** Unified diff suitable for a downloadable `.patch` (empty when unchanged). */
655
+ patch: string;
656
+ /** Suggested filename for the downloaded patch. */
657
+ patchName: string;
658
+ }
659
+ /**
660
+ * Assemble the full handoff for an edited source file. `exists` defaults to
661
+ * `true` because the editor edits files that were loaded from the repo; pass
662
+ * `false` for a brand-new entity so the content-pre-filled create-file URL is
663
+ * used as the primary link.
664
+ */
665
+ declare function buildSourceEditHandoff(coords: RepoCoords, file: NodeSourceFile, newContent: string, exists?: boolean): SourceEditHandoff;
666
+
667
+ /**
668
+ * Minimal access render-gate (#445, spec item 4) over core's hoisted access
669
+ * exclusion contract.
670
+ *
671
+ * This template's minimal enforcement still withholds labeled-sensitive nodes
672
+ * from the assembled graph and search index. We preserve one deliberate
673
+ * template-only difference versus core's default-safe boundary: bespoke
674
+ * classifications such as `bespoke-scheme` remain non-withheld, because the
675
+ * template's current gate only withholds the known sensitive classifications
676
+ * from the 0.3 label-only contract.
677
+ */
678
+
679
+ /**
680
+ * True when a node's access label marks it sensitive enough to withhold from
681
+ * render + search. Absent label → `false` (public, unchanged).
682
+ */
683
+ declare function isAccessWithheld(node: Pick<KBNode, 'access'>): boolean;
684
+ /** Drop withheld nodes from a node list (identity when nothing is labeled). */
685
+ declare function filterAccessWithheld(nodes: KBNode[]): KBNode[];
686
+ /**
687
+ * Defensively parse an authored-frontmatter `access` value into a
688
+ * {@link KBAccessLabel}. Frontmatter is untrusted: only well-typed
689
+ * `classification` / `visibility` strings and string `labels` survive.
690
+ * Returns `undefined` when nothing usable is present (node stays unlabeled).
691
+ */
692
+ declare function parseAccessLabel(value: unknown): KBAccessLabel | undefined;
693
+
694
+ /**
695
+ * Render markdown to HTML with untrusted-content defenses (see module header):
696
+ * `marked.parse` produces HTML, which is then filtered through the allowlist
697
+ * sanitizer. Drop-in replacement for `marked.parse(body, { async: false })`.
698
+ */
699
+ declare function renderSafeMarkdown(body: string): string;
700
+
701
+ /** Convert a simple glob pattern to a RegExp for matching file paths. */
702
+ declare function globToRegex(pattern: string): RegExp;
703
+
704
+ /**
705
+ * Authored Provider — wraps authored-content parsing (markdown files + nodemap)
706
+ * into a GraphProvider so the engine can orchestrate it alongside other providers.
707
+ */
708
+
709
+ declare class AuthoredProvider implements GraphProvider {
710
+ id: string;
711
+ name: string;
712
+ dependencies: string[];
713
+ private authoredContent;
714
+ private nodemapRaw?;
715
+ private nodemapFiles?;
716
+ private nodemapDirs?;
717
+ private listFiles?;
718
+ constructor(authoredContent: Record<string, string>, nodemapRaw?: string | null, nodemapFiles?: Record<string, string>, nodemapDirs?: Record<string, Array<{
719
+ path: string;
720
+ type: 'blob' | 'tree';
721
+ size?: number;
722
+ }>>, listFiles?: (pattern: string) => Promise<string[]>);
723
+ resolve(_config: KBConfig, _existingNodes: KBNode[]): Promise<ProviderResult>;
724
+ }
725
+
726
+ /**
727
+ * Adapt the package's ingested node into a template {@link KBNode} the renderer
728
+ * understands. Preserves connections/jsonld/sourceFile from the package and
729
+ * re-shapes only what the template's rich-Markdown contract requires.
730
+ *
731
+ * Identity is NOT passed through verbatim (the unreconciled pass-through was
732
+ * #445's AF-003 / audit finding on PR #432): the node's `id` is the stable
733
+ * local slug, and `identity` is assigned by the template's single mechanism
734
+ * (`assignIdentity` — an `authored` source resolves to `urn:content:<id>`), so
735
+ * a doc that opts into rich-Markdown carries exactly the identity it would
736
+ * have had as plain authored content and merges with other representations of
737
+ * the same content. The package's own `kg://` address remains available in the
738
+ * package output; the template does not carry two competing schemes.
739
+ */
740
+ declare function adaptIngestedNode(ingested: IngestedNode): KBNode;
741
+ declare class AuthoredRichMarkdownProvider implements GraphProvider {
742
+ id: string;
743
+ name: string;
744
+ dependencies: string[];
745
+ private authoredContent;
746
+ constructor(authoredContent: Record<string, string>);
747
+ resolve(_config: KBConfig, _existingNodes: KBNode[]): Promise<ProviderResult>;
748
+ }
749
+
750
+ /**
751
+ * ContentModelProvider (F2 / T2.2 + T2.4 — issues #161, #163).
752
+ *
753
+ * Wraps the schema-driven {@link buildContentModel} pipeline as a
754
+ * {@link GraphProvider}. Relationships are emitted as `connections` on the source
755
+ * nodes (the orchestrator ignores a provider's `edges` and `buildGraph` derives
756
+ * edges from `connections`), so the resolved edges render in the graph.
757
+ *
758
+ * **Safe no-op when no content-model source is present** — it returns no nodes,
759
+ * so existing graphs (this repo has no content-model source) are unchanged.
760
+ */
761
+
762
+ declare class ContentModelProvider implements GraphProvider {
763
+ id: string;
764
+ name: string;
765
+ dependencies: string[];
766
+ private source;
767
+ /**
768
+ * Optional cross-repo synonym overlay (#153) supplied independently of the
769
+ * source's own files — the shared vocabulary layer. Null/absent leaves the
770
+ * synonym layer a safe no-op.
771
+ */
772
+ private vocabularyOverlay;
773
+ constructor(source: ContentModelSource | null | undefined, vocabularyOverlay?: VocabularyOverlay);
774
+ resolve(_config: KBConfig, _existingNodes: KBNode[]): Promise<ProviderResult>;
775
+ }
776
+
777
+ /**
778
+ * Files Provider — wraps treeToNodes() into a GraphProvider.
779
+ * Produces file-tree nodes (repo root, directories, key source files).
780
+ * Edges are implicit via `contains` connections on each node.
781
+ */
782
+
783
+ declare class FilesProvider implements GraphProvider {
784
+ id: string;
785
+ name: string;
786
+ dependencies: string[];
787
+ private treeItems;
788
+ private repoName;
789
+ private excludePaths?;
790
+ constructor(treeItems: GHTreeItem[], repoName: string, excludePaths?: string[]);
791
+ resolve(_config: KBConfig, _existingNodes: KBNode[]): Promise<ProviderResult>;
792
+ }
793
+
794
+ /**
795
+ * OrgChartProvider — creates an organizational chart as graph nodes.
796
+ *
797
+ * Config in config.yaml:
798
+ * providers:
799
+ * - type: orgchart
800
+ * name: Team Structure
801
+ * cluster: team
802
+ * options:
803
+ * people:
804
+ * - id: ceo
805
+ * name: Jane Smith
806
+ * role: CEO
807
+ * reports: []
808
+ * - id: vp-eng
809
+ * name: John Doe
810
+ * role: VP Engineering
811
+ * reports: [ceo]
812
+ * - id: lead-fe
813
+ * name: Alice Chen
814
+ * role: Frontend Lead
815
+ * reports: [vp-eng]
816
+ * connections: [app-shell, hud]
817
+ */
818
+
819
+ declare class OrgChartProvider implements GraphProvider {
820
+ id: string;
821
+ name: string;
822
+ dependencies: string[];
823
+ private people;
824
+ private defaultCluster;
825
+ constructor(config: ExternalProviderConfig);
826
+ resolve(_config: KBConfig, _existingNodes: KBNode[]): Promise<ProviderResult>;
827
+ }
828
+
829
+ type WorkPullRequestForPerson = {
830
+ number: number;
831
+ title: string;
832
+ state: string;
833
+ html_url: string;
834
+ user?: {
835
+ login: string;
836
+ };
837
+ assignees?: Array<{
838
+ login: string;
839
+ }>;
840
+ };
841
+ type PersonProviderPR = WorkPullRequestForPerson;
842
+ declare class PersonProvider implements GraphProvider {
843
+ id: string;
844
+ name: string;
845
+ /**
846
+ * Run after work so we can match existing nodes; run after content-model
847
+ * so we can link to descriptor people.
848
+ */
849
+ dependencies: string[];
850
+ private issues;
851
+ private pullRequests;
852
+ constructor(issues: GHIssue[], pullRequests: PersonProviderPR[]);
853
+ resolve(config: KBConfig, existingNodes: KBNode[]): Promise<ProviderResult>;
854
+ }
855
+
856
+ /**
857
+ * Register the structural node types + their bespoke viewers. Idempotent — safe
858
+ * to call on every `resolve()`.
859
+ */
860
+ declare function registerStructuralTypes(): void;
861
+ /** Parse a CODEOWNERS file into `{ pattern, owners }` rules. */
862
+ declare function parseCodeowners(content: string): Array<{
863
+ pattern: string;
864
+ owners: string[];
865
+ }>;
866
+ /** Build a structural node for a single `.github` file, or `null` to skip it. */
867
+ declare function buildStructuralFileNode(path: string, content: string, map: StructuredNodeMap, repoNodeId?: string): KBNode | null;
868
+ declare class StructuralProvider implements GraphProvider {
869
+ id: string;
870
+ name: string;
871
+ dependencies: string[];
872
+ private readonly structuralFiles;
873
+ private readonly structuredNodeMapRaw;
874
+ private readonly repoNodeId;
875
+ constructor(structuralFiles?: Record<string, string>, structuredNodeMapRaw?: string | null, repoNodeId?: string);
876
+ resolve(_config: KBConfig, _existingNodes: KBNode[]): Promise<ProviderResult>;
877
+ }
878
+
879
+ /**
880
+ * WikipediaProvider — fetches Wikipedia article summaries and creates graph nodes.
881
+ *
882
+ * Config in config.yaml:
883
+ * providers:
884
+ * - type: wikipedia
885
+ * name: Reference Articles
886
+ * cluster: reference
887
+ * options:
888
+ * articles:
889
+ * - title: Knowledge graph
890
+ * connections: [graph-engine, type-system]
891
+ * - title: Force-directed graph drawing
892
+ * connections: [graph-network]
893
+ * - title: React (software)
894
+ * id: react-framework
895
+ */
896
+
897
+ declare class WikipediaProvider implements GraphProvider {
898
+ id: string;
899
+ name: string;
900
+ dependencies: string[];
901
+ private articles;
902
+ private defaultCluster;
903
+ constructor(config: ExternalProviderConfig);
904
+ resolve(_config: KBConfig, _existingNodes: KBNode[]): Promise<ProviderResult>;
905
+ }
906
+
907
+ type WorkPullRequest = {
908
+ number: number;
909
+ title: string;
910
+ body: string;
911
+ state: string;
912
+ labels: Array<{
913
+ name: string;
914
+ color: string;
915
+ }>;
916
+ html_url: string;
917
+ created_at: string;
918
+ updated_at: string;
919
+ head_branch?: string;
920
+ /** GitHub user who opened the pull request. */
921
+ user?: {
922
+ login: string;
923
+ };
924
+ };
925
+ type WorkCommit = {
926
+ sha: string;
927
+ commit: {
928
+ message: string;
929
+ author: {
930
+ name: string;
931
+ date: string;
932
+ };
933
+ };
934
+ html_url: string;
935
+ };
936
+ type WorkRepoMetadata = {
937
+ name: string;
938
+ description: string;
939
+ html_url: string;
940
+ default_branch: string;
941
+ stargazers_count: number;
942
+ forks_count: number;
943
+ private: boolean;
944
+ topics: string[];
945
+ primary_language: string;
946
+ languages: Array<{
947
+ name: string;
948
+ size: number;
949
+ }>;
950
+ owner: {
951
+ login: string;
952
+ avatar_url: string;
953
+ };
954
+ };
955
+ declare class WorkProvider implements GraphProvider {
956
+ id: string;
957
+ name: string;
958
+ dependencies: string[];
959
+ private issues;
960
+ private pullRequests;
961
+ private commits;
962
+ private branches;
963
+ private repoMetadata;
964
+ private releases;
965
+ constructor(issues: GHIssue[], pullRequests: WorkPullRequest[], commits: WorkCommit[], branches?: Array<{
966
+ name: string;
967
+ protected: boolean;
968
+ }>, repoMetadata?: WorkRepoMetadata | null, releases?: GHRelease[]);
969
+ resolve(_config: KBConfig, _existingNodes: KBNode[]): Promise<ProviderResult>;
970
+ }
971
+
972
+ /**
973
+ * External provider plugin loader.
974
+ *
975
+ * Builds {@link GraphProvider} instances from the `providers` entries in
976
+ * config.yaml. Two resolution paths:
977
+ *
978
+ * 1. **Local / third-party module** — when an entry sets `module`, the loader
979
+ * dynamic-imports that ES-module specifier and calls its default export
980
+ * (a `ProviderFactory` created with `defineProvider()` from
981
+ * `@anokye-labs/kbexplorer-core`). This is the headline extensibility path:
982
+ * a provider can be added with no core code change. The specifier may be a
983
+ * **local** relative path (`./`, `../`) or a **bare npm package** name
984
+ * (`pkg`, `@scope/pkg`, `pkg/subpath`) resolved from `node_modules`;
985
+ * absolute paths and URL/scheme specifiers are rejected so the loader
986
+ * never executes arbitrary remote code. A third-party module is guarded
987
+ * against the provider-contract version + capabilities it declares
988
+ * ({@link checkProviderCompatibility}) and skipped with a clear message if
989
+ * incompatible, rather than crashing the build.
990
+ * 2. **First-party built-in** — `wikipedia` / `orgchart` are resolved directly.
991
+ *
992
+ * Providers authored against the core contract expose a `resolve(context)`
993
+ * signature; the template engine runs providers as `resolve(config, existing)`.
994
+ * {@link adaptCoreProvider} bridges the two so a single contract serves both
995
+ * local modules and (later) third-party npm packages.
996
+ */
997
+
998
+ interface LoadExternalProvidersOptions {
999
+ importBaseUrl?: string | URL;
1000
+ }
1001
+ /**
1002
+ * Load external providers from config entries, in declared order. Module-backed
1003
+ * entries are dynamic-imported; built-in types are resolved directly. The
1004
+ * registry topo-sorts the returned providers by their `dependencies`.
1005
+ */
1006
+ declare function loadExternalProviders(configs: ExternalProviderConfig[], options?: LoadExternalProvidersOptions): Promise<GraphProvider[]>;
1007
+
1008
+ /**
1009
+ * Provider orchestrator — runs registered providers in dependency order
1010
+ * and merges their results into a unified KBGraph.
1011
+ */
1012
+
1013
+ /**
1014
+ * Run all registered providers in dependency order and collect their nodes.
1015
+ * Lower-level helper for callers that need to apply transforms before
1016
+ * building the final graph.
1017
+ */
1018
+ declare function collectProviderNodes(registry: ProviderRegistry, config: KBConfig): Promise<KBNode[]>;
1019
+ /**
1020
+ * Run all registered providers in dependency order and merge their
1021
+ * results into a unified KBGraph.
1022
+ */
1023
+ declare function orchestrate(registry: ProviderRegistry, config: KBConfig): Promise<KBGraph>;
1024
+ /**
1025
+ * Run providers, then the ordered post-provider transform stage, then build the
1026
+ * final graph. This is the single assembly path shared by the local and remote
1027
+ * loaders: they wire providers + a {@link TransformContext} and call this; all
1028
+ * post-processing (README synthesis, issue→directory linking, issue splitting)
1029
+ * lives in the transforms, not the loaders.
1030
+ */
1031
+ declare function orchestrateWithTransforms(registry: ProviderRegistry, config: KBConfig, ctx: TransformContext, transforms?: readonly GraphTransform[]): Promise<KBGraph>;
1032
+
1033
+ /**
1034
+ * Unified knowledge-base loader (Phase 4 / F4 #318).
1035
+ *
1036
+ * Collapses the former local + remote loaders into a single entrypoint:
1037
+ * `loadKnowledgeBase(source, config)`. The {@link RepoSource} abstracts *where*
1038
+ * the data comes from (a manifest, the GitHub API, a future custom SoR); this
1039
+ * function wires the providers from the source's {@link RepoData} bundle and
1040
+ * runs the shared transform stage. Provider wiring is conditional on what the
1041
+ * bundle actually carries, so each source produces byte-identical output to its
1042
+ * former dedicated loader.
1043
+ */
1044
+
1045
+ /** Build + register the provider pipeline from a normalized {@link RepoData} bundle. */
1046
+ declare function registerProviders(registry: ProviderRegistry, data: RepoData): void;
1047
+ /**
1048
+ * Load a knowledge base from any {@link RepoSource}. Single replacement for the
1049
+ * former `loadLocalKnowledgeBase` / `loadRemoteKnowledgeBase` bodies.
1050
+ *
1051
+ * Two call shapes are supported (distinguished by the first argument):
1052
+ *
1053
+ * 1. **Positional / advanced** — `loadKnowledgeBase(source, config, env?, options?)`
1054
+ * returns `{ graph, config }`. This is the form the kbexplorer-template
1055
+ * pins by SHA and MUST remain byte-for-byte compatible.
1056
+ *
1057
+ * 2. **Config-first / scripting** — `loadKnowledgeBase(config, options?)`
1058
+ * returns the bare {@link KBGraph}. The source is taken from
1059
+ * `options.source` when provided, otherwise a default
1060
+ * {@link GitHubApiSource} is constructed from `config.source`. This is the
1061
+ * ergonomic entry for scripts that "just want the graph".
1062
+ *
1063
+ * The overload is resolved purely by argument shape: a {@link RepoSource} is an
1064
+ * object exposing a `getRepoData()` method, whereas a {@link KBConfig} is not.
1065
+ */
1066
+ interface LoadKnowledgeBaseOptions {
1067
+ /**
1068
+ * The source to load from. When omitted, a {@link GitHubApiSource} is built
1069
+ * from `config.source`. Supply a {@link ManifestSource}, `FileSystemSource`,
1070
+ * or any custom {@link RepoSource} to load from elsewhere.
1071
+ */
1072
+ source?: RepoSource;
1073
+ /** Optional engine environment (e.g. GitHub API base) threaded to sources/store. */
1074
+ env?: EngineEnv;
1075
+ importBaseUrl?: string | URL;
1076
+ graphStore?: {
1077
+ byteStore?: SqliteByteStore;
1078
+ locateFile?: (file: string) => string;
1079
+ };
1080
+ }
1081
+ type PositionalOptions = {
1082
+ importBaseUrl?: string | URL;
1083
+ graphStore?: {
1084
+ byteStore?: SqliteByteStore;
1085
+ locateFile?: (file: string) => string;
1086
+ };
1087
+ };
1088
+ declare function loadKnowledgeBase(source: RepoSource, config: KBConfig, env?: EngineEnv, options?: PositionalOptions): Promise<{
1089
+ graph: KBGraph;
1090
+ config: KBConfig;
1091
+ }>;
1092
+ declare function loadKnowledgeBase(config: KBConfig, options?: LoadKnowledgeBaseOptions): Promise<KBGraph>;
1093
+
1094
+ /** Inputs `validateGraph` needs — a subset of the fields `buildManifest` already produces. */
1095
+ type GraphValidationInput = Pick<RepoManifest, 'authoredContent'> & Partial<Pick<RepoManifest, 'configRaw' | 'nodemapRaw' | 'tree' | 'issues'>>;
1096
+ type ValidationSeverity = 'error' | 'warning';
1097
+ type ValidationRule = 'broken-inline-link' | 'missing-github-link' | 'duplicate-id' | 'orphan-node' | 'invalid-cluster' | 'missing-nodemap-path' | 'empty-content';
1098
+ /** One structural finding. `severity: 'error'` gates a caller (exit non-zero); `'warning'` does not. */
1099
+ interface ValidationFinding {
1100
+ rule: ValidationRule;
1101
+ severity: ValidationSeverity;
1102
+ message: string;
1103
+ nodeId?: string;
1104
+ target?: string;
1105
+ }
1106
+ interface GraphValidationResult {
1107
+ /** `true` iff there are zero errors — mirrors the script's `process.exit(errors > 0 ? 1 : 0)`. Warnings never fail the gate. */
1108
+ ok: boolean;
1109
+ errorCount: number;
1110
+ warningCount: number;
1111
+ findings: ValidationFinding[];
1112
+ summary: {
1113
+ contentCount: number;
1114
+ issueCount: number;
1115
+ };
1116
+ }
1117
+ /**
1118
+ * Validate structural integrity of the authored-content graph: dangling
1119
+ * inline links, duplicate/orphan node ids, invalid cluster assignments,
1120
+ * nodemap.yaml path integrity, and empty content bodies. Non-gating callers
1121
+ * can ignore `ok`/`errorCount`; a gating CLI should exit non-zero when
1122
+ * `!result.ok`.
1123
+ */
1124
+ declare function validateGraph(input: GraphValidationInput): GraphValidationResult;
1125
+
1126
+ /**
1127
+ * `assessGraph` — non-gating quality scoring + actionable suggestions over
1128
+ * the engine's authored-content graph, ported faithfully from kbexplorer-
1129
+ * template's `scripts/assess-graph.js` (anokye-labs/kbexplorer-engine#18,
1130
+ * epic anokye-labs/kbexplorer-template#463).
1131
+ *
1132
+ * See `./graph-analysis-shared` for why this walks the narrow inline-link
1133
+ * graph directly instead of the fully-computed `KBGraph` from `buildGraph`.
1134
+ */
1135
+
1136
+ /** Inputs `assessGraph` needs — a subset of the fields `buildManifest` already produces. */
1137
+ type GraphAssessmentInput = Pick<RepoManifest, 'authoredContent'>;
1138
+ interface QualityScores {
1139
+ connectivity: number;
1140
+ /**
1141
+ * Defaults to 100 ("perfect balance") when fewer than 2 clusters exist —
1142
+ * verbatim from the script's `let clusterBalanceScore = 100` default,
1143
+ * which is what still feeds `--gate` scoring even though the human log
1144
+ * prints "N/A" in that case. See {@link AssessmentResult.scoreDetails}'s
1145
+ * `clusterBalanceApplicable` for the "N/A" distinction.
1146
+ */
1147
+ clusterBalance: number;
1148
+ density: number;
1149
+ bidirectionality: number;
1150
+ contentDepth: number;
1151
+ }
1152
+ interface GraphAssessmentConstraint {
1153
+ value: number;
1154
+ limit: number;
1155
+ ok: boolean;
1156
+ }
1157
+ interface HubReachability {
1158
+ hubId: string | null;
1159
+ maxHops: number;
1160
+ unreachable: string[];
1161
+ }
1162
+ interface GraphAssessmentGate {
1163
+ pass: boolean;
1164
+ failures: Array<{
1165
+ metric: keyof QualityScores;
1166
+ actual: number;
1167
+ minimum: number;
1168
+ }>;
1169
+ }
1170
+ interface AssessmentResult {
1171
+ summary: {
1172
+ nodeCount: number;
1173
+ edgeCount: number;
1174
+ clusterCount: number;
1175
+ };
1176
+ constraints: {
1177
+ nodeCount: GraphAssessmentConstraint;
1178
+ edgeCount: GraphAssessmentConstraint;
1179
+ clusterCount: GraphAssessmentConstraint;
1180
+ /** Node ids with zero incoming links — includes one entry per authored file, so a duplicated id can appear more than once (matches the script). */
1181
+ orphanNodes: string[];
1182
+ hubReachability: HubReachability;
1183
+ };
1184
+ scores: QualityScores;
1185
+ /** The raw stats each score in {@link scores} was derived from. */
1186
+ scoreDetails: {
1187
+ avgLinksPerNode: number;
1188
+ clusterSizes: number[];
1189
+ /** `null` when fewer than 2 clusters exist (no standard deviation to report). */
1190
+ clusterStdDev: number | null;
1191
+ /** `true` iff 2+ clusters exist, i.e. `clusterBalance`/`clusterStdDev` reflect a real computation rather than the N/A default. */
1192
+ clusterBalanceApplicable: boolean;
1193
+ density: number;
1194
+ bidirectionalPct: number;
1195
+ avgContentLength: number;
1196
+ };
1197
+ suggestions: string[];
1198
+ /** Present only when `options.gate` is requested — mirrors `node scripts/assess-graph.js --gate`. */
1199
+ gate?: GraphAssessmentGate;
1200
+ }
1201
+ interface AssessGraphOptions {
1202
+ /** When true, also evaluates the `--gate` minimum-score thresholds and populates {@link AssessmentResult.gate}. */
1203
+ gate?: boolean;
1204
+ }
1205
+ /**
1206
+ * Assess quality of the authored-content graph: readability constraints
1207
+ * (node/edge/cluster counts, orphans, hub reachability), five 0-100 quality
1208
+ * scores (connectivity, cluster balance, link density, bidirectionality,
1209
+ * content depth), and actionable suggestions. Always non-gating unless the
1210
+ * caller opts into `options.gate` and checks the returned `gate.pass`.
1211
+ */
1212
+ declare function assessGraph(input: GraphAssessmentInput, options?: AssessGraphOptions): AssessmentResult;
1213
+
1214
+ /**
1215
+ * Catalogue-pipeline shapes shared by `deriveNeeds`, `compareContent`, and
1216
+ * `enrichFromManifest` (anokye-labs/kbexplorer-engine#19, part of the
1217
+ * thin-CLI/fat-engine epic anokye-labs/kbexplorer-template#463).
1218
+ *
1219
+ * These mirror the `content/catalogue.json` schema consumed by
1220
+ * kbexplorer-template's `scripts/derive-content.js`, `scripts/compare-content.js`,
1221
+ * and `scripts/enrich-context.js` — the kb-architect/kb-writer authoring
1222
+ * pipeline this module ports into the engine, field for field.
1223
+ */
1224
+ /** A single node in `content/catalogue.json`. */
1225
+ interface CatalogueNode {
1226
+ id: string;
1227
+ title?: string;
1228
+ cluster?: string;
1229
+ file?: string;
1230
+ prompt?: string;
1231
+ edgeHints?: string[];
1232
+ /** Marks hand-written content that must never be regenerated. */
1233
+ authored?: boolean;
1234
+ /** Marks content the kb-writer agent is expected to generate/regenerate. */
1235
+ derived?: boolean;
1236
+ /** Catalogue nodes may carry additional fields (e.g. enrichment output); preserved as-is. */
1237
+ [key: string]: unknown;
1238
+ }
1239
+ /** The `content/catalogue.json` file's top-level shape. */
1240
+ interface Catalogue {
1241
+ nodes: CatalogueNode[];
1242
+ [key: string]: unknown;
1243
+ }
1244
+ /**
1245
+ * Raw content-file text, keyed by catalogue node `id` (i.e. the contents of
1246
+ * `content/${id}.md`), for every content file that currently exists on disk —
1247
+ * including files that don't correspond to any catalogue node (needed to
1248
+ * detect orphans in {@link compareContent}). Callers own the actual file
1249
+ * I/O (reading `content/`); these helpers only reason over what they're handed,
1250
+ * keeping them pure and source-agnostic.
1251
+ */
1252
+ type CatalogueContentFiles = Record<string, string>;
1253
+
1254
+ /**
1255
+ * `deriveNeeds` — faithful port of kbexplorer-template's
1256
+ * `scripts/derive-content.js` (anokye-labs/kbexplorer-engine#19).
1257
+ *
1258
+ * Reports which `derived: true` catalogue nodes are missing authored content,
1259
+ * so a kb-writer agent (or its thin `kbx graph derive` CLI wrapper, landing
1260
+ * in a follow-up) knows what to generate. Pure and source-agnostic: the
1261
+ * template script reads `content/*.md` off disk itself; this helper instead
1262
+ * takes the already-read file contents via {@link CatalogueContentFiles} so
1263
+ * it has no filesystem dependency of its own.
1264
+ */
1265
+
1266
+ /** The subset of `CatalogueNode` fields `derive-content.js --json` emits per node. */
1267
+ interface DeriveNeedsNode {
1268
+ id: string;
1269
+ title?: string;
1270
+ cluster?: string;
1271
+ file?: string;
1272
+ prompt?: string;
1273
+ edgeHints?: string[];
1274
+ }
1275
+ interface DeriveNeedsResult {
1276
+ /** Total number of nodes in the catalogue. */
1277
+ total: number;
1278
+ /** Nodes preserved as-authored (either `authored: true`, or a `derived` node whose content file carries an `authored: true` frontmatter override). */
1279
+ authored: number;
1280
+ /** Nodes that still need content generation. */
1281
+ derived: number;
1282
+ nodes: DeriveNeedsNode[];
1283
+ }
1284
+ /**
1285
+ * A node needs generation when it is `derived` and either has no content
1286
+ * file yet, or its content file lacks an `authored: true` frontmatter
1287
+ * override — matching the template script's `derived.push(...)` /
1288
+ * `needsGeneration.push(...)` branches exactly, including the fact that
1289
+ * `authored: true` nodes are always treated as authored regardless of
1290
+ * whether their content file currently exists.
1291
+ */
1292
+ declare function deriveNeeds(catalogue: Catalogue, contentFiles: CatalogueContentFiles): DeriveNeedsResult;
1293
+
1294
+ /**
1295
+ * `compareContent` — faithful port of kbexplorer-template's
1296
+ * `scripts/compare-content.js` (anokye-labs/kbexplorer-engine#19).
1297
+ *
1298
+ * Compares the catalogue against existing content files and reports coverage
1299
+ * (authored / derived / missing / extra) plus drift (cluster changes,
1300
+ * link-count changes beyond a threshold of 3). Pure and source-agnostic: the
1301
+ * template script reads `content/*.md` itself; this helper instead takes the
1302
+ * already-read file contents via {@link CatalogueContentFiles}.
1303
+ */
1304
+
1305
+ interface ClusterChange {
1306
+ id: string;
1307
+ from: string;
1308
+ to: string;
1309
+ }
1310
+ interface LinkCountDiff {
1311
+ id: string;
1312
+ catalogue: number;
1313
+ file: number;
1314
+ }
1315
+ interface CompareContentResult {
1316
+ totalNodes: number;
1317
+ totalContentFiles: number;
1318
+ /** `authored: true` nodes whose content file exists (preserved as-is). */
1319
+ authoredNodes: CatalogueNode[];
1320
+ /** `derived: true` nodes whose content file currently exists. */
1321
+ derivedCurrent: CatalogueNode[];
1322
+ /** Nodes (authored or derived) whose content file is missing. */
1323
+ missingNodes: CatalogueNode[];
1324
+ /** Content-file ids that exist on disk but have no matching catalogue node. */
1325
+ extraFiles: string[];
1326
+ clusterChanges: ClusterChange[];
1327
+ linkDiffs: LinkCountDiff[];
1328
+ }
1329
+ /**
1330
+ * Classifies every catalogue node against the supplied content files and
1331
+ * reports coverage + drift, matching `scripts/compare-content.js`'s
1332
+ * computation exactly (only its `console.log` report formatting is left out —
1333
+ * that's the CLI's job).
1334
+ */
1335
+ declare function compareContent(catalogue: Catalogue, contentFiles: CatalogueContentFiles): CompareContentResult;
1336
+
1337
+ /**
1338
+ * `enrichFromManifest` — faithful port of kbexplorer-template's
1339
+ * `scripts/enrich-context.js` (anokye-labs/kbexplorer-engine#19).
1340
+ *
1341
+ * Cross-references every catalogue node with a {@link RepoManifest}'s issues,
1342
+ * pull requests, and commits, attaching `relatedIssues` / `relatedPRs` /
1343
+ * `recentCommits` to each node. Pure and source-agnostic: the template script
1344
+ * reads `content/catalogue.json` and `src/generated/repo-manifest.json` off
1345
+ * disk and writes `content/catalogue-enriched.json` itself; this helper takes
1346
+ * the already-parsed catalogue + manifest and returns the enriched result —
1347
+ * writing it out is the CLI's job.
1348
+ */
1349
+
1350
+ interface RelatedIssue {
1351
+ number: number;
1352
+ title: string;
1353
+ state: string;
1354
+ snippet: string;
1355
+ }
1356
+ interface RelatedPullRequest {
1357
+ number: number;
1358
+ title: string;
1359
+ state: string;
1360
+ snippet: string;
1361
+ }
1362
+ interface RelatedCommit {
1363
+ sha: string | undefined;
1364
+ message: string;
1365
+ }
1366
+ interface EnrichedCatalogueNode extends CatalogueNode {
1367
+ relatedIssues: RelatedIssue[];
1368
+ relatedPRs: RelatedPullRequest[];
1369
+ recentCommits: RelatedCommit[];
1370
+ }
1371
+ interface EnrichedCatalogue {
1372
+ nodes: EnrichedCatalogueNode[];
1373
+ [key: string]: unknown;
1374
+ }
1375
+ interface EnrichFromManifestSummary {
1376
+ issueCount: number;
1377
+ prCount: number;
1378
+ commitCount: number;
1379
+ totalNodes: number;
1380
+ nodesWithIssues: number;
1381
+ nodesWithPRs: number;
1382
+ nodesWithCommits: number;
1383
+ }
1384
+ interface EnrichFromManifestResult {
1385
+ catalogue: EnrichedCatalogue;
1386
+ summary: EnrichFromManifestSummary;
1387
+ }
1388
+ /**
1389
+ * Attaches `relatedIssues` / `relatedPRs` / `recentCommits` to every
1390
+ * catalogue node by matching its file / title / id against the manifest's
1391
+ * issues, pull requests, and commits — matching `scripts/enrich-context.js`'s
1392
+ * matching rules and per-node caps exactly.
1393
+ */
1394
+ declare function enrichFromManifest(catalogue: Catalogue, manifest: RepoManifest): EnrichFromManifestResult;
1395
+
1396
+ export { type AssessGraphOptions, type AssessmentResult, AuthoredProvider, AuthoredRichMarkdownProvider, CONTENT_MODEL_KINDS, CONTENT_MODEL_PROVIDER, CacheStore, type Catalogue, type CatalogueContentFiles, type CatalogueNode, type ClusterChange, type CompareContentResult, type ContentModelGraph, ContentModelProvider, ContentModelSchema, ContentModelSource, DEFAULT_CONFIG, DEFAULT_STRUCTURED_CONTENT_PATH, type DeriveNeedsNode, type DeriveNeedsResult, Diagnostic, type Direction, EDGE_TYPE_WEIGHTS, EngineEnv, type EnrichFromManifestResult, type EnrichFromManifestSummary, type EnrichedCatalogue, type EnrichedCatalogueNode, FilesProvider, GHIssue, GHRelease, GHTreeItem, type GraphAssessmentConstraint, type GraphAssessmentGate, type GraphAssessmentInput, GraphProvider, GraphTransform, type GraphValidationInput, type GraphValidationResult, type HubReachability, type IssueToNodeOptions, KindConvention, type LinkCountDiff, type LoadKnowledgeBaseOptions, MAX_VISIBLE_EDGES, MAX_VISIBLE_NODES, type NeighborOptions, NodeLayer, type NodeMap, type NodeMapEntry, type NodeMapRule, OrgChartProvider, PersonProvider, type PersonProviderPR, ProviderRegistry, ProviderResult, type QualityScores, type RelatedCommit, type RelatedIssue, type RelatedPullRequest, type RepoCoords, SCHEMA_PATHS, type ShortestPathOptions, type SourceEditHandoff, StructuralProvider, type StructuredContentConfig, type StructuredFile, type StructuredNodeMap, type SubgraphOptions, TransformContext, type TrimResult, type ValidationFinding, type ValidationResult, type ValidationRule, type ValidationSeverity, VocabularyOverlay, WikipediaProvider, WorkProvider, adaptIngestedNode, applyStructuredNodeMap, assessGraph, assignIdentity, buildContentModel, buildEditUrl, buildGraph, buildHandoffUrl, buildIdentityIndex, buildNewFileUrl, buildSourceEditHandoff, buildStructuralFileNode, buildUnifiedDiff, buildUrn, canEditSource, canonicalKind, collectProviderNodes, compareContent, deriveNeeds, encodeRepoPath, enrichFromManifest, extractClusters, extractImportPaths, extractIssueRefs, filterAccessWithheld, findNodes, getConvention, getEdgeDescription, getEdgeWeight, getHubNodeId, getNode, getNodeDegrees, globToRegex, hasContentModelSource, hasExplicitStructuredContentPath, inferStructuredNode, isAccessWithheld, isOrgScoped, issueToNode, lifecycleBand, loadAuthoredContent, loadConfig, loadExternalProviders, loadKnowledgeBase, loadNodeMap, loadRepoContent, neighbors, normalizeNewlines, normalizeRepoRelativeDir, orchestrate, orchestrateWithTransforms, parseAccessLabel, parseCodeowners, parseMarkdownFile, parseStructuredContent, parseStructuredNodeMap, patchFilename, readContentModelSchema, reconstructSource, registerContentModelTypes, registerProviders, registerStructuralTypes, related, renderSafeMarkdown, repoCoordsFromConfig, resolveCurie, resolveImportPath, resolveSourceFile, resolveStructuredContentPath, shareIdentity, shortestPath, slugify, splitIntoSections, subgraph, treeToNodes, trimGraphToLimits, urnBody, urnIdentity, urnLocalId, validateGraph, validateSourceContent };