create-egregore 0.20.2 → 0.21.0-runtime-mvp.2

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 (706) hide show
  1. package/assets/egregore-launcher.js +1013 -16
  2. package/bin/cli.js +11 -0
  3. package/lib/claude-runtime.js +192 -0
  4. package/lib/local.js +38 -31
  5. package/lib/pi-runtime.js +1 -0
  6. package/lib/prime-runtime.js +1 -0
  7. package/lib/runtime-identity.js +146 -0
  8. package/lib/setup.js +116 -17
  9. package/package.json +10 -6
  10. package/runtime/claude/.claude/context/commit-format.md +110 -0
  11. package/runtime/claude/.claude/context/git-language.md +46 -0
  12. package/runtime/claude/.claude/context/notification-consent.md +66 -0
  13. package/runtime/claude/.claude/context/pr-format.md +67 -0
  14. package/runtime/claude/.claude/context/telemetry.md +133 -0
  15. package/runtime/claude/.claude/hooks/boundary-check.sh +333 -0
  16. package/runtime/claude/.claude/hooks/branch-guard.sh +231 -0
  17. package/runtime/claude/.claude/hooks/card-context.sh +38 -0
  18. package/runtime/claude/.claude/hooks/emissary-detect.sh +53 -0
  19. package/runtime/claude/.claude/hooks/memory-refresh.sh +67 -0
  20. package/runtime/claude/.claude/hooks/onboarding-guard.sh +86 -0
  21. package/runtime/claude/.claude/hooks/retrieval-context.sh +43 -0
  22. package/runtime/claude/.claude/rules/voice-bedrock.md +73 -0
  23. package/runtime/claude/.claude/settings.json +131 -0
  24. package/runtime/claude/.claude/skills/activity/SKILL.md +68 -0
  25. package/runtime/claude/.claude/skills/announce/SKILL.md +54 -0
  26. package/runtime/claude/.claude/skills/archive/SKILL.md +71 -0
  27. package/runtime/claude/.claude/skills/branch/SKILL.md +147 -0
  28. package/runtime/claude/.claude/skills/checkup/SKILL.md +159 -0
  29. package/runtime/claude/.claude/skills/commit/SKILL.md +103 -0
  30. package/runtime/claude/.claude/skills/contribute/SKILL.md +343 -0
  31. package/runtime/claude/.claude/skills/create-skill/SKILL.md +135 -0
  32. package/runtime/claude/.claude/skills/dark-mode/SKILL.md +410 -0
  33. package/runtime/claude/.claude/skills/dashboard/SKILL.md +43 -0
  34. package/runtime/claude/.claude/skills/deep-reflect/REPORT.md +140 -0
  35. package/runtime/claude/.claude/skills/deep-reflect/RESEARCH.md +132 -0
  36. package/runtime/claude/.claude/skills/deep-reflect/ROLES.md +104 -0
  37. package/runtime/claude/.claude/skills/deep-reflect/SKILL.md +170 -0
  38. package/runtime/claude/.claude/skills/delete-user/SKILL.md +150 -0
  39. package/runtime/claude/.claude/skills/emissary/SKILL.md +220 -0
  40. package/runtime/claude/.claude/skills/handoff/SKILL.md +124 -0
  41. package/runtime/claude/.claude/skills/harvest/AUDIT.md +546 -0
  42. package/runtime/claude/.claude/skills/harvest/FORMAT.md +436 -0
  43. package/runtime/claude/.claude/skills/harvest/PROCESS.md +395 -0
  44. package/runtime/claude/.claude/skills/harvest/QUESTION_PALETTE.md +76 -0
  45. package/runtime/claude/.claude/skills/harvest/SKILL.md +100 -0
  46. package/runtime/claude/.claude/skills/ingest/SKILL.md +112 -0
  47. package/runtime/claude/.claude/skills/ingest-corpus/SKILL.md +187 -0
  48. package/runtime/claude/.claude/skills/ingest-notion/SKILL.md +214 -0
  49. package/runtime/claude/.claude/skills/ingest-user-interview/SKILL.md +65 -0
  50. package/runtime/claude/.claude/skills/ingest-user-interview/references/analysis-contract.md +125 -0
  51. package/runtime/claude/.claude/skills/invite/SKILL.md +111 -0
  52. package/runtime/claude/.claude/skills/issue/SKILL.md +104 -0
  53. package/runtime/claude/.claude/skills/me/SKILL.md +81 -0
  54. package/runtime/claude/.claude/skills/meeting/SKILL.md +76 -0
  55. package/runtime/claude/.claude/skills/meeting/references/analysis-contract.md +102 -0
  56. package/runtime/claude/.claude/skills/note/SKILL.md +71 -0
  57. package/runtime/claude/.claude/skills/notion-connect/SKILL.md +124 -0
  58. package/runtime/claude/.claude/skills/onboarding/SKILL.md +159 -0
  59. package/runtime/claude/.claude/skills/pr/SKILL.md +85 -0
  60. package/runtime/claude/.claude/skills/pull/SKILL.md +79 -0
  61. package/runtime/claude/.claude/skills/push/SKILL.md +47 -0
  62. package/runtime/claude/.claude/skills/quest/SKILL.md +121 -0
  63. package/runtime/claude/.claude/skills/reflect/SKILL.md +69 -0
  64. package/runtime/claude/.claude/skills/review-pr/SKILL.md +276 -0
  65. package/runtime/claude/.claude/skills/save/SKILL.md +92 -0
  66. package/runtime/claude/.claude/skills/slack-connect/SKILL.md +148 -0
  67. package/runtime/claude/.claude/skills/summon/SKILL.md +240 -0
  68. package/runtime/claude/.claude/skills/sync-repos/SKILL.md +82 -0
  69. package/runtime/claude/.claude/skills/teams-connect/SKILL.md +216 -0
  70. package/runtime/claude/.claude/skills/telegram-connect/SKILL.md +110 -0
  71. package/runtime/claude/.claude/skills/telemetry/SKILL.md +117 -0
  72. package/runtime/claude/.claude/skills/test/SKILL.md +87 -0
  73. package/runtime/claude/.claude/skills/the-spiral/SKILL.md +492 -0
  74. package/runtime/claude/.claude/skills/tui-design/SKILL.md +188 -0
  75. package/runtime/claude/.claude/skills/tutorial/SKILL.md +100 -0
  76. package/runtime/claude/.claude/skills/update/SKILL.md +265 -0
  77. package/runtime/claude/.claude/skills/view/SKILL.md +393 -0
  78. package/runtime/claude/.claude/skills/view/compose-scaffold.html +221 -0
  79. package/runtime/claude/.claude/skills/visual-explain/SKILL.md +132 -0
  80. package/runtime/claude/.claude/skills/wrap/SKILL.md +98 -0
  81. package/runtime/claude/CLAUDE.md +447 -0
  82. package/runtime/claude/bin/activity-action.sh +92 -0
  83. package/runtime/claude/bin/activity-data.sh +17 -0
  84. package/runtime/claude/bin/agent.sh +783 -0
  85. package/runtime/claude/bin/artifact-register.sh +164 -0
  86. package/runtime/claude/bin/artifact-writeback.sh +53 -0
  87. package/runtime/claude/bin/artifacts.sh +288 -0
  88. package/runtime/claude/bin/attendant.sh +707 -0
  89. package/runtime/claude/bin/autosave.sh +112 -0
  90. package/runtime/claude/bin/boundary.sh +253 -0
  91. package/runtime/claude/bin/build-sealed-dataroom.mjs +722 -0
  92. package/runtime/claude/bin/capture-reconcile.sh +69 -0
  93. package/runtime/claude/bin/capture-run.sh +357 -0
  94. package/runtime/claude/bin/codex-install-skills.sh +78 -0
  95. package/runtime/claude/bin/codex-render-spec.mjs +462 -0
  96. package/runtime/claude/bin/codex-session-start.sh +275 -0
  97. package/runtime/claude/bin/codex-skill-render.mjs +349 -0
  98. package/runtime/claude/bin/codex-sync-skills.sh +237 -0
  99. package/runtime/claude/bin/connect-refresh.sh +33 -0
  100. package/runtime/claude/bin/contribute-guard.sh +79 -0
  101. package/runtime/claude/bin/corpus_map.py +806 -0
  102. package/runtime/claude/bin/corpus_survey.py +304 -0
  103. package/runtime/claude/bin/dashboard-data.sh +18 -0
  104. package/runtime/claude/bin/github-auth.sh +121 -0
  105. package/runtime/claude/bin/graph-batch.sh +86 -0
  106. package/runtime/claude/bin/graph-op.sh +1106 -0
  107. package/runtime/claude/bin/graph-projection.sh +54 -0
  108. package/runtime/claude/bin/graph-wal.sh +242 -0
  109. package/runtime/claude/bin/graph.sh +267 -0
  110. package/runtime/claude/bin/handoff-lifecycle-job.sh +146 -0
  111. package/runtime/claude/bin/handoff-lifecycle.sh +280 -0
  112. package/runtime/claude/bin/handoff-pr-backfill.sh +86 -0
  113. package/runtime/claude/bin/handoff-preview.sh +169 -0
  114. package/runtime/claude/bin/handoff-run.sh +692 -0
  115. package/runtime/claude/bin/handoff-save-egregore.sh +224 -0
  116. package/runtime/claude/bin/index-handoff.sh +322 -0
  117. package/runtime/claude/bin/ingest-graph.sh +86 -0
  118. package/runtime/claude/bin/ingest-item.sh +10 -0
  119. package/runtime/claude/bin/ingest-surface.html +495 -0
  120. package/runtime/claude/bin/ingest.py +1011 -0
  121. package/runtime/claude/bin/ingest.sh +308 -0
  122. package/runtime/claude/bin/ingest_graph.py +856 -0
  123. package/runtime/claude/bin/ingest_surface.py +544 -0
  124. package/runtime/claude/bin/invite.sh +6 -0
  125. package/runtime/claude/bin/issue.sh +10 -0
  126. package/runtime/claude/bin/knowledge.sh +10 -0
  127. package/runtime/claude/bin/lib/artifact-id.sh +46 -0
  128. package/runtime/claude/bin/lib/config.sh +136 -0
  129. package/runtime/claude/bin/lib/context.sh +642 -0
  130. package/runtime/claude/bin/lib/dashboard-artifact.sh +242 -0
  131. package/runtime/claude/bin/lib/ensure-develop.sh +183 -0
  132. package/runtime/claude/bin/lib/git-message.sh +37 -0
  133. package/runtime/claude/bin/lib/git-sync.sh +494 -0
  134. package/runtime/claude/bin/lib/greeting.sh +548 -0
  135. package/runtime/claude/bin/lib/handoff-meta.sh +97 -0
  136. package/runtime/claude/bin/lib/hash.sh +13 -0
  137. package/runtime/claude/bin/lib/identity.sh +213 -0
  138. package/runtime/claude/bin/lib/metrics.sh +374 -0
  139. package/runtime/claude/bin/lib/noncode.sh +31 -0
  140. package/runtime/claude/bin/lib/permissions.sh +106 -0
  141. package/runtime/claude/bin/lib/time.sh +56 -0
  142. package/runtime/claude/bin/lib/worktree-links.sh +85 -0
  143. package/runtime/claude/bin/node-run.sh +42 -0
  144. package/runtime/claude/bin/notification.sh +6 -0
  145. package/runtime/claude/bin/notify.sh +636 -0
  146. package/runtime/claude/bin/observe-context.sh +14 -0
  147. package/runtime/claude/bin/onboarding.sh +6 -0
  148. package/runtime/claude/bin/person.py +634 -0
  149. package/runtime/claude/bin/person.sh +481 -0
  150. package/runtime/claude/bin/pi-render-spec.mjs +140 -0
  151. package/runtime/claude/bin/post-update.sh +37 -0
  152. package/runtime/claude/bin/prime-render-spec.mjs +147 -0
  153. package/runtime/claude/bin/publish-artifact.sh +324 -0
  154. package/runtime/claude/bin/publish-references.sh +144 -0
  155. package/runtime/claude/bin/quest.sh +9 -0
  156. package/runtime/claude/bin/question.sh +8 -0
  157. package/runtime/claude/bin/repo-state.sh +96 -0
  158. package/runtime/claude/bin/research-ingest.sh +7 -0
  159. package/runtime/claude/bin/restore-owned-skills.sh +67 -0
  160. package/runtime/claude/bin/retrieval_eval.py +385 -0
  161. package/runtime/claude/bin/runtime-identity.sh +70 -0
  162. package/runtime/claude/bin/runtime-upgrade.sh +43 -0
  163. package/runtime/claude/bin/scroll.sh +9 -0
  164. package/runtime/claude/bin/search.sh +318 -0
  165. package/runtime/claude/bin/session-autosave.sh +220 -0
  166. package/runtime/claude/bin/session-log.sh +199 -0
  167. package/runtime/claude/bin/session-start.sh +696 -0
  168. package/runtime/claude/bin/settings.sh +532 -0
  169. package/runtime/claude/bin/startup-check.sh +136 -0
  170. package/runtime/claude/bin/statusline.sh +41 -0
  171. package/runtime/claude/bin/telemetry.sh +666 -0
  172. package/runtime/claude/bin/test-changes.sh +344 -0
  173. package/runtime/claude/bin/thread.sh +9 -0
  174. package/runtime/claude/bin/todo.sh +9 -0
  175. package/runtime/claude/bin/view-render.sh +126 -0
  176. package/runtime/claude/bin/worktree-create.sh +145 -0
  177. package/runtime/claude/bin/worktree-remove.sh +50 -0
  178. package/runtime/claude/bin/worktree.sh +198 -0
  179. package/runtime/claude/egregore_runtime/__init__.py +228 -0
  180. package/runtime/claude/egregore_runtime/adapters/__init__.py +5 -0
  181. package/runtime/claude/egregore_runtime/adapters/invitation.py +237 -0
  182. package/runtime/claude/egregore_runtime/adapters/notification.py +59 -0
  183. package/runtime/claude/egregore_runtime/adapters/qmd.py +1701 -0
  184. package/runtime/claude/egregore_runtime/adapters/qmd_cli.py +210 -0
  185. package/runtime/claude/egregore_runtime/adapters/qmd_embed_worker.py +59 -0
  186. package/runtime/claude/egregore_runtime/admin_gate.py +102 -0
  187. package/runtime/claude/egregore_runtime/artifacts.py +523 -0
  188. package/runtime/claude/egregore_runtime/contracts.py +499 -0
  189. package/runtime/claude/egregore_runtime/entitlements.py +85 -0
  190. package/runtime/claude/egregore_runtime/errors.py +10 -0
  191. package/runtime/claude/egregore_runtime/graph.py +356 -0
  192. package/runtime/claude/egregore_runtime/handoff_lookup.py +196 -0
  193. package/runtime/claude/egregore_runtime/harness_cli.py +1221 -0
  194. package/runtime/claude/egregore_runtime/identity.py +734 -0
  195. package/runtime/claude/egregore_runtime/identity_cli.py +91 -0
  196. package/runtime/claude/egregore_runtime/ingest.py +575 -0
  197. package/runtime/claude/egregore_runtime/ingest_cli.py +254 -0
  198. package/runtime/claude/egregore_runtime/ingest_index_cli.py +147 -0
  199. package/runtime/claude/egregore_runtime/invitation_cli.py +51 -0
  200. package/runtime/claude/egregore_runtime/invitations.py +116 -0
  201. package/runtime/claude/egregore_runtime/issue_cli.py +305 -0
  202. package/runtime/claude/egregore_runtime/issues.py +359 -0
  203. package/runtime/claude/egregore_runtime/knowledge.py +376 -0
  204. package/runtime/claude/egregore_runtime/knowledge_cli.py +173 -0
  205. package/runtime/claude/egregore_runtime/lifecycle.py +888 -0
  206. package/runtime/claude/egregore_runtime/lifecycle_cli.py +269 -0
  207. package/runtime/claude/egregore_runtime/notification_cli.py +125 -0
  208. package/runtime/claude/egregore_runtime/notifications.py +230 -0
  209. package/runtime/claude/egregore_runtime/observe.py +342 -0
  210. package/runtime/claude/egregore_runtime/onboarding.py +290 -0
  211. package/runtime/claude/egregore_runtime/onboarding_cli.py +110 -0
  212. package/runtime/claude/egregore_runtime/policy.py +144 -0
  213. package/runtime/claude/egregore_runtime/profile.py +193 -0
  214. package/runtime/claude/egregore_runtime/quest_cli.py +216 -0
  215. package/runtime/claude/egregore_runtime/question_cli.py +180 -0
  216. package/runtime/claude/egregore_runtime/questions.py +304 -0
  217. package/runtime/claude/egregore_runtime/quests.py +419 -0
  218. package/runtime/claude/egregore_runtime/readiness.py +379 -0
  219. package/runtime/claude/egregore_runtime/research_ingest.py +256 -0
  220. package/runtime/claude/egregore_runtime/research_ingest_cli.py +44 -0
  221. package/runtime/claude/egregore_runtime/runtime.py +464 -0
  222. package/runtime/claude/egregore_runtime/scroll_cli.py +185 -0
  223. package/runtime/claude/egregore_runtime/scrolls.py +726 -0
  224. package/runtime/claude/egregore_runtime/status.py +412 -0
  225. package/runtime/claude/egregore_runtime/status_cli.py +174 -0
  226. package/runtime/claude/egregore_runtime/sync.py +218 -0
  227. package/runtime/claude/egregore_runtime/teamsync.py +139 -0
  228. package/runtime/claude/egregore_runtime/telemetry.py +640 -0
  229. package/runtime/claude/egregore_runtime/thread_cli.py +173 -0
  230. package/runtime/claude/egregore_runtime/threads.py +517 -0
  231. package/runtime/claude/egregore_runtime/todo_cli.py +142 -0
  232. package/runtime/claude/egregore_runtime/todos.py +244 -0
  233. package/runtime/claude/egregore_runtime/upgrade.py +770 -0
  234. package/runtime/claude/egregore_runtime/upgrade_worker.py +53 -0
  235. package/runtime/claude/egregore_runtime/writeback.py +391 -0
  236. package/runtime/claude/egregore_runtime/writeback_cli.py +114 -0
  237. package/runtime/claude/manifest.json +284 -0
  238. package/runtime/codex/.codex/hooks/search-hint.js +21 -52
  239. package/runtime/codex/.codex/hooks.json +3 -3
  240. package/runtime/codex/.codex/skills/activity/SKILL.md +23 -66
  241. package/runtime/codex/.codex/skills/announce/SKILL.md +27 -50
  242. package/runtime/codex/.codex/skills/archive/SKILL.md +21 -36
  243. package/runtime/codex/.codex/skills/checkup/SKILL.md +77 -36
  244. package/runtime/codex/.codex/skills/dark-mode/SKILL.md +1 -1
  245. package/runtime/codex/.codex/skills/dashboard/SKILL.md +17 -47
  246. package/runtime/codex/.codex/skills/handoff/SKILL.md +84 -133
  247. package/runtime/codex/.codex/skills/harvest/SKILL.md +32 -117
  248. package/runtime/codex/.codex/skills/ingest/SKILL.md +47 -25
  249. package/runtime/codex/.codex/skills/ingest-user-interview/SKILL.md +18 -36
  250. package/runtime/codex/.codex/skills/ingest-user-interview/references/analysis-contract.md +42 -0
  251. package/runtime/codex/.codex/skills/invite/SKILL.md +21 -35
  252. package/runtime/codex/.codex/skills/issue/SKILL.md +60 -41
  253. package/runtime/codex/.codex/skills/me/SKILL.md +1 -1
  254. package/runtime/codex/.codex/skills/meeting/SKILL.md +16 -36
  255. package/runtime/codex/.codex/skills/meeting/references/analysis-contract.md +48 -0
  256. package/runtime/codex/.codex/skills/note/SKILL.md +30 -25
  257. package/runtime/codex/.codex/skills/onboarding/SKILL.md +36 -41
  258. package/runtime/codex/.codex/skills/pull/SKILL.md +1 -1
  259. package/runtime/codex/.codex/skills/quest/SKILL.md +27 -66
  260. package/runtime/codex/.codex/skills/reflect/SKILL.md +22 -36
  261. package/runtime/codex/.codex/skills/save/SKILL.md +2 -2
  262. package/runtime/codex/.codex/skills/telemetry/SKILL.md +1 -1
  263. package/runtime/codex/.codex/skills/test/SKILL.md +1 -1
  264. package/runtime/codex/.codex/skills/tutorial/SKILL.md +40 -41
  265. package/runtime/codex/.codex/skills/view/SKILL.md +115 -111
  266. package/runtime/codex/.codex/skills/wrap/SKILL.md +45 -41
  267. package/runtime/codex/.codex/spec-manifest.json +9 -9
  268. package/runtime/codex/AGENTS.md +62 -58
  269. package/runtime/codex/bin/activity-action.sh +92 -0
  270. package/runtime/codex/bin/activity-data.sh +12 -233
  271. package/runtime/codex/bin/agent.sh +84 -100
  272. package/runtime/codex/bin/artifact-register.sh +164 -0
  273. package/runtime/codex/bin/artifact-writeback.sh +53 -0
  274. package/runtime/codex/bin/artifacts.sh +288 -0
  275. package/runtime/codex/bin/build-sealed-dataroom.mjs +722 -0
  276. package/runtime/codex/bin/capture-run.sh +18 -2
  277. package/runtime/codex/bin/codex-render-spec.mjs +41 -16
  278. package/runtime/codex/bin/codex-session-start.sh +14 -4
  279. package/runtime/codex/bin/codex-skill-render.mjs +9 -7
  280. package/runtime/codex/bin/corpus_map.py +806 -0
  281. package/runtime/codex/bin/corpus_survey.py +304 -0
  282. package/runtime/codex/bin/dashboard-data.sh +11 -171
  283. package/runtime/codex/bin/github-auth.sh +121 -0
  284. package/runtime/codex/bin/graph-batch.sh +7 -0
  285. package/runtime/codex/bin/graph-op.sh +4 -0
  286. package/runtime/codex/bin/graph-projection.sh +54 -0
  287. package/runtime/codex/bin/graph-wal.sh +13 -0
  288. package/runtime/codex/bin/graph.sh +44 -0
  289. package/runtime/codex/bin/handoff-lifecycle-job.sh +146 -0
  290. package/runtime/codex/bin/handoff-lifecycle.sh +280 -0
  291. package/runtime/codex/bin/handoff-preview.sh +169 -0
  292. package/runtime/codex/bin/handoff-run.sh +42 -26
  293. package/runtime/codex/bin/ingest-graph.sh +6 -0
  294. package/runtime/codex/bin/ingest-item.sh +10 -0
  295. package/runtime/codex/bin/ingest.sh +59 -30
  296. package/runtime/codex/bin/invite.sh +6 -0
  297. package/runtime/codex/bin/issue.sh +10 -0
  298. package/runtime/codex/bin/knowledge.sh +10 -0
  299. package/runtime/codex/bin/lib/config.sh +20 -0
  300. package/runtime/codex/bin/lib/context.sh +46 -25
  301. package/runtime/codex/bin/lib/git-sync.sh +70 -6
  302. package/runtime/codex/bin/lib/greeting.sh +34 -5
  303. package/runtime/codex/bin/lib/handoff-meta.sh +97 -0
  304. package/runtime/codex/bin/lib/worktree-links.sh +6 -0
  305. package/runtime/codex/bin/node-run.sh +42 -0
  306. package/runtime/codex/bin/notification.sh +6 -0
  307. package/runtime/codex/bin/observe-context.sh +14 -0
  308. package/runtime/codex/bin/onboarding.sh +6 -0
  309. package/runtime/codex/bin/person.py +4 -0
  310. package/runtime/codex/bin/person.sh +43 -10
  311. package/runtime/codex/bin/pi-render-spec.mjs +140 -0
  312. package/runtime/codex/bin/post-update.sh +37 -0
  313. package/runtime/codex/bin/prime-render-spec.mjs +147 -0
  314. package/runtime/codex/bin/publish-artifact.sh +55 -2
  315. package/runtime/codex/bin/quest.sh +9 -0
  316. package/runtime/codex/bin/question.sh +8 -0
  317. package/runtime/codex/bin/research-ingest.sh +7 -0
  318. package/runtime/codex/bin/restore-owned-skills.sh +67 -0
  319. package/runtime/codex/bin/retrieval_eval.py +385 -0
  320. package/runtime/codex/bin/runtime-identity.sh +70 -0
  321. package/runtime/codex/bin/runtime-upgrade.sh +43 -0
  322. package/runtime/codex/bin/scroll.sh +9 -0
  323. package/runtime/codex/bin/search.sh +231 -195
  324. package/runtime/codex/bin/settings.sh +174 -5
  325. package/runtime/codex/bin/startup-check.sh +136 -0
  326. package/runtime/codex/bin/telemetry.sh +522 -247
  327. package/runtime/codex/bin/test-changes.sh +344 -0
  328. package/runtime/codex/bin/thread.sh +9 -0
  329. package/runtime/codex/bin/todo.sh +9 -0
  330. package/runtime/codex/bin/view-render.sh +126 -0
  331. package/runtime/codex/bin/worktree-create.sh +19 -5
  332. package/runtime/codex/egregore_runtime/__init__.py +228 -0
  333. package/runtime/codex/egregore_runtime/adapters/__init__.py +5 -0
  334. package/runtime/codex/egregore_runtime/adapters/invitation.py +237 -0
  335. package/runtime/codex/egregore_runtime/adapters/notification.py +59 -0
  336. package/runtime/codex/egregore_runtime/adapters/qmd.py +1701 -0
  337. package/runtime/codex/egregore_runtime/adapters/qmd_cli.py +210 -0
  338. package/runtime/codex/egregore_runtime/adapters/qmd_embed_worker.py +59 -0
  339. package/runtime/codex/egregore_runtime/admin_gate.py +102 -0
  340. package/runtime/codex/egregore_runtime/artifacts.py +523 -0
  341. package/runtime/codex/egregore_runtime/contracts.py +499 -0
  342. package/runtime/codex/egregore_runtime/entitlements.py +85 -0
  343. package/runtime/codex/egregore_runtime/errors.py +10 -0
  344. package/runtime/codex/egregore_runtime/graph.py +356 -0
  345. package/runtime/codex/egregore_runtime/handoff_lookup.py +196 -0
  346. package/runtime/codex/egregore_runtime/harness_cli.py +1221 -0
  347. package/runtime/codex/egregore_runtime/identity.py +734 -0
  348. package/runtime/codex/egregore_runtime/identity_cli.py +91 -0
  349. package/runtime/codex/egregore_runtime/ingest.py +575 -0
  350. package/runtime/codex/egregore_runtime/ingest_cli.py +254 -0
  351. package/runtime/codex/egregore_runtime/ingest_index_cli.py +147 -0
  352. package/runtime/codex/egregore_runtime/invitation_cli.py +51 -0
  353. package/runtime/codex/egregore_runtime/invitations.py +116 -0
  354. package/runtime/codex/egregore_runtime/issue_cli.py +305 -0
  355. package/runtime/codex/egregore_runtime/issues.py +359 -0
  356. package/runtime/codex/egregore_runtime/knowledge.py +376 -0
  357. package/runtime/codex/egregore_runtime/knowledge_cli.py +173 -0
  358. package/runtime/codex/egregore_runtime/lifecycle.py +888 -0
  359. package/runtime/codex/egregore_runtime/lifecycle_cli.py +269 -0
  360. package/runtime/codex/egregore_runtime/notification_cli.py +125 -0
  361. package/runtime/codex/egregore_runtime/notifications.py +230 -0
  362. package/runtime/codex/egregore_runtime/observe.py +342 -0
  363. package/runtime/codex/egregore_runtime/onboarding.py +290 -0
  364. package/runtime/codex/egregore_runtime/onboarding_cli.py +110 -0
  365. package/runtime/codex/egregore_runtime/policy.py +144 -0
  366. package/runtime/codex/egregore_runtime/profile.py +193 -0
  367. package/runtime/codex/egregore_runtime/quest_cli.py +216 -0
  368. package/runtime/codex/egregore_runtime/question_cli.py +180 -0
  369. package/runtime/codex/egregore_runtime/questions.py +304 -0
  370. package/runtime/codex/egregore_runtime/quests.py +419 -0
  371. package/runtime/codex/egregore_runtime/readiness.py +379 -0
  372. package/runtime/codex/egregore_runtime/research_ingest.py +256 -0
  373. package/runtime/codex/egregore_runtime/research_ingest_cli.py +44 -0
  374. package/runtime/codex/egregore_runtime/runtime.py +464 -0
  375. package/runtime/codex/egregore_runtime/scroll_cli.py +185 -0
  376. package/runtime/codex/egregore_runtime/scrolls.py +726 -0
  377. package/runtime/codex/egregore_runtime/status.py +412 -0
  378. package/runtime/codex/egregore_runtime/status_cli.py +174 -0
  379. package/runtime/codex/egregore_runtime/sync.py +218 -0
  380. package/runtime/codex/egregore_runtime/teamsync.py +139 -0
  381. package/runtime/codex/egregore_runtime/telemetry.py +640 -0
  382. package/runtime/codex/egregore_runtime/thread_cli.py +173 -0
  383. package/runtime/codex/egregore_runtime/threads.py +517 -0
  384. package/runtime/codex/egregore_runtime/todo_cli.py +142 -0
  385. package/runtime/codex/egregore_runtime/todos.py +244 -0
  386. package/runtime/codex/egregore_runtime/upgrade.py +770 -0
  387. package/runtime/codex/egregore_runtime/upgrade_worker.py +53 -0
  388. package/runtime/codex/egregore_runtime/writeback.py +391 -0
  389. package/runtime/codex/egregore_runtime/writeback_cli.py +114 -0
  390. package/runtime/codex/manifest.json +98 -1
  391. package/runtime/pi/.codex/hooks/search-hint.js +21 -52
  392. package/runtime/pi/.codex/hooks.json +3 -3
  393. package/runtime/pi/.codex/skills/activity/SKILL.md +23 -66
  394. package/runtime/pi/.codex/skills/announce/SKILL.md +27 -50
  395. package/runtime/pi/.codex/skills/archive/SKILL.md +21 -36
  396. package/runtime/pi/.codex/skills/checkup/SKILL.md +77 -36
  397. package/runtime/pi/.codex/skills/dark-mode/SKILL.md +1 -1
  398. package/runtime/pi/.codex/skills/dashboard/SKILL.md +17 -47
  399. package/runtime/pi/.codex/skills/handoff/SKILL.md +84 -133
  400. package/runtime/pi/.codex/skills/harvest/SKILL.md +32 -117
  401. package/runtime/pi/.codex/skills/ingest/SKILL.md +47 -25
  402. package/runtime/pi/.codex/skills/ingest-user-interview/SKILL.md +18 -36
  403. package/runtime/pi/.codex/skills/ingest-user-interview/references/analysis-contract.md +42 -0
  404. package/runtime/pi/.codex/skills/invite/SKILL.md +21 -35
  405. package/runtime/pi/.codex/skills/issue/SKILL.md +60 -41
  406. package/runtime/pi/.codex/skills/me/SKILL.md +1 -1
  407. package/runtime/pi/.codex/skills/meeting/SKILL.md +16 -36
  408. package/runtime/pi/.codex/skills/meeting/references/analysis-contract.md +48 -0
  409. package/runtime/pi/.codex/skills/note/SKILL.md +30 -25
  410. package/runtime/pi/.codex/skills/onboarding/SKILL.md +36 -41
  411. package/runtime/pi/.codex/skills/pull/SKILL.md +1 -1
  412. package/runtime/pi/.codex/skills/quest/SKILL.md +27 -66
  413. package/runtime/pi/.codex/skills/reflect/SKILL.md +22 -36
  414. package/runtime/pi/.codex/skills/save/SKILL.md +2 -2
  415. package/runtime/pi/.codex/skills/telemetry/SKILL.md +1 -1
  416. package/runtime/pi/.codex/skills/test/SKILL.md +1 -1
  417. package/runtime/pi/.codex/skills/tutorial/SKILL.md +40 -41
  418. package/runtime/pi/.codex/skills/view/SKILL.md +115 -111
  419. package/runtime/pi/.codex/skills/wrap/SKILL.md +45 -41
  420. package/runtime/pi/.codex/spec-manifest.json +9 -9
  421. package/runtime/pi/.pi/APPEND_SYSTEM.md +57 -48
  422. package/runtime/pi/.pi/extensions/egregore.ts +7 -0
  423. package/runtime/pi/.pi/spec-manifest.json +2 -2
  424. package/runtime/pi/AGENTS.md +62 -58
  425. package/runtime/pi/bin/activity-action.sh +92 -0
  426. package/runtime/pi/bin/activity-data.sh +12 -233
  427. package/runtime/pi/bin/agent.sh +84 -100
  428. package/runtime/pi/bin/artifact-register.sh +164 -0
  429. package/runtime/pi/bin/artifact-writeback.sh +53 -0
  430. package/runtime/pi/bin/artifacts.sh +288 -0
  431. package/runtime/pi/bin/build-sealed-dataroom.mjs +722 -0
  432. package/runtime/pi/bin/capture-run.sh +18 -2
  433. package/runtime/pi/bin/codex-render-spec.mjs +41 -16
  434. package/runtime/pi/bin/codex-session-start.sh +14 -4
  435. package/runtime/pi/bin/codex-skill-render.mjs +9 -7
  436. package/runtime/pi/bin/corpus_map.py +806 -0
  437. package/runtime/pi/bin/corpus_survey.py +304 -0
  438. package/runtime/pi/bin/dashboard-data.sh +11 -171
  439. package/runtime/pi/bin/github-auth.sh +121 -0
  440. package/runtime/pi/bin/graph-batch.sh +7 -0
  441. package/runtime/pi/bin/graph-op.sh +4 -0
  442. package/runtime/pi/bin/graph-projection.sh +54 -0
  443. package/runtime/pi/bin/graph-wal.sh +13 -0
  444. package/runtime/pi/bin/graph.sh +44 -0
  445. package/runtime/pi/bin/handoff-lifecycle-job.sh +146 -0
  446. package/runtime/pi/bin/handoff-lifecycle.sh +280 -0
  447. package/runtime/pi/bin/handoff-preview.sh +169 -0
  448. package/runtime/pi/bin/handoff-run.sh +42 -26
  449. package/runtime/pi/bin/ingest-graph.sh +6 -0
  450. package/runtime/pi/bin/ingest-item.sh +10 -0
  451. package/runtime/pi/bin/ingest.sh +59 -30
  452. package/runtime/pi/bin/invite.sh +6 -0
  453. package/runtime/pi/bin/issue.sh +10 -0
  454. package/runtime/pi/bin/knowledge.sh +10 -0
  455. package/runtime/pi/bin/lib/config.sh +20 -0
  456. package/runtime/pi/bin/lib/context.sh +46 -25
  457. package/runtime/pi/bin/lib/git-sync.sh +70 -6
  458. package/runtime/pi/bin/lib/greeting.sh +34 -5
  459. package/runtime/pi/bin/lib/handoff-meta.sh +97 -0
  460. package/runtime/pi/bin/lib/worktree-links.sh +6 -0
  461. package/runtime/pi/bin/node-run.sh +42 -0
  462. package/runtime/pi/bin/notification.sh +6 -0
  463. package/runtime/pi/bin/observe-context.sh +14 -0
  464. package/runtime/pi/bin/onboarding.sh +6 -0
  465. package/runtime/pi/bin/person.py +4 -0
  466. package/runtime/pi/bin/person.sh +43 -10
  467. package/runtime/pi/bin/pi-observe-context.mjs +93 -0
  468. package/runtime/pi/bin/pi-product-workflows.mjs +1 -1
  469. package/runtime/pi/bin/pi-render-spec.mjs +1 -1
  470. package/runtime/pi/bin/post-update.sh +37 -0
  471. package/runtime/pi/bin/prime-render-spec.mjs +147 -0
  472. package/runtime/pi/bin/publish-artifact.sh +55 -2
  473. package/runtime/pi/bin/quest.sh +9 -0
  474. package/runtime/pi/bin/question.sh +8 -0
  475. package/runtime/pi/bin/research-ingest.sh +7 -0
  476. package/runtime/pi/bin/restore-owned-skills.sh +67 -0
  477. package/runtime/pi/bin/retrieval_eval.py +385 -0
  478. package/runtime/pi/bin/runtime-identity.sh +70 -0
  479. package/runtime/pi/bin/runtime-upgrade.sh +43 -0
  480. package/runtime/pi/bin/scroll.sh +9 -0
  481. package/runtime/pi/bin/search.sh +231 -195
  482. package/runtime/pi/bin/settings.sh +174 -5
  483. package/runtime/pi/bin/startup-check.sh +136 -0
  484. package/runtime/pi/bin/telemetry.sh +522 -247
  485. package/runtime/pi/bin/test-changes.sh +344 -0
  486. package/runtime/pi/bin/thread.sh +9 -0
  487. package/runtime/pi/bin/todo.sh +9 -0
  488. package/runtime/pi/bin/view-render.sh +126 -0
  489. package/runtime/pi/bin/worktree-create.sh +19 -5
  490. package/runtime/pi/egregore_runtime/__init__.py +228 -0
  491. package/runtime/pi/egregore_runtime/adapters/__init__.py +5 -0
  492. package/runtime/pi/egregore_runtime/adapters/invitation.py +237 -0
  493. package/runtime/pi/egregore_runtime/adapters/notification.py +59 -0
  494. package/runtime/pi/egregore_runtime/adapters/qmd.py +1701 -0
  495. package/runtime/pi/egregore_runtime/adapters/qmd_cli.py +210 -0
  496. package/runtime/pi/egregore_runtime/adapters/qmd_embed_worker.py +59 -0
  497. package/runtime/pi/egregore_runtime/admin_gate.py +102 -0
  498. package/runtime/pi/egregore_runtime/artifacts.py +523 -0
  499. package/runtime/pi/egregore_runtime/contracts.py +499 -0
  500. package/runtime/pi/egregore_runtime/entitlements.py +85 -0
  501. package/runtime/pi/egregore_runtime/errors.py +10 -0
  502. package/runtime/pi/egregore_runtime/graph.py +356 -0
  503. package/runtime/pi/egregore_runtime/handoff_lookup.py +196 -0
  504. package/runtime/pi/egregore_runtime/harness_cli.py +1221 -0
  505. package/runtime/pi/egregore_runtime/identity.py +734 -0
  506. package/runtime/pi/egregore_runtime/identity_cli.py +91 -0
  507. package/runtime/pi/egregore_runtime/ingest.py +575 -0
  508. package/runtime/pi/egregore_runtime/ingest_cli.py +254 -0
  509. package/runtime/pi/egregore_runtime/ingest_index_cli.py +147 -0
  510. package/runtime/pi/egregore_runtime/invitation_cli.py +51 -0
  511. package/runtime/pi/egregore_runtime/invitations.py +116 -0
  512. package/runtime/pi/egregore_runtime/issue_cli.py +305 -0
  513. package/runtime/pi/egregore_runtime/issues.py +359 -0
  514. package/runtime/pi/egregore_runtime/knowledge.py +376 -0
  515. package/runtime/pi/egregore_runtime/knowledge_cli.py +173 -0
  516. package/runtime/pi/egregore_runtime/lifecycle.py +888 -0
  517. package/runtime/pi/egregore_runtime/lifecycle_cli.py +269 -0
  518. package/runtime/pi/egregore_runtime/notification_cli.py +125 -0
  519. package/runtime/pi/egregore_runtime/notifications.py +230 -0
  520. package/runtime/pi/egregore_runtime/observe.py +342 -0
  521. package/runtime/pi/egregore_runtime/onboarding.py +290 -0
  522. package/runtime/pi/egregore_runtime/onboarding_cli.py +110 -0
  523. package/runtime/pi/egregore_runtime/policy.py +144 -0
  524. package/runtime/pi/egregore_runtime/profile.py +193 -0
  525. package/runtime/pi/egregore_runtime/quest_cli.py +216 -0
  526. package/runtime/pi/egregore_runtime/question_cli.py +180 -0
  527. package/runtime/pi/egregore_runtime/questions.py +304 -0
  528. package/runtime/pi/egregore_runtime/quests.py +419 -0
  529. package/runtime/pi/egregore_runtime/readiness.py +379 -0
  530. package/runtime/pi/egregore_runtime/research_ingest.py +256 -0
  531. package/runtime/pi/egregore_runtime/research_ingest_cli.py +44 -0
  532. package/runtime/pi/egregore_runtime/runtime.py +464 -0
  533. package/runtime/pi/egregore_runtime/scroll_cli.py +185 -0
  534. package/runtime/pi/egregore_runtime/scrolls.py +726 -0
  535. package/runtime/pi/egregore_runtime/status.py +412 -0
  536. package/runtime/pi/egregore_runtime/status_cli.py +174 -0
  537. package/runtime/pi/egregore_runtime/sync.py +218 -0
  538. package/runtime/pi/egregore_runtime/teamsync.py +139 -0
  539. package/runtime/pi/egregore_runtime/telemetry.py +640 -0
  540. package/runtime/pi/egregore_runtime/thread_cli.py +173 -0
  541. package/runtime/pi/egregore_runtime/threads.py +517 -0
  542. package/runtime/pi/egregore_runtime/todo_cli.py +142 -0
  543. package/runtime/pi/egregore_runtime/todos.py +244 -0
  544. package/runtime/pi/egregore_runtime/upgrade.py +770 -0
  545. package/runtime/pi/egregore_runtime/upgrade_worker.py +53 -0
  546. package/runtime/pi/egregore_runtime/writeback.py +391 -0
  547. package/runtime/pi/egregore_runtime/writeback_cli.py +114 -0
  548. package/runtime/pi/manifest.json +98 -1
  549. package/runtime/prime/.codex/hooks/search-hint.js +21 -52
  550. package/runtime/prime/.codex/hooks.json +3 -3
  551. package/runtime/prime/.codex/skills/activity/SKILL.md +23 -66
  552. package/runtime/prime/.codex/skills/announce/SKILL.md +27 -50
  553. package/runtime/prime/.codex/skills/archive/SKILL.md +21 -36
  554. package/runtime/prime/.codex/skills/checkup/SKILL.md +77 -36
  555. package/runtime/prime/.codex/skills/dark-mode/SKILL.md +1 -1
  556. package/runtime/prime/.codex/skills/dashboard/SKILL.md +17 -47
  557. package/runtime/prime/.codex/skills/handoff/SKILL.md +84 -133
  558. package/runtime/prime/.codex/skills/harvest/SKILL.md +32 -117
  559. package/runtime/prime/.codex/skills/ingest/SKILL.md +47 -25
  560. package/runtime/prime/.codex/skills/ingest-user-interview/SKILL.md +18 -36
  561. package/runtime/prime/.codex/skills/ingest-user-interview/references/analysis-contract.md +42 -0
  562. package/runtime/prime/.codex/skills/invite/SKILL.md +21 -35
  563. package/runtime/prime/.codex/skills/issue/SKILL.md +60 -41
  564. package/runtime/prime/.codex/skills/me/SKILL.md +1 -1
  565. package/runtime/prime/.codex/skills/meeting/SKILL.md +16 -36
  566. package/runtime/prime/.codex/skills/meeting/references/analysis-contract.md +48 -0
  567. package/runtime/prime/.codex/skills/note/SKILL.md +30 -25
  568. package/runtime/prime/.codex/skills/onboarding/SKILL.md +36 -41
  569. package/runtime/prime/.codex/skills/pull/SKILL.md +1 -1
  570. package/runtime/prime/.codex/skills/quest/SKILL.md +27 -66
  571. package/runtime/prime/.codex/skills/reflect/SKILL.md +22 -36
  572. package/runtime/prime/.codex/skills/save/SKILL.md +2 -2
  573. package/runtime/prime/.codex/skills/telemetry/SKILL.md +1 -1
  574. package/runtime/prime/.codex/skills/test/SKILL.md +1 -1
  575. package/runtime/prime/.codex/skills/tutorial/SKILL.md +40 -41
  576. package/runtime/prime/.codex/skills/view/SKILL.md +115 -111
  577. package/runtime/prime/.codex/skills/wrap/SKILL.md +45 -41
  578. package/runtime/prime/.codex/spec-manifest.json +9 -9
  579. package/runtime/prime/.prime/agent/APPEND_SYSTEM.md +57 -48
  580. package/runtime/prime/.prime/agent/extensions/egregore.ts +7 -0
  581. package/runtime/prime/.prime/agent/spec-manifest.json +2 -2
  582. package/runtime/prime/AGENTS.md +62 -58
  583. package/runtime/prime/bin/activity-action.sh +92 -0
  584. package/runtime/prime/bin/activity-data.sh +12 -233
  585. package/runtime/prime/bin/agent.sh +84 -100
  586. package/runtime/prime/bin/artifact-register.sh +164 -0
  587. package/runtime/prime/bin/artifact-writeback.sh +53 -0
  588. package/runtime/prime/bin/artifacts.sh +288 -0
  589. package/runtime/prime/bin/build-sealed-dataroom.mjs +722 -0
  590. package/runtime/prime/bin/capture-run.sh +18 -2
  591. package/runtime/prime/bin/codex-render-spec.mjs +41 -16
  592. package/runtime/prime/bin/codex-session-start.sh +14 -4
  593. package/runtime/prime/bin/codex-skill-render.mjs +9 -7
  594. package/runtime/prime/bin/corpus_map.py +806 -0
  595. package/runtime/prime/bin/corpus_survey.py +304 -0
  596. package/runtime/prime/bin/dashboard-data.sh +11 -171
  597. package/runtime/prime/bin/github-auth.sh +121 -0
  598. package/runtime/prime/bin/graph-batch.sh +7 -0
  599. package/runtime/prime/bin/graph-op.sh +4 -0
  600. package/runtime/prime/bin/graph-projection.sh +54 -0
  601. package/runtime/prime/bin/graph-wal.sh +13 -0
  602. package/runtime/prime/bin/graph.sh +44 -0
  603. package/runtime/prime/bin/handoff-lifecycle-job.sh +146 -0
  604. package/runtime/prime/bin/handoff-lifecycle.sh +280 -0
  605. package/runtime/prime/bin/handoff-preview.sh +169 -0
  606. package/runtime/prime/bin/handoff-run.sh +42 -26
  607. package/runtime/prime/bin/ingest-graph.sh +6 -0
  608. package/runtime/prime/bin/ingest-item.sh +10 -0
  609. package/runtime/prime/bin/ingest.sh +59 -30
  610. package/runtime/prime/bin/invite.sh +6 -0
  611. package/runtime/prime/bin/issue.sh +10 -0
  612. package/runtime/prime/bin/knowledge.sh +10 -0
  613. package/runtime/prime/bin/lib/config.sh +20 -0
  614. package/runtime/prime/bin/lib/context.sh +46 -25
  615. package/runtime/prime/bin/lib/git-sync.sh +70 -6
  616. package/runtime/prime/bin/lib/greeting.sh +34 -5
  617. package/runtime/prime/bin/lib/handoff-meta.sh +97 -0
  618. package/runtime/prime/bin/lib/worktree-links.sh +6 -0
  619. package/runtime/prime/bin/node-run.sh +42 -0
  620. package/runtime/prime/bin/notification.sh +6 -0
  621. package/runtime/prime/bin/observe-context.sh +14 -0
  622. package/runtime/prime/bin/onboarding.sh +6 -0
  623. package/runtime/prime/bin/person.py +4 -0
  624. package/runtime/prime/bin/person.sh +43 -10
  625. package/runtime/prime/bin/pi-observe-context.mjs +93 -0
  626. package/runtime/prime/bin/pi-product-workflows.mjs +1 -1
  627. package/runtime/prime/bin/pi-render-spec.mjs +140 -0
  628. package/runtime/prime/bin/post-update.sh +37 -0
  629. package/runtime/prime/bin/prime-render-spec.mjs +1 -1
  630. package/runtime/prime/bin/publish-artifact.sh +55 -2
  631. package/runtime/prime/bin/quest.sh +9 -0
  632. package/runtime/prime/bin/question.sh +8 -0
  633. package/runtime/prime/bin/research-ingest.sh +7 -0
  634. package/runtime/prime/bin/restore-owned-skills.sh +67 -0
  635. package/runtime/prime/bin/retrieval_eval.py +385 -0
  636. package/runtime/prime/bin/runtime-identity.sh +70 -0
  637. package/runtime/prime/bin/runtime-upgrade.sh +43 -0
  638. package/runtime/prime/bin/scroll.sh +9 -0
  639. package/runtime/prime/bin/search.sh +231 -195
  640. package/runtime/prime/bin/settings.sh +174 -5
  641. package/runtime/prime/bin/startup-check.sh +136 -0
  642. package/runtime/prime/bin/telemetry.sh +522 -247
  643. package/runtime/prime/bin/test-changes.sh +344 -0
  644. package/runtime/prime/bin/thread.sh +9 -0
  645. package/runtime/prime/bin/todo.sh +9 -0
  646. package/runtime/prime/bin/view-render.sh +126 -0
  647. package/runtime/prime/bin/worktree-create.sh +19 -5
  648. package/runtime/prime/egregore_runtime/__init__.py +228 -0
  649. package/runtime/prime/egregore_runtime/adapters/__init__.py +5 -0
  650. package/runtime/prime/egregore_runtime/adapters/invitation.py +237 -0
  651. package/runtime/prime/egregore_runtime/adapters/notification.py +59 -0
  652. package/runtime/prime/egregore_runtime/adapters/qmd.py +1701 -0
  653. package/runtime/prime/egregore_runtime/adapters/qmd_cli.py +210 -0
  654. package/runtime/prime/egregore_runtime/adapters/qmd_embed_worker.py +59 -0
  655. package/runtime/prime/egregore_runtime/admin_gate.py +102 -0
  656. package/runtime/prime/egregore_runtime/artifacts.py +523 -0
  657. package/runtime/prime/egregore_runtime/contracts.py +499 -0
  658. package/runtime/prime/egregore_runtime/entitlements.py +85 -0
  659. package/runtime/prime/egregore_runtime/errors.py +10 -0
  660. package/runtime/prime/egregore_runtime/graph.py +356 -0
  661. package/runtime/prime/egregore_runtime/handoff_lookup.py +196 -0
  662. package/runtime/prime/egregore_runtime/harness_cli.py +1221 -0
  663. package/runtime/prime/egregore_runtime/identity.py +734 -0
  664. package/runtime/prime/egregore_runtime/identity_cli.py +91 -0
  665. package/runtime/prime/egregore_runtime/ingest.py +575 -0
  666. package/runtime/prime/egregore_runtime/ingest_cli.py +254 -0
  667. package/runtime/prime/egregore_runtime/ingest_index_cli.py +147 -0
  668. package/runtime/prime/egregore_runtime/invitation_cli.py +51 -0
  669. package/runtime/prime/egregore_runtime/invitations.py +116 -0
  670. package/runtime/prime/egregore_runtime/issue_cli.py +305 -0
  671. package/runtime/prime/egregore_runtime/issues.py +359 -0
  672. package/runtime/prime/egregore_runtime/knowledge.py +376 -0
  673. package/runtime/prime/egregore_runtime/knowledge_cli.py +173 -0
  674. package/runtime/prime/egregore_runtime/lifecycle.py +888 -0
  675. package/runtime/prime/egregore_runtime/lifecycle_cli.py +269 -0
  676. package/runtime/prime/egregore_runtime/notification_cli.py +125 -0
  677. package/runtime/prime/egregore_runtime/notifications.py +230 -0
  678. package/runtime/prime/egregore_runtime/observe.py +342 -0
  679. package/runtime/prime/egregore_runtime/onboarding.py +290 -0
  680. package/runtime/prime/egregore_runtime/onboarding_cli.py +110 -0
  681. package/runtime/prime/egregore_runtime/policy.py +144 -0
  682. package/runtime/prime/egregore_runtime/profile.py +193 -0
  683. package/runtime/prime/egregore_runtime/quest_cli.py +216 -0
  684. package/runtime/prime/egregore_runtime/question_cli.py +180 -0
  685. package/runtime/prime/egregore_runtime/questions.py +304 -0
  686. package/runtime/prime/egregore_runtime/quests.py +419 -0
  687. package/runtime/prime/egregore_runtime/readiness.py +379 -0
  688. package/runtime/prime/egregore_runtime/research_ingest.py +256 -0
  689. package/runtime/prime/egregore_runtime/research_ingest_cli.py +44 -0
  690. package/runtime/prime/egregore_runtime/runtime.py +464 -0
  691. package/runtime/prime/egregore_runtime/scroll_cli.py +185 -0
  692. package/runtime/prime/egregore_runtime/scrolls.py +726 -0
  693. package/runtime/prime/egregore_runtime/status.py +412 -0
  694. package/runtime/prime/egregore_runtime/status_cli.py +174 -0
  695. package/runtime/prime/egregore_runtime/sync.py +218 -0
  696. package/runtime/prime/egregore_runtime/teamsync.py +139 -0
  697. package/runtime/prime/egregore_runtime/telemetry.py +640 -0
  698. package/runtime/prime/egregore_runtime/thread_cli.py +173 -0
  699. package/runtime/prime/egregore_runtime/threads.py +517 -0
  700. package/runtime/prime/egregore_runtime/todo_cli.py +142 -0
  701. package/runtime/prime/egregore_runtime/todos.py +244 -0
  702. package/runtime/prime/egregore_runtime/upgrade.py +770 -0
  703. package/runtime/prime/egregore_runtime/upgrade_worker.py +53 -0
  704. package/runtime/prime/egregore_runtime/writeback.py +391 -0
  705. package/runtime/prime/egregore_runtime/writeback_cli.py +114 -0
  706. package/runtime/prime/manifest.json +98 -1
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- // egregore-launcher-version: 0.20.2
2
+ // egregore-launcher-version: 0.21.0-runtime-mvp.2
3
3
  //
