@bevel-software/platform-core-backend 0.25.2 → 0.26.0

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 (248) hide show
  1. package/agent-guide/access-control.md +234 -0
  2. package/agent-guide/conventions.md +27 -0
  3. package/agent-guide/directory-structure.md +145 -0
  4. package/agent-guide/finding-things.md +7 -0
  5. package/agent-guide/introduction.md +27 -0
  6. package/agent-guide/skills.md +47 -0
  7. package/agent-guide/tool-manuals.md +217 -0
  8. package/agent-guide/where-a-new-file-goes.md +36 -0
  9. package/dist/assets.d.ts +7 -0
  10. package/dist/assets.d.ts.map +1 -1
  11. package/dist/assets.js +9 -0
  12. package/dist/assets.js.map +1 -1
  13. package/dist/core/core-ports.d.ts +11 -0
  14. package/dist/core/core-ports.d.ts.map +1 -1
  15. package/dist/core/core-ports.js.map +1 -1
  16. package/dist/core/create-core-server.d.ts.map +1 -1
  17. package/dist/core/create-core-server.js +13 -2
  18. package/dist/core/create-core-server.js.map +1 -1
  19. package/dist/core/create-core-services.d.ts +9 -0
  20. package/dist/core/create-core-services.d.ts.map +1 -1
  21. package/dist/core/create-core-services.js +14 -4
  22. package/dist/core/create-core-services.js.map +1 -1
  23. package/dist/index.d.ts +1 -1
  24. package/dist/index.d.ts.map +1 -1
  25. package/dist/index.js +2 -2
  26. package/dist/index.js.map +1 -1
  27. package/dist/modules/access/access-control.interface.d.ts +9 -0
  28. package/dist/modules/access/access-control.interface.d.ts.map +1 -1
  29. package/dist/modules/access/access-control.service.d.ts +1 -0
  30. package/dist/modules/access/access-control.service.d.ts.map +1 -1
  31. package/dist/modules/access/access-control.service.js +16 -0
  32. package/dist/modules/access/access-control.service.js.map +1 -1
  33. package/dist/modules/agent-guide/agent-guide.d.ts +139 -0
  34. package/dist/modules/agent-guide/agent-guide.d.ts.map +1 -0
  35. package/dist/modules/agent-guide/agent-guide.js +191 -0
  36. package/dist/modules/agent-guide/agent-guide.js.map +1 -0
  37. package/dist/modules/agent-guide/agent-guide.tools.d.ts +24 -0
  38. package/dist/modules/agent-guide/agent-guide.tools.d.ts.map +1 -0
  39. package/dist/modules/agent-guide/agent-guide.tools.js +100 -0
  40. package/dist/modules/agent-guide/agent-guide.tools.js.map +1 -0
  41. package/dist/modules/agent-guide/index.d.ts +4 -0
  42. package/dist/modules/agent-guide/index.d.ts.map +1 -0
  43. package/dist/modules/agent-guide/index.js +4 -0
  44. package/dist/modules/agent-guide/index.js.map +1 -0
  45. package/dist/modules/agent-instructions/agent-instructions.routes.d.ts +3 -2
  46. package/dist/modules/agent-instructions/agent-instructions.routes.d.ts.map +1 -1
  47. package/dist/modules/agent-instructions/agent-instructions.routes.js +3 -2
  48. package/dist/modules/agent-instructions/agent-instructions.routes.js.map +1 -1
  49. package/dist/modules/agent-instructions/compose.d.ts +9 -6
  50. package/dist/modules/agent-instructions/compose.d.ts.map +1 -1
  51. package/dist/modules/agent-instructions/compose.js +9 -6
  52. package/dist/modules/agent-instructions/compose.js.map +1 -1
  53. package/dist/modules/agent-instructions/index.d.ts +1 -1
  54. package/dist/modules/agent-instructions/index.d.ts.map +1 -1
  55. package/dist/modules/agent-instructions/index.js +1 -1
  56. package/dist/modules/agent-instructions/index.js.map +1 -1
  57. package/dist/modules/agent-instructions/shared-file-rules.d.ts +10 -50
  58. package/dist/modules/agent-instructions/shared-file-rules.d.ts.map +1 -1
  59. package/dist/modules/agent-instructions/shared-file-rules.js +32 -85
  60. package/dist/modules/agent-instructions/shared-file-rules.js.map +1 -1
  61. package/dist/modules/mcp/mcp.service.d.ts +29 -2
  62. package/dist/modules/mcp/mcp.service.d.ts.map +1 -1
  63. package/dist/modules/mcp/mcp.service.js +113 -16
  64. package/dist/modules/mcp/mcp.service.js.map +1 -1
  65. package/dist/modules/mcp/tool-schema-guard.d.ts +105 -0
  66. package/dist/modules/mcp/tool-schema-guard.d.ts.map +1 -0
  67. package/dist/modules/mcp/tool-schema-guard.js +171 -0
  68. package/dist/modules/mcp/tool-schema-guard.js.map +1 -0
  69. package/dist/modules/plugins/plugins.tools.d.ts +36 -2
  70. package/dist/modules/plugins/plugins.tools.d.ts.map +1 -1
  71. package/dist/modules/plugins/plugins.tools.js +71 -14
  72. package/dist/modules/plugins/plugins.tools.js.map +1 -1
  73. package/dist/modules/settings/deployment-settings.service.d.ts +0 -7
  74. package/dist/modules/settings/deployment-settings.service.d.ts.map +1 -1
  75. package/dist/modules/settings/deployment-settings.service.js +14 -53
  76. package/dist/modules/settings/deployment-settings.service.js.map +1 -1
  77. package/dist/modules/settings/setup.routes.d.ts.map +1 -1
  78. package/dist/modules/settings/setup.routes.js +3 -6
  79. package/dist/modules/settings/setup.routes.js.map +1 -1
  80. package/dist/modules/skills/skills.tools.d.ts.map +1 -1
  81. package/dist/modules/skills/skills.tools.js +58 -16
  82. package/dist/modules/skills/skills.tools.js.map +1 -1
  83. package/dist/modules/tool-manuals/tool-manuals.contract.d.ts +23 -4
  84. package/dist/modules/tool-manuals/tool-manuals.contract.d.ts.map +1 -1
  85. package/dist/modules/tool-manuals/tool-manuals.contract.js.map +1 -1
  86. package/dist/modules/tool-manuals/tool-manuals.service.d.ts +4 -0
  87. package/dist/modules/tool-manuals/tool-manuals.service.d.ts.map +1 -1
  88. package/dist/modules/tool-manuals/tool-manuals.service.js +14 -0
  89. package/dist/modules/tool-manuals/tool-manuals.service.js.map +1 -1
  90. package/dist/modules/tool-manuals/tool-manuals.tools.d.ts +7 -0
  91. package/dist/modules/tool-manuals/tool-manuals.tools.d.ts.map +1 -1
  92. package/dist/modules/tool-manuals/tool-manuals.tools.js +66 -36
  93. package/dist/modules/tool-manuals/tool-manuals.tools.js.map +1 -1
  94. package/dist/modules/tool-registry/description-length.d.ts +14 -14
  95. package/dist/modules/tool-registry/description-length.d.ts.map +1 -1
  96. package/dist/modules/tool-registry/description-length.js +24 -26
  97. package/dist/modules/tool-registry/description-length.js.map +1 -1
  98. package/dist/modules/tool-registry/guide-first.d.ts +23 -0
  99. package/dist/modules/tool-registry/guide-first.d.ts.map +1 -0
  100. package/dist/modules/tool-registry/guide-first.js +32 -0
  101. package/dist/modules/tool-registry/guide-first.js.map +1 -0
  102. package/dist/modules/tool-registry/tool-registry.d.ts +6 -0
  103. package/dist/modules/tool-registry/tool-registry.d.ts.map +1 -1
  104. package/dist/modules/tool-registry/tool-registry.js +9 -2
  105. package/dist/modules/tool-registry/tool-registry.js.map +1 -1
  106. package/dist/modules/workflow/agent-tools/change-request-read-shape.d.ts +449 -0
  107. package/dist/modules/workflow/agent-tools/change-request-read-shape.d.ts.map +1 -0
  108. package/dist/modules/workflow/agent-tools/change-request-read-shape.js +481 -0
  109. package/dist/modules/workflow/agent-tools/change-request-read-shape.js.map +1 -0
  110. package/dist/modules/workflow/agent-tools/change-request-read.tools.d.ts +73 -0
  111. package/dist/modules/workflow/agent-tools/change-request-read.tools.d.ts.map +1 -0
  112. package/dist/modules/workflow/agent-tools/change-request-read.tools.js +582 -0
  113. package/dist/modules/workflow/agent-tools/change-request-read.tools.js.map +1 -0
  114. package/dist/modules/workflow/agent-tools/change-request-summary.d.ts +12 -1
  115. package/dist/modules/workflow/agent-tools/change-request-summary.d.ts.map +1 -1
  116. package/dist/modules/workflow/agent-tools/change-request-summary.js +5 -1
  117. package/dist/modules/workflow/agent-tools/change-request-summary.js.map +1 -1
  118. package/dist/modules/workflow/agent-tools/workflow.tools.d.ts.map +1 -1
  119. package/dist/modules/workflow/agent-tools/workflow.tools.js +9 -0
  120. package/dist/modules/workflow/agent-tools/workflow.tools.js.map +1 -1
  121. package/dist/modules/workflow/git/git.service.d.ts +210 -13
  122. package/dist/modules/workflow/git/git.service.d.ts.map +1 -1
  123. package/dist/modules/workflow/git/git.service.js +456 -91
  124. package/dist/modules/workflow/git/git.service.js.map +1 -1
  125. package/dist/modules/workflow/git/merge-commit.d.ts +73 -0
  126. package/dist/modules/workflow/git/merge-commit.d.ts.map +1 -0
  127. package/dist/modules/workflow/git/merge-commit.js +89 -0
  128. package/dist/modules/workflow/git/merge-commit.js.map +1 -0
  129. package/dist/modules/workflow/git/pull-request.service.d.ts +94 -1
  130. package/dist/modules/workflow/git/pull-request.service.d.ts.map +1 -1
  131. package/dist/modules/workflow/git/pull-request.service.js +332 -37
  132. package/dist/modules/workflow/git/pull-request.service.js.map +1 -1
  133. package/dist/modules/workflow/review-workflow/review-workflow.service.d.ts +35 -0
  134. package/dist/modules/workflow/review-workflow/review-workflow.service.d.ts.map +1 -1
  135. package/dist/modules/workflow/review-workflow/review-workflow.service.js +178 -12
  136. package/dist/modules/workflow/review-workflow/review-workflow.service.js.map +1 -1
  137. package/dist/modules/workflow/workflow.routes.d.ts +6 -2
  138. package/dist/modules/workflow/workflow.routes.d.ts.map +1 -1
  139. package/dist/modules/workflow/workflow.routes.js +7 -2
  140. package/dist/modules/workflow/workflow.routes.js.map +1 -1
  141. package/dist/modules/workflow/workflow.service.d.ts +4 -0
  142. package/dist/modules/workflow/workflow.service.d.ts.map +1 -1
  143. package/dist/modules/workflow/workflow.service.js +3 -0
  144. package/dist/modules/workflow/workflow.service.js.map +1 -1
  145. package/dist/modules/workspace/startup/steps/seed-tree.d.ts.map +1 -1
  146. package/dist/modules/workspace/startup/steps/seed-tree.js +22 -27
  147. package/dist/modules/workspace/startup/steps/seed-tree.js.map +1 -1
  148. package/dist/modules/workspace/startup/steps/template-files.step.d.ts +58 -52
  149. package/dist/modules/workspace/startup/steps/template-files.step.d.ts.map +1 -1
  150. package/dist/modules/workspace/startup/steps/template-files.step.js +209 -223
  151. package/dist/modules/workspace/startup/steps/template-files.step.js.map +1 -1
  152. package/dist/modules/workspace/startup/steps/template-source.d.ts +5 -3
  153. package/dist/modules/workspace/startup/steps/template-source.d.ts.map +1 -1
  154. package/dist/modules/workspace/startup/steps/template-source.js +5 -3
  155. package/dist/modules/workspace/startup/steps/template-source.js.map +1 -1
  156. package/dist/modules/workspace/workspace.tools.d.ts +10 -1
  157. package/dist/modules/workspace/workspace.tools.d.ts.map +1 -1
  158. package/dist/modules/workspace/workspace.tools.js +211 -18
  159. package/dist/modules/workspace/workspace.tools.js.map +1 -1
  160. package/dist/shared/domain-errors.d.ts +11 -0
  161. package/dist/shared/domain-errors.d.ts.map +1 -1
  162. package/dist/shared/domain-errors.js +14 -0
  163. package/dist/shared/domain-errors.js.map +1 -1
  164. package/dist/shared/hidden-tools.d.ts +44 -0
  165. package/dist/shared/hidden-tools.d.ts.map +1 -0
  166. package/dist/shared/hidden-tools.js +13 -0
  167. package/dist/shared/hidden-tools.js.map +1 -0
  168. package/kb-template/.bevelignore +0 -5
  169. package/package.json +4 -3
  170. package/src/__tests__/kb-layout-config.test.ts +10 -100
  171. package/src/__tests__/packaged-assets-ship.test.ts +54 -0
  172. package/src/assets.ts +10 -0
  173. package/src/core/core-ports.ts +11 -0
  174. package/src/core/create-core-server.ts +13 -2
  175. package/src/core/create-core-services.ts +28 -4
  176. package/src/index.ts +2 -2
  177. package/src/modules/access/__tests__/access-control.atref-batch.test.ts +58 -0
  178. package/src/modules/access/__tests__/access-control.platform-restore.test.ts +8 -7
  179. package/src/modules/access/__tests__/access-personal-plugin.test.ts +1 -18
  180. package/src/modules/access/access-control.interface.ts +15 -0
  181. package/src/modules/access/access-control.service.ts +21 -0
  182. package/src/modules/agent-guide/__tests__/agent-guide.test.ts +328 -0
  183. package/src/modules/agent-guide/__tests__/agent-guide.tools.test.ts +189 -0
  184. package/src/modules/agent-guide/agent-guide.tools.ts +122 -0
  185. package/src/modules/agent-guide/agent-guide.ts +291 -0
  186. package/src/modules/agent-guide/index.ts +21 -0
  187. package/src/modules/agent-instructions/__tests__/shared-file-rules.test.ts +28 -121
  188. package/src/modules/agent-instructions/agent-instructions.routes.ts +3 -2
  189. package/src/modules/agent-instructions/compose.ts +9 -6
  190. package/src/modules/agent-instructions/index.ts +0 -3
  191. package/src/modules/agent-instructions/shared-file-rules.ts +31 -93
  192. package/src/modules/mcp/__tests__/fake-downstream-mcp-server.ts +14 -3
  193. package/src/modules/mcp/__tests__/mcp.e2e.test.ts +250 -0
  194. package/src/modules/mcp/__tests__/mcp.service.test.ts +31 -23
  195. package/src/modules/mcp/__tests__/tool-schema-guard.test.ts +266 -0
  196. package/src/modules/mcp/mcp.service.ts +137 -19
  197. package/src/modules/mcp/tool-schema-guard.ts +196 -0
  198. package/src/modules/plugins/__tests__/plugins.tools.test.ts +154 -4
  199. package/src/modules/plugins/plugins.tools.ts +75 -15
  200. package/src/modules/settings/__tests__/deployment-settings.service.test.ts +26 -55
  201. package/src/modules/settings/deployment-settings.service.ts +13 -54
  202. package/src/modules/settings/setup.routes.ts +3 -6
  203. package/src/modules/skills/__tests__/skills.tools.description.test.ts +91 -0
  204. package/src/modules/skills/skills.tools.ts +62 -16
  205. package/src/modules/tool-manuals/__tests__/tool-manuals.detail.route.test.ts +57 -0
  206. package/src/modules/tool-manuals/__tests__/tool-manuals.tools.test.ts +73 -4
  207. package/src/modules/tool-manuals/tool-manuals.contract.ts +24 -4
  208. package/src/modules/tool-manuals/tool-manuals.service.ts +17 -0
  209. package/src/modules/tool-manuals/tool-manuals.tools.ts +74 -36
  210. package/src/modules/tool-registry/__tests__/own-tool-schemas.test.ts +160 -0
  211. package/src/modules/tool-registry/__tests__/tool-description-length.test.ts +61 -59
  212. package/src/modules/tool-registry/description-length.ts +24 -26
  213. package/src/modules/tool-registry/guide-first.ts +34 -0
  214. package/src/modules/tool-registry/tool-registry.ts +9 -2
  215. package/src/modules/workflow/__tests__/apply-failure.test.ts +6 -1
  216. package/src/modules/workflow/agent-tools/__tests__/change-request-read-shape.test.ts +705 -0
  217. package/src/modules/workflow/agent-tools/__tests__/change-request-read.tools.test.ts +1518 -0
  218. package/src/modules/workflow/agent-tools/__tests__/workflow.tools.test.ts +23 -2
  219. package/src/modules/workflow/agent-tools/change-request-read-shape.ts +712 -0
  220. package/src/modules/workflow/agent-tools/change-request-read.tools.ts +724 -0
  221. package/src/modules/workflow/agent-tools/change-request-summary.ts +5 -1
  222. package/src/modules/workflow/agent-tools/workflow.tools.ts +8 -0
  223. package/src/modules/workflow/git/__tests__/git.service.appliedChange.test.ts +285 -0
  224. package/src/modules/workflow/git/__tests__/git.service.changedFilesForPr.test.ts +124 -0
  225. package/src/modules/workflow/git/__tests__/git.service.mergeChangeRequest.test.ts +334 -0
  226. package/src/modules/workflow/git/__tests__/pull-request.service.list-fetch.test.ts +72 -2
  227. package/src/modules/workflow/git/__tests__/pull-request.service.placeholder.test.ts +24 -2
  228. package/src/modules/workflow/git/__tests__/pull-request.service.test.ts +620 -1
  229. package/src/modules/workflow/git/git.service.ts +537 -94
  230. package/src/modules/workflow/git/merge-commit.ts +88 -0
  231. package/src/modules/workflow/git/pull-request.service.ts +380 -54
  232. package/src/modules/workflow/review-workflow/__tests__/approval-states.test.ts +7 -1
  233. package/src/modules/workflow/review-workflow/__tests__/merge-records-own-commit.test.ts +407 -0
  234. package/src/modules/workflow/review-workflow/review-workflow.service.ts +189 -11
  235. package/src/modules/workflow/workflow.routes.ts +7 -2
  236. package/src/modules/workflow/workflow.service.ts +7 -0
  237. package/src/modules/workspace/__tests__/escape-sequences.routes.test.ts +4 -3
  238. package/src/modules/workspace/__tests__/workspace.routes.move-platform-files.test.ts +21 -10
  239. package/src/modules/workspace/__tests__/workspace.tools.agents-file.test.ts +33 -55
  240. package/src/modules/workspace/__tests__/workspace.tools.test.ts +255 -22
  241. package/src/modules/workspace/startup/steps/__tests__/steps.test.ts +191 -489
  242. package/src/modules/workspace/startup/steps/seed-tree.ts +21 -27
  243. package/src/modules/workspace/startup/steps/template-files.step.ts +217 -249
  244. package/src/modules/workspace/startup/steps/template-source.ts +5 -3
  245. package/src/modules/workspace/workspace.tools.ts +226 -16
  246. package/src/shared/domain-errors.ts +15 -0
  247. package/src/shared/hidden-tools.ts +45 -0
  248. package/kb-template/AGENTS.md +0 -730
