kibi-opencode 0.5.4 → 0.6.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.
- package/README.md +27 -14
- package/dist/config.d.ts +9 -0
- package/dist/config.js +31 -0
- package/dist/guidance-cache.d.ts +75 -0
- package/dist/guidance-cache.js +145 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +428 -56
- package/dist/logger.d.ts +4 -3
- package/dist/logger.js +14 -18
- package/dist/path-kind.d.ts +2 -2
- package/dist/path-kind.js +12 -4
- package/dist/prompt.d.ts +22 -1
- package/dist/prompt.js +297 -121
- package/dist/repo-posture.d.ts +12 -0
- package/dist/repo-posture.js +196 -0
- package/dist/risk-classifier.d.ts +39 -0
- package/dist/risk-classifier.js +111 -0
- package/dist/smart-enforcement.d.ts +41 -0
- package/dist/smart-enforcement.js +48 -0
- package/dist/source-linked-guidance.d.ts +10 -0
- package/dist/source-linked-guidance.js +164 -0
- package/dist/workspace-health.d.ts +2 -0
- package/dist/workspace-health.js +14 -6
- package/package.json +1 -1
package/dist/path-kind.js
CHANGED
|
@@ -14,7 +14,7 @@ const KIBI_DOC_PATTERNS = [
|
|
|
14
14
|
];
|
|
15
15
|
export function analyzePath(
|
|
16
16
|
// implements REQ-opencode-kibi-plugin-v1
|
|
17
|
-
filePath, cwd) {
|
|
17
|
+
filePath, cwd = process.cwd()) {
|
|
18
18
|
const rel = path.isAbsolute(filePath)
|
|
19
19
|
? path.relative(cwd, filePath).split(path.sep).join("/")
|
|
20
20
|
: filePath.split(path.sep).join("/");
|
|
@@ -46,15 +46,23 @@ filePath, cwd) {
|
|
|
46
46
|
else if (patternPrefix.includes("facts"))
|
|
47
47
|
kind = "fact";
|
|
48
48
|
else if (patternPrefix.includes("events"))
|
|
49
|
-
kind = "
|
|
49
|
+
kind = "event";
|
|
50
50
|
else if (patternPrefix.includes("flags"))
|
|
51
|
-
kind = "
|
|
51
|
+
kind = "flag";
|
|
52
52
|
else if (patternPrefix.includes("symbols"))
|
|
53
|
-
kind = "
|
|
53
|
+
kind = "symbol";
|
|
54
54
|
}
|
|
55
55
|
break;
|
|
56
56
|
}
|
|
57
57
|
}
|
|
58
|
+
if (kind === "unknown") {
|
|
59
|
+
const isTestPath = normalized.includes("/__tests__/") ||
|
|
60
|
+
normalized.startsWith("tests/") ||
|
|
61
|
+
/(?:^|\/)[^/]+\.(test|spec)\.[^.]+$/i.test(normalized);
|
|
62
|
+
if (isTestPath) {
|
|
63
|
+
kind = "test";
|
|
64
|
+
}
|
|
65
|
+
}
|
|
58
66
|
// Check for code files
|
|
59
67
|
if (kind === "unknown") {
|
|
60
68
|
const ext = path.extname(rel).toLowerCase();
|
package/dist/prompt.d.ts
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import type { CommentAnalysisResult } from "./comment-analysis.js";
|
|
2
2
|
import type { KibiConfig } from "./config.js";
|
|
3
|
+
import type { GuidanceCache } from "./guidance-cache.js";
|
|
3
4
|
import type { PathKind } from "./path-kind.js";
|
|
5
|
+
import type { RepoPosture } from "./repo-posture.js";
|
|
6
|
+
import type { RiskClass } from "./risk-classifier.js";
|
|
4
7
|
import type { WorkspaceHealth } from "./workspace-health.js";
|
|
5
8
|
declare const SENTINEL = "<!-- kibi-opencode -->";
|
|
6
9
|
export interface PromptContext {
|
|
@@ -11,9 +14,27 @@ export interface PromptContext {
|
|
|
11
14
|
workspaceHealth?: WorkspaceHealth;
|
|
12
15
|
hasRecentKbEdit?: boolean;
|
|
13
16
|
recentCommentSuggestion?: CommentAnalysisResult | null;
|
|
17
|
+
/** Posture detected by repo-posture detection */
|
|
18
|
+
posture?: RepoPosture;
|
|
19
|
+
/** Risk class from risk-classifier */
|
|
20
|
+
riskClass?: RiskClass;
|
|
21
|
+
/** Cache instance for skip-repeated-guidance */
|
|
22
|
+
cache?: GuidanceCache;
|
|
23
|
+
/** Workspace root for cache key */
|
|
24
|
+
workspaceRoot?: string;
|
|
25
|
+
/** Branch for cache key */
|
|
26
|
+
branch?: string;
|
|
27
|
+
/** Whether to append completion reminder for risky classes */
|
|
28
|
+
completionReminder?: boolean;
|
|
29
|
+
/** Merged maintenance-degraded state (static + runtime overlay) */
|
|
30
|
+
maintenanceDegraded?: boolean;
|
|
31
|
+
/** Degraded-mode logging policy */
|
|
32
|
+
degradedMode?: "warn-once" | "structured-only";
|
|
33
|
+
/** Whether to show the degraded advisory block this invocation */
|
|
34
|
+
showDegradedAdvisory?: boolean;
|
|
14
35
|
}
|
|
15
36
|
/**
|
|
16
|
-
* Build prompt with contextual guidance based on
|
|
37
|
+
* Build prompt with contextual guidance based on posture, risk class, and cache state.
|
|
17
38
|
*/
|
|
18
39
|
export declare function buildPrompt(context?: PromptContext): string;
|
|
19
40
|
/**
|
package/dist/prompt.js
CHANGED
|
@@ -1,163 +1,339 @@
|
|
|
1
|
+
import * as path from "node:path";
|
|
1
2
|
import { isPluginEnabled } from "./config.js";
|
|
3
|
+
import { getSourceLinkedRequirementIds } from "./source-linked-guidance.js";
|
|
2
4
|
const SENTINEL = "<!-- kibi-opencode -->";
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
5
|
+
// ── Token budget enforcement ───────────────────────────────────────────
|
|
6
|
+
const MAX_BULLETS = 5;
|
|
7
|
+
const MAX_WORDS = 117; // Reserve 3 words for sentinel so total injected prompt stays ≤ 120
|
|
8
|
+
function countWords(text) {
|
|
9
|
+
return text.split(/\s+/).filter(Boolean).length;
|
|
10
|
+
}
|
|
11
|
+
function countBullets(lines) {
|
|
12
|
+
return lines.filter((l) => l.startsWith("-")).length;
|
|
13
|
+
}
|
|
14
|
+
function enforceBudget(block) {
|
|
15
|
+
const lines = block.split("\n");
|
|
16
|
+
if (countBullets(lines) > MAX_BULLETS || countWords(block) > MAX_WORDS) {
|
|
17
|
+
// Trim to budget: keep header + first MAX_BULLETS bullet lines
|
|
18
|
+
const header = [];
|
|
19
|
+
const bullets = [];
|
|
20
|
+
for (const line of lines) {
|
|
21
|
+
if (line.startsWith("-") && bullets.length < MAX_BULLETS) {
|
|
22
|
+
bullets.push(line);
|
|
23
|
+
}
|
|
24
|
+
else if (!line.startsWith("-")) {
|
|
25
|
+
if (bullets.length === 0)
|
|
26
|
+
header.push(line);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
const trimmed = [...header, ...bullets].join("\n");
|
|
30
|
+
if (countWords(trimmed) <= MAX_WORDS)
|
|
31
|
+
return trimmed;
|
|
32
|
+
// Hard truncate to MAX_WORDS
|
|
33
|
+
const words = trimmed.split(/\s+/).slice(0, MAX_WORDS);
|
|
34
|
+
return words.join(" ");
|
|
35
|
+
}
|
|
36
|
+
return block;
|
|
37
|
+
}
|
|
38
|
+
// ── File bucket derivation ─────────────────────────────────────────────
|
|
39
|
+
function deriveFileBucket(pathKind) {
|
|
40
|
+
return pathKind;
|
|
41
|
+
}
|
|
42
|
+
// ── Guidance blocks by risk class ──────────────────────────────────────
|
|
43
|
+
const GUIDANCE_BY_RISK = {
|
|
44
|
+
safe_docs_only: null,
|
|
45
|
+
safe_test_only: null,
|
|
46
|
+
kb_doc_structural: `📚 **Kibi documentation changes detected**
|
|
47
|
+
|
|
48
|
+
When editing KB documentation:
|
|
49
|
+
- Maintain traceability — link entities using relationships: specified_by, verified_by, etc.
|
|
50
|
+
- Validate — Use kb_check after making changes to catch integrity issues.
|
|
51
|
+
- Follow entity patterns — ensure each entity has proper frontmatter.`,
|
|
52
|
+
req_policy_candidate: `📋 **Requirement changes detected**
|
|
53
|
+
|
|
54
|
+
Requirement edits need policy alignment. Run kb_check with required-fields and no-dangling-refs. For priority:must requirements, also run must-priority-coverage.
|
|
55
|
+
- Keep artifacts separate (REQ, SCEN, TEST)
|
|
56
|
+
- Add verification: create or update linked SCEN and TEST entities`,
|
|
57
|
+
behavior_candidate: `📝 **Code changes detected**
|
|
58
|
+
|
|
59
|
+
Code changes need traceability. Use kb_search for context. For test/e2e symbols, prefer durable relationships (e.g. via symbols.yaml with covered_by + validates/verified_by); inline // implements REQ-xxx comments remain optional and backward-compatible.`,
|
|
60
|
+
traceability_candidate: `📝 **Code changes detected**
|
|
12
61
|
|
|
13
|
-
|
|
62
|
+
Code changes need traceability. Use kb_search for context. For test/e2e symbols, prefer durable relationships (e.g. via symbols.yaml with covered_by + validates/verified_by); inline // implements REQ-xxx comments remain optional and backward-compatible.
|
|
63
|
+
- Durable knowledge comment detected — route to KB instead of inline comments
|
|
64
|
+
- Use kb_upsert for FACT, ADR, or REQ entities as appropriate`,
|
|
65
|
+
manual_kb_edit: `⚠️ **WARNING: Direct .kb/ edits bypass validation**
|
|
14
66
|
|
|
15
|
-
|
|
16
|
-
- Use kb_search to discover relevant entities
|
|
67
|
+
The Kibi knowledge base is managed through public MCP tools. Direct manual edits to .kb/** can cause inconsistencies.
|
|
17
68
|
- Use kb_upsert to create/update entities
|
|
18
|
-
- Use
|
|
19
|
-
- Use kb_check to validate consistency
|
|
20
|
-
|
|
69
|
+
- Use kb_delete to remove entities
|
|
70
|
+
- Use kb_check to validate consistency`,
|
|
71
|
+
};
|
|
72
|
+
// ── Posture overrides ──────────────────────────────────────────────────
|
|
73
|
+
function postureGuidance(posture) {
|
|
74
|
+
switch (posture) {
|
|
75
|
+
case "vendored_only":
|
|
76
|
+
// Minimal guidance only, no bootstrap nags
|
|
77
|
+
return null;
|
|
78
|
+
case "root_uninitialized":
|
|
79
|
+
return `🔧 **Bootstrap required**
|
|
80
|
+
|
|
81
|
+
This repository does not appear to have Kibi initialized. Agents should:
|
|
82
|
+
- Use \`/init-kibi\` for retroactive bootstrap of existing repos (preferred MCP command)
|
|
83
|
+
- Ask the user/operator to run setup or repair outside this session if \`/init-kibi\` is insufficient
|
|
84
|
+
|
|
85
|
+
Do not run \`kibi\` CLI commands directly; use the public MCP tools (kb_search, kb_query, kb_status, kb_find_gaps, kb_coverage, kb_graph, kb_upsert, kb_delete, kb_check).`;
|
|
86
|
+
case "root_partial":
|
|
87
|
+
return `⚠️ **Partial KB setup detected**
|
|
88
|
+
|
|
89
|
+
Root .kb/config.json exists but some configured KB targets are missing. Guidance remains advisory until the user/operator restores the configured KB targets.`;
|
|
90
|
+
default:
|
|
91
|
+
return null;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
// ── Main guidance builder ──────────────────────────────────────────────
|
|
95
|
+
/**
|
|
96
|
+
* Build prompt guidance block based on posture, risk class, and cache state.
|
|
97
|
+
*/
|
|
98
|
+
function buildContextualGuidance(context) {
|
|
99
|
+
const posture = context.posture ?? "root_active";
|
|
100
|
+
const riskClass = context.riskClass;
|
|
101
|
+
const showDegraded = context.showDegradedAdvisory === true &&
|
|
102
|
+
context.maintenanceDegraded === true &&
|
|
103
|
+
context.degradedMode === "warn-once";
|
|
104
|
+
// ── Single-block priority selection ──
|
|
105
|
+
// Priority order (highest wins): manual_kb_edit > posture > risk_class > safe/none
|
|
106
|
+
let selectedBlock = null;
|
|
107
|
+
// Priority 1: vendored_only → sentinel only (unless degraded advisory forced)
|
|
108
|
+
if (posture === "vendored_only" && !showDegraded) {
|
|
109
|
+
return SENTINEL;
|
|
110
|
+
}
|
|
111
|
+
// Priority 2: manual_kb_edit — always fires, never cache-suppressed
|
|
112
|
+
if (context.hasRecentKbEdit || riskClass === "manual_kb_edit") {
|
|
113
|
+
selectedBlock = GUIDANCE_BY_RISK.manual_kb_edit;
|
|
21
114
|
}
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
115
|
+
// Priority 3: Posture warnings for non-active states — not cache-suppressed
|
|
116
|
+
else if (posture === "root_uninitialized" || posture === "root_partial") {
|
|
117
|
+
const postureBlock = postureGuidance(posture);
|
|
118
|
+
if (postureBlock)
|
|
119
|
+
selectedBlock = postureBlock;
|
|
120
|
+
}
|
|
121
|
+
// Priority 4: Legacy workspace health bootstrap (only when no posture) — not cache-suppressed
|
|
122
|
+
else if (!context.posture && context.workspaceHealth?.needsBootstrap) {
|
|
123
|
+
selectedBlock = `🔧 **Bootstrap required**
|
|
25
124
|
|
|
26
125
|
This repository does not appear to have Kibi initialized. Agents should:
|
|
27
126
|
- Use \`/init-kibi\` for retroactive bootstrap of existing repos (preferred MCP command)
|
|
28
127
|
- Ask the user/operator to run setup or repair outside this session if \`/init-kibi\` is insufficient
|
|
29
128
|
|
|
30
|
-
Do not run \`kibi\` CLI commands directly; use the public MCP tools (kb_search, kb_query, kb_status, kb_find_gaps, kb_coverage, kb_graph, kb_upsert, kb_delete, kb_check)
|
|
31
|
-
`);
|
|
129
|
+
Do not run \`kibi\` CLI commands directly; use the public MCP tools (kb_search, kb_query, kb_status, kb_find_gaps, kb_coverage, kb_graph, kb_upsert, kb_delete, kb_check).`;
|
|
32
130
|
}
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
131
|
+
// Advisory guidance: check cache before selecting, since these blocks can be safely suppressed
|
|
132
|
+
else {
|
|
133
|
+
// Cache check: skip repeated advisory guidance — only after critical signals are handled above
|
|
134
|
+
// Allow degraded advisory to bypass cache so it is always visible
|
|
135
|
+
if (!showDegraded &&
|
|
136
|
+
context.cache &&
|
|
137
|
+
context.workspaceRoot &&
|
|
138
|
+
context.branch &&
|
|
139
|
+
riskClass) {
|
|
140
|
+
const lastEdit = context.recentEdits[context.recentEdits.length - 1];
|
|
141
|
+
const key = {
|
|
142
|
+
workspaceRoot: context.workspaceRoot,
|
|
143
|
+
branch: context.branch,
|
|
144
|
+
posture,
|
|
145
|
+
riskClass,
|
|
146
|
+
fileBucket: deriveFileBucket(lastEdit?.kind ?? "unknown"),
|
|
147
|
+
};
|
|
148
|
+
if (context.cache.isSatisfied(key)) {
|
|
149
|
+
return SENTINEL; // skip guidance — recently satisfied
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
// Priority 5: Risk-class-driven guidance (for non-safe classes)
|
|
153
|
+
if (riskClass &&
|
|
154
|
+
riskClass !== "safe_docs_only" &&
|
|
155
|
+
riskClass !== "safe_test_only") {
|
|
156
|
+
// For behavior/traceability with comment suggestions, use suggestion guidance
|
|
157
|
+
if ((riskClass === "behavior_candidate" ||
|
|
158
|
+
riskClass === "traceability_candidate") &&
|
|
159
|
+
context.recentCommentSuggestion) {
|
|
160
|
+
selectedBlock = buildCommentSuggestionGuidance(context.recentCommentSuggestion);
|
|
161
|
+
}
|
|
162
|
+
else {
|
|
163
|
+
const block = GUIDANCE_BY_RISK[riskClass];
|
|
164
|
+
if (block)
|
|
165
|
+
selectedBlock = block;
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
// Priority 6: Legacy path-kind fallback (when no risk class)
|
|
169
|
+
else if (!riskClass) {
|
|
170
|
+
const codeEdits = context.recentEdits.filter((e) => e.kind === "code");
|
|
171
|
+
const reqEdits = context.recentEdits.filter((e) => e.kind === "requirement");
|
|
172
|
+
const kbDocEdits = context.recentEdits.filter((e) => [
|
|
173
|
+
"requirement",
|
|
174
|
+
"scenario",
|
|
175
|
+
"test",
|
|
176
|
+
"adr",
|
|
177
|
+
"fact",
|
|
178
|
+
"flag",
|
|
179
|
+
"event",
|
|
180
|
+
"symbol",
|
|
181
|
+
].includes(e.kind));
|
|
182
|
+
if (codeEdits.length > 0) {
|
|
183
|
+
const suggestion = context.recentCommentSuggestion;
|
|
184
|
+
if (suggestion) {
|
|
185
|
+
selectedBlock = buildCommentSuggestionGuidance(suggestion);
|
|
186
|
+
}
|
|
187
|
+
else {
|
|
188
|
+
selectedBlock = `📝 **Code changes detected**
|
|
189
|
+
|
|
190
|
+
Before implementing or explaining code:
|
|
191
|
+
1. **Discover first** - Run kb_search to find related requirements, ADRs, tests, facts, and symbols.
|
|
192
|
+
2. **Follow up exactly** - Run kb_query by sourceFile, id, type, or tags once you know what you need.
|
|
193
|
+
3. **Prefer Kibi over comments** - Store durable knowledge in KB entities instead of inline comments.
|
|
194
|
+
4. **Add traceability** - For test/e2e symbols, prefer durable symbol/test/requirement relationships (e.g. via symbols.yaml with covered_by + validates/verified_by); inline // implements REQ-xxx comments remain optional and backward-compatible for quick code-only changes.
|
|
195
|
+
|
|
196
|
+
If you're adding long explanatory comments, consider routing that knowledge to:
|
|
197
|
+
- \`FACT\` for domain invariants, properties, limits, cardinalities
|
|
198
|
+
- \`ADR\` for technical decisions, tradeoffs, rationale
|
|
199
|
+
- \`REQ\` for system behavior requirements`;
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
else if (reqEdits.length > 0) {
|
|
203
|
+
selectedBlock = GUIDANCE_BY_RISK.req_policy_candidate;
|
|
204
|
+
}
|
|
205
|
+
else if (kbDocEdits.length > 0) {
|
|
206
|
+
selectedBlock = GUIDANCE_BY_RISK.kb_doc_structural;
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
// Source-linked micro-brief: insert after header line for code risk classes
|
|
211
|
+
// Inserting after the header (not prepending before it) preserves the header
|
|
212
|
+
// under enforceBudget's trimming logic, which only collects non-bullet lines
|
|
213
|
+
// before the first bullet.
|
|
214
|
+
if (selectedBlock &&
|
|
215
|
+
(riskClass === "behavior_candidate" ||
|
|
216
|
+
riskClass === "traceability_candidate") &&
|
|
217
|
+
context.workspaceRoot) {
|
|
218
|
+
try {
|
|
219
|
+
const lastEdit = context.recentEdits[context.recentEdits.length - 1];
|
|
220
|
+
if (lastEdit?.path) {
|
|
221
|
+
const editedPath = lastEdit.path;
|
|
222
|
+
const absEdited = path.isAbsolute(editedPath)
|
|
223
|
+
? editedPath
|
|
224
|
+
: path.join(context.workspaceRoot, editedPath);
|
|
225
|
+
const linkedIds = getSourceLinkedRequirementIds(context.workspaceRoot, absEdited);
|
|
226
|
+
if (linkedIds.length >= 1 && linkedIds.length <= 3) {
|
|
227
|
+
const headerEnd = selectedBlock.indexOf("\n");
|
|
228
|
+
if (headerEnd !== -1) {
|
|
229
|
+
selectedBlock = `${selectedBlock.slice(0, headerEnd + 1)}- Existing Kibi links: ${linkedIds.join(", ")}\n${selectedBlock.slice(headerEnd + 1)}`;
|
|
230
|
+
}
|
|
231
|
+
else {
|
|
232
|
+
selectedBlock = `${selectedBlock}\n- Existing Kibi links: ${linkedIds.join(", ")}`;
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
catch {
|
|
238
|
+
// Non-fatal: source-linked brief is best-effort
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
// Inject degraded advisory block for warn-once mode
|
|
242
|
+
if (showDegraded) {
|
|
243
|
+
const advisory = `⚠️ **Maintenance degraded**
|
|
244
|
+
|
|
245
|
+
The Kibi workspace is in a maintenance-degraded state. Guidance remains advisory.`;
|
|
246
|
+
if (selectedBlock) {
|
|
247
|
+
selectedBlock = `${advisory}\n\n${selectedBlock}`;
|
|
248
|
+
}
|
|
249
|
+
else {
|
|
250
|
+
selectedBlock = advisory;
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
// Record cache after generating (advisory, non-blocking)
|
|
254
|
+
// Do not cache degraded-advisory-only emissions
|
|
255
|
+
if (!showDegraded &&
|
|
256
|
+
context.cache &&
|
|
257
|
+
context.workspaceRoot &&
|
|
258
|
+
context.branch &&
|
|
259
|
+
riskClass) {
|
|
260
|
+
const lastEdit = context.recentEdits[context.recentEdits.length - 1];
|
|
261
|
+
const key = {
|
|
262
|
+
workspaceRoot: context.workspaceRoot,
|
|
263
|
+
branch: context.branch,
|
|
264
|
+
posture,
|
|
265
|
+
riskClass,
|
|
266
|
+
fileBucket: deriveFileBucket(lastEdit?.kind ?? "unknown"),
|
|
267
|
+
};
|
|
268
|
+
context.cache.recordSatisfied(key, "guidance");
|
|
269
|
+
}
|
|
270
|
+
// Append completion reminder for risky classes when enabled
|
|
271
|
+
const REMINDER_RISK_CLASSES = [
|
|
272
|
+
"behavior_candidate",
|
|
273
|
+
"traceability_candidate",
|
|
274
|
+
"req_policy_candidate",
|
|
275
|
+
];
|
|
276
|
+
if (selectedBlock &&
|
|
277
|
+
context.completionReminder === true &&
|
|
278
|
+
!context.maintenanceDegraded &&
|
|
279
|
+
riskClass &&
|
|
280
|
+
REMINDER_RISK_CLASSES.includes(riskClass) &&
|
|
281
|
+
posture !== "root_uninitialized" &&
|
|
282
|
+
posture !== "root_partial") {
|
|
283
|
+
selectedBlock = `${selectedBlock}\n- Run \`kb_check\` before completing this task.`;
|
|
284
|
+
}
|
|
285
|
+
// Return: sentinel + one targeted block (or just sentinel if no block)
|
|
286
|
+
return selectedBlock
|
|
287
|
+
? `${SENTINEL}\n\n${enforceBudget(selectedBlock)}`
|
|
288
|
+
: SENTINEL;
|
|
289
|
+
}
|
|
290
|
+
// ── Comment suggestion guidance (legacy compat) ────────────────────────
|
|
291
|
+
function buildCommentSuggestionGuidance(suggestion) {
|
|
292
|
+
switch (suggestion.suggestionType) {
|
|
293
|
+
case "fact":
|
|
294
|
+
return `🎯 **Durable knowledge detected: FACT**
|
|
43
295
|
|
|
44
296
|
Your recent code edit contains a comment that looks like a **domain invariant** (properties, limits, defaults, or cardinality constraints).
|
|
45
297
|
|
|
46
298
|
**Action**: Instead of inline comments, route this to a FACT entity:
|
|
47
299
|
- Create \`documentation/facts/FACT-xxx.md\` with the invariant
|
|
48
|
-
- Link it to relevant requirements
|
|
49
|
-
- Reference the FACT in code with a comment
|
|
300
|
+
- Link it to relevant requirements
|
|
301
|
+
- Reference the FACT in code with a comment
|
|
50
302
|
|
|
51
303
|
This keeps domain truths centralized and searchable.`;
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
routingMessage = `🎯 **Durable knowledge detected: ADR**
|
|
304
|
+
case "adr":
|
|
305
|
+
return `🎯 **Durable knowledge detected: ADR**
|
|
55
306
|
|
|
56
307
|
Your recent code edit contains a comment that looks like a **technical decision** (tradeoffs, rationale, or architecture choices).
|
|
57
308
|
|
|
58
309
|
**Action**: Instead of inline comments, route this to an ADR entity:
|
|
59
310
|
- Create \`documentation/adr/ADR-xxx.md\` documenting the decision
|
|
60
311
|
- Include context, options considered, and the chosen approach
|
|
61
|
-
- Link to constrained code symbols
|
|
312
|
+
- Link to constrained code symbols
|
|
62
313
|
|
|
63
314
|
This preserves decision context for future maintainers.`;
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
routingMessage = `🎯 **Durable knowledge detected: REQ**
|
|
315
|
+
case "req":
|
|
316
|
+
return `🎯 **Durable knowledge detected: REQ**
|
|
67
317
|
|
|
68
318
|
Your recent code edit contains a comment that looks like **behavior intent** (system capabilities or user-facing requirements).
|
|
69
319
|
|
|
70
320
|
**Action**: Instead of inline comments, route this to a REQ entity:
|
|
71
321
|
- Create \`documentation/requirements/REQ-xxx.md\` with the behavior description
|
|
72
322
|
- Add SCEN and TEST entities for specification and verification
|
|
73
|
-
- Link code to requirements
|
|
323
|
+
- Link code to requirements: for test/e2e symbols prefer durable relationships (e.g. via symbols.yaml with covered_by + validates/verified_by); inline // implements REQ-xxx comments remain optional and backward-compatible
|
|
74
324
|
|
|
75
325
|
This ensures behavior is documented and traceable.`;
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
routingMessage = `📝 **Code changes detected**
|
|
79
|
-
|
|
80
|
-
Before implementing or explaining code:
|
|
81
|
-
1. **Discover first** - Run kb_search to find related requirements, ADRs, tests, facts, and symbols.
|
|
82
|
-
2. **Follow up exactly** - Run kb_query by sourceFile, id, type, or tags once you know what you need.
|
|
83
|
-
3. **Check freshness when needed** - Run kb_status if you need branch or stale-state confirmation.
|
|
84
|
-
4. **Prefer Kibi over comments** - Store durable knowledge in KB entities instead of inline comments.
|
|
85
|
-
5. **Add traceability** - Add traceability comments to new or modified functions/classes so the pre-commit hook can verify coverage (e.g., \`// implements REQ-xxx\` in JS/TS or docstring references in Python).`;
|
|
86
|
-
}
|
|
87
|
-
parts.push(routingMessage);
|
|
88
|
-
}
|
|
89
|
-
else {
|
|
90
|
-
parts.push(`
|
|
91
|
-
📝 **Code changes detected**
|
|
326
|
+
default:
|
|
327
|
+
return `📝 **Code changes detected**
|
|
92
328
|
|
|
93
329
|
Before implementing or explaining code:
|
|
94
330
|
1. **Discover first** - Run kb_search to find related requirements, ADRs, tests, facts, and symbols.
|
|
95
331
|
2. **Follow up exactly** - Run kb_query by sourceFile, id, type, or tags once you know what you need.
|
|
96
|
-
3. **
|
|
97
|
-
4. **
|
|
98
|
-
5. **Add traceability** - Add traceability comments to new or modified functions/classes (e.g., \`// implements REQ-xxx\` in JS/TS or docstring references in Python) so the pre-commit hook can verify coverage.
|
|
99
|
-
|
|
100
|
-
If you're adding long explanatory comments, consider routing that knowledge to:
|
|
101
|
-
- \`FACT\` for domain invariants, properties, limits, cardinalities
|
|
102
|
-
- \`ADR\` for technical decisions, tradeoffs, rationale
|
|
103
|
-
- \`REQ\` for system behavior requirements
|
|
104
|
-
- \`SCEN\` for behavior examples and flows
|
|
105
|
-
- \`TEST\` for verification intent
|
|
106
|
-
`);
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
if (reqEdits.length > 0) {
|
|
110
|
-
parts.push(`
|
|
111
|
-
📋 **Requirement changes detected**
|
|
112
|
-
|
|
113
|
-
When editing requirements:
|
|
114
|
-
1. **Keep artifacts separate** - Do not embed scenarios or tests inside requirement files.
|
|
115
|
-
2. **Add verification** - Create or update linked \`SCEN\` and \`TEST\` entities.
|
|
116
|
-
3. **Check coverage** - For \`priority: must\` requirements, ensure both scenario and test coverage.
|
|
117
|
-
|
|
118
|
-
Preferred structure:
|
|
119
|
-
- \`REQ-xxx.md\` contains the requirement statement
|
|
120
|
-
- \`SCEN-xxx.md\` specifies behavior via Given/When/Then
|
|
121
|
-
- \`TEST-xxx.md\` verifies the requirement
|
|
122
|
-
`);
|
|
123
|
-
}
|
|
124
|
-
if (kbDocEdits.length > 0 && reqEdits.length === 0) {
|
|
125
|
-
parts.push(`
|
|
126
|
-
📚 **Kibi documentation changes detected**
|
|
127
|
-
|
|
128
|
-
When editing KB documentation:
|
|
129
|
-
1. **Maintain traceability** - Link entities using relationships: specified_by (req→scenario), verified_by (req→test), etc.
|
|
130
|
-
2. **Validate** - Use \`kb_check\` after making changes to catch integrity issues.
|
|
131
|
-
3. **Follow entity patterns** - Ensure each entity has proper frontmatter with required fields.
|
|
132
|
-
`);
|
|
133
|
-
}
|
|
134
|
-
if (parts.length === 1) {
|
|
135
|
-
parts.push(`This project uses Kibi (via MCP). Prefer storing durable knowledge in Kibi over code comments.
|
|
136
|
-
|
|
137
|
-
Before changing behavior: use kb_search for discovery, then kb_query by sourceFile, id, type, or tags for exact follow-up; do not rely on undocumented tools.
|
|
138
|
-
|
|
139
|
-
Keep changed symbols traceable: add \`// implements REQ-xxx\` to every new or modified function/class so the pre-commit hook can verify coverage.
|
|
140
|
-
|
|
141
|
-
Run kb_check after KB mutations.
|
|
142
|
-
|
|
143
|
-
Dogfood note for this repo: OpenCode here uses local built \`kibi-mcp\` and \`kibi-opencode\` artifacts. If you change package versions or local package wiring, run \`bun run build\` before relying on OpenCode in this workspace.
|
|
144
|
-
|
|
145
|
-
**Kibi-first workflow:**
|
|
146
|
-
1. **Discover**: Run kb_search to find relevant requirements, ADRs, tests, facts, and symbols.
|
|
147
|
-
2. **Confirm**: Run kb_query with sourceFile, id, type, or tags once you know the exact follow-up target.
|
|
148
|
-
3. **Inspect freshness**: Run kb_status when branch or stale-state confidence matters.
|
|
149
|
-
4. **Document intent**: If you are about to explain code, STOP. Route that explanation to kb_upsert instead of inline comments.
|
|
150
|
-
5. **Link during work**: When creating KB entities, include relationship rows: specified_by (req→scenario), verified_by (req→test), implements (symbol→req), covered_by (symbol→test).
|
|
151
|
-
6. **Validate**: Run kb_check after KB mutations to catch violations early.
|
|
152
|
-
|
|
153
|
-
**Public Kibi tools only:** kb_search, kb_query, kb_status, kb_find_gaps, kb_coverage, kb_graph, kb_upsert, kb_delete, kb_check.
|
|
154
|
-
|
|
155
|
-
Do not invoke Kibi CLI commands directly from the agent.
|
|
156
|
-
|
|
157
|
-
Bootstrap existing repos: use \`/init-kibi\` to run the retroactive initialization workflow.`);
|
|
332
|
+
3. **Prefer Kibi over comments** - Store durable knowledge in KB entities instead of inline comments.
|
|
333
|
+
4. **Add traceability** - For test/e2e symbols, prefer durable symbol/test/requirement relationships (e.g. via symbols.yaml with covered_by + validates/verified_by); inline // implements REQ-xxx comments remain optional and backward-compatible for quick code-only changes.`;
|
|
158
334
|
}
|
|
159
|
-
return parts.join("\n\n").trim();
|
|
160
335
|
}
|
|
336
|
+
// ── Base guidance (no context) ─────────────────────────────────────────
|
|
161
337
|
/**
|
|
162
338
|
* Build the static guidance block (original behavior).
|
|
163
339
|
*/
|
|
@@ -166,7 +342,7 @@ This project uses Kibi (via MCP). Prefer storing durable knowledge in Kibi over
|
|
|
166
342
|
|
|
167
343
|
Before changing behavior: use kb_search for discovery, then kb_query by sourceFile, id, type, or tags for exact follow-up; do not rely on undocumented tools.
|
|
168
344
|
|
|
169
|
-
Keep changed symbols traceable:
|
|
345
|
+
Keep changed symbols traceable: for test and e2e code, prefer durable symbol/test/requirement relationships (e.g. via \`symbols.yaml\`); inline \`// implements REQ-xxx\` comments remain optional and backward-compatible for quick code-only changes.
|
|
170
346
|
|
|
171
347
|
Run kb_check after KB mutations.
|
|
172
348
|
|
|
@@ -186,7 +362,7 @@ Do not invoke Kibi CLI commands directly from the agent.
|
|
|
186
362
|
|
|
187
363
|
Bootstrap existing repos: use \`/init-kibi\` to run the retroactive initialization workflow.`;
|
|
188
364
|
/**
|
|
189
|
-
* Build prompt with contextual guidance based on
|
|
365
|
+
* Build prompt with contextual guidance based on posture, risk class, and cache state.
|
|
190
366
|
*/
|
|
191
367
|
export function buildPrompt(context) {
|
|
192
368
|
if (!context) {
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Classification of the repository's Kibi posture — how Kibi is (or isn't)
|
|
3
|
+
* set up relative to the active workspace root.
|
|
4
|
+
*/
|
|
5
|
+
export type RepoPosture = "root_active" | "root_partial" | "root_uninitialized" | "vendored_only" | "hybrid_root_plus_vendored";
|
|
6
|
+
export interface PostureResult {
|
|
7
|
+
state: RepoPosture;
|
|
8
|
+
needsBootstrap: boolean;
|
|
9
|
+
reason: string;
|
|
10
|
+
maintenanceDegraded: boolean;
|
|
11
|
+
}
|
|
12
|
+
export declare function detectPosture(cwd: string): PostureResult;
|