Skip to content

dev-dashboard: async tmux core (sub-20ms endpoints), unified tmux/ttyd naming, tools update marketplace fix - #307

Open
genesiscz wants to merge 6 commits into
masterfrom
feat/fixes-2026-08-03
Open

dev-dashboard: async tmux core (sub-20ms endpoints), unified tmux/ttyd naming, tools update marketplace fix#307
genesiscz wants to merge 6 commits into
masterfrom
feat/fixes-2026-08-03

Conversation

@genesiscz

@genesiscz genesiscz commented Aug 2, 2026

Copy link
Copy Markdown
Owner

What

Three workstreams that shipped together from the same working tree:

1. One tmux/ttyd identity (feat)

  • Claude /rename writes only the tmux pane title (✳ name); the manager now mirrors it onto the real tmux session name and the ttyd display label, so Session Hub rows and terminal tabs stay in sync.
  • Hub sessions now carry full ttydTabs (id, port, label, cwd, lastCommand) instead of bare ids; label derivation is shared (deriveTtydDisplayName) so tab chrome and hub rows can never diverge.
  • Shorter dd-* / dd-cmux-* tmux session names; tools tmux sessions gets a tree format and tests.

2. tools update marketplace fix

claude plugin marketplace add <github-url> OVERWRITES an existing registration, so every tools update silently re-pointed the local directory: marketplace at a fresh GitHub clone (local skill edits stopped taking effect until pushed). Now: add only when missing (pointing at the local checkout), always marketplace update before install (the cached catalog is never re-read on its own), and always run plugin update (install is a no-op when already installed; the old terminal branch could never advance the version).

3. Async tmux core (perf)

Bun.spawnSync blocks the whole Bun event loop, so one slow tmux call froze every in-flight HTTP request in the dev-dashboard (HAR: a 7.3s spawn made an unrelated 200ms poll take 6.7s). Every tmux call now rides one async Bun.spawn chokepoint with the same 10s wedge timeout; multi-command sequences batch into single tmux invocations via ; argv separators (17 round-trips to 1); server-global option pinning is TTL-cached; listTtyd does ONE tmux list per poll serving heal + rename-sync + lastCommand (was 12+ per request via a per-binding sessionExists storm); the remaining sync ps probes went async. Route adapters time every endpoint under PROFILE=route,ttyd,tmux.

Measured (live agent, 21 tmux sessions)

Endpoint Before (HAR) After
GET /api/tmux/sessions 3.4-6.7s 8-10ms
POST /api/ttyd/spawn 7.3-7.8s 20ms
POST /api/ttyd/kill 2.7-6.8s 10ms
concurrent poll during spawn 6.7s (starved) 9-13ms
20 simultaneous polls n/a max 185ms

Verification

  • tsgo --noEmit: 0 errors
  • bun run test over src/utils/tmux, src/dev-dashboard, src/tmux, src/utils/cmux: green (627 tests in the affected trees; 2445 pass full-tree with 4 load-flaky fails that also fail without this change and pass in isolation: du clone-layout x3, cmux parallel-fetch x1)
  • Sync test doubles keep working: the TmuxSpawnSync seam was widened to allow Promise returns instead of being replaced.

Summary by CodeRabbit

  • New Features

    • Added richer tmux session details, including ttyd ports, labels, working directories, recent commands, activity times, and Claude topics.
    • Added user-defined display names shared between tmux sessions and ttyd tabs.
    • Claude session titles and rename actions now synchronize across the dashboard.
    • Enhanced plugin updates to use the current checkout when available.
  • Bug Fixes

    • Improved rename, restore, create, and reset reliability.
    • Filtered stale processes and noisy shell-history output from session details.
    • Improved session naming and fallback labels for clearer identification.

…refresh catalog before install, always run plugin update
…nblock the server event loop, seed profiling
@eve-bot-lovinka

eve-bot-lovinka Bot commented Aug 2, 2026

Copy link
Copy Markdown

🐉 eve review — ✅ APPROVE · 5 findings

review · run

  • Queued 16:29:17Z
  • Reading diff — 8 files
  • Building repo map
  • Analyzing (find → verify) — 7 candidates → 5 survivors
  • Posting review
  • Review posted 16:32:30Z (3m 13s)
Previous runs (3)
run head outcome findings took
3 c2035c8 ✅ APPROVE 5 8m 34s
2 74e7b08 ✅ APPROVE 0 1m 50s
1 e6445dc 🔴 REQUEST_CHANGES 8 4m 49s

@coderabbitai

coderabbitai Bot commented Aug 2, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

The change unifies tmux and ttyd session identity, adds structured ttyd and pane metadata, converts tmux operations to asynchronous execution, updates dashboard and CLI views, adds profiling, filters snapshot commands, and revises Claude plugin updates.

Changes

Unified session identity and dashboard synchronization

