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

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 +1011 -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.1
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.1";
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,440 @@ 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
+ if (!st || st.status === "none") return "up to date";
2547
+ if (st.status === "active") return `${st.active_version || "?"} active`;
2548
+ return UPGRADE_STATUS_LABEL[st.status] || st.status;
2549
+ }
2550
+
2551
+ function upgradeElapsed(st) {
2552
+ const worker = (st && st.worker) || {};
2553
+ const from = worker.started_at || st?.started_at;
2554
+ if (!from) return "";
2555
+ const s = Math.max(0, Math.floor(Date.now() / 1000 - from));
2556
+ 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`;
2557
+ }
2558
+
2559
+ // Plain-language body shared by the non-interactive `settings <slug> runtime`
2560
+ // output and the interactive panel. Returns undecorated strings; callers add
2561
+ // color. Never JSON, never internal paths beyond the instance's own root.
2562
+ function formatUpgradeLines(inst, st, { candidate = "" } = {}) {
2563
+ const lines = [];
2564
+ const d = st || { status: "none" };
2565
+ lines.push(`Selected Egregore: ${inst.name || inst.slug} (${inst.slug || "?"})`);
2566
+ lines.push(`Location: ${inst.path}`);
2567
+ lines.push("");
2568
+ lines.push(`Runtime: ${d.active_version || "unknown"} active`
2569
+ + (d.candidate_version ? ` → candidate ${d.candidate_version}` : candidate ? ` → candidate ${candidate} available` : ""));
2570
+ lines.push(`Status: ${UPGRADE_STATUS_LABEL[d.status] || d.status}`);
2571
+ if (d.candidate_digest) lines.push(`Package digest: sha256 ${String(d.candidate_digest).slice(0, 16)}…`);
2572
+ if (d.failure && d.failure.message) lines.push(`Attention: ${d.failure.message}`);
2573
+ if (d.rollback_version && d.status !== "none") lines.push(`Rollback target: ${d.rollback_version}`);
2574
+ const ts = d.team_sync;
2575
+ if (ts && ts.label) lines.push(`Team memory: ${ts.label}`);
2576
+ if (d.status === "active") {
2577
+ lines.push("Organizational retrieval: Runtime/QMD");
2578
+ lines.push("Graph retrieval: not used by this Runtime version");
2579
+ } else if (d.status !== "none") {
2580
+ lines.push("Previous Runtime remains active until activation.");
2581
+ }
2582
+ if (Array.isArray(d.stages) && d.status !== "none" && d.status !== "active") {
2583
+ lines.push("");
2584
+ for (const stage of d.stages) {
2585
+ const glyph = stage.state === "done" ? "✓" : stage.state === "running" ? "◦" : stage.state === "failed" ? "✗" : "·";
2586
+ let detail = "";
2587
+ if (stage.key === "embed" || stage.key === "delta") {
2588
+ const p = d.progress || {};
2589
+ if (stage.state === "running") {
2590
+ const parts = [];
2591
+ if (p.vectors) parts.push(`${p.vectors} chunks embedded`);
2592
+ if (p.pending) parts.push(`${p.pending} pending`);
2593
+ parts.push(`${upgradeElapsed(d)} elapsed`);
2594
+ detail = ` — ${parts.join(" · ")}`;
2595
+ } else if (stage.state === "done" && stage.detail) {
2596
+ detail = ` — ${stage.detail}`;
2597
+ }
2598
+ } else if (stage.state === "failed" && stage.detail) {
2599
+ detail = ` — ${stage.detail}`;
2600
+ }
2601
+ lines.push(` ${glyph} ${stage.label}${detail}`);
2602
+ }
2603
+ }
2604
+ return lines;
2605
+ }
2606
+
2607
+ const UPGRADE_STAYS = [
2608
+ "Your team memory and organizational history",
2609
+ "Your identity and organization membership",
2610
+ "Permissions and authorization",
2611
+ "Projects, handoffs, and artifacts",
2612
+ "Connection configuration",
2613
+ "The current Runtime, until the new one is fully ready",
2614
+ ];
2615
+ const UPGRADE_CHANGES = [
2616
+ "Runtime version",
2617
+ "Organizational retrieval moves to Runtime/QMD",
2618
+ "Keyword and meaning search become owned by this Egregore",
2619
+ "Graph retrieval stops after activation (kept only for rollback)",
2620
+ "Cross-session continuity uses the new Runtime",
2621
+ "Readiness and synchronization become visible here in Settings",
2622
+ ];
2623
+
2624
+ async function cmdSettingsRuntime(inst, args) {
2625
+ const action = args[0] || "status";
2626
+ const fetchFlag = args.includes("--fetch");
2627
+ const emit = (st, extra = {}) => {
2628
+ for (const line of formatUpgradeLines(inst, st, extra)) process.stdout.write(line + "\n");
2629
+ };
2630
+ if (action === "status" || action === "review") {
2631
+ const st = upgradeStatus(inst, { fetch: fetchFlag });
2632
+ if (st === null) { process.stderr.write("Runtime update state is unavailable for this egregore.\n"); process.exitCode = 1; return; }
2633
+ emit(st);
2634
+ if (action === "review" || (st.status !== "active" && st.status !== "none")) {
2635
+ process.stdout.write("\nWhat stays:\n");
2636
+ for (const s of UPGRADE_STAYS) process.stdout.write(` · ${s}\n`);
2637
+ process.stdout.write("What changes:\n");
2638
+ for (const c of UPGRADE_CHANGES) process.stdout.write(` · ${c}\n`);
2639
+ }
2640
+ return;
2641
+ }
2642
+ if (action === "init") {
2643
+ const version = (args[1] || "").trim();
2644
+ const tarball = (args[2] || "").trim();
2645
+ if (!RUNTIME_MVP_VERSION_RE.test(version)) {
2646
+ process.stderr.write("Runtime update needs an exact candidate like 0.21.0-runtime-mvp.1.\n");
2647
+ process.exitCode = 1; return;
2648
+ }
2649
+ if (!tarball || !fs.existsSync(tarball)) {
2650
+ process.stderr.write("Runtime update installs actual packaged bytes: egregore settings <slug> runtime init <version> <candidate.tgz>\n");
2651
+ process.exitCode = 1; return;
2652
+ }
2653
+ const r = runUpgrade(inst, ["init", "--candidate", version, "--tarball", path.resolve(tarball)]);
2654
+ if (r.status !== 0) { process.stderr.write(r.stderr || "could not stage the candidate\n"); process.exitCode = r.status; return; }
2655
+ emit(upgradeStatus(inst) || null);
2656
+ return;
2657
+ }
2658
+ if (["prepare", "activate", "cancel", "rollback"].includes(action)) {
2659
+ const r = runUpgrade(inst, [action]);
2660
+ if (r.status !== 0) { process.stderr.write(r.stderr || `${action} failed\n`); process.exitCode = r.status; return; }
2661
+ const st = upgradeStatus(inst);
2662
+ emit(st);
2663
+ if (action === "activate" && st && st.status === "active") {
2664
+ process.stdout.write("\nUpdate complete. Start a session to work on the new Runtime.\n");
2665
+ }
2666
+ return;
2667
+ }
2668
+ process.stderr.write("usage: egregore settings <slug> runtime [status|review|init <version>|prepare|activate|cancel|rollback]\n");
2669
+ process.exitCode = 2;
2670
+ }
2671
+
2148
2672
  async function cmdSettings(args) {
2149
2673
  const registry = readRegistry();
2150
2674
  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"]);
2675
+ const DOMAINS = new Set(["hosting", "workflow", "repo", "admin", "people", "dump", "runtime", "privacy", "telemetry", "boundary"]);
2152
2676
  let rest = args.slice();
2153
2677
  let slug = "";
2154
2678
  if (rest.length && !DOMAINS.has(rest[0])) { slug = rest[0]; rest = rest.slice(1); }
2155
2679
  const inst = resolveInviteInstance(registry, slug);
2156
2680
  if (!inst) {
2157
- die(`multiple egregores installed; choose one:\n egregore settings <slug> [hosting|workflow|repo|admin|people]\n run: egregore ls`);
2681
+ die(`multiple egregores installed; choose one:\n egregore settings <slug> [runtime|hosting|workflow|repo|admin|people|privacy|telemetry]\n run: egregore ls`);
2682
+ }
2683
+ // Runtime update is Runtime-owned state, not org config — it routes to the
2684
+ // upgrade bridge instead of bin/settings.sh.
2685
+ if (rest[0] === "runtime") return cmdSettingsRuntime(inst, rest.slice(1));
2686
+ // Privacy & access: rendered plain-language snapshot backed by the Runtime
2687
+ // authorization decision — never raw JSON, secrets, or internal paths.
2688
+ if (rest[0] === "privacy") {
2689
+ for (const line of formatPrivacyLines(privacySnapshot(inst))) console.log(line);
2690
+ return;
2691
+ }
2692
+ // Telemetry: personal, opt-out, content-free. Mutations require the exact
2693
+ // --confirm because a non-interactive verb has no y/N checkpoint.
2694
+ if (rest[0] === "telemetry") {
2695
+ const verb = rest[1] || "status";
2696
+ if (verb === "status") {
2697
+ for (const line of formatTelemetryLines(telemetrySnapshot(inst))) console.log(line);
2698
+ return;
2699
+ }
2700
+ if (verb === "inspect") {
2701
+ const n = /^\d+$/.test(rest[2] || "") ? Number(rest[2]) : 20;
2702
+ const events = telemetryEvents(inst, n);
2703
+ if (!events.length) { console.log("No buffered events."); return; }
2704
+ for (const event of events) console.log(formatEventRow(event));
2705
+ return;
2706
+ }
2707
+ if (verb === "on" || verb === "off" || verb === "clear") {
2708
+ if (!rest.includes("--confirm")) {
2709
+ const what = verb === "clear"
2710
+ ? `delete ${(telemetrySnapshot(inst) || {}).buffered_events || 0} locally buffered event(s); nothing else is removed`
2711
+ : `turn telemetry ${verb} for this member only`;
2712
+ process.stderr.write(`this will ${what}.\nre-run with --confirm to proceed: egregore settings ${inst.slug} telemetry ${verb} --confirm\n`);
2713
+ process.exitCode = 1;
2714
+ return;
2715
+ }
2716
+ const r = runTelemetry(inst, [verb]);
2717
+ if (r.stdout) process.stdout.write(r.stdout);
2718
+ if (r.stderr) process.stderr.write(r.stderr);
2719
+ process.exitCode = r.status || 0;
2720
+ return;
2721
+ }
2722
+ die("usage: egregore settings <slug> telemetry [status|inspect [n]|on|off|clear] [--confirm]");
2158
2723
  }
2724
+
2159
2725
  // Interactive editing lives in the launcher (press `s`). With no verb, print
2160
2726
  // the snapshot; otherwise forward straight to the deterministic verb.
2161
2727
  if (rest.length === 0) rest = ["dump"];
@@ -2373,7 +2939,7 @@ function renderRuntimePanel({ inst, cursor, width, height }) {
2373
2939
  boxLine(`${DIM}${inst.path}${RESET}`, innerWidth, margin),
2374
2940
  boxLine(
2375
2941
  isConnectedInstance(inst)
2376
- ? `${CYAN}◆ CONNECTED${RESET} ${DIM}· hosted graph active${RESET}`
2942
+ ? `${CYAN}◆ CONNECTED${RESET} ${DIM}${connectedModeSuffix(inst.path)}${RESET}`
2377
2943
  : `${DIM}◇ LOCAL · Markdown + Git${RESET}`,
2378
2944
  innerWidth,
2379
2945
  margin,
@@ -2413,7 +2979,67 @@ const SETTINGS_DOMAIN_LABEL = { repo: "Managed repos", admin: "Admins", people:
2413
2979
  const SETTINGS_DOMAIN_NOUN = { repo: "repo name", admin: "github handle", people: "github handle" };
2414
2980
 
2415
2981
  // 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 }) {
2982
+ // Runtime-update panel: renders the durable upgrade state from the Python
2983
+ // engine. Same geometry as the settings panel; plain-language body from
2984
+ // formatUpgradeLines with launcher glyph decoration only.
2985
+ function renderUpgradePanel({ inst, data, candidate, mode, notice, width }) {
2986
+ const outerWidth = Math.min(Math.max(50, width - 2), 126);
2987
+ const innerWidth = Math.max(48, outerWidth - 2);
2988
+ const margin = " ".repeat(Math.max(0, Math.floor((width - outerWidth) / 2)));
2989
+ const label = " runtime update ";
2990
+ const lines = [
2991
+ `${margin}${DIM}┌${"─".repeat(2)}${label}${"─".repeat(Math.max(0, innerWidth - label.length - 2))}┐${RESET}`,
2992
+ emptyBoxLine(innerWidth, margin),
2993
+ ];
2994
+ const body = data === undefined
2995
+ ? ["Checking this egregore's Runtime state…"]
2996
+ : data === null
2997
+ ? ["Runtime update state is unavailable for this egregore."]
2998
+ : formatUpgradeLines(inst, data, { candidate });
2999
+ for (const raw of body) {
3000
+ let line = raw;
3001
+ if (/^ ✓ /.test(raw)) line = ` ${GREEN}✓${RESET} ${raw.slice(4)}`;
3002
+ else if (/^ ◦ /.test(raw)) line = ` ${CYAN}◦${RESET} ${raw.slice(4)}`;
3003
+ else if (/^ ✗ /.test(raw)) line = ` ${RED}✗${RESET} ${raw.slice(4)}`;
3004
+ else if (/^ · /.test(raw)) line = ` ${DIM}·${RESET} ${DIM}${raw.slice(4)}${RESET}`;
3005
+ else if (/^(Location|Rollback target):/.test(raw)) line = `${DIM}${raw}${RESET}`;
3006
+ lines.push(boxLine(line, innerWidth, margin));
3007
+ }
3008
+ const status = data && data.status;
3009
+ if (mode === "confirm-prepare") {
3010
+ lines.push(emptyBoxLine(innerWidth, margin));
3011
+ lines.push(boxLine(`${BOLD}What stays${RESET}`, innerWidth, margin));
3012
+ for (const s of UPGRADE_STAYS) lines.push(boxLine(` · ${s}`, innerWidth, margin));
3013
+ lines.push(boxLine(`${BOLD}What changes${RESET}`, innerWidth, margin));
3014
+ for (const c of UPGRADE_CHANGES) lines.push(boxLine(` · ${c}`, innerWidth, margin));
3015
+ lines.push(emptyBoxLine(innerWidth, margin));
3016
+ lines.push(boxLine(`${YELLOW}Prepare the update for ${BOLD}${inst.slug}${RESET}${YELLOW} at this location only?${RESET} ${DIM}y / N${RESET}`, innerWidth, margin));
3017
+ } else if (mode === "confirm-activate") {
3018
+ lines.push(emptyBoxLine(innerWidth, margin));
3019
+ lines.push(boxLine(`${YELLOW}Activate the new Runtime for ${BOLD}${inst.slug}${RESET}${YELLOW}?${RESET} ${DIM}y / N${RESET}`, innerWidth, margin));
3020
+ } else if (mode === "confirm-rollback") {
3021
+ lines.push(emptyBoxLine(innerWidth, margin));
3022
+ 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));
3023
+ } else if (mode === "confirm-cancel") {
3024
+ lines.push(emptyBoxLine(innerWidth, margin));
3025
+ lines.push(boxLine(`${YELLOW}Cancel preparation? The current Runtime stays untouched.${RESET} ${DIM}y / N${RESET}`, innerWidth, margin));
3026
+ } else {
3027
+ lines.push(emptyBoxLine(innerWidth, margin));
3028
+ if (notice) lines.push(boxLine(notice, innerWidth, margin));
3029
+ const keys = [];
3030
+ if (!status || status === "none" || ["pending", "failed", "interrupted", "cancelled", "rolled-back"].includes(status)) keys.push(`${BOLD}p${RESET}${DIM} prepare${RESET}`);
3031
+ if (status === "ready-to-activate") keys.push(`${BOLD}a${RESET}${DIM} activate${RESET}`);
3032
+ if (status === "preparing") keys.push(`${BOLD}c${RESET}${DIM} cancel${RESET}`);
3033
+ if (status === "active") keys.push(`${BOLD}r${RESET}${DIM} roll back${RESET}`);
3034
+ keys.push(`${DIM}← back · q quit${RESET}`);
3035
+ lines.push(boxLine(keys.join(`${DIM} · ${RESET}`), innerWidth, margin));
3036
+ }
3037
+ lines.push(emptyBoxLine(innerWidth, margin));
3038
+ lines.push(`${margin}${DIM}└${"─".repeat(innerWidth)}┘${RESET}`);
3039
+ return lines;
3040
+ }
3041
+
3042
+ function renderSettingsPanel({ inst, data, cursor, view, domain, items, listCursor, mode, input, notice, width, upgrade }) {
2417
3043
  const title = inst.slug || inst.name || path.basename(inst.path);
2418
3044
  const outerWidth = Math.min(Math.max(50, width - 2), 126);
2419
3045
  const innerWidth = Math.max(48, outerWidth - 2);
@@ -2428,6 +3054,114 @@ function renderSettingsPanel({ inst, data, cursor, view, domain, items, listCurs
2428
3054
  emptyBoxLine(innerWidth, margin),
2429
3055
  ];
2430
3056
 
3057
+ if (view === "privacy" || view === "telemetry") {
3058
+ lines.push(boxLine(`${BOLD}${view === "privacy" ? "Privacy & access" : "Telemetry"}${RESET}`, innerWidth, margin));
3059
+ lines.push(emptyBoxLine(innerWidth, margin));
3060
+ if (mode === "shareinfo") {
3061
+ const shareLines = (data && data.shareLines) || ["share proposal unavailable"];
3062
+ lines.push(boxLine(`${BOLD}Share proposal${RESET} ${DIM}(nothing has been sent)${RESET}`, innerWidth, margin));
3063
+ lines.push(emptyBoxLine(innerWidth, margin));
3064
+ for (const line of shareLines.slice(0, 16)) {
3065
+ lines.push(boxLine(`${line.includes("--confirm") ? "" : DIM}${line}${RESET}`, innerWidth, margin));
3066
+ }
3067
+ lines.push(emptyBoxLine(innerWidth, margin));
3068
+ lines.push(boxLine(`${DIM}Sharing completes only by running the confirm command above in a terminal. ← back${RESET}`, innerWidth, margin));
3069
+ lines.push(emptyBoxLine(innerWidth, margin));
3070
+ lines.push(`${margin}${DIM}└${"─".repeat(innerWidth)}┘${RESET}`);
3071
+ return lines;
3072
+ }
3073
+ if (mode === "events") {
3074
+ const events = (data && data.telemetryEvents) || [];
3075
+ lines.push(boxLine(`${BOLD}Local events${RESET} ${DIM}(newest last · nothing here ever uploads by itself)${RESET}`, innerWidth, margin));
3076
+ lines.push(emptyBoxLine(innerWidth, margin));
3077
+ if (!events.length) {
3078
+ lines.push(boxLine(`${DIM}(no buffered events)${RESET}`, innerWidth, margin));
3079
+ } else {
3080
+ const cursorAt = Math.min(listCursor || 0, events.length - 1);
3081
+ const windowSize = 12;
3082
+ const start = Math.max(0, Math.min(cursorAt - Math.floor(windowSize / 2), events.length - windowSize));
3083
+ events.slice(start, start + windowSize).forEach((event, i) => {
3084
+ const sel = start + i === cursorAt;
3085
+ lines.push(boxLine(`${sel ? `${CYAN}❯${RESET} ` : " "}${sel ? "" : DIM}${formatEventRow(event)}${RESET}`, innerWidth, margin));
3086
+ });
3087
+ if (events.length > windowSize) lines.push(boxLine(`${DIM}${cursorAt + 1}/${events.length}${RESET}`, innerWidth, margin));
3088
+ }
3089
+ lines.push(emptyBoxLine(innerWidth, margin));
3090
+ if (notice) lines.push(boxLine(notice, innerWidth, margin));
3091
+ 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));
3092
+ lines.push(emptyBoxLine(innerWidth, margin));
3093
+ lines.push(`${margin}${DIM}└${"─".repeat(innerWidth)}┘${RESET}`);
3094
+ return lines;
3095
+ }
3096
+ if (mode === "pickdir") {
3097
+ const pick = (data && data.dirPick) || { dir: os.homedir(), entries: [], cursor: 0, hidden: false };
3098
+ lines.push(boxLine(`${BOLD}Choose a directory to allow reading${RESET}`, innerWidth, margin));
3099
+ lines.push(boxLine(`${DIM}${pick.dir}${RESET}`, innerWidth, margin));
3100
+ lines.push(emptyBoxLine(innerWidth, margin));
3101
+ if (!pick.entries.length) {
3102
+ lines.push(boxLine(`${DIM}(no subdirectories${pick.hidden ? "" : " — press . to show hidden"})${RESET}`, innerWidth, margin));
3103
+ } else {
3104
+ const windowSize = 10;
3105
+ const start = Math.max(0, Math.min(pick.cursor - Math.floor(windowSize / 2), pick.entries.length - windowSize));
3106
+ pick.entries.slice(start, start + windowSize).forEach((name, i) => {
3107
+ const sel = start + i === pick.cursor;
3108
+ lines.push(boxLine(`${sel ? `${CYAN}❯${RESET} ` : " "}${sel ? BOLD : DIM}${name}/${RESET}`, innerWidth, margin));
3109
+ });
3110
+ }
3111
+ lines.push(emptyBoxLine(innerWidth, margin));
3112
+ if (notice) lines.push(boxLine(notice, innerWidth, margin));
3113
+ lines.push(boxLine(`${DIM}↑↓ nav · → enter descend · ← up · ${RESET}${BOLD}space${RESET}${DIM} allow this directory · . hidden · esc cancel${RESET}`, innerWidth, margin));
3114
+ lines.push(emptyBoxLine(innerWidth, margin));
3115
+ lines.push(`${margin}${DIM}└${"─".repeat(innerWidth)}┘${RESET}`);
3116
+ return lines;
3117
+ }
3118
+ if (mode === "pickroot") {
3119
+ const roots = (data && data.privacySnapshot && data.privacySnapshot.boundary && data.privacySnapshot.boundary.personal_read_roots) || [];
3120
+ lines.push(boxLine(`${BOLD}Remove a personal read root${RESET}`, innerWidth, margin));
3121
+ lines.push(emptyBoxLine(innerWidth, margin));
3122
+ if (!roots.length) {
3123
+ lines.push(boxLine(`${DIM}(no personal read roots)${RESET}`, innerWidth, margin));
3124
+ } else {
3125
+ roots.forEach((root, i) => {
3126
+ const sel = i === (listCursor || 0);
3127
+ lines.push(boxLine(`${sel ? `${CYAN}❯${RESET} ` : " "}${sel ? BOLD : DIM}${root}${RESET}`, innerWidth, margin));
3128
+ });
3129
+ }
3130
+ lines.push(emptyBoxLine(innerWidth, margin));
3131
+ if (notice) lines.push(boxLine(notice, innerWidth, margin));
3132
+ lines.push(boxLine(`${DIM}↑↓ nav · enter remove · esc cancel${RESET}`, innerWidth, margin));
3133
+ lines.push(emptyBoxLine(innerWidth, margin));
3134
+ lines.push(`${margin}${DIM}└${"─".repeat(innerWidth)}┘${RESET}`);
3135
+ return lines;
3136
+ }
3137
+ const body = view === "privacy" ? formatPrivacyLines(data && data.privacySnapshot) : formatTelemetryLines(data && data.telemetrySnapshot);
3138
+ for (const line of body) {
3139
+ lines.push(line === "" ? emptyBoxLine(innerWidth, margin) : boxLine(`${line.startsWith("Access needs") || line.startsWith("Telemetry needs") ? YELLOW : ""}${line}${RESET}`, innerWidth, margin));
3140
+ }
3141
+ lines.push(emptyBoxLine(innerWidth, margin));
3142
+ if (mode === "confirm" && input) {
3143
+ lines.push(boxLine(`${YELLOW}${input}${RESET}`, innerWidth, margin));
3144
+ } else if (mode === "input") {
3145
+ lines.push(boxLine(`${BOLD}directory:${RESET} ${input}${DIM}▌${RESET}`, innerWidth, margin));
3146
+ }
3147
+ if (notice) lines.push(boxLine(notice, innerWidth, margin));
3148
+ if (view === "privacy") {
3149
+ const locked = !!(data && data.privacySnapshot && data.privacySnapshot.boundary && data.privacySnapshot.boundary.locked);
3150
+ lines.push(boxLine(
3151
+ locked
3152
+ ? `${DIM}Boundary policy is managed by your organization. ← back · q quit${RESET}`
3153
+ : `${DIM}${RESET}${BOLD}a${RESET}${DIM} add personal read root · ${RESET}${BOLD}r${RESET}${DIM} remove · ← back · q quit${RESET}`,
3154
+ innerWidth, margin,
3155
+ ));
3156
+ } else {
3157
+ const on = !!(data && data.telemetrySnapshot && data.telemetrySnapshot.enabled);
3158
+ 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));
3159
+ }
3160
+ lines.push(emptyBoxLine(innerWidth, margin));
3161
+ lines.push(`${margin}${DIM}└${"─".repeat(innerWidth)}┘${RESET}`);
3162
+ return lines;
3163
+ }
3164
+
2431
3165
  if (view === "list") {
2432
3166
  lines.push(boxLine(`${BOLD}${SETTINGS_DOMAIN_LABEL[domain] || domain}${RESET}`, innerWidth, margin));
2433
3167
  lines.push(emptyBoxLine(innerWidth, margin));
@@ -2450,7 +3184,19 @@ function renderSettingsPanel({ inst, data, cursor, view, domain, items, listCurs
2450
3184
  lines.push(boxLine(`${DIM}↑↓ nav · ${RESET}${BOLD}a${RESET}${DIM} add · ${RESET}${BOLD}d${RESET}${DIM} delete · ← back · q quit${RESET}`, innerWidth, margin));
2451
3185
  }
2452
3186
  } else {
3187
+ const upgradeValue = upgrade === undefined
3188
+ ? `${DIM}checking…${RESET}`
3189
+ : upgrade === null
3190
+ ? `${DIM}unavailable${RESET}`
3191
+ : upgrade.status === "ready-to-activate"
3192
+ ? `${GREEN}${upgradeSummary(upgrade)}${RESET}`
3193
+ : upgrade.status === "failed" || upgrade.status === "interrupted"
3194
+ ? `${YELLOW}${upgradeSummary(upgrade)}${RESET}`
3195
+ : `${DIM}${upgradeSummary(upgrade)}${RESET}`;
2453
3196
  const rows = [
3197
+ { name: "Runtime update", value: upgradeValue },
3198
+ { name: "Privacy & access", value: `${DIM}identity · boundary · consent${RESET}` },
3199
+ { name: "Telemetry", value: `${DIM}personal · opt-out${RESET}` },
2454
3200
  { name: "Hosted Artifacts", value: (d.hosting !== false) ? `${GREEN}ON${RESET}` : `${YELLOW}OFF${RESET}` },
2455
3201
  {
2456
3202
  name: "Git workflow",
@@ -2472,7 +3218,7 @@ function renderSettingsPanel({ inst, data, cursor, view, domain, items, listCurs
2472
3218
  });
2473
3219
  lines.push(emptyBoxLine(innerWidth, margin));
2474
3220
  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));
3221
+ lines.push(boxLine(`${DIM}↑↓ nav · ${RESET}${BOLD}enter${RESET}${DIM} ${cursor === 3 || cursor === 4 ? "toggle" : "open"} · ← back · q quit${RESET}`, innerWidth, margin));
2476
3222
  }
2477
3223
 
2478
3224
  lines.push(emptyBoxLine(innerWidth, margin));
@@ -2597,6 +3343,22 @@ async function browser(args = {}) {
2597
3343
  return;
2598
3344
  }
2599
3345
 
3346
+ if (state.upgradeInst) {
3347
+ const upgradeLines = renderUpgradePanel({
3348
+ inst: state.upgradeInst,
3349
+ data: state.upgradeData,
3350
+ candidate: state.upgradeCandidate,
3351
+ mode: state.upgradeMode,
3352
+ notice: state.upgradeNotice,
3353
+ width: w,
3354
+ });
3355
+ for (const line of upgradeLines) out.push(line + ANSI.clearLine + "\n");
3356
+ const upgradeRemaining = Math.max(0, h - upgradeLines.length - 1);
3357
+ for (let i = 0; i < upgradeRemaining; i++) out.push(ANSI.clearLine + "\n");
3358
+ process.stdout.write(out.join(""));
3359
+ return;
3360
+ }
3361
+
2600
3362
  if (state.settingsInst) {
2601
3363
  const settingsLines = renderSettingsPanel({
2602
3364
  inst: state.settingsInst,
@@ -2610,6 +3372,7 @@ async function browser(args = {}) {
2610
3372
  input: state.settingsInput,
2611
3373
  notice: state.settingsNotice,
2612
3374
  width: w,
3375
+ upgrade: state.settingsUpgrade,
2613
3376
  });
2614
3377
  for (const line of settingsLines) out.push(line + ANSI.clearLine + "\n");
2615
3378
  const remainingRows = Math.max(0, h - settingsLines.length - 1);
@@ -2736,7 +3499,7 @@ async function browser(args = {}) {
2736
3499
  : ` ${label("path")} ${truncate(selected.path, valW)}`);
2737
3500
  if (pv.org) previewLines.push(` ${label("org")} ${truncate(`${pv.org} ${DIM}(${pv.githubOrg || "?"})${RESET}`, valW)}`);
2738
3501
  previewLines.push(pv.connected
2739
- ? ` ${label("mode")} ${CYAN}◆ CONNECTED${RESET} ${DIM}· hosted graph active${RESET}`
3502
+ ? ` ${label("mode")} ${CYAN}◆ CONNECTED${RESET} ${DIM}${connectedModeSuffix(selected.path)}${RESET}`
2740
3503
  : ` ${label("mode")} ${DIM}◇ LOCAL · Markdown + Git${RESET}`);
2741
3504
  if (pv.branch) previewLines.push(` ${label("branch")} ${BOLD}${pv.branch}${RESET}`);
2742
3505
  if (pv.lastCommit) previewLines.push(` ${label("last")} ${DIM}${truncate(pv.lastCommit, valW)}${RESET}`);
@@ -2837,9 +3600,27 @@ async function browser(args = {}) {
2837
3600
  const onResize = () => render(); // render() itself no-ops once done is set
2838
3601
  let onSigint;
2839
3602
  let onSigterm;
3603
+ const stopUpgradePoll = () => {
3604
+ if (state.upgradePoll) { clearInterval(state.upgradePoll); state.upgradePoll = null; }
3605
+ };
3606
+ const refreshUpgrade = () => {
3607
+ if (!state.upgradeInst) return;
3608
+ upgradeStatusAsync(state.upgradeInst, (st) => {
3609
+ if (!state.upgradeInst) return;
3610
+ state.upgradeData = st;
3611
+ if (st && st.status === "preparing") {
3612
+ if (!state.upgradePoll) state.upgradePoll = setInterval(refreshUpgrade, 2500);
3613
+ } else {
3614
+ stopUpgradePoll();
3615
+ }
3616
+ render();
3617
+ });
3618
+ };
3619
+
2840
3620
  const cleanup = () => {
2841
3621
  if (done) return;
2842
3622
  done = true;
3623
+ stopUpgradePoll();
2843
3624
  process.stdin.setRawMode(false);
2844
3625
  process.stdin.pause();
2845
3626
  process.stdin.removeAllListeners("keypress");
@@ -2893,11 +3674,195 @@ async function browser(args = {}) {
2893
3674
  if (!key) return;
2894
3675
  if (isMouseEvent(chunk, key)) return; // drop scroll-wheel / mouse escape sequences
2895
3676
 
3677
+ if (state.upgradeInst) {
3678
+ if (key.ctrl && key.name === "c") { cleanup(); process.exit(130); }
3679
+ if (key.name === "q") { cleanup(); resolve(); return; }
3680
+ if (state.upgradeMode && state.upgradeMode.startsWith("confirm-")) {
3681
+ const mode = state.upgradeMode;
3682
+ state.upgradeMode = "view";
3683
+ if (chunk === "y" || chunk === "Y") {
3684
+ const inst = state.upgradeInst;
3685
+ if (mode === "confirm-prepare") {
3686
+ const r = runUpgrade(inst, ["prepare"]);
3687
+ state.upgradeNotice = r.status === 0
3688
+ ? `${DIM}Preparing in the background — safe to leave this screen.${RESET}`
3689
+ : `${YELLOW}${(r.stderr || "prepare failed").trim().split("\n")[0]}${RESET}`;
3690
+ } else if (mode === "confirm-activate") {
3691
+ const r = runUpgrade(inst, ["activate"]);
3692
+ state.upgradeNotice = r.status === 0
3693
+ ? `${GREEN}Update complete. Start a session to work on the new Runtime.${RESET}`
3694
+ : `${YELLOW}${(r.stderr || "activation refused — a gate has not passed").trim().split("\n")[0]}${RESET}`;
3695
+ } else if (mode === "confirm-rollback") {
3696
+ const r = runUpgrade(inst, ["rollback"]);
3697
+ state.upgradeNotice = r.status === 0
3698
+ ? `${DIM}Rolled back. The previous Runtime is active again.${RESET}`
3699
+ : `${YELLOW}${(r.stderr || "rollback failed").trim().split("\n")[0]}${RESET}`;
3700
+ } else if (mode === "confirm-cancel") {
3701
+ runUpgrade(inst, ["cancel"]);
3702
+ state.upgradeNotice = `${DIM}Preparation cancelled. The current Runtime was never touched.${RESET}`;
3703
+ }
3704
+ refreshUpgrade();
3705
+ }
3706
+ render(); return;
3707
+ }
3708
+ if (key.name === "escape" || key.name === "left") {
3709
+ stopUpgradePoll();
3710
+ state.settingsUpgrade = state.upgradeData;
3711
+ state.upgradeInst = null; state.upgradeNotice = "";
3712
+ render(); return;
3713
+ }
3714
+ const status = state.upgradeData && state.upgradeData.status;
3715
+ if (chunk === "p" && (!status || ["none", "pending", "failed", "interrupted", "cancelled", "rolled-back"].includes(status))) {
3716
+ const staged = state.upgradeData
3717
+ && !["none", "cancelled", "rolled-back"].includes(status)
3718
+ && state.upgradeData.candidate_version;
3719
+ if (!staged) {
3720
+ state.upgradeNotice = `${DIM}Stage a candidate package first: egregore settings ${state.upgradeInst.slug} runtime init <version> <candidate.tgz>${RESET}`;
3721
+ render(); return;
3722
+ }
3723
+ state.upgradeMode = "confirm-prepare"; render(); return;
3724
+ }
3725
+ if (chunk === "a" && status === "ready-to-activate") { state.upgradeMode = "confirm-activate"; render(); return; }
3726
+ if (chunk === "c" && status === "preparing") { state.upgradeMode = "confirm-cancel"; render(); return; }
3727
+ if (chunk === "r" && status === "active") { state.upgradeMode = "confirm-rollback"; render(); return; }
3728
+ return;
3729
+ }
3730
+
2896
3731
  if (state.settingsInst) {
2897
3732
  const S = state;
2898
3733
  if (key.ctrl && key.name === "c") { cleanup(); process.exit(130); }
2899
3734
  if (key.name === "q") { cleanup(); resolve(); return; }
2900
3735
 
3736
+ if (S.settingsView === "privacy" || S.settingsView === "telemetry") {
3737
+ const backToMenu = () => {
3738
+ S.settingsView = "menu"; S.settingsMode = "browse"; S.settingsInput = ""; S.settingsNotice = ""; render();
3739
+ };
3740
+ const refreshPanel = () => {
3741
+ if (S.settingsView === "privacy") S.settingsData = { ...(S.settingsData || {}), privacySnapshot: privacySnapshot(S.settingsInst) };
3742
+ else S.settingsData = { ...(S.settingsData || {}), telemetrySnapshot: telemetrySnapshot(S.settingsInst) };
3743
+ };
3744
+ if (S.settingsMode === "events") {
3745
+ const events = (S.settingsData && S.settingsData.telemetryEvents) || [];
3746
+ if (key.name === "escape" || key.name === "left") { S.settingsMode = "browse"; S.settingsListCursor = 0; S.settingsNotice = ""; render(); return; }
3747
+ if (key.name === "up" || key.name === "k") { S.settingsListCursor = Math.max(0, (S.settingsListCursor || 0) - 1); render(); return; }
3748
+ if (key.name === "down" || key.name === "j") { S.settingsListCursor = Math.min(Math.max(0, events.length - 1), (S.settingsListCursor || 0) + 1); render(); return; }
3749
+ if (chunk === "e") {
3750
+ const stamp = new Date().toISOString().slice(0, 10);
3751
+ S.settingsPendingDir = path.join(os.homedir(), "Desktop", `egregore-telemetry-${S.settingsInst.slug}-${stamp}.jsonl`);
3752
+ S.settingsAction = "tele-export";
3753
+ S.settingsMode = "confirm";
3754
+ S.settingsInput = `Export ${events.length} local event(s) to ${S.settingsPendingDir}? [y/N]`;
3755
+ render(); return;
3756
+ }
3757
+ if (chunk === "s") {
3758
+ const r = runTelemetry(S.settingsInst, ["share"]);
3759
+ const shareLines = ((r && (r.stdout || r.stderr)) || "share preview unavailable").split("\n").filter(Boolean);
3760
+ S.settingsData = { ...(S.settingsData || {}), shareLines };
3761
+ S.settingsMode = "shareinfo"; S.settingsNotice = "";
3762
+ render(); return;
3763
+ }
3764
+ return;
3765
+ }
3766
+ if (S.settingsMode === "shareinfo") {
3767
+ if (key.name === "escape" || key.name === "left") { S.settingsMode = "events"; render(); return; }
3768
+ return;
3769
+ }
3770
+ if (S.settingsMode === "pickdir") {
3771
+ const pick = S.settingsData.dirPick;
3772
+ const reload = () => { pick.entries = listSubdirectories(pick.dir, pick.hidden); pick.cursor = Math.min(pick.cursor, Math.max(0, pick.entries.length - 1)); };
3773
+ if (key.name === "escape") { S.settingsMode = "browse"; S.settingsNotice = ""; render(); return; }
3774
+ if (key.name === "up" || key.name === "k") { pick.cursor = Math.max(0, pick.cursor - 1); render(); return; }
3775
+ if (key.name === "down" || key.name === "j") { pick.cursor = Math.min(Math.max(0, pick.entries.length - 1), pick.cursor + 1); render(); return; }
3776
+ if (key.name === "right" || key.name === "return") {
3777
+ if (pick.entries.length) { pick.dir = path.join(pick.dir, pick.entries[pick.cursor]); pick.cursor = 0; reload(); }
3778
+ render(); return;
3779
+ }
3780
+ if (key.name === "left") {
3781
+ const parent = path.dirname(pick.dir);
3782
+ if (parent !== pick.dir) { pick.dir = parent; pick.cursor = 0; reload(); }
3783
+ render(); return;
3784
+ }
3785
+ if (chunk === ".") { pick.hidden = !pick.hidden; reload(); render(); return; }
3786
+ if (chunk === " ") {
3787
+ S.settingsPendingDir = pick.dir;
3788
+ S.settingsAction = "add";
3789
+ S.settingsMode = "confirm";
3790
+ S.settingsInput = `Allow reading ${pick.dir} for this member? [y/N]`;
3791
+ render(); return;
3792
+ }
3793
+ return;
3794
+ }
3795
+ if (S.settingsMode === "pickroot") {
3796
+ const roots = (S.settingsData.privacySnapshot && S.settingsData.privacySnapshot.boundary && S.settingsData.privacySnapshot.boundary.personal_read_roots) || [];
3797
+ if (key.name === "escape" || key.name === "left") { S.settingsMode = "browse"; S.settingsListCursor = 0; render(); return; }
3798
+ if (key.name === "up" || key.name === "k") { S.settingsListCursor = Math.max(0, (S.settingsListCursor || 0) - 1); render(); return; }
3799
+ if (key.name === "down" || key.name === "j") { S.settingsListCursor = Math.min(Math.max(0, roots.length - 1), (S.settingsListCursor || 0) + 1); render(); return; }
3800
+ if (key.name === "return" && roots.length) {
3801
+ S.settingsPendingDir = roots[Math.min(S.settingsListCursor || 0, roots.length - 1)];
3802
+ S.settingsAction = "remove";
3803
+ S.settingsMode = "confirm";
3804
+ S.settingsInput = `Stop allowing reads of ${S.settingsPendingDir}? [y/N]`;
3805
+ render(); return;
3806
+ }
3807
+ return;
3808
+ }
3809
+ if (S.settingsMode === "confirm") {
3810
+ if (chunk === "y" || chunk === "Y") {
3811
+ let r = null;
3812
+ if (S.settingsAction === "add" || S.settingsAction === "remove") {
3813
+ r = runSettings(S.settingsInst, ["boundary", S.settingsAction, S.settingsPendingDir]);
3814
+ } else if (S.settingsAction === "tele-export") {
3815
+ r = runTelemetry(S.settingsInst, ["export", S.settingsPendingDir]);
3816
+ } else if (S.settingsAction === "tele-on") {
3817
+ r = runTelemetry(S.settingsInst, ["on"]);
3818
+ } else if (S.settingsAction === "tele-off") {
3819
+ r = runTelemetry(S.settingsInst, ["off"]);
3820
+ } else if (S.settingsAction === "tele-clear") {
3821
+ r = runTelemetry(S.settingsInst, ["clear"]);
3822
+ }
3823
+ S.settingsNotice = r && r.status === 0
3824
+ ? `${GREEN}done${RESET}`
3825
+ : `${YELLOW}${((r && (r.stderr || r.stdout)) || "action failed").split("\n")[0]}${RESET}`;
3826
+ S.settingsMode = "browse"; S.settingsInput = ""; S.settingsPendingDir = "";
3827
+ refreshPanel(); render(); return;
3828
+ }
3829
+ S.settingsMode = "browse"; S.settingsInput = ""; S.settingsPendingDir = ""; S.settingsNotice = ""; render(); return;
3830
+ }
3831
+ if (key.name === "escape" || key.name === "left") { backToMenu(); return; }
3832
+ if (S.settingsView === "privacy") {
3833
+ const locked = !!(S.settingsData && S.settingsData.privacySnapshot && S.settingsData.privacySnapshot.boundary && S.settingsData.privacySnapshot.boundary.locked);
3834
+ if (!locked && chunk === "a") {
3835
+ const dir = os.homedir();
3836
+ S.settingsData = { ...(S.settingsData || {}), dirPick: { dir, entries: listSubdirectories(dir, false), cursor: 0, hidden: false } };
3837
+ S.settingsMode = "pickdir"; S.settingsNotice = ""; render(); return;
3838
+ }
3839
+ if (!locked && chunk === "r") {
3840
+ S.settingsMode = "pickroot"; S.settingsListCursor = 0; S.settingsNotice = ""; render(); return;
3841
+ }
3842
+ return;
3843
+ }
3844
+ // telemetry view
3845
+ if (chunk === "i") {
3846
+ S.settingsData = { ...(S.settingsData || {}), telemetryEvents: telemetryEvents(S.settingsInst, 100) };
3847
+ S.settingsMode = "events"; S.settingsListCursor = 0; S.settingsNotice = ""; render(); return;
3848
+ }
3849
+ if (chunk === "t") {
3850
+ const on = !!(S.settingsData && S.settingsData.telemetrySnapshot && S.settingsData.telemetrySnapshot.enabled);
3851
+ S.settingsAction = on ? "tele-off" : "tele-on";
3852
+ S.settingsMode = "confirm";
3853
+ S.settingsInput = `Turn telemetry ${on ? "off" : "on"} for this member only? [y/N]`;
3854
+ render(); return;
3855
+ }
3856
+ if (chunk === "x") {
3857
+ const count = (S.settingsData && S.settingsData.telemetrySnapshot && S.settingsData.telemetrySnapshot.buffered_events) || 0;
3858
+ S.settingsAction = "tele-clear";
3859
+ S.settingsMode = "confirm";
3860
+ S.settingsInput = `Delete ${count} locally buffered telemetry event(s)? Nothing else is removed. [y/N]`;
3861
+ render(); return;
3862
+ }
3863
+ return;
3864
+ }
3865
+
2901
3866
  if (S.settingsView === "list") {
2902
3867
  if (S.settingsMode === "add") {
2903
3868
  if (key.name === "escape") { S.settingsMode = "browse"; S.settingsInput = ""; render(); return; }
@@ -2930,19 +3895,40 @@ async function browser(args = {}) {
2930
3895
 
2931
3896
  // menu view
2932
3897
  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; }
3898
+ if (key.name === "up" || key.name === "k") { S.settingsCursor = (S.settingsCursor - 1 + 8) % 8; render(); return; }
3899
+ if (key.name === "down" || key.name === "j") { S.settingsCursor = (S.settingsCursor + 1) % 8; render(); return; }
2935
3900
  if (key.name === "return" || chunk === " ") {
2936
3901
  if (S.settingsCursor === 0) {
3902
+ state.upgradeInst = S.settingsInst;
3903
+ state.upgradeData = S.settingsUpgrade;
3904
+ state.upgradeMode = "view";
3905
+ state.upgradeNotice = "";
3906
+ refreshUpgrade();
3907
+ if (!state.upgradeCandidate) {
3908
+ fetchRuntimeMvpCandidate().then((v) => {
3909
+ state.upgradeCandidate = v || "";
3910
+ if (state.upgradeInst) render();
3911
+ });
3912
+ }
3913
+ render();
3914
+ } else if (S.settingsCursor === 1) {
3915
+ S.settingsView = "privacy"; S.settingsMode = "browse"; S.settingsInput = ""; S.settingsNotice = "";
3916
+ S.settingsData = { ...(S.settingsData || {}), privacySnapshot: privacySnapshot(S.settingsInst) };
3917
+ render();
3918
+ } else if (S.settingsCursor === 2) {
3919
+ S.settingsView = "telemetry"; S.settingsMode = "browse"; S.settingsInput = ""; S.settingsNotice = "";
3920
+ S.settingsData = { ...(S.settingsData || {}), telemetrySnapshot: telemetrySnapshot(S.settingsInst) };
3921
+ render();
3922
+ } else if (S.settingsCursor === 3) {
2937
3923
  const on = (S.settingsData && S.settingsData.hosting) !== false;
2938
3924
  settingsRunVerb(["hosting", on ? "off" : "on"]);
2939
3925
  render();
2940
- } else if (S.settingsCursor === 1) {
3926
+ } else if (S.settingsCursor === 4) {
2941
3927
  const simple = (S.settingsData && S.settingsData.workflow) === "simple";
2942
3928
  settingsRunVerb(["workflow", simple ? "staged" : "simple"]);
2943
3929
  render();
2944
3930
  } else {
2945
- const domain = S.settingsCursor === 2 ? "repo" : S.settingsCursor === 3 ? "admin" : "people";
3931
+ const domain = S.settingsCursor === 5 ? "repo" : S.settingsCursor === 6 ? "admin" : "people";
2946
3932
  S.settingsView = "list"; S.settingsDomain = domain;
2947
3933
  S.settingsItems = settingsItemsFor(S.settingsData, domain);
2948
3934
  S.settingsListCursor = 0; S.settingsMode = "browse"; S.settingsNotice = "";
@@ -3201,6 +4187,11 @@ async function browser(args = {}) {
3201
4187
  state.settingsData = settingsDump(pick) || { hosting: true, workflow: "staged", base_branch: "develop", repos: [], admins: [], people: [] };
3202
4188
  state.settingsCursor = 0;
3203
4189
  state.settingsView = "menu";
4190
+ state.settingsUpgrade = undefined;
4191
+ upgradeStatusAsync(pick, (st) => {
4192
+ state.settingsUpgrade = st;
4193
+ if (state.settingsInst === pick && !state.upgradeInst) render();
4194
+ });
3204
4195
  state.settingsNotice = "";
3205
4196
  render();
3206
4197
  return;
@@ -3328,6 +4319,8 @@ function main() {
3328
4319
  if (cmd === "ls" || cmd === "list") return cmdLs();
3329
4320
  if (cmd === "use") return cmdUse(args[1]);
3330
4321
  if (cmd === "connect") return cmdConnect(args.slice(1));
4322
+ if (cmd === "telemetry") return cmdTelemetry(args.slice(1));
4323
+ if (cmd === "runtime") return cmdRuntime(args.slice(1));
3331
4324
  if (cmd === "/invite" || cmd === "invite") return cmdInvite(args.slice(1));
3332
4325
  if (cmd === "settings" || cmd === "config") return cmdSettings(args.slice(1));
3333
4326
  if (cmd === "prune") return cmdPruneNonInteractive(args[1] === "--dry-run" || args[1] === "-n");
@@ -3338,6 +4331,8 @@ function main() {
3338
4331
  if (cmd.startsWith("-")) die(`unknown flag: ${cmd}\n run: egregore help`);
3339
4332
 
3340
4333
  const registry = readRegistry();
4334
+ const worktree = currentWorktreeInstance(registry);
4335
+ if (!cmd && worktree) return launch(worktree, runtimeOverride);
3341
4336
  if (registry.length === 0) {
3342
4337
  if (canPrompt()) return setupNewEgregore(runtimeOverride);
3343
4338
  die("no egregores installed yet.\n run: npx -y create-egregore@latest");