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
@@ -17027,8 +17027,8 @@ var require__2020 = __commonJS(function(exports, module) {
17027
17027
  import {
17028
17028
  createServer
17029
17029
  } from "node:http";
17030
- import { readFile as readFile9, realpath as realpath5, stat as stat8 } from "node:fs/promises";
17031
- import { extname as extname7, isAbsolute as isAbsolute7, relative as relative11, resolve as resolve12 } from "node:path";
17030
+ import { readFile as readFile11, realpath as realpath8, stat as stat10 } from "node:fs/promises";
17031
+ import { extname as extname7, isAbsolute as isAbsolute8, relative as relative12, resolve as resolve13 } from "node:path";
17032
17032
 
17033
17033
  // packages/api/src/server/watcher.ts
17034
17034
  import { existsSync, statSync, watch } from "node:fs";
@@ -21993,16 +21993,16 @@ function sendContract(response, url, body, headers = {}) {
21993
21993
 
21994
21994
  // packages/api/src/server/folder-metadata-plugin.ts
21995
21995
  import { realpathSync as realpathSync2 } from "node:fs";
21996
- import { readdir as readdir5, readFile as readFile8, stat as stat7 } from "node:fs/promises";
21997
- import { homedir as homedir3 } from "node:os";
21996
+ import { readdir as readdir6, readFile as readFile10, realpath as realpath7, stat as stat9 } from "node:fs/promises";
21997
+ import { homedir as homedir4 } from "node:os";
21998
21998
  import {
21999
- basename as basename6,
22000
- dirname as dirname9,
21999
+ basename as basename7,
22000
+ dirname as dirname10,
22001
22001
  extname as extname6,
22002
- isAbsolute as isAbsolute6,
22003
- relative as relative10,
22004
- resolve as resolve11,
22005
- sep as sep7
22002
+ isAbsolute as isAbsolute7,
22003
+ relative as relative11,
22004
+ resolve as resolve12,
22005
+ sep as sep8
22006
22006
  } from "node:path";
22007
22007
 
22008
22008
  // node_modules/.bun/@raggle-ai+barkdown@0.7.1+22117d1132b05fb8/node_modules/@raggle-ai/barkdown/dist/react.js
@@ -35561,7 +35561,7 @@ async function readFolder(root) {
35561
35561
  }
35562
35562
 
35563
35563
  // packages/api/src/server/folder-metadata-plugin.ts
35564
- import { execFile as execFile4 } from "node:child_process";
35564
+ import { execFile as execFile5 } from "node:child_process";
35565
35565
 
35566
35566
  // packages/plugins/src/endpoint.ts
35567
35567
  var statusByCode = {
@@ -36688,19 +36688,215 @@ function createSchemaCollectionServer(host, options = {}) {
36688
36688
  // packages/api/src/server/folder-metadata-plugin.ts
36689
36689
  import local from "@raggle-ai/local";
36690
36690
 
36691
- // packages/api/src/server/entity-folder.ts
36692
- import { readdir as readdir3, readFile as readFile4, realpath as realpath3, stat as stat3 } from "node:fs/promises";
36691
+ // packages/notes/src/store.ts
36692
+ import { execFile } from "node:child_process";
36693
+ import {
36694
+ mkdir as mkdir2,
36695
+ readdir as readdir3,
36696
+ readFile as readFile4,
36697
+ realpath as realpath3,
36698
+ rename as rename2,
36699
+ rm,
36700
+ stat as stat3,
36701
+ writeFile as writeFile2
36702
+ } from "node:fs/promises";
36693
36703
  import { homedir as homedir2 } from "node:os";
36704
+ import { basename as basename4, dirname as dirname5, join as join2, relative as relative6, resolve as resolve6, sep as sep4 } from "node:path";
36705
+ import { promisify } from "node:util";
36706
+ var run = promisify(execFile);
36707
+ var REPOSITORY_ID_KEY = "kennel.repoId";
36708
+ var UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/u;
36709
+ var markdownExtensions = new Set([".md", ".markdown"]);
36710
+ var caseInsensitiveHosts = new Set(["github.com", "gitlab.com", "bitbucket.org"]);
36711
+ class RepositoryNotesError extends Error {
36712
+ code;
36713
+ constructor(code, message) {
36714
+ super(message);
36715
+ this.name = "RepositoryNotesError";
36716
+ this.code = code;
36717
+ }
36718
+ }
36719
+ function kennelConfigHome() {
36720
+ const xdg = process.env.XDG_CONFIG_HOME?.trim();
36721
+ return xdg ? xdg : join2(homedir2(), ".config");
36722
+ }
36723
+ function kennelConfigFile() {
36724
+ return join2(kennelConfigHome(), "kennel", "config.json");
36725
+ }
36726
+ function repositoryNotesRoot() {
36727
+ return join2(kennelConfigHome(), "kennel", "repos");
36728
+ }
36729
+ function isRepositoryId(value) {
36730
+ return typeof value === "string" && UUID.test(value);
36731
+ }
36732
+ function managedNotesDirectory(id) {
36733
+ if (!isRepositoryId(id)) {
36734
+ throw new RepositoryNotesError("invalid-id", `Invalid repository id: ${id}`);
36735
+ }
36736
+ return join2(repositoryNotesRoot(), id);
36737
+ }
36738
+ async function readRepositoryNotesSettings() {
36739
+ const file = kennelConfigFile();
36740
+ let text;
36741
+ try {
36742
+ text = await readFile4(file, "utf8");
36743
+ } catch (error) {
36744
+ if (error.code === "ENOENT") {
36745
+ return { enabled: false };
36746
+ }
36747
+ throw new RepositoryNotesError("config-invalid", `Cannot read ${file}: ${errorMessage(error)}`);
36748
+ }
36749
+ let value;
36750
+ try {
36751
+ value = JSON.parse(text);
36752
+ } catch (error) {
36753
+ throw new RepositoryNotesError("config-invalid", `Invalid JSON in ${file}: ${errorMessage(error)}`);
36754
+ }
36755
+ if (!isRecord(value)) {
36756
+ throw new RepositoryNotesError("config-invalid", `${file} must hold an object`);
36757
+ }
36758
+ const section = value.repositoryNotes;
36759
+ if (section === undefined)
36760
+ return { enabled: false };
36761
+ if (!isRecord(section) || section.enabled !== undefined && typeof section.enabled !== "boolean") {
36762
+ throw new RepositoryNotesError("config-invalid", `${file}: repositoryNotes must be an object with a boolean "enabled" field`);
36763
+ }
36764
+ return { enabled: section.enabled === true };
36765
+ }
36766
+ async function git(args, cwd) {
36767
+ const { stdout } = await run("git", args, {
36768
+ cwd,
36769
+ env: { ...process.env, GIT_OPTIONAL_LOCKS: "0", GIT_TERMINAL_PROMPT: "0" },
36770
+ maxBuffer: 1024 * 1024
36771
+ });
36772
+ return stdout.replace(/\r?\n$/u, "");
36773
+ }
36774
+ function gitExitCode(error) {
36775
+ const code = error.code;
36776
+ return typeof code === "number" ? code : undefined;
36777
+ }
36778
+ async function resolveCheckout(folder) {
36779
+ let start;
36780
+ try {
36781
+ start = await realpath3(resolve6(folder));
36782
+ if (!(await stat3(start)).isDirectory())
36783
+ start = dirname5(start);
36784
+ } catch {
36785
+ throw new RepositoryNotesError("not-git", `Folder does not exist: ${folder}`);
36786
+ }
36787
+ let output;
36788
+ try {
36789
+ output = await git(["rev-parse", "--path-format=absolute", "--show-toplevel", "--git-common-dir"], start);
36790
+ } catch (error) {
36791
+ if (gitExitCode(error) === 128) {
36792
+ throw new RepositoryNotesError("not-git", `Not a Git repository: ${start}. Repository notes need a Git checkout.`);
36793
+ }
36794
+ throw error;
36795
+ }
36796
+ const [top, common] = output.split(/\r?\n/u);
36797
+ if (!top || !common) {
36798
+ throw new RepositoryNotesError("not-git", `Not a Git work tree: ${start}`);
36799
+ }
36800
+ const root = await realpath3(top);
36801
+ const commonDir = await realpath3(common);
36802
+ return { root, commonDir, configFile: join2(commonDir, "config") };
36803
+ }
36804
+ async function readBinding(checkout) {
36805
+ let value;
36806
+ try {
36807
+ value = await git(["config", "--file", checkout.configFile, "--get", REPOSITORY_ID_KEY], checkout.root);
36808
+ } catch (error) {
36809
+ if (gitExitCode(error) === 1)
36810
+ return;
36811
+ throw error;
36812
+ }
36813
+ const id = value.trim();
36814
+ if (!id)
36815
+ return;
36816
+ if (!isRepositoryId(id)) {
36817
+ throw new RepositoryNotesError("invalid-binding", `${checkout.configFile} has an invalid ${REPOSITORY_ID_KEY} value. Remove it or run "kennel notes new-identity".`);
36818
+ }
36819
+ return id;
36820
+ }
36821
+ function isRecord(value) {
36822
+ return Boolean(value) && typeof value === "object" && !Array.isArray(value);
36823
+ }
36824
+ function errorMessage(error) {
36825
+ return error instanceof Error ? error.message : String(error);
36826
+ }
36827
+ async function lookupRepositoryNotes(folder) {
36828
+ let checkout;
36829
+ try {
36830
+ checkout = await resolveCheckout(folder);
36831
+ } catch (error) {
36832
+ if (error instanceof RepositoryNotesError && error.code === "not-git") {
36833
+ return { status: "not-git", folder };
36834
+ }
36835
+ throw error;
36836
+ }
36837
+ const id = await readBinding(checkout);
36838
+ if (!id)
36839
+ return { status: "unregistered", checkout, root: checkout.root };
36840
+ return {
36841
+ status: "registered",
36842
+ checkout,
36843
+ id,
36844
+ notesDirectory: managedNotesDirectory(id),
36845
+ root: checkout.root
36846
+ };
36847
+ }
36848
+ async function listManagedNotes(directory) {
36849
+ const notes = [];
36850
+ const visit = async (current) => {
36851
+ let entries;
36852
+ try {
36853
+ entries = await readdir3(current, { withFileTypes: true });
36854
+ } catch (error) {
36855
+ if (error.code === "ENOENT")
36856
+ return;
36857
+ throw error;
36858
+ }
36859
+ for (const entry of entries) {
36860
+ if (entry.name.startsWith(".") || entry.isSymbolicLink())
36861
+ continue;
36862
+ const path = join2(current, entry.name);
36863
+ if (entry.isDirectory()) {
36864
+ await visit(path);
36865
+ continue;
36866
+ }
36867
+ if (!entry.isFile())
36868
+ continue;
36869
+ const dot = entry.name.lastIndexOf(".");
36870
+ const extension = dot === -1 ? "" : entry.name.slice(dot).toLowerCase();
36871
+ if (!markdownExtensions.has(extension))
36872
+ continue;
36873
+ const details = await stat3(path);
36874
+ notes.push({
36875
+ activityAt: details.mtimeMs,
36876
+ name: dot === -1 ? entry.name : entry.name.slice(0, dot),
36877
+ path,
36878
+ relativePath: relative6(directory, path).split(sep4).join("/"),
36879
+ size: details.size
36880
+ });
36881
+ }
36882
+ };
36883
+ await visit(directory);
36884
+ return notes.sort((left, right) => right.activityAt - left.activityAt);
36885
+ }
36886
+
36887
+ // packages/api/src/server/entity-folder.ts
36888
+ import { readdir as readdir4, readFile as readFile5, realpath as realpath4, stat as stat4 } from "node:fs/promises";
36889
+ import { homedir as homedir3 } from "node:os";
36694
36890
  import {
36695
- basename as basename4,
36696
- dirname as dirname5,
36891
+ basename as basename5,
36892
+ dirname as dirname6,
36697
36893
  extname as extname2,
36698
36894
  isAbsolute as isAbsolute4,
36699
- join as join2,
36700
- relative as relative6,
36701
- resolve as resolve6
36895
+ join as join3,
36896
+ relative as relative7,
36897
+ resolve as resolve7
36702
36898
  } from "node:path";
36703
- var CONFIG_FILE = join2("kennel", "config.json");
36899
+ var CONFIG_FILE = join3("kennel", "config.json");
36704
36900
  var IGNORED_DIRECTORIES = new Set([
36705
36901
  ".git",
36706
36902
  ".gtl",
@@ -36741,11 +36937,11 @@ function paragraph(body) {
36741
36937
  }
36742
36938
  function configPath() {
36743
36939
  const xdg = process.env.XDG_CONFIG_HOME?.trim();
36744
- return xdg ? join2(xdg, CONFIG_FILE) : join2(homedir2(), ".config", CONFIG_FILE);
36940
+ return xdg ? join3(xdg, CONFIG_FILE) : join3(homedir3(), ".config", CONFIG_FILE);
36745
36941
  }
36746
36942
  async function readConfig(path) {
36747
36943
  try {
36748
- return parseConfig(await readFile4(path, "utf8"));
36944
+ return parseConfig(await readFile5(path, "utf8"));
36749
36945
  } catch (error) {
36750
36946
  if (error && typeof error === "object" && "code" in error && error.code === "ENOENT")
36751
36947
  return null;
@@ -36762,6 +36958,8 @@ function parseConfig(source) {
36762
36958
  if (!value || typeof value !== "object" || Array.isArray(value))
36763
36959
  throw new Error("Invalid Kennel entity root config");
36764
36960
  const roots = value.allowedRoots;
36961
+ if (roots === undefined)
36962
+ return {};
36765
36963
  if (!Array.isArray(roots))
36766
36964
  throw new Error("Invalid Kennel entity root config");
36767
36965
  const allowedRoots = roots.map((root) => {
@@ -36773,12 +36971,12 @@ function parseConfig(source) {
36773
36971
  }
36774
36972
  async function allowedRoots(currentWorkspaceRoot) {
36775
36973
  const config = await readConfig(configPath());
36776
- if (config)
36974
+ if (config?.allowedRoots)
36777
36975
  return config.allowedRoots;
36778
36976
  return currentWorkspaceRoot?.trim() ? [currentWorkspaceRoot] : [];
36779
36977
  }
36780
36978
  function inside4(root, path) {
36781
- const local = relative6(root, path);
36979
+ const local = relative7(root, path);
36782
36980
  return local === "" || !local.startsWith("..") && !isAbsolute4(local);
36783
36981
  }
36784
36982
  async function normalizeRoot(rootPath, currentWorkspaceRoot) {
@@ -36786,13 +36984,13 @@ async function normalizeRoot(rootPath, currentWorkspaceRoot) {
36786
36984
  const fallback = currentWorkspaceRoot?.trim();
36787
36985
  if (!trimmed && !fallback)
36788
36986
  return null;
36789
- const requested = resolve6(trimmed || fallback);
36790
- const resolved = await realpath3(requested);
36791
- const info = await stat3(resolved);
36987
+ const requested = resolve7(trimmed || fallback);
36988
+ const resolved = await realpath4(requested);
36989
+ const info = await stat4(resolved);
36792
36990
  if (!info.isDirectory())
36793
36991
  throw new Error(`${resolved} is not a folder`);
36794
36992
  const roots = await allowedRoots(currentWorkspaceRoot);
36795
- const realAllowedRoots = await Promise.all(roots.map((root) => realpath3(root)));
36993
+ const realAllowedRoots = await Promise.all(roots.map((root) => realpath4(root)));
36796
36994
  const allowed = realAllowedRoots.some((root) => inside4(root, resolved));
36797
36995
  if (!allowed)
36798
36996
  throw new Error("Entity root is outside the configured roots");
@@ -36801,11 +36999,11 @@ async function normalizeRoot(rootPath, currentWorkspaceRoot) {
36801
36999
  async function markdownFiles(folderPath) {
36802
37000
  const files = [];
36803
37001
  async function walk(currentPath) {
36804
- const entries = await readdir3(currentPath, { withFileTypes: true });
37002
+ const entries = await readdir4(currentPath, { withFileTypes: true });
36805
37003
  for (const entry of entries.sort((left, right) => left.name.localeCompare(right.name))) {
36806
37004
  if (entry.name.startsWith(".") || IGNORED_DIRECTORIES.has(entry.name))
36807
37005
  continue;
36808
- const entryPath = join2(currentPath, entry.name);
37006
+ const entryPath = join3(currentPath, entry.name);
36809
37007
  if (entry.isDirectory()) {
36810
37008
  if (entryPath !== folderPath && await hasIndex(entryPath))
36811
37009
  continue;
@@ -36820,36 +37018,36 @@ async function markdownFiles(folderPath) {
36820
37018
  return files;
36821
37019
  }
36822
37020
  async function hasIndex(folderPath) {
36823
- return stat3(join2(folderPath, "index.md")).then((value) => value.isFile()).catch(() => false);
37021
+ return stat4(join3(folderPath, "index.md")).then((value) => value.isFile()).catch(() => false);
36824
37022
  }
36825
37023
  async function schemaPath(folderPath, rootPath) {
36826
37024
  let current = folderPath;
36827
37025
  while (current === rootPath || current.startsWith(`${rootPath}/`)) {
36828
37026
  for (const name of ["schema.json", "_schema.json"]) {
36829
- const candidate = join2(current, name);
36830
- if (await stat3(candidate).then((value) => value.isFile()).catch(() => false))
37027
+ const candidate = join3(current, name);
37028
+ if (await stat4(candidate).then((value) => value.isFile()).catch(() => false))
36831
37029
  return candidate;
36832
37030
  }
36833
37031
  if (current === rootPath)
36834
37032
  return null;
36835
- current = dirname5(current);
37033
+ current = dirname6(current);
36836
37034
  }
36837
37035
  return null;
36838
37036
  }
36839
37037
  async function readEntity(folderPath, rootPath) {
36840
- const indexPath = join2(folderPath, "index.md");
36841
- const index = parseMarkdown(await readFile4(indexPath, "utf8"));
37038
+ const indexPath = join3(folderPath, "index.md");
37039
+ const index = parseMarkdown(await readFile5(indexPath, "utf8"));
36842
37040
  const files = await markdownFiles(folderPath);
36843
37041
  const documents = await Promise.all(files.map(async (filePath) => {
36844
- const document2 = parseMarkdown(await readFile4(filePath, "utf8"));
37042
+ const document2 = parseMarkdown(await readFile5(filePath, "utf8"));
36845
37043
  return {
36846
37044
  content: document2.body,
36847
- path: relative6(folderPath, filePath),
36848
- title: stringValue(document2.frontmatter.title) ?? heading(document2.body) ?? basename4(filePath, extname2(filePath))
37045
+ path: relative7(folderPath, filePath),
37046
+ title: stringValue(document2.frontmatter.title) ?? heading(document2.body) ?? basename5(filePath, extname2(filePath))
36849
37047
  };
36850
37048
  }));
36851
- const title = stringValue(index.frontmatter.title) ?? heading(index.body) ?? basename4(folderPath);
36852
- const relativePath = relative6(rootPath, folderPath) || ".";
37049
+ const title = stringValue(index.frontmatter.title) ?? heading(index.body) ?? basename5(folderPath);
37050
+ const relativePath = relative7(rootPath, folderPath) || ".";
36853
37051
  return {
36854
37052
  aliases: stringValues(index.frontmatter.aliases),
36855
37053
  content: [
@@ -36877,11 +37075,11 @@ async function discover(rootPath) {
36877
37075
  async function walk(folderPath) {
36878
37076
  if (await hasIndex(folderPath))
36879
37077
  folders.push(folderPath);
36880
- const entries = await readdir3(folderPath, { withFileTypes: true });
37078
+ const entries = await readdir4(folderPath, { withFileTypes: true });
36881
37079
  for (const entry of entries) {
36882
37080
  if (!entry.isDirectory() || entry.name.startsWith(".") || IGNORED_DIRECTORIES.has(entry.name))
36883
37081
  continue;
36884
- await walk(join2(folderPath, entry.name));
37082
+ await walk(join3(folderPath, entry.name));
36885
37083
  }
36886
37084
  }
36887
37085
  await walk(rootPath);
@@ -36921,20 +37119,20 @@ async function loadEntityWorkspace(root, query, selectedPath, currentWorkspaceRo
36921
37119
  // packages/api/src/server/api/document-edit.ts
36922
37120
  import { randomUUID as randomUUID2 } from "node:crypto";
36923
37121
  import {
36924
- readFile as readFile5,
36925
- realpath as realpath4,
36926
- rename as rename2,
36927
- stat as stat4,
37122
+ readFile as readFile6,
37123
+ realpath as realpath5,
37124
+ rename as rename3,
37125
+ stat as stat5,
36928
37126
  unlink as unlink2,
36929
- writeFile as writeFile2
37127
+ writeFile as writeFile3
36930
37128
  } from "node:fs/promises";
36931
37129
  import {
36932
- dirname as dirname6,
37130
+ dirname as dirname7,
36933
37131
  extname as extname3,
36934
37132
  isAbsolute as isAbsolute5,
36935
- relative as relative7,
36936
- resolve as resolve7,
36937
- sep as sep4
37133
+ relative as relative8,
37134
+ resolve as resolve8,
37135
+ sep as sep5
36938
37136
  } from "node:path";
36939
37137
 
36940
37138
  // packages/api/src/shared/selection.ts
@@ -36952,10 +37150,10 @@ async function editDocument(folder, input) {
36952
37150
  throw apiError("bad-request", "Invalid document edit.");
36953
37151
  if (![".md", ".markdown"].includes(extname3(path).toLowerCase()))
36954
37152
  throw apiError("bad-request", "Only Markdown notes can be edited.");
36955
- const root = await realpath4(folder);
36956
- const target = await realpath4(resolve7(root, path));
36957
- const local = relative7(root, target);
36958
- if (!local || local === ".." || local.startsWith(`..${sep4}`) || isAbsolute5(local))
37153
+ const root = await realpath5(folder);
37154
+ const target = await realpath5(resolve8(root, path));
37155
+ const local = relative8(root, target);
37156
+ if (!local || local === ".." || local.startsWith(`..${sep5}`) || isAbsolute5(local))
36959
37157
  throw apiError("forbidden", "The document must be inside its folder.");
36960
37158
  let content;
36961
37159
  try {
@@ -36965,20 +37163,20 @@ async function editDocument(folder, input) {
36965
37163
  }
36966
37164
  const previous = pending.get(target) ?? Promise.resolve();
36967
37165
  const operation = previous.catch(() => {}).then(async () => {
36968
- const details = await stat4(target);
37166
+ const details = await stat5(target);
36969
37167
  if (!details.isFile() || details.nlink !== 1)
36970
37168
  throw apiError("forbidden", "Only regular, unlinked files can be edited.");
36971
- if (await readFile5(target, "utf8") !== original)
37169
+ if (await readFile6(target, "utf8") !== original)
36972
37170
  throw apiError("conflict", "This note changed on disk. Close this preview, reload the note, and try again.");
36973
- const temporary = resolve7(dirname6(target), `.kennel-edit-${randomUUID2()}.tmp`);
37171
+ const temporary = resolve8(dirname7(target), `.kennel-edit-${randomUUID2()}.tmp`);
36974
37172
  try {
36975
- await writeFile2(temporary, content, {
37173
+ await writeFile3(temporary, content, {
36976
37174
  flag: "wx",
36977
37175
  mode: details.mode & 511
36978
37176
  });
36979
- if (await readFile5(target, "utf8") !== original)
37177
+ if (await readFile6(target, "utf8") !== original)
36980
37178
  throw apiError("conflict", "This note changed on disk. Reload before applying an edit.");
36981
- await rename2(temporary, target);
37179
+ await rename3(temporary, target);
36982
37180
  } finally {
36983
37181
  await unlink2(temporary).catch(() => {});
36984
37182
  }
@@ -37001,7 +37199,7 @@ var documentEditRoute = {
37001
37199
  throw apiError("bad-request", "Invalid document edit.");
37002
37200
  const input = parsed.data;
37003
37201
  const folder = context.authorize(input.folder);
37004
- if (context.lockedFile && (typeof input.path !== "string" || await realpath4(resolve7(folder, input.path)) !== context.lockedFile))
37202
+ if (context.lockedFile && (typeof input.path !== "string" || await realpath5(resolve8(folder, input.path)) !== context.lockedFile))
37005
37203
  throw apiError("forbidden", "Only the selected document can be edited.");
37006
37204
  await editDocument(folder, input);
37007
37205
  jsonResponse(response, 200, routes.documentEdit.response.parse({ saved: true }));
@@ -37009,8 +37207,8 @@ var documentEditRoute = {
37009
37207
  };
37010
37208
 
37011
37209
  // packages/api/src/server/api/create-agent-file.ts
37012
- import { writeFile as writeFile3 } from "node:fs/promises";
37013
- import { resolve as resolve8 } from "node:path";
37210
+ import { writeFile as writeFile4 } from "node:fs/promises";
37211
+ import { resolve as resolve9 } from "node:path";
37014
37212
  var createAgentFileRoute = {
37015
37213
  method: "POST",
37016
37214
  maxBodyBytes: 0,
@@ -37021,9 +37219,9 @@ var createAgentFileRoute = {
37021
37219
  } catch (error) {
37022
37220
  throw apiError("not-found", error instanceof Error ? error.message : "Folder not found");
37023
37221
  }
37024
- const target = resolve8(folder, "AGENTS.md");
37222
+ const target = resolve9(folder, "AGENTS.md");
37025
37223
  try {
37026
- await writeFile3(target, "", { flag: "wx" });
37224
+ await writeFile4(target, "", { flag: "wx" });
37027
37225
  } catch (error) {
37028
37226
  if (error && typeof error === "object" && "code" in error && error.code === "EEXIST") {
37029
37227
  throw apiError("conflict", "AGENTS.md already exists in this folder");
@@ -37035,7 +37233,7 @@ var createAgentFileRoute = {
37035
37233
  };
37036
37234
 
37037
37235
  // packages/api/src/server/api/open-path.ts
37038
- import { execFile } from "node:child_process";
37236
+ import { execFile as execFile2 } from "node:child_process";
37039
37237
  var openPathRoute = {
37040
37238
  method: "GET",
37041
37239
  async handle({ context, query, response }) {
@@ -37046,7 +37244,7 @@ var openPathRoute = {
37046
37244
  throw apiError("not-found", error instanceof Error ? error.message : "Path not found");
37047
37245
  }
37048
37246
  await new Promise((done, fail) => {
37049
- execFile("/usr/bin/open", [target], (error) => {
37247
+ execFile2("/usr/bin/open", [target], (error) => {
37050
37248
  if (error)
37051
37249
  fail(apiError("failed", "Could not open path"));
37052
37250
  else
@@ -37059,11 +37257,11 @@ var openPathRoute = {
37059
37257
  };
37060
37258
 
37061
37259
  // packages/api/src/server/folder-favicon.ts
37062
- import { readFile as readFile6 } from "node:fs/promises";
37063
- import { join as join3 } from "node:path";
37260
+ import { readFile as readFile7 } from "node:fs/promises";
37261
+ import { join as join4 } from "node:path";
37064
37262
  async function folderWebsite(folder) {
37065
37263
  for (const name of ["AGENTS.md", "README.md"]) {
37066
- const content = await readFile6(join3(folder, name), "utf8").catch(() => "");
37264
+ const content = await readFile7(join4(folder, name), "utf8").catch(() => "");
37067
37265
  const entry = markdownMetadata(content).entries.find(([key]) => key === "website");
37068
37266
  if (entry)
37069
37267
  return websiteHost(entry[1]);
@@ -37073,7 +37271,7 @@ async function folderWebsite(folder) {
37073
37271
 
37074
37272
  // packages/api/src/server/folder-icons.ts
37075
37273
  import { createHash as createHash4 } from "node:crypto";
37076
- import { readFile as readFile7, stat as stat5 } from "node:fs/promises";
37274
+ import { readFile as readFile8, stat as stat6 } from "node:fs/promises";
37077
37275
  import { extname as extname4 } from "node:path";
37078
37276
  import { discoverFolderIcon } from "@raggle-ai/local";
37079
37277
  var maxIconBytes2 = 4 * 1024 * 1024;
@@ -37112,13 +37310,13 @@ function iconUrl(folder, hash, mediaType) {
37112
37310
  async function folderIcon(folder, assets, assetFiles, fallback) {
37113
37311
  const icon = discoverFolderIcon(folder);
37114
37312
  const mediaType = icon ? iconTypes.get(extname4(icon).toLowerCase()) : null;
37115
- const details = icon && mediaType ? await stat5(icon).catch(() => {
37313
+ const details = icon && mediaType ? await stat6(icon).catch(() => {
37116
37314
  return;
37117
37315
  }) : undefined;
37118
37316
  if (!icon || !mediaType || !details?.size || details.size > maxIconBytes2) {
37119
37317
  return fallback ? await fallback(folder, assets) : undefined;
37120
37318
  }
37121
- const data = await readFile7(icon);
37319
+ const data = await readFile8(icon);
37122
37320
  const hash = assetKey(data);
37123
37321
  const key = `local:${hash}`;
37124
37322
  assetFiles.set(hash, { mediaType, path: icon });
@@ -37137,12 +37335,12 @@ async function searchIconDataUrl(icon) {
37137
37335
  const mediaType = searchIconTypes.get(extname4(icon).toLowerCase());
37138
37336
  if (!mediaType)
37139
37337
  return;
37140
- const details = await stat5(icon).catch(() => {
37338
+ const details = await stat6(icon).catch(() => {
37141
37339
  return;
37142
37340
  });
37143
37341
  if (!details?.size || details.size > maxSearchIconBytes)
37144
37342
  return;
37145
- return `data:${mediaType};base64,${(await readFile7(icon)).toString("base64")}`;
37343
+ return `data:${mediaType};base64,${(await readFile8(icon)).toString("base64")}`;
37146
37344
  }
37147
37345
  async function faviconAsset(folderName, assets) {
37148
37346
  if (process.env.KENNEL_EXTERNAL_ICONS !== "1")
@@ -37188,11 +37386,19 @@ async function workspaceBreadcrumbIcons(breadcrumbs, assetFiles, assets = {}) {
37188
37386
  // packages/api/src/server/plugins.ts
37189
37387
  import { pathToFileURL } from "node:url";
37190
37388
  import { existsSync as existsSync3 } from "node:fs";
37191
- import { dirname as dirname7, resolve as resolve10, sep as sep6 } from "node:path";
37389
+ import { mkdir as mkdir3, readFile as readFile9, stat as stat8, writeFile as writeFile5 } from "node:fs/promises";
37390
+ import { dirname as dirname8, resolve as resolve11, sep as sep7 } from "node:path";
37192
37391
 
37193
37392
  // packages/notes/src/server.ts
37194
- import { readdir as readdir4, stat as stat6 } from "node:fs/promises";
37195
- import { basename as basename5, extname as extname5, relative as relative8, resolve as resolve9, sep as sep5 } from "node:path";
37393
+ import { readdir as readdir5, realpath as realpath6, stat as stat7 } from "node:fs/promises";
37394
+ import {
37395
+ basename as basename6,
37396
+ extname as extname5,
37397
+ isAbsolute as isAbsolute6,
37398
+ relative as relative9,
37399
+ resolve as resolve10,
37400
+ sep as sep6
37401
+ } from "node:path";
37196
37402
 
37197
37403
  // packages/notes/src/manifest.ts
37198
37404
  var notesManifest = {
@@ -37208,52 +37414,122 @@ var notesManifest = {
37208
37414
  };
37209
37415
 
37210
37416
  // packages/notes/src/server.ts
37211
- var markdownExtensions = new Set([".md", ".markdown"]);
37417
+ var markdownExtensions2 = new Set([".md", ".markdown"]);
37418
+ var llmNoteLimit = 20;
37212
37419
  async function listMarkdownFiles(folder) {
37213
37420
  const files = [];
37214
37421
  const visit = async (current) => {
37215
- const entries = await readdir4(current, { withFileTypes: true }).catch(() => []);
37422
+ const entries = await readdir5(current, { withFileTypes: true }).catch(() => []);
37216
37423
  for (const entry of entries) {
37217
37424
  if (entry.name === "node_modules" || entry.name.startsWith("."))
37218
37425
  continue;
37219
- const path = resolve9(current, entry.name);
37426
+ const path = resolve10(current, entry.name);
37220
37427
  if (entry.isDirectory()) {
37221
37428
  await visit(path);
37222
37429
  continue;
37223
37430
  }
37224
- if (!entry.isFile() || !markdownExtensions.has(extname5(entry.name))) {
37431
+ if (!entry.isFile() || !markdownExtensions2.has(extname5(entry.name))) {
37225
37432
  continue;
37226
37433
  }
37227
- const details = await stat6(path).catch(() => {
37434
+ const details = await stat7(path).catch(() => {
37228
37435
  return;
37229
37436
  });
37230
37437
  files.push({
37231
37438
  activityAt: details?.mtimeMs ?? 0,
37232
- name: basename5(entry.name, extname5(entry.name)),
37233
- relativePath: relative8(folder, path).split(sep5).join("/")
37439
+ name: basename6(entry.name, extname5(entry.name)),
37440
+ relativePath: relative9(folder, path).split(sep6).join("/")
37234
37441
  });
37235
37442
  }
37236
37443
  };
37237
37444
  await visit(folder);
37238
37445
  return files.sort((left, right) => right.activityAt - left.activityAt);
37239
37446
  }
37240
- async function loadNotes(folder, linkedFolders = []) {
37447
+ var canonical = (path) => realpath6(path).catch(() => path);
37448
+ function inside5(path, root) {
37449
+ const local = relative9(root, path);
37450
+ return local === "" || !local.startsWith("..") && !isAbsolute6(local);
37451
+ }
37452
+ async function withoutManaged(groups, managedRoots) {
37453
+ if (!managedRoots.length)
37454
+ return groups;
37455
+ const roots = await Promise.all(managedRoots.map(canonical));
37456
+ return Promise.all(groups.map(async (group) => {
37457
+ if (roots.includes(await canonical(group.folder))) {
37458
+ return { ...group, files: [] };
37459
+ }
37460
+ const files = await Promise.all(group.files.map(async (file) => {
37461
+ const real = await canonical(resolve10(group.folder, file.relativePath));
37462
+ return roots.some((root) => inside5(real, root)) ? undefined : file;
37463
+ }));
37464
+ return { ...group, files: files.filter((file) => file !== undefined) };
37465
+ }));
37466
+ }
37467
+ async function loadNotes(folder, linkedFolders = [], managedFolders = []) {
37241
37468
  const localFiles = await listMarkdownFiles(folder);
37242
- const linked = await Promise.all(linkedFolders.map(async (linkedFolder) => ({
37243
- files: await listMarkdownFiles(linkedFolder),
37244
- folder: linkedFolder
37469
+ const listGroups = (folders) => Promise.all(folders.map(async (source) => ({
37470
+ files: await listMarkdownFiles(source),
37471
+ folder: source
37245
37472
  })));
37473
+ const [managed, linked] = await Promise.all([
37474
+ listGroups(managedFolders),
37475
+ listGroups(linkedFolders).then((groups) => withoutManaged(groups, managedFolders))
37476
+ ]);
37246
37477
  return {
37247
37478
  files: {
37248
37479
  items: [
37249
37480
  ...localFiles,
37250
- ...linked.flatMap((group) => group.files.map((file) => ({ ...file, folder: group.folder })))
37481
+ ...[...managed, ...linked].flatMap((group) => group.files.map((file) => ({ ...file, folder: group.folder })))
37251
37482
  ]
37252
37483
  },
37253
37484
  folder,
37254
37485
  status: "available"
37255
37486
  };
37256
37487
  }
37488
+ function codeSpan(text) {
37489
+ const escaped = text.replace(/[\u0000-\u001f\u007f-\u009f\u2028\u2029]/gu, (character) => `\\u${character.charCodeAt(0).toString(16).padStart(4, "0")}`);
37490
+ const longest = Math.max(0, ...(escaped.match(/`+/g) ?? []).map((run) => run.length));
37491
+ const fence = "`".repeat(longest + 1);
37492
+ const pad = escaped.startsWith("`") || escaped.endsWith("`") ? " " : "";
37493
+ return `${fence}${pad}${escaped}${pad}${fence}`;
37494
+ }
37495
+ async function notesLlmSections({
37496
+ folder,
37497
+ linkedFolders = [],
37498
+ managedFolders = [],
37499
+ signal
37500
+ }) {
37501
+ const groups = [];
37502
+ const notes = [];
37503
+ const seen = new Set;
37504
+ for (const source of [folder, ...managedFolders, ...linkedFolders]) {
37505
+ signal.throwIfAborted();
37506
+ const identity = await canonical(source);
37507
+ if (seen.has(identity))
37508
+ continue;
37509
+ seen.add(identity);
37510
+ const files = await listMarkdownFiles(source);
37511
+ const paths = files.map((file) => resolve10(source, file.relativePath));
37512
+ groups.push({ folder: source, paths });
37513
+ files.forEach((file, index) => notes.push({ activityAt: file.activityAt, path: paths[index] }));
37514
+ }
37515
+ if (notes.length === 0)
37516
+ return [];
37517
+ const recent = notes.sort((left, right) => right.activityAt - left.activityAt || (left.path < right.path ? -1 : left.path > right.path ? 1 : 0)).slice(0, llmNoteLimit);
37518
+ return [
37519
+ {
37520
+ id: "notes",
37521
+ title: "Markdown notes",
37522
+ lines: [
37523
+ ...groups.filter((group) => group.paths.length > 0).map((group) => `- Source: ${codeSpan(group.folder)} (${group.paths.length} ${group.paths.length === 1 ? "note" : "notes"})`),
37524
+ `- Total notes: ${notes.length}`,
37525
+ "",
37526
+ `Most recently changed (${recent.length} of ${notes.length}):`,
37527
+ "",
37528
+ ...recent.map((note) => `- ${codeSpan(note.path)}`)
37529
+ ]
37530
+ }
37531
+ ];
37532
+ }
37257
37533
  function createNotesPlugin() {
37258
37534
  return {
37259
37535
  ...notesManifest,
@@ -37265,10 +37541,15 @@ function createNotesPlugin() {
37265
37541
  },
37266
37542
  async handle({
37267
37543
  folder,
37268
- linkedFolders
37544
+ linkedFolders,
37545
+ managedFolders
37269
37546
  }) {
37270
- return { data: await loadNotes(folder, linkedFolders), kind: "available" };
37271
- }
37547
+ return {
37548
+ data: await loadNotes(folder, linkedFolders, managedFolders),
37549
+ kind: "available"
37550
+ };
37551
+ },
37552
+ llm: notesLlmSections
37272
37553
  };
37273
37554
  }
37274
37555
 
@@ -37282,6 +37563,44 @@ function configuredFolderServerRegistry(registry, resolvedConfig) {
37282
37563
  ];
37283
37564
  return new Map(ids.map((id) => [id, registry.get(id)]));
37284
37565
  }
37566
+ var defaultPluginRegistryConfig = {
37567
+ plugins: { disable: [], order: [] }
37568
+ };
37569
+ function stringArray(value) {
37570
+ return Array.isArray(value) ? value.filter((item) => typeof item === "string") : [];
37571
+ }
37572
+ function parsePluginRegistryConfig(content) {
37573
+ const parsed = JSON.parse(content);
37574
+ const config = parsed && typeof parsed === "object" ? parsed : {};
37575
+ return {
37576
+ plugins: {
37577
+ disable: stringArray(config.plugins?.disable),
37578
+ order: stringArray(config.plugins?.order)
37579
+ }
37580
+ };
37581
+ }
37582
+ async function nearestKennelConfig(folder) {
37583
+ for (let current = folder;; current = dirname8(current)) {
37584
+ const candidate = resolve11(current, "kennel.json");
37585
+ const details = await stat8(candidate).catch(() => {
37586
+ return;
37587
+ });
37588
+ if (details?.isFile())
37589
+ return candidate;
37590
+ if (dirname8(current) === current)
37591
+ return;
37592
+ }
37593
+ }
37594
+ async function folderPluginRegistryConfig(folder) {
37595
+ const path = await nearestKennelConfig(folder);
37596
+ if (!path)
37597
+ return defaultPluginRegistryConfig;
37598
+ try {
37599
+ return parsePluginRegistryConfig(await readFile9(path, "utf8"));
37600
+ } catch {
37601
+ return defaultPluginRegistryConfig;
37602
+ }
37603
+ }
37285
37604
  function createFolderServerPlugins() {
37286
37605
  return [createNotesPlugin()];
37287
37606
  }
@@ -37325,7 +37644,7 @@ async function folderServerRegistryAsync() {
37325
37644
  ...server,
37326
37645
  async detect(context) {
37327
37646
  const detection = await server.detect(context);
37328
- if (detection.status === "unavailable" && existsSync3(resolve10(context.folder, entry.trigger))) {
37647
+ if (detection.status === "unavailable" && existsSync3(resolve11(context.folder, entry.trigger))) {
37329
37648
  return { status: "available" };
37330
37649
  }
37331
37650
  return detection;
@@ -37364,13 +37683,13 @@ async function externalApiHandlers() {
37364
37683
  }
37365
37684
 
37366
37685
  // packages/api/src/server/project-discovery.ts
37367
- import { execFile as execFile3 } from "node:child_process";
37686
+ import { execFile as execFile4 } from "node:child_process";
37368
37687
 
37369
37688
  // packages/api/src/server/linked-folder-discovery.ts
37370
- import { execFile as execFile2 } from "node:child_process";
37689
+ import { execFile as execFile3 } from "node:child_process";
37371
37690
  import { existsSync as existsSync4 } from "node:fs";
37372
37691
  import { createRequire as createRequire2 } from "node:module";
37373
- import { dirname as dirname8, join as join4 } from "node:path";
37692
+ import { dirname as dirname9, join as join5 } from "node:path";
37374
37693
  import { fileURLToPath } from "node:url";
37375
37694
  var script = `
37376
37695
  const local = require(process.argv[1]);
@@ -37393,19 +37712,19 @@ function resolveLocalModule(from = import.meta.url) {
37393
37712
  try {
37394
37713
  candidates.push(require2.resolve("@raggle-ai/local"));
37395
37714
  } catch {}
37396
- let directory = dirname8(fileURLToPath(from));
37397
- while (dirname8(directory) !== directory) {
37398
- if (existsSync4(join4(directory, "raggle.json"))) {
37715
+ let directory = dirname9(fileURLToPath(from));
37716
+ while (dirname9(directory) !== directory) {
37717
+ if (existsSync4(join5(directory, "raggle.json"))) {
37399
37718
  for (const candidate of [
37400
- join4(dirname8(directory), "raggle-ai-scout", "packages", "local"),
37401
- join4(directory, "..", "..", "raggle-ai-scout", "packages", "local")
37719
+ join5(dirname9(directory), "raggle-ai-scout", "packages", "local"),
37720
+ join5(directory, "..", "..", "raggle-ai-scout", "packages", "local")
37402
37721
  ]) {
37403
37722
  if (!candidates.includes(candidate))
37404
37723
  candidates.push(candidate);
37405
37724
  }
37406
37725
  break;
37407
37726
  }
37408
- directory = dirname8(directory);
37727
+ directory = dirname9(directory);
37409
37728
  }
37410
37729
  for (const candidate of candidates) {
37411
37730
  try {
@@ -37414,11 +37733,11 @@ function resolveLocalModule(from = import.meta.url) {
37414
37733
  return candidate;
37415
37734
  } catch {}
37416
37735
  }
37417
- return candidates[0] ?? join4(dirname8(fileURLToPath(from)), "..", "..", "..", "raggle-ai-scout", "packages", "local");
37736
+ return candidates[0] ?? join5(dirname9(fileURLToPath(from)), "..", "..", "..", "raggle-ai-scout", "packages", "local");
37418
37737
  }
37419
37738
  function discoverLinkedFolders(folder, signal, modulePath = resolveLocalModule()) {
37420
37739
  return new Promise((resolve, reject) => {
37421
- execFile2(process.execPath, ["-e", script, modulePath, folder], {
37740
+ execFile3(process.execPath, ["-e", script, modulePath, folder], {
37422
37741
  signal,
37423
37742
  timeout: 60000,
37424
37743
  maxBuffer: 8 * 1024 * 1024,
@@ -37509,7 +37828,7 @@ local.discoverProjectsUnderFolder({folder}).then(projects => {
37509
37828
  `;
37510
37829
  function discoverProjects(folder, signal) {
37511
37830
  return new Promise((resolve, reject) => {
37512
- execFile3(process.execPath, ["-e", script2, resolveLocalModule(), folder], {
37831
+ execFile4(process.execPath, ["-e", script2, resolveLocalModule(), folder], {
37513
37832
  signal,
37514
37833
  timeout: 60000,
37515
37834
  maxBuffer: 8 * 1024 * 1024,
@@ -37766,10 +38085,10 @@ var metadataExtensions = new Set([
37766
38085
  ".txt"
37767
38086
  ]);
37768
38087
  function metadataFile(path, root) {
37769
- const candidate = relative10(root, path);
37770
- if (!candidate || candidate.startsWith("..") || isAbsolute6(candidate))
38088
+ const candidate = relative11(root, path);
38089
+ if (!candidate || candidate.startsWith("..") || isAbsolute7(candidate))
37771
38090
  return false;
37772
- const parts = candidate.split(sep7);
38091
+ const parts = candidate.split(sep8);
37773
38092
  if (parts.some((part) => part === "node_modules"))
37774
38093
  return false;
37775
38094
  return metadataExtensions.has(extname6(candidate).toLowerCase());
@@ -37780,11 +38099,11 @@ async function configuredAiInputModule(folder) {
37780
38099
  return typeof module === "string" && module.trim() ? module.trim() : null;
37781
38100
  }
37782
38101
  function folderPath(value, root, lockedRoot) {
37783
- const folder = realpathSync2(resolve11(value ?? root));
38102
+ const folder = realpathSync2(resolve12(value ?? root));
37784
38103
  if (!lockedRoot)
37785
38104
  return folder;
37786
- const path = relative10(lockedRoot, folder);
37787
- if (!path || !path.startsWith("..") && !isAbsolute6(path))
38105
+ const path = relative11(lockedRoot, folder);
38106
+ if (!path || !path.startsWith("..") && !isAbsolute7(path))
37788
38107
  return folder;
37789
38108
  throw new Error(`Folder is outside the viewer root: ${folder}`);
37790
38109
  }
@@ -37855,15 +38174,15 @@ function unique(values) {
37855
38174
  return [...new Set(values)];
37856
38175
  }
37857
38176
  function isMachineRoot(folder) {
37858
- return folder === homedir3() || dirname9(folder) === folder;
38177
+ return folder === homedir4() || dirname10(folder) === folder;
37859
38178
  }
37860
38179
  async function shallowMetadata(folder) {
37861
- const entries = await readdir5(folder, { withFileTypes: true });
38180
+ const entries = await readdir6(folder, { withFileTypes: true });
37862
38181
  const visible = entries.filter((entry) => !entry.name.startsWith(".") && entry.name !== "node_modules");
37863
38182
  const files = await Promise.all(visible.filter((entry) => entry.isFile() && [".markdown", ".md"].includes(extname6(entry.name).toLowerCase())).map(async (entry) => {
37864
- const path = resolve11(folder, entry.name);
37865
- const details = await stat7(path);
37866
- const content = await readFile8(path, "utf8").catch(() => "");
38183
+ const path = resolve12(folder, entry.name);
38184
+ const details = await stat9(path);
38185
+ const content = await readFile10(path, "utf8").catch(() => "");
37867
38186
  const title = markdownMetadata(content).title;
37868
38187
  return {
37869
38188
  activityAt: details.mtimeMs,
@@ -37876,10 +38195,10 @@ async function shallowMetadata(folder) {
37876
38195
  };
37877
38196
  }));
37878
38197
  const projects = visible.filter((entry) => entry.isDirectory()).map((entry) => ({
37879
- id: createHash5("sha1").update(resolve11(folder, entry.name)).digest("hex"),
38198
+ id: createHash5("sha1").update(resolve12(folder, entry.name)).digest("hex"),
37880
38199
  name: entry.name,
37881
38200
  relativePath: entry.name,
37882
- worktree: resolve11(folder, entry.name)
38201
+ worktree: resolve12(folder, entry.name)
37883
38202
  })).sort((left, right) => left.name.localeCompare(right.name));
37884
38203
  return {
37885
38204
  changes: { items: [] },
@@ -37905,8 +38224,8 @@ async function loadFolderData(folder) {
37905
38224
  items: projects.map((project) => ({
37906
38225
  id: project.id,
37907
38226
  worktree: project.worktree,
37908
- name: project.name ?? basename6(project.worktree),
37909
- relativePath: relative10(folder, project.worktree)
38227
+ name: project.name ?? basename7(project.worktree),
38228
+ relativePath: relative11(folder, project.worktree)
37910
38229
  }))
37911
38230
  }
37912
38231
  };
@@ -37914,10 +38233,10 @@ async function loadFolderData(folder) {
37914
38233
  async function notesFiles(folder, files, origin) {
37915
38234
  return {
37916
38235
  items: await Promise.all(files.map(async (file) => {
37917
- const path = file.path ?? (file.relativePath ? resolve11(folder, file.relativePath) : undefined);
38236
+ const path = file.path ?? (file.relativePath ? resolve12(folder, file.relativePath) : undefined);
37918
38237
  if (!path)
37919
38238
  return origin ? { ...file, folder: origin } : file;
37920
- const content = [".markdown", ".md"].includes(extname6(path).toLowerCase()) ? await readFile8(path, "utf8").catch(() => "") : "";
38239
+ const content = [".markdown", ".md"].includes(extname6(path).toLowerCase()) ? await readFile10(path, "utf8").catch(() => "") : "";
37921
38240
  const title = content ? markdownMetadata(content).title : undefined;
37922
38241
  return {
37923
38242
  ...file,
@@ -37928,6 +38247,31 @@ async function notesFiles(folder, files, origin) {
37928
38247
  }))
37929
38248
  };
37930
38249
  }
38250
+ async function managedNotesFiles(directory) {
38251
+ const notes = await listManagedNotes(directory);
38252
+ return Promise.all(notes.map(async (note) => {
38253
+ const content = await readFile10(note.path, "utf8").catch(() => "");
38254
+ const title = content ? markdownMetadata(content).title : undefined;
38255
+ return {
38256
+ ...note,
38257
+ folder: directory,
38258
+ name: title ?? note.name,
38259
+ openHref: openPathHref(note.path)
38260
+ };
38261
+ }));
38262
+ }
38263
+ var canonicalPath = (path) => realpath7(path).catch(() => path);
38264
+ function insideFolder(path, root) {
38265
+ const local = relative11(root, path);
38266
+ return local === "" || !local.startsWith("..") && !isAbsolute7(local);
38267
+ }
38268
+ async function withoutManagedFiles(items, managedRoot) {
38269
+ if (!managedRoot)
38270
+ return items;
38271
+ const root = await canonicalPath(managedRoot);
38272
+ const outside = await Promise.all(items.map(async (item) => !item.path || !insideFolder(await canonicalPath(item.path), root)));
38273
+ return items.filter((_, index) => outside[index]);
38274
+ }
37931
38275
  async function localProjectSearchCache(cache) {
37932
38276
  if (cache && Date.now() - cache.loadedAt < projectSearchCacheMs)
37933
38277
  return cache;
@@ -37950,16 +38294,16 @@ async function localProjectSearchCache(cache) {
37950
38294
  function mediaTarget(folder, file, lockedTarget) {
37951
38295
  if (!file)
37952
38296
  return;
37953
- const rootReal = realpathSync2(resolve11(folder));
37954
- const path = resolve11(folder, file);
38297
+ const rootReal = realpathSync2(resolve12(folder));
38298
+ const path = resolve12(folder, file);
37955
38299
  let pathReal;
37956
38300
  try {
37957
38301
  pathReal = realpathSync2(path);
37958
38302
  } catch {
37959
38303
  return;
37960
38304
  }
37961
- const rel = relative10(rootReal, pathReal);
37962
- if (rel.startsWith("..") || isAbsolute6(rel))
38305
+ const rel = relative11(rootReal, pathReal);
38306
+ if (rel.startsWith("..") || isAbsolute7(rel))
37963
38307
  return;
37964
38308
  if (lockedTarget && pathReal !== lockedTarget)
37965
38309
  return;
@@ -37998,16 +38342,16 @@ function documentError(response) {
37998
38342
  function documentTarget(folder, file, lockedTarget) {
37999
38343
  if (!file)
38000
38344
  return;
38001
- const rootReal = realpathSync2(resolve11(folder));
38002
- const path = resolve11(folder, file);
38345
+ const rootReal = realpathSync2(resolve12(folder));
38346
+ const path = resolve12(folder, file);
38003
38347
  let pathReal;
38004
38348
  try {
38005
38349
  pathReal = realpathSync2(path);
38006
38350
  } catch {
38007
38351
  return;
38008
38352
  }
38009
- const rel = relative10(rootReal, pathReal);
38010
- if (rel.startsWith("..") || isAbsolute6(rel))
38353
+ const rel = relative11(rootReal, pathReal);
38354
+ if (rel.startsWith("..") || isAbsolute7(rel))
38011
38355
  return;
38012
38356
  if (lockedTarget && pathReal !== lockedTarget)
38013
38357
  return;
@@ -38035,44 +38379,6 @@ async function projectSearchItem(project, match) {
38035
38379
  worktree: project.worktree
38036
38380
  };
38037
38381
  }
38038
- var defaultPluginRegistryConfig = {
38039
- plugins: { disable: [], order: [] }
38040
- };
38041
- function stringArray(value) {
38042
- return Array.isArray(value) ? value.filter((item) => typeof item === "string") : [];
38043
- }
38044
- function parsePluginRegistryConfig(content) {
38045
- const parsed = JSON.parse(content);
38046
- const config = parsed && typeof parsed === "object" ? parsed : {};
38047
- return {
38048
- plugins: {
38049
- disable: stringArray(config.plugins?.disable),
38050
- order: stringArray(config.plugins?.order)
38051
- }
38052
- };
38053
- }
38054
- async function nearestKennelConfig(folder) {
38055
- for (let current = folder;; current = dirname9(current)) {
38056
- const candidate = resolve11(current, "kennel.json");
38057
- const details = await stat7(candidate).catch(() => {
38058
- return;
38059
- });
38060
- if (details?.isFile())
38061
- return candidate;
38062
- if (dirname9(current) === current)
38063
- return;
38064
- }
38065
- }
38066
- async function folderPluginRegistryConfig(folder) {
38067
- const path = await nearestKennelConfig(folder);
38068
- if (!path)
38069
- return defaultPluginRegistryConfig;
38070
- try {
38071
- return parsePluginRegistryConfig(await readFile8(path, "utf8"));
38072
- } catch {
38073
- return defaultPluginRegistryConfig;
38074
- }
38075
- }
38076
38382
  function pluginConfigWarnings(registry, config) {
38077
38383
  const ids = unique([...config.plugins.disable, ...config.plugins.order]);
38078
38384
  return ids.filter((id) => !registry.has(id)).map((id) => `unknown plugin id "${id}" — not installed in this build`);
@@ -38123,6 +38429,18 @@ function createFolderApi({
38123
38429
  const linkedDiscovery = createLinkedFolderDiscovery(linkedFolderLoader, (folder) => discoveryChanged(folder));
38124
38430
  const linkedState = (folder) => lockedRoot || isMachineRoot(folder) ? { folders: [], status: "ready" } : linkedDiscovery.read(folder);
38125
38431
  const resolveLinkedFolders = (folder) => lockedRoot || isMachineRoot(folder) ? Promise.resolve([]) : linkedDiscovery.folders(folder);
38432
+ const managedNotesFolder = async (folder) => {
38433
+ if (lockedRoot || lockedTarget || isMachineRoot(folder))
38434
+ return;
38435
+ try {
38436
+ if (!(await readRepositoryNotesSettings()).enabled)
38437
+ return;
38438
+ const lookup = await lookupRepositoryNotes(folder);
38439
+ return lookup.status === "registered" ? lookup.notesDirectory : undefined;
38440
+ } catch {
38441
+ return;
38442
+ }
38443
+ };
38126
38444
  const favicon = faviconService();
38127
38445
  return {
38128
38446
  name: "kennel-folder-metadata",
@@ -38165,7 +38483,7 @@ function createFolderApi({
38165
38483
  if (!folder)
38166
38484
  return;
38167
38485
  server.emit({
38168
- data: { path: relative10(folder, path) },
38486
+ data: { path: relative11(folder, path) },
38169
38487
  event: "kennel:metadata-changed",
38170
38488
  type: "custom"
38171
38489
  });
@@ -38248,7 +38566,7 @@ function createFolderApi({
38248
38566
  "Content-Type": asset.mediaType,
38249
38567
  ETag: `"${hash}"`
38250
38568
  });
38251
- response.end(await readFile8(asset.path));
38569
+ response.end(await readFile10(asset.path));
38252
38570
  } catch (error) {
38253
38571
  response.writeHead(404, {
38254
38572
  "Content-Type": "text/plain; charset=utf-8"
@@ -38313,21 +38631,21 @@ function createFolderApi({
38313
38631
  }
38314
38632
  const folder = folderPath(url.searchParams.get("path"), root, lockedRoot);
38315
38633
  const command = process.platform === "darwin" ? "open" : process.platform === "win32" ? "explorer.exe" : "xdg-open";
38316
- await new Promise((done, fail) => execFile4(command, [folder], (error) => error ? fail(error) : done()));
38634
+ await new Promise((done, fail) => execFile5(command, [folder], (error) => error ? fail(error) : done()));
38317
38635
  response.writeHead(204);
38318
38636
  response.end();
38319
38637
  return;
38320
38638
  }
38321
38639
  if (url.pathname === "/api/documents") {
38322
38640
  const folder = folderPath(url.searchParams.get("path"), root, lockedRoot);
38323
- const target = lockedTarget ? documentTarget(folder, relative10(folder, lockedTarget), lockedTarget) : undefined;
38641
+ const target = lockedTarget ? documentTarget(folder, relative11(folder, lockedTarget), lockedTarget) : undefined;
38324
38642
  const dataset = lockedTarget ? {
38325
38643
  root: folder,
38326
38644
  documents: target ? [
38327
38645
  {
38328
- path: relative10(folder, target.path),
38646
+ path: relative11(folder, target.path),
38329
38647
  kind: target.kind,
38330
- content: previewTextKind(target.kind) ? await readFile8(target.path, "utf8") : ""
38648
+ content: previewTextKind(target.kind) ? await readFile10(target.path, "utf8") : ""
38331
38649
  }
38332
38650
  ] : []
38333
38651
  } : await readFolder(folder);
@@ -38343,7 +38661,7 @@ function createFolderApi({
38343
38661
  documentError(response);
38344
38662
  return;
38345
38663
  }
38346
- const details = await stat7(target.path);
38664
+ const details = await stat9(target.path);
38347
38665
  if (!details.isFile()) {
38348
38666
  documentError(response);
38349
38667
  return;
@@ -38355,16 +38673,16 @@ function createFolderApi({
38355
38673
  return;
38356
38674
  }
38357
38675
  watch(folder);
38358
- const content = previewTextKind(target.kind) ? await readFile8(target.path, "utf8") : "";
38676
+ const content = previewTextKind(target.kind) ? await readFile10(target.path, "utf8") : "";
38359
38677
  sendContract(response, url, {
38360
38678
  content,
38361
38679
  kind: target.kind,
38362
- path: relative10(target.root, target.path),
38680
+ path: relative11(target.root, target.path),
38363
38681
  root: folder,
38364
38682
  ...previewTextKind(target.kind) ? {} : {
38365
38683
  rawUrl: `/api/raw?${new URLSearchParams({
38366
38684
  path: folder,
38367
- file: relative10(target.root, target.path)
38685
+ file: relative11(target.root, target.path)
38368
38686
  })}`
38369
38687
  }
38370
38688
  }, {
@@ -38384,7 +38702,7 @@ function createFolderApi({
38384
38702
  documentError(response);
38385
38703
  return;
38386
38704
  }
38387
- const details = await stat7(target.path);
38705
+ const details = await stat9(target.path);
38388
38706
  if (!details.isFile()) {
38389
38707
  documentError(response);
38390
38708
  return;
@@ -38405,10 +38723,10 @@ function createFolderApi({
38405
38723
  ETag: etag,
38406
38724
  "X-Content-Type-Options": "nosniff",
38407
38725
  ...download ? {
38408
- "Content-Disposition": `attachment; filename="${basename6(target.path)}"`
38726
+ "Content-Disposition": `attachment; filename="${basename7(target.path)}"`
38409
38727
  } : {}
38410
38728
  });
38411
- response.end(await readFile8(target.path));
38729
+ response.end(await readFile10(target.path));
38412
38730
  } catch (error) {
38413
38731
  documentError(response);
38414
38732
  }
@@ -38422,7 +38740,7 @@ function createFolderApi({
38422
38740
  documentError(response);
38423
38741
  return;
38424
38742
  }
38425
- const details = await stat7(target.path);
38743
+ const details = await stat9(target.path);
38426
38744
  if (!details.isFile()) {
38427
38745
  documentError(response);
38428
38746
  return;
@@ -38434,7 +38752,7 @@ function createFolderApi({
38434
38752
  return;
38435
38753
  }
38436
38754
  watch(folder);
38437
- const data = await readFile8(target.path);
38755
+ const data = await readFile10(target.path);
38438
38756
  const baseHeaders = {
38439
38757
  "Accept-Ranges": "bytes",
38440
38758
  "Cache-Control": "private, max-age=0, must-revalidate",
@@ -38488,9 +38806,13 @@ function createFolderApi({
38488
38806
  if (!lockedRoot && !isMachineRoot(folder) && request.headers["x-kennel-retry-linked"] === "1")
38489
38807
  linkedDiscovery.retry(folder);
38490
38808
  const discovery = linkedState(folder);
38809
+ const managed = await managedNotesFolder(folder);
38810
+ if (managed)
38811
+ watch(managed);
38491
38812
  const result = await plugin.handle({
38492
38813
  folder,
38493
- linkedFolders: discovery.folders
38814
+ linkedFolders: discovery.folders,
38815
+ ...managed ? { managedFolders: [managed] } : {}
38494
38816
  }, subpath);
38495
38817
  if (result.kind === "unavailable") {
38496
38818
  response.writeHead(200, {
@@ -38593,7 +38915,7 @@ function createFolderApi({
38593
38915
  workspace.breadcrumbs = await workspaceBreadcrumbIcons(workspace.breadcrumbs ?? [], assetFiles);
38594
38916
  const files = [
38595
38917
  workspace.configPath,
38596
- resolve11(workspace.root, "raggle.json")
38918
+ resolve12(workspace.root, "raggle.json")
38597
38919
  ].filter((path) => Boolean(path));
38598
38920
  for (const path of files)
38599
38921
  contexts.add(path);
@@ -38617,7 +38939,14 @@ function createFolderApi({
38617
38939
  const machine = isMachineRoot(folder);
38618
38940
  if (part === "notes") {
38619
38941
  const scope = url.searchParams.get("notesScope");
38942
+ const managed = await managedNotesFolder(folder);
38620
38943
  const localFiles = scope === "linked" ? Promise.resolve({ items: [] }) : (machine ? shallowMetadata(folder).then((data) => data.files.items) : listLocalMarkdownFiles({ folder, limit: 200 })).then((files) => notesFiles(folder, files));
38944
+ const managedFiles = async () => {
38945
+ if (scope === "linked" || !managed)
38946
+ return [];
38947
+ watch(managed);
38948
+ return managedNotesFiles(managed);
38949
+ };
38621
38950
  const linkedFiles = async () => {
38622
38951
  if (scope === "local" || machine || lockedRoot)
38623
38952
  return [];
@@ -38628,20 +38957,25 @@ function createFolderApi({
38628
38957
  throw new Error("Linked notes discovery failed. Retry to try again.");
38629
38958
  return Promise.all(linkedFolders.map(async (linkedFolder) => {
38630
38959
  watch(linkedFolder);
38631
- return notesFiles(linkedFolder, await listLocalMarkdownFiles({
38960
+ const group = await notesFiles(linkedFolder, await listLocalMarkdownFiles({
38632
38961
  folder: linkedFolder,
38633
38962
  limit: 200
38634
38963
  }), linkedFolder);
38964
+ return {
38965
+ items: await withoutManagedFiles(group.items, managed)
38966
+ };
38635
38967
  }));
38636
38968
  };
38637
- const [files, linked] = await Promise.all([
38969
+ const [files, managedItems, linked] = await Promise.all([
38638
38970
  localFiles,
38971
+ managedFiles(),
38639
38972
  linkedFiles()
38640
38973
  ]);
38641
38974
  sendContract(response, url, {
38642
38975
  files: {
38643
38976
  items: [
38644
38977
  ...files.items,
38978
+ ...managedItems,
38645
38979
  ...linked.flatMap((group) => group.items)
38646
38980
  ]
38647
38981
  },
@@ -38740,26 +39074,26 @@ var contentTypes = {
38740
39074
  ".ttf": "font/ttf"
38741
39075
  };
38742
39076
  async function sendFile(request, response, directory, pathname, fallback) {
38743
- const root = await realpath5(directory);
39077
+ const root = await realpath8(directory);
38744
39078
  const inside = (path) => {
38745
- const local = relative11(root, path);
38746
- return local !== ".." && !local.startsWith("../") && !isAbsolute7(local);
39079
+ const local = relative12(root, path);
39080
+ return local !== ".." && !local.startsWith("../") && !isAbsolute8(local);
38747
39081
  };
38748
- let target = resolve12(root, `.${decodeURIComponent(pathname)}`);
39082
+ let target = resolve13(root, `.${decodeURIComponent(pathname)}`);
38749
39083
  if (!inside(target)) {
38750
39084
  response.writeHead(403);
38751
39085
  response.end();
38752
39086
  return;
38753
39087
  }
38754
- if (!(await stat8(target).catch(() => null))?.isFile()) {
39088
+ if (!(await stat10(target).catch(() => null))?.isFile()) {
38755
39089
  if (!fallback) {
38756
39090
  response.writeHead(404);
38757
39091
  response.end();
38758
39092
  return;
38759
39093
  }
38760
- target = resolve12(root, fallback);
39094
+ target = resolve13(root, fallback);
38761
39095
  }
38762
- target = await realpath5(target);
39096
+ target = await realpath8(target);
38763
39097
  if (!inside(target)) {
38764
39098
  response.writeHead(403);
38765
39099
  response.end();
@@ -38769,7 +39103,7 @@ async function sendFile(request, response, directory, pathname, fallback) {
38769
39103
  "Content-Type": contentTypes[extname7(target)] ?? "application/octet-stream",
38770
39104
  "X-Content-Type-Options": "nosniff"
38771
39105
  });
38772
- response.end(request.method === "HEAD" ? undefined : await readFile9(target));
39106
+ response.end(request.method === "HEAD" ? undefined : await readFile11(target));
38773
39107
  }
38774
39108
  function createKennelApi(options) {
38775
39109
  const remote = options.remoteAccess || !isLoopbackServerHost(options.host);