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,317 @@
1
+ // Python string semantics the port must reproduce byte-for-byte.
2
+ //
3
+ // Python indexes, slices, and orders `str` by Unicode code point. JavaScript
4
+ // does all three by UTF-16 code unit, which agrees with Python on the Basic
5
+ // Multilingual Plane and silently disagrees the moment an emoji or any other
6
+ // astral character appears: `"🎯x".slice(0, 1)` is half a surrogate pair, and
7
+ // `"🎯" < "\uFFFF"` in JS while Python says the opposite. Title truncation,
8
+ // sorted term lists, and path ordering are all parity surfaces graded against
9
+ // Python's output, so every one of them must go through these helpers.
10
+
11
+ /**
12
+ * Python's whitespace set: what `str.isspace()` reports, what `str.strip()`
13
+ * removes, and what `\s` matches in a `str` pattern. It is NOT JavaScript's
14
+ * `\s`, and the difference is load-bearing in both directions:
15
+ *
16
+ * - Python includes U+001C-U+001F (the file/group/record/unit separators);
17
+ * JavaScript does not.
18
+ * - JavaScript includes U+FEFF (the byte-order mark); Python does not.
19
+ *
20
+ * A UTF-8 file that opens with a BOM is the realistic case: both cores read
21
+ * the BOM into the string, and a JavaScript `\s` would silently collapse it
22
+ * away while Python keeps it -- enough to change a checksum and make one core
23
+ * report drift the other cannot see. Written as a class body so callers can
24
+ * build the anchors and quantifiers they need.
25
+ */
26
+ export const PYTHON_WHITESPACE_CLASS =
27
+ "\\t\\n\\v\\f\\r\\u001c-\\u001f \\u0085\\u00a0\\u1680\\u2000-\\u200a\\u2028\\u2029\\u202f\\u205f\\u3000";
28
+
29
+ /** A fresh regex over {@link PYTHON_WHITESPACE_CLASS}; `flags` always gets `u`. */
30
+ export function pythonWhitespaceRegex(quantifier = "+", flags = "g"): RegExp {
31
+ return new RegExp(`[${PYTHON_WHITESPACE_CLASS}]${quantifier}`, `${flags}u`);
32
+ }
33
+
34
+ const PY_STRIP_RE = new RegExp(
35
+ `^[${PYTHON_WHITESPACE_CLASS}]+|[${PYTHON_WHITESPACE_CLASS}]+$`,
36
+ "gu",
37
+ );
38
+
39
+ /** Python `str.strip()` with no argument. Not `String.prototype.trim()`. */
40
+ export function pyStrip(text: string): string {
41
+ return text.replace(PY_STRIP_RE, "");
42
+ }
43
+
44
+ const PY_RSTRIP_RE = new RegExp(`[${PYTHON_WHITESPACE_CLASS}]+$`, "u");
45
+
46
+ /** Python `str.rstrip()` with no argument. */
47
+ export function pyRStrip(text: string): string {
48
+ return text.replace(PY_RSTRIP_RE, "");
49
+ }
50
+
51
+ const PY_SPLIT_RE = new RegExp(`[${PYTHON_WHITESPACE_CLASS}]+`, "u");
52
+
53
+ /**
54
+ * Python `str.split()` with no argument: split on RUNS of whitespace and drop
55
+ * the empty leading/trailing fields. `"a b ".split()` is `["a", "b"]`, while
56
+ * JavaScript's `"a b ".split(/\s+/)` yields a trailing `""` -- and disagrees
57
+ * about the separator set on U+001C-U+001F and U+FEFF besides.
58
+ */
59
+ export function pySplitWhitespace(text: string): string[] {
60
+ const stripped = pyStrip(text);
61
+ if (stripped === "") return [];
62
+ return stripped.split(PY_SPLIT_RE);
63
+ }
64
+
65
+ /**
66
+ * Python `str.splitlines()`: the line boundaries are \n, \r, \r\n, \v, \f,
67
+ * U+001C, U+001D, U+001E, U+0085, U+2028, and U+2029 -- eleven of them, where
68
+ * `split("\n")` knows one. A ritual card fed a form feed renders as two blocks
69
+ * in Python and one long line in a naive port.
70
+ *
71
+ * Like Python, a trailing boundary does not produce a final empty field.
72
+ */
73
+ export function pySplitLines(text: string): string[] {
74
+ if (text === "") return [];
75
+ const lines: string[] = [];
76
+ let current = "";
77
+ for (let index = 0; index < text.length; index += 1) {
78
+ const char = text[index] as string;
79
+ if (char === "\r") {
80
+ lines.push(current);
81
+ current = "";
82
+ if (text[index + 1] === "\n") index += 1;
83
+ continue;
84
+ }
85
+ if (PY_LINE_BOUNDARIES.has(char)) {
86
+ lines.push(current);
87
+ current = "";
88
+ continue;
89
+ }
90
+ current += char;
91
+ }
92
+ if (current !== "") lines.push(current);
93
+ return lines;
94
+ }
95
+
96
+ const PY_LINE_BOUNDARIES = new Set([
97
+ "\n",
98
+ "\v",
99
+ "\f",
100
+ "\u001c",
101
+ "\u001d",
102
+ "\u001e",
103
+ "\u0085",
104
+ "\u2028",
105
+ "\u2029",
106
+ ]);
107
+
108
+ /**
109
+ * Python truthiness. JavaScript and Python agree on `null`/`undefined`,
110
+ * `false`, `0`, `NaN`, and `""` -- and disagree on containers: `[]` and `{}`
111
+ * are falsy in Python and truthy in JavaScript. Manifest validation leans on
112
+ * this exactly (`if not data.get(field)`), so an extension declaring
113
+ * `runtimes: {}` must fail the required-field check, not sail past it.
114
+ */
115
+ export function pyTruthy(value: unknown): boolean {
116
+ if (value === null || value === undefined || value === false) return false;
117
+ if (typeof value === "number") return value !== 0 && !Number.isNaN(value);
118
+ if (typeof value === "string") return value.length > 0;
119
+ if (Array.isArray(value)) return value.length > 0;
120
+ if (typeof value === "object") return Object.keys(value).length > 0;
121
+ return Boolean(value);
122
+ }
123
+
124
+ /**
125
+ * Python `str(value)` for the scalar shapes a YAML manifest can hold. Error
126
+ * messages interpolate parsed YAML directly, so `None`/`True`/`False` must
127
+ * render with Python's capitalization, not JavaScript's.
128
+ */
129
+ export function pyStr(value: unknown): string {
130
+ if (value === null || value === undefined) return "None";
131
+ if (value === true) return "True";
132
+ if (value === false) return "False";
133
+ if (Array.isArray(value)) return `[${value.map(pyRepr).join(", ")}]`;
134
+ if (typeof value === "object") {
135
+ const entries = Object.entries(value as Record<string, unknown>);
136
+ return `{${entries.map(([key, item]) => `${pyRepr(key)}: ${pyRepr(item)}`).join(", ")}}`;
137
+ }
138
+ return String(value);
139
+ }
140
+
141
+ /**
142
+ * Python `str.title()`: uppercase the first cased character of every run of
143
+ * letters, lowercase the rest.
144
+ *
145
+ * Not `capitalize()` and not a naive `[0].toUpperCase() + slice(1)`. Python breaks
146
+ * runs on any non-alphabetic character, so `"plan_approved".title()` is
147
+ * `"Plan_Approved"` and `"don't".title()` is `"Don'T"` -- the second is a famous
148
+ * Python wart, and a port that "fixes" it diverges on any surface that titles a
149
+ * value containing an apostrophe.
150
+ *
151
+ * Only lifecycle `next_event` values reach this today (`prepare`, `implement`),
152
+ * which are single lowercase words, so the wart is reproduced rather than
153
+ * exercised.
154
+ */
155
+ export function pyTitle(text: string): string {
156
+ let previousIsAlpha = false;
157
+ let result = "";
158
+ for (const character of text) {
159
+ const isAlpha = /\p{L}/u.test(character);
160
+ result += previousIsAlpha ? character.toLowerCase() : character.toUpperCase();
161
+ previousIsAlpha = isAlpha;
162
+ }
163
+ return result;
164
+ }
165
+
166
+ /**
167
+ * Python `repr(value)` / an f-string's `!r`.
168
+ *
169
+ * Widened at CV22.DS7.TS4 plateau 2, where `inspect llm-calls` prints stored
170
+ * prompts and responses with `!r` -- arbitrary user text, not the single
171
+ * lowercase words the first caller passed. Python's rules, in order:
172
+ *
173
+ * * the QUOTE is single, unless the string contains a single quote and no
174
+ * double quote, in which case Python switches to double quotes rather
175
+ * than escaping (`"it's fine"`, not `'it\'s fine'`);
176
+ * * a backslash is doubled, and the chosen quote is escaped;
177
+ * * `\n`, `\r`, `\t` get their short escapes; every other C0 control and
178
+ * DEL get `\xNN`;
179
+ * * printable non-ASCII is left ALONE (Python 3 repr is not ASCII-safe),
180
+ * so `descrição` stays itself;
181
+ * * non-printable non-ASCII is escaped by width: `\xNN`, `\uNNNN`, or
182
+ * `\UNNNNNNNN`. Python's printable excludes Unicode's Other and Separator
183
+ * categories, so a C1 control (U+009B, the 8-bit CSI), a bidi override, a
184
+ * zero-width space, and a line separator are all escaped. Until CR104 the
185
+ * port left them raw, so they reached a terminal through every caller.
186
+ *
187
+ * The categories come from the Unicode tables V8 carries, which can differ from
188
+ * the running Python's for code points assigned between their versions.
189
+ */
190
+ export function pyRepr(value: unknown): string {
191
+ return typeof value === "string" ? pyReprString(value) : pyStr(value);
192
+ }
193
+
194
+ /** Unicode's Other and Separator categories: what Python's `str.isprintable` refuses. */
195
+ const OTHER_OR_SEPARATOR = /^[\p{C}\p{Z}]$/u;
196
+
197
+ /** Whether Python's repr prints `character` as itself. The ASCII space is the one printable separator. */
198
+ function pyPrintable(character: string, code: number): boolean {
199
+ if (code < 0x80) return code >= 0x20 && code !== 0x7f;
200
+ return !OTHER_OR_SEPARATOR.test(character);
201
+ }
202
+
203
+ /** Python's escape for a character it will not print, by width. */
204
+ function pyEscape(code: number): string {
205
+ if (code <= 0xff) return `\\x${code.toString(16).padStart(2, "0")}`;
206
+ if (code <= 0xffff) return `\\u${code.toString(16).padStart(4, "0")}`;
207
+ return `\\U${code.toString(16).padStart(8, "0")}`;
208
+ }
209
+
210
+ function pyReprString(value: string): string {
211
+ const quote = value.includes("'") && !value.includes('"') ? '"' : "'";
212
+ let out = "";
213
+ for (const character of value) {
214
+ if (character === "\\") {
215
+ out += "\\\\";
216
+ } else if (character === quote) {
217
+ out += `\\${quote}`;
218
+ } else if (character === "\n") {
219
+ out += "\\n";
220
+ } else if (character === "\r") {
221
+ out += "\\r";
222
+ } else if (character === "\t") {
223
+ out += "\\t";
224
+ } else {
225
+ const code = character.codePointAt(0) ?? 0;
226
+ out += pyPrintable(character, code) ? character : pyEscape(code);
227
+ }
228
+ }
229
+ return `${quote}${out}${quote}`;
230
+ }
231
+
232
+ /** Python `sorted()` / `<` on `str`: lexicographic by code point. */
233
+ export function compareByCodePoint(a: string, b: string): number {
234
+ const left = Array.from(a);
235
+ const right = Array.from(b);
236
+ const shared = Math.min(left.length, right.length);
237
+ for (let index = 0; index < shared; index += 1) {
238
+ const delta = (left[index]?.codePointAt(0) ?? 0) - (right[index]?.codePointAt(0) ?? 0);
239
+ if (delta !== 0) return delta;
240
+ }
241
+ return left.length - right.length;
242
+ }
243
+
244
+ /** Python `sorted(values)` on strings. Returns a new array. */
245
+ export function sortByCodePoint(values: readonly string[]): string[] {
246
+ return [...values].sort(compareByCodePoint);
247
+ }
248
+
249
+ /** Python `len(text)`: the number of code points, not UTF-16 units. */
250
+ export function codePointLength(text: string): number {
251
+ return Array.from(text).length;
252
+ }
253
+
254
+ /** Python `text[:end]`: the first `end` code points. */
255
+ export function sliceCodePoints(text: string, end: number): string {
256
+ return Array.from(text).slice(0, end).join("");
257
+ }
258
+
259
+ /**
260
+ * Python `sorted()` over `pathlib.Path` values: paths compare by their
261
+ * COMPONENT lists, not by their joined string. The two orders diverge whenever
262
+ * a directory name contains a character that sorts before `/` (0x2F) -- `-`
263
+ * (0x2D) is the common one, so `a/b` precedes `a-x/c` for Python while a plain
264
+ * string sort puts `a-x/c` first. Callers pass already-split components.
265
+ */
266
+ export function comparePathComponents(a: readonly string[], b: readonly string[]): number {
267
+ const shared = Math.min(a.length, b.length);
268
+ for (let index = 0; index < shared; index += 1) {
269
+ const delta = compareByCodePoint(a[index] as string, b[index] as string);
270
+ if (delta !== 0) return delta;
271
+ }
272
+ return a.length - b.length;
273
+ }
274
+
275
+ /**
276
+ * Python `str.format(**values)` for named fields, including brace escaping.
277
+ *
278
+ * `"{{"` and `"}}"` are LITERAL braces in a format string and collapse to a
279
+ * single brace, which a naive `replaceAll("{name}", value)` leaves doubled.
280
+ * `WEEK_PLAN_PROMPT` embeds a JSON example written with doubled braces exactly
281
+ * so `.format()` can be applied to it, so the difference is not cosmetic: the
282
+ * assembled prompt bytes -- and therefore the replay digest -- disagree
283
+ * (CV22.DS7.US11, caught by the prompt-assembly golden).
284
+ *
285
+ * Only the subset the vendored templates use is implemented: named fields with
286
+ * no conversion, no format spec, no attribute or index access. An unknown
287
+ * field raises, as Python's `KeyError` does, rather than silently emitting the
288
+ * placeholder into a prompt.
289
+ */
290
+ export function pyFormat(template: string, values: Readonly<Record<string, string>>): string {
291
+ let out = "";
292
+ for (let index = 0; index < template.length; index += 1) {
293
+ const char = template[index];
294
+ const next = template[index + 1];
295
+ if (char === "{" && next === "{") {
296
+ out += "{";
297
+ index += 1;
298
+ } else if (char === "}" && next === "}") {
299
+ out += "}";
300
+ index += 1;
301
+ } else if (char === "{") {
302
+ const close = template.indexOf("}", index + 1);
303
+ if (close === -1) throw new Error("pyFormat: unmatched '{' in template");
304
+ const field = template.slice(index + 1, close);
305
+ if (!Object.hasOwn(values, field)) {
306
+ throw new Error(`pyFormat: no value for field ${JSON.stringify(field)}`);
307
+ }
308
+ out += values[field];
309
+ index = close;
310
+ } else if (char === "}") {
311
+ throw new Error("pyFormat: single '}' in template");
312
+ } else {
313
+ out += char;
314
+ }
315
+ }
316
+ return out;
317
+ }
@@ -0,0 +1,45 @@
1
+ // Version comparison, once. (CV22.DS10.US3 plateau 4)
2
+ //
3
+ // Three readers compared versions before this module and each carried its own
4
+ // copy: the release-notes reader (the oracle's `_parse_semver`), the welcome
5
+ // card's remote tag (the oracle's `_semver_key`, the same function under a
6
+ // second name), and now the package updater's plan, which has to know whether
7
+ // a channel is ahead of or BEHIND the installed version. One rule for all
8
+ // three, with the oracle's sort behavior kept: an unparseable version sorts
9
+ // below every real one.
10
+
11
+ export type SemverTriple = [number, number, number];
12
+
13
+ const LOOSE = /^v?(\d+)\.(\d+)\.(\d+)$/;
14
+ /** No leading `v`, no prerelease, no build, no surrounding space. */
15
+ const PLAIN = /^\d+\.\d+\.\d+$/;
16
+
17
+ /** Port of `_parse_semver`: an unparseable version is `[-1, -1, -1]`. */
18
+ export function parseSemver(version: string): SemverTriple {
19
+ const match = LOOSE.exec(version.trim());
20
+ if (!match) return [-1, -1, -1];
21
+ return [Number(match[1]), Number(match[2]), Number(match[3])];
22
+ }
23
+
24
+ /** Negative when `a` is older than `b`, positive when newer, zero when equal. */
25
+ export function compareSemver(a: string, b: string): number {
26
+ const left = parseSemver(a);
27
+ const right = parseSemver(b);
28
+ for (let index = 0; index < 3; index += 1) {
29
+ const diff = (left[index] as number) - (right[index] as number);
30
+ if (diff !== 0) return diff;
31
+ }
32
+ return 0;
33
+ }
34
+
35
+ /**
36
+ * The strict form: a bare numeric triple and nothing else.
37
+ *
38
+ * This is what a registry's dist-tag answer must look like before it becomes
39
+ * the spec of `npm install -g <name>@<value>`. npm's spec grammar also admits
40
+ * tags, ranges, URLs, and paths, and a value that is one of those installs
41
+ * something other than a version.
42
+ */
43
+ export function isPlainSemver(version: string): boolean {
44
+ return PLAIN.test(version);
45
+ }
@@ -0,0 +1,25 @@
1
+ // CR104 — one shell word for a value a printed command interpolates.
2
+ //
3
+ // Mirror prints commands for its reader to run: `Run: mirror build adopt --journey
4
+ // <slug> --method ariad`, the Pull command, a skipped seed entry's edit command.
5
+ // Agents run them as printed. A value that reaches such a command from the database
6
+ // — a journey slug, a persona id — may predate the grammar that now keeps new keys
7
+ // plain (`#identity/identityKey.ts`), so it can hold anything a shell reads as
8
+ // syntax. Every such command therefore quotes what it interpolates, here, once.
9
+ //
10
+ // CR002 wrote this for the Pull command, private to `scopePhrases.ts`; CR104 made
11
+ // it the one helper every printed command uses.
12
+
13
+ /**
14
+ * A POSIX shell word for `value`: as is when it is a plain token, single-quoted
15
+ * otherwise, so `x;touch PWNED` arrives as one argument and runs nothing. A plain
16
+ * token prints unchanged, which keeps every ordinary slug byte-identical in the
17
+ * commands that print it. The empty string becomes `''`, one empty argument.
18
+ *
19
+ * Quoting does not stop a word that begins with `-` from reading as an option to
20
+ * the command it is passed to. That is the grammar's job, not the shell's.
21
+ */
22
+ export function shellWord(value: string): string {
23
+ if (/^[A-Za-z0-9._-]+$/u.test(value)) return value;
24
+ return `'${value.replaceAll("'", `'\\''`)}'`;
25
+ }
@@ -0,0 +1,64 @@
1
+ // Port of src/memory/utils.py (strip_accents, kebab_slug). Registered in
2
+ // ts/parity/oracle-baseline.json.
3
+ //
4
+ // kebab_slug is the canonical filesystem-safe slug for roadmap folders,
5
+ // journey slugs, and exploration folders across the Python codebase (builder
6
+ // story paths, explorer handoff ids, JourneyService.draft_journey). Ported
7
+ // here as ONE shared function for both writer and locator call sites, per the
8
+ // DS7.US1 rider: the original Python bug this guards against was
9
+ // writer/locator drift from two independently-hand-rolled slugifiers.
10
+
11
+ /**
12
+ * Port of `strip_accents`: NFD-decompose, then drop every Unicode
13
+ * Nonspacing_Mark codepoint (Python's `unicodedata.category(c) == "Mn"`) --
14
+ * e.g. "episódio" -> "episodio". Both runtimes resolve category membership
15
+ * against the same kind of Unicode Character Database assignment (ICU-backed
16
+ * in Python's `unicodedata` and in V8's Unicode property escapes), verified
17
+ * against the live oracle for a broad accent/script sample rather than
18
+ * assumed equivalent.
19
+ */
20
+ export function stripAccents(text: string): string {
21
+ return text.normalize("NFD").replace(/\p{Mn}/gu, "");
22
+ }
23
+
24
+ /**
25
+ * Port of `kebab_slug`: lowercase, strip accents, collapse every run of
26
+ * non-`[a-z0-9]` characters to a single hyphen, trim edge hyphens, then
27
+ * hard-cap at `maxLength` characters (re-trimming any edge hyphen the cap
28
+ * itself exposes) so a single directory component can never exceed the
29
+ * filesystem NAME_MAX limit (255 bytes). Truncation is safe as plain UTF-16
30
+ * slicing because by this point the string contains only ASCII
31
+ * `[a-z0-9-]` characters -- the accent-stripping and character-class
32
+ * collapse already happened, so there is no multi-code-unit codepoint for a
33
+ * slice boundary to split.
34
+ *
35
+ * Returns "" when no alphanumeric content remains; callers decide how to
36
+ * handle that (bare code prefix, fallback token, or a validation error) --
37
+ * this function never substitutes a default itself.
38
+ */
39
+ export function kebabSlug(text: string, maxLength = 80): string {
40
+ const normalized = stripAccents(text).toLowerCase();
41
+ let slug = normalized.replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
42
+ if (slug.length > maxLength) {
43
+ slug = slug.slice(0, maxLength).replace(/^-+|-+$/g, "");
44
+ }
45
+ return slug;
46
+ }
47
+
48
+ const KEBAB_SLUG = /^[a-z0-9]+(?:-[a-z0-9]+)*$/u;
49
+
50
+ /**
51
+ * Whether `value` is a kebab-case slug: lowercase ASCII letters and digits, in runs
52
+ * joined by single hyphens, at most `maxLength` characters. Those are exactly the
53
+ * non-empty strings `kebabSlug` returns unchanged, so a slug is what this module
54
+ * would have made, and `kebabSlug` of anything else is the slug to suggest in its
55
+ * place (CR104).
56
+ *
57
+ * A shell carries such a slug as one plain word, and it can never begin with the
58
+ * hyphen that would make it read as an option. That is why journey slugs and
59
+ * persona ids, which agents put into commands, must be one
60
+ * (`#identity/identityKey.ts`).
61
+ */
62
+ export function isKebabSlug(value: string, maxLength = 80): boolean {
63
+ return value.length <= maxLength && KEBAB_SLUG.test(value);
64
+ }