@pi-unipi/unipi 3.0.0-alpha.2 → 3.0.0-alpha.21

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 (359) hide show
  1. package/CHANGELOG.md +292 -0
  2. package/README.md +8 -11
  3. package/docs/prefix-cache-architecture.md +14 -0
  4. package/package.json +22 -24
  5. package/packages/ask-user/README.md +55 -107
  6. package/packages/ask-user/ask-ui.ts +306 -732
  7. package/packages/ask-user/clipboard.ts +50 -0
  8. package/packages/ask-user/config.ts +74 -78
  9. package/packages/ask-user/package.json +2 -2
  10. package/packages/ask-user/questions.ts +151 -0
  11. package/packages/ask-user/skills/ask-user/SKILL.md +59 -133
  12. package/packages/ask-user/tools.ts +198 -432
  13. package/packages/autocomplete/package.json +1 -1
  14. package/packages/autocomplete/src/constants.ts +38 -43
  15. package/packages/autocomplete/src/sorting.ts +8 -0
  16. package/packages/background-tasks/package.json +2 -2
  17. package/packages/background-tasks/src/config.ts +0 -73
  18. package/packages/background-tasks/src/index.ts +20 -16
  19. package/packages/background-tasks/src/registry.ts +1 -171
  20. package/packages/background-tasks/src/types.ts +0 -32
  21. package/packages/btw/README.md +13 -56
  22. package/packages/btw/extensions/btw.ts +403 -1796
  23. package/packages/btw/package.json +5 -2
  24. package/packages/btw/skills/btw/SKILL.md +7 -143
  25. package/packages/compactor/README.md +56 -287
  26. package/packages/compactor/package.json +5 -9
  27. package/packages/compactor/skills/compactor/SKILL.md +13 -20
  28. package/packages/compactor/src/card.ts +90 -0
  29. package/packages/compactor/src/commands/index.ts +73 -130
  30. package/packages/compactor/src/compaction/compact-args.ts +5 -3
  31. package/packages/compactor/src/compaction/filter-noise.ts +1 -1
  32. package/packages/compactor/src/compaction/hooks.ts +487 -311
  33. package/packages/compactor/src/compaction/source.ts +178 -0
  34. package/packages/compactor/src/compaction/summarize.ts +586 -52
  35. package/packages/compactor/src/config/manager.ts +130 -127
  36. package/packages/compactor/src/config/schema.ts +32 -48
  37. package/packages/compactor/src/index.ts +40 -431
  38. package/packages/compactor/src/info-screen.ts +44 -190
  39. package/packages/compactor/src/session/recall-blocks.ts +1 -1
  40. package/packages/compactor/src/stats.ts +76 -0
  41. package/packages/compactor/src/tools/context-budget.ts +17 -34
  42. package/packages/compactor/src/tools/ctx-doctor.ts +45 -36
  43. package/packages/compactor/src/tools/register.ts +29 -269
  44. package/packages/compactor/src/types.ts +47 -186
  45. package/packages/core/README.md +25 -0
  46. package/packages/core/child-agent.ts +1 -0
  47. package/packages/core/command-echo.ts +63 -0
  48. package/packages/core/compaction-context.ts +41 -0
  49. package/packages/core/constants.ts +8 -38
  50. package/packages/core/events.ts +0 -53
  51. package/packages/core/fusion-status.ts +4 -0
  52. package/packages/core/global.d.ts +10 -0
  53. package/packages/core/harness-messages.ts +392 -0
  54. package/packages/core/index.ts +14 -0
  55. package/packages/core/kanboard-status.ts +49 -0
  56. package/packages/core/long-horizon-owner-status.ts +46 -0
  57. package/packages/core/model-cache.ts +81 -7
  58. package/packages/core/package.json +6 -3
  59. package/packages/core/src/attach/detect.ts +163 -0
  60. package/packages/core/src/child-agent/child-args.ts +31 -0
  61. package/packages/core/src/child-agent/delivery.ts +41 -0
  62. package/packages/core/src/child-agent/index.ts +5 -0
  63. package/packages/core/src/child-agent/readers.ts +203 -0
  64. package/packages/core/src/child-agent/runtime.ts +584 -0
  65. package/packages/core/src/evidence.ts +78 -0
  66. package/packages/core/src/hints/browser.ts +278 -0
  67. package/packages/core/src/hints/crab-data.ts +19 -0
  68. package/packages/core/src/hints/index.ts +865 -0
  69. package/packages/core/src/hints/lines.ts +607 -0
  70. package/packages/core/src/hints/store.ts +120 -0
  71. package/packages/core/src/jev/chatter.ts +24 -0
  72. package/packages/core/src/jev/settings.ts +177 -14
  73. package/packages/core/src/package-colors.ts +2 -0
  74. package/packages/core/src/settings/catalog.ts +39 -15
  75. package/packages/core/src/settings/engine.ts +48 -2
  76. package/packages/core/src/settings/hub.ts +83 -20
  77. package/packages/core/src/settings/open.ts +49 -0
  78. package/packages/core/src/settings/schema.ts +27 -7
  79. package/packages/core/src/tui/hub-kit.ts +18 -5
  80. package/packages/core/src/tui/kit.ts +198 -0
  81. package/packages/core/src/tui/progress.ts +73 -0
  82. package/packages/core/src/turn/arbiter.ts +290 -0
  83. package/packages/footer/README.md +4 -6
  84. package/packages/footer/package.json +3 -3
  85. package/packages/footer/src/glance-editor.ts +19 -11
  86. package/packages/footer/src/help.ts +1 -1
  87. package/packages/footer/src/index.ts +36 -3
  88. package/packages/footer/src/presets.ts +3 -4
  89. package/packages/footer/src/rendering/icons.ts +0 -20
  90. package/packages/footer/src/segments/compactor.ts +52 -142
  91. package/packages/footer/src/segments/status-ext.ts +0 -1
  92. package/packages/fusion/README.md +14 -4
  93. package/packages/fusion/package.json +4 -3
  94. package/packages/fusion/src/child-args.ts +1 -0
  95. package/packages/fusion/src/index.ts +161 -8
  96. package/packages/fusion/src/pi-settings.ts +57 -0
  97. package/packages/fusion/src/picker.ts +186 -51
  98. package/packages/fusion/src/preset.ts +56 -2
  99. package/packages/fusion/src/prompts.ts +1 -1
  100. package/packages/fusion/src/sidekick-runtime.ts +1 -420
  101. package/packages/fusion/src/tools.ts +107 -128
  102. package/packages/fusion/src/transcript.ts +27 -85
  103. package/packages/info-screen/package.json +2 -2
  104. package/packages/input-shortcuts/README.md +18 -30
  105. package/packages/input-shortcuts/package.json +2 -2
  106. package/packages/input-shortcuts/src/burst.ts +88 -0
  107. package/packages/input-shortcuts/src/chord-overlay.ts +20 -70
  108. package/packages/input-shortcuts/src/cleared-input.ts +97 -0
  109. package/packages/input-shortcuts/src/index.ts +99 -193
  110. package/packages/input-shortcuts/src/last-response.ts +40 -0
  111. package/packages/input-shortcuts/src/registers.ts +8 -23
  112. package/packages/input-shortcuts/src/types.ts +4 -16
  113. package/packages/input-shortcuts/src/undo-redo.ts +59 -47
  114. package/packages/kanboard/README.md +51 -47
  115. package/packages/kanboard/index.ts +293 -55
  116. package/packages/kanboard/package.json +8 -8
  117. package/packages/kanboard/skills/kanboard/SKILL.md +61 -27
  118. package/packages/kanboard/src/badges.ts +93 -0
  119. package/packages/kanboard/src/bin.ts +4 -2
  120. package/packages/kanboard/src/commands.ts +218 -84
  121. package/packages/kanboard/src/debug.ts +20 -0
  122. package/packages/kanboard/src/guard.ts +267 -109
  123. package/packages/kanboard/src/monitor.ts +330 -0
  124. package/packages/kanboard/src/notice-buffer.ts +74 -0
  125. package/packages/kanboard/src/progress.ts +27 -0
  126. package/packages/kanboard/src/reminders.ts +234 -0
  127. package/packages/kanboard/src/settings.ts +69 -15
  128. package/packages/kanboard/src/shapes.ts +5 -0
  129. package/packages/long-horizon/index.ts +190 -13
  130. package/packages/long-horizon/package.json +3 -3
  131. package/packages/long-horizon/src/commands.ts +103 -7
  132. package/packages/long-horizon/src/compaction-brief.ts +35 -0
  133. package/packages/long-horizon/src/engine/continuation.ts +84 -15
  134. package/packages/long-horizon/src/engine/goal-state.ts +64 -13
  135. package/packages/long-horizon/src/engine/nudge-stash.ts +71 -0
  136. package/packages/long-horizon/src/engine/ralph.ts +3 -2
  137. package/packages/long-horizon/src/engine/runaway.ts +68 -11
  138. package/packages/long-horizon/src/engine/verifier.ts +19 -0
  139. package/packages/long-horizon/src/gate.ts +37 -12
  140. package/packages/long-horizon/src/judge/resolve.ts +1 -0
  141. package/packages/long-horizon/src/owner.ts +35 -2
  142. package/packages/long-horizon/src/progress.ts +87 -0
  143. package/packages/long-horizon/src/prompts/goal.ts +1 -1
  144. package/packages/long-horizon/src/runtime.ts +172 -18
  145. package/packages/long-horizon/src/settings.ts +54 -39
  146. package/packages/long-horizon/src/tools/goal.ts +6 -0
  147. package/packages/long-horizon/src/tools/graph.ts +1 -1
  148. package/packages/long-horizon/src/tools/swarm.ts +3 -3
  149. package/packages/mcp/package.json +2 -2
  150. package/packages/memory/commands.ts +7 -2
  151. package/packages/memory/index.ts +210 -32
  152. package/packages/memory/package.json +3 -3
  153. package/packages/memory/save-session.ts +264 -0
  154. package/packages/memory/settings.ts +17 -0
  155. package/packages/memory/tools.ts +253 -203
  156. package/packages/notify/package.json +2 -2
  157. package/packages/skill-registry/README.md +53 -16
  158. package/packages/skill-registry/index.ts +230 -0
  159. package/packages/skill-registry/package.json +19 -8
  160. package/packages/skill-registry/src/editor.ts +206 -0
  161. package/packages/skill-registry/src/judge.ts +289 -0
  162. package/packages/skill-registry/src/registry.ts +87 -0
  163. package/packages/skill-registry/src/settings.ts +259 -0
  164. package/packages/skill-registry/src/vault.ts +80 -0
  165. package/packages/subagents/README.md +39 -119
  166. package/packages/subagents/package.json +6 -13
  167. package/packages/subagents/skills/subagents/SKILL.md +18 -42
  168. package/packages/subagents/src/agents.ts +213 -0
  169. package/packages/subagents/src/cards.ts +182 -0
  170. package/packages/subagents/src/index.ts +609 -1084
  171. package/packages/subagents/src/manager.ts +429 -0
  172. package/packages/subagents/src/profiles.ts +130 -0
  173. package/packages/subagents/src/transcript.ts +154 -0
  174. package/packages/subagents/src/ui.ts +389 -0
  175. package/packages/unipi/bundled.js +39756 -45569
  176. package/packages/unipi/index.ts +55 -10
  177. package/packages/updater/package.json +2 -2
  178. package/packages/utility/README.md +76 -69
  179. package/packages/utility/package.json +2 -2
  180. package/packages/utility/skills/image/SKILL.md +37 -0
  181. package/packages/utility/skills/utility/SKILL.md +17 -60
  182. package/packages/utility/src/answer/extract.ts +128 -0
  183. package/packages/utility/src/answer/index.ts +213 -0
  184. package/packages/utility/src/answer/reply.ts +133 -0
  185. package/packages/utility/src/answer/web.ts +167 -0
  186. package/packages/utility/src/attach/index.ts +205 -0
  187. package/packages/utility/src/commands.ts +38 -142
  188. package/packages/utility/src/diagnostics/engine.ts +50 -5
  189. package/packages/utility/src/image/index.ts +33 -0
  190. package/packages/utility/src/image/models.ts +221 -0
  191. package/packages/{image/src → utility/src/image}/recognize.ts +2 -2
  192. package/packages/utility/src/image/settings.ts +178 -0
  193. package/packages/{image/src/image-source.ts → utility/src/image/source.ts} +1 -1
  194. package/packages/utility/src/image/tools.ts +168 -0
  195. package/packages/utility/src/image/transports.ts +186 -0
  196. package/packages/utility/src/index.ts +68 -389
  197. package/packages/utility/src/lifecycle/cleanup.ts +116 -311
  198. package/packages/utility/src/rename/gate.ts +102 -0
  199. package/packages/utility/src/rename/index.ts +191 -0
  200. package/packages/utility/src/rename/session.ts +170 -0
  201. package/packages/utility/src/render/delegated.ts +285 -0
  202. package/packages/utility/src/render/harness.ts +683 -0
  203. package/packages/utility/src/render/parse.ts +156 -0
  204. package/packages/utility/src/render/pi-settings.ts +38 -0
  205. package/packages/utility/src/render/reply-bg.ts +307 -0
  206. package/packages/utility/src/render/simple.ts +724 -0
  207. package/packages/utility/src/render/spacing.ts +235 -0
  208. package/packages/utility/src/render/styled.ts +251 -0
  209. package/packages/utility/src/render/tools.ts +353 -0
  210. package/packages/utility/src/settings.ts +166 -233
  211. package/packages/utility/src/types.ts +1 -222
  212. package/packages/watchdog/index.ts +17 -6
  213. package/packages/watchdog/package.json +3 -3
  214. package/packages/watchdog/src/config.ts +3 -2
  215. package/packages/web-api/package.json +2 -2
  216. package/packages/workflow/README.md +3 -0
  217. package/packages/workflow/index.ts +0 -2
  218. package/packages/workflow/package.json +2 -2
  219. package/packages/workflow/src/permission/decide.ts +6 -4
  220. package/packages/workflow/src/permission/index.ts +17 -7
  221. package/packages/workflow/src/permission/settings.ts +39 -4
  222. package/packages/workflow/src/plan/index.ts +19 -4
  223. package/packages/background-tasks/extensions/delegate-child.ts +0 -1
  224. package/packages/background-tasks/src/context-parent-snapshot.ts +0 -142
  225. package/packages/background-tasks/src/context-token-budget.ts +0 -903
  226. package/packages/background-tasks/src/context-visible-conversation-v2.ts +0 -551
  227. package/packages/background-tasks/src/delegate/artifacts.ts +0 -487
  228. package/packages/background-tasks/src/delegate/budget.ts +0 -415
  229. package/packages/background-tasks/src/delegate/hook-contract-evidence.json +0 -18
  230. package/packages/background-tasks/src/delegate/hook-contract.ts +0 -154
  231. package/packages/background-tasks/src/delegate/launch.ts +0 -484
  232. package/packages/background-tasks/src/delegate/result-package.ts +0 -459
  233. package/packages/background-tasks/src/delegate/runner.ts +0 -430
  234. package/packages/background-tasks/src/delegate/seed.ts +0 -423
  235. package/packages/background-tasks/src/delegate/types.ts +0 -323
  236. package/packages/background-tasks/src/delegate-child-extension.ts +0 -978
  237. package/packages/background-tasks/src/delegate-extension.ts +0 -654
  238. package/packages/background-tasks/src/fixtures/delegate-context-incident.json +0 -17
  239. package/packages/compactor/skills/compactor-detail/SKILL.md +0 -116
  240. package/packages/compactor/skills/compactor-doctor/SKILL.md +0 -71
  241. package/packages/compactor/skills/compactor-stats/SKILL.md +0 -51
  242. package/packages/compactor/src/compaction/build-sections.ts +0 -76
  243. package/packages/compactor/src/compaction/extract/goals.ts +0 -36
  244. package/packages/compactor/src/compaction/extract/preferences.ts +0 -40
  245. package/packages/compactor/src/compaction/format.ts +0 -53
  246. package/packages/compactor/src/compaction/merge.ts +0 -143
  247. package/packages/compactor/src/config/presets.ts +0 -160
  248. package/packages/compactor/src/executor/executor.ts +0 -233
  249. package/packages/compactor/src/executor/runtime.ts +0 -115
  250. package/packages/compactor/src/security/evaluator.ts +0 -89
  251. package/packages/compactor/src/security/policy.ts +0 -96
  252. package/packages/compactor/src/security/scanner.ts +0 -65
  253. package/packages/compactor/src/session/auto-inject.ts +0 -60
  254. package/packages/compactor/src/session/db.ts +0 -325
  255. package/packages/compactor/src/session/extract.ts +0 -144
  256. package/packages/compactor/src/session/resume-inject.ts +0 -64
  257. package/packages/compactor/src/session/snapshot.ts +0 -327
  258. package/packages/compactor/src/tools/ctx-batch-execute.ts +0 -41
  259. package/packages/compactor/src/tools/ctx-execute-file.ts +0 -23
  260. package/packages/compactor/src/tools/ctx-execute.ts +0 -23
  261. package/packages/compactor/src/tools/ctx-stats.ts +0 -96
  262. package/packages/image/README.md +0 -99
  263. package/packages/image/package.json +0 -61
  264. package/packages/image/skills/image/SKILL.md +0 -76
  265. package/packages/image/src/generate.ts +0 -256
  266. package/packages/image/src/index.ts +0 -135
  267. package/packages/image/src/models.ts +0 -519
  268. package/packages/image/src/openai-images-api.ts +0 -282
  269. package/packages/image/src/register-providers.ts +0 -209
  270. package/packages/image/src/settings.ts +0 -173
  271. package/packages/image/src/tools.ts +0 -326
  272. package/packages/input-shortcuts/src/clipboard.ts +0 -119
  273. package/packages/kanboard/src/runner.ts +0 -817
  274. package/packages/skill-registry/skills/auto/SKILL.md +0 -465
  275. package/packages/skill-registry/skills/brainstorm/SKILL.md +0 -219
  276. package/packages/skill-registry/skills/chore-create/SKILL.md +0 -315
  277. package/packages/skill-registry/skills/chore-execute/SKILL.md +0 -314
  278. package/packages/skill-registry/skills/consolidate/SKILL.md +0 -142
  279. package/packages/skill-registry/skills/consultant/SKILL.md +0 -97
  280. package/packages/skill-registry/skills/debug/SKILL.md +0 -228
  281. package/packages/skill-registry/skills/document/SKILL.md +0 -120
  282. package/packages/skill-registry/skills/fix/SKILL.md +0 -212
  283. package/packages/skill-registry/skills/gather-context/SKILL.md +0 -145
  284. package/packages/skill-registry/skills/plan/SKILL.md +0 -231
  285. package/packages/skill-registry/skills/quick-fix/SKILL.md +0 -135
  286. package/packages/skill-registry/skills/quick-work/SKILL.md +0 -110
  287. package/packages/skill-registry/skills/research/SKILL.md +0 -251
  288. package/packages/skill-registry/skills/review-work/SKILL.md +0 -187
  289. package/packages/skill-registry/skills/scan-issues/SKILL.md +0 -195
  290. package/packages/skill-registry/skills/work/SKILL.md +0 -218
  291. package/packages/skill-registry/skills/worktree-create/SKILL.md +0 -69
  292. package/packages/skill-registry/skills/worktree-list/SKILL.md +0 -78
  293. package/packages/skill-registry/skills/worktree-merge/SKILL.md +0 -89
  294. package/packages/subagents/agents/delegate.md +0 -14
  295. package/packages/subagents/agents/oracle.md +0 -78
  296. package/packages/subagents/agents/researcher.md +0 -52
  297. package/packages/subagents/agents/reviewer.md +0 -80
  298. package/packages/subagents/agents/scout.md +0 -50
  299. package/packages/subagents/agents/worker.md +0 -59
  300. package/packages/subagents/prompts/council.md +0 -48
  301. package/packages/subagents/prompts/gather-context-and-clarify.md +0 -13
  302. package/packages/subagents/prompts/parallel-cleanup.md +0 -59
  303. package/packages/subagents/prompts/parallel-research.md +0 -50
  304. package/packages/subagents/prompts/parallel-review.md +0 -54
  305. package/packages/subagents/prompts/review-loop.md +0 -43
  306. package/packages/subagents/skills/council-mode/SKILL.md +0 -230
  307. package/packages/subagents/skills/subagents/references/constraints-and-recipes.md +0 -259
  308. package/packages/subagents/skills/subagents/references/execution-controls.md +0 -454
  309. package/packages/subagents/skills/subagents/references/management-authoring-rpc.md +0 -161
  310. package/packages/subagents/skills/subagents/references/multi-lane-orchestration.md +0 -39
  311. package/packages/subagents/skills/subagents/references/prompting-and-roles.md +0 -267
  312. package/packages/subagents/src/acceptance.ts +0 -331
  313. package/packages/subagents/src/agent-manager.ts +0 -405
  314. package/packages/subagents/src/agent-memory.ts +0 -300
  315. package/packages/subagents/src/agent-overrides.ts +0 -183
  316. package/packages/subagents/src/agent-runner.ts +0 -271
  317. package/packages/subagents/src/async-runner.ts +0 -422
  318. package/packages/subagents/src/authority-policy.ts +0 -47
  319. package/packages/subagents/src/budgets.ts +0 -285
  320. package/packages/subagents/src/child-safety.ts +0 -140
  321. package/packages/subagents/src/config.ts +0 -249
  322. package/packages/subagents/src/conversation-viewer.ts +0 -281
  323. package/packages/subagents/src/core-compat.ts +0 -94
  324. package/packages/subagents/src/custom-agents.ts +0 -343
  325. package/packages/subagents/src/file-system-retry.ts +0 -50
  326. package/packages/subagents/src/fleet-data.ts +0 -105
  327. package/packages/subagents/src/fleet-view.ts +0 -322
  328. package/packages/subagents/src/foreground-detach.ts +0 -59
  329. package/packages/subagents/src/fork-context.ts +0 -285
  330. package/packages/subagents/src/global.d.ts +0 -7
  331. package/packages/subagents/src/guide.ts +0 -129
  332. package/packages/subagents/src/mission-state.ts +0 -133
  333. package/packages/subagents/src/mission-store.ts +0 -436
  334. package/packages/subagents/src/output-limits.ts +0 -142
  335. package/packages/subagents/src/parity-types.ts +0 -315
  336. package/packages/subagents/src/pi-args.ts +0 -167
  337. package/packages/subagents/src/result-files.ts +0 -232
  338. package/packages/subagents/src/result-watcher.ts +0 -221
  339. package/packages/subagents/src/retained-children.ts +0 -176
  340. package/packages/subagents/src/run-fanout-budget.ts +0 -356
  341. package/packages/subagents/src/scheduled-runs.ts +0 -349
  342. package/packages/subagents/src/schemas.ts +0 -263
  343. package/packages/subagents/src/slash-commands.ts +0 -80
  344. package/packages/subagents/src/supervisor-channel.ts +0 -249
  345. package/packages/subagents/src/tool-handler.ts +0 -1144
  346. package/packages/subagents/src/types.ts +0 -238
  347. package/packages/subagents/src/widget.ts +0 -457
  348. package/packages/subagents/src/workflow-script.ts +0 -737
  349. package/packages/subagents/src/workflow-worker.ts +0 -384
  350. package/packages/subagents/src/worktree.ts +0 -614
  351. package/packages/utility/src/analytics/collector.ts +0 -293
  352. package/packages/utility/src/info-screen.ts +0 -80
  353. package/packages/utility/src/lifecycle/process.ts +0 -162
  354. package/packages/utility/src/prefix-cache.ts +0 -263
  355. package/packages/utility/src/skill-discovery.ts +0 -542
  356. package/packages/utility/src/tools/env.ts +0 -113
  357. package/packages/utility/src/tui/name-badge-state.ts +0 -315
  358. package/packages/utility/src/tui/name-badge.ts +0 -109
  359. /package/packages/{subagents/src → core/src/child-agent}/pi-spawn.ts +0 -0
