@towles/tool 0.0.130 → 0.0.131

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@towles/tool",
3
- "version": "0.0.130",
3
+ "version": "0.0.131",
4
4
  "description": "One off quality of life scripts that I use on a daily basis.",
5
5
  "homepage": "https://github.com/ChrisTowles/towles-tool#readme",
6
6
  "bugs": {
@@ -1,6 +1,6 @@
1
1
  import { createSignal, For, Show, onCleanup } from "solid-js";
2
2
  import type { Accessor } from "solid-js";
3
- import type { AgentStatus, SessionData, Theme } from "@tt-agentboard/runtime";
3
+ import type { SessionData, Theme } from "@tt-agentboard/runtime";
4
4
  import { truncate } from "@tt-agentboard/runtime";
5
5
  import { UNSEEN_ICON, BOLD, DIM, toneColor } from "../constants";
6
6
  import { DiffStats } from "./DiffStats";
@@ -9,16 +9,6 @@ import { formatElapsed } from "./elapsed";
9
9
  import { liveStatusIcon, unseenTerminalColor } from "./status-visuals";
10
10
  import { familyColor } from "./family-color";
11
11
 
12
- const STATUS_TEXT: Record<AgentStatus, string> = {
13
- idle: "",
14
- running: "running",
15
- done: "done",
16
- error: "error",
17
- waiting: "waiting",
18
- question: "question",
19
- interrupted: "stopped",
20
- };
21
-
22
12
  export interface SessionCardProps {
23
13
  session: SessionData;
24
14
  isFocused: boolean;
@@ -77,7 +67,7 @@ export function SessionCard(props: SessionCardProps) {
77
67
  };
78
68
 
79
69
  const truncName = () => truncate(props.session.name, 18);
80
- const truncBranch = () => (props.session.branch ? truncate(props.session.branch, 30) : "");
70
+ const truncBranch = () => (props.session.branch ? truncate(props.session.branch, 45) : "");
81
71
 
82
72
  const hasDiff = () => {
83
73
  const { linesAdded, linesRemoved, commitsDelta, filesChanged } = props.session;
@@ -128,7 +118,7 @@ export function SessionCard(props: SessionCardProps) {
128
118
  </Show>
129
119
 
130
120
  <box flexDirection="column" flexGrow={1} paddingRight={1}>
131
- <box flexDirection="row">
121
+ <box flexDirection="row" height={1}>
132
122
  <text truncate flexGrow={1}>
133
123
  <span
134
124
  style={{
@@ -139,25 +129,32 @@ export function SessionCard(props: SessionCardProps) {
139
129
  {truncName()}
140
130
  </span>
141
131
  </text>
142
- <Show when={statusIcon()}>
143
- <text flexShrink={0}>
144
- <span style={{ fg: statusColor() }}>
145
- {" "}
146
- {statusIcon()}
147
- {runningAgents() > 1 ? String(runningAgents()) : ""}
148
- </span>
149
- </text>
132
+ <Show when={hasDiff()}>
133
+ <box flexShrink={0} paddingLeft={1}>
134
+ <DiffStats session={props.session} palette={() => P()} />
135
+ </box>
150
136
  </Show>
137
+ <box width={3} flexShrink={0}>
138
+ <Show when={statusIcon()}>
139
+ <text>
140
+ <span style={{ fg: statusColor() }}>
141
+ {" "}
142
+ {statusIcon()}
143
+ {runningAgents() > 1 ? String(runningAgents()) : ""}
144
+ </span>
145
+ </text>
146
+ </Show>
147
+ </box>
151
148
  </box>
152
149
 
153
150
  <Show when={props.session.branch}>
154
- <text truncate>
155
- <span style={{ fg: props.isFocused ? P().pink : P().overlay0 }}>{truncBranch()}</span>
156
- </text>
157
- </Show>
158
-
159
- <Show when={hasDiff()}>
160
- <DiffStats session={props.session} palette={() => P()} />
151
+ <box flexDirection="row" height={1}>
152
+ <text truncate flexShrink={1}>
153
+ <span style={{ fg: props.isFocused ? P().pink : P().overlay0 }}>
154
+ {truncBranch()}
155
+ </span>
156
+ </text>
157
+ </box>
161
158
  </Show>
162
159
 
163
160
  <Show when={metaSummary()}>
@@ -184,8 +181,6 @@ export function SessionCard(props: SessionCardProps) {
184
181
  </For>
185
182
  </box>
186
183
  </box>
187
-
188
- <box height={1} />
189
184
  </box>
190
185
  );
191
186
  }
@@ -230,15 +225,15 @@ function AgentRow(props: AgentRowProps) {
230
225
  return SC()[props.agent.status];
231
226
  };
232
227
 
233
- const statusText = () => STATUS_TEXT[props.agent.status];
234
-
235
- const isCacheExpired = () => {
228
+ const cacheLabel = () => {
236
229
  const details = props.agent.details;
237
- if (!details) return false;
238
- const now = props.now();
239
- if (details.cacheExpiresAt != null) return now > details.cacheExpiresAt;
240
- if (details.lastActivityAt != null) return now - details.lastActivityAt > 60 * 60 * 1000;
241
- return false;
230
+ if (!details) return null;
231
+ const expiresAt =
232
+ details.cacheExpiresAt ??
233
+ (details.lastActivityAt != null ? details.lastActivityAt + 60 * 60 * 1000 : null);
234
+ if (expiresAt == null) return null;
235
+ const minutesLeft = Math.ceil((expiresAt - props.now()) / 60_000);
236
+ return minutesLeft <= 0 ? "cache expired" : `cache ${minutesLeft}m`;
242
237
  };
243
238
 
244
239
  let flashTimer: ReturnType<typeof setTimeout> | null = null;
@@ -268,10 +263,20 @@ function AgentRow(props: AgentRowProps) {
268
263
  props.onFocusPane();
269
264
  }}
270
265
  >
271
- <box flexDirection="row">
272
- <text flexGrow={1} truncate>
266
+ <box flexDirection="row" height={1}>
267
+ <text flexShrink={0}>
273
268
  <span style={{ fg: color() }}>{icon()}</span>
274
- <Show when={props.agent.status === "running" && props.agent.details?.lastActivityAt}>
269
+ </text>
270
+ <text flexGrow={1} flexShrink={1} truncate>
271
+ <Show when={props.agent.threadName}>
272
+ <span style={{ fg: isUnseen() ? color() : P().overlay0 }}>
273
+ {" "}
274
+ {truncate(props.agent.threadName!.replace(/\s+/g, " ").trim(), 40)}
275
+ </span>
276
+ </Show>
277
+ </text>
278
+ <Show when={props.agent.status === "running" && props.agent.details?.lastActivityAt}>
279
+ <text flexShrink={0}>
275
280
  <span
276
281
  style={{
277
282
  fg: props.isKeyboardFocused ? P().subtext0 : P().overlay1,
@@ -281,11 +286,6 @@ function AgentRow(props: AgentRowProps) {
281
286
  {" "}
282
287
  {formatElapsed(props.now() - (props.agent.details?.lastActivityAt ?? props.now()))}
283
288
  </span>
284
- </Show>
285
- </text>
286
- <Show when={!isUnseen()}>
287
- <text flexShrink={0}>
288
- <span style={{ fg: color(), attributes: DIM }}>{statusText()}</span>
289
289
  </text>
290
290
  </Show>
291
291
  <text
@@ -302,16 +302,6 @@ function AgentRow(props: AgentRowProps) {
302
302
  </text>
303
303
  </box>
304
304
 
305
- <Show when={props.agent.threadName}>
306
- <box height={2} flexShrink={0}>
307
- <text>
308
- <span style={{ fg: isUnseen() ? color() : P().overlay0 }}>
309
- {truncate(props.agent.threadName!.replace(/\s+/g, " ").trim(), 60)}
310
- </span>
311
- </text>
312
- </box>
313
- </Show>
314
-
315
305
  <Show when={props.agent.status === "running" && props.agent.details}>
316
306
  {(d) => {
317
307
  const details = d();
@@ -334,9 +324,9 @@ function AgentRow(props: AgentRowProps) {
334
324
  }}
335
325
  </Show>
336
326
 
337
- <Show when={isCacheExpired()}>
327
+ <Show when={cacheLabel()}>
338
328
  <text truncate>
339
- <span style={{ fg: P().overlay0, attributes: DIM }}>cache expired</span>
329
+ <span style={{ fg: P().overlay0, attributes: DIM }}>{cacheLabel()}</span>
340
330
  </text>
341
331
  </Show>
342
332
  </box>
@@ -555,43 +555,53 @@ function App() {
555
555
  />
556
556
 
557
557
  {/* Session list */}
558
- <scrollbox flexGrow={1} flexShrink={1} paddingTop={1}>
558
+ <scrollbox flexGrow={1} flexShrink={1} paddingTop={0}>
559
+ <box height={1}>
560
+ <text style={{ fg: P().overlay0 }}>{DIVIDER}</text>
561
+ </box>
559
562
  <For each={sessions}>
560
563
  {(session, i) => (
561
- <SessionCard
562
- session={session}
563
- isFocused={isFocused(session.name)}
564
- isCurrent={session.name === currentSession()}
565
- spinIdx={spinIdx}
566
- now={now}
567
- theme={theme}
568
- statusColors={S}
569
- focusedAgentIdx={
570
- isFocused(session.name) && panelFocus() === "agents" ? focusedAgentIdx() : -1
571
- }
572
- onSelect={() => {
573
- setFocusedSession(session.name);
574
- send({ type: "focus-session", name: session.name });
575
- switchToSession(session.name);
576
- }}
577
- onDismissAgent={(agent) => {
578
- send({
579
- type: "dismiss-agent",
580
- session: session.name,
581
- agent: agent.agent,
582
- threadId: agent.threadId,
583
- });
584
- }}
585
- onFocusAgentPane={(agent) => {
586
- send({
587
- type: "focus-agent-pane",
588
- session: session.name,
589
- agent: agent.agent,
590
- threadId: agent.threadId,
591
- threadName: agent.threadName,
592
- });
593
- }}
594
- />
564
+ <box flexDirection="column" flexShrink={0}>
565
+ <Show when={i() > 0}>
566
+ <box height={1}>
567
+ <text style={{ fg: P().surface2 }}>{DIVIDER}</text>
568
+ </box>
569
+ </Show>
570
+ <SessionCard
571
+ session={session}
572
+ isFocused={isFocused(session.name)}
573
+ isCurrent={session.name === currentSession()}
574
+ spinIdx={spinIdx}
575
+ now={now}
576
+ theme={theme}
577
+ statusColors={S}
578
+ focusedAgentIdx={
579
+ isFocused(session.name) && panelFocus() === "agents" ? focusedAgentIdx() : -1
580
+ }
581
+ onSelect={() => {
582
+ setFocusedSession(session.name);
583
+ send({ type: "focus-session", name: session.name });
584
+ switchToSession(session.name);
585
+ }}
586
+ onDismissAgent={(agent) => {
587
+ send({
588
+ type: "dismiss-agent",
589
+ session: session.name,
590
+ agent: agent.agent,
591
+ threadId: agent.threadId,
592
+ });
593
+ }}
594
+ onFocusAgentPane={(agent) => {
595
+ send({
596
+ type: "focus-agent-pane",
597
+ session: session.name,
598
+ agent: agent.agent,
599
+ threadId: agent.threadId,
600
+ threadName: agent.threadName,
601
+ });
602
+ }}
603
+ />
604
+ </box>
595
605
  )}
596
606
  </For>
597
607
  </scrollbox>
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "tt",
3
3
  "description": "Core dev workflow commands and skills: interview-me, write-prd, prd-to-issues, tdd, improve-architecture, refine-text, task, parallel-slots, towles-tool.",
4
- "version": "0.0.130",
4
+ "version": "0.0.131",
5
5
  "author": {
6
6
  "name": "Chris Towles"
7
7
  }