Conversation
There was a problem hiding this comment.
Pull request overview
This PR introduces a “discovery bundle” across Kibi’s MCP server and CLI, expanding the read-only public surface from a fixed 4-tool set to a curated set that includes discovery, freshness/status, gap analysis, coverage reporting, and bounded graph traversal—while keeping kb_query exact/deterministic.
Changes:
- Add new MCP tools:
kb_search,kb_status,kb_find_gaps,kb_coverage,kb_graph, plus richerkb_checktext and improved diagnostic usage logging. - Add new CLI commands mirroring MCP discovery/reporting:
kibi search,kibi status,kibi gaps,kibi coverage,kibi graph, with shared table/JSON rendering helpers. - Update OpenCode/VS Code guidance, docs, requirements/ADR/scenario/test artifacts, and packed E2E coverage to reflect the curated tool surface and discovery-first workflow.
Reviewed changes
Copilot reviewed 74 out of 74 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| scripts/populate-kb.ts | Updates KB seed scenario title to reflect kibi init default hook behavior. |
| README.md | Documents global vs repo-local dogfood setup and adds discovery-first CLI examples. |
| packages/vscode/README.md | Notes dogfood workflow and updates MCP surface guidance for VS Code. |
| packages/opencode/tests/prompt.test.ts | Updates prompt tests to require mentions of new curated tools. |
| packages/opencode/tests/hook-contract.test.ts | Updates public-tool mentions to include kb_search (and curated framing). |
| packages/opencode/tests/agent-surface-policy.test.ts | Adjusts policy language and asserts prompt references kb_search. |
| packages/opencode/src/prompt.ts | Updates injected guidance to discovery-first (kb_search then kb_query) + curated tool list. |
| packages/opencode/README.md | Documents discovery-first guidance and expanded curated MCP surface. |
| packages/mcp/tests/tools/status.test.ts | Adds unit tests for MCP kb_status handler. |
| packages/mcp/tests/tools/search.test.ts | Adds unit tests for MCP kb_search handler (ranking + markdown-only constraint). |
| packages/mcp/tests/tools/graph.test.ts | Adds unit tests for MCP kb_graph handler. |
| packages/mcp/tests/tools/find-gaps.test.ts | Adds unit tests for MCP kb_find_gaps handler. |
| packages/mcp/tests/tools/coverage.test.ts | Adds unit tests for MCP kb_coverage handler. |
| packages/mcp/tests/tools/check-aggregated.test.ts | Tightens assertions on human-readable kb_check output text. |
| packages/mcp/tests/server.test.ts | Expands tools/list expectations, adds live kb_status session test, extends diagnostic assertions. |
| packages/mcp/src/tools/status.ts | Implements MCP kb_status handler using curated Prolog module query. |
| packages/mcp/src/tools/search.ts | Implements MCP kb_search handler (TS ranking + markdown body reads). |
| packages/mcp/src/tools/query.ts | Refactors kb_query to reuse shared entity loading/pagination helpers. |
| packages/mcp/src/tools/graph.ts | Implements MCP kb_graph handler backed by Prolog traversal. |
| packages/mcp/src/tools/find-gaps.ts | Implements MCP kb_find_gaps handler backed by Prolog reporting. |
| packages/mcp/src/tools/entity-query.ts | Extracts shared Prolog-goal building, entity loading, tag filtering, dedupe, pagination. |
| packages/mcp/src/tools/coverage.ts | Implements MCP kb_coverage handler backed by Prolog reporting. |
| packages/mcp/src/tools/core-module.ts | Adds helper for resolving/loading Prolog modules and running JSON-binding queries. |
| packages/mcp/src/tools/check.ts | Improves human-readable kb_check summaries while preserving structured output. |
| packages/mcp/src/tools-config.ts | Registers new curated discovery/reporting tools with schemas and descriptions. |
| packages/mcp/src/server/tools.ts | Registers new tools and logs derived diagnostic fields for usage analytics. |
| packages/mcp/src/server/docs.ts | Updates MCP prompt/docs resource content to reflect curated tool surface and discovery-first guidance. |
| packages/mcp/src/diagnostics.ts | Adds deriveDiagnosticFields to enrich usage logging (counts/summaries). |
| packages/core/src/status.pl | Adds curated Prolog status/freshness reporting (kb_status_json/1). |
| packages/core/src/discovery.pl | Adds curated Prolog discovery/reporting predicates (gaps/coverage/graph) with JSON output. |
| packages/cli/tests/commands/status.test.ts | Adds CLI tests for kibi status freshness behavior and output formats. |
| packages/cli/tests/commands/search.test.ts | Adds CLI tests for markdown-only search behavior. |
| packages/cli/tests/commands/graph.test.ts | Adds CLI tests for bounded graph traversal behavior. |
| packages/cli/tests/commands/gaps.test.ts | Adds CLI tests for relationship gap analysis output. |
| packages/cli/tests/commands/coverage.test.ts | Adds CLI tests for coverage reporting and CLI flags. |
| packages/cli/src/commands/status.ts | Implements CLI status command using curated Prolog module query. |
| packages/cli/src/commands/search.ts | Implements CLI search command with TS ranking + markdown body reads. |
| packages/cli/src/commands/graph.ts | Implements CLI graph command calling Prolog traversal. |
| packages/cli/src/commands/gaps.ts | Implements CLI gaps command calling Prolog reporting. |
| packages/cli/src/commands/doctor.ts | Updates remediation text from kibi init --hooks to kibi init. |
| packages/cli/src/commands/discovery-shared.ts | Adds shared CLI helpers for Prolog module queries + JSON/table rendering. |
| packages/cli/src/commands/coverage.ts | Implements CLI coverage command calling Prolog reporting. |
| packages/cli/src/cli.ts | Wires new CLI commands into Commander. |
| documentation/tests/TEST-mcp-search-discovery.md | Adds KB test entity describing discovery bundle verification scope. |
| documentation/tests/TEST-005.md | Updates MCP toolset expectations from fixed-count to curated set. |
| documentation/tests/e2e/packed/mcp.test.ts | Updates packed E2E MCP tools list expectations. |
| documentation/tests/e2e/packed/mcp-protocol-regression.test.ts | Updates packed E2E protocol regression tool list expectations. |
| documentation/tests/e2e/packed/issue-53-regression.test.ts | Updates regression expectations/messages to curated public tool surface. |
| documentation/tests/e2e/packed/discovery-bundle.test.ts | Adds packed E2E parity suite across CLI and MCP for discovery workflows + diagnostics. |
| documentation/symbols.yaml | Updates symbol coordinates for moved/changed code. |
| documentation/scenarios/SCEN-mcp-search-discovery.md | Adds scenario entity for discovery-first, curated-surface usage. |
| documentation/scenarios/SCEN-002.md | Updates init scenario from --hooks to default kibi init. |
| documentation/requirements/REQ-opencode-kibi-plugin-v1.md | Updates requirement language to reference curated MCP tools (incl. discovery/reporting). |
| documentation/requirements/REQ-opencode-agent-mcp-only.md | Updates agent guidance requirement to list the curated public tool surface. |
| documentation/requirements/REQ-mcp-search-discovery.md | Adds requirement entity defining discovery/reporting tools across MCP + CLI. |
| documentation/requirements/REQ-014.md | Updates hook behavior wording to kibi init default hook install. |
| documentation/requirements/REQ-013.md | Updates inference/public-surface wording to “curated” (not fixed-count). |
| documentation/requirements/REQ-008.md | Updates hooks requirement to kibi init default hook install semantics. |
| documentation/requirements/REQ-002.md | Updates MCP requirement from “4 tools” to “curated public tool surface”. |
| documentation/facts/FACT-MCP-TOOLSET-CORE-6.md | Updates fact to remove fixed tool count framing. |
| documentation/facts/FACT-INFERENCE-TOOLS-CORE-3.md | Updates inference fact to reference curated surface. |
| documentation/facts/FACT-INFERENCE-SURFACE.md | Updates inference-surface fact to remove fixed-count tool framing. |
| documentation/adr/ADR-discovery-tools-public-surface.md | Adds ADR describing expanded read surface without exposing raw inference. |
| docs/troubleshooting.md | Updates hook remediation from kibi init --hooks to kibi init. |
| docs/superpowers/specs/2026-03-22-discovery-bundle-design.md | Adds design spec for discovery bundle architecture and constraints. |
| docs/superpowers/plans/2026-03-22-discovery-bundle.md | Adds implementation plan for discovery bundle. |
| docs/prompts/llm-rules.md | Updates agent rules to prefer kb_search then kb_query and list curated tools. |
| docs/mcp-reference.md | Expands MCP docs to include new tools and discovery-first guidance. |
| docs/install.md | Adds dogfood workflow note and updates quickstart steps to include search/status. |
| docs/inference-rules.md | Updates public-surface framing to curated tools (not fixed-count). |
| docs/cli-reference.md | Updates CLI reference to include new discovery commands and revised semantics. |
| CONTRIBUTING.md | Updates hook guidance to kibi init default behavior. |
| .changeset/docs-discovery-bundle-refresh.md | Adds patch changeset for docs/tooling guidance updates across packages. |
| .changeset/discovery-bundle-tools.md | Adds minor/patch changeset for new discovery bundle features across packages. |
| synced_at(DataFile, SyncedAt) :- | ||
| exists_file(DataFile), | ||
| !, | ||
| time_file(DataFile, Timestamp), | ||
| format_time(atom(SyncedAt), '%FT%TZ', Timestamp). | ||
| synced_at(_, ''). | ||
|
|
There was a problem hiding this comment.
synced_at/2 returns an empty string when kb.rdf is missing (synced_at(_, '')). This makes the JSON payload contain "syncedAt": "", but the TS handler types it as string | null and consumers typically expect null/absent when unknown. Prefer emitting JSON null (e.g. SWI @(null)) or adjust the TS payload contract consistently across MCP/CLI.
| async function rankEntities( | ||
| entities: Record<string, unknown>[], | ||
| query: string, | ||
| workspaceRoot: string, | ||
| ): Promise<SearchMatch[]> { | ||
| const matches: SearchMatch[] = []; | ||
|
|
||
| for (const entity of entities) { | ||
| const match = await rankEntity(entity, query, workspaceRoot); | ||
| if (match) { | ||
| matches.push(match); | ||
| } | ||
| } | ||
|
|
There was a problem hiding this comment.
The ranking implementation here (rankEntities/rankEntity/markdown body loading) appears to be a near-duplicate of packages/mcp/src/tools/search.ts. Keeping two copies of the same scoring logic will likely drift and cause CLI/MCP parity bugs. Consider extracting the shared ranking + markdown-snippet logic into a common module (e.g. under packages/shared or kibi-cli/kibi-mcp shared util) and reuse it from both entrypoints.
| async function loadMarkdownBody( | ||
| source: string, | ||
| workspaceRoot: string, | ||
| ): Promise<string | null> { | ||
| if (!source) { | ||
| return null; | ||
| } | ||
|
|
||
| const normalizedSource = source.split("#", 1)[0]?.trim() ?? ""; | ||
| if (!normalizedSource.endsWith(".md")) { | ||
| return null; | ||
| } | ||
|
|
||
| const filePath = path.isAbsolute(normalizedSource) | ||
| ? normalizedSource | ||
| : path.join(workspaceRoot, normalizedSource); | ||
|
|
||
| try { | ||
| const fileContent = await fs.readFile(filePath, "utf8"); | ||
| return matter(fileContent).content; | ||
| } catch { | ||
| return null; |
There was a problem hiding this comment.
loadMarkdownBody will read any absolute .md path (and relative paths via path.join) based on entity.source. Since source is entity-controlled data, this enables path traversal / arbitrary file reads (e.g. ../../secrets.md or an absolute path) via kb_search. To keep this repo-local as described, resolve against workspaceRoot and reject paths that escape it, and consider disallowing absolute paths entirely (or only allow absolute paths that are still within the workspace root).
| async function loadMarkdownBody( | ||
| source: string, | ||
| workspaceRoot: string, | ||
| ): Promise<string | null> { | ||
| if (!source) { | ||
| return null; | ||
| } | ||
|
|
||
| const normalizedSource = source.split("#", 1)[0]?.trim() ?? ""; | ||
| if (!normalizedSource.endsWith(".md")) { | ||
| return null; | ||
| } | ||
|
|
||
| const fullPath = path.isAbsolute(normalizedSource) | ||
| ? normalizedSource | ||
| : path.join(workspaceRoot, normalizedSource); | ||
|
|
||
| try { | ||
| const content = await fs.readFile(fullPath, "utf8"); | ||
| return matter(content).content; | ||
| } catch { | ||
| return null; | ||
| } |
There was a problem hiding this comment.
loadMarkdownBody reads markdown by turning entity.source into a filesystem path (including allowing absolute paths). Because source comes from entity metadata, this permits path traversal / arbitrary file reads via kibi search (e.g. ../../private.md or an absolute path). Resolve against the workspace root and reject any path that escapes it; ideally disallow absolute paths or only allow them when they’re still inside the workspace root.
|
@copilot open a new pull request to apply changes based on the comments in this thread |
…g, path traversal Co-authored-by: Looted <6255880+Looted@users.noreply.github.com> Agent-Logs-Url: https://github.com/Looted/kibi/sessions/e93522d3-426b-4c57-a231-ecec98867131
fix: shared search ranking, path traversal in loadMarkdownBody, null syncedAt
No description provided.