mirror-mind 0.0.1 → 0.32.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 (467) hide show
  1. package/.claude/hooks/log-session-end.sh +85 -0
  2. package/.claude/hooks/log-user-prompt.sh +85 -0
  3. package/.claude/hooks/mirror-inject.sh +85 -0
  4. package/.claude/hooks/session-start.sh +85 -0
  5. package/.gemini/hooks/log-assistant.sh +85 -0
  6. package/.gemini/hooks/log-user.sh +85 -0
  7. package/.gemini/hooks/session-end.sh +85 -0
  8. package/.gemini/hooks/session-start.sh +85 -0
  9. package/.pi/extensions/mirror-logger.ts +561 -0
  10. package/.pi/skills/mm-backup/SKILL.md +21 -0
  11. package/.pi/skills/mm-build/SKILL.md +1206 -0
  12. package/.pi/skills/mm-consolidate/SKILL.md +112 -0
  13. package/.pi/skills/mm-consult/SKILL.md +70 -0
  14. package/.pi/skills/mm-conversations/SKILL.md +16 -0
  15. package/.pi/skills/mm-discard/SKILL.md +39 -0
  16. package/.pi/skills/mm-explore/SKILL.md +255 -0
  17. package/.pi/skills/mm-help/SKILL.md +74 -0
  18. package/.pi/skills/mm-identity/SKILL.md +96 -0
  19. package/.pi/skills/mm-journal/SKILL.md +16 -0
  20. package/.pi/skills/mm-journey/SKILL.md +23 -0
  21. package/.pi/skills/mm-journeys/SKILL.md +15 -0
  22. package/.pi/skills/mm-memories/SKILL.md +17 -0
  23. package/.pi/skills/mm-mirror/SKILL.md +98 -0
  24. package/.pi/skills/mm-mute/SKILL.md +18 -0
  25. package/.pi/skills/mm-new/SKILL.md +18 -0
  26. package/.pi/skills/mm-recall/SKILL.md +17 -0
  27. package/.pi/skills/mm-release-notes/SKILL.md +52 -0
  28. package/.pi/skills/mm-seed/SKILL.md +19 -0
  29. package/.pi/skills/mm-shadow/SKILL.md +117 -0
  30. package/.pi/skills/mm-soul/SKILL.md +502 -0
  31. package/.pi/skills/mm-tasks/SKILL.md +24 -0
  32. package/.pi/skills/mm-update/SKILL.md +32 -0
  33. package/.pi/skills/mm-week/SKILL.md +28 -0
  34. package/.pi/skills/mm-welcome/SKILL.md +24 -0
  35. package/AGENTS.md +188 -0
  36. package/README.md +126 -9
  37. package/bin/loader.js +67 -0
  38. package/bin/mirror-hook.js +7 -0
  39. package/bin/mirror.js +7 -0
  40. package/docs/releases/index.md +99 -0
  41. package/docs/releases/pending-cutoffs.md +510 -0
  42. package/docs/releases/v0.10.0.md +123 -0
  43. package/docs/releases/v0.10.1.md +66 -0
  44. package/docs/releases/v0.10.10.md +51 -0
  45. package/docs/releases/v0.10.11.md +39 -0
  46. package/docs/releases/v0.10.12.md +57 -0
  47. package/docs/releases/v0.10.13.md +35 -0
  48. package/docs/releases/v0.10.14.md +42 -0
  49. package/docs/releases/v0.10.15.md +53 -0
  50. package/docs/releases/v0.10.16.md +35 -0
  51. package/docs/releases/v0.10.17.md +43 -0
  52. package/docs/releases/v0.10.2.md +64 -0
  53. package/docs/releases/v0.10.3.md +78 -0
  54. package/docs/releases/v0.10.4.md +42 -0
  55. package/docs/releases/v0.10.5.md +57 -0
  56. package/docs/releases/v0.10.6.md +47 -0
  57. package/docs/releases/v0.10.7.md +53 -0
  58. package/docs/releases/v0.10.8.md +49 -0
  59. package/docs/releases/v0.10.9.md +37 -0
  60. package/docs/releases/v0.11.0.md +86 -0
  61. package/docs/releases/v0.12.0.md +67 -0
  62. package/docs/releases/v0.13.0.md +64 -0
  63. package/docs/releases/v0.14.0.md +68 -0
  64. package/docs/releases/v0.15.0.md +48 -0
  65. package/docs/releases/v0.16.0.md +50 -0
  66. package/docs/releases/v0.16.1.md +55 -0
  67. package/docs/releases/v0.16.2.md +36 -0
  68. package/docs/releases/v0.17.0.md +50 -0
  69. package/docs/releases/v0.17.1.md +58 -0
  70. package/docs/releases/v0.17.2.md +42 -0
  71. package/docs/releases/v0.17.3.md +58 -0
  72. package/docs/releases/v0.18.0.md +84 -0
  73. package/docs/releases/v0.18.1.md +51 -0
  74. package/docs/releases/v0.18.2.md +69 -0
  75. package/docs/releases/v0.19.0.md +85 -0
  76. package/docs/releases/v0.19.1.md +46 -0
  77. package/docs/releases/v0.20.0.md +85 -0
  78. package/docs/releases/v0.21.0.md +61 -0
  79. package/docs/releases/v0.21.1.md +47 -0
  80. package/docs/releases/v0.22.0.md +77 -0
  81. package/docs/releases/v0.22.1.md +42 -0
  82. package/docs/releases/v0.23.0.md +105 -0
  83. package/docs/releases/v0.24.0.md +125 -0
  84. package/docs/releases/v0.25.0.md +107 -0
  85. package/docs/releases/v0.26.0.md +139 -0
  86. package/docs/releases/v0.26.1.md +65 -0
  87. package/docs/releases/v0.26.2.md +43 -0
  88. package/docs/releases/v0.27.0.md +63 -0
  89. package/docs/releases/v0.28.0.md +59 -0
  90. package/docs/releases/v0.29.0.md +61 -0
  91. package/docs/releases/v0.29.1.md +59 -0
  92. package/docs/releases/v0.29.2.md +59 -0
  93. package/docs/releases/v0.29.3.md +93 -0
  94. package/docs/releases/v0.30.0.md +75 -0
  95. package/docs/releases/v0.30.1.md +93 -0
  96. package/docs/releases/v0.30.2.md +66 -0
  97. package/docs/releases/v0.31.0.md +122 -0
  98. package/docs/releases/v0.31.1.md +142 -0
  99. package/docs/releases/v0.31.10.md +118 -0
  100. package/docs/releases/v0.31.11.md +88 -0
  101. package/docs/releases/v0.31.12.md +105 -0
  102. package/docs/releases/v0.31.13.md +99 -0
  103. package/docs/releases/v0.31.14.md +97 -0
  104. package/docs/releases/v0.31.2.md +115 -0
  105. package/docs/releases/v0.31.3.md +142 -0
  106. package/docs/releases/v0.31.4.md +117 -0
  107. package/docs/releases/v0.31.5.md +155 -0
  108. package/docs/releases/v0.31.6.md +132 -0
  109. package/docs/releases/v0.31.7.md +132 -0
  110. package/docs/releases/v0.31.8.md +120 -0
  111. package/docs/releases/v0.31.9.md +120 -0
  112. package/docs/releases/v0.32.0.md +261 -0
  113. package/docs/releases/v0.32.1.md +59 -0
  114. package/docs/releases/v0.8.0.md +118 -0
  115. package/docs/releases/v0.9.0.md +98 -0
  116. package/docs/releases/v0.9.1.md +83 -0
  117. package/package.json +102 -6
  118. package/plugins/mirror-mind/.claude-plugin/plugin.json +13 -0
  119. package/plugins/mirror-mind/hooks/hooks.json +50 -0
  120. package/plugins/mirror-mind/hooks/log-session-end.sh +98 -0
  121. package/plugins/mirror-mind/hooks/log-user-prompt.sh +98 -0
  122. package/plugins/mirror-mind/hooks/mirror-inject.sh +98 -0
  123. package/plugins/mirror-mind/hooks/session-start.sh +98 -0
  124. package/plugins/mirror-mind/mcp/launch.sh +52 -0
  125. package/plugins/mirror-mind/skills/mm-backup/SKILL.md +27 -0
  126. package/plugins/mirror-mind/skills/mm-build/SKILL.md +1206 -0
  127. package/plugins/mirror-mind/skills/mm-consolidate/SKILL.md +112 -0
  128. package/plugins/mirror-mind/skills/mm-consult/SKILL.md +83 -0
  129. package/plugins/mirror-mind/skills/mm-conversations/SKILL.md +17 -0
  130. package/plugins/mirror-mind/skills/mm-discard/SKILL.md +40 -0
  131. package/plugins/mirror-mind/skills/mm-explore/SKILL.md +241 -0
  132. package/plugins/mirror-mind/skills/mm-help/SKILL.md +104 -0
  133. package/plugins/mirror-mind/skills/mm-identity/SKILL.md +96 -0
  134. package/plugins/mirror-mind/skills/mm-journal/SKILL.md +21 -0
  135. package/plugins/mirror-mind/skills/mm-journey/SKILL.md +37 -0
  136. package/plugins/mirror-mind/skills/mm-journeys/SKILL.md +15 -0
  137. package/plugins/mirror-mind/skills/mm-memories/SKILL.md +20 -0
  138. package/plugins/mirror-mind/skills/mm-mirror/SKILL.md +65 -0
  139. package/plugins/mirror-mind/skills/mm-mute/SKILL.md +18 -0
  140. package/plugins/mirror-mind/skills/mm-new/SKILL.md +18 -0
  141. package/plugins/mirror-mind/skills/mm-recall/SKILL.md +17 -0
  142. package/plugins/mirror-mind/skills/mm-release-notes/SKILL.md +31 -0
  143. package/plugins/mirror-mind/skills/mm-seed/SKILL.md +15 -0
  144. package/plugins/mirror-mind/skills/mm-shadow/SKILL.md +117 -0
  145. package/plugins/mirror-mind/skills/mm-soul/SKILL.md +488 -0
  146. package/plugins/mirror-mind/skills/mm-tasks/SKILL.md +36 -0
  147. package/plugins/mirror-mind/skills/mm-update/SKILL.md +30 -0
  148. package/plugins/mirror-mind/skills/mm-week/SKILL.md +46 -0
  149. package/plugins/mirror-mind/skills/mm-welcome/SKILL.md +22 -0
  150. package/scripts/codex-hooks/session-end.sh +85 -0
  151. package/scripts/codex-hooks/session-start.sh +85 -0
  152. package/scripts/codex-mirror.sh +48 -0
  153. package/templates/identity/README.md +119 -0
  154. package/templates/identity/ego/behavior.yaml +52 -0
  155. package/templates/identity/ego/constraints.yaml +15 -0
  156. package/templates/identity/ego/identity.yaml +43 -0
  157. package/templates/identity/journeys/personal-growth.yaml +39 -0
  158. package/templates/identity/organization/identity.yaml +24 -0
  159. package/templates/identity/organization/principles.yaml +17 -0
  160. package/templates/identity/personas/coach.yaml +94 -0
  161. package/templates/identity/personas/designer.yaml +93 -0
  162. package/templates/identity/personas/doctor.yaml +96 -0
  163. package/templates/identity/personas/engineer.yaml +77 -0
  164. package/templates/identity/personas/financial.yaml +97 -0
  165. package/templates/identity/personas/prompt-engineer.yaml +122 -0
  166. package/templates/identity/personas/researcher.yaml +88 -0
  167. package/templates/identity/personas/strategist.yaml +91 -0
  168. package/templates/identity/personas/teacher.yaml +95 -0
  169. package/templates/identity/personas/therapist.yaml +91 -0
  170. package/templates/identity/personas/thinker.yaml +74 -0
  171. package/templates/identity/personas/writer.yaml +76 -0
  172. package/templates/identity/self/config.yaml +16 -0
  173. package/templates/identity/self/soul.yaml +105 -0
  174. package/templates/identity/user/identity.yaml +30 -0
  175. package/ts/src/backup/staging.ts +62 -0
  176. package/ts/src/backup/zipBackup.ts +267 -0
  177. package/ts/src/backup/zipReader.ts +109 -0
  178. package/ts/src/backup/zipWriter.ts +148 -0
  179. package/ts/src/builder/activeCheckpoint.ts +134 -0
  180. package/ts/src/builder/aggregateStatus.ts +34 -0
  181. package/ts/src/builder/approve.ts +149 -0
  182. package/ts/src/builder/argv.ts +292 -0
  183. package/ts/src/builder/ariadMethod.ts +536 -0
  184. package/ts/src/builder/artifacts/artifactSurfaces.ts +106 -0
  185. package/ts/src/builder/artifacts/artifactWriter.ts +113 -0
  186. package/ts/src/builder/artifacts/closureArtifacts.ts +199 -0
  187. package/ts/src/builder/artifacts/deliveryStoryArtifacts.ts +316 -0
  188. package/ts/src/builder/artifacts/planArtifacts.ts +242 -0
  189. package/ts/src/builder/artifacts/scaffoldSections.ts +273 -0
  190. package/ts/src/builder/artifacts/scaffoldState.ts +227 -0
  191. package/ts/src/builder/artifacts/storyFiles.ts +61 -0
  192. package/ts/src/builder/artifacts/storyIndex.ts +166 -0
  193. package/ts/src/builder/cadence.ts +59 -0
  194. package/ts/src/builder/card.ts +192 -0
  195. package/ts/src/builder/checkpointRefused.ts +66 -0
  196. package/ts/src/builder/closure.ts +964 -0
  197. package/ts/src/builder/commands.ts +2053 -0
  198. package/ts/src/builder/cursorPosition.ts +176 -0
  199. package/ts/src/builder/cursorTransitions.ts +77 -0
  200. package/ts/src/builder/deliveryCursor.ts +577 -0
  201. package/ts/src/builder/deliveryStoryClosure.ts +521 -0
  202. package/ts/src/builder/deliveryStoryPlan.ts +536 -0
  203. package/ts/src/builder/deliveryStoryReady.ts +160 -0
  204. package/ts/src/builder/deliveryStoryRoadmapClosure.ts +151 -0
  205. package/ts/src/builder/expand.ts +336 -0
  206. package/ts/src/builder/flowUnit.ts +339 -0
  207. package/ts/src/builder/homeSurface.ts +122 -0
  208. package/ts/src/builder/implementationGuard.ts +125 -0
  209. package/ts/src/builder/index.ts +11 -0
  210. package/ts/src/builder/lifecycleRefusal.ts +92 -0
  211. package/ts/src/builder/lifecycleRibbon.ts +159 -0
  212. package/ts/src/builder/load.ts +313 -0
  213. package/ts/src/builder/methodAdoption.ts +113 -0
  214. package/ts/src/builder/methodDefinition.ts +429 -0
  215. package/ts/src/builder/methodInspection.ts +371 -0
  216. package/ts/src/builder/plan.ts +408 -0
  217. package/ts/src/builder/planPreauthorization.ts +265 -0
  218. package/ts/src/builder/prepare.ts +225 -0
  219. package/ts/src/builder/projectPaths.ts +64 -0
  220. package/ts/src/builder/pull.ts +356 -0
  221. package/ts/src/builder/pullCandidates.ts +463 -0
  222. package/ts/src/builder/pullCandidatesRender.ts +238 -0
  223. package/ts/src/builder/refinementField.ts +93 -0
  224. package/ts/src/builder/releaseIntent.ts +173 -0
  225. package/ts/src/builder/resumeState.ts +84 -0
  226. package/ts/src/builder/resumeSurface.ts +149 -0
  227. package/ts/src/builder/roadmapGrammar.ts +300 -0
  228. package/ts/src/builder/roadmapScan.ts +97 -0
  229. package/ts/src/builder/roadmapScope.ts +210 -0
  230. package/ts/src/builder/scopePhrases.ts +146 -0
  231. package/ts/src/builder/storyPaths.ts +196 -0
  232. package/ts/src/builder/storyPlanPreauthorization.ts +287 -0
  233. package/ts/src/builder/surfaceProtocol.ts +42 -0
  234. package/ts/src/builder/templateGeneration.ts +148 -0
  235. package/ts/src/builder/transition.ts +178 -0
  236. package/ts/src/consult/args.ts +99 -0
  237. package/ts/src/consult/core.ts +122 -0
  238. package/ts/src/consult/modelCatalog.ts +55 -0
  239. package/ts/src/consult/render.ts +47 -0
  240. package/ts/src/conversation/append.ts +414 -0
  241. package/ts/src/conversation/backfill.ts +387 -0
  242. package/ts/src/conversation/closeTail.ts +467 -0
  243. package/ts/src/conversation/extraction.ts +333 -0
  244. package/ts/src/conversation/extractionDriver.ts +130 -0
  245. package/ts/src/conversation/extractionRun.ts +70 -0
  246. package/ts/src/conversation/journeyInference.ts +165 -0
  247. package/ts/src/conversation/journeyRepair.ts +114 -0
  248. package/ts/src/conversation/lifecycleFaces.ts +197 -0
  249. package/ts/src/conversation/lifecycleWrites.ts +313 -0
  250. package/ts/src/conversation/listing.ts +77 -0
  251. package/ts/src/conversation/logger.ts +576 -0
  252. package/ts/src/conversation/loggerCli.ts +282 -0
  253. package/ts/src/conversation/loggerRuntime.ts +259 -0
  254. package/ts/src/conversation/metadataLifecycle.ts +443 -0
  255. package/ts/src/conversation/recall.ts +66 -0
  256. package/ts/src/conversation/sessionComposites.ts +258 -0
  257. package/ts/src/conversation/sessionImport.ts +90 -0
  258. package/ts/src/conversation/transcriptBackfill.ts +141 -0
  259. package/ts/src/cultivation/applyActions.ts +241 -0
  260. package/ts/src/cultivation/cluster.ts +86 -0
  261. package/ts/src/cultivation/consolidationStore.ts +281 -0
  262. package/ts/src/cultivation/promptContext.ts +62 -0
  263. package/ts/src/cultivation/propose.ts +305 -0
  264. package/ts/src/cultivation/scan.ts +155 -0
  265. package/ts/src/db/backupGate.ts +47 -0
  266. package/ts/src/db/bootstrap.ts +108 -0
  267. package/ts/src/db/bootstrapLock.ts +223 -0
  268. package/ts/src/db/copyGuard.ts +60 -0
  269. package/ts/src/db/database.ts +397 -0
  270. package/ts/src/db/decode.ts +66 -0
  271. package/ts/src/db/ftsIntegrity.ts +41 -0
  272. package/ts/src/db/migrateOnOpen.ts +214 -0
  273. package/ts/src/db/migrations.ts +576 -0
  274. package/ts/src/db/readyOnOpen.ts +35 -0
  275. package/ts/src/db/rowDecode.ts +42 -0
  276. package/ts/src/db/schema.ts +432 -0
  277. package/ts/src/db/schemaInventory.ts +225 -0
  278. package/ts/src/db/schemaInventorySnapshot.ts +2107 -0
  279. package/ts/src/db/schemaState.ts +91 -0
  280. package/ts/src/db/schemaTsDivergence.ts +114 -0
  281. package/ts/src/descriptor/descriptorRead.ts +49 -0
  282. package/ts/src/docs/docsLint.ts +265 -0
  283. package/ts/src/explorer/handoff.ts +522 -0
  284. package/ts/src/explorer/render.ts +335 -0
  285. package/ts/src/explorer/story.ts +769 -0
  286. package/ts/src/explorer/transition.ts +72 -0
  287. package/ts/src/extensions/bindings.ts +170 -0
  288. package/ts/src/extensions/catalog.ts +249 -0
  289. package/ts/src/extensions/catalogCommands.ts +513 -0
  290. package/ts/src/extensions/catalogWrites.ts +652 -0
  291. package/ts/src/extensions/contextRuntime.ts +325 -0
  292. package/ts/src/extensions/dispatch.ts +374 -0
  293. package/ts/src/extensions/errors.ts +24 -0
  294. package/ts/src/extensions/manifest.ts +356 -0
  295. package/ts/src/extensions/migrations.ts +350 -0
  296. package/ts/src/extraction/conversation.ts +304 -0
  297. package/ts/src/extraction/conversationMetadata.ts +206 -0
  298. package/ts/src/extraction/fencing.ts +106 -0
  299. package/ts/src/extraction/json.ts +17 -0
  300. package/ts/src/extraction/prompts.ts +470 -0
  301. package/ts/src/frontDoor/args.ts +30 -0
  302. package/ts/src/frontDoor/argvShape.ts +106 -0
  303. package/ts/src/frontDoor/buildLoadRuntime.ts +139 -0
  304. package/ts/src/frontDoor/buildRoute.ts +398 -0
  305. package/ts/src/frontDoor/cli.ts +1865 -0
  306. package/ts/src/frontDoor/cloneRoleGuard.ts +98 -0
  307. package/ts/src/frontDoor/consultRoute.ts +50 -0
  308. package/ts/src/frontDoor/contentTailRoute.ts +239 -0
  309. package/ts/src/frontDoor/conversationLoggerRoute.ts +73 -0
  310. package/ts/src/frontDoor/cultivationRoute.ts +210 -0
  311. package/ts/src/frontDoor/dbPath.ts +131 -0
  312. package/ts/src/frontDoor/dbSafetyToolsRoute.ts +240 -0
  313. package/ts/src/frontDoor/exploreRoute.ts +572 -0
  314. package/ts/src/frontDoor/extensionCatalogRoute.ts +252 -0
  315. package/ts/src/frontDoor/frontDoorLog.ts +57 -0
  316. package/ts/src/frontDoor/identityWrite.ts +36 -0
  317. package/ts/src/frontDoor/journeyWriteRoute.ts +26 -0
  318. package/ts/src/frontDoor/lifecycleWriteRoute.ts +110 -0
  319. package/ts/src/frontDoor/liveBackup.ts +100 -0
  320. package/ts/src/frontDoor/mirrorModeRoute.ts +163 -0
  321. package/ts/src/frontDoor/namedRefusal.ts +35 -0
  322. package/ts/src/frontDoor/nodeSupport.ts +24 -0
  323. package/ts/src/frontDoor/render/consolidate.ts +199 -0
  324. package/ts/src/frontDoor/render/conversations.ts +33 -0
  325. package/ts/src/frontDoor/render/cultivationShared.ts +21 -0
  326. package/ts/src/frontDoor/render/descriptor.ts +20 -0
  327. package/ts/src/frontDoor/render/detectPersona.ts +50 -0
  328. package/ts/src/frontDoor/render/icons.ts +13 -0
  329. package/ts/src/frontDoor/render/identity.ts +59 -0
  330. package/ts/src/frontDoor/render/identityRows.ts +17 -0
  331. package/ts/src/frontDoor/render/inspectPersona.ts +53 -0
  332. package/ts/src/frontDoor/render/journeyStatus.ts +78 -0
  333. package/ts/src/frontDoor/render/journeys.ts +79 -0
  334. package/ts/src/frontDoor/render/list.ts +41 -0
  335. package/ts/src/frontDoor/render/memories.ts +67 -0
  336. package/ts/src/frontDoor/render/recall.ts +49 -0
  337. package/ts/src/frontDoor/render/shadow.ts +155 -0
  338. package/ts/src/frontDoor/render/tasks.ts +99 -0
  339. package/ts/src/frontDoor/render/tasksImportSync.ts +63 -0
  340. package/ts/src/frontDoor/render/week.ts +113 -0
  341. package/ts/src/frontDoor/routing.ts +999 -0
  342. package/ts/src/frontDoor/runtimeRoute.ts +623 -0
  343. package/ts/src/frontDoor/searchRoute.ts +166 -0
  344. package/ts/src/frontDoor/seedPaths.ts +98 -0
  345. package/ts/src/frontDoor/soulRoute.ts +433 -0
  346. package/ts/src/frontDoor/tasksImportSyncRoute.ts +110 -0
  347. package/ts/src/frontDoor/tasksWriteRoute.ts +101 -0
  348. package/ts/src/frontDoor/usage.ts +176 -0
  349. package/ts/src/guards/claudePlugin.ts +252 -0
  350. package/ts/src/guards/packContents.ts +306 -0
  351. package/ts/src/guards/piSourcedSkills.ts +70 -0
  352. package/ts/src/guards/retiredRouteShadows.ts +57 -0
  353. package/ts/src/guards/retiredSurfaces.ts +564 -0
  354. package/ts/src/hooks/claude.ts +176 -0
  355. package/ts/src/hooks/codex.ts +45 -0
  356. package/ts/src/hooks/gemini.ts +126 -0
  357. package/ts/src/hooks/main.ts +109 -0
  358. package/ts/src/hooks/mirrorState.ts +71 -0
  359. package/ts/src/hooks/payload.ts +68 -0
  360. package/ts/src/hooks/runtime.ts +159 -0
  361. package/ts/src/identity/applyConsolidationIdentityUpdate.ts +67 -0
  362. package/ts/src/identity/descriptorGenerate.ts +162 -0
  363. package/ts/src/identity/identityEdit.ts +143 -0
  364. package/ts/src/identity/identityKey.ts +77 -0
  365. package/ts/src/identity/identityRead.ts +69 -0
  366. package/ts/src/identity/identityStore.ts +80 -0
  367. package/ts/src/identity/journeyListing.ts +53 -0
  368. package/ts/src/identity/personaInspect.ts +44 -0
  369. package/ts/src/identity/personaListing.ts +37 -0
  370. package/ts/src/identity/setIdentity.ts +64 -0
  371. package/ts/src/index.ts +231 -0
  372. package/ts/src/init/init.ts +111 -0
  373. package/ts/src/journey/journeyOptions.ts +131 -0
  374. package/ts/src/journey/journeyRemoval.ts +158 -0
  375. package/ts/src/journey/journeyStatus.ts +149 -0
  376. package/ts/src/journey/journeySyncFile.ts +87 -0
  377. package/ts/src/journey/journeyWrite.ts +203 -0
  378. package/ts/src/journey/parentJourney.ts +41 -0
  379. package/ts/src/journey/validateParentJourney.ts +55 -0
  380. package/ts/src/mcp/boundary.ts +151 -0
  381. package/ts/src/mcp/guards.ts +244 -0
  382. package/ts/src/mcp/main.ts +121 -0
  383. package/ts/src/mcp/payload.ts +141 -0
  384. package/ts/src/mcp/protocol.ts +141 -0
  385. package/ts/src/mcp/readModels.ts +241 -0
  386. package/ts/src/mcp/registry.ts +201 -0
  387. package/ts/src/mcp/serve.ts +120 -0
  388. package/ts/src/mcp/tools/deterministic.ts +137 -0
  389. package/ts/src/mcp/tools/providerCrossing.ts +187 -0
  390. package/ts/src/mcp/wire.ts +8 -0
  391. package/ts/src/memory/journal.ts +180 -0
  392. package/ts/src/memory/listing.ts +107 -0
  393. package/ts/src/memory/memoryWrite.ts +88 -0
  394. package/ts/src/memory/reinforcement.ts +35 -0
  395. package/ts/src/mirror/context.ts +161 -0
  396. package/ts/src/mirror/defaultResolution.ts +199 -0
  397. package/ts/src/mirror/orchestration.ts +364 -0
  398. package/ts/src/mirror/reception.ts +194 -0
  399. package/ts/src/mirror/render.ts +75 -0
  400. package/ts/src/mirror/runtimeSession.ts +205 -0
  401. package/ts/src/mode/operatingMode.ts +148 -0
  402. package/ts/src/observability/callOutcome.ts +115 -0
  403. package/ts/src/observability/inspectLedger.ts +329 -0
  404. package/ts/src/observability/ledgerHooks.ts +126 -0
  405. package/ts/src/observability/llmCalls.ts +154 -0
  406. package/ts/src/persona/detectPersona.ts +81 -0
  407. package/ts/src/planning/promptAssembly.ts +111 -0
  408. package/ts/src/planning/weekPending.ts +81 -0
  409. package/ts/src/planning/weekPlan.ts +183 -0
  410. package/ts/src/planning/weekSave.ts +117 -0
  411. package/ts/src/providers/config.ts +189 -0
  412. package/ts/src/providers/cost.ts +64 -0
  413. package/ts/src/providers/credits.ts +204 -0
  414. package/ts/src/providers/embedding.ts +321 -0
  415. package/ts/src/providers/familyProviders.ts +147 -0
  416. package/ts/src/providers/llm.ts +334 -0
  417. package/ts/src/providers/openrouter.ts +244 -0
  418. package/ts/src/providers/redaction.ts +110 -0
  419. package/ts/src/providers/replay.ts +15 -0
  420. package/ts/src/providers/transport.ts +410 -0
  421. package/ts/src/repair/encodingRepair.ts +253 -0
  422. package/ts/src/runtime/backup.ts +151 -0
  423. package/ts/src/runtime/channel.ts +146 -0
  424. package/ts/src/runtime/config.ts +168 -0
  425. package/ts/src/runtime/diagnose.ts +686 -0
  426. package/ts/src/runtime/git.ts +510 -0
  427. package/ts/src/runtime/installKind.ts +220 -0
  428. package/ts/src/runtime/migrate.ts +154 -0
  429. package/ts/src/runtime/packageIdentity.ts +102 -0
  430. package/ts/src/runtime/releaseNotes.ts +244 -0
  431. package/ts/src/runtime/status.ts +557 -0
  432. package/ts/src/runtime/strategies/clone.ts +200 -0
  433. package/ts/src/runtime/strategies/package.ts +328 -0
  434. package/ts/src/runtime/treeRoot.ts +30 -0
  435. package/ts/src/runtime/update.ts +445 -0
  436. package/ts/src/runtime/updateGate.ts +40 -0
  437. package/ts/src/runtime/updatePipeline.ts +159 -0
  438. package/ts/src/runtime/version.ts +36 -0
  439. package/ts/src/runtime/wiring.ts +130 -0
  440. package/ts/src/search/memorySearch.ts +338 -0
  441. package/ts/src/search/ranker.ts +154 -0
  442. package/ts/src/seed/seed.ts +256 -0
  443. package/ts/src/seed/seedYaml.ts +133 -0
  444. package/ts/src/soul/apply.ts +218 -0
  445. package/ts/src/soul/harvest.ts +267 -0
  446. package/ts/src/soul/prompts/soul_beauty_voice.md +103 -0
  447. package/ts/src/soul/prompts/soul_self_voice.md +219 -0
  448. package/ts/src/soul/prompts/soul_wisdom_voice.md +121 -0
  449. package/ts/src/soul/prompts.ts +85 -0
  450. package/ts/src/soul/render.ts +358 -0
  451. package/ts/src/soul/state.ts +212 -0
  452. package/ts/src/soul/transition.ts +37 -0
  453. package/ts/src/tasks/journeyPathParse.ts +113 -0
  454. package/ts/src/tasks/taskImportSync.ts +142 -0
  455. package/ts/src/tasks/taskStore.ts +312 -0
  456. package/ts/src/tasks/weekView.ts +120 -0
  457. package/ts/src/util/clipCodePoints.ts +26 -0
  458. package/ts/src/util/paths.ts +63 -0
  459. package/ts/src/util/program.ts +9 -0
  460. package/ts/src/util/pyGenerators.ts +144 -0
  461. package/ts/src/util/pythonText.ts +317 -0
  462. package/ts/src/util/semver.ts +45 -0
  463. package/ts/src/util/shellWord.ts +25 -0
  464. package/ts/src/util/slug.ts +64 -0
  465. package/ts/src/welcome/card.ts +339 -0
  466. package/ts/src/welcome/statusLine.ts +101 -0
  467. package/ts/src/welcome/updateCache.ts +129 -0
