create-egregore 0.21.6 → 0.22.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (719) hide show
  1. package/assets/egregore-launcher.js +58 -6
  2. package/assets/egregore-launcher.sh +15 -2
  3. package/lib/claude-runtime.js +2 -0
  4. package/lib/codex-runtime.js +2 -0
  5. package/lib/setup.js +20 -3
  6. package/package.json +2 -2
  7. package/runtime/claude/.claude/context/commit-format.md +7 -4
  8. package/runtime/claude/.claude/context/handoff-claiming.md +32 -0
  9. package/runtime/claude/.claude/context/notification-consent.md +12 -9
  10. package/runtime/claude/.claude/hooks/boundary-check.sh +14 -331
  11. package/runtime/claude/.claude/hooks/memory-read-guard.sh +9 -1
  12. package/runtime/claude/.claude/rules/voice-bedrock.md +6 -2
  13. package/runtime/claude/.claude/settings.json +1 -1
  14. package/runtime/claude/.claude/skills/commit/SKILL.md +5 -3
  15. package/runtime/claude/.claude/skills/dark-mode/SKILL.md +48 -369
  16. package/runtime/claude/.claude/skills/records/SKILL.md +158 -0
  17. package/runtime/claude/.claude/skills/reflect/SKILL.md +50 -21
  18. package/runtime/claude/.claude/skills/review-pr/SKILL.md +4 -2
  19. package/runtime/claude/.claude/skills/save/SKILL.md +9 -3
  20. package/runtime/claude/.claude/skills/summon/SKILL.md +138 -207
  21. package/runtime/claude/.claude/skills/tui-design/SKILL.md +1 -1
  22. package/runtime/claude/.claude/skills/update/SKILL.md +1 -1
  23. package/runtime/claude/.claude/skills/view/SKILL.md +52 -16
  24. package/runtime/claude/.claude/skills/view/compose-scaffold.html +19 -13
  25. package/runtime/claude/CLAUDE.md +59 -95
  26. package/runtime/claude/bin/agent.sh +231 -7
  27. package/runtime/claude/bin/api-call.sh +44 -23
  28. package/runtime/claude/bin/artifact-register.sh +7 -1
  29. package/runtime/claude/bin/artifact-writeback.sh +1 -1
  30. package/runtime/claude/bin/artifacts.sh +66 -265
  31. package/runtime/claude/bin/attendant.sh +4 -1
  32. package/runtime/claude/bin/boundary-guard.cjs +1848 -0
  33. package/runtime/claude/bin/boundary-hook.sh +41 -0
  34. package/runtime/claude/bin/boundary.sh +15 -155
  35. package/runtime/claude/bin/capture-run.sh +32 -2
  36. package/runtime/claude/bin/codex-render-spec.mjs +24 -46
  37. package/runtime/claude/bin/codex-session-start.sh +19 -0
  38. package/runtime/claude/bin/codex-skill-render.mjs +18 -0
  39. package/runtime/claude/bin/config-get.sh +10 -1
  40. package/runtime/claude/bin/connect-refresh.sh +16 -0
  41. package/runtime/claude/bin/corpus_map.py +5 -0
  42. package/runtime/claude/bin/data/capabilities/claude.json +1480 -0
  43. package/runtime/claude/bin/genui.sh +19 -0
  44. package/runtime/claude/bin/graph-batch.sh +1 -1
  45. package/runtime/claude/bin/graph-op.sh +14 -0
  46. package/runtime/claude/bin/graph.sh +1 -1
  47. package/runtime/claude/bin/handoff-pr-backfill.sh +1 -1
  48. package/runtime/claude/bin/handoff-run.sh +11 -6
  49. package/runtime/claude/bin/ingest-item.sh +1 -1
  50. package/runtime/claude/bin/ingest-surface.html +22 -4
  51. package/runtime/claude/bin/issue.sh +1 -1
  52. package/runtime/claude/bin/knowledge.sh +1 -1
  53. package/runtime/claude/bin/lib/card-delivery.sh +329 -0
  54. package/runtime/claude/bin/lib/config.sh +148 -3
  55. package/runtime/claude/bin/lib/context.sh +38 -67
  56. package/runtime/claude/bin/lib/genui-entry.py +36 -0
  57. package/runtime/claude/bin/lib/git-message.sh +25 -6
  58. package/runtime/claude/bin/lib/git-sync.sh +12 -4
  59. package/runtime/claude/bin/lib/greeting.sh +255 -28
  60. package/runtime/claude/bin/lib/handoff_meta.py +294 -0
  61. package/runtime/claude/bin/lib/health-footer.sh +83 -2
  62. package/runtime/claude/bin/lib/identity.sh +31 -10
  63. package/runtime/claude/bin/lib/instance-registry.sh +71 -0
  64. package/runtime/claude/bin/lib/local_ci_receipt.py +249 -0
  65. package/runtime/claude/bin/lib/metrics.sh +121 -8
  66. package/runtime/claude/bin/lib/notices.sh +36 -0
  67. package/runtime/claude/bin/lib/record_outcomes.py +729 -0
  68. package/runtime/claude/bin/lib/reflexes.py +267 -0
  69. package/runtime/claude/bin/lib/retrieval_scorecard.py +624 -0
  70. package/runtime/claude/bin/lib/session-record-prompt.txt +18 -0
  71. package/runtime/claude/bin/lib/session_record.py +3747 -0
  72. package/runtime/claude/bin/lib/session_trail.py +1983 -0
  73. package/runtime/claude/bin/lib/settings-drift.sh +8 -5
  74. package/runtime/claude/bin/lib/startup-restart.sh +41 -0
  75. package/runtime/claude/bin/lib/worktree-links.sh +92 -0
  76. package/runtime/claude/bin/memory-push.sh +6 -0
  77. package/runtime/claude/bin/merge-gate.sh +498 -0
  78. package/runtime/claude/bin/notify.sh +5 -2
  79. package/runtime/claude/bin/page-access.sh +103 -0
  80. package/runtime/claude/bin/person.sh +13 -2
  81. package/runtime/claude/bin/pi-render-spec.mjs +0 -1
  82. package/runtime/claude/bin/pi-spirit-discovery.mjs +34 -0
  83. package/runtime/claude/bin/post-update.sh +181 -0
  84. package/runtime/claude/bin/prime-render-spec.mjs +0 -1
  85. package/runtime/claude/bin/publish-artifact.sh +89 -42
  86. package/runtime/claude/bin/qa-inventory.sh +2 -2
  87. package/runtime/claude/bin/quest.sh +1 -1
  88. package/runtime/claude/bin/records.sh +11 -0
  89. package/runtime/claude/bin/reflexes/05-vitals.sh +9 -0
  90. package/runtime/claude/bin/reflexes/10-open-items.sh +12 -0
  91. package/runtime/claude/bin/reflexes/15-retrieval.sh +8 -0
  92. package/runtime/claude/bin/reflexes/20-nightly.sh +100 -0
  93. package/runtime/claude/bin/reflexes/20-sandbox.sh +85 -0
  94. package/runtime/claude/bin/reflexes/30-cost.py +116 -0
  95. package/runtime/claude/bin/reflexes/40-shells.py +51 -0
  96. package/runtime/claude/bin/reflexes/README.md +61 -0
  97. package/runtime/claude/bin/reflexes.sh +7 -0
  98. package/runtime/claude/bin/runtime-identity.sh +140 -42
  99. package/runtime/claude/bin/screenshot.sh +77 -0
  100. package/runtime/claude/bin/scroll.sh +1 -1
  101. package/runtime/claude/bin/search.sh +31 -5
  102. package/runtime/claude/bin/session-autosave.sh +6 -3
  103. package/runtime/claude/bin/session-id.sh +147 -0
  104. package/runtime/claude/bin/session-log.sh +19 -0
  105. package/runtime/claude/bin/session-record.sh +26 -0
  106. package/runtime/claude/bin/session-start.sh +121 -151
  107. package/runtime/claude/bin/settings.sh +13 -8
  108. package/runtime/claude/bin/spirit-discovery-claude.sh +4 -0
  109. package/runtime/claude/bin/spirit-discovery-codex.sh +4 -0
  110. package/runtime/claude/bin/spirit-discovery-pi.sh +4 -0
  111. package/runtime/claude/bin/spirit-discovery-prime.sh +4 -0
  112. package/runtime/claude/bin/spirit-discovery.sh +4 -0
  113. package/runtime/claude/bin/startup-check.sh +1 -1
  114. package/runtime/claude/bin/telemetry.sh +11 -4
  115. package/runtime/claude/bin/test-baseline.sh +33 -5
  116. package/runtime/claude/bin/todo.sh +1 -1
  117. package/runtime/claude/bin/worktree-create.sh +15 -45
  118. package/runtime/claude/bin/worktree.sh +6 -0
  119. package/runtime/claude/docs/spirit-discovery.md +96 -0
  120. package/runtime/claude/egregore_runtime/__init__.py +18 -12
  121. package/runtime/claude/egregore_runtime/access.py +608 -0
  122. package/runtime/claude/egregore_runtime/access_cli.py +190 -0
  123. package/runtime/claude/egregore_runtime/adapters/invitation.py +5 -3
  124. package/runtime/claude/egregore_runtime/adapters/qmd.py +521 -47
  125. package/runtime/claude/egregore_runtime/admin_entry.py +56 -0
  126. package/runtime/claude/egregore_runtime/artifacts.py +222 -16
  127. package/runtime/claude/egregore_runtime/contracts.py +27 -0
  128. package/runtime/claude/egregore_runtime/eligibility.py +5 -12
  129. package/runtime/claude/egregore_runtime/episode_binding.py +16 -5
  130. package/runtime/claude/egregore_runtime/github_issues.py +2 -2
  131. package/runtime/claude/egregore_runtime/handoff_lookup.py +9 -20
  132. package/runtime/claude/egregore_runtime/harness_cli.py +674 -149
  133. package/runtime/claude/egregore_runtime/held.py +2436 -0
  134. package/runtime/claude/egregore_runtime/hosted_api.py +137 -0
  135. package/runtime/claude/egregore_runtime/identity.py +196 -65
  136. package/runtime/claude/egregore_runtime/identity_transfer.py +6 -2
  137. package/runtime/claude/egregore_runtime/ingest_cli.py +2 -2
  138. package/runtime/claude/egregore_runtime/ingest_index_cli.py +4 -16
  139. package/runtime/claude/egregore_runtime/installation.py +119 -0
  140. package/runtime/claude/egregore_runtime/investigation.py +64 -41
  141. package/runtime/claude/egregore_runtime/investigation_guidance.py +16 -11
  142. package/runtime/claude/egregore_runtime/issue_cli.py +51 -32
  143. package/runtime/claude/egregore_runtime/issue_reporting.py +4 -3
  144. package/runtime/claude/egregore_runtime/issue_triage_service.py +2 -2
  145. package/runtime/claude/egregore_runtime/issues.py +12 -6
  146. package/runtime/claude/egregore_runtime/knowledge.py +331 -5
  147. package/runtime/claude/egregore_runtime/knowledge_cli.py +57 -17
  148. package/runtime/claude/egregore_runtime/lifecycle.py +88 -22
  149. package/runtime/claude/egregore_runtime/lifecycle_cli.py +21 -13
  150. package/runtime/claude/egregore_runtime/line_ids.py +10 -0
  151. package/runtime/claude/egregore_runtime/memory_push.py +349 -0
  152. package/runtime/claude/egregore_runtime/observe.py +64 -25
  153. package/runtime/claude/egregore_runtime/observers.py +19 -0
  154. package/runtime/claude/egregore_runtime/optional_modules.py +13 -0
  155. package/runtime/claude/egregore_runtime/outputs_cli.py +321 -0
  156. package/runtime/claude/egregore_runtime/policy.py +651 -24
  157. package/runtime/claude/egregore_runtime/private_result.py +2 -1
  158. package/runtime/claude/egregore_runtime/projection.py +946 -0
  159. package/runtime/claude/egregore_runtime/quest_cli.py +20 -23
  160. package/runtime/claude/egregore_runtime/question_cli.py +15 -11
  161. package/runtime/claude/egregore_runtime/questions.py +10 -10
  162. package/runtime/claude/egregore_runtime/quests.py +10 -10
  163. package/runtime/claude/egregore_runtime/record_publish.py +548 -0
  164. package/runtime/claude/egregore_runtime/record_subject.py +564 -0
  165. package/runtime/claude/egregore_runtime/research_ingest.py +2 -4
  166. package/runtime/claude/egregore_runtime/runtime.py +169 -60
  167. package/runtime/claude/egregore_runtime/sandbox.py +194 -0
  168. package/runtime/claude/egregore_runtime/scroll_cli.py +22 -32
  169. package/runtime/claude/egregore_runtime/scrolls.py +23 -15
  170. package/runtime/claude/egregore_runtime/shadow.py +262 -0
  171. package/runtime/claude/egregore_runtime/spirit_discovery.py +487 -0
  172. package/runtime/claude/egregore_runtime/spirit_discovery_cli.py +25 -0
  173. package/runtime/claude/egregore_runtime/spirit_discovery_transport.py +18 -0
  174. package/runtime/claude/egregore_runtime/status.py +101 -44
  175. package/runtime/claude/egregore_runtime/status_cli.py +13 -5
  176. package/runtime/claude/egregore_runtime/sync.py +39 -21
  177. package/runtime/claude/egregore_runtime/taste.py +934 -0
  178. package/runtime/claude/egregore_runtime/taste_cli.py +242 -0
  179. package/runtime/claude/egregore_runtime/taste_rules.py +91 -0
  180. package/runtime/claude/egregore_runtime/telemetry.py +26 -3
  181. package/runtime/claude/egregore_runtime/todo_cli.py +15 -12
  182. package/runtime/claude/egregore_runtime/todos.py +3 -2
  183. package/runtime/claude/egregore_runtime/upgrade.py +85 -58
  184. package/runtime/claude/egregore_runtime/writeback.py +330 -24
  185. package/runtime/claude/manifest.json +65 -5
  186. package/runtime/codex/.claude/context/handoff-claiming.md +32 -0
  187. package/runtime/codex/.claude/context/notification-consent.md +12 -9
  188. package/runtime/codex/.claude/skills/view/SKILL.md +438 -0
  189. package/runtime/codex/.codex/hooks.json +11 -0
  190. package/runtime/codex/.codex/skills/dark-mode/SKILL.md +1 -1
  191. package/runtime/codex/.codex/skills/records/SKILL.md +29 -0
  192. package/runtime/codex/.codex/skills/summon/SKILL.md +1 -1
  193. package/runtime/codex/.codex/skills/view/assets/compose-scaffold.html +19 -13
  194. package/runtime/codex/.codex/spec-manifest.json +13 -19
  195. package/runtime/codex/AGENTS.md +54 -105
  196. package/runtime/codex/bin/agent.sh +231 -7
  197. package/runtime/codex/bin/api-call.sh +44 -23
  198. package/runtime/codex/bin/artifact-register.sh +7 -1
  199. package/runtime/codex/bin/artifact-writeback.sh +1 -1
  200. package/runtime/codex/bin/artifacts.sh +66 -265
  201. package/runtime/codex/bin/attendant.sh +4 -1
  202. package/runtime/codex/bin/boundary-guard.cjs +1848 -0
  203. package/runtime/codex/bin/boundary-hook.sh +41 -0
  204. package/runtime/codex/bin/capture-run.sh +32 -2
  205. package/runtime/codex/bin/codex-render-spec.mjs +24 -46
  206. package/runtime/codex/bin/codex-session-start.sh +19 -0
  207. package/runtime/codex/bin/codex-skill-render.mjs +18 -0
  208. package/runtime/codex/bin/config-get.sh +10 -1
  209. package/runtime/codex/bin/connect-refresh.sh +16 -0
  210. package/runtime/codex/bin/corpus_map.py +5 -0
  211. package/runtime/codex/bin/data/capabilities/codex.json +1458 -0
  212. package/runtime/codex/bin/genui.sh +19 -0
  213. package/runtime/codex/bin/graph-batch.sh +1 -1
  214. package/runtime/codex/bin/graph-op.sh +14 -0
  215. package/runtime/codex/bin/graph.sh +1 -1
  216. package/runtime/codex/bin/handoff-pr-backfill.sh +1 -1
  217. package/runtime/codex/bin/handoff-run.sh +11 -6
  218. package/runtime/codex/bin/ingest-item.sh +1 -1
  219. package/runtime/codex/bin/ingest-surface.html +22 -4
  220. package/runtime/codex/bin/issue.sh +1 -1
  221. package/runtime/codex/bin/knowledge.sh +1 -1
  222. package/runtime/codex/bin/lib/card-delivery.sh +329 -0
  223. package/runtime/codex/bin/lib/config.sh +148 -3
  224. package/runtime/codex/bin/lib/context.sh +38 -67
  225. package/runtime/codex/bin/lib/genui-entry.py +36 -0
  226. package/runtime/codex/bin/lib/git-message.sh +25 -6
  227. package/runtime/codex/bin/lib/git-sync.sh +12 -4
  228. package/runtime/codex/bin/lib/greeting.sh +255 -28
  229. package/runtime/codex/bin/lib/handoff_meta.py +294 -0
  230. package/runtime/codex/bin/lib/health-footer.sh +83 -2
  231. package/runtime/codex/bin/lib/identity.sh +31 -10
  232. package/runtime/codex/bin/lib/instance-registry.sh +71 -0
  233. package/runtime/codex/bin/lib/local_ci_receipt.py +249 -0
  234. package/runtime/codex/bin/lib/metrics.sh +121 -8
  235. package/runtime/codex/bin/lib/notices.sh +36 -0
  236. package/runtime/codex/bin/lib/record_outcomes.py +729 -0
  237. package/runtime/codex/bin/lib/reflexes.py +267 -0
  238. package/runtime/codex/bin/lib/retrieval_scorecard.py +624 -0
  239. package/runtime/codex/bin/lib/session-record-prompt.txt +18 -0
  240. package/runtime/codex/bin/lib/session_record.py +3747 -0
  241. package/runtime/codex/bin/lib/session_trail.py +1983 -0
  242. package/runtime/codex/bin/lib/settings-drift.sh +8 -5
  243. package/runtime/codex/bin/lib/startup-restart.sh +41 -0
  244. package/runtime/codex/bin/lib/worktree-links.sh +92 -0
  245. package/runtime/codex/bin/memory-push.sh +6 -0
  246. package/runtime/codex/bin/merge-gate.sh +498 -0
  247. package/runtime/codex/bin/notify.sh +5 -2
  248. package/runtime/codex/bin/page-access.sh +103 -0
  249. package/runtime/codex/bin/person.sh +13 -2
  250. package/runtime/codex/bin/pi-render-spec.mjs +0 -1
  251. package/runtime/codex/bin/pi-spirit-discovery.mjs +34 -0
  252. package/runtime/codex/bin/post-update.sh +181 -0
  253. package/runtime/codex/bin/prime-render-spec.mjs +0 -1
  254. package/runtime/codex/bin/publish-artifact.sh +89 -42
  255. package/runtime/codex/bin/qa-inventory.sh +2 -2
  256. package/runtime/codex/bin/quest.sh +1 -1
  257. package/runtime/codex/bin/records.sh +11 -0
  258. package/runtime/codex/bin/reflexes/05-vitals.sh +9 -0
  259. package/runtime/codex/bin/reflexes/10-open-items.sh +12 -0
  260. package/runtime/codex/bin/reflexes/15-retrieval.sh +8 -0
  261. package/runtime/codex/bin/reflexes/20-nightly.sh +100 -0
  262. package/runtime/codex/bin/reflexes/20-sandbox.sh +85 -0
  263. package/runtime/codex/bin/reflexes/30-cost.py +116 -0
  264. package/runtime/codex/bin/reflexes/40-shells.py +51 -0
  265. package/runtime/codex/bin/reflexes/README.md +61 -0
  266. package/runtime/codex/bin/reflexes.sh +7 -0
  267. package/runtime/codex/bin/runtime-identity.sh +140 -42
  268. package/runtime/codex/bin/screenshot.sh +77 -0
  269. package/runtime/codex/bin/scroll.sh +1 -1
  270. package/runtime/codex/bin/search.sh +31 -5
  271. package/runtime/codex/bin/session-autosave.sh +6 -3
  272. package/runtime/codex/bin/session-id.sh +147 -0
  273. package/runtime/codex/bin/session-log.sh +19 -0
  274. package/runtime/codex/bin/session-record.sh +26 -0
  275. package/runtime/codex/bin/settings.sh +13 -8
  276. package/runtime/codex/bin/spirit-discovery-claude.sh +4 -0
  277. package/runtime/codex/bin/spirit-discovery-codex.sh +4 -0
  278. package/runtime/codex/bin/spirit-discovery-pi.sh +4 -0
  279. package/runtime/codex/bin/spirit-discovery-prime.sh +4 -0
  280. package/runtime/codex/bin/spirit-discovery.sh +4 -0
  281. package/runtime/codex/bin/startup-check.sh +1 -1
  282. package/runtime/codex/bin/telemetry.sh +11 -4
  283. package/runtime/codex/bin/test-baseline.sh +33 -5
  284. package/runtime/codex/bin/todo.sh +1 -1
  285. package/runtime/codex/bin/worktree-create.sh +15 -45
  286. package/runtime/codex/bin/worktree.sh +6 -0
  287. package/runtime/codex/docs/spirit-discovery.md +96 -0
  288. package/runtime/codex/egregore_runtime/__init__.py +18 -12
  289. package/runtime/codex/egregore_runtime/access.py +608 -0
  290. package/runtime/codex/egregore_runtime/access_cli.py +190 -0
  291. package/runtime/codex/egregore_runtime/adapters/invitation.py +5 -3
  292. package/runtime/codex/egregore_runtime/adapters/qmd.py +521 -47
  293. package/runtime/codex/egregore_runtime/admin_entry.py +56 -0
  294. package/runtime/codex/egregore_runtime/artifacts.py +222 -16
  295. package/runtime/codex/egregore_runtime/contracts.py +27 -0
  296. package/runtime/codex/egregore_runtime/eligibility.py +5 -12
  297. package/runtime/codex/egregore_runtime/episode_binding.py +16 -5
  298. package/runtime/codex/egregore_runtime/github_issues.py +2 -2
  299. package/runtime/codex/egregore_runtime/handoff_lookup.py +9 -20
  300. package/runtime/codex/egregore_runtime/harness_cli.py +674 -149
  301. package/runtime/codex/egregore_runtime/held.py +2436 -0
  302. package/runtime/codex/egregore_runtime/hosted_api.py +137 -0
  303. package/runtime/codex/egregore_runtime/identity.py +196 -65
  304. package/runtime/codex/egregore_runtime/identity_transfer.py +6 -2
  305. package/runtime/codex/egregore_runtime/ingest_cli.py +2 -2
  306. package/runtime/codex/egregore_runtime/ingest_index_cli.py +4 -16
  307. package/runtime/codex/egregore_runtime/installation.py +119 -0
  308. package/runtime/codex/egregore_runtime/investigation.py +64 -41
  309. package/runtime/codex/egregore_runtime/investigation_guidance.py +16 -11
  310. package/runtime/codex/egregore_runtime/issue_cli.py +51 -32
  311. package/runtime/codex/egregore_runtime/issue_reporting.py +4 -3
  312. package/runtime/codex/egregore_runtime/issue_triage_service.py +2 -2
  313. package/runtime/codex/egregore_runtime/issues.py +12 -6
  314. package/runtime/codex/egregore_runtime/knowledge.py +331 -5
  315. package/runtime/codex/egregore_runtime/knowledge_cli.py +57 -17
  316. package/runtime/codex/egregore_runtime/lifecycle.py +88 -22
  317. package/runtime/codex/egregore_runtime/lifecycle_cli.py +21 -13
  318. package/runtime/codex/egregore_runtime/line_ids.py +10 -0
  319. package/runtime/codex/egregore_runtime/memory_push.py +349 -0
  320. package/runtime/codex/egregore_runtime/observe.py +64 -25
  321. package/runtime/codex/egregore_runtime/observers.py +19 -0
  322. package/runtime/codex/egregore_runtime/optional_modules.py +13 -0
  323. package/runtime/codex/egregore_runtime/outputs_cli.py +321 -0
  324. package/runtime/codex/egregore_runtime/policy.py +651 -24
  325. package/runtime/codex/egregore_runtime/private_result.py +2 -1
  326. package/runtime/codex/egregore_runtime/projection.py +946 -0
  327. package/runtime/codex/egregore_runtime/quest_cli.py +20 -23
  328. package/runtime/codex/egregore_runtime/question_cli.py +15 -11
  329. package/runtime/codex/egregore_runtime/questions.py +10 -10
  330. package/runtime/codex/egregore_runtime/quests.py +10 -10
  331. package/runtime/codex/egregore_runtime/record_publish.py +548 -0
  332. package/runtime/codex/egregore_runtime/record_subject.py +564 -0
  333. package/runtime/codex/egregore_runtime/research_ingest.py +2 -4
  334. package/runtime/codex/egregore_runtime/runtime.py +169 -60
  335. package/runtime/codex/egregore_runtime/sandbox.py +194 -0
  336. package/runtime/codex/egregore_runtime/scroll_cli.py +22 -32
  337. package/runtime/codex/egregore_runtime/scrolls.py +23 -15
  338. package/runtime/codex/egregore_runtime/shadow.py +262 -0
  339. package/runtime/codex/egregore_runtime/spirit_discovery.py +487 -0
  340. package/runtime/codex/egregore_runtime/spirit_discovery_cli.py +25 -0
  341. package/runtime/codex/egregore_runtime/spirit_discovery_transport.py +18 -0
  342. package/runtime/codex/egregore_runtime/status.py +101 -44
  343. package/runtime/codex/egregore_runtime/status_cli.py +13 -5
  344. package/runtime/codex/egregore_runtime/sync.py +39 -21
  345. package/runtime/codex/egregore_runtime/taste.py +934 -0
  346. package/runtime/codex/egregore_runtime/taste_cli.py +242 -0
  347. package/runtime/codex/egregore_runtime/taste_rules.py +91 -0
  348. package/runtime/codex/egregore_runtime/telemetry.py +26 -3
  349. package/runtime/codex/egregore_runtime/todo_cli.py +15 -12
  350. package/runtime/codex/egregore_runtime/todos.py +3 -2
  351. package/runtime/codex/egregore_runtime/upgrade.py +85 -58
  352. package/runtime/codex/egregore_runtime/writeback.py +330 -24
  353. package/runtime/codex/manifest.json +66 -5
  354. package/runtime/pi/.claude/context/handoff-claiming.md +32 -0
  355. package/runtime/pi/.claude/context/notification-consent.md +12 -9
  356. package/runtime/pi/.claude/skills/view/SKILL.md +438 -0
  357. package/runtime/pi/.codex/hooks.json +11 -0
  358. package/runtime/pi/.codex/skills/dark-mode/SKILL.md +1 -1
  359. package/runtime/pi/.codex/skills/records/SKILL.md +29 -0
  360. package/runtime/pi/.codex/skills/summon/SKILL.md +1 -1
  361. package/runtime/pi/.codex/skills/view/assets/compose-scaffold.html +19 -13
  362. package/runtime/pi/.codex/spec-manifest.json +13 -19
  363. package/runtime/pi/.pi/APPEND_SYSTEM.md +53 -99
  364. package/runtime/pi/.pi/extensions/egregore.ts +13 -1
  365. package/runtime/pi/.pi/spec-manifest.json +2 -2
  366. package/runtime/pi/AGENTS.md +54 -105
  367. package/runtime/pi/bin/agent.sh +231 -7
  368. package/runtime/pi/bin/api-call.sh +44 -23
  369. package/runtime/pi/bin/artifact-register.sh +7 -1
  370. package/runtime/pi/bin/artifact-writeback.sh +1 -1
  371. package/runtime/pi/bin/artifacts.sh +66 -265
  372. package/runtime/pi/bin/attendant.sh +4 -1
  373. package/runtime/pi/bin/boundary-guard.cjs +1848 -0
  374. package/runtime/pi/bin/boundary-hook.sh +41 -0
  375. package/runtime/pi/bin/capture-run.sh +32 -2
  376. package/runtime/pi/bin/codex-render-spec.mjs +24 -46
  377. package/runtime/pi/bin/codex-session-start.sh +19 -0
  378. package/runtime/pi/bin/codex-skill-render.mjs +18 -0
  379. package/runtime/pi/bin/config-get.sh +10 -1
  380. package/runtime/pi/bin/connect-refresh.sh +16 -0
  381. package/runtime/pi/bin/corpus_map.py +5 -0
  382. package/runtime/pi/bin/data/capabilities/pi.json +1458 -0
  383. package/runtime/pi/bin/genui.sh +19 -0
  384. package/runtime/pi/bin/graph-batch.sh +1 -1
  385. package/runtime/pi/bin/graph-op.sh +14 -0
  386. package/runtime/pi/bin/graph.sh +1 -1
  387. package/runtime/pi/bin/handoff-pr-backfill.sh +1 -1
  388. package/runtime/pi/bin/handoff-run.sh +11 -6
  389. package/runtime/pi/bin/ingest-item.sh +1 -1
  390. package/runtime/pi/bin/ingest-surface.html +22 -4
  391. package/runtime/pi/bin/issue.sh +1 -1
  392. package/runtime/pi/bin/knowledge.sh +1 -1
  393. package/runtime/pi/bin/lib/card-delivery.sh +329 -0
  394. package/runtime/pi/bin/lib/config.sh +148 -3
  395. package/runtime/pi/bin/lib/context.sh +38 -67
  396. package/runtime/pi/bin/lib/genui-entry.py +36 -0
  397. package/runtime/pi/bin/lib/git-message.sh +25 -6
  398. package/runtime/pi/bin/lib/git-sync.sh +12 -4
  399. package/runtime/pi/bin/lib/greeting.sh +255 -28
  400. package/runtime/pi/bin/lib/handoff_meta.py +294 -0
  401. package/runtime/pi/bin/lib/health-footer.sh +83 -2
  402. package/runtime/pi/bin/lib/identity.sh +31 -10
  403. package/runtime/pi/bin/lib/instance-registry.sh +71 -0
  404. package/runtime/pi/bin/lib/local_ci_receipt.py +249 -0
  405. package/runtime/pi/bin/lib/metrics.sh +121 -8
  406. package/runtime/pi/bin/lib/notices.sh +36 -0
  407. package/runtime/pi/bin/lib/record_outcomes.py +729 -0
  408. package/runtime/pi/bin/lib/reflexes.py +267 -0
  409. package/runtime/pi/bin/lib/retrieval_scorecard.py +624 -0
  410. package/runtime/pi/bin/lib/session-record-prompt.txt +18 -0
  411. package/runtime/pi/bin/lib/session_record.py +3747 -0
  412. package/runtime/pi/bin/lib/session_trail.py +1983 -0
  413. package/runtime/pi/bin/lib/settings-drift.sh +8 -5
  414. package/runtime/pi/bin/lib/startup-restart.sh +41 -0
  415. package/runtime/pi/bin/lib/worktree-links.sh +92 -0
  416. package/runtime/pi/bin/memory-push.sh +6 -0
  417. package/runtime/pi/bin/merge-gate.sh +498 -0
  418. package/runtime/pi/bin/notify.sh +5 -2
  419. package/runtime/pi/bin/page-access.sh +103 -0
  420. package/runtime/pi/bin/person.sh +13 -2
  421. package/runtime/pi/bin/pi-branch-policy.mjs +37 -0
  422. package/runtime/pi/bin/pi-observe-context.mjs +97 -4
  423. package/runtime/pi/bin/pi-render-spec.mjs +0 -1
  424. package/runtime/pi/bin/pi-session-start.sh +4 -1
  425. package/runtime/pi/bin/pi-spirit-discovery.mjs +34 -0
  426. package/runtime/pi/bin/post-update.sh +181 -0
  427. package/runtime/pi/bin/prime-render-spec.mjs +0 -1
  428. package/runtime/pi/bin/publish-artifact.sh +89 -42
  429. package/runtime/pi/bin/qa-inventory.sh +2 -2
  430. package/runtime/pi/bin/quest.sh +1 -1
  431. package/runtime/pi/bin/records.sh +11 -0
  432. package/runtime/pi/bin/reflexes/05-vitals.sh +9 -0
  433. package/runtime/pi/bin/reflexes/10-open-items.sh +12 -0
  434. package/runtime/pi/bin/reflexes/15-retrieval.sh +8 -0
  435. package/runtime/pi/bin/reflexes/20-nightly.sh +100 -0
  436. package/runtime/pi/bin/reflexes/20-sandbox.sh +85 -0
  437. package/runtime/pi/bin/reflexes/30-cost.py +116 -0
  438. package/runtime/pi/bin/reflexes/40-shells.py +51 -0
  439. package/runtime/pi/bin/reflexes/README.md +61 -0
  440. package/runtime/pi/bin/reflexes.sh +7 -0
  441. package/runtime/pi/bin/runtime-identity.sh +140 -42
  442. package/runtime/pi/bin/screenshot.sh +77 -0
  443. package/runtime/pi/bin/scroll.sh +1 -1
  444. package/runtime/pi/bin/search.sh +31 -5
  445. package/runtime/pi/bin/session-autosave.sh +6 -3
  446. package/runtime/pi/bin/session-id.sh +147 -0
  447. package/runtime/pi/bin/session-log.sh +19 -0
  448. package/runtime/pi/bin/session-record.sh +26 -0
  449. package/runtime/pi/bin/settings.sh +13 -8
  450. package/runtime/pi/bin/spirit-discovery-claude.sh +4 -0
  451. package/runtime/pi/bin/spirit-discovery-codex.sh +4 -0
  452. package/runtime/pi/bin/spirit-discovery-pi.sh +4 -0
  453. package/runtime/pi/bin/spirit-discovery-prime.sh +4 -0
  454. package/runtime/pi/bin/spirit-discovery.sh +4 -0
  455. package/runtime/pi/bin/startup-check.sh +1 -1
  456. package/runtime/pi/bin/telemetry.sh +11 -4
  457. package/runtime/pi/bin/test-baseline.sh +33 -5
  458. package/runtime/pi/bin/todo.sh +1 -1
  459. package/runtime/pi/bin/worktree-create.sh +15 -45
  460. package/runtime/pi/bin/worktree.sh +6 -0
  461. package/runtime/pi/docs/spirit-discovery.md +96 -0
  462. package/runtime/pi/egregore_runtime/__init__.py +18 -12
  463. package/runtime/pi/egregore_runtime/access.py +608 -0
  464. package/runtime/pi/egregore_runtime/access_cli.py +190 -0
  465. package/runtime/pi/egregore_runtime/adapters/invitation.py +5 -3
  466. package/runtime/pi/egregore_runtime/adapters/qmd.py +521 -47
  467. package/runtime/pi/egregore_runtime/admin_entry.py +56 -0
  468. package/runtime/pi/egregore_runtime/artifacts.py +222 -16
  469. package/runtime/pi/egregore_runtime/contracts.py +27 -0
  470. package/runtime/pi/egregore_runtime/eligibility.py +5 -12
  471. package/runtime/pi/egregore_runtime/episode_binding.py +16 -5
  472. package/runtime/pi/egregore_runtime/github_issues.py +2 -2
  473. package/runtime/pi/egregore_runtime/handoff_lookup.py +9 -20
  474. package/runtime/pi/egregore_runtime/harness_cli.py +674 -149
  475. package/runtime/pi/egregore_runtime/held.py +2436 -0
  476. package/runtime/pi/egregore_runtime/hosted_api.py +137 -0
  477. package/runtime/pi/egregore_runtime/identity.py +196 -65
  478. package/runtime/pi/egregore_runtime/identity_transfer.py +6 -2
  479. package/runtime/pi/egregore_runtime/ingest_cli.py +2 -2
  480. package/runtime/pi/egregore_runtime/ingest_index_cli.py +4 -16
  481. package/runtime/pi/egregore_runtime/installation.py +119 -0
  482. package/runtime/pi/egregore_runtime/investigation.py +64 -41
  483. package/runtime/pi/egregore_runtime/investigation_guidance.py +16 -11
  484. package/runtime/pi/egregore_runtime/issue_cli.py +51 -32
  485. package/runtime/pi/egregore_runtime/issue_reporting.py +4 -3
  486. package/runtime/pi/egregore_runtime/issue_triage_service.py +2 -2
  487. package/runtime/pi/egregore_runtime/issues.py +12 -6
  488. package/runtime/pi/egregore_runtime/knowledge.py +331 -5
  489. package/runtime/pi/egregore_runtime/knowledge_cli.py +57 -17
  490. package/runtime/pi/egregore_runtime/lifecycle.py +88 -22
  491. package/runtime/pi/egregore_runtime/lifecycle_cli.py +21 -13
  492. package/runtime/pi/egregore_runtime/line_ids.py +10 -0
  493. package/runtime/pi/egregore_runtime/memory_push.py +349 -0
  494. package/runtime/pi/egregore_runtime/observe.py +64 -25
  495. package/runtime/pi/egregore_runtime/observers.py +19 -0
  496. package/runtime/pi/egregore_runtime/optional_modules.py +13 -0
  497. package/runtime/pi/egregore_runtime/outputs_cli.py +321 -0
  498. package/runtime/pi/egregore_runtime/policy.py +651 -24
  499. package/runtime/pi/egregore_runtime/private_result.py +2 -1
  500. package/runtime/pi/egregore_runtime/projection.py +946 -0
  501. package/runtime/pi/egregore_runtime/quest_cli.py +20 -23
  502. package/runtime/pi/egregore_runtime/question_cli.py +15 -11
  503. package/runtime/pi/egregore_runtime/questions.py +10 -10
  504. package/runtime/pi/egregore_runtime/quests.py +10 -10
  505. package/runtime/pi/egregore_runtime/record_publish.py +548 -0
  506. package/runtime/pi/egregore_runtime/record_subject.py +564 -0
  507. package/runtime/pi/egregore_runtime/research_ingest.py +2 -4
  508. package/runtime/pi/egregore_runtime/runtime.py +169 -60
  509. package/runtime/pi/egregore_runtime/sandbox.py +194 -0
  510. package/runtime/pi/egregore_runtime/scroll_cli.py +22 -32
  511. package/runtime/pi/egregore_runtime/scrolls.py +23 -15
  512. package/runtime/pi/egregore_runtime/shadow.py +262 -0
  513. package/runtime/pi/egregore_runtime/spirit_discovery.py +487 -0
  514. package/runtime/pi/egregore_runtime/spirit_discovery_cli.py +25 -0
  515. package/runtime/pi/egregore_runtime/spirit_discovery_transport.py +18 -0
  516. package/runtime/pi/egregore_runtime/status.py +101 -44
  517. package/runtime/pi/egregore_runtime/status_cli.py +13 -5
  518. package/runtime/pi/egregore_runtime/sync.py +39 -21
  519. package/runtime/pi/egregore_runtime/taste.py +934 -0
  520. package/runtime/pi/egregore_runtime/taste_cli.py +242 -0
  521. package/runtime/pi/egregore_runtime/taste_rules.py +91 -0
  522. package/runtime/pi/egregore_runtime/telemetry.py +26 -3
  523. package/runtime/pi/egregore_runtime/todo_cli.py +15 -12
  524. package/runtime/pi/egregore_runtime/todos.py +3 -2
  525. package/runtime/pi/egregore_runtime/upgrade.py +85 -58
  526. package/runtime/pi/egregore_runtime/writeback.py +330 -24
  527. package/runtime/pi/manifest.json +66 -5
  528. package/runtime/prime/.claude/context/handoff-claiming.md +32 -0
  529. package/runtime/prime/.claude/context/notification-consent.md +12 -9
  530. package/runtime/prime/.claude/skills/view/SKILL.md +438 -0
  531. package/runtime/prime/.codex/hooks.json +11 -0
  532. package/runtime/prime/.codex/skills/dark-mode/SKILL.md +1 -1
  533. package/runtime/prime/.codex/skills/records/SKILL.md +29 -0
  534. package/runtime/prime/.codex/skills/summon/SKILL.md +1 -1
  535. package/runtime/prime/.codex/skills/view/assets/compose-scaffold.html +19 -13
  536. package/runtime/prime/.codex/spec-manifest.json +13 -19
  537. package/runtime/prime/.prime/agent/APPEND_SYSTEM.md +53 -99
  538. package/runtime/prime/.prime/agent/extensions/egregore.ts +14 -1
  539. package/runtime/prime/.prime/agent/skills/egregore/SKILL.md +14 -6
  540. package/runtime/prime/.prime/agent/skills/egregore/src/egregore/__init__.py +66 -1
  541. package/runtime/prime/.prime/agent/spec-manifest.json +2 -2
  542. package/runtime/prime/AGENTS.md +54 -105
  543. package/runtime/prime/bin/agent.sh +231 -7
  544. package/runtime/prime/bin/api-call.sh +44 -23
  545. package/runtime/prime/bin/artifact-register.sh +7 -1
  546. package/runtime/prime/bin/artifact-writeback.sh +1 -1
  547. package/runtime/prime/bin/artifacts.sh +66 -265
  548. package/runtime/prime/bin/attendant.sh +4 -1
  549. package/runtime/prime/bin/boundary-guard.cjs +1848 -0
  550. package/runtime/prime/bin/boundary-hook.sh +41 -0
  551. package/runtime/prime/bin/capture-run.sh +32 -2
  552. package/runtime/prime/bin/codex-render-spec.mjs +24 -46
  553. package/runtime/prime/bin/codex-session-start.sh +19 -0
  554. package/runtime/prime/bin/codex-skill-render.mjs +18 -0
  555. package/runtime/prime/bin/config-get.sh +10 -1
  556. package/runtime/prime/bin/connect-refresh.sh +16 -0
  557. package/runtime/prime/bin/corpus_map.py +5 -0
  558. package/runtime/prime/bin/data/capabilities/prime.json +1458 -0
  559. package/runtime/prime/bin/genui.sh +19 -0
  560. package/runtime/prime/bin/graph-batch.sh +1 -1
  561. package/runtime/prime/bin/graph-op.sh +14 -0
  562. package/runtime/prime/bin/graph.sh +1 -1
  563. package/runtime/prime/bin/handoff-pr-backfill.sh +1 -1
  564. package/runtime/prime/bin/handoff-run.sh +11 -6
  565. package/runtime/prime/bin/ingest-item.sh +1 -1
  566. package/runtime/prime/bin/ingest-surface.html +22 -4
  567. package/runtime/prime/bin/issue.sh +1 -1
  568. package/runtime/prime/bin/knowledge.sh +1 -1
  569. package/runtime/prime/bin/lib/card-delivery.sh +329 -0
  570. package/runtime/prime/bin/lib/config.sh +148 -3
  571. package/runtime/prime/bin/lib/context.sh +38 -67
  572. package/runtime/prime/bin/lib/genui-entry.py +36 -0
  573. package/runtime/prime/bin/lib/git-message.sh +25 -6
  574. package/runtime/prime/bin/lib/git-sync.sh +12 -4
  575. package/runtime/prime/bin/lib/greeting.sh +255 -28
  576. package/runtime/prime/bin/lib/handoff_meta.py +294 -0
  577. package/runtime/prime/bin/lib/health-footer.sh +83 -2
  578. package/runtime/prime/bin/lib/identity.sh +31 -10
  579. package/runtime/prime/bin/lib/instance-registry.sh +71 -0
  580. package/runtime/prime/bin/lib/local_ci_receipt.py +249 -0
  581. package/runtime/prime/bin/lib/metrics.sh +121 -8
  582. package/runtime/prime/bin/lib/notices.sh +36 -0
  583. package/runtime/prime/bin/lib/record_outcomes.py +729 -0
  584. package/runtime/prime/bin/lib/reflexes.py +267 -0
  585. package/runtime/prime/bin/lib/retrieval_scorecard.py +624 -0
  586. package/runtime/prime/bin/lib/session-record-prompt.txt +18 -0
  587. package/runtime/prime/bin/lib/session_record.py +3747 -0
  588. package/runtime/prime/bin/lib/session_trail.py +1983 -0
  589. package/runtime/prime/bin/lib/settings-drift.sh +8 -5
  590. package/runtime/prime/bin/lib/startup-restart.sh +41 -0
  591. package/runtime/prime/bin/lib/worktree-links.sh +92 -0
  592. package/runtime/prime/bin/memory-push.sh +6 -0
  593. package/runtime/prime/bin/merge-gate.sh +498 -0
  594. package/runtime/prime/bin/notify.sh +5 -2
  595. package/runtime/prime/bin/page-access.sh +103 -0
  596. package/runtime/prime/bin/person.sh +13 -2
  597. package/runtime/prime/bin/pi-branch-policy.mjs +37 -0
  598. package/runtime/prime/bin/pi-observe-context.mjs +97 -4
  599. package/runtime/prime/bin/pi-render-spec.mjs +0 -1
  600. package/runtime/prime/bin/pi-spirit-discovery.mjs +34 -0
  601. package/runtime/prime/bin/post-update.sh +181 -0
  602. package/runtime/prime/bin/prime-render-spec.mjs +0 -1
  603. package/runtime/prime/bin/prime-session-start.sh +3 -1
  604. package/runtime/prime/bin/publish-artifact.sh +89 -42
  605. package/runtime/prime/bin/qa-inventory.sh +2 -2
  606. package/runtime/prime/bin/quest.sh +1 -1
  607. package/runtime/prime/bin/records.sh +11 -0
  608. package/runtime/prime/bin/reflexes/05-vitals.sh +9 -0
  609. package/runtime/prime/bin/reflexes/10-open-items.sh +12 -0
  610. package/runtime/prime/bin/reflexes/15-retrieval.sh +8 -0
  611. package/runtime/prime/bin/reflexes/20-nightly.sh +100 -0
  612. package/runtime/prime/bin/reflexes/20-sandbox.sh +85 -0
  613. package/runtime/prime/bin/reflexes/30-cost.py +116 -0
  614. package/runtime/prime/bin/reflexes/40-shells.py +51 -0
  615. package/runtime/prime/bin/reflexes/README.md +61 -0
  616. package/runtime/prime/bin/reflexes.sh +7 -0
  617. package/runtime/prime/bin/runtime-identity.sh +140 -42
  618. package/runtime/prime/bin/screenshot.sh +77 -0
  619. package/runtime/prime/bin/scroll.sh +1 -1
  620. package/runtime/prime/bin/search.sh +31 -5
  621. package/runtime/prime/bin/session-autosave.sh +6 -3
  622. package/runtime/prime/bin/session-id.sh +147 -0
  623. package/runtime/prime/bin/session-log.sh +19 -0
  624. package/runtime/prime/bin/session-record.sh +26 -0
  625. package/runtime/prime/bin/settings.sh +13 -8
  626. package/runtime/prime/bin/spirit-discovery-claude.sh +4 -0
  627. package/runtime/prime/bin/spirit-discovery-codex.sh +4 -0
  628. package/runtime/prime/bin/spirit-discovery-pi.sh +4 -0
  629. package/runtime/prime/bin/spirit-discovery-prime.sh +4 -0
  630. package/runtime/prime/bin/spirit-discovery.sh +4 -0
  631. package/runtime/prime/bin/startup-check.sh +1 -1
  632. package/runtime/prime/bin/telemetry.sh +11 -4
  633. package/runtime/prime/bin/test-baseline.sh +33 -5
  634. package/runtime/prime/bin/todo.sh +1 -1
  635. package/runtime/prime/bin/worktree-create.sh +15 -45
  636. package/runtime/prime/bin/worktree.sh +6 -0
  637. package/runtime/prime/docs/spirit-discovery.md +96 -0
  638. package/runtime/prime/egregore_runtime/__init__.py +18 -12
  639. package/runtime/prime/egregore_runtime/access.py +608 -0
  640. package/runtime/prime/egregore_runtime/access_cli.py +190 -0
  641. package/runtime/prime/egregore_runtime/adapters/invitation.py +5 -3
  642. package/runtime/prime/egregore_runtime/adapters/qmd.py +521 -47
  643. package/runtime/prime/egregore_runtime/admin_entry.py +56 -0
  644. package/runtime/prime/egregore_runtime/artifacts.py +222 -16
  645. package/runtime/prime/egregore_runtime/contracts.py +27 -0
  646. package/runtime/prime/egregore_runtime/eligibility.py +5 -12
  647. package/runtime/prime/egregore_runtime/episode_binding.py +16 -5
  648. package/runtime/prime/egregore_runtime/github_issues.py +2 -2
  649. package/runtime/prime/egregore_runtime/handoff_lookup.py +9 -20
  650. package/runtime/prime/egregore_runtime/harness_cli.py +674 -149
  651. package/runtime/prime/egregore_runtime/held.py +2436 -0
  652. package/runtime/prime/egregore_runtime/hosted_api.py +137 -0
  653. package/runtime/prime/egregore_runtime/identity.py +196 -65
  654. package/runtime/prime/egregore_runtime/identity_transfer.py +6 -2
  655. package/runtime/prime/egregore_runtime/ingest_cli.py +2 -2
  656. package/runtime/prime/egregore_runtime/ingest_index_cli.py +4 -16
  657. package/runtime/prime/egregore_runtime/installation.py +119 -0
  658. package/runtime/prime/egregore_runtime/investigation.py +64 -41
  659. package/runtime/prime/egregore_runtime/investigation_guidance.py +16 -11
  660. package/runtime/prime/egregore_runtime/issue_cli.py +51 -32
  661. package/runtime/prime/egregore_runtime/issue_reporting.py +4 -3
  662. package/runtime/prime/egregore_runtime/issue_triage_service.py +2 -2
  663. package/runtime/prime/egregore_runtime/issues.py +12 -6
  664. package/runtime/prime/egregore_runtime/knowledge.py +331 -5
  665. package/runtime/prime/egregore_runtime/knowledge_cli.py +57 -17
  666. package/runtime/prime/egregore_runtime/lifecycle.py +88 -22
  667. package/runtime/prime/egregore_runtime/lifecycle_cli.py +21 -13
  668. package/runtime/prime/egregore_runtime/line_ids.py +10 -0
  669. package/runtime/prime/egregore_runtime/memory_push.py +349 -0
  670. package/runtime/prime/egregore_runtime/observe.py +64 -25
  671. package/runtime/prime/egregore_runtime/observers.py +19 -0
  672. package/runtime/prime/egregore_runtime/optional_modules.py +13 -0
  673. package/runtime/prime/egregore_runtime/outputs_cli.py +321 -0
  674. package/runtime/prime/egregore_runtime/policy.py +651 -24
  675. package/runtime/prime/egregore_runtime/private_result.py +2 -1
  676. package/runtime/prime/egregore_runtime/projection.py +946 -0
  677. package/runtime/prime/egregore_runtime/quest_cli.py +20 -23
  678. package/runtime/prime/egregore_runtime/question_cli.py +15 -11
  679. package/runtime/prime/egregore_runtime/questions.py +10 -10
  680. package/runtime/prime/egregore_runtime/quests.py +10 -10
  681. package/runtime/prime/egregore_runtime/record_publish.py +548 -0
  682. package/runtime/prime/egregore_runtime/record_subject.py +564 -0
  683. package/runtime/prime/egregore_runtime/research_ingest.py +2 -4
  684. package/runtime/prime/egregore_runtime/runtime.py +169 -60
  685. package/runtime/prime/egregore_runtime/sandbox.py +194 -0
  686. package/runtime/prime/egregore_runtime/scroll_cli.py +22 -32
  687. package/runtime/prime/egregore_runtime/scrolls.py +23 -15
  688. package/runtime/prime/egregore_runtime/shadow.py +262 -0
  689. package/runtime/prime/egregore_runtime/spirit_discovery.py +487 -0
  690. package/runtime/prime/egregore_runtime/spirit_discovery_cli.py +25 -0
  691. package/runtime/prime/egregore_runtime/spirit_discovery_transport.py +18 -0
  692. package/runtime/prime/egregore_runtime/status.py +101 -44
  693. package/runtime/prime/egregore_runtime/status_cli.py +13 -5
  694. package/runtime/prime/egregore_runtime/sync.py +39 -21
  695. package/runtime/prime/egregore_runtime/taste.py +934 -0
  696. package/runtime/prime/egregore_runtime/taste_cli.py +242 -0
  697. package/runtime/prime/egregore_runtime/taste_rules.py +91 -0
  698. package/runtime/prime/egregore_runtime/telemetry.py +26 -3
  699. package/runtime/prime/egregore_runtime/todo_cli.py +15 -12
  700. package/runtime/prime/egregore_runtime/todos.py +3 -2
  701. package/runtime/prime/egregore_runtime/upgrade.py +85 -58
  702. package/runtime/prime/egregore_runtime/writeback.py +330 -24
  703. package/runtime/prime/manifest.json +66 -5
  704. package/runtime/claude/bin/thread.sh +0 -9
  705. package/runtime/claude/egregore_runtime/admin_gate.py +0 -102
  706. package/runtime/claude/egregore_runtime/thread_cli.py +0 -173
  707. package/runtime/claude/egregore_runtime/threads.py +0 -521
  708. package/runtime/codex/bin/thread.sh +0 -9
  709. package/runtime/codex/egregore_runtime/admin_gate.py +0 -102
  710. package/runtime/codex/egregore_runtime/thread_cli.py +0 -173
  711. package/runtime/codex/egregore_runtime/threads.py +0 -521
  712. package/runtime/pi/bin/thread.sh +0 -9
  713. package/runtime/pi/egregore_runtime/admin_gate.py +0 -102
  714. package/runtime/pi/egregore_runtime/thread_cli.py +0 -173
  715. package/runtime/pi/egregore_runtime/threads.py +0 -521
  716. package/runtime/prime/bin/thread.sh +0 -9
  717. package/runtime/prime/egregore_runtime/admin_gate.py +0 -102
  718. package/runtime/prime/egregore_runtime/thread_cli.py +0 -173
  719. package/runtime/prime/egregore_runtime/threads.py +0 -521