Layer / File(s) Summary
Session identity and metadata contracts
src/dev-dashboard/config.ts, src/dev-dashboard/contract/dto.ts, src/dev-dashboard/lib/tmux/*, src/dev-dashboard/lib/ttyd/*, src/utils/tmux/types.ts, src/dev-dashboard/ui/src/lib/api.ts
Persisted names, display-name rules, tmux names, pane metadata, Claude topics, and ttyd tab data now use shared contracts.
TTYD lifecycle and rename synchronization
src/dev-dashboard/lib/ttyd/manager.*, src/dev-dashboard/lib/tmux/rename.ts
TTYD lifecycle operations use asynchronous tmux checks. Claude topics and explicit renames synchronize tmux bindings and ttyd display names.
Dashboard routes and interface updates
src/dev-dashboard/server/**, src/dev-dashboard/ui/src/**
Routes await asynchronous operations and profile handlers. The UI displays tmux and ttyd metadata and handles unified rename behavior.

Asynchronous tmux operations and session views

Layer / File(s) Summary
Asynchronous tmux runner and session APIs
src/utils/tmux/sessions.ts, src/utils/tmux/types.ts
Tmux execution accepts promises and exposes asynchronous session, pane, rename, persistence, and scroll APIs.
TTYD-aware session listing and formatting
src/tmux/commands/sessions*.ts
The sessions command loads live ttyd bindings and renders tab ports, labels, commands, panes, and JSON metadata.
Snapshot parsing and session restoration
src/utils/tmux/snapshot*.ts, src/utils/tmux/reset.ts, src/dev-dashboard/lib/tmux/presets.ts
Snapshot parsing filters terminal artifacts. Restoration, cleanup, preset restoration, and reset operations await tmux actions.
Command and integration call sites
src/tmux/commands/{create.ts,presets/restore.ts,session.ts}, src/utils/cmux/send-tmux.ts
Tmux create, restore, attach, reset, preset, and cmux call sites await asynchronous operations.
Asynchronous tmux and snapshot validation
src/utils/tmux/*.test.ts, src/tmux/commands/sessions.test.ts
Tests cover asynchronous APIs, active-pane metadata, session operations, scrolling, formatting, and command plausibility.

Claude plugin marketplace update

Layer / File(s) Summary
Marketplace discovery and plugin update
src/update/index.ts
The update command selects marketplace sources and conditionally installs, reinstalls, or updates the plugin.

Estimated code review effort: 5 (Critical) | ~120 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Pane as tmux active pane
  participant Manager as ttyd manager
  participant Tmux as tmux session
  participant Registry as ttyd registry
  Pane->>Manager: provide Claude command and pane title
  Manager->>Manager: parse title and resolve display name
  Manager->>Tmux: rename session
  Manager->>Registry: update ttyd name and binding
  Registry-->>Manager: persist synchronized session state
Loading

Possibly related PRs

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 32.18% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the PR's three main workstreams: async tmux core changes, unified tmux/ttyd naming, and the marketplace fix.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/fixes-2026-08-03

Comment @coderabbitai help to get the list of available commands.

@eve-bot-lovinka eve-bot-lovinka Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🐉 eve review — 🔴 Changes requested

e6445dc · 8 actionable findings · view run ↗

Severity Count
🟠 High 2
🟡 Medium 2
🔵 Low 4

Comment thread src/update/index.ts
// edits in this repo stop taking effect until they are committed AND pushed.
const checkout = resolve(genesisPath);
const isCheckout = existsSync(join(checkout, ".claude-plugin", "marketplace.json"));
const existing = await findMarketplace(MARKETPLACE_NAME);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Quality | 🟠 High · confidence 99/100

⚠️ Potential issue

Marketplace lookup failure is treated as marketplace absence

findMarketplace() returns null both when the marketplace is absent and when claude ... list --json fails or produces unparseable output. On a valid checkout, that makes !pointsAtCheckout true and executes marketplace add, whose documented behavior here is to overwrite an existing registration. A transient CLI/query failure can therefore trigger the exact silent re-pointing this change is intended to prevent. Preserve an error/unknown state separately and abort marketplace mutation unless absence was positively established.

🧩 Analysis

Grep evidence: return null;|const existing = await findMarketplace|isCheckout && !pointsAtCheckout

type TmuxSpawnResult,
type TmuxSpawnSync,
} from "@genesiscz/utils/tmux/sessions";

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚡ Performance | 🟠 High · confidence 99/100

⚠️ Potential issue

Snapshot calls still block dashboard HTTP handlers

The new comment says snapshot capture stays synchronous because it runs in CLI flows, but src/dev-dashboard/server/routes/tmux-presets.ts calls savePreset(body) from an HTTP handler, which calls captureTmuxSnapshot(). That function performs Bun.spawnSync for list-panes, then serial capture-pane and ps calls per pane. Thus POST /api/tmux/presets/save can still block the entire Bun event loop, contradicting this PR's async-tmux goal and becoming increasingly expensive with pane count. Either make snapshot capture async through the shared tmux chokepoint or move the blocking work off the server event loop.

🧩 Analysis

Grep evidence: savePreset\(body\)|captureTmuxSnapshot\(|Bun\.spawnSync

const sessions = enrichSessionsForHub(listTmuxSessions(), await listTtyd(), cmuxBySession);
// Argument order preserved: `listTtyd()` can rename tmux sessions mid-call, and
// this list is deliberately the pre-rename snapshot.
const tmuxSessions = await prof.measureAsync("tmux.listSessions", () => listTmuxSessions());

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Quality | 🟡 Medium · confidence 99/100

⚠️ Potential issue

Pre-rename snapshot returns an internally stale session list

The route deliberately fetches tmuxSessions before listTtyd(), but listTtyd() can rename session A to B. Enrichment then receives a tmux row named A and a ttyd binding named B: A is returned as attachable with no ttyd tab, while B is absent until a later poll. This makes the response that performed the synchronization immediately inconsistent. Run listTtyd() first and list tmux afterward, or have the synchronization return/apply rename mappings to the snapshot.

🧩 Analysis

Grep evidence: listTmuxSessions\(\)|listTtyd\(\)|listTyd\(\) can rename|listTtyd\(\) can rename

Comment thread src/update/index.ts
// by version — so `plugin update` is a no-op whenever the version is unchanged and
// the stale copy would survive the flip. Reinstall to re-copy from this checkout.
out.println(pc.dim("\n Reinstalling plugin from this checkout..."));
await runClaude(["plugin", "uninstall", PLUGIN_REF, "--keep-data", "-y"]);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Quality | 🟡 Medium · confidence 99/100

⚠️ Potential issue

Repointing can remove the working plugin before replacement succeeds

The repointed path uninstalls the currently working plugin and ignores that command's result before attempting installation from the new source. If marketplace refresh/add failed (their exit codes are also ignored) or the install fails, tools update leaves the user with no plugin installed. Validate every prerequisite and stage a successful replacement where supported; at minimum, stop on add/update/uninstall failures and provide rollback/recovery rather than continuing to print overall update success.

🧩 Analysis

Grep evidence: marketplace.*update|plugin", "uninstall"|plugin", "install"

existing.push(binding);
map.set(session.tmuxSessionName, existing);
}
} catch {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Quality | 🔵 Low · confidence 98/100

⚠️ Potential issue

Config failures are silently swallowed

The new bare catch hides malformed/unreadable dashboard config and process-probe failures, making missing ttyd branches impossible to diagnose. Project review memory explicitly rejects bare or silent catches and requires logging the error plus operational context. Capture the error and log it at debug level while retaining the bare-tmux fallback.

🧩 Analysis

Grep evidence: catch \{|Dashboard config missing or unreadable

import { Link } from "@tanstack/react-router";
import type { ReactNode } from "react";
import { useState } from "react";
import { useRef, useState } from "react";

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧪 Tests | 🔵 Low · confidence 65/100

⚠️ Potential issue

No test changes accompany 26 added lines in src/dev-dashboard/ui/src/components/terminal-shell/MobileTerminalShell.tsx

This PR adds 26 lines to src/dev-dashboard/ui/src/components/terminal-shell/MobileTerminalShell.tsx with no touching test change (no changed test names MobileTerminalShell and none under src/dev-dashboard/ui/src/components/terminal-shell/). If the change alters behavior, add or extend a test that pins it (deterministic static check — ignore if the change is genuinely untestable or covered elsewhere).

🧩 Analysis

Grep evidence: MobileTerminalShell

<GlassDialogTitle className="font-mono text-lg">Tmux sessions</GlassDialogTitle>
<GlassDialogDescription className="font-mono text-xs text-zinc-400">
Attach in ttyd or send to cmux — shared tmux I/O across surfaces.
Same names as the terminal tabs — rename once, tmux + ttyd both update. Attach in ttyd

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧪 Tests | 🔵 Low · confidence 65/100

⚠️ Potential issue

No test changes accompany 37 added lines in src/dev-dashboard/ui/src/components/TmuxSessionsPanel.tsx

This PR adds 37 lines to src/dev-dashboard/ui/src/components/TmuxSessionsPanel.tsx with no touching test change (no changed test names TmuxSessionsPanel and none under src/dev-dashboard/ui/src/components/). If the change alters behavior, add or extend a test that pins it (deterministic static check — ignore if the change is genuinely untestable or covered elsewhere).

🧩 Analysis

Grep evidence: TmuxSessionsPanel

Comment thread src/update/index.ts
import { runTool } from "@genesiscz/utils/cli";
import { env } from "@genesiscz/utils/env";
import { out } from "@genesiscz/utils/logger";
import { SafeJSON } from "@genesiscz/utils/json";

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧪 Tests | 🔵 Low · confidence 65/100

⚠️ Potential issue

No test changes accompany 106 added lines in src/update/index.ts

This PR adds 106 lines to src/update/index.ts with no touching test change (no changed test names index and none under src/update/). If the change alters behavior, add or extend a test that pins it (deterministic static check — ignore if the change is genuinely untestable or covered elsewhere).

🧩 Analysis

Grep evidence: index

@eve-bot-lovinka

Copy link
Copy Markdown

Review completed and posted.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 17

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/utils/tmux/sessions.test.ts (1)

170-195: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Assert on chain segments, not on token membership in the whole argv.

ensureTmuxServerPersists and ensureTmuxSessionEnvironment now pack nine and N sub-commands into one argv separated by ";". Every assertion here tests only that the tokens appear somewhere in that flat array. The exit-empty / off check passes even if off belongs to a different sub-command in the chain. The tests no longer prove the pairing they name.

Split each argv on ";" first, then assert on the individual sub-commands.

💚 Proposed helper
+        const subCommands = calls.flatMap((cmd) =>
+            cmd.reduce<string[][]>(
+                (acc, token) => {
+                    if (token === ";") {
+                        acc.push([]);
+                    } else {
+                        acc[acc.length - 1]?.push(token);
+                    }
+
+                    return acc;
+                },
+                [[]]
+            )
+        );
+
         expect(
-            calls.some((cmd) => cmd.includes("set-option") && cmd.includes("exit-empty") && cmd.includes("off"))
+            subCommands.some(
+                (cmd) => cmd[0] === "set-option" && cmd.includes("exit-empty") && cmd.at(-1) === "off"
+            )
         ).toBe(true);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/utils/tmux/sessions.test.ts` around lines 170 - 195, Update the tmux
command assertions in the affected test to split each flattened argv at ";" and
inspect individual sub-command segments. Ensure the exit-empty assertion
verifies both options occur in the same set-option segment, and the
set-environment assertions verify their named variable/value pairs within one
segment rather than across the entire argv.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/dev-dashboard/contract/dto.ts`:
- Around line 117-123: Define and export a named TtydHubTab DTO for the existing
ttyd tab shape in the contract module, then update the ttydTabs declarations in
the hub module and UI API module to import and reuse TtydHubTab instead of
duplicating the inline object type.

In `@src/dev-dashboard/lib/ttyd/manager.ts`:
- Around line 706-747: In syncNamesFromClaudePaneTitles, replace the
single-entry update of tracked.session.name and its duplicated persistence logic
with the existing syncTtydDisplayNamesForTmux helper after the tmux rename,
passing the relevant tmux session name and toName so every bound ttyd entry is
updated consistently. Preserve the candidate lookup and rename flow, and avoid
retaining the tracked-only assignment.

In `@src/dev-dashboard/ui/src/components/terminal-shell/MobileTerminalShell.tsx`:
- Around line 53-67: Remove the stale blur-suppression mechanism from the rename
input flow: eliminate skipBlurCommitRef usage in the Enter/Escape handlers and
onBlur logic, or reset it whenever a new edit input opens. Ensure normal
subsequent blurs still commit the rename through the existing onBlur path.
- Around line 53-55: Update the Enter-key rename handler in MobileTerminalShell
to trim the submitted value, reject empty values, and reject names unchanged
from the current tab name before invoking onRename. Preserve the existing blur
behavior and only clear editing state after a valid rename is handled.

In `@src/dev-dashboard/ui/src/lib/terminal-tabs.test.ts`:
- Around line 5-13: Refactor the test helper s to accept a single object
parameter containing the session fields and optional overrides, then update
every s call site to pass named properties instead of positional arguments.
Preserve the existing TtydSession defaults and override behavior.

In `@src/tmux/commands/presets/restore.ts`:
- Around line 62-67: Replace repeated sessionExists subprocess calls with one
shared live-session name set: in src/tmux/commands/presets/restore.ts lines
62-67, fetch the names once before the plan loop and use Set membership for
targetName; in src/utils/tmux/sessions.ts lines 416-432, update
renameTmuxSession to fetch the set once and check both fromName and trimmed
against it.

In `@src/tmux/commands/sessions-format.ts`:
- Around line 22-34: Remove the duplicate formatting logic from
formatTtydBranchForTest and consolidate it with formatTtydBranch. Refactor the
shared formatter to accept injectable styling parameters or a style object so
production can use picocolors while tests use unstyled output, then update
callers and assertions to use this single implementation.

In `@src/tmux/commands/sessions.ts`:
- Around line 32-42: Update the stale-session check in the sessions listing flow
to replace the synchronous Bun.spawnSync kill probe with
process.kill(session.pid, 0). Preserve the existing positive numeric PID guard
and continue behavior when the existence probe indicates the ttyd process is
gone; handle the expected process.kill failure without introducing a subprocess.
- Around line 54-56: Update the catch block in the sessions command around
getConfig and the session-processing loop to capture the error and log it with
context using the logger imported from `@genesiscz/utils/logger`, at debug or
warning level, instead of silently swallowing failures.
- Around line 44-52: Update the binding construction in loadTtydBindingsByTmux
to populate lastCommand from the live listTtyd() result whenever command display
is required, ensuring formatTtydBranch() receives current command data. If live
command data is not intended to be loaded, remove lastCommand from
TtydSessionBinding and delete the corresponding formatter branch in
formatTtydBranch.

In `@src/update/index.ts`:
- Around line 13-16: Update the “Did you know” section to use the existing
MARKETPLACE_FALLBACK_SOURCE and PLUGIN_REF constants instead of repeating the
marketplace URL and plugin reference string literals, while preserving the
current rendered content.
- Around line 18-24: Update the MarketplaceEntry.source property to use the
literal union "directory" | "git" | "github" instead of string, removing the
redundant documenting comment while preserving its optionality and the existing
comparison behavior.
- Around line 39-44: Update the SafeJSON.parse call in the subprocess-output
parsing try/catch to pass the strict parsing option ({ strict: true }). Keep the
existing error logging through logger.debug and null return behavior unchanged.
- Around line 135-166: Check the return codes from every marketplace mutation in
the update flow: both `marketplace add` branches, `marketplace update`, and the
`plugin uninstall` call in the `repointed` branch. Reuse the existing
failure-handling pattern used by the checked install/reinstall/update commands,
and stop or propagate the failure before proceeding with dependent plugin
operations.
- Around line 26-45: Handle Bun.spawn failures for the missing claude executable
in the shared CLI execution flow: update runClaude to catch spawn errors and
return a nonzero status, and update claudeJson to catch spawn errors and return
null. Keep existing exit-code and JSON parsing behavior unchanged when spawning
succeeds.

In `@src/utils/tmux/snapshot.ts`:
- Around line 300-310: Rename the constant PROMPT_REMANT_RE to PROMPT_REMNANT_RE
and update every reference to it, including within extractPromptCommand, without
changing the matching behavior.
- Around line 85-91: Update setTmuxSnapshotSpawnForTests to forward impl
directly to setTmuxSpawnSyncForTests without the redundant satisfies clause.
Remove the TmuxSpawnSync import if it is no longer referenced elsewhere in the
file.

---

Outside diff comments:
In `@src/utils/tmux/sessions.test.ts`:
- Around line 170-195: Update the tmux command assertions in the affected test
to split each flattened argv at ";" and inspect individual sub-command segments.
Ensure the exit-empty assertion verifies both options occur in the same
set-option segment, and the set-environment assertions verify their named
variable/value pairs within one segment rather than across the entire argv.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 1256676e-818c-4a15-b263-c3d517ea1d7f

📥 Commits

Reviewing files that changed from the base of the PR and between 5758fe8 and e6445dc.

📒 Files selected for processing (41)
  • src/dev-dashboard/config.ts
  • src/dev-dashboard/contract/dto.ts
  • src/dev-dashboard/lib/tmux/claude-pane-title.test.ts
  • src/dev-dashboard/lib/tmux/claude-pane-title.ts
  • src/dev-dashboard/lib/tmux/create-session.ts
  • src/dev-dashboard/lib/tmux/hub.test.ts
  • src/dev-dashboard/lib/tmux/hub.ts
  • src/dev-dashboard/lib/tmux/naming.test.ts
  • src/dev-dashboard/lib/tmux/naming.ts
  • src/dev-dashboard/lib/tmux/presets.ts
  • src/dev-dashboard/lib/tmux/rename.ts
  • src/dev-dashboard/lib/ttyd/label.ts
  • src/dev-dashboard/lib/ttyd/manager.test.ts
  • src/dev-dashboard/lib/ttyd/manager.ts
  • src/dev-dashboard/lib/ttyd/naming.test.ts
  • src/dev-dashboard/lib/ttyd/naming.ts
  • src/dev-dashboard/lib/ttyd/types.ts
  • src/dev-dashboard/server/adapters/bun-serve.ts
  • src/dev-dashboard/server/adapters/node-connect.ts
  • src/dev-dashboard/server/routes/cmux.ts
  • src/dev-dashboard/server/routes/tmux-presets.ts
  • src/dev-dashboard/server/routes/tmux.ts
  • src/dev-dashboard/ui/src/components/TmuxSessionName.tsx
  • src/dev-dashboard/ui/src/components/TmuxSessionsPanel.tsx
  • src/dev-dashboard/ui/src/components/terminal-shell/MobileTerminalShell.tsx
  • src/dev-dashboard/ui/src/lib/api.ts
  • src/dev-dashboard/ui/src/lib/terminal-tabs.test.ts
  • src/dev-dashboard/ui/src/routes/ttyd.tsx
  • src/tmux/commands/create.ts
  • src/tmux/commands/presets/restore.ts
  • src/tmux/commands/session.ts
  • src/tmux/commands/sessions-format.ts
  • src/tmux/commands/sessions.test.ts
  • src/tmux/commands/sessions.ts
  • src/update/index.ts
  • src/utils/cmux/send-tmux.ts
  • src/utils/tmux/reset.ts
  • src/utils/tmux/sessions.test.ts
  • src/utils/tmux/sessions.ts
  • src/utils/tmux/snapshot-last-cmd.test.ts
  • src/utils/tmux/snapshot.ts

Comment on lines +117 to +123
ttydTabs: Array<{
id: string;
port: number;
label: string;
cwd?: string;
lastCommand?: string;
}>;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Define one named ttyd tab contract.

ttydTabs is declared separately in the contract, server, and UI API modules. These declarations can drift without a TypeScript error.

Export a named TtydHubTab DTO here. Import it in src/dev-dashboard/lib/tmux/hub.ts and src/dev-dashboard/ui/src/lib/api.ts.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/dev-dashboard/contract/dto.ts` around lines 117 - 123, Define and export
a named TtydHubTab DTO for the existing ttyd tab shape in the contract module,
then update the ttydTabs declarations in the hub module and UI API module to
import and reuse TtydHubTab instead of duplicating the inline object type.

Comment thread src/dev-dashboard/lib/ttyd/manager.ts Outdated
Comment on lines +706 to +747
if (candidates.length === 0) {
return;
}

for (const { id, fromName, toName, renameTmux } of candidates) {
try {
if (renameTmux) {
// The pane map's keys are the full live session-name set from this
// poll's single list-sessions — no extra tmux call for the clash check.
if (panesByTmux.has(toName)) {
logger.debug(
{ id, fromName, toName },
"claude pane title sync skipped: destination tmux session already exists"
);
continue;
}

await renameTmuxSession(fromName, toName);
await retargetTtydTmuxBindings(fromName, toName);

// Retarget updates bindings under the new name; refresh the pane map key for later reads.
const pane = panesByTmux.get(fromName);

if (pane) {
panesByTmux.delete(fromName);
panesByTmux.set(toName, pane);
}
}

const tracked = registry.get(id);

if (!tracked) {
continue;
}

tracked.session.name = toName;
await persistRegistry();
logger.info({ id, fromName, toName, renameTmux }, "synced tmux/ttyd name from Claude pane title (/rename)");
} catch (err) {
logger.debug({ err, id, fromName, toName }, "claude pane title sync failed");
}
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Reuse syncTtydDisplayNamesForTmux instead of updating a single registry entry.

syncNamesFromClaudePaneTitles sets the display name only for the triggering tracked entry at Line 741 (tracked.session.name = toName;). Other rename paths in this file do not do this. renameTtyd and renameTmuxSessionInHub (src/dev-dashboard/lib/tmux/rename.ts) both call syncTtydDisplayNamesForTmux(tmuxSessionName, displayName) after a tmux rename, which mirrors the name onto every ttyd registry entry bound to that tmux session name. retargetTtydTmuxBindings (Lines 865-901 in this file) also loops over every matching entry, not just one.

If more than one ttyd tab is ever bound to the same tmux session (the exact stale-binding scenario healStaleTtydTmuxTargets exists to detect and repair), only the entry matching the Claude pane title candidate gets its display name synced. The other bound tabs keep a stale label.

Call the existing helper instead of duplicating the name-update logic:

♻️ Proposed fix
     for (const { id, fromName, toName, renameTmux } of candidates) {
         try {
             if (renameTmux) {
                 // ...
                 await renameTmuxSession(fromName, toName);
                 await retargetTtydTmuxBindings(fromName, toName);

                 // Retarget updates bindings under the new name; refresh the pane map key for later reads.
                 const pane = panesByTmux.get(fromName);

                 if (pane) {
                     panesByTmux.delete(fromName);
                     panesByTmux.set(toName, pane);
                 }
             }

-            const tracked = registry.get(id);
-
-            if (!tracked) {
-                continue;
-            }
-
-            tracked.session.name = toName;
-            await persistRegistry();
+            if (!registry.has(id)) {
+                continue;
+            }
+
+            await syncTtydDisplayNamesForTmux(toName, toName);
             logger.info({ id, fromName, toName, renameTmux }, "synced tmux/ttyd name from Claude pane title (/rename)");
         } catch (err) {
             logger.debug({ err, id, fromName, toName }, "claude pane title sync failed");
         }
     }

As per path instructions: "Fix shared bugs at their root implementation rather than applying repeated patches at individual call sites."

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if (candidates.length === 0) {
return;
}
for (const { id, fromName, toName, renameTmux } of candidates) {
try {
if (renameTmux) {
// The pane map's keys are the full live session-name set from this
// poll's single list-sessions — no extra tmux call for the clash check.
if (panesByTmux.has(toName)) {
logger.debug(
{ id, fromName, toName },
"claude pane title sync skipped: destination tmux session already exists"
);
continue;
}
await renameTmuxSession(fromName, toName);
await retargetTtydTmuxBindings(fromName, toName);
// Retarget updates bindings under the new name; refresh the pane map key for later reads.
const pane = panesByTmux.get(fromName);
if (pane) {
panesByTmux.delete(fromName);
panesByTmux.set(toName, pane);
}
}
const tracked = registry.get(id);
if (!tracked) {
continue;
}
tracked.session.name = toName;
await persistRegistry();
logger.info({ id, fromName, toName, renameTmux }, "synced tmux/ttyd name from Claude pane title (/rename)");
} catch (err) {
logger.debug({ err, id, fromName, toName }, "claude pane title sync failed");
}
}
if (candidates.length === 0) {
return;
}
for (const { id, fromName, toName, renameTmux } of candidates) {
try {
if (renameTmux) {
// The pane map's keys are the full live session-name set from this
// poll's single list-sessions — no extra tmux call for the clash check.
if (panesByTmux.has(toName)) {
logger.debug(
{ id, fromName, toName },
"claude pane title sync skipped: destination tmux session already exists"
);
continue;
}
await renameTmuxSession(fromName, toName);
await retargetTtydTmuxBindings(fromName, toName);
// Retarget updates bindings under the new name; refresh the pane map key for later reads.
const pane = panesByTmux.get(fromName);
if (pane) {
panesByTmux.delete(fromName);
panesByTmux.set(toName, pane);
}
}
if (!registry.has(id)) {
continue;
}
await syncTtydDisplayNamesForTmux(toName, toName);
logger.info({ id, fromName, toName, renameTmux }, "synced tmux/ttyd name from Claude pane title (/rename)");
} catch (err) {
logger.debug({ err, id, fromName, toName }, "claude pane title sync failed");
}
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/dev-dashboard/lib/ttyd/manager.ts` around lines 706 - 747, In
syncNamesFromClaudePaneTitles, replace the single-entry update of
tracked.session.name and its duplicated persistence logic with the existing
syncTtydDisplayNamesForTmux helper after the tmux rename, passing the relevant
tmux session name and toName so every bound ttyd entry is updated consistently.
Preserve the candidate lookup and rename flow, and avoid retaining the
tracked-only assignment.

Source: Path instructions

Comment on lines +53 to 55
skipBlurCommitRef.current = true;
onRename?.(tab.id, e.currentTarget.value);
setEditingId(null);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Normalize Enter rename values before the callback.

Enter passes the raw value to onRename. Blur trims the value and rejects empty or unchanged names.

Apply the same trim, non-empty, and changed-value checks before calling onRename on Enter.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/dev-dashboard/ui/src/components/terminal-shell/MobileTerminalShell.tsx`
around lines 53 - 55, Update the Enter-key rename handler in MobileTerminalShell
to trim the submitted value, reject empty values, and reject names unchanged
from the current tab name before invoking onRename. Preserve the existing blur
behavior and only clear editing state after a valid rename is handled.

Comment on lines +53 to +67
skipBlurCommitRef.current = true;
onRename?.(tab.id, e.currentTarget.value);
setEditingId(null);
}

if (e.key === "Escape") {
skipBlurCommitRef.current = true;
setEditingId(null);
}
}}
onBlur={(e) => {
if (skipBlurCommitRef.current) {
skipBlurCommitRef.current = false;
setEditingId(null);
return;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Remove the stale blur-suppression state.

After Enter or Escape removes the focused input, onBlur may not run. skipBlurCommitRef.current then remains true.

The next normal blur skips its rename commit. Remove this suppression mechanism, or reset it before opening each edit input.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/dev-dashboard/ui/src/components/terminal-shell/MobileTerminalShell.tsx`
around lines 53 - 67, Remove the stale blur-suppression mechanism from the
rename input flow: eliminate skipBlurCommitRef usage in the Enter/Escape
handlers and onBlur logic, or reset it whenever a new edit input opens. Ensure
normal subsequent blurs still commit the rename through the existing onBlur
path.

Comment on lines +5 to 13
const s = (id: string, port: number, overrides: Partial<TtydSession> = {}): TtydSession => ({
id,
port,
command: "/bin/zsh",
cwd: "/x",
pid: 1,
startedAt: "now",
name,
...overrides,
});

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Use an object parameter for s.

s has three parameters and an optional overrides parameter. Change it to accept one object parameter and update its call sites to use named fields.

As per coding guidelines, functions with three or more parameters or optional parameters must use an object parameter.

Proposed refactor
-const s = (id: string, port: number, overrides: Partial<TtydSession> = {}): TtydSession => ({
+const s = ({
+    id,
+    port,
+    overrides = {},
+}: {
+    id: string;
+    port: number;
+    overrides?: Partial<TtydSession>;
+}): TtydSession => ({
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/dev-dashboard/ui/src/lib/terminal-tabs.test.ts` around lines 5 - 13,
Refactor the test helper s to accept a single object parameter containing the
session fields and optional overrides, then update every s call site to pass
named properties instead of positional arguments. Preserve the existing
TtydSession defaults and override behavior.

Source: Coding guidelines

Comment thread src/update/index.ts
Comment on lines +26 to +45
async function runClaude(args: string[]): Promise<number> {
return Bun.spawn(["claude", ...args], { stdio: ["inherit", "inherit", "inherit"] }).exited;
}

/** Run a `claude … --json` query and parse it; null on non-zero exit or unparseable output. */
async function claudeJson<T>(args: string[]): Promise<T | null> {
const proc = Bun.spawn(["claude", ...args], { stdio: ["ignore", "pipe", "ignore"] });
const stdout = await new Response(proc.stdout).text();

if ((await proc.exited) !== 0) {
return null;
}

try {
return SafeJSON.parse(stdout) as T;
} catch (err) {
logger.debug({ err, args }, "update: could not parse claude CLI --json output");
return null;
}
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🌐 Web query:

Does Bun.spawn() reject the returned promise or throw synchronously when the executable is not found?

💡 Result:

When you call Bun.spawn, if the specified executable cannot be found, the function throws a synchronous error (typically a TypeError or similar system error like ENOENT) rather than returning a promise that later rejects [1][2]. Because the resolution of the executable happens synchronously during the initialization of the subprocess, Bun validates the existence of the command before the function returns the Subprocess object [1][2]. Consequently, if the file is missing or cannot be located in the provided PATH, the execution halts immediately with a synchronous exception [1][2]. If you need to verify whether a command exists before attempting to spawn it, you can use the synchronous Bun.which(binary) utility [3][4].

Citations:


🏁 Script executed:

#!/bin/bash
set -eu

printf '%s\n' '--- src/update/index.ts outline ---'
ast-grep outline src/update/index.ts --match 'runClaude' --view expanded || true

printf '%s\n' '--- relevant source ---'
sed -n '1,220p' src/update/index.ts

printf '%s\n' '--- symbol references ---'
rg -n -C 3 'runClaude|claudeJson|findMarketplace|pluginIsInstalled' src/update src

Repository: genesiscz/GenesisTools

Length of output: 31702


🏁 Script executed:

#!/bin/bash
set -eu

printf '%s\n' '--- update entrypoints and runTool references ---'
rg -n -C 5 'runTool\(|from "`@genesiscz/utils/cli`"|update' src/update src/utils package.json

printf '%s\n' '--- runTool definitions ---'
rg -n -C 12 'export (async )?function runTool|function runTool|const runTool' src packages 2>/dev/null || true

printf '%s\n' '--- async rejection semantics ---'
node - <<'JS'
async function spawnWrapper() {
    throw new Error("ENOENT");
}

spawnWrapper().then(
    () => console.log("fulfilled"),
    (error) => console.log("rejected:", error.message)
);
JS

Repository: genesiscz/GenesisTools

Length of output: 50381


🏁 Script executed:

#!/bin/bash
set -eu

sed -n '150,280p' src/utils/cli/commander.ts

Repository: genesiscz/GenesisTools

Length of output: 2384


🏁 Script executed:

#!/bin/bash
set -eu

printf '%s\n' '--- declared Bun target ---'
python3 - <<'PY'
from pathlib import Path
import re

for path in [Path("package.json"), Path("bunfig.toml")]:
    if path.exists():
        print(f"--- {path} ---")
        text = path.read_text()
        for line in text.splitlines():
            if re.search(r"bun|engines|packageManager", line, re.I):
                print(line)
PY

printf '%s\n' '--- synchronous throw inside async action ---'
node - <<'JS'
function spawnLike() {
    const error = new Error("spawn claude ENOENT");
    error.code = "ENOENT";
    throw error;
}

async function claudeJson() {
    const processHandle = spawnLike();
    return processHandle;
}

async function updateAction() {
    await claudeJson();
}

updateAction().then(
    () => console.log("fulfilled"),
    (error) => console.log(`rejected ${error.code}: ${error.message}`)
);
JS

Repository: genesiscz/GenesisTools

Length of output: 1258


Handle missing claude executable

Bun.spawn() throws synchronously with ENOENT when claude is not on PATH. The exception rejects the async action, and runTool() does not catch that rejection. Catch the spawn failure in one shared helper; return null for claudeJson() and a nonzero status for runClaude().

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/update/index.ts` around lines 26 - 45, Handle Bun.spawn failures for the
missing claude executable in the shared CLI execution flow: update runClaude to
catch spawn errors and return a nonzero status, and update claudeJson to catch
spawn errors and return null. Keep existing exit-code and JSON parsing behavior
unchanged when spawning succeeds.

Source: Coding guidelines

Comment thread src/update/index.ts
Comment on lines +39 to +44
try {
return SafeJSON.parse(stdout) as T;
} catch (err) {
logger.debug({ err, args }, "update: could not parse claude CLI --json output");
return null;
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Use strict JSON parsing for subprocess output.

SafeJSON.parse(stdout) parses claude CLI stdout without { strict: true }. This is a subprocess-output boundary, not a user-authored config file, so it should use strict RFC 8259 validation per the coding guideline requiring { strict: true }/{ jsonl: true } when strict behavior is required.

♻️ Proposed fix
-        return SafeJSON.parse(stdout) as T;
+        return SafeJSON.parse(stdout, { strict: true }) as T;

Based on learnings, "prefer strict RFC 8259 validation by using SafeJSON.parse(text, { strict: true })... Apply this at non-config boundaries (e.g., API responses, JSONL, cache outputs, subprocess outputs)."

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
try {
return SafeJSON.parse(stdout) as T;
} catch (err) {
logger.debug({ err, args }, "update: could not parse claude CLI --json output");
return null;
}
try {
return SafeJSON.parse(stdout, { strict: true }) as T;
} catch (err) {
logger.debug({ err, args }, "update: could not parse claude CLI --json output");
return null;
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/update/index.ts` around lines 39 - 44, Update the SafeJSON.parse call in
the subprocess-output parsing try/catch to pass the strict parsing option ({
strict: true }). Keep the existing error logging through logger.debug and null
return behavior unchanged.

Sources: Coding guidelines, Learnings

Comment thread src/update/index.ts
Comment on lines +135 to +166
if (isCheckout && !pointsAtCheckout) {
// `marketplace add` OVERWRITES an existing registration's source rather than
// refusing, so this one command covers both first registration and the flip.
out.println(
pc.dim(existing ? `\n Re-pointing marketplace at ${checkout}...` : "\n Adding marketplace...")
);
await runClaude(["plugin", "marketplace", "add", checkout]);
repointed = existing !== null;
} else if (!isCheckout && !existing) {
out.println(pc.yellow(`\n ${checkout} is not a GenesisTools checkout; using the GitHub marketplace.`));
await runClaude(["plugin", "marketplace", "add", MARKETPLACE_FALLBACK_SOURCE]);
} else {
out.println(pc.dim("\n Updating Claude Code marketplace..."));
await Bun.spawn(["claude", "plugin", "marketplace", "update"], {
stdio: ["inherit", "inherit", "inherit"],
}).exited;

out.println(pc.dim("\n Adding marketplace..."));
await Bun.spawn(
["claude", "plugin", "marketplace", "add", "https://github.com/genesiscz/GenesisTools"],
{
stdio: ["inherit", "inherit", "inherit"],
}
).exited;
out.println(pc.dim("\n Refreshing marketplace..."));
}

// Claude Code caches each marketplace's PARSED plugin catalog; even a directory
// source is not re-read on its own. Skipping this is what makes the next step fail
// with `Plugin "genesis-tools" not found in marketplace "genesis-tools"`.
await runClaude(["plugin", "marketplace", "update", MARKETPLACE_NAME]);

if (!(await pluginIsInstalled(PLUGIN_REF))) {
out.println(pc.dim("\n Installing plugin..."));
const pluginInstallCode = await Bun.spawn(
["claude", "plugin", "install", "genesis-tools@genesis-tools"],
{
stdio: ["inherit", "inherit", "inherit"],
}
).exited;
if (pluginInstallCode !== 0) {

if ((await runClaude(["plugin", "install", PLUGIN_REF])) !== 0) {
out.println(pc.yellow(" Plugin install had issues"));
}
} else if (repointed) {
// The installed copy was taken from the OLD source, and the plugin cache is keyed
// by version — so `plugin update` is a no-op whenever the version is unchanged and
// the stale copy would survive the flip. Reinstall to re-copy from this checkout.
out.println(pc.dim("\n Reinstalling plugin from this checkout..."));
await runClaude(["plugin", "uninstall", PLUGIN_REF, "--keep-data", "-y"]);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Unchecked exit codes on marketplace mutation commands.

runClaude(["plugin", "marketplace", "add", ...]) (lines 141, 145), runClaude(["plugin", "marketplace", "update", MARKETPLACE_NAME]) (line 153), and runClaude(["plugin", "uninstall", ...]) (line 166) do not check the returned exit code, unlike the install/reinstall/update calls at lines 158, 168, and 174. The comment at lines 150-152 explicitly documents that the marketplace-update step is required for the following install step to succeed ("Skipping this is what makes the next step fail"), yet its own success is never verified.

If marketplace add or marketplace update fails silently, the flow still proceeds to pluginIsInstalled and to installing/reinstalling/uninstalling the plugin against a stale or unregistered marketplace, surfacing only a generic "had issues" message instead of the real cause. This is more consequential for the uninstall at line 166: if the preceding repoint failed, the plugin is still removed and the following install is not guaranteed to restore it.

🔒️ Proposed fix (sketch)
-                await runClaude(["plugin", "marketplace", "add", checkout]);
+                if ((await runClaude(["plugin", "marketplace", "add", checkout])) !== 0) {
+                    out.println(pc.yellow("  Marketplace registration failed"));
+                }
                 repointed = existing !== null;

Apply the same pattern to the fallback marketplace add (line 145), marketplace update (line 153), and plugin uninstall (line 166) calls.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/update/index.ts` around lines 135 - 166, Check the return codes from
every marketplace mutation in the update flow: both `marketplace add` branches,
`marketplace update`, and the `plugin uninstall` call in the `repointed` branch.
Reuse the existing failure-handling pattern used by the checked
install/reinstall/update commands, and stop or propagate the failure before
proceeding with dependent plugin operations.

Comment on lines +85 to 91
let spawnImpl: SyncTmuxSpawn = defaultSpawnSync;

/** Override spawn for tests. Also forwards to sessions.ts so create/kill mock too. */
export function setTmuxSnapshotSpawnForTests(impl: TmuxSpawnSync | null): void {
export function setTmuxSnapshotSpawnForTests(impl: SyncTmuxSpawn | null): void {
spawnImpl = impl ?? defaultSpawnSync;
setTmuxSpawnSyncForTests(impl);
setTmuxSpawnSyncForTests(impl satisfies TmuxSpawnSync | null);
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Drop the redundant satisfies in the forwarding call.

setTmuxSpawnSyncForTests already accepts TmuxSpawnSync | null, and SyncTmuxSpawn is assignable to it because TmuxSpawnResult is a member of the return union. The satisfies clause adds no checking here and implies the two contracts need reconciliation.

♻️ Proposed change
-    setTmuxSpawnSyncForTests(impl satisfies TmuxSpawnSync | null);
+    setTmuxSpawnSyncForTests(impl);

Remove the now-unused TmuxSpawnSync type import if nothing else uses it.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
let spawnImpl: SyncTmuxSpawn = defaultSpawnSync;
/** Override spawn for tests. Also forwards to sessions.ts so create/kill mock too. */
export function setTmuxSnapshotSpawnForTests(impl: TmuxSpawnSync | null): void {
export function setTmuxSnapshotSpawnForTests(impl: SyncTmuxSpawn | null): void {
spawnImpl = impl ?? defaultSpawnSync;
setTmuxSpawnSyncForTests(impl);
setTmuxSpawnSyncForTests(impl satisfies TmuxSpawnSync | null);
}
let spawnImpl: SyncTmuxSpawn = defaultSpawnSync;
/** Override spawn for tests. Also forwards to sessions.ts so create/kill mock too. */
export function setTmuxSnapshotSpawnForTests(impl: SyncTmuxSpawn | null): void {
spawnImpl = impl ?? defaultSpawnSync;
setTmuxSpawnSyncForTests(impl);
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/utils/tmux/snapshot.ts` around lines 85 - 91, Update
setTmuxSnapshotSpawnForTests to forward impl directly to
setTmuxSpawnSyncForTests without the redundant satisfies clause. Remove the
TmuxSpawnSync import if it is no longer referenced elsewhere in the file.

Comment on lines +300 to +310
/** SGR mouse / CSI leftovers that leak into the tty when mouse mode is on (e.g. in ttyd). */
const CONTROL_JUNK_RE = /\d+;\d+[A-Za-z]|;\d*n|\b997;\d/;

/**
* Oh-my-zsh / similar put `dir git:(branch)` on the same line after `➜` with no command —
* that must not be treated as a typed command.
*/
const PROMPT_REMANT_RE = /^[\w./-]+\s+git:\([^)]*\)/;

function extractPromptCommand(line: string): string | undefined {
const match = PROMPT_LINE.exec(line);
const match = PROMPT_LINE.exec(stripAnsi(line));

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Fix the constant name spelling: PROMPT_REMANT_REPROMPT_REMNANT_RE.

The comment describes prompt remnants. The identifier drops the second "n".

🧰 Tools
🪛 OpenGrep (1.26.0)

[ERROR] 310-310: Dynamic command passed to child_process.exec/execSync. Use child_process.execFile or spawn with an argument array instead.

(coderabbit.command-injection.exec-js)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/utils/tmux/snapshot.ts` around lines 300 - 310, Rename the constant
PROMPT_REMANT_RE to PROMPT_REMNANT_RE and update every reference to it,
including within extractPromptCommand, without changing the matching behavior.

@eve-bot-lovinka eve-bot-lovinka Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🐉 eve review — ✅ Approved

74e7b08 · 0 actionable findings · view run ↗

@eve-bot-lovinka

Copy link
Copy Markdown

Delta review completed and posted.

…ity for every session, accept all braille spinner frames

@eve-bot-lovinka eve-bot-lovinka Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🐉 eve review — 🟡 Review comments

c2035c8 · 5 actionable findings · view run ↗

Severity Count
🟡 Medium 1
🔵 Low 4

@@ -137,20 +206,29 @@ export function listTmuxSessions(): TmuxSessionInfo[] {
const sessions: TmuxSessionInfo[] = [];

for (const line of result.stdout.split("\n")) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Quality | 🟡 Medium · confidence 96/100

⚠️ Potential issue

Do not parse arbitrary pane titles with newline records

The format now appends pane_title, which the nearby comment correctly describes as arbitrary user text, but the response is still split into records on every newline. A pane title containing \n therefore terminates its real session record and makes the remainder look like another session; if it contains tab-separated values, the dashboard can receive a convincing phantom session. Putting the title last only protects tabs, not the record delimiter. Use an escaped/encoded tmux format representation or another framing scheme that cannot collide with field contents before parsing it.

🧩 Analysis

Grep evidence: result\.stdout\.split\("\\n"\)

}

const [name, attachedRaw, windowsRaw] = trimmed.split("\t");
const [name, attachedRaw, windowsRaw, command, cwd, createdRaw, activityRaw, ...titleParts] =

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧪 Tests | 🔵 Low · confidence 99/100

⚠️ Potential issue

Cover parsing of the new session metadata

The parser now has new column ordering and conversions for command, cwd, creation/activity timestamps, and a tab-containing title, but src/utils/tmux/sessions.test.ts still supplies only the old three-column fixture and expects only the old fields. A regression in any new index or timestamp conversion would therefore pass. Extend that test with all new columns, including a title containing a tab and malformed/empty timestamps, and assert the complete parsed object.

🧩 Analysis

Grep evidence: stdout: "dev-dashboard-abc12345\\t1\\t2\\ncmux-test\\t0\\t1\\n"

name: string;
attached: number;
windows: number;
/** Active-pane facts from tmux itself — the only meta available for a session with no ttyd. */

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

♻️ Reuse | 🔵 Low · confidence 94/100

♻️ Duplicate logic

Reuse the shared TmuxHubSession contract

This UI-local interface repeats the same wire shape already owned by src/dev-dashboard/contract/dto.ts:TmuxHubSession and produced by src/dev-dashboard/lib/tmux/hub.ts:TmuxHubSession. The contract's own comment says it was introduced because the web client previously defined this type inline, yet the inline copy remains and now required the same eight fields to be added in parallel. Importing the contract type (or using TmuxSessionsRes from the endpoints contract) prevents future server/client drift.

🧩 Analysis

Grep evidence: export interface TmuxHubSession

const alreadyInTtyd = session.ttydTabIds.length > 0;
const alreadyInTtyd = (session.ttydTabs?.length ?? session.ttydTabIds.length) > 0;
const primaryTab = session.ttydTabs?.[0];
// Fall back to tmux's own active-pane facts. Reading these only off the ttyd binding left every

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧪 Tests | 🔵 Low · confidence 65/100

⚠️ Potential issue

No test changes accompany 52 added lines in src/dev-dashboard/ui/src/components/TmuxSessionsPanel.tsx

This PR adds 52 lines to src/dev-dashboard/ui/src/components/TmuxSessionsPanel.tsx with no touching test change (no changed test names TmuxSessionsPanel and none under src/dev-dashboard/ui/src/components/). If the change alters behavior, add or extend a test that pins it (deterministic static check — ignore if the change is genuinely untestable or covered elsewhere).

🧩 Analysis

Grep evidence: TmuxSessionsPanel

}

const result = spawnSyncImpl([
// Every extra field here is a format column on the SAME call, not another round-trip, so the

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧪 Tests | 🔵 Low · confidence 65/100

⚠️ Potential issue

No test changes accompany 25 added lines in src/utils/tmux/sessions.ts

This PR adds 25 lines to src/utils/tmux/sessions.ts with no touching test change (no changed test names sessions and none under src/utils/tmux/). If the change alters behavior, add or extend a test that pins it (deterministic static check — ignore if the change is genuinely untestable or covered elsewhere).

🧩 Analysis

Grep evidence: sessions

@eve-bot-lovinka

Copy link
Copy Markdown

Delta review completed and posted.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/dev-dashboard/ui/src/components/TmuxSessionsPanel.tsx`:
- Around line 185-193: Update the alreadyInTtyd calculation in the
TmuxSessionsPanel component to fall back to session.ttydTabIds.length when
session.ttydTabs is empty, using length-based fallback semantics rather than
nullish coalescing. Preserve the existing positive-state behavior when either
collection contains bindings.
- Around line 220-226: Update the topic and metadata text elements in
TmuxSessionsPanel’s session rendering to replace the raw text-emerald and
text-zinc utilities with the dashboard’s shared semantic theme text tokens.
Preserve the existing typography, truncation, and layout classes while using the
appropriate tokens for Claude topic text and metadata text.

In `@src/utils/tmux/sessions.ts`:
- Around line 190-199: Update the tmux session field formatting and parsing
around the query field list so tab characters in pane_current_path cannot shift
subsequent values. Use collision-free encoding with matching decoding, or
length-delimited output, while preserving existing metadata mapping; add a
regression test covering a tab-containing cwd.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 6ffabb9b-dce3-4bc4-865c-66ff4804876d

📥 Commits

Reviewing files that changed from the base of the PR and between 74e7b08 and c2035c8.

📒 Files selected for processing (7)
  • src/dev-dashboard/contract/dto.ts
  • src/dev-dashboard/lib/tmux/claude-pane-title.test.ts
  • src/dev-dashboard/lib/tmux/claude-pane-title.ts
  • src/dev-dashboard/ui/src/components/TmuxSessionsPanel.tsx
  • src/dev-dashboard/ui/src/lib/api.ts
  • src/utils/tmux/sessions.ts
  • src/utils/tmux/types.ts
📜 Review details
⏰ Context from checks skipped due to timeout. (1)
  • GitHub Check: test (ubuntu-latest, 4)
🧰 Additional context used
📓 Path-based instructions (4)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.{ts,tsx}: Use bun run test or bun scripts/test.ts <paths> for tests; never invoke bare bun test.
Never read process.env directly in application code; import and use env from @genesiscz/utils/env, including its testing override APIs.
Fix shared bugs at their root implementation rather than applying repeated patches at individual call sites.
Do not add file-path comments as the first line of files or comments that merely restate obvious code.
Always use SafeJSON.parse() and SafeJSON.stringify() from @genesiscz/utils/json; do not use the restricted global JSON. Use { strict: true } or { jsonl: true } when strict behavior is required.
Do not use one-line if statements; always use braces and block form.
Place an empty line before if unless the preceding line is a variable declaration used by that condition, and place an empty line after a closing } unless followed by else, catch, finally, or another }.
Use an object parameter when a function has three or more parameters, optional parameters, or mixed required and optional parameters; positional parameters are acceptable for one or two obvious required arguments.
Never use as any; use proper narrowing, type guards, or explicit interfaces. For unions, use discriminant checks.
Prefer error: err over converting errors to strings when the logging error field accepts unknown.
Never swallow errors with a bare catch {}; log the caught error with context at least at debug or warning level.

Files:

  • src/dev-dashboard/lib/tmux/claude-pane-title.test.ts
  • src/utils/tmux/types.ts
  • src/dev-dashboard/lib/tmux/claude-pane-title.ts
  • src/dev-dashboard/contract/dto.ts
  • src/dev-dashboard/ui/src/components/TmuxSessionsPanel.tsx
  • src/utils/tmux/sessions.ts
  • src/dev-dashboard/ui/src/lib/api.ts
src/**/*.ts

📄 CodeRabbit inference engine (CLAUDE.md)

src/**/*.ts: Place general-purpose helper functions in src/utils/; keep tool-specific logic inside its tool directory.
Before showing prompts, check isInteractive(); in non-interactive mode, either report required CLI flags with suggestCommand() or use a sensible default.
Use Bun.spawn() for external commands and properly consume stdout and stderr streams.
Use Bun native file APIs such as Bun.write() for file operations.
When a tool misbehaves, inspect the day-stamped logs under ~/.genesis-tools/logs/ and search for the tool or error before hypothesizing or reproducing.
Log enough through @genesiscz/utils/logger to reconstruct execution: decision branches, external-resource accesses, configuration resolution, and result counts.
Use named logger and out imports from @genesiscz/utils/logger; logger is for diagnostics and out.result() or out.print() are the only stdout result writers.
For commander entrypoints, end with await runTool(program, { tool }); use execTool for subprocess spawning.
Use @genesiscz/utils/cli/ui instead of out.log.* for high-density CLI status, while retaining out.print() and out.result() as the only stdout writers.
Use shared table helpers from @genesiscz/utils/table for multi-column human inventory output; do not hand-roll new Table({ chars: ... }) or copy box-drawing characters. Use out.result() with --json for machine-readable output.
Use the canonical dashboard and web-service registry in src/utils/ui/dashboards.ts; never hardcode repository web-server ports.

Files:

  • src/dev-dashboard/lib/tmux/claude-pane-title.test.ts
  • src/utils/tmux/types.ts
  • src/dev-dashboard/lib/tmux/claude-pane-title.ts
  • src/dev-dashboard/contract/dto.ts
  • src/utils/tmux/sessions.ts
  • src/dev-dashboard/ui/src/lib/api.ts
src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

src/**/*.{ts,tsx}: For web UI under src/<tool>/ui, src/dashboard, or src/dev-dashboard/ui, read the shared design-system documentation first; use theme tokens and shared UI primitives, avoid raw zinc-* or white/NN palettes, do not override Card surfaces, choose intentional Button/Card variants, and use the shared shell/auth layout.
Use the canonical dynamic model and pricing sources; do not add new rate tables. The static src/ai-proxy/lib/billing/pricing.ts table is the deliberate offline invoicing exception and must not be duplicated or deduplicated into the dynamic path.
Determine subscription billing from DetectedProvider.subscription, never from name suffixes; resolve API keys through AIConfig.resolveApiKey without copying referenced environment values.

Files:

  • src/dev-dashboard/lib/tmux/claude-pane-title.test.ts
  • src/utils/tmux/types.ts
  • src/dev-dashboard/lib/tmux/claude-pane-title.ts
  • src/dev-dashboard/contract/dto.ts
  • src/dev-dashboard/ui/src/components/TmuxSessionsPanel.tsx
  • src/utils/tmux/sessions.ts
  • src/dev-dashboard/ui/src/lib/api.ts
src/**/*.test.ts

📄 CodeRabbit inference engine (CLAUDE.md)

Database tests should use an in-memory new Database(":memory:") beside the source under test.

Files:

  • src/dev-dashboard/lib/tmux/claude-pane-title.test.ts
🧠 Learnings (34)
📚 Learning: 2026-02-24T15:32:37.494Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 54
File: src/github/lib/output.ts:109-113
Timestamp: 2026-02-24T15:32:37.494Z
Learning: In TypeScript files under src/, do not require a leading blank line before an if statement that is the first statement inside a function body (immediately after the function signature). The blank line rule should only apply to if statements that come after other statements within the function body. Apply this guideline consistently across TS files in src to reduce unnecessary vertical whitespace and keep concise function bodies.

Applied to files:

  • src/dev-dashboard/lib/tmux/claude-pane-title.test.ts
  • src/utils/tmux/types.ts
  • src/dev-dashboard/lib/tmux/claude-pane-title.ts
  • src/dev-dashboard/contract/dto.ts
  • src/utils/tmux/sessions.ts
  • src/dev-dashboard/ui/src/lib/api.ts
📚 Learning: 2026-03-09T13:13:58.786Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 81
File: src/github/commands/get.ts:209-212
Timestamp: 2026-03-09T13:13:58.786Z
Learning: In the GenesisTools repo (genesiscz/GenesisTools), do not treat CI formatter warnings as enforceable formatting rules for TypeScript files under src/. Focus reviews on logical correctness and consistency with existing code patterns. For files under src (e.g., src/github/commands/get.ts), prioritize code structure, readability, naming, correctness, and adherence to project conventions over automated formatting warnings from CI tools.

Applied to files:

  • src/dev-dashboard/lib/tmux/claude-pane-title.test.ts
  • src/utils/tmux/types.ts
  • src/dev-dashboard/lib/tmux/claude-pane-title.ts
  • src/dev-dashboard/contract/dto.ts
  • src/utils/tmux/sessions.ts
  • src/dev-dashboard/ui/src/lib/api.ts
📚 Learning: 2026-03-12T01:26:31.610Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 95
File: src/timely/utils/entry-processor.ts:0-0
Timestamp: 2026-03-12T01:26:31.610Z
Learning: In code paths where JSON is consumed, prefer strict RFC 8259 validation by using SafeJSON.parse(text, { strict: true }) instead of the lenient default. Apply this at non-config boundaries (e.g., API responses, JSONL, cache outputs, subprocess outputs). Reserve the lenient comment-json behavior only for user-authored config files that may legitimately contain comments or trailing commas. For src/timely/utils/entry-processor.ts and similar modules, replace or wrap JSON parsing with SafeJSON.parse(text, { strict: true }) unless you are explicitly handling config files that require comments.

Applied to files:

  • src/dev-dashboard/lib/tmux/claude-pane-title.test.ts
  • src/utils/tmux/types.ts
  • src/dev-dashboard/lib/tmux/claude-pane-title.ts
  • src/dev-dashboard/contract/dto.ts
  • src/utils/tmux/sessions.ts
  • src/dev-dashboard/ui/src/lib/api.ts
📚 Learning: 2026-03-12T01:58:27.831Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 103
File: src/port/index.ts:137-144
Timestamp: 2026-03-12T01:58:27.831Z
Learning: In GenesisTools, apply a no-obvious-comments rule: do not add inline comments for well-known POSIX patterns or standard idioms (e.g., a process.kill(pid, 0) probe) when surrounding code is self-documenting through descriptive function/variable names. This guidance applies to TypeScript files under src (src/**/*.ts). Only include comments if they add non-obvious rationale, edge-case behavior, or explain complex logic that cannot be inferred from code alone.

Applied to files:

  • src/dev-dashboard/lib/tmux/claude-pane-title.test.ts
  • src/utils/tmux/types.ts
  • src/dev-dashboard/lib/tmux/claude-pane-title.ts
  • src/dev-dashboard/contract/dto.ts
  • src/utils/tmux/sessions.ts
  • src/dev-dashboard/ui/src/lib/api.ts
📚 Learning: 2026-03-22T22:19:44.520Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 119
File: src/indexer/commands/graph.ts:34-34
Timestamp: 2026-03-22T22:19:44.520Z
Learning: In genesiscz/GenesisTools, when using `SafeJSON.parse` in `src/**/*.ts`, it is acceptable to omit `{ strict: true }` if (and only if) the JSON being parsed is internal cache/state written by the same codebase (e.g., data saved by one internal writer and later read from a corresponding cached file). Do not require strict mode for these internal, machine-generated cache files. Require `{ strict: true }` at external/untrusted boundaries instead (e.g., API responses, third-party JSONL, subprocess output, or any JSON whose contents may not have been produced by trusted internal code).

Applied to files:

  • src/dev-dashboard/lib/tmux/claude-pane-title.test.ts
  • src/utils/tmux/types.ts
  • src/dev-dashboard/lib/tmux/claude-pane-title.ts
  • src/dev-dashboard/contract/dto.ts
  • src/utils/tmux/sessions.ts
  • src/dev-dashboard/ui/src/lib/api.ts
📚 Learning: 2026-03-25T19:55:27.917Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 129
File: src/utils/search/stores/vector-store.ts:19-23
Timestamp: 2026-03-25T19:55:27.917Z
Learning: When reviewing this codebase’s “3+ parameters → object parameter” guideline, only suggest object-parameter refactoring when the function’s parameters are ambiguous or include optional/unclear semantics. Do not flag tightly-defined utility/helper functions where (1) all parameters are required, (2) meanings are semantically clear from parameter names, and (3) the ordering is well-ordered and obvious. For example, functions like bruteForceVectorSearch(memoryIndex, queryVector, limit) should be allowed to keep positional parameters because the intent is clear.

Applied to files:

  • src/dev-dashboard/lib/tmux/claude-pane-title.test.ts
  • src/utils/tmux/types.ts
  • src/dev-dashboard/lib/tmux/claude-pane-title.ts
  • src/dev-dashboard/contract/dto.ts
  • src/utils/tmux/sessions.ts
  • src/dev-dashboard/ui/src/lib/api.ts
📚 Learning: 2026-05-05T03:52:21.057Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 163
File: src/debugging-master/core/dashboard-server.ts:115-127
Timestamp: 2026-05-05T03:52:21.057Z
Learning: When reviewing Bun.serve fetch handlers in this repo, don’t treat `req.signal` as possibly `undefined` at runtime. Bun guarantees an `AbortSignal` on every incoming Request, so `req.signal?.addEventListener(...)` is unnecessary for runtime safety and is only a TypeScript narrowing artifact (e.g., the type might be `AbortSignal | null`). Therefore, don’t raise concerns about SSE/subscription cleanup being skipped because `req.signal` could be missing; cleanup decisions should be based on the actual handler lifecycle, not an imagined runtime absence of `req.signal`.

Applied to files:

  • src/dev-dashboard/lib/tmux/claude-pane-title.test.ts
  • src/utils/tmux/types.ts
  • src/dev-dashboard/lib/tmux/claude-pane-title.ts
  • src/dev-dashboard/contract/dto.ts
  • src/utils/tmux/sessions.ts
  • src/dev-dashboard/ui/src/lib/api.ts
📚 Learning: 2026-06-30T19:43:23.331Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 227
File: src/agents/lib/session-resolve.ts:0-0
Timestamp: 2026-06-30T19:43:23.331Z
Learning: In GenesisTools application code, when you need to read an environment variable using a dynamic key, do not access `process.env` directly. Instead, route the lookup through `env.ai.getByEnvKey()` from `app/utils/env`. This matches the existing dynamic-key lookup pattern used elsewhere (e.g., ask’s `ProviderConfig.envKey`) and preserves `env.testing.set()` / `env.testing.withOverrides()` behavior. For static env keys, follow the project’s existing conventions, but for dynamic-key access prefer `env.ai.getByEnvKey()`.

Applied to files:

  • src/dev-dashboard/lib/tmux/claude-pane-title.test.ts
  • src/utils/tmux/types.ts
  • src/dev-dashboard/lib/tmux/claude-pane-title.ts
  • src/dev-dashboard/contract/dto.ts
  • src/utils/tmux/sessions.ts
  • src/dev-dashboard/ui/src/lib/api.ts
📚 Learning: 2026-07-08T16:01:57.320Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 230
File: src/dev-dashboard/lib/boards/db.ts:183-190
Timestamp: 2026-07-08T16:01:57.320Z
Learning: In TypeScript files under src/**/*.ts, for `if` blocks that act as simple guard-return statements (e.g., `if (condition) { return <expr>; }`) and where execution continues in the same function after the `if`, require a blank line after the closing `}` of the `if` block (i.e., before the next statement), but do NOT require a blank line before the `if` statement itself—even if it immediately follows another statement. (Example: `const override = ...; if (override) { return override; }` should have no blank line before the `if`, but should have a blank line before the subsequent `return`/statement.)

Applied to files:

  • src/dev-dashboard/lib/tmux/claude-pane-title.test.ts
  • src/utils/tmux/types.ts
  • src/dev-dashboard/lib/tmux/claude-pane-title.ts
  • src/dev-dashboard/contract/dto.ts
  • src/utils/tmux/sessions.ts
  • src/dev-dashboard/ui/src/lib/api.ts
📚 Learning: 2026-07-12T03:55:59.351Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 201
File: src/mcp-doctor/index.ts:94-97
Timestamp: 2026-07-12T03:55:59.351Z
Learning: When reviewing call sites that use `out.spinner()` (from `src/logger/out.ts`), do not require additional `isInteractive()`/stdin-TTY guards. `out.spinner()` already switches to a quiet no-op spinner when `isQuietOutput()` is true, and `isQuietOutput()` returns `true` whenever `process.stdout.isTTY` is falsy (common in CI, pipes, and JSON/structured output modes like `--json`/`--toon`). Wrapping with `isInteractive()` would duplicate centralized stdout-based logic and gate on the wrong TTY channel (stdin vs stdout).

Applied to files:

  • src/dev-dashboard/lib/tmux/claude-pane-title.test.ts
  • src/utils/tmux/types.ts
  • src/dev-dashboard/lib/tmux/claude-pane-title.ts
  • src/dev-dashboard/contract/dto.ts
  • src/utils/tmux/sessions.ts
  • src/dev-dashboard/ui/src/lib/api.ts
📚 Learning: 2026-02-24T15:32:44.925Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 54
File: src/github/lib/review-output.ts:18-20
Timestamp: 2026-02-24T15:32:44.925Z
Learning: In TypeScript files, do not require a blank line between the opening brace of a function and the first statement if the first statement is the if statement immediately after the signature. The blank-line rule applies to separating an if from unrelated preceding code within the same block, not to spacing after the function opening brace. Apply this rule to all TS functions across the codebase.

Applied to files:

  • src/dev-dashboard/lib/tmux/claude-pane-title.test.ts
  • src/utils/tmux/types.ts
  • src/dev-dashboard/lib/tmux/claude-pane-title.ts
  • src/dev-dashboard/contract/dto.ts
  • src/utils/tmux/sessions.ts
  • src/dev-dashboard/ui/src/lib/api.ts
📚 Learning: 2026-03-12T01:26:03.611Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 95
File: src/ask/lib/ChatSessionManager.ts:0-0
Timestamp: 2026-03-12T01:26:03.611Z
Learning: Use SafeJSON.parse(text, { strict: true }) for strict RFC 8259 validation in all non-config boundaries (API responses, JSONL, cache, subprocess output). The 3-arg form SafeJSON.parse(text, null, { strict: true }) is invalid and should not be used. Only lenient default (no options) is appropriate for user-authored config files that may contain comments/trailing commas. Apply this guideline across TypeScript files (src/**/*.ts) wherever SafeJSON.parse is used.

Applied to files:

  • src/dev-dashboard/lib/tmux/claude-pane-title.test.ts
  • src/utils/tmux/types.ts
  • src/dev-dashboard/lib/tmux/claude-pane-title.ts
  • src/dev-dashboard/contract/dto.ts
  • src/utils/tmux/sessions.ts
  • src/dev-dashboard/ui/src/lib/api.ts
📚 Learning: 2026-03-12T01:26:18.985Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 95
File: src/claude/lib/history/search.ts:0-0
Timestamp: 2026-03-12T01:26:18.985Z
Learning: When using SafeJSON.parse in TypeScript code, prefer the two-argument form SafeJSON.parse(text, { strict: true }) to enable strict RFC 8259 validation via the native JSON.parse. Do NOT use the three-argument form SafeJSON.parse(text, null, { strict: true }). Apply strict parsing at remote/third-party API boundaries, JSONL parsing points, and subprocess output. Fall back to the lenient/default form only for user-authored config files that may legitimately contain comments or trailing commas. This pattern keeps strict validation where appropriate and preserves leniency for internal/config data.

Applied to files:

  • src/dev-dashboard/lib/tmux/claude-pane-title.test.ts
  • src/utils/tmux/types.ts
  • src/dev-dashboard/lib/tmux/claude-pane-title.ts
  • src/dev-dashboard/contract/dto.ts
  • src/utils/tmux/sessions.ts
  • src/dev-dashboard/ui/src/lib/api.ts
📚 Learning: 2026-03-12T01:26:27.000Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 95
File: src/debugging-master/commands/tail.ts:0-0
Timestamp: 2026-03-12T01:26:27.000Z
Learning: In the genesiscz/GenesisTools repository, prefer using SafeJSON.parse(text, { strict: true }) (2-argument form) at all non-config JSON boundaries such as API responses, JSONL parsers, cache files, and subprocess stdout. Reserve the lenient default (SafeJSON.parse(text) with no options) only for user-authored config files that may legitimately contain comments or trailing commas.

Applied to files:

  • src/dev-dashboard/lib/tmux/claude-pane-title.test.ts
  • src/utils/tmux/types.ts
  • src/dev-dashboard/lib/tmux/claude-pane-title.ts
  • src/dev-dashboard/contract/dto.ts
  • src/utils/tmux/sessions.ts
  • src/dev-dashboard/ui/src/lib/api.ts
📚 Learning: 2026-03-12T01:26:24.859Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 95
File: src/azure-devops/commands/history-sync.ts:0-0
Timestamp: 2026-03-12T01:26:24.859Z
Learning: In GenesisTools, ensure SafeJSON.parse is called with exactly two arguments. Use SafeJSON.parse(text, { strict: true }) for strict RFC 8259 validation, or pass a reviver function as the second argument. Do not call SafeJSON.parse(text, null, { strict: true }) since the function signature does not support a three-argument form. Apply this guideline to all TypeScript files that use SafeJSON.parse (e.g., src/utils/json.ts) and other related code.

Applied to files:

  • src/dev-dashboard/lib/tmux/claude-pane-title.test.ts
  • src/utils/tmux/types.ts
  • src/dev-dashboard/lib/tmux/claude-pane-title.ts
  • src/dev-dashboard/contract/dto.ts
  • src/utils/tmux/sessions.ts
  • src/dev-dashboard/ui/src/lib/api.ts
📚 Learning: 2026-03-17T01:30:56.939Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 107
File: src/utils/macos/tts.ts:130-139
Timestamp: 2026-03-17T01:30:56.939Z
Learning: In genesiscz/GenesisTools, do not suggest converting two-argument functions with an optional second parameter (for example setMute(muted: boolean, app?: string)) to an object-parameter form. The project prefers simple positional parameters for short utility functions, even when an optional argument is present. The object-parameter guideline should only apply when a function has 3 or more parameters.

Applied to files:

  • src/dev-dashboard/lib/tmux/claude-pane-title.test.ts
  • src/utils/tmux/types.ts
  • src/dev-dashboard/lib/tmux/claude-pane-title.ts
  • src/dev-dashboard/contract/dto.ts
  • src/utils/tmux/sessions.ts
  • src/dev-dashboard/ui/src/lib/api.ts
📚 Learning: 2026-03-22T22:19:49.876Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 119
File: src/indexer/index.ts:41-56
Timestamp: 2026-03-22T22:19:49.876Z
Learning: When using Bun projects, treat `import.meta.dir` as an absolute directory path provided by Bun. If you build paths by concatenating with `import.meta.dir` (e.g., `import.meta.dir + "/file.ts"`), do not require `path.resolve()` as it would be redundant. Only apply `path.resolve()` guidance when the base path is relative (not when the base is already an absolute `import.meta.dir`).

Applied to files:

  • src/dev-dashboard/lib/tmux/claude-pane-title.test.ts
  • src/utils/tmux/types.ts
  • src/dev-dashboard/lib/tmux/claude-pane-title.ts
  • src/dev-dashboard/contract/dto.ts
  • src/utils/tmux/sessions.ts
  • src/dev-dashboard/ui/src/lib/api.ts
📚 Learning: 2026-06-30T19:44:04.852Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 227
File: src/agents/tests/matrix-e2e.test.ts:0-0
Timestamp: 2026-06-30T19:44:04.852Z
Learning: In the GenesisTools repo, do not flag code that passes `env: { ...process.env, ... }` into `Bun.spawn()` (i.e., forwarding the inherited environment to a child process) as a violation of the env-helper guideline by itself. Forwarding inherited environment to a subprocess is not the same as application/test logic directly reading configuration from `process.env`. Continue to flag direct `process.env` reads used in TypeScript logic (e.g., feature gates) per the env-helper guideline.

Applied to files:

  • src/dev-dashboard/lib/tmux/claude-pane-title.test.ts
  • src/utils/tmux/types.ts
  • src/dev-dashboard/lib/tmux/claude-pane-title.ts
  • src/dev-dashboard/contract/dto.ts
  • src/utils/tmux/sessions.ts
  • src/dev-dashboard/ui/src/lib/api.ts
📚 Learning: 2026-03-12T03:48:42.474Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 104
File: src/darwinkit/index.ts:146-156
Timestamp: 2026-03-12T03:48:42.474Z
Learning: In TypeScript files that use Commander subcommands and exit after showing help, replace code after Command.help() with the pattern: call sub.outputHelp(); (returns void) followed by process.exit(0) or process.exit(1). This avoids TS7027 unreachable-code because Command.help() returns never. Apply this pattern in all src/**/*.ts files where subcommands need to display help before exiting.

Applied to files:

  • src/dev-dashboard/lib/tmux/claude-pane-title.test.ts
  • src/utils/tmux/types.ts
  • src/dev-dashboard/lib/tmux/claude-pane-title.ts
  • src/dev-dashboard/contract/dto.ts
  • src/utils/tmux/sessions.ts
  • src/dev-dashboard/ui/src/lib/api.ts
📚 Learning: 2026-03-22T22:19:53.048Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 119
File: src/utils/search/stores/qdrant-vector-store.test.ts:192-206
Timestamp: 2026-03-22T22:19:53.048Z
Learning: In src/**/*.test.ts, it is acceptable to include comments that explain the semantic role or conceptual grouping of numeric/vector test data clusters (e.g., “Cluster 1: 'code' vectors”, “Query close to 'docs' cluster”). Even if variable/identifier names partially suggest intent, these comments should be treated as readable context (describing how clusters/queries relate conceptually) rather than “obvious comments,” and should not be flagged by the no-obvious-comments rule when they genuinely clarify the test data grouping and relationships.

Applied to files:

  • src/dev-dashboard/lib/tmux/claude-pane-title.test.ts
📚 Learning: 2026-05-05T11:58:33.420Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 163
File: src/indexer/lib/sources/mail-source.dateSent.probe.test.ts:0-0
Timestamp: 2026-05-05T11:58:33.420Z
Learning: This repo uses Biome 2.x. The console lint rule is `noConsole` (located at `lint/suspicious/noConsole`), not `noConsoleLog`. In this codebase, `noConsole` is disabled in `biome.json`, so adding a `// biome-ignore lint/suspicious/noConsole:<...>` suppression comment is a no-op and should be avoided (CI flags it as having no effect). When reviewing, do not suggest adding Biome suppression comments for console usage; if a `console.*` call must remain, leave it without a `biome-ignore` comment.

Applied to files:

  • src/dev-dashboard/lib/tmux/claude-pane-title.test.ts
  • src/utils/tmux/types.ts
  • src/dev-dashboard/lib/tmux/claude-pane-title.ts
  • src/dev-dashboard/contract/dto.ts
  • src/dev-dashboard/ui/src/components/TmuxSessionsPanel.tsx
  • src/utils/tmux/sessions.ts
  • src/dev-dashboard/ui/src/lib/api.ts
📚 Learning: 2026-07-29T06:42:30.009Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 300
File: src/ai-proxy/lib/providers/openai-api-key.ts:0-0
Timestamp: 2026-07-29T06:42:30.009Z
Learning: In this GenesisTools TypeScript codebase, do not use `as never` type assertions. Treat `as never` as the same code smell as `as any`—avoid both and don’t introduce `as never` during reviews.

For API-key accessor objects created by `createApiKeyAccessor` (e.g., `env.ai.openai`), use only the supported accessor methods: `getKey`, `getEnvKey`, and `hasKey`. Do not recommend or call `getApiKey` (it does not exist).

Applied to files:

  • src/dev-dashboard/lib/tmux/claude-pane-title.test.ts
  • src/utils/tmux/types.ts
  • src/dev-dashboard/lib/tmux/claude-pane-title.ts
  • src/dev-dashboard/contract/dto.ts
  • src/dev-dashboard/ui/src/components/TmuxSessionsPanel.tsx
  • src/utils/tmux/sessions.ts
  • src/dev-dashboard/ui/src/lib/api.ts
📚 Learning: 2026-05-17T14:59:03.963Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 167
File: src/dev-dashboard/lib/ttyd/manager.ts:1-1
Timestamp: 2026-05-17T14:59:03.963Z
Learning: When spawning long-lived daemon child processes in this repo with Bun.spawn (e.g., dashboard subprocess managers), always use Bun’s object form: `Bun.spawn({ cmd, detached: true, ... })` and then call `.unref()` on the returned process. Use `detached: true` (requires Bun >= 0.6.0; it creates a new process group / uses setsid) so the child does not receive SIGHUP when the dashboard parent exits; also call `.unref()` so the parent can exit without waiting on the child. Do not omit `detached: true` for daemon-like subprocesses, since it can cause the child to die with the parent and lead to proxy failures (e.g., 502s) on routes depending on that process.

Applied to files:

  • src/dev-dashboard/lib/tmux/claude-pane-title.test.ts
  • src/dev-dashboard/lib/tmux/claude-pane-title.ts
📚 Learning: 2026-05-18T14:02:30.445Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 171
File: src/utils/ui/layouts/AuthLayout.tsx:34-34
Timestamp: 2026-05-18T14:02:30.445Z
Learning: When reviewing a PR, before leaving any comment on a specific file and hunk, verify that the file (and the relevant lines) actually exist in the PR’s current diff. For example, use `git diff --name-only <base>...<head>` (or the PR’s file list) to confirm the file is part of the diff, since pre-rebase/stale hunk references can lead to incorrect or outdated comments.

Applied to files:

  • src/dev-dashboard/lib/tmux/claude-pane-title.test.ts
  • src/utils/tmux/types.ts
  • src/dev-dashboard/lib/tmux/claude-pane-title.ts
  • src/dev-dashboard/contract/dto.ts
  • src/dev-dashboard/ui/src/components/TmuxSessionsPanel.tsx
  • src/utils/tmux/sessions.ts
  • src/dev-dashboard/ui/src/lib/api.ts
📚 Learning: 2026-06-14T01:28:42.997Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 205
File: src/ai-spend/ai-spend.test.ts:208-219
Timestamp: 2026-06-14T01:28:42.997Z
Learning: When reviewing Bun-based TypeScript tests, do not treat `process.env.KEY = prev` as “setting the string \"undefined\"” if `prev` is actually `undefined`. In Bun, assigning `undefined` to a `process.env` entry does not create a literal `

Applied to files:

  • src/dev-dashboard/lib/tmux/claude-pane-title.test.ts
📚 Learning: 2026-07-07T15:43:17.189Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 229
File: src/claude/commands/tail.logging.test.ts:2-33
Timestamp: 2026-07-07T15:43:17.189Z
Learning: In this repository’s Bun (`bun:test`) unit tests, avoid mocking `node:fs` at the module level (e.g., `jest.mock`-style or top-level mock declarations), because the mock can leak across the shared test process and cause unrelated test failures. Instead, follow the `_setFindClaudeCommandTestHooks` approach used in `src/utils/claude/index.ts`: expose a dedicated test-hooks setter for the function’s dependencies (for example, add something like `_setGetProjectDirsTestHooks` alongside the relevant implementation, such as in `src/claude/commands/tail.ts`) so tests can inject dependency failures (e.g., make `readdirSync` throw) directly into the function under test. Ensure you restore/reset the hooks in `afterEach` to prevent cross-test contamination.

Applied to files:

  • src/dev-dashboard/lib/tmux/claude-pane-title.test.ts
📚 Learning: 2026-07-07T15:46:41.554Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 229
File: src/stash/commands/db-cleanup.test.ts:68-70
Timestamp: 2026-07-07T15:46:41.554Z
Learning: For this GenesisTools repo, do not recommend manually saving/restoring or resetting `process.exitCode` around assertions in individual tests. Tests run under Bun with `bunfig.toml` `[test].preload` pointing to `src/utils/bun/preload-test-process-exit.ts`, which registers a shared `afterEach(() => { process.exitCode = 0; })` for the shared `bun test` process—so stale `process.exitCode` should be cleared structurally between tests. Only consider `process.exitCode` save/restore if a test is executed outside this Bun test harness / preload flow.

Applied to files:

  • src/dev-dashboard/lib/tmux/claude-pane-title.test.ts
📚 Learning: 2026-07-09T11:46:24.499Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 230
File: src/dev-dashboard/server/routes/boards-annotations.test.ts:319-353
Timestamp: 2026-07-09T11:46:24.499Z
Learning: Do not flag a missing blank line before guard-style `if` statements in this codebase’s test files. Specifically, for `if` blocks that immediately throw/return as an early-exit validation (e.g., `if (!def) { throw ... }`, `if (result.kind !== "text") { throw ... }`), it’s acceptable for the `if` to follow directly after a preceding `const`/statement without an intervening blank line, since there is no enforced Biome rule that requires it.

Applied to files:

  • src/dev-dashboard/lib/tmux/claude-pane-title.test.ts
📚 Learning: 2026-07-15T12:16:34.484Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 263
File: src/youtube/lib/server/routes/videos.audio.test.ts:17-31
Timestamp: 2026-07-15T12:16:34.484Z
Learning: In Bun (`bun:test`) tests, module-level `mock.module(<path>, <factory>)` registrations should be treated as not leaking across test files in this repo. Code review should not flag `mock.module` calls in a test file as a cross-file mock leak risk due to missing `afterEach`/`mock.restore()` cleanup. Also note: `mock.restore()` only restores spy/function mocks and does not undo `mock.module` registrations. This guidance applies when the test file uses Bun’s `bun:test` and `mock.module` for module mocking.

Applied to files:

  • src/dev-dashboard/lib/tmux/claude-pane-title.test.ts
📚 Learning: 2026-03-25T21:01:55.569Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 129
File: src/utils/string.ts:104-111
Timestamp: 2026-03-25T21:01:55.569Z
Learning: For GenesisTools utilities under src/utils/**, Windows path support is required. When reviewing files in src/utils, treat POSIX-only path handling as a CRITICAL issue—e.g., code that searches for only "/" as the path separator or ignores "\\". Ensure path utility functions correctly handle both separators ("/" and "\\"), for example by using regex patterns like /[\\/]/ when parsing or splitting paths.

Applied to files:

  • src/utils/tmux/types.ts
  • src/utils/tmux/sessions.ts
📚 Learning: 2026-03-26T00:12:19.016Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 129
File: src/utils/string.ts:100-103
Timestamp: 2026-03-26T00:12:19.016Z
Learning: In this repo’s utility files (src/utils/**/*.ts), prefer minimal JSDoc for functions like truncatePath(path, maxLength). Do not add “obvious” implementation details (e.g., explicitly listing handled path separators such as / and \\) when the function/parameter names are self-documenting. Only expand JSDoc when there is non-obvious rationale, important design constraints, or edge-case behavior that would otherwise be unclear to reviewers.

