kibi-mcp 0.22.0 → 2.0.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/bin/kibi-mcp +12 -11
- package/dist/diagnostics.js +68 -8
- package/dist/env.js +5 -1
- package/dist/runtime/mcp-runtime.js +39 -3
- package/dist/server/docs.js +31 -42
- package/dist/server/icons.js +27 -0
- package/dist/server/jobs.js +107 -0
- package/dist/server/json-schema-to-zod.js +33 -9
- package/dist/server/session.js +129 -133
- package/dist/server/tool-registration.js +46 -9
- package/dist/server/tools-runtime.js +10 -10
- package/dist/server/tools.js +143 -7
- package/dist/server.js +16 -1
- package/dist/startup-resolution.js +15 -17
- package/dist/tools/check-impact.js +1 -1
- package/dist/tools/check.js +1 -1
- package/dist/tools/core-module.js +19 -8
- package/dist/tools/coverage.js +1 -1
- package/dist/tools/delete.js +1 -1
- package/dist/tools/discovery-adapter.js +5 -0
- package/dist/tools/entity-query.js +1 -1
- package/dist/tools/find-gaps.js +9 -1
- package/dist/tools/graph.js +9 -1
- package/dist/tools/model-requirement.js +2 -2
- package/dist/tools/mutation-context.js +15 -9
- package/dist/tools/plan-bootstrap.js +21 -0
- package/dist/tools/prolog-list.js +1 -1
- package/dist/tools/query.js +1 -1
- package/dist/tools/relationship-validation.js +1 -1
- package/dist/tools/search.js +1 -1
- package/dist/tools/semantic-advisor.js +1 -1
- package/dist/tools/skills.js +2 -2
- package/dist/tools/sparql.js +1 -1
- package/dist/tools/status.js +1 -1
- package/dist/tools/suggest-predicates.js +1 -1
- package/dist/tools/symbols.js +310 -68
- package/dist/tools/upsert.js +3 -3
- package/dist/tools/validate-upsert.js +1 -1
- package/dist/tools-config.js +77 -3
- package/dist/workspace.js +10 -8
- package/package.json +15 -9
- package/dist/tools/autopilot-candidates.js +0 -554
- package/dist/tools/autopilot-discovery.js +0 -898
- package/dist/tools/autopilot-generate.js +0 -47
- package/dist/tools/autopilot-test-compat.js +0 -88
- package/dist/tools/autopilot-test-parsers.js +0 -120
- package/dist/tools/check-config.js +0 -67
|
@@ -1,898 +0,0 @@
|
|
|
1
|
-
/*
|
|
2
|
-
* Autopilot discovery helpers
|
|
3
|
-
*
|
|
4
|
-
* Provides lightweight workspace activation classification and source discovery
|
|
5
|
-
* used by the `kb_autopilot_generate` workflow.
|
|
6
|
-
*/
|
|
7
|
-
import fs from "node:fs";
|
|
8
|
-
import path from "node:path";
|
|
9
|
-
import fg from "fast-glob";
|
|
10
|
-
import * as cliSymbolCoordinator from "kibi-cli/extractors/symbols-coordinator";
|
|
11
|
-
import { createRepoIgnorePolicy } from "kibi-cli/ignore-policy";
|
|
12
|
-
import { runJsonModuleQuery } from "./core-module.js";
|
|
13
|
-
// implements REQ-001
|
|
14
|
-
export const AUTOPILOT_PROVIDER_ORDER = [
|
|
15
|
-
// implements REQ-001
|
|
16
|
-
"typed_kibi_docs",
|
|
17
|
-
"generic_repo_docs",
|
|
18
|
-
"repo_metadata",
|
|
19
|
-
"repo_layout",
|
|
20
|
-
"test_topology",
|
|
21
|
-
"source_symbols",
|
|
22
|
-
];
|
|
23
|
-
const IGNORED_DIRECTORY_NAMES = new Set([
|
|
24
|
-
".git",
|
|
25
|
-
".kb",
|
|
26
|
-
".venv",
|
|
27
|
-
"build",
|
|
28
|
-
"coverage",
|
|
29
|
-
"dist",
|
|
30
|
-
"node_modules",
|
|
31
|
-
"target",
|
|
32
|
-
"third-party",
|
|
33
|
-
"third_party",
|
|
34
|
-
"vendor",
|
|
35
|
-
"vendors",
|
|
36
|
-
"venv",
|
|
37
|
-
]);
|
|
38
|
-
// Minimal copy of the opencode defaults used by other packages. Keep in sync
|
|
39
|
-
// with packages/opencode/src/file-filter.ts DEFAULT_SYNC_PATHS.
|
|
40
|
-
const DEFAULT_SYNC_PATHS = {
|
|
41
|
-
requirements: "documentation/requirements/**/*.md",
|
|
42
|
-
scenarios: "documentation/scenarios/**/*.md",
|
|
43
|
-
tests: "documentation/tests/**/*.md",
|
|
44
|
-
adr: "documentation/adr/**/*.md",
|
|
45
|
-
flags: "documentation/flags/**/*.md",
|
|
46
|
-
events: "documentation/events/**/*.md",
|
|
47
|
-
facts: "documentation/facts/**/*.md",
|
|
48
|
-
symbols: "documentation/symbols.yaml",
|
|
49
|
-
};
|
|
50
|
-
const SOURCE_LANGUAGE_EXTENSIONS = {
|
|
51
|
-
".ts": "typescript",
|
|
52
|
-
".tsx": "typescript",
|
|
53
|
-
".mts": "typescript",
|
|
54
|
-
".cts": "typescript",
|
|
55
|
-
".js": "javascript",
|
|
56
|
-
".jsx": "javascript",
|
|
57
|
-
".mjs": "javascript",
|
|
58
|
-
".cjs": "javascript",
|
|
59
|
-
".py": "python",
|
|
60
|
-
".rb": "ruby",
|
|
61
|
-
".go": "go",
|
|
62
|
-
".rs": "rust",
|
|
63
|
-
".java": "java",
|
|
64
|
-
".kt": "kotlin",
|
|
65
|
-
".swift": "swift",
|
|
66
|
-
".php": "php",
|
|
67
|
-
".c": "c",
|
|
68
|
-
".cc": "cpp",
|
|
69
|
-
".cpp": "cpp",
|
|
70
|
-
".h": "c",
|
|
71
|
-
".hpp": "cpp",
|
|
72
|
-
};
|
|
73
|
-
const PROJECT_SIGNAL_FILES = [
|
|
74
|
-
"README.md",
|
|
75
|
-
"README.mdx",
|
|
76
|
-
"package.json",
|
|
77
|
-
"tsconfig.json",
|
|
78
|
-
"pyproject.toml",
|
|
79
|
-
"Cargo.toml",
|
|
80
|
-
"go.mod",
|
|
81
|
-
];
|
|
82
|
-
const PROJECT_SIGNAL_DIRS = [
|
|
83
|
-
"src",
|
|
84
|
-
"app",
|
|
85
|
-
"apps",
|
|
86
|
-
"packages",
|
|
87
|
-
"tests",
|
|
88
|
-
"test",
|
|
89
|
-
"docs",
|
|
90
|
-
"documentation",
|
|
91
|
-
"scripts",
|
|
92
|
-
];
|
|
93
|
-
function findVendoredTrees(cwd) {
|
|
94
|
-
const results = new Set();
|
|
95
|
-
const vendoredMarkers = [
|
|
96
|
-
["kibi", "opencode.json"],
|
|
97
|
-
["kibi", "package.json"],
|
|
98
|
-
["kibi", "packages", "mcp"],
|
|
99
|
-
["kibi", "documentation"],
|
|
100
|
-
];
|
|
101
|
-
for (const marker of vendoredMarkers) {
|
|
102
|
-
const markerPath = path.join(cwd, ...marker);
|
|
103
|
-
if (fs.existsSync(markerPath)) {
|
|
104
|
-
results.add(marker[0] ?? "kibi");
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
const nodeModules = path.join(cwd, "node_modules");
|
|
108
|
-
if (fs.existsSync(nodeModules)) {
|
|
109
|
-
try {
|
|
110
|
-
for (const entry of fs.readdirSync(nodeModules)) {
|
|
111
|
-
if (entry === "kibi" || entry.startsWith("kibi-")) {
|
|
112
|
-
results.add(`node_modules/${entry}`);
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
catch {
|
|
117
|
-
// ignore
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
return Array.from(results).sort();
|
|
121
|
-
}
|
|
122
|
-
function rootKbConfigExists(cwd) {
|
|
123
|
-
return fs.existsSync(path.join(cwd, ".kb", "config.json"));
|
|
124
|
-
}
|
|
125
|
-
function hasWorkspaceProjectSignals(cwd, vendoredRoots) {
|
|
126
|
-
const vendoredTopLevel = new Set(vendoredRoots
|
|
127
|
-
.map((item) => item.split("/")[0])
|
|
128
|
-
.filter((item) => Boolean(item)));
|
|
129
|
-
for (const fileName of PROJECT_SIGNAL_FILES) {
|
|
130
|
-
if (fs.existsSync(path.join(cwd, fileName))) {
|
|
131
|
-
return true;
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
for (const dirName of PROJECT_SIGNAL_DIRS) {
|
|
135
|
-
if (vendoredTopLevel.has(dirName))
|
|
136
|
-
continue;
|
|
137
|
-
const candidate = path.join(cwd, dirName);
|
|
138
|
-
if (fs.existsSync(candidate) && fs.statSync(candidate).isDirectory()) {
|
|
139
|
-
return true;
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
return false;
|
|
143
|
-
}
|
|
144
|
-
function readRootConfig(cwd) {
|
|
145
|
-
try {
|
|
146
|
-
const raw = fs.readFileSync(path.join(cwd, ".kb", "config.json"), "utf8");
|
|
147
|
-
return JSON.parse(raw) || null;
|
|
148
|
-
}
|
|
149
|
-
catch {
|
|
150
|
-
return null;
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
function stripToRoot(p) {
|
|
154
|
-
const segments = p.split("/");
|
|
155
|
-
const rootSegments = [];
|
|
156
|
-
for (const seg of segments) {
|
|
157
|
-
if (seg.includes("*") || seg.includes("?") || seg.includes("["))
|
|
158
|
-
break;
|
|
159
|
-
rootSegments.push(seg);
|
|
160
|
-
}
|
|
161
|
-
const result = rootSegments.join("/");
|
|
162
|
-
return result || ".";
|
|
163
|
-
}
|
|
164
|
-
function normalizePattern(p) {
|
|
165
|
-
if (!p)
|
|
166
|
-
return null;
|
|
167
|
-
if (p.includes("*"))
|
|
168
|
-
return p;
|
|
169
|
-
if (p.endsWith(".yaml") || p.endsWith(".yml") || path.extname(p))
|
|
170
|
-
return p;
|
|
171
|
-
return `${p.replace(/\/+$/, "")}/**/*.md`;
|
|
172
|
-
}
|
|
173
|
-
function buildSourceSummary(activation, vendored) {
|
|
174
|
-
return {
|
|
175
|
-
activationState: activation.activationState,
|
|
176
|
-
activationMode: activation.activationMode,
|
|
177
|
-
applyBlocked: activation.applyBlocked,
|
|
178
|
-
reason: activation.reason,
|
|
179
|
-
...(activation.handoffMessage
|
|
180
|
-
? { handoffMessage: activation.handoffMessage }
|
|
181
|
-
: {}),
|
|
182
|
-
...(vendored.length > 0 ? { vendored } : {}),
|
|
183
|
-
};
|
|
184
|
-
}
|
|
185
|
-
function createEmptyProviderCounts() {
|
|
186
|
-
return Object.fromEntries(AUTOPILOT_PROVIDER_ORDER.map((provider) => [provider, 0]));
|
|
187
|
-
}
|
|
188
|
-
function sortUnique(values) {
|
|
189
|
-
return Array.from(new Set(values)).filter(Boolean).sort();
|
|
190
|
-
}
|
|
191
|
-
function toRelativePosixPath(workspaceRoot, targetPath) {
|
|
192
|
-
return path.relative(workspaceRoot, targetPath).split(path.sep).join("/");
|
|
193
|
-
}
|
|
194
|
-
function normalizeDiscoveryPaths(cwd) {
|
|
195
|
-
const config = readRootConfig(cwd) || {};
|
|
196
|
-
const configured = config.paths ?? {};
|
|
197
|
-
const readPath = (key) => {
|
|
198
|
-
const configuredValue = configured[key];
|
|
199
|
-
if (typeof configuredValue === "string" && configuredValue.length > 0) {
|
|
200
|
-
return configuredValue;
|
|
201
|
-
}
|
|
202
|
-
const fallbackValue = DEFAULT_SYNC_PATHS[key];
|
|
203
|
-
return typeof fallbackValue === "string" ? fallbackValue : "";
|
|
204
|
-
};
|
|
205
|
-
return {
|
|
206
|
-
requirements: readPath("requirements"),
|
|
207
|
-
scenarios: readPath("scenarios"),
|
|
208
|
-
tests: readPath("tests"),
|
|
209
|
-
adr: readPath("adr"),
|
|
210
|
-
flags: readPath("flags"),
|
|
211
|
-
events: readPath("events"),
|
|
212
|
-
facts: readPath("facts"),
|
|
213
|
-
symbols: readPath("symbols"),
|
|
214
|
-
};
|
|
215
|
-
}
|
|
216
|
-
function buildIgnoredGlobs(vendoredRoots, workspaceRoot) {
|
|
217
|
-
const ignored = new Set();
|
|
218
|
-
for (const dirName of IGNORED_DIRECTORY_NAMES) {
|
|
219
|
-
ignored.add(`**/${dirName}`);
|
|
220
|
-
ignored.add(`**/${dirName}/**`);
|
|
221
|
-
}
|
|
222
|
-
for (const vendoredRoot of vendoredRoots) {
|
|
223
|
-
const normalized = vendoredRoot.replace(/\\/g, "/").replace(/^\.\//, "");
|
|
224
|
-
if (!normalized)
|
|
225
|
-
continue;
|
|
226
|
-
ignored.add(normalized);
|
|
227
|
-
ignored.add(`${normalized}/**`);
|
|
228
|
-
ignored.add(`**/${normalized}`);
|
|
229
|
-
ignored.add(`**/${normalized}/**`);
|
|
230
|
-
}
|
|
231
|
-
// Use shared ignore policy to include .gitignore, nested ignores, and other rules.
|
|
232
|
-
try {
|
|
233
|
-
const policy = createRepoIgnorePolicy(workspaceRoot);
|
|
234
|
-
const globs = policy.getFastGlobIgnoreGlobs();
|
|
235
|
-
for (const glob of globs) {
|
|
236
|
-
if (glob)
|
|
237
|
-
ignored.add(glob);
|
|
238
|
-
}
|
|
239
|
-
}
|
|
240
|
-
catch {
|
|
241
|
-
// best-effort only
|
|
242
|
-
}
|
|
243
|
-
return Array.from(ignored);
|
|
244
|
-
}
|
|
245
|
-
function detectLanguagesFromPaths(paths) {
|
|
246
|
-
const detected = new Set();
|
|
247
|
-
for (const filePath of paths) {
|
|
248
|
-
const language = SOURCE_LANGUAGE_EXTENSIONS[path.extname(filePath).toLowerCase()];
|
|
249
|
-
if (language) {
|
|
250
|
-
detected.add(language);
|
|
251
|
-
}
|
|
252
|
-
}
|
|
253
|
-
return Array.from(detected);
|
|
254
|
-
}
|
|
255
|
-
function createFileEvidence(provider, kind, workspaceRoot, absolutePath, data = {}) {
|
|
256
|
-
const relativePath = toRelativePosixPath(workspaceRoot, absolutePath);
|
|
257
|
-
return {
|
|
258
|
-
provider,
|
|
259
|
-
kind,
|
|
260
|
-
label: relativePath,
|
|
261
|
-
relativePath,
|
|
262
|
-
absolutePath,
|
|
263
|
-
data,
|
|
264
|
-
};
|
|
265
|
-
}
|
|
266
|
-
function runTypedKibiDocsProvider(workspaceRoot) {
|
|
267
|
-
const discoveryPaths = normalizeDiscoveryPaths(workspaceRoot);
|
|
268
|
-
const markdownPatterns = [
|
|
269
|
-
normalizePattern(discoveryPaths.requirements),
|
|
270
|
-
normalizePattern(discoveryPaths.scenarios),
|
|
271
|
-
normalizePattern(discoveryPaths.tests),
|
|
272
|
-
normalizePattern(discoveryPaths.adr),
|
|
273
|
-
normalizePattern(discoveryPaths.flags),
|
|
274
|
-
normalizePattern(discoveryPaths.events),
|
|
275
|
-
normalizePattern(discoveryPaths.facts),
|
|
276
|
-
].filter((pattern) => Boolean(pattern));
|
|
277
|
-
const markdownFiles = fg.sync(markdownPatterns, {
|
|
278
|
-
cwd: workspaceRoot,
|
|
279
|
-
absolute: true,
|
|
280
|
-
onlyFiles: true,
|
|
281
|
-
unique: true,
|
|
282
|
-
suppressErrors: true,
|
|
283
|
-
ignore: buildIgnoredGlobs([], workspaceRoot),
|
|
284
|
-
});
|
|
285
|
-
const manifestFiles = discoveryPaths.symbols
|
|
286
|
-
? fg.sync(discoveryPaths.symbols, {
|
|
287
|
-
cwd: workspaceRoot,
|
|
288
|
-
absolute: true,
|
|
289
|
-
onlyFiles: true,
|
|
290
|
-
unique: true,
|
|
291
|
-
suppressErrors: true,
|
|
292
|
-
ignore: buildIgnoredGlobs([], workspaceRoot),
|
|
293
|
-
})
|
|
294
|
-
: [];
|
|
295
|
-
const evidence = [
|
|
296
|
-
...sortUnique(markdownFiles).map((absolutePath) => createFileEvidence("typed_kibi_docs", "typed_markdown", workspaceRoot, absolutePath)),
|
|
297
|
-
...sortUnique(manifestFiles).map((absolutePath) => createFileEvidence("typed_kibi_docs", "symbol_manifest", workspaceRoot, absolutePath)),
|
|
298
|
-
];
|
|
299
|
-
return {
|
|
300
|
-
provider: "typed_kibi_docs",
|
|
301
|
-
evidence,
|
|
302
|
-
};
|
|
303
|
-
}
|
|
304
|
-
function runGenericRepoDocsProvider(workspaceRoot, vendoredRoots, typedFilePaths) {
|
|
305
|
-
const markdownFiles = fg.sync("**/*.md", {
|
|
306
|
-
cwd: workspaceRoot,
|
|
307
|
-
absolute: true,
|
|
308
|
-
onlyFiles: true,
|
|
309
|
-
unique: true,
|
|
310
|
-
suppressErrors: true,
|
|
311
|
-
ignore: buildIgnoredGlobs(vendoredRoots, workspaceRoot),
|
|
312
|
-
});
|
|
313
|
-
const evidence = sortUnique(markdownFiles)
|
|
314
|
-
.map((absolutePath) => createFileEvidence("generic_repo_docs", "generic_markdown", workspaceRoot, absolutePath))
|
|
315
|
-
.filter((item) => !typedFilePaths.has(item.relativePath ?? ""));
|
|
316
|
-
return {
|
|
317
|
-
provider: "generic_repo_docs",
|
|
318
|
-
evidence,
|
|
319
|
-
};
|
|
320
|
-
}
|
|
321
|
-
function detectLanguagesFromPackageJson(packageJson) {
|
|
322
|
-
const detected = new Set();
|
|
323
|
-
const scripts = packageJson.scripts;
|
|
324
|
-
const bin = packageJson.bin;
|
|
325
|
-
if (typeof scripts === "object" && scripts) {
|
|
326
|
-
for (const value of Object.values(scripts)) {
|
|
327
|
-
if (typeof value === "string" &&
|
|
328
|
-
/\.(cts|mts|ts|tsx)\b|\b(tsx|ts-node)\b/i.test(value)) {
|
|
329
|
-
detected.add("typescript");
|
|
330
|
-
}
|
|
331
|
-
if (typeof value === "string" && /\.(cjs|mjs|js|jsx)\b/i.test(value)) {
|
|
332
|
-
detected.add("javascript");
|
|
333
|
-
}
|
|
334
|
-
}
|
|
335
|
-
}
|
|
336
|
-
if (typeof bin === "string" && /\.(cts|mts|ts|tsx)\b/i.test(bin)) {
|
|
337
|
-
detected.add("typescript");
|
|
338
|
-
}
|
|
339
|
-
if (typeof bin === "object" && bin) {
|
|
340
|
-
for (const value of Object.values(bin)) {
|
|
341
|
-
if (typeof value === "string" && /\.(cts|mts|ts|tsx)\b/i.test(value)) {
|
|
342
|
-
detected.add("typescript");
|
|
343
|
-
}
|
|
344
|
-
}
|
|
345
|
-
}
|
|
346
|
-
return Array.from(detected);
|
|
347
|
-
}
|
|
348
|
-
function runRepoMetadataProvider(workspaceRoot) {
|
|
349
|
-
const patterns = [
|
|
350
|
-
"package.json",
|
|
351
|
-
"opencode.json",
|
|
352
|
-
"tsconfig.json",
|
|
353
|
-
"tsconfig.*.json",
|
|
354
|
-
"bun.lock",
|
|
355
|
-
"bun.lockb",
|
|
356
|
-
"bunfig.toml",
|
|
357
|
-
"pnpm-workspace.yaml",
|
|
358
|
-
"pnpm-lock.yaml",
|
|
359
|
-
"package-lock.json",
|
|
360
|
-
"yarn.lock",
|
|
361
|
-
"Cargo.toml",
|
|
362
|
-
"go.mod",
|
|
363
|
-
"pyproject.toml",
|
|
364
|
-
"requirements*.txt",
|
|
365
|
-
];
|
|
366
|
-
const metadataFiles = fg.sync(patterns, {
|
|
367
|
-
cwd: workspaceRoot,
|
|
368
|
-
absolute: true,
|
|
369
|
-
onlyFiles: true,
|
|
370
|
-
unique: true,
|
|
371
|
-
suppressErrors: true,
|
|
372
|
-
});
|
|
373
|
-
const detectedLanguages = new Set();
|
|
374
|
-
const scanWarnings = [];
|
|
375
|
-
const evidence = [];
|
|
376
|
-
for (const absolutePath of sortUnique(metadataFiles)) {
|
|
377
|
-
const relativePath = toRelativePosixPath(workspaceRoot, absolutePath);
|
|
378
|
-
const basename = path.basename(relativePath);
|
|
379
|
-
const data = {
|
|
380
|
-
title: `Repository metadata: ${basename}`,
|
|
381
|
-
factKind: "meta",
|
|
382
|
-
confidence: basename.startsWith("tsconfig") ? 0.9 : 0.86,
|
|
383
|
-
evidence: [`repo_metadata:${relativePath}`],
|
|
384
|
-
};
|
|
385
|
-
if (basename.startsWith("tsconfig")) {
|
|
386
|
-
detectedLanguages.add("typescript");
|
|
387
|
-
}
|
|
388
|
-
if (basename === "Cargo.toml") {
|
|
389
|
-
detectedLanguages.add("rust");
|
|
390
|
-
}
|
|
391
|
-
if (basename === "go.mod") {
|
|
392
|
-
detectedLanguages.add("go");
|
|
393
|
-
}
|
|
394
|
-
if (basename === "pyproject.toml") {
|
|
395
|
-
detectedLanguages.add("python");
|
|
396
|
-
}
|
|
397
|
-
if (basename === "package.json") {
|
|
398
|
-
try {
|
|
399
|
-
const parsed = JSON.parse(fs.readFileSync(absolutePath, "utf8"));
|
|
400
|
-
for (const language of detectLanguagesFromPackageJson(parsed)) {
|
|
401
|
-
detectedLanguages.add(language);
|
|
402
|
-
}
|
|
403
|
-
if (typeof parsed.packageManager === "string") {
|
|
404
|
-
data.packageManager = parsed.packageManager;
|
|
405
|
-
}
|
|
406
|
-
}
|
|
407
|
-
catch (error) {
|
|
408
|
-
scanWarnings.push(`repo_metadata:failed_to_parse:${relativePath}`);
|
|
409
|
-
}
|
|
410
|
-
}
|
|
411
|
-
evidence.push(createFileEvidence("repo_metadata", "repo_metadata", workspaceRoot, absolutePath, data));
|
|
412
|
-
}
|
|
413
|
-
return {
|
|
414
|
-
provider: "repo_metadata",
|
|
415
|
-
evidence,
|
|
416
|
-
detectedLanguages: Array.from(detectedLanguages),
|
|
417
|
-
scanWarnings,
|
|
418
|
-
};
|
|
419
|
-
}
|
|
420
|
-
function runRepoLayoutProvider(workspaceRoot, vendoredRoots) {
|
|
421
|
-
const layoutRoots = [
|
|
422
|
-
"src",
|
|
423
|
-
"app",
|
|
424
|
-
"apps",
|
|
425
|
-
"packages",
|
|
426
|
-
"tests",
|
|
427
|
-
"test",
|
|
428
|
-
"docs",
|
|
429
|
-
"scripts",
|
|
430
|
-
];
|
|
431
|
-
const evidence = [];
|
|
432
|
-
for (const relativePath of layoutRoots) {
|
|
433
|
-
const absolutePath = path.join(workspaceRoot, relativePath);
|
|
434
|
-
if (!fs.existsSync(absolutePath) ||
|
|
435
|
-
!fs.statSync(absolutePath).isDirectory()) {
|
|
436
|
-
continue;
|
|
437
|
-
}
|
|
438
|
-
evidence.push({
|
|
439
|
-
provider: "repo_layout",
|
|
440
|
-
kind: "repo_layout",
|
|
441
|
-
label: relativePath,
|
|
442
|
-
relativePath,
|
|
443
|
-
absolutePath,
|
|
444
|
-
data: {
|
|
445
|
-
title: `Repository layout: ${relativePath} directory`,
|
|
446
|
-
factKind: "observation",
|
|
447
|
-
confidence: 0.84,
|
|
448
|
-
evidence: [`repo_layout:${relativePath}`],
|
|
449
|
-
},
|
|
450
|
-
});
|
|
451
|
-
}
|
|
452
|
-
const codeFiles = fg.sync([
|
|
453
|
-
"src/**/*.{ts,tsx,mts,cts,js,jsx,mjs,cjs,py,rb,go,rs,java,kt,swift,php,c,cc,cpp,h,hpp}",
|
|
454
|
-
"app/**/*.{ts,tsx,mts,cts,js,jsx,mjs,cjs,py,rb,go,rs,java,kt,swift,php,c,cc,cpp,h,hpp}",
|
|
455
|
-
"apps/**/*.{ts,tsx,mts,cts,js,jsx,mjs,cjs,py,rb,go,rs,java,kt,swift,php,c,cc,cpp,h,hpp}",
|
|
456
|
-
"packages/**/*.{ts,tsx,mts,cts,js,jsx,mjs,cjs,py,rb,go,rs,java,kt,swift,php,c,cc,cpp,h,hpp}",
|
|
457
|
-
"tests/**/*.{ts,tsx,mts,cts,js,jsx,mjs,cjs,py,rb,go,rs,java,kt,swift,php,c,cc,cpp,h,hpp}",
|
|
458
|
-
"test/**/*.{ts,tsx,mts,cts,js,jsx,mjs,cjs,py,rb,go,rs,java,kt,swift,php,c,cc,cpp,h,hpp}",
|
|
459
|
-
], {
|
|
460
|
-
cwd: workspaceRoot,
|
|
461
|
-
absolute: true,
|
|
462
|
-
onlyFiles: true,
|
|
463
|
-
unique: true,
|
|
464
|
-
suppressErrors: true,
|
|
465
|
-
ignore: buildIgnoredGlobs(vendoredRoots, workspaceRoot),
|
|
466
|
-
});
|
|
467
|
-
return {
|
|
468
|
-
provider: "repo_layout",
|
|
469
|
-
evidence,
|
|
470
|
-
detectedLanguages: detectLanguagesFromPaths(codeFiles),
|
|
471
|
-
};
|
|
472
|
-
}
|
|
473
|
-
function detectTestFrameworksFromContent(content) {
|
|
474
|
-
const frameworks = new Set();
|
|
475
|
-
if (/\bbun:test\b/.test(content))
|
|
476
|
-
frameworks.add("bun:test");
|
|
477
|
-
if (/\bvitest\b/.test(content))
|
|
478
|
-
frameworks.add("vitest");
|
|
479
|
-
if (/\bnode:test\b/.test(content))
|
|
480
|
-
frameworks.add("node:test");
|
|
481
|
-
if (/\bmocha\b/.test(content))
|
|
482
|
-
frameworks.add("mocha");
|
|
483
|
-
if (/\bjest\b|@jest\/globals/.test(content))
|
|
484
|
-
frameworks.add("jest");
|
|
485
|
-
return Array.from(frameworks);
|
|
486
|
-
}
|
|
487
|
-
function runTestTopologyProvider(workspaceRoot, vendoredRoots) {
|
|
488
|
-
const testFiles = fg.sync([
|
|
489
|
-
"**/*.test.{ts,tsx,mts,cts,js,jsx,mjs,cjs}",
|
|
490
|
-
"**/*.spec.{ts,tsx,mts,cts,js,jsx,mjs,cjs}",
|
|
491
|
-
"**/__tests__/**/*.{ts,tsx,mts,cts,js,jsx,mjs,cjs}",
|
|
492
|
-
], {
|
|
493
|
-
cwd: workspaceRoot,
|
|
494
|
-
absolute: true,
|
|
495
|
-
onlyFiles: true,
|
|
496
|
-
unique: true,
|
|
497
|
-
suppressErrors: true,
|
|
498
|
-
ignore: buildIgnoredGlobs(vendoredRoots, workspaceRoot),
|
|
499
|
-
});
|
|
500
|
-
const detectedFrameworks = new Set();
|
|
501
|
-
const detectedLanguages = new Set();
|
|
502
|
-
const scanWarnings = [];
|
|
503
|
-
const evidence = [];
|
|
504
|
-
for (const absolutePath of sortUnique(testFiles)) {
|
|
505
|
-
const relativePath = toRelativePosixPath(workspaceRoot, absolutePath);
|
|
506
|
-
const frameworks = (() => {
|
|
507
|
-
try {
|
|
508
|
-
return detectTestFrameworksFromContent(fs.readFileSync(absolutePath, "utf8"));
|
|
509
|
-
}
|
|
510
|
-
catch (error) {
|
|
511
|
-
scanWarnings.push(`test_topology:failed_to_read:${relativePath}`);
|
|
512
|
-
return [];
|
|
513
|
-
}
|
|
514
|
-
})();
|
|
515
|
-
for (const framework of frameworks) {
|
|
516
|
-
detectedFrameworks.add(framework);
|
|
517
|
-
}
|
|
518
|
-
for (const language of detectLanguagesFromPaths([absolutePath])) {
|
|
519
|
-
detectedLanguages.add(language);
|
|
520
|
-
}
|
|
521
|
-
evidence.push(createFileEvidence("test_topology", "test_topology", workspaceRoot, absolutePath, {
|
|
522
|
-
title: frameworks.length > 0
|
|
523
|
-
? `Test topology: ${frameworks.join(", ")} in ${relativePath}`
|
|
524
|
-
: `Test topology: ${relativePath}`,
|
|
525
|
-
factKind: "observation",
|
|
526
|
-
confidence: frameworks.length > 0 ? 0.92 : 0.85,
|
|
527
|
-
evidence: [
|
|
528
|
-
`test_topology:${relativePath}`,
|
|
529
|
-
...frameworks.map((framework) => `framework:${framework}`),
|
|
530
|
-
],
|
|
531
|
-
frameworks,
|
|
532
|
-
}));
|
|
533
|
-
}
|
|
534
|
-
return {
|
|
535
|
-
provider: "test_topology",
|
|
536
|
-
evidence,
|
|
537
|
-
detectedLanguages: Array.from(detectedLanguages),
|
|
538
|
-
detectedTestFrameworks: Array.from(detectedFrameworks),
|
|
539
|
-
scanWarnings,
|
|
540
|
-
};
|
|
541
|
-
}
|
|
542
|
-
function runSourceSymbolsProvider(workspaceRoot, vendoredRoots) {
|
|
543
|
-
const analyzeSourceText = cliSymbolCoordinator.analyzeSourceText;
|
|
544
|
-
const sourceFiles = fg.sync([
|
|
545
|
-
"src/**/*.{ts,tsx,mts,cts,js,jsx,mjs,cjs,py,rb,go,rs,java,kt,swift,php,c,cc,cpp,h,hpp}",
|
|
546
|
-
"app/**/*.{ts,tsx,mts,cts,js,jsx,mjs,cjs,py,rb,go,rs,java,kt,swift,php,c,cc,cpp,h,hpp}",
|
|
547
|
-
"apps/**/*.{ts,tsx,mts,cts,js,jsx,mjs,cjs,py,rb,go,rs,java,kt,swift,php,c,cc,cpp,h,hpp}",
|
|
548
|
-
"packages/**/*.{ts,tsx,mts,cts,js,jsx,mjs,cjs,py,rb,go,rs,java,kt,swift,php,c,cc,cpp,h,hpp}",
|
|
549
|
-
], {
|
|
550
|
-
cwd: workspaceRoot,
|
|
551
|
-
absolute: true,
|
|
552
|
-
onlyFiles: true,
|
|
553
|
-
unique: true,
|
|
554
|
-
suppressErrors: true,
|
|
555
|
-
ignore: buildIgnoredGlobs(vendoredRoots, workspaceRoot),
|
|
556
|
-
});
|
|
557
|
-
const evidence = [];
|
|
558
|
-
const detectedLanguages = new Set();
|
|
559
|
-
const scanWarnings = [];
|
|
560
|
-
for (const absolutePath of sortUnique(sourceFiles)) {
|
|
561
|
-
const relativePath = toRelativePosixPath(workspaceRoot, absolutePath);
|
|
562
|
-
const language = SOURCE_LANGUAGE_EXTENSIONS[path.extname(absolutePath).toLowerCase()] ??
|
|
563
|
-
"unknown";
|
|
564
|
-
detectedLanguages.add(language);
|
|
565
|
-
try {
|
|
566
|
-
const content = fs.readFileSync(absolutePath, "utf8");
|
|
567
|
-
const analysis = analyzeSourceText
|
|
568
|
-
? analyzeSourceText(relativePath, content)
|
|
569
|
-
: {
|
|
570
|
-
sourceFile: relativePath,
|
|
571
|
-
language,
|
|
572
|
-
providerId: null,
|
|
573
|
-
module: {
|
|
574
|
-
title: path.basename(relativePath, path.extname(relativePath)) ||
|
|
575
|
-
relativePath,
|
|
576
|
-
analysisMode: "fallback",
|
|
577
|
-
fallbackReason: "provider_unavailable",
|
|
578
|
-
},
|
|
579
|
-
symbols: [],
|
|
580
|
-
};
|
|
581
|
-
if (analysis.symbols.length > 0) {
|
|
582
|
-
evidence.push({
|
|
583
|
-
provider: "source_symbols",
|
|
584
|
-
kind: "source_symbols",
|
|
585
|
-
label: relativePath,
|
|
586
|
-
relativePath,
|
|
587
|
-
absolutePath,
|
|
588
|
-
data: {
|
|
589
|
-
title: `Source symbols: ${analysis.module.title}`,
|
|
590
|
-
factKind: "observation",
|
|
591
|
-
confidence: 0.9,
|
|
592
|
-
evidence: [
|
|
593
|
-
`source_symbols:${relativePath}`,
|
|
594
|
-
`language:${analysis.language}`,
|
|
595
|
-
`provider:${analysis.providerId ?? "fallback"}`,
|
|
596
|
-
...analysis.symbols
|
|
597
|
-
.slice(0, 5)
|
|
598
|
-
.map((symbol) => `symbol:${symbol.kind}:${symbol.name}`),
|
|
599
|
-
],
|
|
600
|
-
analysisMode: analysis.module.analysisMode,
|
|
601
|
-
providerId: analysis.providerId,
|
|
602
|
-
symbolCount: analysis.symbols.length,
|
|
603
|
-
},
|
|
604
|
-
});
|
|
605
|
-
continue;
|
|
606
|
-
}
|
|
607
|
-
evidence.push({
|
|
608
|
-
provider: "source_symbols",
|
|
609
|
-
kind: "source_symbols",
|
|
610
|
-
label: relativePath,
|
|
611
|
-
relativePath,
|
|
612
|
-
absolutePath,
|
|
613
|
-
data: {
|
|
614
|
-
title: `Source module: ${analysis.module.title}`,
|
|
615
|
-
factKind: "observation",
|
|
616
|
-
confidence: 0.82,
|
|
617
|
-
evidence: [
|
|
618
|
-
`source_symbols:${relativePath}`,
|
|
619
|
-
`language:${analysis.language}`,
|
|
620
|
-
`analysis_mode:${analysis.module.analysisMode}`,
|
|
621
|
-
...(analysis.module.fallbackReason
|
|
622
|
-
? [`fallback:${analysis.module.fallbackReason}`]
|
|
623
|
-
: []),
|
|
624
|
-
],
|
|
625
|
-
analysisMode: analysis.module.analysisMode,
|
|
626
|
-
fallbackReason: analysis.module.fallbackReason,
|
|
627
|
-
providerId: analysis.providerId,
|
|
628
|
-
symbolCount: 0,
|
|
629
|
-
},
|
|
630
|
-
});
|
|
631
|
-
}
|
|
632
|
-
catch {
|
|
633
|
-
scanWarnings.push(`source_symbols:failed_to_analyze:${relativePath}`);
|
|
634
|
-
}
|
|
635
|
-
}
|
|
636
|
-
return {
|
|
637
|
-
provider: "source_symbols",
|
|
638
|
-
evidence,
|
|
639
|
-
detectedLanguages: Array.from(detectedLanguages),
|
|
640
|
-
scanWarnings,
|
|
641
|
-
};
|
|
642
|
-
}
|
|
643
|
-
function buildDiscoverySummary(activation, vendored, providerResults) {
|
|
644
|
-
const providerCounts = createEmptyProviderCounts();
|
|
645
|
-
const detectedLanguages = new Set();
|
|
646
|
-
const detectedTestFrameworks = new Set();
|
|
647
|
-
const scanWarnings = [];
|
|
648
|
-
let truncated = false;
|
|
649
|
-
for (const result of providerResults) {
|
|
650
|
-
providerCounts[result.provider] = result.evidence.length;
|
|
651
|
-
for (const language of result.detectedLanguages ?? []) {
|
|
652
|
-
detectedLanguages.add(language);
|
|
653
|
-
}
|
|
654
|
-
for (const framework of result.detectedTestFrameworks ?? []) {
|
|
655
|
-
detectedTestFrameworks.add(framework);
|
|
656
|
-
}
|
|
657
|
-
scanWarnings.push(...(result.scanWarnings ?? []));
|
|
658
|
-
truncated ||= Boolean(result.truncated);
|
|
659
|
-
}
|
|
660
|
-
return {
|
|
661
|
-
...buildSourceSummary(activation, vendored),
|
|
662
|
-
providersRun: providerResults.map((result) => result.provider),
|
|
663
|
-
providerCounts,
|
|
664
|
-
detectedLanguages: Array.from(detectedLanguages).sort(),
|
|
665
|
-
detectedTestFrameworks: Array.from(detectedTestFrameworks).sort(),
|
|
666
|
-
excludedRoots: Array.from(IGNORED_DIRECTORY_NAMES).sort(),
|
|
667
|
-
truncated,
|
|
668
|
-
scanWarnings: sortUnique(scanWarnings),
|
|
669
|
-
};
|
|
670
|
-
}
|
|
671
|
-
// implements REQ-001
|
|
672
|
-
export function discoverProviderEvidence(workspaceRoot, activation) {
|
|
673
|
-
const vendored = findVendoredTrees(workspaceRoot);
|
|
674
|
-
if (!activation.allowCandidateGeneration) {
|
|
675
|
-
return {
|
|
676
|
-
evidence: [],
|
|
677
|
-
providerResults: [],
|
|
678
|
-
summary: {
|
|
679
|
-
...buildSourceSummary(activation, vendored),
|
|
680
|
-
providersRun: [],
|
|
681
|
-
providerCounts: createEmptyProviderCounts(),
|
|
682
|
-
detectedLanguages: [],
|
|
683
|
-
detectedTestFrameworks: [],
|
|
684
|
-
excludedRoots: Array.from(IGNORED_DIRECTORY_NAMES).sort(),
|
|
685
|
-
truncated: false,
|
|
686
|
-
scanWarnings: [],
|
|
687
|
-
},
|
|
688
|
-
};
|
|
689
|
-
}
|
|
690
|
-
const typedKibiDocs = runTypedKibiDocsProvider(workspaceRoot);
|
|
691
|
-
const typedPaths = new Set(typedKibiDocs.evidence
|
|
692
|
-
.map((item) => item.relativePath)
|
|
693
|
-
.filter((item) => Boolean(item)));
|
|
694
|
-
const providerResults = [
|
|
695
|
-
typedKibiDocs,
|
|
696
|
-
runGenericRepoDocsProvider(workspaceRoot, vendored, typedPaths),
|
|
697
|
-
runRepoMetadataProvider(workspaceRoot),
|
|
698
|
-
runRepoLayoutProvider(workspaceRoot, vendored),
|
|
699
|
-
runTestTopologyProvider(workspaceRoot, vendored),
|
|
700
|
-
runSourceSymbolsProvider(workspaceRoot, vendored),
|
|
701
|
-
];
|
|
702
|
-
const evidence = providerResults.flatMap((result) => result.evidence);
|
|
703
|
-
evidence.sort((left, right) => {
|
|
704
|
-
const providerCompare = AUTOPILOT_PROVIDER_ORDER.indexOf(left.provider) -
|
|
705
|
-
AUTOPILOT_PROVIDER_ORDER.indexOf(right.provider);
|
|
706
|
-
if (providerCompare !== 0)
|
|
707
|
-
return providerCompare;
|
|
708
|
-
const leftKey = left.relativePath ?? left.label;
|
|
709
|
-
const rightKey = right.relativePath ?? right.label;
|
|
710
|
-
return leftKey.localeCompare(rightKey);
|
|
711
|
-
});
|
|
712
|
-
return {
|
|
713
|
-
evidence,
|
|
714
|
-
providerResults,
|
|
715
|
-
summary: buildDiscoverySummary(activation, vendored, providerResults),
|
|
716
|
-
};
|
|
717
|
-
}
|
|
718
|
-
function toActivationPolicy(activationState) {
|
|
719
|
-
switch (activationState) {
|
|
720
|
-
case "root_partial":
|
|
721
|
-
return {
|
|
722
|
-
activationState,
|
|
723
|
-
activationMode: "repair_bootstrap",
|
|
724
|
-
applyBlocked: true,
|
|
725
|
-
allowCandidateGeneration: true,
|
|
726
|
-
reason: "Workspace root is only partially configured; run a repair bootstrap scan and keep apply blocked until the root is repaired.",
|
|
727
|
-
};
|
|
728
|
-
case "root_active_thin":
|
|
729
|
-
return {
|
|
730
|
-
activationState,
|
|
731
|
-
activationMode: "attached_thin_handoff",
|
|
732
|
-
applyBlocked: true,
|
|
733
|
-
allowCandidateGeneration: false,
|
|
734
|
-
reason: "Workspace already has an attached but thin KB; bootstrap synthesis is replaced by an explicit thin handoff.",
|
|
735
|
-
handoffMessage: "Attached thin KB detected. Review the sparse KB coverage and continue with a handoff instead of a bootstrap apply plan.",
|
|
736
|
-
};
|
|
737
|
-
case "root_active_seeded":
|
|
738
|
-
return {
|
|
739
|
-
activationState,
|
|
740
|
-
activationMode: "attached_seeded_handoff",
|
|
741
|
-
applyBlocked: true,
|
|
742
|
-
allowCandidateGeneration: false,
|
|
743
|
-
reason: "Workspace already has an attached seeded KB; bootstrap synthesis is replaced by an explicit seeded handoff.",
|
|
744
|
-
handoffMessage: "Attached seeded KB detected. Use the existing KB context instead of generating bootstrap candidates.",
|
|
745
|
-
};
|
|
746
|
-
case "vendored_only":
|
|
747
|
-
return {
|
|
748
|
-
activationState,
|
|
749
|
-
activationMode: "vendored_blocked",
|
|
750
|
-
applyBlocked: true,
|
|
751
|
-
allowCandidateGeneration: false,
|
|
752
|
-
reason: "Workspace appears to contain vendored Kibi sources only; bootstrap generation is blocked in this posture.",
|
|
753
|
-
handoffMessage: "Vendored Kibi posture detected. Move to the real project root before attempting bootstrap.",
|
|
754
|
-
};
|
|
755
|
-
case "root_uninitialized":
|
|
756
|
-
return {
|
|
757
|
-
activationState,
|
|
758
|
-
activationMode: "cold_start_bootstrap",
|
|
759
|
-
applyBlocked: false,
|
|
760
|
-
allowCandidateGeneration: true,
|
|
761
|
-
reason: "Workspace has no attached root KB yet; run a cold-start bootstrap scan across repository evidence.",
|
|
762
|
-
};
|
|
763
|
-
}
|
|
764
|
-
}
|
|
765
|
-
// implements REQ-mcp-init-kibi-autopilot-v1
|
|
766
|
-
export async function resolveActivationPolicy(workspaceRoot, prolog) {
|
|
767
|
-
return toActivationPolicy(await classifyActivationState(workspaceRoot, prolog));
|
|
768
|
-
}
|
|
769
|
-
function rootTargetsAllResolve(cwd) {
|
|
770
|
-
const config = readRootConfig(cwd) || {};
|
|
771
|
-
const paths = config.paths ?? {};
|
|
772
|
-
const keys = [
|
|
773
|
-
"requirements",
|
|
774
|
-
"scenarios",
|
|
775
|
-
"tests",
|
|
776
|
-
"adr",
|
|
777
|
-
"flags",
|
|
778
|
-
"events",
|
|
779
|
-
"facts",
|
|
780
|
-
"symbols",
|
|
781
|
-
];
|
|
782
|
-
for (const key of keys) {
|
|
783
|
-
const raw = paths[key] ?? DEFAULT_SYNC_PATHS[key];
|
|
784
|
-
if (!raw)
|
|
785
|
-
return false;
|
|
786
|
-
const normalized = raw.replace(/\/+$|\s+$/g, "");
|
|
787
|
-
const isFile = normalized.endsWith(".yaml") || normalized.endsWith(".yml");
|
|
788
|
-
if (isFile) {
|
|
789
|
-
if (!fs.existsSync(path.resolve(cwd, normalized)))
|
|
790
|
-
return false;
|
|
791
|
-
}
|
|
792
|
-
else {
|
|
793
|
-
const root = stripToRoot(normalized);
|
|
794
|
-
if (!fs.existsSync(path.resolve(cwd, root)))
|
|
795
|
-
return false;
|
|
796
|
-
}
|
|
797
|
-
}
|
|
798
|
-
return true;
|
|
799
|
-
}
|
|
800
|
-
/** Classify activation readiness for autopilot. */
|
|
801
|
-
// implements REQ-mcp-init-kibi-autopilot-v1
|
|
802
|
-
export async function classifyActivationState(workspaceRoot, prolog) {
|
|
803
|
-
const hasRootConfig = rootKbConfigExists(workspaceRoot);
|
|
804
|
-
const vendored = findVendoredTrees(workspaceRoot);
|
|
805
|
-
if (!hasRootConfig &&
|
|
806
|
-
vendored.length > 0 &&
|
|
807
|
-
!hasWorkspaceProjectSignals(workspaceRoot, vendored)) {
|
|
808
|
-
return "vendored_only";
|
|
809
|
-
}
|
|
810
|
-
if (!hasRootConfig) {
|
|
811
|
-
return "root_uninitialized";
|
|
812
|
-
}
|
|
813
|
-
// Root config exists → check targets
|
|
814
|
-
const allResolve = rootTargetsAllResolve(workspaceRoot);
|
|
815
|
-
if (!allResolve)
|
|
816
|
-
return "root_partial";
|
|
817
|
-
// Config exists and targets resolve — consult KB counts via Prolog
|
|
818
|
-
try {
|
|
819
|
-
const payload = await runJsonModuleQuery(prolog, "discovery.pl", "discovery:coverage_report_json(type, [], true, false, 100, 0, JsonString)", "Autopilot activation counts");
|
|
820
|
-
const rows = payload?.rows ?? [];
|
|
821
|
-
const counts = {};
|
|
822
|
-
for (const r of rows)
|
|
823
|
-
counts[r.type ?? r.id] = Number(r.count || 0);
|
|
824
|
-
const reqCount = counts.req ?? 0;
|
|
825
|
-
const nonSymbolTypes = [
|
|
826
|
-
"req",
|
|
827
|
-
"scenario",
|
|
828
|
-
"test",
|
|
829
|
-
"adr",
|
|
830
|
-
"flag",
|
|
831
|
-
"event",
|
|
832
|
-
"fact",
|
|
833
|
-
];
|
|
834
|
-
const nonSymbolTotal = nonSymbolTypes.reduce((s, t) => s + (counts[t] ?? 0), 0);
|
|
835
|
-
const scenarioTestAdrFact = (counts.scenario ?? 0) +
|
|
836
|
-
(counts.test ?? 0) +
|
|
837
|
-
(counts.adr ?? 0) +
|
|
838
|
-
(counts.fact ?? 0);
|
|
839
|
-
if (reqCount >= 1 && nonSymbolTotal >= 5 && scenarioTestAdrFact >= 1) {
|
|
840
|
-
return "root_active_seeded";
|
|
841
|
-
}
|
|
842
|
-
return "root_active_thin";
|
|
843
|
-
}
|
|
844
|
-
catch {
|
|
845
|
-
// If Prolog is unavailable or the query fails, conservatively treat as thin
|
|
846
|
-
return "root_active_thin";
|
|
847
|
-
}
|
|
848
|
-
}
|
|
849
|
-
// Recursively collect markdown files under `dir`, excluding known ignore dirs.
|
|
850
|
-
export function collectMarkdownFiles(dir, workspaceRoot, vendoredRoots) {
|
|
851
|
-
const results = [];
|
|
852
|
-
if (!fs.existsSync(dir))
|
|
853
|
-
return results;
|
|
854
|
-
const stat = fs.statSync(dir);
|
|
855
|
-
if (!stat.isDirectory())
|
|
856
|
-
return results;
|
|
857
|
-
const entries = fs.readdirSync(dir).sort();
|
|
858
|
-
for (const entry of entries) {
|
|
859
|
-
const full = path.join(dir, entry);
|
|
860
|
-
// Skip ignores
|
|
861
|
-
if (IGNORED_DIRECTORY_NAMES.has(entry.toLowerCase()))
|
|
862
|
-
continue;
|
|
863
|
-
// Skip vendored roots
|
|
864
|
-
const rel = path.relative(workspaceRoot, full).split(path.sep).join("/");
|
|
865
|
-
if (vendoredRoots.some((v) => rel === v || rel.startsWith(`${v}/`)))
|
|
866
|
-
continue;
|
|
867
|
-
const st = fs.statSync(full);
|
|
868
|
-
if (st.isDirectory()) {
|
|
869
|
-
results.push(...collectMarkdownFiles(full, workspaceRoot, vendoredRoots));
|
|
870
|
-
continue;
|
|
871
|
-
}
|
|
872
|
-
if (st.isFile() && entry.endsWith(".md")) {
|
|
873
|
-
results.push(path.relative(workspaceRoot, full).split(path.sep).join("/"));
|
|
874
|
-
}
|
|
875
|
-
}
|
|
876
|
-
return results;
|
|
877
|
-
}
|
|
878
|
-
/** Discover eligible source inputs for autopilot. */
|
|
879
|
-
// implements REQ-mcp-init-kibi-autopilot-v1
|
|
880
|
-
export function discoverSources(workspaceRoot, activation) {
|
|
881
|
-
const discovery = discoverProviderEvidence(workspaceRoot, activation);
|
|
882
|
-
const candidates = new Set();
|
|
883
|
-
for (const item of discovery.evidence) {
|
|
884
|
-
if (item.kind === "typed_markdown" ||
|
|
885
|
-
item.kind === "symbol_manifest" ||
|
|
886
|
-
item.kind === "generic_markdown" ||
|
|
887
|
-
item.kind === "source_symbols") {
|
|
888
|
-
const relativePath = item.relativePath;
|
|
889
|
-
if (relativePath) {
|
|
890
|
-
candidates.add(relativePath);
|
|
891
|
-
}
|
|
892
|
-
}
|
|
893
|
-
}
|
|
894
|
-
return {
|
|
895
|
-
candidates: Array.from(candidates).sort(),
|
|
896
|
-
summary: discovery.summary,
|
|
897
|
-
};
|
|
898
|
-
}
|