4
4
  // egregore — launch and browse Egregore instances from your terminal.
5
5
  // Installed by create-egregore. Reads ~/.egregore/instances.json.
@@ -14,6 +14,8 @@
14
14
  // egregore ls | list list instances (non-interactive table)
15
15
  // egregore use <slug> set the default for bare `egregore`
16
16
  // egregore connect [slug] activate Connected for an existing instance
17
+ // egregore telemetry <verb> manage local telemetry for an instance
18
+ // egregore runtime <verb> inspect or synchronize Local retrieval
17
19
  // egregore /invite <user> invite a GitHub user to an instance
18
20
  // egregore settings [slug] hosting on/off, managed repos, admins (s in browser)
19
21
  // egregore prune [--dry-run] remove entries whose path is gone
@@ -33,7 +35,7 @@ const crypto = require("node:crypto");
33
35
  const { spawnSync, spawn } = require("node:child_process");
34
36
  const readline = require("node:readline");
35
37
 
36
- const VERSION = "0.20.2";
38
+ const VERSION = "0.21.0-runtime-mvp.2";
37
39
  const REGISTRY = path.join(os.homedir(), ".egregore", "instances.json");
38
40
  const DEFAULT_FILE = path.join(os.homedir(), ".egregore", "default");
39
41
  const UPDATE_CACHE_FILE = path.join(os.homedir(), ".egregore", "update-check.json");
