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,166 @@
1
+ import type { WritableDatabase } from "#db/database.ts";
2
+ import { requireString } from "#db/rowDecode.ts";
3
+ import type { EmbeddingProvider } from "#providers/embedding.ts";
4
+ import { resolveFamilyProviders } from "#providers/familyProviders.ts";
5
+ import { SEARCH_TRANSPORT } from "#providers/transport.ts";
6
+ import type { FreshSearchResult } from "#search/memorySearch.ts";
7
+ import { searchMemoriesWithStatus } from "#search/memorySearch.ts";
8
+ import { optionValue } from "./args.ts";
9
+ import { ICONS } from "./render/icons.ts";
10
+ import { tagsText } from "./render/memories.ts";
11
+
12
+ // Mirrors Python's cli/memories.py degraded_note wording exactly (AI-04),
13
+ // including under a degraded search with zero results.
14
+ const DEGRADED_NOTE =
15
+ "⚠ Degraded: lexical-only search (embedding unavailable — offline or no API key).";
16
+
17
+ export interface SearchMemoryRow {
18
+ id: string;
19
+ memory_type: string;
20
+ layer: string;
21
+ title: string;
22
+ content: string;
23
+ created_at: string;
24
+ journey: string | null;
25
+ tags: string | null;
26
+ }
27
+
28
+ /**
29
+ * Resolve the embedding provider by the same precedence the router used
30
+ * (CV22.DS8.US1), so the route can never disagree with the routing decision
31
+ * about which transport is in play.
32
+ */
33
+ export async function resolveSearchEmbeddingProvider(
34
+ env: NodeJS.ProcessEnv = process.env,
35
+ ): Promise<EmbeddingProvider> {
36
+ // The live provider resolves its config lazily, so a missing key surfaces
37
+ // inside searchMemoriesWithStatus and degrades to lexical-only rather than
38
+ // failing the command -- Python's behavior for an unconfigured install.
39
+ const family = await resolveFamilyProviders(env, SEARCH_TRANSPORT);
40
+ if (!family.embedding) {
41
+ // The search family declares an embedding fixture, so replay and live both
42
+ // build one; this is defense in depth, not a reachable state.
43
+ throw new Error("memories --search requires an embedding provider");
44
+ }
45
+ return family.embedding;
46
+ }
47
+
48
+ export interface MemorySearchRouteOptions {
49
+ /** Receives a content-free degraded category for the front-door log. */
50
+ onDegraded?: (detail: string) => void;
51
+ }
52
+
53
+ export async function runMemorySearchRoute(
54
+ db: WritableDatabase,
55
+ args: readonly string[],
56
+ options: MemorySearchRouteOptions = {},
57
+ ): Promise<string> {
58
+ const query = optionValue(args, "--search");
59
+ if (!query) throw new Error("memories --search requires a query");
60
+ const provider = await resolveSearchEmbeddingProvider();
61
+ const limit = Number(optionValue(args, "--limit") ?? 20);
62
+ const { results, degraded, degradedKind } = await searchMemoriesWithStatus(db, {
63
+ query,
64
+ limit,
65
+ memoryType: optionValue(args, "--type"),
66
+ layer: optionValue(args, "--layer"),
67
+ journey: optionValue(args, "--journey"),
68
+ provider,
69
+ // An exploratory CLI search is not a genuine context load, so it must not
70
+ // reinforce: `access_count` feeds `reinforcement_score` and the hybrid
71
+ // ranker, and a ranker fed by its own exhaust drifts toward whatever was
72
+ // searched for rather than whatever was used (AI-12). Python's
73
+ // `cli/memories.py` has passed `log_access=False` since 2026-07-16; this
74
+ // port did not, and reinforced on every `memories --search` from the flip
75
+ // until CV22.DS9.US2 measured it. Builder context load deliberately keeps
76
+ // the default and still reinforces -- that one IS a genuine load.
77
+ logAccess: false,
78
+ });
79
+ // A degraded live search is the one failure an operator has to diagnose
80
+ // without a re-run, so the taxonomy class reaches the front-door log rather
81
+ // than dying inside the degrade path. Category only -- never a message,
82
+ // never the query (RS005/CR026).
83
+ if (degraded && degradedKind) {
84
+ options.onDegraded?.(`embedding_degraded kind=${degradedKind}`);
85
+ }
86
+ const rows = memoriesById(
87
+ db,
88
+ results.map((result) => result.id),
89
+ );
90
+ return formatSearchResults(query, results, rows, degraded);
91
+ }
92
+
93
+ /**
94
+ * Pure rendering of a fresh-search outcome (AI-04). Split out from
95
+ * `runMemorySearchRoute` so the degraded/normal render branches are directly
96
+ * unit-testable without a database or a live-failing embedding provider (none
97
+ * exists yet -- see CR037 plan).
98
+ */
99
+ export function formatSearchResults(
100
+ query: string,
101
+ results: readonly FreshSearchResult[],
102
+ rows: ReadonlyMap<string, SearchMemoryRow>,
103
+ degraded: boolean,
104
+ ): string {
105
+ if (results.length === 0) {
106
+ return degraded ? `${DEGRADED_NOTE}\n` : "No memories found.\n";
107
+ }
108
+ const lines: string[] = [];
109
+ if (degraded) {
110
+ lines.push(`${DEGRADED_NOTE} Ranked by keyword match.`, "");
111
+ }
112
+ lines.push(`🔍 Search: "${query}" (${results.length} results)`, "");
113
+ for (const result of results) {
114
+ const memory = rows.get(result.id);
115
+ if (!memory) continue;
116
+ const icon = ICONS[memory.memory_type] ?? "•";
117
+ const date = memory.created_at ? memory.created_at.slice(0, 10) : "?";
118
+ const journey = memory.journey ? ` 🧭 ${memory.journey}` : "";
119
+ const tags = tagsText(memory.tags);
120
+ lines.push(`${icon} **${memory.title}** (score: ${result.score.toFixed(3)})`);
121
+ lines.push(
122
+ ` ${date} | \`${memory.id.slice(0, 8)}\` | ${memory.memory_type} [${memory.layer}]${journey}`,
123
+ );
124
+ lines.push(` ${memory.content.slice(0, 200)}`);
125
+ if (tags) lines.push(` 🏷 ${tags}`);
126
+ lines.push("");
127
+ }
128
+ return `${lines.join("\n")}\n`;
129
+ }
130
+
131
+ /**
132
+ * The row reader both search faces share.
133
+ *
134
+ * Exported for `build load` (CV22.DS7.US8 plateau 7), which renders the same
135
+ * memories from the same ranked ids: the ranker returns `{id, score}` only, so a
136
+ * second copy of this query is a second chance for the two blocks to disagree
137
+ * about what a memory says.
138
+ */
139
+ export function memoriesById(
140
+ db: WritableDatabase,
141
+ ids: readonly string[],
142
+ ): Map<string, SearchMemoryRow> {
143
+ if (ids.length === 0) return new Map();
144
+ const placeholders = ids.map(() => "?").join(", ");
145
+ const rows = db
146
+ .prepare(
147
+ `SELECT id, memory_type, layer, title, content, created_at, journey, tags FROM memories ` +
148
+ `WHERE id IN (${placeholders})`,
149
+ )
150
+ .all(...ids);
151
+ return new Map(
152
+ rows.map((row) => [
153
+ requireString(row, "id"),
154
+ {
155
+ id: requireString(row, "id"),
156
+ memory_type: requireString(row, "memory_type"),
157
+ layer: requireString(row, "layer"),
158
+ title: requireString(row, "title"),
159
+ content: requireString(row, "content"),
160
+ created_at: requireString(row, "created_at"),
161
+ journey: typeof row.journey === "string" ? row.journey : null,
162
+ tags: typeof row.tags === "string" ? row.tags : null,
163
+ },
164
+ ]),
165
+ );
166
+ }
@@ -0,0 +1,98 @@
1
+ // `seed` path resolution — the port of the bespoke resolution inside
2
+ // memory.cli.seed.seed(), which is NOT the same precedence chain
3
+ // `resolveDbPath` (dbPath.ts) uses for every other command. Verified against
4
+ // the live oracle (never assumed) because it diverges in a genuinely
5
+ // surprising way:
6
+ //
7
+ // - `identity_root`/`mirror_home` resolve via `--mirror-home` or the plain
8
+ // MIRROR_HOME/MIRROR_USER chain (`resolveMirrorHome`) -- completely
9
+ // independent of `--env`, `MEMORY_DIR`, or `DB_PATH`.
10
+ // - When `--mirror-home` IS given, the database path is ALWAYS
11
+ // `<mirror_home>/memory.db` -- `--env` is silently ignored for the
12
+ // filename (`default_db_path_for_home` hardcodes "memory.db"). Confirmed
13
+ // live: `seed --mirror-home X --env test` writes `X/memory.db`, not
14
+ // `X/memory_test.db`.
15
+ // - When `--mirror-home` is absent and `--env` differs from the ambient
16
+ // `MEMORY_ENV` (or ambient is unset, defaulting "production" like Python's
17
+ // module-level MEMORY_ENV constant), the database path follows
18
+ // `db_path_for_env`: MEMORY_PROD_DIR (production only) > MEMORY_DIR > the
19
+ // resolved mirror home, with the env-appropriate filename -- but WITHOUT
20
+ // checking `DB_PATH` (that env var is only wired to the *ambient*
21
+ // environment in Python, cached once at import time).
22
+ // - When `--mirror-home` is absent and `--env` matches the ambient
23
+ // `MEMORY_ENV` (or is omitted), the database path follows the SAME full
24
+ // generic chain every other command uses (`require_db_path()` ==
25
+ // `resolveDbPath`).
26
+ //
27
+ // `--db-path` is a TS-front-door-only convenience (Python's `seed` CLI has no
28
+ // such flag), kept here for consistency with every other ported command --
29
+ // it wins over everything, as documented in dbPath.ts.
30
+
31
+ import { join } from "node:path";
32
+ import { expandHome } from "#util/paths.ts";
33
+ import { optionValue } from "./args.ts";
34
+ import {
35
+ type DbPathEnv,
36
+ type DbPathIo,
37
+ dbNameForEnv,
38
+ MirrorHomeNotConfiguredError,
39
+ resolveDbPath,
40
+ resolveMirrorHome,
41
+ } from "./dbPath.ts";
42
+
43
+ export interface SeedPaths {
44
+ dbPath: string;
45
+ identityRoot: string;
46
+ mirrorHome: string;
47
+ env: string;
48
+ }
49
+
50
+ /**
51
+ * Resolve `seed`'s database path, identity root, mirror home, and effective
52
+ * env, mirroring the Python branches documented above exactly. Throws
53
+ * MirrorHomeNotConfiguredError when nothing resolves (mapped to exit 2 by the
54
+ * caller, matching every other command's unconfigured-home contract).
55
+ */
56
+ export function resolveSeedPaths(
57
+ args: readonly string[],
58
+ env: DbPathEnv = process.env,
59
+ io: DbPathIo = {},
60
+ ): SeedPaths {
61
+ const explicitDbPath = optionValue(args, "--db-path");
62
+ const explicitMirrorHome = optionValue(args, "--mirror-home");
63
+ const envFlag = optionValue(args, "--env");
64
+ const ambientEnv = env.MEMORY_ENV || "production";
65
+ const effectiveEnv = envFlag || ambientEnv;
66
+
67
+ const mirrorHome = explicitMirrorHome
68
+ ? expandHome(explicitMirrorHome)
69
+ : resolveMirrorHome(env, io);
70
+ const identityRoot = join(mirrorHome, "identity");
71
+
72
+ if (explicitDbPath) {
73
+ return { dbPath: expandHome(explicitDbPath), identityRoot, mirrorHome, env: effectiveEnv };
74
+ }
75
+ if (explicitMirrorHome) {
76
+ // default_db_path_for_home: ALWAYS "memory.db", --env is not consulted.
77
+ return { dbPath: join(mirrorHome, "memory.db"), identityRoot, mirrorHome, env: effectiveEnv };
78
+ }
79
+ if (envFlag && envFlag !== ambientEnv) {
80
+ const dbName = dbNameForEnv(envFlag);
81
+ let dir: string;
82
+ if (envFlag === "production" && env.MEMORY_PROD_DIR) {
83
+ dir = expandHome(env.MEMORY_PROD_DIR);
84
+ } else if (env.MEMORY_DIR) {
85
+ dir = expandHome(env.MEMORY_DIR);
86
+ } else {
87
+ dir = mirrorHome;
88
+ }
89
+ return { dbPath: join(dir, dbName), identityRoot, mirrorHome, env: effectiveEnv };
90
+ }
91
+ // require_db_path(): by construction neither --db-path nor --mirror-home is
92
+ // present here, so this is exactly resolveDbPath's own DB_PATH ->
93
+ // MEMORY_PROD_DIR -> MEMORY_DIR -> resolved-mirror-home chain -- the same
94
+ // one every other command uses. Reused directly rather than re-implemented.
95
+ return { dbPath: resolveDbPath(args, env, io), identityRoot, mirrorHome, env: effectiveEnv };
96
+ }
97
+
98
+ export { MirrorHomeNotConfiguredError };
@@ -0,0 +1,433 @@
1
+ // CV22.DS7.US6 plateau 6 — the `soul` front-door route.
2
+ //
3
+ // Dispatch only: every behavior it reaches was ported and graded in plateaus
4
+ // 1-5. What this module owns is the argv shape, the exit codes, and the
5
+ // refusals — Python's `cmd_*` wrappers print `Error: <message>` to stderr and
6
+ // exit 1 for a ValueError, and argparse exits 2 for an unknown subcommand or a
7
+ // missing required argument.
8
+ //
9
+ // The subcommands are allowlisted BY NAME by `routing.ts`, so a subcommand
10
+ // Python grows later reaches Python instead of inheriting this route. That is
11
+ // the `conversations append` lesson (RS009/CR055): a family claimed wholesale
12
+ // answered an argv shape it had never implemented, exited 0, and discarded the
13
+ // caller's data.
14
+
15
+ import type { Database, WritableDatabase } from "#db/database.ts";
16
+ import { getIdentityContent } from "#identity/identityRead.ts";
17
+ import { persistStickyDefaults } from "#mirror/orchestration.ts";
18
+ import { resolveRuntimeSessionId } from "#mirror/runtimeSession.ts";
19
+ import { activateOperatingMode } from "#mode/operatingMode.ts";
20
+ import { embeddingLedgerHook } from "#observability/ledgerHooks.ts";
21
+ import { generateEmbeddingSafely } from "#providers/embedding.ts";
22
+ import { resolveFamilyProviders } from "#providers/familyProviders.ts";
23
+ import { SOUL_HARVEST_TRANSPORT } from "#providers/transport.ts";
24
+ import { applyIdentityIntegration, SoulApplyError } from "#soul/apply.ts";
25
+ import {
26
+ persistHarvestSave,
27
+ planHarvestSave,
28
+ SoulHarvestError,
29
+ type TranscriptMessage,
30
+ } from "#soul/harvest.ts";
31
+ import {
32
+ composeSoulBeautyVoicePrompt,
33
+ composeSoulSelfVoicePrompt,
34
+ composeSoulWisdomVoicePrompt,
35
+ } from "#soul/prompts.ts";
36
+ import {
37
+ renderActiveRite,
38
+ renderClosingRite,
39
+ renderEnrichmentProposal,
40
+ renderFruitInMaturation,
41
+ renderHarvestedFruit,
42
+ renderIdentityChangeApplied,
43
+ renderIntegrationReview,
44
+ renderPossibleListenings,
45
+ type SoulListeningOption,
46
+ SoulSurfaceError,
47
+ } from "#soul/render.ts";
48
+ import {
49
+ clearFruitInMaturation,
50
+ clearHarvestedFruit,
51
+ getFruitInMaturation,
52
+ getHarvestedFruit,
53
+ harvestFruit,
54
+ resolveCliSoulSessionId,
55
+ SoulStateError,
56
+ setFruitInMaturation,
57
+ } from "#soul/state.ts";
58
+ import { renderSoulModeTransition } from "#soul/transition.ts";
59
+ import { newId, nowIso } from "#util/pyGenerators.ts";
60
+ import { pyStrip } from "#util/pythonText.ts";
61
+ import { optionValue, stripOptionWithValue } from "./args.ts";
62
+
63
+ /** Python's argparse subcommands, by name. Order is irrelevant; membership is not. */
64
+ export const SOUL_SUBCOMMANDS = [
65
+ "load",
66
+ "listen",
67
+ "rite",
68
+ "close",
69
+ "review",
70
+ "propose",
71
+ "apply",
72
+ "fruit",
73
+ "harvest",
74
+ "prompt",
75
+ ] as const;
76
+
77
+ const OPTIONS_WITH_VALUES = [
78
+ "--mirror-home",
79
+ "--db-path",
80
+ "--session-id",
81
+ "--matter",
82
+ "--self",
83
+ "--shadow",
84
+ "--wisdom",
85
+ "--beauty",
86
+ "--says",
87
+ "--listening-for",
88
+ "--question",
89
+ "--harvested",
90
+ "--echoes",
91
+ "--open",
92
+ "--integration",
93
+ "--origin",
94
+ "--journal",
95
+ "--ego",
96
+ "--persona",
97
+ "--key",
98
+ "--current",
99
+ "--proposed",
100
+ "--why",
101
+ "--confirm",
102
+ "--conversation-id",
103
+ "--journal-id",
104
+ "--journey",
105
+ ];
106
+
107
+ function positionals(args: readonly string[]): string[] {
108
+ return OPTIONS_WITH_VALUES.reduce<string[]>(
109
+ (remaining, option) => stripOptionWithValue(remaining, option),
110
+ [...args],
111
+ );
112
+ }
113
+
114
+ function fail(message: string): number {
115
+ process.stderr.write(`Error: ${message}\n`);
116
+ return 1;
117
+ }
118
+
119
+ /** argparse's own exit for an unknown subcommand or a missing required argument. */
120
+ function usageError(message: string): number {
121
+ process.stderr.write(`${message}\n`);
122
+ return 2;
123
+ }
124
+
125
+ export interface SoulRouteDeps {
126
+ readMessages: (db: Database, conversationId: string) => TranscriptMessage[];
127
+ /**
128
+ * The harvest journal's embedding (CV22.DS8.US3).
129
+ *
130
+ * Async because it is a real, billable provider round trip: `routing.ts`
131
+ * used to keep `harvest save` on Python unless a replay fixture was set, and
132
+ * nothing ever supplied one through the front door -- so the only leaf of
133
+ * the Soul command that crosses the provider seam could not complete on
134
+ * TypeScript at all. The route now resolves the family's provider and
135
+ * embeds through `generateEmbeddingSafely`, with the ledger hook attached.
136
+ */
137
+ embed: (db: WritableDatabase, text: string) => Promise<Uint8Array>;
138
+ newId: () => string;
139
+ nowIso: () => string;
140
+ }
141
+
142
+ export const defaultSoulRouteDeps: SoulRouteDeps = {
143
+ readMessages: (db, conversationId) =>
144
+ db
145
+ .prepare("SELECT role, content FROM messages WHERE conversation_id = ? ORDER BY rowid")
146
+ .all(conversationId) as unknown as TranscriptMessage[],
147
+ embed: async (db, text) => {
148
+ const family = await resolveFamilyProviders(process.env, SOUL_HARVEST_TRANSPORT);
149
+ if (!family?.embedding) {
150
+ // `routing.ts` reverts the family before the route is reached, so this
151
+ // is defense in depth rather than the operator-facing message.
152
+ throw new SoulHarvestError("soul harvest save requires an embedding provider");
153
+ }
154
+ // Through the wrapper, like every other embedding path: bounded retry of a
155
+ // transient empty payload, the AI-07 dimension guard, and one ledger row
156
+ // per round trip, priced (CR075's adjacent case).
157
+ const vector = await generateEmbeddingSafely(family.embedding, text, {
158
+ onAttempt: embeddingLedgerHook(db),
159
+ });
160
+ return new Uint8Array(new Float32Array(vector).buffer);
161
+ },
162
+ newId,
163
+ nowIso,
164
+ };
165
+
166
+ export async function runSoulRoute(
167
+ db: WritableDatabase,
168
+ argv: readonly string[],
169
+ deps: SoulRouteDeps = defaultSoulRouteDeps,
170
+ ): Promise<number> {
171
+ const rawArgs = argv.slice(1);
172
+ const args = positionals(rawArgs);
173
+ const sub = args[0];
174
+ const option = (name: string) => optionValue(rawArgs, name);
175
+
176
+ try {
177
+ switch (sub) {
178
+ case "load":
179
+ return runLoad(db, args[1] ?? null, option("--session-id"), deps);
180
+ case "listen":
181
+ return write(renderPossibleListenings(listeningOptions(option)));
182
+ case "rite": {
183
+ const voice = args[1];
184
+ if (!voice) return usageError("soul rite requires a voice");
185
+ return write(
186
+ renderActiveRite(voice, {
187
+ utterance: option("--says"),
188
+ listeningFor: option("--listening-for"),
189
+ question: option("--question"),
190
+ }),
191
+ );
192
+ }
193
+ case "close":
194
+ return write(
195
+ renderClosingRite({
196
+ harvested: option("--harvested"),
197
+ echoes: option("--echoes"),
198
+ remainsOpen: option("--open"),
199
+ integration: option("--integration"),
200
+ }),
201
+ );
202
+ case "review":
203
+ return write(
204
+ renderIntegrationReview({
205
+ // `--origin` and its legacy alias `--journal` share one argparse
206
+ // destination, so the LAST one on the command line wins -- not a
207
+ // fixed precedence between them.
208
+ journal: lastOptionValue(rawArgs, ["--origin", "--journal"]),
209
+ selfMaterial: option("--self"),
210
+ shadow: option("--shadow"),
211
+ ego: option("--ego"),
212
+ persona: option("--persona"),
213
+ leaveOpen: option("--open"),
214
+ }),
215
+ );
216
+ case "propose": {
217
+ const layer = args[1];
218
+ if (!layer) return usageError("soul propose requires a layer");
219
+ const key = resolveIdentityKey(layer, option("--key"));
220
+ return write(
221
+ renderEnrichmentProposal(layer, {
222
+ key,
223
+ origin: option("--origin") ?? "",
224
+ current: option("--current"),
225
+ proposed: option("--proposed") ?? "",
226
+ why: option("--why") ?? "",
227
+ }),
228
+ );
229
+ }
230
+ case "apply":
231
+ return runApply(db, args[1], option, deps);
232
+ case "fruit":
233
+ return runFruit(db, args[1], args[2] ?? null, option("--session-id"), deps);
234
+ case "harvest":
235
+ return await runHarvest(db, args[1], args[2] ?? null, option, deps);
236
+ case "prompt":
237
+ return runPrompt(db, args[1]);
238
+ default:
239
+ return usageError(`soul: unknown subcommand ${sub ?? ""}`);
240
+ }
241
+ } catch (error) {
242
+ if (
243
+ error instanceof SoulSurfaceError ||
244
+ error instanceof SoulStateError ||
245
+ error instanceof SoulApplyError ||
246
+ error instanceof SoulHarvestError
247
+ ) {
248
+ return fail(error.message);
249
+ }
250
+ throw error;
251
+ }
252
+ }
253
+
254
+ /** The value of whichever of `names` appears LAST in argv, as argparse resolves a shared dest. */
255
+ function lastOptionValue(args: readonly string[], names: readonly string[]): string | null {
256
+ let value: string | null = null;
257
+ for (let index = 0; index < args.length; index += 1) {
258
+ if (names.includes(args[index] as string)) value = args[index + 1] ?? null;
259
+ }
260
+ return value;
261
+ }
262
+
263
+ function write(rendered: string): number {
264
+ process.stdout.write(`${rendered}\n`);
265
+ return 0;
266
+ }
267
+
268
+ function listeningOptions(option: (name: string) => string | null): SoulListeningOption[] {
269
+ const raw: [string, string | null][] = [
270
+ ["self", option("--self")],
271
+ ["shadow", option("--shadow")],
272
+ ["wisdom", option("--wisdom")],
273
+ ["beauty", option("--beauty")],
274
+ ];
275
+ return raw
276
+ .filter(([, description]) => description !== null && description.trim() !== "")
277
+ .map(([voice, description]) => ({ voice, description: (description as string).trim() }));
278
+ }
279
+
280
+ /** Port of `_resolve_identity_key`. */
281
+ function resolveIdentityKey(layer: string, key: string | null): string {
282
+ if (layer === "self") return key || "soul";
283
+ if (layer === "shadow") return key || "profile";
284
+ if (layer === "ego") return key || "behavior";
285
+ if (layer === "persona") {
286
+ if (!key) throw new SoulApplyError("persona proposals require --key");
287
+ return key;
288
+ }
289
+ throw new SoulApplyError(`unsupported psyche layer: ${layer}`);
290
+ }
291
+
292
+ function runLoad(
293
+ db: WritableDatabase,
294
+ slug: string | null,
295
+ sessionId: string | null,
296
+ deps: SoulRouteDeps,
297
+ ): number {
298
+ const now = deps.nowIso();
299
+ if (slug) {
300
+ if (!getIdentityContent(db, "journey", slug)) {
301
+ process.stderr.write(`Error: journey '${slug}' not found.\n`);
302
+ return 1;
303
+ }
304
+ persistStickyDefaults(db, null, slug, now);
305
+ }
306
+ // `cmd_load` uses the OPERATING-MODE resolver, not the Soul one, and that
307
+ // resolver may return null -- in which case `activate_mode` writes the GLOBAL
308
+ // mode row instead of a session row. Falling back to `__global_soul_mode__`
309
+ // here would silently write to a different row than Python does.
310
+ const resolvedSessionId = resolveRuntimeSessionId(
311
+ db,
312
+ sessionId,
313
+ process.env.MIRROR_SESSION_ID ?? null,
314
+ );
315
+ activateOperatingMode(
316
+ db,
317
+ { mode: "Soul Mode", journey: slug, sessionId: resolvedSessionId },
318
+ now,
319
+ );
320
+ return write(renderSoulModeTransition(slug));
321
+ }
322
+
323
+ function runApply(
324
+ db: WritableDatabase,
325
+ layer: string | undefined,
326
+ option: (name: string) => string | null,
327
+ deps: SoulRouteDeps,
328
+ ): number {
329
+ if (!layer) return usageError("soul apply requires a layer");
330
+ const key = resolveIdentityKey(layer, option("--key"));
331
+ if (option("--confirm") !== "APPLY") {
332
+ return fail("identity update requires --confirm APPLY");
333
+ }
334
+ const content = pyStrip(option("--proposed") ?? "");
335
+ if (!content) return fail("identity content must not be empty");
336
+ applyIdentityIntegration(
337
+ db,
338
+ {
339
+ layer,
340
+ key,
341
+ content,
342
+ origin: option("--origin"),
343
+ conversationId: option("--conversation-id"),
344
+ journalId: option("--journal-id"),
345
+ },
346
+ { newId: deps.newId, nowIso: deps.nowIso() },
347
+ );
348
+ return write(renderIdentityChangeApplied(layer, { key, content }));
349
+ }
350
+
351
+ function runFruit(
352
+ db: WritableDatabase,
353
+ action: string | undefined,
354
+ fruit: string | null,
355
+ sessionId: string | null,
356
+ deps: SoulRouteDeps,
357
+ ): number {
358
+ const resolved = resolveCliSoulSessionId(db, sessionId, process.env.MIRROR_SESSION_ID ?? null);
359
+ if (action === "set") {
360
+ if (fruit === null) return usageError("soul fruit set requires a fruit");
361
+ const state = setFruitInMaturation(db, fruit, resolved, deps.nowIso());
362
+ return write(renderFruitInMaturation(state.fruit ?? ""));
363
+ }
364
+ if (action === "show") {
365
+ const state = getFruitInMaturation(db, resolved);
366
+ if (!state.fruit) return fail("No fruit in maturation.");
367
+ return write(renderFruitInMaturation(state.fruit));
368
+ }
369
+ if (action === "clear") {
370
+ clearFruitInMaturation(db, resolved, deps.nowIso());
371
+ process.stdout.write("Fruit in maturation cleared.\n");
372
+ return 0;
373
+ }
374
+ return usageError(`soul fruit: unknown action ${action ?? ""}`);
375
+ }
376
+
377
+ async function runHarvest(
378
+ db: WritableDatabase,
379
+ action: string | undefined,
380
+ fruit: string | null,
381
+ option: (name: string) => string | null,
382
+ deps: SoulRouteDeps,
383
+ ): Promise<number> {
384
+ const resolved = resolveCliSoulSessionId(
385
+ db,
386
+ option("--session-id"),
387
+ process.env.MIRROR_SESSION_ID ?? null,
388
+ );
389
+ if (action === "set") {
390
+ const state = harvestFruit(db, { fruit, sessionId: resolved }, deps.nowIso());
391
+ return write(renderHarvestedFruit(state.fruit ?? ""));
392
+ }
393
+ if (action === "show") {
394
+ const state = getHarvestedFruit(db, resolved);
395
+ if (!state.fruit) return fail("No harvested fruit.");
396
+ return write(renderHarvestedFruit(state.fruit));
397
+ }
398
+ if (action === "save") {
399
+ const state = getHarvestedFruit(db, resolved);
400
+ if (!state.fruit) return fail("No harvested fruit.");
401
+ // Compose, then embed, then persist. The fruit is cleared inside the
402
+ // persist step, so a failing embedding leaves it harvested for a retry
403
+ // rather than losing it to a provider outage -- Python's ordering.
404
+ const plan = planHarvestSave(db, resolved, (conversationId) =>
405
+ deps.readMessages(db, conversationId),
406
+ );
407
+ const embedding = await deps.embed(db, plan.embedText);
408
+ const result = persistHarvestSave(
409
+ db,
410
+ plan,
411
+ { sessionId: resolved, journey: option("--journey") },
412
+ { newId: deps.newId, nowIso: deps.nowIso(), embedding },
413
+ );
414
+ process.stdout.write(`Harvest saved to journal. ${result.memoryId}\n`);
415
+ return 0;
416
+ }
417
+ if (action === "decline") {
418
+ clearHarvestedFruit(db, resolved, deps.nowIso());
419
+ process.stdout.write("Harvest discarded without journal save.\n");
420
+ return 0;
421
+ }
422
+ return usageError(`soul harvest: unknown action ${action ?? ""}`);
423
+ }
424
+
425
+ function runPrompt(db: Database, voice: string | undefined): number {
426
+ // `print(prompt)` appends a newline to a template that already ends with one,
427
+ // so the shipped output ends with two. Stripping either is a divergence.
428
+ if (voice === "self") return write(composeSoulSelfVoicePrompt(db));
429
+ if (voice === "wisdom") return write(composeSoulWisdomVoicePrompt());
430
+ if (voice === "beauty") return write(composeSoulBeautyVoicePrompt());
431
+ process.stderr.write(`Error: unsupported Soul Mode prompt voice: ${voice ?? ""}\n`);
432
+ return 1;
433
+ }