@rse/ase 1.0.6 → 2.0.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.
- package/dst/ase-agent-definition-api.d.ts +184 -0
- package/dst/ase-agent-definition-api.js +6 -0
- package/dst/ase-agent-definition-builtin/condenser.js +42 -0
- package/dst/ase-agent-definition-builtin/editor.js +45 -0
- package/dst/ase-agent-definition-builtin/estimator.js +98 -0
- package/dst/ase-agent-definition-builtin/implementer.js +121 -0
- package/dst/ase-agent-definition-builtin/integrator.js +56 -0
- package/dst/ase-agent-definition-builtin/qualifier.js +61 -0
- package/dst/ase-agent-definition-builtin/rebooter.js +41 -0
- package/dst/ase-agent-definition-builtin/researcher.js +77 -0
- package/dst/ase-agent-definition-core.js +827 -0
- package/dst/ase-agent-fleet.js +772 -0
- package/dst/ase-agent-process.js +232 -0
- package/dst/ase-agent-relay-client.js +509 -0
- package/dst/ase-agent-relay-core.js +213 -0
- package/dst/ase-agent-relay-server-bind.js +242 -0
- package/dst/ase-agent-relay-server-cli.js +50 -0
- package/dst/ase-agent-runtime-claim.js +189 -0
- package/dst/ase-agent-runtime-engine.js +1100 -0
- package/dst/ase-agent-runtime-exec.js +275 -0
- package/dst/ase-agent-runtime-phase.js +581 -0
- package/dst/ase-agent-runtime-request.js +272 -0
- package/dst/ase-agent-runtime-tool.js +207 -0
- package/dst/ase-agent-runtime.js +1316 -0
- package/dst/ase-agent.js +65 -0
- package/dst/ase-artifact.js +6 -5
- package/dst/ase-config-core.js +465 -0
- package/dst/ase-config-schema.js +351 -0
- package/dst/ase-config-scope.js +172 -0
- package/dst/ase-config.js +25 -585
- package/dst/ase-hook.js +230 -116
- package/dst/ase-lib-daemon-cli.js +130 -0
- package/dst/ase-lib-hash.js +20 -0
- package/dst/{ase-log.js → ase-lib-log.js} +40 -7
- package/dst/ase-lib-server-bind.js +115 -0
- package/dst/ase-lib-server-cli.js +474 -0
- package/dst/{ase-table.js → ase-lib-table.js} +1 -1
- package/dst/{ase-version.js → ase-lib-version.js} +3 -0
- package/dst/ase-man.js +96 -0
- package/dst/ase-mcp.js +166 -13
- package/dst/{ase-getopt.js → ase-service-getopt.js} +82 -80
- package/dst/{ase-markdown.js → ase-service-markdown.js} +18 -11
- package/dst/ase-service-otel.js +1246 -0
- package/dst/{ase-skills.js → ase-service-skills.js} +29 -64
- package/dst/ase-service-state.js +101 -0
- package/dst/ase-service.js +251 -76
- package/dst/ase-setup-common.js +141 -0
- package/dst/ase-setup-mcp.js +359 -0
- package/dst/ase-setup-settings.js +837 -0
- package/dst/ase-setup.js +113 -869
- package/dst/ase-spec.js +31 -19
- package/dst/ase-statusline.js +108 -43
- package/dst/ase-task-board-core.js +739 -0
- package/dst/ase-task-board-filter.js +127 -0
- package/dst/ase-task-board-flow.js +237 -0
- package/dst/ase-task-board-graph.js +497 -0
- package/dst/ase-task-board-stats.js +263 -0
- package/dst/ase-task-board-tui-control.js +775 -0
- package/dst/ase-task-board-tui-model.js +1008 -0
- package/dst/ase-task-board-tui-popup.js +626 -0
- package/dst/ase-task-board-tui-style.js +106 -0
- package/dst/ase-task-board-tui-view.js +847 -0
- package/dst/ase-task-board-tui.js +189 -0
- package/dst/ase-task-board-web-client.css +1 -0
- package/dst/ase-task-board-web-client.html +20 -0
- package/dst/ase-task-board-web-client.js +81 -0
- package/dst/ase-task-board-web-server.js +823 -0
- package/dst/ase-task-board.js +134 -0
- package/dst/ase-task-format.js +757 -0
- package/dst/ase-task-request.js +208 -0
- package/dst/ase-task-store-client.js +495 -0
- package/dst/ase-task-store-core.js +804 -0
- package/dst/ase-task-store-issues.js +237 -0
- package/dst/ase-task-store-plugin-api.js +6 -0
- package/dst/ase-task-store-plugin-ase.js +484 -0
- package/dst/ase-task-store-plugin-delegate.js +137 -0
- package/dst/ase-task-store-plugin-gitea.js +666 -0
- package/dst/ase-task-store-plugin-github.js +696 -0
- package/dst/ase-task-store-plugin-gitlab.js +668 -0
- package/dst/ase-task-store-server-bind.js +538 -0
- package/dst/ase-task-store-server-cli.js +154 -0
- package/dst/ase-task.js +1335 -357
- package/dst/{ase-compat.js → ase-util-compat.js} +2 -2
- package/dst/{ase-diagram.js → ase-util-diagram.js} +107 -32
- package/dst/{ase-meta.js → ase-util-meta.js} +2 -2
- package/dst/{ase-metric.js → ase-util-metric.js} +2 -2
- package/dst/{ase-mint.js → ase-util-mint.js} +19 -12
- package/dst/{ase-worktree.js → ase-util-worktree.js} +30 -19
- package/dst/ase-util.js +20 -0
- package/dst/ase.js +108 -32
- package/dst/man/ase-agent-adhoc.1 +515 -0
- package/dst/man/ase-agent-condenser.1 +193 -0
- package/dst/man/ase-agent-editor.1 +212 -0
- package/dst/man/ase-agent-estimator.1 +214 -0
- package/dst/man/ase-agent-fleet.1 +185 -0
- package/dst/man/ase-agent-implementer.1 +233 -0
- package/dst/man/ase-agent-integrator.1 +213 -0
- package/dst/man/ase-agent-qualifier.1 +214 -0
- package/dst/man/ase-agent-rebooter.1 +193 -0
- package/dst/man/ase-agent-relay.1 +128 -0
- package/dst/man/ase-agent-researcher.1 +217 -0
- package/dst/man/ase-agent.1 +264 -0
- package/dst/man/ase-artifact-list.1 +38 -0
- package/dst/man/ase-artifact-name.1 +39 -0
- package/dst/man/ase-artifact.1 +56 -0
- package/dst/man/ase-config-delete.1 +45 -0
- package/dst/man/ase-config-edit.1 +48 -0
- package/dst/man/ase-config-get.1 +46 -0
- package/dst/man/ase-config-init.1 +60 -0
- package/dst/man/ase-config-list.1 +42 -0
- package/dst/man/ase-config-set.1 +52 -0
- package/dst/man/ase-config.1 +225 -0
- package/dst/man/ase-hook-permission-request.1 +36 -0
- package/dst/man/ase-hook-pre-tool-use.1 +59 -0
- package/dst/man/ase-hook-session-end.1 +47 -0
- package/dst/man/ase-hook-session-persona.1 +37 -0
- package/dst/man/ase-hook-session-start.1 +81 -0
- package/dst/man/ase-hook-stop.1 +34 -0
- package/dst/man/ase-hook-user-prompt-submit.1 +34 -0
- package/dst/man/ase-hook.1 +51 -0
- package/dst/man/ase-man.1 +67 -0
- package/dst/man/ase-mcp.1 +61 -0
- package/dst/man/ase-service-log.1 +43 -0
- package/dst/man/ase-service-send.1 +47 -0
- package/dst/man/ase-service-start.1 +54 -0
- package/dst/man/ase-service-status.1 +46 -0
- package/dst/man/ase-service-stop.1 +37 -0
- package/dst/man/ase-service.1 +85 -0
- package/dst/man/ase-setup-disable.1 +44 -0
- package/dst/man/ase-setup-enable.1 +45 -0
- package/dst/man/ase-setup-install.1 +73 -0
- package/dst/man/ase-setup-mcp.1 +124 -0
- package/dst/man/ase-setup-status.1 +60 -0
- package/dst/man/ase-setup-statusline.1 +92 -0
- package/dst/man/ase-setup-telemetry.1 +92 -0
- package/dst/man/ase-setup-uninstall.1 +59 -0
- package/dst/man/ase-setup-update.1 +64 -0
- package/dst/man/ase-setup.1 +93 -0
- package/dst/man/ase-spec-export.1 +62 -0
- package/dst/man/ase-spec-lint.1 +40 -0
- package/dst/man/ase-spec-preview.1 +40 -0
- package/dst/man/ase-spec.1 +46 -0
- package/dst/man/ase-statusline.1 +189 -0
- package/dst/man/ase-task-assign.1 +71 -0
- package/dst/man/ase-task-board.1 +384 -0
- package/dst/man/ase-task-delete.1 +35 -0
- package/dst/man/ase-task-edit.1 +46 -0
- package/dst/man/ase-task-idscheme.1 +65 -0
- package/dst/man/ase-task-lifecycle.1 +57 -0
- package/dst/man/ase-task-list.1 +47 -0
- package/dst/man/ase-task-load.1 +36 -0
- package/dst/man/ase-task-newid.1 +61 -0
- package/dst/man/ase-task-purge.1 +37 -0
- package/dst/man/ase-task-rename.1 +44 -0
- package/dst/man/ase-task-request.1 +61 -0
- package/dst/man/ase-task-save.1 +48 -0
- package/dst/man/ase-task-status.1 +55 -0
- package/dst/man/ase-task-store.1 +194 -0
- package/dst/man/ase-task-view.1 +40 -0
- package/dst/man/ase-task.1 +160 -0
- package/dst/man/ase-util-compat.1 +25 -0
- package/dst/man/ase-util-diagram.1 +76 -0
- package/dst/man/ase-util-meta.1 +45 -0
- package/dst/man/ase-util-metric.1 +54 -0
- package/dst/man/ase-util-mint.1 +59 -0
- package/dst/man/ase-util-worktree.1 +72 -0
- package/dst/man/ase-util.1 +50 -0
- package/dst/man/ase.1 +124 -0
- package/package.json +79 -21
- package/plugin/.claude-plugin/plugin.json +1 -1
- package/plugin/.codex-plugin/plugin.json +1 -1
- package/plugin/.github/plugin/plugin.json +1 -1
- package/plugin/agents/ase-meta-diagram.md +12 -8
- package/plugin/agents/ase-repo-review.md +322 -0
- package/plugin/hooks/hooks-codex.json +5 -0
- package/plugin/hooks/hooks-copilot.json +5 -0
- package/plugin/hooks/hooks.json +8 -0
- package/plugin/meta/ase-common-approach.md +117 -0
- package/plugin/meta/ase-common-changeset.md +256 -0
- package/plugin/meta/ase-common-code.md +10 -173
- package/plugin/meta/ase-common-grill.md +317 -0
- package/plugin/meta/ase-common-resolve.md +305 -0
- package/plugin/meta/ase-common-task.md +83 -4
- package/plugin/meta/ase-constitution.md +0 -2
- package/plugin/meta/ase-format-specbook.md +208 -25
- package/plugin/meta/ase-format-specbook.yaml +242 -851
- package/plugin/meta/ase-format-task.md +669 -125
- package/plugin/meta/ase-persona.md +3 -4
- package/plugin/meta/ase-skill.md +9 -0
- package/plugin/mods/ase-mods.ts +409 -0
- package/plugin/mods/claude-code.d.ts +13417 -0
- package/plugin/skills/ase-arch-analyze/SKILL.md +1 -1
- package/plugin/skills/ase-arch-analyze/help.md +2 -2
- package/plugin/skills/ase-code-analyze/SKILL.md +59 -8
- package/plugin/skills/ase-code-analyze/help.md +29 -4
- package/plugin/skills/ase-code-edit/SKILL.md +107 -181
- package/plugin/skills/ase-code-edit/help.md +118 -31
- package/plugin/skills/ase-code-lint/SKILL.md +64 -13
- package/plugin/skills/ase-code-lint/help.md +32 -2
- package/plugin/skills/ase-docs-proofread/SKILL.md +4 -4
- package/plugin/skills/ase-docs-proofread/help.md +1 -1
- package/plugin/skills/ase-docs-refine/SKILL.md +4 -4
- package/plugin/skills/ase-docs-shorten/SKILL.md +4 -4
- package/plugin/skills/ase-help-intent/data.md +2164 -1087
- package/plugin/skills/ase-help-intent/help.md +1 -1
- package/plugin/skills/ase-help-skill/catalog.md +12 -9
- package/plugin/skills/ase-meta-brainstorm/SKILL.md +1 -1
- package/plugin/skills/ase-meta-config/SKILL.md +3 -4
- package/plugin/skills/ase-meta-config/help.md +31 -10
- package/plugin/skills/ase-meta-mint/SKILL.md +5 -5
- package/plugin/skills/ase-meta-mint/help.md +10 -5
- package/plugin/skills/ase-meta-why/help.md +1 -1
- package/plugin/skills/ase-meta-workflow/SKILL.md +7 -7
- package/plugin/skills/ase-meta-workflow/help.md +4 -4
- package/plugin/skills/ase-meta-workflow/sample.md +3 -3
- package/plugin/skills/ase-meta-workflow/workflow.txt +22 -18
- package/plugin/skills/{ase-meta-changelog → ase-repo-changelog}/SKILL.md +4 -3
- package/plugin/skills/{ase-meta-changelog → ase-repo-changelog}/help.md +5 -5
- package/plugin/skills/{ase-meta-commit → ase-repo-commit}/SKILL.md +6 -6
- package/plugin/skills/{ase-meta-commit → ase-repo-commit}/help.md +5 -5
- package/plugin/skills/{ase-meta-diff → ase-repo-diff}/SKILL.md +6 -6
- package/plugin/skills/{ase-meta-diff → ase-repo-diff}/help.md +8 -8
- package/plugin/skills/{ase-code-dissect → ase-repo-dissect}/SKILL.md +19 -19
- package/plugin/skills/{ase-code-dissect → ase-repo-dissect}/help.md +10 -12
- package/plugin/skills/ase-repo-merge/SKILL.md +346 -0
- package/plugin/skills/ase-repo-merge/help.md +107 -0
- package/plugin/skills/ase-repo-resolve/SKILL.md +158 -0
- package/plugin/skills/ase-repo-resolve/help.md +102 -0
- package/plugin/skills/ase-repo-review/SKILL.md +406 -0
- package/plugin/skills/ase-repo-review/help.md +181 -0
- package/plugin/skills/ase-spec-edit/SKILL.md +117 -183
- package/plugin/skills/ase-spec-edit/help.md +107 -26
- package/plugin/skills/ase-sync-reconcile/help.md +1 -1
- package/plugin/skills/ase-task-assign/SKILL.md +111 -0
- package/plugin/skills/ase-task-assign/help.md +119 -0
- package/plugin/skills/ase-task-condense/SKILL.md +21 -21
- package/plugin/skills/ase-task-condense/help.md +12 -10
- package/plugin/skills/ase-task-delete/SKILL.md +1 -1
- package/plugin/skills/ase-task-dissect/SKILL.md +71 -48
- package/plugin/skills/ase-task-dissect/help.md +10 -9
- package/plugin/skills/ase-task-edit/SKILL.md +379 -94
- package/plugin/skills/ase-task-edit/help.md +95 -22
- package/plugin/skills/ase-task-estimate/SKILL.md +492 -0
- package/plugin/skills/ase-task-estimate/help.md +158 -0
- package/plugin/skills/ase-task-grill/SKILL.md +246 -138
- package/plugin/skills/ase-task-grill/help.md +129 -26
- package/plugin/skills/ase-task-id/SKILL.md +8 -1
- package/plugin/skills/ase-task-id/help.md +6 -3
- package/plugin/skills/ase-task-implement/SKILL.md +535 -145
- package/plugin/skills/ase-task-implement/help.md +138 -47
- package/plugin/skills/ase-task-integrate/SKILL.md +549 -0
- package/plugin/skills/ase-task-integrate/help.md +187 -0
- package/plugin/skills/ase-task-list/SKILL.md +55 -23
- package/plugin/skills/ase-task-list/help.md +45 -21
- package/plugin/skills/ase-task-reboot/SKILL.md +84 -43
- package/plugin/skills/ase-task-reboot/help.md +28 -8
- package/plugin/skills/ase-task-rename/SKILL.md +10 -2
- package/plugin/skills/ase-task-research/SKILL.md +386 -0
- package/plugin/skills/ase-task-research/help.md +102 -0
- package/plugin/skills/ase-task-status/SKILL.md +137 -0
- package/plugin/skills/ase-task-status/help.md +80 -0
- package/plugin/skills/ase-task-view/SKILL.md +49 -24
- package/plugin/skills/ase-task-view/help.md +19 -9
- package/plugin/agents/ase-meta-review.md +0 -187
- package/plugin/skills/ase-code-craft/SKILL.md +0 -264
- package/plugin/skills/ase-code-craft/help.md +0 -122
- package/plugin/skills/ase-code-refactor/SKILL.md +0 -232
- package/plugin/skills/ase-code-refactor/help.md +0 -109
- package/plugin/skills/ase-code-resolve/SKILL.md +0 -289
- package/plugin/skills/ase-code-resolve/help.md +0 -116
- package/plugin/skills/ase-meta-review/SKILL.md +0 -210
- package/plugin/skills/ase-meta-review/help.md +0 -96
- package/plugin/skills/ase-task-preflight/SKILL.md +0 -166
- package/plugin/skills/ase-task-preflight/help.md +0 -82
- /package/dst/{ase-ignore.js → ase-lib-ignore.js} +0 -0
- /package/dst/{ase-stdio.js → ase-lib-stdio.js} +0 -0
- /package/dst/{ase-kv.js → ase-service-kv.js} +0 -0
- /package/dst/{ase-sleep.js → ase-service-sleep.js} +0 -0
- /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
|
+
};
|