Applied to files:

  • src/utils/tmux/types.ts
  • src/utils/tmux/sessions.ts
📚 Learning: 2026-05-18T09:50:40.172Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 170
File: src/utils/ui/layouts/DashboardLayout.tsx:1-2
Timestamp: 2026-05-18T09:50:40.172Z
Learning: In the genesiscz/GenesisTools repo, UI visual correctness is verified via Playwright before/after sweeps plus canary screenshots (see CLAUDE.md “No Tests” and evidence under .claude/docs/assets/). During code reviews of TSX UI code, do NOT suggest adding snapshot tests, Storybook/story tests, or introducing any UI test framework. If visual correctness is in question, ask whether the relevant Playwright sweep/canary screenshot evidence has been captured and linked in .claude/docs/assets/ instead of proposing new UI test infrastructure.

Applied to files:

  • src/dev-dashboard/ui/src/components/TmuxSessionsPanel.tsx
📚 Learning: 2026-05-18T09:50:40.172Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 170
File: src/utils/ui/layouts/DashboardLayout.tsx:1-2
Timestamp: 2026-05-18T09:50:40.172Z
Learning: For this repository (genesiscz/GenesisTools), UI verification is intentionally done via Playwright: before/after sweep screenshot sweeps and canary runs, with evidence stored under .claude/docs/assets/ (e.g., ui-drift-*/ containing sweep/after PNGs). During code reviews of TSX/UI changes, do NOT recommend adding snapshot tests, story tests, or any new UI test framework. If visual correctness is questioned, first ask whether the relevant Playwright sweep/canary evidence has been captured (per the repo’s documented “No Tests” policy in CLAUDE.md) rather than requesting new test infrastructure.