@@ -0,0 +1,291 @@
1
+ /**
2
+ * The platform's agent guide: what every agent is told to read before its
3
+ * first read or change in a knowledge base — the layout, where a new file
4
+ * goes, the rules every file tool shares, access control, skills and tool
5
+ * manuals.
6
+ *
7
+ * It is TEXT THE CODE OWNS, not a file in the repository. It used to be
8
+ * written to every protected branch as `AGENTS.md` and refreshed on every
9
+ * start, which had three costs: a repository that already had an `AGENTS.md`
10
+ * of its own lost it or had to rename ours; a distribution that wanted to add
11
+ * to the guide had to fork the whole file and then drifted from every change
12
+ * made here; and the file sat in git history on every branch, hidden from the
13
+ * tree by an ignore rule it had to keep writing. Now the guide is composed
14
+ * from SECTIONS at the moment an agent asks for it — `get_agent_guide`, or a
15
+ * `read_file` of the guide's name at the repository root — and a distribution
16
+ * shapes it with a hook that sees the sections and returns the sections it
17
+ * wants (append, replace or drop by id), so what Hexis says reaches it without
18
+ * a copy to maintain.
19
+ *
20
+ * The sections ship as markdown files in the package's `agent-guide/` folder
21
+ * (see `assets.ts`), one per section, with the layout placeholders the old
22
+ * template carried (`{{knowledgeBaseDir}}`, `{{skillsDir}}`, `{{pluginsDir}}`,
23
+ * `{{agentsFile}}`). ONE section is computed rather than read: the rules every
24
+ * file tool shares, which the MCP handshake also sends — the same string in
25
+ * both places, from `shared-file-rules.ts`, so a rule cannot be changed in one
26
+ * and left stale in the other.
27
+ */
28
+
29
+ import fs from 'node:fs/promises';
30
+ import path from 'node:path';
31
+ import {
32
+
33
+ renderKbLayoutPlaceholders,
34
+ resolveKbLayout,
35
+ type KbLayout,
36
+ } from '@bevel-software/platform-shared';
37
+ import { agentGuideDir } from '../../assets.js';
38
+ import { sharedFileRulesSection } from '../agent-instructions/shared-file-rules.js';
39
+
40
+ /** One section of the guide. */
41
+ export interface AgentGuideSection {
42
+ /**
43
+ * Stable id, so a distribution's hook can replace or drop a section by name
44
+ * and a test can say which one went missing. Core's ids are the
45
+ * {@link CORE_SECTION_IDS}.
46
+ */
47
+ id: string;
48
+ /**
49
+ * The section's markdown, heading included (`## …`). May carry the layout
50
+ * placeholders; they are rendered with the names in effect when the guide is
51
+ * composed, so a section can name the knowledge folder without knowing what
52
+ * this deployment calls it.
53
+ */
54
+ body: string;
55
+ /**
56
+ * The body is FINAL: already rendered, and not to be passed through the
57
+ * placeholder renderer. Set on the computed shared-rules section, which is
58
+ * rendered for the layout by the code that builds it; rendering it again
59
+ * could only misread a folder name it STATES as a placeholder.
60
+ */
61
+ literal?: boolean;
62
+ }
63
+
64
+ /**
65
+ * A distribution's say over the guide. Called with core's sections, in order,
66
+ * and the layout in effect; returns the sections the guide is composed from.
67
+ * Append to the array to add sections, map over it to replace one by id,
68
+ * filter it to drop one. Called on every composition, so a hook that reads
69
+ * something live (a feature flag, a setting) is read each time.
70
+ */
71
+ export type AgentGuideHook = (
72
+ sections: readonly AgentGuideSection[],
73
+ layout: Required<KbLayout>,
74
+ ) => readonly AgentGuideSection[] | Promise<readonly AgentGuideSection[]>;
75
+
76
+ /** Composes the guide for the layout in effect — the shape every consumer reads. */
77
+ export type AgentGuideReader = () => Promise<string>;
78
+
79
+ /** The computed section: the rules every file tool shares. */
80
+ export const WORKING_WITH_FILES_SECTION_ID = 'working-with-files';
81
+
82
+ /**
83
+ * Core's sections, in reading order. Every id but the shared-rules one names
84
+ * a file in the package's `agent-guide/` folder.
85
+ */
86
+ export const CORE_SECTION_IDS: readonly string[] = Object.freeze([
87
+ 'introduction',
88
+ 'directory-structure',
89
+ 'where-a-new-file-goes',
90
+ WORKING_WITH_FILES_SECTION_ID,
91
+ 'access-control',
92
+ 'skills',
93
+ 'tool-manuals',
94
+ 'conventions',
95
+ 'finding-things',
96
+ ]);
97
+
98
+ /**
99
+ * The one line that proves a root guide file is the platform's and not the
100
+ * customer's: the blockquote the packaged guide opened with for as long as
101
+ * the guide was a file, under every release. Asked for WHERE the header put
102
+ * it — a blockquote line among the first lines of the file — and not anywhere
103
+ * in the text: a note of the organisation's own that quotes the platform's
104
+ * sentence in its body must not read as ours, because the consequence of the
105
+ * mistake is a deletion. Still read on every start, to take the copies
106
+ * earlier releases wrote out of the repository (see template-files.step.ts),
107
+ * and on every read of the guide's name, so a copy still on a draft is not
108
+ * served twice.
109
+ */
110
+ const MANAGED_GUIDE_HEADER_LINE = '> **This file is managed by the platform.**';
111
+
112
+ /** How far down a file the header's blockquote can sit: under the title and a sentence or two, never further. */
113
+ const MANAGED_GUIDE_HEADER_WITHIN_LINES = 12;
114
+
115
+ /** Whether `text` is a copy of the guide a release wrote to disk, of any vintage. */
116
+ export function isManagedGuide(text: string): boolean {
117
+ return text
118
+ .split('\n', MANAGED_GUIDE_HEADER_WITHIN_LINES)
119
+ .some((line) => line.trimStart().startsWith(MANAGED_GUIDE_HEADER_LINE));
120
+ }
121
+
122
+ /** The raw section files, read once per process: the package does not change while it runs. */
123
+ let coreSectionFiles: Promise<ReadonlyMap<string, string>> | null = null;
124
+
125
+ async function readCoreSectionFiles(): Promise<ReadonlyMap<string, string>> {
126
+ const dir = agentGuideDir();
127
+ const entries = await Promise.all(
128
+ CORE_SECTION_IDS.filter((id) => id !== WORKING_WITH_FILES_SECTION_ID).map(
129
+ // Line endings normalised: a checkout on Windows may carry CRLF, and the
130
+ // guide is one text with one ending wherever it is served from.
131
+ async (id) => [id, (await fs.readFile(path.join(dir, `${id}.md`), 'utf8')).replace(/\r\n?/g, '\n')] as const,
132
+ ),
133
+ );
134
+ return new Map(entries);
135
+ }
136
+
137
+ /**
138
+ * Core's sections for `layout`: the files, placeholders unrendered, with the
139
+ * shared-rules section computed and in its place. What a distribution's hook
140
+ * is handed.
141
+ */
142
+ export async function coreAgentGuideSections(layout: KbLayout): Promise<AgentGuideSection[]> {
143
+ coreSectionFiles ??= readCoreSectionFiles();
144
+ let files: ReadonlyMap<string, string>;
145
+ try {
146
+ files = await coreSectionFiles;
147
+ } catch (err) {
148
+ // A failed read is not cached: the next composition tries the disk again.
149
+ coreSectionFiles = null;
150
+ throw err;
151
+ }
152
+ return CORE_SECTION_IDS.map((id) =>
153
+ id === WORKING_WITH_FILES_SECTION_ID
154
+ ? { id, body: sharedFileRulesSection(layout), literal: true }
155
+ : { id, body: files.get(id)! },
156
+ );
157
+ }
158
+
159
+ /** What the guide is composed for, beyond the layout. */
160
+ export interface AgentGuideContext {
161
+ /**
162
+ * The name of the repository's checkout folder inside a workspace, which
163
+ * the file tools take paths under (`knowledge-base/` by default). Rendered
164
+ * into `{{kbDirName}}` so the paths the guide shows are the paths this
165
+ * deployment's tools report back.
166
+ */
167
+ kbDirName?: string;
168
+ }
169
+
170
+ /** The checkout folder's name when none is given — core's own default. */
171
+ const DEFAULT_KB_DIR_NAME = 'knowledge-base';
172
+
173
+ /** One section of the guide as an agent reads it: rendered, with the heading's text as its title. */
174
+ export interface RenderedGuideSection {
175
+ id: string;
176
+ /** The heading line's text, without its `#`s — what the tool lists the section as. */
177
+ title: string;
178
+ /** The section's markdown, heading included, rendered for the layout. */
179
+ body: string;
180
+ }
181
+
182
+ /** Composes the guide's sections for the layout in effect — what the guide tool reads. */
183
+ export type AgentGuideSectionsReader = () => Promise<RenderedGuideSection[]>;
184
+
185
+ /**
186
+ * The guide's sections as an agent reads them: core's sections through the
187
+ * distribution's hook (when there is one), rendered for the layout, each
188
+ * with its title. The whole guide is these joined (see
189
+ * {@link composeAgentGuide}); `get_agent_guide` also serves one at a time.
190
+ *
191
+ * A hook may append, replace and drop sections, with ONE exception: the
192
+ * shared file rules ({@link WORKING_WITH_FILES_SECTION_ID}) must come back
193
+ * WITH TEXT IN THEM, as core's or as the hook's own replacement. A client
194
+ * that drops the handshake instructions has the guide as the only place to
195
+ * read the rules — a guide without them would leave agents nothing to read.
196
+ * A hook that drops the section, or hands it back empty, is a composition
197
+ * error, thrown rather than served. Judged on what would be SERVED — the
198
+ * rendered, trimmed body — so a replacement of whitespace is caught like a
199
+ * missing one, rather than passing the check and vanishing with the other
200
+ * empty sections below.
201
+ */
202
+ export async function agentGuideSections(
203
+ layout: KbLayout,
204
+ hook?: AgentGuideHook,
205
+ context: AgentGuideContext = {},
206
+ ): Promise<RenderedGuideSection[]> {
207
+ const resolved = resolveKbLayout(layout);
208
+ const core = await coreAgentGuideSections(resolved);
209
+ const sections = hook ? await hook(core, resolved) : core;
210
+ const kbDirName = context.kbDirName?.trim() || DEFAULT_KB_DIR_NAME;
211
+ const rendered = sections.map((section) => {
212
+ const body = (
213
+ section.literal
214
+ ? section.body
215
+ : renderKbLayoutPlaceholders(section.body.replaceAll('{{kbDirName}}', () => kbDirName), resolved)
216
+ ).trim();
217
+ return { id: section.id, title: titleOf(body, section.id), body };
218
+ });
219
+ // Judged over EVERY section under the id — a hook may return the id twice
220
+ // — on what survives the empty-body filter below: one with text is enough.
221
+ const rules = rendered.filter((section) => section.id === WORKING_WITH_FILES_SECTION_ID);
222
+ if (!rules.some((section) => section.body.length > 0)) {
223
+ throw new Error(
224
+ `The agent guide hook ${rules.length === 0 ? 'dropped' : 'emptied'} the "${WORKING_WITH_FILES_SECTION_ID}" ` +
225
+ 'section, which every file tool points at. Keep it, or replace it under the same id with the rules in it.',
226
+ );
227
+ }
228
+ return rendered.filter((section) => section.body.length > 0);
229
+ }
230
+
231
+ /**
232
+ * The heading's text, or the id when the section opens with no heading. An
233
+ * ATX heading may close with a run of `#` of its own (`## Custom ##`); that
234
+ * is a delimiter, not part of the title.
235
+ */
236
+ function titleOf(body: string, id: string): string {
237
+ const first = body.split('\n', 1)[0] ?? '';
238
+ const heading = /^#{1,6}\s+(.*?)(?:\s+#+)?\s*$/.exec(first);
239
+ return heading ? heading[1]!.trim() : id;
240
+ }
241
+
242
+ /**
243
+ * The guide as an agent reads it whole: the sections joined by blank lines,
244
+ * ending in one newline.
245
+ */
246
+ export async function composeAgentGuide(
247
+ layout: KbLayout,
248
+ hook?: AgentGuideHook,
249
+ context: AgentGuideContext = {},
250
+ ): Promise<string> {
251
+ return joinGuideSections(await agentGuideSections(layout, hook, context));
252
+ }
253
+
254
+ /** The sections as one document, the way `composeAgentGuide` joins them. */
255
+ export function joinGuideSections(sections: readonly RenderedGuideSection[]): string {
256
+ return `${sections.map((section) => section.body).join('\n\n')}\n`;
257
+ }
258
+
259
+ /**
260
+ * What a `read_file` of the guide's name answers when the knowledge base has an
261
+ * `AGENTS.md` of its own: their text first, whole, then a rule and one line
262
+ * saying what follows, then the platform's guide. Theirs first because it is
263
+ * the more specific of the two, and the line between so neither is read as
264
+ * part of the other.
265
+ */
266
+ export const PLATFORM_GUIDE_SEPARATOR =
267
+ "---\n\n_The text above is this knowledge base's own conventions file. The platform's guide follows; `get_agent_guide` returns it on its own._";
268
+
269
+ export function withPlatformGuideAppended(ownText: string, guide: string): string {
270
+ // Their text as they wrote it, line endings normalised and the trailing
271
+ // newlines folded into the one blank line before the separator. Nothing
272
+ // else is touched: leading indentation and trailing spaces are markdown.
273
+ const own = ownText.replace(/\r\n?/g, '\n').replace(/\n+$/, '');
274
+ // A file with nothing in it has nothing to put first, and a separator
275
+ // above nothing would claim a conventions file that says nothing.
276
+ if (own.trim().length === 0) return guide;
277
+ return `${own}\n\n${PLATFORM_GUIDE_SEPARATOR}\n\n${guide}`;
278
+ }
279
+
280
+ /** The one name the guide is read by: `AGENTS.md` at the repository root, the name coding agents look for. */
281
+ export const AGENT_GUIDE_FILE = 'AGENTS.md';
282
+
283
+ /**
284
+ * Whether a repository-relative path is where the guide is read — `AGENTS.md`
285
+ * at the repository root, exactly spelled, like every platform path. One
286
+ * name on every deployment: there is no setting for it any more.
287
+ */
288
+ export function isAgentGuidePath(repoRelativePath: string): boolean {
289
+ const norm = repoRelativePath.replace(/^\.?\/+/, '').replace(/\/+$/, '');
290
+ return norm === AGENT_GUIDE_FILE;
291
+ }
@@ -0,0 +1,21 @@
1
+ export {
2
+ AGENT_GUIDE_FILE,
3
+ CORE_SECTION_IDS,
4
+ PLATFORM_GUIDE_SEPARATOR,
5
+ WORKING_WITH_FILES_SECTION_ID,
6
+ agentGuideSections,
7
+ composeAgentGuide,
8
+ coreAgentGuideSections,
9
+ isAgentGuidePath,
10
+ isManagedGuide,
11
+ joinGuideSections,
12
+ withPlatformGuideAppended,
13
+ type AgentGuideContext,
14
+ type AgentGuideHook,
15
+ type AgentGuideReader,
16
+ type AgentGuideSection,
17
+ type AgentGuideSectionsReader,
18
+ type RenderedGuideSection,
19
+ } from './agent-guide.js';
20
+ export { GET_AGENT_GUIDE_TOOL, registerAgentGuideTool } from './agent-guide.tools.js';
21
+ export { GUIDE_FIRST_SENTENCE, withGuideFirst } from '../tool-registry/guide-first.js';
@@ -1,14 +1,6 @@
1
- import { readFile } from 'node:fs/promises';
2
- import path from 'node:path';
3
- import {
4
- DEFAULT_KB_LAYOUT,
5
- LEGACY_AGENTS_FILE,
6
- PREAMBLE_CAP,
7
- renderKbLayoutPlaceholders,
8
- } from '@bevel-software/platform-shared';
1
+ import { DEFAULT_KB_LAYOUT, PREAMBLE_CAP } from '@bevel-software/platform-shared';
9
2
  import { describe, expect, it } from 'vitest';
