@north-light/crouter 0.3.227 → 0.3.228

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 (286) hide show
  1. package/dist/api/client.d.ts +5 -5
  2. package/dist/api/client.js +3 -3
  3. package/dist/api/dto/broker-ops.d.ts +8 -0
  4. package/dist/api/dto/memory.d.ts +8 -7
  5. package/dist/api/dto/memory.js +2 -2
  6. package/dist/builtin-memory/05-kinds/design/00-base.md +3 -4
  7. package/dist/builtin-memory/05-kinds/design/01-orchestrator.md +4 -3
  8. package/dist/builtin-memory/05-kinds/design/design-contract.md +3 -6
  9. package/dist/builtin-memory/05-kinds/plan/00-base.md +3 -4
  10. package/dist/builtin-memory/05-kinds/plan/01-orchestrator.md +4 -5
  11. package/dist/builtin-memory/05-kinds/plan/plan-contract.md +5 -13
  12. package/dist/builtin-memory/design/guide.md +38 -16
  13. package/dist/builtin-memory/design/roadmap.md +4 -4
  14. package/dist/builtin-memory/insights/init.md +5 -5
  15. package/dist/builtin-memory/internal/INDEX.md +1 -1
  16. package/dist/builtin-memory/internal/examples/INDEX.md +1 -1
  17. package/dist/builtin-memory/internal/memory-loading.md +6 -6
  18. package/dist/builtin-memory/internal/plugins.md +1 -1
  19. package/dist/builtin-memory/plan/guide.md +53 -0
  20. package/dist/builtin-memory/plan/roadmap.md +10 -8
  21. package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/memory-slash-commands.ts +50 -37
  22. package/dist/clients/attach/__tests__/pane-tag-successor.test.js +56 -0
  23. package/dist/clients/attach/__tests__/profile-files.test.js +21 -0
  24. package/dist/clients/attach/chrome/inbox-strip.js +11 -7
  25. package/dist/clients/attach/session/pane-tag.d.ts +28 -2
  26. package/dist/clients/attach/session/pane-tag.js +48 -22
  27. package/dist/clients/attach/session/profile-files.d.ts +6 -0
  28. package/dist/clients/attach/session/profile-files.js +44 -11
  29. package/dist/clients/attach/slash/dispatch.js +4 -7
  30. package/dist/clients/attach/viewer.js +588 -596
  31. package/dist/commands/__tests__/surface-reload-target.test.js +21 -0
  32. package/dist/commands/api-client.d.ts +3 -7
  33. package/dist/commands/api-client.js +7 -12
  34. package/dist/commands/memory/__tests__/command-selector-and-mutation-guards.test.js +292 -0
  35. package/dist/commands/memory/__tests__/repository-root-lint.test.js +146 -0
  36. package/dist/commands/memory/delete.js +51 -24
  37. package/dist/commands/memory/edit.js +50 -10
  38. package/dist/commands/memory/find.js +105 -56
  39. package/dist/commands/memory/history.js +51 -31
  40. package/dist/commands/memory/lint.d.ts +0 -9
  41. package/dist/commands/memory/lint.js +78 -322
  42. package/dist/commands/memory/list.d.ts +12 -4
  43. package/dist/commands/memory/list.js +64 -25
  44. package/dist/commands/memory/move.js +103 -73
  45. package/dist/commands/memory/origin.js +35 -5
  46. package/dist/commands/memory/read.d.ts +4 -0
  47. package/dist/commands/memory/read.js +132 -80
  48. package/dist/commands/memory/shared.d.ts +115 -25
  49. package/dist/commands/memory/shared.js +331 -74
  50. package/dist/commands/memory/write.js +72 -21
  51. package/dist/commands/memory.js +2 -2
  52. package/dist/commands/node/lifecycle.js +2 -2
  53. package/dist/commands/pkg/market-manage.js +25 -24
  54. package/dist/commands/pkg/plugin-manage.d.ts +22 -6
  55. package/dist/commands/pkg/plugin-manage.js +118 -25
  56. package/dist/commands/pkg/shared.d.ts +8 -0
  57. package/dist/commands/pkg/shared.js +23 -20
  58. package/dist/commands/surface-reload.d.ts +5 -0
  59. package/dist/commands/surface-reload.js +9 -1
  60. package/dist/commands/sys/__tests__/migrate.test.js +1140 -22
  61. package/dist/commands/sys/__tests__/sync-project-guidance.test.js +218 -0
  62. package/dist/commands/sys/migrate.js +126 -140
  63. package/dist/commands/sys/panels/profiles-panel.d.ts +66 -0
  64. package/dist/commands/sys/panels/profiles-panel.js +599 -0
  65. package/dist/commands/sys/settings-shell.d.ts +4 -2
  66. package/dist/commands/sys/settings-shell.js +35 -4
  67. package/dist/commands/sys/settings.js +21 -5
  68. package/dist/commands/sys/sync-deps.d.ts +2 -0
  69. package/dist/commands/sys/sync-deps.js +26 -15
  70. package/dist/commands/sys/sync-project-guidance.js +228 -145
  71. package/dist/commands/sys/sync-skills.js +28 -17
  72. package/dist/commands/sys/update.js +11 -3
  73. package/dist/core/__tests__/canvas-inbox-watcher-hold.test.js +15 -2
  74. package/dist/core/__tests__/config-change-delta.test.js +106 -0
  75. package/dist/core/__tests__/context-intro.test.js +13 -9
  76. package/dist/core/__tests__/daemon-boot.test.js +22 -16
  77. package/dist/core/__tests__/fixtures/memory-slash-live-probe.js +5 -1
  78. package/dist/core/__tests__/human-deliver.test.js +19 -0
  79. package/dist/core/__tests__/inline-memory-refs.test.js +50 -46
  80. package/dist/core/__tests__/{serial → integration}/broker-fork-seam.test.js +1 -1
  81. package/dist/core/__tests__/{serial → integration}/broker-sdk-wiring.test.js +1 -1
  82. package/dist/core/__tests__/{serial → integration}/broker-snapshot-history.test.js +1 -1
  83. package/dist/core/__tests__/{serial → integration}/command-plugins.test.js +94 -3
  84. package/dist/core/__tests__/{serial → integration}/deferred-no-wake.test.js +1 -1
  85. package/dist/core/__tests__/{serial → integration}/flagship-lifecycle.test.js +13 -27
  86. package/dist/core/__tests__/{serial → integration}/host-teardown-process-group.test.js +1 -1
  87. package/dist/core/__tests__/{serial → integration}/human-deliver-e2e.test.js +1 -1
  88. package/dist/core/__tests__/{live-mutation-verbs.test.js → integration/live-mutation-verbs.test.js} +22 -33
  89. package/dist/core/__tests__/{serial → integration}/live-mutation.test.js +21 -41
  90. package/dist/core/__tests__/integration/refresh-stall-recycle.test.d.ts +1 -0
  91. package/dist/core/__tests__/{serial → integration}/refresh-stall-recycle.test.js +1 -1
  92. package/dist/core/__tests__/integration/revive.test.d.ts +1 -0
  93. package/dist/core/__tests__/{serial → integration}/revive.test.js +38 -19
  94. package/dist/core/__tests__/integration/spawn-root.test.d.ts +1 -0
  95. package/dist/core/__tests__/{serial → integration}/spawn-root.test.js +104 -1
  96. package/dist/core/__tests__/integration/subscription-delivery.test.d.ts +1 -0
  97. package/dist/core/__tests__/{serial → integration}/subscription-delivery.test.js +1 -1
  98. package/dist/core/__tests__/integration/tmux-surface.test.d.ts +1 -0
  99. package/dist/core/__tests__/{serial → integration}/tmux-surface.test.js +1 -1
  100. package/dist/core/__tests__/integration/worktree-land.test.d.ts +1 -0
  101. package/dist/core/__tests__/integration/worktree-land.test.js +400 -0
  102. package/dist/core/__tests__/integration/worktree-reap.test.d.ts +1 -0
  103. package/dist/core/__tests__/{serial/worktree.test.js → integration/worktree-reap.test.js} +6 -338
  104. package/dist/core/__tests__/kickoff.test.js +16 -5
  105. package/dist/core/__tests__/memory-resolver-precedence.test.js +122 -91
  106. package/dist/core/__tests__/nested-store-discovery.test.js +5 -3
  107. package/dist/core/__tests__/on-read-crouter-home-fence.test.js +2 -1
  108. package/dist/core/__tests__/on-read-dedup-resume.test.js +39 -27
  109. package/dist/core/__tests__/on-read-nested-store.test.js +19 -13
  110. package/dist/core/__tests__/profile-project-memory-delivery.test.js +138 -44
  111. package/dist/core/__tests__/repository-association.test.d.ts +1 -0
  112. package/dist/core/__tests__/repository-association.test.js +153 -0
  113. package/dist/core/__tests__/repository-root-identity.test.d.ts +1 -0
  114. package/dist/core/__tests__/repository-root-identity.test.js +219 -0
  115. package/dist/core/__tests__/seam/memory-slash-node-relative-inventory.test.js +27 -11
  116. package/dist/core/__tests__/warm-claim-preference-snapshot.test.d.ts +1 -0
  117. package/dist/core/__tests__/warm-claim-preference-snapshot.test.js +40 -0
  118. package/dist/core/canvas/canvas.d.ts +3 -0
  119. package/dist/core/canvas/canvas.js +30 -10
  120. package/dist/core/canvas/db.js +28 -1
  121. package/dist/core/canvas/paths.d.ts +4 -6
  122. package/dist/core/canvas/paths.js +8 -6
  123. package/dist/core/canvas/render-source.js +2 -2
  124. package/dist/core/canvas/types.d.ts +9 -13
  125. package/dist/core/exclusive-lock.d.ts +2 -0
  126. package/dist/core/exclusive-lock.js +21 -0
  127. package/dist/core/git.d.ts +0 -1
  128. package/dist/core/git.js +0 -3
  129. package/dist/core/human/__tests__/integration/inbox-core.test.d.ts +1 -0
  130. package/dist/core/human/feedback-companion.js +3 -0
  131. package/dist/core/human/scan.js +8 -1
  132. package/dist/core/keybindings/catalog.d.ts +2 -2
  133. package/dist/core/keybindings/catalog.js +2 -1
  134. package/dist/core/memory/doc-link-grammar.js +2 -2
  135. package/dist/core/memory/history.d.ts +24 -0
  136. package/dist/core/memory/history.js +66 -1
  137. package/dist/core/memory/identity.d.ts +65 -0
  138. package/dist/core/memory/identity.js +185 -0
  139. package/dist/core/memory/inline-ref-guidance.d.ts +1 -1
  140. package/dist/core/memory/inline-ref-guidance.js +1 -1
  141. package/dist/core/memory/inline-ref-inventory.d.ts +3 -10
  142. package/dist/core/memory/inline-ref-inventory.js +30 -62
  143. package/dist/core/memory/lint.d.ts +129 -0
  144. package/dist/core/memory/lint.js +517 -0
  145. package/dist/core/memory/project-namespace.d.ts +31 -0
  146. package/dist/core/memory/project-namespace.js +85 -0
  147. package/dist/core/memory/repository-association.d.ts +31 -0
  148. package/dist/core/memory/repository-association.js +129 -0
  149. package/dist/core/memory/tree.d.ts +39 -0
  150. package/dist/core/memory/tree.js +93 -0
  151. package/dist/core/memory-resolver.d.ts +193 -90
  152. package/dist/core/memory-resolver.js +461 -375
  153. package/dist/core/nested-stores.js +6 -13
  154. package/dist/core/profiles/select.d.ts +4 -1
  155. package/dist/core/profiles/select.js +108 -48
  156. package/dist/core/review/__tests__/capture-origin.test.js +2 -2
  157. package/dist/core/review/__tests__/stage-identity.test.js +2 -2
  158. package/dist/core/review/companion.js +11 -2
  159. package/dist/core/runtime/bearings.d.ts +2 -2
  160. package/dist/core/runtime/bearings.js +3 -3
  161. package/dist/core/runtime/broker/daemon-ops.d.ts +2 -2
  162. package/dist/core/runtime/broker/rebind.js +5 -0
  163. package/dist/core/runtime/broker-extension-render.d.ts +7 -3
  164. package/dist/core/runtime/broker-extension-render.js +10 -5
  165. package/dist/core/runtime/broker-persona-guidance.d.ts +19 -5
  166. package/dist/core/runtime/broker-persona-guidance.js +122 -27
  167. package/dist/core/runtime/deliver-live.d.ts +16 -4
  168. package/dist/core/runtime/deliver-live.js +29 -15
  169. package/dist/core/runtime/kickoff.d.ts +3 -3
  170. package/dist/core/runtime/kickoff.js +8 -16
  171. package/dist/core/runtime/lifecycle.js +2 -3
  172. package/dist/core/runtime/nodes.d.ts +3 -4
  173. package/dist/core/runtime/nodes.js +3 -4
  174. package/dist/core/runtime/persona.d.ts +8 -12
  175. package/dist/core/runtime/persona.js +24 -96
  176. package/dist/core/runtime/promote.d.ts +2 -2
  177. package/dist/core/runtime/promote.js +8 -21
  178. package/dist/core/runtime/revive.js +20 -17
  179. package/dist/core/runtime/spawn.js +18 -7
  180. package/dist/core/runtime/tmux-bindings.js +2 -3
  181. package/dist/core/runtime/warm-pool.js +3 -4
  182. package/dist/core/scope.js +2 -0
  183. package/dist/core/self-update.d.ts +0 -2
  184. package/dist/core/self-update.js +2 -35
  185. package/dist/core/substrate/__tests__/surface-match-memory-read.test.d.ts +1 -0
  186. package/dist/core/substrate/__tests__/surface-match-memory-read.test.js +28 -0
  187. package/dist/core/substrate/index.d.ts +2 -2
  188. package/dist/core/substrate/index.js +1 -1
  189. package/dist/core/substrate/injected-store.d.ts +43 -27
  190. package/dist/core/substrate/injected-store.js +208 -104
  191. package/dist/core/substrate/listings.d.ts +19 -12
  192. package/dist/core/substrate/listings.js +75 -52
  193. package/dist/core/substrate/on-read-node.d.ts +4 -7
  194. package/dist/core/substrate/on-read-node.js +6 -8
  195. package/dist/core/substrate/on-read.d.ts +22 -25
  196. package/dist/core/substrate/on-read.js +103 -147
  197. package/dist/core/substrate/render-node.d.ts +4 -7
  198. package/dist/core/substrate/render-node.js +5 -7
  199. package/dist/core/substrate/render.d.ts +21 -3
  200. package/dist/core/substrate/render.js +291 -223
  201. package/dist/core/substrate/schema.d.ts +1 -13
  202. package/dist/core/substrate/schema.js +5 -40
  203. package/dist/core/substrate/session-cache.d.ts +14 -4
  204. package/dist/core/substrate/session-cache.js +40 -22
  205. package/dist/core/substrate/surface-match.d.ts +9 -7
  206. package/dist/core/substrate/surface-match.js +26 -25
  207. package/dist/daemon/__tests__/helpers/source-daemon.d.ts +30 -0
  208. package/dist/daemon/__tests__/helpers/source-daemon.js +174 -0
  209. package/dist/daemon/__tests__/integration/migration-startup.test.d.ts +1 -0
  210. package/dist/daemon/__tests__/integration/migration-startup.test.js +97 -0
  211. package/dist/daemon/api/__tests__/bridge-heartbeat.test.js +21 -5
  212. package/dist/daemon/api/handlers/broker-ops.js +21 -16
  213. package/dist/daemon/api/handlers/memory.js +2 -0
  214. package/dist/daemon/crtrd.js +2 -0
  215. package/dist/daemon/human/finish.js +8 -1
  216. package/dist/daemon/manage.d.ts +0 -1
  217. package/dist/daemon/manage.js +7 -16
  218. package/dist/daemon/startup-policy.d.ts +1 -0
  219. package/dist/daemon/startup-policy.js +1 -0
  220. package/dist/migrations/001-surfaces-frontmatter.js +21 -109
  221. package/dist/migrations/002-profile-project-memory.js +1 -0
  222. package/dist/migrations/003-repository-root-memory-identity/front-door.d.ts +26 -0
  223. package/dist/migrations/003-repository-root-memory-identity/front-door.js +231 -0
  224. package/dist/migrations/003-repository-root-memory-identity/index.d.ts +2 -0
  225. package/dist/migrations/003-repository-root-memory-identity/index.js +513 -0
  226. package/dist/migrations/003-repository-root-memory-identity/references.d.ts +95 -0
  227. package/dist/migrations/003-repository-root-memory-identity/references.js +469 -0
  228. package/dist/migrations/003-repository-root-memory-identity/repository-facts.d.ts +39 -0
  229. package/dist/migrations/003-repository-root-memory-identity/repository-facts.js +349 -0
  230. package/dist/migrations/__tests__/activation-concurrency.test.d.ts +1 -0
  231. package/dist/migrations/__tests__/activation-concurrency.test.js +145 -0
  232. package/dist/migrations/__tests__/activation.test.d.ts +1 -0
  233. package/dist/migrations/__tests__/activation.test.js +149 -0
  234. package/dist/migrations/__tests__/deletion-and-root-declaration.test.d.ts +1 -0
  235. package/dist/migrations/__tests__/deletion-and-root-declaration.test.js +149 -0
  236. package/dist/migrations/activation.d.ts +16 -0
  237. package/dist/migrations/activation.js +78 -0
  238. package/dist/migrations/convergent.d.ts +14 -3
  239. package/dist/migrations/convergent.js +21 -10
  240. package/dist/migrations/corpus.d.ts +78 -0
  241. package/dist/migrations/corpus.js +497 -0
  242. package/dist/migrations/frontmatter-splice.d.ts +15 -0
  243. package/dist/migrations/frontmatter-splice.js +176 -0
  244. package/dist/migrations/registry.d.ts +6 -1
  245. package/dist/migrations/registry.js +7 -2
  246. package/dist/migrations/runner.d.ts +41 -0
  247. package/dist/migrations/runner.js +81 -0
  248. package/dist/migrations/types.d.ts +148 -9
  249. package/dist/migrations/types.js +9 -2
  250. package/dist/pi-extensions/__tests__/canvas-context-intro.test.js +225 -17
  251. package/dist/pi-extensions/__tests__/canvas-goal-capture-envelope.test.js +11 -3
  252. package/dist/pi-extensions/canvas-context-intro.d.ts +3 -5
  253. package/dist/pi-extensions/canvas-context-intro.js +50 -46
  254. package/dist/pi-extensions/canvas-doc-substrate.d.ts +1 -8
  255. package/dist/pi-extensions/canvas-doc-substrate.js +55 -122
  256. package/dist/pi-extensions/canvas-stophook.js +6 -13
  257. package/dist/shared/generated-context.d.ts +0 -3
  258. package/dist/shared/generated-context.js +0 -57
  259. package/dist/shared/tool-groups.js +2 -3
  260. package/package.json +5 -4
  261. package/runtime.lock.json +2 -2
  262. /package/dist/api/__tests__/{serial → integration}/client.test.d.ts +0 -0
  263. /package/dist/api/__tests__/{serial → integration}/client.test.js +0 -0
  264. /package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/__tests__/{serial → integration}/provider-rotation.test.ts +0 -0
  265. /package/dist/clients/{inbox/__tests__/serial/inbox-controller.test.d.ts → attach/__tests__/pane-tag-successor.test.d.ts} +0 -0
  266. /package/dist/clients/{inbox/__tests__/serial/mount-panel.test.d.ts → attach/__tests__/profile-files.test.d.ts} +0 -0
  267. /package/dist/{core/__tests__/live-mutation-verbs.test.d.ts → clients/inbox/__tests__/integration/inbox-controller.test.d.ts} +0 -0
  268. /package/dist/clients/inbox/__tests__/{serial → integration}/inbox-controller.test.js +0 -0
  269. /package/dist/{core/__tests__/serial/broker-fork-seam.test.d.ts → clients/inbox/__tests__/integration/mount-panel.test.d.ts} +0 -0
  270. /package/dist/clients/inbox/__tests__/{serial → integration}/mount-panel.test.js +0 -0
  271. /package/dist/{core/__tests__/serial/broker-sdk-wiring.test.d.ts → commands/__tests__/surface-reload-target.test.d.ts} +0 -0
  272. /package/dist/{core/__tests__/serial/broker-snapshot-history.test.d.ts → commands/memory/__tests__/command-selector-and-mutation-guards.test.d.ts} +0 -0
  273. /package/dist/{core/__tests__/serial/command-plugins.test.d.ts → commands/memory/__tests__/repository-root-lint.test.d.ts} +0 -0
  274. /package/dist/{core/__tests__/serial/deferred-no-wake.test.d.ts → commands/sys/__tests__/sync-project-guidance.test.d.ts} +0 -0
  275. /package/dist/core/__tests__/{serial/flagship-lifecycle.test.d.ts → config-change-delta.test.d.ts} +0 -0
  276. /package/dist/core/__tests__/{serial/host-teardown-process-group.test.d.ts → integration/broker-fork-seam.test.d.ts} +0 -0
  277. /package/dist/core/__tests__/{serial/human-deliver-e2e.test.d.ts → integration/broker-sdk-wiring.test.d.ts} +0 -0
  278. /package/dist/core/__tests__/{serial/live-mutation.test.d.ts → integration/broker-snapshot-history.test.d.ts} +0 -0
  279. /package/dist/core/__tests__/{serial/refresh-stall-recycle.test.d.ts → integration/command-plugins.test.d.ts} +0 -0
  280. /package/dist/core/__tests__/{serial/revive.test.d.ts → integration/deferred-no-wake.test.d.ts} +0 -0
  281. /package/dist/core/__tests__/{serial/spawn-root.test.d.ts → integration/flagship-lifecycle.test.d.ts} +0 -0
  282. /package/dist/core/__tests__/{serial/subscription-delivery.test.d.ts → integration/host-teardown-process-group.test.d.ts} +0 -0
  283. /package/dist/core/__tests__/{serial/tmux-surface.test.d.ts → integration/human-deliver-e2e.test.d.ts} +0 -0
  284. /package/dist/core/__tests__/{serial/worktree.test.d.ts → integration/live-mutation-verbs.test.d.ts} +0 -0
  285. /package/dist/core/{human/__tests__/serial/inbox-core.test.d.ts → __tests__/integration/live-mutation.test.d.ts} +0 -0
  286. /package/dist/core/human/__tests__/{serial → integration}/inbox-core.test.js +0 -0
