kennel 0.2.31 → 0.2.33
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/cli/dispatch.js +10 -0
- package/dist/cli/usage.js +9 -0
- package/dist/core/runtime.js +531 -197
- package/dist/llm/clipboard.d.ts +1 -0
- package/dist/llm/clipboard.js +59 -0
- package/dist/llm/command.d.ts +1 -0
- package/dist/llm/command.js +226 -0
- package/dist/llm/worker.d.ts +19 -0
- package/dist/llm/worker.js +1131 -0
- package/dist/notes/command.d.ts +1 -0
- package/dist/notes/command.js +556 -0
- package/dist/package/preview.js +554 -219
- package/dist/plugins/external.d.ts +6 -0
- package/dist/plugins/external.js +16 -0
- package/dist/site/server-plugins.d.ts +12 -0
- package/dist/site/server-plugins.js +132 -11
- package/package.json +4 -2
- package/schemas/config.schema.json +11 -0
- package/templates/plugin/package.json +1 -0
- package/templates/plugin/server.ts +43 -0
- package/templates/plugin/tsconfig.json +1 -1
- package/web/assets/{abnfDiagram-VRR7QNED-DPZyna8O.js → abnfDiagram-VRR7QNED-CpP1VHKI.js} +1 -1
- package/web/assets/{arc-BAcU9y_V.js → arc-kL4AxfvR.js} +1 -1
- package/web/assets/{architectureDiagram-ZJ3FMSHR-D5yd7Cag.js → architectureDiagram-ZJ3FMSHR-BWaRktRJ.js} +1 -1
- package/web/assets/{ask-tray-PMT6VVXN-C4dJH21Q.js → ask-tray-PMT6VVXN-FqPDpwtw.js} +3 -3
- package/web/assets/{blockDiagram-677ZJIJ3-C7LSGyxV.js → blockDiagram-677ZJIJ3-B07uVoPU.js} +1 -1
- package/web/assets/{c4Diagram-LMCZKHZV-BW2KfoQr.js → c4Diagram-LMCZKHZV-uvoaQC09.js} +1 -1
- package/web/assets/channel-Djmf1Ik1.js +1 -0
- package/web/assets/{chunk-2Q5K7J3B-4Og6VAFG.js → chunk-2Q5K7J3B-BH3SaHqk.js} +1 -1
- package/web/assets/{chunk-32BRIVSS-BYOowQl5.js → chunk-32BRIVSS-SPD8Eraw.js} +1 -1
- package/web/assets/{chunk-5VM5RSS4-C1m-lq64.js → chunk-5VM5RSS4-DNeOgOuZ.js} +1 -1
- package/web/assets/{chunk-EX3LRPZG-B2hriRpJ.js → chunk-EX3LRPZG-SJQ-vKGz.js} +1 -1
- package/web/assets/{chunk-JWPE2WC7-C6RsnzVA.js → chunk-JWPE2WC7-Dy_9wZ2Y.js} +1 -1
- package/web/assets/{chunk-MOJQB5TN-yKbMmUWf.js → chunk-MOJQB5TN-CuIx80Vn.js} +1 -1
- package/web/assets/{chunk-RYQCIY6F-BOokm3Qc.js → chunk-RYQCIY6F-DDG61RKR.js} +1 -1
- package/web/assets/{chunk-V7JOEXUC-CKW-H5An.js → chunk-V7JOEXUC-7OMVhaoH.js} +1 -1
- package/web/assets/{chunk-VR4S4FIN-BU5pFrup.js → chunk-VR4S4FIN-Y-HNaSmV.js} +1 -1
- package/web/assets/{chunk-XXDRQBXY-DYtdSJHU.js → chunk-XXDRQBXY-CtG0MPgC.js} +1 -1
- package/web/assets/classDiagram-OUVF2IWQ-hlDS9RWf.js +1 -0
- package/web/assets/classDiagram-v2-EOCWNBFH-hlDS9RWf.js +1 -0
- package/web/assets/{cose-bilkent-JH36ORCC-BZeXtC6E.js → cose-bilkent-JH36ORCC-BZaPMYcl.js} +1 -1
- package/web/assets/{cynefin-VYW2F7L2-BSDVyzMI.js → cynefin-VYW2F7L2-DxAlRHA1.js} +1 -1
- package/web/assets/{cynefinDiagram-TSTJHNR4-CReoDBYo.js → cynefinDiagram-TSTJHNR4-gjgzmagS.js} +1 -1
- package/web/assets/{dagre-VKFMJZFB-CBLLzMxd.js → dagre-VKFMJZFB-DvLPeYuw.js} +1 -1
- package/web/assets/{diagram-FQU43EPY-BcGWiZXZ.js → diagram-FQU43EPY-443LFnzG.js} +1 -1
- package/web/assets/{diagram-G47NLZAW-BXUYZA9N.js → diagram-G47NLZAW-CqfUewot.js} +1 -1
- package/web/assets/{diagram-NH7WQ7WH-BrvrZs7N.js → diagram-NH7WQ7WH-D-FElUGW.js} +1 -1
- package/web/assets/{diagram-OA4YK3LP-7lnVcjXL.js → diagram-OA4YK3LP-DbidUAp5.js} +1 -1
- package/web/assets/{diagram-WEI45ONY-s58_bMFf.js → diagram-WEI45ONY-CPLeckCZ.js} +1 -1
- package/web/assets/{ebnfDiagram-CCIWWBDH-BemWecoD.js → ebnfDiagram-CCIWWBDH-DR0rqZzN.js} +1 -1
- package/web/assets/{entity-search-page-G6QEQNXT--sKP5dP3.js → entity-search-page-G6QEQNXT-CnG8d-at.js} +1 -1
- package/web/assets/{erDiagram-Q63AITRT-ClVJddch.js → erDiagram-Q63AITRT-DMXv_8Tj.js} +1 -1
- package/web/assets/{flowDiagram-23GEKE2U-xzUJu-i_.js → flowDiagram-23GEKE2U-DnbQjZs0.js} +1 -1
- package/web/assets/{ganttDiagram-NO4QXBWP-CXQmtF5g.js → ganttDiagram-NO4QXBWP-DBKIrjj8.js} +1 -1
- package/web/assets/{gitGraphDiagram-IHSO6WYX-DCdljcLl.js → gitGraphDiagram-IHSO6WYX-BqqxKWT_.js} +1 -1
- package/web/assets/{index-CtTx1o2d.js → index-Bd3ghcW-.js} +5 -5
- package/web/assets/{index-vjrhIAJn.css → index-Dt8OzQCe.css} +1 -1
- package/web/assets/{infoDiagram-FWYZ7A6U-B11SJYSK.js → infoDiagram-FWYZ7A6U-Ba6Fs17y.js} +1 -1
- package/web/assets/{ishikawaDiagram-FXEZZL3T-B2CCGlAm.js → ishikawaDiagram-FXEZZL3T-D3BOtFjy.js} +1 -1
- package/web/assets/{journeyDiagram-5HDEW3XC-CBPETzOk.js → journeyDiagram-5HDEW3XC-MbzFPQm8.js} +1 -1
- package/web/assets/{kanban-definition-HUTT4EX6-unhQ3RPE.js → kanban-definition-HUTT4EX6-CurBmwu1.js} +1 -1
- package/web/assets/{linear-DuP_pG51.js → linear-CQIr4JjI.js} +1 -1
- package/web/assets/{linked-session-header-M7WY6IGF-DwlNCJlj.js → linked-session-header-M7WY6IGF-BBgN8zTq.js} +1 -1
- package/web/assets/{mermaid.core-BlYNzkpn.js → mermaid.core-bE6Vl3bZ.js} +4 -4
- package/web/assets/{mindmap-definition-LN4V7U3C-CtKHN6O1.js → mindmap-definition-LN4V7U3C-C1a46OGU.js} +1 -1
- package/web/assets/{pegDiagram-2B236MQR-CRyz6XWY.js → pegDiagram-2B236MQR-DZxMOxek.js} +1 -1
- package/web/assets/{pieDiagram-ENE6RG2P-BXfkPm3Q.js → pieDiagram-ENE6RG2P-BN0XOKLi.js} +1 -1
- package/web/assets/{plugin-host-modules-5NQgs5Cq.js → plugin-host-modules-CaRD5_5q.js} +1 -1
- package/web/assets/{project-search-dialog-HJFGW4DG-DPly4OiB.js → project-search-dialog-HJFGW4DG-h_dHeh7g.js} +1 -1
- package/web/assets/{project-search-page-NKQEBGPF-Cc6HV7BN.js → project-search-page-NKQEBGPF-2AzjjLEC.js} +1 -1
- package/web/assets/{quadrantDiagram-ABIIQ3AL-Drb_qWex.js → quadrantDiagram-ABIIQ3AL-C6IegTP-.js} +1 -1
- package/web/assets/{railroadDiagram-RFXS5EU6-DCKk2Pix.js → railroadDiagram-RFXS5EU6-bsjyAl-q.js} +1 -1
- package/web/assets/{requirementDiagram-TGXJPOKE-6WwZwcpD.js → requirementDiagram-TGXJPOKE-Cl8EZWy7.js} +1 -1
- package/web/assets/{sankeyDiagram-HTMAVEWB-DGGk3WCE.js → sankeyDiagram-HTMAVEWB-DhK4rRux.js} +1 -1
- package/web/assets/{selection-popup-532EHNCZ-yPhTrk7L.js → selection-popup-532EHNCZ-E2iQ2gQQ.js} +1 -1
- package/web/assets/{sequenceDiagram-DBY2YBRQ-a4tGi3DH.js → sequenceDiagram-DBY2YBRQ-DQTaPSD6.js} +1 -1
- package/web/assets/{sizeCapture-X5ZJPWSS-CHehBmy9.js → sizeCapture-X5ZJPWSS-k77bZ1zg.js} +1 -1
- package/web/assets/{stateDiagram-2N3HPSRC-Bn9GSWgV.js → stateDiagram-2N3HPSRC-Cv_5WuZJ.js} +1 -1
- package/web/assets/{stateDiagram-v2-6OUMAXLB-DI7fdBd7.js → stateDiagram-v2-6OUMAXLB-DJ1Lrskh.js} +1 -1
- package/web/assets/{swimlanes-5IMT3BWC-BX0WRzfR.js → swimlanes-5IMT3BWC-gBtAjG3z.js} +2 -2
- package/web/assets/swimlanesDiagram-G3AALYLV-DF1K9voG.js +8 -0
- package/web/assets/{timeline-definition-FHXFAJF6-9Ni2kIzq.js → timeline-definition-FHXFAJF6-CFjnM1Qq.js} +1 -1
- package/web/assets/{vennDiagram-L72KCM5P-rfBSNh3U.js → vennDiagram-L72KCM5P-CeMWrThY.js} +1 -1
- package/web/assets/{wardleyDiagram-EHGQE667-CzziImCC.js → wardleyDiagram-EHGQE667-R-ZaAOAI.js} +1 -1
- package/web/assets/{xychartDiagram-FW5EYKEG-DTLI4h5R.js → xychartDiagram-FW5EYKEG-BFXoMXiN.js} +1 -1
- package/web/index.html +2 -2
- package/web/assets/channel-um92dqpm.js +0 -1
- package/web/assets/classDiagram-OUVF2IWQ-h7sFEJBg.js +0 -1
- package/web/assets/classDiagram-v2-EOCWNBFH-h7sFEJBg.js +0 -1
- package/web/assets/swimlanesDiagram-G3AALYLV-OGHC9nqi.js +0 -8
|
@@ -83,6 +83,12 @@ export type PluginBuild = {
|
|
|
83
83
|
};
|
|
84
84
|
/** Points the core runtime at a plugin build. */
|
|
85
85
|
export declare function usePluginBuild(build: PluginBuild): void;
|
|
86
|
+
/**
|
|
87
|
+
* The pinned build of the installed plugins, read without building. `knl llm`
|
|
88
|
+
* uses it: a missing or stale build is an error, never a browser build or an
|
|
89
|
+
* install during context generation.
|
|
90
|
+
*/
|
|
91
|
+
export declare function readPluginBuild(plugins?: ExternalPluginEntry[]): Promise<PluginBuild>;
|
|
86
92
|
/**
|
|
87
93
|
* Compiles the installed external plugins once per plugin set. The host web
|
|
88
94
|
* app is never rebuilt: plugin browser modules load at runtime against the
|
package/dist/plugins/external.js
CHANGED
|
@@ -350,6 +350,8 @@ async function validateImports(entry) {
|
|
|
350
350
|
throw new Error("server id mismatch");
|
|
351
351
|
if (typeof server.detect !== "function" || typeof server.handle !== "function")
|
|
352
352
|
throw new Error("server export missing detect/handle");
|
|
353
|
+
if (server.llm !== undefined && typeof server.llm !== "function")
|
|
354
|
+
throw new Error("server export llm hook must be a function");
|
|
353
355
|
if (!client || typeof client !== "object" || client.id !== entry.id)
|
|
354
356
|
throw new Error("client id mismatch");
|
|
355
357
|
if (typeof client.component !== "function")
|
|
@@ -570,6 +572,20 @@ async function readBuildMarker(marker, key) {
|
|
|
570
572
|
return undefined;
|
|
571
573
|
}
|
|
572
574
|
}
|
|
575
|
+
/**
|
|
576
|
+
* The pinned build of the installed plugins, read without building. `knl llm`
|
|
577
|
+
* uses it: a missing or stale build is an error, never a browser build or an
|
|
578
|
+
* install during context generation.
|
|
579
|
+
*/
|
|
580
|
+
export async function readPluginBuild(plugins = readExternalPlugins()) {
|
|
581
|
+
const key = pluginSetKey(plugins);
|
|
582
|
+
if (plugins.length === 0)
|
|
583
|
+
return { key, serverEntries: [], apiEntries: [] };
|
|
584
|
+
const build = await readBuildMarker(join(buildRoot(key), "build.json"), key);
|
|
585
|
+
if (build)
|
|
586
|
+
return build;
|
|
587
|
+
throw new Error(`The installed plugin build is missing or stale (${plugins.map((plugin) => plugin.id).join(", ")}). Refresh each changed plugin with \`kennel plugin add <source>\`, or run knl llm --no-plugins.`);
|
|
588
|
+
}
|
|
573
589
|
function acquireBuildLock(key) {
|
|
574
590
|
const locks = join(cacheRoot(), "plugin-builds", ".locks");
|
|
575
591
|
mkdirSync(locks, { recursive: true });
|
|
@@ -19,6 +19,18 @@ export declare class FolderPluginFailure extends Error {
|
|
|
19
19
|
constructor(message: string);
|
|
20
20
|
}
|
|
21
21
|
export declare function folderServerRegistry(): Map<string, FolderPluginServer>;
|
|
22
|
+
/** Core plugins plus the installed plugins named by the active plugin build. */
|
|
23
|
+
export declare function folderServerRegistryAsync(): Promise<Map<string, FolderPluginServer>>;
|
|
24
|
+
export type FolderPluginRegistryConfig = {
|
|
25
|
+
plugins: {
|
|
26
|
+
disable: string[];
|
|
27
|
+
order: string[];
|
|
28
|
+
};
|
|
29
|
+
};
|
|
30
|
+
/** Applies kennel.json disable/order settings to a registry; never adds plugins. */
|
|
31
|
+
export declare function configuredFolderServerRegistry(registry: Map<string, FolderPluginServer>, config: FolderPluginRegistryConfig): Map<string, FolderPluginServer>;
|
|
32
|
+
/** Reads the plugin settings from the folder's nearest kennel.json. */
|
|
33
|
+
export declare function folderPluginRegistryConfig(folder: string): Promise<FolderPluginRegistryConfig>;
|
|
22
34
|
export type FolderPluginScaffoldFile = {
|
|
23
35
|
plugin: string;
|
|
24
36
|
path: string;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// packages/api/src/server/plugins.ts
|
|
2
2
|
import { pathToFileURL } from "node:url";
|
|
3
3
|
import { existsSync } from "node:fs";
|
|
4
|
-
import { mkdir, stat as stat2, writeFile } from "node:fs/promises";
|
|
4
|
+
import { mkdir, readFile, stat as stat2, writeFile } from "node:fs/promises";
|
|
5
5
|
import { dirname, resolve as resolve2, sep as sep2 } from "node:path";
|
|
6
6
|
|
|
7
7
|
// packages/plugins/src/server.ts
|
|
@@ -13,8 +13,15 @@ class FolderPluginFailure extends Error {
|
|
|
13
13
|
}
|
|
14
14
|
|
|
15
15
|
// packages/notes/src/server.ts
|
|
16
|
-
import { readdir, stat } from "node:fs/promises";
|
|
17
|
-
import {
|
|
16
|
+
import { readdir, realpath, stat } from "node:fs/promises";
|
|
17
|
+
import {
|
|
18
|
+
basename,
|
|
19
|
+
extname,
|
|
20
|
+
isAbsolute,
|
|
21
|
+
relative,
|
|
22
|
+
resolve,
|
|
23
|
+
sep
|
|
24
|
+
} from "node:path";
|
|
18
25
|
|
|
19
26
|
// packages/notes/src/manifest.ts
|
|
20
27
|
var notesManifest = {
|
|
@@ -31,6 +38,7 @@ var notesManifest = {
|
|
|
31
38
|
|
|
32
39
|
// packages/notes/src/server.ts
|
|
33
40
|
var markdownExtensions = new Set([".md", ".markdown"]);
|
|
41
|
+
var llmNoteLimit = 20;
|
|
34
42
|
async function listMarkdownFiles(folder) {
|
|
35
43
|
const files = [];
|
|
36
44
|
const visit = async (current) => {
|
|
@@ -59,23 +67,92 @@ async function listMarkdownFiles(folder) {
|
|
|
59
67
|
await visit(folder);
|
|
60
68
|
return files.sort((left, right) => right.activityAt - left.activityAt);
|
|
61
69
|
}
|
|
62
|
-
|
|
70
|
+
var canonical = (path) => realpath(path).catch(() => path);
|
|
71
|
+
function inside(path, root) {
|
|
72
|
+
const local = relative(root, path);
|
|
73
|
+
return local === "" || !local.startsWith("..") && !isAbsolute(local);
|
|
74
|
+
}
|
|
75
|
+
async function withoutManaged(groups, managedRoots) {
|
|
76
|
+
if (!managedRoots.length)
|
|
77
|
+
return groups;
|
|
78
|
+
const roots = await Promise.all(managedRoots.map(canonical));
|
|
79
|
+
return Promise.all(groups.map(async (group) => {
|
|
80
|
+
if (roots.includes(await canonical(group.folder))) {
|
|
81
|
+
return { ...group, files: [] };
|
|
82
|
+
}
|
|
83
|
+
const files = await Promise.all(group.files.map(async (file) => {
|
|
84
|
+
const real = await canonical(resolve(group.folder, file.relativePath));
|
|
85
|
+
return roots.some((root) => inside(real, root)) ? undefined : file;
|
|
86
|
+
}));
|
|
87
|
+
return { ...group, files: files.filter((file) => file !== undefined) };
|
|
88
|
+
}));
|
|
89
|
+
}
|
|
90
|
+
async function loadNotes(folder, linkedFolders = [], managedFolders = []) {
|
|
63
91
|
const localFiles = await listMarkdownFiles(folder);
|
|
64
|
-
const
|
|
65
|
-
files: await listMarkdownFiles(
|
|
66
|
-
folder:
|
|
92
|
+
const listGroups = (folders) => Promise.all(folders.map(async (source) => ({
|
|
93
|
+
files: await listMarkdownFiles(source),
|
|
94
|
+
folder: source
|
|
67
95
|
})));
|
|
96
|
+
const [managed, linked] = await Promise.all([
|
|
97
|
+
listGroups(managedFolders),
|
|
98
|
+
listGroups(linkedFolders).then((groups) => withoutManaged(groups, managedFolders))
|
|
99
|
+
]);
|
|
68
100
|
return {
|
|
69
101
|
files: {
|
|
70
102
|
items: [
|
|
71
103
|
...localFiles,
|
|
72
|
-
...linked.flatMap((group) => group.files.map((file) => ({ ...file, folder: group.folder })))
|
|
104
|
+
...[...managed, ...linked].flatMap((group) => group.files.map((file) => ({ ...file, folder: group.folder })))
|
|
73
105
|
]
|
|
74
106
|
},
|
|
75
107
|
folder,
|
|
76
108
|
status: "available"
|
|
77
109
|
};
|
|
78
110
|
}
|
|
111
|
+
function codeSpan(text) {
|
|
112
|
+
const escaped = text.replace(/[\u0000-\u001f\u007f-\u009f\u2028\u2029]/gu, (character) => `\\u${character.charCodeAt(0).toString(16).padStart(4, "0")}`);
|
|
113
|
+
const longest = Math.max(0, ...(escaped.match(/`+/g) ?? []).map((run) => run.length));
|
|
114
|
+
const fence = "`".repeat(longest + 1);
|
|
115
|
+
const pad = escaped.startsWith("`") || escaped.endsWith("`") ? " " : "";
|
|
116
|
+
return `${fence}${pad}${escaped}${pad}${fence}`;
|
|
117
|
+
}
|
|
118
|
+
async function notesLlmSections({
|
|
119
|
+
folder,
|
|
120
|
+
linkedFolders = [],
|
|
121
|
+
managedFolders = [],
|
|
122
|
+
signal
|
|
123
|
+
}) {
|
|
124
|
+
const groups = [];
|
|
125
|
+
const notes = [];
|
|
126
|
+
const seen = new Set;
|
|
127
|
+
for (const source of [folder, ...managedFolders, ...linkedFolders]) {
|
|
128
|
+
signal.throwIfAborted();
|
|
129
|
+
const identity = await canonical(source);
|
|
130
|
+
if (seen.has(identity))
|
|
131
|
+
continue;
|
|
132
|
+
seen.add(identity);
|
|
133
|
+
const files = await listMarkdownFiles(source);
|
|
134
|
+
const paths = files.map((file) => resolve(source, file.relativePath));
|
|
135
|
+
groups.push({ folder: source, paths });
|
|
136
|
+
files.forEach((file, index) => notes.push({ activityAt: file.activityAt, path: paths[index] }));
|
|
137
|
+
}
|
|
138
|
+
if (notes.length === 0)
|
|
139
|
+
return [];
|
|
140
|
+
const recent = notes.sort((left, right) => right.activityAt - left.activityAt || (left.path < right.path ? -1 : left.path > right.path ? 1 : 0)).slice(0, llmNoteLimit);
|
|
141
|
+
return [
|
|
142
|
+
{
|
|
143
|
+
id: "notes",
|
|
144
|
+
title: "Markdown notes",
|
|
145
|
+
lines: [
|
|
146
|
+
...groups.filter((group) => group.paths.length > 0).map((group) => `- Source: ${codeSpan(group.folder)} (${group.paths.length} ${group.paths.length === 1 ? "note" : "notes"})`),
|
|
147
|
+
`- Total notes: ${notes.length}`,
|
|
148
|
+
"",
|
|
149
|
+
`Most recently changed (${recent.length} of ${notes.length}):`,
|
|
150
|
+
"",
|
|
151
|
+
...recent.map((note) => `- ${codeSpan(note.path)}`)
|
|
152
|
+
]
|
|
153
|
+
}
|
|
154
|
+
];
|
|
155
|
+
}
|
|
79
156
|
function createNotesPlugin() {
|
|
80
157
|
return {
|
|
81
158
|
...notesManifest,
|
|
@@ -87,10 +164,15 @@ function createNotesPlugin() {
|
|
|
87
164
|
},
|
|
88
165
|
async handle({
|
|
89
166
|
folder,
|
|
90
|
-
linkedFolders
|
|
167
|
+
linkedFolders,
|
|
168
|
+
managedFolders
|
|
91
169
|
}) {
|
|
92
|
-
return {
|
|
93
|
-
|
|
170
|
+
return {
|
|
171
|
+
data: await loadNotes(folder, linkedFolders, managedFolders),
|
|
172
|
+
kind: "available"
|
|
173
|
+
};
|
|
174
|
+
},
|
|
175
|
+
llm: notesLlmSections
|
|
94
176
|
};
|
|
95
177
|
}
|
|
96
178
|
|
|
@@ -104,6 +186,44 @@ function configuredFolderServerRegistry(registry, resolvedConfig) {
|
|
|
104
186
|
];
|
|
105
187
|
return new Map(ids.map((id) => [id, registry.get(id)]));
|
|
106
188
|
}
|
|
189
|
+
var defaultPluginRegistryConfig = {
|
|
190
|
+
plugins: { disable: [], order: [] }
|
|
191
|
+
};
|
|
192
|
+
function stringArray(value) {
|
|
193
|
+
return Array.isArray(value) ? value.filter((item) => typeof item === "string") : [];
|
|
194
|
+
}
|
|
195
|
+
function parsePluginRegistryConfig(content) {
|
|
196
|
+
const parsed = JSON.parse(content);
|
|
197
|
+
const config = parsed && typeof parsed === "object" ? parsed : {};
|
|
198
|
+
return {
|
|
199
|
+
plugins: {
|
|
200
|
+
disable: stringArray(config.plugins?.disable),
|
|
201
|
+
order: stringArray(config.plugins?.order)
|
|
202
|
+
}
|
|
203
|
+
};
|
|
204
|
+
}
|
|
205
|
+
async function nearestKennelConfig(folder) {
|
|
206
|
+
for (let current = folder;; current = dirname(current)) {
|
|
207
|
+
const candidate = resolve2(current, "kennel.json");
|
|
208
|
+
const details = await stat2(candidate).catch(() => {
|
|
209
|
+
return;
|
|
210
|
+
});
|
|
211
|
+
if (details?.isFile())
|
|
212
|
+
return candidate;
|
|
213
|
+
if (dirname(current) === current)
|
|
214
|
+
return;
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
async function folderPluginRegistryConfig(folder) {
|
|
218
|
+
const path = await nearestKennelConfig(folder);
|
|
219
|
+
if (!path)
|
|
220
|
+
return defaultPluginRegistryConfig;
|
|
221
|
+
try {
|
|
222
|
+
return parsePluginRegistryConfig(await readFile(path, "utf8"));
|
|
223
|
+
} catch {
|
|
224
|
+
return defaultPluginRegistryConfig;
|
|
225
|
+
}
|
|
226
|
+
}
|
|
107
227
|
function createFolderServerPlugins() {
|
|
108
228
|
return [createNotesPlugin()];
|
|
109
229
|
}
|
|
@@ -225,6 +345,7 @@ export {
|
|
|
225
345
|
configuredFolderServerRegistry,
|
|
226
346
|
createFolderServerPlugins,
|
|
227
347
|
externalApiHandlers,
|
|
348
|
+
folderPluginRegistryConfig,
|
|
228
349
|
folderServerRegistry,
|
|
229
350
|
folderServerRegistryAsync,
|
|
230
351
|
scaffoldFolderPlugins
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "kennel",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.33",
|
|
4
4
|
"description": "Open-source pop-up previews for AI output: markdown, HTML artifacts, email, images, code, tables, notes, and calendars, from the terminal or your agent session.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -67,7 +67,7 @@
|
|
|
67
67
|
"effect": "^3.19.7",
|
|
68
68
|
"esbuild": "^0.27.0",
|
|
69
69
|
"kennel-mcp": "https://registry.npmjs.org/%40raggle-ai%2fkennel-mcp/-/kennel-mcp-0.1.8.tgz",
|
|
70
|
-
"kennel-plugins": "0.1.
|
|
70
|
+
"kennel-plugins": "0.1.16",
|
|
71
71
|
"koffi": "^3.3.1",
|
|
72
72
|
"lucide-react": "^0.562.0",
|
|
73
73
|
"radix-ui": "^1.6.7",
|
|
@@ -84,6 +84,8 @@
|
|
|
84
84
|
"devDependencies": {
|
|
85
85
|
"@types/node": "^22.13.4",
|
|
86
86
|
"kennel-api": "0.1.0",
|
|
87
|
+
"kennel-llm": "0.1.0",
|
|
88
|
+
"kennel-notes": "0.1.0",
|
|
87
89
|
"kennel-preview": "0.1.0",
|
|
88
90
|
"typescript": "^5.9.3"
|
|
89
91
|
},
|
|
@@ -20,6 +20,17 @@
|
|
|
20
20
|
"description": "Resolved external folder plugins. Managed by `kennel plugin add`.",
|
|
21
21
|
"type": "array",
|
|
22
22
|
"items": { "$ref": "#/$defs/externalPlugin" }
|
|
23
|
+
},
|
|
24
|
+
"repositoryNotes": {
|
|
25
|
+
"description": "Opt-in Markdown notes per Git repository, stored in $XDG_CONFIG_HOME/kennel/repos/<uuid>/ and shown in that repository's Notes view. `kennel notes path` registers a repository only when enabled is true.",
|
|
26
|
+
"type": "object",
|
|
27
|
+
"additionalProperties": false,
|
|
28
|
+
"properties": {
|
|
29
|
+
"enabled": {
|
|
30
|
+
"description": "Allow `kennel notes` registration and show managed notes in local repository views.",
|
|
31
|
+
"type": "boolean"
|
|
32
|
+
}
|
|
33
|
+
}
|
|
23
34
|
}
|
|
24
35
|
},
|
|
25
36
|
"$defs": {
|
|
@@ -1,7 +1,29 @@
|
|
|
1
|
+
import { readFile } from "node:fs/promises";
|
|
2
|
+
import { join } from "node:path";
|
|
1
3
|
import type { FolderPluginServer } from "kennel-plugins/server";
|
|
2
4
|
|
|
3
5
|
import manifest from "./manifest";
|
|
4
6
|
|
|
7
|
+
// Example content file read by the `llm` hook below: a JSON array of items
|
|
8
|
+
// with a string `title`, for example [{ "title": "First item" }].
|
|
9
|
+
const CONTENT_FILE = "__PLUGIN_ID__.json";
|
|
10
|
+
|
|
11
|
+
async function readItems(folder: string) {
|
|
12
|
+
const source = join(folder, CONTENT_FILE);
|
|
13
|
+
let text: string;
|
|
14
|
+
try {
|
|
15
|
+
text = await readFile(source, "utf8");
|
|
16
|
+
} catch (error) {
|
|
17
|
+
if ((error as NodeJS.ErrnoException).code === "ENOENT") return undefined;
|
|
18
|
+
throw error;
|
|
19
|
+
}
|
|
20
|
+
const items: unknown = JSON.parse(text);
|
|
21
|
+
if (!Array.isArray(items) || !items.every((item) => typeof item?.title === "string")) {
|
|
22
|
+
throw new Error(`${source} must be a JSON array of items with a string title`);
|
|
23
|
+
}
|
|
24
|
+
return { source, titles: items.map((item: { title: string }) => item.title) };
|
|
25
|
+
}
|
|
26
|
+
|
|
5
27
|
export default {
|
|
6
28
|
...manifest,
|
|
7
29
|
async detect() {
|
|
@@ -10,4 +32,25 @@ export default {
|
|
|
10
32
|
async handle() {
|
|
11
33
|
return { data: { items: [] } };
|
|
12
34
|
},
|
|
35
|
+
// Optional `knl llm` context. Read only: no writes, network calls, or other
|
|
36
|
+
// side effects. Return [] when the folder has no content for this plugin.
|
|
37
|
+
// The host adds the headings; each line is one Markdown line without line
|
|
38
|
+
// breaks or headings. Invalid output or a thrown error fails `knl llm`.
|
|
39
|
+
async llm({ folder, signal }) {
|
|
40
|
+
signal.throwIfAborted();
|
|
41
|
+
const items = await readItems(folder);
|
|
42
|
+
if (!items) return [];
|
|
43
|
+
return [
|
|
44
|
+
{
|
|
45
|
+
id: "summary",
|
|
46
|
+
title: "Summary",
|
|
47
|
+
lines: [
|
|
48
|
+
`- Source: ${items.source}`,
|
|
49
|
+
`- Items: ${items.titles.length}`,
|
|
50
|
+
...items.titles.slice(0, 5).map((title) => `- Item: ${title}`),
|
|
51
|
+
"- Use the source file as the authority for these items.",
|
|
52
|
+
],
|
|
53
|
+
},
|
|
54
|
+
];
|
|
55
|
+
},
|
|
13
56
|
} satisfies FolderPluginServer;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{g as p,r as u,d as a}from"./chunk-MOJQB5TN-
|
|
1
|
+
import{g as p,r as u,d as a}from"./chunk-MOJQB5TN-CuIx80Vn.js";import{p as f}from"./chunk-JWPE2WC7-Dy_9wZ2Y.js";import{_ as n,l as o}from"./mermaid.core-bE6Vl3bZ.js";import{b as c,M as d}from"./cynefin-VYW2F7L2-DxAlRHA1.js";import"./index-Bd3ghcW-.js";import"./react-DELVxLrX.js";import"./markdown-BP-54rPU.js";import"./tanstack-CRaSxWtK.js";var v=c().RailroadAbnf.parser.LangiumParser,i=n(e=>{const r=e.alternatives.map(g);return r.length===1?r[0]:{type:"choice",alternatives:r}},"transformAlternation"),g=n(e=>{const r=e.elements.map(y);return r.length===1?r[0]:{type:"sequence",elements:r}},"transformConcatenation"),b=n(e=>{if(e.includes("*")){const[t,s]=e.split("*"),m=t?parseInt(t,10):0,l=s?parseInt(s,10):1/0;return{min:m,max:l}}const r=parseInt(e,10);return{min:r,max:r}},"parseRepeat"),y=n(e=>{const r=A(e.primary);if(!e.repeat)return r;const{min:t,max:s}=b(e.repeat);return t===0&&s===1?{type:"optional",element:r}:{type:"repetition",element:r,min:t,max:s}},"transformElement"),A=n(e=>{switch(e.$type){case"AbnfStringLiteral":return{type:"terminal",value:e.value};case"AbnfNumVal":return{type:"terminal",value:e.value};case"AbnfRuleName":return{type:"nonterminal",name:e.name};case"AbnfGroup":return i(e.element);case"AbnfOptionalGroup":return{type:"optional",element:i(e.element)};default:throw new Error(`Unsupported ABNF primary node: ${e.$type}`)}},"transformPrimary"),P=n(e=>({name:e.name,definition:i(e.definition)}),"transformRule"),h=n(e=>{f(e,a),e.title&&a.setTitle(e.title),e.rules.map(r=>a.addRule(P(r)))},"populateDb"),R={parse:n(e=>{a.clear(),o.debug("[ABNF Parser] Starting Langium parse");const r=v.parse(e);if(r.lexerErrors.length>0||r.parserErrors.length>0)throw new d(r);const t=r.value;o.debug("[ABNF Parser] Parsed rules:",t.rules.length),h(t),o.debug("[ABNF Parser] Parse complete")},"parse"),parser:{yy:a}},C={parser:R,db:a,renderer:u,styles:p};export{C as diagram};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{Y as ln,$ as an,a0 as F,a1 as q,a2 as j,a3 as un,a4 as y,a5 as tn,a6 as J,a7 as _,a8 as rn,a9 as o,aa as sn,ab as on,ac as fn}from"./mermaid.core-
|
|
1
|
+
import{Y as ln,$ as an,a0 as F,a1 as q,a2 as j,a3 as un,a4 as y,a5 as tn,a6 as J,a7 as _,a8 as rn,a9 as o,aa as sn,ab as on,ac as fn}from"./mermaid.core-bE6Vl3bZ.js";function cn(l){return l.innerRadius}function yn(l){return l.outerRadius}function gn(l){return l.startAngle}function dn(l){return l.endAngle}function mn(l){return l&&l.padAngle}function pn(l,h,I,D,v,A,z,a){var O=I-l,i=D-h,n=z-v,d=a-A,u=d*O-n*i;if(!(u*u<y))return u=(n*(h-A)-d*(l-v))/u,[l+u*O,h+u*i]}function U(l,h,I,D,v,A,z){var a=l-I,O=h-D,i=(z?A:-A)/J(a*a+O*O),n=i*O,d=-i*a,u=l+n,s=h+d,f=I+n,c=D+d,B=(u+f)/2,t=(s+c)/2,m=f-u,g=c-s,R=m*m+g*g,T=v-A,P=u*c-f*s,S=(g<0?-1:1)*J(on(0,T*T*R-P*P)),Y=(P*g-m*S)/R,$=(-P*m-g*S)/R,w=(P*g+m*S)/R,p=(-P*m+g*S)/R,x=Y-B,e=$-t,r=w-B,C=p-t;return x*x+e*e>r*r+C*C&&(Y=w,$=p),{cx:Y,cy:$,x01:-n,y01:-d,x11:Y*(v/T-1),y11:$*(v/T-1)}}function hn(){var l=cn,h=yn,I=j(0),D=null,v=gn,A=dn,z=mn,a=null,O=ln(i);function i(){var n,d,u=+l.apply(this,arguments),s=+h.apply(this,arguments),f=v.apply(this,arguments)-un,c=A.apply(this,arguments)-un,B=rn(c-f),t=c>f;if(a||(a=n=O()),s<u&&(d=s,s=u,u=d),!(s>y))a.moveTo(0,0);else if(B>tn-y)a.moveTo(s*F(f),s*q(f)),a.arc(0,0,s,f,c,!t),u>y&&(a.moveTo(u*F(c),u*q(c)),a.arc(0,0,u,c,f,t));else{var m=f,g=c,R=f,T=c,P=B,S=B,Y=z.apply(this,arguments)/2,$=Y>y&&(D?+D.apply(this,arguments):J(u*u+s*s)),w=_(rn(s-u)/2,+I.apply(this,arguments)),p=w,x=w,e,r;if($>y){var C=sn($/u*q(Y)),K=sn($/s*q(Y));(P-=C*2)>y?(C*=t?1:-1,R+=C,T-=C):(P=0,R=T=(f+c)/2),(S-=K*2)>y?(K*=t?1:-1,m+=K,g-=K):(S=0,m=g=(f+c)/2)}var G=s*F(m),H=s*q(m),L=u*F(T),M=u*q(T);if(w>y){var N=s*F(g),Q=s*q(g),V=u*F(R),W=u*q(R),E;if(B<an)if(E=pn(G,H,V,W,N,Q,L,M)){var X=G-E[0],Z=H-E[1],b=N-E[0],k=Q-E[1],nn=1/q(fn((X*b+Z*k)/(J(X*X+Z*Z)*J(b*b+k*k)))/2),en=J(E[0]*E[0]+E[1]*E[1]);p=_(w,(u-en)/(nn-1)),x=_(w,(s-en)/(nn+1))}else p=x=0}S>y?x>y?(e=U(V,W,G,H,s,x,t),r=U(N,Q,L,M,s,x,t),a.moveTo(e.cx+e.x01,e.cy+e.y01),x<w?a.arc(e.cx,e.cy,x,o(e.y01,e.x01),o(r.y01,r.x01),!t):(a.arc(e.cx,e.cy,x,o(e.y01,e.x01),o(e.y11,e.x11),!t),a.arc(0,0,s,o(e.cy+e.y11,e.cx+e.x11),o(r.cy+r.y11,r.cx+r.x11),!t),a.arc(r.cx,r.cy,x,o(r.y11,r.x11),o(r.y01,r.x01),!t))):(a.moveTo(G,H),a.arc(0,0,s,m,g,!t)):a.moveTo(G,H),!(u>y)||!(P>y)?a.lineTo(L,M):p>y?(e=U(L,M,N,Q,u,-p,t),r=U(G,H,V,W,u,-p,t),a.lineTo(e.cx+e.x01,e.cy+e.y01),p<w?a.arc(e.cx,e.cy,p,o(e.y01,e.x01),o(r.y01,r.x01),!t):(a.arc(e.cx,e.cy,p,o(e.y01,e.x01),o(e.y11,e.x11),!t),a.arc(0,0,u,o(e.cy+e.y11,e.cx+e.x11),o(r.cy+r.y11,r.cx+r.x11),t),a.arc(r.cx,r.cy,p,o(r.y11,r.x11),o(r.y01,r.x01),!t))):a.arc(0,0,u,T,R,t)}if(a.closePath(),n)return a=null,n+""||null}return i.centroid=function(){var n=(+l.apply(this,arguments)+ +h.apply(this,arguments))/2,d=(+v.apply(this,arguments)+ +A.apply(this,arguments))/2-an/2;return[F(d)*n,q(d)*n]},i.innerRadius=function(n){return arguments.length?(l=typeof n=="function"?n:j(+n),i):l},i.outerRadius=function(n){return arguments.length?(h=typeof n=="function"?n:j(+n),i):h},i.cornerRadius=function(n){return arguments.length?(I=typeof n=="function"?n:j(+n),i):I},i.padRadius=function(n){return arguments.length?(D=n==null?null:typeof n=="function"?n:j(+n),i):D},i.startAngle=function(n){return arguments.length?(v=typeof n=="function"?n:j(+n),i):v},i.endAngle=function(n){return arguments.length?(A=typeof n=="function"?n:j(+n),i):A},i.padAngle=function(n){return arguments.length?(z=typeof n=="function"?n:j(+n),i):z},i.context=function(n){return arguments.length?(a=n??null,i):a},i}export{hn as d};
|