10
- import { defaultKbTemplateDir } from '../../../assets.js';
11
- import { renderTemplateText, SHARED_FILE_RULES_PLACEHOLDER } from '../../workspace/startup/steps/template-source.js';
3
+ import { composeAgentGuide } from '../../agent-guide/agent-guide.js';
12
4
  import {
13
5
  INSTRUCTIONS_CAP,
14
6
  PLATFORM_HEADER,
@@ -17,37 +9,29 @@ import {
17
9
  platformInstructions,
18
10
  } from '../compose.js';
19
11
  import {
20
- POINTER_GUIDE_NAME_BUDGET,
21
12
  SHARED_FILE_RULES_CAP,
22
- SHARED_RULES_POINTER_MAX,
23
13
  SHARED_RULES_SECTION,
24
14
  sharedFileRules,
25
15
  sharedFileRulesSection,
26
- sharedRulesPointer,
27
16
  } from '../shared-file-rules.js';
28
17
 
29
18
  /**
30
19
  * The rules every file tool shares are stated in TWO places — the handshake
31
- * `instructions` and the platform-managed agent guide — and in neither tool
20
+ * `instructions` and the platform's agent guide — and in neither tool
32
21
  * description. These tests are what makes "stated once" true: the two places
33
22
  * carry the SAME string, built from one text, so a rule cannot be changed in one
34
23
  * and left stale in the other.
35
24
  */
36
25
 
37
- /** The packaged template's agent guide, as it ships (placeholders unrendered). */
38
- async function guideTemplate(): Promise<string> {
39
- return readFile(path.join(defaultKbTemplateDir(), LEGACY_AGENTS_FILE), 'utf8');
40
- }
41
-
42
26
  describe('the shared file rules are one text, in two places', () => {
43
- it('puts the identical section in the handshake instructions and in the managed guide', async () => {
27
+ it('puts the identical section in the handshake instructions and in the agent guide', async () => {
44
28
  const section = sharedFileRulesSection(DEFAULT_KB_LAYOUT);
45
29
  expect(section.startsWith(`## ${SHARED_RULES_SECTION}\n\n`)).toBe(true);
46
30
 
47
31
  const instructions = composeAgentInstructions(null).instructions;
48
32
  expect(instructions).toContain(section);
49
33
 
50
- const guide = renderTemplateText(await guideTemplate(), DEFAULT_KB_LAYOUT);
34
+ const guide = await composeAgentGuide(DEFAULT_KB_LAYOUT);
51
35
  expect(guide).toContain(section);
52
36
 
53
37
  // Byte-for-byte the same text in both, which is the whole point: neither is
@@ -62,7 +46,7 @@ describe('the shared file rules are one text, in two places', () => {
62
46
 
63
47
  it('states every rule in both places, and the content rule exactly once in each', async () => {
64
48
  const instructions = composeAgentInstructions(null).instructions;
65
- const guide = renderTemplateText(await guideTemplate(), DEFAULT_KB_LAYOUT);
49
+ const guide = await composeAgentGuide(DEFAULT_KB_LAYOUT);
66
50
  const rules = sharedFileRules(DEFAULT_KB_LAYOUT);
67
51
  expect(rules.map((r) => r.id)).toEqual([
68
52
  'agent-guide',
@@ -87,7 +71,7 @@ describe('the shared file rules are one text, in two places', () => {
87
71
  });
88
72
 
89
73
  it('carries the whole content rule — the refused families, the byte tools and the upload path', async () => {
90
- for (const text of [composeAgentInstructions(null).instructions, renderTemplateText(await guideTemplate(), DEFAULT_KB_LAYOUT)]) {
74
+ for (const text of [composeAgentInstructions(null).instructions, await composeAgentGuide(DEFAULT_KB_LAYOUT)]) {
91
75
  expect(text).toContain('`binary_not_writable`');
92
76
  expect(text).toContain('copy_file, move_file and delete_file act on bytes of any kind');
93
77
  expect(text).toContain('unzip extracts the entries of a `.zip`');
@@ -102,109 +86,32 @@ describe('the shared file rules are one text, in two places', () => {
102
86
  }
103
87
  });
104
88
 
105
- it('names the guide under the name this deployment gave it, in both places and in the pointer', async () => {
89
+ /**
90
+ * The guide is no file: it is reached by one name at the KB root and by one
91
+ * tool, on every deployment. The first rule says so, and names the
92
+ * organisation's own conventions file beside it — which is what a
93
+ * `read_file` of that name serves first.
94
+ */
95
+ it('tells an agent to call get_agent_guide first, and that AGENTS.md answers with the same guide after the organisation\'s own', () => {
96
+ const rule = sharedFileRules(DEFAULT_KB_LAYOUT).find((r) => r.id === 'agent-guide')!.body;
97
+ expect(rule).toContain("call `get_agent_guide` and read the platform's guide");
98
+ expect(rule).toContain('read_file on `AGENTS.md` at the KB root answers with the same guide');
99
+ expect(rule).toContain("after the organisation's own conventions file of that name");
100
+ // The pre-rename name is still offered, for a knowledge base that kept one.
101
+ expect(rule).toContain('`CLAUDE.md`');
102
+ });
103
+
104
+ it('names one guide on every deployment, whatever name a deployment once saved for the written one', () => {
106
105
  const layout = { ...DEFAULT_KB_LAYOUT, agentsFile: 'HEXIS.md' };
107
106
  const section = sharedFileRulesSection(layout);
108
- // Ours first, then the organisation's own AGENTS.md, then the pre-rename name.
109
- expect(section).toContain('read `HEXIS.md` at the KB root, then `AGENTS.md` if it also exists');
110
- expect(section).toContain('`CLAUDE.md` on a knowledge base seeded before it was renamed');
111
- // The platform files a move refuses list the guide under its own name too.
112
- expect(section).toContain('`HEXIS.md`');
107
+ expect(section).toBe(sharedFileRulesSection(DEFAULT_KB_LAYOUT));
108
+ expect(section).not.toContain('HEXIS.md');
113
109
  expect(composeAgentInstructions(null, layout).instructions).toContain(section);
114
- expect(renderTemplateText(await guideTemplate(), layout)).toContain(section);
115
- expect(sharedRulesPointer(layout)).toBe(` Shared rules for all file tools: see "${SHARED_RULES_SECTION}" in HEXIS.md.`);
116
- });
117
-
118
- it('keeps the pointer bounded whatever the guide is called', () => {
119
- // The pointer rides on every file tool, so its length is not the guide's
120
- // business: `validateFilename` allows a 255-byte name, and an unbounded
121
- // pointer would reach 318 characters and push `file_stat` past the
122
- // description cap — on a renamed deployment only, which no measurement
123
- // taken under the default layout would ever show.
124
- const nameOfLength = (n: number): string => `${'x'.repeat(n - 3)}.md`;
125
- for (const length of [9, POINTER_GUIDE_NAME_BUDGET, POINTER_GUIDE_NAME_BUDGET + 1, 120, 255]) {
126
- const pointer = sharedRulesPointer({ ...DEFAULT_KB_LAYOUT, agentsFile: nameOfLength(length) });
127
- expect(pointer.length, `a ${length}-character name`).toBeLessThanOrEqual(SHARED_RULES_POINTER_MAX);
128
- }
129
- // Up to the budget the file is NAMED, which is the better sentence.
130
- const named = nameOfLength(POINTER_GUIDE_NAME_BUDGET);
131
- expect(sharedRulesPointer({ ...DEFAULT_KB_LAYOUT, agentsFile: named })).toContain(named);
132
- // Past it the guide is named by its role instead — a sentence that still
133
- // says where to look, rather than a catalog entry the client cuts.
134
- const tooLong = nameOfLength(POINTER_GUIDE_NAME_BUDGET + 1);
135
- const fallback = sharedRulesPointer({ ...DEFAULT_KB_LAYOUT, agentsFile: tooLong });
136
- expect(fallback).not.toContain(tooLong);
137
- expect(fallback).toBe(` Shared rules for all file tools: see "${SHARED_RULES_SECTION}" in the agent guide at the KB root.`);
138
- // Either way the section's first rule still names the file, so the name is
139
- // never actually lost.
140
- expect(sharedFileRulesSection({ ...DEFAULT_KB_LAYOUT, agentsFile: tooLong })).toContain(tooLong);
141
- });
142
-
143
- it('points at the section with one short sentence under the default name', () => {
144
- expect(sharedRulesPointer(DEFAULT_KB_LAYOUT)).toBe(
145
- ' Shared rules for all file tools: see "Working with files" in AGENTS.md.',
146
- );
147
- // An unset guide name means the default, as it does everywhere else.
148
- expect(sharedRulesPointer({ ...DEFAULT_KB_LAYOUT, agentsFile: undefined })).toBe(
149
- sharedRulesPointer(DEFAULT_KB_LAYOUT),
150
- );
151
- });
152
- });
153
-
154
- describe('the guide template asks for the rules rather than repeating them', () => {
155
- it('carries the placeholder once, and no rule text of its own', async () => {
156
- const template = await guideTemplate();
157
- expect(template.split(SHARED_FILE_RULES_PLACEHOLDER)).toHaveLength(2);
158
- // The rules are not ALSO written into the template — that is the copy that
159
- // would drift. Checked on a fragment of each rule rather than on the whole.
160
- for (const rule of sharedFileRules(DEFAULT_KB_LAYOUT)) {
161
- expect(template, rule.id).not.toContain(rule.body.slice(0, 60));
162
- }
163
- });
164
-
165
- it('renders the placeholder nowhere else, so other managed files are untouched', async () => {
166
- const access = await readFile(path.join(defaultKbTemplateDir(), 'access.md'), 'utf8');
167
- expect(access).not.toContain(SHARED_FILE_RULES_PLACEHOLDER);
168
- // So rendering it gains no rule text: whatever the layout renderer does to
169
- // its own tokens, the section is nowhere in the result.
170
- const rendered = renderTemplateText(access, DEFAULT_KB_LAYOUT);
171
- expect(rendered).not.toContain(sharedFileRulesSection(DEFAULT_KB_LAYOUT));
172
- expect(rendered).not.toContain(`## ${SHARED_RULES_SECTION}`);
173
- });
174
-
175
- it('leaves no placeholder behind once rendered', async () => {
176
- const guide = renderTemplateText(await guideTemplate(), DEFAULT_KB_LAYOUT);
177
- expect(guide).not.toContain('{{');
110
+ // The guide is not a platform file under any name: the list a move
111
+ // refuses does not carry it.
112
+ expect(section).toContain('`access.md` or `.bevelignore` in any folder, `roles.yaml` at the repository root');
178
113
  });
179
114
 
180
- it('renders the layout tokens first, then injects the rules — so the rules are never re-rendered', async () => {
181
- // A layout the two orders DISAGREE on, which an ordinary one does not: the
182
- // section states the knowledge-base folder's name, so a deployment whose
183
- // folder is literally called `{{skillsDir}}` puts a layout token inside the
184
- // rendered section. Injecting first would then rewrite it on the second
185
- // pass and the guide would name the skills folder where the rule means the
186
- // knowledge-base one. Pathological on purpose: it is the only kind of input
187
- // on which the order is observable at all, which is why a test that pins
188
- // the order has to use one.
189
- const layout = { ...DEFAULT_KB_LAYOUT, knowledgeBaseDir: '{{skillsDir}}', skillsDir: 'Playbooks' };
190
- const section = sharedFileRulesSection(layout);
191
- expect(section).toContain('{{skillsDir}}/');
192
-
193
- const template = `Skills live in {{skillsDir}}/.\n\n${SHARED_FILE_RULES_PLACEHOLDER}\n`;
194
- const rendered = renderTemplateText(template, layout);
195
- // The author's token is rendered, and the section survives byte for byte.
196
- expect(rendered).toBe(`Skills live in Playbooks/.\n\n${section}\n`);
197
-
198
- // And the other order really does differ on this input, so the assertion
199
- // above is load-bearing rather than true of both.
200
- const injectedFirst = renderKbLayoutPlaceholders(
201
- template.replaceAll(SHARED_FILE_RULES_PLACEHOLDER, () => section),
202
- layout,
203
- );
204
- expect(injectedFirst).not.toBe(rendered);
205
- expect(injectedFirst).not.toContain(section);
206
- expect(injectedFirst).toContain('`Playbooks/`) and git metadata are refused');
207
- });
208
115
  });
209
116
 
210
117
  describe('the handshake text stays inside the length it pins', () => {
@@ -28,8 +28,9 @@ export function createAgentInstructionsRoutes(
28
28
  readPreamble: AgentPreambleReader,
29
29
  /**
30
30
  * The layout in effect, read per request — the shared file rules name the
31
- * managed guide, whose name the setup save may change without a restart.
32
- * Optional so a construction with no layout in hand gets `AGENTS.md`.
31
+ * guide by the name a deployment saved for it, which the setup save may
32
+ * change without a restart. Optional so a construction with no layout in
33
+ * hand gets `AGENTS.md`.
33
34
  */
34
35
  kbLayout?: () => KbLayout,
35
36
  ): express.Router {
@@ -64,8 +64,10 @@ export const TOOL_PREFIX_LINE = "This organisation's knowledge base. Search it b
64
64
  * `header` field carries and what the card shows as fixed and not editable;
65
65
  * the admin's preamble follows it.
66
66
  *
67
- * A FUNCTION of the layout, because the shared rules name the managed guide
68
- * and its file name is a deployment setting.
67
+ * A FUNCTION of the layout, because the shared rules name the platform files
68
+ * by the root names a deployment chose (`Skills/`, `Plugins/`), which are
69
+ * deployment settings. The guide's name is not one of them: it is `AGENTS.md`
70
+ * everywhere.
69
71
  */
70
72
  export function platformInstructions(layout: KbLayout): string {
71
73
  return `${PLATFORM_HEADER}\n\n${sharedFileRulesSection(layout)}`;
@@ -119,10 +121,11 @@ export interface ComposedAgentInstructions {
119
121
  * unterminated `<!--` strips everything after it, so the most likely editing
120
122
  * slip withholds text rather than leaking it.
121
123
  *
122
- * `layout` decides only the guide's name inside the shared rules, and defaults
123
- * to the standard one — a caller with no layout in hand (a test, a surface that
124
- * predates the setting) gets `AGENTS.md`, which is what an unset name means
125
- * everywhere else.
124
+ * `layout` decides the root names the shared rules spell (`Skills/`,
125
+ * `Plugins/`, the knowledge folder) and defaults to the standard ones — a
126
+ * caller with no layout in hand (a test, a surface that predates the setting)
127
+ * gets the defaults. The guide's name is not among them: it is `AGENTS.md` on
128
+ * every deployment.
126
129
  */
127
130
  export function composeAgentInstructions(
128
131
  preamble: string | null,
@@ -13,13 +13,10 @@ export {
13
13
  type ComposedAgentInstructions,
14
14
  } from './compose.js';
15
15
  export {
16
- POINTER_GUIDE_NAME_BUDGET,
17
16
  SHARED_FILE_RULES_CAP,
18
- SHARED_RULES_POINTER_MAX,
19
17
  SHARED_RULES_SECTION,
20
18
  sharedFileRules,
21
19
  sharedFileRulesSection,
22
- sharedRulesPointer,
23
20
  type SharedFileRule,
24
21
  } from './shared-file-rules.js';
25
22
  export { readAgentPreamble, type AgentPreambleReader, type PreambleWorkspace } from './read-preamble.js';