-
Notifications
You must be signed in to change notification settings - Fork 0
Feat/tui2 phase3 parity #187
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
3b53091
18f797d
fb5dd1a
331ed24
cd721e3
4b8f61f
cbbf25a
fc6df4a
0067a20
0e665ac
bf2290e
ccb3fcb
103697f
0c4e222
1283d8e
d802e8b
ca2542c
5ae667e
dc1f4fb
663b477
0122a68
002f7d8
04ae451
41994d7
eed5f32
6e56f74
b9fc7c3
e94276e
869ba73
9b62c3d
0ed46f6
299d48f
2f4f300
04563b7
ed0f479
5e07ab2
a9e5e6f
717be6d
0127ffa
7dd4dc4
4045b0f
b82eacf
92235de
759224e
8e2ead7
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -14,7 +14,7 @@ | |
| > | ||
| > ☐ **Part C — tui2 target architecture** — not yet implemented. | ||
| > | ||
| > ☐ **Part D — phased execution** — Phase 0-2 complete; Phases 3-4 pending. | ||
| > ☐ **Part D — phased execution** — Phase 0-2 complete; Phase 3 in progress; Phase 4 pending. | ||
|
|
||
| ## Goals | ||
|
|
||
|
|
@@ -423,17 +423,45 @@ rather than deferring them. | |
|
|
||
| ## Phase 3 — Feature parity port (the B-inventory) | ||
| Port in priority order, each as its own small PR: | ||
| 1. **Steer + FollowUp** (wire `OnSteer`/`OnFollowUp` in `tui2.go`; Enter-while-running → | ||
| follow-up). Highest user-visible value. | ||
| 2. **Fix `:compact`** to call `sess.Compact()`. | ||
| 3. **Search mode** (`/`, n/N, scroll-to-match) over the messages TextView. | ||
| 4. **Verbose toggle** + per-block verbose collapse semantics (match tui1). | ||
| 5. **Click-to-expand** via tview regions. | ||
| 6. **Clipboard**: copy-last-response (Ctrl+Y) + `:copyview`. | ||
| 7. **`:login`/`:logout`/`:stop`/`:banner`/`:mcp`** command wiring. | ||
| 8. **Model picker** data + filter correctness. | ||
| 9. **Ephemeral messages** + **active-prompt display**. | ||
| 10. **Min terminal size guard**. | ||
|
|
||
| ### 3.1 Ctrl-only hotkeys + command palette (✅ complete 2026-08-07) | ||
| - ✅ Stripped single-char hotkeys (`?`, `/`, `j`, `k`, `g`, `G`) from `DefaultBindings()` | ||
| - ✅ Only Ctrl+ combinations, Esc, Enter, arrows, navigation keys, Tab remain as hotkeys | ||
| - ✅ All other actions routed through command palette (`:help`, `:search`, `:top`, `:bottom`, `:verbose`, `:stop`, `:steer`, `:banner`) | ||
|
|
||
| ### 3.2 Steer + FollowUp (✅ complete 2026-08-07) | ||
| - ✅ Wire `OnSteer`/`OnFollowUp` callbacks in `tui2.go` | ||
| - ✅ Enter-while-streaming → follow-up (submitFollowUp) | ||
| - ✅ `:steer <text>` command for mid-turn injection | ||
|
|
||
| ### 3.3 Fix `:compact` (✅ complete 2026-08-07) | ||
| - ✅ `:compact` now calls `OnCompact` → `sess.Compact()` instead of `CollapseAll` | ||
|
|
||
| ### 3.4 Search (✅ complete 2026-08-07) | ||
| - ✅ `:search <query>` scrolls to first match in messages TextView | ||
| - ✅ Ephemeral result message in info pane | ||
|
|
||
| ### 3.5 Verbose toggle (✅ complete 2026-08-07) | ||
| - ✅ `:verbose` command toggles verbose mode | ||
| - ✅ `verbose` field on TUI2 struct | ||
|
|
||
| ### 3.6 Stop (✅ complete 2026-08-07) | ||
| - ✅ `:stop` command calls `OnStop` → aborts running agent + hides thinking | ||
|
|
||
| ### 3.7 Banner toggle (✅ complete 2026-08-07) | ||
| - ✅ `:banner` toggles banner visibility | ||
|
|
||
| ### 3.8 Ephemeral messages (✅ complete 2026-08-07) | ||
| - ✅ `SetEphemeral(msg)` displays temporary messages in info pane (3s timeout) | ||
|
|
||
| ### Remaining items | ||
| - ☐ Click-to-expand via tview regions | ||
| - ☐ Clipboard: `:copyview` + copy-last-response | ||
| - ☐ `:login`/`:logout`/`:mcp` command wiring (callbacks declared, not dispatched) | ||
| - ☐ Model picker data + filter correctness | ||
| - ☐ Min terminal size guard | ||
| - ☐ Per-block verbose collapse semantics (match tui1) | ||
|
|
||
|
Comment on lines
+457
to
+464
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win Move the remaining-work checklist to Beads. The 🤖 Prompt for AI AgentsSource: Coding guidelines |
||
| - **Exit gate:** every row in Part B reads ✅; tui2 feature-parity with tui1 confirmed by a | ||
| manual + scripted checklist. | ||
|
|
||
|
|
@@ -449,10 +477,8 @@ Port in priority order, each as its own small PR: | |
|
|
||
| # Risks & open questions | ||
|
|
||
| - **Ctrl+T conflict**: tui1 = reasoning, tui2 = tools. Recommend adopting tui1's | ||
| reasoning binding and rebinding tools to something else (e.g. Ctrl+Shift+T / `:tools`). | ||
| - **`:compact` semantic bug** (CollapseAll vs real compaction) should be fixed in Phase 3 | ||
| item 2 regardless of the larger decision — it's a correctness issue today. | ||
| - **Ctrl+T conflict**: ✅ **Resolved 2026-08-07** — Ctrl+T = toggle tools, Ctrl+R = toggle reasoning. No conflict. | ||
| - **`:compact` semantic bug**: ✅ **Fixed 2026-08-07** — now calls `sess.Compact()` via `OnCompact`. | ||
|
Comment on lines
+480
to
+481
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win Synchronize resolved risks with the feature inventory. These lines mark the Ctrl+T conflict and 🤖 Prompt for AI Agents |
||
| - **Chroma weight**: chroma adds lexer binaries; if binary size matters, gate highlighting | ||
| behind a build tag or lazy-init only the lexers seen. Measure before Phase 4. | ||
| - **tview mouse on Windows**: verify region-click works under the Windows tcell backend | ||
|
|
@@ -473,3 +499,4 @@ Port in priority order, each as its own small PR: | |
| | 2026-08-07 | — | Committed on branch `feat/tviewmd` | ||
| | 2026-08-07 | 2 | Phase 2 plan expanded with sub-tasks (2.1–2.6); none implemented yet on main | ||
| | 2026-08-07 | 2 | Phase 2 implemented on `feat/tui2-phase2-arch`: Theme + DetectTheme, streaming ordering, RenderCtx + Renderable, raw markdown storage, input dispatch consolidation, mechanical cleanups. Commit `3cda8a5`. Resize reflow deferred (SetAfterDrawFunc caused rendering issues); plainMessages not yet deleted. | | ||
| | 2026-08-07 | 3 | Phase 3 started on `feat/tui2-phase3-parity`: stripped single-char hotkeys (Ctrl-only + command palette), wired OnSteer/OnFollowUp/OnStop, fixed `:compact`, added `:search`/`:verbose`/`:banner`/`:stop`/`:steer` commands, ephemeral messages, Enter-while-running → follow-up. | | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -8,6 +8,8 @@ import ( | |
| "time" | ||
|
|
||
| "go.opentelemetry.io/otel/attribute" | ||
|
|
||
| "github.com/buchenberg/yaah/internal/tools" | ||
| "go.opentelemetry.io/otel/trace" | ||
|
|
||
| "github.com/buchenberg/yaah/internal/observability" | ||
|
|
@@ -48,6 +50,14 @@ func (c *Client) runStream(ctx context.Context, sp StreamProvider, req types.Cha | |
| observability.RecordStreamEnd(streamSpan, path, finishReason, usageCaptured, len(msg.Content), len(msg.ToolCalls)) | ||
| } | ||
|
|
||
| var lastHeartbeat time.Time | ||
| emitHeartbeat := func() { | ||
| if time.Since(lastHeartbeat) > 5*time.Second { | ||
| tools.SendHeartbeat(ctx) | ||
| lastHeartbeat = time.Now() | ||
| } | ||
| } | ||
|
Comment on lines
+53
to
+59
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win 🧩 Analysis chain🏁 Script executed: #!/bin/bash
set -eu
echo '--- outline ---'
ast-grep outline internal/agent/llm/stream.go --view expanded || true
echo '--- stream.go relevant ranges ---'
nl -ba internal/agent/llm/stream.go | sed -n '1,180p'
echo '--- heartbeat and stream symbols ---'
rg -n -C 4 'SendStream|SendHeartbeat|emitHeartbeat|Reason|reason|tool.?call|delta|Heartbeat' internal/agent internal 2>/dev/null | head -n 500Repository: buchenberg/yaah Length of output: 39292 🏁 Script executed: #!/bin/bash
set -eu
printf '%s\n' '--- stream.go ---'
sed -n '1,180p' internal/agent/llm/stream.go
printf '%s\n' '--- relevant definitions and call sites ---'
rg -n -C 6 'func .*Send(Stream|Heartbeat)|SendStream|SendHeartbeat|emitHeartbeat|Stream' internal/agent internal 2>/dev/null | head -n 600Repository: buchenberg/yaah Length of output: 42601 🏁 Script executed: #!/bin/bash
set -eu
echo '--- target file ---'
nl -ba internal/agent/llm/stream.go | sed -n '1,180p'
echo '--- symbols ---'
rg -n -C 5 'SendStream|SendHeartbeat|emitHeartbeat|type .*Delta|Reasoning|ToolCall|heartbeat' internal/agent internal 2>/dev/null | head -n 600Repository: buchenberg/yaah Length of output: 40463 🏁 Script executed: #!/bin/bash
set -eu
printf '%s\n' '--- target ---'
nl -ba internal/agent/llm/stream.go | sed -n '1,180p'
printf '%s\n' '--- related definitions ---'
rg -n -C 8 'SendStream|SendHeartbeat|emitHeartbeat|Heartbeat|Reasoning|ToolCall|Stream' internal/agent internal 2>/dev/null | head -n 800Repository: buchenberg/yaah Length of output: 50371 🏁 Script executed: #!/bin/bash
set -eu
git ls-files | rg '(^|/)(stream|.*agent.*|.*llm.*)' | head -n 200
nl -ba internal/agent/llm/stream.go | sed -n '1,180p'
rg -n -C 8 'SendStream|SendHeartbeat|emitHeartbeat|Heartbeat|Reasoning|ToolCall' . --glob '!vendor/**' --glob '!node_modules/**' | head -n 800Repository: buchenberg/yaah Length of output: 50373 🌐 Web query:
💡 Result: The concept of emitting heartbeats independently of content deltas—often implemented as SendHeartbeat or similar mechanisms—is a common design pattern used in streaming applications (such as Server-Sent Events, WebSockets, or gRPC streams) to maintain connection liveness and prevent timeouts [1][2][3][4]. Key characteristics of this pattern include: 1. Protocol Liveness: When a stream is idle (e.g., waiting for a slow upstream process like LLM reasoning), intermediaries like proxies, load balancers, or firewalls may close the connection due to inactivity [1][3][5]. Emitting a synthetic heartbeat (e.g., an SSE comment frame like: keepalive or: prefill) at regular intervals ensures the TCP connection remains active without interfering with the actual content stream, as these comments are specified to be ignored by clients [1][3][6][4]. 2. Independent Cadence: Heartbeats are often generated by a separate timer or goroutine that monitors for inactivity [2][7][4]. This ticker is typically gated by the actual transmission of data; if a visible chunk is sent, the heartbeat timer is reset [1][3][6][4]. This ensures heartbeats are emitted only during silent windows and never interleave with or corrupt real data payloads [1][7][4]. 3. Implementation Variations: - Streaming APIs: In systems like SSE or custom streaming protocols, this involves injecting non-functional frames (comments or empty-delta chunks) during idle periods [1][3][5][4]. - Request-Response Heartbeats: In some control-plane protocols, SendHeartbeat may require an explicit acknowledgment (ACK) from the peer to verify the connection is healthy, triggering a state update (e.g., marking a connection unhealthy) if the ACK is not received within a timeout period [8][9][4]. - Consumer Group Management: In messaging services, heartbeat operations inform the server that a consumer instance is still active and participating in a consumer group, preventing it from being removed from the group due to apparent inactivity [10]. Technical implementations often use configuration variables (e.g., HEARTBEAT_INTERVAL) to allow operators to tune or disable this behavior without modifying core streaming logic [3][6][5]. Citations:
🏁 Script executed: #!/bin/bash
set -eu
echo '--- heartbeat implementation ---'
sed -n '80,112p' internal/jobs/context.go
echo '--- child watchdog ---'
sed -n '160,225p' internal/agent/agent_tools.go
echo '--- focused source verifier ---'
python3 - <<'PY'
from pathlib import Path
source = Path("internal/agent/llm/stream.go").read_text()
required = [
'emitHeartbeat()',
'if delta.Content != ""',
'if delta.ReasoningContent != ""',
'for _, tc := range delta.ToolCalls',
'case <-ctx.Done():',
]
missing = [text for text in required if text not in source]
if missing:
raise SystemExit(f"missing expected source fragments: {missing}")
content_start = source.index('if delta.Content != ""')
heartbeat_pos = source.index('emitHeartbeat()', content_start)
content_end = source.index('\n\t\t\t}', content_start)
if not (content_start < heartbeat_pos < content_end):
raise SystemExit("heartbeat call is not inside the content-delta branch")
select_start = source.index('\t\tselect {')
select_end = source.index('\n\t\t}', select_start)
select_body = source[select_start:select_end]
if 'time.NewTicker' in select_body or 'ticker.C' in select_body:
raise SystemExit("unexpected ticker found in stream select")
print("heartbeat call is content-only; stream select has no ticker case")
PYRepository: buchenberg/yaah Length of output: 3404 🏁 Script executed: #!/bin/bash
set -eu
sed -n '80,112p' internal/jobs/context.go
sed -n '160,225p' internal/agent/agent_tools.go
python3 - <<'PY'
from pathlib import Path
source = Path("internal/agent/llm/stream.go").read_text()
assert source.index("emitHeartbeat()") > source.index('if delta.Content != ""')
assert source.index("emitHeartbeat()") < source.index("for _, tc := range delta.ToolCalls")
select = source[source.index("\t\tselect {"):source.index("\n\t\t}", source.index("\t\tselect {"))]
assert "ticker.C" not in select
print("content-only heartbeat and no ticker case")
PYRepository: buchenberg/yaah Length of output: 3293 Emit heartbeats independently of stream deltas. When 🤖 Prompt for AI Agents |
||
|
|
||
| for { | ||
| select { | ||
| case chunk, ok := <-chunks: | ||
|
|
@@ -94,6 +104,7 @@ func (c *Client) runStream(ctx context.Context, sp StreamProvider, req types.Cha | |
| if delta.Content != "" { | ||
| content.WriteString(delta.Content) | ||
| tokenCount++ | ||
| emitHeartbeat() | ||
| if c.OnToken != nil { | ||
| if clean := dsmlFilter.filterToken(delta.Content); clean != "" { | ||
| c.OnToken(clean) | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -27,6 +27,7 @@ type SubAgentConfig struct { | |
| ContextWindow int | ||
| OtelEnabled bool | ||
| OtelVerbose bool | ||
| WrapUpThreshold int | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win 🧩 Analysis chain🏁 Script executed: #!/usr/bin/env bash
set -euo pipefail
rg -n -C 8 'SubAgentConfig[[:space:]]*\{|WrapUpThreshold|NewSubAgentLoop' --glob '*.go'Repository: buchenberg/yaah Length of output: 153 🏁 Script executed: #!/usr/bin/env bash
set -euo pipefail
printf '%s\n' '--- candidate files ---'
git ls-files '*subagent_loop.go' '*runner.go' '*.go' | sed -n '1,160p'
printf '%s\n' '--- relevant symbols ---'
rg -n -C 6 'SubAgentConfig|WrapUpThreshold|NewSubAgentLoop' --glob '*.go' . || trueRepository: buchenberg/yaah Length of output: 38139 🏁 Script executed: #!/usr/bin/env bash
set -euo pipefail
printf '%s\n' '--- runner construction ---'
sed -n '245,310p' internal/agent/runner/runner.go
printf '%s\n' '--- configuration types and defaults ---'
sed -n '70,125p' internal/config/load.go
sed -n '150,190p' internal/config/load.go
sed -n '250,285p' internal/config/load.go
printf '%s\n' '--- loop construction and tests ---'
sed -n '1,90p' internal/agent/subagent_loop.go
sed -n '120,175p' cmd/yaah/build_loop.go
sed -n '90,165p' cmd/yaah/config_parity_test.goRepository: buchenberg/yaah Length of output: 17100 🏁 Script executed: #!/usr/bin/env bash
set -euo pipefail
printf '%s\n' '--- all NewSubAgentLoop call sites ---'
rg -n -C 12 'NewSubAgentLoop\(' --glob '*.go' .
printf '%s\n' '--- runner option initialization and call path ---'
sed -n '1,145p' internal/agent/runner/runner.go
sed -n '145,245p' internal/agent/runner/runner.go
printf '%s\n' '--- sub-agent configuration references ---'
rg -n -C 4 'Agent\.SubAgent|subCfg|defaults config\.Defaults|opts\.defaults' internal cmd --glob '*.go'Repository: buchenberg/yaah Length of output: 36608 🏁 Script executed: #!/usr/bin/env bash
set -euo pipefail
python3 - <<'PY'
from pathlib import Path
import re
runner = Path("internal/agent/runner/runner.go").read_text()
loop = Path("internal/agent/subagent_loop.go").read_text()
load = Path("internal/config/load.go").read_text()
call = re.search(
r"agent\.NewSubAgentLoop\([^;]+?agent\.SubAgentConfig\{(.*?)\n\s*\}\)",
runner,
re.S,
)
if not call:
raise SystemExit("NewSubAgentLoop configuration literal not found")
literal = call.group(1)
constructor = re.search(
r"if cfg\.WrapUpThreshold <= 0 \{\s*cfg\.WrapUpThreshold = (\d+)",
loop,
re.S,
)
if not constructor:
raise SystemExit("WrapUpThreshold fallback not found")
default_field = re.search(
r"WrapUpThreshold\s+int\s+`yaml:\"wrap_up_turns\"`\s+// (.*)",
load,
)
if not default_field:
raise SystemExit("configured WrapUpThreshold field not found")
print("runner literal sets WrapUpThreshold:", "WrapUpThreshold:" in literal)
print("sub-agent fallback for non-positive values:", constructor.group(1))
print("configuration semantics:", default_field.group(1).strip())
PYRepository: buchenberg/yaah Length of output: 355 Propagate and preserve the configured wrap-up threshold for sub-agent loops.
🤖 Prompt for AI Agents |
||
| } | ||
|
|
||
| // NewSubAgentLoop creates a Loop optimized for sub-agent execution. | ||
|
|
@@ -46,6 +47,9 @@ func NewSubAgentLoop(provider Provider, registry *tools.Registry, model, systemP | |
| if cfg.MaxToolConcurrency <= 0 { | ||
| cfg.MaxToolConcurrency = 5 | ||
| } | ||
| if cfg.WrapUpThreshold <= 0 { | ||
| cfg.WrapUpThreshold = 5 | ||
| } | ||
|
|
||
| l := &Loop{ | ||
| Provider: provider, | ||
|
|
@@ -68,6 +72,7 @@ func NewSubAgentLoop(provider Provider, registry *tools.Registry, model, systemP | |
| ToolsLevel: FullTools, | ||
| PipelineNames: nil, | ||
| PipelineDisabled: nil, | ||
| WrapUpThreshold: cfg.WrapUpThreshold, | ||
| }, | ||
|
|
||
| // Sub-agents use an in-memory pruner only — no compaction pipeline. | ||
|
|
||
| Original file line number | Diff line number | Diff line change | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -18,25 +18,27 @@ tools: | |||||||||
| - glob | ||||||||||
| - ls | ||||||||||
| - sed | ||||||||||
| - powershell | ||||||||||
| - bash | ||||||||||
| - calculate | ||||||||||
| - file_info | ||||||||||
| - go_outline | ||||||||||
| - json_query | ||||||||||
| - webfetch | ||||||||||
| - http | ||||||||||
| - git | ||||||||||
| - diff | ||||||||||
| - staticcheck | ||||||||||
| max_iterations: 25 | ||||||||||
| max_turns: 3 | ||||||||||
| timeout: 240 | ||||||||||
| timeout: 480 | ||||||||||
| --- | ||||||||||
|
|
||||||||||
| You are a REVIEWER sub-agent on yaah's team. Inspect code, count files and | ||||||||||
| lines, measure complexity, and report findings. You do NOT modify files. | ||||||||||
| Use the shell specified in the Environment section for counting. | ||||||||||
| - You are a CODE REVIEWER sub-agent on yaah's team. Inspect code for code quality and report findings. | ||||||||||
| - You are concerned with SOLID design and easy to maintain code. | ||||||||||
|
Comment on lines
+33
to
+34
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win Use a hyphenated compound modifier. Change Proposed fix-- You are concerned with SOLID design and easy to maintain code.
+- You are concerned with SOLID design and easy-to-maintain code.📝 Committable suggestion
Suggested change
🧰 Tools🪛 LanguageTool[grammar] ~34-~34: Use a hyphen to join words. (QB_NEW_EN_HYPHEN) 🤖 Prompt for AI AgentsSource: Linters/SAST tools |
||||||||||
| - Look for dead code in what you are asked to review. | ||||||||||
| - You do NOT modify files. | ||||||||||
|
|
||||||||||
| **Tool selection**: Prefer `read`, `grep`, `glob`, `ls`, and `file_info` | ||||||||||
| for all file inspection. These tools are optimized for context efficiency | ||||||||||
| and produce chunked/deduplicated output. | ||||||||||
|
|
||||||||||
| Synthesize results concisely. Use the fewest | ||||||||||
| tools needed. Batch independent tool calls in one turn: fire all reads, | ||||||||||
| globs, and go_outline calls at once instead of one per turn. | ||||||||||
|
|
||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -14,6 +14,7 @@ type Theme struct { | |
| ToolBg string | ||
| CodeBg string | ||
| InputBorder string | ||
| PaneBorder string | ||
| NoColor bool | ||
|
|
||
| ToolColors map[string]string | ||
|
|
@@ -89,6 +90,7 @@ func NewDarkTheme() Theme { | |
| ToolBg: "#1a1a2e", | ||
| CodeBg: "#1e1e2e", | ||
| InputBorder: "#ff87af", | ||
| PaneBorder: "#af5fff", | ||
| ToolColors: newToolColors(), | ||
| RoleColors: newRoleColors(), | ||
| } | ||
|
|
@@ -104,6 +106,7 @@ func NewLightTheme() Theme { | |
| ToolBg: "#eeeeee", | ||
| CodeBg: "#f0f0f0", | ||
| InputBorder: "#d75f87", | ||
| PaneBorder: "#7f3faf", | ||
| ToolColors: newToolColors(), | ||
| RoleColors: newRoleColors(), | ||
| } | ||
|
|
@@ -148,7 +151,7 @@ func (th *Theme) TagBold(color, text string) string { | |
| if th.NoColor { | ||
| return text | ||
| } | ||
| return "[" + color + "::b]" + text + "[-]" | ||
| return "[" + color + "::b]" + text + "[-:-:-]" | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win Apply the same reset to the package-level This fix is correct: 🛠️ Proposed fix in internal/tui2/colors/colors.go-func TagBold(color, text string) string { return "[" + color + "::b]" + text + Reset }
+func TagBold(color, text string) string { return "[" + color + "::b]" + text + "[-:-:-]" }🤖 Prompt for AI Agents |
||
| } | ||
|
|
||
| func (th *Theme) DimTag() string { | ||
|
|
||
| Original file line number | Diff line number | Diff line change | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -21,6 +21,14 @@ const ( | |||||||||
| CmdClear | ||||||||||
| CmdHelp | ||||||||||
| CmdCompact | ||||||||||
| CmdStop | ||||||||||
| CmdSteer | ||||||||||
| CmdFollowUp | ||||||||||
| CmdVerbose | ||||||||||
| CmdSearch | ||||||||||
| CmdTop | ||||||||||
| CmdBottom | ||||||||||
| CmdBanner | ||||||||||
| CmdReloadRoles | ||||||||||
| CmdLogin | ||||||||||
| CmdLogout | ||||||||||
|
|
@@ -41,6 +49,22 @@ func Parse(input string) Cmd { | |||||||||
| return CmdHelp | ||||||||||
| case input == "compact": | ||||||||||
| return CmdCompact | ||||||||||
| case input == "stop": | ||||||||||
| return CmdStop | ||||||||||
| case strings.HasPrefix(input, "steer "): | ||||||||||
| return CmdSteer | ||||||||||
| case strings.HasPrefix(input, "followup "): | ||||||||||
| return CmdFollowUp | ||||||||||
| case input == "verbose": | ||||||||||
| return CmdVerbose | ||||||||||
| case strings.HasPrefix(input, "search "): | ||||||||||
| return CmdSearch | ||||||||||
| case input == "top": | ||||||||||
| return CmdTop | ||||||||||
| case input == "bottom": | ||||||||||
| return CmdBottom | ||||||||||
| case input == "banner": | ||||||||||
| return CmdBanner | ||||||||||
| case input == "roles": | ||||||||||
| return CmdReloadRoles | ||||||||||
| case input == "login": | ||||||||||
|
|
@@ -51,7 +75,7 @@ func Parse(input string) Cmd { | |||||||||
| return CmdSession | ||||||||||
| case input == "mcp": | ||||||||||
| return CmdMCP | ||||||||||
| case strings.HasPrefix(input, "model "): | ||||||||||
| case strings.HasPrefix(input, "model"): | ||||||||||
| return CmdModel | ||||||||||
|
Comment on lines
+78
to
79
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win Restrict the
🛠️ Proposed fix- case strings.HasPrefix(input, "model"):
+ case input == "model" || strings.HasPrefix(input, "model "):
return CmdModel📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||
| default: | ||||||||||
| return CmdNone | ||||||||||
|
|
@@ -79,7 +103,7 @@ func Build(onCommand func(cmd Cmd, arg string)) *Palette { | |||||||||
| p.SetFieldTextColor(tcell.ColorWhite) | ||||||||||
| p.SetLabel(" ") | ||||||||||
| p.SetLabelColor(tcell.ColorYellow) | ||||||||||
| p.SetPlaceholder("command (q/quit, clear, help, model, session, mcp, roles, compact)") | ||||||||||
| p.SetPlaceholder("command (help, clear, compact, stop, steer, model, mcp, verbose, search...)") | ||||||||||
| p.SetPlaceholderTextColor(tcell.ColorGray) | ||||||||||
|
|
||||||||||
| p.SetDoneFunc(func(key tcell.Key) { | ||||||||||
|
|
@@ -92,8 +116,15 @@ func Build(onCommand func(cmd Cmd, arg string)) *Palette { | |||||||||
|
|
||||||||||
| cmd := Parse(input) | ||||||||||
| arg := "" | ||||||||||
| if cmd == CmdModel && strings.HasPrefix(input, "model ") { | ||||||||||
| switch { | ||||||||||
| case cmd == CmdModel && strings.HasPrefix(input, "model "): | ||||||||||
| arg = strings.TrimPrefix(input, "model ") | ||||||||||
| case cmd == CmdSteer && strings.HasPrefix(input, "steer "): | ||||||||||
| arg = strings.TrimPrefix(input, "steer ") | ||||||||||
| case cmd == CmdFollowUp && strings.HasPrefix(input, "followup "): | ||||||||||
| arg = strings.TrimPrefix(input, "followup ") | ||||||||||
| case cmd == CmdSearch && strings.HasPrefix(input, "search "): | ||||||||||
| arg = strings.TrimPrefix(input, "search ") | ||||||||||
| } | ||||||||||
| if cmd != CmdNone { | ||||||||||
| p.onCommand(cmd, arg) | ||||||||||
|
|
||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win
Keep the parity exit gate consistent with the documented scope.
The new notes mark the priority work complete, but the plan still lists unimplemented parity items. Update the exit gate and inventory to identify deferred items, or keep Phase 3 open until they are complete.
🤖 Prompt for AI Agents