@crouton-kit/crouter 0.3.18 → 0.3.19

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 (257) hide show
  1. package/dist/build-root.js +6 -0
  2. package/dist/builtin-memory/crouter-development/marketplaces.md +164 -0
  3. package/dist/builtin-memory/crouter-development/personas/base-prompt.md +58 -0
  4. package/dist/builtin-memory/crouter-development/personas/orchestrator-prompt.md +60 -0
  5. package/dist/builtin-memory/crouter-development/personas.md +117 -0
  6. package/dist/builtin-memory/crouter-development/plugins.md +163 -0
  7. package/dist/builtin-memory/design.md +60 -0
  8. package/dist/builtin-memory/development.md +116 -0
  9. package/dist/builtin-memory/planning.md +66 -0
  10. package/dist/builtin-memory/spec.md +99 -0
  11. package/dist/builtin-personas/design/orchestrator.md +1 -1
  12. package/dist/builtin-personas/developer/orchestrator.md +1 -1
  13. package/dist/builtin-personas/lifecycle/resident.md +1 -1
  14. package/dist/builtin-personas/lifecycle/terminal.md +5 -2
  15. package/dist/builtin-personas/orchestration-kernel.md +15 -24
  16. package/dist/builtin-personas/plan/orchestrator.md +1 -1
  17. package/dist/builtin-personas/runtime-base.md +6 -3
  18. package/dist/builtin-personas/spec/orchestrator.md +1 -1
  19. package/dist/builtin-personas/waiting.md +8 -0
  20. package/dist/builtin-skills/skills/crouter-development/personas/orchestrator-prompt/SKILL.md +1 -1
  21. package/dist/builtin-skills/skills/crouter-development/plugins/SKILL.md +2 -2
  22. package/dist/builtin-views/_lib/states.mjs +161 -0
  23. package/dist/builtin-views/canvas/client.mjs +303 -0
  24. package/dist/builtin-views/canvas/view.mjs +576 -0
  25. package/dist/builtin-views/git-pr/client.mjs +440 -0
  26. package/dist/builtin-views/git-pr/view.mjs +675 -0
  27. package/dist/builtin-views/inbox/_lib/render.mjs +177 -0
  28. package/dist/builtin-views/inbox/sources/gmail.mjs +965 -0
  29. package/dist/builtin-views/inbox/sources/linkedin.mjs +427 -0
  30. package/dist/builtin-views/inbox/view.mjs +889 -0
  31. package/dist/builtin-views/linkedin/client.mjs +610 -0
  32. package/dist/builtin-views/linkedin/view.mjs +1171 -0
  33. package/dist/clients/attach/attach-cmd.d.ts +2 -0
  34. package/dist/clients/attach/attach-cmd.js +354 -0
  35. package/dist/clients/attach/chat-view.d.ts +77 -0
  36. package/dist/clients/attach/chat-view.js +450 -0
  37. package/dist/clients/attach/clipboard-image.d.ts +16 -0
  38. package/dist/clients/attach/clipboard-image.js +113 -0
  39. package/dist/clients/attach/config-load.d.ts +31 -0
  40. package/dist/clients/attach/config-load.js +113 -0
  41. package/dist/clients/attach/extension-dialogs.d.ts +29 -0
  42. package/dist/clients/attach/extension-dialogs.js +101 -0
  43. package/dist/clients/attach/input-controller.d.ts +54 -0
  44. package/dist/clients/attach/input-controller.js +204 -0
  45. package/dist/clients/attach/slash-commands.d.ts +36 -0
  46. package/dist/clients/attach/slash-commands.js +200 -0
  47. package/dist/clients/attach/view-socket.d.ts +48 -0
  48. package/dist/clients/attach/view-socket.js +126 -0
  49. package/dist/commands/attention.js +3 -3
  50. package/dist/commands/canvas-prune.js +1 -1
  51. package/dist/commands/daemon.js +3 -2
  52. package/dist/commands/human/prompts.js +1 -1
  53. package/dist/commands/human/queue.js +43 -8
  54. package/dist/commands/human/shared.d.ts +2 -0
  55. package/dist/commands/memory/find.d.ts +1 -0
  56. package/dist/commands/memory/find.js +180 -0
  57. package/dist/commands/memory/lint.d.ts +1 -0
  58. package/dist/commands/memory/lint.js +140 -0
  59. package/dist/commands/memory/list.d.ts +1 -0
  60. package/dist/commands/memory/list.js +79 -0
  61. package/dist/commands/memory/read.js +103 -0
  62. package/dist/commands/memory/shared.d.ts +30 -0
  63. package/dist/commands/memory/shared.js +122 -0
  64. package/dist/commands/memory/write.d.ts +1 -0
  65. package/dist/commands/memory/write.js +85 -0
  66. package/dist/commands/memory.d.ts +2 -0
  67. package/dist/commands/memory.js +27 -0
  68. package/dist/commands/node.d.ts +3 -2
  69. package/dist/commands/node.js +607 -61
  70. package/dist/commands/pkg/market-manage.js +1 -1
  71. package/dist/commands/push.js +6 -6
  72. package/dist/commands/revive.js +1 -1
  73. package/dist/commands/skill/author.js +1 -1
  74. package/dist/commands/skill/shared.d.ts +1 -8
  75. package/dist/commands/skill/shared.js +2 -55
  76. package/dist/commands/skill.js +9 -14
  77. package/dist/commands/sys/doctor.js +1 -1
  78. package/dist/commands/sys/update.js +1 -1
  79. package/dist/commands/view-cycle.d.ts +2 -0
  80. package/dist/commands/view-cycle.js +125 -0
  81. package/dist/commands/view-list.d.ts +2 -0
  82. package/dist/commands/view-list.js +66 -0
  83. package/dist/commands/view-new.d.ts +2 -0
  84. package/dist/commands/view-new.js +70 -0
  85. package/dist/commands/view-pick.d.ts +2 -0
  86. package/dist/commands/view-pick.js +119 -0
  87. package/dist/commands/view-run.d.ts +2 -0
  88. package/dist/commands/view-run.js +191 -0
  89. package/dist/commands/view.d.ts +2 -0
  90. package/dist/commands/view.js +29 -0
  91. package/dist/core/__tests__/broker-lifecycle.test.d.ts +1 -0
  92. package/dist/core/__tests__/broker-lifecycle.test.js +677 -0
  93. package/dist/core/__tests__/broker-sdk-wiring.test.d.ts +1 -0
  94. package/dist/core/__tests__/broker-sdk-wiring.test.js +166 -0
  95. package/dist/core/__tests__/cascade-close.test.js +12 -2
  96. package/dist/core/__tests__/child-death-wake.test.d.ts +1 -0
  97. package/dist/core/__tests__/child-death-wake.test.js +245 -0
  98. package/dist/core/__tests__/context-intro.test.js +76 -62
  99. package/dist/core/__tests__/daemon-boot.test.js +14 -5
  100. package/dist/core/__tests__/daemon-liveness.test.js +34 -9
  101. package/dist/core/__tests__/detach-focus.test.d.ts +1 -0
  102. package/dist/core/__tests__/detach-focus.test.js +206 -0
  103. package/dist/core/__tests__/draw-style.test.d.ts +1 -0
  104. package/dist/core/__tests__/draw-style.test.js +258 -0
  105. package/dist/core/__tests__/fixtures/c3-custom-provider-ext.d.ts +2 -0
  106. package/dist/core/__tests__/fixtures/c3-custom-provider-ext.js +18 -0
  107. package/dist/core/__tests__/fixtures/fake-engine.d.ts +138 -0
  108. package/dist/core/__tests__/fixtures/fake-engine.js +614 -0
  109. package/dist/core/__tests__/fixtures/fake-pi-host.js +1 -0
  110. package/dist/core/__tests__/flagship-lifecycle.test.js +7 -1
  111. package/dist/core/__tests__/frame-decoder-perf.test.d.ts +1 -0
  112. package/dist/core/__tests__/frame-decoder-perf.test.js +198 -0
  113. package/dist/core/__tests__/helpers/harness.d.ts +9 -0
  114. package/dist/core/__tests__/helpers/harness.js +111 -1
  115. package/dist/core/__tests__/home-session.test.js +41 -8
  116. package/dist/core/__tests__/human-new-window-regression.test.d.ts +1 -0
  117. package/dist/core/__tests__/human-new-window-regression.test.js +101 -0
  118. package/dist/core/__tests__/kickoff.test.js +37 -3
  119. package/dist/core/__tests__/live-mutation.test.js +50 -33
  120. package/dist/core/__tests__/memory.test.js +23 -115
  121. package/dist/core/__tests__/placement-focus.test.js +5 -0
  122. package/dist/core/__tests__/placement-teardown.test.js +54 -11
  123. package/dist/core/__tests__/relaunch.test.js +4 -3
  124. package/dist/core/__tests__/review-render-pane-regression.test.d.ts +1 -0
  125. package/dist/core/__tests__/review-render-pane-regression.test.js +133 -0
  126. package/dist/core/__tests__/spawn-root.test.js +10 -0
  127. package/dist/core/__tests__/spike-harness.test.js +1 -0
  128. package/dist/core/__tests__/wake-bearings.test.d.ts +1 -0
  129. package/dist/core/__tests__/wake-bearings.test.js +156 -0
  130. package/dist/core/__tests__/wake-origin.test.d.ts +1 -0
  131. package/dist/core/__tests__/wake-origin.test.js +110 -0
  132. package/dist/core/bootstrap.js +1 -1
  133. package/dist/core/canvas/browse/__tests__/render.test.js +1 -0
  134. package/dist/core/canvas/browse/app.js +24 -2
  135. package/dist/core/canvas/browse/model.d.ts +38 -2
  136. package/dist/core/canvas/browse/model.js +134 -10
  137. package/dist/core/canvas/browse/render.d.ts +6 -12
  138. package/dist/core/canvas/browse/render.js +72 -104
  139. package/dist/core/canvas/canvas.js +15 -22
  140. package/dist/core/canvas/db.js +46 -0
  141. package/dist/core/canvas/index.d.ts +1 -0
  142. package/dist/core/canvas/index.js +1 -0
  143. package/dist/core/canvas/pid.d.ts +4 -0
  144. package/dist/core/canvas/pid.js +23 -0
  145. package/dist/core/canvas/render.d.ts +11 -2
  146. package/dist/core/canvas/render.js +69 -0
  147. package/dist/core/canvas/types.d.ts +85 -3
  148. package/dist/core/canvas/wakeups.d.ts +76 -0
  149. package/dist/core/canvas/wakeups.js +185 -0
  150. package/dist/core/config.js +4 -1
  151. package/dist/core/frontmatter.js +37 -124
  152. package/dist/core/memory-resolver.d.ts +49 -0
  153. package/dist/core/memory-resolver.js +141 -0
  154. package/dist/core/personas/loader.d.ts +7 -0
  155. package/dist/core/personas/loader.js +36 -5
  156. package/dist/core/personas/resolve.js +13 -3
  157. package/dist/core/predicate.d.ts +63 -0
  158. package/dist/core/predicate.js +189 -0
  159. package/dist/core/resolver.js +26 -5
  160. package/dist/core/runtime/bearings.d.ts +53 -12
  161. package/dist/core/runtime/bearings.js +132 -59
  162. package/dist/core/runtime/broker-cli.d.ts +1 -0
  163. package/dist/core/runtime/broker-cli.js +46 -0
  164. package/dist/core/runtime/broker-protocol.d.ts +332 -0
  165. package/dist/core/runtime/broker-protocol.js +153 -0
  166. package/dist/core/runtime/broker-sdk.d.ts +48 -0
  167. package/dist/core/runtime/broker-sdk.js +72 -0
  168. package/dist/core/runtime/broker.d.ts +55 -0
  169. package/dist/core/runtime/broker.js +1128 -0
  170. package/dist/core/runtime/close.js +35 -6
  171. package/dist/core/runtime/host.d.ts +53 -0
  172. package/dist/core/runtime/host.js +186 -0
  173. package/dist/core/runtime/kickoff.d.ts +2 -1
  174. package/dist/core/runtime/kickoff.js +91 -5
  175. package/dist/core/runtime/launch.d.ts +45 -2
  176. package/dist/core/runtime/launch.js +65 -2
  177. package/dist/core/runtime/lifecycle.js +23 -6
  178. package/dist/core/runtime/memory.d.ts +2 -42
  179. package/dist/core/runtime/memory.js +11 -162
  180. package/dist/core/runtime/nodes.d.ts +33 -0
  181. package/dist/core/runtime/nodes.js +59 -1
  182. package/dist/core/runtime/persona.js +21 -11
  183. package/dist/core/runtime/pi-vendored.d.ts +18 -0
  184. package/dist/core/runtime/pi-vendored.js +49 -0
  185. package/dist/core/runtime/placement.d.ts +32 -14
  186. package/dist/core/runtime/placement.js +206 -52
  187. package/dist/core/runtime/promote.d.ts +0 -6
  188. package/dist/core/runtime/promote.js +1 -12
  189. package/dist/core/runtime/{demote.d.ts → recycle.d.ts} +5 -5
  190. package/dist/core/runtime/{demote.js → recycle.js} +27 -11
  191. package/dist/core/runtime/reset.js +8 -6
  192. package/dist/core/runtime/revive.d.ts +2 -0
  193. package/dist/core/runtime/revive.js +34 -34
  194. package/dist/core/runtime/spawn.d.ts +19 -0
  195. package/dist/core/runtime/spawn.js +75 -22
  196. package/dist/core/runtime/stop-guard.d.ts +1 -1
  197. package/dist/core/runtime/stop-guard.js +6 -1
  198. package/dist/core/runtime/tmux-chrome.d.ts +1 -1
  199. package/dist/core/runtime/tmux-chrome.js +1 -1
  200. package/dist/core/runtime/tmux.d.ts +28 -0
  201. package/dist/core/runtime/tmux.js +80 -6
  202. package/dist/core/scope.d.ts +11 -0
  203. package/dist/core/scope.js +39 -0
  204. package/dist/core/spawn.d.ts +5 -0
  205. package/dist/core/spawn.js +20 -2
  206. package/dist/core/substrate/gate.d.ts +13 -0
  207. package/dist/core/substrate/gate.js +21 -0
  208. package/dist/core/substrate/index.d.ts +7 -0
  209. package/dist/core/substrate/index.js +18 -0
  210. package/dist/core/substrate/on-read.d.ts +14 -0
  211. package/dist/core/substrate/on-read.js +292 -0
  212. package/dist/core/substrate/render.d.ts +25 -0
  213. package/dist/core/substrate/render.js +256 -0
  214. package/dist/core/substrate/schema.d.ts +76 -0
  215. package/dist/core/substrate/schema.js +124 -0
  216. package/dist/core/substrate/session-cache.d.ts +30 -0
  217. package/dist/core/substrate/session-cache.js +77 -0
  218. package/dist/core/substrate/subject.d.ts +41 -0
  219. package/dist/core/substrate/subject.js +54 -0
  220. package/dist/core/tui/contract.d.ts +83 -0
  221. package/dist/core/tui/contract.js +8 -0
  222. package/dist/core/tui/draw.d.ts +96 -0
  223. package/dist/core/tui/draw.js +339 -0
  224. package/dist/core/tui/host.d.ts +29 -0
  225. package/dist/core/tui/host.js +379 -0
  226. package/dist/core/tui/loader.d.ts +16 -0
  227. package/dist/core/tui/loader.js +94 -0
  228. package/dist/core/{canvas/browse → tui}/terminal.js +7 -6
  229. package/dist/core/wake.d.ts +86 -0
  230. package/dist/core/wake.js +308 -0
  231. package/dist/daemon/crtrd.d.ts +29 -4
  232. package/dist/daemon/crtrd.js +662 -46
  233. package/dist/pi-extensions/__tests__/canvas-context-intro.test.d.ts +1 -0
  234. package/dist/pi-extensions/__tests__/canvas-context-intro.test.js +171 -0
  235. package/dist/pi-extensions/__tests__/canvas-stophook-agentend.test.js +19 -12
  236. package/dist/pi-extensions/canvas-commands.d.ts +3 -0
  237. package/dist/pi-extensions/canvas-commands.js +10 -0
  238. package/dist/pi-extensions/canvas-context-intro.d.ts +17 -0
  239. package/dist/pi-extensions/canvas-context-intro.js +55 -15
  240. package/dist/pi-extensions/canvas-doc-substrate.d.ts +44 -0
  241. package/dist/pi-extensions/canvas-doc-substrate.js +112 -0
  242. package/dist/pi-extensions/canvas-nav.d.ts +3 -0
  243. package/dist/pi-extensions/canvas-nav.js +87 -23
  244. package/dist/pi-extensions/canvas-stophook.js +17 -8
  245. package/dist/pi-extensions/canvas-view.d.ts +21 -0
  246. package/dist/pi-extensions/canvas-view.js +75 -0
  247. package/dist/prompts/skill.js +19 -26
  248. package/dist/prompts/view.d.ts +7 -0
  249. package/dist/prompts/view.js +101 -0
  250. package/dist/types.d.ts +4 -0
  251. package/dist/types.js +1 -0
  252. package/package.json +9 -4
  253. package/dist/commands/skill/find.d.ts +0 -4
  254. package/dist/commands/skill/find.js +0 -257
  255. package/dist/commands/skill/read.js +0 -91
  256. /package/dist/commands/{skill → memory}/read.d.ts +0 -0
  257. /package/dist/core/{canvas/browse → tui}/terminal.d.ts +0 -0
