kennel 0.2.12 → 0.2.13

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 (164) hide show
  1. package/dist/cli/src/plugins/external.js +19 -7
  2. package/dist/cli/src/site/server-plugins.d.ts +1 -4
  3. package/dist/core/api-client.js +3 -11
  4. package/dist/core/runtime.js +84 -695
  5. package/dist/package/preview.js +99 -729
  6. package/dist/plugins/external.js +19 -7
  7. package/dist/site/server-plugins.d.ts +1 -4
  8. package/dist/site/server-plugins.js +28 -537
  9. package/kernel/apps/web/package.json +0 -1
  10. package/kernel/apps/web/src/app.css +0 -1
  11. package/kernel/apps/web/src/app.tsx +7 -24
  12. package/kernel/apps/web/src/folder-plugin-view.tsx +6 -3
  13. package/kernel/apps/web/src/plugins.ts +2 -14
  14. package/kernel/apps/web/vite.config.ts +16 -3
  15. package/kernel/packages/api/openapi.json +85 -224
  16. package/kernel/packages/api/package.json +0 -1
  17. package/kernel/packages/api/src/contracts/schemas.ts +1 -9
  18. package/kernel/packages/api/src/server/folder-metadata-plugin.ts +9 -151
  19. package/kernel/packages/api/src/server/plugins.ts +11 -26
  20. package/kernel/packages/api/src/shared/viewer-route.ts +3 -5
  21. package/kernel/packages/api-client/src/generated.d.ts +16 -53
  22. package/kernel/packages/markdown/README.md +2 -2
  23. package/kernel/packages/notes/package.json +1 -0
  24. package/kernel/packages/notes/src/client.tsx +0 -1
  25. package/kernel/packages/notes/src/manifest.ts +7 -0
  26. package/kernel/packages/notes/src/pages/notes-page.tsx +1 -1
  27. package/kernel/packages/notes/src/ui/folder-notes-list.tsx +52 -7
  28. package/kernel/packages/plugins/SKILL.md +3 -2
  29. package/kernel/packages/plugins/package.json +5 -1
  30. package/kernel/packages/plugins/src/hooks.ts +24 -0
  31. package/kernel/packages/plugins/src/index.ts +1 -7
  32. package/kernel/packages/plugins/src/page.ts +0 -7
  33. package/kernel/packages/plugins/src/types.ts +8 -0
  34. package/kernel/packages/plugins/tsconfig.json +1 -1
  35. package/kernel/packages/plugins/tsup.config.ts +1 -0
  36. package/kernel/packages/ui/src/components/folder-activity.tsx +16 -98
  37. package/kernel/packages/ui/src/components/folder-landing.tsx +4 -0
  38. package/kernel/packages/ui/src/components/pagination.tsx +88 -2
  39. package/kernel/packages/ui/src/components/plugin-icon.tsx +19 -0
  40. package/kernel/packages/ui/src/lib/folder-activity.ts +4 -17
  41. package/kernel/packages/ui/src/styles/folder-activity.css +1 -31
  42. package/package.json +2 -2
  43. package/web/assets/{abnfDiagram-VRR7QNED-Brm1vcGG.js → abnfDiagram-VRR7QNED-Dt7QEF5H.js} +1 -1
  44. package/web/assets/app-C1j063QX.js +2 -0
  45. package/web/assets/app-options-98-a6ivv.js +25 -0
  46. package/web/assets/{app-view-header-BSOI-wB6.js → app-view-header-BJ9CWTE1.js} +1 -1
  47. package/web/assets/{arc-zngHsMKV.js → arc-PFB0H7oD.js} +1 -1
  48. package/web/assets/{architectureDiagram-ZJ3FMSHR-BLklLqRP.js → architectureDiagram-ZJ3FMSHR-BHLqEXlZ.js} +1 -1
  49. package/web/assets/{ask-tray-B4SPj-fH.js → ask-tray-C3gBesHJ.js} +1 -1
  50. package/web/assets/{blockDiagram-677ZJIJ3-yhC2fEtu.js → blockDiagram-677ZJIJ3-UbnYoYDg.js} +1 -1
  51. package/web/assets/{c4Diagram-LMCZKHZV-DMcweQme.js → c4Diagram-LMCZKHZV-4OSx1V4W.js} +1 -1
  52. package/web/assets/channel-BuvY5cQN.js +1 -0
  53. package/web/assets/{chunk-2Q5K7J3B-DD-S6n8h.js → chunk-2Q5K7J3B-CgxJ1aJb.js} +1 -1
  54. package/web/assets/{chunk-32BRIVSS-DFIAKRv_.js → chunk-32BRIVSS-Bfwxg2LM.js} +1 -1
  55. package/web/assets/{chunk-5VM5RSS4-B23APb3c.js → chunk-5VM5RSS4-BZN9a2hI.js} +1 -1
  56. package/web/assets/{chunk-EX3LRPZG-BSVEB7db.js → chunk-EX3LRPZG-CrH6hhqq.js} +1 -1
  57. package/web/assets/{chunk-JWPE2WC7-B6qGd0ng.js → chunk-JWPE2WC7-Ddn7ZSp8.js} +1 -1
  58. package/web/assets/{chunk-MOJQB5TN-N4T1-ew1.js → chunk-MOJQB5TN-9lJ7f_hI.js} +1 -1
  59. package/web/assets/{chunk-RYQCIY6F-6oc6i_Xw.js → chunk-RYQCIY6F-QrFo_00s.js} +1 -1
  60. package/web/assets/{chunk-V7JOEXUC-Dftgm9ck.js → chunk-V7JOEXUC-Dn0lQjVf.js} +1 -1
  61. package/web/assets/{chunk-VR4S4FIN-DfjqwEsO.js → chunk-VR4S4FIN-DXVPySkB.js} +1 -1
  62. package/web/assets/{chunk-XXDRQBXY-CsFsQYow.js → chunk-XXDRQBXY-CBiFDOdZ.js} +1 -1
  63. package/web/assets/classDiagram-OUVF2IWQ-DCYNCJhe.js +1 -0
  64. package/web/assets/classDiagram-v2-EOCWNBFH-DCYNCJhe.js +1 -0
  65. package/web/assets/client-DBJY3XDL.js +43 -0
  66. package/web/assets/client-DKJs0w4y.js +6 -0
  67. package/web/assets/{command-D_9V8ejS.js → command-onjTnuXT.js} +1 -1
  68. package/web/assets/{copy-context-CevDWX59.js → copy-context-CciRaLNN.js} +1 -1
  69. package/web/assets/{cose-bilkent-JH36ORCC-DzKsIuDb.js → cose-bilkent-JH36ORCC-CGRic2um.js} +1 -1
  70. package/web/assets/{cynefin-VYW2F7L2-AL45ZHbw.js → cynefin-VYW2F7L2-Blk0Lu0l.js} +1 -1
  71. package/web/assets/{cynefinDiagram-TSTJHNR4-DSbBegxZ.js → cynefinDiagram-TSTJHNR4-DpnnHRDK.js} +1 -1
  72. package/web/assets/{dagre-VKFMJZFB-BaG6MGUE.js → dagre-VKFMJZFB-llzMokKp.js} +1 -1
  73. package/web/assets/{diagram-FQU43EPY-WbDotTX5.js → diagram-FQU43EPY-DC6xHovR.js} +1 -1
  74. package/web/assets/{diagram-G47NLZAW-DMbouYVI.js → diagram-G47NLZAW-B8GVS3EA.js} +1 -1
  75. package/web/assets/{diagram-NH7WQ7WH-DoEuaWd_.js → diagram-NH7WQ7WH-DWUaARfh.js} +1 -1
  76. package/web/assets/{diagram-OA4YK3LP-BjGd-PoH.js → diagram-OA4YK3LP-9Uaes_Yi.js} +1 -1
  77. package/web/assets/{diagram-WEI45ONY-CXHi27jF.js → diagram-WEI45ONY-DPPTBWud.js} +1 -1
  78. package/web/assets/{document-CVJCfWiQ.js → document-D23WzFsW.js} +3 -3
  79. package/web/assets/{ebnfDiagram-CCIWWBDH-B5vMUDOs.js → ebnfDiagram-CCIWWBDH-BPrKneNw.js} +1 -1
  80. package/web/assets/{entities-BTKzWJ-r.js → entities-DPg0B8vd.js} +1 -1
  81. package/web/assets/{erDiagram-Q63AITRT-DhO7ayzF.js → erDiagram-Q63AITRT-CFqYBcnb.js} +1 -1
  82. package/web/assets/{file-viewer-D1HK9J3A.js → file-viewer-Ck73SsLA.js} +1 -1
  83. package/web/assets/{flowDiagram-23GEKE2U-CublTUlC.js → flowDiagram-23GEKE2U-C7RpYqaT.js} +1 -1
  84. package/web/assets/folder-filter-bar-DzejUj87.js +4 -0
  85. package/web/assets/{folder-image-L_AjkRfI.js → folder-image-BjIicmQ6.js} +1 -1
  86. package/web/assets/folder-landing-CCIXZfkp.js +55 -0
  87. package/web/assets/folder-list-TuRwuTss.js +1 -0
  88. package/web/assets/folder-mobile-bar-Bqnfwi-z.js +1 -0
  89. package/web/assets/folder-plugin-view-CrinCX1o.js +2 -0
  90. package/web/assets/{ganttDiagram-NO4QXBWP-BLMJWerM.js → ganttDiagram-NO4QXBWP-Cz2sxU8F.js} +1 -1
  91. package/web/assets/{gitGraphDiagram-IHSO6WYX-Br0E6qyZ.js → gitGraphDiagram-IHSO6WYX-BSuz0yF-.js} +1 -1
  92. package/web/assets/index-DLn_2LPp.js +2 -0
  93. package/web/assets/index-uY9jLiL5.css +1 -0
  94. package/web/assets/{infoDiagram-FWYZ7A6U-Dgz0npJO.js → infoDiagram-FWYZ7A6U-CpeKgqoS.js} +1 -1
  95. package/web/assets/input-Bz_puamS.js +9 -0
  96. package/web/assets/{ishikawaDiagram-FXEZZL3T-rY5pdS3b.js → ishikawaDiagram-FXEZZL3T-5c2Tmknb.js} +1 -1
  97. package/web/assets/{journeyDiagram-5HDEW3XC-CKZALyR6.js → journeyDiagram-5HDEW3XC-Cu-wRkR4.js} +1 -1
  98. package/web/assets/{kanban-definition-HUTT4EX6-EGPY9Vda.js → kanban-definition-HUTT4EX6-BunSqkPe.js} +1 -1
  99. package/web/assets/{linear-DkJW9OwI.js → linear-C8e8bGVv.js} +1 -1
  100. package/web/assets/{mermaid.core-DVEpXYiF.js → mermaid.core-DLgjY1g2.js} +4 -4
  101. package/web/assets/{metadata-Bu_ffLUd.js → metadata-DEfJKlnd.js} +1 -1
  102. package/web/assets/{mindmap-definition-LN4V7U3C-BEOUVnDN.js → mindmap-definition-LN4V7U3C-W9ENYFOD.js} +1 -1
  103. package/web/assets/{pegDiagram-2B236MQR-DThjl3eK.js → pegDiagram-2B236MQR-0vPlHCFp.js} +1 -1
  104. package/web/assets/{pieDiagram-ENE6RG2P-DzmoGgw2.js → pieDiagram-ENE6RG2P-CakaveRV.js} +1 -1
  105. package/web/assets/{project-search-dialog-e2Jmhvfq.js → project-search-dialog-BSKA4peX.js} +1 -1
  106. package/web/assets/{quadrantDiagram-ABIIQ3AL-BqT8U3FS.js → quadrantDiagram-ABIIQ3AL-jxHw_egj.js} +1 -1
  107. package/web/assets/{railroadDiagram-RFXS5EU6-1k1tfPk1.js → railroadDiagram-RFXS5EU6-lIjDXPZt.js} +1 -1
  108. package/web/assets/{requirementDiagram-TGXJPOKE-bM4RTd2J.js → requirementDiagram-TGXJPOKE-C-JiT4bc.js} +1 -1
  109. package/web/assets/{sankeyDiagram-HTMAVEWB-TCgfqDLy.js → sankeyDiagram-HTMAVEWB-SuLPAw9X.js} +1 -1
  110. package/web/assets/{search-DQiFH2up.js → search-CKujW14f.js} +1 -1
  111. package/web/assets/{selection-popup-bqfgVd-L.js → selection-popup-CtxT9SWn.js} +3 -3
  112. package/web/assets/{sequenceDiagram-DBY2YBRQ-BRhGHI0i.js → sequenceDiagram-DBY2YBRQ-o9UwTkZR.js} +1 -1
  113. package/web/assets/{sizeCapture-X5ZJPWSS-EeUWgn0N.js → sizeCapture-X5ZJPWSS-ClbWGybF.js} +1 -1
  114. package/web/assets/{stateDiagram-2N3HPSRC-BSOG758d.js → stateDiagram-2N3HPSRC-Ce4avoWx.js} +1 -1
  115. package/web/assets/stateDiagram-v2-6OUMAXLB-CsrPTZoJ.js +1 -0
  116. package/web/assets/{swimlanes-5IMT3BWC-DpCo4aEm.js → swimlanes-5IMT3BWC-DMZI4XZT.js} +2 -2
  117. package/web/assets/swimlanesDiagram-G3AALYLV-pShSVcTd.js +8 -0
  118. package/web/assets/{timeline-definition-FHXFAJF6-BpaObe6u.js → timeline-definition-FHXFAJF6-CPIpIgPN.js} +1 -1
  119. package/web/assets/{vennDiagram-L72KCM5P-ClvUzW8B.js → vennDiagram-L72KCM5P-_rha8uDZ.js} +1 -1
  120. package/web/assets/view-context-BDtIUHQy.js +229 -0
  121. package/web/assets/{viewer-capabilities-CFBwvpcI.js → viewer-capabilities-YYZqMs56.js} +1 -1
  122. package/web/assets/{wardleyDiagram-EHGQE667-Dg1CX0Fl.js → wardleyDiagram-EHGQE667-D4EAOS35.js} +1 -1
  123. package/web/assets/{xychartDiagram-FW5EYKEG-C0c9zQTr.js → xychartDiagram-FW5EYKEG-DR8PwXpe.js} +1 -1
  124. package/web/index.html +2 -2
  125. package/kernel/packages/agents/eslint.config.js +0 -10
  126. package/kernel/packages/agents/package.json +0 -75
  127. package/kernel/packages/agents/src/agent-card.tsx +0 -334
  128. package/kernel/packages/agents/src/client.tsx +0 -13
  129. package/kernel/packages/agents/src/index.ts +0 -18
  130. package/kernel/packages/agents/src/manifest.ts +0 -7
  131. package/kernel/packages/agents/src/node.ts +0 -447
  132. package/kernel/packages/agents/src/profiles.ts +0 -137
  133. package/kernel/packages/agents/src/resolve.ts +0 -69
  134. package/kernel/packages/agents/src/server.ts +0 -60
  135. package/kernel/packages/agents/src/types.ts +0 -82
  136. package/kernel/packages/agents/src/viewer.tsx +0 -370
  137. package/kernel/packages/agents/test/profiles.test.mjs +0 -88
  138. package/kernel/packages/agents/test/resolve.test.mjs +0 -729
  139. package/kernel/packages/agents/tsconfig.json +0 -17
  140. package/kernel/packages/api/test/agents-loading.test.ts +0 -94
  141. package/kernel/packages/ui/src/components/folder-skill-list.tsx +0 -62
  142. package/web/assets/app-Debw-lUT.js +0 -2
  143. package/web/assets/app-options-UU_JlJQv.js +0 -25
  144. package/web/assets/channel-BgYLn5Cm.js +0 -1
  145. package/web/assets/classDiagram-OUVF2IWQ-Fzhmfbkt.js +0 -1
  146. package/web/assets/classDiagram-v2-EOCWNBFH-Fzhmfbkt.js +0 -1
  147. package/web/assets/client-C1WdY7jQ.js +0 -22
  148. package/web/assets/client-D__L4Hl0.js +0 -17
  149. package/web/assets/client-DoMhF9Tv.js +0 -5
  150. package/web/assets/folder-filter-bar-BHz7s9Vb.js +0 -4
  151. package/web/assets/folder-landing-Byh8W67L.js +0 -54
  152. package/web/assets/folder-list-jMyo-7pV.js +0 -1
  153. package/web/assets/folder-metadata-bCocvxpP.js +0 -2
  154. package/web/assets/folder-mobile-bar-DvvyOS-a.js +0 -1
  155. package/web/assets/folder-plugin-view-sib0Dwfw.js +0 -3
  156. package/web/assets/index-4Xh9UKim.js +0 -1
  157. package/web/assets/index-Bu75S2qS.js +0 -1
  158. package/web/assets/index-CPcMcCUY.js +0 -2
  159. package/web/assets/index-CXxkvzNJ.css +0 -1
  160. package/web/assets/select-CkOwL81C.js +0 -30
  161. package/web/assets/stateDiagram-v2-6OUMAXLB-DUeGDz1r.js +0 -1
  162. package/web/assets/swimlanesDiagram-G3AALYLV-BGOaCfyV.js +0 -8
  163. package/web/assets/use-folder-filter-hA6fa7nm.js +0 -1
  164. package/web/assets/view-context-CVvDw2JM.js +0 -229