@@ -8,18 +8,19 @@ knowledge artifact.
8
8
 
9
9
  from __future__ import annotations
10
10
 
11
- from dataclasses import dataclass
11
+ from dataclasses import dataclass, replace
12
12
  from datetime import UTC, datetime
13
13
  import json
14
14
  import os
15
15
  from pathlib import Path
16
16
  import re
17
- from typing import Callable, Iterable
17
+ from typing import Any, Callable, Iterable, Iterator, Mapping
18
18
 
19
19
  from .artifacts import (
20
20
  ARTIFACT_SCHEMA_VERSION,
21
21
  CanonicalDocument,
22
22
  artifact_id_from_path,
23
+ canonical_artifact_id,
23
24
  content_digest,
24
25
  normalize_markdown_body,
25
26
  split_frontmatter,
@@ -32,8 +33,9 @@ from .contracts import (
32
33
  SourceProvenance,
33
34
  WritebackEvent,
34
35
  )
36
+ from . import held
35
37
  from .errors import AuthorizationDenied
36
- from .policy import scope_allows_path
38
+ from .policy import allows, check, action_for
37
39
  from .runtime import EgregoreRuntime
38
40
 
39
41
 
@@ -41,6 +43,11 @@ KNOWLEDGE_SCHEMA_VERSION = "egregore-knowledge/v1"
41
43
  PRIVATE_NOTE_SCHEMA_VERSION = "egregore-private-note/v1"
42
44
  KNOWLEDGE_TYPES = frozenset({"decision", "finding", "pattern"})
43
45
  NOTE_TYPES = frozenset({"thought", "session", "retrospective", "journal"})
46
+ # Knowledge has two linked layers: a pattern states a general motif, and an
47
+ # evidence record (a finding with subtype ``evidence``) keeps the specific,
48
+ # dated, sourced case and points at its pattern with ``instance-of``.
49
+ RELATIONSHIP_VOCABULARY = ("instance-of", "responds-to", "applies", "supersedes", "relates-to")
50
+ SITUATION_KEYS = ("domains", "paths", "activities")
44
51
 
45
52
 
46
53
  class KnowledgeCaptureError(ValueError):
@@ -56,6 +63,165 @@ def _iso(value: datetime) -> str:
56
63
  return value.astimezone(UTC).isoformat().replace("+00:00", "Z")
57
64
 
58
65
 
66
+ def relationship_warnings(relationships: Iterable[ArtifactRelationship]) -> tuple[str, ...]:
67
+ """Unknown relations are kept as written and reported, never rejected."""
68
+
69
+ known = ", ".join(RELATIONSHIP_VOCABULARY)
70
+ return tuple(
71
+ f"relationship '{row.relation}' is not in the vocabulary ({known}); kept as written"
72
+ for row in relationships
73
+ if row.relation.strip().casefold() not in RELATIONSHIP_VOCABULARY
74
+ )
75
+
76
+
77
+ def normalize_situations(value: Mapping[str, Iterable[str]] | None) -> dict[str, list[str]]:
78
+ """Return ``{domains, paths, activities}`` or ``{}`` when nothing is given."""
79
+
80
+ if not value:
81
+ return {}
82
+ unknown = sorted(set(value) - set(SITUATION_KEYS))
83
+ if unknown:
84
+ raise KnowledgeCaptureError(f"unknown situation keys: {', '.join(unknown)}")
85
+ result = {
86
+ key: list(dict.fromkeys(" ".join(str(item).split()) for item in value.get(key, ()) if str(item).strip()))
87
+ for key in SITUATION_KEYS
88
+ }
89
+ for glob in result["paths"]:
90
+ if glob.startswith("/") or ".." in glob.split("/"):
91
+ raise KnowledgeCaptureError("situation paths are repository-relative globs")
92
+ return result if any(result.values()) else {}
93
+
94
+
95
+ def _relations(fields: Mapping[str, Any]) -> Iterator[tuple[str, str]]:
96
+ value = fields.get("relationships")
97
+ for item in value if isinstance(value, list) else ():
98
+ if isinstance(item, Mapping):
99
+ relation = str(item.get("relation") or item.get("type") or "")
100
+ target = str(item.get("target_id") or item.get("target") or item.get("id") or "")
101
+ if relation and target:
102
+ yield relation.strip().casefold(), target.strip()
103
+
104
+
105
+ def _relative(value: str) -> str:
106
+ return value.strip().replace("\\", "/").removeprefix("memory/")
107
+
108
+
109
+ # Situated patterns: a deterministic match of a pattern's situations against
110
+ # the paths someone is changing and the words they use. No model calls.
111
+ SITUATED_HINT_PREFIX = "Patterns that may apply: "
112
+ _STOPWORDS = frozenset(
113
+ "about after again also and any are been before being but can could did does doing for from had has have"
114
+ " how into its just more most not off once only other our out over own same should some such than that the"
115
+ " their them then there these they this those through too under until very was were what when where which"
116
+ " while who why will with would you your".split()
117
+ )
118
+
119
+
120
+ def _stem(token: str) -> str:
121
+ if token.endswith("sses"):
122
+ return token[:-2]
123
+ if len(token) > 5 and token.endswith("ing"):
124
+ return token[:-3]
125
+ if len(token) > 3 and token.endswith("s") and not token.endswith("ss"):
126
+ return token[:-1]
127
+ return token
128
+
129
+
130
+ def situation_tokens(text: str) -> frozenset[str]:
131
+ """Casefolded word stems of three or more letters, without stopwords."""
132
+
133
+ words = re.findall(r"[^\W_]+", str(text).casefold(), re.UNICODE)
134
+ return frozenset(_stem(word) for word in words if len(word) >= 3 and word not in _STOPWORDS)
135
+
136
+
137
+ def situation_glob_matches(glob: str, path: str) -> bool:
138
+ """Repository-relative glob: ``**`` crosses directories, ``*`` and ``?`` do not.
139
+
140
+ Only ``*``, ``**`` and ``?`` are wildcards; ``[`` is a literal character.
141
+ A glob without wildcards names a file or a directory prefix.
142
+ """
143
+
144
+ glob = glob.strip().strip("`").removeprefix("./")
145
+ path = path.strip().replace("\\", "/").removeprefix("./")
146
+ if not glob or not path:
147
+ return False
148
+ if not any(char in glob for char in "*?"):
149
+ prefix = glob.rstrip("/")
150
+ return path == prefix or path.startswith(prefix + "/")
151
+ parts, index = [], 0
152
+ while index < len(glob):
153
+ if glob.startswith("**/", index):
154
+ parts.append("(?:.*/)?")
155
+ index += 3
156
+ elif glob.startswith("**", index):
157
+ parts.append(".*")
158
+ index += 2
159
+ else:
160
+ char = glob[index]
161
+ parts.append("[^/]*" if char == "*" else "[^/]" if char == "?" else re.escape(char))
162
+ index += 1
163
+ return re.fullmatch("".join(parts), path) is not None
164
+
165
+
166
+ def _section(body: str, heading: str) -> str:
167
+ """Text under the first ``## <heading>…`` up to the next level-2 heading."""
168
+
169
+ match = re.search(rf"^##\s+{re.escape(heading)}[^\n]*\n(.*?)(?=^##\s|\Z)", body, re.I | re.M | re.S)
170
+ return match.group(1).strip() if match else ""
171
+
172
+
173
+ def motif_sentence(body: str, limit: int = 240) -> str:
174
+ """The first sentence of the Motif section, or of the first paragraph."""
175
+
176
+ text = _section(body, "Motif")
177
+ if not text:
178
+ paragraphs = [part for part in re.split(r"\n\s*\n", body) if part.strip() and not part.lstrip().startswith("#")]
179
+ text = paragraphs[0] if paragraphs else ""
180
+ text = " ".join(text.replace("*", "").split())
181
+ sentence = re.split(r"(?<=[.!?])\s+", text, maxsplit=1)[0]
182
+ return sentence if len(sentence) <= limit else sentence[: limit - 1].rstrip() + "…"
183
+
184
+
185
+ def _body_situations(body: str) -> dict[str, list[str]]:
186
+ """Read the template's ``Situations (for discovery)`` section for patterns
187
+ written before situations were structured frontmatter."""
188
+
189
+ lines: list[str] = []
190
+ for line in _section(body, "Situations").splitlines():
191
+ if re.match(r"^\s*[-*]\s", line) or not lines:
192
+ lines.append(line)
193
+ else:
194
+ lines[-1] += " " + line.strip()
195
+ result: dict[str, list[str]] = {}
196
+ for line in lines:
197
+ match = re.match(r"^\s*[-*]\s*(domains|paths|activities)\s*:\s*(.+)$", line, re.I)
198
+ if not match:
199
+ continue
200
+ key, value = match.group(1).casefold(), match.group(2)
201
+ items = re.findall(r"`([^`]+)`", value) if key == "paths" else re.split(r"[;,]", value)
202
+ result[key] = [item.strip() for item in items if item.strip() and not item.strip().startswith("-")]
203
+ return result
204
+
205
+
206
+ def _instances_any(fields: Mapping[str, Any], keys: set[str]) -> bool:
207
+ return any(
208
+ relation == "instance-of" and (target in keys or _relative(target) in keys)
209
+ for relation, target in _relations(fields)
210
+ )
211
+
212
+
213
+ def situated_hint_line(title: str, canonical_path: str, limit: int = 200) -> str:
214
+ """One offer line within ``limit`` characters, or "" when the path alone is too long."""
215
+
216
+ room = limit - len(SITUATED_HINT_PREFIX) - len(canonical_path) - 3
217
+ title = " ".join(str(title).split())
218
+ if room < 8:
219
+ return ""
220
+ if len(title) > room:
221
+ title = title[: room - 1].rstrip() + "…"
222
+ return f"{SITUATED_HINT_PREFIX}{title} ({canonical_path})"
223
+
224
+
59
225
  def _authorized(runtime: EgregoreRuntime, actor: ActorContext, permission: Permission, resource: str) -> None:
60
226
  decision = runtime.authorize(actor, permission, (resource,))
61
227
  if not decision.allowed:
@@ -105,10 +271,17 @@ class CanonicalKnowledgeService:
105
271
  supersedes: Iterable[str] = (),
106
272
  source_provenance: Iterable[SourceProvenance] = (),
107
273
  policy_hints: dict[str, object] | None = None,
274
+ situations: Mapping[str, Iterable[str]] | None = None,
108
275
  ) -> WritebackEvent:
