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.
Files changed (90) hide show
  1. package/dist/cli/dispatch.js +10 -0
  2. package/dist/cli/usage.js +9 -0
  3. package/dist/core/runtime.js +531 -197
  4. package/dist/llm/clipboard.d.ts +1 -0
  5. package/dist/llm/clipboard.js +59 -0
  6. package/dist/llm/command.d.ts +1 -0
  7. package/dist/llm/command.js +226 -0
  8. package/dist/llm/worker.d.ts +19 -0
  9. package/dist/llm/worker.js +1131 -0
  10. package/dist/notes/command.d.ts +1 -0
  11. package/dist/notes/command.js +556 -0
  12. package/dist/package/preview.js +554 -219
  13. package/dist/plugins/external.d.ts +6 -0
  14. package/dist/plugins/external.js +16 -0
  15. package/dist/site/server-plugins.d.ts +12 -0
  16. package/dist/site/server-plugins.js +132 -11
  17. package/package.json +4 -2
  18. package/schemas/config.schema.json +11 -0
  19. package/templates/plugin/package.json +1 -0
  20. package/templates/plugin/server.ts +43 -0
  21. package/templates/plugin/tsconfig.json +1 -1
  22. package/web/assets/{abnfDiagram-VRR7QNED-DPZyna8O.js → abnfDiagram-VRR7QNED-CpP1VHKI.js} +1 -1
  23. package/web/assets/{arc-BAcU9y_V.js → arc-kL4AxfvR.js} +1 -1
  24. package/web/assets/{architectureDiagram-ZJ3FMSHR-D5yd7Cag.js → architectureDiagram-ZJ3FMSHR-BWaRktRJ.js} +1 -1
  25. package/web/assets/{ask-tray-PMT6VVXN-C4dJH21Q.js → ask-tray-PMT6VVXN-FqPDpwtw.js} +3 -3
  26. package/web/assets/{blockDiagram-677ZJIJ3-C7LSGyxV.js → blockDiagram-677ZJIJ3-B07uVoPU.js} +1 -1
  27. package/web/assets/{c4Diagram-LMCZKHZV-BW2KfoQr.js → c4Diagram-LMCZKHZV-uvoaQC09.js} +1 -1
  28. package/web/assets/channel-Djmf1Ik1.js +1 -0
  29. package/web/assets/{chunk-2Q5K7J3B-4Og6VAFG.js → chunk-2Q5K7J3B-BH3SaHqk.js} +1 -1
  30. package/web/assets/{chunk-32BRIVSS-BYOowQl5.js → chunk-32BRIVSS-SPD8Eraw.js} +1 -1
  31. package/web/assets/{chunk-5VM5RSS4-C1m-lq64.js → chunk-5VM5RSS4-DNeOgOuZ.js} +1 -1
  32. package/web/assets/{chunk-EX3LRPZG-B2hriRpJ.js → chunk-EX3LRPZG-SJQ-vKGz.js} +1 -1
  33. package/web/assets/{chunk-JWPE2WC7-C6RsnzVA.js → chunk-JWPE2WC7-Dy_9wZ2Y.js} +1 -1
  34. package/web/assets/{chunk-MOJQB5TN-yKbMmUWf.js → chunk-MOJQB5TN-CuIx80Vn.js} +1 -1
  35. package/web/assets/{chunk-RYQCIY6F-BOokm3Qc.js → chunk-RYQCIY6F-DDG61RKR.js} +1 -1
  36. package/web/assets/{chunk-V7JOEXUC-CKW-H5An.js → chunk-V7JOEXUC-7OMVhaoH.js} +1 -1
  37. package/web/assets/{chunk-VR4S4FIN-BU5pFrup.js → chunk-VR4S4FIN-Y-HNaSmV.js} +1 -1
  38. package/web/assets/{chunk-XXDRQBXY-DYtdSJHU.js → chunk-XXDRQBXY-CtG0MPgC.js} +1 -1
  39. package/web/assets/classDiagram-OUVF2IWQ-hlDS9RWf.js +1 -0
  40. package/web/assets/classDiagram-v2-EOCWNBFH-hlDS9RWf.js +1 -0
  41. package/web/assets/{cose-bilkent-JH36ORCC-BZeXtC6E.js → cose-bilkent-JH36ORCC-BZaPMYcl.js} +1 -1
  42. package/web/assets/{cynefin-VYW2F7L2-BSDVyzMI.js → cynefin-VYW2F7L2-DxAlRHA1.js} +1 -1
  43. package/web/assets/{cynefinDiagram-TSTJHNR4-CReoDBYo.js → cynefinDiagram-TSTJHNR4-gjgzmagS.js} +1 -1
  44. package/web/assets/{dagre-VKFMJZFB-CBLLzMxd.js → dagre-VKFMJZFB-DvLPeYuw.js} +1 -1
  45. package/web/assets/{diagram-FQU43EPY-BcGWiZXZ.js → diagram-FQU43EPY-443LFnzG.js} +1 -1
  46. package/web/assets/{diagram-G47NLZAW-BXUYZA9N.js → diagram-G47NLZAW-CqfUewot.js} +1 -1
  47. package/web/assets/{diagram-NH7WQ7WH-BrvrZs7N.js → diagram-NH7WQ7WH-D-FElUGW.js} +1 -1
  48. package/web/assets/{diagram-OA4YK3LP-7lnVcjXL.js → diagram-OA4YK3LP-DbidUAp5.js} +1 -1
  49. package/web/assets/{diagram-WEI45ONY-s58_bMFf.js → diagram-WEI45ONY-CPLeckCZ.js} +1 -1
  50. package/web/assets/{ebnfDiagram-CCIWWBDH-BemWecoD.js → ebnfDiagram-CCIWWBDH-DR0rqZzN.js} +1 -1
  51. package/web/assets/{entity-search-page-G6QEQNXT--sKP5dP3.js → entity-search-page-G6QEQNXT-CnG8d-at.js} +1 -1
  52. package/web/assets/{erDiagram-Q63AITRT-ClVJddch.js → erDiagram-Q63AITRT-DMXv_8Tj.js} +1 -1
  53. package/web/assets/{flowDiagram-23GEKE2U-xzUJu-i_.js → flowDiagram-23GEKE2U-DnbQjZs0.js} +1 -1
  54. package/web/assets/{ganttDiagram-NO4QXBWP-CXQmtF5g.js → ganttDiagram-NO4QXBWP-DBKIrjj8.js} +1 -1
  55. package/web/assets/{gitGraphDiagram-IHSO6WYX-DCdljcLl.js → gitGraphDiagram-IHSO6WYX-BqqxKWT_.js} +1 -1
  56. package/web/assets/{index-CtTx1o2d.js → index-Bd3ghcW-.js} +5 -5
  57. package/web/assets/{index-vjrhIAJn.css → index-Dt8OzQCe.css} +1 -1
  58. package/web/assets/{infoDiagram-FWYZ7A6U-B11SJYSK.js → infoDiagram-FWYZ7A6U-Ba6Fs17y.js} +1 -1
  59. package/web/assets/{ishikawaDiagram-FXEZZL3T-B2CCGlAm.js → ishikawaDiagram-FXEZZL3T-D3BOtFjy.js} +1 -1
  60. package/web/assets/{journeyDiagram-5HDEW3XC-CBPETzOk.js → journeyDiagram-5HDEW3XC-MbzFPQm8.js} +1 -1
  61. package/web/assets/{kanban-definition-HUTT4EX6-unhQ3RPE.js → kanban-definition-HUTT4EX6-CurBmwu1.js} +1 -1
  62. package/web/assets/{linear-DuP_pG51.js → linear-CQIr4JjI.js} +1 -1
  63. package/web/assets/{linked-session-header-M7WY6IGF-DwlNCJlj.js → linked-session-header-M7WY6IGF-BBgN8zTq.js} +1 -1
  64. package/web/assets/{mermaid.core-BlYNzkpn.js → mermaid.core-bE6Vl3bZ.js} +4 -4
  65. package/web/assets/{mindmap-definition-LN4V7U3C-CtKHN6O1.js → mindmap-definition-LN4V7U3C-C1a46OGU.js} +1 -1
  66. package/web/assets/{pegDiagram-2B236MQR-CRyz6XWY.js → pegDiagram-2B236MQR-DZxMOxek.js} +1 -1
  67. package/web/assets/{pieDiagram-ENE6RG2P-BXfkPm3Q.js → pieDiagram-ENE6RG2P-BN0XOKLi.js} +1 -1
  68. package/web/assets/{plugin-host-modules-5NQgs5Cq.js → plugin-host-modules-CaRD5_5q.js} +1 -1
  69. package/web/assets/{project-search-dialog-HJFGW4DG-DPly4OiB.js → project-search-dialog-HJFGW4DG-h_dHeh7g.js} +1 -1
  70. package/web/assets/{project-search-page-NKQEBGPF-Cc6HV7BN.js → project-search-page-NKQEBGPF-2AzjjLEC.js} +1 -1
  71. package/web/assets/{quadrantDiagram-ABIIQ3AL-Drb_qWex.js → quadrantDiagram-ABIIQ3AL-C6IegTP-.js} +1 -1
  72. package/web/assets/{railroadDiagram-RFXS5EU6-DCKk2Pix.js → railroadDiagram-RFXS5EU6-bsjyAl-q.js} +1 -1
  73. package/web/assets/{requirementDiagram-TGXJPOKE-6WwZwcpD.js → requirementDiagram-TGXJPOKE-Cl8EZWy7.js} +1 -1
  74. package/web/assets/{sankeyDiagram-HTMAVEWB-DGGk3WCE.js → sankeyDiagram-HTMAVEWB-DhK4rRux.js} +1 -1
  75. package/web/assets/{selection-popup-532EHNCZ-yPhTrk7L.js → selection-popup-532EHNCZ-E2iQ2gQQ.js} +1 -1
  76. package/web/assets/{sequenceDiagram-DBY2YBRQ-a4tGi3DH.js → sequenceDiagram-DBY2YBRQ-DQTaPSD6.js} +1 -1
  77. package/web/assets/{sizeCapture-X5ZJPWSS-CHehBmy9.js → sizeCapture-X5ZJPWSS-k77bZ1zg.js} +1 -1
  78. package/web/assets/{stateDiagram-2N3HPSRC-Bn9GSWgV.js → stateDiagram-2N3HPSRC-Cv_5WuZJ.js} +1 -1
  79. package/web/assets/{stateDiagram-v2-6OUMAXLB-DI7fdBd7.js → stateDiagram-v2-6OUMAXLB-DJ1Lrskh.js} +1 -1
  80. package/web/assets/{swimlanes-5IMT3BWC-BX0WRzfR.js → swimlanes-5IMT3BWC-gBtAjG3z.js} +2 -2
  81. package/web/assets/swimlanesDiagram-G3AALYLV-DF1K9voG.js +8 -0
  82. package/web/assets/{timeline-definition-FHXFAJF6-9Ni2kIzq.js → timeline-definition-FHXFAJF6-CFjnM1Qq.js} +1 -1
  83. package/web/assets/{vennDiagram-L72KCM5P-rfBSNh3U.js → vennDiagram-L72KCM5P-CeMWrThY.js} +1 -1
  84. package/web/assets/{wardleyDiagram-EHGQE667-CzziImCC.js → wardleyDiagram-EHGQE667-R-ZaAOAI.js} +1 -1
  85. package/web/assets/{xychartDiagram-FW5EYKEG-DTLI4h5R.js → xychartDiagram-FW5EYKEG-BFXoMXiN.js} +1 -1
  86. package/web/index.html +2 -2
  87. package/web/assets/channel-um92dqpm.js +0 -1
  88. package/web/assets/classDiagram-OUVF2IWQ-h7sFEJBg.js +0 -1
  89. package/web/assets/classDiagram-v2-EOCWNBFH-h7sFEJBg.js +0 -1
  90. 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