@@ -18,11 +18,9 @@ import { markdownMetadata } from "@raggle-ai/markdown";
18
18
  import { readFolder } from "@raggle-ai/markdown/node";
19
19
  import { execFile } from "node:child_process";
20
20
  import { sameOrigin } from "kennel-plugins/endpoint";
21
- import { parse as parseYaml } from "yaml";
22
21
 
23
22
  import type { FolderPluginServer } from "kennel-plugins/server";
24
23
  import type { LocalMarkdownFileSummary, LocalProject } from "@raggle-ai/local";
25
- import type { FolderSkill } from "@raggle-ai/ui/lib/folder-activity";
26
24
  import { runApiRoute } from "kennel-plugins/endpoint";
27
25
  import { FolderPluginFailure } from "kennel-plugins/server";
28
26
  import { folderWorkspace } from "@raggle-ai/schemas/workspace";
@@ -74,26 +72,6 @@ const {
74
72
  sortProjectList,
75
73
  } = local;
76
74
 
77
- type SkillSchema = {
78
- description?: unknown;
79
- };
80
-
81
- function skillDescription(content: string) {
82
- if (!content.startsWith("---\n")) return undefined;
83
- const end = content.indexOf("\n---", 4);
84
- if (end < 0) return undefined;
85
- const schema = parseYaml(content.slice(4, end)) as SkillSchema;
86
- return typeof schema.description === "string"
87
- ? schema.description
88
- : undefined;
89
- }
90
-
91
- function skillSource(folder: string, root: string): FolderSkill["source"] {
92
- if (root === folder) return "local";
93
- if (root.startsWith(resolve(folder, ".gtl") + sep)) return "local";
94
- return "global";
95
- }
96
-
97
75
  type IconAsset = {
98
76
  mediaType: "image/jpeg" | "image/png";
99
77
  source: "local" | "favicon";
@@ -135,8 +113,6 @@ export type FolderMetadataPluginOptions = {
135
113
  restrictToRoot?: boolean;
136
114
  root: string;
137
115
  sessionFinding?: SessionFinding;
138
- /** Override the home directory used for Agent global files. Intended for tests. */
139
- agentsHomeDir?: string;
140
116
  /** Override isolated linked-project discovery, for tests. */
141
117
  linkedFolderLoader?: LinkedFolderLoader;
142
118
  /**
@@ -355,105 +331,6 @@ function unique(values: string[]) {
355
331
  return [...new Set(values)];
356
332
  }
357
333
 
358
- async function existingDirectory(path: string) {
359
- const details = await stat(path).catch(() => undefined);
360
- return details?.isDirectory() ? path : undefined;
361
- }
362
-
363
- function metadataPaths(metadata: {
364
- files?: { items: Array<{ path?: string; relativePath?: string }> };
365
- }) {
366
- return (metadata.files?.items ?? [])
367
- .flatMap((file) => [file.path, file.relativePath])
368
- .filter((path): path is string => Boolean(path));
369
- }
370
-
371
- function ancestorFolders(folder: string) {
372
- const folders = [];
373
- for (
374
- let current = folder;
375
- dirname(current) !== current;
376
- current = dirname(current)
377
- ) {
378
- folders.push(current);
379
- }
380
- return folders;
381
- }
382
-
383
- async function linkedSkillRoots(
384
- folder: string,
385
- metadata: {
386
- files?: { items: Array<{ path?: string; relativePath?: string }> };
387
- },
388
- ) {
389
- const gtlNames = metadataPaths(metadata)
390
- .map((path) => path.split(/[\\/]\.gtl[\\/]/)[1])
391
- .filter((path): path is string => Boolean(path))
392
- .map((path) => path.split(/[\\/]/)[0])
393
- .filter((name): name is string => Boolean(name));
394
- const gtlRoots = gtlNames.flatMap((name) => [
395
- resolve(folder, ".gtl", name),
396
- ...ancestorFolders(folder).map((ancestor) => resolve(ancestor, name)),
397
- ]);
398
-
399
- const name = folder
400
- .replace(/[\\/]+$/, "")
401
- .split(/[\\/]/)
402
- .at(-1);
403
- const siblingIndex = name
404
- ? [
405
- resolve(dirname(folder), `${name}-index`),
406
- resolve(dirname(dirname(folder)), `${name}-index`),
407
- ]
408
- : [];
409
- const roots = await Promise.all(
410
- unique([...gtlRoots, ...siblingIndex]).map(existingDirectory),
411
- );
412
- return roots.filter((path): path is string => Boolean(path));
413
- }
414
-
415
- async function listFolderSkills(
416
- folder: string,
417
- metadata: {
418
- files?: { items: Array<{ path?: string; relativePath?: string }> };
419
- },
420
- ) {
421
- const roots = unique([folder, ...(await linkedSkillRoots(folder, metadata))]);
422
- const skills = await Promise.all(
423
- roots.map(async (root) => {
424
- const skillsFolder = resolve(root, ".agents", "skills");
425
- const entries = await readdir(skillsFolder, {
426
- withFileTypes: true,
427
- }).catch(() => []);
428
- return (
429
- await Promise.all(
430
- entries
431
- .filter(
432
- (entry) => entry.isDirectory() && !entry.name.startsWith("."),
433
- )
434
- .map(async (entry) => {
435
- const path = resolve(skillsFolder, entry.name);
436
- const skillPath = resolve(path, "SKILL.md");
437
- const details = await stat(skillPath).catch(() => undefined);
438
- if (!details?.isFile()) return undefined;
439
- const content = await readFile(skillPath, "utf8").catch(() => "");
440
- return {
441
- description: skillDescription(content),
442
- name: label(entry.name),
443
- openHref: openPathHref(path),
444
- relativePath: relative(root, path),
445
- source: skillSource(folder, root),
446
- };
447
- }),
448
- )
449
- ).filter((skill) => skill);
450
- }),
451
- );
452
- return unique(skills.flat().map((skill) => JSON.stringify(skill)))
453
- .map((skill) => JSON.parse(skill) as FolderSkill)
454
- .sort((left, right) => left.name.localeCompare(right.name));
455
- }
456
-
457
334
  function isMachineRoot(folder: string) {
458
335
  return folder === homedir() || dirname(folder) === folder;
459
336
  }
@@ -506,7 +383,6 @@ async function shallowMetadata(folder: string) {
506
383
  projects: { items: projects },
507
384
  schemaVersion: 3 as const,
508
385
  sessions: { items: [] },
509
- skills: { items: [] },
510
386
  };
511
387
  }
512
388
 
@@ -773,6 +649,7 @@ async function pluginAvailability(
773
649
  const items: Array<{
774
650
  count?: number;
775
651
  error?: string;
652
+ icon?: { paths: string[] };
776
653
  id: string;
777
654
  label: string;
778
655
  }> = [];
@@ -785,6 +662,7 @@ async function pluginAvailability(
785
662
  if (detection.status === "available") {
786
663
  items.push({
787
664
  count: detection.count,
665
+ icon: plugin.icon,
788
666
  id: plugin.id,
789
667
  label: plugin.label,
790
668
  });
@@ -794,6 +672,7 @@ async function pluginAvailability(
794
672
  // shell can surface it instead of treating it as absent content.
795
673
  items.push({
796
674
  error: detection.error,
675
+ icon: plugin.icon,
797
676
  id: plugin.id,
798
677
  label: plugin.label,
799
678
  });
@@ -811,7 +690,6 @@ export function createFolderApi({
811
690
  restrictToRoot = false,
812
691
  root,
813
692
  sessionFinding,
814
- agentsHomeDir,
815
693
  linkedFolderLoader,
816
694
  remoteAccess = false,
817
695
  lockedFile,
@@ -822,9 +700,7 @@ export function createFolderApi({
822
700
  listFolderSessions,
823
701
  readSessionTranscript,
824
702
  };
825
- const basePlugins = folderServerRegistryAsync({
826
- agentsHomeDir,
827
- });
703
+ const basePlugins = folderServerRegistryAsync();
828
704
  const baseExternalApiHandlers = externalApiHandlers();
829
705
  const assetFiles = new Map<
830
706
  string,
@@ -1374,14 +1250,11 @@ export function createFolderApi({
1374
1250
  request.headers["x-kennel-retry-linked"] === "1"
1375
1251
  )
1376
1252
  linkedDiscovery.retry(folder);
1377
- const discovery =
1378
- pluginId === "agents" || pluginId === "tasks"
1379
- ? linkedState(folder)
1380
- : undefined;
1253
+ const discovery = linkedState(folder);
1381
1254
  const result = await plugin.handle(
1382
1255
  {
1383
1256
  folder,
1384
- linkedFolders: discovery?.folders ?? [],
1257
+ linkedFolders: discovery.folders,
1385
1258
  },
1386
1259
  subpath,
1387
1260
  );
@@ -1395,7 +1268,7 @@ export function createFolderApi({
1395
1268
  folder,
1396
1269
  plugin: pluginId,
1397
1270
  status: "unavailable",
1398
- ...(discovery ? { linkedStatus: discovery.status } : {}),
1271
+ linkedStatus: discovery.status,
1399
1272
  }),
1400
1273
  );
1401
1274
  return;
@@ -1410,7 +1283,7 @@ export function createFolderApi({
1410
1283
  folder,
1411
1284
  plugin: pluginId,
1412
1285
  ...data,
1413
- ...(discovery ? { linkedStatus: discovery.status } : {}),
1286
+ linkedStatus: discovery.status,
1414
1287
  }),
1415
1288
  );
1416
1289
  } catch (error) {
@@ -1498,7 +1371,6 @@ export function createFolderApi({
1498
1371
  folder,
1499
1372
  projects: { items: [] },
1500
1373
  sessions: { items: [] },
1501
- skills: { items: [] },
1502
1374
  workspace: await readWorkspace(folder),
1503
1375
  },
1504
1376
  { "Cache-Control": "private, max-age=0, must-revalidate" },
@@ -1697,26 +1569,12 @@ export function createFolderApi({
1697
1569
  ? await shallowMetadata(folder)
1698
1570
  : await loadFolderData(folder);
1699
1571
 
1700
- if (part === "skills") {
1701
- respond({
1702
- folder: metadata.folder,
1703
- skills: {
1704
- items: await listFolderSkills(metadata.folder, metadata),
1705
- },
1706
- });
1707
- return;
1708
- }
1709
-
1710
- const [assets, skills] = await Promise.all([
1711
- hydrateAssets(metadata, assetFiles),
1712
- listFolderSkills(metadata.folder, metadata),
1713
- ]);
1572
+ const assets = await hydrateAssets(metadata, assetFiles);
1714
1573
  const body = {
1715
1574
  ...metadata,
1716
1575
  ...assets,
1717
1576
  sessions: { items: [] },
1718
1577
  files: await notesFiles(metadata.folder, metadata.files.items),
1719
- skills: { items: skills },
1720
1578
  };
1721
1579
  respond(body);
1722
1580
  } catch (error) {
@@ -4,17 +4,11 @@ import { pathToFileURL } from "node:url";
4
4
  import { existsSync } from "node:fs";
5
5
  import { mkdir, stat, writeFile } from "node:fs/promises";
6
6
  import { dirname, relative, resolve, sep } from "node:path";
7
- import { createAgentsPlugin } from "@raggle-ai/agents/server";
8
7
  import { FolderPluginFailure } from "kennel-plugins/server";
9
8
  import { createNotesPlugin } from "@raggle-ai/notes/server";
10
9
 
11
10
  export { FolderPluginFailure };
12
11
 
13
- export type FolderServerRegistryOptions = {
14
- /** Override the home directory used for Agent global files. Intended for tests. */
15
- agentsHomeDir?: string;
16
- };
17
-
18
12
  export type FolderPluginRegistryConfig = {
19
13
  plugins: {
20
14
  disable: string[];
@@ -44,22 +38,13 @@ export function configuredFolderServerRegistry(
44
38
  }
45
39
 
46
40
  /** Core server plugins. Installed plugins are loaded separately. */
47
- export function createFolderServerPlugins(
48
- options: FolderServerRegistryOptions = {},
49
- ): FolderPluginServer[] {
50
- return [
51
- createAgentsPlugin({
52
- homeDir: options.agentsHomeDir,
53
- }),
54
- createNotesPlugin(),
55
- ];
41
+ export function createFolderServerPlugins(): FolderPluginServer[] {
42
+ return [createNotesPlugin()];
56
43
  }
57
44
 
58
- export function folderServerRegistry(
59
- options: FolderServerRegistryOptions = {},
60
- ): Map<string, FolderPluginServer> {
45
+ export function folderServerRegistry(): Map<string, FolderPluginServer> {
61
46
  return new Map(
62
- createFolderServerPlugins(options).map((plugin) => [plugin.id, plugin]),
47
+ createFolderServerPlugins().map((plugin) => [plugin.id, plugin]),
63
48
  );
64
49
  }
65
50
 
@@ -107,17 +92,17 @@ function externalServerEntries(): ExternalServerEntry[] {
107
92
  });
108
93
  }
109
94
 
110
- export async function folderServerRegistryAsync(
111
- options: FolderServerRegistryOptions = {},
112
- ): Promise<Map<string, FolderPluginServer>> {
113
- const registry = folderServerRegistry(options);
95
+ export async function folderServerRegistryAsync(): Promise<
96
+ Map<string, FolderPluginServer>
97
+ > {
98
+ const registry = folderServerRegistry();
114
99
  for (const entry of externalServerEntries()) {
115
100
  if (registry.has(entry.id))
116
101
  throw new Error(`External plugin id collides with core plugin: ${entry.id}`);
117
102
  const imported = await import(pathToFileURL(entry.module).href);
118
103
  const value = imported[entry.symbol];
119
104
  const plugin =
120
- typeof value === "function" ? value(options) : value;
105
+ typeof value === "function" ? value() : value;
121
106
  if (!plugin || typeof plugin !== "object")
122
107
  throw new Error(`External plugin ${entry.id} did not export ${entry.symbol}`);
123
108
  const server = plugin as FolderPluginServer;
@@ -198,10 +183,10 @@ export type FolderPluginScaffoldResult = {
198
183
  */
199
184
  export async function scaffoldFolderPlugins(
200
185
  folder: string,
201
- options: FolderServerRegistryOptions & { force?: boolean; write?: boolean } = {},
186
+ options: { force?: boolean; write?: boolean } = {},
202
187
  ): Promise<FolderPluginScaffoldResult> {
203
188
  const root = resolve(folder);
204
- const registry = folderServerRegistry(options);
189
+ const registry = folderServerRegistry();
205
190
  const created: FolderPluginScaffoldFile[] = [];
206
191
  const skipped: FolderPluginScaffoldFile[] = [];
207
192
  const supported: FolderPluginScaffoldResult["supported"] = [];
@@ -1,6 +1,6 @@
1
1
  import type { FolderTab } from "@raggle-ai/ui/lib/folder-activity";
2
2
 
3
- export type ViewerSection = "notes" | "projects" | "sessions" | "skills";
3
+ export type ViewerSection = "notes" | "projects" | "sessions";
4
4
 
5
5
  export type ViewerPluginView = {
6
6
  pluginId: string;
@@ -30,7 +30,6 @@ const sectionTabs: Record<ViewerSection, FolderTab> = {
30
30
  notes: "files",
31
31
  projects: "projects",
32
32
  sessions: "sessions",
33
- skills: "skills",
34
33
  };
35
34
 
36
35
  import { folderPathId as encodeBase64Url } from "kennel-plugins/client";
@@ -51,7 +50,7 @@ function url(value: string) {
51
50
 
52
51
  function tabSection(tab: string | null): ViewerSection | undefined {
53
52
  if (tab === "files") return "notes";
54
- if (tab === "projects" || tab === "sessions" || tab === "skills") return tab;
53
+ if (tab === "projects" || tab === "sessions") return tab;
55
54
  return undefined;
56
55
  }
57
56
 
@@ -156,8 +155,7 @@ export function parseViewerRoute(value: string): ViewerRoute {
156
155
  if (
157
156
  section === "notes" ||
158
157
  section === "projects" ||
159
- section === "sessions" ||
160
- section === "skills"
158
+ section === "sessions"
161
159
  ) {
162
160
  return { ...route, section };
163
161
  }
@@ -303,7 +303,7 @@ export interface operations {
303
303
  parameters: {
304
304
  query?: {
305
305
  path?: string;
306
- part?: "notes" | "identity" | "discovery" | "workspace" | "collections" | "plugins" | "skills" | "sessions" | "calendar" | "schema";
306
+ part?: "notes" | "identity" | "discovery" | "workspace" | "collections" | "plugins" | "sessions" | "calendar" | "schema";
307
307
  notesScope?: "local" | "linked" | "all";
308
308
  id?: string;
309
309
  sort?: "alpha" | "recent";
@@ -348,6 +348,9 @@ export interface operations {
348
348
  items: {
349
349
  count?: number;
350
350
  error?: string;
351
+ icon?: {
352
+ paths: string[];
353
+ };
351
354
  id: string;
352
355
  label: string;
353
356
  }[];
@@ -373,16 +376,6 @@ export interface operations {
373
376
  title: string;
374
377
  }[];
375
378
  };
376
- skills?: {
377
- items: {
378
- description?: string;
379
- name: string;
380
- openHref?: string;
381
- relativePath: string;
382
- /** @enum {string} */
383
- source: "global" | "local";
384
- }[];
385
- };
386
379
  workspace?: {
387
380
  breadcrumbs: {
388
381
  icon?: {
@@ -608,22 +601,13 @@ export interface operations {
608
601
  items: {
609
602
  count?: number;
610
603
  error?: string;
604
+ icon?: {
605
+ paths: string[];
606
+ };
611
607
  id: string;
612
608
  label: string;
613
609
  }[];
614
610
  };
615
- } | {
616
- folder: string;
617
- skills: {
618
- items: {
619
- description?: string;
620
- name: string;
621
- openHref?: string;
622
- relativePath: string;
623
- /** @enum {string} */
624
- source: "global" | "local";
625
- }[];
626
- };
627
611
  } | {
628
612
  folder: string;
629
613
  assets: {
@@ -651,6 +635,9 @@ export interface operations {
651
635
  items: {
652
636
  count?: number;
653
637
  error?: string;
638
+ icon?: {
639
+ paths: string[];
640
+ };
654
641
  id: string;
655
642
  label: string;
656
643
  }[];
@@ -676,16 +663,6 @@ export interface operations {
676
663
  title: string;
677
664
  }[];
678
665
  };
679
- skills?: {
680
- items: {
681
- description?: string;
682
- name: string;
683
- openHref?: string;
684
- relativePath: string;
685
- /** @enum {string} */
686
- source: "global" | "local";
687
- }[];
688
- };
689
666
  workspace?: {
690
667
  breadcrumbs: {
691
668
  icon?: {
@@ -750,6 +727,9 @@ export interface operations {
750
727
  items: {
751
728
  count?: number;
752
729
  error?: string;
730
+ icon?: {
731
+ paths: string[];
732
+ };
753
733
  id: string;
754
734
  label: string;
755
735
  }[];
@@ -775,16 +755,6 @@ export interface operations {
775
755
  title: string;
776
756
  }[];
777
757
  };
778
- skills?: {
779
- items: {
780
- description?: string;
781
- name: string;
782
- openHref?: string;
783
- relativePath: string;
784
- /** @enum {string} */
785
- source: "global" | "local";
786
- }[];
787
- };
788
758
  workspace?: {
789
759
  breadcrumbs: {
790
760
  icon?: {
@@ -849,6 +819,9 @@ export interface operations {
849
819
  items: {
850
820
  count?: number;
851
821
  error?: string;
822
+ icon?: {
823
+ paths: string[];
824
+ };
852
825
  id: string;
853
826
  label: string;
854
827
  }[];
@@ -874,16 +847,6 @@ export interface operations {
874
847
  title: string;
875
848
  }[];
876
849
  };
877
- skills?: {
878
- items: {
879
- description?: string;
880
- name: string;
881
- openHref?: string;
882
- relativePath: string;
883
- /** @enum {string} */
884
- source: "global" | "local";
885
- }[];
886
- };
887
850
  workspace?: {
888
851
  breadcrumbs: {
889
852
  icon?: {
@@ -1,8 +1,8 @@
1
1
  # @raggle-ai/markdown
2
2
 
3
3
  Internal BarackDown re-export consumed by every Kennel package that renders
4
- Markdown (`packages/ui`, `packages/agents`, `packages/api`, `packages/dist`,
5
- `packages/core-components`, external plugins, `cli`, and `apps/web`). The
4
+ Markdown (`packages/ui`, `packages/api`, external plugins, `cli`, and
5
+ `apps/web`). The
6
6
  purpose of this tiny package is to be the single place where Kennel decides
7
7
  which BarkDown implementation it uses.
8
8
 
@@ -21,6 +21,7 @@
21
21
  "exports": {
22
22
  ".": "./src/index.ts",
23
23
  "./client": "./src/client.tsx",
24
+ "./data/use-folder-notes": "./src/data/use-folder-notes.ts",
24
25
  "./manifest": "./src/manifest.ts",
25
26
  "./parse": "./src/data/parse.ts",
26
27
  "./pages/single-note-page": "./src/pages/single-note-page.tsx",
@@ -4,7 +4,6 @@ import { notesManifest } from "./manifest.ts"
4
4
  import { NotesPage } from "./pages/notes-page.tsx"
5
5
 
6
6
  export { NotesPage } from "./pages/notes-page.tsx"
7
- export { useFolderNotes } from "./data/use-folder-notes.ts"
8
7
 
9
8
  export const notesClient = {
10
9
  ...notesManifest,
@@ -2,5 +2,12 @@ import type { FolderPluginManifest } from "kennel-plugins"
2
2
 
3
3
  export const notesManifest = {
4
4
  id: "notes",
5
+ icon: {
6
+ paths: [
7
+ "M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8Z",
8
+ "M14 2v6h6",
9
+ "M8 13h8M8 17h6",
10
+ ],
11
+ },
5
12
  label: "Notes",
6
13
  } satisfies FolderPluginManifest
@@ -50,7 +50,7 @@ export function NotesPage(): ReactElement {
50
50
  return (
51
51
  <>
52
52
  <FolderNotesList
53
- key={folderId}
53
+ key={`${folderId}:${filter.effectiveSelected}`}
54
54
  action={
55
55
  aiInput ? (
56
56
  <AiComposerAction
@@ -1,7 +1,13 @@
1
1
  import type { ReactNode } from "react"
2
+ import { useMemo, useState } from "react"
2
3
 
3
4
  import type { FolderFile } from "@raggle-ai/ui/lib/folder-activity"
5
+ import { Input } from "@raggle-ai/ui/components/input"
4
6
  import { NoteCard } from "@raggle-ai/ui/components/note-card"
7
+ import { OffsetPagination } from "@raggle-ai/ui/components/pagination"
8
+
9
+ const pageSize = 25
10
+ const emptyFiles: FolderFile[] = []
5
11
 
6
12
  /**
7
13
  * Note rows for the main Notes tab, backed by the shared NoteCard so linked
@@ -11,7 +17,7 @@ export function FolderNotesList({
11
17
  action,
12
18
  documentHref,
13
19
  error,
14
- files = [],
20
+ files = emptyFiles,
15
21
  filter,
16
22
  folder,
17
23
  folderHref,
@@ -26,13 +32,33 @@ export function FolderNotesList({
26
32
  folderHref: (folder: string) => string
27
33
  loading: boolean
28
34
  }) {
29
- const visible = [...files].sort(
30
- (left, right) =>
31
- right.activityAt - left.activityAt ||
32
- `${left.folder ?? ""}/${left.relativePath}`.localeCompare(
33
- `${right.folder ?? ""}/${right.relativePath}`,
35
+ const [offset, setOffset] = useState(0)
36
+ const [search, setSearch] = useState("")
37
+ const sorted = useMemo(
38
+ () =>
39
+ [...files].sort(
40
+ (left, right) =>
41
+ right.activityAt - left.activityAt ||
42
+ `${left.folder ?? ""}/${left.relativePath}`.localeCompare(
43
+ `${right.folder ?? ""}/${right.relativePath}`,
44
+ ),
34
45
  ),
46
+ [files],
47
+ )
48
+ const matching = useMemo(() => {
49
+ const query = search.trim().toLowerCase()
50
+ if (!query) return sorted
51
+ return sorted.filter((file) =>
52
+ `${file.name}\n${file.folder ?? ""}/${file.relativePath}`
53
+ .toLowerCase()
54
+ .includes(query),
55
+ )
56
+ }, [sorted, search])
57
+ const effectiveOffset = Math.min(
58
+ offset,
59
+ Math.max(0, Math.ceil(matching.length / pageSize) - 1) * pageSize,
35
60
  )
61
+ const visible = matching.slice(effectiveOffset, effectiveOffset + pageSize)
36
62
 
37
63
  return (
38
64
  <div className="mx-auto grid w-full max-w-3xl gap-4 px-4 py-5">
@@ -40,12 +66,22 @@ export function FolderNotesList({
40
66
  <h1 className="m-0 text-2xl font-semibold text-[#233025]">Notes</h1>
41
67
  {action}
42
68
  </div>
69
+ <Input
70
+ type="search"
71
+ aria-label="Search notes by name or path"
72
+ placeholder="Search notes by name or path…"
73
+ value={search}
74
+ onChange={(event) => {
75
+ setSearch(event.target.value)
76
+ setOffset(0)
77
+ }}
78
+ />
43
79
  {filter}
44
80
  {loading ? <p className="m-0 text-sm text-[#6c7a6d]">Loading…</p> : null}
45
81
  {error ? <p className="m-0 text-sm text-[#9c3a3a]">{error}</p> : null}
46
82
  {!loading && !error && !visible.length ? (
47
83
  <p className="m-0 text-sm text-[#6c7a6d]">
48
- {files.length ? "No notes match this filter." : "No notes found."}
84
+ {search.trim() ? "No notes match this search." : "No notes found."}
49
85
  </p>
50
86
  ) : null}
51
87
  {visible.length ? (
@@ -71,6 +107,15 @@ export function FolderNotesList({
71
107
  ))}
72
108
  </ol>
73
109
  ) : null}
110
+ {matching.length ? (
111
+ <OffsetPagination
112
+ label="Notes"
113
+ total={matching.length}
114
+ offset={effectiveOffset}
115
+ pageSize={pageSize}
116
+ onOffsetChange={setOffset}
117
+ />
118
+ ) : null}
74
119
  </div>
75
120
  )
76
121
  }