@agimon-ai/doompi 0.0.1-alpha.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 (448) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +546 -0
  3. package/dist/_virtual/_rolldown/runtime.cjs +1 -0
  4. package/dist/bin/cli.cjs +3 -0
  5. package/dist/bin/cli.cjs.map +1 -0
  6. package/dist/bin/cli.d.cts +1 -0
  7. package/dist/bin/cli.d.mts +1 -0
  8. package/dist/bin/cli.mjs +3 -0
  9. package/dist/bin/cli.mjs.map +1 -0
  10. package/dist/bin/doomRunner.cjs +3 -0
  11. package/dist/bin/doomRunner.cjs.map +1 -0
  12. package/dist/bin/doomRunner.d.cts +1 -0
  13. package/dist/bin/doomRunner.d.mts +1 -0
  14. package/dist/bin/doomRunner.mjs +3 -0
  15. package/dist/bin/doomRunner.mjs.map +1 -0
  16. package/dist/cli/cliApp.cjs +2 -0
  17. package/dist/cli/cliApp.cjs.map +1 -0
  18. package/dist/cli/cliApp.d.cts +50 -0
  19. package/dist/cli/cliApp.d.cts.map +1 -0
  20. package/dist/cli/cliApp.d.mts +50 -0
  21. package/dist/cli/cliApp.d.mts.map +1 -0
  22. package/dist/cli/cliApp.mjs +2 -0
  23. package/dist/cli/cliApp.mjs.map +1 -0
  24. package/dist/cli/compatibilityOptions.cjs +2 -0
  25. package/dist/cli/compatibilityOptions.cjs.map +1 -0
  26. package/dist/cli/compatibilityOptions.d.cts +15 -0
  27. package/dist/cli/compatibilityOptions.d.cts.map +1 -0
  28. package/dist/cli/compatibilityOptions.d.mts +15 -0
  29. package/dist/cli/compatibilityOptions.d.mts.map +1 -0
  30. package/dist/cli/compatibilityOptions.mjs +2 -0
  31. package/dist/cli/compatibilityOptions.mjs.map +1 -0
  32. package/dist/cli/help.cjs +61 -0
  33. package/dist/cli/help.cjs.map +1 -0
  34. package/dist/cli/help.d.cts +6 -0
  35. package/dist/cli/help.d.cts.map +1 -0
  36. package/dist/cli/help.d.mts +6 -0
  37. package/dist/cli/help.d.mts.map +1 -0
  38. package/dist/cli/help.mjs +61 -0
  39. package/dist/cli/help.mjs.map +1 -0
  40. package/dist/cli/index.cjs +1 -0
  41. package/dist/cli/index.d.cts +5 -0
  42. package/dist/cli/index.d.mts +5 -0
  43. package/dist/cli/index.mjs +1 -0
  44. package/dist/cli/matrixOptions.cjs +2 -0
  45. package/dist/cli/matrixOptions.cjs.map +1 -0
  46. package/dist/cli/matrixOptions.d.cts +99 -0
  47. package/dist/cli/matrixOptions.d.cts.map +1 -0
  48. package/dist/cli/matrixOptions.d.mts +99 -0
  49. package/dist/cli/matrixOptions.d.mts.map +1 -0
  50. package/dist/cli/matrixOptions.mjs +2 -0
  51. package/dist/cli/matrixOptions.mjs.map +1 -0
  52. package/dist/cli/options.cjs +2 -0
  53. package/dist/cli/options.cjs.map +1 -0
  54. package/dist/cli/options.d.cts +7 -0
  55. package/dist/cli/options.d.cts.map +1 -0
  56. package/dist/cli/options.d.mts +7 -0
  57. package/dist/cli/options.d.mts.map +1 -0
  58. package/dist/cli/options.mjs +2 -0
  59. package/dist/cli/options.mjs.map +1 -0
  60. package/dist/commands/baseCommand.cjs +2 -0
  61. package/dist/commands/baseCommand.cjs.map +1 -0
  62. package/dist/commands/baseCommand.d.cts +36 -0
  63. package/dist/commands/baseCommand.d.cts.map +1 -0
  64. package/dist/commands/baseCommand.d.mts +36 -0
  65. package/dist/commands/baseCommand.d.mts.map +1 -0
  66. package/dist/commands/baseCommand.mjs +2 -0
  67. package/dist/commands/baseCommand.mjs.map +1 -0
  68. package/dist/commands/compatibilityCommand.cjs +2 -0
  69. package/dist/commands/compatibilityCommand.cjs.map +1 -0
  70. package/dist/commands/compatibilityCommand.d.cts +14 -0
  71. package/dist/commands/compatibilityCommand.d.cts.map +1 -0
  72. package/dist/commands/compatibilityCommand.d.mts +14 -0
  73. package/dist/commands/compatibilityCommand.d.mts.map +1 -0
  74. package/dist/commands/compatibilityCommand.mjs +2 -0
  75. package/dist/commands/compatibilityCommand.mjs.map +1 -0
  76. package/dist/commands/emitMcpCommand.cjs +2 -0
  77. package/dist/commands/emitMcpCommand.cjs.map +1 -0
  78. package/dist/commands/emitMcpCommand.d.cts +19 -0
  79. package/dist/commands/emitMcpCommand.d.cts.map +1 -0
  80. package/dist/commands/emitMcpCommand.d.mts +19 -0
  81. package/dist/commands/emitMcpCommand.d.mts.map +1 -0
  82. package/dist/commands/emitMcpCommand.mjs +2 -0
  83. package/dist/commands/emitMcpCommand.mjs.map +1 -0
  84. package/dist/commands/explainCommand.cjs +21 -0
  85. package/dist/commands/explainCommand.cjs.map +1 -0
  86. package/dist/commands/explainCommand.d.cts +63 -0
  87. package/dist/commands/explainCommand.d.cts.map +1 -0
  88. package/dist/commands/explainCommand.d.mts +63 -0
  89. package/dist/commands/explainCommand.d.mts.map +1 -0
  90. package/dist/commands/explainCommand.mjs +21 -0
  91. package/dist/commands/explainCommand.mjs.map +1 -0
  92. package/dist/commands/index.cjs +1 -0
  93. package/dist/commands/index.d.cts +8 -0
  94. package/dist/commands/index.d.mts +8 -0
  95. package/dist/commands/index.mjs +1 -0
  96. package/dist/commands/initCommand.cjs +3 -0
  97. package/dist/commands/initCommand.cjs.map +1 -0
  98. package/dist/commands/initCommand.d.cts +11 -0
  99. package/dist/commands/initCommand.d.cts.map +1 -0
  100. package/dist/commands/initCommand.d.mts +11 -0
  101. package/dist/commands/initCommand.d.mts.map +1 -0
  102. package/dist/commands/initCommand.mjs +3 -0
  103. package/dist/commands/initCommand.mjs.map +1 -0
  104. package/dist/commands/launchCommand.cjs +2 -0
  105. package/dist/commands/launchCommand.cjs.map +1 -0
  106. package/dist/commands/launchCommand.d.cts +31 -0
  107. package/dist/commands/launchCommand.d.cts.map +1 -0
  108. package/dist/commands/launchCommand.d.mts +31 -0
  109. package/dist/commands/launchCommand.d.mts.map +1 -0
  110. package/dist/commands/launchCommand.mjs +2 -0
  111. package/dist/commands/launchCommand.mjs.map +1 -0
  112. package/dist/commands/syncCommand.cjs +6 -0
  113. package/dist/commands/syncCommand.cjs.map +1 -0
  114. package/dist/commands/syncCommand.d.cts +37 -0
  115. package/dist/commands/syncCommand.d.cts.map +1 -0
  116. package/dist/commands/syncCommand.d.mts +37 -0
  117. package/dist/commands/syncCommand.d.mts.map +1 -0
  118. package/dist/commands/syncCommand.mjs +6 -0
  119. package/dist/commands/syncCommand.mjs.map +1 -0
  120. package/dist/config/harnessState.cjs +2 -0
  121. package/dist/config/harnessState.cjs.map +1 -0
  122. package/dist/config/harnessState.d.cts +24 -0
  123. package/dist/config/harnessState.d.cts.map +1 -0
  124. package/dist/config/harnessState.d.mts +24 -0
  125. package/dist/config/harnessState.d.mts.map +1 -0
  126. package/dist/config/harnessState.mjs +2 -0
  127. package/dist/config/harnessState.mjs.map +1 -0
  128. package/dist/config/index.cjs +1 -0
  129. package/dist/config/index.d.cts +7 -0
  130. package/dist/config/index.d.mts +7 -0
  131. package/dist/config/index.mjs +1 -0
  132. package/dist/config/projectTrust.cjs +2 -0
  133. package/dist/config/projectTrust.cjs.map +1 -0
  134. package/dist/config/projectTrust.d.cts +14 -0
  135. package/dist/config/projectTrust.d.cts.map +1 -0
  136. package/dist/config/projectTrust.d.mts +14 -0
  137. package/dist/config/projectTrust.d.mts.map +1 -0
  138. package/dist/config/projectTrust.mjs +2 -0
  139. package/dist/config/projectTrust.mjs.map +1 -0
  140. package/dist/entries/autoStop.cjs +2 -0
  141. package/dist/entries/autoStop.cjs.map +1 -0
  142. package/dist/entries/autoStop.d.cts +6 -0
  143. package/dist/entries/autoStop.d.cts.map +1 -0
  144. package/dist/entries/autoStop.d.mts +7 -0
  145. package/dist/entries/autoStop.d.mts.map +1 -0
  146. package/dist/entries/autoStop.mjs +2 -0
  147. package/dist/entries/autoStop.mjs.map +1 -0
  148. package/dist/entries/components/matrixPicker.cjs +2 -0
  149. package/dist/entries/components/matrixPicker.cjs.map +1 -0
  150. package/dist/entries/components/matrixPicker.d.cts +55 -0
  151. package/dist/entries/components/matrixPicker.d.cts.map +1 -0
  152. package/dist/entries/components/matrixPicker.d.mts +55 -0
  153. package/dist/entries/components/matrixPicker.d.mts.map +1 -0
  154. package/dist/entries/components/matrixPicker.mjs +2 -0
  155. package/dist/entries/components/matrixPicker.mjs.map +1 -0
  156. package/dist/entries/components/skillsOverlay.cjs +4 -0
  157. package/dist/entries/components/skillsOverlay.cjs.map +1 -0
  158. package/dist/entries/components/skillsOverlay.d.cts +58 -0
  159. package/dist/entries/components/skillsOverlay.d.cts.map +1 -0
  160. package/dist/entries/components/skillsOverlay.d.mts +58 -0
  161. package/dist/entries/components/skillsOverlay.d.mts.map +1 -0
  162. package/dist/entries/components/skillsOverlay.mjs +4 -0
  163. package/dist/entries/components/skillsOverlay.mjs.map +1 -0
  164. package/dist/entries/domains.cjs +2 -0
  165. package/dist/entries/domains.cjs.map +1 -0
  166. package/dist/entries/domains.d.cts +10 -0
  167. package/dist/entries/domains.d.cts.map +1 -0
  168. package/dist/entries/domains.d.mts +10 -0
  169. package/dist/entries/domains.d.mts.map +1 -0
  170. package/dist/entries/domains.mjs +2 -0
  171. package/dist/entries/domains.mjs.map +1 -0
  172. package/dist/entries/doom.cjs +2 -0
  173. package/dist/entries/doom.cjs.map +1 -0
  174. package/dist/entries/doom.d.cts +13 -0
  175. package/dist/entries/doom.d.cts.map +1 -0
  176. package/dist/entries/doom.d.mts +14 -0
  177. package/dist/entries/doom.d.mts.map +1 -0
  178. package/dist/entries/doom.mjs +2 -0
  179. package/dist/entries/doom.mjs.map +1 -0
  180. package/dist/entries/effort.cjs +2 -0
  181. package/dist/entries/effort.cjs.map +1 -0
  182. package/dist/entries/effort.d.cts +10 -0
  183. package/dist/entries/effort.d.cts.map +1 -0
  184. package/dist/entries/effort.d.mts +10 -0
  185. package/dist/entries/effort.d.mts.map +1 -0
  186. package/dist/entries/effort.mjs +2 -0
  187. package/dist/entries/effort.mjs.map +1 -0
  188. package/dist/entries/majorMode.cjs +6 -0
  189. package/dist/entries/majorMode.cjs.map +1 -0
  190. package/dist/entries/majorMode.d.cts +29 -0
  191. package/dist/entries/majorMode.d.cts.map +1 -0
  192. package/dist/entries/majorMode.d.mts +29 -0
  193. package/dist/entries/majorMode.d.mts.map +1 -0
  194. package/dist/entries/majorMode.mjs +6 -0
  195. package/dist/entries/majorMode.mjs.map +1 -0
  196. package/dist/entries/notifications.cjs +2 -0
  197. package/dist/entries/notifications.cjs.map +1 -0
  198. package/dist/entries/notifications.d.cts +13 -0
  199. package/dist/entries/notifications.d.cts.map +1 -0
  200. package/dist/entries/notifications.d.mts +13 -0
  201. package/dist/entries/notifications.d.mts.map +1 -0
  202. package/dist/entries/notifications.mjs +2 -0
  203. package/dist/entries/notifications.mjs.map +1 -0
  204. package/dist/entries/ollamaProvider.cjs +2 -0
  205. package/dist/entries/ollamaProvider.cjs.map +1 -0
  206. package/dist/entries/ollamaProvider.d.cts +6 -0
  207. package/dist/entries/ollamaProvider.d.cts.map +1 -0
  208. package/dist/entries/ollamaProvider.d.mts +7 -0
  209. package/dist/entries/ollamaProvider.d.mts.map +1 -0
  210. package/dist/entries/ollamaProvider.mjs +2 -0
  211. package/dist/entries/ollamaProvider.mjs.map +1 -0
  212. package/dist/entries/persona.cjs +2 -0
  213. package/dist/entries/persona.cjs.map +1 -0
  214. package/dist/entries/persona.d.cts +14 -0
  215. package/dist/entries/persona.d.cts.map +1 -0
  216. package/dist/entries/persona.d.mts +15 -0
  217. package/dist/entries/persona.d.mts.map +1 -0
  218. package/dist/entries/persona.mjs +2 -0
  219. package/dist/entries/persona.mjs.map +1 -0
  220. package/dist/entries/profile.cjs +3 -0
  221. package/dist/entries/profile.cjs.map +1 -0
  222. package/dist/entries/profile.d.cts +16 -0
  223. package/dist/entries/profile.d.cts.map +1 -0
  224. package/dist/entries/profile.d.mts +16 -0
  225. package/dist/entries/profile.d.mts.map +1 -0
  226. package/dist/entries/profile.mjs +3 -0
  227. package/dist/entries/profile.mjs.map +1 -0
  228. package/dist/entries/repositoryHooks.cjs +9 -0
  229. package/dist/entries/repositoryHooks.cjs.map +1 -0
  230. package/dist/entries/repositoryHooks.d.cts +7 -0
  231. package/dist/entries/repositoryHooks.d.cts.map +1 -0
  232. package/dist/entries/repositoryHooks.d.mts +8 -0
  233. package/dist/entries/repositoryHooks.d.mts.map +1 -0
  234. package/dist/entries/repositoryHooks.mjs +9 -0
  235. package/dist/entries/repositoryHooks.mjs.map +1 -0
  236. package/dist/entries/skills.cjs +2 -0
  237. package/dist/entries/skills.cjs.map +1 -0
  238. package/dist/entries/skills.d.cts +34 -0
  239. package/dist/entries/skills.d.cts.map +1 -0
  240. package/dist/entries/skills.d.mts +34 -0
  241. package/dist/entries/skills.d.mts.map +1 -0
  242. package/dist/entries/skills.mjs +2 -0
  243. package/dist/entries/skills.mjs.map +1 -0
  244. package/dist/entries/styleSystem.cjs +4 -0
  245. package/dist/entries/styleSystem.cjs.map +1 -0
  246. package/dist/entries/styleSystem.d.cts +32 -0
  247. package/dist/entries/styleSystem.d.cts.map +1 -0
  248. package/dist/entries/styleSystem.d.mts +32 -0
  249. package/dist/entries/styleSystem.d.mts.map +1 -0
  250. package/dist/entries/styleSystem.mjs +4 -0
  251. package/dist/entries/styleSystem.mjs.map +1 -0
  252. package/dist/index.cjs +1 -0
  253. package/dist/index.d.cts +34 -0
  254. package/dist/index.d.mts +34 -0
  255. package/dist/index.mjs +1 -0
  256. package/dist/interfaces/compatibility.d.cts +23 -0
  257. package/dist/interfaces/compatibility.d.cts.map +1 -0
  258. package/dist/interfaces/compatibility.d.mts +23 -0
  259. package/dist/interfaces/compatibility.d.mts.map +1 -0
  260. package/dist/interfaces/harness.d.cts +83 -0
  261. package/dist/interfaces/harness.d.cts.map +1 -0
  262. package/dist/interfaces/harness.d.mts +83 -0
  263. package/dist/interfaces/harness.d.mts.map +1 -0
  264. package/dist/logSinkTelemetry.cjs +2 -0
  265. package/dist/logSinkTelemetry.cjs.map +1 -0
  266. package/dist/logSinkTelemetry.d.cts +49 -0
  267. package/dist/logSinkTelemetry.d.cts.map +1 -0
  268. package/dist/logSinkTelemetry.d.mts +49 -0
  269. package/dist/logSinkTelemetry.d.mts.map +1 -0
  270. package/dist/logSinkTelemetry.mjs +2 -0
  271. package/dist/logSinkTelemetry.mjs.map +1 -0
  272. package/dist/node_modules/.pnpm/@juicesharp_rpiv-ask-user-question@2.2.0_@earendil-works_pi-coding-agent@0.84.1_support_6ba1249e24a50b62791c1c91c9f324c6/node_modules/@juicesharp/rpiv-ask-user-question/events.cjs +2 -0
  273. package/dist/node_modules/.pnpm/@juicesharp_rpiv-ask-user-question@2.2.0_@earendil-works_pi-coding-agent@0.84.1_support_6ba1249e24a50b62791c1c91c9f324c6/node_modules/@juicesharp/rpiv-ask-user-question/events.cjs.map +1 -0
  274. package/dist/node_modules/.pnpm/@juicesharp_rpiv-ask-user-question@2.2.0_@earendil-works_pi-coding-agent@0.84.1_support_6ba1249e24a50b62791c1c91c9f324c6/node_modules/@juicesharp/rpiv-ask-user-question/events.mjs +2 -0
  275. package/dist/node_modules/.pnpm/@juicesharp_rpiv-ask-user-question@2.2.0_@earendil-works_pi-coding-agent@0.84.1_support_6ba1249e24a50b62791c1c91c9f324c6/node_modules/@juicesharp/rpiv-ask-user-question/events.mjs.map +1 -0
  276. package/dist/services/compatibility/antigravity.cjs +2 -0
  277. package/dist/services/compatibility/antigravity.cjs.map +1 -0
  278. package/dist/services/compatibility/antigravity.d.cts +11 -0
  279. package/dist/services/compatibility/antigravity.d.cts.map +1 -0
  280. package/dist/services/compatibility/antigravity.d.mts +11 -0
  281. package/dist/services/compatibility/antigravity.d.mts.map +1 -0
  282. package/dist/services/compatibility/antigravity.mjs +2 -0
  283. package/dist/services/compatibility/antigravity.mjs.map +1 -0
  284. package/dist/services/compatibility/codex.cjs +2 -0
  285. package/dist/services/compatibility/codex.cjs.map +1 -0
  286. package/dist/services/compatibility/codex.d.cts +19 -0
  287. package/dist/services/compatibility/codex.d.cts.map +1 -0
  288. package/dist/services/compatibility/codex.d.mts +19 -0
  289. package/dist/services/compatibility/codex.d.mts.map +1 -0
  290. package/dist/services/compatibility/codex.mjs +2 -0
  291. package/dist/services/compatibility/codex.mjs.map +1 -0
  292. package/dist/services/compatibility/index.cjs +2 -0
  293. package/dist/services/compatibility/index.cjs.map +1 -0
  294. package/dist/services/compatibility/index.d.cts +16 -0
  295. package/dist/services/compatibility/index.d.cts.map +1 -0
  296. package/dist/services/compatibility/index.d.mts +16 -0
  297. package/dist/services/compatibility/index.d.mts.map +1 -0
  298. package/dist/services/compatibility/index.mjs +2 -0
  299. package/dist/services/compatibility/index.mjs.map +1 -0
  300. package/dist/services/compatibility/process.cjs +2 -0
  301. package/dist/services/compatibility/process.cjs.map +1 -0
  302. package/dist/services/compatibility/process.d.cts +35 -0
  303. package/dist/services/compatibility/process.d.cts.map +1 -0
  304. package/dist/services/compatibility/process.d.mts +35 -0
  305. package/dist/services/compatibility/process.d.mts.map +1 -0
  306. package/dist/services/compatibility/process.mjs +2 -0
  307. package/dist/services/compatibility/process.mjs.map +1 -0
  308. package/dist/services/compatibility/shared.cjs +2 -0
  309. package/dist/services/compatibility/shared.cjs.map +1 -0
  310. package/dist/services/compatibility/shared.d.cts +33 -0
  311. package/dist/services/compatibility/shared.d.cts.map +1 -0
  312. package/dist/services/compatibility/shared.d.mts +33 -0
  313. package/dist/services/compatibility/shared.d.mts.map +1 -0
  314. package/dist/services/compatibility/shared.mjs +2 -0
  315. package/dist/services/compatibility/shared.mjs.map +1 -0
  316. package/dist/services/compatibilityContext.cjs +2 -0
  317. package/dist/services/compatibilityContext.cjs.map +1 -0
  318. package/dist/services/compatibilityContext.d.cts +23 -0
  319. package/dist/services/compatibilityContext.d.cts.map +1 -0
  320. package/dist/services/compatibilityContext.d.mts +23 -0
  321. package/dist/services/compatibilityContext.d.mts.map +1 -0
  322. package/dist/services/compatibilityContext.mjs +2 -0
  323. package/dist/services/compatibilityContext.mjs.map +1 -0
  324. package/dist/services/composer.cjs +2 -0
  325. package/dist/services/composer.cjs.map +1 -0
  326. package/dist/services/composer.d.cts +133 -0
  327. package/dist/services/composer.d.cts.map +1 -0
  328. package/dist/services/composer.d.mts +133 -0
  329. package/dist/services/composer.d.mts.map +1 -0
  330. package/dist/services/composer.mjs +2 -0
  331. package/dist/services/composer.mjs.map +1 -0
  332. package/dist/services/extensionAssembler.cjs +2 -0
  333. package/dist/services/extensionAssembler.cjs.map +1 -0
  334. package/dist/services/extensionAssembler.d.cts +72 -0
  335. package/dist/services/extensionAssembler.d.cts.map +1 -0
  336. package/dist/services/extensionAssembler.d.mts +72 -0
  337. package/dist/services/extensionAssembler.d.mts.map +1 -0
  338. package/dist/services/extensionAssembler.mjs +2 -0
  339. package/dist/services/extensionAssembler.mjs.map +1 -0
  340. package/dist/services/extensionCompiler.cjs +2 -0
  341. package/dist/services/extensionCompiler.cjs.map +1 -0
  342. package/dist/services/extensionCompiler.mjs +2 -0
  343. package/dist/services/extensionCompiler.mjs.map +1 -0
  344. package/dist/services/harnessContext.cjs +2 -0
  345. package/dist/services/harnessContext.cjs.map +1 -0
  346. package/dist/services/harnessContext.d.cts +61 -0
  347. package/dist/services/harnessContext.d.cts.map +1 -0
  348. package/dist/services/harnessContext.d.mts +61 -0
  349. package/dist/services/harnessContext.d.mts.map +1 -0
  350. package/dist/services/harnessContext.mjs +2 -0
  351. package/dist/services/harnessContext.mjs.map +1 -0
  352. package/dist/services/index.cjs +1 -0
  353. package/dist/services/index.d.cts +14 -0
  354. package/dist/services/index.d.mts +14 -0
  355. package/dist/services/index.mjs +1 -0
  356. package/dist/services/matrixSwitcher.cjs +2 -0
  357. package/dist/services/matrixSwitcher.cjs.map +1 -0
  358. package/dist/services/matrixSwitcher.d.cts +46 -0
  359. package/dist/services/matrixSwitcher.d.cts.map +1 -0
  360. package/dist/services/matrixSwitcher.d.mts +46 -0
  361. package/dist/services/matrixSwitcher.d.mts.map +1 -0
  362. package/dist/services/matrixSwitcher.mjs +2 -0
  363. package/dist/services/matrixSwitcher.mjs.map +1 -0
  364. package/dist/services/mcpFilter.cjs +3 -0
  365. package/dist/services/mcpFilter.cjs.map +1 -0
  366. package/dist/services/mcpFilter.d.cts +40 -0
  367. package/dist/services/mcpFilter.d.cts.map +1 -0
  368. package/dist/services/mcpFilter.d.mts +40 -0
  369. package/dist/services/mcpFilter.d.mts.map +1 -0
  370. package/dist/services/mcpFilter.mjs +3 -0
  371. package/dist/services/mcpFilter.mjs.map +1 -0
  372. package/dist/services/piSettings.cjs +2 -0
  373. package/dist/services/piSettings.cjs.map +1 -0
  374. package/dist/services/piSettings.d.cts +24 -0
  375. package/dist/services/piSettings.d.cts.map +1 -0
  376. package/dist/services/piSettings.d.mts +24 -0
  377. package/dist/services/piSettings.d.mts.map +1 -0
  378. package/dist/services/piSettings.mjs +2 -0
  379. package/dist/services/piSettings.mjs.map +1 -0
  380. package/dist/services/resourceCollector.cjs +3 -0
  381. package/dist/services/resourceCollector.cjs.map +1 -0
  382. package/dist/services/resourceCollector.d.cts +16 -0
  383. package/dist/services/resourceCollector.d.cts.map +1 -0
  384. package/dist/services/resourceCollector.d.mts +16 -0
  385. package/dist/services/resourceCollector.d.mts.map +1 -0
  386. package/dist/services/resourceCollector.mjs +3 -0
  387. package/dist/services/resourceCollector.mjs.map +1 -0
  388. package/dist/services/skillCatalog.cjs +2 -0
  389. package/dist/services/skillCatalog.cjs.map +1 -0
  390. package/dist/services/skillCatalog.d.cts +56 -0
  391. package/dist/services/skillCatalog.d.cts.map +1 -0
  392. package/dist/services/skillCatalog.d.mts +56 -0
  393. package/dist/services/skillCatalog.d.mts.map +1 -0
  394. package/dist/services/skillCatalog.mjs +2 -0
  395. package/dist/services/skillCatalog.mjs.map +1 -0
  396. package/dist/services/skillSources.cjs +2 -0
  397. package/dist/services/skillSources.cjs.map +1 -0
  398. package/dist/services/skillSources.d.cts +24 -0
  399. package/dist/services/skillSources.d.cts.map +1 -0
  400. package/dist/services/skillSources.d.mts +24 -0
  401. package/dist/services/skillSources.d.mts.map +1 -0
  402. package/dist/services/skillSources.mjs +2 -0
  403. package/dist/services/skillSources.mjs.map +1 -0
  404. package/dist/services/syncState.cjs +2 -0
  405. package/dist/services/syncState.cjs.map +1 -0
  406. package/dist/services/syncState.d.cts +130 -0
  407. package/dist/services/syncState.d.cts.map +1 -0
  408. package/dist/services/syncState.d.mts +130 -0
  409. package/dist/services/syncState.d.mts.map +1 -0
  410. package/dist/services/syncState.mjs +2 -0
  411. package/dist/services/syncState.mjs.map +1 -0
  412. package/dist/utils/index.cjs +1 -0
  413. package/dist/utils/index.d.cts +5 -0
  414. package/dist/utils/index.d.mts +5 -0
  415. package/dist/utils/index.mjs +1 -0
  416. package/dist/utils/json.cjs +2 -0
  417. package/dist/utils/json.cjs.map +1 -0
  418. package/dist/utils/json.d.cts +23 -0
  419. package/dist/utils/json.d.cts.map +1 -0
  420. package/dist/utils/json.d.mts +23 -0
  421. package/dist/utils/json.d.mts.map +1 -0
  422. package/dist/utils/json.mjs +2 -0
  423. package/dist/utils/json.mjs.map +1 -0
  424. package/dist/utils/moduleResolution.cjs +2 -0
  425. package/dist/utils/moduleResolution.cjs.map +1 -0
  426. package/dist/utils/moduleResolution.d.cts +56 -0
  427. package/dist/utils/moduleResolution.d.cts.map +1 -0
  428. package/dist/utils/moduleResolution.d.mts +56 -0
  429. package/dist/utils/moduleResolution.d.mts.map +1 -0
  430. package/dist/utils/moduleResolution.mjs +2 -0
  431. package/dist/utils/moduleResolution.mjs.map +1 -0
  432. package/dist/utils/repository.cjs +2 -0
  433. package/dist/utils/repository.cjs.map +1 -0
  434. package/dist/utils/repository.d.cts +21 -0
  435. package/dist/utils/repository.d.cts.map +1 -0
  436. package/dist/utils/repository.d.mts +21 -0
  437. package/dist/utils/repository.d.mts.map +1 -0
  438. package/dist/utils/repository.mjs +2 -0
  439. package/dist/utils/repository.mjs.map +1 -0
  440. package/dist/utils/toolNames.cjs +2 -0
  441. package/dist/utils/toolNames.cjs.map +1 -0
  442. package/dist/utils/toolNames.d.cts +25 -0
  443. package/dist/utils/toolNames.d.cts.map +1 -0
  444. package/dist/utils/toolNames.d.mts +25 -0
  445. package/dist/utils/toolNames.d.mts.map +1 -0
  446. package/dist/utils/toolNames.mjs +2 -0
  447. package/dist/utils/toolNames.mjs.map +1 -0
  448. package/package.json +369 -0
@@ -0,0 +1,24 @@
1
+ import { ExtensionAPI } from "@earendil-works/pi-coding-agent";
2
+
3
+ //#region src/services/skillSources.d.ts
4
+ interface SkillSourceEntry {
5
+ source: string;
6
+ directories: string[];
7
+ }
8
+ interface SkillSourceRegistry {
9
+ /** Registered sources, sorted by name so the catalogue order is stable. */
10
+ list(): readonly SkillSourceEntry[];
11
+ dispose(): void;
12
+ }
13
+ /**
14
+ * Hosts the registry extensions publish their skill directories to.
15
+ *
16
+ * Contributions are keyed by source rather than by handle, so a reload that
17
+ * re-registers the same package replaces its entry instead of doubling it. The
18
+ * handle is still tracked, because an unregistration that arrives after a
19
+ * replacement must not take the newer entry down with it.
20
+ */
21
+ declare function provideSkillSources(pi: Pick<ExtensionAPI, 'events' | 'on'>): SkillSourceRegistry;
22
+ //#endregion
23
+ export { SkillSourceEntry, SkillSourceRegistry, provideSkillSources };
24
+ //# sourceMappingURL=skillSources.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"skillSources.d.mts","names":[],"sources":["../../src/services/skillSources.ts"],"mappings":";;;UAWiB,gBAAA;EACf,MAAA;EACA,WAAA;AAAA;AAAA,UAGe,mBAAA;EAJf;EAMA,IAAA,aAAiB,gBAAA;EACjB,OAAA;AAAA;;;;;;;;AAWF;iBAAgB,mBAAA,CAAoB,EAAA,EAAI,IAAA,CAAK,YAAA,qBAAiC,mBAAA"}
@@ -0,0 +1,2 @@
1
+ import e from"node:path";import t from"node:fs";import{SkillSourceRegistrationProtocol as n,SkillSourceUnregistrationProtocol as r}from"@agimon-ai/doompi-extension-contracts/skills";import{createProtocolRuntime as i}from"@agimon-ai/doompi-extension-contracts/protocol";function a(a){let o=new Map,s=new Map,c=0,l,u=()=>{l?.(),l=void 0};return a.on(`session_start`,(d,f)=>{u(),o.clear(),s.clear();let p=i({bus:a.events,source:`@agimon-ai/doompi`,sessionId:f.sessionManager.getSessionId()}),m=p.provide(n,({contribution:n})=>{let r=n.directories.filter(n=>e.isAbsolute(n)&&t.existsSync(n)),i=`skill-source-${++c}`,a=o.get(n.source);return a&&s.delete(a.handleId),o.set(n.source,{handleId:i,directories:r}),s.set(i,n.source),{handleId:i}}),h=p.provide(r,({handleId:e})=>{let t=s.get(e);return t?(s.delete(e),o.get(t)?.handleId===e&&o.delete(t),{removed:!0}):{removed:!1}});l=()=>{m(),h()}}),a.on(`session_shutdown`,()=>{u(),o.clear(),s.clear()}),{list(){return[...o.entries()].map(([e,t])=>({source:e,directories:[...t.directories]})).sort((e,t)=>e.source.localeCompare(t.source))},dispose(){u(),o.clear(),s.clear()}}}export{a as provideSkillSources};
2
+ //# sourceMappingURL=skillSources.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"skillSources.mjs","names":[],"sources":["../../src/services/skillSources.ts"],"sourcesContent":["import fs from 'node:fs';\nimport path from 'node:path';\nimport {\n SkillSourceRegistrationProtocol,\n SkillSourceUnregistrationProtocol,\n} from '@agimon-ai/doompi-extension-contracts/skills';\nimport { createProtocolRuntime } from '@agimon-ai/doompi-extension-contracts/protocol';\nimport type { ExtensionAPI } from '@earendil-works/pi-coding-agent';\n\nconst HOST_SOURCE = '@agimon-ai/doompi';\n\nexport interface SkillSourceEntry {\n source: string;\n directories: string[];\n}\n\nexport interface SkillSourceRegistry {\n /** Registered sources, sorted by name so the catalogue order is stable. */\n list(): readonly SkillSourceEntry[];\n dispose(): void;\n}\n\n/**\n * Hosts the registry extensions publish their skill directories to.\n *\n * Contributions are keyed by source rather than by handle, so a reload that\n * re-registers the same package replaces its entry instead of doubling it. The\n * handle is still tracked, because an unregistration that arrives after a\n * replacement must not take the newer entry down with it.\n */\nexport function provideSkillSources(pi: Pick<ExtensionAPI, 'events' | 'on'>): SkillSourceRegistry {\n const bySource = new Map<string, { handleId: string; directories: string[] }>();\n const sourceByHandle = new Map<string, string>();\n let handleSequence = 0;\n let disposeProviders: (() => void) | undefined;\n\n const stopProviders = (): void => {\n disposeProviders?.();\n disposeProviders = undefined;\n };\n\n pi.on('session_start', (_event, ctx) => {\n stopProviders();\n bySource.clear();\n sourceByHandle.clear();\n const runtime = createProtocolRuntime({\n bus: pi.events,\n source: HOST_SOURCE,\n sessionId: ctx.sessionManager.getSessionId(),\n });\n\n const stopRegister = runtime.provide(SkillSourceRegistrationProtocol, ({ contribution }) => {\n // A path that does not resolve is dropped rather than rejected: the\n // contributor guards on existence too, and failing its registration would\n // surface as a session-start error for a directory nobody can browse.\n const directories = contribution.directories.filter(\n (directory) => path.isAbsolute(directory) && fs.existsSync(directory),\n );\n const handleId = `skill-source-${++handleSequence}`;\n const previous = bySource.get(contribution.source);\n if (previous) sourceByHandle.delete(previous.handleId);\n bySource.set(contribution.source, { handleId, directories });\n sourceByHandle.set(handleId, contribution.source);\n return { handleId };\n });\n\n const stopUnregister = runtime.provide(SkillSourceUnregistrationProtocol, ({ handleId }) => {\n const source = sourceByHandle.get(handleId);\n if (!source) return { removed: false };\n sourceByHandle.delete(handleId);\n // Guarded so a late unregistration cannot remove the replacement that\n // already took this source's slot.\n if (bySource.get(source)?.handleId === handleId) bySource.delete(source);\n return { removed: true };\n });\n\n disposeProviders = () => {\n stopRegister();\n stopUnregister();\n };\n });\n\n pi.on('session_shutdown', () => {\n stopProviders();\n bySource.clear();\n sourceByHandle.clear();\n });\n\n return {\n list() {\n return [...bySource.entries()]\n .map(([source, entry]) => ({ source, directories: [...entry.directories] }))\n .sort((left, right) => left.source.localeCompare(right.source));\n },\n dispose() {\n stopProviders();\n bySource.clear();\n sourceByHandle.clear();\n },\n };\n}\n"],"mappings":"6QA8BA,SAAgB,EAAoB,EAA8D,CAChG,IAAM,EAAW,IAAI,IACf,EAAiB,IAAI,IACvB,EAAiB,EACjB,EAEE,MAA4B,CAChC,KAAoB,CACpB,EAAmB,IAAA,IAkDrB,OA/CA,EAAG,GAAG,iBAAkB,EAAQ,IAAQ,CACtC,GAAe,CACf,EAAS,OAAO,CAChB,EAAe,OAAO,CACtB,IAAM,EAAU,EAAsB,CACpC,IAAK,EAAG,OACR,OAAQ,oBACR,UAAW,EAAI,eAAe,cAAc,CAC7C,CAAC,CAEI,EAAe,EAAQ,QAAQ,GAAkC,CAAE,kBAAmB,CAI1F,IAAM,EAAc,EAAa,YAAY,OAC1C,GAAc,EAAK,WAAW,EAAU,EAAI,EAAG,WAAW,EAAU,CACtE,CACK,EAAW,gBAAgB,EAAE,IAC7B,EAAW,EAAS,IAAI,EAAa,OAAO,CAIlD,OAHI,GAAU,EAAe,OAAO,EAAS,SAAS,CACtD,EAAS,IAAI,EAAa,OAAQ,CAAE,WAAU,cAAa,CAAC,CAC5D,EAAe,IAAI,EAAU,EAAa,OAAO,CAC1C,CAAE,WAAU,EACnB,CAEI,EAAiB,EAAQ,QAAQ,GAAoC,CAAE,cAAe,CAC1F,IAAM,EAAS,EAAe,IAAI,EAAS,CAM3C,OALK,GACL,EAAe,OAAO,EAAS,CAG3B,EAAS,IAAI,EAAO,EAAE,WAAa,GAAU,EAAS,OAAO,EAAO,CACjE,CAAE,QAAS,GAAM,EALJ,CAAE,QAAS,GAAO,EAMtC,CAEF,MAAyB,CACvB,GAAc,CACd,GAAgB,GAElB,CAEF,EAAG,GAAG,uBAA0B,CAC9B,GAAe,CACf,EAAS,OAAO,CAChB,EAAe,OAAO,EACtB,CAEK,CACL,MAAO,CACL,MAAO,CAAC,GAAG,EAAS,SAAS,CAAC,CAC3B,KAAK,CAAC,EAAQ,MAAY,CAAE,SAAQ,YAAa,CAAC,GAAG,EAAM,YAAY,CAAE,EAAE,CAC3E,MAAM,EAAM,IAAU,EAAK,OAAO,cAAc,EAAM,OAAO,CAAC,EAEnE,SAAU,CACR,GAAe,CACf,EAAS,OAAO,CAChB,EAAe,OAAO,EAEzB"}
@@ -0,0 +1,2 @@
1
+ Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require(`../_virtual/_rolldown/runtime.cjs`),t=require(`../utils/json.cjs`);require(`../utils/index.cjs`);const n=require(`./extensionAssembler.cjs`);let r=require(`node:path`);r=e.__toESM(r,1);let i=require(`node:fs`);i=e.__toESM(i,1);let a=require(`node:os`);a=e.__toESM(a,1);let o=require(`node:crypto`);o=e.__toESM(o,1);const s=`.doom`,c=[`config.yaml`,`modes.yaml`,`domains.yaml`,`profiles.yaml`,`hooks.yaml`];function l(e){return r.default.join(e,`.pi`,`doom`)}function u(e){return r.default.join(l(e),`state.json`)}function d(e,t=process.pid){return r.default.join(l(e),`run`,String(t))}function f(e,t){let n=r.default.relative(e,t);return n.startsWith(`..`)||r.default.isAbsolute(n)?t:r.default.relative(r.default.join(e,`.pi`),t).split(r.default.sep).join(`/`)}function p(e){return i.default.existsSync(e)?i.default.readFileSync(e,`utf8`):``}function m(e,t,n=a.default.homedir()){let i=o.default.createHash(`sha256`);i.update(JSON.stringify({...t,domains:[...t.domains].sort()}));for(let t of[r.default.join(n,`.pi`,s),r.default.join(e,s)])for(let e of c)i.update(p(r.default.join(t,e)));return i.digest(`hex`)}function h(e,n,r){if(!t.isRecord(e))throw Error(`Doom sync state at ${r} requires ${n} to be an object`);return Object.fromEntries(Object.entries(e).filter(e=>typeof e[1]==`string`))}function g(e,n){let r;try{r=JSON.parse(e)}catch(e){throw Error(`Doom sync state at ${n} is not valid JSON`,{cause:e})}if(!t.isRecord(r))throw Error(`Doom sync state at ${n} must be an object`);if(r.version!==4)throw Error(`Doom sync state at ${n} has version ${String(r.version)}, expected 4. Run doompi sync.`);if(typeof r.root!=`string`||typeof r.inputsHash!=`string`)throw Error(`Doom sync state at ${n} requires string root and inputsHash`);if(!t.isRecord(r.selection)||typeof r.selection.majorMode!=`string`)throw Error(`Doom sync state at ${n} requires a selection with a majorMode`);if(!t.isRecord(r.baseline)||typeof r.baseline.themePath!=`string`)throw Error(`Doom sync state at ${n} requires a baseline with a themePath`);let{majorMode:i,domains:a,profile:o,preset:s}=r.selection,c=t.isRecord(r.fileState)?r.fileState:{};return{version:4,root:r.root,inputsHash:r.inputsHash,selection:{majorMode:i,domains:Array.isArray(a)?a.filter(e=>typeof e==`string`):[],profile:typeof o==`string`?o:void 0,preset:typeof s==`string`?s:`default`},env:h(r.env,`env`,n),fileState:{profileEnvironment:t.isRecord(c.profileEnvironment)?c.profileEnvironment:{},pluginHooks:Array.isArray(c.pluginHooks)?c.pluginHooks:[]},resolved:h(r.resolved,`resolved`,n),compiled:r.compiled===void 0?void 0:h(r.compiled,`compiled`,n),baseline:r.baseline}}function _(e){let t=u(e);if(i.default.existsSync(t))return g(i.default.readFileSync(t,`utf8`),t)}function v(e){return`${JSON.stringify(e,null,2)}\n`}async function y(e,t){let n=u(e);return await i.default.promises.mkdir(r.default.dirname(n),{mode:448,recursive:!0}),await i.default.promises.writeFile(n,v(t),{mode:384}),n}function b(e){return`own:${e}`}function x(e){return`pkg:${e}`}function S(e,t){return`local:${r.default.resolve(t,e)}`}function C(e=n.LAYER_RESOLVERS){let t={};return{resolved:t,ownEntry(n){let r=e.ownEntry(n);return t[b(n)]=r,r},packageEntry(n){let r=e.packageEntry(n);return t[x(n)]=r,r},optionalPackageEntry(n){let r=e.optionalPackageEntry(n);return r&&(t[x(n)]=r),r},localEntry(n,r){let i=e.localEntry(n,r);return i&&(t[S(n,r)]=i),i}}}function w(e,t=n.LAYER_RESOLVERS){let r=C(t);for(let e of Object.values(n.CONTEXT_PACKAGES))r.optionalPackageEntry(e);return r.ownEntry(n.PERSONA_ENTRY),n.assembleExtensions({agents:!0,autoStop:!0,mute:!1,preset:n.OLLAMA_PRESET,layers:Object.keys(e.layers),majorModesConfig:e,resolvers:r}),r.resolved}function T(e,t={}){let n=n=>t[n]??e[n],r=(e,t)=>{let r=n(e);if(!r)throw Error(`${t} is missing from the doompi sync state. Run doompi sync.`);return r};return{ownEntry:e=>r(b(e),e),packageEntry:e=>r(x(e),e),optionalPackageEntry:e=>n(x(e)),localEntry:(e,t)=>n(S(e,t))}}function E(e){if(!i.default.existsSync(e))return[];let n=JSON.parse(i.default.readFileSync(e,`utf8`));return t.isRecord(n.mcpServers)?Object.keys(n.mcpServers):[]}exports.SYNC_STATE_VERSION=4,exports.computeInputsHash=m,exports.createMapResolvers=T,exports.createRecordingResolvers=C,exports.localKey=S,exports.ownKey=b,exports.packageKey=x,exports.readMcpServerNames=E,exports.readSyncState=_,exports.recordResolvedEntries=w,exports.runDirectory=d,exports.serializeSyncState=v,exports.settingsRelativePath=f,exports.syncDirectory=l,exports.syncStatePath=u,exports.writeSyncState=y;
2
+ //# sourceMappingURL=syncState.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"syncState.cjs","names":["path","fs","os","crypto","isRecord","LAYER_RESOLVERS","CONTEXT_PACKAGES","PERSONA_ENTRY","OLLAMA_PRESET"],"sources":["../../src/services/syncState.ts"],"sourcesContent":["import crypto from 'node:crypto';\nimport fs from 'node:fs';\nimport os from 'node:os';\nimport path from 'node:path';\nimport type { HarnessState } from '@agimon-ai/doompi-config';\nimport type { LayerResolvers, MajorModesConfig } from '@agimon-ai/doompi-config/majorModes';\nimport { isRecord, type JsonObject } from '../utils/index.ts';\nimport {\n CONTEXT_PACKAGES,\n LAYER_RESOLVERS,\n OLLAMA_PRESET,\n PERSONA_ENTRY,\n assembleExtensions,\n} from './extensionAssembler.ts';\n\n/**\n * The file `doom-pi sync` writes and the doom-pi Pi extension reads back.\n *\n * This is the whole contract between the two halves of the synced path. Sync\n * runs in plain Node, where module resolution and the repository config are\n * available; the extension runs inside a Pi session, where neither can be\n * relied on. Everything the extension needs is therefore resolved once here,\n * and everything in this file has to survive JSON.\n */\n\n/**\n * Bumped whenever the recorded shape changes, so a stale file re-syncs.\n *\n * 4 renamed the environment namespace from `AGENT_HARNESS_*` to `DOOMPI_*`. The\n * recorded `environment` map is keyed by variable name, so every state file\n * written before that holds keys nothing reads.\n */\nexport const SYNC_STATE_VERSION = 4;\nconst PI_DIRECTORY = '.pi';\nconst DOOM_DIRECTORY = 'doom';\n/** The `.doom` config directory name, under both the repo root and `~/.pi`. */\nconst DOOM_CONFIG_DIRECTORY = '.doom';\nconst STATE_FILE = 'state.json';\nconst RUN_DIRECTORY = 'run';\nconst DEFAULT_PRESET = 'default';\nconst PRIVATE_FILE_MODE = 0o600;\nconst PRIVATE_DIRECTORY_MODE = 0o700;\nconst OWN_PREFIX = 'own:';\nconst PACKAGE_PREFIX = 'pkg:';\nconst LOCAL_PREFIX = 'local:';\n\n/** Config that feeds a sync, in the order the hash reads it. */\nconst HASHED_CONFIG_FILES = ['config.yaml', 'modes.yaml', 'domains.yaml', 'profiles.yaml', 'hooks.yaml'] as const;\n\nexport interface SyncSelection {\n majorMode: string;\n domains: string[];\n profile?: string;\n preset: string;\n}\n\n/** Paths to the resources sync staged, before any live switch replaces them. */\nexport interface SyncBaseline {\n mcpConfigPath?: string;\n personaFile?: string;\n themePath: string;\n themeName: string;\n}\n\nexport interface SyncState {\n version: number;\n root: string;\n /** Hash of the config that produced this state, for the staleness warning. */\n inputsHash: string;\n selection: SyncSelection;\n /** Harness environment the extension applies when the values are unset. */\n env: Record<string, string>;\n /**\n * State the environment does not carry, so it has to be recorded on its own.\n *\n * Plugin hooks and the profile's environment defaults live in the session\n * state file rather than in variables. Without them here a synced session\n * would start with no plugin hooks at all, which fails silently.\n */\n fileState: Pick<HarnessState, 'profileEnvironment' | 'pluginHooks'>;\n /** Entry name to absolute path, covering every layer, not just the selected mode's. */\n resolved: Record<string, string>;\n /**\n * The same entries after TypeScript ones were bundled to plain ESM.\n *\n * Kept apart from `resolved` so the staleness check still compares resolution\n * against resolution; a state written before this existed simply has none and\n * falls back to loading the source.\n */\n compiled?: Record<string, string>;\n baseline: SyncBaseline;\n}\n\nexport function syncDirectory(repoRoot: string): string {\n return path.join(repoRoot, PI_DIRECTORY, DOOM_DIRECTORY);\n}\n\nexport function syncStatePath(repoRoot: string): string {\n return path.join(syncDirectory(repoRoot), STATE_FILE);\n}\n\n/**\n * Per-process scratch directory for live `/domains` and `/profile` switches.\n *\n * Switching rewrites the MCP config and the persona prompt. Writing those back\n * into the synced directory would both mutate the pinned baseline and let two\n * Pi sessions in one repository overwrite each other, which the launcher avoids\n * by giving every run its own temporary directory.\n */\nexport function runDirectory(repoRoot: string, processId: number = process.pid): string {\n return path.join(syncDirectory(repoRoot), RUN_DIRECTORY, String(processId));\n}\n\n/**\n * How a Pi project settings file should refer to a generated path.\n *\n * Relative to `.pi` when the target lives in the repository, which keeps the\n * committed settings file identical on every machine. A doom-pi installed\n * outside the repository stays absolute: Pi accepts both, and a relative path\n * climbing out of the project would be neither portable nor readable.\n */\nexport function settingsRelativePath(repoRoot: string, target: string): string {\n const relative = path.relative(repoRoot, target);\n if (relative.startsWith('..') || path.isAbsolute(relative)) return target;\n return path.relative(path.join(repoRoot, PI_DIRECTORY), target).split(path.sep).join('/');\n}\n\nfunction readIfPresent(filePath: string): string {\n return fs.existsSync(filePath) ? fs.readFileSync(filePath, 'utf8') : '';\n}\n\n/**\n * Hashes the configuration a sync consumed.\n *\n * Deliberately cheap: the extension recomputes this on every session start, so\n * it reads text files and never re-resolves packages. A dependency upgrade that\n * moves a package path therefore does not show up here; `doom-pi sync --check`\n * re-resolves and is the check that catches that.\n */\nexport function computeInputsHash(\n repoRoot: string,\n selection: SyncSelection,\n homeDirectory: string = os.homedir(),\n): string {\n const hash = crypto.createHash('sha256');\n hash.update(JSON.stringify({ ...selection, domains: [...selection.domains].sort() }));\n // Every .doom document layers the global copy under the repository one, so an\n // edit in either location is a real change.\n for (const directory of [\n path.join(homeDirectory, PI_DIRECTORY, DOOM_CONFIG_DIRECTORY),\n path.join(repoRoot, DOOM_CONFIG_DIRECTORY),\n ]) {\n for (const fileName of HASHED_CONFIG_FILES) hash.update(readIfPresent(path.join(directory, fileName)));\n }\n return hash.digest('hex');\n}\n\nfunction stringRecord(value: unknown, field: string, statePath: string): Record<string, string> {\n if (!isRecord(value)) throw new Error(`Doom sync state at ${statePath} requires ${field} to be an object`);\n return Object.fromEntries(\n Object.entries(value).filter((entry): entry is [string, string] => typeof entry[1] === 'string'),\n );\n}\n\nfunction parseSyncState(source: string, statePath: string): SyncState {\n let parsed: unknown;\n try {\n parsed = JSON.parse(source);\n } catch (error) {\n throw new Error(`Doom sync state at ${statePath} is not valid JSON`, { cause: error });\n }\n if (!isRecord(parsed)) throw new Error(`Doom sync state at ${statePath} must be an object`);\n if (parsed.version !== SYNC_STATE_VERSION) {\n throw new Error(\n `Doom sync state at ${statePath} has version ${String(parsed.version)}, expected ${SYNC_STATE_VERSION}. Run doompi sync.`,\n );\n }\n if (typeof parsed.root !== 'string' || typeof parsed.inputsHash !== 'string') {\n throw new Error(`Doom sync state at ${statePath} requires string root and inputsHash`);\n }\n if (!isRecord(parsed.selection) || typeof parsed.selection.majorMode !== 'string') {\n throw new Error(`Doom sync state at ${statePath} requires a selection with a majorMode`);\n }\n if (!isRecord(parsed.baseline) || typeof parsed.baseline.themePath !== 'string') {\n throw new Error(`Doom sync state at ${statePath} requires a baseline with a themePath`);\n }\n const { majorMode, domains, profile, preset } = parsed.selection;\n const fileState = isRecord(parsed.fileState) ? parsed.fileState : {};\n return {\n version: SYNC_STATE_VERSION,\n root: parsed.root,\n inputsHash: parsed.inputsHash,\n selection: {\n majorMode,\n domains: Array.isArray(domains) ? domains.filter((name): name is string => typeof name === 'string') : [],\n profile: typeof profile === 'string' ? profile : undefined,\n preset: typeof preset === 'string' ? preset : DEFAULT_PRESET,\n },\n env: stringRecord(parsed.env, 'env', statePath),\n fileState: {\n profileEnvironment: isRecord(fileState.profileEnvironment)\n ? (fileState.profileEnvironment as Record<string, string>)\n : {},\n pluginHooks: Array.isArray(fileState.pluginHooks) ? (fileState.pluginHooks as HarnessState['pluginHooks']) : [],\n },\n resolved: stringRecord(parsed.resolved, 'resolved', statePath),\n // Absent from a state written before compilation existed, which is why it\n // is read back optionally rather than demanded.\n compiled: parsed.compiled === undefined ? undefined : stringRecord(parsed.compiled, 'compiled', statePath),\n baseline: parsed.baseline as unknown as SyncBaseline,\n };\n}\n\n/** Reads the synced state, or undefined when the repository was never synced. */\nexport function readSyncState(repoRoot: string): SyncState | undefined {\n const statePath = syncStatePath(repoRoot);\n if (!fs.existsSync(statePath)) return undefined;\n return parseSyncState(fs.readFileSync(statePath, 'utf8'), statePath);\n}\n\nexport function serializeSyncState(state: SyncState): string {\n return `${JSON.stringify(state, null, 2)}\\n`;\n}\n\nexport async function writeSyncState(repoRoot: string, state: SyncState): Promise<string> {\n const statePath = syncStatePath(repoRoot);\n await fs.promises.mkdir(path.dirname(statePath), { mode: PRIVATE_DIRECTORY_MODE, recursive: true });\n // Holds resolved environment values, so it stays readable by its owner only.\n await fs.promises.writeFile(statePath, serializeSyncState(state), { mode: PRIVATE_FILE_MODE });\n return statePath;\n}\n\nexport function ownKey(name: string): string {\n return `${OWN_PREFIX}${name}`;\n}\n\nexport function packageKey(specifier: string): string {\n return `${PACKAGE_PREFIX}${specifier}`;\n}\n\n/**\n * Keyed by the absolute target, because the same relative specifier means\n * different files under the repository and the global config directory.\n */\nexport function localKey(specifier: string, baseDirectory: string): string {\n return `${LOCAL_PREFIX}${path.resolve(baseDirectory, specifier)}`;\n}\n\nexport interface RecordingResolvers extends LayerResolvers {\n /** Every name this resolver was asked for, keyed for the state file. */\n readonly resolved: Record<string, string>;\n}\n\n/**\n * Resolvers that record what they resolve.\n *\n * Sync composes once with every layer selected and hands the recording to the\n * state file, so the extension never has to resolve a specifier itself.\n */\nexport function createRecordingResolvers(base: LayerResolvers = LAYER_RESOLVERS): RecordingResolvers {\n const resolved: Record<string, string> = {};\n return {\n resolved,\n ownEntry(name) {\n const entry = base.ownEntry(name);\n resolved[ownKey(name)] = entry;\n return entry;\n },\n packageEntry(name) {\n const entry = base.packageEntry(name);\n resolved[packageKey(name)] = entry;\n return entry;\n },\n optionalPackageEntry(name) {\n const entry = base.optionalPackageEntry(name);\n // A miss is recorded by omission, which is what later makes the extension\n // treat it as absent instead of failing the whole composition.\n if (entry) resolved[packageKey(name)] = entry;\n return entry;\n },\n localEntry(specifier, baseDirectory) {\n const entry = base.localEntry(specifier, baseDirectory);\n if (entry) resolved[localKey(specifier, baseDirectory)] = entry;\n return entry;\n },\n };\n}\n\n/**\n * Resolves every entry any layer could contribute, in one pass.\n *\n * Composing with every layer selected and the widest options is what makes the\n * map complete: a later `/major-mode` switch inside a Pi session can then pick a\n * different set without resolving anything itself. The context packages and the\n * persona entry are asked for separately because they reach `assembleExtensions`\n * as already-resolved paths rather than through the resolvers.\n */\nexport function recordResolvedEntries(\n majorModesConfig: MajorModesConfig,\n base: LayerResolvers = LAYER_RESOLVERS,\n): Record<string, string> {\n const recording = createRecordingResolvers(base);\n for (const specifier of Object.values(CONTEXT_PACKAGES)) recording.optionalPackageEntry(specifier);\n recording.ownEntry(PERSONA_ENTRY);\n assembleExtensions({\n agents: true,\n autoStop: true,\n mute: false,\n // The one preset that contributes an extension of its own.\n preset: OLLAMA_PRESET,\n layers: Object.keys(majorModesConfig.layers),\n majorModesConfig,\n resolvers: recording,\n });\n return recording.resolved;\n}\n\n/** Resolvers backed by a synced map, for composing inside a Pi session. */\nexport function createMapResolvers(\n resolved: Record<string, string>,\n compiled: Record<string, string> = {},\n): LayerResolvers {\n // The compiled bundle when this entry has one, the source otherwise.\n const entryFor = (key: string): string | undefined => compiled[key] ?? resolved[key];\n const lookup = (key: string, name: string): string => {\n const entry = entryFor(key);\n if (!entry) throw new Error(`${name} is missing from the doompi sync state. Run doompi sync.`);\n return entry;\n };\n return {\n ownEntry: (name) => lookup(ownKey(name), name),\n packageEntry: (name) => lookup(packageKey(name), name),\n optionalPackageEntry: (name) => entryFor(packageKey(name)),\n localEntry: (specifier, baseDirectory) => entryFor(localKey(specifier, baseDirectory)),\n };\n}\n\n/** Reads the resolved MCP servers back out of a generated config. */\nexport function readMcpServerNames(configPath: string): string[] {\n if (!fs.existsSync(configPath)) return [];\n const config = JSON.parse(fs.readFileSync(configPath, 'utf8')) as JsonObject;\n return isRecord(config.mcpServers) ? Object.keys(config.mcpServers) : [];\n}\n"],"mappings":"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"}
@@ -0,0 +1,130 @@
1
+ import { HarnessState } from "@agimon-ai/doompi-config";
2
+ import { LayerResolvers, MajorModesConfig } from "@agimon-ai/doompi-config/majorModes";
3
+
4
+ //#region src/services/syncState.d.ts
5
+ /**
6
+ * The file `doom-pi sync` writes and the doom-pi Pi extension reads back.
7
+ *
8
+ * This is the whole contract between the two halves of the synced path. Sync
9
+ * runs in plain Node, where module resolution and the repository config are
10
+ * available; the extension runs inside a Pi session, where neither can be
11
+ * relied on. Everything the extension needs is therefore resolved once here,
12
+ * and everything in this file has to survive JSON.
13
+ */
14
+ /**
15
+ * Bumped whenever the recorded shape changes, so a stale file re-syncs.
16
+ *
17
+ * 4 renamed the environment namespace from `AGENT_HARNESS_*` to `DOOMPI_*`. The
18
+ * recorded `environment` map is keyed by variable name, so every state file
19
+ * written before that holds keys nothing reads.
20
+ */
21
+ declare const SYNC_STATE_VERSION = 4;
22
+ interface SyncSelection {
23
+ majorMode: string;
24
+ domains: string[];
25
+ profile?: string;
26
+ preset: string;
27
+ }
28
+ /** Paths to the resources sync staged, before any live switch replaces them. */
29
+ interface SyncBaseline {
30
+ mcpConfigPath?: string;
31
+ personaFile?: string;
32
+ themePath: string;
33
+ themeName: string;
34
+ }
35
+ interface SyncState {
36
+ version: number;
37
+ root: string;
38
+ /** Hash of the config that produced this state, for the staleness warning. */
39
+ inputsHash: string;
40
+ selection: SyncSelection;
41
+ /** Harness environment the extension applies when the values are unset. */
42
+ env: Record<string, string>;
43
+ /**
44
+ * State the environment does not carry, so it has to be recorded on its own.
45
+ *
46
+ * Plugin hooks and the profile's environment defaults live in the session
47
+ * state file rather than in variables. Without them here a synced session
48
+ * would start with no plugin hooks at all, which fails silently.
49
+ */
50
+ fileState: Pick<HarnessState, 'profileEnvironment' | 'pluginHooks'>;
51
+ /** Entry name to absolute path, covering every layer, not just the selected mode's. */
52
+ resolved: Record<string, string>;
53
+ /**
54
+ * The same entries after TypeScript ones were bundled to plain ESM.
55
+ *
56
+ * Kept apart from `resolved` so the staleness check still compares resolution
57
+ * against resolution; a state written before this existed simply has none and
58
+ * falls back to loading the source.
59
+ */
60
+ compiled?: Record<string, string>;
61
+ baseline: SyncBaseline;
62
+ }
63
+ declare function syncDirectory(repoRoot: string): string;
64
+ declare function syncStatePath(repoRoot: string): string;
65
+ /**
66
+ * Per-process scratch directory for live `/domains` and `/profile` switches.
67
+ *
68
+ * Switching rewrites the MCP config and the persona prompt. Writing those back
69
+ * into the synced directory would both mutate the pinned baseline and let two
70
+ * Pi sessions in one repository overwrite each other, which the launcher avoids
71
+ * by giving every run its own temporary directory.
72
+ */
73
+ declare function runDirectory(repoRoot: string, processId?: number): string;
74
+ /**
75
+ * How a Pi project settings file should refer to a generated path.
76
+ *
77
+ * Relative to `.pi` when the target lives in the repository, which keeps the
78
+ * committed settings file identical on every machine. A doom-pi installed
79
+ * outside the repository stays absolute: Pi accepts both, and a relative path
80
+ * climbing out of the project would be neither portable nor readable.
81
+ */
82
+ declare function settingsRelativePath(repoRoot: string, target: string): string;
83
+ /**
84
+ * Hashes the configuration a sync consumed.
85
+ *
86
+ * Deliberately cheap: the extension recomputes this on every session start, so
87
+ * it reads text files and never re-resolves packages. A dependency upgrade that
88
+ * moves a package path therefore does not show up here; `doom-pi sync --check`
89
+ * re-resolves and is the check that catches that.
90
+ */
91
+ declare function computeInputsHash(repoRoot: string, selection: SyncSelection, homeDirectory?: string): string;
92
+ /** Reads the synced state, or undefined when the repository was never synced. */
93
+ declare function readSyncState(repoRoot: string): SyncState | undefined;
94
+ declare function serializeSyncState(state: SyncState): string;
95
+ declare function writeSyncState(repoRoot: string, state: SyncState): Promise<string>;
96
+ declare function ownKey(name: string): string;
97
+ declare function packageKey(specifier: string): string;
98
+ /**
99
+ * Keyed by the absolute target, because the same relative specifier means
100
+ * different files under the repository and the global config directory.
101
+ */
102
+ declare function localKey(specifier: string, baseDirectory: string): string;
103
+ interface RecordingResolvers extends LayerResolvers {
104
+ /** Every name this resolver was asked for, keyed for the state file. */
105
+ readonly resolved: Record<string, string>;
106
+ }
107
+ /**
108
+ * Resolvers that record what they resolve.
109
+ *
110
+ * Sync composes once with every layer selected and hands the recording to the
111
+ * state file, so the extension never has to resolve a specifier itself.
112
+ */
113
+ declare function createRecordingResolvers(base?: LayerResolvers): RecordingResolvers;
114
+ /**
115
+ * Resolves every entry any layer could contribute, in one pass.
116
+ *
117
+ * Composing with every layer selected and the widest options is what makes the
118
+ * map complete: a later `/major-mode` switch inside a Pi session can then pick a
119
+ * different set without resolving anything itself. The context packages and the
120
+ * persona entry are asked for separately because they reach `assembleExtensions`
121
+ * as already-resolved paths rather than through the resolvers.
122
+ */
123
+ declare function recordResolvedEntries(majorModesConfig: MajorModesConfig, base?: LayerResolvers): Record<string, string>;
124
+ /** Resolvers backed by a synced map, for composing inside a Pi session. */
125
+ declare function createMapResolvers(resolved: Record<string, string>, compiled?: Record<string, string>): LayerResolvers;
126
+ /** Reads the resolved MCP servers back out of a generated config. */
127
+ declare function readMcpServerNames(configPath: string): string[];
128
+ //#endregion
129
+ export { RecordingResolvers, SYNC_STATE_VERSION, SyncBaseline, SyncSelection, SyncState, computeInputsHash, createMapResolvers, createRecordingResolvers, localKey, ownKey, packageKey, readMcpServerNames, readSyncState, recordResolvedEntries, runDirectory, serializeSyncState, settingsRelativePath, syncDirectory, syncStatePath, writeSyncState };
130
+ //# sourceMappingURL=syncState.d.cts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"syncState.d.cts","names":[],"sources":["../../src/services/syncState.ts"],"mappings":";;;;;;AAgCA;;;;;AAiBA;;;;;;;;;cAjBa,kBAAA;AAAA,UAiBI,aAAA;EACf,SAAA;EACA,OAAA;EACA,OAAA;EACA,MAAA;AAAA;;UAIe,YAAA;EACf,aAAA;EACA,WAAA;EACA,SAAA;EACA,SAAA;AAAA;AAAA,UAGe,SAAA;EACf,OAAA;EACA,IAAA;EAaW;EAXX,UAAA;EACA,SAAA,EAAW,aAAA;EAqBD;EAnBV,GAAA,EAAK,MAAA;EAmBiB;;;;;;;EAXtB,SAAA,EAAW,IAAA,CAAK,YAAA;EAAhB;EAEA,QAAA,EAAU,MAAA;EAFM;;;;;;;EAUhB,QAAA,GAAW,MAAA;EACX,QAAA,EAAU,YAAA;AAAA;AAAA,iBAGI,aAAA,CAAc,QAAA;AAAA,iBAId,aAAA,CAAc,QAAA;;;AAA9B;;;;;AAYA;iBAAgB,YAAA,CAAa,QAAA,UAAkB,SAAA;;;;AAY/C;;;;;iBAAgB,oBAAA,CAAqB,QAAA,UAAkB,MAAA;;;;;;;;;iBAkBvC,iBAAA,CACd,QAAA,UACA,SAAA,EAAW,aAAA,EACX,aAAA;AAwEF;AAAA,iBAAgB,aAAA,CAAc,QAAA,WAAmB,SAAA;AAAA,iBAMjC,kBAAA,CAAmB,KAAA,EAAO,SAAA;AAAA,iBAIpB,cAAA,CAAe,QAAA,UAAkB,KAAA,EAAO,SAAA,GAAY,OAAA;AAAA,iBAQ1D,MAAA,CAAO,IAAA;AAAA,iBAIP,UAAA,CAAW,SAAA;;;;;iBAQX,QAAA,CAAS,SAAA,UAAmB,aAAA;AAAA,UAI3B,kBAAA,SAA2B,cAAA;;WAEjC,QAAA,EAAU,MAAA;AAAA;;;;;;AAlBrB;iBA2BgB,wBAAA,CAAyB,IAAA,GAAM,cAAA,GAAmC,kBAAA;;;;AAvBlF;;;;;AAQA;iBAqDgB,qBAAA,CACd,gBAAA,EAAkB,gBAAA,EAClB,IAAA,GAAM,cAAA,GACL,MAAA;;iBAkBa,kBAAA,CACd,QAAA,EAAU,MAAA,kBACV,QAAA,GAAU,MAAA,mBACT,cAAA;;iBAiBa,kBAAA,CAAmB,UAAA"}
@@ -0,0 +1,130 @@
1
+ import { HarnessState } from "@agimon-ai/doompi-config";
2
+ import { LayerResolvers, MajorModesConfig } from "@agimon-ai/doompi-config/majorModes";
3
+
4
+ //#region src/services/syncState.d.ts
5
+ /**
6
+ * The file `doom-pi sync` writes and the doom-pi Pi extension reads back.
7
+ *
8
+ * This is the whole contract between the two halves of the synced path. Sync
9
+ * runs in plain Node, where module resolution and the repository config are
10
+ * available; the extension runs inside a Pi session, where neither can be
11
+ * relied on. Everything the extension needs is therefore resolved once here,
12
+ * and everything in this file has to survive JSON.
13
+ */
14
+ /**
15
+ * Bumped whenever the recorded shape changes, so a stale file re-syncs.
16
+ *
17
+ * 4 renamed the environment namespace from `AGENT_HARNESS_*` to `DOOMPI_*`. The
18
+ * recorded `environment` map is keyed by variable name, so every state file
19
+ * written before that holds keys nothing reads.
20
+ */
21
+ declare const SYNC_STATE_VERSION = 4;
22
+ interface SyncSelection {
23
+ majorMode: string;
24
+ domains: string[];
25
+ profile?: string;
26
+ preset: string;
27
+ }
28
+ /** Paths to the resources sync staged, before any live switch replaces them. */
29
+ interface SyncBaseline {
30
+ mcpConfigPath?: string;
31
+ personaFile?: string;
32
+ themePath: string;
33
+ themeName: string;
34
+ }
35
+ interface SyncState {
36
+ version: number;
37
+ root: string;
38
+ /** Hash of the config that produced this state, for the staleness warning. */
39
+ inputsHash: string;
40
+ selection: SyncSelection;
41
+ /** Harness environment the extension applies when the values are unset. */
42
+ env: Record<string, string>;
43
+ /**
44
+ * State the environment does not carry, so it has to be recorded on its own.
45
+ *
46
+ * Plugin hooks and the profile's environment defaults live in the session
47
+ * state file rather than in variables. Without them here a synced session
48
+ * would start with no plugin hooks at all, which fails silently.
49
+ */
50
+ fileState: Pick<HarnessState, 'profileEnvironment' | 'pluginHooks'>;
51
+ /** Entry name to absolute path, covering every layer, not just the selected mode's. */
52
+ resolved: Record<string, string>;
53
+ /**
54
+ * The same entries after TypeScript ones were bundled to plain ESM.
55
+ *
56
+ * Kept apart from `resolved` so the staleness check still compares resolution
57
+ * against resolution; a state written before this existed simply has none and
58
+ * falls back to loading the source.
59
+ */
60
+ compiled?: Record<string, string>;
61
+ baseline: SyncBaseline;
62
+ }
63
+ declare function syncDirectory(repoRoot: string): string;
64
+ declare function syncStatePath(repoRoot: string): string;
65
+ /**
66
+ * Per-process scratch directory for live `/domains` and `/profile` switches.
67
+ *
68
+ * Switching rewrites the MCP config and the persona prompt. Writing those back
69
+ * into the synced directory would both mutate the pinned baseline and let two
70
+ * Pi sessions in one repository overwrite each other, which the launcher avoids
71
+ * by giving every run its own temporary directory.
72
+ */
73
+ declare function runDirectory(repoRoot: string, processId?: number): string;
74
+ /**
75
+ * How a Pi project settings file should refer to a generated path.
76
+ *
77
+ * Relative to `.pi` when the target lives in the repository, which keeps the
78
+ * committed settings file identical on every machine. A doom-pi installed
79
+ * outside the repository stays absolute: Pi accepts both, and a relative path
80
+ * climbing out of the project would be neither portable nor readable.
81
+ */
82
+ declare function settingsRelativePath(repoRoot: string, target: string): string;
83
+ /**
84
+ * Hashes the configuration a sync consumed.
85
+ *
86
+ * Deliberately cheap: the extension recomputes this on every session start, so
87
+ * it reads text files and never re-resolves packages. A dependency upgrade that
88
+ * moves a package path therefore does not show up here; `doom-pi sync --check`
89
+ * re-resolves and is the check that catches that.
90
+ */
91
+ declare function computeInputsHash(repoRoot: string, selection: SyncSelection, homeDirectory?: string): string;
92
+ /** Reads the synced state, or undefined when the repository was never synced. */
93
+ declare function readSyncState(repoRoot: string): SyncState | undefined;
94
+ declare function serializeSyncState(state: SyncState): string;
95
+ declare function writeSyncState(repoRoot: string, state: SyncState): Promise<string>;
96
+ declare function ownKey(name: string): string;
97
+ declare function packageKey(specifier: string): string;
98
+ /**
99
+ * Keyed by the absolute target, because the same relative specifier means
100
+ * different files under the repository and the global config directory.
101
+ */
102
+ declare function localKey(specifier: string, baseDirectory: string): string;
103
+ interface RecordingResolvers extends LayerResolvers {
104
+ /** Every name this resolver was asked for, keyed for the state file. */
105
+ readonly resolved: Record<string, string>;
106
+ }
107
+ /**
108
+ * Resolvers that record what they resolve.
109
+ *
110
+ * Sync composes once with every layer selected and hands the recording to the
111
+ * state file, so the extension never has to resolve a specifier itself.
112
+ */
113
+ declare function createRecordingResolvers(base?: LayerResolvers): RecordingResolvers;
114
+ /**
115
+ * Resolves every entry any layer could contribute, in one pass.
116
+ *
117
+ * Composing with every layer selected and the widest options is what makes the
118
+ * map complete: a later `/major-mode` switch inside a Pi session can then pick a
119
+ * different set without resolving anything itself. The context packages and the
120
+ * persona entry are asked for separately because they reach `assembleExtensions`
121
+ * as already-resolved paths rather than through the resolvers.
122
+ */
123
+ declare function recordResolvedEntries(majorModesConfig: MajorModesConfig, base?: LayerResolvers): Record<string, string>;
124
+ /** Resolvers backed by a synced map, for composing inside a Pi session. */
125
+ declare function createMapResolvers(resolved: Record<string, string>, compiled?: Record<string, string>): LayerResolvers;
126
+ /** Reads the resolved MCP servers back out of a generated config. */
127
+ declare function readMcpServerNames(configPath: string): string[];
128
+ //#endregion
129
+ export { RecordingResolvers, SYNC_STATE_VERSION, SyncBaseline, SyncSelection, SyncState, computeInputsHash, createMapResolvers, createRecordingResolvers, localKey, ownKey, packageKey, readMcpServerNames, readSyncState, recordResolvedEntries, runDirectory, serializeSyncState, settingsRelativePath, syncDirectory, syncStatePath, writeSyncState };
130
+ //# sourceMappingURL=syncState.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"syncState.d.mts","names":[],"sources":["../../src/services/syncState.ts"],"mappings":";;;;;;AAgCA;;;;;AAiBA;;;;;;;;;cAjBa,kBAAA;AAAA,UAiBI,aAAA;EACf,SAAA;EACA,OAAA;EACA,OAAA;EACA,MAAA;AAAA;;UAIe,YAAA;EACf,aAAA;EACA,WAAA;EACA,SAAA;EACA,SAAA;AAAA;AAAA,UAGe,SAAA;EACf,OAAA;EACA,IAAA;EAaW;EAXX,UAAA;EACA,SAAA,EAAW,aAAA;EAqBD;EAnBV,GAAA,EAAK,MAAA;EAmBiB;;;;;;;EAXtB,SAAA,EAAW,IAAA,CAAK,YAAA;EAAhB;EAEA,QAAA,EAAU,MAAA;EAFM;;;;;;;EAUhB,QAAA,GAAW,MAAA;EACX,QAAA,EAAU,YAAA;AAAA;AAAA,iBAGI,aAAA,CAAc,QAAA;AAAA,iBAId,aAAA,CAAc,QAAA;;;AAA9B;;;;;AAYA;iBAAgB,YAAA,CAAa,QAAA,UAAkB,SAAA;;;;AAY/C;;;;;iBAAgB,oBAAA,CAAqB,QAAA,UAAkB,MAAA;;;;;;;;;iBAkBvC,iBAAA,CACd,QAAA,UACA,SAAA,EAAW,aAAA,EACX,aAAA;AAwEF;AAAA,iBAAgB,aAAA,CAAc,QAAA,WAAmB,SAAA;AAAA,iBAMjC,kBAAA,CAAmB,KAAA,EAAO,SAAA;AAAA,iBAIpB,cAAA,CAAe,QAAA,UAAkB,KAAA,EAAO,SAAA,GAAY,OAAA;AAAA,iBAQ1D,MAAA,CAAO,IAAA;AAAA,iBAIP,UAAA,CAAW,SAAA;;;;;iBAQX,QAAA,CAAS,SAAA,UAAmB,aAAA;AAAA,UAI3B,kBAAA,SAA2B,cAAA;;WAEjC,QAAA,EAAU,MAAA;AAAA;;;;;;AAlBrB;iBA2BgB,wBAAA,CAAyB,IAAA,GAAM,cAAA,GAAmC,kBAAA;;;;AAvBlF;;;;;AAQA;iBAqDgB,qBAAA,CACd,gBAAA,EAAkB,gBAAA,EAClB,IAAA,GAAM,cAAA,GACL,MAAA;;iBAkBa,kBAAA,CACd,QAAA,EAAU,MAAA,kBACV,QAAA,GAAU,MAAA,mBACT,cAAA;;iBAiBa,kBAAA,CAAmB,UAAA"}
@@ -0,0 +1,2 @@
1
+ import{isRecord as e}from"../utils/json.mjs";import"../utils/index.mjs";import{CONTEXT_PACKAGES as t,LAYER_RESOLVERS as n,OLLAMA_PRESET as r,PERSONA_ENTRY as i,assembleExtensions as a}from"./extensionAssembler.mjs";import o from"node:path";import s from"node:fs";import c from"node:os";import l from"node:crypto";const u=4,d=`.doom`,f=[`config.yaml`,`modes.yaml`,`domains.yaml`,`profiles.yaml`,`hooks.yaml`];function p(e){return o.join(e,`.pi`,`doom`)}function m(e){return o.join(p(e),`state.json`)}function h(e,t=process.pid){return o.join(p(e),`run`,String(t))}function g(e,t){let n=o.relative(e,t);return n.startsWith(`..`)||o.isAbsolute(n)?t:o.relative(o.join(e,`.pi`),t).split(o.sep).join(`/`)}function _(e){return s.existsSync(e)?s.readFileSync(e,`utf8`):``}function v(e,t,n=c.homedir()){let r=l.createHash(`sha256`);r.update(JSON.stringify({...t,domains:[...t.domains].sort()}));for(let t of[o.join(n,`.pi`,d),o.join(e,d)])for(let e of f)r.update(_(o.join(t,e)));return r.digest(`hex`)}function y(t,n,r){if(!e(t))throw Error(`Doom sync state at ${r} requires ${n} to be an object`);return Object.fromEntries(Object.entries(t).filter(e=>typeof e[1]==`string`))}function b(t,n){let r;try{r=JSON.parse(t)}catch(e){throw Error(`Doom sync state at ${n} is not valid JSON`,{cause:e})}if(!e(r))throw Error(`Doom sync state at ${n} must be an object`);if(r.version!==4)throw Error(`Doom sync state at ${n} has version ${String(r.version)}, expected 4. Run doompi sync.`);if(typeof r.root!=`string`||typeof r.inputsHash!=`string`)throw Error(`Doom sync state at ${n} requires string root and inputsHash`);if(!e(r.selection)||typeof r.selection.majorMode!=`string`)throw Error(`Doom sync state at ${n} requires a selection with a majorMode`);if(!e(r.baseline)||typeof r.baseline.themePath!=`string`)throw Error(`Doom sync state at ${n} requires a baseline with a themePath`);let{majorMode:i,domains:a,profile:o,preset:s}=r.selection,c=e(r.fileState)?r.fileState:{};return{version:4,root:r.root,inputsHash:r.inputsHash,selection:{majorMode:i,domains:Array.isArray(a)?a.filter(e=>typeof e==`string`):[],profile:typeof o==`string`?o:void 0,preset:typeof s==`string`?s:`default`},env:y(r.env,`env`,n),fileState:{profileEnvironment:e(c.profileEnvironment)?c.profileEnvironment:{},pluginHooks:Array.isArray(c.pluginHooks)?c.pluginHooks:[]},resolved:y(r.resolved,`resolved`,n),compiled:r.compiled===void 0?void 0:y(r.compiled,`compiled`,n),baseline:r.baseline}}function x(e){let t=m(e);if(s.existsSync(t))return b(s.readFileSync(t,`utf8`),t)}function S(e){return`${JSON.stringify(e,null,2)}\n`}async function C(e,t){let n=m(e);return await s.promises.mkdir(o.dirname(n),{mode:448,recursive:!0}),await s.promises.writeFile(n,S(t),{mode:384}),n}function w(e){return`own:${e}`}function T(e){return`pkg:${e}`}function E(e,t){return`local:${o.resolve(t,e)}`}function D(e=n){let t={};return{resolved:t,ownEntry(n){let r=e.ownEntry(n);return t[w(n)]=r,r},packageEntry(n){let r=e.packageEntry(n);return t[T(n)]=r,r},optionalPackageEntry(n){let r=e.optionalPackageEntry(n);return r&&(t[T(n)]=r),r},localEntry(n,r){let i=e.localEntry(n,r);return i&&(t[E(n,r)]=i),i}}}function O(e,o=n){let s=D(o);for(let e of Object.values(t))s.optionalPackageEntry(e);return s.ownEntry(i),a({agents:!0,autoStop:!0,mute:!1,preset:r,layers:Object.keys(e.layers),majorModesConfig:e,resolvers:s}),s.resolved}function k(e,t={}){let n=n=>t[n]??e[n],r=(e,t)=>{let r=n(e);if(!r)throw Error(`${t} is missing from the doompi sync state. Run doompi sync.`);return r};return{ownEntry:e=>r(w(e),e),packageEntry:e=>r(T(e),e),optionalPackageEntry:e=>n(T(e)),localEntry:(e,t)=>n(E(e,t))}}function A(t){if(!s.existsSync(t))return[];let n=JSON.parse(s.readFileSync(t,`utf8`));return e(n.mcpServers)?Object.keys(n.mcpServers):[]}export{u as SYNC_STATE_VERSION,v as computeInputsHash,k as createMapResolvers,D as createRecordingResolvers,E as localKey,w as ownKey,T as packageKey,A as readMcpServerNames,x as readSyncState,O as recordResolvedEntries,h as runDirectory,S as serializeSyncState,g as settingsRelativePath,p as syncDirectory,m as syncStatePath,C as writeSyncState};
2
+ //# sourceMappingURL=syncState.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"syncState.mjs","names":[],"sources":["../../src/services/syncState.ts"],"sourcesContent":["import crypto from 'node:crypto';\nimport fs from 'node:fs';\nimport os from 'node:os';\nimport path from 'node:path';\nimport type { HarnessState } from '@agimon-ai/doompi-config';\nimport type { LayerResolvers, MajorModesConfig } from '@agimon-ai/doompi-config/majorModes';\nimport { isRecord, type JsonObject } from '../utils/index.ts';\nimport {\n CONTEXT_PACKAGES,\n LAYER_RESOLVERS,\n OLLAMA_PRESET,\n PERSONA_ENTRY,\n assembleExtensions,\n} from './extensionAssembler.ts';\n\n/**\n * The file `doom-pi sync` writes and the doom-pi Pi extension reads back.\n *\n * This is the whole contract between the two halves of the synced path. Sync\n * runs in plain Node, where module resolution and the repository config are\n * available; the extension runs inside a Pi session, where neither can be\n * relied on. Everything the extension needs is therefore resolved once here,\n * and everything in this file has to survive JSON.\n */\n\n/**\n * Bumped whenever the recorded shape changes, so a stale file re-syncs.\n *\n * 4 renamed the environment namespace from `AGENT_HARNESS_*` to `DOOMPI_*`. The\n * recorded `environment` map is keyed by variable name, so every state file\n * written before that holds keys nothing reads.\n */\nexport const SYNC_STATE_VERSION = 4;\nconst PI_DIRECTORY = '.pi';\nconst DOOM_DIRECTORY = 'doom';\n/** The `.doom` config directory name, under both the repo root and `~/.pi`. */\nconst DOOM_CONFIG_DIRECTORY = '.doom';\nconst STATE_FILE = 'state.json';\nconst RUN_DIRECTORY = 'run';\nconst DEFAULT_PRESET = 'default';\nconst PRIVATE_FILE_MODE = 0o600;\nconst PRIVATE_DIRECTORY_MODE = 0o700;\nconst OWN_PREFIX = 'own:';\nconst PACKAGE_PREFIX = 'pkg:';\nconst LOCAL_PREFIX = 'local:';\n\n/** Config that feeds a sync, in the order the hash reads it. */\nconst HASHED_CONFIG_FILES = ['config.yaml', 'modes.yaml', 'domains.yaml', 'profiles.yaml', 'hooks.yaml'] as const;\n\nexport interface SyncSelection {\n majorMode: string;\n domains: string[];\n profile?: string;\n preset: string;\n}\n\n/** Paths to the resources sync staged, before any live switch replaces them. */\nexport interface SyncBaseline {\n mcpConfigPath?: string;\n personaFile?: string;\n themePath: string;\n themeName: string;\n}\n\nexport interface SyncState {\n version: number;\n root: string;\n /** Hash of the config that produced this state, for the staleness warning. */\n inputsHash: string;\n selection: SyncSelection;\n /** Harness environment the extension applies when the values are unset. */\n env: Record<string, string>;\n /**\n * State the environment does not carry, so it has to be recorded on its own.\n *\n * Plugin hooks and the profile's environment defaults live in the session\n * state file rather than in variables. Without them here a synced session\n * would start with no plugin hooks at all, which fails silently.\n */\n fileState: Pick<HarnessState, 'profileEnvironment' | 'pluginHooks'>;\n /** Entry name to absolute path, covering every layer, not just the selected mode's. */\n resolved: Record<string, string>;\n /**\n * The same entries after TypeScript ones were bundled to plain ESM.\n *\n * Kept apart from `resolved` so the staleness check still compares resolution\n * against resolution; a state written before this existed simply has none and\n * falls back to loading the source.\n */\n compiled?: Record<string, string>;\n baseline: SyncBaseline;\n}\n\nexport function syncDirectory(repoRoot: string): string {\n return path.join(repoRoot, PI_DIRECTORY, DOOM_DIRECTORY);\n}\n\nexport function syncStatePath(repoRoot: string): string {\n return path.join(syncDirectory(repoRoot), STATE_FILE);\n}\n\n/**\n * Per-process scratch directory for live `/domains` and `/profile` switches.\n *\n * Switching rewrites the MCP config and the persona prompt. Writing those back\n * into the synced directory would both mutate the pinned baseline and let two\n * Pi sessions in one repository overwrite each other, which the launcher avoids\n * by giving every run its own temporary directory.\n */\nexport function runDirectory(repoRoot: string, processId: number = process.pid): string {\n return path.join(syncDirectory(repoRoot), RUN_DIRECTORY, String(processId));\n}\n\n/**\n * How a Pi project settings file should refer to a generated path.\n *\n * Relative to `.pi` when the target lives in the repository, which keeps the\n * committed settings file identical on every machine. A doom-pi installed\n * outside the repository stays absolute: Pi accepts both, and a relative path\n * climbing out of the project would be neither portable nor readable.\n */\nexport function settingsRelativePath(repoRoot: string, target: string): string {\n const relative = path.relative(repoRoot, target);\n if (relative.startsWith('..') || path.isAbsolute(relative)) return target;\n return path.relative(path.join(repoRoot, PI_DIRECTORY), target).split(path.sep).join('/');\n}\n\nfunction readIfPresent(filePath: string): string {\n return fs.existsSync(filePath) ? fs.readFileSync(filePath, 'utf8') : '';\n}\n\n/**\n * Hashes the configuration a sync consumed.\n *\n * Deliberately cheap: the extension recomputes this on every session start, so\n * it reads text files and never re-resolves packages. A dependency upgrade that\n * moves a package path therefore does not show up here; `doom-pi sync --check`\n * re-resolves and is the check that catches that.\n */\nexport function computeInputsHash(\n repoRoot: string,\n selection: SyncSelection,\n homeDirectory: string = os.homedir(),\n): string {\n const hash = crypto.createHash('sha256');\n hash.update(JSON.stringify({ ...selection, domains: [...selection.domains].sort() }));\n // Every .doom document layers the global copy under the repository one, so an\n // edit in either location is a real change.\n for (const directory of [\n path.join(homeDirectory, PI_DIRECTORY, DOOM_CONFIG_DIRECTORY),\n path.join(repoRoot, DOOM_CONFIG_DIRECTORY),\n ]) {\n for (const fileName of HASHED_CONFIG_FILES) hash.update(readIfPresent(path.join(directory, fileName)));\n }\n return hash.digest('hex');\n}\n\nfunction stringRecord(value: unknown, field: string, statePath: string): Record<string, string> {\n if (!isRecord(value)) throw new Error(`Doom sync state at ${statePath} requires ${field} to be an object`);\n return Object.fromEntries(\n Object.entries(value).filter((entry): entry is [string, string] => typeof entry[1] === 'string'),\n );\n}\n\nfunction parseSyncState(source: string, statePath: string): SyncState {\n let parsed: unknown;\n try {\n parsed = JSON.parse(source);\n } catch (error) {\n throw new Error(`Doom sync state at ${statePath} is not valid JSON`, { cause: error });\n }\n if (!isRecord(parsed)) throw new Error(`Doom sync state at ${statePath} must be an object`);\n if (parsed.version !== SYNC_STATE_VERSION) {\n throw new Error(\n `Doom sync state at ${statePath} has version ${String(parsed.version)}, expected ${SYNC_STATE_VERSION}. Run doompi sync.`,\n );\n }\n if (typeof parsed.root !== 'string' || typeof parsed.inputsHash !== 'string') {\n throw new Error(`Doom sync state at ${statePath} requires string root and inputsHash`);\n }\n if (!isRecord(parsed.selection) || typeof parsed.selection.majorMode !== 'string') {\n throw new Error(`Doom sync state at ${statePath} requires a selection with a majorMode`);\n }\n if (!isRecord(parsed.baseline) || typeof parsed.baseline.themePath !== 'string') {\n throw new Error(`Doom sync state at ${statePath} requires a baseline with a themePath`);\n }\n const { majorMode, domains, profile, preset } = parsed.selection;\n const fileState = isRecord(parsed.fileState) ? parsed.fileState : {};\n return {\n version: SYNC_STATE_VERSION,\n root: parsed.root,\n inputsHash: parsed.inputsHash,\n selection: {\n majorMode,\n domains: Array.isArray(domains) ? domains.filter((name): name is string => typeof name === 'string') : [],\n profile: typeof profile === 'string' ? profile : undefined,\n preset: typeof preset === 'string' ? preset : DEFAULT_PRESET,\n },\n env: stringRecord(parsed.env, 'env', statePath),\n fileState: {\n profileEnvironment: isRecord(fileState.profileEnvironment)\n ? (fileState.profileEnvironment as Record<string, string>)\n : {},\n pluginHooks: Array.isArray(fileState.pluginHooks) ? (fileState.pluginHooks as HarnessState['pluginHooks']) : [],\n },\n resolved: stringRecord(parsed.resolved, 'resolved', statePath),\n // Absent from a state written before compilation existed, which is why it\n // is read back optionally rather than demanded.\n compiled: parsed.compiled === undefined ? undefined : stringRecord(parsed.compiled, 'compiled', statePath),\n baseline: parsed.baseline as unknown as SyncBaseline,\n };\n}\n\n/** Reads the synced state, or undefined when the repository was never synced. */\nexport function readSyncState(repoRoot: string): SyncState | undefined {\n const statePath = syncStatePath(repoRoot);\n if (!fs.existsSync(statePath)) return undefined;\n return parseSyncState(fs.readFileSync(statePath, 'utf8'), statePath);\n}\n\nexport function serializeSyncState(state: SyncState): string {\n return `${JSON.stringify(state, null, 2)}\\n`;\n}\n\nexport async function writeSyncState(repoRoot: string, state: SyncState): Promise<string> {\n const statePath = syncStatePath(repoRoot);\n await fs.promises.mkdir(path.dirname(statePath), { mode: PRIVATE_DIRECTORY_MODE, recursive: true });\n // Holds resolved environment values, so it stays readable by its owner only.\n await fs.promises.writeFile(statePath, serializeSyncState(state), { mode: PRIVATE_FILE_MODE });\n return statePath;\n}\n\nexport function ownKey(name: string): string {\n return `${OWN_PREFIX}${name}`;\n}\n\nexport function packageKey(specifier: string): string {\n return `${PACKAGE_PREFIX}${specifier}`;\n}\n\n/**\n * Keyed by the absolute target, because the same relative specifier means\n * different files under the repository and the global config directory.\n */\nexport function localKey(specifier: string, baseDirectory: string): string {\n return `${LOCAL_PREFIX}${path.resolve(baseDirectory, specifier)}`;\n}\n\nexport interface RecordingResolvers extends LayerResolvers {\n /** Every name this resolver was asked for, keyed for the state file. */\n readonly resolved: Record<string, string>;\n}\n\n/**\n * Resolvers that record what they resolve.\n *\n * Sync composes once with every layer selected and hands the recording to the\n * state file, so the extension never has to resolve a specifier itself.\n */\nexport function createRecordingResolvers(base: LayerResolvers = LAYER_RESOLVERS): RecordingResolvers {\n const resolved: Record<string, string> = {};\n return {\n resolved,\n ownEntry(name) {\n const entry = base.ownEntry(name);\n resolved[ownKey(name)] = entry;\n return entry;\n },\n packageEntry(name) {\n const entry = base.packageEntry(name);\n resolved[packageKey(name)] = entry;\n return entry;\n },\n optionalPackageEntry(name) {\n const entry = base.optionalPackageEntry(name);\n // A miss is recorded by omission, which is what later makes the extension\n // treat it as absent instead of failing the whole composition.\n if (entry) resolved[packageKey(name)] = entry;\n return entry;\n },\n localEntry(specifier, baseDirectory) {\n const entry = base.localEntry(specifier, baseDirectory);\n if (entry) resolved[localKey(specifier, baseDirectory)] = entry;\n return entry;\n },\n };\n}\n\n/**\n * Resolves every entry any layer could contribute, in one pass.\n *\n * Composing with every layer selected and the widest options is what makes the\n * map complete: a later `/major-mode` switch inside a Pi session can then pick a\n * different set without resolving anything itself. The context packages and the\n * persona entry are asked for separately because they reach `assembleExtensions`\n * as already-resolved paths rather than through the resolvers.\n */\nexport function recordResolvedEntries(\n majorModesConfig: MajorModesConfig,\n base: LayerResolvers = LAYER_RESOLVERS,\n): Record<string, string> {\n const recording = createRecordingResolvers(base);\n for (const specifier of Object.values(CONTEXT_PACKAGES)) recording.optionalPackageEntry(specifier);\n recording.ownEntry(PERSONA_ENTRY);\n assembleExtensions({\n agents: true,\n autoStop: true,\n mute: false,\n // The one preset that contributes an extension of its own.\n preset: OLLAMA_PRESET,\n layers: Object.keys(majorModesConfig.layers),\n majorModesConfig,\n resolvers: recording,\n });\n return recording.resolved;\n}\n\n/** Resolvers backed by a synced map, for composing inside a Pi session. */\nexport function createMapResolvers(\n resolved: Record<string, string>,\n compiled: Record<string, string> = {},\n): LayerResolvers {\n // The compiled bundle when this entry has one, the source otherwise.\n const entryFor = (key: string): string | undefined => compiled[key] ?? resolved[key];\n const lookup = (key: string, name: string): string => {\n const entry = entryFor(key);\n if (!entry) throw new Error(`${name} is missing from the doompi sync state. Run doompi sync.`);\n return entry;\n };\n return {\n ownEntry: (name) => lookup(ownKey(name), name),\n packageEntry: (name) => lookup(packageKey(name), name),\n optionalPackageEntry: (name) => entryFor(packageKey(name)),\n localEntry: (specifier, baseDirectory) => entryFor(localKey(specifier, baseDirectory)),\n };\n}\n\n/** Reads the resolved MCP servers back out of a generated config. */\nexport function readMcpServerNames(configPath: string): string[] {\n if (!fs.existsSync(configPath)) return [];\n const config = JSON.parse(fs.readFileSync(configPath, 'utf8')) as JsonObject;\n return isRecord(config.mcpServers) ? Object.keys(config.mcpServers) : [];\n}\n"],"mappings":"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"}
@@ -0,0 +1 @@
1
+ Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require(`./json.cjs`),t=require(`./moduleResolution.cjs`),n=require(`./repository.cjs`),r=require(`./toolNames.cjs`);exports.consumerPackageEntry=t.consumerPackageEntry,exports.findRepositoryRoot=n.findRepositoryRoot,exports.isRecord=e.isRecord,exports.isRepositoryRoot=n.isRepositoryRoot,exports.localEntry=t.localEntry,exports.optionalPackageEntry=t.optionalPackageEntry,exports.ownEntry=t.ownEntry,exports.packageEntry=t.packageEntry,exports.piCliPath=t.piCliPath,exports.readJson=e.readJson,exports.splitPackageSpecifier=t.splitPackageSpecifier,exports.toClaudeToolName=r.toClaudeToolName,exports.toPiToolName=r.toPiToolName,exports.writeFileAtomic=e.writeFileAtomic,exports.writeJson=e.writeJson;
@@ -0,0 +1,5 @@
1
+ import { JsonObject, isRecord, readJson, writeFileAtomic, writeJson } from "./json.cjs";
2
+ import { consumerPackageEntry, localEntry, optionalPackageEntry, ownEntry, packageEntry, piCliPath, splitPackageSpecifier } from "./moduleResolution.cjs";
3
+ import { findRepositoryRoot, isRepositoryRoot } from "./repository.cjs";
4
+ import { toClaudeToolName, toPiToolName } from "./toolNames.cjs";
5
+ export { type JsonObject, consumerPackageEntry, findRepositoryRoot, isRecord, isRepositoryRoot, localEntry, optionalPackageEntry, ownEntry, packageEntry, piCliPath, readJson, splitPackageSpecifier, toClaudeToolName, toPiToolName, writeFileAtomic, writeJson };
@@ -0,0 +1,5 @@
1
+ import { JsonObject, isRecord, readJson, writeFileAtomic, writeJson } from "./json.mjs";
2
+ import { consumerPackageEntry, localEntry, optionalPackageEntry, ownEntry, packageEntry, piCliPath, splitPackageSpecifier } from "./moduleResolution.mjs";
3
+ import { findRepositoryRoot, isRepositoryRoot } from "./repository.mjs";
4
+ import { toClaudeToolName, toPiToolName } from "./toolNames.mjs";
5
+ export { type JsonObject, consumerPackageEntry, findRepositoryRoot, isRecord, isRepositoryRoot, localEntry, optionalPackageEntry, ownEntry, packageEntry, piCliPath, readJson, splitPackageSpecifier, toClaudeToolName, toPiToolName, writeFileAtomic, writeJson };
@@ -0,0 +1 @@
1
+ import{isRecord as e,readJson as t,writeFileAtomic as n,writeJson as r}from"./json.mjs";import{consumerPackageEntry as i,localEntry as a,optionalPackageEntry as o,ownEntry as s,packageEntry as c,piCliPath as l,splitPackageSpecifier as u}from"./moduleResolution.mjs";import{findRepositoryRoot as d,isRepositoryRoot as f}from"./repository.mjs";import{toClaudeToolName as p,toPiToolName as m}from"./toolNames.mjs";export{i as consumerPackageEntry,d as findRepositoryRoot,e as isRecord,f as isRepositoryRoot,a as localEntry,o as optionalPackageEntry,s as ownEntry,c as packageEntry,l as piCliPath,t as readJson,u as splitPackageSpecifier,p as toClaudeToolName,m as toPiToolName,n as writeFileAtomic,r as writeJson};
@@ -0,0 +1,2 @@
1
+ Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require(`../_virtual/_rolldown/runtime.cjs`);let t=require(`node:path`);t=e.__toESM(t,1);let n=require(`node:fs`);n=e.__toESM(n,1);let r=require(`node:crypto`);function i(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}function a(e,t={}){if(!n.default.existsSync(e))return t;let r=n.default.readFileSync(e,`utf8`).trim();return r?JSON.parse(r):t}function o(e,i){n.default.mkdirSync(t.default.dirname(e),{recursive:!0});let a=`${e}.${process.pid}.${(0,r.randomUUID)()}.tmp`;try{n.default.writeFileSync(a,i),n.default.renameSync(a,e)}catch(e){throw n.default.rmSync(a,{force:!0}),e}}function s(e,t){o(e,`${JSON.stringify(t,null,2)}\n`)}exports.isRecord=i,exports.readJson=a,exports.writeFileAtomic=o,exports.writeJson=s;
2
+ //# sourceMappingURL=json.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"json.cjs","names":["fs","path"],"sources":["../../src/utils/json.ts"],"sourcesContent":["import { randomUUID } from 'node:crypto';\nimport fs from 'node:fs';\nimport path from 'node:path';\n\n/**\n * JSON state helpers for files the harness owns on disk.\n *\n * Compatibility frontends read these files while another repository's harness\n * may be writing them, so writes go through a rename rather than a truncate.\n */\n\nexport type JsonObject = Record<string, unknown>;\n\nconst ATOMIC_FILE_SUFFIX = '.tmp';\n\nexport function isRecord(value: unknown): value is JsonObject {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\n/** Reads a JSON object, treating a missing or empty file as the fallback. */\nexport function readJson(filePath: string, fallback: JsonObject = {}): JsonObject {\n if (!fs.existsSync(filePath)) return fallback;\n const content = fs.readFileSync(filePath, 'utf8').trim();\n return content ? (JSON.parse(content) as JsonObject) : fallback;\n}\n\n/**\n * Writes through a uniquely named temporary file in the same directory.\n *\n * The pid and a UUID are both in the name so two harnesses writing the same\n * target cannot collide on the temporary path, and a failed write cleans up\n * after itself rather than leaving the fragment behind.\n */\nexport function writeFileAtomic(filePath: string, content: string): void {\n fs.mkdirSync(path.dirname(filePath), { recursive: true });\n const temporaryPath = `${filePath}.${process.pid}.${randomUUID()}${ATOMIC_FILE_SUFFIX}`;\n try {\n fs.writeFileSync(temporaryPath, content);\n fs.renameSync(temporaryPath, filePath);\n } catch (error) {\n fs.rmSync(temporaryPath, { force: true });\n throw error;\n }\n}\n\nexport function writeJson(filePath: string, value: JsonObject): void {\n writeFileAtomic(filePath, `${JSON.stringify(value, null, 2)}\\n`);\n}\n"],"mappings":"2OAeA,SAAgB,EAAS,EAAqC,CAC5D,OAAO,OAAO,GAAU,YAAY,GAAkB,CAAC,MAAM,QAAQ,EAAM,CAI7E,SAAgB,EAAS,EAAkB,EAAuB,EAAE,CAAc,CAChF,GAAI,CAACA,EAAAA,QAAG,WAAW,EAAS,CAAE,OAAO,EACrC,IAAM,EAAUA,EAAAA,QAAG,aAAa,EAAU,OAAO,CAAC,MAAM,CACxD,OAAO,EAAW,KAAK,MAAM,EAAQ,CAAkB,EAUzD,SAAgB,EAAgB,EAAkB,EAAuB,CACvE,EAAA,QAAG,UAAUC,EAAAA,QAAK,QAAQ,EAAS,CAAE,CAAE,UAAW,GAAM,CAAC,CACzD,IAAM,EAAgB,GAAG,EAAS,GAAG,QAAQ,IAAI,IAAA,EAAA,EAAA,aAAe,OAChE,GAAI,CACF,EAAA,QAAG,cAAc,EAAe,EAAQ,CACxC,EAAA,QAAG,WAAW,EAAe,EAAS,OAC/B,EAAO,CAEd,MADA,EAAA,QAAG,OAAO,EAAe,CAAE,MAAO,GAAM,CAAC,CACnC,GAIV,SAAgB,EAAU,EAAkB,EAAyB,CACnE,EAAgB,EAAU,GAAG,KAAK,UAAU,EAAO,KAAM,EAAE,CAAC,IAAI"}
@@ -0,0 +1,23 @@
1
+ //#region src/utils/json.d.ts
2
+ /**
3
+ * JSON state helpers for files the harness owns on disk.
4
+ *
5
+ * Compatibility frontends read these files while another repository's harness
6
+ * may be writing them, so writes go through a rename rather than a truncate.
7
+ */
8
+ type JsonObject = Record<string, unknown>;
9
+ declare function isRecord(value: unknown): value is JsonObject;
10
+ /** Reads a JSON object, treating a missing or empty file as the fallback. */
11
+ declare function readJson(filePath: string, fallback?: JsonObject): JsonObject;
12
+ /**
13
+ * Writes through a uniquely named temporary file in the same directory.
14
+ *
15
+ * The pid and a UUID are both in the name so two harnesses writing the same
16
+ * target cannot collide on the temporary path, and a failed write cleans up
17
+ * after itself rather than leaving the fragment behind.
18
+ */
19
+ declare function writeFileAtomic(filePath: string, content: string): void;
20
+ declare function writeJson(filePath: string, value: JsonObject): void;
21
+ //#endregion
22
+ export { JsonObject, isRecord, readJson, writeFileAtomic, writeJson };
23
+ //# sourceMappingURL=json.d.cts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"json.d.cts","names":[],"sources":["../../src/utils/json.ts"],"mappings":";;AAWA;;;;;KAAY,UAAA,GAAa,MAAA;AAAA,iBAIT,QAAA,CAAS,KAAA,YAAiB,KAAA,IAAS,UAAA;;iBAKnC,QAAA,CAAS,QAAA,UAAkB,QAAA,GAAU,UAAA,GAAkB,UAAA;;;;;;AAAvE;;iBAagB,eAAA,CAAgB,QAAA,UAAkB,OAAA;AAAA,iBAYlC,SAAA,CAAU,QAAA,UAAkB,KAAA,EAAO,UAAA"}
@@ -0,0 +1,23 @@
1
+ //#region src/utils/json.d.ts
2
+ /**
3
+ * JSON state helpers for files the harness owns on disk.
4
+ *
5
+ * Compatibility frontends read these files while another repository's harness
6
+ * may be writing them, so writes go through a rename rather than a truncate.
7
+ */
8
+ type JsonObject = Record<string, unknown>;
9
+ declare function isRecord(value: unknown): value is JsonObject;
10
+ /** Reads a JSON object, treating a missing or empty file as the fallback. */
11
+ declare function readJson(filePath: string, fallback?: JsonObject): JsonObject;
12
+ /**
13
+ * Writes through a uniquely named temporary file in the same directory.
14
+ *
15
+ * The pid and a UUID are both in the name so two harnesses writing the same
16
+ * target cannot collide on the temporary path, and a failed write cleans up
17
+ * after itself rather than leaving the fragment behind.
18
+ */
19
+ declare function writeFileAtomic(filePath: string, content: string): void;
20
+ declare function writeJson(filePath: string, value: JsonObject): void;
21
+ //#endregion
22
+ export { JsonObject, isRecord, readJson, writeFileAtomic, writeJson };
23
+ //# sourceMappingURL=json.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"json.d.mts","names":[],"sources":["../../src/utils/json.ts"],"mappings":";;AAWA;;;;;KAAY,UAAA,GAAa,MAAA;AAAA,iBAIT,QAAA,CAAS,KAAA,YAAiB,KAAA,IAAS,UAAA;;iBAKnC,QAAA,CAAS,QAAA,UAAkB,QAAA,GAAU,UAAA,GAAkB,UAAA;;;;;;AAAvE;;iBAagB,eAAA,CAAgB,QAAA,UAAkB,OAAA;AAAA,iBAYlC,SAAA,CAAU,QAAA,UAAkB,KAAA,EAAO,UAAA"}
@@ -0,0 +1,2 @@
1
+ import e from"node:path";import t from"node:fs";import{randomUUID as n}from"node:crypto";function r(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}function i(e,n={}){if(!t.existsSync(e))return n;let r=t.readFileSync(e,`utf8`).trim();return r?JSON.parse(r):n}function a(r,i){t.mkdirSync(e.dirname(r),{recursive:!0});let a=`${r}.${process.pid}.${n()}.tmp`;try{t.writeFileSync(a,i),t.renameSync(a,r)}catch(e){throw t.rmSync(a,{force:!0}),e}}function o(e,t){a(e,`${JSON.stringify(t,null,2)}\n`)}export{r as isRecord,i as readJson,a as writeFileAtomic,o as writeJson};
2
+ //# sourceMappingURL=json.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"json.mjs","names":[],"sources":["../../src/utils/json.ts"],"sourcesContent":["import { randomUUID } from 'node:crypto';\nimport fs from 'node:fs';\nimport path from 'node:path';\n\n/**\n * JSON state helpers for files the harness owns on disk.\n *\n * Compatibility frontends read these files while another repository's harness\n * may be writing them, so writes go through a rename rather than a truncate.\n */\n\nexport type JsonObject = Record<string, unknown>;\n\nconst ATOMIC_FILE_SUFFIX = '.tmp';\n\nexport function isRecord(value: unknown): value is JsonObject {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\n/** Reads a JSON object, treating a missing or empty file as the fallback. */\nexport function readJson(filePath: string, fallback: JsonObject = {}): JsonObject {\n if (!fs.existsSync(filePath)) return fallback;\n const content = fs.readFileSync(filePath, 'utf8').trim();\n return content ? (JSON.parse(content) as JsonObject) : fallback;\n}\n\n/**\n * Writes through a uniquely named temporary file in the same directory.\n *\n * The pid and a UUID are both in the name so two harnesses writing the same\n * target cannot collide on the temporary path, and a failed write cleans up\n * after itself rather than leaving the fragment behind.\n */\nexport function writeFileAtomic(filePath: string, content: string): void {\n fs.mkdirSync(path.dirname(filePath), { recursive: true });\n const temporaryPath = `${filePath}.${process.pid}.${randomUUID()}${ATOMIC_FILE_SUFFIX}`;\n try {\n fs.writeFileSync(temporaryPath, content);\n fs.renameSync(temporaryPath, filePath);\n } catch (error) {\n fs.rmSync(temporaryPath, { force: true });\n throw error;\n }\n}\n\nexport function writeJson(filePath: string, value: JsonObject): void {\n writeFileAtomic(filePath, `${JSON.stringify(value, null, 2)}\\n`);\n}\n"],"mappings":"yFAeA,SAAgB,EAAS,EAAqC,CAC5D,OAAO,OAAO,GAAU,YAAY,GAAkB,CAAC,MAAM,QAAQ,EAAM,CAI7E,SAAgB,EAAS,EAAkB,EAAuB,EAAE,CAAc,CAChF,GAAI,CAAC,EAAG,WAAW,EAAS,CAAE,OAAO,EACrC,IAAM,EAAU,EAAG,aAAa,EAAU,OAAO,CAAC,MAAM,CACxD,OAAO,EAAW,KAAK,MAAM,EAAQ,CAAkB,EAUzD,SAAgB,EAAgB,EAAkB,EAAuB,CACvE,EAAG,UAAU,EAAK,QAAQ,EAAS,CAAE,CAAE,UAAW,GAAM,CAAC,CACzD,IAAM,EAAgB,GAAG,EAAS,GAAG,QAAQ,IAAI,GAAG,GAAY,OAChE,GAAI,CACF,EAAG,cAAc,EAAe,EAAQ,CACxC,EAAG,WAAW,EAAe,EAAS,OAC/B,EAAO,CAEd,MADA,EAAG,OAAO,EAAe,CAAE,MAAO,GAAM,CAAC,CACnC,GAIV,SAAgB,EAAU,EAAkB,EAAyB,CACnE,EAAgB,EAAU,GAAG,KAAK,UAAU,EAAO,KAAM,EAAE,CAAC,IAAI"}
@@ -0,0 +1,2 @@
1
+ Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require(`../_virtual/_rolldown/runtime.cjs`);let t=require(`node:path`);t=e.__toESM(t,1);let n=require(`node:fs`);n=e.__toESM(n,1);let r=require(`node:url`);const i=`package.json`,a=[`import`,`module`,`node`,`default`];function o(e){return(0,r.fileURLToPath)({}.resolve(e))}function s(e){try{return o(e)}catch{return}}function c(e){let t=e.split(`/`),n=e.startsWith(`@`)?2:1,r=t.slice(0,n).join(`/`),i=t.slice(n).join(`/`);return{name:r,subpath:i?`./${i}`:`.`}}function l(e){if(typeof e==`string`)return e;if(Array.isArray(e)){for(let t of e){let e=l(t);if(e)return e}return}if(typeof e!=`object`||!e)return;let t=e;for(let e of a)if(e in t){let n=l(t[e]);if(n)return n}}function u(e,r){let a=t.default.resolve(r);for(;;){let r=t.default.join(a,`node_modules`,e);if(n.default.existsSync(t.default.join(r,i)))return r;let o=t.default.dirname(a);if(o===a)return;a=o}}function d(e,r){let{name:a,subpath:o}=c(e),s=u(a,r);if(!s)return;let d=t.default.join(s,i),f=t.default.join(s,o),p;try{p=JSON.parse(n.default.readFileSync(d,`utf8`))}catch{return n.default.existsSync(f)?f:void 0}let m=p.exports;if(m!=null){let e=l(typeof m==`object`&&!Array.isArray(m)?m[o]:o===`.`?m:void 0);if(e){let r=t.default.join(s,e);if(n.default.existsSync(r))return r}}return o===`.`?void 0:n.default.existsSync(f)?f:void 0}const f=[`module`,`main`];function p(e,r){let a=t.default.resolve(r,e),o;try{let e=n.default.statSync(a);if(e.isFile())return a;o=e.isDirectory()}catch{return}if(!o)return;let s;try{s=JSON.parse(n.default.readFileSync(t.default.join(a,i),`utf8`))}catch{return}let c=s.exports,u=[l(typeof c==`object`&&c&&!Array.isArray(c)?c[`.`]:c)];for(let e of f)typeof s[e]==`string`&&u.push(s[e]);for(let e of u){if(!e)continue;let r=t.default.join(a,e);if(n.default.existsSync(r))return r}}function m(e){let n=require(`url`).pathToFileURL(__filename).href.endsWith(`.ts`)?`ts`:`mjs`,i=t.default.dirname(t.default.dirname((0,r.fileURLToPath)(require(`url`).pathToFileURL(__filename).href)));return t.default.join(i,`entries`,`${e}.${n}`)}function h(){return t.default.join(t.default.dirname(o(`@earendil-works/pi-coding-agent`)),`cli.js`)}exports.consumerPackageEntry=d,exports.localEntry=p,exports.optionalPackageEntry=s,exports.ownEntry=m,exports.packageEntry=o,exports.piCliPath=h,exports.splitPackageSpecifier=c;
2
+ //# sourceMappingURL=moduleResolution.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"moduleResolution.cjs","names":["path","fs"],"sources":["../../src/utils/moduleResolution.ts"],"sourcesContent":["import fs from 'node:fs';\nimport path from 'node:path';\nimport { fileURLToPath } from 'node:url';\n\nconst PACKAGE_MANIFEST = 'package.json';\nconst NODE_MODULES = 'node_modules';\nconst CURRENT_DIRECTORY_PREFIX = './';\n/** Import-side conditions, in the order Node prefers them for an ESM consumer. */\nconst IMPORT_CONDITIONS = ['import', 'module', 'node', 'default'] as const;\n\n/**\n * Resolves the on-disk entry point of an installed package.\n *\n * Pi loads extensions by absolute path, so anything contributed as an\n * extension has to be resolved to a file before it can be passed along.\n */\nexport function packageEntry(name: string): string {\n return fileURLToPath(import.meta.resolve(name));\n}\n\n/** Same as packageEntry, but returns undefined when the package is absent. */\nexport function optionalPackageEntry(name: string): string | undefined {\n try {\n return packageEntry(name);\n } catch {\n return undefined;\n }\n}\n\n/**\n * Splits `@scope/name/sub/path` into the package name and its export subpath.\n *\n * A scoped name owns two segments, an unscoped name one. Everything after that\n * is the subpath the package's own `exports` map has to answer for.\n */\nexport function splitPackageSpecifier(specifier: string): { name: string; subpath: string } {\n const segments = specifier.split('/');\n const nameSegments = specifier.startsWith('@') ? 2 : 1;\n const name = segments.slice(0, nameSegments).join('/');\n const rest = segments.slice(nameSegments).join('/');\n return { name, subpath: rest ? `${CURRENT_DIRECTORY_PREFIX}${rest}` : '.' };\n}\n\n/** Picks the import-side target out of one `exports` entry. */\nfunction selectImportTarget(value: unknown): string | undefined {\n if (typeof value === 'string') return value;\n if (Array.isArray(value)) {\n for (const candidate of value) {\n const target = selectImportTarget(candidate);\n if (target) return target;\n }\n return undefined;\n }\n if (typeof value !== 'object' || value === null) return undefined;\n const conditions = value as Record<string, unknown>;\n for (const condition of IMPORT_CONDITIONS) {\n if (condition in conditions) {\n const target = selectImportTarget(conditions[condition]);\n if (target) return target;\n }\n }\n return undefined;\n}\n\n/**\n * Finds an installed package directory by walking the consumer's module chain.\n *\n * Node's own lookup order, so a workspace link in the repository root is found\n * exactly where the repository installed it, and nothing outside that chain can\n * answer for a specifier the repository declared.\n */\nfunction findInstalledPackage(name: string, consumerRoot: string): string | undefined {\n let directory = path.resolve(consumerRoot);\n while (true) {\n const candidate = path.join(directory, NODE_MODULES, name);\n if (fs.existsSync(path.join(candidate, PACKAGE_MANIFEST))) return candidate;\n const parent = path.dirname(directory);\n if (parent === directory) return undefined;\n directory = parent;\n }\n}\n\n/**\n * Resolves a specifier against the consumer repository's own dependency tree.\n *\n * The meta-package cannot resolve what the repository declares: under an\n * isolated node_modules layout Doom Pi only sees its own dependencies. Anchoring\n * a require at the consumer's manifest is what lets a repository-declared layer\n * package resolve from the repository that declared it.\n *\n * The `node_modules` chain is walked directly rather than handed to\n * `require.resolve`, which would answer with the CJS build that Pi cannot load\n * as an extension. The package's own `exports` map then supplies the\n * import-side target.\n */\nexport function consumerPackageEntry(specifier: string, consumerRoot: string): string | undefined {\n const { name, subpath } = splitPackageSpecifier(specifier);\n const packageRoot = findInstalledPackage(name, consumerRoot);\n if (!packageRoot) return undefined;\n\n const manifestPath = path.join(packageRoot, PACKAGE_MANIFEST);\n // A relative subpath that the package does not export is still loadable by\n // path, which is how layers point at a file inside a package (`.../index.ts`).\n const directTarget = path.join(packageRoot, subpath);\n let manifest: { exports?: unknown };\n try {\n manifest = JSON.parse(fs.readFileSync(manifestPath, 'utf8')) as { exports?: unknown };\n } catch {\n return fs.existsSync(directTarget) ? directTarget : undefined;\n }\n\n const exports = manifest.exports;\n if (exports !== undefined && exports !== null) {\n const entry =\n typeof exports === 'object' && !Array.isArray(exports)\n ? (exports as Record<string, unknown>)[subpath]\n : subpath === '.'\n ? exports\n : undefined;\n const target = selectImportTarget(entry);\n if (target) {\n const resolved = path.join(packageRoot, target);\n if (fs.existsSync(resolved)) return resolved;\n }\n }\n\n return subpath === '.' ? undefined : fs.existsSync(directTarget) ? directTarget : undefined;\n}\n\n/** Manifest fields consulted when a local package declares no `exports`. */\nconst MAIN_FIELDS = ['module', 'main'] as const;\n\n/**\n * Resolves a path-style layer package against the config that declared it.\n *\n * A file is taken as the extension entry as written. A directory is read as a\n * package: its import-side `exports` target first, then module/main. This is\n * what lets an extension that was never published to npm live in the\n * repository, or beside the global config in `~/.pi/.doom`, and still load.\n */\nexport function localEntry(specifier: string, baseDirectory: string): string | undefined {\n const target = path.resolve(baseDirectory, specifier);\n let isDirectory: boolean;\n try {\n const stats = fs.statSync(target);\n if (stats.isFile()) return target;\n isDirectory = stats.isDirectory();\n } catch {\n return undefined;\n }\n if (!isDirectory) return undefined;\n\n let manifest: { exports?: unknown; module?: unknown; main?: unknown };\n try {\n manifest = JSON.parse(fs.readFileSync(path.join(target, PACKAGE_MANIFEST), 'utf8')) as typeof manifest;\n } catch {\n return undefined;\n }\n\n const exports = manifest.exports;\n const rootExport =\n typeof exports === 'object' && exports !== null && !Array.isArray(exports)\n ? (exports as Record<string, unknown>)['.']\n : exports;\n const candidates = [selectImportTarget(rootExport)];\n for (const field of MAIN_FIELDS) {\n if (typeof manifest[field] === 'string') candidates.push(manifest[field]);\n }\n\n for (const candidate of candidates) {\n if (!candidate) continue;\n const resolved = path.join(target, candidate);\n if (fs.existsSync(resolved)) return resolved;\n }\n return undefined;\n}\n\n/**\n * Resolves one of this package's own extensions in src/entries.\n *\n * The file extension differs between running from source (.ts, how the shell\n * launchers invoke this) and running the build output (.mjs), so it is derived\n * from this module's own URL rather than assumed.\n */\nexport function ownEntry(name: string): string {\n const extension = import.meta.url.endsWith('.ts') ? 'ts' : 'mjs';\n // This file sits in src/utils, so entries is one directory up.\n const sourceRoot = path.dirname(path.dirname(fileURLToPath(import.meta.url)));\n return path.join(sourceRoot, 'entries', `${name}.${extension}`);\n}\n\n/** Path to Pi's own CLI, which the launch command spawns. */\nexport function piCliPath(): string {\n return path.join(path.dirname(packageEntry('@earendil-works/pi-coding-agent')), 'cli.js');\n}\n"],"mappings":"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"}