@@ -15,10 +15,11 @@
15
15
  //
16
16
  // Crash-safety invariant (was a comment repeated in reset/close/reapDescendants):
17
17
  // "flip status to a non-supervised value + clear intent BEFORE killing the
18
- // window" — the daemon only ever revives active|idle nodes, so a teardown must
18
+ // host" (tmux pane or headless broker) — the daemon only ever revives
19
+ // active|idle nodes, so a teardown must
19
20
  // leave the node done/canceled first to close the revive race. That invariant is
20
21
  // now the DEFINITION of the `cancel` event: callers flip via transition()
21
- // and only THEN kill the window.
22
+ // and only THEN tear the host down.
22
23
  //
23
24
  // Unification (A5, human-confirmed 2026-06-06): an externally-reaped node — torn
24
25
  // down because the user moved on (close cascade) OR because a root reset/relaunch
@@ -33,7 +34,7 @@
33
34
  // row UPDATE directly via openDb — the one sanctioned exception to "only
34
35
  // canvas.ts touches the db" (see canvas/CLAUDE.md), exactly as db.ts's backfill
35
36
  // is the sanctioned exception for a data migration.
36
- import { openDb, getNode } from '../canvas/index.js';
37
+ import { openDb, getNode, cancelWakesFor, cancelSelfAlarms } from '../canvas/index.js';
37
38
  const ANY = '*';
