kibi-mcp 0.12.0 → 0.14.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 +15 -11
- package/dist/server/tools.js +6 -0
- package/dist/tools/autopilot-candidates.js +120 -6
- package/dist/tools/autopilot-discovery.js +20 -5
- package/dist/tools/autopilot-generate.js +47 -1
- package/dist/tools/briefing-generate.js +100 -12
- package/dist/tools/delete.js +46 -0
- package/dist/tools/model-requirement.js +334 -0
- package/dist/tools/symbols.js +116 -30
- package/dist/tools/upsert.js +11 -0
- package/dist/tools-config.js +50 -0
- package/dist/utils/brief-marker.js +107 -0
- package/package.json +3 -3
package/dist/server/docs.js
CHANGED
|
@@ -62,7 +62,7 @@ export const PROMPTS = [
|
|
|
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`:
|
|
65
|
+
"- `promptBlock`: review text that can be surfaced in optional human-facing briefs",
|
|
66
66
|
"- `recommendedActions`: agent-facing next steps, including any REQ/SCEN/TEST authoring routed for manual handling",
|
|
67
67
|
"- `declaredContext`: the user-provided bootstrap context",
|
|
68
68
|
"- `confidence`: confidence summary for the generated output",
|
|
@@ -70,19 +70,19 @@ export const PROMPTS = [
|
|
|
70
70
|
"- `candidates`: synthesized entities grounded in declared context and source evidence",
|
|
71
71
|
"- `discoverySummary`: source-backed discovery notes",
|
|
72
72
|
"",
|
|
73
|
-
"## Step 3:
|
|
73
|
+
"## Step 3: Optional Human Review",
|
|
74
74
|
"",
|
|
75
|
-
"
|
|
75
|
+
"Surface the `promptBlock` and a summary of `candidates` when optional human review is useful. Human review is post-hoc/optional via VS Code briefs and must not block writes.",
|
|
76
76
|
"",
|
|
77
|
-
"## Step 4: Apply
|
|
77
|
+
"## Step 4: Apply Candidates",
|
|
78
78
|
"",
|
|
79
|
-
"Apply
|
|
80
|
-
"1. Execute each
|
|
79
|
+
"Apply candidates sequentially using `kb_upsert`.",
|
|
80
|
+
"1. Execute each candidate's `applyPlan` in ascending phase order.",
|
|
81
81
|
"2. Confirm success of each `kb_upsert` before moving to the next.",
|
|
82
82
|
"3. Run `kb_check` after the batch to verify KB integrity.",
|
|
83
83
|
"",
|
|
84
84
|
"## Rules",
|
|
85
|
-
"-
|
|
85
|
+
"- Human review is optional and post-hoc via VS Code briefs; do not gate writes on synchronous sign-off.",
|
|
86
86
|
"- `kb_autopilot_generate` is strictly read-only; synthesis is the backend, not the actor.",
|
|
87
87
|
"- Guidance must stay MCP-only; do not suggest `kibi` CLI commands.",
|
|
88
88
|
].join("\n"),
|
|
@@ -135,7 +135,10 @@ export const PROMPTS = [
|
|
|
135
135
|
"Core modeling principles:",
|
|
136
136
|
"- Kibi has eight entity types: common authoring (req, scenario, test, fact) and supporting/system (adr, flag, event, symbol).",
|
|
137
137
|
"- Encode requirements as linked facts: `req --constrains--> fact` plus `req --requires_property--> fact`.",
|
|
138
|
-
"-
|
|
138
|
+
"- High-confidence modeling (>= 0.7) is fully automated; optional human review can happen post-hoc.",
|
|
139
|
+
"- Low-confidence claims (< 0.7) are downgraded to `observation` facts to prevent false-positive contradictions.",
|
|
140
|
+
"- Only strict domain facts participate in contradiction inference; observation and meta facts are non-blocking notes.",
|
|
141
|
+
"- v1 contradictions are limited to exact-value, boolean/enum, numeric range, and polarity conflicts.",
|
|
139
142
|
"- Use `kb_search` first for discovery, then `kb_query` for exact follow-up before any mutation.",
|
|
140
143
|
"- Use `kb_upsert` and `kb_delete` only for intentional, traceable KB changes.",
|
|
141
144
|
"- Run `kb_check` after meaningful mutations to catch integrity issues early.",
|
|
@@ -152,9 +155,10 @@ export const PROMPTS = [
|
|
|
152
155
|
"Follow this sequence for reliable operation:",
|
|
153
156
|
"",
|
|
154
157
|
"1. **Discover first**: Call `kb_search` for exploratory discovery, then `kb_query` to confirm exact current state before mutation.",
|
|
155
|
-
"2. **
|
|
156
|
-
"3. **
|
|
157
|
-
"4. **
|
|
158
|
+
"2. **Check schema status**: Call `kb_status` to see if a schema migration is required for the branch KB.",
|
|
159
|
+
"3. **Create-before-link**: Create endpoint entities with `kb_upsert` before linking them.",
|
|
160
|
+
"4. **Validate intent**: If creating links, call `kb_query` for both endpoint IDs first to ensure they exist.",
|
|
161
|
+
"5. **Model requirements as facts**: For new/updated reqs, create/reuse fact entities first, then express req semantics with `constrains` + `requires_property` (automated via `kb_model_requirement`).",
|
|
158
162
|
"5. **Mutate**: Call `kb_upsert` for create/update, or `kb_delete` for explicit removals.",
|
|
159
163
|
"6. **Targeted checks**: Run `kb_check` after meaningful mutations; specify only the rules you need.",
|
|
160
164
|
"",
|
package/dist/server/tools.js
CHANGED
|
@@ -11,6 +11,7 @@ import { handleKbQuery } from "../tools/query.js";
|
|
|
11
11
|
import { handleKbSearch } from "../tools/search.js";
|
|
12
12
|
import { handleKbStatus } from "../tools/status.js";
|
|
13
13
|
import { handleKbUpsert } from "../tools/upsert.js";
|
|
14
|
+
import { handleKbModelRequirement, } from "../tools/model-requirement.js";
|
|
14
15
|
import { handleKbAutopilotGenerate, } from "../tools/autopilot-generate.js";
|
|
15
16
|
import { handleKbBriefingGenerate, } from "../tools/briefing-generate.js";
|
|
16
17
|
const defaultToolsServerDeps = {
|
|
@@ -59,6 +60,7 @@ const DEFAULT_TOOLS_RUNTIME = {
|
|
|
59
60
|
handleKbSearch,
|
|
60
61
|
handleKbStatus,
|
|
61
62
|
handleKbUpsert,
|
|
63
|
+
handleKbModelRequirement,
|
|
62
64
|
handleKbAutopilotGenerate,
|
|
63
65
|
handleKbBriefingGenerate,
|
|
64
66
|
};
|
|
@@ -321,6 +323,10 @@ runtime = DEFAULT_TOOLS_RUNTIME) {
|
|
|
321
323
|
const prolog = await runtime.ensureProlog();
|
|
322
324
|
return runtime.handleKbCheck(prolog, args);
|
|
323
325
|
}, runtime);
|
|
326
|
+
addTool(server, "kb_model_requirement", toolDef("kb_model_requirement").description, toolDef("kb_model_requirement").inputSchema, async (args) => {
|
|
327
|
+
const prolog = await runtime.ensureProlog();
|
|
328
|
+
return runtime.handleKbModelRequirement(prolog, args);
|
|
329
|
+
}, runtime);
|
|
324
330
|
addTool(server, "kb_autopilot_generate", toolDef("kb_autopilot_generate").description, toolDef("kb_autopilot_generate").inputSchema, async (args) => {
|
|
325
331
|
const prolog = await runtime.ensureProlog();
|
|
326
332
|
return runtime.handleKbAutopilotGenerate(prolog, args);
|
|
@@ -2,8 +2,11 @@
|
|
|
2
2
|
// Implements candidate assembly from public CLI extractors
|
|
3
3
|
import { extractFromManifest } from "kibi-cli/extractors/manifest";
|
|
4
4
|
import { extractFromMarkdown } from "kibi-cli/extractors/markdown";
|
|
5
|
+
import { buildStrictWriteSet, modelRequirementClaims, } from "kibi-cli/public/check-types";
|
|
5
6
|
import path from "node:path";
|
|
6
7
|
import fs from "node:fs";
|
|
8
|
+
import { createRepoIgnorePolicy } from "kibi-cli/ignore-policy";
|
|
9
|
+
import { estimateNormativeSignalConfidence, extractRequirementClaim, strictWriteSetToApplyPlan, writeSetPrimaryEntityId, } from "./model-requirement.js";
|
|
7
10
|
function slugify(value, maxLength = 80) {
|
|
8
11
|
return value
|
|
9
12
|
.toLowerCase()
|
|
@@ -63,10 +66,8 @@ function resolveCandidatePaths(filePath, workspaceRoot) {
|
|
|
63
66
|
.join("/");
|
|
64
67
|
return { absolutePath, relativePath };
|
|
65
68
|
}
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
return /(^|\/)(documentation|\.kb|\.git|node_modules|vendor|vendors|third_party|third-party|dist|coverage)(\/|$)/.test(normalized);
|
|
69
|
-
}
|
|
69
|
+
// Legacy helper removed in favor of the shared ignore policy from kibi-cli/ignore-policy.
|
|
70
|
+
// Use createRepoIgnorePolicy(workspaceRoot).isIgnored(relativePath) in builders.
|
|
70
71
|
function shouldIncludeGenericMarkdown(relativePath, providerScopedMarkdown) {
|
|
71
72
|
const base = path.basename(relativePath).toLowerCase();
|
|
72
73
|
const inDocsDir = /(^|\/)docs\//.test(relativePath);
|
|
@@ -103,6 +104,7 @@ function buildUpsertFromExtraction(er, typeOverride) {
|
|
|
103
104
|
export function buildTypedMarkdownCandidates(discoveryResult, existingEntities) {
|
|
104
105
|
const candidates = [];
|
|
105
106
|
const workspaceRoot = existingEntities.workspaceRoot ?? process.cwd();
|
|
107
|
+
const ignorePolicy = createRepoIgnorePolicy(workspaceRoot);
|
|
106
108
|
for (const filePath of getTypedMarkdownFiles(discoveryResult)) {
|
|
107
109
|
try {
|
|
108
110
|
const extraction = extractFromMarkdown(filePath);
|
|
@@ -110,6 +112,8 @@ export function buildTypedMarkdownCandidates(discoveryResult, existingEntities)
|
|
|
110
112
|
if (existingEntities.ids.has(entity.id))
|
|
111
113
|
continue;
|
|
112
114
|
const { absolutePath, relativePath } = resolveCandidatePaths(filePath, workspaceRoot);
|
|
115
|
+
if (ignorePolicy.isIgnored(relativePath))
|
|
116
|
+
continue;
|
|
113
117
|
const candidateId = `md:${relativePath}:${entity.id}`;
|
|
114
118
|
const upsert = buildUpsertFromExtraction({ entity, relationships });
|
|
115
119
|
candidates.push({
|
|
@@ -186,13 +190,14 @@ export function buildSymbolManifestCandidates(discoveryResult, existingEntities)
|
|
|
186
190
|
export function buildGenericMarkdownCandidates(discoveryResult, existingEntities, minConfidence = 0.8) {
|
|
187
191
|
const candidates = [];
|
|
188
192
|
const workspaceRoot = existingEntities.workspaceRoot ?? process.cwd();
|
|
193
|
+
const ignorePolicy = createRepoIgnorePolicy(workspaceRoot);
|
|
189
194
|
const providerScopedMarkdown = hasGenericMarkdownEvidence(discoveryResult);
|
|
190
195
|
const files = getGenericMarkdownFiles(discoveryResult);
|
|
191
196
|
for (const rawPath of files) {
|
|
192
197
|
try {
|
|
193
198
|
const filePath = String(rawPath);
|
|
194
199
|
const { absolutePath, relativePath } = resolveCandidatePaths(filePath, workspaceRoot);
|
|
195
|
-
if (
|
|
200
|
+
if (ignorePolicy.isIgnored(relativePath))
|
|
196
201
|
continue;
|
|
197
202
|
// Legacy path-only discovery was conservative. Provider-scoped discovery
|
|
198
203
|
// already filters eligible generic docs, so allow broader repo markdown there.
|
|
@@ -287,12 +292,13 @@ export function collectSourceOnlyAuthoringSignals(discoveryResult, existingEntit
|
|
|
287
292
|
const signals = [];
|
|
288
293
|
const seen = new Set();
|
|
289
294
|
const workspaceRoot = existingEntities.workspaceRoot ?? process.cwd();
|
|
295
|
+
const ignorePolicy = createRepoIgnorePolicy(workspaceRoot);
|
|
290
296
|
const providerScopedMarkdown = hasGenericMarkdownEvidence(discoveryResult);
|
|
291
297
|
for (const rawPath of getGenericMarkdownFiles(discoveryResult)) {
|
|
292
298
|
try {
|
|
293
299
|
const filePath = String(rawPath);
|
|
294
300
|
const { absolutePath, relativePath } = resolveCandidatePaths(filePath, workspaceRoot);
|
|
295
|
-
if (
|
|
301
|
+
if (ignorePolicy.isIgnored(relativePath))
|
|
296
302
|
continue;
|
|
297
303
|
if (!shouldIncludeGenericMarkdown(relativePath, providerScopedMarkdown))
|
|
298
304
|
continue;
|
|
@@ -370,6 +376,113 @@ export function collectSourceOnlyAuthoringSignals(discoveryResult, existingEntit
|
|
|
370
376
|
});
|
|
371
377
|
}
|
|
372
378
|
// implements REQ-mcp-init-kibi-autopilot-v1
|
|
379
|
+
export function buildNormativeRequirementCandidates(discoveryResult, existingEntities, minConfidence = 0.8) {
|
|
380
|
+
const candidates = [];
|
|
381
|
+
const seeds = [];
|
|
382
|
+
const workspaceRoot = existingEntities.workspaceRoot ?? process.cwd();
|
|
383
|
+
const providerScopedMarkdown = hasGenericMarkdownEvidence(discoveryResult);
|
|
384
|
+
const ignorePolicy = createRepoIgnorePolicy(workspaceRoot);
|
|
385
|
+
for (const rawPath of getGenericMarkdownFiles(discoveryResult)) {
|
|
386
|
+
try {
|
|
387
|
+
const filePath = String(rawPath);
|
|
388
|
+
const { absolutePath, relativePath } = resolveCandidatePaths(filePath, workspaceRoot);
|
|
389
|
+
if (ignorePolicy.isIgnored(relativePath))
|
|
390
|
+
continue;
|
|
391
|
+
if (!shouldIncludeGenericMarkdown(relativePath, providerScopedMarkdown))
|
|
392
|
+
continue;
|
|
393
|
+
if (!fs.existsSync(absolutePath))
|
|
394
|
+
continue;
|
|
395
|
+
const content = fs.readFileSync(absolutePath, "utf8");
|
|
396
|
+
const lines = content.split(/\r?\n/);
|
|
397
|
+
let activeHeading;
|
|
398
|
+
let activeHeadingLine;
|
|
399
|
+
let inCodeFence = false;
|
|
400
|
+
for (let i = 0; i < lines.length; i++) {
|
|
401
|
+
const line = lines[i];
|
|
402
|
+
if (line === undefined)
|
|
403
|
+
continue;
|
|
404
|
+
if (/^\s*(```|~~~)/.test(line)) {
|
|
405
|
+
inCodeFence = !inCodeFence;
|
|
406
|
+
continue;
|
|
407
|
+
}
|
|
408
|
+
if (inCodeFence)
|
|
409
|
+
continue;
|
|
410
|
+
const headingMatch = line.match(/^\s*#+\s*(.+)$/);
|
|
411
|
+
if (headingMatch?.[1]) {
|
|
412
|
+
activeHeading = headingMatch[1].trim();
|
|
413
|
+
activeHeadingLine = i + 1;
|
|
414
|
+
continue;
|
|
415
|
+
}
|
|
416
|
+
const statement = line
|
|
417
|
+
.replace(/^\s*[-*+]\s+/, "")
|
|
418
|
+
.replace(/^\s*\d+[.)]\s+/, "")
|
|
419
|
+
.trim();
|
|
420
|
+
if (!statement || !/\b(must|shall|should)\b/i.test(statement))
|
|
421
|
+
continue;
|
|
422
|
+
const confidence = estimateNormativeSignalConfidence(statement, activeHeading);
|
|
423
|
+
if (confidence < minConfidence)
|
|
424
|
+
continue;
|
|
425
|
+
const extracted = extractRequirementClaim({
|
|
426
|
+
text: statement,
|
|
427
|
+
source: relativePath,
|
|
428
|
+
confidence,
|
|
429
|
+
provenance: `${relativePath}#L${i + 1}`,
|
|
430
|
+
});
|
|
431
|
+
const writeSet = buildStrictWriteSet({
|
|
432
|
+
claim: extracted.claim,
|
|
433
|
+
statement: extracted.statement,
|
|
434
|
+
});
|
|
435
|
+
if (!writeSet.isStrict)
|
|
436
|
+
continue;
|
|
437
|
+
seeds.push({
|
|
438
|
+
input: {
|
|
439
|
+
claim: extracted.claim,
|
|
440
|
+
statement: extracted.statement,
|
|
441
|
+
},
|
|
442
|
+
writeSet,
|
|
443
|
+
sourcePath: absolutePath,
|
|
444
|
+
evidence: [
|
|
445
|
+
`normative_statement:${relativePath}#L${i + 1}`,
|
|
446
|
+
...(activeHeading && activeHeadingLine
|
|
447
|
+
? [`generic_heading:${relativePath}#L${activeHeadingLine}`]
|
|
448
|
+
: []),
|
|
449
|
+
],
|
|
450
|
+
});
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
catch {
|
|
454
|
+
// ignore unreadable files when deriving strict requirement candidates
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
const modeledIds = new Set(modelRequirementClaims(seeds.map((seed) => seed.input)).map((writeSet) => writeSetPrimaryEntityId(writeSet)));
|
|
458
|
+
const emittedIds = new Set();
|
|
459
|
+
for (const seed of seeds) {
|
|
460
|
+
const entityId = writeSetPrimaryEntityId(seed.writeSet);
|
|
461
|
+
if (!modeledIds.has(entityId) || emittedIds.has(entityId))
|
|
462
|
+
continue;
|
|
463
|
+
if (existingEntities.ids.has(entityId))
|
|
464
|
+
continue;
|
|
465
|
+
emittedIds.add(entityId);
|
|
466
|
+
candidates.push({
|
|
467
|
+
candidateId: `norm:${entityId.toLowerCase()}`,
|
|
468
|
+
entityType: "req",
|
|
469
|
+
title: seed.input.statement,
|
|
470
|
+
sourceKind: "generic_markdown",
|
|
471
|
+
sourcePath: seed.sourcePath,
|
|
472
|
+
confidence: seed.writeSet.confidence,
|
|
473
|
+
confidenceBand: toConfidenceBand(seed.writeSet.confidence),
|
|
474
|
+
evidence: seed.evidence,
|
|
475
|
+
relationships: seed.writeSet.relationships.map((relationship) => ({
|
|
476
|
+
type: relationship.type,
|
|
477
|
+
from: relationship.from,
|
|
478
|
+
to: relationship.to,
|
|
479
|
+
})),
|
|
480
|
+
applyPlan: strictWriteSetToApplyPlan(seed.writeSet),
|
|
481
|
+
});
|
|
482
|
+
}
|
|
483
|
+
return candidates;
|
|
484
|
+
}
|
|
485
|
+
// implements REQ-mcp-init-kibi-autopilot-v1
|
|
373
486
|
export function buildProviderEvidenceCandidates(discoveryResult, existingEntities, minConfidence = 0.8) {
|
|
374
487
|
const candidates = [];
|
|
375
488
|
const workspaceRoot = existingEntities.workspaceRoot ?? process.cwd();
|
|
@@ -431,5 +544,6 @@ export default {
|
|
|
431
544
|
buildSymbolManifestCandidates,
|
|
432
545
|
buildGenericMarkdownCandidates,
|
|
433
546
|
collectSourceOnlyAuthoringSignals,
|
|
547
|
+
buildNormativeRequirementCandidates,
|
|
434
548
|
buildProviderEvidenceCandidates,
|
|
435
549
|
};
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
import fs from "node:fs";
|
|
8
8
|
import path from "node:path";
|
|
9
9
|
import fg from "fast-glob";
|
|
10
|
+
import { createRepoIgnorePolicy } from "kibi-cli/ignore-policy";
|
|
10
11
|
import * as cliSymbolCoordinator from "kibi-cli/extractors/symbols-coordinator";
|
|
11
12
|
import { runJsonModuleQuery } from "./core-module.js";
|
|
12
13
|
// implements REQ-001
|
|
@@ -211,7 +212,7 @@ function normalizeDiscoveryPaths(cwd) {
|
|
|
211
212
|
symbols: readPath("symbols"),
|
|
212
213
|
};
|
|
213
214
|
}
|
|
214
|
-
function buildIgnoredGlobs(vendoredRoots) {
|
|
215
|
+
function buildIgnoredGlobs(vendoredRoots, workspaceRoot) {
|
|
215
216
|
const ignored = new Set();
|
|
216
217
|
for (const dirName of IGNORED_DIRECTORY_NAMES) {
|
|
217
218
|
ignored.add(`**/${dirName}`);
|
|
@@ -226,6 +227,18 @@ function buildIgnoredGlobs(vendoredRoots) {
|
|
|
226
227
|
ignored.add(`**/${normalized}`);
|
|
227
228
|
ignored.add(`**/${normalized}/**`);
|
|
228
229
|
}
|
|
230
|
+
// Use shared ignore policy to include .gitignore, nested ignores, and other rules.
|
|
231
|
+
try {
|
|
232
|
+
const policy = createRepoIgnorePolicy(workspaceRoot);
|
|
233
|
+
const globs = policy.getFastGlobIgnoreGlobs();
|
|
234
|
+
for (const glob of globs) {
|
|
235
|
+
if (glob)
|
|
236
|
+
ignored.add(glob);
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
catch {
|
|
240
|
+
// best-effort only
|
|
241
|
+
}
|
|
229
242
|
return Array.from(ignored);
|
|
230
243
|
}
|
|
231
244
|
function detectLanguagesFromPaths(paths) {
|
|
@@ -266,6 +279,7 @@ function runTypedKibiDocsProvider(workspaceRoot) {
|
|
|
266
279
|
onlyFiles: true,
|
|
267
280
|
unique: true,
|
|
268
281
|
suppressErrors: true,
|
|
282
|
+
ignore: buildIgnoredGlobs([], workspaceRoot),
|
|
269
283
|
});
|
|
270
284
|
const manifestFiles = discoveryPaths.symbols
|
|
271
285
|
? fg.sync(discoveryPaths.symbols, {
|
|
@@ -274,6 +288,7 @@ function runTypedKibiDocsProvider(workspaceRoot) {
|
|
|
274
288
|
onlyFiles: true,
|
|
275
289
|
unique: true,
|
|
276
290
|
suppressErrors: true,
|
|
291
|
+
ignore: buildIgnoredGlobs([], workspaceRoot),
|
|
277
292
|
})
|
|
278
293
|
: [];
|
|
279
294
|
const evidence = [
|
|
@@ -292,7 +307,7 @@ function runGenericRepoDocsProvider(workspaceRoot, vendoredRoots, typedFilePaths
|
|
|
292
307
|
onlyFiles: true,
|
|
293
308
|
unique: true,
|
|
294
309
|
suppressErrors: true,
|
|
295
|
-
ignore: buildIgnoredGlobs(vendoredRoots),
|
|
310
|
+
ignore: buildIgnoredGlobs(vendoredRoots, workspaceRoot),
|
|
296
311
|
});
|
|
297
312
|
const evidence = sortUnique(markdownFiles)
|
|
298
313
|
.map((absolutePath) => createFileEvidence("generic_repo_docs", "generic_markdown", workspaceRoot, absolutePath))
|
|
@@ -435,7 +450,7 @@ function runRepoLayoutProvider(workspaceRoot, vendoredRoots) {
|
|
|
435
450
|
onlyFiles: true,
|
|
436
451
|
unique: true,
|
|
437
452
|
suppressErrors: true,
|
|
438
|
-
ignore: buildIgnoredGlobs(vendoredRoots),
|
|
453
|
+
ignore: buildIgnoredGlobs(vendoredRoots, workspaceRoot),
|
|
439
454
|
});
|
|
440
455
|
return {
|
|
441
456
|
provider: "repo_layout",
|
|
@@ -468,7 +483,7 @@ function runTestTopologyProvider(workspaceRoot, vendoredRoots) {
|
|
|
468
483
|
onlyFiles: true,
|
|
469
484
|
unique: true,
|
|
470
485
|
suppressErrors: true,
|
|
471
|
-
ignore: buildIgnoredGlobs(vendoredRoots),
|
|
486
|
+
ignore: buildIgnoredGlobs(vendoredRoots, workspaceRoot),
|
|
472
487
|
});
|
|
473
488
|
const detectedFrameworks = new Set();
|
|
474
489
|
const detectedLanguages = new Set();
|
|
@@ -525,7 +540,7 @@ function runSourceSymbolsProvider(workspaceRoot, vendoredRoots) {
|
|
|
525
540
|
onlyFiles: true,
|
|
526
541
|
unique: true,
|
|
527
542
|
suppressErrors: true,
|
|
528
|
-
ignore: buildIgnoredGlobs(vendoredRoots),
|
|
543
|
+
ignore: buildIgnoredGlobs(vendoredRoots, workspaceRoot),
|
|
529
544
|
});
|
|
530
545
|
const evidence = [];
|
|
531
546
|
const detectedLanguages = new Set();
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import path from "node:path";
|
|
2
|
-
import
|
|
2
|
+
import fg from "fast-glob";
|
|
3
|
+
import { createRepoIgnorePolicy } from "kibi-cli/ignore-policy";
|
|
4
|
+
import { buildNormativeRequirementCandidates, collectSourceOnlyAuthoringSignals, buildGenericMarkdownCandidates, buildProviderEvidenceCandidates, buildTypedMarkdownCandidates, buildSymbolManifestCandidates, } from "./autopilot-candidates.js";
|
|
5
|
+
import { getWorkspaceMigrationWarning } from "./model-requirement.js";
|
|
3
6
|
import { discoverProviderEvidence, resolveActivationPolicy, } from "./autopilot-discovery.js";
|
|
4
7
|
import { loadEntities } from "./entity-query.js";
|
|
5
8
|
import { resolveWorkspaceRoot } from "../workspace.js";
|
|
@@ -373,6 +376,7 @@ _prolog, args) {
|
|
|
373
376
|
const activation = await resolveActivationPolicy(workspaceRoot, prolog);
|
|
374
377
|
const activationState = activation.activationState;
|
|
375
378
|
const activationDiscovery = discoverProviderEvidence(workspaceRoot, activation);
|
|
379
|
+
const migrationWarning = await getWorkspaceMigrationWarning(workspaceRoot);
|
|
376
380
|
const declaredContext = normalizeBootstrapContext(bootstrapContext);
|
|
377
381
|
const discoveredCandidatePaths = activationDiscovery.evidence.reduce((acc, item) => {
|
|
378
382
|
const relativePath = item.relativePath;
|
|
@@ -405,6 +409,7 @@ _prolog, args) {
|
|
|
405
409
|
let typedMarkdownCandidates = [];
|
|
406
410
|
let manifestCandidates = [];
|
|
407
411
|
let genericCandidates = [];
|
|
412
|
+
let normativeRequirementCandidates = [];
|
|
408
413
|
let providerEvidenceCandidates = [];
|
|
409
414
|
let allCandidates = [];
|
|
410
415
|
const seenByKey = new Map();
|
|
@@ -427,6 +432,10 @@ _prolog, args) {
|
|
|
427
432
|
ids: existingIds,
|
|
428
433
|
workspaceRoot,
|
|
429
434
|
}, normalizedMinConfidence);
|
|
435
|
+
normativeRequirementCandidates = buildNormativeRequirementCandidates(candidateDiscovery, {
|
|
436
|
+
ids: existingIds,
|
|
437
|
+
workspaceRoot,
|
|
438
|
+
}, normalizedMinConfidence);
|
|
430
439
|
}
|
|
431
440
|
providerEvidenceCandidates = buildProviderEvidenceCandidates(candidateDiscovery, {
|
|
432
441
|
ids: existingIds,
|
|
@@ -436,6 +445,7 @@ _prolog, args) {
|
|
|
436
445
|
...typedMarkdownCandidates,
|
|
437
446
|
...manifestCandidates,
|
|
438
447
|
...genericCandidates,
|
|
448
|
+
...normativeRequirementCandidates,
|
|
439
449
|
...providerEvidenceCandidates,
|
|
440
450
|
];
|
|
441
451
|
if (entityTypes && entityTypes.length > 0) {
|
|
@@ -516,6 +526,40 @@ _prolog, args) {
|
|
|
516
526
|
seenByKey.set(titleKey, record);
|
|
517
527
|
}
|
|
518
528
|
}
|
|
529
|
+
// Detect repository files that would be candidate inputs but are ignored by
|
|
530
|
+
// the repo ignore policy (e.g. .sisyphus drafts, .gitignore entries). Add
|
|
531
|
+
// them to suppressedCandidates with reason `ignored_source` so callers see
|
|
532
|
+
// why those files were omitted from candidate output.
|
|
533
|
+
try {
|
|
534
|
+
const repoIgnore = createRepoIgnorePolicy(workspaceRoot);
|
|
535
|
+
const potentialFiles = fg.sync(["**/*.md", "**/symbols.{yml,yaml}"], {
|
|
536
|
+
cwd: workspaceRoot,
|
|
537
|
+
absolute: true,
|
|
538
|
+
onlyFiles: true,
|
|
539
|
+
unique: true,
|
|
540
|
+
dot: true,
|
|
541
|
+
suppressErrors: true,
|
|
542
|
+
});
|
|
543
|
+
for (const absPath of potentialFiles) {
|
|
544
|
+
const rel = toWorkspaceRelativePath(workspaceRoot, absPath);
|
|
545
|
+
const explain = repoIgnore.explain(rel);
|
|
546
|
+
if (explain.ignored) {
|
|
547
|
+
// avoid duplicating existing suppressed entries for the same source
|
|
548
|
+
if (!suppressed.some((s) => String(s.sourcePath ?? "") === rel && s.reason === "ignored_source")) {
|
|
549
|
+
suppressed.push({
|
|
550
|
+
candidateId: String("") /* no candidate id for ignored source */,
|
|
551
|
+
reason: "ignored_source",
|
|
552
|
+
sourcePath: rel,
|
|
553
|
+
entityType: String("") /* unknown at this stage */,
|
|
554
|
+
detail: explain.reason,
|
|
555
|
+
});
|
|
556
|
+
}
|
|
557
|
+
}
|
|
558
|
+
}
|
|
559
|
+
}
|
|
560
|
+
catch {
|
|
561
|
+
// best-effort only; ignore failures here so generation can continue
|
|
562
|
+
}
|
|
519
563
|
const candidateRecords = Array.from(seenByKey.values());
|
|
520
564
|
const payoffSummary = buildPayoffSummary(candidateRecords);
|
|
521
565
|
const promptBlock = buildPromptBlock(workspaceRoot, activationState, activation.activationMode, activation.reason, activation.applyBlocked, declaredContext, candidateRecords, sourceOnlySignals, activationDiscovery.summary.scanWarnings);
|
|
@@ -533,6 +577,7 @@ _prolog, args) {
|
|
|
533
577
|
bootstrapMode: activation.activationMode,
|
|
534
578
|
activationReason: activation.reason,
|
|
535
579
|
applyBlocked: effectiveApplyBlocked,
|
|
580
|
+
migrationWarning,
|
|
536
581
|
...(activation.handoffMessage
|
|
537
582
|
? { handoffMessage: activation.handoffMessage }
|
|
538
583
|
: {}),
|
|
@@ -554,6 +599,7 @@ _prolog, args) {
|
|
|
554
599
|
},
|
|
555
600
|
],
|
|
556
601
|
structuredContent,
|
|
602
|
+
migrationWarning,
|
|
557
603
|
candidates: candidateRecords,
|
|
558
604
|
suppressedCandidates: suppressed,
|
|
559
605
|
payoffSummary,
|