kibi-cli 0.6.2 → 0.8.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/dist/commands/check.d.ts.map +1 -1
- package/dist/commands/check.js +18 -0
- package/dist/commands/init-helpers.d.ts +1 -0
- package/dist/commands/init-helpers.d.ts.map +1 -1
- package/dist/commands/init-helpers.js +39 -6
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +2 -1
- package/dist/commands/sync/manifest.d.ts +1 -0
- package/dist/commands/sync/manifest.d.ts.map +1 -1
- package/dist/commands/sync/manifest.js +1 -1
- package/dist/commands/sync/persistence.d.ts.map +1 -1
- package/dist/commands/sync/persistence.js +4 -2
- package/dist/commands/sync/staging.d.ts +3 -0
- package/dist/commands/sync/staging.d.ts.map +1 -1
- package/dist/commands/sync/staging.js +58 -1
- package/dist/commands/sync.d.ts +18 -1
- package/dist/commands/sync.d.ts.map +1 -1
- package/dist/commands/sync.js +19 -4
- package/dist/extractors/markdown.d.ts +2 -0
- package/dist/extractors/markdown.d.ts.map +1 -1
- package/dist/extractors/symbols-coordinator.d.ts +34 -0
- package/dist/extractors/symbols-coordinator.d.ts.map +1 -1
- package/dist/extractors/symbols-coordinator.js +70 -1
- package/dist/extractors/symbols-ts.d.ts +2 -0
- package/dist/extractors/symbols-ts.d.ts.map +1 -1
- package/dist/extractors/symbols-ts.js +108 -1
- package/dist/public/brief-config.d.ts +4 -0
- package/dist/public/brief-config.d.ts.map +1 -0
- package/dist/public/brief-config.js +21 -0
- package/dist/public/extractors/symbols-coordinator.d.ts +1 -1
- package/dist/public/extractors/symbols-coordinator.d.ts.map +1 -1
- package/dist/public/extractors/symbols-coordinator.js +1 -1
- package/dist/public/operational-artifacts.d.ts +2 -0
- package/dist/public/operational-artifacts.d.ts.map +1 -0
- package/dist/public/operational-artifacts.js +4 -0
- package/dist/search-ranking.d.ts.map +1 -1
- package/dist/search-ranking.js +132 -25
- package/dist/utils/config.d.ts +18 -3
- package/dist/utils/config.d.ts.map +1 -1
- package/dist/utils/config.js +39 -0
- package/dist/utils/rule-registry.d.ts.map +1 -1
- package/dist/utils/rule-registry.js +6 -0
- package/package.json +10 -2
- package/schema/config.json +73 -0
- package/schema/entities.pl +1 -0
- package/schema/relationships.pl +4 -0
- package/src/public/brief-config.ts +25 -0
- package/src/public/extractors/symbols-coordinator.ts +7 -0
- package/src/public/operational-artifacts.ts +5 -0
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
*/
|
|
18
18
|
import { access, readFile } from "node:fs/promises";
|
|
19
19
|
import * as path from "node:path";
|
|
20
|
-
import { Project, } from "ts-morph";
|
|
20
|
+
import { Project, ScriptKind, } from "ts-morph";
|
|
21
21
|
const SUPPORTED_SOURCE_EXTENSIONS = new Set([
|
|
22
22
|
".ts",
|
|
23
23
|
".tsx",
|
|
@@ -28,6 +28,35 @@ const SUPPORTED_SOURCE_EXTENSIONS = new Set([
|
|
|
28
28
|
".mjs",
|
|
29
29
|
".cjs",
|
|
30
30
|
]);
|
|
31
|
+
// implements REQ-001
|
|
32
|
+
export function createTsMorphSourceAnalysisProvider() {
|
|
33
|
+
const project = new Project({
|
|
34
|
+
skipAddingFilesFromTsConfig: true,
|
|
35
|
+
});
|
|
36
|
+
return {
|
|
37
|
+
id: "ts-morph",
|
|
38
|
+
supportsFile(filePath) {
|
|
39
|
+
return SUPPORTED_SOURCE_EXTENSIONS.has(path.extname(filePath).toLowerCase());
|
|
40
|
+
},
|
|
41
|
+
analyzeText(filePath, content) {
|
|
42
|
+
const sourceFile = project.createSourceFile(filePath, content, {
|
|
43
|
+
overwrite: true,
|
|
44
|
+
scriptKind: chooseScriptKind(filePath),
|
|
45
|
+
});
|
|
46
|
+
return {
|
|
47
|
+
sourceFile: filePath,
|
|
48
|
+
language: inferSourceLanguage(filePath),
|
|
49
|
+
providerId: "ts-morph",
|
|
50
|
+
module: {
|
|
51
|
+
title: inferModuleTitle(filePath),
|
|
52
|
+
language: inferSourceLanguage(filePath),
|
|
53
|
+
analysisMode: "parser",
|
|
54
|
+
},
|
|
55
|
+
symbols: collectSourceSymbols(sourceFile),
|
|
56
|
+
};
|
|
57
|
+
},
|
|
58
|
+
};
|
|
59
|
+
}
|
|
31
60
|
export async function enrichSymbolCoordinatesWithTsMorph(entries, workspaceRoot) {
|
|
32
61
|
// implements REQ-vscode-traceability
|
|
33
62
|
const project = new Project({
|
|
@@ -142,6 +171,84 @@ async function enrichWithTextFallbackInternal(entry, absolutePath) {
|
|
|
142
171
|
return entry;
|
|
143
172
|
}
|
|
144
173
|
}
|
|
174
|
+
function collectSourceSymbols(sourceFile) {
|
|
175
|
+
const symbols = [];
|
|
176
|
+
for (const decl of sourceFile.getFunctions()) {
|
|
177
|
+
if (!decl.isExported())
|
|
178
|
+
continue;
|
|
179
|
+
symbols.push(toSourceSymbolAnalysis(sourceFile, decl.getName() ?? "<anonymous>", "function", decl.getNameNode() ?? decl, decl, `${decl.getFullText()}\n${decl
|
|
180
|
+
.getJsDocs()
|
|
181
|
+
.map((doc) => doc.getFullText())
|
|
182
|
+
.join("\n")}`));
|
|
183
|
+
}
|
|
184
|
+
for (const decl of sourceFile.getClasses()) {
|
|
185
|
+
if (!decl.isExported())
|
|
186
|
+
continue;
|
|
187
|
+
symbols.push(toSourceSymbolAnalysis(sourceFile, decl.getName() ?? "<anonymous>", "class", decl.getNameNode() ?? decl, decl, `${decl.getText()}\n${decl
|
|
188
|
+
.getJsDocs()
|
|
189
|
+
.map((doc) => doc.getFullText())
|
|
190
|
+
.join("\n")}`));
|
|
191
|
+
}
|
|
192
|
+
for (const decl of sourceFile.getInterfaces()) {
|
|
193
|
+
if (!decl.isExported())
|
|
194
|
+
continue;
|
|
195
|
+
symbols.push(toSourceSymbolAnalysis(sourceFile, decl.getName() ?? "<anonymous>", "interface", decl.getNameNode() ?? decl, decl, decl.getText()));
|
|
196
|
+
}
|
|
197
|
+
for (const decl of sourceFile.getTypeAliases()) {
|
|
198
|
+
if (!decl.isExported())
|
|
199
|
+
continue;
|
|
200
|
+
symbols.push(toSourceSymbolAnalysis(sourceFile, decl.getName() ?? "<anonymous>", "type", decl.getNameNode() ?? decl, decl, decl.getText()));
|
|
201
|
+
}
|
|
202
|
+
for (const decl of sourceFile.getEnums()) {
|
|
203
|
+
if (!decl.isExported())
|
|
204
|
+
continue;
|
|
205
|
+
symbols.push(toSourceSymbolAnalysis(sourceFile, decl.getName() ?? "<anonymous>", "enum", decl.getNameNode() ?? decl, decl, decl.getText()));
|
|
206
|
+
}
|
|
207
|
+
for (const statement of sourceFile.getVariableStatements()) {
|
|
208
|
+
if (!statement.isExported())
|
|
209
|
+
continue;
|
|
210
|
+
for (const declaration of statement.getDeclarations()) {
|
|
211
|
+
symbols.push(toSourceSymbolAnalysis(sourceFile, declaration.getName(), "variable", declaration.getNameNode() ?? declaration, declaration, declaration.getText()));
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
return symbols;
|
|
215
|
+
}
|
|
216
|
+
function toSourceSymbolAnalysis(sourceFile, name, kind, startNode, endNode, directiveText) {
|
|
217
|
+
const start = sourceFile.getLineAndColumnAtPos(startNode.getStart());
|
|
218
|
+
const end = sourceFile.getLineAndColumnAtPos(endNode.getEnd());
|
|
219
|
+
return {
|
|
220
|
+
name,
|
|
221
|
+
kind,
|
|
222
|
+
startLine: start.line,
|
|
223
|
+
startColumn: Math.max(0, start.column - 1),
|
|
224
|
+
endLine: end.line,
|
|
225
|
+
endColumn: Math.max(0, end.column - 1),
|
|
226
|
+
directiveText,
|
|
227
|
+
};
|
|
228
|
+
}
|
|
229
|
+
function chooseScriptKind(filePath) {
|
|
230
|
+
const lower = filePath.toLowerCase();
|
|
231
|
+
if (lower.endsWith(".tsx"))
|
|
232
|
+
return ScriptKind.TSX;
|
|
233
|
+
if (lower.endsWith(".ts") || lower.endsWith(".mts") || lower.endsWith(".cts")) {
|
|
234
|
+
return ScriptKind.TS;
|
|
235
|
+
}
|
|
236
|
+
if (lower.endsWith(".jsx"))
|
|
237
|
+
return ScriptKind.JSX;
|
|
238
|
+
return ScriptKind.JS;
|
|
239
|
+
}
|
|
240
|
+
function inferSourceLanguage(filePath) {
|
|
241
|
+
const extension = path.extname(filePath).toLowerCase();
|
|
242
|
+
if ([".ts", ".tsx", ".mts", ".cts"].includes(extension)) {
|
|
243
|
+
return "typescript";
|
|
244
|
+
}
|
|
245
|
+
return "javascript";
|
|
246
|
+
}
|
|
247
|
+
function inferModuleTitle(filePath) {
|
|
248
|
+
const extension = path.extname(filePath);
|
|
249
|
+
const basename = path.basename(filePath, extension);
|
|
250
|
+
return basename.length > 0 ? basename : path.basename(filePath);
|
|
251
|
+
}
|
|
145
252
|
function findNamedDeclaration(sourceFile, title) {
|
|
146
253
|
const candidates = [];
|
|
147
254
|
for (const decl of sourceFile.getFunctions()) {
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"brief-config.d.ts","sourceRoot":"","sources":["../../src/public/brief-config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAc,KAAK,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAEnE,YAAY,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAEvD,wBAAgB,eAAe,CAAC,GAAG,GAAE,MAAsB,GAAG,YAAY,CAoBzE"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { loadConfig } from "../utils/config.js";
|
|
2
|
+
export function loadBriefConfig(cwd = process.cwd()) {
|
|
3
|
+
const briefs = loadConfig(cwd).briefs;
|
|
4
|
+
return {
|
|
5
|
+
enabled: briefs?.enabled ?? true,
|
|
6
|
+
retention: {
|
|
7
|
+
maxPerBranch: briefs?.retention?.maxPerBranch ?? 200,
|
|
8
|
+
maxAgeDays: briefs?.retention?.maxAgeDays ?? 14,
|
|
9
|
+
keepUnread: briefs?.retention?.keepUnread ?? true,
|
|
10
|
+
},
|
|
11
|
+
channels: {
|
|
12
|
+
vscode: briefs?.channels?.vscode ?? true,
|
|
13
|
+
tui: briefs?.channels?.tui ?? true,
|
|
14
|
+
},
|
|
15
|
+
tui: {
|
|
16
|
+
toast: briefs?.tui?.toast ?? true,
|
|
17
|
+
appendPrompt: briefs?.tui?.appendPrompt ?? true,
|
|
18
|
+
idleDelayMs: briefs?.tui?.idleDelayMs ?? 1500,
|
|
19
|
+
},
|
|
20
|
+
};
|
|
21
|
+
}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export { enrichSymbolCoordinates, type ManifestSymbolEntry, } from "../../extractors/symbols-coordinator.js";
|
|
1
|
+
export { analyzeSourceText, enrichSymbolCoordinates, type ManifestSymbolEntry, type AnalyzeSourceTextOptions, type SourceAnalysisProvider, type SourceAnalysisResult, type SourceModuleAnalysis, type SourceSymbolAnalysis, type SourceSymbolKind, } from "../../extractors/symbols-coordinator.js";
|
|
2
2
|
//# sourceMappingURL=symbols-coordinator.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"symbols-coordinator.d.ts","sourceRoot":"","sources":["../../../src/public/extractors/symbols-coordinator.ts"],"names":[],"mappings":"AAkBA,OAAO,EACL,uBAAuB,EACvB,KAAK,mBAAmB,
|
|
1
|
+
{"version":3,"file":"symbols-coordinator.d.ts","sourceRoot":"","sources":["../../../src/public/extractors/symbols-coordinator.ts"],"names":[],"mappings":"AAkBA,OAAO,EACL,iBAAiB,EACjB,uBAAuB,EACvB,KAAK,mBAAmB,EACxB,KAAK,wBAAwB,EAC7B,KAAK,sBAAsB,EAC3B,KAAK,oBAAoB,EACzB,KAAK,oBAAoB,EACzB,KAAK,oBAAoB,EACzB,KAAK,gBAAgB,GACtB,MAAM,yCAAyC,CAAC"}
|
|
@@ -15,4 +15,4 @@
|
|
|
15
15
|
You should have received a copy of the GNU Affero General Public License
|
|
16
16
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
17
17
|
*/
|
|
18
|
-
export { enrichSymbolCoordinates, } from "../../extractors/symbols-coordinator.js";
|
|
18
|
+
export { analyzeSourceText, enrichSymbolCoordinates, } from "../../extractors/symbols-coordinator.js";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"operational-artifacts.d.ts","sourceRoot":"","sources":["../../src/public/operational-artifacts.ts"],"names":[],"mappings":"AAAA,wBAAgB,yBAAyB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAInE"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"search-ranking.d.ts","sourceRoot":"","sources":["../src/search-ranking.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAChC,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;
|
|
1
|
+
{"version":3,"file":"search-ranking.d.ts","sourceRoot":"","sources":["../src/search-ranking.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAChC,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAgDD,wBAAsB,YAAY,CAChC,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,EACnC,KAAK,EAAE,MAAM,EACb,aAAa,EAAE,MAAM,GACpB,OAAO,CAAC,WAAW,EAAE,CAAC,CA8BxB;AAmGD,wBAAsB,gBAAgB,CAEpC,MAAM,EAAE,MAAM,EACd,aAAa,EAAE,MAAM,GACpB,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CA6BxB"}
|
package/dist/search-ranking.js
CHANGED
|
@@ -1,10 +1,47 @@
|
|
|
1
1
|
import fs from "node:fs/promises";
|
|
2
2
|
import path from "node:path";
|
|
3
|
+
const SEARCH_STOP_WORDS = new Set([
|
|
4
|
+
"to",
|
|
5
|
+
"in",
|
|
6
|
+
"out",
|
|
7
|
+
"log",
|
|
8
|
+
"logged",
|
|
9
|
+
"unable",
|
|
10
|
+
"the",
|
|
11
|
+
"a",
|
|
12
|
+
"an",
|
|
13
|
+
"is",
|
|
14
|
+
"are",
|
|
15
|
+
"was",
|
|
16
|
+
"were",
|
|
17
|
+
"be",
|
|
18
|
+
"been",
|
|
19
|
+
"being",
|
|
20
|
+
"have",
|
|
21
|
+
"has",
|
|
22
|
+
"had",
|
|
23
|
+
"do",
|
|
24
|
+
"does",
|
|
25
|
+
"did",
|
|
26
|
+
"will",
|
|
27
|
+
"would",
|
|
28
|
+
"could",
|
|
29
|
+
"should",
|
|
30
|
+
"may",
|
|
31
|
+
"might",
|
|
32
|
+
"shall",
|
|
33
|
+
"can",
|
|
34
|
+
"not",
|
|
35
|
+
]);
|
|
3
36
|
// implements REQ-mcp-search-discovery, REQ-002, REQ-003
|
|
4
37
|
export async function rankEntities(entities, query, workspaceRoot) {
|
|
38
|
+
const queryContext = buildSearchQueryContext(query);
|
|
39
|
+
if (!queryContext.rawTrimmedQuery || queryContext.signalTokens.length === 0) {
|
|
40
|
+
return [];
|
|
41
|
+
}
|
|
5
42
|
const matches = [];
|
|
6
43
|
for (const entity of entities) {
|
|
7
|
-
const match = await rankEntity(entity,
|
|
44
|
+
const match = await rankEntity(entity, queryContext, workspaceRoot);
|
|
8
45
|
if (match) {
|
|
9
46
|
matches.push(match);
|
|
10
47
|
}
|
|
@@ -22,9 +59,8 @@ export async function rankEntities(entities, query, workspaceRoot) {
|
|
|
22
59
|
});
|
|
23
60
|
return matches;
|
|
24
61
|
}
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
const tokens = normalizedQuery.split(/\s+/).filter(Boolean);
|
|
62
|
+
// implements REQ-mcp-search-discovery
|
|
63
|
+
async function rankEntity(entity, queryContext, workspaceRoot) {
|
|
28
64
|
const reasons = [];
|
|
29
65
|
let score = 0;
|
|
30
66
|
const id = String(entity.id ?? "");
|
|
@@ -37,55 +73,56 @@ async function rankEntity(entity, query, workspaceRoot) {
|
|
|
37
73
|
const tags = Array.isArray(entity.tags)
|
|
38
74
|
? entity.tags.map((tag) => String(tag))
|
|
39
75
|
: [];
|
|
40
|
-
const
|
|
41
|
-
const
|
|
42
|
-
if (
|
|
76
|
+
const titleForms = buildSearchTextForms(title);
|
|
77
|
+
const idForms = buildSearchTextForms(id);
|
|
78
|
+
if (isExactSearchMatch(titleForms, queryContext.phrase)) {
|
|
43
79
|
score += 100;
|
|
44
80
|
reasons.push("exact title match");
|
|
45
81
|
}
|
|
46
|
-
else if (
|
|
82
|
+
else if (isPhraseSearchMatch(titleForms, queryContext.phrase)) {
|
|
47
83
|
score += 60;
|
|
48
84
|
reasons.push("title phrase match");
|
|
49
85
|
}
|
|
50
|
-
if (
|
|
86
|
+
if (isExactSearchMatch(idForms, queryContext.phrase)) {
|
|
51
87
|
score += 90;
|
|
52
88
|
reasons.push("exact ID match");
|
|
53
89
|
}
|
|
54
|
-
else if (
|
|
90
|
+
else if (isPhraseSearchMatch(idForms, queryContext.phrase)) {
|
|
55
91
|
score += 55;
|
|
56
92
|
reasons.push("ID match");
|
|
57
93
|
}
|
|
58
94
|
const metadataFields = [type, source, owner, priority, severity];
|
|
59
|
-
const metadataMatched = metadataFields.some((field) =>
|
|
95
|
+
const metadataMatched = metadataFields.some((field) => isPhraseSearchMatch(buildSearchTextForms(field), queryContext.phrase));
|
|
60
96
|
if (metadataMatched) {
|
|
61
97
|
score += 20;
|
|
62
98
|
reasons.push("metadata match");
|
|
63
99
|
}
|
|
64
|
-
const matchingTags = tags.filter((tag) =>
|
|
100
|
+
const matchingTags = tags.filter((tag) => isPhraseSearchMatch(buildSearchTextForms(tag), queryContext.phrase));
|
|
65
101
|
if (matchingTags.length > 0) {
|
|
66
102
|
score += 30;
|
|
67
103
|
reasons.push("tag match");
|
|
68
104
|
}
|
|
69
|
-
const titleTokenMatches = countTokenMatches(
|
|
105
|
+
const titleTokenMatches = countTokenMatches(titleForms, queryContext.signalTokens);
|
|
70
106
|
if (titleTokenMatches > 0) {
|
|
71
107
|
score += titleTokenMatches * 8;
|
|
72
108
|
reasons.push("title token coverage");
|
|
73
109
|
}
|
|
74
|
-
const bodyText = await loadMarkdownBody(source, workspaceRoot)
|
|
110
|
+
const bodyText = (await loadMarkdownBody(source, workspaceRoot)) ??
|
|
111
|
+
getInlineBodyText(entity);
|
|
75
112
|
let snippet;
|
|
76
113
|
if (bodyText) {
|
|
77
|
-
const
|
|
78
|
-
if (
|
|
114
|
+
const bodyForms = buildSearchTextForms(bodyText);
|
|
115
|
+
if (isPhraseSearchMatch(bodyForms, queryContext.phrase)) {
|
|
79
116
|
score += 15;
|
|
80
117
|
reasons.push("markdown body match");
|
|
81
|
-
snippet = buildSnippet(bodyText,
|
|
118
|
+
snippet = buildSnippet(bodyText, queryContext.phrase);
|
|
82
119
|
}
|
|
83
120
|
else {
|
|
84
|
-
const bodyTokenMatches = countTokenMatches(
|
|
121
|
+
const bodyTokenMatches = countTokenMatches(bodyForms, queryContext.signalTokens);
|
|
85
122
|
if (bodyTokenMatches > 0) {
|
|
86
123
|
score += bodyTokenMatches * 3;
|
|
87
124
|
reasons.push("markdown body token coverage");
|
|
88
|
-
snippet = buildSnippet(bodyText,
|
|
125
|
+
snippet = buildSnippet(bodyText, queryContext.phrase);
|
|
89
126
|
}
|
|
90
127
|
}
|
|
91
128
|
}
|
|
@@ -126,6 +163,7 @@ source, workspaceRoot) {
|
|
|
126
163
|
return null;
|
|
127
164
|
}
|
|
128
165
|
}
|
|
166
|
+
// implements REQ-mcp-search-discovery
|
|
129
167
|
function stripFrontmatter(content) {
|
|
130
168
|
const trimmedContent = content.trimStart();
|
|
131
169
|
if (!trimmedContent.startsWith("---")) {
|
|
@@ -143,19 +181,88 @@ function stripFrontmatter(content) {
|
|
|
143
181
|
}
|
|
144
182
|
return trimmedContent.slice(match.index + match[0].length);
|
|
145
183
|
}
|
|
146
|
-
|
|
147
|
-
|
|
184
|
+
// implements REQ-mcp-search-discovery
|
|
185
|
+
function buildSearchQueryContext(query) {
|
|
186
|
+
return {
|
|
187
|
+
phrase: buildSearchTextForms(query),
|
|
188
|
+
signalTokens: tokenizeSignalTerms(query),
|
|
189
|
+
rawTrimmedQuery: query.trim(),
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
// implements REQ-mcp-search-discovery
|
|
193
|
+
function buildSearchTextForms(value) {
|
|
194
|
+
const normalized = normalizeSearchText(value);
|
|
195
|
+
return {
|
|
196
|
+
normalized,
|
|
197
|
+
compact: normalized.replace(/\s+/g, ""),
|
|
198
|
+
};
|
|
199
|
+
}
|
|
200
|
+
// implements REQ-mcp-search-discovery
|
|
201
|
+
function normalizeSearchText(value) {
|
|
202
|
+
return value
|
|
203
|
+
.trim()
|
|
204
|
+
.toLowerCase()
|
|
205
|
+
.replace(/[-_]+/g, " ")
|
|
206
|
+
.replace(/[^a-z0-9\s]+/g, " ")
|
|
207
|
+
.trim()
|
|
208
|
+
.split(/\s+/)
|
|
209
|
+
.filter(Boolean)
|
|
210
|
+
.map(singularizeSimplePlural)
|
|
211
|
+
.join(" ");
|
|
212
|
+
}
|
|
213
|
+
// implements REQ-mcp-search-discovery
|
|
214
|
+
function tokenizeSignalTerms(value) {
|
|
215
|
+
return Array.from(new Set(normalizeSearchText(value)
|
|
216
|
+
.split(/\s+/)
|
|
217
|
+
.filter((token) => token && !SEARCH_STOP_WORDS.has(token))));
|
|
148
218
|
}
|
|
219
|
+
// implements REQ-mcp-search-discovery
|
|
220
|
+
function singularizeSimplePlural(token) {
|
|
221
|
+
if (token.length <= 4 ||
|
|
222
|
+
!token.endsWith("s") ||
|
|
223
|
+
token.endsWith("ss") ||
|
|
224
|
+
token.endsWith("us") ||
|
|
225
|
+
token.endsWith("is")) {
|
|
226
|
+
return token;
|
|
227
|
+
}
|
|
228
|
+
return token.slice(0, -1);
|
|
229
|
+
}
|
|
230
|
+
// implements REQ-mcp-search-discovery
|
|
231
|
+
function isExactSearchMatch(haystack, needle) {
|
|
232
|
+
return (haystack.normalized === needle.normalized ||
|
|
233
|
+
(needle.compact !== "" && haystack.compact === needle.compact));
|
|
234
|
+
}
|
|
235
|
+
// implements REQ-mcp-search-discovery
|
|
236
|
+
function isPhraseSearchMatch(haystack, needle) {
|
|
237
|
+
return (haystack.normalized.includes(needle.normalized) ||
|
|
238
|
+
(needle.compact !== "" && haystack.compact.includes(needle.compact)));
|
|
239
|
+
}
|
|
240
|
+
// implements REQ-mcp-search-discovery
|
|
149
241
|
function countTokenMatches(haystack, tokens) {
|
|
150
|
-
return tokens.filter((token) => haystack.includes(token)).length;
|
|
242
|
+
return tokens.filter((token) => haystack.normalized.includes(token) || haystack.compact.includes(token)).length;
|
|
243
|
+
}
|
|
244
|
+
// implements REQ-mcp-search-discovery
|
|
245
|
+
function getInlineBodyText(entity) {
|
|
246
|
+
const candidates = [
|
|
247
|
+
entity.body,
|
|
248
|
+
entity.markdownBody,
|
|
249
|
+
entity.markdown_body,
|
|
250
|
+
entity.content,
|
|
251
|
+
];
|
|
252
|
+
for (const candidate of candidates) {
|
|
253
|
+
if (typeof candidate === "string" && candidate.trim() !== "") {
|
|
254
|
+
return candidate;
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
return null;
|
|
151
258
|
}
|
|
152
|
-
|
|
259
|
+
// implements REQ-mcp-search-discovery
|
|
260
|
+
function buildSnippet(bodyText, queryForms) {
|
|
153
261
|
const lines = bodyText
|
|
154
262
|
.split(/\r?\n/)
|
|
155
263
|
.map((line) => line.trim())
|
|
156
264
|
.filter(Boolean);
|
|
157
|
-
const
|
|
158
|
-
const matchedLine = lines.find((line) => normalize(line).includes(normalizedQuery)) ?? lines[0];
|
|
265
|
+
const matchedLine = lines.find((line) => isPhraseSearchMatch(buildSearchTextForms(line), queryForms)) ?? lines[0];
|
|
159
266
|
if (!matchedLine) {
|
|
160
267
|
return undefined;
|
|
161
268
|
}
|
package/dist/utils/config.d.ts
CHANGED
|
@@ -12,12 +12,30 @@ export interface KbConfigPaths {
|
|
|
12
12
|
facts?: string;
|
|
13
13
|
symbols?: string;
|
|
14
14
|
}
|
|
15
|
+
export interface BriefsConfig {
|
|
16
|
+
enabled: boolean;
|
|
17
|
+
retention?: {
|
|
18
|
+
maxPerBranch?: number;
|
|
19
|
+
maxAgeDays?: number;
|
|
20
|
+
keepUnread?: boolean;
|
|
21
|
+
};
|
|
22
|
+
channels: {
|
|
23
|
+
vscode: boolean;
|
|
24
|
+
tui: boolean;
|
|
25
|
+
};
|
|
26
|
+
tui: {
|
|
27
|
+
toast: boolean;
|
|
28
|
+
appendPrompt: boolean;
|
|
29
|
+
idleDelayMs?: number;
|
|
30
|
+
};
|
|
31
|
+
}
|
|
15
32
|
/**
|
|
16
33
|
* Shared configuration for Kibi.
|
|
17
34
|
* Stored in .kb/config.json
|
|
18
35
|
*/
|
|
19
36
|
export interface KbConfig {
|
|
20
37
|
paths: KbConfigPaths;
|
|
38
|
+
briefs?: BriefsConfig;
|
|
21
39
|
/**
|
|
22
40
|
* @deprecated defaultBranch is deprecated. Branch lifecycle now follows git naturally
|
|
23
41
|
* without requiring a configured default. This field is ignored but kept for compatibility.
|
|
@@ -26,9 +44,6 @@ export interface KbConfig {
|
|
|
26
44
|
checks?: ChecksConfig;
|
|
27
45
|
}
|
|
28
46
|
export type { ChecksConfig, SymbolTraceabilityOptions };
|
|
29
|
-
/**
|
|
30
|
-
* Default configuration values for new repositories.
|
|
31
|
-
*/
|
|
32
47
|
export declare const DEFAULT_CONFIG: KbConfig & {
|
|
33
48
|
$schema: string;
|
|
34
49
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/utils/config.ts"],"names":[],"mappings":"AAoBA,OAAO,EACL,KAAK,YAAY,EAEjB,KAAK,yBAAyB,EAC/B,MAAM,oBAAoB,CAAC;AAE5B;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED;;;GAGG;AACH,MAAM,WAAW,QAAQ;IACvB,KAAK,EAAE,aAAa,CAAC;IACrB;;;OAGG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,MAAM,CAAC,EAAE,YAAY,CAAC;CACvB;AAED,YAAY,EAAE,YAAY,EAAE,yBAAyB,EAAE,CAAC;
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/utils/config.ts"],"names":[],"mappings":"AAoBA,OAAO,EACL,KAAK,YAAY,EAEjB,KAAK,yBAAyB,EAC/B,MAAM,oBAAoB,CAAC;AAE5B;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,OAAO,CAAC;IACjB,SAAS,CAAC,EAAE;QACV,YAAY,CAAC,EAAE,MAAM,CAAC;QACtB,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,UAAU,CAAC,EAAE,OAAO,CAAC;KACtB,CAAC;IACF,QAAQ,EAAE;QACR,MAAM,EAAE,OAAO,CAAC;QAChB,GAAG,EAAE,OAAO,CAAC;KACd,CAAC;IACF,GAAG,EAAE;QACH,KAAK,EAAE,OAAO,CAAC;QACf,YAAY,EAAE,OAAO,CAAC;QACtB,WAAW,CAAC,EAAE,MAAM,CAAC;KACtB,CAAC;CACH;AAED;;;GAGG;AACH,MAAM,WAAW,QAAQ;IACvB,KAAK,EAAE,aAAa,CAAC;IACrB,MAAM,CAAC,EAAE,YAAY,CAAC;IACtB;;;OAGG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,MAAM,CAAC,EAAE,YAAY,CAAC;CACvB;AAED,YAAY,EAAE,YAAY,EAAE,yBAAyB,EAAE,CAAC;AAwBxD,eAAO,MAAM,cAAc,EAAE,QAAQ,GAAG;IAAE,OAAO,EAAE,MAAM,CAAA;CAexD,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,kBAAkB,EAAE,aAShC,CAAC;AAqBF;;;;;;GAMG;AACH,wBAAgB,UAAU,CAAC,GAAG,GAAE,MAAsB,GAAG,QAAQ,CAqChE;AAED;;;;;;;GAOG;AACH,wBAAgB,cAAc,CAAC,GAAG,GAAE,MAAsB,GAAG,QAAQ,CAqCpE"}
|
package/dist/utils/config.js
CHANGED
|
@@ -21,6 +21,24 @@ import { DEFAULT_CHECKS_CONFIG, } from "./rule-registry.js";
|
|
|
21
21
|
/**
|
|
22
22
|
* Default configuration values for new repositories.
|
|
23
23
|
*/
|
|
24
|
+
const DEFAULT_BRIEFS_CONFIG = {
|
|
25
|
+
enabled: true,
|
|
26
|
+
retention: {
|
|
27
|
+
maxPerBranch: 200,
|
|
28
|
+
maxAgeDays: 14,
|
|
29
|
+
keepUnread: true,
|
|
30
|
+
},
|
|
31
|
+
channels: {
|
|
32
|
+
vscode: true,
|
|
33
|
+
tui: true,
|
|
34
|
+
},
|
|
35
|
+
tui: {
|
|
36
|
+
toast: true,
|
|
37
|
+
appendPrompt: true,
|
|
38
|
+
idleDelayMs: 1500,
|
|
39
|
+
},
|
|
40
|
+
};
|
|
41
|
+
// implements REQ-003
|
|
24
42
|
export const DEFAULT_CONFIG = {
|
|
25
43
|
$schema: "https://raw.githubusercontent.com/Looted/kibi/master/packages/cli/schema/config.json",
|
|
26
44
|
paths: {
|
|
@@ -33,6 +51,7 @@ export const DEFAULT_CONFIG = {
|
|
|
33
51
|
facts: "documentation/facts",
|
|
34
52
|
symbols: "documentation/symbols.yaml",
|
|
35
53
|
},
|
|
54
|
+
briefs: DEFAULT_BRIEFS_CONFIG,
|
|
36
55
|
checks: DEFAULT_CHECKS_CONFIG,
|
|
37
56
|
};
|
|
38
57
|
/**
|
|
@@ -48,6 +67,24 @@ export const DEFAULT_SYNC_PATHS = {
|
|
|
48
67
|
facts: "facts/**/*.md",
|
|
49
68
|
symbols: "symbols.yaml",
|
|
50
69
|
};
|
|
70
|
+
function mergeBriefsConfig(userBriefs) {
|
|
71
|
+
return {
|
|
72
|
+
...DEFAULT_BRIEFS_CONFIG,
|
|
73
|
+
...userBriefs,
|
|
74
|
+
channels: {
|
|
75
|
+
...DEFAULT_BRIEFS_CONFIG.channels,
|
|
76
|
+
...userBriefs?.channels,
|
|
77
|
+
},
|
|
78
|
+
tui: {
|
|
79
|
+
...DEFAULT_BRIEFS_CONFIG.tui,
|
|
80
|
+
...userBriefs?.tui,
|
|
81
|
+
},
|
|
82
|
+
retention: {
|
|
83
|
+
...DEFAULT_BRIEFS_CONFIG.retention,
|
|
84
|
+
...userBriefs?.retention,
|
|
85
|
+
},
|
|
86
|
+
};
|
|
87
|
+
}
|
|
51
88
|
/**
|
|
52
89
|
* Load and parse the Kibi configuration from .kb/config.json.
|
|
53
90
|
* Falls back to DEFAULT_CONFIG if the file doesn't exist or is invalid.
|
|
@@ -74,6 +111,7 @@ export function loadConfig(cwd = process.cwd()) {
|
|
|
74
111
|
...DEFAULT_CONFIG.paths,
|
|
75
112
|
...userConfig.paths,
|
|
76
113
|
},
|
|
114
|
+
briefs: mergeBriefsConfig(userConfig.briefs),
|
|
77
115
|
...(userConfig.defaultBranch !== undefined
|
|
78
116
|
? { defaultBranch: userConfig.defaultBranch }
|
|
79
117
|
: {}),
|
|
@@ -118,6 +156,7 @@ export function loadSyncConfig(cwd = process.cwd()) {
|
|
|
118
156
|
...DEFAULT_SYNC_PATHS,
|
|
119
157
|
...userConfig.paths,
|
|
120
158
|
},
|
|
159
|
+
briefs: mergeBriefsConfig(userConfig.briefs),
|
|
121
160
|
...(userConfig.defaultBranch !== undefined
|
|
122
161
|
? { defaultBranch: userConfig.defaultBranch }
|
|
123
162
|
: {}),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rule-registry.d.ts","sourceRoot":"","sources":["../../src/utils/rule-registry.ts"],"names":[],"mappings":"AAkBA;;;GAGG;AAEH,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,cAAc,EAAE,OAAO,CAAC;IACxB,QAAQ,EAAE,UAAU,GAAG,WAAW,GAAG,WAAW,GAAG,cAAc,CAAC;CACnE;AAED,MAAM,WAAW,yBAAyB;IACxC,UAAU,EAAE,OAAO,CAAC;CACrB;AAED,mCAAmC;AACnC,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC/B,kBAAkB,EAAE,yBAAyB,CAAC;CAC/C;AAED;;;GAGG;AACH,eAAO,MAAM,KAAK,EAAE,SAAS,cAAc,
|
|
1
|
+
{"version":3,"file":"rule-registry.d.ts","sourceRoot":"","sources":["../../src/utils/rule-registry.ts"],"names":[],"mappings":"AAkBA;;;GAGG;AAEH,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,cAAc,EAAE,OAAO,CAAC;IACxB,QAAQ,EAAE,UAAU,GAAG,WAAW,GAAG,WAAW,GAAG,cAAc,CAAC;CACnE;AAED,MAAM,WAAW,yBAAyB;IACxC,UAAU,EAAE,OAAO,CAAC;CACrB;AAED,mCAAmC;AACnC,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC/B,kBAAkB,EAAE,yBAAyB,CAAC;CAC/C;AAED;;;GAGG;AACH,eAAO,MAAM,KAAK,EAAE,SAAS,cAAc,EAqEjC,CAAC;AAEX;;GAEG;AACH,eAAO,MAAM,UAAU,aAAoC,CAAC;AAE5D;;GAEG;AACH,eAAO,MAAM,qBAAqB,EAAE,YAKnC,CAAC;AAEF;;;;;GAKG;AAEH,wBAAgB,iBAAiB,CAC/B,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACrC,QAAQ,CAAC,EAAE,MAAM,GAChB,GAAG,CAAC,MAAM,CAAC,CAsBb;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAK5D;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAC/B,OAAO,CAAC,EAAE,OAAO,CAAC,YAAY,CAAC,GAC9B,YAAY,CAWd"}
|
|
@@ -75,6 +75,12 @@ export const RULES = [
|
|
|
75
75
|
defaultEnabled: false,
|
|
76
76
|
category: "integrity",
|
|
77
77
|
},
|
|
78
|
+
{
|
|
79
|
+
name: "strict-req-fact-pairing",
|
|
80
|
+
description: "Detect requirements with incomplete strict subject/property fact pairing for contradiction-safe semantics",
|
|
81
|
+
defaultEnabled: false,
|
|
82
|
+
category: "integrity",
|
|
83
|
+
},
|
|
78
84
|
];
|
|
79
85
|
/**
|
|
80
86
|
* Set of all rule names for quick lookups.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "kibi-cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.8.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Kibi CLI for knowledge base management",
|
|
6
6
|
"engines": {
|
|
@@ -74,6 +74,14 @@
|
|
|
74
74
|
"./public/check-types": {
|
|
75
75
|
"types": "./dist/public/check-types.d.ts",
|
|
76
76
|
"default": "./dist/public/check-types.js"
|
|
77
|
+
},
|
|
78
|
+
"./brief-config": {
|
|
79
|
+
"types": "./dist/public/brief-config.d.ts",
|
|
80
|
+
"default": "./dist/public/brief-config.js"
|
|
81
|
+
},
|
|
82
|
+
"./operational-artifacts": {
|
|
83
|
+
"types": "./dist/public/operational-artifacts.d.ts",
|
|
84
|
+
"default": "./dist/public/operational-artifacts.js"
|
|
77
85
|
}
|
|
78
86
|
},
|
|
79
87
|
"types": "./dist/cli.d.ts",
|
|
@@ -84,7 +92,7 @@
|
|
|
84
92
|
"fast-glob": "^3.2.12",
|
|
85
93
|
"gray-matter": "^4.0.3",
|
|
86
94
|
"js-yaml": "^4.1.0",
|
|
87
|
-
"kibi-core": "^0.5.
|
|
95
|
+
"kibi-core": "^0.5.2",
|
|
88
96
|
"ts-morph": "^23.0.0"
|
|
89
97
|
},
|
|
90
98
|
"devDependencies": {
|