@@ -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 { basename, extname, relative, resolve, sep } from "node:path";
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
- async function loadNotes(folder, linkedFolders = []) {
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 linked = await Promise.all(linkedFolders.map(async (linkedFolder) => ({
65
- files: await listMarkdownFiles(linkedFolder),
66
- folder: linkedFolder
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 { data: await loadNotes(folder, linkedFolders), kind: "available" };
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.31",
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.14",
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": {
@@ -4,6 +4,7 @@
4
4
  "private": true,
5
5
  "type": "module",
6
6
  "devDependencies": {
7
+ "@types/node": "latest",
7
8
  "@types/react": "latest",
8
9
  "kennel-plugins": "latest",
9
10
  "typescript": "latest"
@@ -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;
@@ -5,7 +5,7 @@
5
5
  "moduleResolution": "NodeNext",
6
6
  "strict": true,
7
7
  "target": "ES2022",
8
- "types": []
8
+ "types": ["node"]
9
9
  },
10
10
  "include": ["*.ts", "*.tsx"]
11
11
  }
@@ -1 +1 @@
1
- import{g as p,r as u,d as a}from"./chunk-MOJQB5TN-yKbMmUWf.js";import{p as f}from"./chunk-JWPE2WC7-C6RsnzVA.js";import{_ as n,l as o}from"./mermaid.core-BlYNzkpn.js";import{b as c,M as d}from"./cynefin-VYW2F7L2-BSDVyzMI.js";import"./index-CtTx1o2d.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
+ 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-BlYNzkpn.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};
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};