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,522 @@
1
+ // CV22.DS7.US7 plateau 3 — the Explorer → Builder handoff artifacts, ported
2
+ // from `src/memory/services/explorer_handoff.py`.
3
+ //
4
+ // This module writes five Markdown documents into the USER'S OWN PROJECT, under
5
+ // `<project>/docs/project/explorations/<slug>/`. That makes it the security
6
+ // boundary of the story: `obfuscateSensitiveText` is the last thing standing
7
+ // between a conversation transcript and a file the user commits, and an
8
+ // under-matching port does not fail loudly — it writes a real key into git.
9
+ //
10
+ // So none of the five patterns uses a JavaScript character class shorthand.
11
+ // Every one of them diverges from Python in a way the corpus proves:
12
+ //
13
+ // * `\s` — Python includes U+001C-U+001F and excludes U+FEFF; JavaScript is
14
+ // the exact opposite. The path pattern terminates on it.
15
+ // * `\d` — Python matches EVERY Unicode decimal digit; JavaScript's `\d` is
16
+ // ASCII-only. A phone number in Devanagari digits is redacted by the oracle
17
+ // and, with `\d`, silently is not. Under-redaction is the dangerous
18
+ // direction, and this is the one place the port could reach it.
19
+ // * `\b` — Python's word boundary follows its Unicode-aware `\w`;
20
+ // JavaScript's is ASCII. Reproduced with explicit lookarounds.
21
+ // * `(?i)` — an inline flag JavaScript has no equivalent for; the whole
22
+ // pattern takes `i` instead, which is equivalent only because no other part
23
+ // of it is case-sensitive.
24
+ //
25
+ // Replacements are literal. `String.prototype.replace` interprets `$&`, `` $` ``,
26
+ // `$'`, `$1`, and `$$` in the REPLACEMENT string, so a redaction marker that
27
+ // happened to contain one would rewrite itself from the matched secret. The
28
+ // markers here are bracketed literals, and the one backreference is written
29
+ // deliberately as `$1`.
30
+
31
+ import { existsSync, mkdirSync, writeFileSync } from "node:fs";
32
+ import { join } from "node:path";
33
+
34
+ import type { ExplorerBuilderHandoff, ExplorerStory } from "#explorer/story.ts";
35
+ import { PYTHON_WHITESPACE_CLASS } from "#util/pythonText.ts";
36
+ import { kebabSlug } from "#util/slug.ts";
37
+
38
+ export interface HandoffSourceMessage {
39
+ readonly role: string;
40
+ readonly content: string;
41
+ }
42
+
43
+ export interface HandoffConversationSource {
44
+ readonly conversationId: string;
45
+ readonly title: string | null;
46
+ readonly role: string;
47
+ readonly messages: readonly HandoffSourceMessage[];
48
+ }
49
+
50
+ // --- redaction -------------------------------------------------------------
51
+
52
+ /** Python's `\w`: `str.isalnum()` or underscore, so Unicode-aware. */
53
+ const PY_WORD = "\\p{L}\\p{N}_";
54
+ /** Python's `\d`: every Unicode decimal digit (category Nd), not `[0-9]`. */
55
+ const PY_DIGIT = "\\p{Nd}";
56
+
57
+ /**
58
+ * Python's `\b` over the Unicode `\w` above.
59
+ *
60
+ * A negative lookbehind is NOT a word boundary, and the difference is visible:
61
+ * `\b` asserts a TRANSITION — word-then-nonword or nonword-then-word — while
62
+ * `(?<![\w])` only asserts "no word character behind". On `call +55 21 ...`
63
+ * the position before `+` satisfies the lookbehind but is not a boundary, so
64
+ * the oracle starts its match at the first digit and leaves the `+` in place.
65
+ * A lookbehind-only port swallows the `+`, over-redacting by one character.
66
+ */
67
+ const PY_BOUNDARY = `(?:(?<=[${PY_WORD}])(?![${PY_WORD}])|(?<![${PY_WORD}])(?=[${PY_WORD}]))`;
68
+
69
+ const REDACTIONS: readonly { pattern: RegExp; replacement: string }[] = [
70
+ // `/Users/[^\s)\]]+`
71
+ {
72
+ pattern: new RegExp(`/Users/[^${PYTHON_WHITESPACE_CLASS})\\]]+`, "gu"),
73
+ replacement: "[LOCAL_PATH]",
74
+ },
75
+ // `(?i)(api[_-]?key|token|secret|password)\s*=\s*[^\s]+`
76
+ {
77
+ pattern: new RegExp(
78
+ `(api[_-]?key|token|secret|password)[${PYTHON_WHITESPACE_CLASS}]*=` +
79
+ `[${PYTHON_WHITESPACE_CLASS}]*[^${PYTHON_WHITESPACE_CLASS}]+`,
80
+ "giu",
81
+ ),
82
+ replacement: "$1=[SECRET]",
83
+ },
84
+ // `sk-[A-Za-z0-9_-]{12,}`
85
+ { pattern: /sk-[A-Za-z0-9_-]{12,}/gu, replacement: "[SECRET]" },
86
+ // `[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}`
87
+ {
88
+ pattern: /[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}/gu,
89
+ replacement: "[PRIVATE_EMAIL]",
90
+ },
91
+ // `\b(?:\+?\d[\d .()-]{8,}\d)\b`
92
+ {
93
+ pattern: new RegExp(
94
+ `${PY_BOUNDARY}(?:\\+?${PY_DIGIT}[${PY_DIGIT} .()-]{8,}${PY_DIGIT})${PY_BOUNDARY}`,
95
+ "gu",
96
+ ),
97
+ replacement: "[PRIVATE_PHONE]",
98
+ },
99
+ ];
100
+
101
+ /**
102
+ * Port of `_obfuscate_sensitive_text`. Applied to transcript text before it
103
+ * reaches `full-conversation.md`.
104
+ *
105
+ * Ported at its current strength, gaps included — `/home/...` survives, and so
106
+ * does a bearer token without an `=`. Strengthening the patterns would break
107
+ * parity and is a product decision, not a port decision (US7 plan, Non-Goals).
108
+ */
109
+ export function obfuscateSensitiveText(text: string): string {
110
+ let redacted = text;
111
+ for (const { pattern, replacement } of REDACTIONS) {
112
+ redacted = redacted.replace(pattern, replacement);
113
+ }
114
+ return redacted;
115
+ }
116
+
117
+ // --- shared document fragments ---------------------------------------------
118
+
119
+ function renderAttractors(story: ExplorerStory): string {
120
+ if (story.attractors.length === 0) return "_No attractors recorded._";
121
+ const lines: string[] = [];
122
+ for (const attractor of story.attractors) {
123
+ lines.push(`- **${attractor.label}** (\`${attractor.status}\`)`);
124
+ if (attractor.description) lines.push(` - ${attractor.description}`);
125
+ }
126
+ return lines.join("\n");
127
+ }
128
+
129
+ function renderExperiment(story: ExplorerStory): string {
130
+ const proposal = story.experimentProposal;
131
+ if (!proposal) return "_No experiment proposal recorded._";
132
+ const lines = [`**${proposal.title}** (\`${proposal.status}\`)`];
133
+ if (proposal.description) lines.push("", proposal.description);
134
+ return lines.join("\n");
135
+ }
136
+
137
+ function renderSourceEvidence(sources: readonly HandoffConversationSource[]): string {
138
+ if (sources.length === 0) return "_No source conversations were attached to this handoff._";
139
+ return sources
140
+ .map(
141
+ (source) =>
142
+ `- \`${source.conversationId}\` — ${source.title || "Untitled conversation"} (${source.role})`,
143
+ )
144
+ .join("\n");
145
+ }
146
+
147
+ function renderDecidedProductDirection(story: ExplorerStory): string {
148
+ const parts: string[] = [];
149
+ if (story.currentExploratoryStory) parts.push(story.currentExploratoryStory);
150
+ if (story.narrativeFieldSummary) parts.push(story.narrativeFieldSummary);
151
+ return parts.join("\n\n") || "_No decided product direction recorded._";
152
+ }
153
+
154
+ function renderCompletenessChecklist(
155
+ story: ExplorerStory,
156
+ sources: readonly HandoffConversationSource[],
157
+ includesFullConversation: boolean,
158
+ ): string {
159
+ const checks: readonly [string, boolean][] = [
160
+ [
161
+ "continuous exploratory thickening",
162
+ Boolean(story.narrativeFieldSummary || story.lastStoryCard),
163
+ ],
164
+ ["source evidence list", sources.length > 0],
165
+ ["surfaces and story state", Boolean(story.currentExploratoryStory)],
166
+ ["phases or evolution narrative", Boolean(story.narrativeFieldSummary)],
167
+ ["examples or simulations", includesFullConversation],
168
+ ["product decisions", Boolean(story.currentExploratoryStory)],
169
+ ["user conversation flows", includesFullConversation],
170
+ ["transition rules", true],
171
+ ["risks", true],
172
+ ["boundaries", true],
173
+ ["open questions", true],
174
+ ["what Builder should preserve", true],
175
+ ["what Builder should not assume", true],
176
+ ];
177
+ const lines = checks.map(([label, present]) => `- [${present ? "x" : " "}] ${label}`);
178
+ const missing = checks.filter(([, present]) => !present).map(([label]) => label);
179
+ if (missing.length > 0) {
180
+ lines.push("", "Missing or weak evidence before Builder should treat this as complete:");
181
+ lines.push(...missing.map((label) => `- ${label}`));
182
+ }
183
+ return lines.join("\n");
184
+ }
185
+
186
+ // --- documents -------------------------------------------------------------
187
+
188
+ function renderIndexDoc(
189
+ story: ExplorerStory,
190
+ options: {
191
+ title: string;
192
+ summary: string | null;
193
+ editorialSynthesis: string | null;
194
+ sourceConversations: readonly HandoffConversationSource[];
195
+ includesFullConversation: boolean;
196
+ },
197
+ ): string {
198
+ const fullConversationLine = options.includesFullConversation
199
+ ? "- [Full Conversation Evidence](full-conversation.md): privacy-reviewed raw source material."
200
+ : "- Full conversation evidence was not included in this handoff.";
201
+
202
+ return `# Exploration Handoff: ${options.title}
203
+
204
+ ## Editorial Synthesis
205
+
206
+ ${options.editorialSynthesis || options.summary || story.narrativeFieldSummary || "_No editorial synthesis recorded._"}
207
+
208
+ ## Durable Story
209
+
210
+ - Story id: \`${story.id || "not recorded"}\`
211
+ - Journey: \`${story.journey}\`
212
+ - Status: \`${story.status}\`
213
+
214
+ ## Source Evidence
215
+
216
+ ${renderSourceEvidence(options.sourceConversations)}
217
+
218
+ ## What Was Decided
219
+
220
+ ${renderDecidedProductDirection(story)}
221
+
222
+ ## Transfer Documents
223
+
224
+ - [Exploratory Story](exploratory-story.md): discovery narrative and continuous thickening.
225
+ - [Handoff Info](handoff-info.md): risks, open questions, boundaries, and non-assumptions for Builder.
226
+ - [Product Design Proposal](product-design-proposal.md): user-facing product behavior, without implementation detail.
227
+ ${fullConversationLine}
228
+
229
+ ## Current Attractors
230
+
231
+ ${renderAttractors(story)}
232
+
233
+ ## Current Experiment Proposal
234
+
235
+ ${renderExperiment(story)}
236
+
237
+ ## Builder Reading Order
238
+
239
+ Read this \`index.md\` first, then \`exploratory-story.md\`, then \`handoff-info.md\`, then \`product-design-proposal.md\`. If \`full-conversation.md\` exists, read it as source evidence, not as a delivery plan. Treat the set as exploration output, not as a completed delivery plan.
240
+ `;
241
+ }
242
+
243
+ function renderExploratoryStoryDoc(
244
+ story: ExplorerStory,
245
+ options: { title: string; editorialSynthesis: string | null },
246
+ ): string {
247
+ return `# Exploratory Story: ${options.title}
248
+
249
+ ## Source
250
+
251
+ - Journey: \`${story.journey}\`
252
+ - Story id: \`${story.id || "not recorded"}\`
253
+ - Mode: Explorer Mode
254
+
255
+ ## Continuous Thickening Narrative
256
+
257
+ ${options.editorialSynthesis || story.narrativeFieldSummary || "_No continuous thickening narrative recorded._"}
258
+
259
+ ## Current Exploratory Story
260
+
261
+ ${story.currentExploratoryStory || "_No story text recorded._"}
262
+
263
+ ## Narrative Summary
264
+
265
+ ${story.narrativeFieldSummary || "_No narrative summary recorded._"}
266
+
267
+ ## Last Story Card
268
+
269
+ ${story.lastStoryCard || "_No last story card recorded._"}
270
+
271
+ ## Attractors
272
+
273
+ ${renderAttractors(story)}
274
+
275
+ ## Experiment Proposal
276
+
277
+ ${renderExperiment(story)}
278
+
279
+ ## What Changed Through Exploration
280
+
281
+ This section should preserve the evolution of the exploration: the original question, the meaningful pivots, the corrections that changed the story, and the current point of promotion. If this document was generated from a short runtime summary, Builder should ask the Navigator whether more conversation evidence must be folded in before roadmap work starts.
282
+ `;
283
+ }
284
+
285
+ function renderHandoffInfoDoc(
286
+ story: ExplorerStory,
287
+ options: {
288
+ title: string;
289
+ summary: string | null;
290
+ sourceConversations: readonly HandoffConversationSource[];
291
+ includesFullConversation: boolean;
292
+ },
293
+ ): string {
294
+ return `# Handoff Info: ${options.title}
295
+
296
+ ## Handoff Summary
297
+
298
+ ${options.summary || story.narrativeFieldSummary || "_No handoff summary recorded._"}
299
+
300
+ ## Handoff Completeness Checklist
301
+
302
+ ${renderCompletenessChecklist(story, options.sourceConversations, options.includesFullConversation)}
303
+
304
+ ## What Builder Should Preserve
305
+
306
+ - The exploration is not only a feature request. It carries discovery context and product judgment.
307
+ - The attractor and experiment proposal should guide Builder's first roadmap framing.
308
+ - The product design proposal should be translated into roadmap/story docs only after Builder reads the project.
309
+
310
+ ## Risks
311
+
312
+ - Builder may over-treat exploratory material as settled delivery scope.
313
+ - Builder may flatten open questions into implementation assumptions.
314
+ - Builder may focus on mechanism before preserving the user-facing product shape.
315
+
316
+ ## Open Questions
317
+
318
+ - Which parts of this exploration should become roadmap stories?
319
+ - What validation route proves the product behavior externally?
320
+ - What should remain outside the first delivery slice?
321
+
322
+ ## Boundaries
323
+
324
+ - This handoff is not a delivery plan.
325
+ - Builder must still read the project, create or update roadmap/story docs, and validate with the Navigator.
326
+ - Explorer preserved uncertainty; Builder should not erase it prematurely.
327
+
328
+ ## Non-Assumptions
329
+
330
+ - Do not assume implementation architecture from this handoff.
331
+ - Do not assume all open questions are in scope.
332
+ - Do not assume the experiment proposal has already been validated.
333
+
334
+ ## Attractors
335
+
336
+ ${renderAttractors(story)}
337
+
338
+ ## Experiment Status
339
+
340
+ ${renderExperiment(story)}
341
+
342
+ ## Promotion Boundary
343
+
344
+ Builder executes only after explicit confirmation from the Navigator.
345
+ `;
346
+ }
347
+
348
+ function renderProductDesignDoc(
349
+ story: ExplorerStory,
350
+ options: { title: string; summary: string | null },
351
+ ): string {
352
+ return `# Product Design Proposal: ${options.title}
353
+
354
+ ## Product Intent
355
+
356
+ ${options.summary || story.narrativeFieldSummary || "_No product intent summary recorded._"}
357
+
358
+ ## User-Facing Behavior
359
+
360
+ ${story.currentExploratoryStory || "_No current story recorded._"}
361
+
362
+ ## What The Product Should Feel Like
363
+
364
+ The product should preserve the exploratory shape discovered by Explorer Mode. It should show the user what is happening at the product level, not expose implementation mechanics first.
365
+
366
+ ## Interaction Flow
367
+
368
+ - User works in Explorer Mode while uncertainty is still alive.
369
+ - Explorer surfaces story changes visibly.
370
+ - Explorer names attractors and proposes small experiments.
371
+ - Explorer proposes Builder handoff only when the user asks or confirms readiness.
372
+ - Builder begins only after explicit confirmation.
373
+
374
+ ## Product-Level States
375
+
376
+ - Exploratory Story active.
377
+ - Attractor proposed or accepted.
378
+ - Experiment proposal proposed or accepted.
379
+ - Builder handoff proposed.
380
+
381
+ ## Acceptance Behavior
382
+
383
+ - The user can understand what is being proposed without reading implementation details.
384
+ - The proposal preserves uncertainty and open questions.
385
+ - The proposal gives Builder enough product shape to create roadmap or story plans.
386
+
387
+ ## Explicit Non-Goals
388
+
389
+ - This document does not define implementation architecture.
390
+ - This document does not create delivery tasks by itself.
391
+ - This document does not replace Builder planning.
392
+
393
+ ## Open Product Questions
394
+
395
+ - Which behavior is necessary for the first delivery slice?
396
+ - What should remain exploratory after Builder starts?
397
+ - What user validation will prove the product behavior works?
398
+ `;
399
+ }
400
+
401
+ function renderFullConversationDoc(sources: readonly HandoffConversationSource[]): string {
402
+ const sections: string[] = [
403
+ "# Full Conversation Evidence",
404
+ "",
405
+ "This document is source evidence for a Builder handoff. Sensitive personal or local details may have been obfuscated before writing.",
406
+ ];
407
+ for (const source of sources) {
408
+ sections.push(
409
+ "",
410
+ `## Conversation ${source.conversationId}`,
411
+ "",
412
+ `- Title: ${source.title || "Untitled conversation"}`,
413
+ `- Evidence role: ${source.role}`,
414
+ );
415
+ for (const message of source.messages) {
416
+ sections.push("", `### ${message.role}`, "", obfuscateSensitiveText(message.content));
417
+ }
418
+ }
419
+ return `${sections.join("\n")}\n`;
420
+ }
421
+
422
+ // --- artifact write --------------------------------------------------------
423
+
424
+ export interface WriteHandoffOptions {
425
+ readonly title: string;
426
+ readonly summary?: string | null;
427
+ readonly editorialSynthesis?: string | null;
428
+ readonly sourceConversations?: readonly HandoffConversationSource[];
429
+ readonly includeFullConversation?: boolean;
430
+ }
431
+
432
+ /**
433
+ * Port of `write_builder_handoff_artifacts`.
434
+ *
435
+ * The directory name is `kebab_slug` of the caller-supplied title, falling back
436
+ * to the story text, then the journey, then the literal `"exploration"`. The
437
+ * collision loop appends `-2`, `-3`, ... and is a pre-existing TOCTOU race
438
+ * (check-then-create); it is reproduced rather than fixed, because changing it
439
+ * would be a behavior change hiding inside a port.
440
+ *
441
+ * `kebabSlug` collapses every non-alphanumeric run to a hyphen, so a
442
+ * traversal-looking title cannot escape: `../../etc/passwd` slugs to
443
+ * `etc-passwd`. The corpus proves that rather than the comment asserting it.
444
+ */
445
+ export function writeBuilderHandoffArtifacts(
446
+ projectPath: string,
447
+ story: ExplorerStory,
448
+ options: WriteHandoffOptions,
449
+ ): ExplorerBuilderHandoff {
450
+ const summary = options.summary ?? null;
451
+ const sourceConversations = options.sourceConversations ?? [];
452
+ const includeFullConversation = options.includeFullConversation ?? false;
453
+ const fullConversationWritten = includeFullConversation && sourceConversations.length > 0;
454
+
455
+ const explorationsRoot = join(projectPath, "docs", "project", "explorations");
456
+ const slug =
457
+ kebabSlug(options.title || story.currentExploratoryStory || story.journey) || "exploration";
458
+
459
+ let base = join(explorationsRoot, slug);
460
+ let suffix = 2;
461
+ while (existsSync(base)) {
462
+ base = join(explorationsRoot, `${slug}-${suffix}`);
463
+ suffix += 1;
464
+ }
465
+ mkdirSync(base, { recursive: true });
466
+
467
+ const indexPath = join(base, "index.md");
468
+ const exploratoryStoryPath = join(base, "exploratory-story.md");
469
+ const handoffInfoPath = join(base, "handoff-info.md");
470
+ const productDesignPath = join(base, "product-design-proposal.md");
471
+ const fullConversationPath = join(base, "full-conversation.md");
472
+
473
+ writeFileSync(
474
+ indexPath,
475
+ renderIndexDoc(story, {
476
+ title: options.title,
477
+ summary,
478
+ editorialSynthesis: options.editorialSynthesis ?? null,
479
+ sourceConversations,
480
+ includesFullConversation: fullConversationWritten,
481
+ }),
482
+ "utf-8",
483
+ );
484
+ writeFileSync(
485
+ exploratoryStoryPath,
486
+ renderExploratoryStoryDoc(story, {
487
+ title: options.title,
488
+ editorialSynthesis: options.editorialSynthesis ?? null,
489
+ }),
490
+ "utf-8",
491
+ );
492
+ writeFileSync(
493
+ handoffInfoPath,
494
+ renderHandoffInfoDoc(story, {
495
+ title: options.title,
496
+ summary,
497
+ sourceConversations,
498
+ includesFullConversation: fullConversationWritten,
499
+ }),
500
+ "utf-8",
501
+ );
502
+ writeFileSync(
503
+ productDesignPath,
504
+ renderProductDesignDoc(story, { title: options.title, summary }),
505
+ "utf-8",
506
+ );
507
+ if (fullConversationWritten) {
508
+ writeFileSync(fullConversationPath, renderFullConversationDoc(sourceConversations), "utf-8");
509
+ }
510
+
511
+ return {
512
+ title: options.title,
513
+ summary,
514
+ readiness: "proposed",
515
+ artifactDir: base,
516
+ indexPath,
517
+ exploratoryStoryPath,
518
+ handoffInfoPath,
519
+ productDesignProposalPath: productDesignPath,
520
+ fullConversationPath: fullConversationWritten ? fullConversationPath : null,
521
+ };
522
+ }