@towles/tool 0.0.109 → 0.0.111
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/package.json +9 -4
- package/{plugins/tt-agentboard → packages/agentboard}/README.md +1 -1
- package/{plugins/tt-agentboard → packages/agentboard}/apps/server/package.json +2 -1
- package/{plugins/tt-agentboard → packages/agentboard}/apps/server/src/main.ts +6 -20
- package/{plugins/tt-agentboard → packages/agentboard}/apps/tui/package.json +4 -0
- package/{plugins/tt-agentboard → packages/agentboard}/apps/tui/src/components/DetailPanel.tsx +3 -2
- package/{plugins/tt-agentboard → packages/agentboard}/apps/tui/src/components/StatusBar.tsx +35 -0
- package/{plugins/tt-agentboard → packages/agentboard}/apps/tui/src/constants.ts +1 -0
- package/{plugins/tt-agentboard → packages/agentboard}/apps/tui/src/index.tsx +206 -226
- package/packages/agentboard/apps/tui/src/session-status.test.ts +70 -0
- package/packages/agentboard/apps/tui/src/session-status.ts +19 -0
- package/{plugins/tt-agentboard → packages/agentboard}/package.json +2 -6
- package/{plugins/tt-agentboard → packages/agentboard}/packages/runtime/package.json +3 -0
- package/{plugins/tt-agentboard/packages/runtime/test → packages/agentboard/packages/runtime/src/agents}/tracker.test.ts +2 -2
- package/packages/agentboard/packages/runtime/src/agents/watchers/claude-code.test.ts +63 -0
- package/{plugins/tt-agentboard → packages/agentboard}/packages/runtime/src/agents/watchers/claude-code.ts +26 -2
- package/packages/agentboard/packages/runtime/src/config.test.ts +107 -0
- package/packages/agentboard/packages/runtime/src/config.ts +80 -0
- package/{plugins/tt-agentboard → packages/agentboard}/packages/runtime/src/index.ts +1 -1
- package/{plugins/tt-agentboard → packages/agentboard}/packages/runtime/src/plugins/loader.ts +1 -33
- package/{plugins/tt-agentboard → packages/agentboard}/packages/runtime/src/server/git-info.ts +3 -2
- package/{plugins/tt-agentboard → packages/agentboard}/packages/runtime/src/server/index.ts +23 -37
- package/{plugins/tt-agentboard → packages/agentboard}/packages/runtime/src/server/launcher.ts +6 -18
- package/{plugins/tt-agentboard → packages/agentboard}/packages/runtime/src/server/pane-scanner.ts +6 -0
- package/{plugins/tt-agentboard → packages/agentboard}/packages/runtime/src/shared.ts +7 -2
- package/{plugins/tt-agentboard → packages/agentboard}/packages/runtime/tsconfig.json +1 -1
- package/packages/shared/package.json +15 -0
- package/packages/shared/src/git/exec.ts +41 -0
- package/{src/utils → packages/shared/src}/git/gh-cli-wrapper.ts +13 -18
- package/packages/shared/src/index.ts +8 -0
- package/packages/shared/tsconfig.json +16 -0
- package/src/cli.ts +1 -1
- package/src/commands/agentboard.ts +51 -67
- package/src/{lib → commands}/auto-claude/claude-cli.ts +1 -1
- package/src/commands/auto-claude/config-init-helpers.ts +79 -0
- package/src/commands/auto-claude/config-init.test.ts +137 -0
- package/src/commands/auto-claude/config-init.ts +159 -0
- package/src/{lib → commands}/auto-claude/config.ts +4 -8
- package/src/{lib → commands}/auto-claude/e2e.test.ts +6 -6
- package/src/commands/auto-claude/explain.test.ts +58 -0
- package/src/commands/auto-claude/explain.ts +97 -0
- package/src/commands/auto-claude/index.ts +37 -14
- package/src/{lib → commands}/auto-claude/labels.ts +1 -1
- package/src/commands/auto-claude/list.ts +5 -4
- package/src/{lib → commands}/auto-claude/pipeline-execution.test.ts +1 -1
- package/src/{lib → commands}/auto-claude/pipeline.ts +1 -3
- package/src/commands/auto-claude/retry.test.ts +2 -2
- package/src/commands/auto-claude/retry.ts +5 -5
- package/src/commands/auto-claude/shell.ts +3 -0
- package/src/commands/auto-claude/status.test.ts +2 -2
- package/src/commands/auto-claude/status.ts +4 -4
- package/src/{lib → commands}/auto-claude/steps/create-pr.ts +1 -3
- package/src/{lib → commands}/auto-claude/steps/fetch-issues.ts +1 -1
- package/src/{lib → commands}/auto-claude/steps/implement.ts +1 -2
- package/src/{lib → commands}/auto-claude/utils-execution.test.ts +6 -6
- package/src/{lib → commands}/auto-claude/utils.ts +10 -4
- package/src/{lib/install → commands}/claude-settings.ts +1 -1
- package/src/commands/config/config.test.ts +129 -0
- package/src/commands/config/index.ts +11 -0
- package/src/commands/config/reset.ts +53 -0
- package/src/commands/config/schema.ts +19 -0
- package/src/commands/{config.ts → config/show.ts} +2 -2
- package/src/commands/config/validate.ts +51 -0
- package/src/commands/doctor/checks.ts +167 -0
- package/src/commands/doctor/format.test.ts +63 -0
- package/src/commands/doctor/format.ts +5 -0
- package/src/commands/doctor/history.test.ts +161 -0
- package/src/commands/doctor/history.ts +130 -0
- package/src/commands/doctor.ts +80 -151
- package/src/commands/gh/branch-clean.ts +4 -4
- package/src/commands/gh/branch.test.ts +4 -5
- package/src/commands/gh/branch.ts +10 -5
- package/src/commands/gh/pr.ts +6 -7
- package/src/{lib → commands}/graph/analyzer.test.ts +4 -4
- package/src/commands/graph/format.test.ts +130 -0
- package/src/commands/graph/format.ts +94 -0
- package/src/commands/graph/index.ts +69 -41
- package/src/{lib → commands}/graph/labels.ts +4 -4
- package/src/{lib → commands}/graph/server.ts +2 -2
- package/src/{lib → commands}/graph/types.ts +2 -0
- package/src/commands/graph.test.ts +1 -1
- package/src/commands/install.ts +6 -6
- package/src/commands/journal/daily-notes.ts +4 -7
- package/src/{lib → commands}/journal/fs.ts +1 -1
- package/src/commands/journal/index.ts +2 -0
- package/src/commands/journal/list.test.ts +174 -0
- package/src/commands/journal/list.ts +213 -0
- package/src/commands/journal/meeting.ts +4 -7
- package/src/commands/journal/note.ts +4 -7
- package/src/{lib → commands}/journal/paths.ts +1 -1
- package/src/commands/journal/search.test.ts +156 -0
- package/src/commands/journal/search.ts +256 -0
- package/src/{lib → commands}/journal/templates.ts +1 -1
- package/src/config/settings.ts +35 -26
- package/plugins/tt-agentboard/bun.lock +0 -444
- package/plugins/tt-agentboard/packages/runtime/src/config.ts +0 -70
- package/plugins/tt-agentboard/packages/runtime/test/config.test.ts +0 -83
- package/plugins/tt-auto-claude/.claude-plugin/plugin.json +0 -8
- package/plugins/tt-auto-claude/commands/create-issue.md +0 -20
- package/plugins/tt-auto-claude/commands/list.md +0 -21
- package/plugins/tt-auto-claude/skills/auto-claude/SKILL.md +0 -71
- package/plugins/tt-core/promptfooconfig.interview-me.yaml +0 -155
- package/plugins/tt-core/promptfooconfig.refine-text.yaml +0 -242
- package/plugins/tt-core/promptfooconfig.tdd.yaml +0 -144
- package/plugins/tt-core/promptfooconfig.write-prd.yaml +0 -145
- package/src/commands/config.test.ts +0 -9
- package/src/lib/auto-claude/index.ts +0 -15
- package/src/lib/auto-claude/shell.ts +0 -6
- package/src/lib/graph/index.ts +0 -24
- package/src/lib/journal/index.ts +0 -11
- package/src/utils/git/exec.ts +0 -18
- /package/{plugins/tt-agentboard → packages/agentboard}/apps/tui/build.ts +0 -0
- /package/{plugins/tt-agentboard → packages/agentboard}/apps/tui/bunfig.toml +0 -0
- /package/{plugins/tt-agentboard → packages/agentboard}/apps/tui/scripts/sessionizer.sh +0 -0
- /package/{plugins/tt-agentboard → packages/agentboard}/apps/tui/src/components/DiffStats.tsx +0 -0
- /package/{plugins/tt-agentboard → packages/agentboard}/apps/tui/src/components/SessionCard.tsx +0 -0
- /package/{plugins/tt-agentboard → packages/agentboard}/apps/tui/src/detail-panel-height.ts +0 -0
- /package/{plugins/tt-agentboard → packages/agentboard}/apps/tui/src/mux-context.ts +0 -0
- /package/{plugins/tt-agentboard → packages/agentboard}/apps/tui/tsconfig.json +0 -0
- /package/{plugins/tt-agentboard → packages/agentboard}/packages/mux-tmux/package.json +0 -0
- /package/{plugins/tt-agentboard → packages/agentboard}/packages/mux-tmux/src/client.ts +0 -0
- /package/{plugins/tt-agentboard → packages/agentboard}/packages/mux-tmux/src/index.ts +0 -0
- /package/{plugins/tt-agentboard → packages/agentboard}/packages/mux-tmux/src/provider.ts +0 -0
- /package/{plugins/tt-agentboard → packages/agentboard}/packages/mux-tmux/tsconfig.json +0 -0
- /package/{plugins/tt-agentboard → packages/agentboard}/packages/runtime/src/agents/tracker.ts +0 -0
- /package/{plugins/tt-agentboard → packages/agentboard}/packages/runtime/src/agents/watchers/amp.ts +0 -0
- /package/{plugins/tt-agentboard → packages/agentboard}/packages/runtime/src/agents/watchers/codex.ts +0 -0
- /package/{plugins/tt-agentboard → packages/agentboard}/packages/runtime/src/agents/watchers/opencode.ts +0 -0
- /package/{plugins/tt-agentboard → packages/agentboard}/packages/runtime/src/contracts/agent-watcher.ts +0 -0
- /package/{plugins/tt-agentboard → packages/agentboard}/packages/runtime/src/contracts/agent.ts +0 -0
- /package/{plugins/tt-agentboard → packages/agentboard}/packages/runtime/src/contracts/index.ts +0 -0
- /package/{plugins/tt-agentboard → packages/agentboard}/packages/runtime/src/contracts/mux.ts +0 -0
- /package/{plugins/tt-agentboard → packages/agentboard}/packages/runtime/src/debug.ts +0 -0
- /package/{plugins/tt-agentboard → packages/agentboard}/packages/runtime/src/mux/detect.ts +0 -0
- /package/{plugins/tt-agentboard → packages/agentboard}/packages/runtime/src/mux/registry.ts +0 -0
- /package/{plugins/tt-agentboard → packages/agentboard}/packages/runtime/src/server/context.ts +0 -0
- /package/{plugins/tt-agentboard → packages/agentboard}/packages/runtime/src/server/metadata-store.ts +0 -0
- /package/{plugins/tt-agentboard → packages/agentboard}/packages/runtime/src/server/port-scanner.ts +0 -0
- /package/{plugins/tt-agentboard → packages/agentboard}/packages/runtime/src/server/session-order.ts +0 -0
- /package/{plugins/tt-agentboard → packages/agentboard}/packages/runtime/src/server/sidebar-manager.ts +0 -0
- /package/{plugins/tt-agentboard → packages/agentboard}/packages/runtime/src/server/sidebar-width-sync.ts +0 -0
- /package/{plugins/tt-agentboard → packages/agentboard}/packages/runtime/src/themes.ts +0 -0
- /package/{plugins/tt-agentboard → packages/agentboard}/tsconfig.json +0 -0
- /package/{plugins/tt-core → packages/core}/.claude-plugin/plugin.json +0 -0
- /package/{plugins/tt-core → packages/core}/README.md +0 -0
- /package/{plugins/tt-core → packages/core}/commands/improve-architecture.md +0 -0
- /package/{plugins/tt-core → packages/core}/commands/interview-me.md +0 -0
- /package/{plugins/tt-core → packages/core}/commands/prd-to-issues.md +0 -0
- /package/{plugins/tt-core → packages/core}/commands/refine-text.md +0 -0
- /package/{plugins/tt-core → packages/core}/commands/task.md +0 -0
- /package/{plugins/tt-core → packages/core}/commands/tdd.md +0 -0
- /package/{plugins/tt-core → packages/core}/commands/write-prd.md +0 -0
- /package/{plugins/tt-core → packages/core}/skills/towles-tool/SKILL.md +0 -0
- /package/{src/utils → packages/shared/src}/date-utils.test.ts +0 -0
- /package/{src/utils → packages/shared/src}/date-utils.ts +0 -0
- /package/{src/utils → packages/shared/src}/fs.ts +0 -0
- /package/{src/utils → packages/shared/src}/git/branch-name.test.ts +0 -0
- /package/{src/utils → packages/shared/src}/git/branch-name.ts +0 -0
- /package/{src/utils → packages/shared/src}/git/gh-cli-wrapper.test.ts +0 -0
- /package/{src/utils → packages/shared/src}/render.test.ts +0 -0
- /package/{src/utils → packages/shared/src}/render.ts +0 -0
- /package/src/{lib → commands}/auto-claude/config.test.ts +0 -0
- /package/src/{lib → commands}/auto-claude/labels.test.ts +0 -0
- /package/src/{lib → commands}/auto-claude/pipeline.test.ts +0 -0
- /package/src/{lib → commands}/auto-claude/prompt-templates/01_plan.prompt.md +0 -0
- /package/src/{lib → commands}/auto-claude/prompt-templates/02_implement.prompt.md +0 -0
- /package/src/{lib → commands}/auto-claude/prompt-templates/03_simplify.prompt.md +0 -0
- /package/src/{lib → commands}/auto-claude/prompt-templates/04_review.prompt.md +0 -0
- /package/src/{lib → commands}/auto-claude/prompt-templates/CLAUDE.md +0 -0
- /package/src/{lib → commands}/auto-claude/prompt-templates/index.test.ts +0 -0
- /package/src/{lib → commands}/auto-claude/prompt-templates/index.ts +0 -0
- /package/src/{lib → commands}/auto-claude/run-claude.test.ts +0 -0
- /package/src/{lib → commands}/auto-claude/spawn-claude.ts +0 -0
- /package/src/{lib → commands}/auto-claude/steps/simple-steps.ts +0 -0
- /package/src/{lib → commands}/auto-claude/steps/steps.test.ts +0 -0
- /package/src/{lib → commands}/auto-claude/stream-parser.test.ts +0 -0
- /package/src/{lib → commands}/auto-claude/stream-parser.ts +0 -0
- /package/src/{lib → commands}/auto-claude/templates.test.ts +0 -0
- /package/src/{lib → commands}/auto-claude/templates.ts +0 -0
- /package/src/{lib → commands}/auto-claude/test-helpers.ts +0 -0
- /package/src/{lib → commands}/auto-claude/utils.test.ts +0 -0
- /package/src/{lib → commands}/graph/analyzer.ts +0 -0
- /package/src/{lib → commands}/graph/graph-template.html +0 -0
- /package/src/{lib → commands}/graph/parser.test.ts +0 -0
- /package/src/{lib → commands}/graph/parser.ts +0 -0
- /package/src/{lib → commands}/graph/render.ts +0 -0
- /package/src/{lib → commands}/graph/sessions.ts +0 -0
- /package/src/{lib → commands}/graph/tools.ts +0 -0
- /package/src/{lib → commands}/graph/treemap.ts +0 -0
- /package/src/{lib → commands}/journal/editor.ts +0 -0
|
@@ -13,124 +13,72 @@ import {
|
|
|
13
13
|
} from "solid-js";
|
|
14
14
|
import type { Accessor } from "solid-js";
|
|
15
15
|
import { createStore, reconcile } from "solid-js/store";
|
|
16
|
-
import { TextAttributes } from "@opentui/core";
|
|
17
16
|
import type { MouseEvent } from "@opentui/core";
|
|
18
17
|
|
|
19
|
-
import {
|
|
20
|
-
ensureServer,
|
|
21
|
-
SERVER_PORT,
|
|
22
|
-
SERVER_HOST,
|
|
23
|
-
loadConfig,
|
|
24
|
-
resolveTheme,
|
|
25
|
-
saveConfig,
|
|
26
|
-
} from "@tt-agentboard/runtime";
|
|
18
|
+
import { ensureServer, SERVER_PORT, SERVER_HOST, resolveTheme } from "@tt-agentboard/runtime";
|
|
27
19
|
import type { ServerMessage, SessionData, ClientCommand, Theme } from "@tt-agentboard/runtime";
|
|
28
|
-
import { TmuxClient } from "@tt-agentboard/mux-tmux";
|
|
29
20
|
import { SessionCard } from "./components/SessionCard";
|
|
30
21
|
import { DetailPanel } from "./components/DetailPanel";
|
|
31
22
|
import { StatusBar } from "./components/StatusBar";
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
23
|
+
import { computeSessionStatusCounts } from "./session-status";
|
|
24
|
+
import {
|
|
25
|
+
detectMuxContext,
|
|
26
|
+
refocusMainPane,
|
|
27
|
+
getClientTty,
|
|
28
|
+
getLocalSessionName,
|
|
29
|
+
} from "./mux-context";
|
|
30
|
+
import {
|
|
31
|
+
clampDetailPanelHeight,
|
|
32
|
+
getStoredDetailPanelHeight,
|
|
33
|
+
persistDetailPanelHeight,
|
|
34
|
+
} from "./detail-panel-height";
|
|
35
|
+
import {
|
|
36
|
+
SPINNERS,
|
|
37
|
+
BOLD,
|
|
38
|
+
DIM,
|
|
39
|
+
DEFAULT_DETAIL_PANEL_HEIGHT,
|
|
40
|
+
DIVIDER,
|
|
41
|
+
logResizeDebug,
|
|
42
|
+
} from "./constants";
|
|
42
43
|
|
|
43
44
|
const muxCtx = detectMuxContext();
|
|
44
45
|
|
|
45
|
-
const SPINNERS = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"];
|
|
46
|
-
const BOLD = TextAttributes.BOLD;
|
|
47
|
-
const DIM = TextAttributes.DIM;
|
|
48
|
-
const DEFAULT_DETAIL_PANEL_HEIGHT = 10;
|
|
49
|
-
const MIN_DETAIL_PANEL_HEIGHT = 4;
|
|
50
|
-
const DIVIDER = "─".repeat(200);
|
|
51
|
-
const RESIZE_DEBUG_LOG = "/tmp/agentboard-tui-resize.log";
|
|
52
|
-
|
|
53
46
|
const TUI_DEBUG = !!process.env.TT_AGENTBOARD_DEBUG;
|
|
54
47
|
|
|
55
|
-
function
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
return Math.max(MIN_DETAIL_PANEL_HEIGHT, Math.round(height));
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
function getStoredDetailPanelHeight(sessionName: string): number {
|
|
69
|
-
const stored = loadConfig().detailPanelHeights?.[sessionName];
|
|
70
|
-
return typeof stored === "number" ? clampDetailPanelHeight(stored) : DEFAULT_DETAIL_PANEL_HEIGHT;
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
function persistDetailPanelHeight(sessionName: string, height: number): void {
|
|
74
|
-
const config = loadConfig();
|
|
75
|
-
saveConfig({
|
|
76
|
-
detailPanelHeights: {
|
|
77
|
-
...(config.detailPanelHeights ?? {}),
|
|
78
|
-
[sessionName]: clampDetailPanelHeight(height),
|
|
79
|
-
},
|
|
80
|
-
});
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
/** Refocus the main (non-sidebar) pane after TUI capability detection finishes.
|
|
84
|
-
* This must happen from the TUI process — doing it from the server races with
|
|
85
|
-
* capability query responses and leaks escape sequences to the main pane. */
|
|
86
|
-
function refocusMainPane() {
|
|
87
|
-
if (muxCtx.type === "tmux") {
|
|
88
|
-
try {
|
|
89
|
-
// Use the TUI's own pane ID to find its current window (handles stash restore
|
|
90
|
-
// where the pane may have moved to a different window than the original).
|
|
91
|
-
const windowId =
|
|
92
|
-
process.env.REFOCUS_WINDOW ||
|
|
93
|
-
Bun.spawnSync(["tmux", "display-message", "-t", muxCtx.paneId, "-p", "#{window_id}"], {
|
|
94
|
-
stdout: "pipe",
|
|
95
|
-
stderr: "pipe",
|
|
96
|
-
})
|
|
97
|
-
.stdout.toString()
|
|
98
|
-
.trim();
|
|
99
|
-
if (!windowId) return;
|
|
100
|
-
const r = Bun.spawnSync(
|
|
101
|
-
["tmux", "list-panes", "-t", windowId, "-F", "#{pane_id} #{pane_title}"],
|
|
102
|
-
{ stdout: "pipe", stderr: "pipe" },
|
|
103
|
-
);
|
|
104
|
-
const lines = r.stdout.toString().trim().split("\n");
|
|
105
|
-
const main = lines.find((l) => !l.includes("agentboard-sidebar"));
|
|
106
|
-
if (main) {
|
|
107
|
-
const paneId = main.split(" ")[0];
|
|
108
|
-
Bun.spawnSync(["tmux", "select-pane", "-t", paneId], { stdout: "pipe", stderr: "pipe" });
|
|
109
|
-
}
|
|
110
|
-
} catch {}
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
function getClientTty(): string {
|
|
115
|
-
if (muxCtx.type === "tmux") {
|
|
116
|
-
const { sdk, paneId } = muxCtx;
|
|
117
|
-
const sessName = sdk.display("#{session_name}", { target: paneId });
|
|
118
|
-
if (sessName) {
|
|
119
|
-
const clients = sdk.listClients();
|
|
120
|
-
const client = clients.find((c) => c.sessionName === sessName);
|
|
121
|
-
if (client) return client.tty;
|
|
48
|
+
function KeyHints(props: {
|
|
49
|
+
hints: [string, string][];
|
|
50
|
+
palette: Accessor<Theme["palette"]>;
|
|
51
|
+
cols?: number;
|
|
52
|
+
}) {
|
|
53
|
+
const cols = () => props.cols ?? 2;
|
|
54
|
+
const rows = () => {
|
|
55
|
+
const pairs: [string, string][][] = [];
|
|
56
|
+
for (let i = 0; i < props.hints.length; i += cols()) {
|
|
57
|
+
pairs.push(props.hints.slice(i, i + cols()));
|
|
122
58
|
}
|
|
123
|
-
return
|
|
124
|
-
}
|
|
125
|
-
return "";
|
|
126
|
-
}
|
|
59
|
+
return pairs;
|
|
60
|
+
};
|
|
127
61
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
62
|
+
return (
|
|
63
|
+
<box flexDirection="column">
|
|
64
|
+
<For each={rows()}>
|
|
65
|
+
{(row) => (
|
|
66
|
+
<box flexDirection="row">
|
|
67
|
+
<For each={row}>
|
|
68
|
+
{([key, label]) => (
|
|
69
|
+
<box width={16} flexShrink={0}>
|
|
70
|
+
<text>
|
|
71
|
+
<span style={{ fg: props.palette().overlay0 }}>{key}</span>
|
|
72
|
+
<span style={{ fg: props.palette().overlay1 }}>{` ${label}`}</span>
|
|
73
|
+
</text>
|
|
74
|
+
</box>
|
|
75
|
+
)}
|
|
76
|
+
</For>
|
|
77
|
+
</box>
|
|
78
|
+
)}
|
|
79
|
+
</For>
|
|
80
|
+
</box>
|
|
81
|
+
);
|
|
134
82
|
}
|
|
135
83
|
|
|
136
84
|
function App() {
|
|
@@ -148,6 +96,7 @@ function App() {
|
|
|
148
96
|
const [connected, setConnected] = createSignal(false);
|
|
149
97
|
const [spinIdx, setSpinIdx] = createSignal(0);
|
|
150
98
|
const [detailPanelHeight, setDetailPanelHeight] = createSignal(DEFAULT_DETAIL_PANEL_HEIGHT);
|
|
99
|
+
const [preferredEditor, setPreferredEditor] = createSignal("code");
|
|
151
100
|
const [isDetailResizeHover, setIsDetailResizeHover] = createSignal(false);
|
|
152
101
|
const [isDetailResizing, setIsDetailResizing] = createSignal(false);
|
|
153
102
|
const detailPanelSessionName = createMemo(() => focusedSession() ?? mySession());
|
|
@@ -161,12 +110,12 @@ function App() {
|
|
|
161
110
|
const [modal, setModal] = createSignal<"none" | "confirm-kill" | "help">("none");
|
|
162
111
|
const [killTarget, setKillTarget] = createSignal<string | null>(null);
|
|
163
112
|
|
|
164
|
-
const [clientTty, setClientTty] = createSignal(getClientTty());
|
|
113
|
+
const [clientTty, setClientTty] = createSignal(getClientTty(muxCtx));
|
|
165
114
|
let ws: WebSocket | null = null;
|
|
166
115
|
let startupFocusSynced = false;
|
|
167
116
|
let detailResizeStartY = 0;
|
|
168
117
|
let detailResizeStartHeight = DEFAULT_DETAIL_PANEL_HEIGHT;
|
|
169
|
-
const startupSessionName = getLocalSessionName();
|
|
118
|
+
const startupSessionName = getLocalSessionName(muxCtx);
|
|
170
119
|
|
|
171
120
|
const focusedData = createMemo(() => sessions.find((s) => s.name === focusedSession()) ?? null);
|
|
172
121
|
|
|
@@ -186,7 +135,7 @@ function App() {
|
|
|
186
135
|
}
|
|
187
136
|
|
|
188
137
|
function reIdentify() {
|
|
189
|
-
const sessionName = getLocalSessionName();
|
|
138
|
+
const sessionName = getLocalSessionName(muxCtx);
|
|
190
139
|
if (!sessionName) return;
|
|
191
140
|
|
|
192
141
|
if (muxCtx.type === "tmux") {
|
|
@@ -274,14 +223,15 @@ function App() {
|
|
|
274
223
|
}
|
|
275
224
|
|
|
276
225
|
function beginDetailResize(event: MouseEvent) {
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
226
|
+
if (TUI_DEBUG)
|
|
227
|
+
logResizeDebug("beginDetailResize", {
|
|
228
|
+
button: event.button,
|
|
229
|
+
x: event.x,
|
|
230
|
+
y: event.y,
|
|
231
|
+
currentHeight: detailPanelHeight(),
|
|
232
|
+
session: detailPanelSessionName(),
|
|
233
|
+
target: event.target?.id ?? null,
|
|
234
|
+
});
|
|
285
235
|
if (event.button !== 0) return;
|
|
286
236
|
(renderer as any).setCapturedRenderable?.(event.target ?? undefined);
|
|
287
237
|
detailResizeStartY = event.y;
|
|
@@ -291,36 +241,39 @@ function App() {
|
|
|
291
241
|
}
|
|
292
242
|
|
|
293
243
|
function handleDetailResizeDrag(event: MouseEvent) {
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
244
|
+
if (TUI_DEBUG)
|
|
245
|
+
logResizeDebug("handleDetailResizeDrag", {
|
|
246
|
+
x: event.x,
|
|
247
|
+
y: event.y,
|
|
248
|
+
isResizing: isDetailResizing(),
|
|
249
|
+
startY: detailResizeStartY,
|
|
250
|
+
startHeight: detailResizeStartHeight,
|
|
251
|
+
currentHeight: detailPanelHeight(),
|
|
252
|
+
session: detailPanelSessionName(),
|
|
253
|
+
});
|
|
303
254
|
if (!isDetailResizing()) return;
|
|
304
255
|
const delta = detailResizeStartY - event.y;
|
|
305
256
|
const nextHeight = clampDetailPanelHeight(detailResizeStartHeight + delta);
|
|
306
257
|
setDetailPanelHeight(nextHeight);
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
258
|
+
if (TUI_DEBUG)
|
|
259
|
+
logResizeDebug("handleDetailResizeDrag:applied", {
|
|
260
|
+
delta,
|
|
261
|
+
nextHeight,
|
|
262
|
+
session: detailPanelSessionName(),
|
|
263
|
+
});
|
|
312
264
|
event.stopPropagation();
|
|
313
265
|
}
|
|
314
266
|
|
|
315
267
|
function endDetailResize(event?: MouseEvent) {
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
268
|
+
if (TUI_DEBUG)
|
|
269
|
+
logResizeDebug("endDetailResize", {
|
|
270
|
+
x: event?.x,
|
|
271
|
+
y: event?.y,
|
|
272
|
+
isResizing: isDetailResizing(),
|
|
273
|
+
currentHeight: detailPanelHeight(),
|
|
274
|
+
session: detailPanelSessionName(),
|
|
275
|
+
target: event?.target?.id ?? null,
|
|
276
|
+
});
|
|
324
277
|
if (!isDetailResizing()) return;
|
|
325
278
|
(renderer as any).setCapturedRenderable?.(undefined);
|
|
326
279
|
setIsDetailResizing(false);
|
|
@@ -329,10 +282,11 @@ function App() {
|
|
|
329
282
|
const sessionName = detailPanelSessionName();
|
|
330
283
|
if (sessionName) {
|
|
331
284
|
persistDetailPanelHeight(sessionName, detailPanelHeight());
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
285
|
+
if (TUI_DEBUG)
|
|
286
|
+
logResizeDebug("endDetailResize:persisted", {
|
|
287
|
+
session: sessionName,
|
|
288
|
+
height: detailPanelHeight(),
|
|
289
|
+
});
|
|
336
290
|
}
|
|
337
291
|
|
|
338
292
|
event?.stopPropagation();
|
|
@@ -353,13 +307,25 @@ function App() {
|
|
|
353
307
|
});
|
|
354
308
|
}
|
|
355
309
|
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
310
|
+
function openInEditor() {
|
|
311
|
+
const data = focusedData();
|
|
312
|
+
if (!data?.dir) return;
|
|
313
|
+
const editor = preferredEditor();
|
|
314
|
+
Bun.spawn([editor, data.dir], {
|
|
315
|
+
stdout: "ignore",
|
|
316
|
+
stderr: "ignore",
|
|
317
|
+
stdin: "ignore",
|
|
362
318
|
});
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
onMount(() => {
|
|
322
|
+
if (TUI_DEBUG)
|
|
323
|
+
logResizeDebug("mount", {
|
|
324
|
+
startupSessionName,
|
|
325
|
+
localSessionName: getLocalSessionName(muxCtx),
|
|
326
|
+
muxType: muxCtx.type,
|
|
327
|
+
tmuxPane: process.env.TMUX_PANE ?? null,
|
|
328
|
+
});
|
|
363
329
|
// Refocus the main pane once terminal capability detection finishes.
|
|
364
330
|
// This avoids the race where the server refocuses too early and capability
|
|
365
331
|
// responses leak as garbage text into the main pane.
|
|
@@ -367,7 +333,7 @@ function App() {
|
|
|
367
333
|
const doStartupRefocus = () => {
|
|
368
334
|
if (startupRefocused) return;
|
|
369
335
|
startupRefocused = true;
|
|
370
|
-
refocusMainPane();
|
|
336
|
+
refocusMainPane(muxCtx);
|
|
371
337
|
};
|
|
372
338
|
renderer.on("capabilities", doStartupRefocus);
|
|
373
339
|
// Fallback: if no capability response arrives within 2s, refocus anyway
|
|
@@ -412,6 +378,7 @@ function App() {
|
|
|
412
378
|
setFocusedSession(startupFocus);
|
|
413
379
|
setCurrentSession(msg.currentSession);
|
|
414
380
|
setTheme(resolveTheme(msg.theme));
|
|
381
|
+
if (msg.preferredEditor) setPreferredEditor(msg.preferredEditor);
|
|
415
382
|
} else if (msg.type === "focus") {
|
|
416
383
|
setFocusedSession(msg.focusedSession);
|
|
417
384
|
setCurrentSession(msg.currentSession);
|
|
@@ -421,13 +388,17 @@ function App() {
|
|
|
421
388
|
|
|
422
389
|
if (!startupFocusSynced && sessions.some((session) => session.name === msg.name)) {
|
|
423
390
|
startupFocusSynced = true;
|
|
391
|
+
const oldFocus = focusedSession();
|
|
424
392
|
setFocusedSession(msg.name);
|
|
425
|
-
if (
|
|
393
|
+
if (oldFocus !== msg.name) {
|
|
426
394
|
startupFocusToPublish = msg.name;
|
|
427
395
|
}
|
|
428
396
|
}
|
|
429
397
|
} else if (msg.type === "re-identify") {
|
|
430
398
|
reIdentify();
|
|
399
|
+
} else if (msg.type === "quit") {
|
|
400
|
+
if (ws) ws.close();
|
|
401
|
+
renderer.destroy();
|
|
431
402
|
}
|
|
432
403
|
});
|
|
433
404
|
|
|
@@ -443,17 +414,6 @@ function App() {
|
|
|
443
414
|
};
|
|
444
415
|
|
|
445
416
|
onCleanup(() => socket.close());
|
|
446
|
-
|
|
447
|
-
// Listen for quit messages from server
|
|
448
|
-
socket.addEventListener("message", (event) => {
|
|
449
|
-
try {
|
|
450
|
-
const msg = JSON.parse(event.data as string);
|
|
451
|
-
if (msg.type === "quit") {
|
|
452
|
-
if (ws) ws.close();
|
|
453
|
-
renderer.destroy();
|
|
454
|
-
}
|
|
455
|
-
} catch {}
|
|
456
|
-
});
|
|
457
417
|
});
|
|
458
418
|
|
|
459
419
|
const hasRunning = createMemo(() => sessions.some((s) => s.agentState?.status === "running"));
|
|
@@ -470,19 +430,21 @@ function App() {
|
|
|
470
430
|
const sessionName = detailPanelSessionName();
|
|
471
431
|
if (!sessionName) return;
|
|
472
432
|
const storedHeight = getStoredDetailPanelHeight(sessionName);
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
433
|
+
if (TUI_DEBUG)
|
|
434
|
+
logResizeDebug("loadStoredDetailPanelHeight", {
|
|
435
|
+
session: sessionName,
|
|
436
|
+
storedHeight,
|
|
437
|
+
});
|
|
477
438
|
setDetailPanelHeight(storedHeight);
|
|
478
439
|
});
|
|
479
440
|
|
|
480
441
|
createEffect(() => {
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
442
|
+
if (TUI_DEBUG)
|
|
443
|
+
logResizeDebug("detailPanelHeight:changed", {
|
|
444
|
+
height: detailPanelHeight(),
|
|
445
|
+
session: detailPanelSessionName(),
|
|
446
|
+
isResizing: isDetailResizing(),
|
|
447
|
+
});
|
|
486
448
|
});
|
|
487
449
|
|
|
488
450
|
useKeyboard((key) => {
|
|
@@ -581,9 +543,6 @@ function App() {
|
|
|
581
543
|
case "r":
|
|
582
544
|
send({ type: "refresh" });
|
|
583
545
|
break;
|
|
584
|
-
case "t":
|
|
585
|
-
// reserved — was theme picker
|
|
586
|
-
break;
|
|
587
546
|
case "u":
|
|
588
547
|
send({ type: "show-all-sessions" });
|
|
589
548
|
break;
|
|
@@ -608,8 +567,10 @@ function App() {
|
|
|
608
567
|
}
|
|
609
568
|
break;
|
|
610
569
|
}
|
|
570
|
+
case "e":
|
|
571
|
+
openInEditor();
|
|
572
|
+
break;
|
|
611
573
|
case "n":
|
|
612
|
-
case "c":
|
|
613
574
|
createNewSession();
|
|
614
575
|
break;
|
|
615
576
|
case "?":
|
|
@@ -635,6 +596,7 @@ function App() {
|
|
|
635
596
|
);
|
|
636
597
|
|
|
637
598
|
const unseenCount = createMemo(() => sessions.filter((s) => s.unseen).length);
|
|
599
|
+
const sessionStatusCounts = createMemo(() => computeSessionStatusCounts(sessions));
|
|
638
600
|
|
|
639
601
|
const isFocused = createSelector(focusedSession);
|
|
640
602
|
|
|
@@ -646,6 +608,7 @@ function App() {
|
|
|
646
608
|
runningCount={runningAgentCount()}
|
|
647
609
|
errorCount={errorAgentCount()}
|
|
648
610
|
unseenCount={unseenCount()}
|
|
611
|
+
sessionStatusCounts={sessionStatusCounts()}
|
|
649
612
|
theme={theme}
|
|
650
613
|
/>
|
|
651
614
|
|
|
@@ -722,30 +685,32 @@ function App() {
|
|
|
722
685
|
<Show
|
|
723
686
|
when={panelFocus() === "sessions"}
|
|
724
687
|
fallback={
|
|
725
|
-
<
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
</text>
|
|
688
|
+
<KeyHints
|
|
689
|
+
palette={P}
|
|
690
|
+
hints={[
|
|
691
|
+
["←", "back"],
|
|
692
|
+
["⏎", "focus"],
|
|
693
|
+
["d", "dismiss"],
|
|
694
|
+
["x", "kill"],
|
|
695
|
+
]}
|
|
696
|
+
/>
|
|
735
697
|
}
|
|
736
698
|
>
|
|
737
|
-
<
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
699
|
+
<KeyHints
|
|
700
|
+
palette={P}
|
|
701
|
+
hints={[
|
|
702
|
+
["⇥", "cycle"],
|
|
703
|
+
["⏎", "go"],
|
|
704
|
+
["→", "select"],
|
|
705
|
+
["n", "new"],
|
|
706
|
+
["e", "edit"],
|
|
707
|
+
["d", "hide"],
|
|
708
|
+
["x", "kill"],
|
|
709
|
+
["r", "refresh"],
|
|
710
|
+
["q", "quit"],
|
|
711
|
+
["?", "help"],
|
|
712
|
+
]}
|
|
713
|
+
/>
|
|
749
714
|
</Show>
|
|
750
715
|
</box>
|
|
751
716
|
|
|
@@ -796,24 +761,31 @@ function App() {
|
|
|
796
761
|
|
|
797
762
|
// --- Help Overlay ---
|
|
798
763
|
|
|
764
|
+
const HELP_KEYS: [string, string][] = [
|
|
765
|
+
["j/k ↑↓", "Move focus"],
|
|
766
|
+
["Enter", "Switch to session"],
|
|
767
|
+
["1-9", "Jump to session"],
|
|
768
|
+
["Tab", "Cycle sessions"],
|
|
769
|
+
["n", "New session"],
|
|
770
|
+
["e", "Open in editor"],
|
|
771
|
+
["d", "Hide session"],
|
|
772
|
+
["x", "Kill session"],
|
|
773
|
+
["r", "Refresh"],
|
|
774
|
+
["u", "Show all sessions"],
|
|
775
|
+
["→/l", "Select panel"],
|
|
776
|
+
["←/h/Esc", "Back to sessions"],
|
|
777
|
+
["Alt+↑↓", "Reorder sessions"],
|
|
778
|
+
["q", "Quit"],
|
|
779
|
+
];
|
|
780
|
+
|
|
781
|
+
const HELP_COLS = 2;
|
|
782
|
+
const HELP_ROWS = Math.ceil(HELP_KEYS.length / HELP_COLS);
|
|
783
|
+
const HELP_COLUMNS = Array.from({ length: HELP_COLS }, (_, c) =>
|
|
784
|
+
HELP_KEYS.slice(c * HELP_ROWS, (c + 1) * HELP_ROWS),
|
|
785
|
+
);
|
|
786
|
+
|
|
799
787
|
function HelpOverlay(props: { palette: Accessor<Theme["palette"]>; onClose: () => void }) {
|
|
800
788
|
const P = () => props.palette();
|
|
801
|
-
const keys: [string, string][] = [
|
|
802
|
-
["j/k ↑↓", "Move focus"],
|
|
803
|
-
["Enter", "Switch to session"],
|
|
804
|
-
["1-9", "Jump to session"],
|
|
805
|
-
["Tab", "Cycle sessions"],
|
|
806
|
-
["n/c", "New session"],
|
|
807
|
-
["d", "Hide session"],
|
|
808
|
-
["x", "Kill session"],
|
|
809
|
-
["r", "Refresh"],
|
|
810
|
-
["u", "Show all sessions"],
|
|
811
|
-
["→/l", "Detail panel"],
|
|
812
|
-
["←/h/Esc", "Back to sessions"],
|
|
813
|
-
["Alt+↑↓", "Reorder sessions"],
|
|
814
|
-
["q", "Quit"],
|
|
815
|
-
];
|
|
816
|
-
|
|
817
789
|
return (
|
|
818
790
|
<box
|
|
819
791
|
position="absolute"
|
|
@@ -832,7 +804,7 @@ function HelpOverlay(props: { palette: Accessor<Theme["palette"]>; onClose: () =
|
|
|
832
804
|
backgroundColor={P().mantle}
|
|
833
805
|
padding={1}
|
|
834
806
|
flexDirection="column"
|
|
835
|
-
width={
|
|
807
|
+
width={56}
|
|
836
808
|
>
|
|
837
809
|
<text>
|
|
838
810
|
<span style={{ fg: P().blue, attributes: BOLD }}>Keybindings</span>
|
|
@@ -840,20 +812,28 @@ function HelpOverlay(props: { palette: Accessor<Theme["palette"]>; onClose: () =
|
|
|
840
812
|
<box height={1}>
|
|
841
813
|
<text style={{ fg: P().surface2 }}>{DIVIDER}</text>
|
|
842
814
|
</box>
|
|
843
|
-
<
|
|
844
|
-
{
|
|
845
|
-
|
|
846
|
-
<box
|
|
847
|
-
<
|
|
848
|
-
|
|
849
|
-
|
|
815
|
+
<box flexDirection="row">
|
|
816
|
+
<For each={HELP_COLUMNS}>
|
|
817
|
+
{(col) => (
|
|
818
|
+
<box flexDirection="column" flexGrow={1}>
|
|
819
|
+
<For each={col}>
|
|
820
|
+
{([key, desc]) => (
|
|
821
|
+
<box flexDirection="row" paddingLeft={1}>
|
|
822
|
+
<box width={12} flexShrink={0}>
|
|
823
|
+
<text>
|
|
824
|
+
<span style={{ fg: P().sky }}>{key}</span>
|
|
825
|
+
</text>
|
|
826
|
+
</box>
|
|
827
|
+
<text truncate>
|
|
828
|
+
<span style={{ fg: P().subtext0 }}>{desc}</span>
|
|
829
|
+
</text>
|
|
830
|
+
</box>
|
|
831
|
+
)}
|
|
832
|
+
</For>
|
|
850
833
|
</box>
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
</box>
|
|
855
|
-
)}
|
|
856
|
-
</For>
|
|
834
|
+
)}
|
|
835
|
+
</For>
|
|
836
|
+
</box>
|
|
857
837
|
<box height={1}>
|
|
858
838
|
<text style={{ fg: P().surface2 }}>{DIVIDER}</text>
|
|
859
839
|
</box>
|