38
39
  /** The supervised statuses — a live node the daemon watches. */
39
40
  const LIVE = ['active', 'idle'];
@@ -47,12 +48,12 @@ const TRANSITIONS = {
47
48
  // of a node that did NOT finish its own work → canceled, intent cleared. (A5:
48
49
  // done is reserved for finalize; every external reap unifies on canceled.)
49
50
  cancel: { status: 'canceled', intent: null, from: ANY },
50
- // daemon superviseTick: window gone with no yield/release intent. Intent KEPT
51
- // (the dead log line still reports it).
51
+ // daemon superviseTick: engine container gone (tmux pane or broker process)
52
+ // with no yield/release intent. Intent KEPT (the dead log line still reports it).
52
53
  crash: { status: 'dead', from: LIVE },
53
54
  // requestYield · relaunchRoot new-node safety net. Status KEPT (already active).
54
55
  yield: { intent: 'refresh', from: LIVE },
55
- // stophook idle-release: free the window, stay woken by the inbox.
56
+ // stophook idle-release: free the host, stay woken by the inbox.
56
57
  release: { status: 'idle', intent: 'idle-release', from: LIVE },
57
58
  // reviveNode · resetRoot · stophook boot-confirm (clear a pending refresh net).
58
59
  revive: { status: 'active', intent: null, from: ANY },
@@ -90,5 +91,21 @@ export function transition(nodeId, event) {
90
91
  else if (writeIntent) {
91
92
  db.prepare('UPDATE nodes SET intent = ? WHERE node_id = ?').run(spec.intent ?? null, nodeId);
92
93
  }
94
+ // Wakeups cleanup — writes a DIFFERENT table (wakeups) AFTER the atomic
95
+ // status/intent write above, so the single-(status,intent)-writer rule holds.
96
+ // Event-gated; exactly ONE delegated wakeups helper per event, never inline
97
+ // wakeups SQL here (design §6.7 / D2 / D6, Q1/Q2):
98
+ // cancel → cancelWakesFor: reap node-anchored AND owner-armed detached
99
+ // wakes this node armed (ruling A) — covers all three cancel
100
+ // sites uniformly (close cascade · reapDescendants · relaunch
101
+ // park-old).
102
+ // finalize → cancelSelfAlarms: clear this node's one-shot self-alarms only;
103
+ // node-anchored declarative crons SURVIVE a finishing instance (Q1).
104
+ // crash → NOTHING. A fault is not a deliberate end-of-waiting; self-alarms
105
+ // and crons MUST survive instance death (D2 / Invariant E / AC-R3).
106
+ if (event === 'cancel')
107
+ cancelWakesFor(nodeId);
108
+ else if (event === 'finalize')
109
+ cancelSelfAlarms(nodeId);
93
110
  return getNode(nodeId);
94
111
  }
@@ -1,43 +1,3 @@
1
- /** The node-local index template. Named export kept for callers/tests that
2
- * assert the seeded node store verbatim. */
3
- export declare const MEMORY_TEMPLATE: string;
4
- /** The user-global index template — framed around the human, not a goal. */
5
- export declare const USER_MEMORY_TEMPLATE: string;
6
- /** The project index template — framed around the repo. */
7
- export declare const PROJECT_MEMORY_TEMPLATE: string;
8
- /** The node-local memory directory in a node's context dir — holds MEMORY.md
9
- * (the index) and the one-fact detail files it points at. */
1
+ /** The node-local memory directory in a node's context dir — holds substrate
2
+ * docs (.md files with kind/when/why frontmatter). */
10
3
  export declare function memoryDir(nodeId: string): string;
11
- /** The node-local MEMORY.md index path (inside the memory dir). */
12
- export declare function memoryPath(nodeId: string): string;
13
- /** Whether the node has a node-local memory store. This is ALSO the
14
- * orchestrator gate: only orchestrators are ever seeded one, so a node with no
15
- * node-local store is a terminal worker (no memory framing at all). */
16
- export declare function hasMemory(nodeId: string): boolean;
17
- /** Read the node-local MEMORY.md index, or null when it doesn't exist. */
18
- export declare function readMemory(nodeId: string): string | null;
19
- /** Seed the node-local memory dir + index IF the node has none yet. */
20
- export declare function seedMemory(nodeId: string): boolean;
21
- /** The user-global memory directory — one per machine, key-less, loaded into
22
- * every orchestrator everywhere. */
23
- export declare function userMemoryDir(): string;
24
- /** The user-global MEMORY.md index path. */
25
- export declare function userMemoryPath(): string;
26
- export declare function hasUserMemory(): boolean;
27
- /** Read the user-global MEMORY.md index, or null when it doesn't exist. */
28
- export declare function readUserMemory(): string | null;
29
- /** Seed the user-global memory dir + index IF absent. */
30
- export declare function seedUserMemory(): boolean;
31
- /** The project key for `cwd`: its git-repo-root when inside a repo, else the
32
- * cwd itself, mangled into a flat directory name (reuses artifact mangleCwd).
33
- * This keys the per-project memory store under <crtrHome>/projects/. */
34
- export declare function projectKey(cwd: string): string;
35
- /** The project memory directory for `cwd`. */
36
- export declare function projectMemoryDir(cwd: string): string;
37
- /** The project MEMORY.md index path for `cwd`. */
38
- export declare function projectMemoryPath(cwd: string): string;
39
- export declare function hasProjectMemory(cwd: string): boolean;
40
- /** Read the project MEMORY.md index for `cwd`, or null when it doesn't exist. */
41
- export declare function readProjectMemory(cwd: string): string | null;
42
- /** Seed the project memory dir + index for `cwd` IF absent. */
43
- export declare function seedProjectMemory(cwd: string): boolean;
@@ -1,165 +1,14 @@
1
- // MEMORY.md + memory/ an orchestrator's persistent file-based memory.
2
- //
3
- // One layout, three scopes. Each store is a `memory/` directory of one-fact
4
- // files (each with typed frontmatter and [[wikilinks]]) indexed by a single
5
- // MEMORY.md that holds one pointer line per memory and NEVER any content — the
6
- // architecture in examples/memory-instructions.md. The pointer lines are the
7
- // load-bearing read: a node's <crtr-context> bearings block extracts every
8
- // applicable store's pointer lines each brand-new chat (see canvas-context-intro
9
- // + bearings), so the indexes must stay lean; the detail files load on demand
10
- // mid-session.
11
- //
12
- // The three scopes differ only in WHERE they live and HOW LONG they outlast a
13
- // node — the `type` taxonomy in each memory's frontmatter drives which store a
14
- // fact lands in (the mapping lives in the orchestration kernel's "Your long-term
15
- // memory"). ALL THREE live under the canvas home (crtrHome), all machine-local:
16
- //
17
- // user-global <crtrHome>/memory/ — who the human is, how
18
- // they like to work; loaded into EVERY orchestrator everywhere.
19
- // project <crtrHome>/projects/<key>/memory/ — facts bound to one
20
- // repo; loaded into orchestrators whose cwd resolves to that project. <key>
21
- // is the git-repo-root (walked up from the cwd), else the cwd, mangled.
22
- // node-local <crtrHome>/nodes/<id>/context/memory/ — facts specific to this
23
- // node's goal; dies with the node.
24
- //
25
- // An ORCHESTRATOR-only artifact — the resident, multi-cycle nodes that survive
26
- // refreshes and accumulate durable lessons/preferences; terminal workers are
27
- // one-shot and get none. All three stores are seeded the moment a node becomes
28
- // an orchestrator (promotion, or born one — where the roadmap is seeded too),
29
- // guarded so a re-seed never clobbers an evolved memory.
30
- import { existsSync, readFileSync, writeFileSync, mkdirSync } from 'node:fs';
31
- import { join, dirname, resolve } from 'node:path';
32
- import { contextDir, crtrHome } from '../canvas/index.js';
33
- import { mangleCwd } from '../artifact.js';
34
- // ---------------------------------------------------------------------------
35
- // Index template + generic store ops (shared by all three scopes).
36
- // ---------------------------------------------------------------------------
37
- /** Build the seed contents of a fresh MEMORY.md index. Deliberately tiny: the
38
- * bearings block only ever extracts the pointer lines, so this prose never
39
- * rides into context — it's only for a human/agent opening the file directly,
40
- * and the how-to lives once in the orchestrator kernel ("Your long-term
41
- * memory"), not here. `holds` is a short scope hint so the empty index still
42
- * orients a fresh write. */
43
- function indexTemplate(holds) {
44
- return ('# memory index — one pointer line per memory (`- [Title](slug.md) — hook`); ' +
45
- `how-to in "Your long-term memory". Holds ${holds}.\n\n(no memories yet)\n`);
46
- }
47
- /** The node-local index template. Named export kept for callers/tests that
48
- * assert the seeded node store verbatim. */
49
- export const MEMORY_TEMPLATE = indexTemplate('your saved memories');
50
- /** The user-global index template — framed around the human, not a goal. */
51
- export const USER_MEMORY_TEMPLATE = indexTemplate('your saved memories about the human — who they are and how they like to work');
52
- /** The project index template — framed around the repo. */
53
- export const PROJECT_MEMORY_TEMPLATE = indexTemplate('your saved memories about this project');
54
- /** The MEMORY.md index path inside a memory `dir`. */
55
- function indexPathOf(dir) {
56
- return join(dir, 'MEMORY.md');
57
- }
58
- /** Seed `dir` + its MEMORY.md index with `template` IFF the index is absent.
59
- * Idempotent and guarded so it never clobbers an evolved memory; creating the
60
- * dir up front lets the node write detail files into it directly (no mkdir).
61
- * Returns true when it seeded, false when an index already existed. */
62
- function seedStore(dir, template) {
63
- const idx = indexPathOf(dir);
64
- if (existsSync(idx))
65
- return false;
66
- mkdirSync(dir, { recursive: true });
67
- writeFileSync(idx, template);
68
- return true;
69
- }
70
- /** Read a store's MEMORY.md index, or null when it doesn't exist. */
71
- function readStore(dir) {
72
- const idx = indexPathOf(dir);
73
- return existsSync(idx) ? readFileSync(idx, 'utf8') : null;
74
- }
75
- // ---------------------------------------------------------------------------
76
- // node-local store — <crtrHome>/nodes/<id>/context/memory/ (facts for this goal)
77
- // ---------------------------------------------------------------------------
78
- /** The node-local memory directory in a node's context dir — holds MEMORY.md
79
- * (the index) and the one-fact detail files it points at. */
1
+ // memory.tsthe node-local substrate document store.
2
+ //
3
+ // One scope lives here: node-local memory, a `memory/` directory of substrate
4
+ // documents (.md files with typed frontmatter kind, when, why,
5
+ // system-prompt-visibility, gate) inside a node's context dir; it dies with the
6
+ // node. User-global and project memory are scope-resolved stores — see
7
+ // `scopeMemoryDir` in core/scope.ts.
8
+ import { join } from 'node:path';
9
+ import { contextDir } from '../canvas/index.js';
10
+ /** The node-local memory directory in a node's context dir — holds substrate
11
+ * docs (.md files with kind/when/why frontmatter). */
80
12
  export function memoryDir(nodeId) {
81
13
  return join(contextDir(nodeId), 'memory');
82
14
  }
83
- /** The node-local MEMORY.md index path (inside the memory dir). */
84
- export function memoryPath(nodeId) {
85
- return indexPathOf(memoryDir(nodeId));
86
- }
87
- /** Whether the node has a node-local memory store. This is ALSO the
88
- * orchestrator gate: only orchestrators are ever seeded one, so a node with no
89
- * node-local store is a terminal worker (no memory framing at all). */
90
- export function hasMemory(nodeId) {
91
- return existsSync(memoryPath(nodeId));
92
- }
93
- /** Read the node-local MEMORY.md index, or null when it doesn't exist. */
94
- export function readMemory(nodeId) {
95
- return readStore(memoryDir(nodeId));
96
- }
97
- /** Seed the node-local memory dir + index IF the node has none yet. */
98
- export function seedMemory(nodeId) {
99
- return seedStore(memoryDir(nodeId), MEMORY_TEMPLATE);
100
- }
101
- // ---------------------------------------------------------------------------
102
- // user-global store — <crtrHome>/memory/ (who the human is, how they work)
103
- // ---------------------------------------------------------------------------
104
- /** The user-global memory directory — one per machine, key-less, loaded into
105
- * every orchestrator everywhere. */
106
- export function userMemoryDir() {
107
- return join(crtrHome(), 'memory');
108
- }
109
- /** The user-global MEMORY.md index path. */
110
- export function userMemoryPath() {
111
- return indexPathOf(userMemoryDir());
112
- }
113
- export function hasUserMemory() {
114
- return existsSync(userMemoryPath());
115
- }
116
- /** Read the user-global MEMORY.md index, or null when it doesn't exist. */
117
- export function readUserMemory() {
118
- return readStore(userMemoryDir());
119
- }
120
- /** Seed the user-global memory dir + index IF absent. */
121
- export function seedUserMemory() {
122
- return seedStore(userMemoryDir(), USER_MEMORY_TEMPLATE);
123
- }
124
- // ---------------------------------------------------------------------------
125
- // project store — <crtrHome>/projects/<key>/memory/ (facts bound to one repo)
126
- // ---------------------------------------------------------------------------
127
- /** The git repo root containing `cwd` — walk up for a `.git` entry — or null
128
- * when `cwd` is not inside a repo. `.git` may be a dir (normal) or a file
129
- * (worktree/submodule); existsSync catches both. */
130
- function gitRoot(cwd) {
131
- let dir = resolve(cwd);
132
- for (;;) {
133
- if (existsSync(join(dir, '.git')))
134
- return dir;
135
- const parent = dirname(dir);
136
- if (parent === dir)
137
- return null; // hit the filesystem root
138
- dir = parent;
139
- }
140
- }
141
- /** The project key for `cwd`: its git-repo-root when inside a repo, else the
142
- * cwd itself, mangled into a flat directory name (reuses artifact mangleCwd).
143
- * This keys the per-project memory store under <crtrHome>/projects/. */
144
- export function projectKey(cwd) {
145
- return mangleCwd(gitRoot(cwd) ?? cwd);
146
- }
147
- /** The project memory directory for `cwd`. */
148
- export function projectMemoryDir(cwd) {
149
- return join(crtrHome(), 'projects', projectKey(cwd), 'memory');
150
- }
151
- /** The project MEMORY.md index path for `cwd`. */
152
- export function projectMemoryPath(cwd) {
153
- return indexPathOf(projectMemoryDir(cwd));
154
- }
155
- export function hasProjectMemory(cwd) {
156
- return existsSync(projectMemoryPath(cwd));
157
- }
158
- /** Read the project MEMORY.md index for `cwd`, or null when it doesn't exist. */
159
- export function readProjectMemory(cwd) {
160
- return readStore(projectMemoryDir(cwd));
161
- }
162
- /** Seed the project memory dir + index for `cwd` IF absent. */
163
- export function seedProjectMemory(cwd) {
164
- return seedStore(projectMemoryDir(cwd), PROJECT_MEMORY_TEMPLATE);
165
- }
@@ -1,6 +1,12 @@
1
1
  import { type NodeMeta, type Mode, type Lifecycle, type LaunchSpec } from '../canvas/index.js';
2
2
  /** Generate a node id in the same shape as job ids (time-sortable + random). */
3
3
  export declare function newNodeId(): string;
4
+ /** The root of a node's spine: walk the `parent` column up to `parent == null`.
5
+ * Cycle-guarded (parents must not cycle, but never loop forever). Lives in the
6
+ * node layer (not placement) so the env-builder and placement both reach it
7
+ * without an import cycle — `CRTR_SUBTREE` is emitted as `rootOfSpine(nodeId)`
8
+ * at every launch site. */
9
+ export declare function rootOfSpine(nodeId: string): string;
4
10
  /** The single, shared tmux session that ALL canvas node windows live in.
5
11
  * Overridable with CRTR_NODE_SESSION (default `crtr`). Every root and every
6
12
  * child opens a window here rather than cluttering the user's own working
@@ -34,6 +40,25 @@ export declare function resolveBirthSession(opts: {
34
40
  * (`nodeSession()`). The defaulted read for the placement layer; a present
35
41
  * `home_session` is always returned verbatim. */
36
42
  export declare function homeSessionOf(nodeId: string): string;
43
+ /** The session a node's CHILDREN spawn into — the value its pi process exports
44
+ * as CRTR_ROOT_SESSION (the subtree's backstage). NOT the same as where the
45
+ * node's OWN pane lives.
46
+ *
47
+ * For a managed CHILD this is its `home_session`: a child's home_session is
48
+ * ALWAYS the backstage it was born into (focus may move its live pane into a
49
+ * user session, but home_session stays the taint-immune backstage), so its
50
+ * descendants correctly inherit that backstage.
51
+ *
52
+ * For a ROOT (`parent === null`) `home_session` may be a USER session it
53
+ * ADOPTED — the inline front door keeps the root's own pane where the user is
54
+ * working (`home_session = cli`). Sourcing children's CRTR_ROOT_SESSION from
55
+ * that would exile the root's entire subtree into the user's session. A root's
56
+ * children must instead flow to the shared backstage `nodeSession()`, NEVER the
57
+ * user's session — mirroring spawnRoot's birth-time split (root pane → user
58
+ * session; children → nodeSession() via CRTR_ROOT_SESSION). The revive paths
59
+ * MUST honor the same split, or a refreshed front-door root re-points its whole
60
+ * subtree into the user's session on every yield (the bug this fixes). */
61
+ export declare function childBackstageOf(nodeId: string): string;
37
62
  export interface NodeContext {
38
63
  nodeId: string | null;
39
64
  kind: string | null;
@@ -61,6 +86,14 @@ export interface SpawnNodeOpts {
61
86
  * `parent` — e.g. an independent root (parent=null) still records its
62
87
  * spawner. Defaults to `parent`. */
63
88
  spawnedBy?: string | null;
89
+ /** Fork provenance — the `--fork-from` reference this node was forked from (a
90
+ * node id / session path / pi session uuid). Persisted to `meta.fork_from` so
91
+ * the boot intro can re-assert this node's OWN identity over the source's
92
+ * copied-in conversation. Omit for a fresh node. */
93
+ forkFrom?: string | null;
94
+ /** Which HOST launches + supervises this node: a tmux pane (default) or the
95
+ * headless broker. Persisted to `meta.host_kind` (NULL/'tmux' ⇒ tmux). */
96
+ hostKind?: 'tmux' | 'broker';
64
97
  /** New subscriptions this node opens default to passive when true. */
65
98
  passiveDefault?: boolean;
66
99
  /** Resolved pi launch recipe (from resolve(kind,mode)). */
@@ -13,11 +13,31 @@
13
13
  // graph to mirror the spawn structure. A `spawned_by` audit edge
14
14
  // is also recorded.
15
15
  import { randomBytes } from 'node:crypto';
16
- import { createNode, getNode, subscribe, recordSpawn, } from '../canvas/index.js';
16
+ import { mkdirSync } from 'node:fs';
17
+ import { createNode, getNode, getRow, subscribe, recordSpawn, } from '../canvas/index.js';
18
+ import { memoryDir } from './memory.js';
17
19
  /** Generate a node id in the same shape as job ids (time-sortable + random). */
18
20
  export function newNodeId() {
19
21
  return `${Date.now().toString(36)}-${randomBytes(4).toString('hex')}`;
20
22
  }
23
+ /** The root of a node's spine: walk the `parent` column up to `parent == null`.
24
+ * Cycle-guarded (parents must not cycle, but never loop forever). Lives in the
25
+ * node layer (not placement) so the env-builder and placement both reach it
26
+ * without an import cycle — `CRTR_SUBTREE` is emitted as `rootOfSpine(nodeId)`
27
+ * at every launch site. */
28
+ export function rootOfSpine(nodeId) {
29
+ let cur = nodeId;
30
+ const seen = new Set();
31
+ for (;;) {
32
+ if (seen.has(cur))
33
+ return cur;
34
+ seen.add(cur);
35
+ const row = getRow(cur);
36
+ if (row === null || row.parent == null)
37
+ return cur;
38
+ cur = row.parent;
39
+ }
40
+ }
21
41
  /** The single, shared tmux session that ALL canvas node windows live in.
22
42
  * Overridable with CRTR_NODE_SESSION (default `crtr`). Every root and every
23
43
  * child opens a window here rather than cluttering the user's own working
@@ -59,6 +79,30 @@ export function homeSessionOf(nodeId) {
59
79
  return nodeSession();
60
80
  return meta.home_session ?? meta.tmux_session ?? nodeSession();
61
81
  }
82
+ /** The session a node's CHILDREN spawn into — the value its pi process exports
83
+ * as CRTR_ROOT_SESSION (the subtree's backstage). NOT the same as where the
84
+ * node's OWN pane lives.
85
+ *
86
+ * For a managed CHILD this is its `home_session`: a child's home_session is
87
+ * ALWAYS the backstage it was born into (focus may move its live pane into a
88
+ * user session, but home_session stays the taint-immune backstage), so its
89
+ * descendants correctly inherit that backstage.
90
+ *
91
+ * For a ROOT (`parent === null`) `home_session` may be a USER session it
92
+ * ADOPTED — the inline front door keeps the root's own pane where the user is
93
+ * working (`home_session = cli`). Sourcing children's CRTR_ROOT_SESSION from
94
+ * that would exile the root's entire subtree into the user's session. A root's
95
+ * children must instead flow to the shared backstage `nodeSession()`, NEVER the
96
+ * user's session — mirroring spawnRoot's birth-time split (root pane → user
97
+ * session; children → nodeSession() via CRTR_ROOT_SESSION). The revive paths
98
+ * MUST honor the same split, or a refreshed front-door root re-points its whole
99
+ * subtree into the user's session on every yield (the bug this fixes). */
100
+ export function childBackstageOf(nodeId) {
101
+ const meta = getNode(nodeId);
102
+ if (meta !== null && meta.parent === null)
103
+ return nodeSession();
104
+ return homeSessionOf(nodeId);
105
+ }
62
106
  /** Read the current node's identity from the environment. A spawned pi process
63
107
  * runs with CRTR_NODE_ID set; its own `crtr` invocations spawn children under
64
108
  * it by reading CRTR_NODE_ID as the parent. */
@@ -92,6 +136,12 @@ export function nodeEnv(meta) {
92
136
  const rootSession = process.env['CRTR_ROOT_SESSION'];
93
137
  if (rootSession !== undefined && rootSession !== '')
94
138
  env['CRTR_ROOT_SESSION'] = rootSession;
139
+ // Propagate the subtree-root node id (additive in Phase 3; tmux still keys on
140
+ // CRTR_ROOT_SESSION). The broker uses it to group a subtree; each launch site
141
+ // also emits the authoritative rootOfSpine(nodeId) over this passthrough.
142
+ const subtree = process.env['CRTR_SUBTREE'];
143
+ if (subtree !== undefined && subtree !== '')
144
+ env['CRTR_SUBTREE'] = subtree;
95
145
  // Merge any launch-spec env last (it may override / extend).
96
146
  return { ...env, ...(meta.launch?.env ?? {}) };
97
147
  }
@@ -117,6 +167,7 @@ export function spawnNode(opts) {
117
167
  cycles: 0,
118
168
  created: new Date().toISOString(),
119
169
  cwd: opts.cwd,
170
+ host_kind: opts.hostKind ?? 'tmux',
120
171
  kind: opts.kind,
121
172
  mode,
122
173
  lifecycle,
@@ -127,6 +178,7 @@ export function spawnNode(opts) {
127
178
  status: 'active',
128
179
  parent,
129
180
  spawned_by: spawnedBy,
181
+ fork_from: opts.forkFrom,
130
182
  passive_default: opts.passiveDefault ?? false,
131
183
  intent: null,
132
184
  pi_session_id: null,
@@ -140,6 +192,12 @@ export function spawnNode(opts) {
140
192
  throw new Error(`cannot spawn under unknown parent node: ${parent}`);
141
193
  }
142
194
  createNode(meta);
195
+ // Create the node-local memory directory so substrate docs can be written
196
+ // directly into it without a separate mkdir. Skip for ephemeral human-bridge
197
+ // rows (kind 'human') — they are not agent nodes.
198
+ if (meta.kind !== 'human') {
199
+ mkdirSync(memoryDir(meta.node_id), { recursive: true });
200
+ }
143
201
  if (parent !== null) {
144
202
  // The load-bearing seed: parent subscribes (active) to child so it learns
145
203
  // when the work finishes. This mirrors spawn structure into the spine.
@@ -26,11 +26,22 @@ import { readText } from '../fs-utils.js';
26
26
  import { parseFrontmatter } from '../frontmatter.js';
27
27
  import { readRoadmap, roadmapPath } from './roadmap.js';
28
28
  import { orchestratorContextNote } from './bearings.js';
29
- import { memoryPath, memoryDir, userMemoryPath, userMemoryDir, projectMemoryPath, projectMemoryDir, } from './memory.js';
30
29
  // ---------------------------------------------------------------------------
31
30
  // base→orchestrator guidance (the roadmap-shaping dump) — MOVED here from
32
31
  // promote.ts so the injector is the one place that builds it.
33
32
  // ---------------------------------------------------------------------------
33
+ /** Coerce a frontmatter scalar to its string form, matching the legacy
34
+ * hand-rolled parser. Strings pass through; number/boolean coerce via
35
+ * String(); null/undefined and non-scalars are dropped (→ null). Without this,
36
+ * the `yaml` package's native scalar types would let a numeric/boolean
37
+ * `roadmapSkill` be silently dropped by a `typeof === 'string'` guard. */
38
+ function scalarToString(v) {
39
+ if (typeof v === 'string')
40
+ return v;
41
+ if (typeof v === 'number' || typeof v === 'boolean')
42
+ return String(v);
43
+ return null;
44
+ }
34
45
  /** Load a skill's body text by name, or null if it can't be resolved. Used to
35
46
  * inline a kind's roadmap-shaping skill into the orchestration guidance. */
36
47
  function loadSkillBody(name) {
@@ -44,16 +55,15 @@ function loadSkillBody(name) {
44
55
  }
45
56
  /** The base→orchestrator guidance dump, specialized to the node's kind: the
46
57
  * shared kernel + that kind's roadmap-shaping skill + the roadmap scaffold the
47
- * node must author + the orchestrator context-dir framing + the three memory
48
- * stores. The node is now a delegator whose scarce resource is its own context
49
- * window. (Lifecycle is left to its own section promotion no longer forces
50
- * resident, so this never asserts residency.) */
51
- function orchestrationGuidance(nodeId, kind, cwd) {
58
+ * node must author + the orchestrator context-dir framing + a short pointer at
59
+ * the substrate memory flow (promotion re-informs of nothing about the stores,
60
+ * it only adds the across-cycles relevance). The node is now a delegator whose
61
+ * scarce resource is its own context window. (Lifecycle is left to its own
62
+ * section — promotion no longer forces resident, so this never asserts residency.) */
63
+ function orchestrationGuidance(nodeId, kind) {
52
64
  const kernel = loadKernel();
53
65
  const orch = loadPersona(kind, 'orchestrator');
54
- const roadmapSkill = typeof orch?.frontmatter?.['roadmapSkill'] === 'string'
55
- ? orch.frontmatter['roadmapSkill']
56
- : undefined;
66
+ const roadmapSkill = scalarToString(orch?.frontmatter?.['roadmapSkill']) ?? undefined;
57
67
  const skillBody = roadmapSkill ? loadSkillBody(roadmapSkill) : null;
58
68
  const roadmap = readRoadmap(nodeId) ?? '(no roadmap yet)';
59
69
  const rmPath = roadmapPath(nodeId);
@@ -70,7 +80,7 @@ function orchestrationGuidance(nodeId, kind, cwd) {
70
80
  // The orchestrator framing for the context dir — the missing guidance a
71
81
  // promoted node never got at spawn (it spawned as a base worker). Same note
72
82
  // a born-orchestrator gets in its <crtr-context> bearings block.
73
- orchestratorContextNote(nodeId), '', 'Your long-term memory now exists across three seeded stores (write to them directly), each a different scope per "Your long-term memory" above:', ` • user-global \`${userMemoryDir()}\` (index \`${userMemoryPath()}\`) who the human is, how they like to work; loaded into every orchestrator everywhere.`, ` • project \`${projectMemoryDir(cwd)}\` (index \`${projectMemoryPath(cwd)}\`) facts bound to this repo; loaded into every orchestrator working here.`, ` node-local \`${memoryDir(nodeId)}\` (index \`${memoryPath(nodeId)}\`) facts specific to this goal; they die with this node.`, 'A memory\'s `type` decides which store it lands in (see "Your long-term memory"). These same paths ride into every future wake in your `<crtr-context>` block.', '', 'Then delegate each phase with `crtr node new --kind <kind>`. When your context fills, run `crtr node yield` to refresh against this roadmap.');
83
+ orchestratorContextNote(nodeId), '', 'Your three long-term memory scopes (user-global, project, node-local) already surface in your `<crtr-context>` block. What promotion changes is USE, not access: you now persist across refresh cycles, so node-local carries what THIS goal needs between your refreshes, and durable facts you commit to user-global or project outlive this node. Use `crtr memory write` to save a document; `crtr memory list` to browse; `crtr memory find` to search (see "Your long-term memory" above for the full substrate flow).', '', 'Then delegate each phase with `crtr node new --kind <kind>`. When your context fills, run `crtr node yield` to refresh against this roadmap.');
74
84
  return parts.join('\n');
75
85
  }
76
86
  // ---------------------------------------------------------------------------
@@ -103,7 +113,7 @@ export function transitionGuidance(nodeId, from, to) {
103
113
  if (from.mode !== to.mode) {
104
114
  if (to.mode === 'orchestrator') {
105
115
  const node = getNode(nodeId);
106
- sections.push(orchestrationGuidance(nodeId, node?.kind ?? 'general', node?.cwd ?? process.cwd()));
116
+ sections.push(orchestrationGuidance(nodeId, node?.kind ?? 'general'));
107
117
  }
108
118
  else {
109
119
  sections.push(baseModeGuidance());
@@ -0,0 +1,18 @@
1
+ /**
2
+ * pi's builtin slash commands — vendored verbatim from pi `core/slash-commands.js`
3
+ * `BUILTIN_SLASH_COMMANDS` (review C1). **21 entries at 0.78.1** (review n1 — NOT 23).
4
+ *
5
+ * Builtins are NOT engine-interpreted (`session.prompt('/model')` ships `/model`
6
+ * to the LLM as literal text), so the broker's `get_commands` op MERGES this list
7
+ * with the engine's registered commands/templates/skills, and the viewer
8
+ * autocomplete parses these locally. Both the broker (T3) and the viewer (T6)
9
+ * import this single copy.
10
+ *
11
+ * Upstream interpolates `${APP_NAME}` into the `quit` description; `APP_NAME`
12
+ * defaults to "pi" (overridable via pi config), so the vendored copy hardcodes
13
+ * "Quit pi".
14
+ */
15
+ export declare const BUILTIN_SLASH_COMMANDS: ReadonlyArray<{
16
+ name: string;
17
+ description: string;
18
+ }>;
@@ -0,0 +1,49 @@
1
+ // pi-vendored.ts — symbols copied verbatim from pi that pi does NOT re-export.
2
+ //
3
+ // VENDORED, not imported, because pi's `exports` map is `.`-only (+`./hooks`):
4
+ // a deep import like `@earendil-works/pi-coding-agent/dist/core/slash-commands`
5
+ // fails with ERR_PACKAGE_PATH_NOT_EXPORTED, and the symbol is absent from the
6
+ // package-root `index.d.ts` at BOTH 0.78.1 and 0.79.0 — so there is no pin at
7
+ // which it becomes importable (review C1). The only safe access is to copy it.
8
+ //
9
+ // MAINTENANCE: re-sync on EVERY pi SDK bump. The source of truth is
10
+ // `node_modules/@earendil-works/pi-coding-agent/dist/core/slash-commands.js`.
11
+ // If the upstream list changes (entries added/removed/reworded), update the copy
12
+ // below to match and adjust the count note.
13
+ /**
14
+ * pi's builtin slash commands — vendored verbatim from pi `core/slash-commands.js`
15
+ * `BUILTIN_SLASH_COMMANDS` (review C1). **21 entries at 0.78.1** (review n1 — NOT 23).
16
+ *
17
+ * Builtins are NOT engine-interpreted (`session.prompt('/model')` ships `/model`
18
+ * to the LLM as literal text), so the broker's `get_commands` op MERGES this list
19
+ * with the engine's registered commands/templates/skills, and the viewer
20
+ * autocomplete parses these locally. Both the broker (T3) and the viewer (T6)
21
+ * import this single copy.
22
+ *
23
+ * Upstream interpolates `${APP_NAME}` into the `quit` description; `APP_NAME`
24
+ * defaults to "pi" (overridable via pi config), so the vendored copy hardcodes
25
+ * "Quit pi".
26
+ */
27
+ export const BUILTIN_SLASH_COMMANDS = [
28
+ { name: 'settings', description: 'Open settings menu' },
29
+ { name: 'model', description: 'Select model (opens selector UI)' },
30
+ { name: 'scoped-models', description: 'Enable/disable models for Ctrl+P cycling' },
31
+ { name: 'export', description: 'Export session (HTML default, or specify path: .html/.jsonl)' },
32
+ { name: 'import', description: 'Import and resume a session from a JSONL file' },
33
+ { name: 'share', description: 'Share session as a secret GitHub gist' },
34
+ { name: 'copy', description: 'Copy last agent message to clipboard' },
35
+ { name: 'name', description: 'Set session display name' },
36
+ { name: 'session', description: 'Show session info and stats' },
37
+ { name: 'changelog', description: 'Show changelog entries' },
38
+ { name: 'hotkeys', description: 'Show all keyboard shortcuts' },
39
+ { name: 'fork', description: 'Create a new fork from a previous user message' },
40
+ { name: 'clone', description: 'Duplicate the current session at the current position' },
41
+ { name: 'tree', description: 'Navigate session tree (switch branches)' },
42
+ { name: 'login', description: 'Configure provider authentication' },
43
+ { name: 'logout', description: 'Remove provider authentication' },
44
+ { name: 'new', description: 'Start a new session' },
45
+ { name: 'compact', description: 'Manually compact the session context' },
46
+ { name: 'resume', description: 'Resume a different session' },
47
+ { name: 'reload', description: 'Reload keybindings, extensions, skills, prompts, and themes' },
48
+ { name: 'quit', description: 'Quit pi' },
49
+ ];