Applied to files:

  • src/dev-dashboard/ui/src/components/TmuxSessionsPanel.tsx
📚 Learning: 2026-07-15T12:14:52.502Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 263
File: src/utils/ui/components/youtube/llm-confirm-dialog.tsx:0-0
Timestamp: 2026-07-15T12:14:52.502Z
Learning: When reviewing GenesisTools UI files under `src/**/ui/**/*.{ts,tsx}`, do not treat usage of raw semantic status/warning palette utilities (e.g., `amber-*` or similar `status-color`/warning colors) as a violation of the “avoid raw palette utilities” guideline if they are used specifically for status/warning indicators (e.g., warning panels). This is an accepted exception only because no dedicated theme token for warning/status colors exists yet. If a proper warning/status theme token is introduced later, then future reviews should start flagging raw palette/status-color usage in those warning/status contexts and prefer the new token.

Applied to files:

  • src/dev-dashboard/ui/src/components/TmuxSessionsPanel.tsx
  • src/dev-dashboard/ui/src/lib/api.ts
🔇 Additional comments (5)
src/dev-dashboard/ui/src/components/TmuxSessionsPanel.tsx (1)

279-316: LGTM!

src/dev-dashboard/contract/dto.ts (1)

116-123: Keep TmuxHubSession in one module.