@@ -0,0 +1,176 @@
1
+ // Claude Code hook entries (CV22.DS10.TS5 plateau 1, decision D4).
2
+ //
3
+ // Replaces the bodies of `.claude/hooks/*.sh` and the identical set under
4
+ // `plugins/mirror-mind/hooks/`, which spawned the Python interpreter for the
5
+ // core's CLI, for two hook modules, and for an inline import of the
6
+ // conversation logger's `hook_user_prompt` -- the last two reaching into
7
+ // modules that were never commands, and so never in DS7's denominator.
8
+ //
9
+ // Behavior is preserved case for case, including the cases that look like
10
+ // bugs and are not: a skill invocation is still logged (Claude's hook fires
11
+ // for every prompt and the logger decides), a missing session id still fails
12
+ // loud, and every failure is still non-fatal to the user's turn.
13
+
14
+ import { resolveDbPath } from "#frontDoor/dbPath.ts";
15
+ import { openLiveWriteDatabase } from "#frontDoor/liveBackup.ts";
16
+ import { markInjected, needsInject, readMirrorState } from "./mirrorState.ts";
17
+ import { parseHookPayload, readStdin } from "./payload.ts";
18
+ import {
19
+ noteHookFailure,
20
+ operatingInstructionsFor,
21
+ runFrontDoor,
22
+ runFrontDoorQuietly,
23
+ sessionStartOutput,
24
+ } from "./runtime.ts";
25
+
26
+ /**
27
+ * `session-start.sh`: make sure logging is on for this session, and hand
28
+ * Claude Code the Operating Instructions unless the project it opened in is
29
+ * this same tree (US3, D13 -- `operatingInstructionsFor`). The plugin in
30
+ * Claude Code's cache delivers them this way; the checkout's own wrappers
31
+ * find the project already carrying the file and stay quiet, as before.
32
+ */
33
+ export async function claudeSessionStart(env: NodeJS.ProcessEnv = process.env): Promise<number> {
34
+ await runFrontDoorQuietly("claude:session-start", ["conversation-logger", "session-start"]);
35
+ process.stdout.write(
36
+ sessionStartOutput(operatingInstructionsFor("claude:session-start", env.CLAUDE_PROJECT_DIR)),
37
+ );
38
+ return 0;
39
+ }
40
+
41
+ /**
42
+ * `log-user-prompt.sh`: record the user's turn.
43
+ *
44
+ * The shell piped stdin into `hook_user_prompt()`. The front door already owns
45
+ * that surface -- `conversation-logger user-prompt` is one of its two
46
+ * stdin-reading subcommands -- so the payload is handed straight to it rather
47
+ * than re-parsed here.
48
+ */
49
+ export async function claudeUserPrompt(): Promise<number> {
50
+ // stdin is deliberately NOT consumed here: the route reads it itself, so
51
+ // handing it the payload means not reading it first.
52
+ await runFrontDoorQuietly("claude:user-prompt", ["conversation-logger", "user-prompt"]);
53
+ return 0;
54
+ }
55
+
56
+ /** `log-session-end.sh`: close the session, then take the silent backup. */
57
+ export async function claudeSessionEnd(): Promise<number> {
58
+ await runFrontDoorQuietly("claude:session-end", ["conversation-logger", "session-end"]);
59
+ await runFrontDoorQuietly("claude:session-end", ["backup", "--silent"]);
60
+ return 0;
61
+ }
62
+
63
+ /**
64
+ * `mirror-inject.sh`: inject identity context before the model answers.
65
+ *
66
+ * Two cases, exactly as the shell had them:
67
+ *
68
+ * 1. an explicit `/mm:mirror [query]` -- load context for that query;
69
+ * 2. Mirror Mode already active for this session with context not yet
70
+ * injected -- load it with the session's persona and journey, then mark
71
+ * it injected so it happens once.
72
+ *
73
+ * `--context-only` matters in both: it loads context WITHOUT starting a
74
+ * conversation session, because the model does that. A hook that silently
75
+ * opened conversations would fork every session in two.
76
+ */
77
+ export async function claudeInject(stdin: string): Promise<number> {
78
+ const payload = parseHookPayload(stdin);
79
+ const sessionId = payload.sessionId;
80
+
81
+ // Case 1: the explicit invocation.
82
+ if (/^\/mm:mirror/.test(payload.prompt)) {
83
+ const query = payload.prompt.replace(/^\/mm:mirror\s*/, "");
84
+ const argv = ["mirror", "load", "--context-only", "--query", query];
85
+ if (sessionId) argv.push("--session-id", sessionId);
86
+ const result = await runFrontDoor(argv);
87
+ if (result.stdout.trim()) process.stdout.write(result.stdout);
88
+ return 0;
89
+ }
90
+
91
+ // Case 2: active Mirror Mode owed an injection.
92
+ if (!sessionId) {
93
+ // Python's `mirror_state` warned on stderr rather than no-op'ing silently,
94
+ // so a hook wired without a session id is visible. Kept: it is the
95
+ // difference between "Mirror Mode is off" and "this hook is misconfigured".
96
+ process.stderr.write("warning: mirror inject hook received no session_id; no state read.\n");
97
+ return 0;
98
+ }
99
+
100
+ let dbPath: string;
101
+ try {
102
+ dbPath = resolveDbPath([], process.env);
103
+ } catch (error) {
104
+ noteHookFailure("claude:inject", error instanceof Error ? error.message : String(error));
105
+ return 0;
106
+ }
107
+
108
+ const state = readMirrorState(dbPath, sessionId);
109
+ if (!needsInject(state)) return 0;
110
+
111
+ const argv = [
112
+ "mirror",
113
+ "load",
114
+ "--context-only",
115
+ "--query",
116
+ payload.prompt,
117
+ "--session-id",
118
+ sessionId,
119
+ ];
120
+ if (state.persona) argv.push("--persona", state.persona);
121
+ if (state.journey) argv.push("--journey", state.journey);
122
+
123
+ const result = await runFrontDoor(argv);
124
+ if (!result.stdout.trim()) return 0;
125
+
126
+ process.stdout.write(result.stdout);
127
+ // Only after the context actually reached the model: the shell marked it
128
+ // inside `if [ -n "$CONTEXT" ]` for the same reason. Marking first would
129
+ // lose the injection whenever the load produced nothing.
130
+ try {
131
+ // The sanctioned live-write path: a verified pre-write snapshot, same as
132
+ // every other routed write.
133
+ //
134
+ // Python's `mark_injected` wrote UNGATED, so this adds a `VACUUM INTO`
135
+ // that the old hook did not pay. Measured on a real 53 MB database:
136
+ // **95 ms**, once per Mirror Mode session -- only the single prompt that
137
+ // owes an injection reaches this line, and that turn is already loading
138
+ // identity and memories. The ordinary turn returns above without opening
139
+ // a writable handle at all (114 ms end to end, nothing written).
140
+ //
141
+ // Paying the front door's own safety contract for a real write to a real
142
+ // user database beat inventing a second narrowed write handle inside a
143
+ // deletion story. Recorded with the number so the trade is visible.
144
+ //
145
+ // This hook runs AT THE SAME TIME as `log-user-prompt`, which writes too.
146
+ // Until finding F21 the two snapshots collided on one fixed file and one
147
+ // write was lost -- in the Navigator walk, two injections in three
148
+ // prompts. `openLiveWriteDatabase` gives each writer its own staging
149
+ // snapshot, so concurrent hooks no longer race.
150
+ const db = openLiveWriteDatabase(dbPath);
151
+ try {
152
+ markInjected(db, sessionId, new Date().toISOString());
153
+ } finally {
154
+ db.close();
155
+ }
156
+ } catch (error) {
157
+ noteHookFailure("claude:inject", error instanceof Error ? error.message : String(error));
158
+ }
159
+ return 0;
160
+ }
161
+
162
+ export async function claudeHook(name: string): Promise<number> {
163
+ switch (name) {
164
+ case "session-start":
165
+ return claudeSessionStart();
166
+ case "user-prompt":
167
+ return claudeUserPrompt();
168
+ case "session-end":
169
+ return claudeSessionEnd();
170
+ case "inject":
171
+ return claudeInject(readStdin());
172
+ default:
173
+ noteHookFailure("claude", `unknown hook: ${name}`);
174
+ return 0;
175
+ }
176
+ }
@@ -0,0 +1,45 @@
1
+ // Codex hook entries (CV22.DS10.TS5 handoff review, finding N3).
2
+ //
3
+ // Codex has no hook system, so `scripts/codex-mirror.sh` wraps the `codex`
4
+ // command itself and calls these through two generated wrappers under
5
+ // `scripts/codex-hooks/`. Until the handoff review the script called the front
6
+ // door directly: it found `node` on PATH alone, silenced every failure, and
7
+ // built its command as a string that a checkout path with a space split in
8
+ // two. Going through the wrappers gives it the same Node resolution and the
9
+ // same hooks.log record as every other runtime.
10
+ //
11
+ // The payload comes as arguments, not stdin: the wrapper, not a runtime,
12
+ // decides what to pass, and a path is safer as one quoted argument than
13
+ // embedded in hand-written JSON.
14
+
15
+ import { runFrontDoorQuietly } from "./runtime.ts";
16
+
17
+ /** Wrapper start: make sure logging is on for this session. */
18
+ export async function codexSessionStart(): Promise<number> {
19
+ await runFrontDoorQuietly("codex:session-start", ["conversation-logger", "session-start"]);
20
+ return 0;
21
+ }
22
+
23
+ /**
24
+ * Wrapper exit: import the session Codex wrote, close it, then take the silent
25
+ * backup. The transcript and its session id are both absent when Codex wrote
26
+ * no session for this directory; the backup runs either way, as it always has.
27
+ */
28
+ export async function codexSessionEnd(transcriptPath: string, sessionId: string): Promise<number> {
29
+ if (transcriptPath && sessionId) {
30
+ await runFrontDoorQuietly("codex:session-end", [
31
+ "conversation-logger",
32
+ "backfill-codex-session",
33
+ transcriptPath,
34
+ "--interface",
35
+ "codex",
36
+ ]);
37
+ await runFrontDoorQuietly("codex:session-end", [
38
+ "conversation-logger",
39
+ "session-end-pi",
40
+ sessionId,
41
+ ]);
42
+ }
43
+ await runFrontDoorQuietly("codex:session-end", ["backup", "--silent"]);
44
+ return 0;
45
+ }
@@ -0,0 +1,126 @@
1
+ // Gemini CLI hook entries (CV22.DS10.TS5 plateau 1, decision D4).
2
+ //
3
+ // These hooks already entered the TypeScript front door for every Mirror
4
+ // command -- the Python they carried was only inline interpreter one-liners
5
+ // importing `sys` and `json`, to read a field out of the payload and to print
6
+ // one back. Four spawns of an interpreter to parse JSON, in a Node process's
7
+ // hook.
8
+ //
9
+ // The Gemini contract, preserved exactly:
10
+ // stdin -- JSON: { session_id, prompt, ... }
11
+ // stdout -- JSON: { hookSpecificOutput: { additionalContext } } or {}
12
+ // stderr -- logs only; never parsed
13
+ //
14
+ // `GEMINI_SESSION_ID` still wins over the payload's `session_id`, because the
15
+ // runtime sets it and the payload is the fallback.
16
+
17
+ import { parseHookPayload } from "./payload.ts";
18
+ import {
19
+ operatingInstructionsFor,
20
+ runFrontDoor,
21
+ runFrontDoorQuietly,
22
+ sessionStartOutput,
23
+ } from "./runtime.ts";
24
+
25
+ function sessionIdFrom(payloadSessionId: string, env = process.env): string {
26
+ return env.GEMINI_SESSION_ID || payloadSessionId;
27
+ }
28
+
29
+ /**
30
+ * BeforeAgent: log the user's turn, and inject context when Mirror Mode is on.
31
+ *
32
+ * A prompt starting with `/` is a skill invocation -- a command, not a
33
+ * conversational message -- and is not logged. That rule is Gemini-specific
34
+ * and stays; Claude's hook has no equivalent because its logger decides.
35
+ */
36
+ export async function geminiLogUser(stdin: string): Promise<number> {
37
+ const payload = parseHookPayload(stdin);
38
+ const sessionId = sessionIdFrom(payload.sessionId);
39
+
40
+ if (payload.prompt.startsWith("/")) {
41
+ process.stdout.write("{}\n");
42
+ return 0;
43
+ }
44
+
45
+ if (payload.prompt && sessionId) {
46
+ await runFrontDoorQuietly("gemini:log-user", [
47
+ "conversation-logger",
48
+ "log-user",
49
+ sessionId,
50
+ payload.prompt,
51
+ "--interface",
52
+ "gemini_cli",
53
+ ]);
54
+ }
55
+
56
+ const context = await runFrontDoor([
57
+ "mirror",
58
+ "load",
59
+ "--context-only",
60
+ "--query",
61
+ payload.prompt,
62
+ "--session-id",
63
+ sessionId,
64
+ ]);
65
+
66
+ process.stdout.write(
67
+ context.stdout.trim()
68
+ ? `${JSON.stringify({ hookSpecificOutput: { additionalContext: context.stdout } })}\n`
69
+ : "{}\n",
70
+ );
71
+ return 0;
72
+ }
73
+
74
+ /** AfterAgent: record the assistant's turn. */
75
+ export async function geminiLogAssistant(stdin: string): Promise<number> {
76
+ const payload = parseHookPayload(stdin);
77
+ const sessionId = sessionIdFrom(payload.sessionId);
78
+ if (payload.response && sessionId) {
79
+ await runFrontDoorQuietly("gemini:log-assistant", [
80
+ "conversation-logger",
81
+ "log-assistant",
82
+ sessionId,
83
+ payload.response,
84
+ "--interface",
85
+ "gemini_cli",
86
+ ]);
87
+ }
88
+ process.stdout.write("{}\n");
89
+ return 0;
90
+ }
91
+
92
+ /** SessionEnd: close the conversation for this session. */
93
+ export async function geminiSessionEnd(stdin: string): Promise<number> {
94
+ const payload = parseHookPayload(stdin);
95
+ const sessionId = sessionIdFrom(payload.sessionId);
96
+ if (sessionId) {
97
+ await runFrontDoorQuietly("gemini:session-end", [
98
+ "conversation-logger",
99
+ "session-end-pi",
100
+ sessionId,
101
+ ]);
102
+ }
103
+ process.stdout.write("{}\n");
104
+ return 0;
105
+ }
106
+
107
+ /**
108
+ * SessionStart: unmute logging for the new session.
109
+ *
110
+ * Fires on startup, resume, and `/clear`. This wrapper was already free of
111
+ * Python -- it entered the front door directly -- but it is generated from the
112
+ * same template as the other eleven so it cannot drift away from them, which
113
+ * is the failure CR071 recorded for the skill copies.
114
+ */
115
+ export async function geminiSessionStart(env: NodeJS.ProcessEnv = process.env): Promise<number> {
116
+ await runFrontDoorQuietly("gemini:session-start", ["conversation-logger", "session-start"]);
117
+ // US3, D13: the Operating Instructions as SessionStart context, unless the
118
+ // project is this tree -- the checkout's GEMINI/CLAUDE.md import the file.
119
+ process.stdout.write(
120
+ sessionStartOutput(
121
+ operatingInstructionsFor("gemini:session-start", env.GEMINI_PROJECT_DIR),
122
+ "{}\n",
123
+ ),
124
+ );
125
+ return 0;
126
+ }
@@ -0,0 +1,109 @@
1
+ #!/usr/bin/env node
2
+
3
+ // The single entry point every Mirror hook wrapper calls.
4
+ //
5
+ // CV22.DS10.TS5 plateau 1, decision D4. Twelve shell scripts across four
6
+ // runtimes each spawned the interpreter one to five times per event. They now
7
+ // run one Node process and name which hook they are:
8
+ //
9
+ // node ts/src/hooks/main.ts claude:inject
10
+ //
11
+ // Arguments after the name are the hook's own; only the Codex wrapper passes
12
+ // any, since Codex has no hook payload to read (`codex.ts`).
13
+ //
14
+ // One entry rather than one file per hook because the generated wrappers then
15
+ // differ only in the hook name and their depth, and `hooks.test.ts` asserts
16
+ // that -- copies that can differ in only one place cannot drift the way the
17
+ // skill copies did (CR071, eleven skills, two runtimes wrong for months).
18
+ //
19
+ // Since CV22.DS10.US3 (D3, D5) the wrappers run this file through the
20
+ // `mirror-hook` bin (`bin/mirror-hook.js`), and it reads configuration and
21
+ // silences the sqlite warning itself, in `runAsEntry()`: the `--env-file`
22
+ // and `--no-warnings` flags the wrappers used to pass could only ever point
23
+ // at a checkout's `.env`, and an installed package has none.
24
+ //
25
+ // **A hook never fails the user's turn.** Every path returns 0 and every
26
+ // failure is recorded in `<mirror-home>/hooks.log` instead. That was the
27
+ // Python posture too (`2>/dev/null || true`), with one difference that
28
+ // matters: silence there hid nothing, because `python3` is on every macOS.
29
+ // Node is often absent from a GUI-launched runtime's PATH, so silence here
30
+ // would hide a NEW failure -- which is why the wrappers write the log line
31
+ // themselves when they cannot find a Node that runs this file. That is also
32
+ // why this file must exit 0: the wrappers read any other exit as Node failing.
33
+
34
+ import { pathToFileURL } from "node:url";
35
+ import { loadConfiguration, silenceExperimentalWarnings } from "#runtime/config.ts";
36
+ import { claudeHook } from "./claude.ts";
37
+ import { codexSessionEnd, codexSessionStart } from "./codex.ts";
38
+ import {
39
+ geminiLogAssistant,
40
+ geminiLogUser,
41
+ geminiSessionEnd,
42
+ geminiSessionStart,
43
+ } from "./gemini.ts";
44
+ import { readStdin } from "./payload.ts";
45
+ import { noteHookFailure } from "./runtime.ts";
46
+
47
+ async function dispatch(name: string, args: readonly string[]): Promise<number> {
48
+ const [runtime, hook] = name.split(":");
49
+
50
+ switch (runtime) {
51
+ case "claude":
52
+ return claudeHook(hook ?? "");
53
+ case "gemini":
54
+ switch (hook) {
55
+ case "log-user":
56
+ return geminiLogUser(readStdin());
57
+ case "log-assistant":
58
+ return geminiLogAssistant(readStdin());
59
+ case "session-end":
60
+ return geminiSessionEnd(readStdin());
61
+ case "session-start":
62
+ return geminiSessionStart();
63
+ default:
64
+ break;
65
+ }
66
+ break;
67
+ case "codex":
68
+ // Called by `scripts/codex-mirror.sh` through its generated wrappers.
69
+ switch (hook) {
70
+ case "session-start":
71
+ return codexSessionStart();
72
+ case "session-end":
73
+ return codexSessionEnd(args[0] ?? "", args[1] ?? "");
74
+ default:
75
+ break;
76
+ }
77
+ break;
78
+ default:
79
+ break;
80
+ }
81
+
82
+ noteHookFailure("hooks", `unknown hook name: ${name}`);
83
+ return 0;
84
+ }
85
+
86
+ /**
87
+ * What the entry does, for the two ways it is started: this file run directly
88
+ * (a checkout), or the `bin/mirror-hook.js` shim (D15). Configuration and the
89
+ * warning filter run here and nowhere else, so importing this module does
90
+ * neither.
91
+ */
92
+ export async function runAsEntry(rawArgv = process.argv.slice(2)): Promise<number> {
93
+ silenceExperimentalWarnings();
94
+ loadConfiguration({ entryPath: import.meta.filename });
95
+ const name = rawArgv[0] ?? "";
96
+ try {
97
+ return await dispatch(name, rawArgv.slice(1));
98
+ } catch (error) {
99
+ // The last line of defense. A hook that throws must still not break the
100
+ // turn, and must still leave a trace that it happened.
101
+ noteHookFailure(name || "hooks", error instanceof Error ? error.message : String(error));
102
+ return 0;
103
+ }
104
+ }
105
+
106
+ // Run only when invoked as the entry, not when imported.
107
+ if (import.meta.url === pathToFileURL(process.argv[1] ?? "").href) {
108
+ process.exitCode = await runAsEntry();
109
+ }
@@ -0,0 +1,71 @@
1
+ // Mirror Mode state for one runtime session, as the inject hook needs it.
2
+ //
3
+ // The TypeScript replacement for the Python hook module `mirror_state` (144
4
+ // lines, deleted with the core at CV22.DS10.TS5), which
5
+ // `mirror-inject.sh` invoked up to three times per prompt -- `needs-inject`,
6
+ // `get persona`, `get journey` -- each a separate interpreter start, each
7
+ // re-opening the database and re-running bootstrap.
8
+ //
9
+ // It is NOT ported as a front-door command. Its only caller was ever that one
10
+ // script, and the state it reads already belongs to `#mirror/runtimeSession.ts`;
11
+ // adding two commands whose sole consumer is a hook would widen the command
12
+ // surface this story exists to shrink.
13
+ //
14
+ // One Python behavior is deliberately kept, because it is a real safeguard:
15
+ // a missing session id FAILS LOUD on stderr rather than silently answering
16
+ // "nothing to do". A hook integration that forgets to pass one would otherwise
17
+ // look like a user who simply has Mirror Mode off.
18
+
19
+ import { openDatabaseReadOnly, type WritableDatabase } from "#db/database.ts";
20
+ import { getRuntimeSession, upsertRuntimeSession } from "#mirror/runtimeSession.ts";
21
+
22
+ export interface MirrorHookState {
23
+ readonly active: boolean;
24
+ readonly hookInjected: boolean;
25
+ readonly persona: string;
26
+ readonly journey: string;
27
+ }
28
+
29
+ const NO_STATE: MirrorHookState = {
30
+ active: false,
31
+ hookInjected: false,
32
+ persona: "",
33
+ journey: "",
34
+ };
35
+
36
+ /** Python `_load_state`: the session's Mirror fields, or empty when unknown. */
37
+ export function readMirrorState(dbPath: string, sessionId: string): MirrorHookState {
38
+ if (!sessionId) return NO_STATE;
39
+ let db: ReturnType<typeof openDatabaseReadOnly>;
40
+ try {
41
+ db = openDatabaseReadOnly(dbPath);
42
+ } catch {
43
+ return NO_STATE;
44
+ }
45
+ try {
46
+ const session = getRuntimeSession(db, sessionId);
47
+ if (!session) return NO_STATE;
48
+ return {
49
+ active: session.mirrorActive,
50
+ hookInjected: session.hookInjected,
51
+ persona: session.persona ?? "",
52
+ journey: session.journey ?? "",
53
+ };
54
+ } catch {
55
+ return NO_STATE;
56
+ } finally {
57
+ db.close();
58
+ }
59
+ }
60
+
61
+ /** Python `needs_inject`: Mirror Mode is on and context has not been injected. */
62
+ export function needsInject(state: MirrorHookState): boolean {
63
+ return state.active && !state.hookInjected;
64
+ }
65
+
66
+ /** Python `mark_injected`, over an already-open writable connection. */
67
+ export function markInjected(db: WritableDatabase, sessionId: string, nowIso: string): void {
68
+ if (!sessionId) return;
69
+ if (!getRuntimeSession(db, sessionId)) return;
70
+ upsertRuntimeSession(db, sessionId, { hookInjected: true }, nowIso);
71
+ }
@@ -0,0 +1,68 @@
1
+ // Reading a runtime's hook payload from stdin.
2
+ //
3
+ // Replaces the Python hook module `extract_prompt` (25 lines) and the four
4
+ // inline interpreter one-liners the Claude and Gemini hooks used to parse the
5
+ // same JSON. None of them was a command, so none was in
6
+ // DS7's denominator of 29 -- which is why TS5's inventory had to find them by
7
+ // reading the hooks rather than by counting commands.
8
+ //
9
+ // Python's `extract_prompt` returns "" for malformed JSON rather than raising,
10
+ // because a hook that throws on a payload it did not recognize would break the
11
+ // user's turn. That posture is preserved exactly.
12
+
13
+ import { readFileSync } from "node:fs";
14
+
15
+ export interface HookPayload {
16
+ /** The user's prompt text, or "" when the payload has none. */
17
+ readonly prompt: string;
18
+ /** The runtime's session id, or "" when the payload has none. */
19
+ readonly sessionId: string;
20
+ /** The assistant's response text (Gemini's `prompt_response`), or "". */
21
+ readonly response: string;
22
+ /** Everything else, for hooks that need a field these three do not cover. */
23
+ readonly raw: Record<string, unknown>;
24
+ }
25
+
26
+ const EMPTY: HookPayload = { prompt: "", sessionId: "", response: "", raw: {} };
27
+
28
+ function text(value: unknown): string {
29
+ return typeof value === "string" ? value : "";
30
+ }
31
+
32
+ /** Parse a hook payload. Never throws: an unparseable payload is empty. */
33
+ export function parseHookPayload(input: string): HookPayload {
34
+ let parsed: unknown;
35
+ try {
36
+ parsed = JSON.parse(input);
37
+ } catch {
38
+ return EMPTY;
39
+ }
40
+ if (typeof parsed !== "object" || parsed === null) return EMPTY;
41
+ const raw = parsed as Record<string, unknown>;
42
+ return {
43
+ prompt: text(raw.prompt),
44
+ sessionId: text(raw.session_id),
45
+ // Gemini's AfterAgent payload names it `prompt_response`, and that is the
46
+ // field the Python hook read. The first port read three guessed names
47
+ // instead and dropped every Gemini assistant turn in silence until
48
+ // `smoke_gemini_cli.sh` caught it (CV22.DS10.TS5 plateau 3).
49
+ response: text(raw.prompt_response),
50
+ raw,
51
+ };
52
+ }
53
+
54
+ /**
55
+ * Read stdin to the end, synchronously.
56
+ *
57
+ * Hooks are short-lived processes whose whole job is one payload, and the
58
+ * runtimes close the pipe. Reading fd 0 directly avoids an async entry point
59
+ * for every hook, and returns "" when there is no stdin at all -- which is the
60
+ * case for `session-start`, invoked with no payload.
61
+ */
62
+ export function readStdin(): string {
63
+ try {
64
+ return readFileSync(0, "utf8");
65
+ } catch {
66
+ return "";
67
+ }
68
+ }