mirror-mind 0.0.1 → 0.33.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (474) hide show
  1. package/.claude/hooks/log-assistant-reply.sh +85 -0
  2. package/.claude/hooks/log-session-end.sh +85 -0
  3. package/.claude/hooks/log-user-prompt.sh +85 -0
  4. package/.claude/hooks/mirror-inject.sh +85 -0
  5. package/.claude/hooks/session-start.sh +85 -0
  6. package/.gemini/hooks/log-assistant.sh +85 -0
  7. package/.gemini/hooks/log-user.sh +85 -0
  8. package/.gemini/hooks/session-end.sh +85 -0
  9. package/.gemini/hooks/session-start.sh +85 -0
  10. package/.pi/extensions/mirror-logger.ts +646 -0
  11. package/.pi/skills/mm-backup/SKILL.md +21 -0
  12. package/.pi/skills/mm-build/SKILL.md +1206 -0
  13. package/.pi/skills/mm-consolidate/SKILL.md +112 -0
  14. package/.pi/skills/mm-consult/SKILL.md +70 -0
  15. package/.pi/skills/mm-conversations/SKILL.md +16 -0
  16. package/.pi/skills/mm-discard/SKILL.md +39 -0
  17. package/.pi/skills/mm-explore/SKILL.md +255 -0
  18. package/.pi/skills/mm-help/SKILL.md +74 -0
  19. package/.pi/skills/mm-identity/SKILL.md +96 -0
  20. package/.pi/skills/mm-journal/SKILL.md +16 -0
  21. package/.pi/skills/mm-journey/SKILL.md +23 -0
  22. package/.pi/skills/mm-journeys/SKILL.md +15 -0
  23. package/.pi/skills/mm-memories/SKILL.md +17 -0
  24. package/.pi/skills/mm-mirror/SKILL.md +98 -0
  25. package/.pi/skills/mm-mute/SKILL.md +18 -0
  26. package/.pi/skills/mm-new/SKILL.md +18 -0
  27. package/.pi/skills/mm-recall/SKILL.md +17 -0
  28. package/.pi/skills/mm-release-notes/SKILL.md +52 -0
  29. package/.pi/skills/mm-seed/SKILL.md +19 -0
  30. package/.pi/skills/mm-shadow/SKILL.md +117 -0
  31. package/.pi/skills/mm-soul/SKILL.md +502 -0
  32. package/.pi/skills/mm-tasks/SKILL.md +24 -0
  33. package/.pi/skills/mm-update/SKILL.md +32 -0
  34. package/.pi/skills/mm-week/SKILL.md +28 -0
  35. package/.pi/skills/mm-welcome/SKILL.md +24 -0
  36. package/AGENTS.md +188 -0
  37. package/README.md +126 -9
  38. package/bin/loader.js +67 -0
  39. package/bin/mirror-hook.js +7 -0
  40. package/bin/mirror.js +7 -0
  41. package/docs/releases/index.md +100 -0
  42. package/docs/releases/pending-cutoffs.md +510 -0
  43. package/docs/releases/v0.10.0.md +123 -0
  44. package/docs/releases/v0.10.1.md +66 -0
  45. package/docs/releases/v0.10.10.md +51 -0
  46. package/docs/releases/v0.10.11.md +39 -0
  47. package/docs/releases/v0.10.12.md +57 -0
  48. package/docs/releases/v0.10.13.md +35 -0
  49. package/docs/releases/v0.10.14.md +42 -0
  50. package/docs/releases/v0.10.15.md +53 -0
  51. package/docs/releases/v0.10.16.md +35 -0
  52. package/docs/releases/v0.10.17.md +43 -0
  53. package/docs/releases/v0.10.2.md +64 -0
  54. package/docs/releases/v0.10.3.md +78 -0
  55. package/docs/releases/v0.10.4.md +42 -0
  56. package/docs/releases/v0.10.5.md +57 -0
  57. package/docs/releases/v0.10.6.md +47 -0
  58. package/docs/releases/v0.10.7.md +53 -0
  59. package/docs/releases/v0.10.8.md +49 -0
  60. package/docs/releases/v0.10.9.md +37 -0
  61. package/docs/releases/v0.11.0.md +86 -0
  62. package/docs/releases/v0.12.0.md +67 -0
  63. package/docs/releases/v0.13.0.md +64 -0
  64. package/docs/releases/v0.14.0.md +68 -0
  65. package/docs/releases/v0.15.0.md +48 -0
  66. package/docs/releases/v0.16.0.md +50 -0
  67. package/docs/releases/v0.16.1.md +55 -0
  68. package/docs/releases/v0.16.2.md +36 -0
  69. package/docs/releases/v0.17.0.md +50 -0
  70. package/docs/releases/v0.17.1.md +58 -0
  71. package/docs/releases/v0.17.2.md +42 -0
  72. package/docs/releases/v0.17.3.md +58 -0
  73. package/docs/releases/v0.18.0.md +84 -0
  74. package/docs/releases/v0.18.1.md +51 -0
  75. package/docs/releases/v0.18.2.md +69 -0
  76. package/docs/releases/v0.19.0.md +85 -0
  77. package/docs/releases/v0.19.1.md +46 -0
  78. package/docs/releases/v0.20.0.md +85 -0
  79. package/docs/releases/v0.21.0.md +61 -0
  80. package/docs/releases/v0.21.1.md +47 -0
  81. package/docs/releases/v0.22.0.md +77 -0
  82. package/docs/releases/v0.22.1.md +42 -0
  83. package/docs/releases/v0.23.0.md +105 -0
  84. package/docs/releases/v0.24.0.md +125 -0
  85. package/docs/releases/v0.25.0.md +107 -0
  86. package/docs/releases/v0.26.0.md +139 -0
  87. package/docs/releases/v0.26.1.md +65 -0
  88. package/docs/releases/v0.26.2.md +43 -0
  89. package/docs/releases/v0.27.0.md +63 -0
  90. package/docs/releases/v0.28.0.md +59 -0
  91. package/docs/releases/v0.29.0.md +61 -0
  92. package/docs/releases/v0.29.1.md +59 -0
  93. package/docs/releases/v0.29.2.md +59 -0
  94. package/docs/releases/v0.29.3.md +93 -0
  95. package/docs/releases/v0.30.0.md +75 -0
  96. package/docs/releases/v0.30.1.md +93 -0
  97. package/docs/releases/v0.30.2.md +66 -0
  98. package/docs/releases/v0.31.0.md +122 -0
  99. package/docs/releases/v0.31.1.md +142 -0
  100. package/docs/releases/v0.31.10.md +118 -0
  101. package/docs/releases/v0.31.11.md +88 -0
  102. package/docs/releases/v0.31.12.md +105 -0
  103. package/docs/releases/v0.31.13.md +99 -0
  104. package/docs/releases/v0.31.14.md +97 -0
  105. package/docs/releases/v0.31.2.md +115 -0
  106. package/docs/releases/v0.31.3.md +142 -0
  107. package/docs/releases/v0.31.4.md +117 -0
  108. package/docs/releases/v0.31.5.md +155 -0
  109. package/docs/releases/v0.31.6.md +132 -0
  110. package/docs/releases/v0.31.7.md +132 -0
  111. package/docs/releases/v0.31.8.md +120 -0
  112. package/docs/releases/v0.31.9.md +120 -0
  113. package/docs/releases/v0.32.0.md +261 -0
  114. package/docs/releases/v0.32.1.md +59 -0
  115. package/docs/releases/v0.33.0.md +164 -0
  116. package/docs/releases/v0.8.0.md +118 -0
  117. package/docs/releases/v0.9.0.md +98 -0
  118. package/docs/releases/v0.9.1.md +83 -0
  119. package/package.json +102 -6
  120. package/plugins/mirror-mind/.claude-plugin/plugin.json +17 -0
  121. package/plugins/mirror-mind/hooks/hooks.json +62 -0
  122. package/plugins/mirror-mind/hooks/log-assistant-reply.sh +98 -0
  123. package/plugins/mirror-mind/hooks/log-session-end.sh +98 -0
  124. package/plugins/mirror-mind/hooks/log-user-prompt.sh +98 -0
  125. package/plugins/mirror-mind/hooks/mirror-inject.sh +98 -0
  126. package/plugins/mirror-mind/hooks/session-start.sh +98 -0
  127. package/plugins/mirror-mind/skills/mm-backup/SKILL.md +27 -0
  128. package/plugins/mirror-mind/skills/mm-build/SKILL.md +1206 -0
  129. package/plugins/mirror-mind/skills/mm-consolidate/SKILL.md +112 -0
  130. package/plugins/mirror-mind/skills/mm-consult/SKILL.md +83 -0
  131. package/plugins/mirror-mind/skills/mm-conversations/SKILL.md +17 -0
  132. package/plugins/mirror-mind/skills/mm-discard/SKILL.md +40 -0
  133. package/plugins/mirror-mind/skills/mm-explore/SKILL.md +241 -0
  134. package/plugins/mirror-mind/skills/mm-help/SKILL.md +104 -0
  135. package/plugins/mirror-mind/skills/mm-identity/SKILL.md +96 -0
  136. package/plugins/mirror-mind/skills/mm-journal/SKILL.md +21 -0
  137. package/plugins/mirror-mind/skills/mm-journey/SKILL.md +37 -0
  138. package/plugins/mirror-mind/skills/mm-journeys/SKILL.md +15 -0
  139. package/plugins/mirror-mind/skills/mm-memories/SKILL.md +20 -0
  140. package/plugins/mirror-mind/skills/mm-mirror/SKILL.md +67 -0
  141. package/plugins/mirror-mind/skills/mm-mute/SKILL.md +18 -0
  142. package/plugins/mirror-mind/skills/mm-new/SKILL.md +18 -0
  143. package/plugins/mirror-mind/skills/mm-recall/SKILL.md +17 -0
  144. package/plugins/mirror-mind/skills/mm-release-notes/SKILL.md +31 -0
  145. package/plugins/mirror-mind/skills/mm-seed/SKILL.md +15 -0
  146. package/plugins/mirror-mind/skills/mm-shadow/SKILL.md +117 -0
  147. package/plugins/mirror-mind/skills/mm-soul/SKILL.md +488 -0
  148. package/plugins/mirror-mind/skills/mm-tasks/SKILL.md +36 -0
  149. package/plugins/mirror-mind/skills/mm-update/SKILL.md +30 -0
  150. package/plugins/mirror-mind/skills/mm-week/SKILL.md +46 -0
  151. package/plugins/mirror-mind/skills/mm-welcome/SKILL.md +22 -0
  152. package/scripts/codex-hooks/session-end.sh +85 -0
  153. package/scripts/codex-hooks/session-start.sh +85 -0
  154. package/scripts/codex-mirror.sh +48 -0
  155. package/templates/identity/README.md +119 -0
  156. package/templates/identity/ego/behavior.yaml +52 -0
  157. package/templates/identity/ego/constraints.yaml +15 -0
  158. package/templates/identity/ego/identity.yaml +43 -0
  159. package/templates/identity/journeys/personal-growth.yaml +39 -0
  160. package/templates/identity/organization/identity.yaml +24 -0
  161. package/templates/identity/organization/principles.yaml +17 -0
  162. package/templates/identity/personas/coach.yaml +94 -0
  163. package/templates/identity/personas/designer.yaml +93 -0
  164. package/templates/identity/personas/doctor.yaml +96 -0
  165. package/templates/identity/personas/engineer.yaml +77 -0
  166. package/templates/identity/personas/financial.yaml +97 -0
  167. package/templates/identity/personas/prompt-engineer.yaml +122 -0
  168. package/templates/identity/personas/researcher.yaml +88 -0
  169. package/templates/identity/personas/strategist.yaml +91 -0
  170. package/templates/identity/personas/teacher.yaml +95 -0
  171. package/templates/identity/personas/therapist.yaml +91 -0
  172. package/templates/identity/personas/thinker.yaml +74 -0
  173. package/templates/identity/personas/writer.yaml +76 -0
  174. package/templates/identity/self/config.yaml +16 -0
  175. package/templates/identity/self/soul.yaml +105 -0
  176. package/templates/identity/user/identity.yaml +30 -0
  177. package/ts/src/backup/staging.ts +62 -0
  178. package/ts/src/backup/zipBackup.ts +267 -0
  179. package/ts/src/backup/zipReader.ts +109 -0
  180. package/ts/src/backup/zipWriter.ts +148 -0
  181. package/ts/src/builder/activeCheckpoint.ts +134 -0
  182. package/ts/src/builder/aggregateStatus.ts +34 -0
  183. package/ts/src/builder/approve.ts +149 -0
  184. package/ts/src/builder/argv.ts +292 -0
  185. package/ts/src/builder/ariadMethod.ts +536 -0
  186. package/ts/src/builder/artifacts/artifactSurfaces.ts +106 -0
  187. package/ts/src/builder/artifacts/artifactWriter.ts +113 -0
  188. package/ts/src/builder/artifacts/closureArtifacts.ts +199 -0
  189. package/ts/src/builder/artifacts/deliveryStoryArtifacts.ts +316 -0
  190. package/ts/src/builder/artifacts/planArtifacts.ts +242 -0
  191. package/ts/src/builder/artifacts/scaffoldSections.ts +273 -0
  192. package/ts/src/builder/artifacts/scaffoldState.ts +227 -0
  193. package/ts/src/builder/artifacts/storyFiles.ts +61 -0
  194. package/ts/src/builder/artifacts/storyIndex.ts +166 -0
  195. package/ts/src/builder/cadence.ts +59 -0
  196. package/ts/src/builder/card.ts +192 -0
  197. package/ts/src/builder/checkpointRefused.ts +66 -0
  198. package/ts/src/builder/closure.ts +964 -0
  199. package/ts/src/builder/commands.ts +2053 -0
  200. package/ts/src/builder/cursorPosition.ts +176 -0
  201. package/ts/src/builder/cursorTransitions.ts +77 -0
  202. package/ts/src/builder/deliveryCursor.ts +577 -0
  203. package/ts/src/builder/deliveryStoryClosure.ts +521 -0
  204. package/ts/src/builder/deliveryStoryPlan.ts +536 -0
  205. package/ts/src/builder/deliveryStoryReady.ts +160 -0
  206. package/ts/src/builder/deliveryStoryRoadmapClosure.ts +151 -0
  207. package/ts/src/builder/expand.ts +336 -0
  208. package/ts/src/builder/flowUnit.ts +339 -0
  209. package/ts/src/builder/homeSurface.ts +122 -0
  210. package/ts/src/builder/implementationGuard.ts +125 -0
  211. package/ts/src/builder/index.ts +11 -0
  212. package/ts/src/builder/lifecycleRefusal.ts +92 -0
  213. package/ts/src/builder/lifecycleRibbon.ts +159 -0
  214. package/ts/src/builder/load.ts +313 -0
  215. package/ts/src/builder/methodAdoption.ts +113 -0
  216. package/ts/src/builder/methodDefinition.ts +429 -0
  217. package/ts/src/builder/methodInspection.ts +371 -0
  218. package/ts/src/builder/plan.ts +408 -0
  219. package/ts/src/builder/planPreauthorization.ts +265 -0
  220. package/ts/src/builder/prepare.ts +225 -0
  221. package/ts/src/builder/projectPaths.ts +64 -0
  222. package/ts/src/builder/pull.ts +356 -0
  223. package/ts/src/builder/pullCandidates.ts +463 -0
  224. package/ts/src/builder/pullCandidatesRender.ts +238 -0
  225. package/ts/src/builder/refinementField.ts +93 -0
  226. package/ts/src/builder/releaseIntent.ts +173 -0
  227. package/ts/src/builder/resumeState.ts +84 -0
  228. package/ts/src/builder/resumeSurface.ts +149 -0
  229. package/ts/src/builder/roadmapGrammar.ts +300 -0
  230. package/ts/src/builder/roadmapScan.ts +97 -0
  231. package/ts/src/builder/roadmapScope.ts +210 -0
  232. package/ts/src/builder/scopePhrases.ts +146 -0
  233. package/ts/src/builder/storyPaths.ts +196 -0
  234. package/ts/src/builder/storyPlanPreauthorization.ts +287 -0
  235. package/ts/src/builder/surfaceProtocol.ts +42 -0
  236. package/ts/src/builder/templateGeneration.ts +148 -0
  237. package/ts/src/builder/transition.ts +178 -0
  238. package/ts/src/consult/args.ts +99 -0
  239. package/ts/src/consult/core.ts +122 -0
  240. package/ts/src/consult/modelCatalog.ts +55 -0
  241. package/ts/src/consult/render.ts +47 -0
  242. package/ts/src/conversation/append.ts +414 -0
  243. package/ts/src/conversation/backfill.ts +387 -0
  244. package/ts/src/conversation/closeTail.ts +467 -0
  245. package/ts/src/conversation/extraction.ts +333 -0
  246. package/ts/src/conversation/extractionDriver.ts +130 -0
  247. package/ts/src/conversation/extractionRun.ts +70 -0
  248. package/ts/src/conversation/hookPayload.ts +51 -0
  249. package/ts/src/conversation/journeyInference.ts +165 -0
  250. package/ts/src/conversation/journeyRepair.ts +114 -0
  251. package/ts/src/conversation/lifecycleFaces.ts +197 -0
  252. package/ts/src/conversation/lifecycleWrites.ts +313 -0
  253. package/ts/src/conversation/listing.ts +77 -0
  254. package/ts/src/conversation/logger.ts +851 -0
  255. package/ts/src/conversation/loggerCli.ts +321 -0
  256. package/ts/src/conversation/loggerRuntime.ts +259 -0
  257. package/ts/src/conversation/metadataLifecycle.ts +443 -0
  258. package/ts/src/conversation/recall.ts +66 -0
  259. package/ts/src/conversation/sessionComposites.ts +258 -0
  260. package/ts/src/conversation/sessionImport.ts +90 -0
  261. package/ts/src/conversation/transcriptBackfill.ts +141 -0
  262. package/ts/src/cultivation/applyActions.ts +241 -0
  263. package/ts/src/cultivation/cluster.ts +86 -0
  264. package/ts/src/cultivation/consolidationStore.ts +281 -0
  265. package/ts/src/cultivation/promptContext.ts +62 -0
  266. package/ts/src/cultivation/propose.ts +305 -0
  267. package/ts/src/cultivation/scan.ts +155 -0
  268. package/ts/src/db/backupGate.ts +47 -0
  269. package/ts/src/db/bootstrap.ts +108 -0
  270. package/ts/src/db/bootstrapLock.ts +223 -0
  271. package/ts/src/db/copyGuard.ts +60 -0
  272. package/ts/src/db/database.ts +397 -0
  273. package/ts/src/db/decode.ts +66 -0
  274. package/ts/src/db/ftsIntegrity.ts +41 -0
  275. package/ts/src/db/migrateOnOpen.ts +214 -0
  276. package/ts/src/db/migrations.ts +576 -0
  277. package/ts/src/db/readyOnOpen.ts +35 -0
  278. package/ts/src/db/rowDecode.ts +42 -0
  279. package/ts/src/db/schema.ts +432 -0
  280. package/ts/src/db/schemaInventory.ts +225 -0
  281. package/ts/src/db/schemaInventorySnapshot.ts +2107 -0
  282. package/ts/src/db/schemaState.ts +91 -0
  283. package/ts/src/db/schemaTsDivergence.ts +114 -0
  284. package/ts/src/descriptor/descriptorRead.ts +49 -0
  285. package/ts/src/docs/docsLint.ts +265 -0
  286. package/ts/src/explorer/handoff.ts +522 -0
  287. package/ts/src/explorer/render.ts +335 -0
  288. package/ts/src/explorer/story.ts +769 -0
  289. package/ts/src/explorer/transition.ts +72 -0
  290. package/ts/src/extensions/bindings.ts +170 -0
  291. package/ts/src/extensions/catalog.ts +249 -0
  292. package/ts/src/extensions/catalogCommands.ts +513 -0
  293. package/ts/src/extensions/catalogWrites.ts +652 -0
  294. package/ts/src/extensions/contextRuntime.ts +325 -0
  295. package/ts/src/extensions/dispatch.ts +374 -0
  296. package/ts/src/extensions/errors.ts +24 -0
  297. package/ts/src/extensions/manifest.ts +356 -0
  298. package/ts/src/extensions/migrations.ts +350 -0
  299. package/ts/src/extraction/conversation.ts +304 -0
  300. package/ts/src/extraction/conversationMetadata.ts +206 -0
  301. package/ts/src/extraction/fencing.ts +106 -0
  302. package/ts/src/extraction/json.ts +17 -0
  303. package/ts/src/extraction/prompts.ts +470 -0
  304. package/ts/src/frontDoor/args.ts +30 -0
  305. package/ts/src/frontDoor/argvShape.ts +106 -0
  306. package/ts/src/frontDoor/buildLoadRuntime.ts +139 -0
  307. package/ts/src/frontDoor/buildRoute.ts +398 -0
  308. package/ts/src/frontDoor/cli.ts +1872 -0
  309. package/ts/src/frontDoor/cloneRoleGuard.ts +98 -0
  310. package/ts/src/frontDoor/consultRoute.ts +50 -0
  311. package/ts/src/frontDoor/contentTailRoute.ts +239 -0
  312. package/ts/src/frontDoor/conversationLoggerRoute.ts +83 -0
  313. package/ts/src/frontDoor/cultivationRoute.ts +210 -0
  314. package/ts/src/frontDoor/dbPath.ts +131 -0
  315. package/ts/src/frontDoor/dbSafetyToolsRoute.ts +240 -0
  316. package/ts/src/frontDoor/exploreRoute.ts +572 -0
  317. package/ts/src/frontDoor/extensionCatalogRoute.ts +252 -0
  318. package/ts/src/frontDoor/frontDoorLog.ts +57 -0
  319. package/ts/src/frontDoor/identityWrite.ts +36 -0
  320. package/ts/src/frontDoor/journeyWriteRoute.ts +26 -0
  321. package/ts/src/frontDoor/lifecycleWriteRoute.ts +110 -0
  322. package/ts/src/frontDoor/liveBackup.ts +100 -0
  323. package/ts/src/frontDoor/mirrorModeRoute.ts +163 -0
  324. package/ts/src/frontDoor/namedRefusal.ts +35 -0
  325. package/ts/src/frontDoor/nodeSupport.ts +24 -0
  326. package/ts/src/frontDoor/render/consolidate.ts +199 -0
  327. package/ts/src/frontDoor/render/conversations.ts +33 -0
  328. package/ts/src/frontDoor/render/cultivationShared.ts +21 -0
  329. package/ts/src/frontDoor/render/descriptor.ts +20 -0
  330. package/ts/src/frontDoor/render/detectPersona.ts +50 -0
  331. package/ts/src/frontDoor/render/icons.ts +13 -0
  332. package/ts/src/frontDoor/render/identity.ts +59 -0
  333. package/ts/src/frontDoor/render/identityRows.ts +17 -0
  334. package/ts/src/frontDoor/render/inspectPersona.ts +53 -0
  335. package/ts/src/frontDoor/render/journeyStatus.ts +78 -0
  336. package/ts/src/frontDoor/render/journeys.ts +79 -0
  337. package/ts/src/frontDoor/render/list.ts +41 -0
  338. package/ts/src/frontDoor/render/memories.ts +67 -0
  339. package/ts/src/frontDoor/render/recall.ts +49 -0
  340. package/ts/src/frontDoor/render/shadow.ts +155 -0
  341. package/ts/src/frontDoor/render/tasks.ts +99 -0
  342. package/ts/src/frontDoor/render/tasksImportSync.ts +63 -0
  343. package/ts/src/frontDoor/render/week.ts +113 -0
  344. package/ts/src/frontDoor/routing.ts +1001 -0
  345. package/ts/src/frontDoor/runtimeRoute.ts +623 -0
  346. package/ts/src/frontDoor/searchRoute.ts +166 -0
  347. package/ts/src/frontDoor/seedPaths.ts +98 -0
  348. package/ts/src/frontDoor/sessionStartWelcomeRoute.ts +166 -0
  349. package/ts/src/frontDoor/soulRoute.ts +433 -0
  350. package/ts/src/frontDoor/stdinSource.ts +37 -0
  351. package/ts/src/frontDoor/tasksImportSyncRoute.ts +110 -0
  352. package/ts/src/frontDoor/tasksWriteRoute.ts +101 -0
  353. package/ts/src/frontDoor/usage.ts +176 -0
  354. package/ts/src/guards/claudePlugin.ts +298 -0
  355. package/ts/src/guards/packContents.ts +306 -0
  356. package/ts/src/guards/piSourcedSkills.ts +70 -0
  357. package/ts/src/guards/retiredRouteShadows.ts +57 -0
  358. package/ts/src/guards/retiredSurfaces.ts +564 -0
  359. package/ts/src/hooks/claude.ts +214 -0
  360. package/ts/src/hooks/codex.ts +45 -0
  361. package/ts/src/hooks/gemini.ts +126 -0
  362. package/ts/src/hooks/main.ts +109 -0
  363. package/ts/src/hooks/mirrorState.ts +71 -0
  364. package/ts/src/hooks/payload.ts +73 -0
  365. package/ts/src/hooks/runtime.ts +179 -0
  366. package/ts/src/hooks/runtimeDetection.ts +34 -0
  367. package/ts/src/identity/applyConsolidationIdentityUpdate.ts +67 -0
  368. package/ts/src/identity/descriptorGenerate.ts +162 -0
  369. package/ts/src/identity/identityEdit.ts +143 -0
  370. package/ts/src/identity/identityKey.ts +77 -0
  371. package/ts/src/identity/identityRead.ts +69 -0
  372. package/ts/src/identity/identityStore.ts +80 -0
  373. package/ts/src/identity/journeyListing.ts +53 -0
  374. package/ts/src/identity/personaInspect.ts +44 -0
  375. package/ts/src/identity/personaListing.ts +37 -0
  376. package/ts/src/identity/setIdentity.ts +64 -0
  377. package/ts/src/index.ts +231 -0
  378. package/ts/src/init/init.ts +111 -0
  379. package/ts/src/journey/journeyOptions.ts +131 -0
  380. package/ts/src/journey/journeyRemoval.ts +158 -0
  381. package/ts/src/journey/journeyStatus.ts +149 -0
  382. package/ts/src/journey/journeySyncFile.ts +87 -0
  383. package/ts/src/journey/journeyWrite.ts +203 -0
  384. package/ts/src/journey/parentJourney.ts +41 -0
  385. package/ts/src/journey/validateParentJourney.ts +55 -0
  386. package/ts/src/mcp/boundary.ts +151 -0
  387. package/ts/src/mcp/guards.ts +244 -0
  388. package/ts/src/mcp/main.ts +121 -0
  389. package/ts/src/mcp/payload.ts +141 -0
  390. package/ts/src/mcp/protocol.ts +141 -0
  391. package/ts/src/mcp/readModels.ts +241 -0
  392. package/ts/src/mcp/registry.ts +201 -0
  393. package/ts/src/mcp/serve.ts +120 -0
  394. package/ts/src/mcp/tools/deterministic.ts +137 -0
  395. package/ts/src/mcp/tools/providerCrossing.ts +187 -0
  396. package/ts/src/mcp/wire.ts +8 -0
  397. package/ts/src/memory/journal.ts +180 -0
  398. package/ts/src/memory/listing.ts +107 -0
  399. package/ts/src/memory/memoryWrite.ts +88 -0
  400. package/ts/src/memory/reinforcement.ts +35 -0
  401. package/ts/src/mirror/context.ts +161 -0
  402. package/ts/src/mirror/defaultResolution.ts +199 -0
  403. package/ts/src/mirror/mirrorForm.ts +43 -0
  404. package/ts/src/mirror/orchestration.ts +364 -0
  405. package/ts/src/mirror/reception.ts +194 -0
  406. package/ts/src/mirror/render.ts +75 -0
  407. package/ts/src/mirror/runtimeSession.ts +205 -0
  408. package/ts/src/mode/operatingMode.ts +148 -0
  409. package/ts/src/observability/callOutcome.ts +115 -0
  410. package/ts/src/observability/inspectLedger.ts +329 -0
  411. package/ts/src/observability/ledgerHooks.ts +126 -0
  412. package/ts/src/observability/llmCalls.ts +154 -0
  413. package/ts/src/persona/detectPersona.ts +81 -0
  414. package/ts/src/planning/promptAssembly.ts +111 -0
  415. package/ts/src/planning/weekPending.ts +81 -0
  416. package/ts/src/planning/weekPlan.ts +183 -0
  417. package/ts/src/planning/weekSave.ts +117 -0
  418. package/ts/src/providers/config.ts +189 -0
  419. package/ts/src/providers/cost.ts +64 -0
  420. package/ts/src/providers/credits.ts +204 -0
  421. package/ts/src/providers/embedding.ts +321 -0
  422. package/ts/src/providers/familyProviders.ts +147 -0
  423. package/ts/src/providers/llm.ts +334 -0
  424. package/ts/src/providers/openrouter.ts +244 -0
  425. package/ts/src/providers/redaction.ts +110 -0
  426. package/ts/src/providers/replay.ts +15 -0
  427. package/ts/src/providers/transport.ts +410 -0
  428. package/ts/src/repair/encodingRepair.ts +253 -0
  429. package/ts/src/runtime/backup.ts +151 -0
  430. package/ts/src/runtime/channel.ts +146 -0
  431. package/ts/src/runtime/config.ts +168 -0
  432. package/ts/src/runtime/diagnose.ts +686 -0
  433. package/ts/src/runtime/git.ts +510 -0
  434. package/ts/src/runtime/installKind.ts +220 -0
  435. package/ts/src/runtime/migrate.ts +154 -0
  436. package/ts/src/runtime/packageIdentity.ts +102 -0
  437. package/ts/src/runtime/releaseNotes.ts +244 -0
  438. package/ts/src/runtime/status.ts +557 -0
  439. package/ts/src/runtime/strategies/clone.ts +200 -0
  440. package/ts/src/runtime/strategies/package.ts +328 -0
  441. package/ts/src/runtime/treeRoot.ts +30 -0
  442. package/ts/src/runtime/update.ts +445 -0
  443. package/ts/src/runtime/updateGate.ts +40 -0
  444. package/ts/src/runtime/updatePipeline.ts +159 -0
  445. package/ts/src/runtime/version.ts +36 -0
  446. package/ts/src/runtime/wiring.ts +130 -0
  447. package/ts/src/search/memorySearch.ts +338 -0
  448. package/ts/src/search/ranker.ts +154 -0
  449. package/ts/src/seed/seed.ts +256 -0
  450. package/ts/src/seed/seedYaml.ts +133 -0
  451. package/ts/src/soul/apply.ts +218 -0
  452. package/ts/src/soul/harvest.ts +267 -0
  453. package/ts/src/soul/prompts/soul_beauty_voice.md +103 -0
  454. package/ts/src/soul/prompts/soul_self_voice.md +219 -0
  455. package/ts/src/soul/prompts/soul_wisdom_voice.md +121 -0
  456. package/ts/src/soul/prompts.ts +85 -0
  457. package/ts/src/soul/render.ts +358 -0
  458. package/ts/src/soul/state.ts +212 -0
  459. package/ts/src/soul/transition.ts +37 -0
  460. package/ts/src/tasks/journeyPathParse.ts +113 -0
  461. package/ts/src/tasks/taskImportSync.ts +142 -0
  462. package/ts/src/tasks/taskStore.ts +312 -0
  463. package/ts/src/tasks/weekView.ts +120 -0
  464. package/ts/src/util/clipCodePoints.ts +26 -0
  465. package/ts/src/util/paths.ts +63 -0
  466. package/ts/src/util/program.ts +9 -0
  467. package/ts/src/util/pyGenerators.ts +144 -0
  468. package/ts/src/util/pythonText.ts +317 -0
  469. package/ts/src/util/semver.ts +45 -0
  470. package/ts/src/util/shellWord.ts +25 -0
  471. package/ts/src/util/slug.ts +64 -0
  472. package/ts/src/welcome/card.ts +339 -0
  473. package/ts/src/welcome/statusLine.ts +101 -0
  474. package/ts/src/welcome/updateCache.ts +129 -0