@@ -1,19 +1,20 @@
1
1
  import { CrtrClient } from '../../api/index.js';
2
2
  import { interpolateNodePaths } from '../../core/canvas/paths.js';
3
3
  import { defineLeaf } from '../../core/command.js';
4
- import { CrtrError, notFound } from '../../core/errors.js';
4
+ import { CrtrError } from '../../core/errors.js';
5
5
  import { memoryExtensionEffectiveCatalog, projectEffectiveMemoryExtensions } from '../../core/memory/extensions.js';
6
6
  import { readText, realpathOrSelf } from '../../core/fs-utils.js';
7
7
  import { parseFrontmatterGeneric } from '../../core/frontmatter.js';
8
8
  import { docLinkNames } from '../../core/memory/doc-link-grammar.js';
9
- import { createMemoryDocSnapshot, listAllMemoryDocs, resolveMemoryDoc, resolveMemoryDocs } from '../../core/memory-resolver.js';
9
+ import { createMemoryDocSnapshot, listAllMemoryDocs, loadMemoryStoreView, resolveMemoryCandidates, resolveMemoryDocs, } from '../../core/memory-resolver.js';
10
10
  import { expandShellBlocks, hasShellBlocks, makeNodeShellRunner } from '../../core/runtime/shell-expansion.js';
11
- import { deliveredAtOrAbove, loadInjectedDocs, recordDelivery, saveInjectedDocs } from '../../core/substrate/injected-store.js';
12
- import { ancestorDirsOf, dirDedupKey, docsByName, isDirName, renderDirListing } from '../../core/substrate/listings.js';
11
+ import { emptyContextExposureState, exposedAtOrAbove, exposureTarget, loadContextExposureState, registerDocumentExposure, registerExposure, saveContextExposureState, } from '../../core/substrate/injected-store.js';
12
+ import { dirDedupKey, docListingDirs, docsByName, isDirName, renderDirListing } from '../../core/substrate/listings.js';
13
13
  import { memoryReadDocBlocks } from '../../core/substrate/on-read.js';
14
14
  import { renderResult } from '../../core/render.js';
15
- import { effectiveDocKind, normalizeDocName } from '../../core/substrate/schema.js';
16
- import { MEMORY_KINDS } from './shared.js';
15
+ import { effectiveDocKind } from '../../core/substrate/schema.js';
16
+ import { canonicalSegments } from '../../core/memory/identity.js';
17
+ import { MEMORY_KINDS, requireMountedStore, resolveReadSelector, selectorParam } from './shared.js';
17
18
  export { createMemoryDocSnapshot, resolveMemoryDocs };