@@ -360,6 +362,52 @@ function initialCursorForCwd(registry, cwd = safeCwd()) {
360
362
  return 0;
361
363
  }
362
364
 
365
+ function currentWorktreeInstance(registry, cwd = safeCwd()) {
366
+ // Development checkouts are intentionally not persisted as separate
367
+ // Egregore instances, but launching from one must preserve the exact code
368
+ // under test. A linked worktree has a `.git` file; a safe standalone clone
369
+ // has a `.git` directory. Walk upward and borrow only display/runtime
370
+ // preferences from a registered parent when one exists.
371
+ let candidate = path.resolve(cwd);
372
+ const filesystemRoot = path.parse(candidate).root;
373
+ while (candidate !== filesystemRoot) {
374
+ const marker = path.join(candidate, ".git");
375
+ const config = path.join(candidate, "egregore.json");
376
+ try {
377
+ const gitMarker = fs.statSync(marker);
378
+ if ((gitMarker.isFile() || gitMarker.isDirectory()) && fs.statSync(config).isFile()) {
379
+ const exact = registry.find(
380
+ (entry) => entry?.path && path.resolve(entry.path) === candidate,
381
+ );
382
+ if (exact) return null;
383
+ const parent = registry
384
+ .filter((entry) => entry?.path && pathContains(entry.path, candidate))
385
+ .sort((a, b) => path.resolve(b.path).length - path.resolve(a.path).length)[0];
386
+ if (parent) return { ...parent, path: candidate, transientWorktree: true };
387
+ const local = JSON.parse(fs.readFileSync(config, "utf-8"));
388
+ return {
389
+ slug: local.slug || path.basename(candidate),
390
+ name: local.org_name || local.slug || path.basename(candidate),
391
+ path: candidate,
392
+ transientWorktree: true,
393
+ };
394
+ }
395
+ } catch {
396
+ // Keep walking: normal subdirectories do not contain both markers.
397
+ }
398
+ candidate = path.dirname(candidate);
399
+ }
400
+ return null;
401
+ }
402
+
403
+ function isLinkedGitWorktree(directory) {
404
+ try {
405
+ return fs.statSync(path.join(directory, ".git")).isFile();
406
+ } catch {
407
+ return false;
408
+ }
409
+ }
410
+
363
411
  // ── launch ──────────────────────────────────────────────────────────