@@ -0,0 +1,176 @@
1
+ // The front door's own answer for a name it does not know (CV22.DS10.TS5, D2).
2
+ //
3
+ // Until TS5 every such name fell through to Python: an unknown command reached
4
+ // `_dispatch`'s `Unknown command` and its usage block, an unknown subcommand
5
+ // reached argparse, which names the program `__main__.py`. With the fallback
6
+ // gone nobody would own the answer, and an unowned answer is how a surface
7
+ // disappears without anyone deciding to remove it. So TypeScript owns it, in
8
+ // the shapes D2 records:
9
+ //
10
+ // * top level -- `Unknown command: <name>`, a blank line, and the usage
11
+ // block, all on stdout, exit 1: `_dispatch`'s shape, the one scripts saw.
12
+ // No command at all prints the usage block alone, exit 1.
13
+ // * a family -- argparse's shape: one `usage:` line and one `error:` line on
14
+ // stderr, exit 2, with the FAMILY as the program name instead of
15
+ // `__main__.py`. US2 gave `runtime` exactly this shape first; this module
16
+ // generalizes it, byte-for-byte.
17
+ // * `-h` / `--help` -- the usage on stdout, exit 0, at either level (the
18
+ // plateau-2 amendment to D2): help must not become an error just because
19
+ // the program that used to print it is gone.
20
+ //
21
+ // Seven families answered an unknown subcommand differently in the oracle
22
+ // (inventory F6): a usage line on stdout with exit 1, `Unknown subcommand` with
23
+ // exit 1, or -- `conversation-logger` -- nothing at all with exit 0. They move
24
+ // to the uniform shape deliberately; the table is in the story's inventory.
25
+
26
+ import { PROGRAM } from "#util/program.ts";
27
+
28
+ /** The program name the top-level usage prints: `#util/program.ts`, the one place US3 renames. */
29
+ export { PROGRAM };
30
+
31
+ /**
32
+ * Every top-level command the front door answers, in the oracle's order, with a
33
+ * one-line summary. The unknown-command answer prints this; a test proves every
34
+ * name here routes somewhere other than the unknown-command answer.
35
+ */
36
+ export const FRONT_DOOR_COMMANDS: ReadonlyArray<readonly [name: string, summary: string]> = [
37
+ ["init", "Initialize a user home from identity templates"],
38
+ ["seed", "Seed identity YAML files into the database"],
39
+ ["identity", "Read and update identity in the database"],
40
+ ["list", "List personas, journeys, extensions, or all"],
41
+ ["inspect", "Inspect a persona, an extension, the runtime catalog, or the call ledgers"],
42
+ ["detect-persona", "Show persona routing matches for a query"],
43
+ ["extensions", "List, validate, sync, install, or uninstall extensions"],
44
+ ["ext", "Run a command-skill extension's CLI"],
45
+ ["mirror", "Mirror Mode: load context, log, deactivate"],
46
+ ["mode", "Operating mode lifecycle"],
47
+ ["conversation-logger", "Conversation logging"],
48
+ ["backup", "Create a zipped backup of the memory database"],
49
+ ["repair-encoding", "Repair reversible UTF-8 mojibake in user text"],
50
+ ["journal", "Record a journal entry"],
51
+ ["journey", "Inspect or update a journey"],
52
+ ["build", "Builder Mode and the Ariad lifecycle"],
53
+ ["explore", "Explorer Mode"],
54
+ ["soul", "Soul Mode"],
55
+ ["memories", "List or search memories"],
56
+ ["conversations", "List conversations, or append a message batch"],
57
+ ["recall", "Load messages from a previous conversation"],
58
+ ["tasks", "Task management"],
59
+ ["week", "Weekly planning"],
60
+ ["journeys", "List journeys with status and stage"],
61
+ ["consult", "Ask other LLMs through OpenRouter"],
62
+ ["descriptor", "Generate and list routing descriptors"],
63
+ ["consolidate", "Scan memories for patterns and manage consolidation proposals"],
64
+ ["shadow", "Surface and promote shadow-layer observations"],
65
+ ["mcp", "Run the Mirror MCP server over stdio"],
66
+ ["runtime", "Runtime status, drift, backups, version, and updates"],
67
+ ["welcome", "Render the welcome card"],
68
+ ];
69
+
70
+ const NAME_WIDTH = Math.max(...FRONT_DOOR_COMMANDS.map(([name]) => name.length)) + 2;
71
+
72
+ /** The top-level usage block. */
73
+ export const USAGE = [
74
+ `Usage: ${PROGRAM} <command> [args]`,
75
+ "",
76
+ "Commands:",
77
+ ...FRONT_DOOR_COMMANDS.map(([name, summary]) => ` ${name.padEnd(NAME_WIDTH)}${summary}`),
78
+ "",
79
+ ].join("\n");
80
+
81
+ /** What routing hands the front door when a name is not one it answers. */
82
+ export type UsageRequest =
83
+ | {
84
+ readonly scope: "top-level";
85
+ /** The unknown name, or null when no command was given at all. */
86
+ readonly given: string | null;
87
+ }
88
+ | {
89
+ readonly scope: "family";
90
+ /** The family as a user types it: `build`, `explore story`. */
91
+ readonly program: string;
92
+ /** What the family does answer, from routing's own allowlist. */
93
+ readonly choices: readonly string[];
94
+ /** The unrecognized subcommand, or "" when none was given. */
95
+ readonly given: string;
96
+ };
97
+
98
+ export interface UsageAnswer {
99
+ readonly stream: "stdout" | "stderr";
100
+ readonly exitCode: number;
101
+ readonly text: string;
102
+ }
103
+
104
+ const HELP = new Set(["-h", "--help"]);
105
+
106
+ /** One family's `usage:` line. */
107
+ export function familyUsageLine(program: string, choices: readonly string[]): string {
108
+ return `usage: ${program} [-h] {${choices.join(",")}} ...\n`;
109
+ }
110
+
111
+ /**
112
+ * argparse's refusal, in the product's vocabulary: the usage line, then one
113
+ * `error:` line naming what was wrong.
114
+ */
115
+ export function renderUnknownSubcommand(
116
+ program: string,
117
+ choices: readonly string[],
118
+ given: string,
119
+ ): string {
120
+ const detail =
121
+ given === ""
122
+ ? "the following arguments are required: command"
123
+ : `argument command: invalid choice: '${given}' (choose from ${choices.join(", ")})`;
124
+ return `${familyUsageLine(program, choices)}${program}: error: ${detail}\n`;
125
+ }
126
+
127
+ /**
128
+ * What the front door knows about the machine when it answers bare `mirror`:
129
+ * whether any source configured a user, and where the config file would be.
130
+ */
131
+ export interface UsageContext {
132
+ readonly userConfigured: boolean;
133
+ readonly configFile: string;
134
+ }
135
+
136
+ /**
137
+ * The first seconds after `npm install -g mirror-mind` (CV22.DS10.US3): bare
138
+ * `mirror` on a machine with no user configured is an orientation, not a
139
+ * usage dump -- three lines that say what is missing, what to run, and where
140
+ * the configuration will live. With a user configured, bare `mirror` is the
141
+ * usage block, as before.
142
+ */
143
+ export function renderOrientation(configFile: string): string {
144
+ return [
145
+ "No user configured yet.",
146
+ ` Run: ${PROGRAM} init <your-name>`,
147
+ ` Configuration will live at: ${configFile}`,
148
+ "",
149
+ ].join("\n");
150
+ }
151
+
152
+ /** Render the answer for a request routing could not place. */
153
+ export function answerUsage(request: UsageRequest, context?: UsageContext): UsageAnswer {
154
+ if (request.scope === "top-level") {
155
+ if (request.given !== null && HELP.has(request.given)) {
156
+ return { stream: "stdout", exitCode: 0, text: USAGE };
157
+ }
158
+ if (request.given === null && context !== undefined && !context.userConfigured) {
159
+ return { stream: "stdout", exitCode: 1, text: renderOrientation(context.configFile) };
160
+ }
161
+ const unknown = request.given === null ? "" : `Unknown command: ${request.given}\n\n`;
162
+ return { stream: "stdout", exitCode: 1, text: `${unknown}${USAGE}` };
163
+ }
164
+ if (HELP.has(request.given)) {
165
+ return {
166
+ stream: "stdout",
167
+ exitCode: 0,
168
+ text: familyUsageLine(request.program, request.choices),
169
+ };
170
+ }
171
+ return {
172
+ stream: "stderr",
173
+ exitCode: 2,
174
+ text: renderUnknownSubcommand(request.program, request.choices, request.given),
175
+ };
176
+ }
@@ -0,0 +1,298 @@
1
+ // Generate the canonical Mirror Mind Claude plugin (manifest + skills), and the
2
+ // Claude Code copies of Pi-sourced skills.
3
+ //
4
+ // The Node port of `src/memory/plugins/claude.py` and its wrapper
5
+ // `scripts/build_claude_plugin.py` (CV22.DS10.TS5, slice B).
6
+ //
7
+ // The Zero Python gate assigns the builder to **US3**, on the grounds that US3
8
+ // decides what it builds. But it is a `.py` file, and TS5's done condition is
9
+ // that `git ls-files '*.py'` is empty -- so the file cannot survive this story
10
+ // whoever owns its content. Decision D6 resolves that: the port lands here,
11
+ // byte-identical in output; US3 still owns the shape of what it emits when the
12
+ // npm entry point becomes real.
13
+ //
14
+ // The plugin skills are generated from `.claude/skills/` (the Claude-tuned
15
+ // source), with one exception since CR102. A Pi-sourced skill
16
+ // (`piSourcedSkills.ts`) has one body in every runtime, so this module also
17
+ // generates its `.claude/skills/` copy from `.pi/skills/`, and takes its plugin
18
+ // copy from that generated content rather than from the file on disk. Hooks
19
+ // under `plugins/mirror-mind/hooks/` are hand-authored plugin source and are
20
+ // NOT managed by this module -- which is precisely why TS5's inventory had to
21
+ // find their interpreter spawns by reading them.
22
+
23
+ import {
24
+ existsSync,
25
+ mkdirSync,
26
+ readdirSync,
27
+ readFileSync,
28
+ rmdirSync,
29
+ unlinkSync,
30
+ writeFileSync,
31
+ } from "node:fs";
32
+ import { dirname, join, relative } from "node:path";
33
+
34
+ import { packageVersion } from "#runtime/version.ts";
35
+ import { claudeVariant, PI_SKILLS_DIR, PI_SOURCED_SKILLS } from "./piSourcedSkills.ts";
36
+
37
+ export const PLUGIN_NAME = "mirror-mind";
38
+ export const PLUGIN_DESCRIPTION =
39
+ "Mirror Mind — local-first memory and identity for agentic runtimes.";
40
+ export const PLUGIN_AUTHOR = "Mirror Mind";
41
+
42
+ export const SKILLS_SOURCE_DIR = ".claude/skills";
43
+ export const PLUGIN_DIR = "plugins/mirror-mind";
44
+ export const SKILL_FILENAME = "SKILL.md";
45
+
46
+ /** A file the generator owns, addressed relative to the repo root. */
47
+ export interface GeneratedFile {
48
+ readonly relativePath: string;
49
+ readonly content: string;
50
+ /** The authored file it derives from, when a person could mistake it for one. */
51
+ readonly source?: string;
52
+ }
53
+
54
+ /**
55
+ * The project version, read from source.
56
+ *
57
+ * Read from source rather than from installed metadata, so the build is
58
+ * deterministic. `packageVersion` is the single body that answers this for the
59
+ * whole core (US2 decision D2); CV22.DS10.TS5 re-pointed it at
60
+ * `package.json` and this builder followed with no edit, which is the point
61
+ * of there being one body.
62
+ */
63
+ export function readVersion(repoRoot: string): string {
64
+ const version = packageVersion(repoRoot);
65
+ if (!version) throw new Error(`version not found from ${repoRoot}`);
66
+ return version;
67
+ }
68
+
69
+ /**
70
+ * The directories the MCP entry and the plugin's hook wrappers search for
71
+ * `mirror` and `node` when the PATH does not hold them: a GUI launch leaves
72
+ * the npm global bin off it. One list, so `mcpEntry.test.ts` can hold the
73
+ * entry's search equal to the wrappers' (`generate_hook_wrappers.sh`).
74
+ */
75
+ export const GLOBAL_BIN_DIRS = ['"$HOME/.nvm/current/bin"', "/opt/homebrew/bin", "/usr/local/bin"];
76
+
77
+ /**
78
+ * The MCP entry's locator (CV21.DS2.US6).
79
+ *
80
+ * The manifest used to name `${CLAUDE_PLUGIN_ROOT}/mcp/launch.sh`. Claude Code
81
+ * expands that variable; Codex does not expand it in an MCP command (DS12.TS2
82
+ * L6), so the server never started there. The locator is the launcher's
83
+ * search, inline: `MIRROR_BIN`, the PATH, then the global bin directories for
84
+ * `mirror`; `MIRROR_NODE`, the PATH, the directory `mirror` was found in, then
85
+ * the same directories for `node`. A client whose PATH holds neither (a GUI
86
+ * launch) still starts the server, and one with no `mirror` anywhere gets one
87
+ * stderr line naming the install step, and exit 1.
88
+ *
89
+ * Written for what three runtimes do to the string (the US6 spike):
90
+ *
91
+ * * POSIX `sh`: `/bin/sh` is dash on Linux;
92
+ * * no `${...}`: Claude Code expands that form in MCP configuration, and a
93
+ * runtime that did so with an unset name would blank it;
94
+ * * no positional parameter (`$1`, `$@`, ...) and no backtick, for the same
95
+ * reason, and because the text travels through two JSON encoders;
96
+ * * variables named `mmbin`, `mmnode`, `mmdir`: Grok substitutes SET
97
+ * environment variables by name inside MCP args at load time (`$HOME`
98
+ * became the path), so a script variable with a name a person exports
99
+ * would be replaced before the shell ever saw it.
100
+ */
101
+ export const MCP_LOCATOR = [
102
+ 'mmbin=""',
103
+ '[ -n "$MIRROR_BIN" ] && [ -x "$MIRROR_BIN/mirror" ] && mmbin="$MIRROR_BIN/mirror"',
104
+ '[ -n "$mmbin" ] || mmbin="$(command -v mirror 2>/dev/null)"',
105
+ `for mmdir in ${GLOBAL_BIN_DIRS.join(" ")}; do [ -n "$mmbin" ] && break; [ -x "$mmdir/mirror" ] && mmbin="$mmdir/mirror"; done`,
106
+ '[ -n "$mmbin" ] || { echo "mirror-mind: mirror not found on PATH; the MCP server cannot start. Install mirror-mind (npm install -g mirror-mind) or set MIRROR_BIN to its bin directory." >&2; exit 1; }',
107
+ 'mmnode="$MIRROR_NODE"',
108
+ '[ -n "$mmnode" ] && [ -x "$mmnode" ] || mmnode="$(command -v node 2>/dev/null)"',
109
+ `for mmdir in "$(dirname "$mmbin")" ${GLOBAL_BIN_DIRS.join(" ")}; do [ -n "$mmnode" ] && break; [ -x "$mmdir/node" ] && mmnode="$mmdir/node"; done`,
110
+ '[ -n "$mmnode" ] || { echo "mirror-mind: node not found on PATH; the MCP server cannot start. Set MIRROR_NODE." >&2; exit 1; }',
111
+ 'exec "$mmnode" "$mmbin" mcp',
112
+ ].join("; ");
113
+
114
+ /** The `mcpServers` entry: the locator, run by the system shell. */
115
+ export const MCP_ENTRY: { readonly command: string; readonly args: readonly string[] } = {
116
+ command: "/bin/sh",
117
+ args: ["-c", MCP_LOCATOR],
118
+ };
119
+
120
+ /**
121
+ * The Claude plugin manifest (minimal; no unsupported keys).
122
+ *
123
+ * Declares the Mirror MCP server so the canonical package carries it. The
124
+ * entry names no file of the plugin's and no variable a runtime must expand
125
+ * (`MCP_LOCATOR`): Claude Code, Codex, and Grok render it as written, and
126
+ * each starts the server from it (CV21.DS2.US6).
127
+ */
128
+ export function buildManifest(version: string): Record<string, unknown> {
129
+ return {
130
+ name: PLUGIN_NAME,
131
+ version,
132
+ description: PLUGIN_DESCRIPTION,
133
+ author: { name: PLUGIN_AUTHOR },
134
+ mcpServers: {
135
+ [PLUGIN_NAME]: { command: MCP_ENTRY.command, args: [...MCP_ENTRY.args] },
136
+ },
137
+ };
138
+ }
139
+
140
+ /** The manifest as deterministic JSON text, byte-identical to Python's. */
141
+ export function manifestJson(version: string): string {
142
+ return `${JSON.stringify(buildManifest(version), null, 2)}\n`;
143
+ }
144
+
145
+ /**
146
+ * The skill markdown file, matching `skill.md` case-insensitively.
147
+ *
148
+ * Case-insensitive makes the generator robust on both case-sensitive and
149
+ * case-insensitive filesystems, including older checkouts that may still
150
+ * contain lowercase `skill.md`.
151
+ */
152
+ function findSkillMarkdown(skillDir: string): string | null {
153
+ for (const entry of readdirSync(skillDir, { withFileTypes: true, encoding: "utf8" }).sort(
154
+ (a, b) => (a.name < b.name ? -1 : a.name > b.name ? 1 : 0),
155
+ )) {
156
+ if (entry.isFile() && entry.name.toLowerCase() === SKILL_FILENAME.toLowerCase()) {
157
+ return join(skillDir, entry.name);
158
+ }
159
+ }
160
+ return null;
161
+ }
162
+
163
+ /**
164
+ * `[skillDirName, markdownPath]` for each skill under `.claude/skills/`. Most
165
+ * are authored there. A Pi-sourced skill's file is generated instead
166
+ * (`piSourcedSkills.ts`), and `planGeneratedFiles` takes that one from its Pi
167
+ * copy, never from this path.
168
+ */
169
+ export function discoverSkillSources(repoRoot: string): [string, string][] {
170
+ const base = join(repoRoot, SKILLS_SOURCE_DIR);
171
+ const sources: [string, string][] = [];
172
+ const entries = readdirSync(base, { withFileTypes: true, encoding: "utf8" }).sort((a, b) =>
173
+ a.name < b.name ? -1 : a.name > b.name ? 1 : 0,
174
+ );
175
+ for (const child of entries) {
176
+ if (!child.isDirectory()) continue;
177
+ const markdown = findSkillMarkdown(join(base, child.name));
178
+ if (markdown === null) continue;
179
+ sources.push([child.name, markdown]);
180
+ }
181
+ return sources;
182
+ }
183
+
184
+ /**
185
+ * The `.claude/skills/` copy of each Pi-sourced skill, generated from its Pi
186
+ * copy. Refuses, before anything is written, when a listed skill has no Pi
187
+ * copy: generation would otherwise stop without a word, and the old Claude copy
188
+ * would pass for an authored one again.
189
+ */
190
+ function planPiSourcedClaudeCopies(repoRoot: string): Map<string, GeneratedFile> {
191
+ const copies = new Map<string, GeneratedFile>();
192
+ for (const skill of PI_SOURCED_SKILLS) {
193
+ const source = join(PI_SKILLS_DIR, skill, SKILL_FILENAME);
194
+ const path = join(repoRoot, source);
195
+ if (!existsSync(path)) {
196
+ throw new Error(
197
+ `${source}: missing, but ${skill} is listed as Pi-sourced in ts/src/guards/piSourcedSkills.ts`,
198
+ );
199
+ }
200
+ copies.set(skill, {
201
+ relativePath: join(SKILLS_SOURCE_DIR, skill, SKILL_FILENAME),
202
+ content: claudeVariant(skill, readFileSync(path, "utf8"), source),
203
+ source,
204
+ });
205
+ }
206
+ return copies;
207
+ }
208
+
209
+ /** Every file the generator owns, without touching the filesystem. */
210
+ export function planGeneratedFiles(repoRoot: string): GeneratedFile[] {
211
+ const version = readVersion(repoRoot);
212
+ const claudeCopies = planPiSourcedClaudeCopies(repoRoot);
213
+
214
+ const pluginSkills = new Map<string, GeneratedFile>();
215
+ for (const [name, markdown] of discoverSkillSources(repoRoot)) {
216
+ if (claudeCopies.has(name)) continue;
217
+ pluginSkills.set(name, {
218
+ relativePath: join(PLUGIN_DIR, "skills", name, SKILL_FILENAME),
219
+ content: readFileSync(markdown, "utf8"),
220
+ });
221
+ }
222
+ for (const [name, claudeCopy] of claudeCopies) {
223
+ pluginSkills.set(name, {
224
+ relativePath: join(PLUGIN_DIR, "skills", name, SKILL_FILENAME),
225
+ content: claudeCopy.content,
226
+ source: claudeCopy.source,
227
+ });
228
+ }
229
+
230
+ return [
231
+ {
232
+ relativePath: join(PLUGIN_DIR, ".claude-plugin", "plugin.json"),
233
+ content: manifestJson(version),
234
+ },
235
+ ...claudeCopies.values(),
236
+ ...[...pluginSkills].sort(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0)).map(([, file]) => file),
237
+ ];
238
+ }
239
+
240
+ /** One drift line; a derived file also names the source a person should edit. */
241
+ function driftLine(kind: "missing" | "out of date", file: GeneratedFile): string {
242
+ const from = file.source
243
+ ? ` (generated from ${file.source}: edit the source, then regenerate)`
244
+ : "";
245
+ return `${kind}: ${file.relativePath}${from}`;
246
+ }
247
+
248
+ function generatedSkillFiles(repoRoot: string): string[] {
249
+ const skillsRoot = join(repoRoot, PLUGIN_DIR, "skills");
250
+ if (!existsSync(skillsRoot)) return [];
251
+ const found: string[] = [];
252
+ for (const entry of readdirSync(skillsRoot, { withFileTypes: true, encoding: "utf8" })) {
253
+ if (!entry.isDirectory()) continue;
254
+ const candidate = join(skillsRoot, entry.name, SKILL_FILENAME);
255
+ if (existsSync(candidate)) found.push(candidate);
256
+ }
257
+ return found.sort();
258
+ }
259
+
260
+ /**
261
+ * Write every generated file, the plugin and the `.claude/skills/` copies of
262
+ * Pi-sourced skills, or report drift when `write` is false.
263
+ *
264
+ * Returns human-readable drift descriptions. In check mode the list is the
265
+ * assertion target (empty == in sync). In write mode it is empty and the files
266
+ * are written; stale generated skills are removed.
267
+ */
268
+ export function materialize(repoRoot: string, options: { write: boolean }): string[] {
269
+ const desired = planGeneratedFiles(repoRoot);
270
+ const desiredPaths = new Set(desired.map((file) => file.relativePath));
271
+ const problems: string[] = [];
272
+
273
+ for (const generated of desired) {
274
+ const target = join(repoRoot, generated.relativePath);
275
+ if (options.write) {
276
+ mkdirSync(dirname(target), { recursive: true });
277
+ writeFileSync(target, generated.content, "utf8");
278
+ } else if (!existsSync(target)) {
279
+ problems.push(driftLine("missing", generated));
280
+ } else if (readFileSync(target, "utf8") !== generated.content) {
281
+ problems.push(driftLine("out of date", generated));
282
+ }
283
+ }
284
+
285
+ for (const existing of generatedSkillFiles(repoRoot)) {
286
+ const rel = relative(repoRoot, existing);
287
+ if (desiredPaths.has(rel)) continue;
288
+ if (options.write) {
289
+ const parent = dirname(existing);
290
+ for (const leftover of readdirSync(parent).sort()) unlinkSync(join(parent, leftover));
291
+ rmdirSync(parent);
292
+ } else {
293
+ problems.push(`stale: ${rel}`);
294
+ }
295
+ }
296
+
297
+ return problems;
298
+ }