This change repeats the new active-pane fields in the duplicate TmuxHubSession declaration in src/dev-dashboard/ui/src/lib/api.ts. Export the canonical contract from src/dev-dashboard/contract/dto.ts and import it in the UI API module. Otherwise, the server and UI declarations can drift on field optionality and units.

src/dev-dashboard/lib/tmux/claude-pane-title.test.ts (1)

11-17: LGTM!

src/dev-dashboard/lib/tmux/claude-pane-title.ts (1)

2-12: LGTM!

src/utils/tmux/types.ts (1)

5-18: LGTM!

Comment on lines +185 to +193
const alreadyInTtyd = (session.ttydTabs?.length ?? session.ttydTabIds.length) > 0;
const primaryTab = session.ttydTabs?.[0];
// Fall back to tmux's own active-pane facts. Reading these only off the ttyd binding left every
// unbound session (a plain `tools tmux create`) rendering as a bare name with no meta at all.
const cwd = primaryTab?.cwd ?? session.cwd;
const lastCommand = primaryTab?.lastCommand ?? session.command;
const shortCwd = cwd ? shortenPath(cwd) : null;
const topic = claudeTopicFromTitle(session.title);
const idleFor = session.lastActivity ? formatSinceSeconds(session.lastActivity) : null;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Use the ID fallback when ttydTabs is empty.

