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,151 @@
1
+ // `runtime backup` — create, VERIFY, and say how to recover (CV22.DS10.US2).
2
+ //
3
+ // Not the `backup` command DS7.TS1 ported. That one creates an archive and
4
+ // stops; this one is the updater's safety stage, and the promise it makes is
5
+ // "a backup exists and was verified before the tree moved". Three functions,
6
+ // characterized from `src/memory/cli/runtime.py:1052-1113`, with exactly one
7
+ // deliberate difference, documented on `verifyBackupArchive`.
8
+
9
+ import { existsSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs";
10
+ import { tmpdir } from "node:os";
11
+ import { join } from "node:path";
12
+ import { NotAReadableZipError, readZipEntry, readZipEntryNames } from "#backup/zipReader.ts";
13
+ import { quickCheckDatabaseFile } from "#db/database.ts";
14
+
15
+ /** The member every Mirror backup must hold. */
16
+ const ARCHIVE_MEMBER = "memory.db";
17
+ /** The only members it may hold: the database and SQLite's own sidecars. */
18
+ const ALLOWED_MEMBERS = new Set(["memory.db", "memory.db-wal", "memory.db-shm"]);
19
+
20
+ export interface BackupVerification {
21
+ backupPath: string;
22
+ valid: boolean;
23
+ entries: string[];
24
+ note: string | null;
25
+ }
26
+
27
+ function refuse(backupPath: string, entries: string[], note: string): BackupVerification {
28
+ return { backupPath, valid: false, entries, note };
29
+ }
30
+
31
+ /** Python's `Path(entry).is_absolute() or ".." in Path(entry).parts`. */
32
+ function isUnsafeEntry(name: string): boolean {
33
+ if (name.startsWith("/")) return true;
34
+ // Windows-shaped absolutes, which `PurePath` would not call absolute on
35
+ // POSIX but which no Mirror archive has any reason to carry.
36
+ if (/^[A-Za-z]:[\\/]/.test(name)) return true;
37
+ return name.split(/[\\/]/).includes("..");
38
+ }
39
+
40
+ /**
41
+ * Does this database open and pass SQLite's own consistency check?
42
+ *
43
+ * THE ONE DELIBERATE DEVIATION FROM THE ORACLE. Python verifies entry NAMES:
44
+ * an archive holding a `memory.db` of 4 KB of zeros is reported `valid`, which
45
+ * is measured and recorded in the story's plateau-0 baseline. The updater
46
+ * relies on this verdict immediately before it moves the tree, so a backup
47
+ * that has never been opened is a belief rather than a backup.
48
+ *
49
+ * Runs on an extracted copy in a temp dir removed afterwards: the archive is
50
+ * untrusted input and this never touches the live database. The check itself
51
+ * goes through `#db/database.ts`, the one module allowed to hold the driver --
52
+ * an external `sqlite3` binary is not guaranteed on a user's machine, and a
53
+ * second SQLite access path is not something a verifier should introduce.
54
+ */
55
+ function integrityNote(data: Buffer): string | null {
56
+ const dir = mkdtempSync(join(tmpdir(), "mirror-verify-"));
57
+ const path = join(dir, ARCHIVE_MEMBER);
58
+ try {
59
+ writeFileSync(path, data, { mode: 0o600 });
60
+ const { ok, verdict } = quickCheckDatabaseFile(path);
61
+ return ok ? null : `${ARCHIVE_MEMBER} failed integrity check: ${verdict}`;
62
+ } finally {
63
+ rmSync(dir, { recursive: true, force: true });
64
+ }
65
+ }
66
+
67
+ /** Port of `verify_backup_archive`, in the oracle's order of refusal. */
68
+ export function verifyBackupArchive(backupPath: string): BackupVerification {
69
+ if (!existsSync(backupPath)) return refuse(backupPath, [], "backup file not found");
70
+
71
+ let entries: string[];
72
+ let buffer: Buffer;
73
+ try {
74
+ buffer = readFileSync(backupPath);
75
+ entries = readZipEntryNames(buffer);
76
+ } catch (error) {
77
+ if (error instanceof NotAReadableZipError) {
78
+ return refuse(backupPath, [], "backup file is not a readable zip");
79
+ }
80
+ throw error;
81
+ }
82
+
83
+ // Before anything is read OUT of the archive: an entry that would escape the
84
+ // Mirror home is refused, with the names still reported so the operator can
85
+ // see what it held.
86
+ for (const entry of entries) {
87
+ if (isUnsafeEntry(entry)) return refuse(backupPath, entries, `unsafe archive entry: ${entry}`);
88
+ }
89
+ if (!entries.includes(ARCHIVE_MEMBER)) {
90
+ return refuse(backupPath, entries, `${ARCHIVE_MEMBER} missing from backup`);
91
+ }
92
+ const unexpected = entries.filter((entry) => !ALLOWED_MEMBERS.has(entry));
93
+ if (unexpected.length > 0) {
94
+ return refuse(backupPath, entries, `unexpected archive entries: ${unexpected.join(", ")}`);
95
+ }
96
+
97
+ let member: Buffer | null;
98
+ try {
99
+ member = readZipEntry(buffer, ARCHIVE_MEMBER);
100
+ } catch (error) {
101
+ if (error instanceof NotAReadableZipError) {
102
+ return refuse(backupPath, entries, `${ARCHIVE_MEMBER} failed to open: ${error.message}`);
103
+ }
104
+ throw error;
105
+ }
106
+ if (member === null) {
107
+ return refuse(backupPath, entries, `${ARCHIVE_MEMBER} missing from backup`);
108
+ }
109
+ const note = integrityNote(member);
110
+ if (note !== null) return refuse(backupPath, entries, note);
111
+
112
+ return { backupPath, valid: true, entries, note: null };
113
+ }
114
+
115
+ /** Port of `render_backup_verification`. */
116
+ export function renderBackupVerification(verification: BackupVerification): string {
117
+ const lines = ["Mirror runtime backup verification", ""];
118
+ lines.push(`Backup: ${verification.backupPath}`);
119
+ if (verification.entries.length > 0) lines.push(`Entries: ${verification.entries.join(", ")}`);
120
+ if (verification.note) lines.push(`Verification note: ${verification.note}`);
121
+ lines.push(`Verification result: ${verification.valid ? "valid" : "invalid"}`);
122
+ return `${lines.join("\n")}\n`;
123
+ }
124
+
125
+ export interface BackupCreatedInput {
126
+ backupPath: string;
127
+ mirrorHome: string;
128
+ verification: BackupVerification;
129
+ }
130
+
131
+ /** Port of `render_runtime_backup_created`, recovery route included. */
132
+ export function renderRuntimeBackupCreated(input: BackupCreatedInput): string {
133
+ const lines = ["Mirror runtime backup", ""];
134
+ lines.push(`Mirror home: ${input.mirrorHome}`);
135
+ lines.push(`Backup: ${input.backupPath}`);
136
+ lines.push(`Verification result: ${input.verification.valid ? "valid" : "invalid"}`);
137
+ if (input.verification.entries.length > 0) {
138
+ lines.push(`Entries: ${input.verification.entries.join(", ")}`);
139
+ }
140
+ if (input.verification.note) lines.push(`Verification note: ${input.verification.note}`);
141
+ lines.push("");
142
+ lines.push("Manual recovery route:");
143
+ lines.push(" 1. Stop active runtime sessions that could write to the database.");
144
+ lines.push(" 2. Move current memory.db, memory.db-wal, and memory.db-shm aside.");
145
+ lines.push(" 3. Extract memory.db and sidecars from this backup into the Mirror home.");
146
+ lines.push(" 4. Run runtime status against the Mirror home.");
147
+ lines.push(" 5. Do not retry update execution until status is ready.");
148
+ lines.push("");
149
+ lines.push("Recovery is manual in this version; no files were restored.");
150
+ return `${lines.join("\n")}\n`;
151
+ }
@@ -0,0 +1,146 @@
1
+ // `runtime channel [stable|main]` — where an install keeps the channel it
2
+ // follows, read and written in one place. (CV22.DS10.US3 plateau 4, D9; pays
3
+ // US2's D-027, which left the package channel file with no writer.)
4
+ //
5
+ // The channel is scoped the way the INSTALL is scoped. A clone keeps its
6
+ // `.mirror-update-channel` marker at the checkout root, per checkout and
7
+ // gitignored, so switching it leaves the tree clean for the update gate. A
8
+ // package is one `npm install -g` per OS user while a Mirror home is not:
9
+ // reading the channel from `<mirror-home>/…` would let two homes over one
10
+ // global install re-tag each other on alternating updates, so it lives beside
11
+ // the user's own config file instead, `${XDG_CONFIG_HOME:-~/.config}/mirror/`.
12
+
13
+ import { chmodSync, existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
14
+ import { homedir } from "node:os";
15
+ import { dirname, join } from "node:path";
16
+ import { type ConfigEnv, configFilePath } from "#runtime/config.ts";
17
+ import { inspectUpdateChannel, type MarkerValue } from "#runtime/git.ts";
18
+ import { describeInstallKind, type InstallKind } from "#runtime/installKind.ts";
19
+ import { DEFAULT_CHANNEL, KNOWN_CHANNELS } from "#runtime/updatePipeline.ts";
20
+
21
+ const CLONE_MARKER = ".mirror-update-channel";
22
+
23
+ /** The file this install's channel lives in, or null when it has nowhere to. */
24
+ export function channelPathFor(
25
+ install: InstallKind,
26
+ env: ConfigEnv = process.env,
27
+ home: string = homedir(),
28
+ ): string | null {
29
+ if (install.kind === "clone") return join(install.repository, CLONE_MARKER);
30
+ if (install.kind === "package") return join(dirname(configFilePath(env, home)), "update-channel");
31
+ return null;
32
+ }
33
+
34
+ /** `stable`, `main`, or null for anything else, after trimming and lowercasing. */
35
+ export function normalizeChannel(raw: string): string | null {
36
+ const value = raw.trim().toLowerCase();
37
+ return KNOWN_CHANNELS.has(value) ? value : null;
38
+ }
39
+
40
+ function readPackageChannel(path: string): MarkerValue {
41
+ if (!existsSync(path)) return { value: DEFAULT_CHANNEL, source: null, note: null };
42
+ let raw: string;
43
+ try {
44
+ raw = readFileSync(path, "utf8");
45
+ } catch (error) {
46
+ const message = error instanceof Error ? error.message : String(error);
47
+ return { value: DEFAULT_CHANNEL, source: path, note: `unreadable: ${message}` };
48
+ }
49
+ const value = normalizeChannel(raw);
50
+ if (value !== null) return { value, source: path, note: null };
51
+ return {
52
+ value: DEFAULT_CHANNEL,
53
+ source: path,
54
+ note: `unknown channel '${raw.trim().toLowerCase()}', defaulting to ${DEFAULT_CHANNEL}`,
55
+ };
56
+ }
57
+
58
+ /**
59
+ * The channel this install follows: the override when one was given, else
60
+ * the install's own file, else the default. One answer for `update`,
61
+ * `--check`, `status`, `version`, and `channel`.
62
+ */
63
+ export function channelFor(
64
+ install: InstallKind,
65
+ env: ConfigEnv = process.env,
66
+ home: string = homedir(),
67
+ override: string | null = null,
68
+ ): MarkerValue {
69
+ if (install.kind === "clone") return inspectUpdateChannel(install.repository, override);
70
+ if (override) {
71
+ const value = normalizeChannel(override);
72
+ if (value !== null) return { value, source: null, note: "command override" };
73
+ return {
74
+ value: DEFAULT_CHANNEL,
75
+ source: null,
76
+ note: `unknown channel '${override.trim().toLowerCase()}', defaulting to ${DEFAULT_CHANNEL}`,
77
+ };
78
+ }
79
+ if (install.kind === "package") {
80
+ return readPackageChannel(join(dirname(configFilePath(env, home)), "update-channel"));
81
+ }
82
+ return { value: DEFAULT_CHANNEL, source: null, note: "install kind unknown" };
83
+ }
84
+
85
+ export type ChannelWrite =
86
+ | { ok: true; path: string; value: string; previous: string }
87
+ | { ok: false; reason: string; exitCode: 1 | 2 };
88
+
89
+ /**
90
+ * Persist the channel for this install. The value is checked against the
91
+ * vocabulary before anything is created; an unknown install is refused
92
+ * rather than guessed. For a package the config directory may not exist yet
93
+ * (`init` has not run), and it is created `0700`, as `init` creates it: the
94
+ * env file that later lands there holds the API key.
95
+ */
96
+ export function setChannel(
97
+ install: InstallKind,
98
+ raw: string,
99
+ env: ConfigEnv = process.env,
100
+ home: string = homedir(),
101
+ ): ChannelWrite {
102
+ const value = normalizeChannel(raw);
103
+ if (value === null) {
104
+ return {
105
+ ok: false,
106
+ reason: `unknown channel ${JSON.stringify(raw)}: choose stable or main`,
107
+ exitCode: 2,
108
+ };
109
+ }
110
+ const path = channelPathFor(install, env, home);
111
+ if (path === null) {
112
+ return {
113
+ ok: false,
114
+ reason: `install kind unknown (${describeInstallKind(install)}): the channel has no home`,
115
+ exitCode: 1,
116
+ };
117
+ }
118
+ const previous = channelFor(install, env, home).value;
119
+ if (install.kind === "package") {
120
+ const dir = dirname(path);
121
+ mkdirSync(dir, { recursive: true, mode: 0o700 });
122
+ chmodSync(dir, 0o700);
123
+ }
124
+ writeFileSync(path, `${value}\n`, "utf8");
125
+ return { ok: true, path, value, previous };
126
+ }
127
+
128
+ export interface ChannelView {
129
+ install: InstallKind;
130
+ channel: MarkerValue;
131
+ /** Present after a write: what the channel was before it. */
132
+ written: { previous: string } | null;
133
+ }
134
+
135
+ export function renderChannel(view: ChannelView): string {
136
+ const lines = ["Mirror runtime update channel", ""];
137
+ lines.push(`Install: ${describeInstallKind(view.install)}`);
138
+ const was =
139
+ view.written !== null && view.written.previous !== view.channel.value
140
+ ? ` (was ${view.written.previous})`
141
+ : "";
142
+ lines.push(`Update channel: ${view.channel.value}${was}`);
143
+ if (view.channel.note) lines.push(`Update channel note: ${view.channel.note}`);
144
+ lines.push(`Source: ${view.channel.source ?? "default"}`);
145
+ return `${lines.join("\n")}\n`;
146
+ }
@@ -0,0 +1,168 @@
1
+ // Where Mirror Mind's configuration comes from, and the warning filter every
2
+ // entry point needs. CV22.DS10.US3 plateau 1, decision D3.
3
+ //
4
+ // Until this story every invocation carried `--env-file=.env` and
5
+ // `NODE_OPTIONS=--no-warnings`: 135 skill lines, the Pi extension, the twelve
6
+ // hook wrappers, and the MCP launcher each repeated both. Only a checkout can
7
+ // satisfy the first (an installed package ships no `.env`), and a `bin` shim
8
+ // sets neither. So the program does both itself, once, here, and the three
9
+ // entries (`frontDoor/cli.ts`, `hooks/main.ts`, `mcp/main.ts`) call this
10
+ // module before they touch the database.
11
+ //
12
+ // Two files, one order, nothing overridden:
13
+ //
14
+ // 1. the real environment -- whatever the shell or the runtime set;
15
+ // 2. `<tree root>/.env` -- the clone's, located from the ENTRY FILE rather
16
+ // than from the cwd, so a skill run from /tmp reads the same file the
17
+ // checkout's own commands do. The tarball excludes `.env*`, so for an
18
+ // installed package this step finds nothing and says so;
19
+ // 3. `${XDG_CONFIG_HOME:-~/.config}/mirror/env` -- the OS user's, in the
20
+ // directory CV22.DS10.US2 already chose for the package update channel.
21
+ //
22
+ // `process.loadEnvFile` has `--env-file` semantics: it fills what is unset and
23
+ // never overrides, which is what makes the order above a precedence rather
24
+ // than a last-writer-wins. A second mirror on the same machine runs with
25
+ // `MIRROR_USER=<other>` in the shell, exactly as before.
26
+
27
+ import { chmodSync, existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
28
+ import { homedir } from "node:os";
29
+ import { dirname, join } from "node:path";
30
+ import { parseEnv } from "node:util";
31
+ import { findPackageIdentity } from "./packageIdentity.ts";
32
+
33
+ export type ConfigSource =
34
+ | { kind: "environment" }
35
+ | { kind: "tree_env"; path: string }
36
+ | { kind: "user_config"; path: string };
37
+
38
+ export interface ConfigurationReport {
39
+ /** Every source that contributed, in precedence order. */
40
+ readonly sources: readonly ConfigSource[];
41
+ }
42
+
43
+ /** The env-var slice this module reads. `process.env` satisfies it. */
44
+ export interface ConfigEnv {
45
+ XDG_CONFIG_HOME?: string | undefined;
46
+ [key: string]: string | undefined;
47
+ }
48
+
49
+ export interface ConfigIo {
50
+ env?: ConfigEnv;
51
+ home?: string;
52
+ }
53
+
54
+ /** `${XDG_CONFIG_HOME:-~/.config}/mirror/env`, the OS user's Mirror configuration. */
55
+ export function configFilePath(env: ConfigEnv = process.env, home: string = homedir()): string {
56
+ const xdg = env.XDG_CONFIG_HOME;
57
+ const configHome = xdg && xdg.length > 0 ? xdg : join(home, ".config");
58
+ return join(configHome, "mirror", "env");
59
+ }
60
+
61
+ export interface LoadConfigurationOptions extends ConfigIo {
62
+ /**
63
+ * The running entry file (`import.meta.filename` of `cli.ts`, `hooks/main.ts`,
64
+ * or `mcp/main.ts`). The tree's `.env` is located by walking up from here.
65
+ */
66
+ entryPath: string;
67
+ /** Present only so a test can prove the cwd is NOT consulted. */
68
+ cwd?: string;
69
+ /** Injection for tests; defaults to `process.loadEnvFile` semantics. */
70
+ loadEnvFile?: (path: string, env: ConfigEnv) => void;
71
+ }
72
+
73
+ /**
74
+ * Fill `env` from the two files, in order, without overriding anything already
75
+ * set. Returns which sources contributed so `runtime status` and `diagnose`
76
+ * can print them.
77
+ */
78
+ export function loadConfiguration(options: LoadConfigurationOptions): ConfigurationReport {
79
+ const env = options.env ?? process.env;
80
+ const home = options.home ?? homedir();
81
+ const load = options.loadEnvFile ?? loadEnvFileWithoutOverride;
82
+ const sources: ConfigSource[] = [{ kind: "environment" }];
83
+
84
+ const tree = findPackageIdentity(dirname(options.entryPath));
85
+ if (tree !== null) {
86
+ const treeEnv = join(tree.root, ".env");
87
+ if (existsSync(treeEnv)) {
88
+ load(treeEnv, env);
89
+ sources.push({ kind: "tree_env", path: treeEnv });
90
+ }
91
+ }
92
+
93
+ const userConfig = configFilePath(env, home);
94
+ if (existsSync(userConfig)) {
95
+ load(userConfig, env);
96
+ sources.push({ kind: "user_config", path: userConfig });
97
+ }
98
+
99
+ return { sources };
100
+ }
101
+
102
+ /**
103
+ * `process.loadEnvFile` fills `process.env` and nothing else. When the target
104
+ * is `process.env` that is exactly the call; for any other object (a test's),
105
+ * the file is parsed with Node's own parser and applied with the same
106
+ * no-override rule, so the two paths cannot disagree about precedence.
107
+ */
108
+ function loadEnvFileWithoutOverride(path: string, env: ConfigEnv): void {
109
+ if (env === process.env) {
110
+ process.loadEnvFile(path);
111
+ return;
112
+ }
113
+ for (const [key, value] of Object.entries(parseEnv(readFileSync(path, "utf8")))) {
114
+ if (!(key in env)) env[key] = value;
115
+ }
116
+ }
117
+
118
+ /**
119
+ * Write one `KEY=value` line into the user's config file, creating the file
120
+ * `0600` inside a `0700` directory, replacing the key's line if it is already
121
+ * there and appending otherwise. Everything else in the file is preserved
122
+ * byte for byte: this is where `OPENROUTER_API_KEY` lives, added by hand, and
123
+ * `init` must never rewrite it.
124
+ */
125
+ export function writeConfigValue(key: string, value: string, io: ConfigIo = {}): string {
126
+ if (!/^[A-Z_][A-Z0-9_]*$/.test(key)) {
127
+ throw new Error(`config key must be an environment variable name, got ${JSON.stringify(key)}`);
128
+ }
129
+ if (/[\r\n]/.test(value)) {
130
+ throw new Error("config value must not contain a newline");
131
+ }
132
+ const path = configFilePath(io.env ?? process.env, io.home ?? homedir());
133
+ const dir = dirname(path);
134
+ mkdirSync(dir, { recursive: true, mode: 0o700 });
135
+ chmodSync(dir, 0o700);
136
+
137
+ const line = `${key}=${value}`;
138
+ let body = "";
139
+ if (existsSync(path)) {
140
+ const lines = readFileSync(path, "utf8").split("\n");
141
+ if (lines.at(-1) === "") lines.pop();
142
+ const index = lines.findIndex((l) => l.replace(/^\s*(export\s+)?/, "").startsWith(`${key}=`));
143
+ if (index >= 0) lines[index] = line;
144
+ else lines.push(line);
145
+ body = `${lines.join("\n")}\n`;
146
+ } else {
147
+ body = `${line}\n`;
148
+ }
149
+ writeFileSync(path, body, { encoding: "utf8", mode: 0o600 });
150
+ chmodSync(path, 0o600);
151
+ return path;
152
+ }
153
+
154
+ /**
155
+ * Keep `node:sqlite`'s ExperimentalWarning off stderr, in-process.
156
+ *
157
+ * Every skill used to pass `NODE_OPTIONS=--no-warnings`; a `bin` shim, an MCP
158
+ * client, and a hook wrapper set no such thing, and for the MCP server stderr
159
+ * is the client's log. Other warnings still print, one line each, because a
160
+ * deprecation or a memory-leak warning is information.
161
+ */
162
+ export function silenceExperimentalWarnings(): void {
163
+ process.removeAllListeners("warning");
164
+ process.on("warning", (warning) => {
165
+ if (warning.name === "ExperimentalWarning") return;
166
+ process.stderr.write(`${warning.name}: ${warning.message}\n`);
167
+ });
168
+ }