kibi-cursor 0.2.2 → 0.5.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/.cursor-plugin/plugin.json +1 -1
- package/CHANGELOG.md +71 -0
- package/README.md +7 -5
- package/dist/.cursor-plugin/plugin.json +1 -1
- package/dist/guidance.d.ts +10 -2
- package/dist/guidance.js +27 -17
- package/dist/hook-runner.d.ts +1 -0
- package/dist/hook-runner.js +41 -11
- package/dist/hook-state-storage.d.ts +11 -0
- package/dist/hook-state-storage.js +187 -0
- package/dist/hook-state.d.ts +9 -5
- package/dist/hook-state.js +14 -196
- package/dist/kb-mcp-tools.d.ts +9 -0
- package/dist/kb-mcp-tools.js +94 -0
- package/dist/messages.d.ts +4 -2
- package/dist/messages.js +33 -3
- package/dist/path-policy.d.ts +1 -0
- package/dist/path-policy.js +18 -0
- package/dist/rules/kibi-traceability.mdc +2 -0
- package/dist/rules/kibi-workflow.mdc +3 -3
- package/dist/skills/.canon-hash.json +10 -0
- package/dist/skills/init-kibi/SKILL.md +50 -11
- package/dist/skills/kibi-freshness/SKILL.md +32 -10
- package/dist/skills/kibi-traceability/SKILL.md +41 -9
- package/dist/skills/kibi-usage/SKILL.md +123 -95
- package/dist/skills/kibi-usage/resources/fact-lanes.md +225 -0
- package/dist/skills/kibi-usage/resources/operation-access.md +44 -0
- package/dist/skills/kibi-usage/resources/relationship-directions.md +119 -0
- package/dist/skills/kibi-usage/resources/workflows.md +65 -0
- package/package.json +2 -2
- package/rules/kibi-traceability.mdc +2 -0
- package/rules/kibi-workflow.mdc +3 -3
- package/skills/.canon-hash.json +10 -0
- package/skills/init-kibi/SKILL.md +50 -11
- package/skills/kibi-freshness/SKILL.md +32 -10
- package/skills/kibi-traceability/SKILL.md +41 -9
- package/skills/kibi-usage/SKILL.md +123 -95
- package/skills/kibi-usage/resources/fact-lanes.md +225 -0
- package/skills/kibi-usage/resources/operation-access.md +44 -0
- package/skills/kibi-usage/resources/relationship-directions.md +119 -0
- package/skills/kibi-usage/resources/workflows.md +65 -0
package/dist/hook-state.js
CHANGED
|
@@ -1,195 +1,5 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
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
|
-
};
|
|
48
|
-
}
|
|
49
|
-
function acquireLock(pluginData) {
|
|
50
|
-
const targetLockPath = lockPath(pluginData);
|
|
51
|
-
for (let attempt = 0; attempt < lockRetryCount; attempt++) {
|
|
52
|
-
try {
|
|
53
|
-
return fs.openSync(targetLockPath, "wx");
|
|
54
|
-
}
|
|
55
|
-
catch (error) {
|
|
56
|
-
if (error instanceof Error &&
|
|
57
|
-
"code" in error &&
|
|
58
|
-
error.code === "EEXIST") {
|
|
59
|
-
if (removeStaleLock(targetLockPath)) {
|
|
60
|
-
continue;
|
|
61
|
-
}
|
|
62
|
-
sleepSync(lockRetryDelayMs);
|
|
63
|
-
continue;
|
|
64
|
-
}
|
|
65
|
-
return undefined;
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
return undefined;
|
|
69
|
-
}
|
|
70
|
-
function removeStaleLock(targetLockPath) {
|
|
71
|
-
let stats;
|
|
72
|
-
try {
|
|
73
|
-
stats = fs.statSync(targetLockPath);
|
|
74
|
-
}
|
|
75
|
-
catch {
|
|
76
|
-
return false;
|
|
77
|
-
}
|
|
78
|
-
if (Date.now() - stats.mtimeMs < staleLockAgeMs) {
|
|
79
|
-
return false;
|
|
80
|
-
}
|
|
81
|
-
try {
|
|
82
|
-
fs.unlinkSync(targetLockPath);
|
|
83
|
-
return true;
|
|
84
|
-
}
|
|
85
|
-
catch {
|
|
86
|
-
return false;
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
function releaseLock(pluginData, fileDescriptor) {
|
|
90
|
-
try {
|
|
91
|
-
fs.closeSync(fileDescriptor);
|
|
92
|
-
}
|
|
93
|
-
catch {
|
|
94
|
-
// Ignore close failures; unlink still gives future hook runs a chance.
|
|
95
|
-
}
|
|
96
|
-
try {
|
|
97
|
-
fs.unlinkSync(lockPath(pluginData));
|
|
98
|
-
}
|
|
99
|
-
catch {
|
|
100
|
-
// A missing lock file is safe: bounded retries prevent permanent blocking.
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
function coerceHookState(value) {
|
|
104
|
-
if (!isRecord(value)) {
|
|
105
|
-
return emptyHookState();
|
|
106
|
-
}
|
|
107
|
-
const dirtyPaths = Array.isArray(value.dirtyPaths)
|
|
108
|
-
? value.dirtyPaths
|
|
109
|
-
.filter((dirtyPath) => typeof dirtyPath === "string")
|
|
110
|
-
.map(normalizePath)
|
|
111
|
-
.filter((dirtyPath) => dirtyPath.length > 0)
|
|
112
|
-
: [];
|
|
113
|
-
const guidedReadPaths = Array.isArray(value.guidedReadPaths)
|
|
114
|
-
? value.guidedReadPaths
|
|
115
|
-
.filter((entry) => typeof entry === "string")
|
|
116
|
-
.map(normalizePath)
|
|
117
|
-
.filter((entry) => entry.length > 0)
|
|
118
|
-
: [];
|
|
119
|
-
const guidedWritePaths = Array.isArray(value.guidedWritePaths)
|
|
120
|
-
? value.guidedWritePaths
|
|
121
|
-
.filter((entry) => typeof entry === "string")
|
|
122
|
-
.map(normalizePath)
|
|
123
|
-
.filter((entry) => entry.length > 0)
|
|
124
|
-
: [];
|
|
125
|
-
const kbMutationTools = Array.isArray(value.kbMutationTools)
|
|
126
|
-
? value.kbMutationTools
|
|
127
|
-
.filter((entry) => typeof entry === "string")
|
|
128
|
-
.map((entry) => entry.trim())
|
|
129
|
-
.filter((entry) => entry.length > 0)
|
|
130
|
-
: [];
|
|
131
|
-
const kbCheckRun = value.kbCheckRun === true;
|
|
132
|
-
return {
|
|
133
|
-
dirtyPaths: [...new Set(dirtyPaths)].slice(-maxDirtyPaths),
|
|
134
|
-
guidedReadPaths: [...new Set(guidedReadPaths)].slice(-maxGuidedPaths),
|
|
135
|
-
guidedWritePaths: [...new Set(guidedWritePaths)].slice(-maxGuidedPaths),
|
|
136
|
-
kbMutationTools: [...new Set(kbMutationTools)].slice(-maxKbMutationTools),
|
|
137
|
-
kbCheckRun,
|
|
138
|
-
};
|
|
139
|
-
}
|
|
140
|
-
export function resolveStateDir(pluginData, conversationId) {
|
|
141
|
-
if (pluginData) {
|
|
142
|
-
return pluginData;
|
|
143
|
-
}
|
|
144
|
-
if (!conversationId) {
|
|
145
|
-
return undefined;
|
|
146
|
-
}
|
|
147
|
-
return path.join(os.tmpdir(), "kibi-cursor-hook-state", conversationId.replaceAll(/[^a-zA-Z0-9._-]/g, "_"));
|
|
148
|
-
}
|
|
149
|
-
export function loadHookState(stateDir) {
|
|
150
|
-
if (!stateDir) {
|
|
151
|
-
return emptyHookState();
|
|
152
|
-
}
|
|
153
|
-
try {
|
|
154
|
-
return coerceHookState(JSON.parse(fs.readFileSync(statePath(stateDir), "utf8")));
|
|
155
|
-
}
|
|
156
|
-
catch {
|
|
157
|
-
return emptyHookState();
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
export function saveHookState(stateDir, state) {
|
|
161
|
-
if (!stateDir) {
|
|
162
|
-
return;
|
|
163
|
-
}
|
|
164
|
-
fs.mkdirSync(stateDir, { recursive: true });
|
|
165
|
-
const boundedState = coerceHookState(state);
|
|
166
|
-
const tempPath = uniqueTempPath(stateDir);
|
|
167
|
-
fs.writeFileSync(tempPath, `${JSON.stringify(boundedState)}\n`);
|
|
168
|
-
fs.renameSync(tempPath, statePath(stateDir));
|
|
169
|
-
}
|
|
170
|
-
export function updateHookState(stateDir, updater) {
|
|
171
|
-
const initialState = loadHookState(stateDir);
|
|
172
|
-
if (!stateDir) {
|
|
173
|
-
return updater(initialState);
|
|
174
|
-
}
|
|
175
|
-
fs.mkdirSync(stateDir, { recursive: true });
|
|
176
|
-
const lockFileDescriptor = acquireLock(stateDir);
|
|
177
|
-
if (lockFileDescriptor === undefined) {
|
|
178
|
-
return updater(initialState);
|
|
179
|
-
}
|
|
180
|
-
try {
|
|
181
|
-
const lockedState = loadHookState(stateDir);
|
|
182
|
-
const nextState = updater(lockedState);
|
|
183
|
-
saveHookState(stateDir, nextState);
|
|
184
|
-
return nextState;
|
|
185
|
-
}
|
|
186
|
-
catch {
|
|
187
|
-
return updater(initialState);
|
|
188
|
-
}
|
|
189
|
-
finally {
|
|
190
|
-
releaseLock(stateDir, lockFileDescriptor);
|
|
191
|
-
}
|
|
192
|
-
}
|
|
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";
|
|
193
3
|
export function addDirtyPaths(stateDir, dirtyPaths) {
|
|
194
4
|
return updateHookState(stateDir, (state) => ({
|
|
195
5
|
...state,
|
|
@@ -219,24 +29,32 @@ export function hasGuidedPath(state, kind, guidedPath) {
|
|
|
219
29
|
const bucket = kind === "read" ? state.guidedReadPaths : state.guidedWritePaths;
|
|
220
30
|
return bucket.includes(normalized);
|
|
221
31
|
}
|
|
222
|
-
export function recordKbMcpTool(stateDir, toolName) {
|
|
32
|
+
export function recordKbMcpTool(stateDir, toolName, options = {}) {
|
|
223
33
|
const normalized = toolName.trim();
|
|
224
34
|
if (normalized.length === 0) {
|
|
225
35
|
return loadHookState(stateDir);
|
|
226
36
|
}
|
|
227
37
|
return updateHookState(stateDir, (state) => {
|
|
38
|
+
const observedState = { ...state, mcpState: "observed" };
|
|
228
39
|
if (normalized === "kb_check") {
|
|
229
|
-
return {
|
|
40
|
+
return {
|
|
41
|
+
...observedState,
|
|
42
|
+
kbCheckRun: true,
|
|
43
|
+
impactCheckRun: state.impactCheckRun || options.impactCheckRun === true,
|
|
44
|
+
impactCheckedPaths: options.impactCheckRun === true
|
|
45
|
+
? mergeStringPaths(state.impactCheckedPaths, options.sourceFiles ?? [])
|
|
46
|
+
: state.impactCheckedPaths,
|
|
47
|
+
};
|
|
230
48
|
}
|
|
231
49
|
if (normalized === "kb_upsert" || normalized === "kb_delete") {
|
|
232
50
|
return {
|
|
233
|
-
...
|
|
51
|
+
...observedState,
|
|
234
52
|
kbMutationTools: mergeStringPaths(state.kbMutationTools, [
|
|
235
53
|
normalized,
|
|
236
54
|
]).slice(-maxKbMutationTools),
|
|
237
55
|
};
|
|
238
56
|
}
|
|
239
|
-
return
|
|
57
|
+
return observedState;
|
|
240
58
|
});
|
|
241
59
|
}
|
|
242
60
|
export function clearSessionHookState(stateDir) {
|
package/dist/kb-mcp-tools.d.ts
CHANGED
|
@@ -1 +1,10 @@
|
|
|
1
|
+
export type KbMcpToolCall = {
|
|
2
|
+
toolName: string;
|
|
3
|
+
impactCheckRun: boolean;
|
|
4
|
+
sourceFiles: string[];
|
|
5
|
+
};
|
|
6
|
+
export type McpState = "observed" | "unknown";
|
|
7
|
+
export type KibiInterface = "mcp" | "cli" | "setup";
|
|
8
|
+
export declare function resolveKibiInterface(mcpState: McpState, workspaceTrusted: boolean): KibiInterface;
|
|
9
|
+
export declare function extractKbMcpToolCall(toolName: string | undefined, toolInput: unknown): KbMcpToolCall | undefined;
|
|
1
10
|
export declare function extractKbMcpToolName(toolName: string | undefined, toolInput: unknown): string | undefined;
|
package/dist/kb-mcp-tools.js
CHANGED
|
@@ -11,7 +11,101 @@ function readString(record, keys) {
|
|
|
11
11
|
}
|
|
12
12
|
return undefined;
|
|
13
13
|
}
|
|
14
|
+
function readBoolean(record, keys) {
|
|
15
|
+
for (const key of keys) {
|
|
16
|
+
const value = record[key];
|
|
17
|
+
if (typeof value === "boolean") {
|
|
18
|
+
return value;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
return undefined;
|
|
22
|
+
}
|
|
23
|
+
function readStringArray(record, keys) {
|
|
24
|
+
for (const key of keys) {
|
|
25
|
+
const value = record[key];
|
|
26
|
+
if (!Array.isArray(value)) {
|
|
27
|
+
continue;
|
|
28
|
+
}
|
|
29
|
+
return value.filter((item) => typeof item === "string" && item.length > 0);
|
|
30
|
+
}
|
|
31
|
+
return [];
|
|
32
|
+
}
|
|
33
|
+
function readRecord(record, keys) {
|
|
34
|
+
for (const key of keys) {
|
|
35
|
+
const value = record[key];
|
|
36
|
+
if (isRecord(value)) {
|
|
37
|
+
return value;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
return undefined;
|
|
41
|
+
}
|
|
42
|
+
export function resolveKibiInterface(mcpState, workspaceTrusted) {
|
|
43
|
+
if (mcpState === "observed") {
|
|
44
|
+
return "mcp";
|
|
45
|
+
}
|
|
46
|
+
return workspaceTrusted ? "cli" : "setup";
|
|
47
|
+
}
|
|
48
|
+
export function extractKbMcpToolCall(toolName, toolInput) {
|
|
49
|
+
const directToolName = toolName?.trim();
|
|
50
|
+
let normalizedToolName = directToolName?.startsWith("kb_")
|
|
51
|
+
? directToolName
|
|
52
|
+
: undefined;
|
|
53
|
+
if (isRecord(toolInput)) {
|
|
54
|
+
normalizedToolName ??= readString(toolInput, [
|
|
55
|
+
"toolName",
|
|
56
|
+
"tool_name",
|
|
57
|
+
"name",
|
|
58
|
+
]);
|
|
59
|
+
const args = readRecord(toolInput, ["arguments", "args"]);
|
|
60
|
+
const payload = args ?? toolInput;
|
|
61
|
+
const includeImpactDiagnostics = readBoolean(payload, [
|
|
62
|
+
"includeImpactDiagnostics",
|
|
63
|
+
"include_impact_diagnostics",
|
|
64
|
+
]);
|
|
65
|
+
const includeWorkingTreeDiff = readBoolean(payload, [
|
|
66
|
+
"includeWorkingTreeDiff",
|
|
67
|
+
"include_working_tree_diff",
|
|
68
|
+
]);
|
|
69
|
+
const sourceFiles = readStringArray(payload, [
|
|
70
|
+
"sourceFiles",
|
|
71
|
+
"source_files",
|
|
72
|
+
]);
|
|
73
|
+
if (normalizedToolName?.startsWith("kb_")) {
|
|
74
|
+
return {
|
|
75
|
+
toolName: normalizedToolName,
|
|
76
|
+
impactCheckRun: normalizedToolName === "kb_check" &&
|
|
77
|
+
includeImpactDiagnostics === true &&
|
|
78
|
+
includeWorkingTreeDiff === true &&
|
|
79
|
+
sourceFiles.length > 0,
|
|
80
|
+
sourceFiles,
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
const nestedArgs = toolInput.arguments ?? toolInput.args;
|
|
84
|
+
if (isRecord(nestedArgs)) {
|
|
85
|
+
const nestedTool = readString(nestedArgs, [
|
|
86
|
+
"toolName",
|
|
87
|
+
"tool_name",
|
|
88
|
+
"name",
|
|
89
|
+
]);
|
|
90
|
+
if (nestedTool?.startsWith("kb_")) {
|
|
91
|
+
return { toolName: nestedTool, impactCheckRun: false, sourceFiles: [] };
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
if (normalizedToolName?.startsWith("kb_")) {
|
|
96
|
+
return {
|
|
97
|
+
toolName: normalizedToolName,
|
|
98
|
+
impactCheckRun: false,
|
|
99
|
+
sourceFiles: [],
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
return undefined;
|
|
103
|
+
}
|
|
14
104
|
export function extractKbMcpToolName(toolName, toolInput) {
|
|
105
|
+
const toolCall = extractKbMcpToolCall(toolName, toolInput);
|
|
106
|
+
if (toolCall) {
|
|
107
|
+
return toolCall.toolName;
|
|
108
|
+
}
|
|
15
109
|
if (toolName) {
|
|
16
110
|
const normalized = toolName.trim();
|
|
17
111
|
if (normalized.startsWith("kb_")) {
|
package/dist/messages.d.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import type { HookState } from "./hook-state.js";
|
|
2
|
-
|
|
3
|
-
export declare const
|
|
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 when MCP is unavailable, 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 when MCP is unavailable. 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,11 +1,31 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
export const
|
|
1
|
+
import { resolveKibiInterface } from "./kb-mcp-tools.js";
|
|
2
|
+
import { isKbFreshnessRelevantPath, isSourceImpactRelevantPath, } from "./path-policy.js";
|
|
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 when MCP is unavailable, 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 when MCP is unavailable. 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)];
|
|
7
19
|
return `Kibi KB updated (${tools.join(", ")}).`;
|
|
8
20
|
}
|
|
21
|
+
const sourceImpactPaths = state.dirtyPaths.filter(isSourceImpactRelevantPath);
|
|
22
|
+
const uncheckedSourcePaths = sourceImpactPaths.filter((sourcePath) => !state.impactCheckedPaths.includes(sourcePath));
|
|
23
|
+
if (uncheckedSourcePaths.length > 0 && !state.impactCheckRun) {
|
|
24
|
+
return impactCheckFollowup(uncheckedSourcePaths);
|
|
25
|
+
}
|
|
26
|
+
if (uncheckedSourcePaths.length > 0) {
|
|
27
|
+
return impactCheckFollowup(uncheckedSourcePaths);
|
|
28
|
+
}
|
|
9
29
|
const freshnessPaths = state.dirtyPaths.filter(isKbFreshnessRelevantPath);
|
|
10
30
|
if (freshnessPaths.length === 0 || state.kbCheckRun) {
|
|
11
31
|
return undefined;
|
|
@@ -14,6 +34,16 @@ export function stopFollowupMessage(state) {
|
|
|
14
34
|
const noun = fileCount === 1 ? "file" : "files";
|
|
15
35
|
return `Kibi: sync or record no-impact after ${fileCount} edited ${noun}.`;
|
|
16
36
|
}
|
|
37
|
+
function impactCheckFollowup(sourcePaths) {
|
|
38
|
+
const fileCount = sourcePaths.length;
|
|
39
|
+
const noun = fileCount === 1 ? "file" : "files";
|
|
40
|
+
const sourceFiles = JSON.stringify(sourcePaths.slice(0, 10));
|
|
41
|
+
return [
|
|
42
|
+
`Kibi: run impact-enabled kb_check after ${fileCount} edited source ${noun}.`,
|
|
43
|
+
`Use kb_check({sourceFiles:${sourceFiles}, includeImpactDiagnostics:true, includeWorkingTreeDiff:true}).`,
|
|
44
|
+
"Review symbol granularity and semantic review of linked requirements/tests before stopping.",
|
|
45
|
+
].join("\n");
|
|
46
|
+
}
|
|
17
47
|
/** @deprecated Use stopFollowupMessage */
|
|
18
48
|
export function freshnessReminder(dirtyPaths) {
|
|
19
49
|
const fileCount = dirtyPaths.length;
|
package/dist/path-policy.d.ts
CHANGED
|
@@ -3,5 +3,6 @@ export declare function isDirectKbPath(candidate: string): boolean;
|
|
|
3
3
|
export declare function isMeaningfulTrackedPath(candidate: string): boolean;
|
|
4
4
|
/** Paths whose edits should trigger a KB freshness stop follow-up. */
|
|
5
5
|
export declare function isKbFreshnessRelevantPath(candidate: string): boolean;
|
|
6
|
+
export declare function isSourceImpactRelevantPath(candidate: string): boolean;
|
|
6
7
|
export declare function isDocumentationTrackedPath(candidate: string): boolean;
|
|
7
8
|
export declare function toRepoRelativePath(candidate: string, cwd: string | undefined): string;
|
package/dist/path-policy.js
CHANGED
|
@@ -29,6 +29,7 @@ const sourceExtensions = new Set([
|
|
|
29
29
|
".mjs",
|
|
30
30
|
".mts",
|
|
31
31
|
".php",
|
|
32
|
+
".pl",
|
|
32
33
|
".py",
|
|
33
34
|
".rb",
|
|
34
35
|
".rs",
|
|
@@ -122,6 +123,23 @@ export function isKbFreshnessRelevantPath(candidate) {
|
|
|
122
123
|
segments[1] === "core" &&
|
|
123
124
|
segments[2] === "src");
|
|
124
125
|
}
|
|
126
|
+
export function isSourceImpactRelevantPath(candidate) {
|
|
127
|
+
const normalized = normalizePath(candidate);
|
|
128
|
+
const segments = pathSegments(normalized);
|
|
129
|
+
if (segments.includes(".kb") ||
|
|
130
|
+
segments.includes("dist") ||
|
|
131
|
+
segments.includes("tests") ||
|
|
132
|
+
segments.includes("test") ||
|
|
133
|
+
segments.includes("docs") ||
|
|
134
|
+
segments.includes("documentation")) {
|
|
135
|
+
return false;
|
|
136
|
+
}
|
|
137
|
+
const basename = segments.at(-1) ?? "";
|
|
138
|
+
const extension = basename.includes(".")
|
|
139
|
+
? `.${basename.split(".").at(-1) ?? ""}`
|
|
140
|
+
: "";
|
|
141
|
+
return segments.includes("src") && sourceExtensions.has(extension);
|
|
142
|
+
}
|
|
125
143
|
export function isDocumentationTrackedPath(candidate) {
|
|
126
144
|
const normalized = normalizePath(candidate);
|
|
127
145
|
const segments = pathSegments(normalized);
|
|
@@ -7,6 +7,8 @@ alwaysApply: false
|
|
|
7
7
|
|
|
8
8
|
Before changing production code, discover linked requirements with `kb_search` and `kb_query(sourceFile=...)`.
|
|
9
9
|
|
|
10
|
+
After meaningful source edits, run `kb_check({sourceFiles:[...], includeImpactDiagnostics:true, includeWorkingTreeDiff:true})` while the edit context is fresh. Review symbol granularity and whether linked requirements, scenarios, and tests still cover the changed behavior or UI copy.
|
|
11
|
+
|
|
10
12
|
Preserve the canonical chain `REQ-* -> SCEN-* -> TEST-*` when modeling behavior.
|
|
11
13
|
|
|
12
14
|
Prefer symbol manifest + `executable_for` for test/e2e code. Inline `// implements REQ-xxx` remains acceptable for quick code-only changes.
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
---
|
|
2
|
-
description: Kibi
|
|
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
|
|
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
|
|
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,10 @@
|
|
|
1
|
+
{
|
|
2
|
+
"init-kibi/SKILL.md": "4e936774cd31cf366e9e17452e9030fdaeabab345bce5ee163656e0fe5dc73d7",
|
|
3
|
+
"kibi-freshness/SKILL.md": "68345a514233503f8f78d53e7d602d8a5fc566e1ec118388c7393716c9f7490d",
|
|
4
|
+
"kibi-traceability/SKILL.md": "a621553397c1f8813f05fd9552fc30a5e8ed118442e019add35804de7d43a0b1",
|
|
5
|
+
"kibi-usage/SKILL.md": "f4a713d8c95d9c9a254e2f23b27013293f0caacb4009409409e56c7deeccc8a5",
|
|
6
|
+
"kibi-usage/resources/fact-lanes.md": "8b2f835775e57427db980c5412d6e58840a53e71e8e508ae39f14ebfa693e4b8",
|
|
7
|
+
"kibi-usage/resources/operation-access.md": "d8653b544305d4c00734c737b1156110208a9a82fbec6b04726d25b973f2b840",
|
|
8
|
+
"kibi-usage/resources/relationship-directions.md": "aecbde048bb84e210318e4aca98e07f8370168c640c3a9d65696d2195c8598ec",
|
|
9
|
+
"kibi-usage/resources/workflows.md": "36e50fe83fc7ac6bc68fcf51d2c2175e09c68815b6bbebed8cfa1d5611617913"
|
|
10
|
+
}
|
|
@@ -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
|
|
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
|
-
##
|
|
30
|
+
## Capability Workflow
|
|
11
31
|
|
|
12
|
-
-
|
|
13
|
-
- Show the generated preview and
|
|
14
|
-
-
|
|
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
|
|
21
|
-
-
|
|
22
|
-
-
|
|
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
|
+
```
|
|
@@ -1,22 +1,44 @@
|
|
|
1
1
|
---
|
|
2
|
+
id: kibi-freshness
|
|
2
3
|
name: kibi-freshness
|
|
3
4
|
description: Check branch freshness and source linkage quality before decisions or risky writes.
|
|
5
|
+
version: 1.0.0
|
|
6
|
+
kibiCompatibility: "*"
|
|
7
|
+
tags:
|
|
8
|
+
- kibi
|
|
9
|
+
- mcp
|
|
10
|
+
- freshness
|
|
11
|
+
- agent-guidance
|
|
4
12
|
---
|
|
5
|
-
|
|
6
13
|
## Goal
|
|
7
14
|
|
|
8
|
-
Help agents decide whether
|
|
15
|
+
Help agents decide whether Kibi knowledge is current enough to support safe changes while preserving source-file traceability and approved interface boundaries.
|
|
16
|
+
|
|
17
|
+
## Interface Selection
|
|
18
|
+
|
|
19
|
+
1. If Kibi MCP tools are visible and approved for the workspace, use the MCP surface only.
|
|
20
|
+
2. Otherwise, in a trusted workspace, use the canonical project-local CLI fallback through `npx --no-install kibi ...` or `bunx --no-install kibi ...`.
|
|
21
|
+
3. If the project-local CLI is unavailable or too old, stop and tell the operator to enable or install Kibi.
|
|
22
|
+
4. Never use a global fallback or an installing runner.
|
|
23
|
+
|
|
24
|
+
Use `kibi-usage/resources/operation-access.md` for exact dedicated routes. A project-local CLI freshness check is executable as:
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
echo '{}' | npx --no-install kibi status --input -
|
|
28
|
+
```
|
|
9
29
|
|
|
10
|
-
##
|
|
30
|
+
## Capability Workflow
|
|
11
31
|
|
|
12
|
-
- Use `kb_status` first to confirm branch attachment and
|
|
13
|
-
- Use `kb_query` with `sourceFile
|
|
14
|
-
- Re-run `kb_search` after sync gaps are detected.
|
|
15
|
-
- If knowledge must change,
|
|
32
|
+
- Use `kb_status`, or `status --input`, first to confirm branch attachment, freshness state, dirty-worktree evidence, and whether the knowledge snapshot is safe to rely on.
|
|
33
|
+
- Use `kb_query`, or `query --input`, with `sourceFile` and text references for source-linked discovery.
|
|
34
|
+
- Re-run `kb_search`, or `search --input`, after sync gaps or stale context are detected.
|
|
35
|
+
- If knowledge must change, use the selected interface's `kb_upsert`/`upsert` or `kb_delete`/`delete` operation sequentially, then finish with `kb_check`/`check`.
|
|
16
36
|
|
|
17
37
|
## Guidance
|
|
18
38
|
|
|
39
|
+
- Prefer the public Kibi MCP surface whenever it is available and approved.
|
|
19
40
|
- Never proceed with broad changes when freshness is stale and unresolved.
|
|
20
|
-
- If entities look stale,
|
|
21
|
-
-
|
|
22
|
-
-
|
|
41
|
+
- If entities look stale, refresh context through an approved Kibi capability and then re-query.
|
|
42
|
+
- Preserve source-file traceability by carrying `sourceFile`, text references, and validation evidence through discovery, mutation, and completion.
|
|
43
|
+
- Do not read or edit files inside `.kb` directly; use the selected Kibi interface instead.
|
|
44
|
+
- Completion must state one freshness outcome: KB updated, no KB impact with rationale, or deferred/failed.
|