Line 185 treats an empty ttydTabs array as authoritative because ?? only falls back for null or undefined. If ttydTabIds contains bindings, the row incorrectly shows “Attach in ttyd” and hides the existing ttyd state. Use session.ttydTabs?.length || session.ttydTabIds.length.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/dev-dashboard/ui/src/components/TmuxSessionsPanel.tsx` around lines 185 -
193, Update the alreadyInTtyd calculation in the TmuxSessionsPanel component to
fall back to session.ttydTabIds.length when session.ttydTabs is empty, using
length-based fallback semantics rather than nullish coalescing. Preserve the
existing positive-state behavior when either collection contains bindings.

Comment on lines +220 to +226
{topic ? (
<p className="truncate font-mono text-[10px] text-emerald-300/80" title={session.title}>
{topic}
</p>
) : null}
{shortCwd || lastCommand ? (
<p className="truncate font-mono text-[10px] text-zinc-400" title={cwd}>

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win

Replace raw palette utilities with shared theme tokens.

Lines 221 and 226 introduce raw emerald-* and zinc-* utilities in dashboard UI. Use the shared semantic text tokens for the Claude topic and metadata text.

As per coding guidelines, “use theme tokens and shared UI primitives, avoid raw zinc-* or white/NN palettes.” Based on learnings, the raw status-color exception applies only to status or warning indicators.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/dev-dashboard/ui/src/components/TmuxSessionsPanel.tsx` around lines 220 -
226, Update the topic and metadata text elements in TmuxSessionsPanel’s session
rendering to replace the raw text-emerald and text-zinc utilities with the
dashboard’s shared semantic theme text tokens. Preserve the existing typography,
truncation, and layout classes while using the appropriate tokens for Claude
topic text and metadata text.

Sources: Coding guidelines, Learnings

Comment on lines +190 to +199
[
"#{session_name}",
"#{session_attached}",
"#{session_windows}",
"#{pane_current_command}",
"#{pane_current_path}",
"#{session_created}",
"#{session_activity}",
"#{pane_title}",
].join("\t"),

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

socket="coderabbit-review-$$"
tmp_dir="$(mktemp -d)"
cwd="${tmp_dir}/tab$(printf '\t')path"

cleanup() {
    tmux -L "$socket" kill-server >/dev/null 2>&1 || true
    rm -rf "$tmp_dir"
}
trap cleanup EXIT

mkdir -p "$cwd"
tmux -L "$socket" -f /dev/null new-session -d -s review -c "$cwd" "sleep 30"
tmux -L "$socket" -f /dev/null select-pane -t review:0.0 -T $'line1\nline2'

tmux -L "$socket" list-sessions -F $'#{session_name}\t#{session_attached}\t#{session_windows}\t#{pane_current_command}\t#{pane_current_path}\t#{session_created}\t#{session_activity}\t#{pane_title}' | cat -vet

Repository: genesiscz/GenesisTools

Length of output: 203


🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

printf '%s\n' '--- candidate files ---'
git ls-files 'src/utils/tmux/sessions.ts' '*tmux*' '*session*' | head -80

printf '%s\n' '--- file outline ---'
ast-grep outline src/utils/tmux/sessions.ts --view compact 2>/dev/null || true

printf '%s\n' '--- relevant source ---'
cat -n src/utils/tmux/sessions.ts | sed -n '150,250p'

printf '%s\n' '--- parser and call-site references ---'
rg -n -C 4 'listTmuxSessions|pane_current_path|createdRaw|activityRaw|titleParts|split\\("\\\\t"\\)' src test tests 2>/dev/null || true

Repository: genesiscz/GenesisTools

Length of output: 3579


🏁 Script executed:

#!/usr/bin/env bash
set -u

printf '%s\n' '--- matching paths ---'
fd -i 'sessions' . | head -100
fd -i 'tmux' . | head -100

printf '%s\n' '--- exact symbol search ---'
rg -n -C 5 'listTmuxSessions|createdRaw|activityRaw|titleParts|pane_current_path' . --glob '!node_modules' --glob '!dist' --glob '!build' 2>/dev/null | head -240

printf '%s\n' '--- repository status and tracked target ---'
git status --short -- src/utils/tmux/sessions.ts
git ls-files --error-unmatch src/utils/tmux/sessions.ts 2>&1 || true

Repository: genesiscz/GenesisTools

Length of output: 15884


🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

printf '%s\n' '--- listTmuxSessions implementation ---'
cat -n src/utils/tmux/sessions.ts | sed -n '174,242p'

printf '%s\n' '--- related parser implementation ---'
cat -n src/utils/tmux/sessions.ts | sed -n '270,312p'

printf '%s\n' '--- existing tests ---'
cat -n src/utils/tmux/sessions.test.ts | sed -n '1,125p'

printf '%s\n' '--- tmux utility execution and test hooks ---'
cat -n src/utils/tmux/sessions.ts | sed -n '1,174p'

Repository: genesiscz/GenesisTools

Length of output: 16238


🌐 Web query:

tmux format strings pane_current_path tab escaping list-sessions -F literal tab delimiter

💡 Result:

To work with tab characters and format strings in tmux, use the following guidelines based on current tmux documentation and behavior. Format Strings and Tab Characters: Tmux format strings generally do not have a dedicated built-in format variable for a literal tab character. However, in configuration strings (like those set via set-option), you can insert a literal tab by using standard shell expansion within double quotes if your shell supports it (e.g., using $'\t' in bash or zsh), or by inserting a literal tab character directly into your tmux.conf file [1]. When using tmux command-line arguments that interpret escape sequences, such as those that support the \t notation, you must ensure the shell does not consume the backslash prematurely [2]. Listing Sessions with Custom Delimiters: To use a tab as a delimiter in tmux list-sessions (or any command accepting a -F format string), you can include the tab character directly in the format string. Example using a literal tab: tmux list-sessions -F "#{session_name} #{session_windows}" In this example, the space between the braces is a literal tab character. If you are constructing this command in a script, you can use printf or shell variables to inject the tab: tab=$'\t' tmux list-sessions -F "#{session_name}${tab}#{session_windows}" Pane Current Path: To display the path of the current pane, use the #{pane_current_path} variable [3]. To display only the basename (the final directory name) instead of the full path, use the #{b:pane_current_path} modifier [4]. Example to display the basename: set -g status-left "#{b:pane_current_path}" Summary of relevant modifiers: - #{b:variable}: Basename of the path (equivalent to basename(3)) [4]. - #{d:variable}: Directory name of the path (equivalent to dirname(3)) [4]. - #{l:string}: Treats the provided string as a literal [4]. Note on Escaping: Tmux uses # as the escape character for format variables [5][6]. If you need to print a literal #, use ## [7][8]. For other special characters like commas or braces within complex format logic (like conditionals), you may need to escape them with # (e.g., #, for a comma) [8].

Citations:


🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

python3 - <<'PY'
# Read-only behavioral verifier for the parser shape shown in src/utils/tmux/sessions.ts.
name = "review"
cwd = "/tmp/tab\tdir"
line = "\t".join([
    name, "0", "1", "zsh", cwd, "100", "200", "pane title",
])
fields = line.rstrip().split("\t")
name_, attached, windows, command, parsed_cwd, created, activity, *title_parts = fields

print("encoded fields:", fields)
print("parsed cwd:", repr(parsed_cwd))
print("createdRaw:", repr(created))
print("activityRaw:", repr(activity))
print("title:", repr("\t".join(title_parts)))
print("metadata shifted:", parsed_cwd != cwd and created != "100" and activity != "200")
PY

printf '%s\n' '--- tmux source references, if network access is available ---'
curl -fsSL https://raw.githubusercontent.com/tmux/tmux/master/format.c |
    rg -n -C 3 'pane_current_path|format_expand|format_cb|escape' | head -160 || true

Repository: genesiscz/GenesisTools

Length of output: 4394


Use collision-free framing for tmux session fields.

If #{pane_current_path} contains a tab, split("\t") shifts createdRaw, activityRaw, and titleParts, which corrupts the returned metadata. Encode dynamic fields before formatting and decode them after parsing, or use length-delimited output. Add a regression test with a tab-containing cwd.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/utils/tmux/sessions.ts` around lines 190 - 199, Update the tmux session
field formatting and parsing around the query field list so tab characters in
pane_current_path cannot shift subsequent values. Use collision-free encoding
with matching decoding, or length-delimited output, while preserving existing
metadata mapping; add a regression test covering a tab-containing cwd.

@eve-bot-lovinka eve-bot-lovinka Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🐉 eve review — 🟡 Review comments

7c3c7f0 · 5 actionable findings · view run ↗

Severity Count
🟡 Medium 1
🔵 Low 4

return null;
}

return parseClaudePaneTitle(pane.title);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Quality | 🟡 Medium · confidence 99/100

⚠️ Potential issue

Do not sanitize punctuation in the displayed Claude topic

parseClaudePaneTitle is a session-name parser: it deliberately replaces : and . because they are invalid in tmux target names (its tests expect Fix v1.2 to become Fix v1-2). The new title field is informational display text, so reusing that parser corrupts normal topic punctuation. It also makes bound sessions disagree with the UI fallback, whose claudeTopicFromTitle only strips the marker and preserves punctuation. Split marker extraction from tmux-name sanitization, and use the non-sanitizing form here.

🧩 Analysis

Grep evidence: replace\(/\[:\.\]/g, "-"\)|parseClaudePaneTitle\("✳ Fix v1\.2 bug"\)|title: claudeTopicForPane

label: string;
cwd?: string;
lastCommand?: string;
title?: string;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🏛️ Architecture | 🔵 Low · confidence 99/100

⚠️ Potential issue

Consume the contract-owned hub DTO instead of duplicating it

src/dev-dashboard/contract/dto.ts:TmuxHubSession explicitly says the API DTO is owned by the contract, yet the browser API layer maintains a second full TmuxHubSession declaration and this change has to add title to both. Importing the contract type (or TmuxSessionsRes from the endpoint contract) would make contract drift impossible and preserve the package boundary already used by other types at the top of this file.

🧩 Analysis

Grep evidence: export interface TmuxHubSession

