mirror-mind 0.0.1 → 0.33.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (474) hide show
  1. package/.claude/hooks/log-assistant-reply.sh +85 -0
  2. package/.claude/hooks/log-session-end.sh +85 -0
  3. package/.claude/hooks/log-user-prompt.sh +85 -0
  4. package/.claude/hooks/mirror-inject.sh +85 -0
  5. package/.claude/hooks/session-start.sh +85 -0
  6. package/.gemini/hooks/log-assistant.sh +85 -0
  7. package/.gemini/hooks/log-user.sh +85 -0
  8. package/.gemini/hooks/session-end.sh +85 -0
  9. package/.gemini/hooks/session-start.sh +85 -0
  10. package/.pi/extensions/mirror-logger.ts +646 -0
  11. package/.pi/skills/mm-backup/SKILL.md +21 -0
  12. package/.pi/skills/mm-build/SKILL.md +1206 -0
  13. package/.pi/skills/mm-consolidate/SKILL.md +112 -0
  14. package/.pi/skills/mm-consult/SKILL.md +70 -0
  15. package/.pi/skills/mm-conversations/SKILL.md +16 -0
  16. package/.pi/skills/mm-discard/SKILL.md +39 -0
  17. package/.pi/skills/mm-explore/SKILL.md +255 -0
  18. package/.pi/skills/mm-help/SKILL.md +74 -0
  19. package/.pi/skills/mm-identity/SKILL.md +96 -0
  20. package/.pi/skills/mm-journal/SKILL.md +16 -0
  21. package/.pi/skills/mm-journey/SKILL.md +23 -0
  22. package/.pi/skills/mm-journeys/SKILL.md +15 -0
  23. package/.pi/skills/mm-memories/SKILL.md +17 -0
  24. package/.pi/skills/mm-mirror/SKILL.md +98 -0
  25. package/.pi/skills/mm-mute/SKILL.md +18 -0
  26. package/.pi/skills/mm-new/SKILL.md +18 -0
  27. package/.pi/skills/mm-recall/SKILL.md +17 -0
  28. package/.pi/skills/mm-release-notes/SKILL.md +52 -0
  29. package/.pi/skills/mm-seed/SKILL.md +19 -0
  30. package/.pi/skills/mm-shadow/SKILL.md +117 -0
  31. package/.pi/skills/mm-soul/SKILL.md +502 -0
  32. package/.pi/skills/mm-tasks/SKILL.md +24 -0
  33. package/.pi/skills/mm-update/SKILL.md +32 -0
  34. package/.pi/skills/mm-week/SKILL.md +28 -0
  35. package/.pi/skills/mm-welcome/SKILL.md +24 -0
  36. package/AGENTS.md +188 -0
  37. package/README.md +126 -9
  38. package/bin/loader.js +67 -0
  39. package/bin/mirror-hook.js +7 -0
  40. package/bin/mirror.js +7 -0
  41. package/docs/releases/index.md +100 -0
  42. package/docs/releases/pending-cutoffs.md +510 -0
  43. package/docs/releases/v0.10.0.md +123 -0
  44. package/docs/releases/v0.10.1.md +66 -0
  45. package/docs/releases/v0.10.10.md +51 -0
  46. package/docs/releases/v0.10.11.md +39 -0
  47. package/docs/releases/v0.10.12.md +57 -0
  48. package/docs/releases/v0.10.13.md +35 -0
  49. package/docs/releases/v0.10.14.md +42 -0
  50. package/docs/releases/v0.10.15.md +53 -0
  51. package/docs/releases/v0.10.16.md +35 -0
  52. package/docs/releases/v0.10.17.md +43 -0
  53. package/docs/releases/v0.10.2.md +64 -0
  54. package/docs/releases/v0.10.3.md +78 -0
  55. package/docs/releases/v0.10.4.md +42 -0
  56. package/docs/releases/v0.10.5.md +57 -0
  57. package/docs/releases/v0.10.6.md +47 -0
  58. package/docs/releases/v0.10.7.md +53 -0
  59. package/docs/releases/v0.10.8.md +49 -0
  60. package/docs/releases/v0.10.9.md +37 -0
  61. package/docs/releases/v0.11.0.md +86 -0
  62. package/docs/releases/v0.12.0.md +67 -0
  63. package/docs/releases/v0.13.0.md +64 -0
  64. package/docs/releases/v0.14.0.md +68 -0
  65. package/docs/releases/v0.15.0.md +48 -0
  66. package/docs/releases/v0.16.0.md +50 -0
  67. package/docs/releases/v0.16.1.md +55 -0
  68. package/docs/releases/v0.16.2.md +36 -0
  69. package/docs/releases/v0.17.0.md +50 -0
  70. package/docs/releases/v0.17.1.md +58 -0
  71. package/docs/releases/v0.17.2.md +42 -0
  72. package/docs/releases/v0.17.3.md +58 -0
  73. package/docs/releases/v0.18.0.md +84 -0
  74. package/docs/releases/v0.18.1.md +51 -0
  75. package/docs/releases/v0.18.2.md +69 -0
  76. package/docs/releases/v0.19.0.md +85 -0
  77. package/docs/releases/v0.19.1.md +46 -0
  78. package/docs/releases/v0.20.0.md +85 -0
  79. package/docs/releases/v0.21.0.md +61 -0
  80. package/docs/releases/v0.21.1.md +47 -0
  81. package/docs/releases/v0.22.0.md +77 -0
  82. package/docs/releases/v0.22.1.md +42 -0
  83. package/docs/releases/v0.23.0.md +105 -0
  84. package/docs/releases/v0.24.0.md +125 -0
  85. package/docs/releases/v0.25.0.md +107 -0
  86. package/docs/releases/v0.26.0.md +139 -0
  87. package/docs/releases/v0.26.1.md +65 -0
  88. package/docs/releases/v0.26.2.md +43 -0
  89. package/docs/releases/v0.27.0.md +63 -0
  90. package/docs/releases/v0.28.0.md +59 -0
  91. package/docs/releases/v0.29.0.md +61 -0
  92. package/docs/releases/v0.29.1.md +59 -0
  93. package/docs/releases/v0.29.2.md +59 -0
  94. package/docs/releases/v0.29.3.md +93 -0
  95. package/docs/releases/v0.30.0.md +75 -0
  96. package/docs/releases/v0.30.1.md +93 -0
  97. package/docs/releases/v0.30.2.md +66 -0
  98. package/docs/releases/v0.31.0.md +122 -0
  99. package/docs/releases/v0.31.1.md +142 -0
  100. package/docs/releases/v0.31.10.md +118 -0
  101. package/docs/releases/v0.31.11.md +88 -0
  102. package/docs/releases/v0.31.12.md +105 -0
  103. package/docs/releases/v0.31.13.md +99 -0
  104. package/docs/releases/v0.31.14.md +97 -0
  105. package/docs/releases/v0.31.2.md +115 -0
  106. package/docs/releases/v0.31.3.md +142 -0
  107. package/docs/releases/v0.31.4.md +117 -0
  108. package/docs/releases/v0.31.5.md +155 -0
  109. package/docs/releases/v0.31.6.md +132 -0
  110. package/docs/releases/v0.31.7.md +132 -0
  111. package/docs/releases/v0.31.8.md +120 -0
  112. package/docs/releases/v0.31.9.md +120 -0
  113. package/docs/releases/v0.32.0.md +261 -0
  114. package/docs/releases/v0.32.1.md +59 -0
  115. package/docs/releases/v0.33.0.md +164 -0
  116. package/docs/releases/v0.8.0.md +118 -0
  117. package/docs/releases/v0.9.0.md +98 -0
  118. package/docs/releases/v0.9.1.md +83 -0
  119. package/package.json +102 -6
  120. package/plugins/mirror-mind/.claude-plugin/plugin.json +17 -0
  121. package/plugins/mirror-mind/hooks/hooks.json +62 -0
  122. package/plugins/mirror-mind/hooks/log-assistant-reply.sh +98 -0
  123. package/plugins/mirror-mind/hooks/log-session-end.sh +98 -0
  124. package/plugins/mirror-mind/hooks/log-user-prompt.sh +98 -0
  125. package/plugins/mirror-mind/hooks/mirror-inject.sh +98 -0
  126. package/plugins/mirror-mind/hooks/session-start.sh +98 -0
  127. package/plugins/mirror-mind/skills/mm-backup/SKILL.md +27 -0
  128. package/plugins/mirror-mind/skills/mm-build/SKILL.md +1206 -0
  129. package/plugins/mirror-mind/skills/mm-consolidate/SKILL.md +112 -0
  130. package/plugins/mirror-mind/skills/mm-consult/SKILL.md +83 -0
  131. package/plugins/mirror-mind/skills/mm-conversations/SKILL.md +17 -0
  132. package/plugins/mirror-mind/skills/mm-discard/SKILL.md +40 -0
  133. package/plugins/mirror-mind/skills/mm-explore/SKILL.md +241 -0
  134. package/plugins/mirror-mind/skills/mm-help/SKILL.md +104 -0
  135. package/plugins/mirror-mind/skills/mm-identity/SKILL.md +96 -0
  136. package/plugins/mirror-mind/skills/mm-journal/SKILL.md +21 -0
  137. package/plugins/mirror-mind/skills/mm-journey/SKILL.md +37 -0
  138. package/plugins/mirror-mind/skills/mm-journeys/SKILL.md +15 -0
  139. package/plugins/mirror-mind/skills/mm-memories/SKILL.md +20 -0
  140. package/plugins/mirror-mind/skills/mm-mirror/SKILL.md +67 -0
  141. package/plugins/mirror-mind/skills/mm-mute/SKILL.md +18 -0
  142. package/plugins/mirror-mind/skills/mm-new/SKILL.md +18 -0
  143. package/plugins/mirror-mind/skills/mm-recall/SKILL.md +17 -0
  144. package/plugins/mirror-mind/skills/mm-release-notes/SKILL.md +31 -0
  145. package/plugins/mirror-mind/skills/mm-seed/SKILL.md +15 -0
  146. package/plugins/mirror-mind/skills/mm-shadow/SKILL.md +117 -0
  147. package/plugins/mirror-mind/skills/mm-soul/SKILL.md +488 -0
  148. package/plugins/mirror-mind/skills/mm-tasks/SKILL.md +36 -0
  149. package/plugins/mirror-mind/skills/mm-update/SKILL.md +30 -0
  150. package/plugins/mirror-mind/skills/mm-week/SKILL.md +46 -0
  151. package/plugins/mirror-mind/skills/mm-welcome/SKILL.md +22 -0
  152. package/scripts/codex-hooks/session-end.sh +85 -0
  153. package/scripts/codex-hooks/session-start.sh +85 -0
  154. package/scripts/codex-mirror.sh +48 -0
  155. package/templates/identity/README.md +119 -0
  156. package/templates/identity/ego/behavior.yaml +52 -0
  157. package/templates/identity/ego/constraints.yaml +15 -0
  158. package/templates/identity/ego/identity.yaml +43 -0
  159. package/templates/identity/journeys/personal-growth.yaml +39 -0
  160. package/templates/identity/organization/identity.yaml +24 -0
  161. package/templates/identity/organization/principles.yaml +17 -0
  162. package/templates/identity/personas/coach.yaml +94 -0
  163. package/templates/identity/personas/designer.yaml +93 -0
  164. package/templates/identity/personas/doctor.yaml +96 -0
  165. package/templates/identity/personas/engineer.yaml +77 -0
  166. package/templates/identity/personas/financial.yaml +97 -0
  167. package/templates/identity/personas/prompt-engineer.yaml +122 -0
  168. package/templates/identity/personas/researcher.yaml +88 -0
  169. package/templates/identity/personas/strategist.yaml +91 -0
  170. package/templates/identity/personas/teacher.yaml +95 -0
  171. package/templates/identity/personas/therapist.yaml +91 -0
  172. package/templates/identity/personas/thinker.yaml +74 -0
  173. package/templates/identity/personas/writer.yaml +76 -0
  174. package/templates/identity/self/config.yaml +16 -0
  175. package/templates/identity/self/soul.yaml +105 -0
  176. package/templates/identity/user/identity.yaml +30 -0
  177. package/ts/src/backup/staging.ts +62 -0
  178. package/ts/src/backup/zipBackup.ts +267 -0
  179. package/ts/src/backup/zipReader.ts +109 -0
  180. package/ts/src/backup/zipWriter.ts +148 -0
  181. package/ts/src/builder/activeCheckpoint.ts +134 -0
  182. package/ts/src/builder/aggregateStatus.ts +34 -0
  183. package/ts/src/builder/approve.ts +149 -0
  184. package/ts/src/builder/argv.ts +292 -0
  185. package/ts/src/builder/ariadMethod.ts +536 -0
  186. package/ts/src/builder/artifacts/artifactSurfaces.ts +106 -0
  187. package/ts/src/builder/artifacts/artifactWriter.ts +113 -0
  188. package/ts/src/builder/artifacts/closureArtifacts.ts +199 -0
  189. package/ts/src/builder/artifacts/deliveryStoryArtifacts.ts +316 -0
  190. package/ts/src/builder/artifacts/planArtifacts.ts +242 -0
  191. package/ts/src/builder/artifacts/scaffoldSections.ts +273 -0
  192. package/ts/src/builder/artifacts/scaffoldState.ts +227 -0
  193. package/ts/src/builder/artifacts/storyFiles.ts +61 -0
  194. package/ts/src/builder/artifacts/storyIndex.ts +166 -0
  195. package/ts/src/builder/cadence.ts +59 -0
  196. package/ts/src/builder/card.ts +192 -0
  197. package/ts/src/builder/checkpointRefused.ts +66 -0
  198. package/ts/src/builder/closure.ts +964 -0
  199. package/ts/src/builder/commands.ts +2053 -0
  200. package/ts/src/builder/cursorPosition.ts +176 -0
  201. package/ts/src/builder/cursorTransitions.ts +77 -0
  202. package/ts/src/builder/deliveryCursor.ts +577 -0
  203. package/ts/src/builder/deliveryStoryClosure.ts +521 -0
  204. package/ts/src/builder/deliveryStoryPlan.ts +536 -0
  205. package/ts/src/builder/deliveryStoryReady.ts +160 -0
  206. package/ts/src/builder/deliveryStoryRoadmapClosure.ts +151 -0
  207. package/ts/src/builder/expand.ts +336 -0
  208. package/ts/src/builder/flowUnit.ts +339 -0
  209. package/ts/src/builder/homeSurface.ts +122 -0
  210. package/ts/src/builder/implementationGuard.ts +125 -0
  211. package/ts/src/builder/index.ts +11 -0
  212. package/ts/src/builder/lifecycleRefusal.ts +92 -0
  213. package/ts/src/builder/lifecycleRibbon.ts +159 -0
  214. package/ts/src/builder/load.ts +313 -0
  215. package/ts/src/builder/methodAdoption.ts +113 -0
  216. package/ts/src/builder/methodDefinition.ts +429 -0
  217. package/ts/src/builder/methodInspection.ts +371 -0
  218. package/ts/src/builder/plan.ts +408 -0
  219. package/ts/src/builder/planPreauthorization.ts +265 -0
  220. package/ts/src/builder/prepare.ts +225 -0
  221. package/ts/src/builder/projectPaths.ts +64 -0
  222. package/ts/src/builder/pull.ts +356 -0
  223. package/ts/src/builder/pullCandidates.ts +463 -0
  224. package/ts/src/builder/pullCandidatesRender.ts +238 -0
  225. package/ts/src/builder/refinementField.ts +93 -0
  226. package/ts/src/builder/releaseIntent.ts +173 -0
  227. package/ts/src/builder/resumeState.ts +84 -0
  228. package/ts/src/builder/resumeSurface.ts +149 -0
  229. package/ts/src/builder/roadmapGrammar.ts +300 -0
  230. package/ts/src/builder/roadmapScan.ts +97 -0
  231. package/ts/src/builder/roadmapScope.ts +210 -0
  232. package/ts/src/builder/scopePhrases.ts +146 -0
  233. package/ts/src/builder/storyPaths.ts +196 -0
  234. package/ts/src/builder/storyPlanPreauthorization.ts +287 -0
  235. package/ts/src/builder/surfaceProtocol.ts +42 -0
  236. package/ts/src/builder/templateGeneration.ts +148 -0
  237. package/ts/src/builder/transition.ts +178 -0
  238. package/ts/src/consult/args.ts +99 -0
  239. package/ts/src/consult/core.ts +122 -0
  240. package/ts/src/consult/modelCatalog.ts +55 -0
  241. package/ts/src/consult/render.ts +47 -0
  242. package/ts/src/conversation/append.ts +414 -0
  243. package/ts/src/conversation/backfill.ts +387 -0
  244. package/ts/src/conversation/closeTail.ts +467 -0
  245. package/ts/src/conversation/extraction.ts +333 -0
  246. package/ts/src/conversation/extractionDriver.ts +130 -0
  247. package/ts/src/conversation/extractionRun.ts +70 -0
  248. package/ts/src/conversation/hookPayload.ts +51 -0
  249. package/ts/src/conversation/journeyInference.ts +165 -0
  250. package/ts/src/conversation/journeyRepair.ts +114 -0
  251. package/ts/src/conversation/lifecycleFaces.ts +197 -0
  252. package/ts/src/conversation/lifecycleWrites.ts +313 -0
  253. package/ts/src/conversation/listing.ts +77 -0
  254. package/ts/src/conversation/logger.ts +851 -0
  255. package/ts/src/conversation/loggerCli.ts +321 -0
  256. package/ts/src/conversation/loggerRuntime.ts +259 -0
  257. package/ts/src/conversation/metadataLifecycle.ts +443 -0
  258. package/ts/src/conversation/recall.ts +66 -0
  259. package/ts/src/conversation/sessionComposites.ts +258 -0
  260. package/ts/src/conversation/sessionImport.ts +90 -0
  261. package/ts/src/conversation/transcriptBackfill.ts +141 -0
  262. package/ts/src/cultivation/applyActions.ts +241 -0
  263. package/ts/src/cultivation/cluster.ts +86 -0
  264. package/ts/src/cultivation/consolidationStore.ts +281 -0
  265. package/ts/src/cultivation/promptContext.ts +62 -0
  266. package/ts/src/cultivation/propose.ts +305 -0
  267. package/ts/src/cultivation/scan.ts +155 -0
  268. package/ts/src/db/backupGate.ts +47 -0
  269. package/ts/src/db/bootstrap.ts +108 -0
  270. package/ts/src/db/bootstrapLock.ts +223 -0
  271. package/ts/src/db/copyGuard.ts +60 -0
  272. package/ts/src/db/database.ts +397 -0
  273. package/ts/src/db/decode.ts +66 -0
  274. package/ts/src/db/ftsIntegrity.ts +41 -0
  275. package/ts/src/db/migrateOnOpen.ts +214 -0
  276. package/ts/src/db/migrations.ts +576 -0
  277. package/ts/src/db/readyOnOpen.ts +35 -0
  278. package/ts/src/db/rowDecode.ts +42 -0
  279. package/ts/src/db/schema.ts +432 -0
  280. package/ts/src/db/schemaInventory.ts +225 -0
  281. package/ts/src/db/schemaInventorySnapshot.ts +2107 -0
  282. package/ts/src/db/schemaState.ts +91 -0
  283. package/ts/src/db/schemaTsDivergence.ts +114 -0
  284. package/ts/src/descriptor/descriptorRead.ts +49 -0
  285. package/ts/src/docs/docsLint.ts +265 -0
  286. package/ts/src/explorer/handoff.ts +522 -0
  287. package/ts/src/explorer/render.ts +335 -0
  288. package/ts/src/explorer/story.ts +769 -0
  289. package/ts/src/explorer/transition.ts +72 -0
  290. package/ts/src/extensions/bindings.ts +170 -0
  291. package/ts/src/extensions/catalog.ts +249 -0
  292. package/ts/src/extensions/catalogCommands.ts +513 -0
  293. package/ts/src/extensions/catalogWrites.ts +652 -0
  294. package/ts/src/extensions/contextRuntime.ts +325 -0
  295. package/ts/src/extensions/dispatch.ts +374 -0
  296. package/ts/src/extensions/errors.ts +24 -0
  297. package/ts/src/extensions/manifest.ts +356 -0
  298. package/ts/src/extensions/migrations.ts +350 -0
  299. package/ts/src/extraction/conversation.ts +304 -0
  300. package/ts/src/extraction/conversationMetadata.ts +206 -0
  301. package/ts/src/extraction/fencing.ts +106 -0
  302. package/ts/src/extraction/json.ts +17 -0
  303. package/ts/src/extraction/prompts.ts +470 -0
  304. package/ts/src/frontDoor/args.ts +30 -0
  305. package/ts/src/frontDoor/argvShape.ts +106 -0
  306. package/ts/src/frontDoor/buildLoadRuntime.ts +139 -0
  307. package/ts/src/frontDoor/buildRoute.ts +398 -0
  308. package/ts/src/frontDoor/cli.ts +1872 -0
  309. package/ts/src/frontDoor/cloneRoleGuard.ts +98 -0
  310. package/ts/src/frontDoor/consultRoute.ts +50 -0
  311. package/ts/src/frontDoor/contentTailRoute.ts +239 -0
  312. package/ts/src/frontDoor/conversationLoggerRoute.ts +83 -0
  313. package/ts/src/frontDoor/cultivationRoute.ts +210 -0
  314. package/ts/src/frontDoor/dbPath.ts +131 -0
  315. package/ts/src/frontDoor/dbSafetyToolsRoute.ts +240 -0
  316. package/ts/src/frontDoor/exploreRoute.ts +572 -0
  317. package/ts/src/frontDoor/extensionCatalogRoute.ts +252 -0
  318. package/ts/src/frontDoor/frontDoorLog.ts +57 -0
  319. package/ts/src/frontDoor/identityWrite.ts +36 -0
  320. package/ts/src/frontDoor/journeyWriteRoute.ts +26 -0
  321. package/ts/src/frontDoor/lifecycleWriteRoute.ts +110 -0
  322. package/ts/src/frontDoor/liveBackup.ts +100 -0
  323. package/ts/src/frontDoor/mirrorModeRoute.ts +163 -0
  324. package/ts/src/frontDoor/namedRefusal.ts +35 -0
  325. package/ts/src/frontDoor/nodeSupport.ts +24 -0
  326. package/ts/src/frontDoor/render/consolidate.ts +199 -0
  327. package/ts/src/frontDoor/render/conversations.ts +33 -0
  328. package/ts/src/frontDoor/render/cultivationShared.ts +21 -0
  329. package/ts/src/frontDoor/render/descriptor.ts +20 -0
  330. package/ts/src/frontDoor/render/detectPersona.ts +50 -0
  331. package/ts/src/frontDoor/render/icons.ts +13 -0
  332. package/ts/src/frontDoor/render/identity.ts +59 -0
  333. package/ts/src/frontDoor/render/identityRows.ts +17 -0
  334. package/ts/src/frontDoor/render/inspectPersona.ts +53 -0
  335. package/ts/src/frontDoor/render/journeyStatus.ts +78 -0
  336. package/ts/src/frontDoor/render/journeys.ts +79 -0
  337. package/ts/src/frontDoor/render/list.ts +41 -0
  338. package/ts/src/frontDoor/render/memories.ts +67 -0
  339. package/ts/src/frontDoor/render/recall.ts +49 -0
  340. package/ts/src/frontDoor/render/shadow.ts +155 -0
  341. package/ts/src/frontDoor/render/tasks.ts +99 -0
  342. package/ts/src/frontDoor/render/tasksImportSync.ts +63 -0
  343. package/ts/src/frontDoor/render/week.ts +113 -0
  344. package/ts/src/frontDoor/routing.ts +1001 -0
  345. package/ts/src/frontDoor/runtimeRoute.ts +623 -0
  346. package/ts/src/frontDoor/searchRoute.ts +166 -0
  347. package/ts/src/frontDoor/seedPaths.ts +98 -0
  348. package/ts/src/frontDoor/sessionStartWelcomeRoute.ts +166 -0
  349. package/ts/src/frontDoor/soulRoute.ts +433 -0
  350. package/ts/src/frontDoor/stdinSource.ts +37 -0
  351. package/ts/src/frontDoor/tasksImportSyncRoute.ts +110 -0
  352. package/ts/src/frontDoor/tasksWriteRoute.ts +101 -0
  353. package/ts/src/frontDoor/usage.ts +176 -0
  354. package/ts/src/guards/claudePlugin.ts +298 -0
  355. package/ts/src/guards/packContents.ts +306 -0
  356. package/ts/src/guards/piSourcedSkills.ts +70 -0
  357. package/ts/src/guards/retiredRouteShadows.ts +57 -0
  358. package/ts/src/guards/retiredSurfaces.ts +564 -0
  359. package/ts/src/hooks/claude.ts +214 -0
  360. package/ts/src/hooks/codex.ts +45 -0
  361. package/ts/src/hooks/gemini.ts +126 -0
  362. package/ts/src/hooks/main.ts +109 -0
  363. package/ts/src/hooks/mirrorState.ts +71 -0
  364. package/ts/src/hooks/payload.ts +73 -0
  365. package/ts/src/hooks/runtime.ts +179 -0
  366. package/ts/src/hooks/runtimeDetection.ts +34 -0
  367. package/ts/src/identity/applyConsolidationIdentityUpdate.ts +67 -0
  368. package/ts/src/identity/descriptorGenerate.ts +162 -0
  369. package/ts/src/identity/identityEdit.ts +143 -0
  370. package/ts/src/identity/identityKey.ts +77 -0
  371. package/ts/src/identity/identityRead.ts +69 -0
  372. package/ts/src/identity/identityStore.ts +80 -0
  373. package/ts/src/identity/journeyListing.ts +53 -0
  374. package/ts/src/identity/personaInspect.ts +44 -0
  375. package/ts/src/identity/personaListing.ts +37 -0
  376. package/ts/src/identity/setIdentity.ts +64 -0
  377. package/ts/src/index.ts +231 -0
  378. package/ts/src/init/init.ts +111 -0
  379. package/ts/src/journey/journeyOptions.ts +131 -0
  380. package/ts/src/journey/journeyRemoval.ts +158 -0
  381. package/ts/src/journey/journeyStatus.ts +149 -0
  382. package/ts/src/journey/journeySyncFile.ts +87 -0
  383. package/ts/src/journey/journeyWrite.ts +203 -0
  384. package/ts/src/journey/parentJourney.ts +41 -0
  385. package/ts/src/journey/validateParentJourney.ts +55 -0
  386. package/ts/src/mcp/boundary.ts +151 -0
  387. package/ts/src/mcp/guards.ts +244 -0
  388. package/ts/src/mcp/main.ts +121 -0
  389. package/ts/src/mcp/payload.ts +141 -0
  390. package/ts/src/mcp/protocol.ts +141 -0
  391. package/ts/src/mcp/readModels.ts +241 -0
  392. package/ts/src/mcp/registry.ts +201 -0
  393. package/ts/src/mcp/serve.ts +120 -0
  394. package/ts/src/mcp/tools/deterministic.ts +137 -0
  395. package/ts/src/mcp/tools/providerCrossing.ts +187 -0
  396. package/ts/src/mcp/wire.ts +8 -0
  397. package/ts/src/memory/journal.ts +180 -0
  398. package/ts/src/memory/listing.ts +107 -0
  399. package/ts/src/memory/memoryWrite.ts +88 -0
  400. package/ts/src/memory/reinforcement.ts +35 -0
  401. package/ts/src/mirror/context.ts +161 -0
  402. package/ts/src/mirror/defaultResolution.ts +199 -0
  403. package/ts/src/mirror/mirrorForm.ts +43 -0
  404. package/ts/src/mirror/orchestration.ts +364 -0
  405. package/ts/src/mirror/reception.ts +194 -0
  406. package/ts/src/mirror/render.ts +75 -0
  407. package/ts/src/mirror/runtimeSession.ts +205 -0
  408. package/ts/src/mode/operatingMode.ts +148 -0
  409. package/ts/src/observability/callOutcome.ts +115 -0
  410. package/ts/src/observability/inspectLedger.ts +329 -0
  411. package/ts/src/observability/ledgerHooks.ts +126 -0
  412. package/ts/src/observability/llmCalls.ts +154 -0
  413. package/ts/src/persona/detectPersona.ts +81 -0
  414. package/ts/src/planning/promptAssembly.ts +111 -0
  415. package/ts/src/planning/weekPending.ts +81 -0
  416. package/ts/src/planning/weekPlan.ts +183 -0
  417. package/ts/src/planning/weekSave.ts +117 -0
  418. package/ts/src/providers/config.ts +189 -0
  419. package/ts/src/providers/cost.ts +64 -0
  420. package/ts/src/providers/credits.ts +204 -0
  421. package/ts/src/providers/embedding.ts +321 -0
  422. package/ts/src/providers/familyProviders.ts +147 -0
  423. package/ts/src/providers/llm.ts +334 -0
  424. package/ts/src/providers/openrouter.ts +244 -0
  425. package/ts/src/providers/redaction.ts +110 -0
  426. package/ts/src/providers/replay.ts +15 -0
  427. package/ts/src/providers/transport.ts +410 -0
  428. package/ts/src/repair/encodingRepair.ts +253 -0
  429. package/ts/src/runtime/backup.ts +151 -0
  430. package/ts/src/runtime/channel.ts +146 -0
  431. package/ts/src/runtime/config.ts +168 -0
  432. package/ts/src/runtime/diagnose.ts +686 -0
  433. package/ts/src/runtime/git.ts +510 -0
  434. package/ts/src/runtime/installKind.ts +220 -0
  435. package/ts/src/runtime/migrate.ts +154 -0
  436. package/ts/src/runtime/packageIdentity.ts +102 -0
  437. package/ts/src/runtime/releaseNotes.ts +244 -0
  438. package/ts/src/runtime/status.ts +557 -0
  439. package/ts/src/runtime/strategies/clone.ts +200 -0
  440. package/ts/src/runtime/strategies/package.ts +328 -0
  441. package/ts/src/runtime/treeRoot.ts +30 -0
  442. package/ts/src/runtime/update.ts +445 -0
  443. package/ts/src/runtime/updateGate.ts +40 -0
  444. package/ts/src/runtime/updatePipeline.ts +159 -0
  445. package/ts/src/runtime/version.ts +36 -0
  446. package/ts/src/runtime/wiring.ts +130 -0
  447. package/ts/src/search/memorySearch.ts +338 -0
  448. package/ts/src/search/ranker.ts +154 -0
  449. package/ts/src/seed/seed.ts +256 -0
  450. package/ts/src/seed/seedYaml.ts +133 -0
  451. package/ts/src/soul/apply.ts +218 -0
  452. package/ts/src/soul/harvest.ts +267 -0
  453. package/ts/src/soul/prompts/soul_beauty_voice.md +103 -0
  454. package/ts/src/soul/prompts/soul_self_voice.md +219 -0
  455. package/ts/src/soul/prompts/soul_wisdom_voice.md +121 -0
  456. package/ts/src/soul/prompts.ts +85 -0
  457. package/ts/src/soul/render.ts +358 -0
  458. package/ts/src/soul/state.ts +212 -0
  459. package/ts/src/soul/transition.ts +37 -0
  460. package/ts/src/tasks/journeyPathParse.ts +113 -0
  461. package/ts/src/tasks/taskImportSync.ts +142 -0
  462. package/ts/src/tasks/taskStore.ts +312 -0
  463. package/ts/src/tasks/weekView.ts +120 -0
  464. package/ts/src/util/clipCodePoints.ts +26 -0
  465. package/ts/src/util/paths.ts +63 -0
  466. package/ts/src/util/program.ts +9 -0
  467. package/ts/src/util/pyGenerators.ts +144 -0
  468. package/ts/src/util/pythonText.ts +317 -0
  469. package/ts/src/util/semver.ts +45 -0
  470. package/ts/src/util/shellWord.ts +25 -0
  471. package/ts/src/util/slug.ts +64 -0
  472. package/ts/src/welcome/card.ts +339 -0
  473. package/ts/src/welcome/statusLine.ts +101 -0
  474. package/ts/src/welcome/updateCache.ts +129 -0
@@ -0,0 +1,163 @@
1
+ import { existsSync } from "node:fs";
2
+ import { basename, dirname, join } from "node:path";
3
+ import type { Database, WritableDatabase } from "#db/database.ts";
4
+ import {
5
+ deactivateMirrorState,
6
+ listActiveMirrorJourneys,
7
+ logMirrorResponse,
8
+ runMirrorLoad,
9
+ } from "#mirror/orchestration.ts";
10
+ import {
11
+ activateOperatingMode,
12
+ deactivateOperatingMode,
13
+ getActiveOperatingMode,
14
+ renderModeActivation,
15
+ renderModeStatus,
16
+ } from "#mode/operatingMode.ts";
17
+ import type { OnProviderCallOutcome } from "#observability/callOutcome.ts";
18
+ import { resolveFamilyProviders } from "#providers/familyProviders.ts";
19
+ import { MIRROR_QUERY_TRANSPORT } from "#providers/transport.ts";
20
+ import { newId, nowIso } from "#util/pyGenerators.ts";
21
+ import { hasOption, optionValue, stripOptionWithValue } from "./args.ts";
22
+
23
+ /**
24
+ * A type alias rather than an interface, for the same reason `RouteEnvironment`
25
+ * is one: aliases get an implicit index signature, which is what lets the named
26
+ * variables still be handed to `resolveFamilyProviders`, whose families name
27
+ * their variables as data.
28
+ */
29
+ export type MirrorRouteEnvironment = {
30
+ MIRROR_SESSION_ID?: string;
31
+ MEMORY_RECEPTION?: string;
32
+ MIRROR_TS_MIRROR_LLM_REPLAY?: string;
33
+ MIRROR_TS_MIRROR_EMBEDDING_REPLAY?: string;
34
+ };
35
+
36
+ export function isMirrorWrite(argv: readonly string[]): boolean {
37
+ return argv[0] === "mirror" && ["load", "deactivate", "log"].includes(argv[1] ?? "");
38
+ }
39
+
40
+ export function isModeWrite(argv: readonly string[]): boolean {
41
+ if (argv[0] !== "mode") return false;
42
+ const args = modePositionals(argv.slice(1));
43
+ return args[0] === "activate" || args[0] === "deactivate";
44
+ }
45
+
46
+ export function mirrorDeactivateMissingSession(argv: readonly string[]): boolean {
47
+ return argv[0] === "mirror" && argv[1] === "deactivate" && !optionValue(argv, "--session-id");
48
+ }
49
+
50
+ export function runMirrorRead(db: Database, args: readonly string[]): number {
51
+ if (args[0] !== "journeys") throw new Error(`Unsupported TS mirror read: ${args[0]}`);
52
+ process.stdout.write(listActiveMirrorJourneys(db));
53
+ return 0;
54
+ }
55
+
56
+ export function runModeRead(db: Database, args: readonly string[]): number {
57
+ const sessionId = optionValue(args, "--session-id");
58
+ process.stdout.write(renderModeStatus(getActiveOperatingMode(db, sessionId)));
59
+ return 0;
60
+ }
61
+
62
+ export async function runMirrorWriteRoute(
63
+ db: WritableDatabase,
64
+ dbPath: string,
65
+ argv: readonly string[],
66
+ env: MirrorRouteEnvironment = process.env,
67
+ options: { onReceptionOutcome?: OnProviderCallOutcome } = {},
68
+ ): Promise<number> {
69
+ const sub = argv[1];
70
+ const args = argv.slice(2);
71
+ const sessionId = optionValue(args, "--session-id");
72
+ if (sub === "deactivate") {
73
+ deactivateMirrorState(db, sessionId as string, nowIso());
74
+ console.error("Mirror Mode deactivated.");
75
+ return 0;
76
+ }
77
+ if (sub === "log") {
78
+ const summary = stripOptionWithValue(
79
+ stripOptionWithValue(stripOptionWithValue(args, "--session-id"), "--mirror-home"),
80
+ "--db-path",
81
+ )[0];
82
+ if (!summary) {
83
+ console.error("mirror log requires a summary");
84
+ return 2;
85
+ }
86
+ logMirrorResponse(db, {
87
+ summary,
88
+ sessionId,
89
+ environmentSessionId: env.MIRROR_SESSION_ID ?? null,
90
+ muted: existsSync(join(dirname(dbPath), "mute")),
91
+ newId,
92
+ nowIso,
93
+ });
94
+ console.error("Response recorded.");
95
+ return 0;
96
+ }
97
+ const query = optionValue(args, "--query");
98
+ const receptionEnabled = env.MEMORY_RECEPTION !== "0";
99
+ // Built from the same spec `routing.ts` decided with, and only for a query
100
+ // run: a deterministic `mirror load` must not construct a provider, let
101
+ // alone resolve a key (CV22.DS8.US3).
102
+ const family = query ? await resolveFamilyProviders(env, MIRROR_QUERY_TRANSPORT) : null;
103
+ const mirrorHome = dirname(dbPath);
104
+ const rendered = await runMirrorLoad(db, {
105
+ identity: basename(mirrorHome),
106
+ databasePath: dbPath,
107
+ mirrorHome,
108
+ user: basename(mirrorHome),
109
+ persona: optionValue(args, "--persona"),
110
+ journey: optionValue(args, "--journey"),
111
+ query,
112
+ org: hasOption(args, "--org"),
113
+ contextOnly: hasOption(args, "--context-only"),
114
+ sessionId,
115
+ environmentSessionId: env.MIRROR_SESSION_ID ?? null,
116
+ receptionEnabled,
117
+ onReceptionOutcome: options.onReceptionOutcome,
118
+ llmProvider: receptionEnabled ? family?.llm : undefined,
119
+ embeddingProvider: family?.embedding,
120
+ newId,
121
+ nowIso,
122
+ });
123
+ process.stdout.write(rendered.stdout);
124
+ process.stderr.write(rendered.stderr);
125
+ for (const diagnostic of rendered.extensionDiagnostics) {
126
+ process.stderr.write(`warning: extension context ${diagnostic.kind}; continuing.\n`);
127
+ }
128
+ return 0;
129
+ }
130
+
131
+ export function runModeWriteRoute(db: WritableDatabase, argv: readonly string[]): number {
132
+ const rawArgs = argv.slice(1);
133
+ const args = modePositionals(rawArgs);
134
+ const sub = args[0];
135
+ const sessionId = optionValue(rawArgs, "--session-id");
136
+ if (sub === "activate") {
137
+ const mode = args[1];
138
+ if (!mode) {
139
+ console.error("mode activate requires a mode");
140
+ return 2;
141
+ }
142
+ const state = activateOperatingMode(
143
+ db,
144
+ { mode, journey: optionValue(rawArgs, "--journey"), sessionId },
145
+ nowIso(),
146
+ );
147
+ process.stdout.write(renderModeActivation(state));
148
+ return 0;
149
+ }
150
+ deactivateOperatingMode(db, sessionId, nowIso());
151
+ process.stdout.write("Deactivated active mode\n");
152
+ return 0;
153
+ }
154
+
155
+ function modePositionals(args: readonly string[]): string[] {
156
+ return stripOptionWithValue(
157
+ stripOptionWithValue(
158
+ stripOptionWithValue(stripOptionWithValue(args, "--mirror-home"), "--db-path"),
159
+ "--session-id",
160
+ ),
161
+ "--journey",
162
+ );
163
+ }
@@ -0,0 +1,35 @@
1
+ // The refusals the front door answers by name when no route answered them.
2
+ //
3
+ // A route answers its own refusals. Two can reach the top of the front door
4
+ // unanswered, and each is still a refusal the user can act on, so each gets one
5
+ // line and an exit code, never a stack trace:
6
+ //
7
+ // * a half-configured replay fixture, raised wherever a provider is built. Until
8
+ // CV22.DS10.TS5 a plain family sent this case to the Python fallback instead,
9
+ // which had no replay transport and would have called the live provider;
10
+ // * a new journey slug or persona id outside the grammar, which the identity
11
+ // store refuses for any route that did not ask first (CR104). Today every route
12
+ // asks first, so this answers the next route that creates a key.
13
+ //
14
+ // Like every `Error:` line, the answer claims nothing about what changed before
15
+ // the refusal was raised.
16
+
17
+ import { InvalidIdentityKeyError } from "#identity/identityKey.ts";
18
+ import { ReplayFixtureIncompleteError } from "#providers/transport.ts";
19
+
20
+ export interface NamedRefusal {
21
+ /** The one line printed to stderr. */
22
+ readonly line: string;
23
+ readonly exitCode: number;
24
+ }
25
+
26
+ /** The line and exit code for a refusal the front door can name, or null for a crash. */
27
+ export function namedRefusal(error: unknown): NamedRefusal | null {
28
+ if (error instanceof ReplayFixtureIncompleteError) {
29
+ return { line: `Mirror TS front door: ${error.message}`, exitCode: 2 };
30
+ }
31
+ if (error instanceof InvalidIdentityKeyError) {
32
+ return { line: `Error: ${error.message}`, exitCode: 1 };
33
+ }
34
+ return null;
35
+ }
@@ -0,0 +1,24 @@
1
+ // Node runtime support check for the front door (CR025).
2
+ //
3
+ // Kept in its own module so it is importable without executing the CLI entry
4
+ // (`cli.ts` runs `main()` at module load). The front door calls this first.
5
+
6
+ /** Minimum Node major the TS core supports (node:sqlite + TS type-stripping). */
7
+ export const MINIMUM_NODE_MAJOR = 24;
8
+
9
+ /**
10
+ * Return an actionable message when `version` is below the supported Node
11
+ * major, or null when it is fine. Pure and side-effect-free. (On Node too old
12
+ * to strip TypeScript the front door never loads at all — that band is covered
13
+ * by the documented prerequisite, not this guard.)
14
+ */
15
+ export function nodeVersionError(version: string): string | null {
16
+ const major = Number(version.split(".")[0]);
17
+ if (Number.isFinite(major) && major < MINIMUM_NODE_MAJOR) {
18
+ return (
19
+ `Mirror TS front door requires Node >= ${MINIMUM_NODE_MAJOR} (running ${version}). ` +
20
+ "Upgrade Node (https://nodejs.org/) — node:sqlite and TypeScript execution need it."
21
+ );
22
+ }
23
+ return null;
24
+ }
@@ -0,0 +1,199 @@
1
+ // `consolidate` command rendering (CV22.DS7.US3) -- the port of
2
+ // `cli/consolidate_cmd.py`'s print statements. Every function here is a pure
3
+ // string builder over already-decided data (`cultivationRoute.ts` decides
4
+ // WHAT happened; this module decides HOW it is printed), matching Python's
5
+ // exact output including its blank-line quirks (`print(f"...\n")` followed by
6
+ // `print()`'s own newline -- see `tasks.ts`'s module doc for the same idiom).
7
+
8
+ import type { ConsolidationRow, CultivationMemory } from "#cultivation/consolidationStore.ts";
9
+ import type { ConsolidateScanResult } from "#cultivation/scan.ts";
10
+ import type { ConsolidateApplyOutcome, RejectOutcome } from "#frontDoor/cultivationRoute.ts";
11
+ import { PROGRAM } from "#util/program.ts";
12
+ import { renderAlreadyReviewed, renderProposalNotFound } from "./cultivationShared.ts";
13
+
14
+ const ACTION_ICON: Record<string, string> = {
15
+ merge: "🔀",
16
+ identity_update: "🧬",
17
+ shadow_candidate: "🌑",
18
+ };
19
+
20
+ const STATUS_ICON: Record<string, string> = {
21
+ pending: "⏳",
22
+ accepted: "✓",
23
+ rejected: "✗",
24
+ };
25
+
26
+ function sourceIdCount(sourceMemoryIds: string): number {
27
+ const parsed: unknown = sourceMemoryIds ? JSON.parse(sourceMemoryIds) : [];
28
+ return Array.isArray(parsed) ? parsed.length : 0;
29
+ }
30
+
31
+ /** Port of `consolidate list`'s `cmd_list`. */
32
+ export function renderConsolidateList(
33
+ items: readonly ConsolidationRow[],
34
+ status: string | null,
35
+ ): string {
36
+ if (items.length === 0) {
37
+ const label = status ? ` (${status})` : "";
38
+ return `No consolidations found${label}.\n`;
39
+ }
40
+ const lines: string[] = [];
41
+ for (const c of items) {
42
+ const icon = ACTION_ICON[c.action] ?? "•";
43
+ const st = STATUS_ICON[c.status] ?? "?";
44
+ const date = c.created_at.slice(0, 10);
45
+ const target = c.target_layer && c.target_key ? ` → ${c.target_layer}/${c.target_key}` : "";
46
+ lines.push(
47
+ `${st} [${c.id.slice(0, 8)}] ${date} ${icon} ${c.action}${target} (${sourceIdCount(c.source_memory_ids)} memories)`,
48
+ );
49
+ if (c.rationale) lines.push(` ${c.rationale}`);
50
+ }
51
+ return lines.map((line) => `${line}\n`).join("");
52
+ }
53
+
54
+ /** Port of `consolidate reject`'s success message; not-found/already-reviewed are shared. */
55
+ export function renderConsolidateRejected(consolidation: ConsolidationRow): string {
56
+ return `Proposal [${consolidation.id.slice(0, 8)}] rejected. Source memories unchanged.\n`;
57
+ }
58
+
59
+ /** Port of `consolidate reject`'s full output (not_found/already_reviewed shared, success family-specific). */
60
+ export function renderConsolidateReject(outcome: RejectOutcome): RenderedCommand {
61
+ if (outcome.kind === "not_found") {
62
+ return { text: renderProposalNotFound(outcome.proposalId), stderr: true, exitCode: 1 };
63
+ }
64
+ if (outcome.kind === "already_reviewed") {
65
+ return { text: renderAlreadyReviewed(outcome.consolidation), stderr: false, exitCode: 0 };
66
+ }
67
+ return { text: renderConsolidateRejected(outcome.consolidation), stderr: false, exitCode: 0 };
68
+ }
69
+
70
+ /**
71
+ * Port of `consolidate apply`'s full output, one branch per
72
+ * `ConsolidateApplyOutcome` kind. Returns the text plus which stream/exit
73
+ * code it belongs to -- `cultivationRoute.ts` decided the outcome, this
74
+ * decides the Python-exact bytes AND (unlike every prior front-door
75
+ * renderer) the stream/exit code, because `consolidate apply` fans out into
76
+ * more distinct terminal states than any single-outcome command ported so
77
+ * far.
78
+ */
79
+ export interface RenderedCommand {
80
+ text: string;
81
+ stderr: boolean;
82
+ exitCode: number;
83
+ }
84
+
85
+ export function renderConsolidateApply(outcome: ConsolidateApplyOutcome): RenderedCommand {
86
+ if (outcome.kind === "not_found") {
87
+ return { text: renderProposalNotFound(outcome.proposalId), stderr: true, exitCode: 1 };
88
+ }
89
+ if (outcome.kind === "already_reviewed") {
90
+ return { text: renderAlreadyReviewed(outcome.consolidation), stderr: false, exitCode: 0 };
91
+ }
92
+ if (outcome.kind === "identity_missing_target") {
93
+ return {
94
+ text: "Error: identity_update proposal has no target_layer/target_key.\n",
95
+ stderr: true,
96
+ exitCode: 1,
97
+ };
98
+ }
99
+ if (outcome.kind === "identity_refused") {
100
+ return { text: `Error: ${outcome.message}\n`, stderr: true, exitCode: 1 };
101
+ }
102
+ if (outcome.kind === "merge_source_not_found") {
103
+ return { text: "Error: source memory not found.\n", stderr: true, exitCode: 1 };
104
+ }
105
+
106
+ // outcome.kind === "applied"
107
+ const id8 = outcome.consolidation.id.slice(0, 8);
108
+ let text = "";
109
+ if (outcome.identityUpdate) {
110
+ const { targetLayer, targetKey, sourceMemoryIds } = outcome.identityUpdate;
111
+ text += `✓ Updated identity: ${targetLayer}/${targetKey}\n`;
112
+ text += ` Source memories (${sourceMemoryIds.length}) advanced to 'acknowledged'.\n`;
113
+ } else if (outcome.merge) {
114
+ const { mergedMemoryId, mergedTitle, sourceMemoryIds } = outcome.merge;
115
+ text += `✓ Created merged memory: [${mergedMemoryId.slice(0, 8)}] ${mergedTitle}\n`;
116
+ text += ` Source memories (${sourceMemoryIds.length}) marked as 'integrated'.\n`;
117
+ } else if (outcome.shadowCandidate) {
118
+ text += `✓ Shadow candidate accepted. ${outcome.shadowCandidate.sourceMemoryIds.length} memories advanced to 'candidate'.\n`;
119
+ text += " Run mm-shadow to surface these in the next shadow review pass.\n";
120
+ }
121
+ // Unrecognized action (Python's silent fallthrough): no branch text above,
122
+ // only the shared trailing line below.
123
+ text += `\nProposal [${id8}] marked as accepted.\n`;
124
+ return { text, stderr: false, exitCode: 0 };
125
+ }
126
+
127
+ const BAR = "─".repeat(60);
128
+
129
+ /** Port of `_print_proposal` in `consolidate_cmd.py` (the scan-time proposal card). */
130
+ function formatProposalBlock(
131
+ proposal: ConsolidationRow,
132
+ cluster: readonly CultivationMemory[],
133
+ index: number,
134
+ total: number,
135
+ ): string {
136
+ const icon = ACTION_ICON[proposal.action] ?? "•";
137
+ let out = `\n${BAR}\n`;
138
+ out += `Proposal ${index}/${total} [${proposal.id.slice(0, 8)}] ${icon} ${proposal.action.toUpperCase()}\n`;
139
+ out += `${BAR}\n`;
140
+ out += "\n**Source memories:**\n";
141
+ for (const memory of cluster) {
142
+ const date = memory.created_at.slice(0, 10);
143
+ out += ` • [${memory.id.slice(0, 8)}] ${memory.title} (${memory.memory_type}/${memory.layer}, ${date})\n`;
144
+ }
145
+ if (proposal.target_layer && proposal.target_key) {
146
+ out += `\n**Target:** \`${proposal.target_layer}/${proposal.target_key}\`\n`;
147
+ }
148
+ if (proposal.rationale) {
149
+ out += `\n**Rationale:** ${proposal.rationale}\n`;
150
+ }
151
+ out += `\n**Proposed content:**\n${proposal.proposal}\n`;
152
+ out += "\n";
153
+ return out;
154
+ }
155
+
156
+ /**
157
+ * Port of `consolidate scan`'s full output. `threshold` is rendered two
158
+ * different ways in Python -- plain `str(float)` in the opening "Scanning..."
159
+ * line, `:.2f` in the "Try lowering" hint -- both reproduced here. Plain
160
+ * `String(threshold)` matches Python's float-to-str for ordinary decimal
161
+ * thresholds (e.g. the 0.75 default); exotic float inputs are not
162
+ * byte-audited against CPython's `repr` here.
163
+ */
164
+ export function renderConsolidateScan(result: ConsolidateScanResult, threshold: number): string {
165
+ if (result.memoriesScanned === 0) {
166
+ return "No memories with embeddings found for the given filters.\n";
167
+ }
168
+ let out = `Scanning ${result.memoriesScanned} memories (threshold=${threshold})...\n`;
169
+ if (result.results.length === 0) {
170
+ out += "No clusters found above the similarity threshold.\n";
171
+ out += `Try lowering --threshold (current: ${threshold.toFixed(2)}).\n`;
172
+ return out;
173
+ }
174
+ out += `Found ${result.results.length} cluster(s). Generating proposals...\n\n`;
175
+
176
+ let createdCount = 0;
177
+ for (const { cluster, proposal } of result.results) {
178
+ if (proposal === null) {
179
+ out += ` ⚠ LLM returned no valid proposal for cluster of ${cluster.length} memories.\n`;
180
+ continue;
181
+ }
182
+ createdCount += 1;
183
+ out += formatProposalBlock(proposal, cluster, createdCount, result.results.length);
184
+ }
185
+
186
+ if (createdCount === 0) {
187
+ out += "No proposals were generated.\n";
188
+ return out;
189
+ }
190
+
191
+ out +=
192
+ `\n${createdCount} proposal(s) created with status='pending'.\n` +
193
+ "Review each proposal above, then:\n" +
194
+ ` Accept: ${PROGRAM} consolidate apply <proposal_id>\n` +
195
+ ` Edit: ${PROGRAM} consolidate apply <proposal_id> --content "revised text"\n` +
196
+ ` Reject: ${PROGRAM} consolidate reject <proposal_id>\n` +
197
+ ` List all: ${PROGRAM} consolidate list\n`;
198
+ return out;
199
+ }
@@ -0,0 +1,33 @@
1
+ // `conversations` (plain listing) rendering — the port of the summary print
2
+ // loop in memory.cli.conversations.main. The metadata-lifecycle/backfill flags
3
+ // on the same Python command are out of scope (see routing.ts) and never reach
4
+ // this renderer.
5
+
6
+ import {
7
+ type ListRecentConversationsFilters,
8
+ listRecentConversationSummaries,
9
+ } from "#conversation/listing.ts";
10
+ import type { Database } from "#db/database.ts";
11
+
12
+ /** Render the `conversations` plain listing. */
13
+ export function renderConversationsListing(
14
+ db: Database,
15
+ filters: ListRecentConversationsFilters,
16
+ ): string {
17
+ const summaries = listRecentConversationSummaries(db, filters);
18
+ if (summaries.length === 0) return "No conversations found.\n";
19
+ const prints: string[] = [];
20
+ for (const summary of summaries) {
21
+ const title = summary.title || "(untitled)";
22
+ const date = summary.started_at ? summary.started_at.slice(0, 10) : "?";
23
+ const journeyStr = summary.journey ? ` [${summary.journey}]` : "";
24
+ const personaStr = summary.persona ? ` \u25c7 ${summary.persona}` : "";
25
+ prints.push(
26
+ `**${date}** | \`${summary.id.slice(0, 8)}\`${journeyStr}${personaStr} ` +
27
+ `(${summary.message_count} msgs)`,
28
+ );
29
+ prints.push(` ${title}`);
30
+ prints.push("");
31
+ }
32
+ return prints.map((line) => `${line}\n`).join("");
33
+ }
@@ -0,0 +1,21 @@
1
+ // Shared rendering for the `consolidate`/`shadow` proposal-review commands
2
+ // (CV22.DS7.US3): the "not found" / "already reviewed" messages are
3
+ // byte-identical across both families' `reject`/`apply` commands in Python
4
+ // (each CLI module inlines the same two checks independently), so they are
5
+ // ported once here instead of copy-pasted into `consolidate.ts`/`shadow.ts`.
6
+
7
+ import type { ConsolidationRow } from "#cultivation/consolidationStore.ts";
8
+
9
+ /** Port of `f"Error: proposal '{proposal_id}' not found."` (stderr, exit 1). */
10
+ export function renderProposalNotFound(proposalId: string): string {
11
+ return `Error: proposal '${proposalId}' not found.\n`;
12
+ }
13
+
14
+ /**
15
+ * Port of `f"Proposal {consolidation.id[:8]} is already '{consolidation.status}'."`
16
+ * (stdout, exit 0). Deliberately NO brackets around the id excerpt here --
17
+ * unlike every success message in both families, which wraps it `[id8]`.
18
+ */
19
+ export function renderAlreadyReviewed(consolidation: ConsolidationRow): string {
20
+ return `Proposal ${consolidation.id.slice(0, 8)} is already '${consolidation.status}'.\n`;
21
+ }
@@ -0,0 +1,20 @@
1
+ // `descriptor list` rendering — the port of memory.cli.descriptor._cmd_list's
2
+ // print loop. `descriptor generate` is LLM-backed and stays on Python.
3
+
4
+ import type { DescriptorRow } from "#descriptor/descriptorRead.ts";
5
+
6
+ /**
7
+ * Render `descriptor list`. Each row is three Python `print()` calls
8
+ * (`[layer/key]`, the indented descriptor, and a bare blank-line `print()`) —
9
+ * modeled as three array elements, each getting exactly one trailing newline.
10
+ */
11
+ export function renderDescriptorList(rows: DescriptorRow[]): string {
12
+ if (rows.length === 0) return "No descriptors stored.\n";
13
+ const prints: string[] = [];
14
+ for (const row of rows) {
15
+ prints.push(`[${row.layer}/${row.key}]`);
16
+ prints.push(` ${row.descriptor}`);
17
+ prints.push("");
18
+ }
19
+ return prints.map((line) => `${line}\n`).join("");
20
+ }
@@ -0,0 +1,50 @@
1
+ // `detect-persona` rendering: read persona routing rows, score the query,
2
+ // format the matches. Extracted from cli.ts (CR002).
3
+
4
+ import type { Database } from "#db/database.ts";
5
+ import { requireString } from "#db/rowDecode.ts";
6
+ import { stripOptionWithValue } from "#frontDoor/args.ts";
7
+ import { detectPersona, type PersonaRoutingRow } from "#persona/detectPersona.ts";
8
+ import { identityRows } from "./identityRows.ts";
9
+
10
+ /** Parse each persona row's `routing_keywords` out of its identity metadata. */
11
+ export function routingRows(db: Database): PersonaRoutingRow[] {
12
+ return identityRows(db, "persona").map((row) => {
13
+ let keywords: string[] = [];
14
+ const metadata = row.metadata;
15
+ if (typeof metadata === "string" && metadata) {
16
+ try {
17
+ const parsed = JSON.parse(metadata) as Record<string, unknown>;
18
+ if (Array.isArray(parsed.routing_keywords)) {
19
+ keywords = parsed.routing_keywords.filter(
20
+ (item): item is string => typeof item === "string",
21
+ );
22
+ }
23
+ } catch {
24
+ keywords = [];
25
+ }
26
+ }
27
+ return { key: requireString(row, "key"), routing_keywords: keywords };
28
+ });
29
+ }
30
+
31
+ /**
32
+ * Render `detect-persona`. Strips known option flags (and their values) so only
33
+ * the query tokens remain — both `--mirror-home` and `--db-path`, the latter of
34
+ * which previously leaked into the query text.
35
+ */
36
+ export function renderDetectPersona(db: Database, args: readonly string[]): string {
37
+ const query = stripOptionWithValue(stripOptionWithValue(args, "--mirror-home"), "--db-path")
38
+ .join(" ")
39
+ .trim();
40
+ const matches = detectPersona(query, routingRows(db));
41
+ const lines = [`query: ${query}`];
42
+ if (matches.length === 0) {
43
+ lines.push(" (no persona match)");
44
+ } else {
45
+ for (const match of matches) {
46
+ lines.push(` ${match.key} score=${match.score} match=${match.matchType}`);
47
+ }
48
+ }
49
+ return `${lines.join("\n")}\n`;
50
+ }
@@ -0,0 +1,13 @@
1
+ // Memory-type icons shared by the memory renderer.
2
+
3
+ export const ICONS: Record<string, string> = {
4
+ decision: "⚖️",
5
+ insight: "💡",
6
+ idea: "🌱",
7
+ journal: "📓",
8
+ tension: "⚡",
9
+ learning: "📚",
10
+ pattern: "🔄",
11
+ commitment: "🤝",
12
+ reflection: "🪞",
13
+ };
@@ -0,0 +1,59 @@
1
+ // `identity list` / `identity get` rendering — the port of
2
+ // memory.cli.identity_cmd.cmd_list / cmd_get (read-only subcommands; `set` is
3
+ // US4/identityWrite.ts and `edit` stays on Python — it spawns `$EDITOR`).
4
+
5
+ import type { Database } from "#db/database.ts";
6
+ import {
7
+ getIdentityContent,
8
+ type IdentityListRow,
9
+ listAllIdentity,
10
+ listIdentityByLayer,
11
+ } from "#identity/identityRead.ts";
12
+
13
+ /** Raised when `identity get` finds no row for (layer, key) — mirrors Python's exit 1. */
14
+ export class IdentityEntryNotFoundError extends Error {
15
+ readonly layer: string;
16
+ readonly key: string;
17
+ constructor(layer: string, key: string) {
18
+ super(`No identity entry found for ${layer}/${key}`);
19
+ this.layer = layer;
20
+ this.key = key;
21
+ }
22
+ }
23
+
24
+ /** `entry.content[:70]`, newlines collapsed to spaces, "..." appended when truncated. */
25
+ function preview(content: string): string {
26
+ const clipped = content.slice(0, 70).replaceAll("\n", " ");
27
+ return content.length > 70 ? `${clipped}...` : clipped;
28
+ }
29
+
30
+ /**
31
+ * Render `identity list`: one "print call" per array element (each gets exactly
32
+ * one trailing newline appended, matching Python's `print()`), so a header's own
33
+ * embedded leading `\n` reproduces the blank line before every layer group.
34
+ */
35
+ export function renderIdentityList(rows: IdentityListRow[]): string {
36
+ if (rows.length === 0) return "No identity entries found.\n";
37
+ const prints: string[] = [];
38
+ let currentLayer: string | null = null;
39
+ for (const row of rows) {
40
+ if (row.layer !== currentLayer) {
41
+ currentLayer = row.layer;
42
+ prints.push(`\n[${row.layer}]`);
43
+ }
44
+ prints.push(` ${row.key.padEnd(22)} ${preview(row.content)}`);
45
+ }
46
+ return prints.map((line) => `${line}\n`).join("");
47
+ }
48
+
49
+ /** Read identity rows for `identity list`, optionally filtered by `--layer`. */
50
+ export function identityListRows(db: Database, layer: string | null): IdentityListRow[] {
51
+ return layer ? listIdentityByLayer(db, layer) : listAllIdentity(db);
52
+ }
53
+
54
+ /** Render `identity get <layer> <key>`: raw content + newline, or throw not-found. */
55
+ export function renderIdentityGet(db: Database, layer: string, key: string): string {
56
+ const content = getIdentityContent(db, layer, key);
57
+ if (content === null) throw new IdentityEntryNotFoundError(layer, key);
58
+ return `${content}\n`;
59
+ }
@@ -0,0 +1,17 @@
1
+ // Shared read of `identity`-layer rows for the read renderers.
2
+
3
+ import type { Database, Row } from "#db/database.ts";
4
+
5
+ /**
6
+ * All identity rows for a layer, ordered by key (as the Python oracle reads
7
+ * them). Includes migration 017's `parent_journey` projection so parity and
8
+ * drift diagnostics can observe it; mixed-engine semantic reads still resolve
9
+ * journey parentage from metadata (CR050).
10
+ */
11
+ export function identityRows(db: Database, layer: string): Row[] {
12
+ return db
13
+ .prepare(
14
+ "SELECT key, content, metadata, parent_journey FROM identity WHERE layer = ? ORDER BY key",
15
+ )
16
+ .all(layer);
17
+ }