18
19
  /** Load the body at an already-resolved memory path with the same frontmatter
19
20
  * stripping and in-node path interpolation as `memory read`. Callers that
@@ -48,6 +49,17 @@ function corpusDocs() {
48
49
  return [];
49
50
  }
50
51
  }
52
+ /** One physical document sharing a canonical identity, as the path-aware
53
+ * surfaces report it. `--dir <its project>` is how a non-winner is reached. */
54
+ export function candidateRow(doc, winner) {
55
+ return {
56
+ winner: doc === winner,
57
+ scope: doc.scope,
58
+ store_root: doc.root,
59
+ physical_relative_path: doc.physicalRelativePath,
60
+ path: doc.path,
61
+ };
62
+ }
51
63
  function attr(s) {
52
64
  return s
53
65
  .replace(/&/g, '&amp;')
@@ -58,25 +70,32 @@ function attr(s) {
58
70
  export const readLeaf = defineLeaf({
59
71
  name: 'read',
60
72
  description: 'load a memory document body by name',
61
- whenToUse: 'a task in front of you matches a stored document and you already know its name — read it before improvising. Exact identity and direct-path matches resolve by scope precedence (node > project stack > profile > user > builtin); bare leaf-name fallback is considered only when no such match exists. A directory name is also a valid target: it returns the neighborhood listing, and walking directories is the sanctioned browse move. You name the document by its crtr identifier, never a file path — do not cat or find the markdown off disk. Reach for `crtr memory find` first when you do not yet know which document applies.',
73
+ whenToUse: 'a task in front of you matches a stored document and you already know its canonical name — read it before improvising. Resolution is exact and takes the full canonical name (a project document is `<namespace>/<local name>`); among documents sharing one name the winner is the nearest scope (node > project stack > profile > user > builtin). A directory name is a valid target too: it answers with that directory\u2019s own document, when it has one, together with its immediate listing, and walking directories is the sanctioned browse move. You name the document by its crtr identifier, never a file path — do not cat or find the markdown off disk. Reach for `crtr memory find` first when you do not yet know which document applies.',
62
74
  help: {
63
75
  name: 'memory read',
64
- summary: 'resolve a path-derived name to its document body (frontmatter stripped unless --frontmatter), or a directory name to its listing',
76
+ summary: 'resolve an exact canonical name to its document body (frontmatter stripped unless --frontmatter), plus the directory listing when that name has members',
65
77
  params: [
66
- { kind: 'positional', name: 'name', required: true, constraint: 'Path-derived memory identifier (e.g. `topic` or `area/topic`). Exact identity and direct-path matches resolve by scope precedence: node > project stack > profile > user > builtin. A bare leaf falls back only when no exact/direct match exists. A directory name returns that directory\u2019s listing instead of a document.' },
78
+ { kind: 'positional', name: 'name', required: true, constraint: 'Full canonical memory name — a project document is `<namespace>/<local name>`, every other store\u2019s local names are already canonical, where a nested store\'s namespace includes its path under the repository root. Resolution is exact: a file path, a bare leaf, a `<scope>/<name>` spelling, and a trailing `/INDEX` all fail. A name that carries members returns its listing alongside its body; a name that is only a directory returns the listing alone.' },
67
79
  { kind: 'flag', name: 'kind', type: 'enum', choices: [...MEMORY_KINDS], required: false, constraint: 'Narrows resolution when the name is ambiguous across kinds.' },
68
80
  { kind: 'flag', name: 'frontmatter', type: 'bool', required: false, constraint: 'When present, includes the YAML frontmatter in the returned body. Off by default — only the body is returned.' },
81
+ selectorParam('scope-read'),
82
+ selectorParam('dir', {}, 'Reads that store alone: the name resolves against its documents only, and the listing shows its members only.'),
69
83
  ],
70
84
  output: [
71
- { name: 'name', type: 'string', required: true, constraint: 'Resolved document name, or the directory name on a directory read.' },
85
+ { name: 'name', type: 'string', required: true, constraint: 'Resolved canonical document name, or the directory name on a directory read.' },
86
+ { name: 'local_name', type: 'string', required: false, constraint: 'Store-local identity, before the store namespace composed the canonical name. Absent on a directory read.' },
72
87
  { name: 'kind', type: 'string', required: false, constraint: 'Resolved kind: knowledge or preference. Absent on a directory read.' },
73
88
  { name: 'scope', type: 'string', required: false, constraint: 'Scope the document was resolved from: node, project, profile, user, or builtin. Absent on a directory read.' },
74
89
  { name: 'path', type: 'string', required: false, constraint: 'Absolute path to the document on disk. Revise it with `crtr memory edit`, never by editing this file — an edit records why the change happened and lands in the doc’s revision history. Absent on a directory read.' },
90
+ { name: 'store_root', type: 'string', required: false, constraint: 'Absolute memory store root the document loaded from. Absent on a directory read.' },
91
+ { name: 'physical_relative_path', type: 'string', required: false, constraint: 'Store-relative physical markdown path, ordering prefixes and any `INDEX.md` kept. Absent on a directory read.' },
92
+ { name: 'representation', type: 'string', required: false, constraint: 'Physical representation of this identity: leaf-file or directory-index. Absent on a directory read.' },
93
+ { name: 'candidates', type: 'object[]', required: false, constraint: 'Present only when several physical documents share this canonical name: one row per candidate in source order, {winner, scope, store_root, physical_relative_path, path}. The winner is what this read returned; reach a non-winning one with `--dir <its project>`.' },
75
94
  { name: 'content', type: 'string', required: false, constraint: 'Document body. Frontmatter stripped unless --frontmatter is set. A document may embed shell as `!`cmd`` or a ```! fenced block; each runs once per read, in the current working directory, and is replaced by its output. Read `path` off disk when you need the literal unexecuted text. May be preceded by one `<auto-loaded-context>` block carrying the doc\u2019s neighborhood listings and any docs routed to this read. Absent on a directory read.' },
76
- { name: 'listing', type: 'string[]', required: false, constraint: 'Present only on a directory read: one `[[name]]: <when-and-why>` line per member doc, one bare `[[name]]` per subdirectory. Follow any line with `crtr memory read <name>`.' },
95
+ { name: 'listing', type: 'string[]', required: false, constraint: 'Present when the name has members — a directory document returns its body AND this listing, a bare directory returns the listing alone: one `[[name]]: <when-and-why>` line per member doc, one bare `[[name]]` per subdirectory. Follow any line with `crtr memory read <name>`.' },
77
96
  { name: 'extensions', type: 'object', required: false, constraint: 'Effective valid plugin-owned metadata, keyed by plugin namespace then field. Includes enabled declaration defaults without writing them to frontmatter; disabled and unresolved namespaces are omitted. Present only on a document read.' },
78
97
  { name: 'links', type: 'string[]', required: false, constraint: 'Canonical names this document links to via `[[name]]` that resolve in the current corpus — further reading, loaded only on demand with `crtr memory read <name>`. A directory link returns that directory\u2019s listing. Omitted when the body carries no resolvable links.' },
79
- { name: 'follow_up', type: 'string', required: false, constraint: 'Present on a directory read, or on a document read whose body links to further memory documents.' },
98
+ { name: 'follow_up', type: 'string', required: false, constraint: 'Present whenever the read returned a listing or resolvable body links.' },
80
99
  ],
81
100
  outputKind: 'object',
82
101
  effects: [
@@ -88,34 +107,64 @@ export const readLeaf = defineLeaf({
88
107
  const nameRaw = input['name'];
89
108
  const kindFilter = input['kind'];
90
109
  const includeFrontmatter = input['frontmatter'];
91
- // Resolve a substrate/memory document across scopes: exact/direct matches
92
- // by precedence first, then leaf-name fallback. --kind is threaded
93
- // INTO resolution (not a post-filter): a nearer wrong-kind doc is skipped
94
- // so a farther matching-kind doc is found instead of shadowing it into a
95
- // false not_found. The substrate corpus now includes plugin docs (mounted
96
- // under <pluginName>/), so this resolves every name.
97
- let doc;
98
- try {
99
- doc = resolveMemoryDoc(nameRaw, { kind: kindFilter, includeDescendants: true });
110
+ // The selector decides the corpus: a scope filter over the target view, or
111
+ // the one exact store `--dir` names. --kind is threaded INTO resolution
112
+ // (not a post-filter): a nearer wrong-kind doc is skipped so a farther
113
+ // matching-kind doc is found instead of shadowing it into a false
114
+ // not_found.
115
+ const selector = resolveReadSelector({
116
+ scope: input['scope'],
117
+ dir: input['dir'],
118
+ });
119
+ const kindOpts = kindFilter === undefined ? {} : { kind: kindFilter };
120
+ let set = null;
121
+ // Re-runs the throwing resolve when nothing answered, so the canonical
122
+ // not-found (with its spelling guidance) is the resolver's, not a copy.
123
+ let fail;
124
+ let listingByName;
125
+ let linkByName;
126
+ if (selector.store !== null) {
127
+ // `--dir` reads ONE exact store: its documents alone decide what resolves
128
+ // and what the listings show. Links stay corpus-wide — a body legitimately
129
+ // points at another store — with the selected store first so it wins its
130
+ // own names.
131
+ const view = loadMemoryStoreView(requireMountedStore(selector.store), true);
132
+ listingByName = docsByName(view.docs);
133
+ linkByName = docsByName([...view.docs, ...corpusDocs()]);
134
+ set = view.candidates(nameRaw, kindOpts);
135
+ fail = () => view.resolve(nameRaw, kindOpts);
100
136
  }
101
- catch (e) {
102
- if (!(e instanceof CrtrError && e.code === 'not_found'))
103
- throw e;
137
+ else {
138
+ const resolveOpts = {
139
+ ...kindOpts,
140
+ ...(selector.scope === undefined ? {} : { scope: selector.scope }),
141
+ includeDescendants: true,
142
+ };
143
+ listingByName = linkByName = docsByName(listAllMemoryDocs(selector.scope, false, true));
144
+ try {
145
+ set = resolveMemoryCandidates(nameRaw, resolveOpts);
146
+ }
147
+ catch (e) {
148
+ if (!(e instanceof CrtrError && e.code === 'not_found'))
149
+ throw e;
150
+ }
151
+ fail = () => resolveMemoryCandidates(nameRaw, resolveOpts);
104
152
  }
105
153
  const nodeId = process.env['CRTR_NODE_ID'] || undefined;
106
- if (doc === undefined) {
107
- // No doc answers for a directory — the bare listing does. An explicit
108
- // dir read is a deliberate act, so every line renders (unlisted
109
- // excluded), never dedup-filtered; rendered members still record at
110
- // preview so unsolicited channels stay quiet about them later.
111
- const dirName = normalizeDocName(nameRaw.replace(/\/+$/, ''));
112
- const byName = docsByName(corpusDocs());
113
- if (dirName !== '' && isDirName(byName, dirName)) {
114
- const seen = nodeId !== undefined ? loadInjectedDocs(nodeId) : null;
115
- const listing = renderDirListing(byName, dirName, seen, false);
116
- if (nodeId !== undefined && seen !== null) {
117
- recordDelivery(seen, dirDedupKey(dirName), 'preview');
118
- saveInjectedDocs(nodeId, seen);
154
+ if (set === null) {
155
+ // No document answers this name — a directory with no document of its own
156
+ // still does, through its listing. An explicit dir read is a deliberate
157
+ // act, so every line renders (unlisted excluded), never dedup-filtered;
158
+ // rendered members still record at preview so unsolicited channels stay
159
+ // quiet about them later.
160
+ const dirName = canonicalSegments(nameRaw.trim()).join('/');
161
+ if (dirName !== '' && isDirName(listingByName, dirName)) {
162
+ const contextExposure = nodeId !== undefined ? loadContextExposureState(nodeId) : null;
163
+ const target = contextExposure === null ? null : exposureTarget(contextExposure, 'transcript');
164
+ const listing = renderDirListing(listingByName, dirName, target, false);
165
+ if (nodeId !== undefined && contextExposure !== null && target !== null) {
166
+ registerExposure(target, dirDedupKey(dirName), 'preview');
167
+ saveContextExposureState(nodeId, contextExposure);
119
168
  }
120
169
  return {
121
170
  name: dirName,
@@ -123,11 +172,9 @@ export const readLeaf = defineLeaf({
123
172
  follow_up: 'Each line is readable with `crtr memory read <name>` — a `[[name]]: <line>` entry is a doc, a bare `[[name]]` is a subdirectory whose read browses deeper. Browse the whole inventory with `crtr memory list`.',
124
173
  };
125
174
  }
126
- throw notFound(`memory document not found: ${nameRaw}`, {
127
- memory: nameRaw,
128
- next: 'Run `crtr memory find <query>` to discover documents, or `crtr memory list` to browse the inventory.',
129
- });
175
+ fail();
130
176
  }
177
+ const doc = set.winner;
131
178
  const kind = effectiveDocKind(doc);
132
179
  const raw = readMemoryDocContent(doc.path, includeFrontmatter);
133
180
  // A read is an explicit act by an agent that can already run shell, so a
@@ -137,71 +184,76 @@ export const readLeaf = defineLeaf({
137
184
  const content = hasShellBlocks(raw)
138
185
  ? await expandShellBlocks(raw, makeNodeShellRunner({ cwd: process.cwd() }))
139
186
  : raw;
140
- const byName = docsByName(corpusDocs());
141
187
  // `[[name]]` doc links are pointers, never transclusion: surface which
142
188
  // linked names actually resolve so the reader can follow one when the
143
189
  // task needs that depth, without ever auto-loading a linked body. A link
144
- // may also name a directory — a browse link to its listing.
145
- const links = docLinkNames(doc.body).filter((linkName) => {
146
- if (isDirName(byName, linkName))
147
- return true;
148
- try {
149
- // `resolveMemoryDoc` permits leaf-name fallback for interactive reads;
150
- // a stored graph edge does not. Compare the resolved canonical name
151
- // so an old shorthand never masquerades as a first-class doc link.
152
- return resolveMemoryDoc(linkName, { includeDescendants: true }).name === linkName;
153
- }
154
- catch {
155
- return false;
156
- }
157
- });
190
+ // is an exact canonical address — a directory name included, which browses
191
+ // to its listing.
192
+ const links = docLinkNames(doc.body).filter((linkName) => linkByName.has(linkName) || isDirName(linkByName, linkName));
158
193
  // Reading a doc discloses where it sits (its directory's listing and each
159
- // ancestor's, once per transcript) and fires the memory-read event
194
+ // ancestor's, once per loaded context) and fires the memory-read event
160
195
  // (corpus docs whose `memory-read` entries match the resolved name).
161
196
  // Both prepend in one <auto-loaded-context> block. Without a node
162
197
  // identity the call is stateless: listings render undeduped and nothing
163
198
  // records.
164
- const seen = nodeId !== undefined ? loadInjectedDocs(nodeId) : null;
199
+ const contextExposure = nodeId !== undefined
200
+ ? loadContextExposureState(nodeId)
201
+ : emptyContextExposureState();
202
+ const target = exposureTarget(contextExposure, 'transcript');
165
203
  const subject = await fetchSubject(nodeId);
166
204
  const excludeReal = realpathOrSelf(doc.path);
167
205
  // The explicit read IS a content delivery: record it first so unsolicited
168
206
  // channels (including this doc's own line in its directory listing) stay
169
207
  // quiet about a doc whose full body is already in the transcript.
170
- if (seen !== null)
171
- recordDelivery(seen, excludeReal, 'content');
208
+ registerDocumentExposure(target, doc.path, doc.body, 'content');
172
209
  const blocks = [];
173
- for (const dir of ancestorDirsOf(doc.name)) {
174
- if (seen !== null && deliveredAtOrAbove(seen, dirDedupKey(dir), 'preview'))
210
+ // A doc that fronts a directory carries its members with its body: that
211
+ // listing is part of the deliberate read (every line renders) and returns
212
+ // as `listing`, while the ancestors above it stay unsolicited neighborhood
213
+ // context. The node's own document is never among its children, so nothing
214
+ // repeats.
215
+ const dirs = docListingDirs(listingByName, doc.name);
216
+ const ownDir = dirs[0] === doc.name;
217
+ let listing;
218
+ if (ownDir) {
219
+ listing = renderDirListing(listingByName, doc.name, target, false);
220
+ registerExposure(target, dirDedupKey(doc.name), 'preview');
221
+ }
222
+ for (const dir of ownDir ? dirs.slice(1) : dirs) {
223
+ if (exposedAtOrAbove(contextExposure, dirDedupKey(dir), 'preview'))
175
224
  continue;
176
- const lines = renderDirListing(byName, dir, seen, true);
177
- if (seen !== null)
178
- recordDelivery(seen, dirDedupKey(dir), 'preview');
225
+ const lines = renderDirListing(listingByName, dir, target, true);
226
+ registerExposure(target, dirDedupKey(dir), 'preview');
179
227
  if (lines.length > 0)
180
228
  blocks.push(`<memory-listing dir="${attr(dir)}">\n${lines.join('\n')}\n</memory-listing>`);
181
229
  }
182
- const targetNames = [doc.name];
183
- if (doc.plugin !== undefined) {
184
- const slash = doc.name.indexOf('/');
185
- if (slash > 0)
186
- targetNames.push(doc.name.slice(slash + 1));
187
- }
188
- blocks.push(...memoryReadDocBlocks(subject, excludeReal, targetNames, seen ?? new Map()));
189
- if (nodeId !== undefined && seen !== null)
190
- saveInjectedDocs(nodeId, seen);
230
+ blocks.push(...memoryReadDocBlocks(subject, excludeReal, doc.name, target));
231
+ if (nodeId !== undefined)
232
+ saveContextExposureState(nodeId, contextExposure);
191
233
  const finalContent = blocks.length === 0 ? content : `<auto-loaded-context>\n${blocks.join('\n')}\n</auto-loaded-context>\n\n${content}`;
234
+ const hasListing = listing !== undefined && listing.length > 0;
235
+ const followUp = hasListing
236
+ ? links.length > 0
237
+ ? 'Each `[[name]]` line in `listing` is one of this directory’s immediate members, and the `[[name]]` links in the body are further reading — follow either with `crtr memory read <name>` only when the task needs that depth.'
238
+ : 'Each `[[name]]` line in `listing` is one of this directory’s immediate members, readable with `crtr memory read <name>` — follow one only when the task needs that depth.'
239
+ : links.length > 0
240
+ ? 'The `[[name]]` links in the body are further reading — follow one with `crtr memory read <name>` only when the task needs that depth.'
241
+ : null;
192
242
  return {
193
243
  name: doc.name,
244
+ local_name: doc.localName,
194
245
  kind,
195
246
  scope: doc.scope,
196
247
  path: doc.path,
248
+ store_root: doc.root,
249
+ physical_relative_path: doc.physicalRelativePath,
250
+ representation: doc.representation,
251
+ ...(set.candidates.length > 1 ? { candidates: set.candidates.map((c) => candidateRow(c, doc)) } : {}),
197
252
  content: finalContent,
253
+ ...(hasListing ? { listing } : {}),
198
254
  extensions: projectEffectiveMemoryExtensions(doc.frontmatter?.['extensions'], memoryExtensionEffectiveCatalog(doc)),
199
- ...(links.length > 0
200
- ? {
201
- links,
202
- follow_up: 'The `[[name]]` links in the body are further reading — follow one with `crtr memory read <name>` only when the task needs that depth.',
203
- }
204
- : {}),
255
+ ...(links.length > 0 ? { links } : {}),
256
+ ...(followUp === null ? {} : { follow_up: followUp }),
205
257
  };
206
258
  },
207
259
  render: (result) => {
@@ -1,40 +1,122 @@
1
1
  import { type FlagParam } from '../../core/help.js';
2
- import type { MemoryDoc, MemoryScope } from '../../core/memory-resolver.js';
2
+ import { type MemoryCandidateSet, type MemoryDoc, type MemoryScope, type MemoryStoreDescriptor } from '../../core/memory-resolver.js';
3
+ import { type DocRepresentation } from '../../core/memory/identity.js';
3
4
  import { type MemoryExtensionCatalog } from '../../core/memory/extensions.js';
4
5
  import type { MemoryExtensionScalar } from '../../types.js';
5
6
  export declare const MEMORY_KINDS: readonly ["knowledge", "preference"];
7
+ export declare const MEMORY_READ_SCOPES: readonly ["user", "project", "profile", "node", "builtin"];
8
+ export declare const MEMORY_WRITE_SCOPES: readonly ["user", "project", "profile", "node"];
6
9
  export declare const MEMORY_SCOPES: readonly ["user", "project", "profile", "node"];
7
10
  /** Scope sort weight matching resolution precedence (node > project stack >
8
11
  * profile > user > builtin). Used by `list` for its "scope then kind then
9
12
  * name" ordering. */
10
13
  export declare function scopeRank(scope: MemoryScope): number;
11
- /** Resolve the write target scope + its memory dir. Default: project when a
12
- * project scope exists for the cwd, else user. An explicit `--scope project`
13
- * with no project scope yet scaffolds one (ensureProjectScopeRoot). User scope
14
- * always resolves. `--scope profile` is NEVER a default — it requires a
15
- * selected profile, from `profileArg` (an explicit `--profile <id-or-name>`)
16
- * or else the process's `CRTR_PROFILE_ID`, resolved through the centralized
17
- * `loadProfileManifest` (never a raw path join). `dirArg` (an explicit
18
- * `--dir <path>`) pins the EXACT project directory, cwd-free: the target is
19
- * `<dir>/.crouter/memory/`, scaffolded if absent, never the cwd-ancestor walk
20
- * — the way a profiled agent writes into any project in its purview, and the
21
- * only way to target a dir shadowed by an ancestor store. `--scope node`
22
- * targets the this-node store (`nodes/<CRTR_NODE_ID>/context/memory/`) and
23
- * requires a running node. Returns the absolute `<root>/memory` dir to write
24
- * under. */
14
+ export declare function registerMemoryListingExposure(identity: string): void;
15
+ /** The raw selector flags a memory leaf accepts. */
16
+ export interface MemorySelectorInput {
17
+ scope?: string | undefined;
18
+ dir?: string | undefined;
19
+ profile?: string | undefined;
20
+ }
21
+ export interface MemoryReadSelector {
22
+ /** Source-stack filter for a target view, absent when unfiltered. */
23
+ scope?: MemoryScope;
24
+ /** The one exact store `--dir` selected, or null for a target view. Returned
25
+ * whatever its mount status: lint inspects a store the runtime refuses to
26
+ * mount, everything else calls `requireMountedStore` first. */
27
+ store: MemoryStoreDescriptor | null;
28
+ }
29
+ export interface MemoryWriteSelector {
30
+ scope: MemoryScope;
31
+ /** Absolute `<root>/memory` dir the document is written under. */
32
+ memoryDir: string;
33
+ /** The store its canonical name composes under. */
34
+ store: MemoryStoreDescriptor;
35
+ }
36
+ /** Resolve the read selection: a scope filter, or the exact store `--dir`
37
+ * names. */
38
+ export declare function resolveReadSelector(input: MemorySelectorInput): MemoryReadSelector;
39
+ /** Resolve the one store a mutation writes to. Default: project when a project
40
+ * scope exists for the cwd, else user. An explicit `--scope project` with no
41
+ * project scope yet scaffolds one (`ensureProjectScopeRoot`). `--scope
42
+ * profile` is NEVER a default — it requires a selected profile, from
43
+ * `--profile <id-or-name>` or else the process's `CRTR_PROFILE_ID`, resolved
44
+ * through the centralized `loadProfileManifest` (never a raw path join).
45
+ * `--scope node` targets the this-node store
46
+ * (`nodes/<CRTR_NODE_ID>/context/memory/`) and requires a running node. */
47
+ export declare function resolveWriteSelector(input: MemorySelectorInput): MemoryWriteSelector;
48
+ /** The scope + memory dir a write lands in. Retained for the leaves that still
49
+ * place documents by path alone; they cut over to `resolveWriteSelector` and
50
+ * `planNewDocumentPlacement` in their own phases. */
25
51
  export declare function resolveWriteTarget(scopeArg: string | undefined, profileArg?: string, dirArg?: string): {
26
52
  scope: MemoryScope;
27
53
  memoryDir: string;
28
54
  };
55
+ /** Refuse a project store the runtime will not resolve through. A project
56
+ * repository-root store carries the identity every canonical name in it composes under, so
57
+ * without a valid declaration no name can be validated, stripped, or placed;
58
+ * user, profile, and node stores have no declaration and always pass, even
59
+ * before their directory exists. */
60
+ export declare function requireMountedStore(store: MemoryStoreDescriptor): MemoryStoreDescriptor;
61
+ /** Normalize and validate a full canonical name: ordering prefixes are
62
+ * physical-path-only, a trailing `INDEX` is never an address, and the segment
63
+ * charset is fixed. */
64
+ export declare function requireCanonicalName(rawName: string): string;
65
+ /** The store-local name a full canonical name has inside one exact store, or a
66
+ * usage error when it falls outside that store's namespace. `''` names the
67
+ * store's own root document — a project store answers at its namespace. */
68
+ export declare function requireLocalNameInStore(store: MemoryStoreDescriptor, rawName: string): string;
69
+ export interface MemoryPlacement {
70
+ store: MemoryStoreDescriptor;
71
+ /** Full canonical name — the address the document answers at. */
72
+ canonicalName: string;
73
+ /** Store-local name, `''` for the store's own root document. */
74
+ localName: string;
75
+ representation: DocRepresentation;
76
+ /** Store-relative markdown path. */
77
+ physicalRelativePath: string;
78
+ /** Absolute path of the `.md` file. */
79
+ path: string;
80
+ }
81
+ /** Where a NEW document (a `write`, a `move` destination) physically lands in
82
+ * one exact store, after proving the canonical identity is free.
83
+ *
84
+ * Both physical forms carry the same identity, so occupancy is checked against
85
+ * the loaded identities AND both `<local>.md` and `<local>/INDEX.md` — a file
86
+ * whose explicit `name:` points elsewhere still owns its path. The chosen form
87
+ * is `<local>/INDEX.md` when the canonical directory already has descendants
88
+ * in this store (the document fronts a directory that exists), else
89
+ * `<local>.md`; descendants created later never relocate it, because either
90
+ * form validly owns the identity. Pass `storeDocs` when the caller already
91
+ * loaded the store, and `source` when planning a move, whose own identity and
92
+ * file are being vacated. */
93
+ export declare function planNewDocumentPlacement(store: MemoryStoreDescriptor, rawName: string, storeDocs?: readonly MemoryDoc[], source?: MemoryDoc): MemoryPlacement;
94
+ /** The document a mutation acts on, refusing an identity two files in ONE
95
+ * store both own. Reads stay answerable (`<name>/INDEX.md` deterministically
96
+ * wins), but a mutation would silently pick one of two files, so it stops with
97
+ * both paths. Candidates in OTHER stores are ordinary alternates — precedence
98
+ * picked the winner and `--dir` reaches the rest. */
99
+ export declare function requireUnambiguousDoc(set: MemoryCandidateSet): MemoryDoc;
100
+ /** Move and delete are the only mutation recovery path for a same-store
101
+ * collision. The exact-store winner remains the ordinary target; only the
102
+ * selected store's candidates are ordered by their physical path. */
103
+ export interface MutationDocSelection {
104
+ doc: MemoryDoc;
105
+ collisionPaths: readonly string[];
106
+ }
107
+ export declare function selectMutationDoc(set: MemoryCandidateSet): MutationDocSelection;
29
108
  /** Remove a just-removed file's now-empty parent directories up to (never
30
- * including) its tree root, so removing the last entry under an `area/`
31
- * prefix does not orphan an empty directory. The root is derived by walking
32
- * up one dirname per name segment: a doc named `a/b` sits at `<root>/a/b.md`,
33
- * so its root is two dirnames above — the same arithmetic holds for a
34
- * `.history/<name>.jsonl` sidecar. Stops at the first non-empty ancestor. */
35
- export declare function pruneEmptyParents(filePath: string, nameSegments: number): void;
36
- /** Map a path-derived name (`topic` or `area/topic`) to its file path under a
37
- * memory dir, guarding against traversal/absolute escapes. */
109
+ * including) its store root, so removing the last entry under an `area/`
110
+ * prefix does not orphan an empty directory. `pathSegments` counts the
111
+ * PHYSICAL store-relative path (`doc.physicalRelativePath.split('/').length`),
112
+ * never the canonical name, which carries the store's namespace on top of it —
113
+ * the same count addresses a `.history/<path>.jsonl` sidecar. Stops at the
114
+ * first non-empty ancestor. */
115
+ export declare function pruneEmptyParents(filePath: string, pathSegments: number): void;
116
+ /** Map a STORE-LOCAL name (`topic`, `area/topic`, `INDEX`) to its file path
117
+ * under a memory dir, guarding against traversal/absolute escapes. Physical
118
+ * mapping only: it composes no namespace and collapses no `INDEX`, so a
119
+ * canonical name reaches it through `planNewDocumentPlacement`. */
38
120
  export declare function memoryFilePath(memoryDir: string, name: string): string;
39
121
  export declare function coerceGate(raw: string): Record<string, unknown>;
40
122
  /** Coerce one `--surface` value into a validated surfaces entry. Strict where
@@ -96,6 +178,13 @@ export declare function applyExtensionChanges(frontmatter: Record<string, unknow
96
178
  /** The frontmatter-overlay flags, keyed by flag name, carrying the prose true
97
179
  * on BOTH leaves. `write` appends its creation clauses with `withConstraint`;
98
180
  * neither leaf restates the other's rules. */
181
+ /** The selector flags, keyed by flag name, carrying the prose true on every
182
+ * leaf that accepts them. A leaf appends its own clause with `selectorParam`;
183
+ * none of them restates the shared rules. */
184
+ export declare const SELECTOR_PARAMS: Record<string, FlagParam>;
185
+ /** One selector flag, with leaf-specific prose appended to the shared
186
+ * constraint and any leaf-specific schema override applied. */
187
+ export declare function selectorParam(name: string, overrides?: Partial<FlagParam>, extraConstraint?: string): FlagParam;
99
188
  export declare const FRONTMATTER_OVERLAY_PARAMS: Record<string, FlagParam>;
100
189
  /** One overlay flag, with leaf-specific prose appended to the shared
101
190
  * constraint and any leaf-specific schema override applied. */
@@ -105,8 +194,9 @@ export declare function overlayParam(name: string, overrides?: Partial<FlagParam
105
194
  * `--doc-rationale`, because `--rationale` there means why THIS REVISION is
106
195
  * happening. Same field, same prose, two flag names that cannot be confused. */
107
196
  export declare const DOC_RATIONALE_CONSTRAINT = "Frontmatter rationale \u2014 the observed agent failure that made this doc necessary. Maintainer-facing only: never ships in any delivered surface (boot render, on-read injection, `memory read` content), visible only via `memory read --frontmatter`. Omitting the flag preserves an existing rationale unchanged.";
108
- export declare const GUIDE_SURFACES = "Every doc appears in its directory\u2019s listing by default (suppress with --unlisted); everything beyond the listing is explicit `surfaces` routing \u2014 entries declaring WHEN the doc delivers and how much. Events: `boot` delivers in the boot catalog every agent sees; `workspace-open` delivers in first-message context when cwd/profile mounts the doc\u2019s project store (project stores only); `read` fires when the agent reads a matching file (globs vs the file\u2019s absolute path and basename, `./`-anchored globs vs its path relative to the store\u2019s owning repo dir, `match-frontmatter` predicates over the read file\u2019s own YAML frontmatter); `memory-read` fires when another memory doc is read (globs vs that doc\u2019s canonical name, `./` anchored to this doc\u2019s own name directory); `command` fires when a matching shell command runs (globs vs the whole command string, `*` crossing `/`). An entry may also carry `gate`, a node-config predicate using the same vocabulary as the document-level `gate`: event constraints and entry gate both must match, then all participating entries fold to the highest `at` (`content` > `preview` > `name`), with no cross-entry deny precedence. The document-level `gate` remains the hard eligibility check: when it fails, no automatic surface can deliver the document. `at` sets how much delivers: `name` the bare tag, `preview` the routing line, `content` the whole body. Explicit `memory read` and listings remain deliberate access and ignore gates/rungs; only companion docs routed by their `memory-read` entries use entry gates. Each entry is paid by every future agent it fires on, so default down: no surfaces at all is correct for reference depth reached through listings and [[links]], and when a doc fits in a single sentence, deliver `content` directly \u2014 a `preview` routing line would run longer than the doc itself. Sentence-length `content` docs are correct; never pad a memory to be more verbose than the rule or fact it carries.";
197
+ export declare const GUIDE_SURFACES = "Every doc appears in its directory\u2019s listing by default (suppress with --unlisted); everything beyond the listing is explicit `surfaces` routing \u2014 entries declaring WHEN the doc delivers and how much. Events: `boot` delivers in the boot catalog every agent sees; `workspace-open` delivers in first-message context when cwd/profile mounts the doc\u2019s project store (project stores only); `read` fires when the agent reads a matching file (globs vs the file\u2019s absolute path and basename, `./`-anchored globs vs its path relative to the store\u2019s owning repo dir, `match-frontmatter` predicates over the read file\u2019s own YAML frontmatter); `memory-read` fires when another memory doc is read (globs vs that doc\u2019s canonical name, `./` anchored to this doc\u2019s routing anchor \u2014 its own canonical name when it is its directory\u2019s document, otherwise the canonical directory it sits in); `command` fires when a matching shell command runs (globs vs the whole command string, `*` crossing `/`). An entry may also carry `gate`, a node-config predicate using the same vocabulary as the document-level `gate`: event constraints and entry gate both must match, then all participating entries fold to the highest `at` (`content` > `preview` > `name`), with no cross-entry deny precedence. The document-level `gate` remains the hard eligibility check: when it fails, no automatic surface can deliver the document. `at` sets how much delivers: `name` the bare tag, `preview` the routing line, `content` the whole body. Explicit `memory read` and listings remain deliberate access and ignore gates/rungs; only companion docs routed by their `memory-read` entries use entry gates. Each entry is paid by every future agent it fires on, so default down: no surfaces at all is correct for reference depth reached through listings and [[links]], and when a doc fits in a single sentence, deliver `content` directly \u2014 a `preview` routing line would run longer than the doc itself. Sentence-length `content` docs are correct; never pad a memory to be more verbose than the rule or fact it carries.";
109
198
  export declare const GUIDE_ROUTING_LINE = "The routing line (--when-and-why-to-read) is the only text an agent reads before deciding to load the doc. The test for its when-clause: it names an observable circumstance in the reader\u2019s current work, not the doc\u2019s topic reworded as an activity. If the WHEN can be inferred from the title alone, it is not a real trigger. Bad on a todo list: \"When planning or prioritizing work across this profile.\" Good: \"When the user mentions something from their todos, or asks what is still outstanding across this profile.\" The test for its because-clause: if it can be derived by paraphrasing the doc\u2019s advice, it is a restatement, not a payoff \u2014 a real payoff names a consequence in the reader\u2019s world that the document itself never asserts. Bad: \"because only genuine first principles belong in taste memory.\" Bad: \"because keeping the test loop fast and free of speculative tests protects the development pace\" \u2014 the doc\u2019s rule as an outcome, derivable straight from its advice. Good: \"because identifying throughlines in user taste lets future decisions be made faster and with less re-litigation.\" Someone mid-task who has not read the doc must be able to decide from this line alone whether the read is worth it. If you cannot name the concrete situation that triggers it, you do not yet understand the memory \u2014 ask the user one sharp question instead of improvising.";
110
199
  export declare const GUIDE_PREDICATE_VOCABULARY = "Document gates, surface-entry gates, and match-frontmatter share the same predicate language: a field map is AND-ed across fields; dotted fields resolve nested values; field matchers may be scalar, array, or object. Scalar matchers do exact equality, with arrays matching any element. Array matchers do membership or intersection. Object matchers accept `eq`, `ne`, `in`, `nin`, `exists`, `contains`, `containsAll`, `containsAny`, `matches`, `imatches`, `gt`, `gte`, `lt`, and `lte`. Combinators are `all`, `any`, and `not`; sibling field matchers next to them are AND-ed in. An empty condition is inert. An unknown op never matches.";
111
- export declare const GUIDE_DOC_LINKS = "Link related docs with `[[canonical/name]]`. A doc link in any body is a first-class cross-reference to another memory document by its exact canonical name \u2014 the same identifier `crtr memory read` takes. A bare directory name is a valid link too: following it returns that directory\u2019s listing, a browse entrance rather than a doc. Links are pointers, never transclusion: the linked body is loaded only when a reader follows it, so a link costs nothing until needed. `crtr memory lint` fails on a link that resolves to no document or directory, and `crtr memory read` lists a doc\u2019s resolvable links alongside its body. There is no alias or label form.";
200
+ export declare const GUIDE_DOC_LINKS = "Link related docs with `[[canonical/name]]`. A doc link in any body is a first-class cross-reference to another memory document by its exact canonical name \u2014 the same identifier `crtr memory read` takes, namespace included for a project document. A bare directory name is a valid link too: following it returns that directory\u2019s own document when it has one, plus the directory listing. Links are pointers, never transclusion: the linked body is loaded only when a reader follows it, so a link costs nothing until needed. `crtr memory lint` fails on a link that resolves to no document or directory, and `crtr memory read` lists a doc\u2019s resolvable links alongside its body. There is no alias, label, or old-name form \u2014 a renamed target needs its links rewritten.";
201
+ export declare const GUIDE_CANONICAL_NAMES = "A document has exactly one address: its canonical name, the store\u2019s namespace composed with the document\u2019s store-local name. A repository declares its namespace once, as `namespace:` on the root `INDEX.md` of `<repository root>/.crouter/memory/`; a store nested inside that repository composes under the repository namespace plus its own directory path relative to the repository root, and declares nothing itself. An installed plugin\u2019s store mounts under the plugin\u2019s own name; user, profile, node, and builtin stores have no namespace, so their local names are already canonical. A local doc `unit-tests` in a store that declares `namespace: acme/core` therefore answers only at `acme/core/unit-tests`, and the store\u2019s root document answers at `acme/core`; a store at `packages/api/.crouter/memory/` in a repository declaring `namespace: acme` answers under `acme/packages/api`. A non-root explicit `name:` in frontmatter is store-LOCAL \u2014 it never carries the namespace, and composition is unconditional, so a name that merely looks prefixed is prefixed again. The namespace itself is immutable through the memory commands: `crtr memory write <namespace> --dir <repository root>` declares it once in an empty repository-root store, `crtr sys migrate --dir <repository root>` converts a legacy one, and nothing else rewrites it. A trailing `INDEX` is never part of an address in any store: `area/INDEX.md` is the document attached to directory `area`, so it answers at `area` and reading `area` returns that body together with the directory\u2019s immediate listing. Resolution is exact \u2014 a file path, a bare leaf, a `<scope>/<name>` spelling, and a trailing `/INDEX` all fail \u2014 so run `crtr memory find <leaf>` when you do not know the canonical name.";
112
202
  export {};