@rse/ase 1.0.7 → 2.0.1

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 (279) hide show
  1. package/dst/ase-agent-definition-api.d.ts +184 -0
  2. package/dst/ase-agent-definition-api.js +6 -0
  3. package/dst/ase-agent-definition-builtin/condenser.js +42 -0
  4. package/dst/ase-agent-definition-builtin/editor.js +45 -0
  5. package/dst/ase-agent-definition-builtin/estimator.js +98 -0
  6. package/dst/ase-agent-definition-builtin/implementer.js +121 -0
  7. package/dst/ase-agent-definition-builtin/integrator.js +56 -0
  8. package/dst/ase-agent-definition-builtin/qualifier.js +61 -0
  9. package/dst/ase-agent-definition-builtin/rebooter.js +41 -0
  10. package/dst/ase-agent-definition-builtin/researcher.js +77 -0
  11. package/dst/ase-agent-definition-core.js +827 -0
  12. package/dst/ase-agent-fleet.js +772 -0
  13. package/dst/ase-agent-process.js +232 -0
  14. package/dst/ase-agent-relay-client.js +509 -0
  15. package/dst/ase-agent-relay-core.js +213 -0
  16. package/dst/ase-agent-relay-server-bind.js +242 -0
  17. package/dst/ase-agent-relay-server-cli.js +50 -0
  18. package/dst/ase-agent-runtime-claim.js +189 -0
  19. package/dst/ase-agent-runtime-engine.js +1100 -0
  20. package/dst/ase-agent-runtime-exec.js +275 -0
  21. package/dst/ase-agent-runtime-phase.js +581 -0
  22. package/dst/ase-agent-runtime-request.js +272 -0
  23. package/dst/ase-agent-runtime-tool.js +207 -0
  24. package/dst/ase-agent-runtime.js +1316 -0
  25. package/dst/ase-agent.js +65 -0
  26. package/dst/ase-artifact.js +6 -5
  27. package/dst/ase-config-core.js +465 -0
  28. package/dst/ase-config-schema.js +357 -0
  29. package/dst/ase-config-scope.js +172 -0
  30. package/dst/ase-config.js +25 -585
  31. package/dst/ase-hook.js +228 -111
  32. package/dst/ase-lib-daemon-cli.js +130 -0
  33. package/dst/ase-lib-hash.js +20 -0
  34. package/dst/{ase-log.js → ase-lib-log.js} +40 -7
  35. package/dst/ase-lib-server-bind.js +115 -0
  36. package/dst/ase-lib-server-cli.js +474 -0
  37. package/dst/{ase-table.js → ase-lib-table.js} +1 -1
  38. package/dst/{ase-version.js → ase-lib-version.js} +8 -1
  39. package/dst/ase-man.js +96 -0
  40. package/dst/ase-mcp.js +166 -13
  41. package/dst/{ase-getopt.js → ase-service-getopt.js} +82 -80
  42. package/dst/{ase-markdown.js → ase-service-markdown.js} +18 -11
  43. package/dst/ase-service-otel.js +1246 -0
  44. package/dst/{ase-skills.js → ase-service-skills.js} +29 -64
  45. package/dst/ase-service-state.js +101 -0
  46. package/dst/ase-service.js +269 -81
  47. package/dst/ase-setup-common.js +141 -0
  48. package/dst/ase-setup-mcp.js +359 -0
  49. package/dst/ase-setup-settings.js +837 -0
  50. package/dst/ase-setup.js +113 -869
  51. package/dst/ase-spec.js +31 -19
  52. package/dst/ase-statusline.js +108 -43
  53. package/dst/ase-task-board-core.js +739 -0
  54. package/dst/ase-task-board-filter.js +127 -0
  55. package/dst/ase-task-board-flow.js +237 -0
  56. package/dst/ase-task-board-graph.js +497 -0
  57. package/dst/ase-task-board-stats.js +263 -0
  58. package/dst/ase-task-board-tui-control.js +775 -0
  59. package/dst/ase-task-board-tui-model.js +1008 -0
  60. package/dst/ase-task-board-tui-popup.js +626 -0
  61. package/dst/ase-task-board-tui-style.js +106 -0
  62. package/dst/ase-task-board-tui-view.js +847 -0
  63. package/dst/ase-task-board-tui.js +189 -0
  64. package/dst/ase-task-board-web-client.css +1 -0
  65. package/dst/ase-task-board-web-client.html +20 -0
  66. package/dst/ase-task-board-web-client.js +81 -0
  67. package/dst/ase-task-board-web-server.js +823 -0
  68. package/dst/ase-task-board.js +134 -0
  69. package/dst/ase-task-format.js +757 -0
  70. package/dst/ase-task-request.js +208 -0
  71. package/dst/ase-task-store-client.js +495 -0
  72. package/dst/ase-task-store-core.js +804 -0
  73. package/dst/ase-task-store-issues.js +237 -0
  74. package/dst/ase-task-store-plugin-api.js +6 -0
  75. package/dst/ase-task-store-plugin-ase.js +484 -0
  76. package/dst/ase-task-store-plugin-delegate.js +137 -0
  77. package/dst/ase-task-store-plugin-gitea.js +666 -0
  78. package/dst/ase-task-store-plugin-github.js +696 -0
  79. package/dst/ase-task-store-plugin-gitlab.js +668 -0
  80. package/dst/ase-task-store-server-bind.js +538 -0
  81. package/dst/ase-task-store-server-cli.js +154 -0
  82. package/dst/ase-task.js +1335 -357
  83. package/dst/{ase-compat.js → ase-util-compat.js} +2 -2
  84. package/dst/{ase-diagram.js → ase-util-diagram.js} +107 -32
  85. package/dst/{ase-meta.js → ase-util-meta.js} +2 -2
  86. package/dst/{ase-metric.js → ase-util-metric.js} +2 -2
  87. package/dst/{ase-mint.js → ase-util-mint.js} +19 -12
  88. package/dst/{ase-worktree.js → ase-util-worktree.js} +30 -19
  89. package/dst/ase-util.js +20 -0
  90. package/dst/ase.js +108 -32
  91. package/dst/man/ase-agent-adhoc.1 +515 -0
  92. package/dst/man/ase-agent-condenser.1 +193 -0
  93. package/dst/man/ase-agent-editor.1 +212 -0
  94. package/dst/man/ase-agent-estimator.1 +214 -0
  95. package/dst/man/ase-agent-fleet.1 +185 -0
  96. package/dst/man/ase-agent-implementer.1 +233 -0
  97. package/dst/man/ase-agent-integrator.1 +213 -0
  98. package/dst/man/ase-agent-qualifier.1 +214 -0
  99. package/dst/man/ase-agent-rebooter.1 +193 -0
  100. package/dst/man/ase-agent-relay.1 +128 -0
  101. package/dst/man/ase-agent-researcher.1 +217 -0
  102. package/dst/man/ase-agent.1 +264 -0
  103. package/dst/man/ase-artifact-list.1 +38 -0
  104. package/dst/man/ase-artifact-name.1 +39 -0
  105. package/dst/man/ase-artifact.1 +56 -0
  106. package/dst/man/ase-config-delete.1 +45 -0
  107. package/dst/man/ase-config-edit.1 +48 -0
  108. package/dst/man/ase-config-get.1 +46 -0
  109. package/dst/man/ase-config-init.1 +60 -0
  110. package/dst/man/ase-config-list.1 +42 -0
  111. package/dst/man/ase-config-set.1 +52 -0
  112. package/dst/man/ase-config.1 +231 -0
  113. package/dst/man/ase-hook-permission-request.1 +36 -0
  114. package/dst/man/ase-hook-pre-tool-use.1 +59 -0
  115. package/dst/man/ase-hook-session-end.1 +47 -0
  116. package/dst/man/ase-hook-session-persona.1 +37 -0
  117. package/dst/man/ase-hook-session-start.1 +81 -0
  118. package/dst/man/ase-hook-stop.1 +34 -0
  119. package/dst/man/ase-hook-user-prompt-submit.1 +34 -0
  120. package/dst/man/ase-hook.1 +51 -0
  121. package/dst/man/ase-man.1 +67 -0
  122. package/dst/man/ase-mcp.1 +61 -0
  123. package/dst/man/ase-service-log.1 +43 -0
  124. package/dst/man/ase-service-send.1 +47 -0
  125. package/dst/man/ase-service-start.1 +54 -0
  126. package/dst/man/ase-service-status.1 +46 -0
  127. package/dst/man/ase-service-stop.1 +37 -0
  128. package/dst/man/ase-service.1 +85 -0
  129. package/dst/man/ase-setup-disable.1 +44 -0
  130. package/dst/man/ase-setup-enable.1 +45 -0
  131. package/dst/man/ase-setup-install.1 +73 -0
  132. package/dst/man/ase-setup-mcp.1 +124 -0
  133. package/dst/man/ase-setup-status.1 +60 -0
  134. package/dst/man/ase-setup-statusline.1 +92 -0
  135. package/dst/man/ase-setup-telemetry.1 +92 -0
  136. package/dst/man/ase-setup-uninstall.1 +59 -0
  137. package/dst/man/ase-setup-update.1 +64 -0
  138. package/dst/man/ase-setup.1 +93 -0
  139. package/dst/man/ase-spec-export.1 +62 -0
  140. package/dst/man/ase-spec-lint.1 +40 -0
  141. package/dst/man/ase-spec-preview.1 +40 -0
  142. package/dst/man/ase-spec.1 +46 -0
  143. package/dst/man/ase-statusline.1 +189 -0
  144. package/dst/man/ase-task-assign.1 +71 -0
  145. package/dst/man/ase-task-board.1 +384 -0
  146. package/dst/man/ase-task-delete.1 +35 -0
  147. package/dst/man/ase-task-edit.1 +46 -0
  148. package/dst/man/ase-task-idscheme.1 +65 -0
  149. package/dst/man/ase-task-lifecycle.1 +57 -0
  150. package/dst/man/ase-task-list.1 +47 -0
  151. package/dst/man/ase-task-load.1 +36 -0
  152. package/dst/man/ase-task-newid.1 +61 -0
  153. package/dst/man/ase-task-purge.1 +37 -0
  154. package/dst/man/ase-task-rename.1 +44 -0
  155. package/dst/man/ase-task-request.1 +61 -0
  156. package/dst/man/ase-task-save.1 +48 -0
  157. package/dst/man/ase-task-status.1 +55 -0
  158. package/dst/man/ase-task-store.1 +194 -0
  159. package/dst/man/ase-task-view.1 +40 -0
  160. package/dst/man/ase-task.1 +160 -0
  161. package/dst/man/ase-util-compat.1 +25 -0
  162. package/dst/man/ase-util-diagram.1 +76 -0
  163. package/dst/man/ase-util-meta.1 +45 -0
  164. package/dst/man/ase-util-metric.1 +54 -0
  165. package/dst/man/ase-util-mint.1 +59 -0
  166. package/dst/man/ase-util-worktree.1 +72 -0
  167. package/dst/man/ase-util.1 +50 -0
  168. package/dst/man/ase.1 +124 -0
  169. package/package.json +81 -21
  170. package/plugin/.claude-plugin/plugin.json +1 -1
  171. package/plugin/.codex-plugin/plugin.json +1 -1
  172. package/plugin/.github/plugin/plugin.json +1 -1
  173. package/plugin/agents/ase-meta-diagram.md +12 -8
  174. package/plugin/agents/ase-repo-review.md +322 -0
  175. package/plugin/hooks/hooks-codex.json +5 -0
  176. package/plugin/hooks/hooks-copilot.json +5 -0
  177. package/plugin/hooks/hooks.json +8 -0
  178. package/plugin/meta/ase-common-approach.md +117 -0
  179. package/plugin/meta/ase-common-changeset.md +256 -0
  180. package/plugin/meta/ase-common-code.md +10 -173
  181. package/plugin/meta/ase-common-grill.md +317 -0
  182. package/plugin/meta/ase-common-resolve.md +305 -0
  183. package/plugin/meta/ase-common-task.md +83 -4
  184. package/plugin/meta/ase-constitution.md +0 -2
  185. package/plugin/meta/ase-format-specbook.md +208 -25
  186. package/plugin/meta/ase-format-specbook.yaml +242 -851
  187. package/plugin/meta/ase-format-task.md +669 -125
  188. package/plugin/meta/ase-persona.md +3 -4
  189. package/plugin/meta/ase-skill.md +9 -0
  190. package/plugin/mods/ase-mods.ts +409 -0
  191. package/plugin/mods/claude-code.d.ts +13417 -0
  192. package/plugin/skills/ase-arch-analyze/SKILL.md +1 -1
  193. package/plugin/skills/ase-arch-analyze/help.md +2 -2
  194. package/plugin/skills/ase-code-analyze/SKILL.md +59 -8
  195. package/plugin/skills/ase-code-analyze/help.md +29 -4
  196. package/plugin/skills/ase-code-edit/SKILL.md +107 -181
  197. package/plugin/skills/ase-code-edit/help.md +118 -31
  198. package/plugin/skills/ase-code-lint/SKILL.md +64 -13
  199. package/plugin/skills/ase-code-lint/help.md +32 -2
  200. package/plugin/skills/ase-docs-proofread/SKILL.md +4 -4
  201. package/plugin/skills/ase-docs-proofread/help.md +1 -1
  202. package/plugin/skills/ase-docs-refine/SKILL.md +4 -4
  203. package/plugin/skills/ase-docs-shorten/SKILL.md +4 -4
  204. package/plugin/skills/ase-help-intent/data.md +2164 -1087
  205. package/plugin/skills/ase-help-intent/help.md +1 -1
  206. package/plugin/skills/ase-help-skill/catalog.md +12 -9
  207. package/plugin/skills/ase-meta-brainstorm/SKILL.md +1 -1
  208. package/plugin/skills/ase-meta-config/SKILL.md +3 -4
  209. package/plugin/skills/ase-meta-config/help.md +31 -10
  210. package/plugin/skills/ase-meta-mint/SKILL.md +5 -5
  211. package/plugin/skills/ase-meta-mint/help.md +10 -5
  212. package/plugin/skills/ase-meta-why/help.md +1 -1
  213. package/plugin/skills/ase-meta-workflow/SKILL.md +7 -7
  214. package/plugin/skills/ase-meta-workflow/help.md +4 -4
  215. package/plugin/skills/ase-meta-workflow/sample.md +3 -3
  216. package/plugin/skills/ase-meta-workflow/workflow.txt +22 -18
  217. package/plugin/skills/{ase-meta-changelog → ase-repo-changelog}/SKILL.md +4 -3
  218. package/plugin/skills/{ase-meta-changelog → ase-repo-changelog}/help.md +5 -5
  219. package/plugin/skills/{ase-meta-commit → ase-repo-commit}/SKILL.md +6 -6
  220. package/plugin/skills/{ase-meta-commit → ase-repo-commit}/help.md +5 -5
  221. package/plugin/skills/{ase-meta-diff → ase-repo-diff}/SKILL.md +6 -6
  222. package/plugin/skills/{ase-meta-diff → ase-repo-diff}/help.md +8 -8
  223. package/plugin/skills/{ase-code-dissect → ase-repo-dissect}/SKILL.md +19 -19
  224. package/plugin/skills/{ase-code-dissect → ase-repo-dissect}/help.md +10 -12
  225. package/plugin/skills/ase-repo-merge/SKILL.md +346 -0
  226. package/plugin/skills/ase-repo-merge/help.md +107 -0
  227. package/plugin/skills/ase-repo-resolve/SKILL.md +158 -0
  228. package/plugin/skills/ase-repo-resolve/help.md +102 -0
  229. package/plugin/skills/ase-repo-review/SKILL.md +406 -0
  230. package/plugin/skills/ase-repo-review/help.md +181 -0
  231. package/plugin/skills/ase-spec-edit/SKILL.md +117 -183
  232. package/plugin/skills/ase-spec-edit/help.md +107 -26
  233. package/plugin/skills/ase-sync-reconcile/help.md +1 -1
  234. package/plugin/skills/ase-task-assign/SKILL.md +111 -0
  235. package/plugin/skills/ase-task-assign/help.md +119 -0
  236. package/plugin/skills/ase-task-condense/SKILL.md +21 -21
  237. package/plugin/skills/ase-task-condense/help.md +12 -10
  238. package/plugin/skills/ase-task-delete/SKILL.md +1 -1
  239. package/plugin/skills/ase-task-dissect/SKILL.md +71 -48
  240. package/plugin/skills/ase-task-dissect/help.md +10 -9
  241. package/plugin/skills/ase-task-edit/SKILL.md +379 -94
  242. package/plugin/skills/ase-task-edit/help.md +95 -22
  243. package/plugin/skills/ase-task-estimate/SKILL.md +492 -0
  244. package/plugin/skills/ase-task-estimate/help.md +158 -0
  245. package/plugin/skills/ase-task-grill/SKILL.md +246 -138
  246. package/plugin/skills/ase-task-grill/help.md +129 -26
  247. package/plugin/skills/ase-task-id/SKILL.md +8 -1
  248. package/plugin/skills/ase-task-id/help.md +6 -3
  249. package/plugin/skills/ase-task-implement/SKILL.md +535 -145
  250. package/plugin/skills/ase-task-implement/help.md +138 -47
  251. package/plugin/skills/ase-task-integrate/SKILL.md +549 -0
  252. package/plugin/skills/ase-task-integrate/help.md +187 -0
  253. package/plugin/skills/ase-task-list/SKILL.md +55 -23
  254. package/plugin/skills/ase-task-list/help.md +45 -21
  255. package/plugin/skills/ase-task-reboot/SKILL.md +84 -43
  256. package/plugin/skills/ase-task-reboot/help.md +28 -8
  257. package/plugin/skills/ase-task-rename/SKILL.md +10 -2
  258. package/plugin/skills/ase-task-research/SKILL.md +386 -0
  259. package/plugin/skills/ase-task-research/help.md +102 -0
  260. package/plugin/skills/ase-task-status/SKILL.md +137 -0
  261. package/plugin/skills/ase-task-status/help.md +80 -0
  262. package/plugin/skills/ase-task-view/SKILL.md +49 -24
  263. package/plugin/skills/ase-task-view/help.md +19 -9
  264. package/plugin/agents/ase-meta-review.md +0 -187
  265. package/plugin/skills/ase-code-craft/SKILL.md +0 -264
  266. package/plugin/skills/ase-code-craft/help.md +0 -122
  267. package/plugin/skills/ase-code-refactor/SKILL.md +0 -232
  268. package/plugin/skills/ase-code-refactor/help.md +0 -109
  269. package/plugin/skills/ase-code-resolve/SKILL.md +0 -289
  270. package/plugin/skills/ase-code-resolve/help.md +0 -116
  271. package/plugin/skills/ase-meta-review/SKILL.md +0 -210
  272. package/plugin/skills/ase-meta-review/help.md +0 -96
  273. package/plugin/skills/ase-task-preflight/SKILL.md +0 -166
  274. package/plugin/skills/ase-task-preflight/help.md +0 -82
  275. /package/dst/{ase-ignore.js → ase-lib-ignore.js} +0 -0
  276. /package/dst/{ase-stdio.js → ase-lib-stdio.js} +0 -0
  277. /package/dst/{ase-kv.js → ase-service-kv.js} +0 -0
  278. /package/dst/{ase-sleep.js → ase-service-sleep.js} +0 -0
  279. /package/dst/{ase-timestamp.js → ase-service-timestamp.js} +0 -0
