@xingular/harness 1.0.0 → 1.2.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 (397) hide show
  1. package/CHANGELOG.md +46 -5
  2. package/README.md +75 -21
  3. package/dist/cli.js +17421 -8711
  4. package/dist/sdk/accounts/accounts.d.ts +27 -3
  5. package/dist/sdk/accounts/accounts.js +78 -24
  6. package/dist/sdk/accounts/secrets.d.ts +12 -0
  7. package/dist/sdk/accounts/secrets.js +114 -7
  8. package/dist/sdk/accounts/store.d.ts +26 -1
  9. package/dist/sdk/accounts/store.js +1 -1
  10. package/dist/sdk/agents/builtin.d.ts +2 -0
  11. package/dist/sdk/agents/builtin.js +48 -0
  12. package/dist/sdk/agents/narrow.d.ts +13 -0
  13. package/dist/sdk/agents/narrow.js +29 -0
  14. package/dist/sdk/agents/planResearch.d.ts +5 -0
  15. package/dist/sdk/agents/planResearch.js +19 -0
  16. package/dist/sdk/agents/profiles.d.ts +81 -0
  17. package/dist/sdk/agents/profiles.js +226 -0
  18. package/dist/sdk/annotations/bridge.d.ts +42 -0
  19. package/dist/sdk/annotations/bridge.js +264 -0
  20. package/dist/sdk/annotations/capture.d.ts +116 -0
  21. package/dist/sdk/annotations/capture.js +308 -0
  22. package/dist/sdk/annotations/delivery.d.ts +72 -0
  23. package/dist/sdk/annotations/delivery.js +130 -0
  24. package/dist/sdk/annotations/inspect.d.ts +10 -0
  25. package/dist/sdk/annotations/inspect.js +33 -0
  26. package/dist/sdk/annotations/isolated.d.ts +6 -0
  27. package/dist/sdk/annotations/isolated.js +51 -0
  28. package/dist/sdk/annotations/pageScript.d.ts +28 -0
  29. package/dist/sdk/annotations/pageScript.js +871 -0
  30. package/dist/sdk/annotations/part.d.ts +10 -0
  31. package/dist/sdk/annotations/part.js +155 -0
  32. package/dist/sdk/annotations/queue.d.ts +4 -0
  33. package/dist/sdk/annotations/queue.js +40 -0
  34. package/dist/sdk/annotations/serverMethods.d.ts +3 -0
  35. package/dist/sdk/annotations/serverMethods.js +62 -0
  36. package/dist/sdk/annotations/store.d.ts +167 -0
  37. package/dist/sdk/annotations/store.js +394 -0
  38. package/dist/sdk/browser/backend.d.ts +16 -0
  39. package/dist/sdk/browser/backend.js +17 -0
  40. package/dist/sdk/browser/contracts.d.ts +19 -0
  41. package/dist/sdk/browser/contracts.js +116 -0
  42. package/dist/sdk/browser/index.d.ts +14 -3
  43. package/dist/sdk/browser/index.js +11 -4
  44. package/dist/sdk/browser/serverMethods.d.ts +3 -0
  45. package/dist/sdk/browser/serverMethods.js +7 -0
  46. package/dist/sdk/browser/session.d.ts +1 -0
  47. package/dist/sdk/browser/session.js +45 -9
  48. package/dist/sdk/browser/tools.d.ts +1 -4
  49. package/dist/sdk/browser/tools.js +14 -91
  50. package/dist/sdk/cli.js +24 -15
  51. package/dist/sdk/commands/list.d.ts +3 -0
  52. package/dist/sdk/commands/list.js +75 -0
  53. package/dist/sdk/commands/registry.d.ts +250 -0
  54. package/dist/sdk/commands/registry.js +47 -0
  55. package/dist/sdk/commands/routes.d.ts +16 -0
  56. package/dist/sdk/commands/routes.js +33 -0
  57. package/dist/sdk/core/bus.js +1 -1
  58. package/dist/sdk/core/commandBudget.d.ts +23 -0
  59. package/dist/sdk/core/commandBudget.js +57 -0
  60. package/dist/sdk/core/env.d.ts +1 -1
  61. package/dist/sdk/core/env.js +1 -1
  62. package/dist/sdk/core/index.js +17 -1
  63. package/dist/sdk/core/interactiveProfile.d.ts +46 -0
  64. package/dist/sdk/core/interactiveProfile.js +143 -0
  65. package/dist/sdk/core/workspace.d.ts +13 -0
  66. package/dist/sdk/core/workspace.js +88 -0
  67. package/dist/sdk/core/workspaceBashTool.d.ts +4 -0
  68. package/dist/sdk/core/workspaceBashTool.js +36 -0
  69. package/dist/sdk/core/workspaceSandbox.d.ts +31 -0
  70. package/dist/sdk/core/workspaceSandbox.js +320 -0
  71. package/dist/sdk/core/workspaceWriteOperations.d.ts +22 -0
  72. package/dist/sdk/core/workspaceWriteOperations.js +206 -0
  73. package/dist/sdk/doctor/serverMethods.d.ts +3 -0
  74. package/dist/sdk/doctor/serverMethods.js +8 -0
  75. package/dist/sdk/doctor/types.d.ts +2 -2
  76. package/dist/sdk/harness.d.ts +22 -0
  77. package/dist/sdk/harness.js +84 -5
  78. package/dist/sdk/history/recall.js +8 -8
  79. package/dist/sdk/history/serverMethods.d.ts +3 -0
  80. package/dist/sdk/history/serverMethods.js +8 -0
  81. package/dist/sdk/hub/cli.d.ts +1 -1
  82. package/dist/sdk/hub/cli.js +1 -1
  83. package/dist/sdk/hub/schema.d.ts +1 -1
  84. package/dist/sdk/hub/serverMethods.d.ts +3 -0
  85. package/dist/sdk/hub/serverMethods.js +11 -0
  86. package/dist/sdk/index.d.ts +16 -1
  87. package/dist/sdk/index.js +10 -0
  88. package/dist/sdk/mcp/connections.js +14 -7
  89. package/dist/sdk/mcp/serverMethods.d.ts +3 -0
  90. package/dist/sdk/mcp/serverMethods.js +22 -0
  91. package/dist/sdk/pi/runtime.d.ts +2 -1
  92. package/dist/sdk/pi/runtime.js +2 -2
  93. package/dist/sdk/plugins/builtin.d.ts +1 -0
  94. package/dist/sdk/plugins/builtin.js +145 -33
  95. package/dist/sdk/plugins/builtinRegistry.d.ts +60 -0
  96. package/dist/sdk/plugins/builtinRegistry.js +71 -0
  97. package/dist/sdk/plugins/catalogue.js +2 -1
  98. package/dist/sdk/plugins/cli.d.ts +1 -1
  99. package/dist/sdk/plugins/cli.js +4 -3
  100. package/dist/sdk/plugins/commandPrefix.d.ts +8 -0
  101. package/dist/sdk/plugins/commandPrefix.js +21 -0
  102. package/dist/sdk/plugins/firstParty.d.ts +1 -1
  103. package/dist/sdk/plugins/firstParty.js +8 -2
  104. package/dist/sdk/plugins/inventory.js +4 -3
  105. package/dist/sdk/plugins/manifest.d.ts +2 -0
  106. package/dist/sdk/plugins/manifest.js +2 -1
  107. package/dist/sdk/plugins/surfaces.d.ts +5 -0
  108. package/dist/sdk/plugins/surfaces.js +11 -0
  109. package/dist/sdk/plugins/types.d.ts +15 -0
  110. package/dist/sdk/prompt/compile.d.ts +19 -2
  111. package/dist/sdk/prompt/compile.js +79 -26
  112. package/dist/sdk/prompt/standard.d.ts +17 -0
  113. package/dist/sdk/prompt/standard.js +21 -0
  114. package/dist/sdk/refine/cli.d.ts +1 -1
  115. package/dist/sdk/refine/cli.js +2 -2
  116. package/dist/sdk/refine/serverMethods.d.ts +3 -0
  117. package/dist/sdk/refine/serverMethods.js +20 -0
  118. package/dist/sdk/server/methods.js +27 -86
  119. package/dist/sdk/server/params.d.ts +4 -0
  120. package/dist/sdk/server/params.js +11 -0
  121. package/dist/sdk/server/remote.js +36 -11
  122. package/dist/sdk/sessions/compactionCheckpoint.d.ts +11 -0
  123. package/dist/sdk/sessions/compactionCheckpoint.js +79 -0
  124. package/dist/sdk/sessions/continuation.d.ts +3 -0
  125. package/dist/sdk/sessions/continuation.js +5 -1
  126. package/dist/sdk/sessions/contracts.d.ts +185 -0
  127. package/dist/sdk/sessions/contracts.js +101 -4
  128. package/dist/sdk/sessions/control.d.ts +72 -0
  129. package/dist/sdk/sessions/control.js +158 -0
  130. package/dist/sdk/sessions/initialBash.d.ts +7 -0
  131. package/dist/sdk/sessions/initialBash.js +7 -0
  132. package/dist/sdk/sessions/planMode.d.ts +18 -4
  133. package/dist/sdk/sessions/planMode.js +82 -16
  134. package/dist/sdk/sessions/reasoningSummaries.d.ts +2 -2
  135. package/dist/sdk/sessions/reasoningSummaries.js +3 -3
  136. package/dist/sdk/sessions/registry.d.ts +2 -1
  137. package/dist/sdk/sessions/registry.js +6 -3
  138. package/dist/sdk/sessions/runIndex.js +7 -7
  139. package/dist/sdk/sessions/runStore.d.ts +14 -0
  140. package/dist/sdk/sessions/runStore.js +38 -1
  141. package/dist/sdk/sessions/runner.d.ts +40 -2
  142. package/dist/sdk/sessions/runner.js +163 -15
  143. package/dist/sdk/sessions/sessions.d.ts +40 -8
  144. package/dist/sdk/sessions/sessions.js +279 -27
  145. package/dist/sdk/sessions/sideQuestionServerMethods.d.ts +3 -0
  146. package/dist/sdk/sessions/sideQuestionServerMethods.js +19 -0
  147. package/dist/sdk/sessions/sideQuestions.d.ts +66 -0
  148. package/dist/sdk/sessions/sideQuestions.js +290 -0
  149. package/dist/sdk/sessions/subagentServerMethods.d.ts +3 -0
  150. package/dist/sdk/sessions/subagentServerMethods.js +41 -0
  151. package/dist/sdk/sessions/subagentService.d.ts +22 -2
  152. package/dist/sdk/sessions/subagentService.js +93 -16
  153. package/dist/sdk/sessions/subagents.d.ts +9 -1
  154. package/dist/sdk/sessions/subagents.js +19 -0
  155. package/dist/sdk/sessions/workSlice.d.ts +103 -0
  156. package/dist/sdk/sessions/workSlice.js +297 -0
  157. package/dist/sdk/sessions/workspaceBoundary.d.ts +15 -0
  158. package/dist/sdk/sessions/workspaceBoundary.js +46 -0
  159. package/dist/sdk/terminal/browser.d.ts +62 -0
  160. package/dist/sdk/terminal/browser.js +41 -0
  161. package/dist/sdk/terminal/browserControl.d.ts +29 -0
  162. package/dist/sdk/terminal/browserControl.js +187 -0
  163. package/dist/sdk/terminal/commandHelp.d.ts +17 -0
  164. package/dist/sdk/terminal/commandHelp.js +20 -0
  165. package/dist/sdk/terminal/controller.d.ts +15 -1
  166. package/dist/sdk/terminal/controller.js +67 -8
  167. package/dist/sdk/terminal/opentui/subagentsWidget.d.ts +28 -0
  168. package/dist/sdk/terminal/opentui/subagentsWidget.js +52 -0
  169. package/dist/sdk/terminal/opentui/theme.d.ts +43 -0
  170. package/dist/sdk/terminal/opentui/theme.js +87 -0
  171. package/dist/sdk/terminal/piExtension.d.ts +6 -1
  172. package/dist/sdk/terminal/piExtension.js +169 -84
  173. package/dist/sdk/terminal/socket.d.ts +6 -3
  174. package/dist/sdk/terminal/socket.js +40 -9
  175. package/dist/sdk/terminal/standalone.d.ts +9 -0
  176. package/dist/sdk/terminal/standalone.js +118 -19
  177. package/dist/sdk/terminal/subagentMonitor.d.ts +47 -0
  178. package/dist/sdk/terminal/subagentMonitor.js +152 -0
  179. package/dist/sdk/terminal/terminals.d.ts +60 -7
  180. package/dist/sdk/terminal/terminals.js +598 -47
  181. package/dist/sdk/terminal/trajectory.js +2 -2
  182. package/dist/sdk/terminal/tui/annotateCommand.d.ts +3 -0
  183. package/dist/sdk/terminal/tui/annotateCommand.js +22 -0
  184. package/dist/sdk/terminal/tui/commandPages.d.ts +7 -1
  185. package/dist/sdk/terminal/tui/commandPages.js +57 -6
  186. package/dist/sdk/terminal/tui/commands.d.ts +10 -4
  187. package/dist/sdk/terminal/tui/commands.js +269 -65
  188. package/dist/sdk/terminal/tui/dialogGeometry.d.ts +2 -2
  189. package/dist/sdk/terminal/tui/dialogGeometry.js +4 -2
  190. package/dist/sdk/terminal/tui/dialogs.d.ts +6 -1
  191. package/dist/sdk/terminal/tui/dialogs.js +9 -2
  192. package/dist/sdk/terminal/tui/list.d.ts +14 -1
  193. package/dist/sdk/terminal/tui/list.js +96 -14
  194. package/dist/sdk/terminal/tui/modelPicker.js +1 -6
  195. package/dist/sdk/terminal/tui/page.d.ts +3 -0
  196. package/dist/sdk/terminal/tui/pageOverlay.js +27 -7
  197. package/dist/sdk/terminal/tui/pages/accountsPage.d.ts +26 -4
  198. package/dist/sdk/terminal/tui/pages/accountsPage.js +264 -36
  199. package/dist/sdk/terminal/tui/pages/agentsPage.d.ts +74 -0
  200. package/dist/sdk/terminal/tui/pages/agentsPage.js +190 -0
  201. package/dist/sdk/terminal/tui/pages/annotatePage.d.ts +94 -0
  202. package/dist/sdk/terminal/tui/pages/annotatePage.js +341 -0
  203. package/dist/sdk/terminal/tui/pages/browserPage.d.ts +87 -0
  204. package/dist/sdk/terminal/tui/pages/browserPage.js +738 -0
  205. package/dist/sdk/terminal/tui/pages/doctorPage.d.ts +2 -0
  206. package/dist/sdk/terminal/tui/pages/doctorPage.js +103 -15
  207. package/dist/sdk/terminal/tui/pages/notesPage.d.ts +75 -0
  208. package/dist/sdk/terminal/tui/pages/notesPage.js +201 -0
  209. package/dist/sdk/terminal/tui/pages/pluginsPage.d.ts +0 -5
  210. package/dist/sdk/terminal/tui/pages/pluginsPage.js +9 -3
  211. package/dist/sdk/terminal/tui/pages/settingsPage.d.ts +8 -0
  212. package/dist/sdk/terminal/tui/pages/settingsPage.js +16 -1
  213. package/dist/sdk/terminal/tui/pages/subagentsPage.d.ts +24 -0
  214. package/dist/sdk/terminal/tui/pages/subagentsPage.js +152 -0
  215. package/dist/sdk/terminal/tui/pages/trajectoryPage.d.ts +12 -5
  216. package/dist/sdk/terminal/tui/pages/trajectoryPage.js +156 -83
  217. package/dist/sdk/terminal/tui/pages/usagePage.d.ts +43 -0
  218. package/dist/sdk/terminal/tui/pages/usagePage.js +370 -0
  219. package/dist/sdk/terminal/tui/pages/worktreesPage.d.ts +72 -0
  220. package/dist/sdk/terminal/tui/pages/worktreesPage.js +203 -0
  221. package/dist/sdk/terminal/tui/piAdapter.d.ts +1 -2
  222. package/dist/sdk/terminal/tui/piAdapter.js +3 -5
  223. package/dist/sdk/terminal/tui/planMode.d.ts +1 -1
  224. package/dist/sdk/terminal/tui/planMode.js +1 -1
  225. package/dist/sdk/terminal/tui/pluginCommands.d.ts +16 -0
  226. package/dist/sdk/terminal/tui/pluginCommands.js +31 -0
  227. package/dist/sdk/terminal/tui/questions.d.ts +2 -0
  228. package/dist/sdk/terminal/tui/runtime.js +27 -5
  229. package/dist/sdk/terminal/tui/sessionPicker.d.ts +40 -0
  230. package/dist/sdk/terminal/tui/sessionPicker.js +141 -0
  231. package/dist/sdk/terminal/tui/settingsModel.d.ts +2 -0
  232. package/dist/sdk/terminal/tui/settingsModel.js +32 -2
  233. package/dist/sdk/terminal/tui/sideQuestion.d.ts +26 -26
  234. package/dist/sdk/terminal/tui/sideQuestion.js +131 -98
  235. package/dist/sdk/terminal/tui/ui.d.ts +2 -0
  236. package/dist/sdk/terminal/tui/ui.js +1 -0
  237. package/dist/sdk/tools/askUser.js +26 -8
  238. package/dist/sdk/tools/checklist.js +3 -2
  239. package/dist/sdk/tools/checkpoint.d.ts +27 -0
  240. package/dist/sdk/tools/checkpoint.js +154 -0
  241. package/dist/sdk/tools/getCapabilities.d.ts +31 -0
  242. package/dist/sdk/tools/getCapabilities.js +34 -0
  243. package/dist/sdk/tools/harnessTools.d.ts +2 -0
  244. package/dist/sdk/tools/harnessTools.js +1 -1
  245. package/dist/sdk/tools/spawnSubagent.js +14 -4
  246. package/dist/sdk/usage/analytics.d.ts +20 -0
  247. package/dist/sdk/usage/analytics.js +151 -0
  248. package/dist/sdk/usage/ledger.d.ts +38 -0
  249. package/dist/sdk/usage/ledger.js +142 -0
  250. package/dist/sdk/usage/limits/adapters.d.ts +11 -0
  251. package/dist/sdk/usage/limits/adapters.js +349 -0
  252. package/dist/sdk/usage/limits/capture.d.ts +8 -0
  253. package/dist/sdk/usage/limits/capture.js +47 -0
  254. package/dist/sdk/usage/limits/http.d.ts +12 -0
  255. package/dist/sdk/usage/limits/http.js +51 -0
  256. package/dist/sdk/usage/limits/normalize.d.ts +15 -0
  257. package/dist/sdk/usage/limits/normalize.js +211 -0
  258. package/dist/sdk/usage/limits/service.d.ts +30 -0
  259. package/dist/sdk/usage/limits/service.js +176 -0
  260. package/dist/sdk/usage/limits/types.d.ts +65 -0
  261. package/dist/sdk/usage/limits/types.js +1 -0
  262. package/dist/sdk/usage/serverMethods.d.ts +3 -0
  263. package/dist/sdk/usage/serverMethods.js +22 -0
  264. package/dist/sdk/usage/service.d.ts +32 -0
  265. package/dist/sdk/usage/service.js +66 -0
  266. package/dist/sdk/usage/settings.d.ts +7 -0
  267. package/dist/sdk/usage/settings.js +28 -0
  268. package/dist/sdk/usage/types.d.ts +60 -0
  269. package/dist/sdk/usage/types.js +4 -0
  270. package/dist/sdk/web/part.d.ts +10 -0
  271. package/dist/sdk/web/part.js +6 -0
  272. package/dist/sdk/web/serverMethods.d.ts +3 -0
  273. package/dist/sdk/web/serverMethods.js +7 -0
  274. package/dist/sdk/wiki/serverMethods.d.ts +3 -0
  275. package/dist/sdk/wiki/serverMethods.js +26 -0
  276. package/dist/sdk/worktrees/cli.d.ts +11 -0
  277. package/dist/sdk/worktrees/cli.js +146 -0
  278. package/dist/sdk/worktrees/copy.d.ts +9 -0
  279. package/dist/sdk/worktrees/copy.js +111 -0
  280. package/dist/sdk/worktrees/git.d.ts +16 -0
  281. package/dist/sdk/worktrees/git.js +81 -0
  282. package/dist/sdk/worktrees/serverMethods.d.ts +3 -0
  283. package/dist/sdk/worktrees/serverMethods.js +30 -0
  284. package/dist/sdk/worktrees/service.d.ts +73 -0
  285. package/dist/sdk/worktrees/service.js +483 -0
  286. package/dist/sdk/worktrees/setup.d.ts +23 -0
  287. package/dist/sdk/worktrees/setup.js +65 -0
  288. package/dist/sdk/worktrees/types.d.ts +91 -0
  289. package/dist/sdk/worktrees/types.js +1 -0
  290. package/dist/sdk/world/cli.d.ts +2 -2
  291. package/dist/sdk/world/cli.js +6 -6
  292. package/dist/sdk/world/world.d.ts +3 -1
  293. package/dist/sdk/world/world.js +8 -9
  294. package/dist/terminal-opentui.js +16456 -5966
  295. package/dist/terminal-pi.js +1579 -573
  296. package/docs/README.md +22 -2
  297. package/docs/architecture/00-overview.md +28 -16
  298. package/docs/architecture/02-sessions-and-runs.md +184 -25
  299. package/docs/architecture/03-variants.md +10 -2
  300. package/docs/architecture/04-prompt.md +65 -7
  301. package/docs/architecture/06-plugins-and-capabilities.md +57 -4
  302. package/docs/architecture/07-tools-and-gate.md +112 -17
  303. package/docs/architecture/08-memory-and-history.md +4 -2
  304. package/docs/architecture/09-accounts-and-connectors.md +39 -1
  305. package/docs/architecture/10-server-protocol.md +168 -4
  306. package/docs/architecture/11-terminal.md +251 -8
  307. package/docs/architecture/12-storage.md +45 -3
  308. package/docs/architecture/13-world-tools.md +49 -6
  309. package/docs/architecture/14-surfaces-and-parity.md +136 -0
  310. package/docs/architecture/README.md +17 -4
  311. package/docs/architecture/proposals/PROP-0006-run-checklist.md +4 -2
  312. package/docs/architecture/proposals/PROP-0013-resource-browser.md +58 -0
  313. package/docs/architecture/proposals/PROP-0014-fullscreen-browser-tabs.md +58 -0
  314. package/docs/architecture/proposals/PROP-0015-one-capability-surface.md +88 -0
  315. package/docs/architecture/proposals/PROP-0016-managed-worktrees.md +156 -0
  316. package/docs/architecture/proposals/PROP-0017-page-annotations.md +131 -0
  317. package/docs/architecture/proposals/PROP-0018-annotation-queue-and-app-targets.md +179 -0
  318. package/docs/architecture/proposals/PROP-0019-contextual-side-questions.md +122 -0
  319. package/docs/architecture/proposals/PROP-0020-builtin-feature-plugins.md +105 -0
  320. package/docs/architecture/proposals/PROP-0021-command-namespaces-and-duplicates.md +99 -0
  321. package/docs/architecture/proposals/PROP-0022-plan-only-questions.md +102 -0
  322. package/docs/architecture/proposals/PROP-0023-xagent-cli-name.md +61 -0
  323. package/docs/architecture/proposals/PROP-0024-question-sketch-previews.md +130 -0
  324. package/docs/architecture/proposals/PROP-0025-contextual-question-wireframes.md +49 -0
  325. package/docs/architecture/proposals/PROP-0026-connected-sketch-display.md +94 -0
  326. package/docs/architecture/proposals/PROP-0027-xingular-agent-product-identity.md +60 -0
  327. package/docs/architecture/proposals/PROP-0028-normalize-question-wireframes.md +55 -0
  328. package/docs/architecture/proposals/PROP-0029-agent-profiles.md +124 -0
  329. package/docs/architecture/proposals/PROP-0030-subagent-title.md +86 -0
  330. package/docs/architecture/proposals/PROP-0031-plugin-command-owner-namespaces.md +54 -0
  331. package/docs/architecture/proposals/PROP-0032-planning-quality.md +196 -0
  332. package/docs/architecture/proposals/PROP-0033-compaction-plan-checkpoints.md +75 -0
  333. package/docs/architecture/proposals/PROP-0034-conversation-fast-mode.md +120 -0
  334. package/docs/architecture/proposals/PROP-0035-retain-subagent-status.md +47 -0
  335. package/docs/architecture/proposals/PROP-0036-first-class-harness-commands.md +59 -0
  336. package/docs/architecture/proposals/PROP-0037-workspace-write-boundary.md +63 -0
  337. package/docs/architecture/proposals/PROP-0038-annotate-open-shadow-roots.md +73 -0
  338. package/docs/architecture/proposals/PROP-0039-workspace-boundary-hardening.md +139 -0
  339. package/docs/architecture/proposals/PROP-0040-one-launch-path.md +87 -0
  340. package/docs/architecture/proposals/PROP-0041-bounded-work-slices.md +247 -0
  341. package/docs/architecture/proposals/PROP-0042-usage-and-provider-limits.md +127 -0
  342. package/docs/architecture/proposals/PROP-0043-read-only-plan-bash.md +74 -0
  343. package/docs/architecture/proposals/README.md +39 -0
  344. package/docs/decisions/ADR-0001-owned-terminal.md +1 -1
  345. package/docs/decisions/ADR-0009-xharness-command.md +6 -5
  346. package/docs/decisions/ADR-0014-public-release-baseline.md +3 -2
  347. package/docs/decisions/ADR-0015-resource-browser.md +28 -0
  348. package/docs/decisions/ADR-0016-fullscreen-browser-tabs.md +31 -0
  349. package/docs/decisions/ADR-0017-one-capability-surface.md +34 -0
  350. package/docs/decisions/ADR-0018-builtin-feature-plugins.md +35 -0
  351. package/docs/decisions/ADR-0019-managed-git-worktrees.md +31 -0
  352. package/docs/decisions/ADR-0020-page-annotations.md +37 -0
  353. package/docs/decisions/ADR-0021-required-command-namespaces.md +40 -0
  354. package/docs/decisions/ADR-0022-plan-only-question-tools.md +34 -0
  355. package/docs/decisions/ADR-0023-contextual-side-question-runs.md +35 -0
  356. package/docs/decisions/ADR-0024-xagent-command.md +40 -0
  357. package/docs/decisions/ADR-0025-question-choice-sketches.md +49 -0
  358. package/docs/decisions/ADR-0026-contextual-question-wireframes.md +58 -0
  359. package/docs/decisions/ADR-0027-connected-sketch-display.md +60 -0
  360. package/docs/decisions/ADR-0028-xingular-agent-product-identity.md +56 -0
  361. package/docs/decisions/ADR-0029-normalized-question-wireframe-borders.md +33 -0
  362. package/docs/decisions/ADR-0030-agent-profiles.md +43 -0
  363. package/docs/decisions/ADR-0031-plugin-command-owner-namespaces.md +37 -0
  364. package/docs/decisions/ADR-0032-compaction-run-checkpoints.md +33 -0
  365. package/docs/decisions/ADR-0033-plan-mode-research-delegation.md +47 -0
  366. package/docs/decisions/ADR-0034-first-class-harness-terminal-commands.md +25 -0
  367. package/docs/decisions/ADR-0035-one-launch-path.md +40 -0
  368. package/docs/decisions/ADR-0036-workspace-write-operation-boundary.md +54 -0
  369. package/docs/decisions/ADR-0037-usage-ledger-and-provider-limits.md +44 -0
  370. package/docs/decisions/ADR-0038-plan-bash-os-read-only.md +29 -0
  371. package/docs/doctor.md +10 -4
  372. package/docs/experiments/EXP-0003-performance-audit/data/session-runtime-replication.json +420 -0
  373. package/docs/experiments/EXP-0003-performance-audit/data/session-runtime.json +420 -0
  374. package/docs/experiments/EXP-0003-performance-audit/data/terminal-compact.json +376 -0
  375. package/docs/experiments/EXP-0003-performance-audit/data/terminal-history.json +422 -0
  376. package/docs/experiments/EXP-0003-performance-audit/data/terminal-question.json +95 -0
  377. package/docs/experiments/EXP-0003-performance-audit/data/terminal-wide.json +1036 -0
  378. package/docs/experiments/EXP-0003-performance-audit/plan.md +109 -0
  379. package/docs/experiments/EXP-0003-performance-audit/results.md +151 -0
  380. package/docs/features/README.md +26 -0
  381. package/docs/features/interfaces/README.md +1 -1
  382. package/docs/future-work.md +48 -0
  383. package/docs/handoff-2026-10-25-app-parity-and-solo-tabs.md +202 -0
  384. package/docs/parity.md +134 -0
  385. package/docs/releases/1.0.0.md +2 -2
  386. package/docs/releases/1.1.1.md +28 -0
  387. package/docs/releases/1.2.0.md +49 -0
  388. package/docs/releases/4.0.0.md +47 -0
  389. package/docs/releases/README.md +37 -27
  390. package/docs/study/00-index.md +8 -0
  391. package/docs/study/STUDY-0003-long-horizon-runs.md +210 -0
  392. package/docs/study/autonomous-engineering-sources.md +277 -0
  393. package/docs/study/autonomy-sources.md +147 -0
  394. package/docs/study/proactive-autonomy.md +408 -0
  395. package/docs/study/tui-resource-browser.md +78 -0
  396. package/docs/terminal-tui.md +386 -113
  397. package/package.json +6 -5
