kibi-cursor 0.3.1 → 0.5.1

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.
Files changed (53) hide show
  1. package/.cursor-plugin/plugin.json +1 -1
  2. package/CHANGELOG.md +92 -0
  3. package/README.md +21 -4
  4. package/agent-plugin/mcp.json +10 -0
  5. package/agent-plugin/plugin.json +11 -0
  6. package/agent-plugin/skills/init-kibi/SKILL.md +62 -0
  7. package/agent-plugin/skills/kibi-freshness/SKILL.md +44 -0
  8. package/agent-plugin/skills/kibi-traceability/SKILL.md +54 -0
  9. package/agent-plugin/skills/kibi-usage/SKILL.md +270 -0
  10. package/agent-plugin/skills/kibi-usage/resources/fact-lanes.md +235 -0
  11. package/agent-plugin/skills/kibi-usage/resources/logic-ir.md +43 -0
  12. package/agent-plugin/skills/kibi-usage/resources/operation-access.md +44 -0
  13. package/agent-plugin/skills/kibi-usage/resources/relationship-directions.md +119 -0
  14. package/agent-plugin/skills/kibi-usage/resources/ui-requirements.md +96 -0
  15. package/agent-plugin/skills/kibi-usage/resources/workflows.md +80 -0
  16. package/dist/.cursor-plugin/plugin.json +1 -1
  17. package/dist/guidance.d.ts +10 -2
  18. package/dist/guidance.js +26 -17
  19. package/dist/hook-runner.d.ts +1 -0
  20. package/dist/hook-runner.js +34 -7
  21. package/dist/hook-state-storage.d.ts +11 -0
  22. package/dist/hook-state-storage.js +187 -0
  23. package/dist/hook-state.d.ts +3 -4
  24. package/dist/hook-state.js +6 -206
  25. package/dist/kb-mcp-tools.d.ts +3 -0
  26. package/dist/kb-mcp-tools.js +6 -0
  27. package/dist/messages.d.ts +4 -2
  28. package/dist/messages.js +14 -2
  29. package/dist/rules/kibi-workflow.mdc +3 -3
  30. package/dist/skills/.canon-hash.json +12 -0
  31. package/dist/skills/init-kibi/SKILL.md +50 -11
  32. package/dist/skills/kibi-freshness/SKILL.md +32 -10
  33. package/dist/skills/kibi-traceability/SKILL.md +41 -11
  34. package/dist/skills/kibi-usage/SKILL.md +149 -97
  35. package/dist/skills/kibi-usage/resources/fact-lanes.md +235 -0
  36. package/dist/skills/kibi-usage/resources/logic-ir.md +43 -0
  37. package/dist/skills/kibi-usage/resources/operation-access.md +44 -0
  38. package/dist/skills/kibi-usage/resources/relationship-directions.md +119 -0
  39. package/dist/skills/kibi-usage/resources/ui-requirements.md +96 -0
  40. package/dist/skills/kibi-usage/resources/workflows.md +80 -0
  41. package/package.json +3 -2
  42. package/rules/kibi-workflow.mdc +3 -3
  43. package/skills/.canon-hash.json +12 -0
  44. package/skills/init-kibi/SKILL.md +50 -11
  45. package/skills/kibi-freshness/SKILL.md +32 -10
  46. package/skills/kibi-traceability/SKILL.md +41 -11
  47. package/skills/kibi-usage/SKILL.md +149 -97
  48. package/skills/kibi-usage/resources/fact-lanes.md +235 -0
  49. package/skills/kibi-usage/resources/logic-ir.md +43 -0
  50. package/skills/kibi-usage/resources/operation-access.md +44 -0
  51. package/skills/kibi-usage/resources/relationship-directions.md +119 -0
  52. package/skills/kibi-usage/resources/ui-requirements.md +96 -0
  53. package/skills/kibi-usage/resources/workflows.md +80 -0