// concurrently. (hydrate stays sequential: prune reads the hydrated registry.)
const [, panesByTmux] = await Promise.all([
prof.measureAsync("list.prune", () => pruneDeadSessions()),
// One list-sessions call serving THREE consumers: heal's existence set,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Quality | 🔵 Low · confidence 98/100

🛠️ Refactor suggestion

Update the stale three-consumer comment

The adjacent comment still says the single tmux listing serves three consumers, including syncNames, but this change deletes syncNamesFromClaudePaneTitles and its invocation. The result now serves only healing and return-value enrichment. This is particularly misleading in performance-sensitive lifecycle code because it claims a removed mutation still occurs during listing.

🧩 Analysis

Grep evidence: One list-sessions call serving THREE consumers|syncNamesFromClaudePaneTitles

Suggested change
// One list-sessions call serving THREE consumers: heal's existence set,
// One list-sessions call serving heal's existence set and the enrichment below.

return {
...session,
lastCommand: pane?.command || undefined,
title: claudeTopicForPane(pane) ?? undefined,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧪 Tests | 🔵 Low · confidence 98/100

⚠️ Potential issue

Cover the new derived-title behavior

The changed behavior derives TtydSession.title, propagates it through hub tabs, and maps it to mobile-tab metadata, but the existing manager and terminal-tabs.test.ts cases contain no title assertions. Add tests proving a Claude pane title is exposed without changing name, non-Claude/default titles are omitted, punctuation is preserved, and buildTtydTabs maps the topic to lastLine. This would also catch the current session-name sanitization leak.

🧩 Analysis

Grep evidence: expect\(.*title|lastLine.*expect|title: claudeTopicForPane

listTmuxSessionActivePanes,
renameTmuxSession,
sessionExists,
type TmuxActivePaneInfo,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧪 Tests | 🔵 Low · confidence 65/100

⚠️ Potential issue

No test changes accompany 22 added lines in src/dev-dashboard/lib/ttyd/manager.ts

This PR adds 22 lines to src/dev-dashboard/lib/ttyd/manager.ts with no touching test change (no changed test names manager and none under src/dev-dashboard/lib/ttyd/). If the change alters behavior, add or extend a test that pins it (deterministic static check — ignore if the change is genuinely untestable or covered elsewhere).

🧩 Analysis

Grep evidence: manager

@eve-bot-lovinka

Copy link
Copy Markdown

Delta review completed and posted.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (4)
src/dev-dashboard/ui/src/lib/api.ts (1)

24-48: 📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win

Reuse the shared TmuxHubSession contract instead of a local copy.

This interface duplicates TmuxHubSession from src/dev-dashboard/contract/dto.ts, including the nested ttydTabs shape also duplicated in src/dev-dashboard/lib/tmux/hub.ts. Import the contract type here instead.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/dev-dashboard/ui/src/lib/api.ts` around lines 24 - 48, Remove the local
TmuxHubSession interface from the API module and import the shared
TmuxHubSession contract from the dashboard DTO module. Update any dependent
references to use that imported type, preserving the existing ttydTabs structure
and behavior.
src/dev-dashboard/lib/tmux/hub.ts (1)

5-21: 📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win

TtydHubTab and TmuxHubSession duplicate the contract shapes.

This file independently defines TtydHubTab and TmuxHubSession rather than importing them from src/dev-dashboard/contract/dto.ts. The identical shape is also duplicated in src/dev-dashboard/ui/src/lib/api.ts. Import the contract types here instead of redefining them.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/dev-dashboard/lib/tmux/hub.ts` around lines 5 - 21, Replace the local
TtydHubTab and TmuxHubSession interface declarations in this module with imports
of the corresponding contract types from the dashboard DTO contract, and remove
the duplicate definitions while preserving all existing usages.
src/dev-dashboard/contract/dto.ts (1)

1-1: 📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win

TmuxHubSession and its nested ttydTabs shape are defined independently in three files. The same wire shape is hand-copied into the contract, the hub, and the UI API client instead of being defined once and imported; this PR had to add the new title field in all three places, which is the drift risk a single shared type would eliminate.

  • src/dev-dashboard/contract/dto.ts#L112-136: keep this as the canonical, exported definition of TmuxHubSession (and the ttydTabs entry shape, e.g. as a named TtydHubTab).
  • src/dev-dashboard/lib/tmux/hub.ts#L5-21: remove the locally defined TtydHubTab and TmuxHubSession; import both from src/dev-dashboard/contract/dto.ts.
  • src/dev-dashboard/ui/src/lib/api.ts#L24-48: remove the locally defined TmuxHubSession; import it from src/dev-dashboard/contract/dto.ts.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/dev-dashboard/contract/dto.ts` at line 1, Make
src/dev-dashboard/contract/dto.ts the single exported source of truth for
TmuxHubSession and its nested tab shape by defining or exporting a named
TtydHubTab there. In the hub module, remove the local TtydHubTab and
TmuxHubSession declarations and import the shared types; in the UI API module,
remove its local TmuxHubSession declaration and import the contract type
instead, preserving the existing wire shape including title.
src/dev-dashboard/lib/ttyd/manager.ts (1)

70-83: 📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win

Extract the duplicated ps inspection into a shared helper.

processMatchesSessionAsync and ttydProcessTargetsTmuxAsync both spawn /bin/ps -p <pid> -o command=, read stdout, and check exited the same way. Extract a shared readProcessCommand(pid): Promise<string | null> helper and have both functions call it, then apply their own post-processing to the result. This keeps the subprocess-spawning logic in one place so future fixes (timeouts, error handling) do not need to be applied twice.

♻️ Proposed extraction
+async function readProcessCommand(pid: number): Promise<string | null> {
+    const proc = Bun.spawn(["/bin/ps", "-p", String(pid), "-o", "command="], {
+        stdio: ["ignore", "pipe", "ignore"],
+    });
+    const stdout = await new Response(proc.stdout).text();
+    const exitCode = await proc.exited;
+
+    return exitCode === 0 ? stdout : null;
+}
+
 async function processMatchesSessionAsync(session: TtydSession): Promise<boolean> {
-    const proc = Bun.spawn(["/bin/ps", "-p", String(session.pid), "-o", "command="], {
-        stdio: ["ignore", "pipe", "ignore"],
-    });
-    const stdout = await new Response(proc.stdout).text();
-    const exitCode = await proc.exited;
-
-    if (exitCode !== 0) {
-        return false;
-    }
-
-    const cmd = stdout.trim();
+    const stdout = await readProcessCommand(session.pid);
+
+    if (stdout === null) {
+        return false;
+    }
+
+    const cmd = stdout.trim();
     return cmd.includes("ttyd") && cmd.includes(`/ttyd/${session.id}`);
 }

Also applies to: 278-294

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/dev-dashboard/lib/ttyd/manager.ts` around lines 70 - 83, Extract the
shared `/bin/ps` spawning, stdout reading, and exit-status handling from
processMatchesSessionAsync and ttydProcessTargetsTmuxAsync into a
readProcessCommand(pid): Promise<string | null> helper. Have the helper return
the trimmed command for successful processes and null for nonzero exits, then
preserve each caller’s existing matching or target-processing logic using that
result.
♻️ Duplicate comments (2)
src/dev-dashboard/ui/src/components/TmuxSessionsPanel.tsx (1)

206-233: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Replace raw palette utilities with shared theme tokens.

Lines 206, 209, 223, and 228-230 use raw emerald-*, zinc-*, and amber-* utilities for the Claude topic and metadata rows. Use the shared semantic text tokens instead.

As per coding guidelines, "use theme tokens and shared UI primitives, avoid raw zinc-* or white/NN palettes."

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/dev-dashboard/ui/src/components/TmuxSessionsPanel.tsx` around lines 206 -
233, Replace the raw emerald, zinc, and amber text color utilities in
TmuxSessionsPanel’s metadata, topic, and command rows with the project’s shared
semantic text theme tokens, including the Terminal icon styling. Preserve the
existing layout, typography, conditional rendering, and color distinctions while
removing direct palette utilities.

Source: Coding guidelines

src/dev-dashboard/contract/dto.ts (1)

112-136: 📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win

Centralize the TmuxHubSession/ttydTabs shape instead of repeating it.

This interface, including the nested ttydTabs entry shape, is duplicated verbatim in src/dev-dashboard/lib/tmux/hub.ts and src/dev-dashboard/ui/src/lib/api.ts. hub.ts now exports its own TtydHubTab, but this contract file does not import it, so the same shape has to be edited in three places — exactly what happened when title was added here in this PR. Export this contract type and have the other two files import it.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/dev-dashboard/contract/dto.ts` around lines 112 - 136, Export the
TmuxHubSession contract and its ttydTabs entry type from the DTO module, reusing
the existing exported TtydHubTab shape. Update hub.ts and the UI api.ts to
import these shared types and remove their duplicated session/tab definitions,
preserving all current fields including title.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@src/dev-dashboard/contract/dto.ts`:
- Line 1: Make src/dev-dashboard/contract/dto.ts the single exported source of
truth for TmuxHubSession and its nested tab shape by defining or exporting a
named TtydHubTab there. In the hub module, remove the local TtydHubTab and
TmuxHubSession declarations and import the shared types; in the UI API module,
remove its local TmuxHubSession declaration and import the contract type
instead, preserving the existing wire shape including title.

In `@src/dev-dashboard/lib/tmux/hub.ts`:
- Around line 5-21: Replace the local TtydHubTab and TmuxHubSession interface
declarations in this module with imports of the corresponding contract types
from the dashboard DTO contract, and remove the duplicate definitions while
preserving all existing usages.

In `@src/dev-dashboard/lib/ttyd/manager.ts`:
- Around line 70-83: Extract the shared `/bin/ps` spawning, stdout reading, and
exit-status handling from processMatchesSessionAsync and
ttydProcessTargetsTmuxAsync into a readProcessCommand(pid): Promise<string |
null> helper. Have the helper return the trimmed command for successful
processes and null for nonzero exits, then preserve each caller’s existing
matching or target-processing logic using that result.

In `@src/dev-dashboard/ui/src/lib/api.ts`:
- Around line 24-48: Remove the local TmuxHubSession interface from the API
module and import the shared TmuxHubSession contract from the dashboard DTO
module. Update any dependent references to use that imported type, preserving
the existing ttydTabs structure and behavior.

---

Duplicate comments:
In `@src/dev-dashboard/contract/dto.ts`:
- Around line 112-136: Export the TmuxHubSession contract and its ttydTabs entry
type from the DTO module, reusing the existing exported TtydHubTab shape. Update
hub.ts and the UI api.ts to import these shared types and remove their
duplicated session/tab definitions, preserving all current fields including
title.

In `@src/dev-dashboard/ui/src/components/TmuxSessionsPanel.tsx`:
- Around line 206-233: Replace the raw emerald, zinc, and amber text color
utilities in TmuxSessionsPanel’s metadata, topic, and command rows with the
project’s shared semantic text theme tokens, including the Terminal icon
styling. Preserve the existing layout, typography, conditional rendering, and
color distinctions while removing direct palette utilities.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 119a2119-7266-4687-9e25-f8d657b52769

📥 Commits

Reviewing files that changed from the base of the PR and between c2035c8 and 7c3c7f0.

📒 Files selected for processing (8)
  • src/dev-dashboard/contract/dto.ts
  • src/dev-dashboard/lib/tmux/hub.ts
  • src/dev-dashboard/lib/ttyd/manager.ts
  • src/dev-dashboard/lib/ttyd/types.ts
  • src/dev-dashboard/ui/src/components/TmuxSessionsPanel.tsx
  • src/dev-dashboard/ui/src/lib/api.ts
  • src/dev-dashboard/ui/src/lib/terminal-tabs.ts
  • src/dev-dashboard/ui/src/routes/ttyd.tsx
📜 Review details
⏰ Context from checks skipped due to timeout. (1)
  • GitHub Check: test (ubuntu-latest, 4)
🧰 Additional context used
📓 Path-based instructions (3)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.{ts,tsx}: Use bun run test or bun scripts/test.ts <paths> for tests; never invoke bare bun test.
Never read process.env directly in application code; import and use env from @genesiscz/utils/env, including its testing override APIs.
Fix shared bugs at their root implementation rather than applying repeated patches at individual call sites.
Do not add file-path comments as the first line of files or comments that merely restate obvious code.
Always use SafeJSON.parse() and SafeJSON.stringify() from @genesiscz/utils/json; do not use the restricted global JSON. Use { strict: true } or { jsonl: true } when strict behavior is required.
Do not use one-line if statements; always use braces and block form.
Place an empty line before if unless the preceding line is a variable declaration used by that condition, and place an empty line after a closing } unless followed by else, catch, finally, or another }.
Use an object parameter when a function has three or more parameters, optional parameters, or mixed required and optional parameters; positional parameters are acceptable for one or two obvious required arguments.
Never use as any; use proper narrowing, type guards, or explicit interfaces. For unions, use discriminant checks.
Prefer error: err over converting errors to strings when the logging error field accepts unknown.
Never swallow errors with a bare catch {}; log the caught error with context at least at debug or warning level.

Files:

  • src/dev-dashboard/ui/src/routes/ttyd.tsx
  • src/dev-dashboard/ui/src/lib/terminal-tabs.ts
  • src/dev-dashboard/lib/ttyd/types.ts
  • src/dev-dashboard/contract/dto.ts
  • src/dev-dashboard/ui/src/components/TmuxSessionsPanel.tsx
  • src/dev-dashboard/ui/src/lib/api.ts
  • src/dev-dashboard/lib/tmux/hub.ts
  • src/dev-dashboard/lib/ttyd/manager.ts
src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

src/**/*.{ts,tsx}: For web UI under src/<tool>/ui, src/dashboard, or src/dev-dashboard/ui, read the shared design-system documentation first; use theme tokens and shared UI primitives, avoid raw zinc-* or white/NN palettes, do not override Card surfaces, choose intentional Button/Card variants, and use the shared shell/auth layout.
Use the canonical dynamic model and pricing sources; do not add new rate tables. The static src/ai-proxy/lib/billing/pricing.ts table is the deliberate offline invoicing exception and must not be duplicated or deduplicated into the dynamic path.
Determine subscription billing from DetectedProvider.subscription, never from name suffixes; resolve API keys through AIConfig.resolveApiKey without copying referenced environment values.

Files:

  • src/dev-dashboard/ui/src/routes/ttyd.tsx
  • src/dev-dashboard/ui/src/lib/terminal-tabs.ts
  • src/dev-dashboard/lib/ttyd/types.ts
  • src/dev-dashboard/contract/dto.ts
  • src/dev-dashboard/ui/src/components/TmuxSessionsPanel.tsx
  • src/dev-dashboard/ui/src/lib/api.ts
  • src/dev-dashboard/lib/tmux/hub.ts
  • src/dev-dashboard/lib/ttyd/manager.ts
src/**/*.ts

📄 CodeRabbit inference engine (CLAUDE.md)

src/**/*.ts: Place general-purpose helper functions in src/utils/; keep tool-specific logic inside its tool directory.
Before showing prompts, check isInteractive(); in non-interactive mode, either report required CLI flags with suggestCommand() or use a sensible default.
Use Bun.spawn() for external commands and properly consume stdout and stderr streams.
Use Bun native file APIs such as Bun.write() for file operations.
When a tool misbehaves, inspect the day-stamped logs under ~/.genesis-tools/logs/ and search for the tool or error before hypothesizing or reproducing.
Log enough through @genesiscz/utils/logger to reconstruct execution: decision branches, external-resource accesses, configuration resolution, and result counts.
Use named logger and out imports from @genesiscz/utils/logger; logger is for diagnostics and out.result() or out.print() are the only stdout result writers.
For commander entrypoints, end with await runTool(program, { tool }); use execTool for subprocess spawning.
Use @genesiscz/utils/cli/ui instead of out.log.* for high-density CLI status, while retaining out.print() and out.result() as the only stdout writers.
Use shared table helpers from @genesiscz/utils/table for multi-column human inventory output; do not hand-roll new Table({ chars: ... }) or copy box-drawing characters. Use out.result() with --json for machine-readable output.
Use the canonical dashboard and web-service registry in src/utils/ui/dashboards.ts; never hardcode repository web-server ports.

Files:

  • src/dev-dashboard/ui/src/lib/terminal-tabs.ts
  • src/dev-dashboard/lib/ttyd/types.ts
  • src/dev-dashboard/contract/dto.ts
  • src/dev-dashboard/ui/src/lib/api.ts
  • src/dev-dashboard/lib/tmux/hub.ts
  • src/dev-dashboard/lib/ttyd/manager.ts
🧠 Learnings (27)
📚 Learning: 2026-05-05T11:58:33.420Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 163
File: src/indexer/lib/sources/mail-source.dateSent.probe.test.ts:0-0
Timestamp: 2026-05-05T11:58:33.420Z
Learning: This repo uses Biome 2.x. The console lint rule is `noConsole` (located at `lint/suspicious/noConsole`), not `noConsoleLog`. In this codebase, `noConsole` is disabled in `biome.json`, so adding a `// biome-ignore lint/suspicious/noConsole:<...>` suppression comment is a no-op and should be avoided (CI flags it as having no effect). When reviewing, do not suggest adding Biome suppression comments for console usage; if a `console.*` call must remain, leave it without a `biome-ignore` comment.

Applied to files:

  • src/dev-dashboard/ui/src/routes/ttyd.tsx
  • src/dev-dashboard/ui/src/lib/terminal-tabs.ts
  • src/dev-dashboard/lib/ttyd/types.ts
  • src/dev-dashboard/contract/dto.ts
  • src/dev-dashboard/ui/src/components/TmuxSessionsPanel.tsx
  • src/dev-dashboard/ui/src/lib/api.ts
  • src/dev-dashboard/lib/tmux/hub.ts
  • src/dev-dashboard/lib/ttyd/manager.ts
📚 Learning: 2026-07-29T06:42:30.009Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 300
File: src/ai-proxy/lib/providers/openai-api-key.ts:0-0
Timestamp: 2026-07-29T06:42:30.009Z
Learning: In this GenesisTools TypeScript codebase, do not use `as never` type assertions. Treat `as never` as the same code smell as `as any`—avoid both and don’t introduce `as never` during reviews.

For API-key accessor objects created by `createApiKeyAccessor` (e.g., `env.ai.openai`), use only the supported accessor methods: `getKey`, `getEnvKey`, and `hasKey`. Do not recommend or call `getApiKey` (it does not exist).

Applied to files:

  • src/dev-dashboard/ui/src/routes/ttyd.tsx
  • src/dev-dashboard/ui/src/lib/terminal-tabs.ts
  • src/dev-dashboard/lib/ttyd/types.ts
  • src/dev-dashboard/contract/dto.ts
  • src/dev-dashboard/ui/src/components/TmuxSessionsPanel.tsx
  • src/dev-dashboard/ui/src/lib/api.ts
  • src/dev-dashboard/lib/tmux/hub.ts
  • src/dev-dashboard/lib/ttyd/manager.ts
📚 Learning: 2026-05-16T23:06:22.685Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 167
File: src/dev-dashboard/ui/src/routes/obsidian.tsx:12-12
Timestamp: 2026-05-16T23:06:22.685Z
Learning: In genesiscz/GenesisTools, the dev-dashboard UI is intentionally desktop-first in the routes under src/dev-dashboard/ui/src/routes. For fixed two-column grid layouts, do not require responsive `md:` breakpoint utility classes (e.g., missing `md:` variants) as long as mobile/phone layout is handled via the dedicated separate mobile layout logic (panel stacking vertically). When reviewing, avoid flagging the two-column grid for “missing mobile responsiveness” solely due to absent `md:` breakpoint classes.

Applied to files:

  • src/dev-dashboard/ui/src/routes/ttyd.tsx
📚 Learning: 2026-05-18T09:50:40.172Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 170
File: src/utils/ui/layouts/DashboardLayout.tsx:1-2
Timestamp: 2026-05-18T09:50:40.172Z
Learning: In the genesiscz/GenesisTools repo, UI visual correctness is verified via Playwright before/after sweeps plus canary screenshots (see CLAUDE.md “No Tests” and evidence under .claude/docs/assets/). During code reviews of TSX UI code, do NOT suggest adding snapshot tests, Storybook/story tests, or introducing any UI test framework. If visual correctness is in question, ask whether the relevant Playwright sweep/canary screenshot evidence has been captured and linked in .claude/docs/assets/ instead of proposing new UI test infrastructure.

Applied to files:

  • src/dev-dashboard/ui/src/routes/ttyd.tsx
  • src/dev-dashboard/ui/src/components/TmuxSessionsPanel.tsx
📚 Learning: 2026-05-18T09:50:40.172Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 170
File: src/utils/ui/layouts/DashboardLayout.tsx:1-2
Timestamp: 2026-05-18T09:50:40.172Z
Learning: For this repository (genesiscz/GenesisTools), UI verification is intentionally done via Playwright: before/after sweep screenshot sweeps and canary runs, with evidence stored under .claude/docs/assets/ (e.g., ui-drift-*/ containing sweep/after PNGs). During code reviews of TSX/UI changes, do NOT recommend adding snapshot tests, story tests, or any new UI test framework. If visual correctness is questioned, first ask whether the relevant Playwright sweep/canary evidence has been captured (per the repo’s documented “No Tests” policy in CLAUDE.md) rather than requesting new test infrastructure.

Applied to files:

  • src/dev-dashboard/ui/src/routes/ttyd.tsx
  • src/dev-dashboard/ui/src/components/TmuxSessionsPanel.tsx
📚 Learning: 2026-05-18T14:02:30.445Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 171
File: src/utils/ui/layouts/AuthLayout.tsx:34-34
Timestamp: 2026-05-18T14:02:30.445Z
Learning: When reviewing a PR, before leaving any comment on a specific file and hunk, verify that the file (and the relevant lines) actually exist in the PR’s current diff. For example, use `git diff --name-only <base>...<head>` (or the PR’s file list) to confirm the file is part of the diff, since pre-rebase/stale hunk references can lead to incorrect or outdated comments.

Applied to files:

  • src/dev-dashboard/ui/src/routes/ttyd.tsx
  • src/dev-dashboard/ui/src/lib/terminal-tabs.ts
  • src/dev-dashboard/lib/ttyd/types.ts
  • src/dev-dashboard/contract/dto.ts
  • src/dev-dashboard/ui/src/components/TmuxSessionsPanel.tsx
  • src/dev-dashboard/ui/src/lib/api.ts
  • src/dev-dashboard/lib/tmux/hub.ts
  • src/dev-dashboard/lib/ttyd/manager.ts
📚 Learning: 2026-07-15T12:14:52.502Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 263
File: src/utils/ui/components/youtube/llm-confirm-dialog.tsx:0-0
Timestamp: 2026-07-15T12:14:52.502Z
Learning: When reviewing GenesisTools UI files under `src/**/ui/**/*.{ts,tsx}`, do not treat usage of raw semantic status/warning palette utilities (e.g., `amber-*` or similar `status-color`/warning colors) as a violation of the “avoid raw palette utilities” guideline if they are used specifically for status/warning indicators (e.g., warning panels). This is an accepted exception only because no dedicated theme token for warning/status colors exists yet. If a proper warning/status theme token is introduced later, then future reviews should start flagging raw palette/status-color usage in those warning/status contexts and prefer the new token.

Applied to files:

  • src/dev-dashboard/ui/src/routes/ttyd.tsx
  • src/dev-dashboard/ui/src/lib/terminal-tabs.ts
  • src/dev-dashboard/ui/src/components/TmuxSessionsPanel.tsx
  • src/dev-dashboard/ui/src/lib/api.ts
📚 Learning: 2026-02-24T15:32:37.494Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 54
File: src/github/lib/output.ts:109-113
Timestamp: 2026-02-24T15:32:37.494Z
Learning: In TypeScript files under src/, do not require a leading blank line before an if statement that is the first statement inside a function body (immediately after the function signature). The blank line rule should only apply to if statements that come after other statements within the function body. Apply this guideline consistently across TS files in src to reduce unnecessary vertical whitespace and keep concise function bodies.

Applied to files:

  • src/dev-dashboard/ui/src/lib/terminal-tabs.ts
  • src/dev-dashboard/lib/ttyd/types.ts
  • src/dev-dashboard/contract/dto.ts
  • src/dev-dashboard/ui/src/lib/api.ts
  • src/dev-dashboard/lib/tmux/hub.ts
  • src/dev-dashboard/lib/ttyd/manager.ts
📚 Learning: 2026-03-09T13:13:58.786Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 81
File: src/github/commands/get.ts:209-212
Timestamp: 2026-03-09T13:13:58.786Z
Learning: In the GenesisTools repo (genesiscz/GenesisTools), do not treat CI formatter warnings as enforceable formatting rules for TypeScript files under src/. Focus reviews on logical correctness and consistency with existing code patterns. For files under src (e.g., src/github/commands/get.ts), prioritize code structure, readability, naming, correctness, and adherence to project conventions over automated formatting warnings from CI tools.

Applied to files:

  • src/dev-dashboard/ui/src/lib/terminal-tabs.ts
  • src/dev-dashboard/lib/ttyd/types.ts
  • src/dev-dashboard/contract/dto.ts
  • src/dev-dashboard/ui/src/lib/api.ts
  • src/dev-dashboard/lib/tmux/hub.ts
  • src/dev-dashboard/lib/ttyd/manager.ts
📚 Learning: 2026-03-12T01:26:31.610Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 95
File: src/timely/utils/entry-processor.ts:0-0
Timestamp: 2026-03-12T01:26:31.610Z
Learning: In code paths where JSON is consumed, prefer strict RFC 8259 validation by using SafeJSON.parse(text, { strict: true }) instead of the lenient default. Apply this at non-config boundaries (e.g., API responses, JSONL, cache outputs, subprocess outputs). Reserve the lenient comment-json behavior only for user-authored config files that may legitimately contain comments or trailing commas. For src/timely/utils/entry-processor.ts and similar modules, replace or wrap JSON parsing with SafeJSON.parse(text, { strict: true }) unless you are explicitly handling config files that require comments.

Applied to files:

  • src/dev-dashboard/ui/src/lib/terminal-tabs.ts
  • src/dev-dashboard/lib/ttyd/types.ts
  • src/dev-dashboard/contract/dto.ts
  • src/dev-dashboard/ui/src/lib/api.ts
  • src/dev-dashboard/lib/tmux/hub.ts
  • src/dev-dashboard/lib/ttyd/manager.ts
📚 Learning: 2026-03-12T01:58:27.831Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 103
File: src/port/index.ts:137-144
Timestamp: 2026-03-12T01:58:27.831Z
Learning: In GenesisTools, apply a no-obvious-comments rule: do not add inline comments for well-known POSIX patterns or standard idioms (e.g., a process.kill(pid, 0) probe) when surrounding code is self-documenting through descriptive function/variable names. This guidance applies to TypeScript files under src (src/**/*.ts). Only include comments if they add non-obvious rationale, edge-case behavior, or explain complex logic that cannot be inferred from code alone.

Applied to files:

  • src/dev-dashboard/ui/src/lib/terminal-tabs.ts
  • src/dev-dashboard/lib/ttyd/types.ts
  • src/dev-dashboard/contract/dto.ts
  • src/dev-dashboard/ui/src/lib/api.ts
  • src/dev-dashboard/lib/tmux/hub.ts
  • src/dev-dashboard/lib/ttyd/manager.ts
📚 Learning: 2026-03-22T22:19:44.520Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 119
File: src/indexer/commands/graph.ts:34-34
Timestamp: 2026-03-22T22:19:44.520Z
Learning: In genesiscz/GenesisTools, when using `SafeJSON.parse` in `src/**/*.ts`, it is acceptable to omit `{ strict: true }` if (and only if) the JSON being parsed is internal cache/state written by the same codebase (e.g., data saved by one internal writer and later read from a corresponding cached file). Do not require strict mode for these internal, machine-generated cache files. Require `{ strict: true }` at external/untrusted boundaries instead (e.g., API responses, third-party JSONL, subprocess output, or any JSON whose contents may not have been produced by trusted internal code).

Applied to files:

  • src/dev-dashboard/ui/src/lib/terminal-tabs.ts
  • src/dev-dashboard/lib/ttyd/types.ts
  • src/dev-dashboard/contract/dto.ts
  • src/dev-dashboard/ui/src/lib/api.ts
  • src/dev-dashboard/lib/tmux/hub.ts
  • src/dev-dashboard/lib/ttyd/manager.ts
📚 Learning: 2026-03-25T19:55:27.917Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 129
File: src/utils/search/stores/vector-store.ts:19-23
Timestamp: 2026-03-25T19:55:27.917Z
Learning: When reviewing this codebase’s “3+ parameters → object parameter” guideline, only suggest object-parameter refactoring when the function’s parameters are ambiguous or include optional/unclear semantics. Do not flag tightly-defined utility/helper functions where (1) all parameters are required, (2) meanings are semantically clear from parameter names, and (3) the ordering is well-ordered and obvious. For example, functions like bruteForceVectorSearch(memoryIndex, queryVector, limit) should be allowed to keep positional parameters because the intent is clear.

Applied to files:

  • src/dev-dashboard/ui/src/lib/terminal-tabs.ts
  • src/dev-dashboard/lib/ttyd/types.ts
  • src/dev-dashboard/contract/dto.ts
  • src/dev-dashboard/ui/src/lib/api.ts
  • src/dev-dashboard/lib/tmux/hub.ts
  • src/dev-dashboard/lib/ttyd/manager.ts
📚 Learning: 2026-05-05T03:52:21.057Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 163
File: src/debugging-master/core/dashboard-server.ts:115-127
Timestamp: 2026-05-05T03:52:21.057Z
Learning: When reviewing Bun.serve fetch handlers in this repo, don’t treat `req.signal` as possibly `undefined` at runtime. Bun guarantees an `AbortSignal` on every incoming Request, so `req.signal?.addEventListener(...)` is unnecessary for runtime safety and is only a TypeScript narrowing artifact (e.g., the type might be `AbortSignal | null`). Therefore, don’t raise concerns about SSE/subscription cleanup being skipped because `req.signal` could be missing; cleanup decisions should be based on the actual handler lifecycle, not an imagined runtime absence of `req.signal`.

Applied to files:

  • src/dev-dashboard/ui/src/lib/terminal-tabs.ts
  • src/dev-dashboard/lib/ttyd/types.ts
  • src/dev-dashboard/contract/dto.ts
  • src/dev-dashboard/ui/src/lib/api.ts
  • src/dev-dashboard/lib/tmux/hub.ts
  • src/dev-dashboard/lib/ttyd/manager.ts
📚 Learning: 2026-06-30T19:43:23.331Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 227
File: src/agents/lib/session-resolve.ts:0-0
Timestamp: 2026-06-30T19:43:23.331Z
Learning: In GenesisTools application code, when you need to read an environment variable using a dynamic key, do not access `process.env` directly. Instead, route the lookup through `env.ai.getByEnvKey()` from `app/utils/env`. This matches the existing dynamic-key lookup pattern used elsewhere (e.g., ask’s `ProviderConfig.envKey`) and preserves `env.testing.set()` / `env.testing.withOverrides()` behavior. For static env keys, follow the project’s existing conventions, but for dynamic-key access prefer `env.ai.getByEnvKey()`.

Applied to files:

  • src/dev-dashboard/ui/src/lib/terminal-tabs.ts
  • src/dev-dashboard/lib/ttyd/types.ts
  • src/dev-dashboard/contract/dto.ts
  • src/dev-dashboard/ui/src/lib/api.ts
  • src/dev-dashboard/lib/tmux/hub.ts
  • src/dev-dashboard/lib/ttyd/manager.ts
📚 Learning: 2026-07-08T16:01:57.320Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 230
File: src/dev-dashboard/lib/boards/db.ts:183-190
Timestamp: 2026-07-08T16:01:57.320Z
Learning: In TypeScript files under src/**/*.ts, for `if` blocks that act as simple guard-return statements (e.g., `if (condition) { return <expr>; }`) and where execution continues in the same function after the `if`, require a blank line after the closing `}` of the `if` block (i.e., before the next statement), but do NOT require a blank line before the `if` statement itself—even if it immediately follows another statement. (Example: `const override = ...; if (override) { return override; }` should have no blank line before the `if`, but should have a blank line before the subsequent `return`/statement.)

Applied to files:

  • src/dev-dashboard/ui/src/lib/terminal-tabs.ts
  • src/dev-dashboard/lib/ttyd/types.ts
  • src/dev-dashboard/contract/dto.ts
  • src/dev-dashboard/ui/src/lib/api.ts
  • src/dev-dashboard/lib/tmux/hub.ts
  • src/dev-dashboard/lib/ttyd/manager.ts
📚 Learning: 2026-07-12T03:55:59.351Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 201
File: src/mcp-doctor/index.ts:94-97
Timestamp: 2026-07-12T03:55:59.351Z
Learning: When reviewing call sites that use `out.spinner()` (from `src/logger/out.ts`), do not require additional `isInteractive()`/stdin-TTY guards. `out.spinner()` already switches to a quiet no-op spinner when `isQuietOutput()` is true, and `isQuietOutput()` returns `true` whenever `process.stdout.isTTY` is falsy (common in CI, pipes, and JSON/structured output modes like `--json`/`--toon`). Wrapping with `isInteractive()` would duplicate centralized stdout-based logic and gate on the wrong TTY channel (stdin vs stdout).

Applied to files:

  • src/dev-dashboard/ui/src/lib/terminal-tabs.ts
  • src/dev-dashboard/lib/ttyd/types.ts
  • src/dev-dashboard/contract/dto.ts
  • src/dev-dashboard/ui/src/lib/api.ts
  • src/dev-dashboard/lib/tmux/hub.ts
  • src/dev-dashboard/lib/ttyd/manager.ts
📚 Learning: 2026-02-24T15:32:44.925Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 54
File: src/github/lib/review-output.ts:18-20
Timestamp: 2026-02-24T15:32:44.925Z
Learning: In TypeScript files, do not require a blank line between the opening brace of a function and the first statement if the first statement is the if statement immediately after the signature. The blank-line rule applies to separating an if from unrelated preceding code within the same block, not to spacing after the function opening brace. Apply this rule to all TS functions across the codebase.

Applied to files:

  • src/dev-dashboard/ui/src/lib/terminal-tabs.ts
  • src/dev-dashboard/lib/ttyd/types.ts
  • src/dev-dashboard/contract/dto.ts
  • src/dev-dashboard/ui/src/lib/api.ts
  • src/dev-dashboard/lib/tmux/hub.ts
  • src/dev-dashboard/lib/ttyd/manager.ts
📚 Learning: 2026-03-12T01:26:03.611Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 95
File: src/ask/lib/ChatSessionManager.ts:0-0
Timestamp: 2026-03-12T01:26:03.611Z
Learning: Use SafeJSON.parse(text, { strict: true }) for strict RFC 8259 validation in all non-config boundaries (API responses, JSONL, cache, subprocess output). The 3-arg form SafeJSON.parse(text, null, { strict: true }) is invalid and should not be used. Only lenient default (no options) is appropriate for user-authored config files that may contain comments/trailing commas. Apply this guideline across TypeScript files (src/**/*.ts) wherever SafeJSON.parse is used.

Applied to files:

  • src/dev-dashboard/ui/src/lib/terminal-tabs.ts
  • src/dev-dashboard/lib/ttyd/types.ts
  • src/dev-dashboard/contract/dto.ts
  • src/dev-dashboard/ui/src/lib/api.ts
  • src/dev-dashboard/lib/tmux/hub.ts
  • src/dev-dashboard/lib/ttyd/manager.ts
📚 Learning: 2026-03-12T01:26:18.985Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 95
File: src/claude/lib/history/search.ts:0-0
Timestamp: 2026-03-12T01:26:18.985Z
Learning: When using SafeJSON.parse in TypeScript code, prefer the two-argument form SafeJSON.parse(text, { strict: true }) to enable strict RFC 8259 validation via the native JSON.parse. Do NOT use the three-argument form SafeJSON.parse(text, null, { strict: true }). Apply strict parsing at remote/third-party API boundaries, JSONL parsing points, and subprocess output. Fall back to the lenient/default form only for user-authored config files that may legitimately contain comments or trailing commas. This pattern keeps strict validation where appropriate and preserves leniency for internal/config data.

Applied to files:

  • src/dev-dashboard/ui/src/lib/terminal-tabs.ts
  • src/dev-dashboard/lib/ttyd/types.ts
  • src/dev-dashboard/contract/dto.ts
  • src/dev-dashboard/ui/src/lib/api.ts
  • src/dev-dashboard/lib/tmux/hub.ts
  • src/dev-dashboard/lib/ttyd/manager.ts
📚 Learning: 2026-03-12T01:26:27.000Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 95
File: src/debugging-master/commands/tail.ts:0-0
Timestamp: 2026-03-12T01:26:27.000Z
Learning: In the genesiscz/GenesisTools repository, prefer using SafeJSON.parse(text, { strict: true }) (2-argument form) at all non-config JSON boundaries such as API responses, JSONL parsers, cache files, and subprocess stdout. Reserve the lenient default (SafeJSON.parse(text) with no options) only for user-authored config files that may legitimately contain comments or trailing commas.

Applied to files:

  • src/dev-dashboard/ui/src/lib/terminal-tabs.ts
  • src/dev-dashboard/lib/ttyd/types.ts
  • src/dev-dashboard/contract/dto.ts
  • src/dev-dashboard/ui/src/lib/api.ts
  • src/dev-dashboard/lib/tmux/hub.ts
  • src/dev-dashboard/lib/ttyd/manager.ts
📚 Learning: 2026-03-12T01:26:24.859Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 95
File: src/azure-devops/commands/history-sync.ts:0-0
Timestamp: 2026-03-12T01:26:24.859Z
Learning: In GenesisTools, ensure SafeJSON.parse is called with exactly two arguments. Use SafeJSON.parse(text, { strict: true }) for strict RFC 8259 validation, or pass a reviver function as the second argument. Do not call SafeJSON.parse(text, null, { strict: true }) since the function signature does not support a three-argument form. Apply this guideline to all TypeScript files that use SafeJSON.parse (e.g., src/utils/json.ts) and other related code.

Applied to files:

  • src/dev-dashboard/ui/src/lib/terminal-tabs.ts
  • src/dev-dashboard/lib/ttyd/types.ts
  • src/dev-dashboard/contract/dto.ts
  • src/dev-dashboard/ui/src/lib/api.ts
  • src/dev-dashboard/lib/tmux/hub.ts
  • src/dev-dashboard/lib/ttyd/manager.ts
📚 Learning: 2026-03-17T01:30:56.939Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 107
File: src/utils/macos/tts.ts:130-139
Timestamp: 2026-03-17T01:30:56.939Z
Learning: In genesiscz/GenesisTools, do not suggest converting two-argument functions with an optional second parameter (for example setMute(muted: boolean, app?: string)) to an object-parameter form. The project prefers simple positional parameters for short utility functions, even when an optional argument is present. The object-parameter guideline should only apply when a function has 3 or more parameters.

Applied to files:

  • src/dev-dashboard/ui/src/lib/terminal-tabs.ts
  • src/dev-dashboard/lib/ttyd/types.ts
  • src/dev-dashboard/contract/dto.ts
  • src/dev-dashboard/ui/src/lib/api.ts
  • src/dev-dashboard/lib/tmux/hub.ts
  • src/dev-dashboard/lib/ttyd/manager.ts
📚 Learning: 2026-03-22T22:19:49.876Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 119
File: src/indexer/index.ts:41-56
Timestamp: 2026-03-22T22:19:49.876Z
Learning: When using Bun projects, treat `import.meta.dir` as an absolute directory path provided by Bun. If you build paths by concatenating with `import.meta.dir` (e.g., `import.meta.dir + "/file.ts"`), do not require `path.resolve()` as it would be redundant. Only apply `path.resolve()` guidance when the base path is relative (not when the base is already an absolute `import.meta.dir`).

Applied to files:

  • src/dev-dashboard/ui/src/lib/terminal-tabs.ts
  • src/dev-dashboard/lib/ttyd/types.ts
  • src/dev-dashboard/contract/dto.ts
  • src/dev-dashboard/ui/src/lib/api.ts
  • src/dev-dashboard/lib/tmux/hub.ts
  • src/dev-dashboard/lib/ttyd/manager.ts
📚 Learning: 2026-06-30T19:44:04.852Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 227
File: src/agents/tests/matrix-e2e.test.ts:0-0
Timestamp: 2026-06-30T19:44:04.852Z
Learning: In the GenesisTools repo, do not flag code that passes `env: { ...process.env, ... }` into `Bun.spawn()` (i.e., forwarding the inherited environment to a child process) as a violation of the env-helper guideline by itself. Forwarding inherited environment to a subprocess is not the same as application/test logic directly reading configuration from `process.env`. Continue to flag direct `process.env` reads used in TypeScript logic (e.g., feature gates) per the env-helper guideline.

Applied to files:

  • src/dev-dashboard/ui/src/lib/terminal-tabs.ts
  • src/dev-dashboard/lib/ttyd/types.ts
  • src/dev-dashboard/contract/dto.ts
  • src/dev-dashboard/ui/src/lib/api.ts
  • src/dev-dashboard/lib/tmux/hub.ts
  • src/dev-dashboard/lib/ttyd/manager.ts
📚 Learning: 2026-03-12T03:48:42.474Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 104
File: src/darwinkit/index.ts:146-156
Timestamp: 2026-03-12T03:48:42.474Z
Learning: In TypeScript files that use Commander subcommands and exit after showing help, replace code after Command.help() with the pattern: call sub.outputHelp(); (returns void) followed by process.exit(0) or process.exit(1). This avoids TS7027 unreachable-code because Command.help() returns never. Apply this pattern in all src/**/*.ts files where subcommands need to display help before exiting.

Applied to files:

  • src/dev-dashboard/ui/src/lib/terminal-tabs.ts
  • src/dev-dashboard/lib/ttyd/types.ts
  • src/dev-dashboard/contract/dto.ts
  • src/dev-dashboard/ui/src/lib/api.ts
  • src/dev-dashboard/lib/tmux/hub.ts
  • src/dev-dashboard/lib/ttyd/manager.ts
📚 Learning: 2026-05-17T14:59:03.963Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 167
File: src/dev-dashboard/lib/ttyd/manager.ts:1-1
Timestamp: 2026-05-17T14:59:03.963Z
Learning: When spawning long-lived daemon child processes in this repo with Bun.spawn (e.g., dashboard subprocess managers), always use Bun’s object form: `Bun.spawn({ cmd, detached: true, ... })` and then call `.unref()` on the returned process. Use `detached: true` (requires Bun >= 0.6.0; it creates a new process group / uses setsid) so the child does not receive SIGHUP when the dashboard parent exits; also call `.unref()` so the parent can exit without waiting on the child. Do not omit `detached: true` for daemon-like subprocesses, since it can cause the child to die with the parent and lead to proxy failures (e.g., 502s) on routes depending on that process.

Applied to files:

  • src/dev-dashboard/lib/ttyd/types.ts
  • src/dev-dashboard/lib/tmux/hub.ts
  • src/dev-dashboard/lib/ttyd/manager.ts
🔇 Additional comments (7)
src/dev-dashboard/lib/ttyd/manager.ts (2)

746-808: 🎯 Functional Correctness

Verify syncNamesFromClaudePaneTitles updates every bound ttyd entry, not just the triggering one.

The line-range summary states this range added an exported syncTtydDisplayNamesForTmux helper that mirrors a hub-side tmux rename onto all bound ttyd display names, matching what retargetTtydTmuxBindings (lines 809-845) and renameTtyd already do by looping over every matching registry entry. A prior review round on this file flagged that syncNamesFromClaudePaneTitles set the display name only on the single triggering registry entry, which would leave other ttyd tabs bound to the same tmux session with a stale label — the exact scenario healStaleTtydTmuxTargets exists to catch. Confirm syncNamesFromClaudePaneTitles now calls syncTtydDisplayNamesForTmux (or an equivalent all-entries update) instead of writing to a single tracked entry.

#!/bin/bash
# Description: Check whether syncNamesFromClaudePaneTitles updates every bound ttyd entry.
ast-grep run --pattern 'function syncNamesFromClaudePaneTitles($$$) { $$$ }' --lang typescript src/dev-dashboard/lib/ttyd/manager.ts
rg -n -A 40 'function syncNamesFromClaudePaneTitles' src/dev-dashboard/lib/ttyd/manager.ts

646-692: LGTM!

src/dev-dashboard/lib/tmux/hub.ts (1)

24-72: LGTM!

src/dev-dashboard/lib/ttyd/types.ts (1)

20-25: LGTM!

src/dev-dashboard/ui/src/components/TmuxSessionsPanel.tsx (1)

185-195: LGTM!

src/dev-dashboard/ui/src/lib/terminal-tabs.ts (1)

4-19: LGTM!

src/dev-dashboard/ui/src/routes/ttyd.tsx (1)

40-40: LGTM!

Also applies to: 199-204, 403-404

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant