@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.
- package/LICENSE +21 -0
- package/README.md +77 -0
- package/dist/build-manifest-BT0sY84J.d.ts +239 -0
- package/dist/build-manifest-BkgZUL0E.d.cts +239 -0
- package/dist/chunk-ASLGNOYV.js +220 -0
- package/dist/chunk-BA526FDQ.js +16 -0
- package/dist/chunk-DJ2DBEEK.js +424 -0
- package/dist/chunk-FJ4GTN4U.js +973 -0
- package/dist/chunk-HXAS3BSD.js +187 -0
- package/dist/chunk-IWEURXYH.js +273 -0
- package/dist/chunk-JNQVSNLC.js +55 -0
- package/dist/chunk-MW4BLXVK.js +72 -0
- package/dist/chunk-NOGBKE7C.js +351 -0
- package/dist/chunk-XVI6CBSX.js +412 -0
- package/dist/chunk-YTMIERYM.js +67 -0
- package/dist/github-api-source-KV5HZARH.js +4 -0
- package/dist/index.cjs +6218 -0
- package/dist/index.d.cts +1396 -0
- package/dist/index.d.ts +1396 -0
- package/dist/index.js +2892 -0
- package/dist/node-wasm-VSXBOCPD.js +9 -0
- package/dist/plugin-loader-K4VU6ODV.js +2 -0
- package/dist/repo-data-0JvFdLGv.d.cts +461 -0
- package/dist/repo-data-0JvFdLGv.d.ts +461 -0
- package/dist/sources.cjs +1191 -0
- package/dist/sources.d.cts +194 -0
- package/dist/sources.d.ts +194 -0
- package/dist/sources.js +357 -0
- package/dist/sqlite-graph-store-NH2NHL2B.js +2 -0
- package/dist/sqlite-runtime-CysPjjzX.d.cts +96 -0
- package/dist/sqlite-runtime-CysPjjzX.d.ts +96 -0
- package/dist/store-orchestrator-337XWIXO.js +5 -0
- package/dist/store.cjs +1112 -0
- package/dist/store.d.cts +37 -0
- package/dist/store.d.ts +37 -0
- package/dist/store.js +7 -0
- package/package.json +70 -0
package/dist/index.d.cts
ADDED
|
@@ -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.cjs';
|
|
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.cjs';
|
|
4
|
+
import { C as CacheStore, R as RepoManifest } from './build-manifest-BkgZUL0E.cjs';
|
|
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-BkgZUL0E.cjs';
|
|
6
|
+
import { G as GraphProvider, P as ProviderResult, a as ProviderRegistry, T as TransformContext, b as GraphTransform, S as SqliteByteStore } from './sqlite-runtime-CysPjjzX.cjs';
|
|
7
|
+
export { D as DEFAULT_TRANSFORMS, c as applyTransforms, i as issueDirectoryLinkTransform, d as issueSplitTransform, r as readmeTransform } from './sqlite-runtime-CysPjjzX.cjs';
|
|
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 };
|