364
412
 
365
413
  function normalizeRuntime(raw) {
@@ -676,7 +724,7 @@ async function renderCodexStartupCard(egregoreDir) {
676
724
  }, "Preparing Codex startup");
677
725
  let output = result.stdout || "";
678
726
  if (isConnectedPath(egregoreDir) && !output.includes("◆ CONNECTED")) {
679
- const connectedLine = " ◆ CONNECTED · hosted graph active\n";
727
+ const connectedLine = ` ◆ CONNECTED ${connectedModeSuffix(egregoreDir)}\n`;
680
728
  output = output.includes("\nWhat are you working on?")
681
729
  ? output.replace("\nWhat are you working on?", `\n${connectedLine}\nWhat are you working on?`)
682
730
  : `${output}${output.endsWith("\n") || !output ? "" : "\n"}${connectedLine}`;
@@ -846,6 +894,9 @@ function installCodexSkills(egregoreDir) {
846
894
  }
847
895
 
848
896
  function claudeLaunchArgs(egregoreDir) {
897
+ // Claude treats the positional value as the initial user prompt. Egregore
898
+ // deliberately submits `start` so the opening experience renders without
899
+ // requiring the user to type first.
849
900
  const args = ["start"];
850
901
  const memoryLink = path.join(egregoreDir, "memory");
851
902
  try {
@@ -863,14 +914,21 @@ function claudeLaunchArgs(egregoreDir) {
863
914
  return args;
864
915
  }
865
916
 
866
- function runtimeLaunchArgs(runtime, egregoreDir) {
917
+ function runtimeLaunchArgs(runtime, egregoreDir, options = {}) {
867
918
  if (runtime === "codex") {
868
- return [
919
+ const args = [
869
920
  "--enable", "hooks",
870
921
  "--sandbox", "danger-full-access",
871
922
  "--ask-for-approval", "never",
872
923
  "--cd", egregoreDir,
873
924
  ];
925
+ // Codex treats each linked worktree as a separate hook-trust root, while
926
+ // project hooks may still be discovered from the registered primary
927
+ // checkout. The Egregore launcher validates the Runtime surface above;
928
+ // trust that bounded hook set for this transient invocation so Observe
929
+ // and the branch guard are not silently skipped in the worktree.
930
+ if (options.bypassHookTrust) args.push("--dangerously-bypass-hook-trust");
931
+ return args;
874
932
  }
875
933
  if (runtime === "pi") return piTrustArgs();
876
934
  if (runtime === "prime") return primeTrustArgs();
@@ -894,6 +952,12 @@ async function launch(inst, runtimeOverride) {
894
952
  }
895
953
  const runtime = await chooseRuntimeForLaunch(inst, runtimeOverride);
896
954
  if (!runtime) return;
955
+ if (runtime === "claude-code" && isLinkedGitWorktree(inst.path)) {
956
+ die(
957
+ "Claude Code automatically removes linked Git worktrees when its worktree session ends.\n" +
958
+ " Launch Claude from a standalone clone of this branch instead; Codex, Pi, and Prime may use this worktree directly.",
959
+ );
960
+ }
897
961
  if (runtime === "claude-code") {
898
962
  normalizeClaudeDenyRules(inst.path);
899
963
  ensureClaudeModeStatusLine(inst.path);
@@ -949,9 +1013,12 @@ async function launch(inst, runtimeOverride) {
949
1013
  const launchEnv = {
950
1014
  ...process.env,
951
1015
  EGREGORE_RUNTIME: runtime,
1016
+ EGREGORE_TRANSIENT_CHECKOUT: inst.transientWorktree ? "1" : process.env.EGREGORE_TRANSIENT_CHECKOUT,
952
1017
  EGREGORE_CODEX_TRUSTED: runtime === "codex" ? "1" : process.env.EGREGORE_CODEX_TRUSTED,
953
1018
  };
954
- const child = spawn(binary, launchArgs || runtimeLaunchArgs(runtime, inst.path), {
1019
+ const child = spawn(binary, launchArgs || runtimeLaunchArgs(runtime, inst.path, {
1020
+ bypassHookTrust: runtime === "codex" && Boolean(inst.transientWorktree),
1021
+ }), {
955
1022
  cwd: inst.path,
956
1023
  stdio: "inherit",
957
1024
  env: launchEnv,
@@ -1080,8 +1147,11 @@ function cmdHelp() {
1080
1147
  ` ${CYAN}egregore ls${RESET} ${DIM}| list${RESET} list installed instances`,
1081
1148
  ` ${CYAN}egregore use${RESET} ${DIM}<slug>${RESET} set the default for bare ${BOLD}egregore${RESET}`,
1082
1149
  ` ${CYAN}egregore connect${RESET} ${DIM}[slug]${RESET} activate Connected for an existing instance`,
1150
+ ` ${CYAN}egregore telemetry${RESET} ${DIM}<verb>${RESET} status, inspect, enable, disable, export, or share`,
1151
+ ` ${CYAN}egregore runtime${RESET} ${DIM}<verb>${RESET} status, sync, start, or stop Local retrieval`,
1083
1152
  ` ${CYAN}egregore /invite${RESET} ${DIM}<user> [slug]${RESET} invite a GitHub user`,
1084
1153
  ` ${CYAN}egregore settings${RESET} ${DIM}[slug]${RESET} hosting on/off, repos, admins`,
1154
+ ` ${CYAN}egregore settings${RESET} ${DIM}<slug> runtime${RESET} Runtime update: review, prepare, activate, roll back`,
1085
1155
  ` ${CYAN}egregore prune${RESET} ${DIM}[--dry-run]${RESET} remove entries with missing paths`,
1086
1156
  ` ${CYAN}egregore rm${RESET} ${DIM}<slug|path>${RESET} remove a specific entry`,
1087
1157
  ``,
@@ -2116,6 +2186,79 @@ async function cmdInvite(args) {
2116
2186
  return inviteToInstance(inst, username);
2117
2187
  }
2118
2188
 
2189
+ function cmdTelemetry(args) {
2190
+ const registry = readRegistry();
2191
+ if (registry.length === 0) die("no egregores installed yet.\n run: npx -y create-egregore@latest");
2192
+
2193
+ const forwarded = args.slice();
2194
+ let instanceQuery = "";
2195
+ const marker = forwarded.indexOf("--instance");
2196
+ if (marker !== -1) {
2197
+ instanceQuery = forwarded[marker + 1] || "";
2198
+ if (!instanceQuery) die("usage: egregore telemetry <verb> [args] --instance <slug>");
2199
+ forwarded.splice(marker, 2);
2200
+ }
2201
+ if (forwarded.length === 0) forwarded.push("status");
2202
+
2203
+ const inst = resolveInviteInstance(registry, instanceQuery);
2204
+ if (!inst) {
2205
+ die("multiple egregores installed; choose one:\n egregore telemetry <verb> --instance <slug>\n run: egregore ls");
2206
+ }
2207
+ const script = path.join(inst.path, "bin", "telemetry.sh");
2208
+ if (!fs.existsSync(script)) {
2209
+ die("bin/telemetry.sh not found — update this Egregore before managing telemetry");
2210
+ }
2211
+ const result = spawnSync("bash", [script, ...forwarded], {
2212
+ cwd: inst.path,
2213
+ stdio: "inherit",
2214
+ env: { ...process.env },
2215
+ });
2216
+ if (result.error) die(`telemetry command failed: ${result.error.message}`);
2217
+ process.exitCode = result.status ?? 1;
2218
+ }
2219
+
2220
+ function cmdRuntime(args) {
2221
+ const registry = readRegistry();
2222
+ if (registry.length === 0) die("no egregores installed yet.\n run: npx -y create-egregore@latest");
2223
+
2224
+ const forwarded = args.slice();
2225
+ let instanceQuery = "";
2226
+ const marker = forwarded.indexOf("--instance");
2227
+ if (marker !== -1) {
2228
+ instanceQuery = forwarded[marker + 1] || "";
2229
+ if (!instanceQuery) die("usage: egregore runtime <status|sync|start|stop> --instance <slug>");
2230
+ forwarded.splice(marker, 2);
2231
+ }
2232
+ const verb = forwarded[0] || "status";
2233
+ if (!["status", "sync", "start", "stop", "update"].includes(verb) || forwarded.length > 1) {
2234
+ die("usage: egregore runtime <status|sync|start|stop> [--instance <slug>]");
2235
+ }
2236
+ const inst = resolveInviteInstance(registry, instanceQuery);
2237
+ if (!inst) {
2238
+ die("multiple egregores installed; choose one:\n egregore runtime status --instance <slug>\n run: egregore ls");
2239
+ }
2240
+
2241
+ let script;
2242
+ let scriptArgs;
2243
+ if (verb === "sync" || verb === "update") {
2244
+ script = path.join(inst.path, "bin", "agent.sh");
2245
+ scriptArgs = ["sync"];
2246
+ } else {
2247
+ script = path.join(inst.path, "bin", "search.sh");
2248
+ scriptArgs = [verb];
2249
+ }
2250
+ if (!fs.existsSync(script)) {
2251
+ die("Local Runtime controls are missing — update this Egregore before managing retrieval");
2252
+ }
2253
+ const result = spawnSync("bash", [script, ...scriptArgs], {
2254
+ cwd: inst.path,
2255
+ stdio: "inherit",
2256
+ env: { ...process.env },
2257
+ });
2258
+ if (result.error) die(`runtime command failed: ${result.error.message}`);
2259
+ process.exitCode = result.status ?? 1;
2260
+ }
2261
+
2119
2262
  // ── settings ───────────────────────────────────────────────────────
2120
2263
  //
2121
2264
  // The launcher stays a THIN caller: every config change shells out to the
@@ -2145,17 +2288,442 @@ function settingsDump(inst) {
2145
2288
  try { return JSON.parse(r.stdout); } catch { return null; }
2146
2289
  }
2147
2290
 
2291
+ // ── runtime upgrade (per-instance, settings-first) ──────────────────
2292
+ // The launcher stays a thin caller: the transactional engine, gates,
2293
+ // activation, and rollback live in the instance's Python Runtime behind
2294
+ // bin/runtime-upgrade.sh. The launcher renders that state and forwards
2295
+ // explicit verbs — one source of truth, no JS reimplementation.
2296
+
2297
+ const RUNTIME_MVP_VERSION_RE = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)-runtime-mvp\.(0|[1-9]\d*)$/;
2298
+ const RUNTIME_MVP_REGISTRY_URL = process.env.EGREGORE_RUNTIME_MVP_REGISTRY_URL
2299
+ || "https://registry.npmjs.org/create-egregore/runtime-mvp";
2300
+
2301
+ function upgradeScriptPath(inst) {
2302
+ return path.join(inst.path, "bin", "runtime-upgrade.sh");
2303
+ }
2304
+
2305
+ // After the Runtime cutover, a Connected instance's hosted link no longer
2306
+ // serves retrieval — saying "hosted graph active" would misstate what the
2307
+ // connection is for. Mirror the shell gate (bin/search.sh): stable org
2308
+ // identity composed with the main installation path keys the activation
2309
+ // record; only a real record flips the wording.
2310
+ function upgradedGraphRetrievalOff(instPath) {
2311
+ try {
2312
+ const cfg = JSON.parse(fs.readFileSync(path.join(instPath, "egregore.json"), "utf-8"));
2313
+ const orgId = String(cfg.org_id || "").trim();
2314
+ if (!orgId) return false;
2315
+ let main = instPath;
2316
+ const gitFile = path.join(instPath, ".git");
2317
+ if (fs.existsSync(gitFile) && fs.statSync(gitFile).isFile()) {
2318
+ const m = /^gitdir: (.*)$/m.exec(fs.readFileSync(gitFile, "utf-8"));
2319
+ if (m && m[1].includes("/.git/worktrees/")) main = m[1].split("/.git/worktrees/")[0];
2320
+ }
2321
+ main = fs.realpathSync(main);
2322
+ const key = crypto.createHash("sha256").update(`${orgId}|${main}`).digest("hex").slice(0, 16);
2323
+ const base = process.env.EGREGORE_UPGRADE_ROOT || path.join(os.homedir(), ".egregore", "runtime", "upgrade");
2324
+ const record = JSON.parse(fs.readFileSync(path.join(base, key, "active.json"), "utf-8"));
2325
+ return record.retrieval === "runtime-qmd";
2326
+ } catch {
2327
+ return false;
2328
+ }
2329
+ }
2330
+
2331
+ function connectedModeSuffix(instPath) {
2332
+ return upgradedGraphRetrievalOff(instPath)
2333
+ ? "· hosted services · graph retrieval off"
2334
+ : "· hosted graph active";
2335
+ }
2336
+
2337
+ // An instance that predates the bridge (0.20.1 and earlier) is still
2338
+ // upgradable: the installed launcher package carries the candidate's own
2339
+ // engine, so the bootstrap never depends on a git sync delivering it.
2340
+ // EGREGORE_RUNTIME_PACKAGED_BRIDGE is the test seam ("" disables).
2341
+ function packagedUpgradeScript() {
2342
+ const seeded = process.env.EGREGORE_RUNTIME_PACKAGED_BRIDGE;
2343
+ if (seeded !== undefined) return seeded || null;
2344
+ const script = path.join(__dirname, "..", "runtime", "claude", "bin", "runtime-upgrade.sh");
2345
+ return fs.existsSync(script) ? script : null;
2346
+ }
2347
+
2348
+ function resolveUpgradeBridge(inst) {
2349
+ const own = upgradeScriptPath(inst);
2350
+ if (fs.existsSync(own)) return { script: own, env: {} };
2351
+ const packaged = packagedUpgradeScript();
2352
+ if (packaged && fs.existsSync(packaged)) {
2353
+ return { script: packaged, env: { EGREGORE_INSTANCE_ROOT: inst.path } };
2354
+ }
2355
+ return null;
2356
+ }
2357
+
2358
+ function runUpgrade(inst, args, timeout = 60000) {
2359
+ const bridge = resolveUpgradeBridge(inst);
2360
+ if (!bridge) {
2361
+ return { status: 127, stdout: "", stderr: "Runtime update is unavailable — no upgrade engine in this egregore or the installed launcher package.\n", missing: true };
2362
+ }
2363
+ try {
2364
+ return spawnSync("bash", [bridge.script, ...args], { cwd: inst.path, stdio: "pipe", encoding: "utf-8", timeout, env: { ...process.env, ...bridge.env } });
2365
+ } catch (err) {
2366
+ return { status: 1, stdout: "", stderr: String(err) };
2367
+ }
2368
+ }
2369
+
2370
+ // ── privacy & access + telemetry (Settings sections) ───────────────
2371
+ // Thin callers over deterministic adapters: bin/settings.sh privacy and
2372
+ // bin/telemetry.sh. The Runtime authorization backend supplies identity,
2373
+ // membership, and capability truth; the launcher only renders it. No model
2374
+ // round-trip, no raw JSON shown, no secrets or internal paths.
2375
+
2376
+ function runTelemetry(inst, args) {
2377
+ const script = path.join(inst.path, "bin", "telemetry.sh");
2378
+ if (!fs.existsSync(script)) return { status: 127, stdout: "", stderr: "telemetry adapter unavailable\n" };
2379
+ try {
2380
+ return spawnSync("bash", [script, ...args], { cwd: inst.path, stdio: "pipe", encoding: "utf-8", timeout: 15000 });
2381
+ } catch (err) {
2382
+ return { status: 1, stdout: "", stderr: String(err) };
2383
+ }
2384
+ }
2385
+
2386
+ function privacySnapshot(inst) {
2387
+ const r = runSettings(inst, ["privacy"]);
2388
+ if (!r || r.status !== 0 || !r.stdout) return null;
2389
+ try { return JSON.parse(r.stdout); } catch { return null; }
2390
+ }
2391
+
2392
+ function telemetrySnapshot(inst) {
2393
+ const r = runTelemetry(inst, ["status", "--json"]);
2394
+ if (!r || r.status !== 0 || !r.stdout) return null;
2395
+ try { return JSON.parse(r.stdout); } catch { return null; }
2396
+ }
2397
+
2398
+ function formatPrivacyLines(snapshot) {
2399
+ if (!snapshot) return ["Privacy & access state is unavailable for this egregore."];
2400
+ const a = snapshot.access || {};
2401
+ const b = snapshot.boundary || {};
2402
+ const lines = [];
2403
+ if (a.status !== "ok") {
2404
+ lines.push("Access needs attention — identity could not be resolved.");
2405
+ return lines;
2406
+ }
2407
+ lines.push(`Member: ${a.display_name || "unknown"}`);
2408
+ lines.push(`Organization: ${a.org_name || "unknown"} — membership ${a.membership_status || "unknown"}`);
2409
+ lines.push(`Details: actor ${String(a.actor_id || "").slice(0, 18)}…`);
2410
+ lines.push("");
2411
+ const caps = a.capabilities || {};
2412
+ const capLine = ["read", "create", "update", "publish", "notify", "org_settings"]
2413
+ .map((k) => `${k.replace("_", " ")} ${caps[k] ? "yes" : "no"}`)
2414
+ .join(" · ");
2415
+ lines.push(`Authorized capabilities: ${capLine}`);
2416
+ lines.push("For publish and notify, yes means you may request the action —");
2417
+ lines.push("every delivery still needs its own explicit approval.");
2418
+ lines.push("Custom roles: not enabled in this MVP.");
2419
+ lines.push(a.admin ? "Admin content: visible (you are an organization admin)" : "Admin content: hidden on Egregore surfaces");
2420
+ lines.push("");
2421
+ lines.push("Shared memory: available to authorized organization members");
2422
+ lines.push("Per-document restrictions: not enabled (raw repository access is organization-wide)");
2423
+ lines.push("Admin-marked content: hidden in Egregore surfaces from non-admin members");
2424
+ lines.push("External publication of admin-marked content: blocked unless an authorized admin separately confirms");
2425
+ lines.push("Publications require approval; notifications require separate exact approval.");
2426
+ lines.push("");
2427
+ lines.push(`Boundary posture: ${b.posture || "standard"}${b.locked ? " · organization policy locked" : ""}`);
2428
+ lines.push(`Other Egregore instances: ${snapshot.isolation ? snapshot.isolation.foreign_instances_denied : 0} isolated (hard, no consent path)`);
2429
+ const orgRoots = (b.org_read_roots || []).length;
2430
+ const personalRoots = b.personal_read_roots || [];
2431
+ lines.push(`Organization read roots: ${orgRoots} (managed by your organization)`);
2432
+ lines.push(`Personal read roots: ${personalRoots.length ? personalRoots.join(", ") : "none"}`);
2433
+ lines.push(
2434
+ snapshot.graph_retrieval === "not-used-by-this-runtime"
2435
+ ? "Graph retrieval: not used by this Runtime"
2436
+ : "Graph retrieval: legacy configuration",
2437
+ );
2438
+ return lines;
2439
+ }
2440
+
2441
+ function telemetryEvents(inst, n = 50) {
2442
+ const r = runTelemetry(inst, ["inspect", String(n), "--json"]);
2443
+ if (!r || r.status !== 0 || !r.stdout) return [];
2444
+ try {
2445
+ const events = JSON.parse(r.stdout);
2446
+ return Array.isArray(events) ? events : [];
2447
+ } catch { return []; }
2448
+ }
2449
+
2450
+ function formatEventRow(event) {
2451
+ const at = String(event.occurred_at || "").replace("T", " ").slice(0, 16) || "????-?? ??:??";
2452
+ const metrics = event.metrics || {};
2453
+ const what = metrics.command || metrics.subcommand || metrics.action || "";
2454
+ const extras = [];
2455
+ if (metrics.duration_ms != null) extras.push(`${metrics.duration_ms}ms`);
2456
+ if (metrics.model) extras.push(String(metrics.model));
2457
+ if (metrics.result) extras.push(String(metrics.result));
2458
+ return `${at} ${pad(String(event.event_type || "event"), 12)} ${pad(String(what), 16)} ${extras.join(" · ")}`;
2459
+ }
2460
+
2461
+ function listSubdirectories(dir, showHidden) {
2462
+ try {
2463
+ return fs.readdirSync(dir, { withFileTypes: true })
2464
+ .filter((entry) => entry.isDirectory() && (showHidden || !entry.name.startsWith(".")))
2465
+ .map((entry) => entry.name)
2466
+ .sort();
2467
+ } catch { return []; }
2468
+ }
2469
+
2470
+ function formatTelemetryLines(status) {
2471
+ if (!status) return ["Telemetry state is unavailable for this egregore."];
2472
+ const lines = [];
2473
+ lines.push(`Telemetry: ${status.enabled ? "On" : "Off"}${status.reason ? ` (${status.reason})` : ""}`);
2474
+ lines.push(`Notice shown: ${status.notice_shown ? "yes" : "not yet"}`);
2475
+ lines.push(
2476
+ status.buffered_events > 0
2477
+ ? `Local events waiting: ${status.buffered_events}`
2478
+ : "No buffered events",
2479
+ );
2480
+ if (status.last_event_at) lines.push(`Last event: ${status.last_event_at}`);
2481
+ lines.push("Events are held in a local buffer and never shared automatically.");
2482
+ lines.push("Collected: command names, timings, counts, model tiers — documented metadata only.");
2483
+ lines.push("Never collected: file contents, code, prompts, document excerpts,");
2484
+ lines.push("environment values, credentials, message bodies, personal memory content.");
2485
+ lines.push("Turning telemetry on or off is a personal setting for this member only.");
2486
+ return lines;
2487
+ }
2488
+
2489
+ function upgradeStatus(inst, { fetch = false } = {}) {
2490
+ const args = ["status"]; if (fetch) args.push("--fetch");
2491
+ const r = runUpgrade(inst, args);
2492
+ if (!r || r.status !== 0 || !r.stdout) return null;
2493
+ try { return JSON.parse(r.stdout); } catch { return null; }
2494
+ }
2495
+
2496
+ function upgradeStatusAsync(inst, cb) {
2497
+ const bridge = resolveUpgradeBridge(inst);
2498
+ if (!bridge) return cb(null);
2499
+ let buf = "";
2500
+ try {
2501
+ const child = spawn("bash", [bridge.script, "status"], { cwd: inst.path, stdio: ["ignore", "pipe", "ignore"], env: { ...process.env, ...bridge.env } });
2502
+ child.stdout.on("data", (d) => { buf += d; });
2503
+ child.on("close", () => { try { cb(JSON.parse(buf)); } catch { cb(null); } });
2504
+ child.on("error", () => cb(null));
2505
+ } catch { cb(null); }
2506
+ }
2507
+
2508
+ // Exact candidate resolution: the runtime-mvp dist-tag only, validated
2509
+ // against the prerelease scheme — never latest, never a retired scheme.
2510
+ // EGREGORE_RUNTIME_MVP_CANDIDATE is the offline/test seam.
2511
+ async function fetchRuntimeMvpCandidate() {
2512
+ const seeded = process.env.EGREGORE_RUNTIME_MVP_CANDIDATE;
2513
+ if (seeded !== undefined) return RUNTIME_MVP_VERSION_RE.test(seeded) ? seeded : null;
2514
+ try {
2515
+ const url = new URL(RUNTIME_MVP_REGISTRY_URL);
2516
+ const result = await requestJson({
2517
+ hostname: url.hostname,
2518
+ port: url.port || undefined,
2519
+ path: `${url.pathname}${url.search}`,
2520
+ method: "GET",
2521
+ protocol: url.protocol,
2522
+ timeoutMs: 1500,
2523
+ unref: true,
2524
+ headers: { Accept: "application/json" },
2525
+ });
2526
+ const version = result.status === 200 && result.data && result.data.version;
2527
+ return version && RUNTIME_MVP_VERSION_RE.test(version) ? version : null;
2528
+ } catch {
2529
+ return null;
2530
+ }
2531
+ }
2532
+
2533
+ const UPGRADE_STATUS_LABEL = {
2534
+ none: "no update staged",
2535
+ pending: "staged, not started",
2536
+ preparing: "preparing…",
2537
+ interrupted: "interrupted — resume to continue",
2538
+ failed: "needs attention",
2539
+ "ready-to-activate": "ready to activate",
2540
+ active: "active",
2541
+ cancelled: "cancelled",
2542
+ "rolled-back": "rolled back",
2543
+ };
2544
+
2545
+ function upgradeSummary(st) {
2546
+ // "none" means no activation record — a first-time state, not freshness.
2547
+ if (!st || st.status === "none") return "no update staged";
2548
+ if (st.status === "active") return `${st.active_version || "?"} active`;
2549
+ return UPGRADE_STATUS_LABEL[st.status] || st.status;
2550
+ }
2551
+
2552
+ function upgradeElapsed(st) {
2553
+ const worker = (st && st.worker) || {};
2554
+ const from = worker.started_at || st?.started_at;
2555
+ if (!from) return "";
2556
+ const s = Math.max(0, Math.floor(Date.now() / 1000 - from));
2557
+ return s >= 3600 ? `${Math.floor(s / 3600)}h ${Math.floor((s % 3600) / 60)}m` : s >= 60 ? `${Math.floor(s / 60)}m ${s % 60}s` : `${s}s`;
2558
+ }
2559
+
2560
+ // Plain-language body shared by the non-interactive `settings <slug> runtime`
2561
+ // output and the interactive panel. Returns undecorated strings; callers add
2562
+ // color. Never JSON, never internal paths beyond the instance's own root.
2563
+ function formatUpgradeLines(inst, st, { candidate = "" } = {}) {
2564
+ const lines = [];
2565
+ const d = st || { status: "none" };
2566
+ lines.push(`Selected Egregore: ${inst.name || inst.slug} (${inst.slug || "?"})`);
2567
+ lines.push(`Location: ${inst.path}`);
2568
+ lines.push("");
2569
+ const activeLabel = d.active_version ? `${d.active_version} active` : "not yet activated here";
2570
+ lines.push(`Runtime: ${activeLabel}`
2571
+ + (d.candidate_version ? ` → candidate ${d.candidate_version}` : candidate ? ` → candidate ${candidate} available` : ""));
2572
+ lines.push(`Status: ${UPGRADE_STATUS_LABEL[d.status] || d.status}`);
2573
+ if (d.candidate_digest) lines.push(`Package digest: sha256 ${String(d.candidate_digest).slice(0, 16)}…`);
2574
+ if (d.failure && d.failure.message) lines.push(`Attention: ${d.failure.message}`);
2575
+ if (d.rollback_version && d.status !== "none") lines.push(`Rollback target: ${d.rollback_version}`);
2576
+ const ts = d.team_sync;
2577
+ if (ts && ts.label) lines.push(`Team memory: ${ts.label}`);
2578
+ if (d.status === "active") {
2579
+ lines.push("Organizational retrieval: Runtime/QMD");
2580
+ lines.push("Graph retrieval: not used by this Runtime version");
2581
+ } else if (d.status !== "none") {
2582
+ lines.push("Previous Runtime remains active until activation.");
2583
+ }
2584
+ if (Array.isArray(d.stages) && d.status !== "none" && d.status !== "active") {
2585
+ lines.push("");
2586
+ for (const stage of d.stages) {
2587
+ const glyph = stage.state === "done" ? "✓" : stage.state === "running" ? "◦" : stage.state === "failed" ? "✗" : "·";
2588
+ let detail = "";
2589
+ if (stage.key === "embed" || stage.key === "delta") {
2590
+ const p = d.progress || {};
2591
+ if (stage.state === "running") {
2592
+ const parts = [];
2593
+ if (p.vectors) parts.push(`${p.vectors} chunks embedded`);
2594
+ if (p.pending) parts.push(`${p.pending} pending`);
2595
+ parts.push(`${upgradeElapsed(d)} elapsed`);
2596
+ detail = ` — ${parts.join(" · ")}`;
2597
+ } else if (stage.state === "done" && stage.detail) {
2598
+ detail = ` — ${stage.detail}`;
2599
+ }
2600
+ } else if (stage.state === "failed" && stage.detail) {
2601
+ detail = ` — ${stage.detail}`;
2602
+ }
2603
+ lines.push(` ${glyph} ${stage.label}${detail}`);
2604
+ }
2605
+ }
2606
+ return lines;
2607
+ }
2608
+
2609
+ const UPGRADE_STAYS = [
2610
+ "Your team memory and organizational history",
2611
+ "Your identity and organization membership",
2612
+ "Permissions and authorization",
2613
+ "Projects, handoffs, and artifacts",
2614
+ "Connection configuration",
2615
+ "The current Runtime, until the new one is fully ready",
2616
+ ];
2617
+ const UPGRADE_CHANGES = [
2618
+ "Runtime version",
2619
+ "Organizational retrieval moves to Runtime/QMD",
2620
+ "Keyword and meaning search become owned by this Egregore",
2621
+ "Graph retrieval stops after activation (kept only for rollback)",
2622
+ "Cross-session continuity uses the new Runtime",
2623
+ "Readiness and synchronization become visible here in Settings",
2624
+ ];
2625
+
2626
+ async function cmdSettingsRuntime(inst, args) {
2627
+ const action = args[0] || "status";
2628
+ const fetchFlag = args.includes("--fetch");
2629
+ const emit = (st, extra = {}) => {
2630
+ for (const line of formatUpgradeLines(inst, st, extra)) process.stdout.write(line + "\n");
2631
+ };
2632
+ if (action === "status" || action === "review") {
2633
+ const st = upgradeStatus(inst, { fetch: fetchFlag });
2634
+ if (st === null) { process.stderr.write("Runtime update state is unavailable for this egregore.\n"); process.exitCode = 1; return; }
2635
+ emit(st);
2636
+ if (action === "review" || (st.status !== "active" && st.status !== "none")) {
2637
+ process.stdout.write("\nWhat stays:\n");
2638
+ for (const s of UPGRADE_STAYS) process.stdout.write(` · ${s}\n`);
2639
+ process.stdout.write("What changes:\n");
2640
+ for (const c of UPGRADE_CHANGES) process.stdout.write(` · ${c}\n`);
2641
+ }
2642
+ return;
2643
+ }
2644
+ if (action === "init") {
2645
+ const version = (args[1] || "").trim();
2646
+ const tarball = (args[2] || "").trim();
2647
+ if (!RUNTIME_MVP_VERSION_RE.test(version)) {
2648
+ process.stderr.write("Runtime update needs an exact candidate like 0.21.0-runtime-mvp.1.\n");
2649
+ process.exitCode = 1; return;
2650
+ }
2651
+ if (!tarball || !fs.existsSync(tarball)) {
2652
+ process.stderr.write("Runtime update installs actual packaged bytes: egregore settings <slug> runtime init <version> <candidate.tgz>\n");
2653
+ process.exitCode = 1; return;
2654
+ }
2655
+ const r = runUpgrade(inst, ["init", "--candidate", version, "--tarball", path.resolve(tarball)]);
2656
+ if (r.status !== 0) { process.stderr.write(r.stderr || "could not stage the candidate\n"); process.exitCode = r.status; return; }
2657
+ emit(upgradeStatus(inst) || null);
2658
+ return;
2659
+ }
2660
+ if (["prepare", "activate", "cancel", "rollback"].includes(action)) {
2661
+ const r = runUpgrade(inst, [action]);
2662
+ if (r.status !== 0) { process.stderr.write(r.stderr || `${action} failed\n`); process.exitCode = r.status; return; }
2663
+ const st = upgradeStatus(inst);
2664
+ emit(st);
2665
+ if (action === "activate" && st && st.status === "active") {
2666
+ process.stdout.write("\nUpdate complete. Start a session to work on the new Runtime.\n");
2667
+ }
2668
+ return;
2669
+ }
2670
+ process.stderr.write("usage: egregore settings <slug> runtime [status|review|init <version>|prepare|activate|cancel|rollback]\n");
2671
+ process.exitCode = 2;
2672
+ }
2673
+
2148
2674
  async function cmdSettings(args) {
2149
2675
  const registry = readRegistry();
2150
2676
  if (registry.length === 0) die("no egregores installed yet.\n run: npx -y create-egregore@latest");
2151
- const DOMAINS = new Set(["hosting", "workflow", "repo", "admin", "people", "dump"]);
2677
+ const DOMAINS = new Set(["hosting", "workflow", "repo", "admin", "people", "dump", "runtime", "privacy", "telemetry", "boundary"]);
2152
2678
  let rest = args.slice();
2153
2679
  let slug = "";
2154
2680
  if (rest.length && !DOMAINS.has(rest[0])) { slug = rest[0]; rest = rest.slice(1); }
2155
2681
  const inst = resolveInviteInstance(registry, slug);
2156
2682
  if (!inst) {
2157
- die(`multiple egregores installed; choose one:\n egregore settings <slug> [hosting|workflow|repo|admin|people]\n run: egregore ls`);
2683
+ die(`multiple egregores installed; choose one:\n egregore settings <slug> [runtime|hosting|workflow|repo|admin|people|privacy|telemetry]\n run: egregore ls`);
2684
+ }
2685
+ // Runtime update is Runtime-owned state, not org config — it routes to the
2686
+ // upgrade bridge instead of bin/settings.sh.
2687
+ if (rest[0] === "runtime") return cmdSettingsRuntime(inst, rest.slice(1));
2688
+ // Privacy & access: rendered plain-language snapshot backed by the Runtime
2689
+ // authorization decision — never raw JSON, secrets, or internal paths.
2690
+ if (rest[0] === "privacy") {
2691
+ for (const line of formatPrivacyLines(privacySnapshot(inst))) console.log(line);
2692
+ return;
2693
+ }
2694
+ // Telemetry: personal, opt-out, content-free. Mutations require the exact
2695
+ // --confirm because a non-interactive verb has no y/N checkpoint.
2696
+ if (rest[0] === "telemetry") {
2697
+ const verb = rest[1] || "status";
2698
+ if (verb === "status") {
2699
+ for (const line of formatTelemetryLines(telemetrySnapshot(inst))) console.log(line);
2700
+ return;
2701
+ }
2702
+ if (verb === "inspect") {
2703
+ const n = /^\d+$/.test(rest[2] || "") ? Number(rest[2]) : 20;
2704
+ const events = telemetryEvents(inst, n);
2705
+ if (!events.length) { console.log("No buffered events."); return; }
2706
+ for (const event of events) console.log(formatEventRow(event));
2707
+ return;
2708
+ }
2709
+ if (verb === "on" || verb === "off" || verb === "clear") {
2710
+ if (!rest.includes("--confirm")) {
2711
+ const what = verb === "clear"
2712
+ ? `delete ${(telemetrySnapshot(inst) || {}).buffered_events || 0} locally buffered event(s); nothing else is removed`
2713
+ : `turn telemetry ${verb} for this member only`;
2714
+ process.stderr.write(`this will ${what}.\nre-run with --confirm to proceed: egregore settings ${inst.slug} telemetry ${verb} --confirm\n`);
2715
+ process.exitCode = 1;
2716
+ return;
2717
+ }
2718
+ const r = runTelemetry(inst, [verb]);
2719
+ if (r.stdout) process.stdout.write(r.stdout);
2720
+ if (r.stderr) process.stderr.write(r.stderr);
2721
+ process.exitCode = r.status || 0;
2722
+ return;
2723
+ }
2724
+ die("usage: egregore settings <slug> telemetry [status|inspect [n]|on|off|clear] [--confirm]");
2158
2725
  }
2726
+
2159
2727
  // Interactive editing lives in the launcher (press `s`). With no verb, print
2160
2728
  // the snapshot; otherwise forward straight to the deterministic verb.
2161
2729
  if (rest.length === 0) rest = ["dump"];
@@ -2373,7 +2941,7 @@ function renderRuntimePanel({ inst, cursor, width, height }) {
2373
2941
  boxLine(`${DIM}${inst.path}${RESET}`, innerWidth, margin),
2374
2942
  boxLine(
2375
2943
  isConnectedInstance(inst)
2376
- ? `${CYAN}◆ CONNECTED${RESET} ${DIM}· hosted graph active${RESET}`
2944
+ ? `${CYAN}◆ CONNECTED${RESET} ${DIM}${connectedModeSuffix(inst.path)}${RESET}`
2377
2945
  : `${DIM}◇ LOCAL · Markdown + Git${RESET}`,
2378
2946
  innerWidth,
2379
2947
  margin,
@@ -2413,7 +2981,67 @@ const SETTINGS_DOMAIN_LABEL = { repo: "Managed repos", admin: "Admins", people:
2413
2981
  const SETTINGS_DOMAIN_NOUN = { repo: "repo name", admin: "github handle", people: "github handle" };
2414
2982
 
2415
2983
  // Same box geometry as renderRuntimePanel so pressing `s` keeps the frame put.
2416
- function renderSettingsPanel({ inst, data, cursor, view, domain, items, listCursor, mode, input, notice, width }) {
2984
+ // Runtime-update panel: renders the durable upgrade state from the Python
2985
+ // engine. Same geometry as the settings panel; plain-language body from
2986
+ // formatUpgradeLines with launcher glyph decoration only.
2987
+ function renderUpgradePanel({ inst, data, candidate, mode, notice, width }) {
2988
+ const outerWidth = Math.min(Math.max(50, width - 2), 126);
2989
+ const innerWidth = Math.max(48, outerWidth - 2);
2990
+ const margin = " ".repeat(Math.max(0, Math.floor((width - outerWidth) / 2)));
2991
+ const label = " runtime update ";
2992
+ const lines = [
2993
+ `${margin}${DIM}┌${"─".repeat(2)}${label}${"─".repeat(Math.max(0, innerWidth - label.length - 2))}┐${RESET}`,
2994
+ emptyBoxLine(innerWidth, margin),
2995
+ ];
2996
+ const body = data === undefined
2997
+ ? ["Checking this egregore's Runtime state…"]
2998
+ : data === null
2999
+ ? ["Runtime update state is unavailable for this egregore."]
3000
+ : formatUpgradeLines(inst, data, { candidate });
3001
+ for (const raw of body) {
3002
+ let line = raw;
3003
+ if (/^ ✓ /.test(raw)) line = ` ${GREEN}✓${RESET} ${raw.slice(4)}`;
3004
+ else if (/^ ◦ /.test(raw)) line = ` ${CYAN}◦${RESET} ${raw.slice(4)}`;
3005
+ else if (/^ ✗ /.test(raw)) line = ` ${RED}✗${RESET} ${raw.slice(4)}`;
3006
+ else if (/^ · /.test(raw)) line = ` ${DIM}·${RESET} ${DIM}${raw.slice(4)}${RESET}`;
3007
+ else if (/^(Location|Rollback target):/.test(raw)) line = `${DIM}${raw}${RESET}`;
3008
+ lines.push(boxLine(line, innerWidth, margin));
3009
+ }
3010
+ const status = data && data.status;
3011
+ if (mode === "confirm-prepare") {
3012
+ lines.push(emptyBoxLine(innerWidth, margin));
3013
+ lines.push(boxLine(`${BOLD}What stays${RESET}`, innerWidth, margin));
3014
+ for (const s of UPGRADE_STAYS) lines.push(boxLine(` · ${s}`, innerWidth, margin));
3015
+ lines.push(boxLine(`${BOLD}What changes${RESET}`, innerWidth, margin));
3016
+ for (const c of UPGRADE_CHANGES) lines.push(boxLine(` · ${c}`, innerWidth, margin));
3017
+ lines.push(emptyBoxLine(innerWidth, margin));
3018
+ lines.push(boxLine(`${YELLOW}Prepare the update for ${BOLD}${inst.slug}${RESET}${YELLOW} at this location only?${RESET} ${DIM}y / N${RESET}`, innerWidth, margin));
3019
+ } else if (mode === "confirm-activate") {
3020
+ lines.push(emptyBoxLine(innerWidth, margin));
3021
+ lines.push(boxLine(`${YELLOW}Activate the new Runtime for ${BOLD}${inst.slug}${RESET}${YELLOW}?${RESET} ${DIM}y / N${RESET}`, innerWidth, margin));
3022
+ } else if (mode === "confirm-rollback") {
3023
+ lines.push(emptyBoxLine(innerWidth, margin));
3024
+ lines.push(boxLine(`${YELLOW}Roll ${BOLD}${inst.slug}${RESET}${YELLOW} back to ${data?.rollback_version || "the previous Runtime"}?${RESET} ${DIM}y / N${RESET}`, innerWidth, margin));
3025
+ } else if (mode === "confirm-cancel") {
3026
+ lines.push(emptyBoxLine(innerWidth, margin));
3027
+ lines.push(boxLine(`${YELLOW}Cancel preparation? The current Runtime stays untouched.${RESET} ${DIM}y / N${RESET}`, innerWidth, margin));
3028
+ } else {
3029
+ lines.push(emptyBoxLine(innerWidth, margin));
3030
+ if (notice) lines.push(boxLine(notice, innerWidth, margin));
3031
+ const keys = [];
3032
+ if (!status || status === "none" || ["pending", "failed", "interrupted", "cancelled", "rolled-back"].includes(status)) keys.push(`${BOLD}p${RESET}${DIM} prepare${RESET}`);
3033
+ if (status === "ready-to-activate") keys.push(`${BOLD}a${RESET}${DIM} activate${RESET}`);
3034
+ if (status === "preparing") keys.push(`${BOLD}c${RESET}${DIM} cancel${RESET}`);
3035
+ if (status === "active") keys.push(`${BOLD}r${RESET}${DIM} roll back${RESET}`);
3036
+ keys.push(`${DIM}← back · q quit${RESET}`);
3037
+ lines.push(boxLine(keys.join(`${DIM} · ${RESET}`), innerWidth, margin));
3038
+ }
3039
+ lines.push(emptyBoxLine(innerWidth, margin));
3040
+ lines.push(`${margin}${DIM}└${"─".repeat(innerWidth)}┘${RESET}`);
3041
+ return lines;
3042
+ }
3043
+
3044
+ function renderSettingsPanel({ inst, data, cursor, view, domain, items, listCursor, mode, input, notice, width, upgrade }) {
2417
3045
  const title = inst.slug || inst.name || path.basename(inst.path);
2418
3046
  const outerWidth = Math.min(Math.max(50, width - 2), 126);
2419
3047
  const innerWidth = Math.max(48, outerWidth - 2);
@@ -2428,6 +3056,114 @@ function renderSettingsPanel({ inst, data, cursor, view, domain, items, listCurs
2428
3056
  emptyBoxLine(innerWidth, margin),
2429
3057
  ];
2430
3058
 
3059
+ if (view === "privacy" || view === "telemetry") {
3060
+ lines.push(boxLine(`${BOLD}${view === "privacy" ? "Privacy & access" : "Telemetry"}${RESET}`, innerWidth, margin));
3061
+ lines.push(emptyBoxLine(innerWidth, margin));
3062
+ if (mode === "shareinfo") {
3063
+ const shareLines = (data && data.shareLines) || ["share proposal unavailable"];
3064
+ lines.push(boxLine(`${BOLD}Share proposal${RESET} ${DIM}(nothing has been sent)${RESET}`, innerWidth, margin));
3065
+ lines.push(emptyBoxLine(innerWidth, margin));
3066
+ for (const line of shareLines.slice(0, 16)) {
3067
+ lines.push(boxLine(`${line.includes("--confirm") ? "" : DIM}${line}${RESET}`, innerWidth, margin));
3068
+ }
3069
+ lines.push(emptyBoxLine(innerWidth, margin));
3070
+ lines.push(boxLine(`${DIM}Sharing completes only by running the confirm command above in a terminal. ← back${RESET}`, innerWidth, margin));
3071
+ lines.push(emptyBoxLine(innerWidth, margin));
3072
+ lines.push(`${margin}${DIM}└${"─".repeat(innerWidth)}┘${RESET}`);
3073
+ return lines;
3074
+ }
3075
+ if (mode === "events") {
3076
+ const events = (data && data.telemetryEvents) || [];
3077
+ lines.push(boxLine(`${BOLD}Local events${RESET} ${DIM}(newest last · nothing here ever uploads by itself)${RESET}`, innerWidth, margin));
3078
+ lines.push(emptyBoxLine(innerWidth, margin));
3079
+ if (!events.length) {
3080
+ lines.push(boxLine(`${DIM}(no buffered events)${RESET}`, innerWidth, margin));
3081
+ } else {
3082
+ const cursorAt = Math.min(listCursor || 0, events.length - 1);
3083
+ const windowSize = 12;
3084
+ const start = Math.max(0, Math.min(cursorAt - Math.floor(windowSize / 2), events.length - windowSize));
3085
+ events.slice(start, start + windowSize).forEach((event, i) => {
3086
+ const sel = start + i === cursorAt;
3087
+ lines.push(boxLine(`${sel ? `${CYAN}❯${RESET} ` : " "}${sel ? "" : DIM}${formatEventRow(event)}${RESET}`, innerWidth, margin));
3088
+ });
3089
+ if (events.length > windowSize) lines.push(boxLine(`${DIM}${cursorAt + 1}/${events.length}${RESET}`, innerWidth, margin));
3090
+ }
3091
+ lines.push(emptyBoxLine(innerWidth, margin));
3092
+ if (notice) lines.push(boxLine(notice, innerWidth, margin));
3093
+ lines.push(boxLine(`${DIM}↑↓ scroll · ${RESET}${BOLD}e${RESET}${DIM} export to file · ${RESET}${BOLD}s${RESET}${DIM} share (two-step consent) · ← back${RESET}`, innerWidth, margin));
3094
+ lines.push(emptyBoxLine(innerWidth, margin));
3095
+ lines.push(`${margin}${DIM}└${"─".repeat(innerWidth)}┘${RESET}`);
3096
+ return lines;
3097
+ }
3098
+ if (mode === "pickdir") {
3099
+ const pick = (data && data.dirPick) || { dir: os.homedir(), entries: [], cursor: 0, hidden: false };
3100
+ lines.push(boxLine(`${BOLD}Choose a directory to allow reading${RESET}`, innerWidth, margin));
3101
+ lines.push(boxLine(`${DIM}${pick.dir}${RESET}`, innerWidth, margin));
3102
+ lines.push(emptyBoxLine(innerWidth, margin));
3103
+ if (!pick.entries.length) {
3104
+ lines.push(boxLine(`${DIM}(no subdirectories${pick.hidden ? "" : " — press . to show hidden"})${RESET}`, innerWidth, margin));
3105
+ } else {
3106
+ const windowSize = 10;
3107
+ const start = Math.max(0, Math.min(pick.cursor - Math.floor(windowSize / 2), pick.entries.length - windowSize));
3108
+ pick.entries.slice(start, start + windowSize).forEach((name, i) => {
3109
+ const sel = start + i === pick.cursor;
3110
+ lines.push(boxLine(`${sel ? `${CYAN}❯${RESET} ` : " "}${sel ? BOLD : DIM}${name}/${RESET}`, innerWidth, margin));
3111
+ });
3112
+ }
3113
+ lines.push(emptyBoxLine(innerWidth, margin));
3114
+ if (notice) lines.push(boxLine(notice, innerWidth, margin));
3115
+ lines.push(boxLine(`${DIM}↑↓ nav · → enter descend · ← up · ${RESET}${BOLD}space${RESET}${DIM} allow this directory · . hidden · esc cancel${RESET}`, innerWidth, margin));
3116
+ lines.push(emptyBoxLine(innerWidth, margin));
3117
+ lines.push(`${margin}${DIM}└${"─".repeat(innerWidth)}┘${RESET}`);
3118
+ return lines;
3119
+ }
3120
+ if (mode === "pickroot") {
3121
+ const roots = (data && data.privacySnapshot && data.privacySnapshot.boundary && data.privacySnapshot.boundary.personal_read_roots) || [];
3122
+ lines.push(boxLine(`${BOLD}Remove a personal read root${RESET}`, innerWidth, margin));
3123
+ lines.push(emptyBoxLine(innerWidth, margin));
3124
+ if (!roots.length) {
3125
+ lines.push(boxLine(`${DIM}(no personal read roots)${RESET}`, innerWidth, margin));
3126
+ } else {
3127
+ roots.forEach((root, i) => {
3128
+ const sel = i === (listCursor || 0);
3129
+ lines.push(boxLine(`${sel ? `${CYAN}❯${RESET} ` : " "}${sel ? BOLD : DIM}${root}${RESET}`, innerWidth, margin));
3130
+ });
3131
+ }
3132
+ lines.push(emptyBoxLine(innerWidth, margin));
3133
+ if (notice) lines.push(boxLine(notice, innerWidth, margin));
3134
+ lines.push(boxLine(`${DIM}↑↓ nav · enter remove · esc cancel${RESET}`, innerWidth, margin));
3135
+ lines.push(emptyBoxLine(innerWidth, margin));
3136
+ lines.push(`${margin}${DIM}└${"─".repeat(innerWidth)}┘${RESET}`);
3137
+ return lines;
3138
+ }
3139
+ const body = view === "privacy" ? formatPrivacyLines(data && data.privacySnapshot) : formatTelemetryLines(data && data.telemetrySnapshot);
3140
+ for (const line of body) {
3141
+ lines.push(line === "" ? emptyBoxLine(innerWidth, margin) : boxLine(`${line.startsWith("Access needs") || line.startsWith("Telemetry needs") ? YELLOW : ""}${line}${RESET}`, innerWidth, margin));
3142
+ }
3143
+ lines.push(emptyBoxLine(innerWidth, margin));
3144
+ if (mode === "confirm" && input) {
3145
+ lines.push(boxLine(`${YELLOW}${input}${RESET}`, innerWidth, margin));
3146
+ } else if (mode === "input") {
3147
+ lines.push(boxLine(`${BOLD}directory:${RESET} ${input}${DIM}▌${RESET}`, innerWidth, margin));
3148
+ }
3149
+ if (notice) lines.push(boxLine(notice, innerWidth, margin));
3150
+ if (view === "privacy") {
3151
+ const locked = !!(data && data.privacySnapshot && data.privacySnapshot.boundary && data.privacySnapshot.boundary.locked);
3152
+ lines.push(boxLine(
3153
+ locked
3154
+ ? `${DIM}Boundary policy is managed by your organization. ← back · q quit${RESET}`
3155
+ : `${DIM}${RESET}${BOLD}a${RESET}${DIM} add personal read root · ${RESET}${BOLD}r${RESET}${DIM} remove · ← back · q quit${RESET}`,
3156
+ innerWidth, margin,
3157
+ ));
3158
+ } else {
3159
+ const on = !!(data && data.telemetrySnapshot && data.telemetrySnapshot.enabled);
3160
+ lines.push(boxLine(`${DIM}${RESET}${BOLD}i${RESET}${DIM} view events · ${RESET}${BOLD}t${RESET}${DIM} turn ${on ? "off" : "on"} · ${RESET}${BOLD}x${RESET}${DIM} clear local buffer · ← back · q quit${RESET}`, innerWidth, margin));
3161
+ }
3162
+ lines.push(emptyBoxLine(innerWidth, margin));
3163
+ lines.push(`${margin}${DIM}└${"─".repeat(innerWidth)}┘${RESET}`);
3164
+ return lines;
3165
+ }
3166
+
2431
3167
  if (view === "list") {
2432
3168
  lines.push(boxLine(`${BOLD}${SETTINGS_DOMAIN_LABEL[domain] || domain}${RESET}`, innerWidth, margin));
2433
3169
  lines.push(emptyBoxLine(innerWidth, margin));
@@ -2450,7 +3186,19 @@ function renderSettingsPanel({ inst, data, cursor, view, domain, items, listCurs
2450
3186
  lines.push(boxLine(`${DIM}↑↓ nav · ${RESET}${BOLD}a${RESET}${DIM} add · ${RESET}${BOLD}d${RESET}${DIM} delete · ← back · q quit${RESET}`, innerWidth, margin));
2451
3187
  }
2452
3188
  } else {
3189
+ const upgradeValue = upgrade === undefined
3190
+ ? `${DIM}checking…${RESET}`
3191
+ : upgrade === null
3192
+ ? `${DIM}unavailable${RESET}`
3193
+ : upgrade.status === "ready-to-activate"
3194
+ ? `${GREEN}${upgradeSummary(upgrade)}${RESET}`
3195
+ : upgrade.status === "failed" || upgrade.status === "interrupted"
3196
+ ? `${YELLOW}${upgradeSummary(upgrade)}${RESET}`
3197
+ : `${DIM}${upgradeSummary(upgrade)}${RESET}`;
2453
3198
  const rows = [
3199
+ { name: "Runtime update", value: upgradeValue },
3200
+ { name: "Privacy & access", value: `${DIM}identity · boundary · consent${RESET}` },
3201
+ { name: "Telemetry", value: `${DIM}personal · opt-out${RESET}` },
2454
3202
  { name: "Hosted Artifacts", value: (d.hosting !== false) ? `${GREEN}ON${RESET}` : `${YELLOW}OFF${RESET}` },
2455
3203
  {
2456
3204
  name: "Git workflow",
@@ -2472,7 +3220,7 @@ function renderSettingsPanel({ inst, data, cursor, view, domain, items, listCurs
2472
3220
  });
2473
3221
  lines.push(emptyBoxLine(innerWidth, margin));
2474
3222
  if (notice) lines.push(boxLine(notice, innerWidth, margin));
2475
- lines.push(boxLine(`${DIM}↑↓ nav · ${RESET}${BOLD}enter${RESET}${DIM} ${cursor < 2 ? "toggle" : "open"} · ← back · q quit${RESET}`, innerWidth, margin));
3223
+ lines.push(boxLine(`${DIM}↑↓ nav · ${RESET}${BOLD}enter${RESET}${DIM} ${cursor === 3 || cursor === 4 ? "toggle" : "open"} · ← back · q quit${RESET}`, innerWidth, margin));
2476
3224
  }
2477
3225
 
2478
3226
  lines.push(emptyBoxLine(innerWidth, margin));
@@ -2597,6 +3345,22 @@ async function browser(args = {}) {
2597
3345
  return;
2598
3346
  }
2599
3347
 
3348
+ if (state.upgradeInst) {
3349
+ const upgradeLines = renderUpgradePanel({
3350
+ inst: state.upgradeInst,
3351
+ data: state.upgradeData,
3352
+ candidate: state.upgradeCandidate,
3353
+ mode: state.upgradeMode,
3354
+ notice: state.upgradeNotice,
3355
+ width: w,
3356
+ });
3357
+ for (const line of upgradeLines) out.push(line + ANSI.clearLine + "\n");
3358
+ const upgradeRemaining = Math.max(0, h - upgradeLines.length - 1);
3359
+ for (let i = 0; i < upgradeRemaining; i++) out.push(ANSI.clearLine + "\n");
3360
+ process.stdout.write(out.join(""));
3361
+ return;
3362
+ }
3363
+
2600
3364
  if (state.settingsInst) {
2601
3365
  const settingsLines = renderSettingsPanel({
2602
3366
  inst: state.settingsInst,
@@ -2610,6 +3374,7 @@ async function browser(args = {}) {
2610
3374
  input: state.settingsInput,
2611
3375
  notice: state.settingsNotice,
2612
3376
  width: w,
3377
+ upgrade: state.settingsUpgrade,
2613
3378
  });
2614
3379
  for (const line of settingsLines) out.push(line + ANSI.clearLine + "\n");
2615
3380
  const remainingRows = Math.max(0, h - settingsLines.length - 1);
@@ -2736,7 +3501,7 @@ async function browser(args = {}) {
2736
3501
  : ` ${label("path")} ${truncate(selected.path, valW)}`);
2737
3502
  if (pv.org) previewLines.push(` ${label("org")} ${truncate(`${pv.org} ${DIM}(${pv.githubOrg || "?"})${RESET}`, valW)}`);
2738
3503
  previewLines.push(pv.connected
2739
- ? ` ${label("mode")} ${CYAN}◆ CONNECTED${RESET} ${DIM}· hosted graph active${RESET}`
3504
+ ? ` ${label("mode")} ${CYAN}◆ CONNECTED${RESET} ${DIM}${connectedModeSuffix(selected.path)}${RESET}`
2740
3505
  : ` ${label("mode")} ${DIM}◇ LOCAL · Markdown + Git${RESET}`);
2741
3506
  if (pv.branch) previewLines.push(` ${label("branch")} ${BOLD}${pv.branch}${RESET}`);
2742
3507
  if (pv.lastCommit) previewLines.push(` ${label("last")} ${DIM}${truncate(pv.lastCommit, valW)}${RESET}`);
@@ -2837,9 +3602,27 @@ async function browser(args = {}) {
2837
3602
  const onResize = () => render(); // render() itself no-ops once done is set
2838
3603
  let onSigint;
2839
3604
  let onSigterm;
3605
+ const stopUpgradePoll = () => {
3606
+ if (state.upgradePoll) { clearInterval(state.upgradePoll); state.upgradePoll = null; }
3607
+ };
3608
+ const refreshUpgrade = () => {
3609
+ if (!state.upgradeInst) return;
3610
+ upgradeStatusAsync(state.upgradeInst, (st) => {
3611
+ if (!state.upgradeInst) return;
3612
+ state.upgradeData = st;
3613
+ if (st && st.status === "preparing") {
3614
+ if (!state.upgradePoll) state.upgradePoll = setInterval(refreshUpgrade, 2500);
3615
+ } else {
3616
+ stopUpgradePoll();
3617
+ }
3618
+ render();
3619
+ });
3620
+ };
3621
+
2840
3622
  const cleanup = () => {
2841
3623
  if (done) return;
2842
3624
  done = true;
3625
+ stopUpgradePoll();
2843
3626
  process.stdin.setRawMode(false);
2844
3627
  process.stdin.pause();
2845
3628
  process.stdin.removeAllListeners("keypress");
@@ -2893,11 +3676,195 @@ async function browser(args = {}) {
2893
3676
  if (!key) return;
2894
3677
  if (isMouseEvent(chunk, key)) return; // drop scroll-wheel / mouse escape sequences
2895
3678
 
3679
+ if (state.upgradeInst) {
3680
+ if (key.ctrl && key.name === "c") { cleanup(); process.exit(130); }
3681
+ if (key.name === "q") { cleanup(); resolve(); return; }
3682
+ if (state.upgradeMode && state.upgradeMode.startsWith("confirm-")) {
3683
+ const mode = state.upgradeMode;
3684
+ state.upgradeMode = "view";
3685
+ if (chunk === "y" || chunk === "Y") {
3686
+ const inst = state.upgradeInst;
3687
+ if (mode === "confirm-prepare") {
3688
+ const r = runUpgrade(inst, ["prepare"]);
3689
+ state.upgradeNotice = r.status === 0
3690
+ ? `${DIM}Preparing in the background — safe to leave this screen.${RESET}`
3691
+ : `${YELLOW}${(r.stderr || "prepare failed").trim().split("\n")[0]}${RESET}`;
3692
+ } else if (mode === "confirm-activate") {
3693
+ const r = runUpgrade(inst, ["activate"]);
3694
+ state.upgradeNotice = r.status === 0
3695
+ ? `${GREEN}Update complete. Start a session to work on the new Runtime.${RESET}`
3696
+ : `${YELLOW}${(r.stderr || "activation refused — a gate has not passed").trim().split("\n")[0]}${RESET}`;
3697
+ } else if (mode === "confirm-rollback") {
3698
+ const r = runUpgrade(inst, ["rollback"]);
3699
+ state.upgradeNotice = r.status === 0
3700
+ ? `${DIM}Rolled back. The previous Runtime is active again.${RESET}`
3701
+ : `${YELLOW}${(r.stderr || "rollback failed").trim().split("\n")[0]}${RESET}`;
3702
+ } else if (mode === "confirm-cancel") {
3703
+ runUpgrade(inst, ["cancel"]);
3704
+ state.upgradeNotice = `${DIM}Preparation cancelled. The current Runtime was never touched.${RESET}`;
3705
+ }
3706
+ refreshUpgrade();
3707
+ }
3708
+ render(); return;
3709
+ }
3710
+ if (key.name === "escape" || key.name === "left") {
3711
+ stopUpgradePoll();
3712
+ state.settingsUpgrade = state.upgradeData;
3713
+ state.upgradeInst = null; state.upgradeNotice = "";
3714
+ render(); return;
3715
+ }
3716
+ const status = state.upgradeData && state.upgradeData.status;
3717
+ if (chunk === "p" && (!status || ["none", "pending", "failed", "interrupted", "cancelled", "rolled-back"].includes(status))) {
3718
+ const staged = state.upgradeData
3719
+ && !["none", "cancelled", "rolled-back"].includes(status)
3720
+ && state.upgradeData.candidate_version;
3721
+ if (!staged) {
3722
+ state.upgradeNotice = `${DIM}Stage a candidate package first: egregore settings ${state.upgradeInst.slug} runtime init <version> <candidate.tgz>${RESET}`;
3723
+ render(); return;
3724
+ }
3725
+ state.upgradeMode = "confirm-prepare"; render(); return;
3726
+ }
3727
+ if (chunk === "a" && status === "ready-to-activate") { state.upgradeMode = "confirm-activate"; render(); return; }
3728
+ if (chunk === "c" && status === "preparing") { state.upgradeMode = "confirm-cancel"; render(); return; }
3729
+ if (chunk === "r" && status === "active") { state.upgradeMode = "confirm-rollback"; render(); return; }
3730
+ return;
3731
+ }
3732
+
2896
3733
  if (state.settingsInst) {
2897
3734
  const S = state;
2898
3735
  if (key.ctrl && key.name === "c") { cleanup(); process.exit(130); }
2899
3736
  if (key.name === "q") { cleanup(); resolve(); return; }
2900
3737
 
3738
+ if (S.settingsView === "privacy" || S.settingsView === "telemetry") {
3739
+ const backToMenu = () => {
3740
+ S.settingsView = "menu"; S.settingsMode = "browse"; S.settingsInput = ""; S.settingsNotice = ""; render();
3741
+ };
3742
+ const refreshPanel = () => {
3743
+ if (S.settingsView === "privacy") S.settingsData = { ...(S.settingsData || {}), privacySnapshot: privacySnapshot(S.settingsInst) };
3744
+ else S.settingsData = { ...(S.settingsData || {}), telemetrySnapshot: telemetrySnapshot(S.settingsInst) };
3745
+ };
3746
+ if (S.settingsMode === "events") {
3747
+ const events = (S.settingsData && S.settingsData.telemetryEvents) || [];
3748
+ if (key.name === "escape" || key.name === "left") { S.settingsMode = "browse"; S.settingsListCursor = 0; S.settingsNotice = ""; render(); return; }
3749
+ if (key.name === "up" || key.name === "k") { S.settingsListCursor = Math.max(0, (S.settingsListCursor || 0) - 1); render(); return; }
3750
+ if (key.name === "down" || key.name === "j") { S.settingsListCursor = Math.min(Math.max(0, events.length - 1), (S.settingsListCursor || 0) + 1); render(); return; }
3751
+ if (chunk === "e") {
3752
+ const stamp = new Date().toISOString().slice(0, 10);
3753
+ S.settingsPendingDir = path.join(os.homedir(), "Desktop", `egregore-telemetry-${S.settingsInst.slug}-${stamp}.jsonl`);
3754
+ S.settingsAction = "tele-export";
3755
+ S.settingsMode = "confirm";
3756
+ S.settingsInput = `Export ${events.length} local event(s) to ${S.settingsPendingDir}? [y/N]`;
3757
+ render(); return;
3758
+ }
3759
+ if (chunk === "s") {
3760
+ const r = runTelemetry(S.settingsInst, ["share"]);
3761
+ const shareLines = ((r && (r.stdout || r.stderr)) || "share preview unavailable").split("\n").filter(Boolean);
3762
+ S.settingsData = { ...(S.settingsData || {}), shareLines };
3763
+ S.settingsMode = "shareinfo"; S.settingsNotice = "";
3764
+ render(); return;
3765
+ }
3766
+ return;
3767
+ }
3768
+ if (S.settingsMode === "shareinfo") {
3769
+ if (key.name === "escape" || key.name === "left") { S.settingsMode = "events"; render(); return; }
3770
+ return;
3771
+ }
3772
+ if (S.settingsMode === "pickdir") {
3773
+ const pick = S.settingsData.dirPick;
3774
+ const reload = () => { pick.entries = listSubdirectories(pick.dir, pick.hidden); pick.cursor = Math.min(pick.cursor, Math.max(0, pick.entries.length - 1)); };
3775
+ if (key.name === "escape") { S.settingsMode = "browse"; S.settingsNotice = ""; render(); return; }
3776
+ if (key.name === "up" || key.name === "k") { pick.cursor = Math.max(0, pick.cursor - 1); render(); return; }
3777
+ if (key.name === "down" || key.name === "j") { pick.cursor = Math.min(Math.max(0, pick.entries.length - 1), pick.cursor + 1); render(); return; }
3778
+ if (key.name === "right" || key.name === "return") {
3779
+ if (pick.entries.length) { pick.dir = path.join(pick.dir, pick.entries[pick.cursor]); pick.cursor = 0; reload(); }
3780
+ render(); return;
3781
+ }
3782
+ if (key.name === "left") {
3783
+ const parent = path.dirname(pick.dir);
3784
+ if (parent !== pick.dir) { pick.dir = parent; pick.cursor = 0; reload(); }
3785
+ render(); return;
3786
+ }
3787
+ if (chunk === ".") { pick.hidden = !pick.hidden; reload(); render(); return; }
3788
+ if (chunk === " ") {
3789
+ S.settingsPendingDir = pick.dir;
3790
+ S.settingsAction = "add";
3791
+ S.settingsMode = "confirm";
3792
+ S.settingsInput = `Allow reading ${pick.dir} for this member? [y/N]`;
3793
+ render(); return;
3794
+ }
3795
+ return;
3796
+ }
3797
+ if (S.settingsMode === "pickroot") {
3798
+ const roots = (S.settingsData.privacySnapshot && S.settingsData.privacySnapshot.boundary && S.settingsData.privacySnapshot.boundary.personal_read_roots) || [];
3799
+ if (key.name === "escape" || key.name === "left") { S.settingsMode = "browse"; S.settingsListCursor = 0; render(); return; }
3800
+ if (key.name === "up" || key.name === "k") { S.settingsListCursor = Math.max(0, (S.settingsListCursor || 0) - 1); render(); return; }
3801
+ if (key.name === "down" || key.name === "j") { S.settingsListCursor = Math.min(Math.max(0, roots.length - 1), (S.settingsListCursor || 0) + 1); render(); return; }
3802
+ if (key.name === "return" && roots.length) {
3803
+ S.settingsPendingDir = roots[Math.min(S.settingsListCursor || 0, roots.length - 1)];
3804
+ S.settingsAction = "remove";
3805
+ S.settingsMode = "confirm";
3806
+ S.settingsInput = `Stop allowing reads of ${S.settingsPendingDir}? [y/N]`;
3807
+ render(); return;
3808
+ }
3809
+ return;
3810
+ }
3811
+ if (S.settingsMode === "confirm") {
3812
+ if (chunk === "y" || chunk === "Y") {
3813
+ let r = null;
3814
+ if (S.settingsAction === "add" || S.settingsAction === "remove") {
3815
+ r = runSettings(S.settingsInst, ["boundary", S.settingsAction, S.settingsPendingDir]);
3816
+ } else if (S.settingsAction === "tele-export") {
3817
+ r = runTelemetry(S.settingsInst, ["export", S.settingsPendingDir]);
3818
+ } else if (S.settingsAction === "tele-on") {
3819
+ r = runTelemetry(S.settingsInst, ["on"]);
3820
+ } else if (S.settingsAction === "tele-off") {
3821
+ r = runTelemetry(S.settingsInst, ["off"]);
3822
+ } else if (S.settingsAction === "tele-clear") {
3823
+ r = runTelemetry(S.settingsInst, ["clear"]);
3824
+ }
3825
+ S.settingsNotice = r && r.status === 0
3826
+ ? `${GREEN}done${RESET}`
3827
+ : `${YELLOW}${((r && (r.stderr || r.stdout)) || "action failed").split("\n")[0]}${RESET}`;
3828
+ S.settingsMode = "browse"; S.settingsInput = ""; S.settingsPendingDir = "";
3829
+ refreshPanel(); render(); return;
3830
+ }
3831
+ S.settingsMode = "browse"; S.settingsInput = ""; S.settingsPendingDir = ""; S.settingsNotice = ""; render(); return;
3832
+ }
3833
+ if (key.name === "escape" || key.name === "left") { backToMenu(); return; }
3834
+ if (S.settingsView === "privacy") {
3835
+ const locked = !!(S.settingsData && S.settingsData.privacySnapshot && S.settingsData.privacySnapshot.boundary && S.settingsData.privacySnapshot.boundary.locked);
3836
+ if (!locked && chunk === "a") {
3837
+ const dir = os.homedir();
3838
+ S.settingsData = { ...(S.settingsData || {}), dirPick: { dir, entries: listSubdirectories(dir, false), cursor: 0, hidden: false } };
3839
+ S.settingsMode = "pickdir"; S.settingsNotice = ""; render(); return;
3840
+ }
3841
+ if (!locked && chunk === "r") {
3842
+ S.settingsMode = "pickroot"; S.settingsListCursor = 0; S.settingsNotice = ""; render(); return;
3843
+ }
3844
+ return;
3845
+ }
3846
+ // telemetry view
3847
+ if (chunk === "i") {
3848
+ S.settingsData = { ...(S.settingsData || {}), telemetryEvents: telemetryEvents(S.settingsInst, 100) };
3849
+ S.settingsMode = "events"; S.settingsListCursor = 0; S.settingsNotice = ""; render(); return;
3850
+ }
3851
+ if (chunk === "t") {
3852
+ const on = !!(S.settingsData && S.settingsData.telemetrySnapshot && S.settingsData.telemetrySnapshot.enabled);
3853
+ S.settingsAction = on ? "tele-off" : "tele-on";
3854
+ S.settingsMode = "confirm";
3855
+ S.settingsInput = `Turn telemetry ${on ? "off" : "on"} for this member only? [y/N]`;
3856
+ render(); return;
3857
+ }
3858
+ if (chunk === "x") {
3859
+ const count = (S.settingsData && S.settingsData.telemetrySnapshot && S.settingsData.telemetrySnapshot.buffered_events) || 0;
3860
+ S.settingsAction = "tele-clear";
3861
+ S.settingsMode = "confirm";
3862
+ S.settingsInput = `Delete ${count} locally buffered telemetry event(s)? Nothing else is removed. [y/N]`;
3863
+ render(); return;
3864
+ }
3865
+ return;
3866
+ }
3867
+
2901
3868
  if (S.settingsView === "list") {
2902
3869
  if (S.settingsMode === "add") {
2903
3870
  if (key.name === "escape") { S.settingsMode = "browse"; S.settingsInput = ""; render(); return; }
@@ -2930,19 +3897,40 @@ async function browser(args = {}) {
2930
3897
 
2931
3898
  // menu view
2932
3899
  if (key.name === "escape" || key.name === "left") { S.settingsInst = null; S.settingsNotice = ""; render(); return; }
2933
- if (key.name === "up" || key.name === "k") { S.settingsCursor = (S.settingsCursor - 1 + 5) % 5; render(); return; }
2934
- if (key.name === "down" || key.name === "j") { S.settingsCursor = (S.settingsCursor + 1) % 5; render(); return; }
3900
+ if (key.name === "up" || key.name === "k") { S.settingsCursor = (S.settingsCursor - 1 + 8) % 8; render(); return; }
3901
+ if (key.name === "down" || key.name === "j") { S.settingsCursor = (S.settingsCursor + 1) % 8; render(); return; }
2935
3902
  if (key.name === "return" || chunk === " ") {
2936
3903
  if (S.settingsCursor === 0) {
3904
+ state.upgradeInst = S.settingsInst;
3905
+ state.upgradeData = S.settingsUpgrade;
3906
+ state.upgradeMode = "view";
3907
+ state.upgradeNotice = "";
3908
+ refreshUpgrade();
3909
+ if (!state.upgradeCandidate) {
3910
+ fetchRuntimeMvpCandidate().then((v) => {
3911
+ state.upgradeCandidate = v || "";
3912
+ if (state.upgradeInst) render();
3913
+ });
3914
+ }
3915
+ render();
3916
+ } else if (S.settingsCursor === 1) {
3917
+ S.settingsView = "privacy"; S.settingsMode = "browse"; S.settingsInput = ""; S.settingsNotice = "";
3918
+ S.settingsData = { ...(S.settingsData || {}), privacySnapshot: privacySnapshot(S.settingsInst) };
3919
+ render();
3920
+ } else if (S.settingsCursor === 2) {
3921
+ S.settingsView = "telemetry"; S.settingsMode = "browse"; S.settingsInput = ""; S.settingsNotice = "";
3922
+ S.settingsData = { ...(S.settingsData || {}), telemetrySnapshot: telemetrySnapshot(S.settingsInst) };
3923
+ render();
3924
+ } else if (S.settingsCursor === 3) {
2937
3925
  const on = (S.settingsData && S.settingsData.hosting) !== false;
2938
3926
  settingsRunVerb(["hosting", on ? "off" : "on"]);
2939
3927
  render();
2940
- } else if (S.settingsCursor === 1) {
3928
+ } else if (S.settingsCursor === 4) {
2941
3929
  const simple = (S.settingsData && S.settingsData.workflow) === "simple";
2942
3930
  settingsRunVerb(["workflow", simple ? "staged" : "simple"]);
2943
3931
  render();
2944
3932
  } else {
2945
- const domain = S.settingsCursor === 2 ? "repo" : S.settingsCursor === 3 ? "admin" : "people";
3933
+ const domain = S.settingsCursor === 5 ? "repo" : S.settingsCursor === 6 ? "admin" : "people";
2946
3934
  S.settingsView = "list"; S.settingsDomain = domain;
2947
3935
  S.settingsItems = settingsItemsFor(S.settingsData, domain);
2948
3936
  S.settingsListCursor = 0; S.settingsMode = "browse"; S.settingsNotice = "";
@@ -3201,6 +4189,11 @@ async function browser(args = {}) {
3201
4189
  state.settingsData = settingsDump(pick) || { hosting: true, workflow: "staged", base_branch: "develop", repos: [], admins: [], people: [] };
3202
4190
  state.settingsCursor = 0;
3203
4191
  state.settingsView = "menu";
4192
+ state.settingsUpgrade = undefined;
4193
+ upgradeStatusAsync(pick, (st) => {
4194
+ state.settingsUpgrade = st;
4195
+ if (state.settingsInst === pick && !state.upgradeInst) render();
4196
+ });
3204
4197
  state.settingsNotice = "";
3205
4198
  render();
3206
4199
  return;
@@ -3328,6 +4321,8 @@ function main() {
3328
4321
  if (cmd === "ls" || cmd === "list") return cmdLs();
3329
4322
  if (cmd === "use") return cmdUse(args[1]);
3330
4323
  if (cmd === "connect") return cmdConnect(args.slice(1));
4324
+ if (cmd === "telemetry") return cmdTelemetry(args.slice(1));
4325
+ if (cmd === "runtime") return cmdRuntime(args.slice(1));
3331
4326
  if (cmd === "/invite" || cmd === "invite") return cmdInvite(args.slice(1));
3332
4327
  if (cmd === "settings" || cmd === "config") return cmdSettings(args.slice(1));
3333
4328
  if (cmd === "prune") return cmdPruneNonInteractive(args[1] === "--dry-run" || args[1] === "-n");
@@ -3338,6 +4333,8 @@ function main() {
3338
4333
  if (cmd.startsWith("-")) die(`unknown flag: ${cmd}\n run: egregore help`);
3339
4334
 
3340
4335
  const registry = readRegistry();
4336
+ const worktree = currentWorktreeInstance(registry);
4337
+ if (!cmd && worktree) return launch(worktree, runtimeOverride);
3341
4338
  if (registry.length === 0) {
3342
4339
  if (canPrompt()) return setupNewEgregore(runtimeOverride);
3343
4340
  die("no egregores installed yet.\n run: npx -y create-egregore@latest");