Skip to content

Modernize toward latest Claude Code (prompts, hooks, skills, agents, UI)#2865

Open
lmclaw wants to merge 9 commits into
Hmbown:mainfrom
lmclaw:chore/modernize-claude-code
Open

Modernize toward latest Claude Code (prompts, hooks, skills, agents, UI)#2865
lmclaw wants to merge 9 commits into
Hmbown:mainfrom
lmclaw:chore/modernize-claude-code

Conversation

@lmclaw
Copy link
Copy Markdown

@lmclaw lmclaw commented Jun 6, 2026

Summary

Modernizes DeepSeek TUI toward the latest Claude Code across behavior, lifecycle, skills/agents, and UI — driven by a grounded gap analysis. The system was already a near-complete Claude Code analog; this closes the long tail in safe, independently-tested increments.

What's included

  • Repo hygiene — enforce LF via .gitattributes; the working tree had drifted to CRLF on every file (467 files of pure line-ending noise), making diffs unreviewable.
  • Gap analysisCLAUDE_CODE_GAP_ANALYSIS.md: grounded comparison + prioritized roadmap.
  • Wave 1 · prompts — behavioral alignment in base.md/plan.md: Engineering Altitude, Acting Safely (irreversible/outward-facing actions), Editing Code: Fit In, clickable refs, scheduling discipline, plan-mode etiquette. Byte-stable; 51 prompt tests pass.
  • Wave 2 · hooks — four new lifecycle hook events (stop, subagent_stop, pre_compact, notification) wired at clean UI-thread seams + documented in /hooks. 25 hook tests pass.
  • Wave 3 · skillsmemory-consolidate bundled skill; generalized the system-skill installer to a skill list with a recorded known-set marker (new skills install on upgrade, user deletions respected). 9 tests pass.
  • UI · Claude Code visual language — terracotta accent (#D97757), / tool markers, rounded > composer (synced wrap/cursor math), welcome banner. Full TUI suite 2849 pass.
  • Wave 4 · agents — file-defined sub-agent roles (~/.deepseek/agents/<name>.md) overriding built-in role prompts at a single hermetic seam. 10 tests pass.
  • Wave 5 · output stylesconcise + explanatory overlays + Personality::from_str/as_str.

Testing

cargo test -p deepseek-tui --bins → 2849 passed, 0 failed (one localhost-socket MCP test is a pre-existing concurrency flake; passes in isolation). cargo check clean.

Deferred (noted in the gap doc)

Runtime selection (config + picker) for output styles/personalities (a pre-existing dormant gap), prompt advertisement of file-defined agents, arbitrary new user-named agent types (persisted-enum change), Wave 6 small UX, and the plugin/marketplace system.

🤖 Generated with Claude Code

Greptile Summary

This PR modernizes DeepSeek TUI toward the latest Claude Code across five waves: behavioral prompt alignment, four new lifecycle hook events, a memory-consolidate bundled skill with a generalized installer, file-defined sub-agent roles, and two new output-style overlays — plus a Claude Code-inspired UI reskin (terracotta accent, / glyphs, rounded composer).

  • Hooks (hooks.rs, tui/ui.rs): Stop, SubagentStop, PreCompact, and Notification wired at the right event-loop seams; serde rename_all = "snake_case" means existing config parsers pick them up automatically without manual from_str changes.
  • Skill installer (skills/system.rs): Extended to a multi-skill bundle with a known-set marker so newly-added skills auto-install on upgrade while respecting user deletions — though the newly-bundled case installs unconditionally regardless of whether a directory already exists, which can silently overwrite a user-created skill with a colliding name.
  • Agents module (agents.rs, tools/subagent/mod.rs): Clean new module for ~/.deepseek/agents/<name>.md; build_subagent_system_prompt correctly falls back to built-ins and always appends the shared output contract.

Confidence Score: 4/5

Safe to merge with one targeted fix: the skill installer can silently overwrite user content on upgrade.

The bulk of the change is well-structured and thoroughly tested (2849 passing). The concern in skills/system.rs — the newly-bundled branch installs without checking dir_exists — can silently destroy a user skill with a colliding name on upgrade. Everything else reviewed cleanly.

crates/tui/src/skills/system.rs — the newly-bundled skill installation path; crates/tui/src/commands/review.rs — the --effort fallthrough.

Important Files Changed

Filename Overview
crates/tui/src/skills/system.rs Refactored skill installer to support multiple bundled skills with a known-set marker; upgrade/deletion logic is sound except that newly-bundled skills overwrite existing user directories with the same name without checking dir_exists.
crates/tui/src/commands/review.rs Added ReviewEffort enum and effort-tier parsing for /review; parser falls through silently when --effort is given an unknown level, returning the raw flag string as the review target instead of an error.
crates/tui/src/agents.rs New module for file-defined sub-agent roles (~/.deepseek/agents/.md); frontmatter parsing, case-insensitive loading, and context rendering all look correct and well-tested.
crates/tui/src/hooks.rs Four new HookEvent variants (Stop, SubagentStop, PreCompact, Notification) added with serde rename_all = snake_case covering config deserialization automatically; turn_status field and with_turn_status builder added cleanly to HookContext.
crates/tui/src/tui/ui.rs New lifecycle hook dispatch points wired at correct seams: Stop after turn completion/interrupt/failure, SubagentStop on sub-agent completion, PreCompact on CompactionStarted, Notification when the long-turn notification fires.
crates/tui/src/tui/widgets/mod.rs Composer updated with "> " prompt prefix; input_width and cursor_x calculations consistently subtract/add COMPOSER_PROMPT_WIDTH in both render and cursor-position paths, preventing off-by-one drift.
crates/tui/src/tools/subagent/mod.rs build_subagent_system_prompt split into public/inner form for testability; file-defined agent lookup tries explicit role then agent-type name, appends shared output contract, falls back to built-ins.
crates/tui/src/palette.rs Terracotta accent constants added; light-mode adaptation correctly maps both CLAUDE_TERRACOTTA variants to #A64B2F for legibility, with DEEPSEEK_SKY kept on its own branch.
crates/tui/src/tui/history.rs ASSISTANT_GLYPH changed to ⏺; TOOL_RESULT_CONNECTOR/TOOL_RESULT_INDENT added; render_card_detail_line correctly uses ⎿ only on the first detail line and indents continuations.

Sequence Diagram

sequenceDiagram
    participant UI as Event Loop (ui.rs)
    participant HM as HookManager
    participant FS as Filesystem

    UI->>HM: TurnOutcome event
    HM->>HM: has_hooks_for_event(Stop)?
    UI->>HM: execute_hooks(Stop, ctx.with_turn_status("completed"))

    UI->>HM: CompactionStarted event
    HM->>HM: has_hooks_for_event(PreCompact)?
    UI->>HM: execute_hooks(PreCompact, ctx)

    UI->>HM: SubAgentDone event
    HM->>HM: has_hooks_for_event(SubagentStop)?
    UI->>HM: execute_hooks(SubagentStop, ctx.with_message("subagent N: …"))

    UI->>HM: LongTurnNotification
    HM->>HM: has_hooks_for_event(Notification)?
    UI->>HM: execute_hooks(Notification, ctx.with_message(msg))

    Note over UI,FS: Sub-agent spawn path
    UI->>FS: default_agents_dir() → ~/.deepseek/agents/
    FS-->>UI: load_agent_definition(dir, role)
    UI->>UI: build_subagent_system_prompt_in(type, assignment, dir)
Loading

Fix All in Codex Fix All in Claude Code Fix All in Cursor

Reviews (1): Last reviewed commit: "feat(review): add effort tiers to /revie..." | Re-trigger Greptile

Greptile also left 2 inline comments on this PR.

StevFang and others added 9 commits June 2, 2026 22:59
The working tree had drifted to CRLF on every text file, making diffs
unreviewable (467 files, all pure line-ending noise). Add a .gitattributes
that normalizes text to LF and marks binary assets, so future diffs reflect
real changes only.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Grounded comparison of DeepSeek TUI v0.8.32 against latest Claude Code
across prompts, tools, skills/subagents, and lifecycle (hooks/memory).
Concludes the system is already a near-complete analog; defines a
6-wave prioritized roadmap to close the remaining long tail.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Surgical, additive behavioral refinements to the static prompt prefix:

- Engineering Altitude: solve at the right level; no over/under-engineering
- Acting Safely: confirm irreversible & outward-facing actions; look
  before overwrite; report outcomes faithfully
- Editing Code: Fit In: match surrounding style, reuse first, no churn
- References: emit clickable path:line and PR/issue URLs
- Scheduling discipline: when (not) to reach for automation_*
- Plan-mode etiquette: investigate fully; the plan is the approval ask

All additions land above the locale closer and keep the prompt
byte-stable for unchanged workspaces. 51 prompt tests pass.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…ve 2)

