kibi-mcp 0.10.0 → 0.11.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/dist/server/docs.js +29 -30
- package/dist/tools/autopilot-candidates.js +220 -14
- package/dist/tools/autopilot-discovery.js +677 -51
- package/dist/tools/autopilot-generate.js +467 -177
- package/dist/tools/briefing-generate.js +28 -6
- package/dist/tools/delete.js +33 -3
- package/dist/tools/upsert.js +3 -3
- package/dist/tools-config.js +31 -1
- package/package.json +3 -3
package/dist/server/docs.js
CHANGED
|
@@ -45,47 +45,46 @@ export const PROMPTS = [
|
|
|
45
45
|
name: "init-kibi",
|
|
46
46
|
description: "Activation workflow to populate a new or empty Kibi KB from an existing repository.",
|
|
47
47
|
text: [
|
|
48
|
-
"# Kibi Activation Workflow",
|
|
48
|
+
"# Kibi Interactive Activation Workflow",
|
|
49
49
|
"",
|
|
50
|
-
"Use this workflow to
|
|
50
|
+
"Use this workflow to onboard a new or empty repository into Kibi through interactive discovery.",
|
|
51
51
|
"",
|
|
52
|
-
"## Step 1:
|
|
52
|
+
"## Step 1: Gather Declared Context",
|
|
53
53
|
"",
|
|
54
|
-
"
|
|
54
|
+
"The agent must ask at most 4 bounded questions to gather declared intent from the user:",
|
|
55
|
+
"1. **Project Summary**: What is the core purpose of this project?",
|
|
56
|
+
"2. **Source of Truth**: Where is the primary documentation (canonical requirements, ADRs)?",
|
|
57
|
+
"3. **Priority Root**: In a monorepo, which package should be prioritized?",
|
|
58
|
+
"4. **Verification Anchors**: Where are the primary tests or verification configs located?",
|
|
55
59
|
"",
|
|
56
|
-
"
|
|
57
|
-
"- `activationState`: the current KB state (e.g. `root_uninitialized`, `root_partial`)",
|
|
58
|
-
"- `candidates[]`: proposed entities with confidence scores and evidence",
|
|
59
|
-
"- `suppressedCandidates[]`: candidates suppressed due to duplicates, existing entities, or shadowed by typed sources",
|
|
60
|
-
"- `discoverySummary` / `payoffSummary`: context for agent review",
|
|
60
|
+
"## Step 2: Synthesize Candidates (read-only)",
|
|
61
61
|
"",
|
|
62
|
-
"
|
|
62
|
+
"Call `kb_autopilot_generate` with the gathered context to synthesize candidate entities.",
|
|
63
63
|
"",
|
|
64
|
-
"
|
|
64
|
+
"This tool is **read-only**. It returns additive `structuredContent` with:",
|
|
65
|
+
"- `promptBlock`: preview text for the user-facing approval prompt",
|
|
66
|
+
"- `recommendedActions`: agent-facing next steps, including any REQ/SCEN/TEST authoring routed for manual handling",
|
|
67
|
+
"- `declaredContext`: the user-provided bootstrap context",
|
|
68
|
+
"- `confidence`: confidence summary for the generated output",
|
|
69
|
+
"- `bootstrapMode`: current KB state (e.g., `root_uninitialized`)",
|
|
70
|
+
"- `candidates`: synthesized entities grounded in declared context and source evidence",
|
|
71
|
+
"- `discoverySummary`: source-backed discovery notes",
|
|
65
72
|
"",
|
|
66
|
-
"
|
|
67
|
-
"- **confidence** (0–1): prefer high-confidence entities first",
|
|
68
|
-
"- **evidence**: verify the source reference is real before applying",
|
|
73
|
+
"## Step 3: Preview and Approval",
|
|
69
74
|
"",
|
|
70
|
-
"
|
|
71
|
-
"## Step 3: Apply Approved Candidates", "",
|
|
72
|
-
"Apply approved candidates by executing each candidate.applyPlan sequentially:",
|
|
73
|
-
"1. For each approved candidate, run its `candidate.applyPlan` steps in ascending phase order and keep the candidate sequence deterministic",
|
|
74
|
-
"2. Execute each step with `kb_upsert` using the step's provided args, and confirm success before moving to the next step",
|
|
75
|
-
"3. After each batch, call `kb_check` with targeted rules (`required-fields`, `no-dangling-refs`) to catch issues early",
|
|
75
|
+
"Present the `promptBlock` and a summary of `candidates` to the user. **Wait for explicit approval** before proceeding to writes.",
|
|
76
76
|
"",
|
|
77
|
-
"## Step 4:
|
|
77
|
+
"## Step 4: Apply Approved Candidates",
|
|
78
78
|
"",
|
|
79
|
-
"
|
|
80
|
-
"
|
|
81
|
-
"
|
|
82
|
-
"
|
|
83
|
-
"## Doc Hygiene",
|
|
79
|
+
"Apply approved candidates sequentially using `kb_upsert`.",
|
|
80
|
+
"1. Execute each approved candidate's `applyPlan` in ascending phase order.",
|
|
81
|
+
"2. Confirm success of each `kb_upsert` before moving to the next.",
|
|
82
|
+
"3. Run `kb_check` after the batch to verify KB integrity.",
|
|
84
83
|
"",
|
|
85
|
-
"
|
|
86
|
-
"-
|
|
87
|
-
"-
|
|
88
|
-
"-
|
|
84
|
+
"## Rules",
|
|
85
|
+
"- Never apply changes without a user-facing preview and approval.",
|
|
86
|
+
"- `kb_autopilot_generate` is strictly read-only; synthesis is the backend, not the actor.",
|
|
87
|
+
"- Guidance must stay MCP-only; do not suggest `kibi` CLI commands.",
|
|
89
88
|
].join("\n"),
|
|
90
89
|
},
|
|
91
90
|
{
|
|
@@ -4,6 +4,56 @@ import { extractFromManifest } from "kibi-cli/extractors/manifest";
|
|
|
4
4
|
import { extractFromMarkdown } from "kibi-cli/extractors/markdown";
|
|
5
5
|
import path from "node:path";
|
|
6
6
|
import fs from "node:fs";
|
|
7
|
+
function slugify(value, maxLength = 80) {
|
|
8
|
+
return value
|
|
9
|
+
.toLowerCase()
|
|
10
|
+
.replace(/[^a-z0-9]+/g, "-")
|
|
11
|
+
.replace(/(^-|-$)/g, "")
|
|
12
|
+
.slice(0, maxLength);
|
|
13
|
+
}
|
|
14
|
+
function sortUniquePaths(paths) {
|
|
15
|
+
return Array.from(new Set(paths)).sort();
|
|
16
|
+
}
|
|
17
|
+
function getEvidenceFilePaths(discoveryResult, kind) {
|
|
18
|
+
return sortUniquePaths((discoveryResult.evidence ?? [])
|
|
19
|
+
.filter((item) => item.kind === kind)
|
|
20
|
+
.map((item) => item.absolutePath ?? "")
|
|
21
|
+
.filter((item) => Boolean(item)));
|
|
22
|
+
}
|
|
23
|
+
function getTypedMarkdownFiles(discoveryResult) {
|
|
24
|
+
const evidenceFiles = getEvidenceFilePaths(discoveryResult, "typed_markdown");
|
|
25
|
+
if (evidenceFiles.length > 0)
|
|
26
|
+
return evidenceFiles;
|
|
27
|
+
return discoveryResult.markdownFiles ?? [];
|
|
28
|
+
}
|
|
29
|
+
function getManifestFiles(discoveryResult) {
|
|
30
|
+
const evidenceFiles = getEvidenceFilePaths(discoveryResult, "symbol_manifest");
|
|
31
|
+
if (evidenceFiles.length > 0)
|
|
32
|
+
return evidenceFiles;
|
|
33
|
+
return discoveryResult.manifestFiles ?? [];
|
|
34
|
+
}
|
|
35
|
+
function getGenericMarkdownFiles(discoveryResult) {
|
|
36
|
+
const evidenceFiles = getEvidenceFilePaths(discoveryResult, "generic_markdown");
|
|
37
|
+
if (evidenceFiles.length > 0)
|
|
38
|
+
return evidenceFiles;
|
|
39
|
+
return discoveryResult.markdownFiles ?? [];
|
|
40
|
+
}
|
|
41
|
+
function hasGenericMarkdownEvidence(discoveryResult) {
|
|
42
|
+
return (discoveryResult.evidence ?? []).some((item) => item.kind === "generic_markdown");
|
|
43
|
+
}
|
|
44
|
+
function getFactEvidence(discoveryResult) {
|
|
45
|
+
return (discoveryResult.evidence ?? []).filter((item) => item.kind === "repo_metadata" ||
|
|
46
|
+
item.kind === "repo_layout" ||
|
|
47
|
+
item.kind === "test_topology" ||
|
|
48
|
+
item.kind === "source_symbols");
|
|
49
|
+
}
|
|
50
|
+
function toConfidenceBand(confidence) {
|
|
51
|
+
if (confidence >= 0.9)
|
|
52
|
+
return "high";
|
|
53
|
+
if (confidence >= 0.8)
|
|
54
|
+
return "medium";
|
|
55
|
+
return "low";
|
|
56
|
+
}
|
|
7
57
|
function resolveCandidatePaths(filePath, workspaceRoot) {
|
|
8
58
|
const absolutePath = path.isAbsolute(filePath)
|
|
9
59
|
? filePath
|
|
@@ -17,6 +67,20 @@ function isIgnoredGenericMarkdownPath(relativePath) {
|
|
|
17
67
|
const normalized = relativePath.replace(/\\/g, "/").replace(/^\.\//, "");
|
|
18
68
|
return /(^|\/)(documentation|\.kb|\.git|node_modules|vendor|vendors|third_party|third-party|dist|coverage)(\/|$)/.test(normalized);
|
|
19
69
|
}
|
|
70
|
+
function shouldIncludeGenericMarkdown(relativePath, providerScopedMarkdown) {
|
|
71
|
+
const base = path.basename(relativePath).toLowerCase();
|
|
72
|
+
const inDocsDir = /(^|\/)docs\//.test(relativePath);
|
|
73
|
+
if (providerScopedMarkdown)
|
|
74
|
+
return true;
|
|
75
|
+
return base === "readme.md" || base === "architecture.md" || inDocsDir;
|
|
76
|
+
}
|
|
77
|
+
function pushSignal(signals, signal, seen) {
|
|
78
|
+
const key = `${signal.kind}::${signal.sourcePath}::${signal.title}`;
|
|
79
|
+
if (seen.has(key))
|
|
80
|
+
return;
|
|
81
|
+
seen.add(key);
|
|
82
|
+
signals.push(signal);
|
|
83
|
+
}
|
|
20
84
|
function buildUpsertFromExtraction(er, typeOverride) {
|
|
21
85
|
const ent = er.entity;
|
|
22
86
|
const type = typeOverride ?? String(ent.type ?? "");
|
|
@@ -39,7 +103,7 @@ function buildUpsertFromExtraction(er, typeOverride) {
|
|
|
39
103
|
export function buildTypedMarkdownCandidates(discoveryResult, existingEntities) {
|
|
40
104
|
const candidates = [];
|
|
41
105
|
const workspaceRoot = existingEntities.workspaceRoot ?? process.cwd();
|
|
42
|
-
for (const filePath of discoveryResult
|
|
106
|
+
for (const filePath of getTypedMarkdownFiles(discoveryResult)) {
|
|
43
107
|
try {
|
|
44
108
|
const extraction = extractFromMarkdown(filePath);
|
|
45
109
|
const { entity, relationships } = extraction;
|
|
@@ -74,7 +138,7 @@ export function buildTypedMarkdownCandidates(discoveryResult, existingEntities)
|
|
|
74
138
|
export function buildSymbolManifestCandidates(discoveryResult, existingEntities) {
|
|
75
139
|
const candidates = [];
|
|
76
140
|
const workspaceRoot = existingEntities.workspaceRoot ?? process.cwd();
|
|
77
|
-
for (const filePath of discoveryResult
|
|
141
|
+
for (const filePath of getManifestFiles(discoveryResult)) {
|
|
78
142
|
try {
|
|
79
143
|
const results = extractFromManifest(filePath);
|
|
80
144
|
for (const res of results) {
|
|
@@ -111,7 +175,7 @@ export function buildSymbolManifestCandidates(discoveryResult, existingEntities)
|
|
|
111
175
|
/**
|
|
112
176
|
* Conservative generic markdown candidate builder.
|
|
113
177
|
* Scans a small, safe set of top-level markdown files and emits only
|
|
114
|
-
* ADR/
|
|
178
|
+
* ADR/FACT candidates when clear heading heuristics match.
|
|
115
179
|
*
|
|
116
180
|
* discoveryResult.markdownFiles is expected to be a list of file paths
|
|
117
181
|
* (absolute or relative). Files under documentation/**, .kb/**, .git/**,
|
|
@@ -122,17 +186,17 @@ export function buildSymbolManifestCandidates(discoveryResult, existingEntities)
|
|
|
122
186
|
export function buildGenericMarkdownCandidates(discoveryResult, existingEntities, minConfidence = 0.8) {
|
|
123
187
|
const candidates = [];
|
|
124
188
|
const workspaceRoot = existingEntities.workspaceRoot ?? process.cwd();
|
|
125
|
-
const
|
|
189
|
+
const providerScopedMarkdown = hasGenericMarkdownEvidence(discoveryResult);
|
|
190
|
+
const files = getGenericMarkdownFiles(discoveryResult);
|
|
126
191
|
for (const rawPath of files) {
|
|
127
192
|
try {
|
|
128
193
|
const filePath = String(rawPath);
|
|
129
194
|
const { absolutePath, relativePath } = resolveCandidatePaths(filePath, workspaceRoot);
|
|
130
195
|
if (isIgnoredGenericMarkdownPath(relativePath))
|
|
131
196
|
continue;
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
if (!(base === "readme.md" || base === "architecture.md" || inDocsDir)) {
|
|
197
|
+
// Legacy path-only discovery was conservative. Provider-scoped discovery
|
|
198
|
+
// already filters eligible generic docs, so allow broader repo markdown there.
|
|
199
|
+
if (!shouldIncludeGenericMarkdown(relativePath, providerScopedMarkdown)) {
|
|
136
200
|
continue;
|
|
137
201
|
}
|
|
138
202
|
if (!fs.existsSync(absolutePath))
|
|
@@ -158,11 +222,6 @@ export function buildGenericMarkdownCandidates(discoveryResult, existingEntities
|
|
|
158
222
|
type = "adr";
|
|
159
223
|
confidence = 0.9;
|
|
160
224
|
}
|
|
161
|
-
// Requirements heuristic: explicit Requirements heading
|
|
162
|
-
if (!type && /\brequirements?\b/i.test(heading)) {
|
|
163
|
-
type = "req";
|
|
164
|
-
confidence = 0.85;
|
|
165
|
-
}
|
|
166
225
|
// Fact/Observation heuristic
|
|
167
226
|
if (!type && /\b(observations?|facts?|notes?)\b/i.test(heading)) {
|
|
168
227
|
type = "fact";
|
|
@@ -180,7 +239,7 @@ export function buildGenericMarkdownCandidates(discoveryResult, existingEntities
|
|
|
180
239
|
.replace(/[^a-z0-9]+/g, "-")
|
|
181
240
|
.replace(/(^-|-$)/g, "")
|
|
182
241
|
.slice(0, 60);
|
|
183
|
-
const idPrefix = type === "adr" ? "ADR" :
|
|
242
|
+
const idPrefix = type === "adr" ? "ADR" : "FACT";
|
|
184
243
|
const genId = `${idPrefix}-GEN-${slug || path.basename(relativePath).replace(/\.[^.]+$/, "")}`.toUpperCase();
|
|
185
244
|
if (existingEntities.ids.has(genId))
|
|
186
245
|
continue;
|
|
@@ -223,7 +282,154 @@ export function buildGenericMarkdownCandidates(discoveryResult, existingEntities
|
|
|
223
282
|
}
|
|
224
283
|
return candidates;
|
|
225
284
|
}
|
|
285
|
+
// implements REQ-mcp-init-kibi-autopilot-v1
|
|
286
|
+
export function collectSourceOnlyAuthoringSignals(discoveryResult, existingEntities, minConfidence = 0.8) {
|
|
287
|
+
const signals = [];
|
|
288
|
+
const seen = new Set();
|
|
289
|
+
const workspaceRoot = existingEntities.workspaceRoot ?? process.cwd();
|
|
290
|
+
const providerScopedMarkdown = hasGenericMarkdownEvidence(discoveryResult);
|
|
291
|
+
for (const rawPath of getGenericMarkdownFiles(discoveryResult)) {
|
|
292
|
+
try {
|
|
293
|
+
const filePath = String(rawPath);
|
|
294
|
+
const { absolutePath, relativePath } = resolveCandidatePaths(filePath, workspaceRoot);
|
|
295
|
+
if (isIgnoredGenericMarkdownPath(relativePath))
|
|
296
|
+
continue;
|
|
297
|
+
if (!shouldIncludeGenericMarkdown(relativePath, providerScopedMarkdown))
|
|
298
|
+
continue;
|
|
299
|
+
if (!fs.existsSync(absolutePath))
|
|
300
|
+
continue;
|
|
301
|
+
const content = fs.readFileSync(absolutePath, "utf8");
|
|
302
|
+
const lines = content.split(/\r?\n/);
|
|
303
|
+
for (let i = 0; i < lines.length; i++) {
|
|
304
|
+
const line = lines[i];
|
|
305
|
+
if (line === undefined)
|
|
306
|
+
continue;
|
|
307
|
+
const headingMatch = line.match(/^\s*#+\s*(.+)$/);
|
|
308
|
+
if (!headingMatch)
|
|
309
|
+
continue;
|
|
310
|
+
const headingRaw = headingMatch[1];
|
|
311
|
+
if (!headingRaw)
|
|
312
|
+
continue;
|
|
313
|
+
const heading = headingRaw.trim();
|
|
314
|
+
const textRef = `${relativePath}#L${i + 1}`;
|
|
315
|
+
if (/\brequirements?\b/i.test(heading) && 0.84 >= minConfidence) {
|
|
316
|
+
pushSignal(signals, {
|
|
317
|
+
kind: "req",
|
|
318
|
+
title: `Author requirements from ${heading}`,
|
|
319
|
+
sourcePath: absolutePath,
|
|
320
|
+
confidence: 0.84,
|
|
321
|
+
evidence: [`generic_heading:${textRef}`],
|
|
322
|
+
}, seen);
|
|
323
|
+
}
|
|
324
|
+
if (/\bscenarios?\b/i.test(heading) && 0.83 >= minConfidence) {
|
|
325
|
+
pushSignal(signals, {
|
|
326
|
+
kind: "scenario",
|
|
327
|
+
title: `Author scenarios from ${heading}`,
|
|
328
|
+
sourcePath: absolutePath,
|
|
329
|
+
confidence: 0.83,
|
|
330
|
+
evidence: [`generic_heading:${textRef}`],
|
|
331
|
+
}, seen);
|
|
332
|
+
}
|
|
333
|
+
if (/\b(tests?|verification)\b/i.test(heading) && 0.82 >= minConfidence) {
|
|
334
|
+
pushSignal(signals, {
|
|
335
|
+
kind: "test",
|
|
336
|
+
title: `Author tests from ${heading}`,
|
|
337
|
+
sourcePath: absolutePath,
|
|
338
|
+
confidence: 0.82,
|
|
339
|
+
evidence: [`generic_heading:${textRef}`],
|
|
340
|
+
}, seen);
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
catch {
|
|
345
|
+
// ignore unreadable files when deriving authoring signals
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
for (const item of discoveryResult.evidence ?? []) {
|
|
349
|
+
const confidence = typeof item.data.confidence === "number" ? item.data.confidence : 0;
|
|
350
|
+
if (item.kind === "test_topology" && confidence >= minConfidence) {
|
|
351
|
+
const sourcePath = item.absolutePath ?? path.resolve(workspaceRoot, item.relativePath ?? item.label);
|
|
352
|
+
const relativePath = item.relativePath ?? item.label;
|
|
353
|
+
pushSignal(signals, {
|
|
354
|
+
kind: "test",
|
|
355
|
+
title: `Author TEST coverage for ${relativePath}`,
|
|
356
|
+
sourcePath,
|
|
357
|
+
confidence,
|
|
358
|
+
evidence: Array.isArray(item.data.evidence)
|
|
359
|
+
? item.data.evidence.filter((value) => typeof value === "string")
|
|
360
|
+
: [`test_topology:${relativePath}`],
|
|
361
|
+
}, seen);
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
return signals.sort((left, right) => {
|
|
365
|
+
if (right.confidence !== left.confidence)
|
|
366
|
+
return right.confidence - left.confidence;
|
|
367
|
+
if (left.kind !== right.kind)
|
|
368
|
+
return left.kind.localeCompare(right.kind);
|
|
369
|
+
return left.sourcePath.localeCompare(right.sourcePath);
|
|
370
|
+
});
|
|
371
|
+
}
|
|
372
|
+
// implements REQ-mcp-init-kibi-autopilot-v1
|
|
373
|
+
export function buildProviderEvidenceCandidates(discoveryResult, existingEntities, minConfidence = 0.8) {
|
|
374
|
+
const candidates = [];
|
|
375
|
+
const workspaceRoot = existingEntities.workspaceRoot ?? process.cwd();
|
|
376
|
+
for (const item of getFactEvidence(discoveryResult)) {
|
|
377
|
+
const relativePath = item.relativePath ?? item.label;
|
|
378
|
+
const absolutePath = item.absolutePath ?? path.resolve(workspaceRoot, relativePath);
|
|
379
|
+
const confidence = typeof item.data.confidence === "number" ? item.data.confidence : 0.8;
|
|
380
|
+
if (confidence < minConfidence)
|
|
381
|
+
continue;
|
|
382
|
+
const factKind = typeof item.data.factKind === "string" && item.data.factKind.length > 0
|
|
383
|
+
? item.data.factKind
|
|
384
|
+
: item.kind === "repo_metadata"
|
|
385
|
+
? "meta"
|
|
386
|
+
: "observation";
|
|
387
|
+
const title = typeof item.data.title === "string" && item.data.title.length > 0
|
|
388
|
+
? item.data.title
|
|
389
|
+
: `Autopilot evidence from ${relativePath}`;
|
|
390
|
+
const slugSource = `${item.kind}-${relativePath}`;
|
|
391
|
+
const generatedId = `FACT-GEN-${slugify(slugSource, 64) || "evidence"}`.toUpperCase();
|
|
392
|
+
if (existingEntities.ids.has(generatedId))
|
|
393
|
+
continue;
|
|
394
|
+
const textRef = relativePath.includes("#") ? relativePath : `${relativePath}`;
|
|
395
|
+
const evidence = Array.isArray(item.data.evidence)
|
|
396
|
+
? item.data.evidence.filter((value) => typeof value === "string")
|
|
397
|
+
: [];
|
|
398
|
+
candidates.push({
|
|
399
|
+
candidateId: `prov:${item.kind}:${slugify(relativePath, 96) || "evidence"}`,
|
|
400
|
+
entityType: "fact",
|
|
401
|
+
title,
|
|
402
|
+
sourceKind: item.kind,
|
|
403
|
+
sourcePath: absolutePath,
|
|
404
|
+
confidence,
|
|
405
|
+
confidenceBand: toConfidenceBand(confidence),
|
|
406
|
+
evidence: evidence.length > 0
|
|
407
|
+
? evidence
|
|
408
|
+
: [`provider:${item.provider}`, `${item.kind}:${relativePath}`],
|
|
409
|
+
relationships: [],
|
|
410
|
+
applyPlan: [
|
|
411
|
+
{
|
|
412
|
+
type: "fact",
|
|
413
|
+
id: generatedId,
|
|
414
|
+
properties: {
|
|
415
|
+
id: generatedId,
|
|
416
|
+
title,
|
|
417
|
+
status: "active",
|
|
418
|
+
fact_kind: factKind,
|
|
419
|
+
source: `autopilot:${item.provider}:${relativePath}`,
|
|
420
|
+
text_ref: textRef,
|
|
421
|
+
},
|
|
422
|
+
relationships: [],
|
|
423
|
+
},
|
|
424
|
+
],
|
|
425
|
+
});
|
|
426
|
+
}
|
|
427
|
+
return candidates;
|
|
428
|
+
}
|
|
226
429
|
export default {
|
|
227
430
|
buildTypedMarkdownCandidates,
|
|
228
431
|
buildSymbolManifestCandidates,
|
|
432
|
+
buildGenericMarkdownCandidates,
|
|
433
|
+
collectSourceOnlyAuthoringSignals,
|
|
434
|
+
buildProviderEvidenceCandidates,
|
|
229
435
|
};
|