Skip to content

feat: Claude Code plugin compatibility + subagent hardening + bump v1.1.26 - #77

Merged
laohanlinux merged 51 commits into
mainfrom
feat/plugin-compat
Sep 7, 2026
Merged

laohanlinux merged 51 commits into
mainfrom
feat/plugin-compat

Conversation

@laohanlinux

Copy link
Copy Markdown
Contributor

Summary

Brings the plugin-compat workstream to main: Claude Code plugin compatibility (skills/commands/agents/hooks/MCP), a hardened subagent system, user-global memory, and a sticky-tab task/subagent overview.

Highlights

  • Plugin compatibility (Claude Code)

    • Accept plugins contributing commands / agents / hooks / MCP-only; load commands/*.md as slash commands
    • Load MCP servers from installed plugin cache (.mcp.json + mcpServers)
    • Declarative subagent definitions (.tact/agents + plugin agents/*.md)
    • Claude plugin command hooks runtime (SessionStart / UserPromptSubmit / Pre·PostToolUse / SubagentStart)
    • Closes acceptance-review gaps: hooks default path, agent model aliases, tool-set safety, Block semantics
  • Subagent system

    • Cancel surface: cancel_subagent tool + /subagent_cancel slash + tool-card [Cancel] button
    • Parent exit cancels running subagents; cancellation persisted across shutdown
    • wait_subagent tool; multi-popup for concurrent subagent transcripts
    • Subagent worktree isolation (worktree_run / worktree_remove), reconcile index with git on startup
    • Depth-limited nested spawns, 24h resume expiry, resumed-child validation
    • Isolated skill cards via spawn_subagent skill:; Subagent sticky-tab overview under the Log
    • Restores depth-0 contract: nested spawns removed as too complex
  • LLM

    • Send x-opencode-session + tact User-Agent to OpenCode Go; bind header to the Tact session id
    • Drop prior-reasoning replay on compatible /responses endpoints
  • Memory / store

    • Persist memories under ~/.tact/memory (user-global)
    • From/TryFrom serde impls for background & subagent status; memory_manager() factory (drop get_ prefix)
  • Housekeeping

    • Prune stale design/plan docs; slim AGENTS.md; fmt + clippy -D warnings cleanup; remove unused deps
  • Version: bump to v1.1.26

Test plan

  • scripts/check-rust.sh passes locally and on push (fmt + clippy -D warnings + unit/integration tests)
  • Subagent/plugin behavior covered by unit tests in crates/tact

Notes

51 commits ahead of main; fast-forwardable.

laohanlinux and others added 30 commits August 27, 2026 09:45
…ult re-injection

- PermissionSnapshot + snapshot()/from_snapshot() for Claude-style inheritance
- spawn_subagent inherits parent mode/allow-list/settings (fixes Plan read-only escape)
- run_in_background/max_turns/resume input fields + async_launched handle
- pending_subagent_results queue drained into <subagent-finished> before next LLM call
- SubagentFinished + SubagentFinishedNotification protocol events + driver wake-up
- subagent_runs SQLite table + SubagentManager with orphan repair
- check_subagent tool; input-aware keep_live; transcript carry-over in TUI
- concurrent permission-select queue; docs + bilingual Ch26 issue log
- spawn_subagent gains worktree: true — child runs in a git worktree lane
  (subagent-<child_id>, branch wt/subagent-<child_id>) with work_dir pointed
  at the lane; created synchronously, reused on resume, kept after completion
- per-invocation resource resolution: worktree-isolated spawns map to
  ToolResources::independent() so they can fan out in the same wave as other
  tools without racing the main tree; plain spawns stay ResourcePolicy::Barrier
- WorktreeManager::get made public (used by the subagent handler)
- summary now appends (worktree: <name> at <path>) for isolated runs
- TUI: mouse-wheel over the slash-command / select popup steps the selection
  instead of scrolling the log behind it; popup hit areas cleared per frame
- docs: Ch 12 subagent bilingual + Ch 26 issue log bilingual, implementation
  plan docs/superpowers/plans/2026-08-27-subagent-worktree-isolation.md
…nds/*.md as slash commands

- validate_plugin_candidate now accepts any supported feature (skills,
  commands, agents, hooks, mcp) instead of hard-requiring skills/ — unblocks
  15+ official marketplace plugins (LSP docs, commit-commands, code-review…)
- full plugin.json manifest parse (description/version/author/hooks/mcpServers)
- InstalledPlugin records command_count/agent_count/has_hooks/has_mcp
  (serde-defaulted, old installed.json records stay valid)
- tact plugin list shows a feature summary per plugin
- SkillRegistry::load_plugin_commands loads commands/*.md as plugin:<name>
  skills (Claude: same as skills, command wins over same-named skill)
- SkillFrontmatter parses argument-hint / allowed-tools / model
- PluginStore::installed_plugin_roots generic root helper
…mcpServers)

- McpProjectConfig parses Claude .mcp.json entries (stdio/http); http/url
  transports are skipped with a warning (client is stdio-only)
- installed_plugin_mcp_servers scans every installed plugin root for
  .claude-plugin/plugin.json mcpServers and .mcp.json, naming servers
  plugin__<id>__<server> (same prefix scheme as the cwd PluginLoader)
- load_mcp_router merges installed-plugin servers after the cwd scan
…agents/*.md)

- new agent_def module: SubagentDefinition registry loading project-local
  .tact/agents/*.md (plain names) and installed plugin agents/*.md
  (plugin:<name>); frontmatter tools/model/permissionMode parsed
- ToolContext carries SharedAgentDefinitionRegistry (built in interactive/
  headless like skill_registry)
- spawn_subagent gains agent: Option<String>: definition body becomes the
  system prompt, tools: frontmatter filters the subagent toolset (Claude
  Read/Glob/Grep/Bash/Edit/Write/Sleep naming), model overrides the child
  model, permissionMode overrides inherited mode (Auto stays sticky)
- subagent_toolset_for(keep) filtered toolset builder; unknown names fall
  back to the default five-tool set
…mptSubmit/Pre/PostToolUse/SubagentStart)

- plugin/hooks.rs: parses Claude hooks JSON (matcher/command/commandWindows/
  timeout/statusMessage/async); run_command_hook executes via sh -c with
  CLAUDE_PLUGIN_ROOT/CLAUDE_PROJECT_DIR env, JSON payload on stdin, stdout
  JSON parsed in both decision and hookSpecificOutput formats; failures/
  timeouts/invalid JSON warn and continue (fail-open)
- Hook enum gains UserPromptSubmit (invoked in agent_loop on the user message
  text; additionalContext appended); SubagentStart is a standalone trait
  stored on ToolContext (no LoopState in the spawn tool path) and invoked by
  spawn_subagent to inject context into the child system prompt
- apply_plugin_hooks registers installed plugins' SessionStart/
  UserPromptSubmit/PreToolUse/PostToolUse command hooks on the Agent builder
  (interactive + headless); with_post_tool_hook added (with_post_tool is
  rtk_filter-gated)
- plugin_subagent_start_hooks builds SubagentStart closures for ToolContext
- TUI plugin list table shows skills/commands/agents/hooks/mcp columns (en/zh)
- Ch 2: commands/*.md load as plugin:<name>, frontmatter fields, precedence
- Ch 8: installed-plugin MCP scan + .mcp.json, http/url limitation
- Ch 9: Hook table gains UserPromptSubmit; SubagentStart standalone; plugin
  command-hook protocol section (events, matcher, env, dual output formats,
  fail-open semantics)
- Ch 12: declarative agent definitions (.tact/agents + plugin agents),
  spawn_subagent agent field, tools/model/permissionMode mapping
- Ch 23: /plugin list feature table; Ch 26 entry (bilingual)
…ext stdout fallback

- hook input payload merges event fields at the top level (Claude protocol
  puts tool_name/tool_input/prompt at the root), not nested under _event
- parse_output accepts plain-text stdout as additionalContext for
  UserPromptSubmit/SessionStart (ponytail's native-Claude path writes raw
  text; JSON-only parsing silently dropped its context)
… model aliases, tool-set safety, Block semantics

Review (subagent acceptance audit, ~75%) found real compatibility gaps; fixed:

- hooks: default discovery path hooks/hooks.json when plugin.json lacks the
  hooks field (6 official marketplace plugins rely on it); unparseable
  manifests now warn; InstalledHooks no longer needs plugin_id
- declarative agents: model frontmatter aliases — inherit = no override,
  sonnet/opus/haiku warn+ignore (never passed to provider), concrete ids
  pass through (resolve_agent_model)
- subagent toolset: all-declared-tools-unknown now yields an empty router
  and spawn_subagent errors out instead of silently widening to the full
  default set (permission safety)
- UserPromptSubmit Block now drops the turn (Claude semantics) instead of
  appending a marker; apply_user_prompt_hooks returns HookControl
- SubagentStart plugin-hook Block propagates to spawn_subagent (bail); the
  closure writes back to the caller's context instead of a clone; payload
  adds agent_type (ponytail PONYTAIL_SUBAGENT_MATCHER scoping reads it)
- timeout: 0 disables the timeout (was coerced to the 60s default)
- new-format suppressOutput parsed at the top level
- SessionStart plain-text/additionalContext output warns explicitly
- statusMessage surfaced via tracing::debug
- tests: hooks default-path load, SubagentStart block propagation, timeout-0,
  new-format suppressOutput, mixed known/unknown toolset, resolve_agent_model,
  installed_plugin_roots; docs spec/chapters/Ch26 synced
…timeout resolution unit test, user_text_target tests

- subagent_toolset_for: an empty tools: list (Some(&[])) keeps the default
  five-tool set (Claude semantics: absent/empty tools: does not restrict);
  only non-empty fully-unknown lists yield an empty router for spawn bail
- resolve_timeout extracted as a pure function; unit-tested (None→60,
  Some(5)→5, Some(0)→disabled) so the timeout-0 regression test is real
- user_text_target unit tests: mutates Text content and the first text block
  while preserving non-text blocks; returns None without a text block
…cel slash + tool-card [Cancel] button

- SubagentManager gains an in-memory cancel-handle registry
  (register_cancel_handle / request_cancel / unregister_cancel_handle);
  spawn_subagent registers the child's runtime.cancel_flag after Agent::new
  and unregisters on both sync and async finish
- new cancel_subagent tool (flips the cooperative flag + marks the run
  Cancelled); registered in the main toolset
- async finish path detects the flag: cancelled runs are recorded as
  Cancelled (not Completed) and the re-injected summary is prefixed
  (cancelled by user) with success=false
- protocol: UserCommand::CancelSubagent; driver flips the flag directly
  (works while the parent task is mid-turn)
- TUI: /subagent_cancel <child-id> slash command (usage flash without
  args); live async-subagent tool cards render a [Cancel] button — child id
  parsed from the async_launched { id } result, button rects returned by the
  pure renderer and routed through mouse state
- tests: manager handle lifecycle, main-toolset registration, slash command
  with/without id, mouse button click, parse_async_launched, keep-live child
  id capture; docs Ch 12 gaps + Ch 26 bilingual
Replace the single Option<SubagentPopup> slot with a HashMap keyed by
tool id plus an active-subagent pointer, so switching between concurrent
subagents preserves each card's scroll / selection / cached layout.

- App.subagent_popup -> subagent_popups + active_subagent_popup
- open_subagent_popup insert-or-reuses each card's entry
- new accessors: subagent_popup / subagent_popup_mut / has_subagent_popup /
  close_subagent_popup
- updated all overlay/mouse/layout/render/config/construct call sites
Let the parent block on a running background subagent until it reaches a
terminal status (Completed/Failed/Cancelled) or a timeout elapses, returning
the summary — so the parent can spawn N subagents then wait on each instead
of burning turns polling check_subagent.

- SubagentManager::wait(child_id, timeout_ms) polls subagent_runs (250ms)
- SubagentManager::get(child_id) -> Option<SubagentRun>
- new Read/Independent tool wait_subagent { child_id, timeout_ms? } (default 60s)
- registered in the main toolset
Isolated subagent lanes (subagent-<child_id>) previously leaked until a
manual git worktree remove. Add a first-class cleanup tool.

- WorktreeManager::remove(name): git worktree remove (no --force, dirty tree
  fails), delete tracking row, append audit event, keep wt/<name> branch
- WorktreeStore::remove_worktree(name) -> bool
- new Write/Barrier tool worktree_remove { name }; refuses a subagent-<id>
  lane whose run is still Running
- registered in the main toolset
- book/12 (en+zh): wait_subagent + worktree_remove fields and lifecycle
- book/15 (en+zh): five -> six worktree tools, remove semantics, audit log,
  gap table updates
- book/26 (en+zh): 2026-09-02 async-subagent follow-ups entry
The driver dropped SubagentFinishedNotification whenever a turn was active.
A result landing in the gap between the final queue drain and turn exit was
never re-injected: the parent stayed silent until the next manual turn.

Select on the in-flight JoinHandle alongside user_cmd_rx, retain a
pending_subagent_wakeup flag, and submit the wake-up turn as soon as the
active turn completes. SubmitTask clears the flag since the new turn drains
the queue itself.
The async completion task emitted SubagentFinished with the raw agent_loop
result, so a cancelled child that exited cleanly (Ok after the flag was set)
was reported success=true while the queued SubagentResult was already
success=false. Use one terminal_success(success, cancelled) helper for both.
resume blindly reused any id: resuming a still-Running child would race the
session (two loops on one session), and resuming an unknown id silently
minted a fresh child instead of a follow-up. Reject both before any spawn
work via SubagentManager::get.
Sync children registered a cancel handle but never wrote a subagent_runs row,
so check_subagent / cancel_subagent were blind to them and a failed sync spawn
left a stale Running row. Move manager.start above the sync/async split and
record Completed/Failed/Cancelled on the sync exit path (also unregister and
release the lock before propagating an error).

run_in_background also needs an interactive UI channel: in headless there is
no driver to submit a wake-up turn and the run ends by cancelling the child.
Degrade to synchronous (with a warning) when ui_tx is absent so the summary
still reaches the parent.
Document the five async-subagent reliability fixes in Ch 12 (wake-up
retention, cancellation success, resume validation, sync lifecycle,
headless degradation) and append a bilingual Ch 26 entry.
Subagents previously could not spawn further subagents (depth 0 by design),
so a worker could never decompose work further. Give ToolContext a
subagent_depth (0 = main agent), register spawn/check/wait/cancel_subagent in
subagent_toolset() (9 tools; Claude Task maps to spawn_subagent), and refuse
to spawn beyond MAX_SUBAGENT_DEPTH = 3. The child's tool context carries
depth + 1 so the same guard applies to grandchildren.
resume had no expiry policy (TBD in the 2026-08-26 design), so a weeks-old
finished session could be resumed with stale context. Reject resume targets
whose finished_at is older than RESUME_EXPIRY_HOURS = 24 (Claude's 24h
expiry) and cover the window check with a unit test.
worktree_remove always left the backing wt/<name> branch behind, requiring a
manual merge or git branch -D. After git worktree remove, attempt
git branch -d — only a fully-merged branch is deleted; unmerged commits are
never destroyed and the outcome is reported and audit-logged.
worktree_run executed arbitrary shell without the validate_shell_command gate
(unique among the shell tools) and wrote no audit log. Apply the same
validation as bash and append 'worktree.run <name> <command>' to the
worktree_events audit log so every lane command is inspectable.
Manual 'git worktree remove' / 'git worktree prune' left stale records in the
worktrees table forever (index drift). WorktreeManager::new now repairs
orphans: any tracked lane whose on-disk path is missing is dropped and logged
as 'worktree.stale-removed <name> (path missing)'.
Test and others added 21 commits September 2, 2026 10:50
Ch 7: subagent toolset is 9 tools (nested spawn + check/wait/cancel), gap
row updated. Ch 12: nested spawn depth limit, 24h resume expiry, nine-tool
set. Ch 15: branch cleanup on remove, run validation + audit, startup
reconciliation; gaps trimmed. Ch 26: bilingual entry for all five leftovers.
Revert 6c32665 (feat(subagent): depth-limited nested spawns). Subagents go
back to a 5-tool set without spawn_subagent/check/wait/cancel, ToolContext
drops subagent_depth, and MAX_SUBAGENT_DEPTH is removed. The resume 24h
expiry (806835d) and the worktree improvements are kept.
Mirror the revert of 6c32665: Ch 7/12 back to the five-tool set and
no-nested-spawn wording, ARCHITECTURE restored, the leftover Ch 26 entry
rewritten without nested spawn, and a bilingual removal entry appended.
Requests to OpenCode Go (https://opencode.ai/zen/go/v1) carried no
x-opencode-session header and only reqwest's generic User-Agent, so the
endpoint could not correlate the session and reported 'Unknown client';
starting 09/06 missing the header may error.

Add an opencode helper module that detects opencode.ai endpoints and emits a
per-process, per-base_url session token plus a tact/<version> User-Agent.
Attach the headers on every path to the endpoint: the Responses SDK config
(create_byot / create_stream_byot), the direct /responses/compact POST, the
Chat Completions config (defensive), and the /v1/models picker fetch.
TACT_OPENCODE_SESSION pins the session value when set.
Ch 21 custom-provider section notes that OpenCode Go endpoints
(opencode.ai and subdomains) automatically receive a stable
x-opencode-session header and a tact/<version> User-Agent (override with
TACT_OPENCODE_SESSION); Ch 26 gains a bilingual bugfix entry.
The first OpenCode fix used a per-process, per-base_url token as
x-opencode-session. OpenCode treats the header as the session key that
distinguishes per-conversation caches, so different Tact sessions sharing a
token would share/pollute each other's cache and resume would not continue
the same cache.

Reuse the LlmProvider::set_user_id hook that Agent::with_session already
calls for DeepSeek KV-cache isolation: the OpenAI Responses adapter now
stores the session id and emits x-opencode-session = <session id> on the SDK
config and the /responses/compact POST. Non-conversation requests (the
/v1/models picker fetch) still fall back to the per-base_url token;
TACT_OPENCODE_SESSION pins that fallback only.
Ch 21 updated to say the header value is the session id (same session and
resumed sessions reuse one value, different sessions differ) with the
per-base_url fallback only for non-conversation fetches; Ch 26 gains a
bilingual refinement entry above the original OpenCode entry.
Stop syncing book / design docs after every intermediate edit — that burns
tokens re-reading chapters mid-work. Agents now batch doc updates: code
first, then compare the final diff against the trigger table once, before
pushing, and sync all touched docs in a single pass.
…ints

Every /responses request replayed historical assistant reasoning items
(persisted signatures with opaque encrypted payloads) into input. Official
OpenAI needs that for turn continuation; compatible endpoints (OpenCode Go,
custom OpenAI-compatible proxies) regenerate reasoning each turn, so
replaying every previous chain of thought is pure input-token waste
(~17% of request bytes, up to ~47% of tokens on DeepSeek-style endpoints).

Add a per-adapter replay_prior_reasoning policy:
- OpenAiResponsesAdapter derives the default from the base URL
  (is_official_openai_base_url: api.openai.com / Azure OpenAI -> replay,
  everything else -> drop); with_replay_prior_reasoning overrides it.
- create_response becomes create_response_with_policy carrying a
  ResponsesRequestPolicy { native_web_search, replay_prior_reasoning }.
- The reasoning signature is still decoded on every path so fc_* function
  call item ids stay attached to their function_call items; only the
  standalone reasoning payload is omitted.
- With replay disabled, stale reasoning items are filtered from the
  persisted state baseline before the body is built and the returned
  input_items exclude reasoning, so older persisted states self-heal.
Ch 22 §6.2.3 documents the new replay policy (base-URL default + override),
and §6.2 / §6.2.2 now scope the earlier "next request replays reasoning"
statements to official OpenAI. Ch 26 issue log gains the 2026-09-05
optimization entry in both languages.
OpenCode's hosted endpoints no longer require an x-opencode-session
header (nor a custom tact/<version> User-Agent), so the mechanism that
detected opencode.ai base URLs and attached the header to every request
was dead weight. It also forced plumbing the Tact session id
(Agent::with_session -> LlmProvider::set_user_id ->
OpenAiResponsesAdapter::set_session_id) purely to feed one header value.

Delete crates/tact_llm/src/opencode.rs and stop attaching the header on
the Responses SDK config, the direct /responses/compact POST, the Chat
Completions config, and the /v1/models picker fetch. Drop the session_id
field / set_session_id from the Responses adapter and the OpenAiResponses
arm of set_user_id. Remove the TACT_OPENCODE_SESSION env override.

opencode.ai endpoints are now treated like any other OpenAI-compatible
base URL; DeepSeek user_id KV-cache isolation is unaffected. Syncs Ch 21
and adds a Ch 26 removal entry.
Like Claude Code's user-level ~/.claude state, memories now live in
$HOME/.tact/memory so they survive across projects. The legacy
<workdir>/.tact/memory path stays as a fallback only when $HOME is
unset; save_memory confirmations render ~/... paths. Docs (store,
skill, memory, prompt chapters, ARCHITECTURE) synced en/zh.
…tus serde

Replace duplicated status_to_str/str_to_status helpers in the sqlite stores with From<Status> for &'static str and TryFrom<&str> on the enums, and refresh the spawn_subagent tool description.
spawn_subagent no longer accepts `agent: <name>`; every child runs on the
generic static system prompt with the fixed subagent_toolset() five tools.

- delete agent_def.rs (registry + frontmatter parsing); drop the
  ToolContext.agent_registry field and TactPath::agents_dir
- subagent.rs: remove SubagentInput.agent, resolve_agent_model, and the
  spawn-time prompt/permission/model/toolset overrides; SubagentStart
  hooks keep running with the child_id as the hook name
- registry.rs: remove subagent_toolset_for / allowed_tool_names and the
  filtered toolset builder
- plugins no longer count or advertise agents/*.md: drop
  InstalledPlugin/PluginFeatures.agent_count from install bookkeeping,
  tact plugin list, and the /plugin table; an agents/-only plugin is now
  rejected at install (test install_rejects_agent_only_plugin)
- docs: Ch 7/12/21 en+zh drop declarative-agent wording; Ch 26 en+zh get
  a removal entry (2026-09-06)

[agent.subagent] config and /model-subagent are unaffected.
Restore reusable worker roles after agent-def removal (8c74f4e) with a
deliberately minimal, isolated mechanism:

- SubagentInput gains an opt-in `skill: <name>`; the handler reads
  ~/.tact/subagent/<name>.md (key = file stem; frontmatter description is
  only for listings) and appends its body as a <skill> block to the
  child's static system prompt before SubagentStart hooks run.
- Names must be plain file stems (no separators / `.`/`..` / NUL) so a
  skill value cannot read outside the card directory; unknown names fail
  the spawn and list available cards. Catalog listing follows symlinks to
  stay consistent with the resolver.
- ToolRouter gains set_tool_description so interactive/headless annotate
  the spawn_subagent tool description with the card catalog at startup
  (single-line, 60-char-capped descriptions, 30-card cap) for model
  discovery; empty/absent dir leaves the description unchanged.
- Cards never enter SkillRegistry, plugin side, or ToolContext; subagent
  toolset stays at five tools.
- Bilingual Ch 12 + Ch 26 synced; spec/plan added.
Add a persistent status overview for subagents in the TUI, mirroring the
Tasks sticky strip, on the current component architecture (no rollback of
98a133f's tool-card detail direction).

- protocol: SubagentStatusSnapshot, SubagentRunSnapshot, and
  AgentUpdate::SubagentsChanged { runs } full-snapshot event
- tact: SubagentManager tracks children started by this process (known
  set); emit_subagents_changed after spawn start / sync+async finish /
  cancel_subagent tool / driver CancelSubagent
- agent_tui_kit: SubagentPanelComponent/State + two-domain sticky host
  (render/sticky_host.rs) with [Tasks] [Subagent] tab segments returning
  per-frame hit areas
- tui: sticky host integration (layout, mouse/normal handlers, registry),
  tab click switches/expands domain, wheel/jk scroll the active domain
- Live subagent detail stays on the tool card / SubagentPopup; the sticky
  shows status-level runs only (Running -> Completed -> Failed ->
  Cancelled), capped with all Running preserved

Docs: Ch 12 / Ch 23 / Ch 26 (EN+ZH); design spec and plan under
docs/superpowers/{specs,plans}/2026-09-07-subagent-sticky-tab*.md
Remove 47 outdated design specs and execution plans whose features are
shipped and documented in book chapters / code. Keep the two files still
linked from stable docs and code (task-progress-panel-design, referenced
by Ch 19; tui-component-library plan, referenced by agent_tui_kit) plus
the latest three design+plan sets (claude-plugin-compat, skill-cards,
subagent-sticky-tab).
Branch's pre-push hook (scripts/check-rust.sh: fmt + clippy -D warnings +
tests) was failing on pre-existing issues. Clean up so the hook passes:
- cargo fmt across workspace (stale formatting from earlier commits)
- tui: drop unused task_panel/subagent_panel re-export modules and dead
  sticky_tab_visible/sticky_tab_scroll helpers
- tui: iterate slices instead of index-only loops (needless_range_loop)
- Remove unused deps: dashmap + tracing-subscriber (workspace/tact/tui),
  tokio (protocol, agent_tui_kit); sync Cargo.lock
- Drop misleading workspace rust-version=1.85 (unused; code needs 1.91+)
@laohanlinux
laohanlinux merged commit ad966c5 into main Sep 7, 2026
2 of 3 checks passed
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