Closes the lifecycle-hook gap vs Claude Code by adding its four
most-used hook events to the user-configurable shell-hook system:

- stop: assistant finished a turn (DEEPSEEK_TURN_STATUS =
  completed/interrupted/failed) — fired from the TurnComplete handler
- subagent_stop: a sub-agent finished — fired from AgentComplete
- pre_compact: context compaction begins (manual /compact or auto),
  before the summary replaces the transcript — fired from
  CompactionStarted, covering both paths
- notification: a user-facing desktop notification was surfaced —
  fired alongside notify_done

Each dispatch is guarded by has_hooks_for_event so zero-config
sessions pay nothing. Adds a turn_status context field
(DEEPSEEK_TURN_STATUS), documents all four in /hooks, and extends
event_label + its coverage test. 25 hook tests pass; cargo check clean.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Ships a reflective memory-maintenance skill (Claude Code's
consolidate-memory analog): classify entries, merge duplicates, fix
contradicted facts, prune stale one-offs, and keep the user memory
file short enough to stay useful on every turn — working with the
existing single-file memory model.

Generalizes the system-skill installer from a single hardcoded skill
to a list, backed by a recorded known-set marker so newly-bundled
skills install on upgrade while user-deleted skills stay deleted.
Bundle version → 2. 9 system-skill tests pass.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Accent: warm terracotta (#D97757) replaces DeepSeek blue as the
  primary accent (composer border + prompt, action bullets, headings,
  live tool markers); light mode uses a darker terracotta for contrast.
- Markers: assistant actions and completed tools lead with ⏺; tool
  results hang under a ⎿ tree connector (continuations align under it).
- Composer: rounded box with a terracotta "> " prompt; input wrap width
  and cursor position account for the prompt gutter in both render and
  cursor_pos so the caret stays aligned.
- Welcome: startup empty state is a rounded "✻ Welcome to DeepSeek TUI"
  banner with model + cwd + quick hints.

Updated the four affected unit tests (cursor +2 prompt offset, ⎿
connector on plan/tool detail lines, new banner text). Full TUI suite:
2849 passed, 0 failed.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Adds ~/.deepseek/agents/<name>.md role definitions (Claude Code's
file-defined agents analog) on top of the existing built-in roles.
A new `agents` module parses YAML-ish frontmatter (name, description,
optional model/tools) + body; when a sub-agent is spawned whose role
or type matches a file, that body replaces the built-in role intro
(the shared output contract is still appended).

Wired at the single build_subagent_system_prompt seam via a
dir-injectable inner fn so tests stay hermetic. Existing prompt tests
moved to the injectable form; new tests cover the loader and override.
10 tests pass. The persisted SubAgentType enum is untouched — arbitrary
new user-named *types* (vs role-prompt override) remain a follow-up.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Generalizes the personality overlay system toward Claude Code's output
styles. Alongside tone-shaping Calm/Playful, adds two behavior-shaping
styles: concise (answer-first, minimal) and explanatory (teaches as it
works), as overlay files composed into the prompt. Adds
Personality::from_str/as_str with aliases. 3 tests pass.

Runtime selection of a non-default style (config + picker) is a
pre-existing dormant gap — even Playful isn't user-selectable yet — and
is left as a separate, runtime-validated change.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
/review [--effort low|medium|high|max] <target> (a bare leading level
also works) injects a depth directive into the review skill — low/medium
report few high-confidence findings, high broadens coverage with flagged
uncertainty, max is exhaustive. Mirrors Claude Code's /code-review levels;
defaults to medium. New ReviewEffort enum + arg parser, usage string
updated, 8 review tests pass.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@github-actions
Copy link
Copy Markdown

github-actions Bot commented Jun 6, 2026

Thanks @lmclaw for taking the time to contribute.

This repository is currently observing a maintainer-managed contribution gate in dry-run mode, so this pull request is staying open. When enforcement is enabled, pull requests from contributors who are not listed in .github/APPROVED_CONTRIBUTORS will be closed automatically.

Please read CONTRIBUTING.md for the expected contribution shape. A maintainer can grant PR access by commenting /lgtm on a pull request.

Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request modernizes the DeepSeek TUI by aligning its visual language, prompts, and lifecycle hooks with Claude Code. Key changes include restyling the TUI with a warm terracotta accent, introducing /review effort tiers, generalizing output styles (adding concise and explanatory presets), adding file-defined sub-agent roles, bundling a new memory-consolidate skill, and adding four new lifecycle hook events (stop, subagent_stop, pre_compact, and notification). Feedback on the implementation highlights that the custom frontmatter parser for agent definitions does not strip comments, which can corrupt parsed values. Additionally, the parsed model and tools fields are currently ignored when spawning sub-agents, and the available user-defined agents are not yet advertised to the system prompt.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment thread crates/tui/src/agents.rs
Comment on lines +69 to +72
let line = line.trim();
let Some((key, value)) = line.split_once(':') else {
continue;
};
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.

high

The custom frontmatter parser does not strip comments (lines starting with # or containing trailing comments). If a user includes comments in the frontmatter—as shown in the documented example in the module-level docstring (e.g., model: deepseek-v4-pro # optional)—the parser will corrupt the parsed values (e.g., parsing the model as "deepseek-v4-pro # optional" and tools as ["read_file", "grep_files # optional", "comma-separated"]).

We should strip comments from each line before parsing.

        let line = match line.split_once('#') {
            Some((left, _)) => left.trim(),
            None => line.trim(),
        };
        let Some((key, value)) = line.split_once(':') else {
            continue;
        };

Comment thread crates/tui/src/agents.rs
/// Render an "Available Agents" context block for the system prompt, or `None`
/// when no definitions exist. Mirrors the skills-context shape.
#[must_use]
pub fn render_available_agents_context(dir: &Path) -> Option<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.

medium

The function render_available_agents_context is defined and tested, but it is never actually called or integrated into the system prompt composition (e.g., in crates/tui/src/prompts.rs). This means the available user-defined agents are never advertised to the model, preventing the model from discovering and spawning them autonomously.

Comment on lines +2647 to +2666
fn build_subagent_system_prompt_in(
agent_type: &SubAgentType,
assignment: &SubAgentAssignment,
agents_dir: Option<&std::path::Path>,
) -> String {
if let Some(dir) = agents_dir {
let candidates = [
assignment
.role
.as_deref()
.map(str::trim)
.filter(|r| !r.is_empty()),
Some(agent_type.as_str()),
];
for name in candidates.into_iter().flatten() {
if let Some(def) = crate::agents::load_agent_definition(dir, name) {
return format!("{}\n\n{SUBAGENT_OUTPUT_FORMAT}", def.body.trim());
}
}
}
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.

medium

The AgentDefinition struct parses model and tools from the agent's frontmatter, but these fields are currently ignored when spawning the sub-agent in build_subagent_system_prompt_in. To fully support file-defined agents with custom model and tool scoping (as described in the gap analysis), the spawning logic should be updated to apply these overrides if present.

Comment on lines +86 to +92
let should_install = if fresh {
true
} else if !is_known {
true
} else {
version_changed && dir_exists
};
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.

P1 Newly-bundled skill silently overwrites user-created skill with same name

When !is_known (a skill newly added to BUNDLED_SKILLS), should_install is true regardless of dir_exists. This means a user who already has ~/.deepseek/skills/memory-consolidate/ with custom content — created before this bundle existed — will have it silently overwritten on first run after upgrading. The version-bump branch correctly guards on dir_exists to respect user deletions; the newly-bundled branch should apply the same guard. Without it, any future skill added to BUNDLED_SKILLS whose name collides with an existing user skill will destroy user data without warning. Was the intent to always overwrite if the skill hasn't been tracked before, or should an existing directory be respected (i.e. only install if !dir_exists)?

Fix in Codex Fix in Claude Code Fix in Cursor

Comment on lines +55 to +62
// `--effort <level> <target>` or `--effort=<level> <target>`
if let Some(rest) = args.strip_prefix("--effort") {
let rest = rest.trim_start_matches('=').trim_start();
let (level_tok, target) = rest.split_once(char::is_whitespace).unwrap_or((rest, ""));
if let Some(effort) = ReviewEffort::from_token(level_tok) {
return (effort, target.trim());
}
}
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.

P2 --effort with an unrecognized level silently falls through and treats the entire --effort <unknown> <target> string as the target path. The user receives a confusing "target not found" error rather than a clear "unknown effort level" message. A guard at the end of the --effort branch returns the error before reaching the fallthrough.

Suggested change
// `--effort <level> <target>` or `--effort=<level> <target>`
if let Some(rest) = args.strip_prefix("--effort") {
let rest = rest.trim_start_matches('=').trim_start();
let (level_tok, target) = rest.split_once(char::is_whitespace).unwrap_or((rest, ""));
if let Some(effort) = ReviewEffort::from_token(level_tok) {
return (effort, target.trim());
}
}
// `--effort <level> <target>` or `--effort=<level> <target>`
if let Some(rest) = args.strip_prefix("--effort") {
let rest = rest.trim_start_matches('=').trim_start();
let (level_tok, target) = rest.split_once(char::is_whitespace).unwrap_or((rest, ""));
if let Some(effort) = ReviewEffort::from_token(level_tok) {
return (effort, target.trim());
}
// `--effort` was present but the level token is unrecognised; signal
// this by returning an empty target so the caller shows usage.
return (ReviewEffort::Medium, "");
}

Fix in Codex Fix in Claude Code Fix in Cursor

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.

2 participants