109
276
  kind = artifact_type.strip().casefold()
110
277
  if kind not in KNOWLEDGE_TYPES:
111
278
  raise KnowledgeCaptureError("knowledge type must be decision, finding, or pattern")
279
+ situated = normalize_situations(situations)
280
+ if situated and kind != "pattern":
281
+ raise KnowledgeCaptureError("situations describe patterns only; use --type pattern")
282
+ if subtype and subtype.strip().casefold() == "evidence" and kind != "finding":
283
+ raise KnowledgeCaptureError("evidence records are findings; use --type finding --subtype evidence")
284
+ relationships = tuple(relationships)
112
285
  clean_title = " ".join(title.split()).strip()
113
286
  if not clean_title:
114
287
  raise KnowledgeCaptureError("knowledge title is required")
@@ -126,7 +299,7 @@ class CanonicalKnowledgeService:
126
299
  raise AuthorizationDenied(
127
300
  "; ".join(decision.reasons) or "knowledge write denied"
128
301
  )
129
- if not scope_allows_path(f"memory/{relative}", decision.scopes):
302
+ if not allows(decision, f"memory/{relative}", action_for(decision.permission), root=self.runtime.canonical_root):
130
303
  raise AuthorizationDenied(
131
304
  "knowledge path is outside the actor's authorized scope"
132
305
  )