@@ -0,0 +1,189 @@
1
+ /*
2
+ ** Agentic Software Engineering (ASE)
3
+ ** Copyright (c) 2025-2026 Dr. Ralf S. Engelschall <rse@engelschall.com>
4
+ ** Licensed under Apache 2.0 <https://spdx.org/licenses/Apache-2.0>
5
+ */
6
+ import React from "react";
7
+ import { render, Box, Text, useInput, useStdin } from "ink";
8
+ import { Task } from "./ase-task.js";
9
+ import { buildBoard, toneOf, viewLabels } from "./ase-task-board-core.js";
10
+ import pkg from "../package.json" with { type: "json" };
11
+ import { useBoardState, mouseReporting, requestChoices, assignChoices, assignUnknown } from "./ase-task-board-tui-model.js";
12
+ import { handleMouse, handleKey } from "./ase-task-board-tui-control.js";
13
+ import { h, renderLanes, renderGraph, renderAgents, renderFlow, renderStats } from "./ase-task-board-tui-view.js";
14
+ import { palette, loadPalette, cx } from "./ase-task-board-tui-style.js";
15
+ import { sanitize, renderDialog, renderConfirm, renderTransfer, renderAssign, renderPrompt, renderBusy } from "./ase-task-board-tui-popup.js";
16
+ /* render the whole screen */
17
+ const renderScreen = (ctx, project) => {
18
+ const { columns, rows, all, board, surface, view, filter, typing, dialog, notice, carry, confirm, transfer, assign, prompt, headRef, tabLabels, dialogSel, dialogLines, transferCard, transferList, dim, store, busy, busyTick, agents } = ctx;
19
+ /* refuse to draw into a too small window */
20
+ if (columns < 40 || rows < 16)
21
+ return h(Box, { width: columns, height: rows, justifyContent: "center", alignItems: "center" }, h(Text, cx("signal"), `window too small (${columns}×${rows}) — needs at least 40×16`));
22
+ /* the status line in the last line of the screen, below the key hints, enclosed
23
+ on its left and right side by the downward lines of the rule above it,
24
+ showing the kind and connection state of the task store (a disconnected one in
25
+ signal color), the number of agents and the connection state of the agent relay (if
26
+ any), and the ASE version (dimmed) while there is nothing else to report */
27
+ const report = notice !== null ? sanitize(notice) :
28
+ carry !== null ? `moving task "${carry.id}" from ${carry.from}: select a bold lane, SPACE drops, ESC cancels` :
29
+ typing ? "filtering tasks by fuzzy matched keywords or effort:<size> (SPACE: and, COMMA: or): ⏎ keeps, ESC clears" : null;
30
+ const status = h(Box, { key: "status", paddingX: 1 }, h(Box, cx("fixed"), h(Text, cx("dim", dim && "dimmed"), "│")), h(Box, cx("grow", "bar"), h(Text, cx("hint", report !== null && "signal", dim && "dimmed"), report ?? ["⧉ ASE: ", h(Text, { key: "app", ...cx("bold") }, "Task Board"),
31
+ ...(store !== null ? [" · store: ", h(Text, { key: "store", ...cx("bold") }, `${store.kind} `),
32
+ h(Text, { key: "state", ...cx(!store.connected && "signal") }, store.connected ? "●" : "○")] : []),
33
+ ...(agents !== null ? [" · agents: ", h(Text, { key: "agents", ...cx("bold") }, `${agents.agents.length} `),
34
+ h(Text, { key: "relay", ...cx(!agents.connected && "signal") }, agents.connected ? "●" : "○")] : []),
35
+ " · version: ", h(Text, { key: "version", ...cx("bold") }, `ASE ${pkg.version}`)])), h(Box, cx("fixed"), h(Text, cx("dim", dim && "dimmed"), "│")));
36
+ /* the counted tasks (without the context tasks of a filtered graph), and
37
+ the (inversely rendered) filter field of the header: the tail of the
38
+ filter query (with the cursor while typing), padded to a fixed width,
39
+ followed by its clear button (shown with a filter query only) */
40
+ const tasks = [...board.cards.values()].filter((c) => !board.context.has(c.id));
41
+ const field = (sanitize(filter) + (typing ? "▏" : "")).slice(-14).padEnd(14);
42
+ /* the filter field is disabled (and dimmed) in the agents view and the agent graph, which are not filtered */
43
+ const off = view === "agents" || view === "flow";
44
+ /* the view value, centered within the fixed width of the longest view name */
45
+ const viewW = Math.max(...Object.values(viewLabels).map((v) => v.length));
46
+ const shown = (" ".repeat(Math.floor((viewW - viewLabels[view].length) / 2)) + viewLabels[view]).padEnd(viewW);
47
+ /* render the screen, with the dialog and popups on top */
48
+ return h(Box, { width: columns, height: rows, flexDirection: "column" },
49
+ /* the header, enclosed on its left and right side by the upward lines of
50
+ the rule below it, with the view value and the filter field (disabled in the
51
+ agents view and the agent graph, which are not filtered) in boxes of their own, so that they
52
+ are measurable for the mouse hit-testing; the parts
53
+ are centered as a single box, as centering them individually places them on
54
+ half columns, which rounds the text and box parts apart by one column */
55
+ h(Box, { paddingX: 1 }, h(Box, cx("fixed"), h(Text, cx("dim", dim && "dimmed"), "│")), h(Box, cx("grow", "bar"), h(Box, { flexShrink: 1, minWidth: 0 }, h(Text, cx("normal", "truncate", dim && "dimmed"), "⧉ ASE: ", h(Text, cx("bold"), "Task Board"), " · project: ", h(Text, cx("bold"), project), " · mode: ", h(Text, cx("bold"), board.mode), " · tasks: ", h(Text, cx("bold"), `${tasks.filter((c) => toneOf(board, c) !== "done").length}/${tasks.length}`), " · view: "), h(Box, { ref: headRef("view"), ...cx("fixed") }, h(Text, cx("dim", "badge", dim && "dimmed"), ` ${shown} `)), h(Text, cx("normal", "truncate", (dim || off) && "dimmed"), " · filter: "), h(Box, { ref: headRef("filter"), ...cx("fixed") }, h(Text, cx(typing ? "signal" : "dim", off ? "inverse" : "badge", dim && "dimmed"), field)), h(Box, { ref: headRef("clear"), ...cx("fixed") }, h(Text, cx(typing ? "signal" : "dim", off ? "inverse" : "badge", dim && "dimmed"), filter !== "" && !off ? "✕ " : " ")))), h(Box, cx("fixed"), h(Text, cx("dim", dim && "dimmed"), "│"))),
56
+ /* the horizontal rule below the header, turning upward with rounded corners at both ends */
57
+ h(Text, cx("dim", dim && "dimmed"), " ╰" + "─".repeat(Math.max(0, columns - 4)) + "╯ "), h(Box, { paddingX: 1 }, h(Text, cx("signal", "truncate", dim && "dimmed"), board.warnings.length > 0 ? `⚠ ${board.warnings.map(sanitize).join(" · ")}` : " ")), ...(view === "lanes" ? renderLanes(ctx) : view === "graph" ? renderGraph(ctx) : view === "stats" ? renderStats(ctx) :
58
+ view === "flow" ? renderFlow(ctx) : renderAgents(ctx))
59
+ .filter((el) => surface.keys || !String(el.key).startsWith("keys")),
60
+ /* the horizontal rule between the key hints and the status line, turning downward with rounded corners at both ends */
61
+ h(Text, cx("dim", dim && "dimmed"), " ╭" + "─".repeat(Math.max(0, columns - 4)) + "╮ "), status, dialog !== null ? renderDialog({
62
+ card: all.cards.get(dialog.id),
63
+ group: all.groups.find((g) => g.lanes.some((l) => l.status === all.cards.get(dialog.id)?.status))?.title,
64
+ id: dialog.id,
65
+ pred: all.pred.get(dialog.id) ?? [],
66
+ succ: all.succ.get(dialog.id) ?? [],
67
+ tint: (ref) => {
68
+ const c = all.cards.get(ref);
69
+ const tone = c !== undefined ? toneOf(all, c) : "idle";
70
+ return tone === "active" ? palette.accent : tone === "done" ? palette.dim : palette.normal;
71
+ },
72
+ tabs: tabLabels,
73
+ tab: dialogSel,
74
+ first: dialog.first,
75
+ scroll: dialog.scrolls[dialogSel] ?? 0,
76
+ lines: dialogLines,
77
+ columns,
78
+ rows,
79
+ notice
80
+ }) : null, confirm !== null ? renderConfirm(confirm.id, confirm.yes, columns, rows) : null, transfer !== null && transferCard !== undefined ?
81
+ renderTransfer(transfer.id, transferCard.status, transferList, transfer.at, columns, rows) : null, assign !== null ? renderAssign(assign, assignChoices(assign), assignUnknown(assign), columns, rows) : null, prompt !== null ? renderPrompt(prompt, columns, rows, prompt.request !== null ? requestChoices(prompt.request) : []) : null, busy !== null ? renderBusy(busy.label, busy.since, busyTick, columns, rows) : null);
82
+ };
83
+ /* pass each pressed SGR mouse report ("ESC[<b;x;y" plus "M"/"m" for press/release, 1-based)
84
+ with its 0-based column/row, read directly from stdin, as Ink 8 drops them before
85
+ "useInput"; the "data" events accompany the reads of Ink, so no input is taken away
86
+ (and none is read while Ink pauses its input) */
87
+ export const useMouseReports = (onPress) => {
88
+ const { stdin } = useStdin();
89
+ const handler = React.useRef(onPress);
90
+ handler.current = onPress;
91
+ React.useEffect(() => {
92
+ let rest = "";
93
+ const onData = (chunk) => {
94
+ const parts = (rest + String(chunk)).split("\x1b");
95
+ const last = parts.length - 1;
96
+ rest = "";
97
+ parts.forEach((part, i) => {
98
+ const report = /^\[<(\d+);(\d+);(\d+)([Mm])/.exec(part);
99
+ if (report !== null && report[4] === "M")
100
+ handler.current(Number(report[1]), Number(report[2]) - 1, Number(report[3]) - 1);
101
+ else if (report === null && i === last && i > 0 && /^(\[(<[\d;]*)?)?$/.test(part))
102
+ rest = "\x1b" + part;
103
+ });
104
+ };
105
+ /* listen only alongside the "readable" listener of Ink, which Ink detaches
106
+ while "suspendTerminal" hands the terminal to a child process (like the
107
+ $EDITOR), as a sole "data" listener would switch stdin into flowing mode
108
+ and hence read the input of the child process away; a removed listener is
109
+ reported after its removal, an added listener before its addition */
110
+ let tapped = false;
111
+ const sync = () => {
112
+ const reading = stdin.listenerCount("readable") > 0;
113
+ if (reading && !tapped)
114
+ stdin.on("data", onData);
115
+ else if (!reading && tapped)
116
+ stdin.off("data", onData);
117
+ tapped = reading;
118
+ };
119
+ const onAdded = (event) => {
120
+ if (event === "readable")
121
+ queueMicrotask(sync);
122
+ };
123
+ const onRemoved = (event) => {
124
+ if (event === "readable")
125
+ sync();
126
+ };
127
+ stdin.on("newListener", onAdded);
128
+ stdin.on("removeListener", onRemoved);
129
+ sync();
130
+ return () => {
131
+ stdin.off("newListener", onAdded);
132
+ stdin.off("removeListener", onRemoved);
133
+ if (tapped)
134
+ stdin.off("data", onData);
135
+ tapped = false;
136
+ };
137
+ }, [stdin]);
138
+ };
139
+ /* the root component of the terminal board */
140
+ const App = ({ log, initial, project, relay }) => {
141
+ const ctx = useBoardState(log, initial, relay);
142
+ /* handle the mouse, but ignore it while the modal busy popup is shown */
143
+ useMouseReports((button, col, row) => {
144
+ if (ctx.busy === null)
145
+ handleMouse(ctx, button, col, row);
146
+ });
147
+ /* handle the keyboard */
148
+ useInput((input, key) => {
149
+ /* ignore key releases (reported under the kitty keyboard protocol),
150
+ as otherwise every toggling key would toggle twice */
151
+ if (key.eventType === "release")
152
+ return;
153
+ /* ignore all input while the modal busy popup is shown */
154
+ if (ctx.busy !== null)
155
+ return;
156
+ /* never take a mouse report for keys (handled by "useMouseReports" above) */
157
+ if (/^\[<\d+;\d+;\d+[Mm]$/.test(input))
158
+ return;
159
+ handleKey(ctx, input, key);
160
+ });
161
+ return renderScreen(ctx, project);
162
+ };
163
+ /* run the terminal board until the user quits */
164
+ export const runTUI = async (log, relay) => {
165
+ const initial = await buildBoard(log);
166
+ const project = Task.projectIdOf(Task.projectRoot());
167
+ loadPalette(log);
168
+ /* defer stderr log output while Ink draws, as it bypasses patchConsole */
169
+ log.defer(true);
170
+ /* ensure the terminal never stays in mouse reporting mode,
171
+ even on a premature process exit (e.g. an uncaught exception) */
172
+ const reset = () => {
173
+ mouseReporting(false);
174
+ };
175
+ process.once("exit", reset);
176
+ try {
177
+ /* enable the kitty keyboard protocol (if the terminal supports it),
178
+ as only under it the Shift modifier of RETURN is reported */
179
+ const app = render(h(App, { log, initial, project, relay }), {
180
+ alternateScreen: true, exitOnCtrlC: true, patchConsole: true, kittyKeyboard: { mode: "auto" }
181
+ });
182
+ await app.waitUntilExit();
183
+ }
184
+ finally {
185
+ process.off("exit", reset);
186
+ mouseReporting(false);
187
+ log.defer(false);
188
+ }
189
+ };