@@ -9,8 +9,10 @@
9
9
  * mise run unipi
10
10
  */
11
11
 
12
- import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
13
- import { migrateState, sweepOrphanSessions } from "@pi-unipi/core";
12
+ import type { ExtensionAPI, ToolDefinition } from "@earendil-works/pi-coding-agent";
13
+ import { installArbiter, installHarnessProvenance, migrateState, sweepOrphanSessions, withCommandEcho } from "@pi-unipi/core";
14
+ import { withHarnessToolAnnotations } from "@pi-unipi/utility";
15
+ import { readUtilSettings, simpleWrapTool, simpleWrapped, installSimpleGroupEvents } from "@pi-unipi/utility";
14
16
 
15
17
  import workflow from "@pi-unipi/workflow";
16
18
  import longHorizon from "@pi-unipi/long-horizon";
@@ -21,6 +23,7 @@ import backgroundTasks from "@pi-unipi/background-tasks";
21
23
  import btw from "@pi-unipi/btw/extensions/btw.js";
22
24
  import webApi from "@pi-unipi/web-api";
23
25
  import utility from "@pi-unipi/utility";
26
+ import skillRegistry from "@pi-unipi/skill-registry";
24
27
  import askUser from "@pi-unipi/ask-user";
25
28
  import mcp from "@pi-unipi/mcp";
26
29
  import notify from "@pi-unipi/notify";
@@ -30,11 +33,44 @@ import compactor from "@pi-unipi/compactor";
30
33
  import footer from "@pi-unipi/footer";
31
34
  import updater from "@pi-unipi/updater";
32
35
  import inputShortcuts from "@pi-unipi/input-shortcuts";
33
- import image from "@pi-unipi/image";
34
36
  import fusion from "@pi-unipi/fusion";
35
37
  import watchdog from "@pi-unipi/watchdog";
36
38
 
37
39
  export default function (pi: ExtensionAPI) {
40
+ const api = withCommandEcho(pi);
41
+ // Harness provenance must observe every input/send — install before modules.
42
+ try {
43
+ installHarnessProvenance(api);
44
+ } catch {
45
+ // Provenance is cosmetic; never blocks startup.
46
+ }
47
+ // The turn arbiter must own the single agent_before_settle handler BEFORE
48
+ // any module mounts (idempotent; a no-op in child processes).
49
+ try {
50
+ installArbiter(pi);
51
+ } catch {
52
+ // Never block startup on the arbiter.
53
+ }
54
+ // "simple" render style = mcode transcript: every tool registered by any
55
+ // unipi module is captured here and, after all modules load, re-registered
56
+ // with the collapsed one-liner wrapper (execute/schema untouched; Ctrl+O
57
+ // falls back to the tool's own renderer). best-effort, never blocks load.
58
+ const captured = new Map<string, ToolDefinition<any, any, any>>();
59
+ const rawRegister = api.registerTool.bind(api);
60
+ const registering = new Proxy(api, {
61
+ get(target, prop, receiver) {
62
+ if (prop === "registerTool") {
63
+ return (tool: ToolDefinition<any, any, any>) => {
64
+ // Harness tool-annotation wrapper (idempotent, applied BEFORE the
65
+ // simple-mode wrapper so simpleResult hooks merge).
66
+ const wrapped = withHarnessToolAnnotations(tool);
67
+ captured.set(tool.name, wrapped);
68
+ return rawRegister(wrapped);
69
+ };
70
+ }
71
+ return Reflect.get(target, prop, receiver);
72
+ },
73
+ });
38
74
  // One-time v3 state relocation into ~/.unipi/{global,workspace}/, then reap
39
75
  // any dead-pid session dirs left by crashed sessions. Both are best-effort
40
76
  // and version/pid-gated, so they are cheap on every subsequent startup.
@@ -45,17 +81,13 @@ export default function (pi: ExtensionAPI) {
45
81
  // Never block startup on housekeeping.
46
82
  }
47
83
 
48
- const load = (_name: string, extension: (api: ExtensionAPI) => void) => extension(pi);
84
+ const load = (_name: string, extension: (api: ExtensionAPI) => void) => extension(registering);
49
85
 
50
86
  load("workflow", workflow);
51
87
  load("long-horizon", longHorizon);
52
88
  load("memory", memory);
53
- // Utility loads BEFORE info-screen: the name badge overlay must be pushed
54
- // to the BOTTOM of the overlay stack. hideOverlay() pops the topmost entry,
55
- // and a capturing overlay's done() callback is one-shot — if the badge were
56
- // stacked above the boot info-screen, the info-screen's auto-close would pop
57
- // the badge (spending its done()) and strand the dashboard uncloseable.
58
89
  load("utility", utility);
90
+ load("skill-registry", skillRegistry);
59
91
  load("info-screen", infoScreen);
60
92
  load("subagents", subagents);
61
93
  load("background-tasks", backgroundTasks);
@@ -70,7 +102,20 @@ export default function (pi: ExtensionAPI) {
70
102
  load("footer", footer);
71
103
  load("updater", updater);
72
104
  load("input-shortcuts", inputShortcuts);
73
- load("image", image);
74
105
  load("fusion", fusion);
75
106
  load("watchdog", watchdog);
107
+
108
+ // After all modules registered: apply the mcode-style wrapper. Re-register
109
+ // with the RAW register (not the capturing proxy) to avoid double-capture.
110
+ if (readUtilSettings().render.style === "simple") {
111
+ installSimpleGroupEvents(api);
112
+ for (const def of captured.values()) {
113
+ if (simpleWrapped.has(def)) continue;
114
+ try {
115
+ rawRegister(simpleWrapTool(def));
116
+ } catch {
117
+ // keep the original tool on any failure
118
+ }
119
+ }
120
+ }
76
121
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi-unipi/updater",
3
- "version": "3.0.0-alpha.2",
3
+ "version": "3.0.0-alpha.21",
4
4
  "description": "Auto-updater, changelog browser, and readme browser for Unipi — checks npm registry, renders CHANGELOG.md and README.md files in TUI overlays",
5
5
  "type": "module",
6
6
  "main": "index.ts",
@@ -34,7 +34,7 @@
34
34
  "access": "public"
35
35
  },
36
36
  "dependencies": {
37
- "@pi-unipi/core": "3.0.0-alpha.2"
37
+ "@pi-unipi/core": "3.0.0-alpha.21"
38
38
  },
39
39
  "peerDependencies": {
40
40
  "@earendil-works/pi-coding-agent": "^0.87.1",
@@ -1,105 +1,112 @@
1
1
  # @pi-unipi/utility
2
2
 
3
- Environment info, diagnostics, cleanup, and the session name badge. The grab-bag package for maintaining your development environment and keeping an eye on provider cache behavior.
3
+ The settings hub, automatic session naming, image tools, pasted-file attachments, `/unipi:answer`, and a few maintenance commands. Also keeps the shared model cache every other module's model picker reads.
4
4
 
5
5
  ## Commands
6
6
 
7
7
  | Command | Description |
8
8
  |---------|-------------|
9
- | `/unipi:env` | Show environment info (Node, Pi, OS, paths) |
10
- | `/unipi:doctor` | Run diagnostics across all modules |
11
- | `/unipi:status` | Show immediate guidance to the live `/unipi:info` dashboard and `/unipi:doctor` diagnostics |
12
- | `/unipi:cleanup` | Clean stale DBs, temp files, old sessions |
13
- | `/unipi:reload` | Explain how to reload extensions |
14
- | `/unipi:name-badge` | Toggle name badge overlay |
15
- | `/unipi:settings` | Badge field + "Set/Generate session name" actions (Utility group) |
16
- | `/unipi:settings` | Hub — badge behavior (Utility group) |
17
- | `/unipi:prefix-cache` | Show privacy-safe request-prefix transitions and provider cache token counters |
9
+ | `/unipi:settings [search]` | Configure every UniPi module in one panel (global + project scopes); a search term opens it filtered (`/unipi:settings image`). Typing `/settings` lists it before pi's own `/settings` |
10
+ | `/unipi:continue` (`/unipi:retry`) | Take another turn from where the agent stopped, without adding text |
11
+ | `/unipi:cleanup` | Remove stale UniPi temp files and leftovers. Shows what it would remove and asks first; `--dry-run` only lists, `--yes` skips the question |
12
+ | `/unipi:doctor` | Check folders, config, the model cache, the Decision Model key and skill exposure |
13
+ | `/unipi:answer` (`reply`\|`questions`\|`web`) | Answer the agent's last reply without scrolling back and forth |
18
14
 
19
- ### Examples
15
+ Skill exposure and the skill manager live in `@pi-unipi/skill-registry` (`/unipi:skills`).
20
16
 
21
- ```
22
- /unipi:env # Show environment
23
- /unipi:doctor # Run diagnostics
24
- /unipi:cleanup # Clean stale files
25
- /unipi:cleanup --dry-run # Preview what would be cleaned
26
- /unipi:name-badge # Toggle the session name badge
27
- /unipi:settings # → Utility → "Generate session name"
28
- ```
17
+ The agent gets the three image tools (below) and nothing else from this package.
29
18
 
30
- ## Special Triggers
19
+ ## Automatic session naming
31
20
 
32
- Utility registers with the info-screen dashboard, showing module status and diagnostic results. The footer subscribes to utility events for its extension status segment. When Herdr is present, the badge module syncs the session name to the pane title.
21
+ After each round that finished normally, if you typed the prompt:
33
22
 
34
- ## Provider prefix-cache diagnostics
23
+ 1. Greetings, thanks, "ok", "continue" and slash commands are skipped outright.
24
+ 2. jev (the Decision Model) answers two short questions: is this a real request with its own subject, and — if the session already has a name — does it move to a different task? Only a confident "yes" renames.
25
+ 3. The name is written by a separate throwaway session whose only tool is `rename_session`. It sees the current name and your last few requests, nothing else, and never touches the main session.
35
26
 
36
- `/unipi:prefix-cache` observes Pi's provider-native request payloads without modifying them. It reports session-local cache epochs, exact structural prefix extensions, retries, request-envelope/history boundaries, and provider-reported `cacheRead`/`cacheWrite` token totals.
27
+ A name you set yourself with pi's `/name` is never overwritten. Without a Decision Model key, only an unnamed session gets named, on its first prompt of four words or more.
37
28
 
38
- The fingerprints are keyed HMAC-SHA-256 values using a random in-memory key that is discarded on reload. Raw prompts, messages, tool arguments, tool schemas, and provider payloads are never retained or logged by this diagnostic. Fingerprints therefore cannot be compared across process lifetimes. A reported prefix extension means the request is structurally eligible for reuse; provider TTL, routing, and cache policy still decide whether a hit occurs.
29
+ Inside Herdr, the name is also shown as the pane title and, while the tab still has its default number, the tab label.
39
30
 
40
- `/unipi:cleanup` includes private `~/.unipi/tool-results/` artifacts in the normal temporary-file retention policy (7 days by default). Use dry-run mode to review candidates before deletion.
31
+ Settings (`/unipi:settings` → Utility → Session name): auto-rename on/off, naming model (defaults to the session model), Herdr sync, and a **Rename now** action.
41
32
 
42
- ## Agent Tools
33
+ ## Answering the last reply
43
34
 
44
- | Tool | Description |
45
- |------|-------------|
46
- | `ctx_env` | Environment inspection for debugging |
47
- | `set_session_name` | Set the session name for badge display (when badge agent-tool is enabled) |
35
+ `/unipi:answer` opens one of three screens (Settings → Utility → Answer picks the default; `/unipi:answer reply|questions|web` overrides it once, with suggestions as you type):
48
36
 
49
- ## Configurables
37
+ - **reply** (default) — like `/unipi:btw`, it takes the place of the input area: the agent's last reply in a scrollable view on top, a fixed input box below. ↑↓ scroll the reply while nothing is typed; once you type they move the cursor in your answer, and Alt+↑↓ or PgUp/PgDn scroll instead. Shift+Enter (or Ctrl+J) adds a line, Enter sends it as your next message, Esc goes back to the normal editor with your draft. If the reply asks questions, Tab switches to **questions**.
38
+ - **questions** — pi's editor holding only `Q1. … / A1:` pairs for every question in the reply: each sentence ending in `?`, in full, even when it wraps over several lines (code blocks and URLs are ignored). The cursor starts on the first answer, Tab / Shift+Tab jump between answers, Ctrl+G opens your `$EDITOR`, Enter sends one message that quotes each question above its answer; empty answers are listed as not answered.
39
+ - **web** — a local page with the full reply on the left and one box per question on the right (plus a free-text note), on 127.0.0.1 behind a random URL. Over SSH (detected from `SSH_CONNECTION`/`SSH_TTY`) it doesn't open a browser: it shows the `ssh -L` command, built from this machine's hostname and the port (47321 unless you set one), and the URL to open on your own machine.
50
40
 
51
- ### Name Badge
41
+ When a reply asks questions, a line above the editor says so (`2 questions in the reply — /unipi:answer questions …`) until you send anything. Settings → Utility → Answer → Questions hint turns it off.
52
42
 
53
- ```
54
- /unipi:settings # Badge + skills discovery (Utility group)
55
- ```
43
+ ## Pasted images and files
56
44
 
57
- Or edit `.unipi/config/util-settings.json` directly (migrated automatically from the legacy `.unipi/config/badge.json` on first read):
45
+ Paste a screenshot (Ctrl+V), drag a file into the terminal, or paste a path, and the path in the editor becomes a token: `[Image #1]` for PNG/JPEG/GIF/WebP, `[File #2]` for documents (PDF, text, office files, archives, media). A chip row above the editor lists what's attached, with small previews in terminals that can draw images (Kitty, Ghostty, iTerm2, WezTerm).
58
46
 
59
- ```json
60
- {
61
- "badge": {
62
- "badgeEnabled": true
63
- }
64
- }
65
- ```
47
+ When you send:
48
+ - Images go to the model as real images, in the order their tokens appear.
49
+ - File tokens become `[File #2: /path/to/file]` so the agent can read them.
50
+ - A line under your message in the transcript shows what was attached; the model doesn't see it.
66
51
 
67
- The badge is a persistent HUD overlay in the top-right corner showing the current session name. It auto-restores visibility on session restart.
52
+ Delete a token to drop its attachment. Only paths that arrive by paste, drop or Ctrl+V are converted; a path you type stays text. Settings → Utility → Attachments turns this or the previews off.
68
53
 
69
- ### Skill Exposure (jev-judged)
54
+ ## Image tools
70
55
 
71
- Controls which discovered skills are cataloged in the agent's system prompt. Configure via `/unipi:settings` (Skills group) or the engine file (`skills` subtree):
56
+ | Tool | What it does |
57
+ |------|--------------|
58
+ | `image_generate` | Text → image |
59
+ | `image_edit` | Image + text → image |
60
+ | `image_recognize` | Image → text with a vision model; only offered while the session model can't see images itself |
72
61
 
73
- - **mode: `judged`** (default) — on the session's first prompt, if more than `maxSkills` skills are discovered, ONE jev (TypeSafe System One) request scores every skill's relevance to your prompt; only relevant ones stay cataloged, sorted by relevance and capped. The set is frozen per session and persisted, so the system prompt is byte-identical on every later turn (provider prefix cache stays intact).
74
- - **mode: `all`** — no judging; every skill stays.
75
- - **mode: `off`** — Unipi's bundled skills are stripped from the catalog (old `discovery: false` behavior).
62
+ Settings → Image has one section per tool. Each model picker only lists models that fit: generation shows models whose output includes images, editing shows models that take an image and output one, recognition shows vision models. The list comes from the shared model cache: pi's registry, OpenRouter's image models (refreshed daily), and a few fal models when a fal key is set.
76
63
 
77
- Extra knobs: `threshold` (min jev relevance, default 0.3), `maxSkills` (cap, default 12), `recheck` (default on — later prompts on new topics get a "newly relevant skills" message revealing up to 5 hidden skills; revealed skills are never announced twice, and the system prompt itself never changes after the freeze).
64
+ How a model is called depends on its provider:
78
65
 
79
- Skills stay invocable in every mode via `/skill:name` or by reading their SKILL.md directly. The judged set reuses the long-horizon **Decision model** (jev) — provider, model, key and base URL come from the Judge settings; missing key, timeout, or any failure exposes ALL skills (fail-open).
66
+ | Model | Sent to | Key |
67
+ |-------|---------|-----|
68
+ | `openrouter/…` | OpenRouter (chat with image output) | Settings → Image → Keys, else pi's `/login`, else `OPENROUTER_API_KEY` |
69
+ | `fal/…` | fal.run | Settings → Image → Keys, else `FAL_KEY` / `FAL_API_KEY` |
70
+ | any other pi provider | that provider's endpoint (OpenAI images format) | pi's key for that provider |
71
+ | anything, with a **custom endpoint** set | your base URL, model id sent as typed, format of your choice (OpenAI images, OpenRouter-style, fal) | the endpoint's own key |
80
72
 
81
- ```json
82
- {
83
- "skills": { "mode": "judged", "threshold": 0.3, "maxSkills": 12, "recheck": true }
84
- }
85
- ```
73
+ Defaults are FLUX.2 [klein] 4B on OpenRouter for both generation and editing, which is cheap and fast. Images are returned inline and saved to `~/.unipi/images/`. When a call fails, the tool reports it as a failure with the missing key or setting spelled out.
86
74
 
87
- ## Programmatic API
75
+ ## Response formatting
88
76
 
89
- | Module | Path | Description |
90
- |--------|------|-------------|
91
- | ProcessLifecycle | `lifecycle/process` | Parent PID polling, orphan detection, signal handlers |
92
- | cleanupStale | `lifecycle/cleanup` | Stale DB/temp/session cleanup with dry-run |
93
- | AnalyticsCollector | `analytics/collector` | Privacy-respecting event collection with daily rollup |
94
- | runDiagnostics | `diagnostics/engine` | Cross-module health checks with plugin architecture |
77
+ Settings → Utility → Response formatting → Style changes how pi's built-in `read`, `bash`, `edit` and `write` calls look in the transcript. The model sees exactly the same tools either way; only the drawing changes. It applies after `/reload` (or a restart) — the renderers are registered once when extensions load, so a change made mid-session takes effect on the next reload.
95
78
 
96
- ## Privacy
79
+ - **simple** — one line per tool, output collapsed:
80
+ ```
81
+ ▪ Read ./demo/app.py · 5 lines
82
+ ▪ Edited ./demo/app.py · +1 -1
83
+ ▪ Ran cd demo && ls -la && python3 app.py · 7 output lines
84
+ ```
85
+ The live (latest) row shows a ticking timer (`• Running npm test · 12s`) and
86
+ done rows pin the final duration (`· 7 output lines · 3s`). Tools that opt in
87
+ keep their result under the row (`ask_user`'s Q→A) or drive the meta live
88
+ (`run_subagent`'s model · calls · duration · tokens · cost). Sidekick steps
89
+ and other non-pi-tool activity draw with the same verbs/markers.
90
+ - **regular** (default) — pi's own rendering, untouched.
91
+ - **advanced** —
92
+ - Commands are syntax-highlighted. Code embedded in a command is highlighted in its own language: heredoc bodies (`python - <<'PY'`, `cat > x.ts <<EOF`) and `python -c` / `node -e` strings.
93
+ - Output shows its last 10 lines. Whole-output JSON is pretty-printed, test-run summaries (node:test, vitest, cargo, pytest) get a ✓/✗ line, and an exit line shows the code and time.
94
+ - Edits show as a diff with line numbers, syntax colouring by file type, and tinted added/removed lines.
95
+ - Writes and reads are highlighted by file type.
97
96
 
98
- The analytics collector is privacy-respecting:
99
- - No file contents recorded
100
- - No sensitive data (API keys, tokens, passwords) — redacted automatically
101
- - Strings truncated to 500 characters
102
- - All data stays local
97
+ Ctrl+O expands anything collapsed in every style. In every style, blank runs
98
+ between transcript blocks are normalized: each block keeps its interior
99
+ spacing, but adjacent blocks are separated by exactly one blank row (pi
100
+ otherwise stacks a block's own padding on top of its leading spacer,
101
+ producing occasional 2–4 blank runs).
102
+
103
+ ## Model cache
104
+
105
+ On every session start, utility writes pi's live model list (models with credentials) to `~/.unipi/config/models-cache.json`, with each model's input and output modalities. The settings hub pickers and kanboard read it; `readModelCache()` / `filterModels()` in `@pi-unipi/core` give the same list to any module.
106
+
107
+ ## Cleanup safety
108
+
109
+ `/unipi:cleanup` can only remove what its allowlist names: saved tool outputs and `unipi-*` temp files older than 7 days, and the old compactor continuity database. Memory, v2 backups, kanboard boards, config and workspace state are never candidates.
103
110
 
104
111
  ## License
105
112
 
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi-unipi/utility",
3
- "version": "3.0.0-alpha.2",
3
+ "version": "3.0.0-alpha.21",
4
4
  "description": "Utility commands and tools for Pi coding agent — lifecycle, diagnostics, cache, analytics, display, batch execution",
5
5
  "type": "module",
6
6
  "main": "src/index.ts",
@@ -36,7 +36,7 @@
36
36
  "access": "public"
37
37
  },
38
38
  "dependencies": {
39
- "@pi-unipi/core": "3.0.0-alpha.2"
39
+ "@pi-unipi/core": "3.0.0-alpha.21"
40
40
  },
41
41
  "devDependencies": {},
42
42
  "peerDependencies": {
@@ -0,0 +1,37 @@
1
+ ---
2
+ name: image
3
+ scope: agent
4
+ description: |
5
+ Generate, edit and read images with the image_generate, image_edit and
6
+ image_recognize tools (@pi-unipi/utility). Use when the user asks for a
7
+ picture, icon, illustration, mockup or logo, wants an existing image
8
+ changed, or needs a screenshot/diagram read while the session model
9
+ cannot see images.
10
+ ---
11
+
12
+ # Image tools
13
+
14
+ | Tool | Use for |
15
+ |------|---------|
16
+ | `image_generate(prompt, model?)` | A new image from text |
17
+ | `image_edit(prompt, image, model?)` | Change an existing image (`image` = file path, data: URL or base64) |
18
+ | `image_recognize(image, prompt?, model?)` | Read an image with a vision model — only offered while your own model cannot see images |
19
+
20
+ ## Prompting
21
+
22
+ - Be concrete: subject, style, composition, lighting, colours, text to render.
23
+ - Say what you want, not what you don't — negation is unreliable.
24
+ - For edits, say what to change AND what to keep; unmentioned details can drift.
25
+ - Images cost money. Generate once, show the result, and only retry when asked.
26
+
27
+ ## Models
28
+
29
+ Omit `model` to use the one set in `/unipi:settings` → Image. To override, pass
30
+ `provider/model-id`, e.g. `openrouter/black-forest-labs/flux.2-klein-4b` or
31
+ `fal/fal-ai/flux-2/klein/4b` (edits: `fal/fal-ai/flux-2/klein/4b/edit`).
32
+
33
+ ## Results
34
+
35
+ Images come back inline and are saved under `~/.unipi/images/` (path in the
36
+ result). If a call fails, the error says which key or setting is missing —
37
+ tell the user that instead of retrying.
@@ -2,69 +2,26 @@
2
2
  name: utility
3
3
  scope: agent
4
4
  description: |
5
- Guidance for using the @pi-unipi/utility extension commands and tools.
6
- Provides lifecycle management, diagnostics, cleanup, batch execution,
7
- environment inspection, and terminal utilities.
5
+ What the @pi-unipi/utility extension does for the user: the /unipi:settings
6
+ hub, /unipi:continue (/unipi:retry), /unipi:cleanup, /unipi:doctor,
7
+ and automatic session naming. It gives the agent no tools.
8
8
  ---
9
9
 
10
- # @pi-unipi/utility — Agent Guidance
10
+ # @pi-unipi/utility
11
11
 
12
- ## Overview
12
+ ## Commands (user-facing)
13
13
 
14
- The `@pi-unipi/utility` extension provides general-purpose utilities for the Pi coding agent. It is **not** related to compaction — it provides helpers that any module or agent can use.
14
+ | Command | Purpose |
15
+ |---------|---------|
16
+ | `/unipi:settings` | Configure every UniPi module |
17
+ | `/unipi:continue` / `/unipi:retry` | Take another turn without new text |
18
+ | `/unipi:cleanup` | Remove stale UniPi temp files (previews and asks first; `--dry-run`, `--yes`) |
19
+ | `/unipi:doctor` | Check config, model cache, Decision Model key, skill exposure |
15
20
 
16
- ## Commands
21
+ ## Notes for the agent
17
22
 
18
- | Command | Purpose | When to Use |
19
- |---------|---------|-------------|
20
- | `/unipi:continue` | Continue agent without polluting context | After interruption, to resume cleanly |
21
- | `/unipi:reload` | Explain how to reload extensions | When user asks about hot-reloading |
22
- | `/unipi:status` | Request status from all modules | To check which modules are loaded |
23
- | `/unipi:cleanup` | Clean stale files, DBs, sessions | When disk space is low or after crashes |
24
- | `/unipi:env` | Show environment info | For debugging version/path issues |
25
- | `/unipi:doctor` | Run diagnostics | When something seems broken |
26
-
27
- ### Cleanup Options
28
-
29
- - `--dry-run` — Report what would be cleaned without removing anything
30
- - Default max ages: DBs 14 days, temp 7 days, sessions 30 days
31
-
32
- ## Tools
33
-
34
- | Tool | Purpose |
35
- |------|---------|
36
- | `ctx_batch` | Atomic batch execution of commands with rollback |
37
- | `ctx_env` | Environment inspection |
38
-
39
- ### Batch Execution Pattern
40
-
41
- ```typescript
42
- import { BatchBuilder } from "@pi-unipi/utility/tools/batch";
43
-
44
- const report = await new BatchBuilder()
45
- .addCommand("search", { query: "foo" })
46
- .addTool("memory_search", { query: "bar" })
47
- .withOptions({ failFast: true, commandTimeoutMs: 30000 })
48
- .execute(myExecutor);
49
- ```
50
-
51
- ## Modules (for code use)
52
-
53
- | Module | Import Path | Purpose |
54
- |--------|-------------|---------|
55
- | ProcessLifecycle | `@pi-unipi/utility/lifecycle/process` | Parent PID polling, orphan detection, cleanup registry |
56
- | cleanupStale | `@pi-unipi/utility/lifecycle/cleanup` | Stale file/DB/session cleanup |
57
- | TTLCache | `@pi-unipi/utility/cache/ttl-cache` | In-memory or SQLite-backed TTL cache |
58
- | AnalyticsCollector | `@pi-unipi/utility/analytics/collector` | Privacy-respecting event collection |
59
- | runDiagnostics | `@pi-unipi/utility/diagnostics/engine` | Cross-module health checks |
60
- | detectCapabilities | `@pi-unipi/utility/display/capabilities` | Terminal feature detection |
61
- | clampWidth, wrapLines | `@pi-unipi/utility/display/width` | ANSI-aware text formatting |
62
- | SettingsInspector | `@pi-unipi/utility/tui/settings-inspector` | Reusable settings overlay model |
63
-
64
- ## Integration Notes
65
-
66
- - Lifecycle manager is a singleton — use `getLifecycle()` to access
67
- - Analytics collector is a singleton — use `getAnalyticsCollector()`
68
- - Diagnostics engine supports custom plugins via `registerDiagnosticPlugin()`
69
- - TTLCache defaults to memory backend; set `persistent: true` for SQLite
70
- - All modules emit unipi events for cross-module integration
23
+ - There is no tool to rename the session. Naming happens automatically after
24
+ real requests; the user can also use pi's `/name`.
25
+ - Skills not listed in your catalog may still be installed. If the user names
26
+ a skill, read its SKILL.md or ask them to run `/skill:<name>`.
27
+ - When something UniPi-related looks broken, suggest `/unipi:doctor`.
@@ -0,0 +1,128 @@
1
+ /**
2
+ * @pi-unipi/utility — /unipi:answer: questions in, answers out (pure)
3
+ *
4
+ * Pulls the questions out of the agent's last reply (any line with a "?"
5
+ * outside code blocks, list/heading markers stripped), builds a Q/A template,
6
+ * parses it back, and composes one user message that quotes each question.
7
+ */
8
+
9
+ export const MAX_QUESTIONS = 20;
10
+
11
+ /** Plain text of an assistant message's content. */
12
+ export function messageText(content: unknown): string {
13
+ if (typeof content === "string") return content;
14
+ if (!Array.isArray(content)) return "";
15
+ return content
16
+ .filter((c): c is { type: "text"; text: string } => !!c && typeof c === "object" && (c as { type?: string }).type === "text")
17
+ .map((c) => c.text)
18
+ .join("\n");
19
+ }
20
+
21
+ function cleanLine(line: string): string {
22
+ return line
23
+ .replace(/^\s*(?:[-*+]|\d+[.)]|#{1,6}|>)\s+/, "")
24
+ .replace(/\*\*|__|`/g, "")
25
+ .replace(/\s+/g, " ")
26
+ .trim();
27
+ }
28
+
29
+ /**
30
+ * Questions in a reply. Lines are first joined into paragraphs (a question
31
+ * that wraps over several source lines stays whole), list items and headings
32
+ * each start their own paragraph, code blocks are skipped, and every sentence
33
+ * ending in "?" becomes one question — in full, never truncated.
34
+ */
35
+ export function extractQuestions(text: string): string[] {
36
+ const paragraphs: string[] = [];
37
+ let current: string[] = [];
38
+ let fenced = false;
39
+ const flush = () => {
40
+ if (current.length) paragraphs.push(current.join(" "));
41
+ current = [];
42
+ };
43
+ for (const raw of text.split("\n")) {
44
+ if (/^\s*(```|~~~)/.test(raw)) {
45
+ flush();
46
+ fenced = !fenced;
47
+ continue;
48
+ }
49
+ if (fenced) continue;
50
+ if (!raw.trim()) {
51
+ flush();
52
+ continue;
53
+ }
54
+ // A new list item / heading / quote starts a new paragraph.
55
+ if (/^\s*(?:[-*+]|\d+[.)]|#{1,6}|>)\s+/.test(raw)) flush();
56
+ current.push(raw);
57
+ }
58
+ flush();
59
+
60
+ const out: string[] = [];
61
+ for (const para of paragraphs) {
62
+ const clean = cleanLine(para);
63
+ if (!clean.includes("?")) continue;
64
+ // A "?" inside a URL (query string) is not a question: park URLs as tokens.
65
+ const urls: string[] = [];
66
+ const masked = clean.replace(/https?:\/\/\S+/g, (u) => `\u0000${urls.push(u) - 1}\u0000`);
67
+ if (!masked.includes("?")) continue;
68
+ for (const sentence of masked.match(/[^.!?]*(?:[.!](?!\s|$)[^.!?]*)*\?+/g) ?? []) {
69
+ const q = sentence.replace(/\u0000(\d+)\u0000/g, (_m, i: string) => urls[Number(i)] ?? "").trim();
70
+ if (q.length < 4 || out.includes(q)) continue;
71
+ out.push(q);
72
+ if (out.length >= MAX_QUESTIONS) return out;
73
+ }
74
+ }
75
+ return out;
76
+ }
77
+
78
+ /** Q/A pairs, nothing else in the buffer; no questions → an empty buffer. */
79
+ export function buildTemplate(questions: readonly string[]): string {
80
+ if (questions.length === 0) return "";
81
+ return questions.flatMap((q, i) => [`Q${i + 1}. ${q}`, `A${i + 1}: `, ""]).join("\n").trimEnd() + " ";
82
+ }
83
+
84
+ /** Answers by question index (0-based); missing/empty → "". */
85
+ export function parseTemplate(text: string, count: number): string[] {
86
+ if (count === 0) return [text.trim()];
87
+ const answers: string[] = Array.from({ length: count }, () => "");
88
+ let current = -1;
89
+ const buf: string[][] = answers.map(() => []);
90
+ for (const line of text.split("\n")) {
91
+ if (/^Q\d+\.\s/.test(line)) {
92
+ current = -1;
93
+ continue;
94
+ }
95
+ const a = line.match(/^A(\d+):\s?(.*)$/);
96
+ if (a) {
97
+ const idx = Number(a[1]) - 1;
98
+ current = idx >= 0 && idx < buf.length ? idx : -1;
99
+ if (current >= 0) buf[current]!.push(a[2]!);
100
+ continue;
101
+ }
102
+ if (current >= 0) buf[current]!.push(line);
103
+ }
104
+ return buf.map((lines) => lines.join("\n").trim());
105
+ }
106
+
107
+ /** One user message; undefined when nothing was answered. */
108
+ export function composeAnswers(questions: readonly string[], answers: readonly string[], note = ""): string | undefined {
109
+ const parts: string[] = [];
110
+ const skipped: number[] = [];
111
+ if (questions.length === 0) {
112
+ const text = [answers[0] ?? "", note].map((s) => s.trim()).filter(Boolean).join("\n\n");
113
+ return text || undefined;
114
+ }
115
+ questions.forEach((q, i) => {
116
+ const a = (answers[i] ?? "").trim();
117
+ if (!a) {
118
+ skipped.push(i + 1);
119
+ return;
120
+ }
121
+ parts.push(`${i + 1}. > ${q}\n\n${a}`);
122
+ });
123
+ if (parts.length === 0 && !note.trim()) return undefined;
124
+ const lines = [parts.join("\n\n")];
125
+ if (note.trim()) lines.push(note.trim());
126
+ if (skipped.length) lines.push(`(Not answered: ${skipped.map((n) => `#${n}`).join(", ")})`);
127
+ return lines.filter(Boolean).join("\n\n");
128
+ }