@@ -166,7 +339,10 @@ class CanonicalKnowledgeService:
166
339
  }
167
340
  if subtype:
168
341
  legacy["subtype"] = subtype.strip()
169
- return self.runtime.write_document(
342
+ if situated:
343
+ legacy["situations"] = situated
344
+ warnings = relationship_warnings(relationships) + self._instance_warnings(actor, relationships)
345
+ receipt = self.runtime.write_document(
170
346
  actor,
171
347
  CanonicalDocument(
172
348
  artifact=artifact,
@@ -175,6 +351,156 @@ class CanonicalKnowledgeService:
175
351
  legacy_fields=legacy,
176
352
  ),
177
353
  )
354
+ return replace(receipt, warnings=tuple(receipt.warnings) + warnings) if warnings else receipt
355
+
356
+ def _readable(self, actor: ActorContext, under: str = "") -> Iterator[tuple[str, dict[str, Any], str]]:
357
+ """Yield ``(memory/path, frontmatter, body)`` for knowledge the actor may read."""
358
+
359
+ decision = self.runtime.authorize(actor, Permission.READ)
360
+ if not decision.allowed:
361
+ raise AuthorizationDenied("; ".join(decision.reasons) or "knowledge read denied")
362
+ base = self.memory_root / "knowledge" / under if under else self.memory_root / "knowledge"
363
+ for path in sorted(base.rglob("*.md")) if base.is_dir() else ():
364
+ relative = path.relative_to(self.memory_root).as_posix()
365
+ canonical = f"memory/{relative}"
366
+ verdict = check(decision, canonical, "read", root=self.memory_root)
367
+ if not verdict.allowed:
368
+ continue
369
+ if not path.is_file():
370
+ continue
371
+ try:
372
+ text = verdict.resolved.read_text(encoding="utf-8")
373
+ fields, body = split_frontmatter(text)
374
+ artifact_id = canonical_artifact_id(text, canonical)
375
+ except (OSError, ValueError):
376
+ continue
377
+ # A grant limited to named artifacts reads only those artifacts.
378
+ if not self.runtime.authorize(actor, Permission.READ, (artifact_id,)).allowed:
379
+ continue
380
+ if fields:
381
+ fields["id"] = artifact_id
382
+ if not fields.get("type") and relative.startswith("knowledge/patterns/"):
383
+ fields["type"] = "pattern" # legacy patterns predate the typed envelope
384
+ yield canonical, fields, body
385
+
386
+ @staticmethod
387
+ def _patterns(rows: Iterable[tuple[str, dict[str, Any], str]]) -> dict[str, tuple[str, dict[str, Any]]]:
388
+ """Map each pattern's stable id and relative path to its record."""
389
+
390
+ index: dict[str, tuple[str, dict[str, Any]]] = {}
391
+ for canonical, fields, _ in rows:
392
+ if str(fields.get("type") or "").casefold() == "pattern":
393
+ index[str(fields["id"])] = index[_relative(canonical)] = (canonical, fields)
394
+ return index
395
+
396
+ def _instance_warnings(
397
+ self, actor: ActorContext, relationships: Iterable[ArtifactRelationship]
398
+ ) -> tuple[str, ...]:
399
+ targets = [row.target_id for row in relationships if row.relation.strip().casefold() == "instance-of"]
400
+ if not targets:
401
+ return ()
402
+ try:
403
+ known = self._patterns(self._readable(actor))
404
+ except AuthorizationDenied:
405
+ return ()
406
+ return tuple(
407
+ f"instance-of target '{target}' is not a readable pattern; check the pattern id"
408
+ for target in targets
409
+ if target not in known and _relative(target) not in known
410
+ )
411
+
412
+ def instances(self, actor: ActorContext, pattern: str) -> dict[str, object]:
413
+ """Reverse lookup: records whose ``instance-of`` points at ``pattern``.
414
+
415
+ A typed scan of canonical frontmatter under the actor's read scope; no
416
+ ranking, no graph. ``pattern`` is a stable id or a canonical path.
417
+ """
418
+
419
+ rows = list(self._readable(actor))
420
+ patterns = self._patterns(rows)
421
+ found = patterns.get(pattern.strip()) or patterns.get(_relative(pattern))
422
+ if found is None:
423
+ raise KnowledgeCaptureError(f"no readable pattern matches {pattern.strip()}")
424
+ canonical, fields = found
425
+ keys = {str(fields["id"]), _relative(canonical)}
426
+
427
+ def summary(path: str, values: Mapping[str, Any]) -> dict[str, object]:
428
+ return {
429
+ "id": str(values["id"]),
430
+ "title": str(values.get("title") or Path(path).stem),
431
+ "type": str(values.get("type") or ""),
432
+ "subtype": str(values.get("subtype") or "") or None,
433
+ "canonical_path": path,
434
+ "created_at": str(values.get("created_at") or values.get("date") or ""),
435
+ }
436
+
437
+ matches = [
438
+ summary(path, values)
439
+ for path, values, _ in rows
440
+ if _instances_any(values, keys)
441
+ ]
442
+ matches.sort(key=lambda row: (str(row["created_at"]), str(row["canonical_path"])), reverse=True)
443
+ return {"pattern": summary(canonical, fields), "count": len(matches), "instances": matches}
444
+
445
+ def situated(
446
+ self,
447
+ actor: ActorContext,
448
+ *,
449
+ paths: Iterable[str] = (),
450
+ text: str = "",
451
+ limit: int = 3,
452
+ count_instances: bool = True,
453
+ ) -> dict[str, object]:
454
+ """Patterns whose situations are live for these paths and this text.
455
+
456
+ Path globs match the given repository paths (3 points per glob);
457
+ domains, activities and topics match by shared word stems (1 point
458
+ per stem). A pattern needs a path match, or two shared stems from at
459
+ least two of its phrases where one phrase matches strongly (all of its
460
+ stems, or at least two). One compound topic alone never fires, and
461
+ common verbs scattered across phrases ("add", "design") do not either.
462
+ Ties order by title, then path. Deterministic; no model calls.
463
+ """
464
+
465
+ changed = list(dict.fromkeys(str(path).strip() for path in paths if str(path).strip()))
466
+ words = situation_tokens(text)
467
+ hits: list[dict[str, Any]] = []
468
+ for canonical, fields, body in self._readable(actor, "patterns"):
469
+ if str(fields.get("type") or "").casefold() != "pattern":
470
+ continue
471
+ given = fields.get("situations")
472
+ situations = given if isinstance(given, Mapping) and given else _body_situations(body)
473
+
474
+ def listed(key: str, source: Mapping[str, Any] = situations) -> list[str]:
475
+ value = source.get(key)
476
+ return [str(item) for item in value] if isinstance(value, list) else []
477
+
478
+ globs = sorted({glob for glob in listed("paths") for path in changed if situation_glob_matches(glob, path)})
479
+ stems = [situation_tokens(phrase) for phrase in
480
+ (*listed("domains"), *listed("activities"), *listed("topics", fields))]
481
+ phrases = [phrase & words for phrase in stems]
482
+ shared = sorted(set().union(*phrases))
483
+ strong = any(len(hit) >= 2 or (hit and hit == full) for hit, full in zip(phrases, stems))
484
+ if not globs and (not strong or len(shared) < 2 or sum(1 for phrase in phrases if phrase) < 2):
485
+ continue
486
+ hits.append({
487
+ "id": str(fields["id"]),
488
+ "title": str(fields.get("title") or Path(canonical).stem),
489
+ "canonical_path": canonical,
490
+ "motif": motif_sentence(body),
491
+ "score": 3 * len(globs) + len(shared),
492
+ "matched": {"paths": globs, "words": shared},
493
+ })
494
+ hits.sort(key=lambda hit: (-hit["score"], hit["title"].casefold(), hit["canonical_path"]))
495
+ # Packaged here, so the prompt hook's offer line and the CLI hand out the same redacted hits.
496
+ selected = held.kept(self.runtime.authorize(actor, Permission.READ), hits[: max(0, limit)],
497
+ root=self.memory_root, path="canonical_path")
498
+ if count_instances and selected:
499
+ rows = list(self._readable(actor))
500
+ for hit in selected:
501
+ keys = {hit["id"], _relative(hit["canonical_path"])}
502
+ hit["instances"] = sum(1 for _, values, _ in rows if _instances_any(values, keys))
503
+ return {"count": len(selected), "total": len(hits), "patterns": selected}
178
504
 
179
505
 
180
506
  @dataclass(frozen=True, slots=True)
@@ -9,9 +9,11 @@ from pathlib import Path
9
9
  import sys
10
10
  from uuid import uuid4
11
11
 
12
+ from . import held
12
13
  from .contracts import ArtifactRelationship
13
14
  from .errors import AuthorizationDenied
14
15
  from .knowledge import (
16
+ RELATIONSHIP_VOCABULARY,
15
17
  CanonicalKnowledgeService,
16
18
  KnowledgeCaptureError,
17
19
  PersonalNoteService,
@@ -36,6 +38,10 @@ def _context(*, push_remote: bool):
36
38
  return actor, knowledge, notes
37
39
 
38
40
 
41
+ def _emit(knowledge, actor, value, **options) -> int:
42
+ return held.emit_for(knowledge.runtime, actor, value, **options)
43
+
44
+
39
45
  def _input(path: str) -> str:
40
46
  return read_and_consume(path, _root()).decode("utf-8")
41
47
 
@@ -76,39 +82,58 @@ def _create(args: argparse.Namespace) -> int:
76
82
  subtype=args.subtype,
77
83
  relationships=_relationships(args.relationship),
78
84
  supersedes=args.supersedes,
85
+ situations={
86
+ "domains": args.situation_domain,
87
+ "paths": args.situation_path,
88
+ "activities": args.situation_activity,
89
+ },
79
90
  )
80
- print(json.dumps(_receipt(receipt), ensure_ascii=False))
81
- return 0 if receipt.status.value in {"accepted", "partial"} else 1
91
+ return _emit(knowledge, actor, _receipt(receipt)) or (0 if receipt.status.value in {"accepted", "partial"} else 1)
92
+
93
+
94
+ def _instances(args: argparse.Namespace) -> int:
95
+ actor, knowledge, _ = _context(push_remote=False)
96
+ return _emit(knowledge, actor, knowledge.instances(actor, args.pattern))
97
+
98
+
99
+ def _situated(args: argparse.Namespace) -> int:
100
+ if not args.paths and not (args.text or "").strip():
101
+ raise KnowledgeCaptureError("give --paths, --text, or both")
102
+ actor, knowledge, _ = _context(push_remote=False)
103
+ found = knowledge.situated(actor, paths=args.paths, text=args.text or "", limit=args.n)
104
+ return _emit(knowledge, actor, found)
105
+
106
+
107
+ def _positive(value: str) -> int:
108
+ number = int(value)
109
+ if number < 1:
110
+ raise argparse.ArgumentTypeError("must be at least 1")
111
+ return number
82
112
 
83
113
 
84
114
  def _note_create(args: argparse.Namespace) -> int:
85
- actor, _, notes = _context(push_remote=False)
115
+ actor, knowledge, notes = _context(push_remote=False)
86
116
  note = notes.create(
87
117
  actor,
88
118
  title=args.title,
89
119
  body=_input(args.input),
90
120
  note_type=args.type,
91
121
  )
92
- print(json.dumps(note.to_dict(), ensure_ascii=False))
93
- return 0
122
+ return _emit(knowledge, actor, note.to_dict())
94
123
 
95
124
 
96
125
  def _note_list(args: argparse.Namespace) -> int:
97
- actor, _, notes = _context(push_remote=False)
98
- rows = notes.list(actor)
99
- print(json.dumps({"notes": [row.to_dict() for row in rows]}, ensure_ascii=False))
100
- return 0
126
+ actor, knowledge, notes = _context(push_remote=False)
127
+ return _emit(knowledge, actor, {"notes": [row.to_dict() for row in notes.list(actor)]})
101
128
 
102
129
 
103
130
  def _note_open(args: argparse.Namespace) -> int:
104
- actor, _, notes = _context(push_remote=False)
105
- note = notes.open(actor, args.path)
106
- print(note.body, end="")
107
- return 0
131
+ actor, knowledge, notes = _context(push_remote=False)
132
+ return _emit(knowledge, actor, notes.open(actor, args.path).body, render=str, end="")
108
133
 
109
134
 
110
135
  def _note_promote(args: argparse.Namespace) -> int:
111
- actor, _, notes = _context(push_remote=not args.no_push)
136
+ actor, knowledge, notes = _context(push_remote=not args.no_push)
112
137
  receipt = notes.promote(
113
138
  actor,
114
139
  raw_path=args.path,
@@ -117,8 +142,7 @@ def _note_promote(args: argparse.Namespace) -> int:
117
142
  topics=args.topic,
118
143
  workstream=args.workstream,
119
144
  )
120
- print(json.dumps(_receipt(receipt), ensure_ascii=False))
121
- return 0 if receipt.status.value in {"accepted", "partial"} else 1
145
+ return _emit(knowledge, actor, _receipt(receipt)) or (0 if receipt.status.value in {"accepted", "partial"} else 1)
122
146
 
123
147
 
124
148
  def _parser() -> argparse.ArgumentParser:
@@ -132,11 +156,27 @@ def _parser() -> argparse.ArgumentParser:
132
156
  create.add_argument("--topic", action="append", default=[])
133
157
  create.add_argument("--workstream")
134
158
  create.add_argument("--subtype")
135
- create.add_argument("--relationship", action="append", default=[])
159
+ create.add_argument(
160
+ "--relationship", action="append", default=[], metavar="RELATION:TARGET",
161
+ help=f"repeatable; relations: {', '.join(RELATIONSHIP_VOCABULARY)} (others warn)",
162
+ )
136
163
  create.add_argument("--supersedes", action="append", default=[])
164
+ create.add_argument("--situation-domain", action="append", default=[], help="pattern only; repeatable")
165
+ create.add_argument("--situation-path", action="append", default=[], help="pattern only; repo-relative glob")
166
+ create.add_argument("--situation-activity", action="append", default=[], help="pattern only; repeatable")
137
167
  create.add_argument("--no-push", action="store_true")
138
168
  create.set_defaults(handler=_create)
139
169
 
170
+ instances = commands.add_parser("instances", help="records whose instance-of points at a pattern")
171
+ instances.add_argument("pattern", help="pattern id or canonical path")
172
+ instances.set_defaults(handler=_instances)
173
+
174
+ situated = commands.add_parser("situated", help="patterns whose situations match these paths or words")
175
+ situated.add_argument("--paths", nargs="+", action="extend", default=[], metavar="PATH")
176
+ situated.add_argument("--text", help="what is being worked on, in words")
177
+ situated.add_argument("-n", type=_positive, default=3, help="at most this many patterns (default 3)")
178
+ situated.set_defaults(handler=_situated)
179
+
140
180
  note_create = commands.add_parser("note-create")
141
181
  note_create.add_argument("--title", required=True)
142
182
  note_create.add_argument("--type", choices=("thought", "session", "retrospective", "journal"), default="thought")