@hasna/hook-knowledge-context 0.1.5 → 0.1.6
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 +5 -2
- package/dist/hook.d.ts +6 -1
- package/dist/hook.js +37 -11
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -34,7 +34,9 @@ text from that pack and emits Codewith-native
|
|
|
34
34
|
`hookSpecificOutput.additionalContext` with the same event name. Citation-only
|
|
35
35
|
packs are expanded into progressive context lines with bounded previews and a
|
|
36
36
|
single `knowledge get --id <item_id> --json` hint so agents can decide which
|
|
37
|
-
full items are worth opening.
|
|
37
|
+
full items are worth opening. Items that mark themselves as
|
|
38
|
+
historical/reference-only or not suitable for auto-loading are filtered out by
|
|
39
|
+
default, because they are not useful ambient context for normal agent work.
|
|
38
40
|
|
|
39
41
|
Example injected context:
|
|
40
42
|
|
|
@@ -55,7 +57,8 @@ bad stdin, or empty packs return `{"continue":true}` without context.
|
|
|
55
57
|
export HOOKS_KNOWLEDGE_CONTEXT_DISABLE=1 # Kill switch
|
|
56
58
|
export HOOKS_KNOWLEDGE_COMMAND=knowledge # CLI command/path
|
|
57
59
|
export HOOKS_KNOWLEDGE_TIMEOUT_MS=5000 # Per-call timeout
|
|
58
|
-
export HOOKS_KNOWLEDGE_MAX_ITEMS=3 #
|
|
60
|
+
export HOOKS_KNOWLEDGE_MAX_ITEMS=3 # Rendered item budget
|
|
61
|
+
export HOOKS_KNOWLEDGE_CANDIDATE_ITEMS=12 # Internal candidate budget before filtering
|
|
59
62
|
export HOOKS_KNOWLEDGE_MAX_TOKENS=1200 # Context pack token budget
|
|
60
63
|
export HOOKS_KNOWLEDGE_REQUIRE_HIGH_SIGNAL=1
|
|
61
64
|
export HOOKS_KNOWLEDGE_MIN_PROMPT_CHARS=6
|
package/dist/hook.d.ts
CHANGED
|
@@ -31,6 +31,7 @@ export interface KnowledgeContextConfig {
|
|
|
31
31
|
command: string;
|
|
32
32
|
timeoutMs: number;
|
|
33
33
|
maxItems: number;
|
|
34
|
+
candidateItems: number;
|
|
34
35
|
maxTokens: number;
|
|
35
36
|
minPromptChars: number;
|
|
36
37
|
minSignalScore: number;
|
|
@@ -54,7 +55,11 @@ export declare function buildQuery(input: HookInput, config: KnowledgeContextCon
|
|
|
54
55
|
export declare function buildKnowledgeArgs(query: string, config: KnowledgeContextConfig): string[];
|
|
55
56
|
export declare function spawnKnowledgeContextPack(query: string, config: KnowledgeContextConfig): Promise<KnowledgeExecResult>;
|
|
56
57
|
export declare function truncate(text: string, max: number): string;
|
|
57
|
-
|
|
58
|
+
interface ExtractContextOptions {
|
|
59
|
+
maxItems?: number;
|
|
60
|
+
includeHistorical?: boolean;
|
|
61
|
+
}
|
|
62
|
+
export declare function extractContextText(pack: unknown, options?: ExtractContextOptions): string | null;
|
|
58
63
|
export declare function formatAdditionalContext(event: KnowledgeContextEvent, context: string, config: KnowledgeContextConfig): string;
|
|
59
64
|
export declare function buildHookOutput(input: HookInput | null, executor?: KnowledgeExecutor, env?: NodeJS.ProcessEnv): Promise<HookOutput>;
|
|
60
65
|
export declare function run(): Promise<void>;
|
package/dist/hook.js
CHANGED
|
@@ -41,10 +41,12 @@ function commandFromEnv(raw) {
|
|
|
41
41
|
return "knowledge";
|
|
42
42
|
}
|
|
43
43
|
function getConfig(env = process.env) {
|
|
44
|
+
const maxItems = boundedNumber(env.HOOKS_KNOWLEDGE_MAX_ITEMS, DEFAULT_MAX_ITEMS, 1, 20);
|
|
44
45
|
return {
|
|
45
46
|
command: commandFromEnv(env.HOOKS_KNOWLEDGE_COMMAND),
|
|
46
47
|
timeoutMs: boundedNumber(env.HOOKS_KNOWLEDGE_TIMEOUT_MS, DEFAULT_TIMEOUT_MS, 100, 1e4),
|
|
47
|
-
maxItems
|
|
48
|
+
maxItems,
|
|
49
|
+
candidateItems: boundedNumber(env.HOOKS_KNOWLEDGE_CANDIDATE_ITEMS, Math.min(20, Math.max(maxItems, maxItems * 4)), maxItems, 20),
|
|
48
50
|
maxTokens: boundedNumber(env.HOOKS_KNOWLEDGE_MAX_TOKENS, DEFAULT_MAX_TOKENS, 100, 8000),
|
|
49
51
|
minPromptChars: boundedNumber(env.HOOKS_KNOWLEDGE_MIN_PROMPT_CHARS, DEFAULT_MIN_PROMPT_CHARS, 0, 4000),
|
|
50
52
|
minSignalScore: boundedNumber(env.HOOKS_KNOWLEDGE_MIN_SIGNAL_SCORE, DEFAULT_MIN_SIGNAL_SCORE, 1, 10),
|
|
@@ -147,7 +149,7 @@ function buildKnowledgeArgs(query, config) {
|
|
|
147
149
|
"--from",
|
|
148
150
|
"search",
|
|
149
151
|
"--max-items",
|
|
150
|
-
String(config.
|
|
152
|
+
String(config.candidateItems),
|
|
151
153
|
"--max-tokens",
|
|
152
154
|
String(config.maxTokens),
|
|
153
155
|
"--json"
|
|
@@ -232,13 +234,24 @@ function knowledgeItemId(source) {
|
|
|
232
234
|
const legacyPath = source?.match(/(?:^|\/)(k_[A-Za-z0-9]+_[A-Za-z0-9]+)(?:[?#].*)?$/);
|
|
233
235
|
return legacyPath?.[1] ?? null;
|
|
234
236
|
}
|
|
235
|
-
|
|
237
|
+
var NON_AUTOLOADABLE_HISTORY_RE = /\b(historical\/reference only|historical reference only|should not be auto-?loaded|do not auto-?load|not an active startup instruction|brain orchestration startup files)\b/i;
|
|
238
|
+
var ARCHIVED_STARTUP_RE = /^Archived on \d{4}-\d{2}-\d{2}\b.*\b(startup context|startup files|CLAUDE\.md)\b/i;
|
|
239
|
+
function shouldSuppressKnowledgeItem(text, options) {
|
|
240
|
+
if (options.includeHistorical)
|
|
241
|
+
return false;
|
|
242
|
+
const compact = compactText(text);
|
|
243
|
+
return NON_AUTOLOADABLE_HISTORY_RE.test(compact) || ARCHIVED_STARTUP_RE.test(compact);
|
|
244
|
+
}
|
|
245
|
+
function formatPackItems(items, options = {}) {
|
|
236
246
|
const lines = [];
|
|
237
247
|
let hasKnowledgeItems = false;
|
|
248
|
+
const maxItems = options.maxItems ?? DEFAULT_MAX_ITEMS;
|
|
238
249
|
for (const [index, item] of items.entries()) {
|
|
250
|
+
if (lines.length >= maxItems)
|
|
251
|
+
break;
|
|
239
252
|
if (typeof item === "string" && item.trim()) {
|
|
240
253
|
const text = cleanStringItem(item);
|
|
241
|
-
if (text)
|
|
254
|
+
if (text && !shouldSuppressKnowledgeItem(text, options))
|
|
242
255
|
lines.push(`- ${truncate(text, 900)}`);
|
|
243
256
|
continue;
|
|
244
257
|
}
|
|
@@ -263,6 +276,9 @@ function formatPackItems(items) {
|
|
|
263
276
|
]);
|
|
264
277
|
if (!body && !source)
|
|
265
278
|
continue;
|
|
279
|
+
const itemText = [title, body, source].filter(Boolean).join(" ");
|
|
280
|
+
if (shouldSuppressKnowledgeItem(itemText, options))
|
|
281
|
+
continue;
|
|
266
282
|
if (itemId) {
|
|
267
283
|
hasKnowledgeItems = true;
|
|
268
284
|
const labelParts = [`item_id=${itemId}`];
|
|
@@ -291,12 +307,19 @@ function formatPackItems(items) {
|
|
|
291
307
|
].join(`
|
|
292
308
|
`);
|
|
293
309
|
}
|
|
294
|
-
function
|
|
310
|
+
function shouldIncludeHistoricalItems(input) {
|
|
311
|
+
const prompt = `${stringField(input, "prompt")} ${stringField(input, "user_prompt")}`;
|
|
312
|
+
return /\b(archive|archived|historical|history|reference-only|reference only|old startup|startup files|CLAUDE\.md)\b/i.test(prompt);
|
|
313
|
+
}
|
|
314
|
+
function extractContextText(pack, options = {}) {
|
|
295
315
|
if (typeof pack === "string") {
|
|
296
|
-
|
|
316
|
+
const text = cleanLegacyEmptyBullet(pack.trim());
|
|
317
|
+
if (!text || shouldSuppressKnowledgeItem(text, options))
|
|
318
|
+
return null;
|
|
319
|
+
return text;
|
|
297
320
|
}
|
|
298
321
|
if (Array.isArray(pack)) {
|
|
299
|
-
return formatPackItems(pack);
|
|
322
|
+
return formatPackItems(pack, options);
|
|
300
323
|
}
|
|
301
324
|
if (!pack || typeof pack !== "object") {
|
|
302
325
|
return null;
|
|
@@ -304,15 +327,15 @@ function extractContextText(pack) {
|
|
|
304
327
|
const obj = pack;
|
|
305
328
|
const direct = firstString(obj, ["additionalContext", "context", "markdown", "content", "text", "summary"]);
|
|
306
329
|
if (direct)
|
|
307
|
-
return direct;
|
|
330
|
+
return shouldSuppressKnowledgeItem(direct, options) ? null : direct;
|
|
308
331
|
for (const key of ["pack", "contextPack", "context_pack", "data", "result"]) {
|
|
309
332
|
const nested = obj[key];
|
|
310
|
-
const text = extractContextText(nested);
|
|
333
|
+
const text = extractContextText(nested, options);
|
|
311
334
|
if (text)
|
|
312
335
|
return text;
|
|
313
336
|
}
|
|
314
337
|
const items = firstArray(obj, ["items", "results", "sources", "citations"]);
|
|
315
|
-
return items ? formatPackItems(items) : null;
|
|
338
|
+
return items ? formatPackItems(items, options) : null;
|
|
316
339
|
}
|
|
317
340
|
function formatAdditionalContext(event, context, config) {
|
|
318
341
|
const header = `[hook-knowledge-context] Knowledge matches (${event}; top ${config.maxItems}, deterministic search):`;
|
|
@@ -346,7 +369,10 @@ async function buildHookOutput(input, executor = spawnKnowledgeContextPack, env
|
|
|
346
369
|
return { continue: true };
|
|
347
370
|
}
|
|
348
371
|
const parsed = JSON.parse(result.stdout);
|
|
349
|
-
const context = extractContextText(parsed
|
|
372
|
+
const context = extractContextText(parsed, {
|
|
373
|
+
maxItems: config.maxItems,
|
|
374
|
+
includeHistorical: shouldIncludeHistoricalItems(input)
|
|
375
|
+
});
|
|
350
376
|
if (!context) {
|
|
351
377
|
return { continue: true };
|
|
352
378
|
}
|
package/package.json
CHANGED