@@ -0,0 +1,187 @@
1
+ import { randomUUID } from "node:crypto";
2
+ import fs from "node:fs";
3
+ import os from "node:os";
4
+ import path from "node:path";
5
+ const stateFileName = "hook-state.json";
6
+ const lockFileName = "hook-state.lock";
7
+ export const maxDirtyPaths = 50;
8
+ export const maxGuidedPaths = 100;
9
+ export const maxKbMutationTools = 20;
10
+ const lockRetryCount = 100;
11
+ const lockRetryDelayMs = 5;
12
+ const staleLockAgeMs = 30_000;
13
+ function statePath(pluginData) {
14
+ return path.join(pluginData, stateFileName);
15
+ }
16
+ function lockPath(pluginData) {
17
+ return path.join(pluginData, lockFileName);
18
+ }
19
+ function isRecord(value) {
20
+ return typeof value === "object" && value !== null && !Array.isArray(value);
21
+ }
22
+ export function normalizePath(dirtyPath) {
23
+ return dirtyPath.trim().replaceAll("\\", "/");
24
+ }
25
+ function sleepSync(milliseconds) {
26
+ const buffer = new SharedArrayBuffer(4);
27
+ const view = new Int32Array(buffer);
28
+ Atomics.wait(view, 0, 0, milliseconds);
29
+ }
30
+ function uniqueTempPath(pluginData) {
31
+ return path.join(pluginData, `${stateFileName}.${process.pid}.${Date.now()}.${randomUUID()}.tmp`);
32
+ }
33
+ export function mergeStringPaths(existingPaths, nextPaths) {
34
+ const merged = [...existingPaths, ...nextPaths]
35
+ .map(normalizePath)
36
+ .filter((dirtyPath) => dirtyPath.length > 0);
37
+ return [...new Set(merged)].slice(-maxGuidedPaths);
38
+ }
39
+ export function emptyHookState() {
40
+ return {
41
+ mcpState: "unknown",
42
+ dirtyPaths: [],
43
+ guidedReadPaths: [],
44
+ guidedWritePaths: [],
45
+ kbMutationTools: [],
46
+ kbCheckRun: false,
47
+ impactCheckRun: false,
48
+ impactCheckedPaths: [],
49
+ };
50
+ }
51
+ function acquireLock(pluginData) {
52
+ const targetLockPath = lockPath(pluginData);
53
+ for (let attempt = 0; attempt < lockRetryCount; attempt++) {
54
+ try {
55
+ return fs.openSync(targetLockPath, "wx");
56
+ }
57
+ catch (error) {
58
+ if (error instanceof Error &&
59
+ "code" in error &&
60
+ error.code === "EEXIST") {
61
+ if (removeStaleLock(targetLockPath)) {
62
+ continue;
63
+ }
64
+ sleepSync(lockRetryDelayMs);
65
+ continue;
66
+ }
67
+ return undefined;
68
+ }
69
+ }
70
+ return undefined;
71
+ }
72
+ function removeStaleLock(targetLockPath) {
73
+ let stats;
74
+ try {
75
+ stats = fs.statSync(targetLockPath);
76
+ }
77
+ catch {
78
+ return false;
79
+ }
80
+ if (Date.now() - stats.mtimeMs < staleLockAgeMs) {
81
+ return false;
82
+ }
83
+ try {
84
+ fs.unlinkSync(targetLockPath);
85
+ return true;
86
+ }
87
+ catch {
88
+ return false;
89
+ }
90
+ }
91
+ function releaseLock(pluginData, fileDescriptor) {
92
+ try {
93
+ fs.closeSync(fileDescriptor);
94
+ }
95
+ catch (error) {
96
+ if (!(error instanceof Error)) {
97
+ throw error;
98
+ }
99
+ }
100
+ try {
101
+ fs.unlinkSync(lockPath(pluginData));
102
+ }
103
+ catch (error) {
104
+ if (!(error instanceof Error)) {
105
+ throw error;
106
+ }
107
+ }
108
+ }
109
+ function coerceHookState(value) {
110
+ if (!isRecord(value)) {
111
+ return emptyHookState();
112
+ }
113
+ const strings = (candidate, normalize) => Array.isArray(candidate)
114
+ ? candidate
115
+ .filter((entry) => typeof entry === "string")
116
+ .map(normalize)
117
+ .filter((entry) => entry.length > 0)
118
+ : [];
119
+ const dirtyPaths = strings(value.dirtyPaths, normalizePath);
120
+ const guidedReadPaths = strings(value.guidedReadPaths, normalizePath);
121
+ const guidedWritePaths = strings(value.guidedWritePaths, normalizePath);
122
+ const kbMutationTools = strings(value.kbMutationTools, (entry) => entry.trim());
123
+ const impactCheckedPaths = strings(value.impactCheckedPaths, normalizePath);
124
+ return {
125
+ mcpState: value.mcpState === "observed" ? "observed" : "unknown",
126
+ dirtyPaths: [...new Set(dirtyPaths)].slice(-maxDirtyPaths),
127
+ guidedReadPaths: [...new Set(guidedReadPaths)].slice(-maxGuidedPaths),
128
+ guidedWritePaths: [...new Set(guidedWritePaths)].slice(-maxGuidedPaths),
129
+ kbMutationTools: [...new Set(kbMutationTools)].slice(-maxKbMutationTools),
130
+ kbCheckRun: value.kbCheckRun === true,
131
+ impactCheckRun: value.impactCheckRun === true,
132
+ impactCheckedPaths: [...new Set(impactCheckedPaths)].slice(-maxGuidedPaths),
133
+ };
134
+ }
135
+ export function resolveStateDir(pluginData, conversationId) {
136
+ if (pluginData) {
137
+ return pluginData;
138
+ }
139
+ if (!conversationId) {
140
+ return undefined;
141
+ }
142
+ return path.join(os.tmpdir(), "kibi-cursor-hook-state", conversationId.replaceAll(/[^a-zA-Z0-9._-]/g, "_"));
143
+ }
144
+ export function loadHookState(stateDir) {
145
+ if (!stateDir) {
146
+ return emptyHookState();
147
+ }
148
+ try {
149
+ return coerceHookState(JSON.parse(fs.readFileSync(statePath(stateDir), "utf8")));
150
+ }
151
+ catch {
152
+ return emptyHookState();
153
+ }
154
+ }
155
+ export function saveHookState(stateDir, state) {
156
+ if (!stateDir) {
157
+ return;
158
+ }
159
+ fs.mkdirSync(stateDir, { recursive: true });
160
+ const boundedState = coerceHookState(state);
161
+ const tempPath = uniqueTempPath(stateDir);
162
+ fs.writeFileSync(tempPath, `${JSON.stringify(boundedState)}\n`);
163
+ fs.renameSync(tempPath, statePath(stateDir));
164
+ }
165
+ export function updateHookState(stateDir, updater) {
166
+ const initialState = loadHookState(stateDir);
167
+ if (!stateDir) {
168
+ return updater(initialState);
169
+ }
170
+ fs.mkdirSync(stateDir, { recursive: true });
171
+ const lockFileDescriptor = acquireLock(stateDir);
172
+ if (lockFileDescriptor === undefined) {
173
+ return updater(initialState);
174
+ }
175
+ try {
176
+ const lockedState = loadHookState(stateDir);
177
+ const nextState = updater(lockedState);
178
+ saveHookState(stateDir, nextState);
179
+ return nextState;
180
+ }
181
+ catch {
182
+ return updater(initialState);
183
+ }
184
+ finally {
185
+ releaseLock(stateDir, lockFileDescriptor);
186
+ }
187
+ }
@@ -1,4 +1,7 @@
1
+ import type { McpState } from "./kb-mcp-tools.js";
2
+ export { loadHookState, resolveStateDir, saveHookState, updateHookState, } from "./hook-state-storage.js";
1
3
  export type HookState = {
4
+ mcpState: McpState;
2
5
  dirtyPaths: string[];
3
6
  guidedReadPaths: string[];
4
7
  guidedWritePaths: string[];
@@ -7,10 +10,6 @@ export type HookState = {
7
10
  impactCheckRun: boolean;
8
11
  impactCheckedPaths: string[];
9
12
  };
10
- export declare function resolveStateDir(pluginData: string | undefined, conversationId: string | undefined): string | undefined;
11
- export declare function loadHookState(stateDir: string | undefined): HookState;
12
- export declare function saveHookState(stateDir: string | undefined, state: HookState): void;
13
- export declare function updateHookState(stateDir: string | undefined, updater: (state: HookState) => HookState): HookState;
14
13
  export declare function addDirtyPaths(stateDir: string | undefined, dirtyPaths: readonly string[]): HookState;
15
14
  export declare function rememberGuidedPath(stateDir: string | undefined, kind: "read" | "write", guidedPath: string): HookState;
16
15
  export declare function hasGuidedPath(state: HookState, kind: "read" | "write", guidedPath: string): boolean;
@@ -1,206 +1,5 @@
1
- // implements REQ-cursor-kibi-plugin-v1
2
- import { randomUUID } from "node:crypto";
3
- import fs from "node:fs";
4
- import os from "node:os";
5
- import path from "node:path";
6
- const stateFileName = "hook-state.json";
7
- const lockFileName = "hook-state.lock";
8
- const maxDirtyPaths = 50;
9
- const maxGuidedPaths = 100;
10
- const lockRetryCount = 100;
11
- const lockRetryDelayMs = 5;
12
- const staleLockAgeMs = 30_000;
13
- function statePath(pluginData) {
14
- return path.join(pluginData, stateFileName);
15
- }
16
- function lockPath(pluginData) {
17
- return path.join(pluginData, lockFileName);
18
- }
19
- function isRecord(value) {
20
- return typeof value === "object" && value !== null && !Array.isArray(value);
21
- }
22
- function normalizePath(dirtyPath) {
23
- return dirtyPath.trim().replaceAll("\\", "/");
24
- }
25
- function sleepSync(milliseconds) {
26
- const buffer = new SharedArrayBuffer(4);
27
- const view = new Int32Array(buffer);
28
- Atomics.wait(view, 0, 0, milliseconds);
29
- }
30
- function uniqueTempPath(pluginData) {
31
- return path.join(pluginData, `${stateFileName}.${process.pid}.${Date.now()}.${randomUUID()}.tmp`);
32
- }
33
- function mergeStringPaths(existingPaths, nextPaths) {
34
- const merged = [...existingPaths, ...nextPaths]
35
- .map(normalizePath)
36
- .filter((dirtyPath) => dirtyPath.length > 0);
37
- return [...new Set(merged)].slice(-maxGuidedPaths);
38
- }
39
- const maxKbMutationTools = 20;
40
- function emptyHookState() {
41
- return {
42
- dirtyPaths: [],
43
- guidedReadPaths: [],
44
- guidedWritePaths: [],
45
- kbMutationTools: [],
46
- kbCheckRun: false,
47
- impactCheckRun: false,
48
- impactCheckedPaths: [],
49
- };
50
- }
51
- function acquireLock(pluginData) {
52
- const targetLockPath = lockPath(pluginData);
53
- for (let attempt = 0; attempt < lockRetryCount; attempt++) {
54
- try {
55
- return fs.openSync(targetLockPath, "wx");
56
- }
57
- catch (error) {
58
- if (error instanceof Error &&
59
- "code" in error &&
60
- error.code === "EEXIST") {
61
- if (removeStaleLock(targetLockPath)) {
62
- continue;
63
- }
64
- sleepSync(lockRetryDelayMs);
65
- continue;
66
- }
67
- return undefined;
68
- }
69
- }
70
- return undefined;
71
- }
72
- function removeStaleLock(targetLockPath) {
73
- let stats;
74
- try {
75
- stats = fs.statSync(targetLockPath);
76
- }
77
- catch {
78
- return false;
79
- }
80
- if (Date.now() - stats.mtimeMs < staleLockAgeMs) {
81
- return false;
82
- }
83
- try {
84
- fs.unlinkSync(targetLockPath);
85
- return true;
86
- }
87
- catch {
88
- return false;
89
- }
90
- }
91
- function releaseLock(pluginData, fileDescriptor) {
92
- try {
93
- fs.closeSync(fileDescriptor);
94
- }
95
- catch {
96
- // Ignore close failures; unlink still gives future hook runs a chance.
97
- }
98
- try {
99
- fs.unlinkSync(lockPath(pluginData));
100
- }
101
- catch {
102
- // A missing lock file is safe: bounded retries prevent permanent blocking.
103
- }
104
- }
105
- function coerceHookState(value) {
106
- if (!isRecord(value)) {
107
- return emptyHookState();
108
- }
109
- const dirtyPaths = Array.isArray(value.dirtyPaths)
110
- ? value.dirtyPaths
111
- .filter((dirtyPath) => typeof dirtyPath === "string")
112
- .map(normalizePath)
113
- .filter((dirtyPath) => dirtyPath.length > 0)
114
- : [];
115
- const guidedReadPaths = Array.isArray(value.guidedReadPaths)
116
- ? value.guidedReadPaths
117
- .filter((entry) => typeof entry === "string")
118
- .map(normalizePath)
119
- .filter((entry) => entry.length > 0)
120
- : [];
121
- const guidedWritePaths = Array.isArray(value.guidedWritePaths)
122
- ? value.guidedWritePaths
123
- .filter((entry) => typeof entry === "string")
124
- .map(normalizePath)
125
- .filter((entry) => entry.length > 0)
126
- : [];
127
- const kbMutationTools = Array.isArray(value.kbMutationTools)
128
- ? value.kbMutationTools
129
- .filter((entry) => typeof entry === "string")
130
- .map((entry) => entry.trim())
131
- .filter((entry) => entry.length > 0)
132
- : [];
133
- const kbCheckRun = value.kbCheckRun === true;
134
- const impactCheckRun = value.impactCheckRun === true;
135
- const impactCheckedPaths = Array.isArray(value.impactCheckedPaths)
136
- ? value.impactCheckedPaths
137
- .filter((entry) => typeof entry === "string")
138
- .map(normalizePath)
139
- .filter((entry) => entry.length > 0)
140
- : [];
141
- return {
142
- dirtyPaths: [...new Set(dirtyPaths)].slice(-maxDirtyPaths),
143
- guidedReadPaths: [...new Set(guidedReadPaths)].slice(-maxGuidedPaths),
144
- guidedWritePaths: [...new Set(guidedWritePaths)].slice(-maxGuidedPaths),
145
- kbMutationTools: [...new Set(kbMutationTools)].slice(-maxKbMutationTools),
146
- kbCheckRun,
147
- impactCheckRun,
148
- impactCheckedPaths: [...new Set(impactCheckedPaths)].slice(-maxGuidedPaths),
149
- };
150
- }
151
- export function resolveStateDir(pluginData, conversationId) {
152
- if (pluginData) {
153
- return pluginData;
154
- }
155
- if (!conversationId) {
156
- return undefined;
157
- }
158
- return path.join(os.tmpdir(), "kibi-cursor-hook-state", conversationId.replaceAll(/[^a-zA-Z0-9._-]/g, "_"));
159
- }
160
- export function loadHookState(stateDir) {
161
- if (!stateDir) {
162
- return emptyHookState();
163
- }
164
- try {
165
- return coerceHookState(JSON.parse(fs.readFileSync(statePath(stateDir), "utf8")));
166
- }
167
- catch {
168
- return emptyHookState();
169
- }
170
- }
171
- export function saveHookState(stateDir, state) {
172
- if (!stateDir) {
173
- return;
174
- }
175
- fs.mkdirSync(stateDir, { recursive: true });
176
- const boundedState = coerceHookState(state);
177
- const tempPath = uniqueTempPath(stateDir);
178
- fs.writeFileSync(tempPath, `${JSON.stringify(boundedState)}\n`);
179
- fs.renameSync(tempPath, statePath(stateDir));
180
- }
181
- export function updateHookState(stateDir, updater) {
182
- const initialState = loadHookState(stateDir);
183
- if (!stateDir) {
184
- return updater(initialState);
185
- }
186
- fs.mkdirSync(stateDir, { recursive: true });
187
- const lockFileDescriptor = acquireLock(stateDir);
188
- if (lockFileDescriptor === undefined) {
189
- return updater(initialState);
190
- }
191
- try {
192
- const lockedState = loadHookState(stateDir);
193
- const nextState = updater(lockedState);
194
- saveHookState(stateDir, nextState);
195
- return nextState;
196
- }
197
- catch {
198
- return updater(initialState);
199
- }
200
- finally {
201
- releaseLock(stateDir, lockFileDescriptor);
202
- }
203
- }
1
+ import { emptyHookState, loadHookState, maxDirtyPaths, maxKbMutationTools, mergeStringPaths, normalizePath, updateHookState, } from "./hook-state-storage.js";
2
+ export { loadHookState, resolveStateDir, saveHookState, updateHookState, } from "./hook-state-storage.js";
204
3
  export function addDirtyPaths(stateDir, dirtyPaths) {
205
4
  return updateHookState(stateDir, (state) => ({
206
5
  ...state,
@@ -236,9 +35,10 @@ export function recordKbMcpTool(stateDir, toolName, options = {}) {
236
35
  return loadHookState(stateDir);
237
36
  }
238
37
  return updateHookState(stateDir, (state) => {
38
+ const observedState = { ...state, mcpState: "observed" };
239
39
  if (normalized === "kb_check") {
240
40
  return {
241
- ...state,
41
+ ...observedState,
242
42
  kbCheckRun: true,
243
43
  impactCheckRun: state.impactCheckRun || options.impactCheckRun === true,
244
44
  impactCheckedPaths: options.impactCheckRun === true
@@ -248,13 +48,13 @@ export function recordKbMcpTool(stateDir, toolName, options = {}) {
248
48
  }
249
49
  if (normalized === "kb_upsert" || normalized === "kb_delete") {
250
50
  return {
251
- ...state,
51
+ ...observedState,
252
52
  kbMutationTools: mergeStringPaths(state.kbMutationTools, [
253
53
  normalized,
254
54
  ]).slice(-maxKbMutationTools),
255
55
  };
256
56
  }
257
- return state;
57
+ return observedState;
258
58
  });
259
59
  }
260
60
  export function clearSessionHookState(stateDir) {
@@ -3,5 +3,8 @@ export type KbMcpToolCall = {
3
3
  impactCheckRun: boolean;
4
4
  sourceFiles: string[];
5
5
  };
6
+ export type McpState = "observed" | "unknown";
7
+ export type KibiInterface = "mcp" | "cli" | "setup";
8
+ export declare function resolveKibiInterface(mcpState: McpState, workspaceTrusted: boolean): KibiInterface;
6
9
  export declare function extractKbMcpToolCall(toolName: string | undefined, toolInput: unknown): KbMcpToolCall | undefined;
7
10
  export declare function extractKbMcpToolName(toolName: string | undefined, toolInput: unknown): string | undefined;
@@ -39,6 +39,12 @@ function readRecord(record, keys) {
39
39
  }
40
40
  return undefined;
41
41
  }
42
+ export function resolveKibiInterface(mcpState, workspaceTrusted) {
43
+ if (mcpState === "observed") {
44
+ return "mcp";
45
+ }
46
+ return workspaceTrusted ? "cli" : "setup";
47
+ }
42
48
  export function extractKbMcpToolCall(toolName, toolInput) {
43
49
  const directToolName = toolName?.trim();
44
50
  let normalizedToolName = directToolName?.startsWith("kb_")
@@ -1,6 +1,8 @@
1
1
  import type { HookState } from "./hook-state.js";
2
- export declare const BOOTSTRAP_REMINDER = "Kibi config was not found at the Cursor workspace root. Use the Kibi MCP /init-kibi workflow to bootstrap project memory before relying on KB lookups; do not edit .kb/ files directly.";
3
- export declare const DIRECT_KB_EDIT_WARNING = "Avoid direct edits to .kb/. Use Kibi MCP tools for KB discovery and mutations so project memory stays valid.";
2
+ import { type McpState } from "./kb-mcp-tools.js";
3
+ export declare const BOOTSTRAP_REMINDER = "Kibi config was not found at the Cursor workspace root. Use visible Kibi MCP tools or a trusted project-local CLI JSON route with --input (peer surfaces) to bootstrap project memory; if neither interface is available, tell the operator. Do not read or edit `.kb/` files directly. Query before mutate, run kb_upsert sequentially, and run kb_check before completion.";
4
+ export declare const DIRECT_KB_EDIT_WARNING = "Do not read or edit `.kb/` files directly. Use visible Kibi MCP tools or trusted project-local CLI JSON routes with --input (peer surfaces). Query before mutate, run kb_upsert sequentially, and run kb_check before completion.";
5
+ export declare function interfaceAdvisory(mcpState: McpState, workspaceTrusted: boolean): string | undefined;
4
6
  export declare function stopFollowupMessage(state: HookState): string | undefined;
5
7
  /** @deprecated Use stopFollowupMessage */
6
8
  export declare function freshnessReminder(dirtyPaths: readonly string[]): string;
package/dist/messages.js CHANGED
@@ -1,6 +1,18 @@
1
+ import { resolveKibiInterface } from "./kb-mcp-tools.js";
1
2
  import { isKbFreshnessRelevantPath, isSourceImpactRelevantPath, } from "./path-policy.js";
2
- export const BOOTSTRAP_REMINDER = "Kibi config was not found at the Cursor workspace root. Use the Kibi MCP /init-kibi workflow to bootstrap project memory before relying on KB lookups; do not edit .kb/ files directly.";
3
- export const DIRECT_KB_EDIT_WARNING = "Avoid direct edits to .kb/. Use Kibi MCP tools for KB discovery and mutations so project memory stays valid.";
3
+ export const BOOTSTRAP_REMINDER = "Kibi config was not found at the Cursor workspace root. Use visible Kibi MCP tools or a trusted project-local CLI JSON route with --input (peer surfaces) to bootstrap project memory; if neither interface is available, tell the operator. Do not read or edit `.kb/` files directly. Query before mutate, run kb_upsert sequentially, and run kb_check before completion.";
4
+ export const DIRECT_KB_EDIT_WARNING = "Do not read or edit `.kb/` files directly. Use visible Kibi MCP tools or trusted project-local CLI JSON routes with --input (peer surfaces). Query before mutate, run kb_upsert sequentially, and run kb_check before completion.";
5
+ export function interfaceAdvisory(mcpState, workspaceTrusted) {
6
+ const selectedInterface = resolveKibiInterface(mcpState, workspaceTrusted);
7
+ switch (selectedInterface) {
8
+ case "mcp":
9
+ return undefined;
10
+ case "cli":
11
+ return "Kibi MCP has not been observed in this session. In this explicitly trusted workspace, the project-local CLI is an advisory fallback: use npx --no-install kibi or bunx --no-install kibi. Do not use global or installing runners.";
12
+ case "setup":
13
+ return "Kibi MCP has not been observed in this session and workspace trust is unknown. Do not probe or execute a CLI fallback; ask the operator to enable MCP or explicitly approve the trusted project-local CLI workflow.";
14
+ }
15
+ }
4
16
  export function stopFollowupMessage(state) {
5
17
  if (state.kbMutationTools.length > 0) {
6
18
  const tools = [...new Set(state.kbMutationTools)];
@@ -1,9 +1,9 @@
1
1
  ---
2
- description: Kibi MCP discovery-first workflow and KB mutation discipline
2
+ description: Kibi capability-based discovery-first workflow and KB mutation discipline
3
3
  alwaysApply: true
4
4
  ---
5
5
 
6
- Use Kibi via MCP tools only. Do not manually read or edit `.kb/` files.
6
+ Use Kibi via MCP tools when available, or via CLI JSON routes when MCP is unavailable. If MCP tools are visible, use MCP. If MCP availability is unknown and a trusted project-local CLI is available, use `kibi <route> --input <file|->`. If neither interface is available, stop and tell the operator. Do not infer MCP availability from config file existence. Do not read or edit `.kb/` files directly.
7
7
 
8
8
  Discovery workflow:
9
9
  - Start with `kb_search` for broad discovery.
@@ -13,7 +13,7 @@ Discovery workflow:
13
13
  Mutation workflow:
14
14
  - Query before mutate.
15
15
  - Create relationship endpoints before linking.
16
- - Use `kb_upsert` sequentially, then `kb_check`.
16
+ - Use `kb_upsert` sequentially. Run `kb_check` before completion.
17
17
  - Use `kb_delete` only for intentional removals with dependency awareness.
18
18
 
19
19
  Completion must state KB freshness outcome: updated, no-impact with rationale, or deferred/failed.
@@ -0,0 +1,12 @@
1
+ {
2
+ "init-kibi/SKILL.md": "4e936774cd31cf366e9e17452e9030fdaeabab345bce5ee163656e0fe5dc73d7",
3
+ "kibi-freshness/SKILL.md": "68345a514233503f8f78d53e7d602d8a5fc566e1ec118388c7393716c9f7490d",
4
+ "kibi-traceability/SKILL.md": "a621553397c1f8813f05fd9552fc30a5e8ed118442e019add35804de7d43a0b1",
5
+ "kibi-usage/SKILL.md": "292359ad032496abf3a4a68bf97ca79ac875e7b7a208be4b83c34e4b8860d76a",
6
+ "kibi-usage/resources/fact-lanes.md": "98979dbb35eb0c0f8d7053635fcab2f4a178793a9ce57ca6eab64136de9ef022",
7
+ "kibi-usage/resources/logic-ir.md": "de6b6833d3c9c628405fa0e0d3c1eb3349a23c2e71fee79d05e27658120123f1",
8
+ "kibi-usage/resources/operation-access.md": "a5ec895aa9b2f30140b2b72baf444843d32dd99e92d6e10be4279d84f5ea799b",
9
+ "kibi-usage/resources/relationship-directions.md": "aecbde048bb84e210318e4aca98e07f8370168c640c3a9d65696d2195c8598ec",
10
+ "kibi-usage/resources/ui-requirements.md": "b7c31ab1affa3ab25ae5242d8fd8c4d546532d1cafc65d84088375317c1682cc",
11
+ "kibi-usage/resources/workflows.md": "aa86766b06745c22346c2bd7076930b9dab03f2bd829a373f6377af84075c995"
12
+ }
@@ -1,23 +1,62 @@
1
1
  ---
2
+ id: init-kibi
2
3
  name: init-kibi
3
4
  description: Bootstrap and model knowledge safely using approved Kibi MCP initialization and mutation workflow.
5
+ version: 1.0.0
6
+ kibiCompatibility: "*"
7
+ tags:
8
+ - kibi
9
+ - mcp
10
+ - bootstrap
11
+ - agent-guidance
4
12
  ---
5
-
6
13
  ## Goal
7
14
 
8
- Set up or refresh Kibi knowledge for the current branch without manual filesystem edits.
15
+ Set up or refresh Kibi knowledge for the current branch through approved Kibi interfaces only. Maintain source-file traceability for every repository-backed claim, and require explicit operator approval before any mutation.
16
+
17
+ ## Interface Selection
18
+
19
+ 1. If approved Kibi MCP tools are visible, use the MCP route.
20
+ 2. Otherwise, in a trusted workspace, use the canonical project-local CLI fallback: `npx --no-install kibi ...` or `bunx --no-install kibi ...`.
21
+ 3. If neither approved MCP tools nor the project-local CLI are available, stop and tell the operator to enable or install Kibi for this workspace.
22
+ 4. Never use a global fallback, an installing runner, or any route outside the approved Kibi interface.
23
+
24
+ Use `kibi-usage/resources/operation-access.md` for the exact operation-to-route mapping. The CLI preview route accepts the same JSON object as `kb_autopilot_generate`:
25
+
26
+ ```bash
27
+ echo '{"maxCandidates":25,"minConfidence":0.8}' | npx --no-install kibi autopilot-generate --input -
28
+ ```
9
29
 
10
- ## MCP workflow
30
+ ## Capability Workflow
11
31
 
12
- - Run `kb_autopilot_generate` for suggested initial facts and claims.
13
- - Show the generated preview and get explicit approval before writes.
14
- - Use `kb_upsert` for reviewed entity creation or updates.
15
- - If cleanup is needed, use `kb_delete` only after checking dependencies.
16
- - Finish with `kb_check` to validate the branch snapshot.
32
+ - Generate a read-only bootstrap preview with `kb_autopilot_generate`, or the dedicated CLI route `autopilot-generate --input`.
33
+ - Show the generated preview and obtain explicit approval before writes.
34
+ - Apply reviewed entity creation or updates with `kb_upsert`, or `upsert --input`.
35
+ - If cleanup is needed, use `kb_delete`, or `delete --input`, only after checking dependencies and obtaining approval for the specific removal.
36
+ - Finish with `kb_check`, or `check --input`, to validate the branch snapshot.
17
37
 
18
38
  ## Guidance
19
39
 
20
- - Keep mutation requests explicit and small.
21
- - Use source-linked entities where possible so future `kb_query` lookups can verify context.
22
- - Do not edit `.kb/**` files directly; all changes go through MCP calls.
40
+ - Keep mutation requests explicit, small, and sequential.
41
+ - Prefer source-linked entities so future `kb_query` lookups can verify repository context against source files.
42
+ - Preserve traceability by linking facts, claims, scenarios, and tests to concrete project files whenever repository evidence supports it.
43
+ - Do not read or edit files inside `.kb` directly; all changes go through the selected Kibi interface.
23
44
  - Ask at most four bounded context questions, and only when repository evidence is insufficient.
45
+ - Stop at the documented operator repair boundary if the setup appears partial, inconsistent, or unsupported by the approved interface.
46
+
47
+ ## Public Training Trajectories
48
+
49
+ - `init-kibi-bootstrap-analysis-train-1`: Analyze the cold-start repository and produce a read-only bootstrap preview using only the public Kibi MCP surface.
50
+ - `init-kibi-bootstrap-analysis-train-2`: Analyze the cold-start repository and produce a read-only bootstrap preview using only the public Kibi MCP surface.
51
+ - `init-kibi-bounded-context-questions-train-1`: Ask only the bounded context questions needed before bootstrap synthesis using only the public Kibi MCP surface.
52
+ - `init-kibi-bounded-context-questions-train-2`: Ask only the bounded context questions needed before bootstrap synthesis using only the public Kibi MCP surface.
53
+ - `init-kibi-approval-sequential-writes-train-1`: Apply the approved bootstrap plan sequentially and finish with validation using only the public Kibi MCP surface.
54
+ - `init-kibi-approval-sequential-writes-train-2`: Apply the approved bootstrap plan sequentially and finish with validation using only the public Kibi MCP surface.
55
+ - `init-kibi-repair-escalation-train-1`: Identify the partial setup and stop at the documented operator repair boundary using only the public Kibi MCP surface.
56
+ - `init-kibi-repair-escalation-train-2`: Identify the partial setup and stop at the documented operator repair boundary using only the public Kibi MCP surface.
57
+
58
+ ## Training Data
59
+
60
+ ```json
61
+ [{"taskId":"init-kibi-bootstrap-analysis-train-1","family":"bootstrap-analysis","reflection":"Analyze the cold-start repository and produce a read-only bootstrap preview. This is train case 1; use only the public Kibi MCP surface."},{"taskId":"init-kibi-bootstrap-analysis-train-2","family":"bootstrap-analysis","reflection":"Analyze the cold-start repository and produce a read-only bootstrap preview. This is train case 2; use only the public Kibi MCP surface."},{"taskId":"init-kibi-bounded-context-questions-train-1","family":"bounded-context-questions","reflection":"Ask only the bounded context questions needed before bootstrap synthesis. This is train case 1; use only the public Kibi MCP surface."},{"taskId":"init-kibi-bounded-context-questions-train-2","family":"bounded-context-questions","reflection":"Ask only the bounded context questions needed before bootstrap synthesis. This is train case 2; use only the public Kibi MCP surface."},{"taskId":"init-kibi-approval-sequential-writes-train-1","family":"approval-sequential-writes","reflection":"Apply the approved bootstrap plan sequentially and finish with validation. This is train case 1; use only the public Kibi MCP surface."},{"taskId":"init-kibi-approval-sequential-writes-train-2","family":"approval-sequential-writes","reflection":"Apply the approved bootstrap plan sequentially and finish with validation. This is train case 2; use only the public Kibi MCP surface."},{"taskId":"init-kibi-repair-escalation-train-1","family":"repair-escalation","reflection":"Identify the partial setup and stop at the documented operator repair boundary. This is train case 1; use only the public Kibi MCP surface."},{"taskId":"init-kibi-repair-escalation-train-2","family":"repair-escalation","reflection":"Identify the partial setup and stop at the documented operator repair boundary. This is train case 2; use only the public Kibi MCP surface."}]
62
+ ```