@hasna/hooks 0.3.2 → 0.3.3
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/bin/index.js +1 -1
- package/dist/index.js +1 -1
- package/hooks/hook-knowledge-context/README.md +4 -1
- package/hooks/hook-knowledge-context/package.json +1 -1
- package/hooks/hook-knowledge-context/src/hook.test.ts +17 -0
- package/hooks/hook-knowledge-context/src/hook.ts +49 -5
- package/package.json +1 -1
package/bin/index.js
CHANGED
|
@@ -2089,7 +2089,7 @@ var init_registry = __esm(() => {
|
|
|
2089
2089
|
name: "knowledge-context",
|
|
2090
2090
|
displayName: "Knowledge Context",
|
|
2091
2091
|
description: "Injects deterministic Knowledge context packs into Codewith SessionStart, UserPromptSubmit, and SubagentStart",
|
|
2092
|
-
version: "0.1.
|
|
2092
|
+
version: "0.1.2",
|
|
2093
2093
|
category: "Context Management",
|
|
2094
2094
|
event: "SessionStart",
|
|
2095
2095
|
events: ["SessionStart", "UserPromptSubmit", "SubagentStart"],
|
package/dist/index.js
CHANGED
|
@@ -1010,7 +1010,7 @@ var HOOKS = [
|
|
|
1010
1010
|
name: "knowledge-context",
|
|
1011
1011
|
displayName: "Knowledge Context",
|
|
1012
1012
|
description: "Injects deterministic Knowledge context packs into Codewith SessionStart, UserPromptSubmit, and SubagentStart",
|
|
1013
|
-
version: "0.1.
|
|
1013
|
+
version: "0.1.2",
|
|
1014
1014
|
category: "Context Management",
|
|
1015
1015
|
event: "SessionStart",
|
|
1016
1016
|
events: ["SessionStart", "UserPromptSubmit", "SubagentStart"],
|
|
@@ -29,7 +29,10 @@ The hook intentionally does not pass `--semantic`, does not call web search,
|
|
|
29
29
|
does not call ask/build/generate flows, and does not crawl raw stores directly.
|
|
30
30
|
When Knowledge returns a nonempty context pack, the hook redacts credential-like
|
|
31
31
|
text from that pack and emits Codewith-native
|
|
32
|
-
`hookSpecificOutput.additionalContext` with the same event name.
|
|
32
|
+
`hookSpecificOutput.additionalContext` with the same event name. Citation-only
|
|
33
|
+
packs are expanded into progressive context lines with a bounded preview and a
|
|
34
|
+
`knowledge get --id ... --json` read hint so agents can decide which full items
|
|
35
|
+
are worth opening.
|
|
33
36
|
|
|
34
37
|
All failures are fail-open: missing CLI, timeout, nonzero exit, malformed JSON,
|
|
35
38
|
bad stdin, or empty packs return `{"continue":true}` without context.
|
|
@@ -170,4 +170,21 @@ describe("hook-knowledge-context", () => {
|
|
|
170
170
|
expect(extractContextText({ pack: { context: "direct context" } })).toBe("direct context");
|
|
171
171
|
expect(extractContextText([{ title: "One", text: "Body", uri: "knowledge://item/one" }])).toContain("Body");
|
|
172
172
|
});
|
|
173
|
+
|
|
174
|
+
test("formats citation previews as useful progressive blurbs with read commands", () => {
|
|
175
|
+
const text = extractContextText({
|
|
176
|
+
citations: [
|
|
177
|
+
{
|
|
178
|
+
id: "cite_123",
|
|
179
|
+
source_ref: "knowledge://item/k_example",
|
|
180
|
+
quote_preview: "A short preview that tells the agent why this Knowledge item might matter.",
|
|
181
|
+
},
|
|
182
|
+
],
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
expect(text).toContain("Knowledge item k_example");
|
|
186
|
+
expect(text).toContain("cite_123");
|
|
187
|
+
expect(text).toContain("A short preview");
|
|
188
|
+
expect(text).toContain("read: knowledge get --id k_example --json");
|
|
189
|
+
});
|
|
173
190
|
});
|
|
@@ -227,6 +227,18 @@ function firstString(obj: Record<string, unknown>, keys: string[]): string | nul
|
|
|
227
227
|
return null;
|
|
228
228
|
}
|
|
229
229
|
|
|
230
|
+
function firstSourceString(obj: Record<string, unknown>): string | null {
|
|
231
|
+
const direct = firstString(obj, ["source_ref", "source_uri", "source", "uri", "ref", "url", "citation"]);
|
|
232
|
+
if (direct) return direct;
|
|
233
|
+
|
|
234
|
+
const source = obj.source;
|
|
235
|
+
if (source && typeof source === "object") {
|
|
236
|
+
return firstString(source as Record<string, unknown>, ["uri", "ref", "url", "id"]);
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
return null;
|
|
240
|
+
}
|
|
241
|
+
|
|
230
242
|
function firstArray(obj: Record<string, unknown>, keys: string[]): unknown[] | null {
|
|
231
243
|
for (const key of keys) {
|
|
232
244
|
const value = obj[key];
|
|
@@ -241,6 +253,15 @@ export function truncate(text: string, max: number): string {
|
|
|
241
253
|
return `${text.slice(0, max - 3)}...`;
|
|
242
254
|
}
|
|
243
255
|
|
|
256
|
+
function compactText(text: string): string {
|
|
257
|
+
return text.replace(/\s+/g, " ").trim();
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
function knowledgeItemId(source: string | null): string | null {
|
|
261
|
+
const match = source?.match(/^knowledge:\/\/item\/([^/?#\s]+)$/);
|
|
262
|
+
return match?.[1] ?? null;
|
|
263
|
+
}
|
|
264
|
+
|
|
244
265
|
function formatPackItems(items: unknown[]): string | null {
|
|
245
266
|
const lines: string[] = [];
|
|
246
267
|
|
|
@@ -252,12 +273,35 @@ function formatPackItems(items: unknown[]): string | null {
|
|
|
252
273
|
if (!item || typeof item !== "object") continue;
|
|
253
274
|
|
|
254
275
|
const obj = item as Record<string, unknown>;
|
|
255
|
-
const
|
|
256
|
-
const
|
|
257
|
-
const
|
|
276
|
+
const citationId = firstString(obj, ["id", "citation_id", "citationId"]);
|
|
277
|
+
const source = firstSourceString(obj);
|
|
278
|
+
const itemId = knowledgeItemId(source);
|
|
279
|
+
const title =
|
|
280
|
+
firstString(obj, ["title", "name"]) ||
|
|
281
|
+
(itemId ? `Knowledge item ${itemId}` : citationId || `item ${index + 1}`);
|
|
282
|
+
const body = firstString(obj, [
|
|
283
|
+
"summary",
|
|
284
|
+
"text",
|
|
285
|
+
"content",
|
|
286
|
+
"snippet",
|
|
287
|
+
"preview",
|
|
288
|
+
"quote_preview",
|
|
289
|
+
"quotePreview",
|
|
290
|
+
"excerpt",
|
|
291
|
+
"body",
|
|
292
|
+
"description",
|
|
293
|
+
]);
|
|
258
294
|
if (!body && !source) continue;
|
|
259
|
-
|
|
260
|
-
|
|
295
|
+
|
|
296
|
+
const suffixParts = [
|
|
297
|
+
citationId && citationId !== title ? citationId : null,
|
|
298
|
+
source ? `source: ${truncate(source, 180)}` : null,
|
|
299
|
+
].filter(Boolean);
|
|
300
|
+
const suffix = suffixParts.length > 0 ? ` (${suffixParts.join("; ")})` : "";
|
|
301
|
+
lines.push(`- ${truncate(title, 160)}${suffix}${body ? `: ${truncate(compactText(body), 520)}` : ""}`);
|
|
302
|
+
if (itemId) {
|
|
303
|
+
lines.push(` read: knowledge get --id ${itemId} --json`);
|
|
304
|
+
}
|
|
261
305
|
}
|
|
262
306
|
|
|
263
307
|
return lines.length > 0 ? lines.join("\n") : null;
|
package/package.json
CHANGED