kibi-cursor 0.2.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.
@@ -0,0 +1,18 @@
1
+ {
2
+ "name": "kibi-cursor",
3
+ "displayName": "Kibi",
4
+ "version": "0.2.0",
5
+ "description": "Optional Cursor adapter for Kibi. Requires SWI-Prolog 9+ and project-local kibi-cli, kibi-mcp, and kibi-core before the bundled MCP server can start.",
6
+ "license": "AGPL-3.0-or-later",
7
+ "author": {
8
+ "name": "Kibi"
9
+ },
10
+ "homepage": "https://github.com/Looted/kibi/blob/develop/docs/install.md",
11
+ "repository": "https://github.com/Looted/kibi",
12
+ "keywords": ["kibi", "mcp", "knowledge-base", "traceability", "swi-prolog"],
13
+ "skills": "./skills/",
14
+ "rules": "./rules/",
15
+ "commands": "./commands/",
16
+ "hooks": "./hooks/hooks.json",
17
+ "mcpServers": "./mcp.json"
18
+ }
package/CHANGELOG.md ADDED
@@ -0,0 +1,21 @@
1
+ # kibi-cursor
2
+
3
+ ## 0.2.0
4
+
5
+ ### Minor Changes
6
+
7
+ - Cursor users can now install an optional Kibi adapter plugin that bundles MCP configuration, workflow rules, skills, slash commands, and advisory editor hooks. The plugin builds on the existing `kibi-cli` and `kibi-mcp` foundation without replacing them, and teams that prefer manual setup can continue configuring the local `kibi-mcp` server directly.
8
+
9
+ - Add the publishable `kibi-cursor` package with Cursor plugin manifest assets, rules, skills, commands, hook declarations, and MCP config.
10
+ - Add advisory hooks for bootstrap reminders, direct `.kb` edit warnings, read/write guidance, and session freshness follow-ups.
11
+ - Wire `kibi-cursor` into workspace build, pack, release-state, publish, CI unit coverage, docs, and marketplace verification.
12
+
13
+ ## 0.1.0
14
+
15
+ ### Minor Changes
16
+
17
+ - Cursor users can install an optional Kibi adapter plugin that bundles MCP configuration, workflow rules, skills, slash commands, and advisory editor hooks. The plugin builds on `kibi-core`, `kibi-cli`, and `kibi-mcp` without replacing them, and teams that prefer manual setup can continue configuring the local `kibi-mcp` server directly.
18
+
19
+ - Add the publishable `kibi-cursor` package with Cursor plugin manifest assets, bundled skills, rules, commands, hook declarations, and MCP config.
20
+ - Add a conservative hook runner with bounded session state, bootstrap reminders, direct `.kb` edit warnings, read/write guidance, and freshness follow-ups.
21
+ - Wire `kibi-cursor` into workspace build, pack, release-state, publish, CI unit coverage, docs, and marketplace verification.
package/README.md ADDED
@@ -0,0 +1,120 @@
1
+ # kibi-cursor
2
+
3
+ Cursor plugin for Kibi — repo-local, per-branch, queryable knowledge base.
4
+
5
+ `kibi-cursor` is optional. It provides Cursor-specific rules, skills, commands, MCP wiring, and advisory editor hooks, but it does not ship a replacement `kibi` CLI or `kibi-mcp` server binary.
6
+
7
+ ## Prerequisites
8
+
9
+ Install these **before** enabling the plugin MCP server:
10
+
11
+ | Requirement | Why |
12
+ | --- | --- |
13
+ | **SWI-Prolog 9+** (`swipl` on `PATH`) | Powers Kibi inference and validation |
14
+ | **`kibi-cli`** | Project CLI (`kibi` command) |
15
+ | **`kibi-mcp`** | MCP server the plugin's `mcp.json` invokes via `npx --no-install kibi-mcp` |
16
+ | **`kibi-core`** | Shared graph/runtime dependency |
17
+
18
+ ```bash
19
+ npm install --save-dev kibi-cli kibi-mcp kibi-core
20
+ swipl --version
21
+ npm exec -- kibi doctor
22
+ ```
23
+
24
+ Full setup: [docs/install.md](https://github.com/Looted/kibi/blob/develop/docs/install.md).
25
+
26
+ Add this plugin only after the base packages work in your project.
27
+
28
+ ## Installation
29
+
30
+ ### Cursor Marketplace (recommended)
31
+
32
+ Submit or install from the Kibi repository marketplace at `.cursor-plugin/marketplace.json`, which points at `./plugins/kibi-cursor`.
33
+
34
+ For local testing before marketplace publication, copy a real directory tree into Cursor's user-plugins folder. Symlinks are rejected when the target lives outside `plugins/local` (common on WSL):
35
+
36
+ ```bash
37
+ ./scripts/sync-cursor-plugin-local.sh
38
+ ```
39
+
40
+ On WSL workspaces, Cursor reads `~/.cursor/plugins/local` in your Linux home, not the Windows profile path. Reload Cursor (**Developer: Reload Window**) and check **Plugins → User**.
41
+
42
+ ### npm package
43
+
44
+ ```bash
45
+ npm install --save-dev kibi-cursor
46
+ ```
47
+
48
+ Copy the installed package into Cursor's local plugin directory (do not symlink):
49
+
50
+ ```bash
51
+ cp -r "$(npm root)/kibi-cursor" ~/.cursor/plugins/local/kibi-cursor
52
+ ```
53
+
54
+ ## MCP configuration
55
+
56
+ The plugin bundles `mcp.json` pointing at the project-local `kibi-mcp` binary:
57
+
58
+ ```json
59
+ {
60
+ "mcpServers": {
61
+ "kibi": {
62
+ "command": "npx",
63
+ "args": ["--no-install", "kibi-mcp"]
64
+ }
65
+ }
66
+ }
67
+ ```
68
+
69
+ Toggle the bundled MCP server from **Settings → Features → Model Context Protocol** after installing the plugin.
70
+
71
+ Manual MCP fallback (no plugin install required):
72
+
73
+ ```json
74
+ {
75
+ "mcpServers": {
76
+ "kibi": {
77
+ "command": "npx",
78
+ "args": ["--no-install", "kibi-mcp"]
79
+ }
80
+ }
81
+ }
82
+ ```
83
+
84
+ ## Features
85
+
86
+ ### Discovery-first rules and skills
87
+
88
+ - **Rules**: always-on MCP workflow guidance plus optional traceability rules for source files.
89
+ - **Skills**: `kibi-usage`, `init-kibi`, `kibi-freshness`, and `kibi-traceability`.
90
+ - **Commands**: `/init-kibi` documents the `kb_autopilot_generate` bootstrap workflow.
91
+
92
+ ### Advisory editor hooks
93
+
94
+ Hooks are warning-only and never replace MCP/CLI behavior:
95
+
96
+ - **sessionStart**: bootstrap reminder when `.kb/config.json` is missing.
97
+ - **preToolUse**: warns on explicit direct `.kb/**` edits without blocking.
98
+ - **beforeReadFile** and **postToolUse (Read)**: inject source-linked lookup guidance once per path per session.
99
+ - **postToolUse (Write/Edit)**: inject traceability and freshness guidance once per path per session.
100
+ - **stop**: emits a single freshness follow-up when meaningful paths changed during the session.
101
+
102
+ ### What the plugin does not do
103
+
104
+ Per the thin-adapter architecture, `kibi-cursor` does not:
105
+
106
+ - Own KB storage, parsing, or validation
107
+ - Run background `kibi sync` or `kibi check` (use git hooks and MCP tools)
108
+ - Block agent actions in advisory mode
109
+
110
+ ## Architecture
111
+
112
+ `kibi-cursor` is a thin bridge layer:
113
+
114
+ - **Agent-visible guidance**: public MCP tools (`kb_search`, `kb_query`, `kb_upsert`, `kb_check`, etc.) and the `/init-kibi` command
115
+ - **Editor-specific value**: Cursor hooks for read/write reminders and session freshness follow-ups
116
+ - **Foundation**: `kibi-core`, `kibi-cli`, and `kibi-mcp` remain required for project-local operations
117
+
118
+ ## License
119
+
120
+ AGPL-3.0-or-later
@@ -0,0 +1,21 @@
1
+ ---
2
+ name: init-kibi
3
+ description: Bootstrap Kibi knowledge for the current branch using the MCP autopilot workflow
4
+ ---
5
+
6
+ # Initialize Kibi
7
+
8
+ Bootstrap repo-local Kibi memory without editing `.kb/` files directly.
9
+
10
+ ## Workflow
11
+
12
+ 1. Run `kb_autopilot_generate` for read-only synthesis.
13
+ 2. Show the preview and get explicit approval before writes.
14
+ 3. Apply approved entities with sequential `kb_upsert`.
15
+ 4. Finish with `kb_check`.
16
+
17
+ ## Guidance
18
+
19
+ - Ask at most four bounded context questions when repository evidence is insufficient.
20
+ - Prefer source-linked entities so future `kb_query` lookups can verify context.
21
+ - Keep mutation batches small and reviewable.
@@ -0,0 +1,18 @@
1
+ {
2
+ "name": "kibi-cursor",
3
+ "displayName": "Kibi",
4
+ "version": "0.2.0",
5
+ "description": "Optional Cursor adapter for Kibi. Requires SWI-Prolog 9+ and project-local kibi-cli, kibi-mcp, and kibi-core before the bundled MCP server can start.",
6
+ "license": "AGPL-3.0-or-later",
7
+ "author": {
8
+ "name": "Kibi"
9
+ },
10
+ "homepage": "https://github.com/Looted/kibi/blob/develop/docs/install.md",
11
+ "repository": "https://github.com/Looted/kibi",
12
+ "keywords": ["kibi", "mcp", "knowledge-base", "traceability", "swi-prolog"],
13
+ "skills": "./skills/",
14
+ "rules": "./rules/",
15
+ "commands": "./commands/",
16
+ "hooks": "./hooks/hooks.json",
17
+ "mcpServers": "./mcp.json"
18
+ }
@@ -0,0 +1,21 @@
1
+ ---
2
+ name: init-kibi
3
+ description: Bootstrap Kibi knowledge for the current branch using the MCP autopilot workflow
4
+ ---
5
+
6
+ # Initialize Kibi
7
+
8
+ Bootstrap repo-local Kibi memory without editing `.kb/` files directly.
9
+
10
+ ## Workflow
11
+
12
+ 1. Run `kb_autopilot_generate` for read-only synthesis.
13
+ 2. Show the preview and get explicit approval before writes.
14
+ 3. Apply approved entities with sequential `kb_upsert`.
15
+ 4. Finish with `kb_check`.
16
+
17
+ ## Guidance
18
+
19
+ - Ask at most four bounded context questions when repository evidence is insufficient.
20
+ - Prefer source-linked entities so future `kb_query` lookups can verify context.
21
+ - Keep mutation batches small and reviewable.
@@ -0,0 +1,2 @@
1
+ export declare function readGuidance(filePath: string, cwd: string | undefined, hasKibi: boolean): string | undefined;
2
+ export declare function writeGuidance(filePath: string, cwd: string | undefined, hasKibi: boolean): string | undefined;
@@ -0,0 +1,37 @@
1
+ // implements REQ-cursor-kibi-plugin-v1
2
+ import { isDocumentationTrackedPath, isMeaningfulTrackedPath, toRepoRelativePath, } from "./path-policy.js";
3
+ export function readGuidance(filePath, cwd, hasKibi) {
4
+ if (!hasKibi) {
5
+ return undefined;
6
+ }
7
+ const relativePath = toRepoRelativePath(filePath, cwd);
8
+ if (!isMeaningfulTrackedPath(relativePath)) {
9
+ return undefined;
10
+ }
11
+ return [
12
+ "Kibi read guidance: before changing this file, discover linked knowledge with Kibi MCP.",
13
+ `Start with kb_search, then kb_query with sourceFile="${relativePath}".`,
14
+ "Prefer Kibi facts and requirements over long inline comments for durable knowledge.",
15
+ ].join("\n");
16
+ }
17
+ export function writeGuidance(filePath, cwd, hasKibi) {
18
+ if (!hasKibi) {
19
+ return undefined;
20
+ }
21
+ const relativePath = toRepoRelativePath(filePath, cwd);
22
+ if (!isMeaningfulTrackedPath(relativePath)) {
23
+ return undefined;
24
+ }
25
+ if (isDocumentationTrackedPath(relativePath)) {
26
+ return [
27
+ "Kibi write guidance: keep REQ, SCEN, and TEST artifacts separate.",
28
+ "Update KB entities through kb_upsert and finish with kb_check.",
29
+ "Do not edit .kb/ files directly.",
30
+ ].join("\n");
31
+ }
32
+ return [
33
+ "Kibi write guidance: link production symbols to requirements.",
34
+ `After editing, resolve freshness with kb_search/kb_query for sourceFile="${relativePath}".`,
35
+ "Prefer symbol manifest + executable_for or // implements REQ-xxx for traceability.",
36
+ ].join("\n");
37
+ }
@@ -0,0 +1,12 @@
1
+ export type HookEvent = "sessionStart" | "preToolUse" | "postToolUse" | "beforeReadFile" | "stop";
2
+ export type HookInput = {
3
+ event: string;
4
+ cwd?: string;
5
+ toolName?: string;
6
+ toolInput?: unknown;
7
+ conversationId?: string;
8
+ filePath?: string;
9
+ };
10
+ export declare function parseHookInput(input: unknown): HookInput;
11
+ export declare function readStdin(): Promise<string>;
12
+ export declare function parseStdinJson(rawInput: string): unknown;
@@ -0,0 +1,75 @@
1
+ function isRecord(value) {
2
+ return typeof value === "object" && value !== null && !Array.isArray(value);
3
+ }
4
+ function readString(record, keys) {
5
+ for (const key of keys) {
6
+ const value = record[key];
7
+ if (typeof value === "string") {
8
+ return value;
9
+ }
10
+ }
11
+ return undefined;
12
+ }
13
+ function normalizeEventName(event) {
14
+ const trimmed = event.trim();
15
+ if (trimmed.length === 0) {
16
+ return "";
17
+ }
18
+ return trimmed.charAt(0).toLowerCase() + trimmed.slice(1);
19
+ }
20
+ export function parseHookInput(input) {
21
+ if (!isRecord(input)) {
22
+ return { event: "" };
23
+ }
24
+ const rawEvent = readString(input, [
25
+ "event",
26
+ "hook_event",
27
+ "hookEvent",
28
+ "hook_event_name",
29
+ "name",
30
+ ]) ?? "";
31
+ const event = normalizeEventName(rawEvent);
32
+ const cwd = readString(input, [
33
+ "cwd",
34
+ "current_working_directory",
35
+ "workspace",
36
+ ]);
37
+ const toolName = readString(input, ["toolName", "tool_name", "tool"]);
38
+ const toolInput = input.toolInput ?? input.tool_input ?? input.input;
39
+ const conversationId = readString(input, [
40
+ "conversation_id",
41
+ "conversationId",
42
+ ]);
43
+ const filePath = readString(input, ["file_path", "filePath", "path"]);
44
+ const parsed = { event };
45
+ if (cwd !== undefined) {
46
+ parsed.cwd = cwd;
47
+ }
48
+ if (toolName !== undefined) {
49
+ parsed.toolName = toolName;
50
+ }
51
+ if (toolInput !== undefined) {
52
+ parsed.toolInput = toolInput;
53
+ }
54
+ if (conversationId !== undefined) {
55
+ parsed.conversationId = conversationId;
56
+ }
57
+ if (filePath !== undefined) {
58
+ parsed.filePath = filePath;
59
+ }
60
+ return parsed;
61
+ }
62
+ export async function readStdin() {
63
+ const chunks = [];
64
+ for await (const chunk of process.stdin) {
65
+ chunks.push(typeof chunk === "string" ? Buffer.from(chunk) : chunk);
66
+ }
67
+ return Buffer.concat(chunks).toString("utf8");
68
+ }
69
+ export function parseStdinJson(rawInput) {
70
+ const trimmed = rawInput.trim();
71
+ if (trimmed.length === 0) {
72
+ return {};
73
+ }
74
+ return JSON.parse(trimmed);
75
+ }
@@ -0,0 +1,13 @@
1
+ #!/usr/bin/env node
2
+ export type CursorHookResult = {
3
+ additional_context?: string;
4
+ permission?: "allow" | "deny";
5
+ user_message?: string;
6
+ agent_message?: string;
7
+ followup_message?: string;
8
+ };
9
+ export type HookEnvironment = {
10
+ pluginData?: string;
11
+ };
12
+ export declare function runHook(rawInput: unknown, environment?: HookEnvironment): Promise<CursorHookResult>;
13
+ export declare const hookRunnerPath: string;
@@ -0,0 +1,163 @@
1
+ #!/usr/bin/env node
2
+ // implements REQ-cursor-kibi-plugin-v1
3
+ import fs from "node:fs";
4
+ import path from "node:path";
5
+ import { fileURLToPath, pathToFileURL } from "node:url";
6
+ import { readGuidance, writeGuidance } from "./guidance.js";
7
+ import { parseHookInput, parseStdinJson, readStdin } from "./hook-input.js";
8
+ import { extractKbMcpToolName } from "./kb-mcp-tools.js";
9
+ import { addDirtyPaths, clearSessionHookState, hasGuidedPath, loadHookState, recordKbMcpTool, rememberGuidedPath, resolveStateDir, } from "./hook-state.js";
10
+ import { BOOTSTRAP_REMINDER, DIRECT_KB_EDIT_WARNING, stopFollowupMessage, } from "./messages.js";
11
+ import { extractExplicitPathFields, isDirectKbPath, isMeaningfulTrackedPath, toRepoRelativePath, } from "./path-policy.js";
12
+ const editableTools = new Set([
13
+ "Edit",
14
+ "MultiEdit",
15
+ "Write",
16
+ "StrReplace",
17
+ "apply_patch",
18
+ ]);
19
+ const readTools = new Set(["Read", "TabRead"]);
20
+ function emptyResult() {
21
+ return {};
22
+ }
23
+ function hasKibiConfig(cwd) {
24
+ if (!cwd) {
25
+ return false;
26
+ }
27
+ return fs.existsSync(path.join(cwd, ".kb", "config.json"));
28
+ }
29
+ function isEditLikeTool(toolName) {
30
+ return toolName === undefined || editableTools.has(toolName);
31
+ }
32
+ function isReadLikeTool(toolName) {
33
+ return toolName === undefined || readTools.has(toolName);
34
+ }
35
+ function resolveWorkspaceRoot(input) {
36
+ return input.cwd ?? process.cwd();
37
+ }
38
+ function resolvePrimaryPath(input) {
39
+ if (input.filePath) {
40
+ return input.filePath;
41
+ }
42
+ const explicitPaths = extractExplicitPathFields(input.toolInput);
43
+ return explicitPaths[0];
44
+ }
45
+ export async function runHook(rawInput, environment = {}) {
46
+ const input = parseHookInput(rawInput);
47
+ const pluginData = environment.pluginData ?? process.env.PLUGIN_DATA;
48
+ const stateDir = resolveStateDir(pluginData, input.conversationId);
49
+ const cwd = resolveWorkspaceRoot(input);
50
+ const kibiReady = hasKibiConfig(cwd);
51
+ switch (input.event) {
52
+ case "sessionStart":
53
+ if (!kibiReady) {
54
+ return { additional_context: BOOTSTRAP_REMINDER };
55
+ }
56
+ return emptyResult();
57
+ case "preToolUse": {
58
+ const explicitPaths = extractExplicitPathFields(input.toolInput);
59
+ const hasDirectKbEdit = isEditLikeTool(input.toolName) && explicitPaths.some(isDirectKbPath);
60
+ if (hasDirectKbEdit) {
61
+ return {
62
+ permission: "allow",
63
+ agent_message: DIRECT_KB_EDIT_WARNING,
64
+ };
65
+ }
66
+ return emptyResult();
67
+ }
68
+ case "beforeReadFile": {
69
+ const primaryPath = resolvePrimaryPath(input);
70
+ if (!primaryPath || !kibiReady) {
71
+ return { permission: "allow" };
72
+ }
73
+ const relativePath = toRepoRelativePath(primaryPath, cwd);
74
+ const state = loadHookState(stateDir);
75
+ if (hasGuidedPath(state, "read", relativePath)) {
76
+ return { permission: "allow" };
77
+ }
78
+ const guidance = readGuidance(primaryPath, cwd, kibiReady);
79
+ if (!guidance) {
80
+ return { permission: "allow" };
81
+ }
82
+ rememberGuidedPath(stateDir, "read", relativePath);
83
+ return {
84
+ permission: "allow",
85
+ agent_message: guidance,
86
+ };
87
+ }
88
+ case "postToolUse": {
89
+ const kbToolName = extractKbMcpToolName(input.toolName, input.toolInput);
90
+ if (kbToolName) {
91
+ recordKbMcpTool(stateDir, kbToolName);
92
+ }
93
+ const explicitPaths = extractExplicitPathFields(input.toolInput);
94
+ const dirtyPaths = explicitPaths
95
+ .map((candidate) => toRepoRelativePath(candidate, cwd))
96
+ .filter(isMeaningfulTrackedPath);
97
+ if (dirtyPaths.length > 0) {
98
+ addDirtyPaths(stateDir, dirtyPaths);
99
+ }
100
+ const primaryPath = resolvePrimaryPath(input);
101
+ if (!primaryPath || !kibiReady) {
102
+ return emptyResult();
103
+ }
104
+ const relativePath = toRepoRelativePath(primaryPath, cwd);
105
+ const state = loadHookState(stateDir);
106
+ if (isReadLikeTool(input.toolName)) {
107
+ if (hasGuidedPath(state, "read", relativePath)) {
108
+ return emptyResult();
109
+ }
110
+ const guidance = readGuidance(primaryPath, cwd, kibiReady);
111
+ if (!guidance) {
112
+ return emptyResult();
113
+ }
114
+ rememberGuidedPath(stateDir, "read", relativePath);
115
+ return { additional_context: guidance };
116
+ }
117
+ if (isEditLikeTool(input.toolName)) {
118
+ if (hasGuidedPath(state, "write", relativePath)) {
119
+ return emptyResult();
120
+ }
121
+ const guidance = writeGuidance(primaryPath, cwd, kibiReady);
122
+ if (!guidance) {
123
+ return emptyResult();
124
+ }
125
+ rememberGuidedPath(stateDir, "write", relativePath);
126
+ return { additional_context: guidance };
127
+ }
128
+ return emptyResult();
129
+ }
130
+ case "stop": {
131
+ const state = loadHookState(stateDir);
132
+ const followupMessage = stopFollowupMessage(state);
133
+ if (followupMessage) {
134
+ clearSessionHookState(stateDir);
135
+ return { followup_message: followupMessage };
136
+ }
137
+ if (state.dirtyPaths.length > 0 ||
138
+ state.kbMutationTools.length > 0 ||
139
+ state.kbCheckRun) {
140
+ clearSessionHookState(stateDir);
141
+ }
142
+ return emptyResult();
143
+ }
144
+ default:
145
+ return emptyResult();
146
+ }
147
+ }
148
+ async function main() {
149
+ const result = await runHook(parseStdinJson(await readStdin()));
150
+ process.stdout.write(`${JSON.stringify(result)}\n`);
151
+ }
152
+ const invokedPath = process.argv[1]
153
+ ? pathToFileURL(path.resolve(process.argv[1])).href
154
+ : "";
155
+ if (import.meta.url === invokedPath) {
156
+ main().catch((error) => {
157
+ const message = error instanceof Error ? error.message : "Unknown hook error";
158
+ process.stdout.write(`${JSON.stringify({
159
+ additional_context: `Kibi hook runner error: ${message}`,
160
+ })}\n`);
161
+ });
162
+ }
163
+ export const hookRunnerPath = fileURLToPath(import.meta.url);
@@ -0,0 +1,18 @@
1
+ export type HookState = {
2
+ dirtyPaths: string[];
3
+ guidedReadPaths: string[];
4
+ guidedWritePaths: string[];
5
+ kbMutationTools: string[];
6
+ kbCheckRun: boolean;
7
+ };
8
+ export declare function resolveStateDir(pluginData: string | undefined, conversationId: string | undefined): string | undefined;
9
+ export declare function loadHookState(stateDir: string | undefined): HookState;
10
+ export declare function saveHookState(stateDir: string | undefined, state: HookState): void;
11
+ export declare function updateHookState(stateDir: string | undefined, updater: (state: HookState) => HookState): HookState;
12
+ export declare function addDirtyPaths(stateDir: string | undefined, dirtyPaths: readonly string[]): HookState;
13
+ export declare function rememberGuidedPath(stateDir: string | undefined, kind: "read" | "write", guidedPath: string): HookState;
14
+ export declare function hasGuidedPath(state: HookState, kind: "read" | "write", guidedPath: string): boolean;
15
+ export declare function recordKbMcpTool(stateDir: string | undefined, toolName: string): HookState;
16
+ export declare function clearSessionHookState(stateDir: string | undefined): HookState;
17
+ /** @deprecated Use clearSessionHookState */
18
+ export declare function clearDirtyPaths(stateDir: string | undefined): HookState;