@@ -1,4 +1,4 @@
1
- import { matchesKey, truncateToWidth } from '@earendil-works/pi-tui';
1
+ import { matchesKey, truncateToWidth, visibleWidth, wrapTextWithAnsi } from '@earendil-works/pi-tui';
2
2
  import { HEALTH, accountActions, accountDetails, addAccount, editPool, runAccountAction } from "../../accountsMenu.js";
3
3
  import { safeText } from "../chrome.js";
4
4
  import { FilterList } from "../list.js";
@@ -10,27 +10,59 @@ const BACK = 'Back';
10
10
  const HEALTH_TONE = { ok: 'success', limited: 'warning', auth_expired: 'error', signed_out: 'error', error: 'error', unknown: 'dim' };
11
11
  export class AccountsPage {
12
12
  options;
13
+ presentation = 'fullscreen';
14
+ ownsNavigation = true;
13
15
  overview;
14
16
  error;
15
17
  section = 'accounts';
18
+ focus = 'list';
19
+ detailOpen = false;
20
+ detailAccountId;
16
21
  busy = false;
17
22
  disposed = false;
23
+ compact = false;
24
+ wideListWidth = 0;
25
+ actionRowsStart = 0;
18
26
  list;
27
+ actionList;
19
28
  constructor(options) {
20
29
  this.options = options;
21
30
  this.list = new FilterList({
22
31
  theme: options.theme, search: 'always', noun: ['row', 'rows'], empty: 'Nothing here yet',
23
32
  searchContext: () => 'accounts and pools',
24
- onActivate: row => { void this.activateRow(row); }
33
+ onActivate: row => this.activateRow(row)
34
+ });
35
+ this.actionList = new FilterList({
36
+ theme: options.theme, search: 'never', noun: ['action', 'actions'],
37
+ onActivate: row => { if (row.data)
38
+ void this.activateAction(row.data); }
25
39
  });
26
40
  }
27
- get focused() { return this.list.focused; }
28
- set focused(value) { this.list.focused = value; }
41
+ get focused() { return this.list.focused || this.actionList.focused; }
42
+ set focused(value) { this.list.focused = value && this.focus === 'list'; this.actionList.focused = value && this.focus === 'actions'; }
29
43
  activate() { void this.reload(); }
30
44
  dispose() { this.disposed = true; }
31
45
  tabs() { return SECTIONS; }
32
46
  activeTab() { return this.section; }
33
- hint() { return this.list.query ? 'Enter open · Esc clear' : 'Enter actions · type to search'; }
47
+ selectTab(id) {
48
+ if (!SECTIONS.some(tab => tab.id === id))
49
+ return;
50
+ this.section = id;
51
+ this.detailOpen = false;
52
+ this.detailAccountId = undefined;
53
+ this.focus = 'list';
54
+ this.list.focused = true;
55
+ this.actionList.focused = false;
56
+ this.fill();
57
+ this.options.changed();
58
+ }
59
+ hint() {
60
+ if (this.compact && this.detailOpen)
61
+ return '↑/↓ actions · Enter activate';
62
+ if (this.focus === 'actions')
63
+ return '↑/↓ actions · Enter activate · Tab focus';
64
+ return this.list.query ? 'Tab focus · Enter open · Esc clear' : 'Tab focus · ←/→ sections · Enter open';
65
+ }
34
66
  async reload() {
35
67
  try {
36
68
  this.overview = await this.options.call('accounts.list');
@@ -45,8 +77,12 @@ export class AccountsPage {
45
77
  this.options.changed();
46
78
  }
47
79
  /** Searching looks through accounts and pools together; otherwise the page shows the current section. */
48
- fill() {
80
+ fill(compact = this.compact) {
81
+ this.compact = compact;
49
82
  this.list.setRows(this.list.query ? [...this.accountRows(), ...this.poolRows()] : this.section === 'accounts' ? this.accountRows() : this.poolRows());
83
+ const account = this.detailOpen ? this.detailAccount() : undefined;
84
+ if (account)
85
+ this.fillActions(account);
50
86
  }
51
87
  accountRows() {
52
88
  const overview = this.overview;
@@ -56,7 +92,7 @@ export class AccountsPage {
56
92
  const accounts = overview.accounts.map(account => ({
57
93
  id: `account:${account.id}`, group: 'Accounts',
58
94
  label: `${account.isDefault ? '●' : '○'} ${safeText(account.label)}`,
59
- detail: `${HEALTH[account.state.health] ?? HEALTH.unknown}${session?.accountId === account.id ? ' · this session' : ''}`,
95
+ detail: `${HEALTH[account.state.health] ?? HEALTH.unknown}${session?.accountId === account.id ? ` · ${this.compact ? 'current' : 'this session'}` : ''}`,
60
96
  tone: HEALTH_TONE[account.state.health] ?? 'dim',
61
97
  searchText: `${account.label} ${account.state.identity ?? ''} ${HEALTH[account.state.health] ?? ''}`
62
98
  }));
@@ -80,6 +116,71 @@ export class AccountsPage {
80
116
  ];
81
117
  }
82
118
  account(id) { return this.overview?.accounts.find(account => `account:${account.id}` === id); }
119
+ detailAccount() { return this.detailAccountId ? this.overview?.accounts.find(account => account.id === this.detailAccountId) : undefined; }
120
+ selectGrouped(title, options) {
121
+ return this.options.ui.selectGrouped
122
+ ? this.options.ui.selectGrouped(title, options)
123
+ : this.options.ui.select(title, options.map(option => option.label));
124
+ }
125
+ accountActionGroup(label) {
126
+ if (label.startsWith('Use for this session') || label === 'Make default for new sessions')
127
+ return 'Session';
128
+ if (['Rename', 'Check', 'Activity'].includes(label))
129
+ return 'Account';
130
+ if (['Sign in', 'Remove a provider', 'Sign out'].includes(label))
131
+ return 'Sign-in';
132
+ if (label === 'Remove account')
133
+ return 'Remove';
134
+ return undefined;
135
+ }
136
+ fillActions(account) {
137
+ const isSession = this.options.session()?.accountId === account.id;
138
+ this.actionList.setRows(accountActions(account, isSession, false).map(label => ({
139
+ id: label, label, group: this.accountActionGroup(label), data: label
140
+ })));
141
+ this.actionList.focused = this.focus === 'actions';
142
+ }
143
+ detailLines(width, summary = false) {
144
+ const theme = this.options.theme();
145
+ const account = this.detailOpen ? this.detailAccount() : this.section === 'accounts' ? this.account(this.list.selected?.id ?? '') : undefined;
146
+ let title;
147
+ let lines;
148
+ if (account) {
149
+ title = account.label;
150
+ if (summary) {
151
+ const state = account.state;
152
+ const signedIn = state.signedIn === null ? 'not checked' : state.signedIn ? 'yes' : 'no';
153
+ lines = [
154
+ `Health: ${HEALTH[state.health] ?? HEALTH.unknown} · Default: ${account.isDefault ? 'yes' : 'no'} · Signed in: ${signedIn}`,
155
+ `Providers: ${state.piProviders.length ? state.piProviders.join(', ') : 'none'}`,
156
+ ...(state.identity ? [`Identity: ${state.identity}`] : []),
157
+ ...(state.plan ? [`Plan: ${state.plan}`] : []),
158
+ `Last checked: ${state.lastCheckedAt ? new Date(state.lastCheckedAt).toLocaleString() : 'never'}`,
159
+ ...state.limits.map(limit => `${limit.label}: ${limit.usedPercent ?? '?'}% used${limit.resetsAt ? `, resets ${new Date(limit.resetsAt).toLocaleString()}` : ''}`),
160
+ ...(state.limits.length ? [] : ['Usage limits: not reported']),
161
+ ...(state.capabilities.length ? [`Detected: ${state.capabilities.join(', ')}`] : []),
162
+ ...(state.lastError ? [`Last error: ${state.lastError.message}`] : [])
163
+ ];
164
+ }
165
+ else {
166
+ lines = accountDetails(account).split('\n').slice(1);
167
+ }
168
+ }
169
+ else if (this.section === 'pools') {
170
+ const pool = this.overview?.pools.find(item => `pool:${item.id}` === this.list.selected?.id);
171
+ const names = new Map(this.overview?.accounts.map(item => [item.id, item.label]) ?? []);
172
+ title = pool ? `Pool · ${pool.label}` : 'Pools';
173
+ lines = pool
174
+ ? ['New sessions use the first ready account in this order:', ...pool.accountIds.map((id, index) => `${index + 1}. ${names.get(id) ?? 'removed account'}`)]
175
+ : ['Select a pool to see its account order.'];
176
+ }
177
+ else {
178
+ title = 'Account details';
179
+ lines = ['Select an account to see health and usage details.'];
180
+ }
181
+ const wrap = (text) => wrapTextWithAnsi(safeText(text), Math.max(1, width));
182
+ return [theme.bold(theme.fg('accent', safeText(title))), ...lines.flatMap(wrap).map(line => theme.fg('muted', line))];
183
+ }
83
184
  /** Runs one thing at a time and always reads the accounts again afterwards. */
84
185
  async guarded(work) {
85
186
  if (this.busy)
@@ -96,8 +197,31 @@ export class AccountsPage {
96
197
  }
97
198
  await this.reload();
98
199
  }
200
+ closeDetails() {
201
+ this.detailOpen = false;
202
+ this.detailAccountId = undefined;
203
+ this.focus = 'list';
204
+ this.list.focused = true;
205
+ this.actionList.focused = false;
206
+ this.options.changed();
207
+ }
208
+ openDetails() {
209
+ const account = this.account(this.list.selected?.id ?? '');
210
+ if (!account)
211
+ return;
212
+ this.detailOpen = true;
213
+ this.detailAccountId = account.id;
214
+ this.focus = 'actions';
215
+ this.list.focused = false;
216
+ this.fillActions(account);
217
+ this.options.changed();
218
+ }
99
219
  activateRow(row) {
100
- return this.guarded(async () => {
220
+ if (this.account(row.id)) {
221
+ this.openDetails();
222
+ return;
223
+ }
224
+ void this.guarded(async () => {
101
225
  const { ui, call } = this.options;
102
226
  const overview = this.overview;
103
227
  if (!overview)
@@ -120,43 +244,119 @@ export class AccountsPage {
120
244
  return;
121
245
  }
122
246
  const pool = overview.pools.find(candidate => `pool:${candidate.id}` === row.id);
123
- if (pool) {
124
- const action = await ui.select(pool.label, ['Edit', 'Delete', BACK]);
125
- if (action === 'Edit')
126
- await editPool(ui, call, overview, pool);
127
- else if (action === 'Delete' && await ui.confirm(`Delete ${pool.label}?`, 'The accounts stay. Only the pool is removed.')) {
128
- try {
129
- await call('accounts.removePool', { poolId: pool.id });
130
- }
131
- catch (error) {
132
- ui.notify(error instanceof Error ? error.message : String(error), 'error');
133
- }
134
- }
247
+ if (!pool)
135
248
  return;
249
+ const action = await this.selectGrouped(pool.label, [
250
+ { label: 'Edit', group: 'Manage' }, { label: 'Delete', group: 'Remove' }, { label: BACK }
251
+ ]);
252
+ if (action === 'Edit')
253
+ await editPool(ui, call, overview, pool);
254
+ else if (action === 'Delete' && await ui.confirm(`Delete ${pool.label}?`, 'The accounts stay. Only the pool is removed.')) {
255
+ try {
256
+ await call('accounts.removePool', { poolId: pool.id });
257
+ }
258
+ catch (error) {
259
+ ui.notify(error instanceof Error ? error.message : String(error), 'error');
260
+ }
136
261
  }
137
- const account = this.account(row.id);
138
- if (!account)
139
- return;
140
- const isSession = this.options.session()?.accountId === account.id;
141
- const action = await ui.select(`${account.label} · ${HEALTH[account.state.health] ?? HEALTH.unknown}`, [...accountActions(account, isSession, false), BACK]);
142
- if (!action || action === BACK)
143
- return;
144
- const outcome = await runAccountAction(ui, call, overview, account, action);
262
+ });
263
+ }
264
+ async activateAction(action) {
265
+ if (this.busy)
266
+ return;
267
+ const account = this.detailAccount();
268
+ const overview = this.overview;
269
+ if (!account || !overview)
270
+ return;
271
+ await this.guarded(async () => {
272
+ const outcome = await runAccountAction(this.options.ui, this.options.call, overview, account, action);
145
273
  if (outcome === 'relaunch')
146
274
  this.options.relaunch();
275
+ if (outcome === 'gone') {
276
+ this.detailOpen = false;
277
+ this.detailAccountId = undefined;
278
+ this.focus = 'list';
279
+ this.list.focused = true;
280
+ this.actionList.focused = false;
281
+ }
147
282
  });
148
283
  }
284
+ moveSection(delta) {
285
+ const index = SECTIONS.findIndex(tab => tab.id === this.section);
286
+ this.section = SECTIONS[(index + delta + SECTIONS.length) % SECTIONS.length].id;
287
+ this.detailOpen = false;
288
+ this.detailAccountId = undefined;
289
+ this.focus = 'list';
290
+ this.list.focused = true;
291
+ this.actionList.focused = false;
292
+ this.fill();
293
+ this.options.changed();
294
+ }
149
295
  handleMouse(event) {
296
+ if (this.compact && this.detailOpen) {
297
+ this.focus = 'actions';
298
+ this.actionList.focused = true;
299
+ return this.actionList.handleMouse({ ...event, y: event.y - this.actionRowsStart });
300
+ }
301
+ if (this.wideListWidth && event.x >= this.wideListWidth + 3) {
302
+ if (!this.detailOpen)
303
+ return undefined;
304
+ this.focus = 'actions';
305
+ this.actionList.focused = true;
306
+ return this.actionList.handleMouse({ ...event, x: event.x - this.wideListWidth - 3, y: event.y - this.actionRowsStart });
307
+ }
308
+ this.focus = 'list';
309
+ this.actionList.focused = false;
310
+ this.list.focused = true;
150
311
  return this.list.handleMouse(event);
151
312
  }
152
313
  handleInput(data) {
153
- if (matchesKey(data, 'tab') || matchesKey(data, 'shift+tab')) {
154
- if (!this.list.query) {
155
- this.section = this.section === 'accounts' ? 'pools' : 'accounts';
156
- this.fill();
314
+ if (matchesKey(data, 'escape')) {
315
+ if (this.detailOpen) {
316
+ this.closeDetails();
317
+ return true;
318
+ }
319
+ if (this.focus !== 'list') {
320
+ this.focus = 'list';
321
+ this.list.focused = true;
322
+ this.actionList.focused = false;
323
+ this.options.changed();
324
+ return true;
157
325
  }
326
+ return this.list.handleInput(data);
327
+ }
328
+ if (matchesKey(data, 'tab') || matchesKey(data, 'shift+tab')) {
329
+ const order = this.compact && this.detailOpen
330
+ ? ['tabs', 'actions']
331
+ : this.detailOpen ? ['tabs', 'list', 'actions'] : ['tabs', 'list'];
332
+ const index = order.indexOf(this.focus);
333
+ const step = matchesKey(data, 'shift+tab') ? -1 : 1;
334
+ this.focus = order[(index + step + order.length) % order.length];
335
+ this.list.focused = this.focus === 'list';
336
+ this.actionList.focused = this.focus === 'actions';
337
+ this.options.changed();
158
338
  return true;
159
339
  }
340
+ if (this.focus === 'tabs') {
341
+ if (matchesKey(data, 'left')) {
342
+ this.moveSection(-1);
343
+ return true;
344
+ }
345
+ if (matchesKey(data, 'right')) {
346
+ this.moveSection(1);
347
+ return true;
348
+ }
349
+ if (matchesKey(data, 'enter')) {
350
+ this.focus = this.compact && this.detailOpen ? 'actions' : 'list';
351
+ this.list.focused = this.focus === 'list';
352
+ this.actionList.focused = this.focus === 'actions';
353
+ this.options.changed();
354
+ return true;
355
+ }
356
+ return false;
357
+ }
358
+ if (this.focus === 'actions')
359
+ return this.actionList.handleInput(data);
160
360
  const before = this.list.query;
161
361
  const used = this.list.handleInput(data);
162
362
  if (this.list.query !== before)
@@ -169,13 +369,41 @@ export class AccountsPage {
169
369
  return [];
170
370
  if (!this.overview)
171
371
  return [theme.fg(this.error ? 'error' : 'dim', this.error ? `Could not read accounts: ${this.error}` : 'Reading accounts…')];
172
- const selected = this.section === 'accounts' ? this.account(this.list.selected?.id ?? '') : undefined;
173
- const details = selected ? accountDetails(selected).split('\n').slice(1) : [];
174
- // The list gets the rows it needs first; the details take what is left.
372
+ const compact = width < 88;
373
+ if (compact !== this.compact)
374
+ this.fill(compact);
375
+ if (compact && this.detailOpen && this.detailAccount()) {
376
+ const details = this.detailLines(width, true);
377
+ this.actionRowsStart = details.length + 1;
378
+ const actions = this.actionList.render(width, Math.max(1, rows - this.actionRowsStart));
379
+ return [...details, '', ...actions].slice(0, rows).map(line => truncateToWidth(line, width, ''));
380
+ }
381
+ if (!compact) {
382
+ this.wideListWidth = Math.min(width - 36, Math.max(32, Math.floor((width - 3) * 0.42)));
383
+ const detailWidth = Math.max(1, width - this.wideListWidth - 3);
384
+ const list = this.list.render(this.wideListWidth, rows);
385
+ const details = this.detailLines(detailWidth);
386
+ let inspector = details;
387
+ const account = this.detailOpen ? this.detailAccount() : undefined;
388
+ if (this.detailOpen && account) {
389
+ this.fillActions(account);
390
+ this.actionRowsStart = inspector.length + 1;
391
+ inspector = [...inspector, '', ...this.actionList.render(detailWidth, Math.max(1, rows - this.actionRowsStart))];
392
+ }
393
+ inspector = inspector.slice(0, rows);
394
+ return Array.from({ length: Math.min(rows, Math.max(list.length, inspector.length)) }, (_, index) => {
395
+ const left = truncateToWidth(list[index] ?? '', this.wideListWidth, '');
396
+ const right = truncateToWidth(inspector[index] ?? '', detailWidth, '');
397
+ const gap = ' '.repeat(Math.max(0, this.wideListWidth - visibleWidth(left)));
398
+ return truncateToWidth(`${left}${gap}${theme.fg('dim', ' │ ')}${right}`, width, '');
399
+ });
400
+ }
401
+ this.wideListWidth = 0;
402
+ const details = this.detailLines(width).slice(1);
175
403
  const detailRows = Math.min(details.length, 9, Math.max(0, rows - 2 - this.list.height));
176
404
  const lines = this.list.render(width, Math.max(2, rows - 1 - (detailRows ? detailRows + 1 : 0)));
177
405
  if (detailRows)
178
- lines.push('', ...details.slice(0, detailRows).map(line => theme.fg('muted', line)));
406
+ lines.push('', ...details.slice(0, detailRows));
179
407
  return lines.slice(0, rows).map(line => truncateToWidth(line, width, ''));
180
408
  }
181
409
  }
@@ -0,0 +1,74 @@
1
+ /**
2
+ * The Agents page (`/agents`): the agent profiles a subagent can start from (PROP-0029). Built-in
3
+ * and variant profiles are read-only and can be copied; a person's own global or project profiles
4
+ * open in their editor and can be deleted. The harness writes every file.
5
+ */
6
+ import type { Theme } from '@earendil-works/pi-coding-agent';
7
+ import type { AgentProfile, ProfileListing, ProfileProblem } from "../../../agents/profiles.js";
8
+ import type { MenuCall, MenuUi } from "../../pluginMenu.js";
9
+ import { type TabInfo } from "../chrome.js";
10
+ import { type ListRow } from "../list.js";
11
+ import type { TerminalMouseInput } from "../mouse.js";
12
+ import type { TerminalPage } from "../page.js";
13
+ export interface AgentsOverview extends ProfileListing {
14
+ dirs: {
15
+ global: string;
16
+ project: string | null;
17
+ };
18
+ }
19
+ export interface AgentsPageOptions {
20
+ theme(): Theme;
21
+ call: MenuCall;
22
+ ui: MenuUi & {
23
+ input(title: string, placeholder?: string): Promise<string | undefined>;
24
+ };
25
+ /** Opens a file in the person's editor; absent when the host has none. */
26
+ edit?(path: string): Promise<void>;
27
+ changed(): void;
28
+ }
29
+ type Row = {
30
+ kind: 'new';
31
+ } | {
32
+ kind: 'profile';
33
+ profile: AgentProfile;
34
+ } | {
35
+ kind: 'problem';
36
+ problem: ProfileProblem;
37
+ };
38
+ export declare const AGENT_ACTIONS: {
39
+ readonly edit: "Edit in your editor";
40
+ readonly copy: "Copy to a new profile";
41
+ readonly remove: "Delete";
42
+ };
43
+ export declare const AGENT_SCOPES: {
44
+ readonly project: "Project: this repository only";
45
+ readonly global: "Global: every folder";
46
+ };
47
+ export declare class AgentsPage implements TerminalPage {
48
+ private readonly options;
49
+ presentation: "fullscreen";
50
+ private overview;
51
+ private error;
52
+ private busy;
53
+ private disposed;
54
+ private readonly list;
55
+ constructor(options: AgentsPageOptions);
56
+ get focused(): boolean;
57
+ set focused(value: boolean);
58
+ activate(): void;
59
+ dispose(): void;
60
+ tabs(): readonly TabInfo[];
61
+ activeTab(): string;
62
+ hint(): string;
63
+ reload(): Promise<void>;
64
+ rows(): ListRow<Row>[];
65
+ private attempt;
66
+ private open;
67
+ private create;
68
+ activateRow(row: Row): Promise<void>;
69
+ private remove;
70
+ handleMouse(event: TerminalMouseInput): import("@earendil-works/pi-tui").TuiMouseEventResult | undefined;
71
+ handleInput(data: string): boolean;
72
+ render(width: number, rows: number): string[];
73
+ }
74
+ export {};
@@ -0,0 +1,190 @@
1
+ import { stripTerminalSequences, truncateToWidth, wrapTextWithAnsi } from '@earendil-works/pi-tui';
2
+ import { formatVariant } from "../../../agents/profiles.js";
3
+ import { headerRow, neutralRule, padded } from "../chrome.js";
4
+ import { FilterList } from "../list.js";
5
+ export const AGENT_ACTIONS = {
6
+ edit: 'Edit in your editor',
7
+ copy: 'Copy to a new profile',
8
+ remove: 'Delete'
9
+ };
10
+ export const AGENT_SCOPES = {
11
+ project: 'Project: this repository only',
12
+ global: 'Global: every folder'
13
+ };
14
+ const TABS = [{ id: 'agents', title: 'Agents' }];
15
+ const GROUP = { builtin: 'Built-in', global: 'Global', project: 'Project', variant: 'From the variant' };
16
+ function clean(value) {
17
+ return stripTerminalSequences(value).replace(/[\x00-\x08\x0b-\x1f\x7f]/g, ' ').replace(/[ \t]+/g, ' ').trim();
18
+ }
19
+ export class AgentsPage {
20
+ options;
21
+ presentation = 'fullscreen';
22
+ overview;
23
+ error;
24
+ busy = false;
25
+ disposed = false;
26
+ list;
27
+ constructor(options) {
28
+ this.options = options;
29
+ this.list = new FilterList({
30
+ theme: options.theme, search: 'always', noun: ['profile', 'profiles'], empty: 'No profiles',
31
+ onActivate: row => { if (row.data)
32
+ void this.activateRow(row.data); }
33
+ });
34
+ }
35
+ get focused() { return this.list.focused; }
36
+ set focused(value) { this.list.focused = value; }
37
+ activate() { void this.reload(); }
38
+ dispose() { this.disposed = true; }
39
+ tabs() { return TABS; }
40
+ activeTab() { return 'agents'; }
41
+ hint() { return 'Enter choose · type to search'; }
42
+ async reload() {
43
+ try {
44
+ this.overview = await this.options.call('terminal.agents.list');
45
+ this.error = undefined;
46
+ }
47
+ catch (error) {
48
+ this.error = error instanceof Error ? error.message : String(error);
49
+ }
50
+ if (this.disposed)
51
+ return;
52
+ this.list.setRows(this.rows());
53
+ this.options.changed();
54
+ }
55
+ rows() {
56
+ const overview = this.overview;
57
+ if (!overview)
58
+ return [];
59
+ const rows = [{ id: 'new', label: 'New profile', group: 'Actions', detail: 'from a template, then opens in your editor', tone: 'accent', data: { kind: 'new' } }];
60
+ for (const source of ['builtin', 'global', 'project', 'variant']) {
61
+ for (const profile of overview.profiles.filter(entry => entry.source === source)) {
62
+ rows.push({
63
+ id: `profile:${profile.name}`, label: profile.name, group: GROUP[source], detail: clean(profile.description), tone: 'muted',
64
+ searchText: `${profile.name} ${profile.description} ${source}`, data: { kind: 'profile', profile }
65
+ });
66
+ }
67
+ }
68
+ for (const problem of overview.problems) {
69
+ rows.push({
70
+ id: `problem:${problem.source}:${problem.name}`, label: problem.name, group: 'Cannot be read', detail: clean(problem.error), tone: 'error',
71
+ searchText: `${problem.name} ${problem.error}`, data: { kind: 'problem', problem }
72
+ });
73
+ }
74
+ return rows;
75
+ }
76
+ async attempt(work) {
77
+ if (this.busy)
78
+ return;
79
+ this.busy = true;
80
+ try {
81
+ const message = await work();
82
+ if (message)
83
+ this.options.ui.notify(message, 'info');
84
+ }
85
+ catch (error) {
86
+ this.options.ui.notify(error instanceof Error ? error.message : String(error), 'error');
87
+ }
88
+ finally {
89
+ this.busy = false;
90
+ }
91
+ await this.reload();
92
+ }
93
+ async open(path) {
94
+ if (!this.options.edit)
95
+ return `Edit the profile at ${path}.`;
96
+ await this.options.edit(path);
97
+ }
98
+ async create(from) {
99
+ const ui = this.options.ui;
100
+ const name = (await ui.input(from ? `Name for the copy of ${from}` : 'Profile name', 'lowercase-with-dashes'))?.trim();
101
+ if (!name)
102
+ return;
103
+ const scopes = this.overview?.dirs.project ? [AGENT_SCOPES.project, AGENT_SCOPES.global] : [AGENT_SCOPES.global];
104
+ const choice = await ui.select(`Where should ${name} live?`, scopes);
105
+ if (!choice)
106
+ return;
107
+ const scope = choice === AGENT_SCOPES.project ? 'project' : 'global';
108
+ const made = await this.options.call('terminal.agents.create', { name, scope, ...(from ? { from } : {}) });
109
+ return (await this.open(made.path)) ?? `Saved ${made.name}.`;
110
+ }
111
+ async activateRow(row) {
112
+ if (row.kind === 'new')
113
+ return this.attempt(() => this.create(null));
114
+ if (row.kind === 'problem') {
115
+ const { problem } = row;
116
+ const scope = problem.source === 'global' || problem.source === 'project' ? problem.source : null;
117
+ const choice = await this.options.ui.select(`${problem.name} · ${problem.error}`, [AGENT_ACTIONS.edit, ...(scope ? [AGENT_ACTIONS.remove] : [])]);
118
+ if (choice === AGENT_ACTIONS.edit)
119
+ return this.attempt(() => this.open(problem.path));
120
+ if (choice === AGENT_ACTIONS.remove && scope)
121
+ return this.remove(problem.name, scope);
122
+ return;
123
+ }
124
+ const { profile } = row;
125
+ const own = profile.source === 'global' || profile.source === 'project' ? profile.source : null;
126
+ const choice = await this.options.ui.select(`${profile.name} · ${GROUP[profile.source]}`, [...(own ? [AGENT_ACTIONS.edit] : []), AGENT_ACTIONS.copy, ...(own ? [AGENT_ACTIONS.remove] : [])]);
127
+ if (choice === AGENT_ACTIONS.edit && profile.path)
128
+ return this.attempt(() => this.open(profile.path));
129
+ if (choice === AGENT_ACTIONS.copy)
130
+ return this.attempt(() => this.create(profile.name));
131
+ if (choice === AGENT_ACTIONS.remove && own)
132
+ return this.remove(profile.name, own);
133
+ }
134
+ async remove(name, scope) {
135
+ if (!await this.options.ui.confirm(`Delete ${name}?`, `This removes your ${scope} profile file. A built-in profile of the same name comes back.`))
136
+ return;
137
+ return this.attempt(async () => {
138
+ await this.options.call('terminal.agents.remove', { name, scope });
139
+ return `Deleted ${name}.`;
140
+ });
141
+ }
142
+ handleMouse(event) {
143
+ return this.list.handleMouse(event);
144
+ }
145
+ handleInput(data) {
146
+ if (this.list.handleInput(data)) {
147
+ this.options.changed();
148
+ return true;
149
+ }
150
+ return false;
151
+ }
152
+ render(width, rows) {
153
+ const theme = this.options.theme();
154
+ if (width < 1 || rows < 1)
155
+ return [];
156
+ if (this.error)
157
+ return [theme.fg('error', `Could not read agent profiles: ${this.error}`)];
158
+ if (!this.overview)
159
+ return [theme.fg('dim', 'Reading agent profiles…')];
160
+ const header = headerRow('Agents', `${this.overview.profiles.length} profiles`, width, theme);
161
+ const bodyRows = Math.max(0, rows - 7);
162
+ const listRows = Math.max(1, Math.min(this.list.height, Math.floor(bodyRows * 0.55)));
163
+ const output = [header, '', ...this.list.render(width, listRows), ''];
164
+ const selected = this.list.selected?.data;
165
+ const textWidth = Math.max(1, width - 4);
166
+ output.push(neutralRule(width, theme));
167
+ if (selected?.kind === 'profile') {
168
+ const profile = selected.profile;
169
+ const field = (label, value) => padded(`${theme.fg('muted', `${label}: `)}${theme.fg('text', clean(value))}`, width);
170
+ output.push(padded(theme.fg('accent', `${profile.name} · ${profile.color}`), width));
171
+ output.push(...wrapTextWithAnsi(theme.fg('text', clean(profile.description)), textWidth));
172
+ output.push(field('Tools', profile.tools?.join(', ') ?? 'all of the parent\'s'));
173
+ output.push(field('Plugins', profile.plugins?.join(', ') ?? 'all of the parent\'s'));
174
+ output.push(field('Variant', formatVariant(profile.variant) ?? 'the parent\'s'));
175
+ output.push(field('Model', [profile.model ?? 'the parent\'s', profile.effort ? `effort ${profile.effort}` : null].filter(Boolean).join(' · ')));
176
+ output.push(field('File', profile.path ?? 'built in'));
177
+ output.push(padded(theme.fg('accent', 'Instructions'), width));
178
+ output.push(...profile.instructions.split('\n').flatMap(line => wrapTextWithAnsi(theme.fg('muted', clean(line)), textWidth)));
179
+ }
180
+ else if (selected?.kind === 'problem') {
181
+ output.push(padded(theme.fg('error', selected.problem.error), width));
182
+ output.push(padded(theme.fg('muted', selected.problem.path), width));
183
+ }
184
+ else {
185
+ const dirs = this.overview.dirs;
186
+ output.push(padded(theme.fg('muted', `Your profiles live in ${dirs.global}${dirs.project ? ` and ${dirs.project}` : ''}. A project profile wins over a global one, and both over a built-in one.`), width));
187
+ }
188
+ return output.slice(0, rows).map(line => truncateToWidth(line, width, ''));
189
+ }
190
+ }