Modernize toward latest Claude Code (prompts, hooks, skills, agents, UI)#2865
Modernize toward latest Claude Code (prompts, hooks, skills, agents, UI)#2865lmclaw wants to merge 9 commits into
Conversation
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>
|
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 Please read |
There was a problem hiding this comment.
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.
| let line = line.trim(); | ||
| let Some((key, value)) = line.split_once(':') else { | ||
| continue; | ||
| }; |
There was a problem hiding this comment.
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;
};| /// 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> { |
There was a problem hiding this comment.
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.
| 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()); | ||
| } | ||
| } | ||
| } |
There was a problem hiding this comment.
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.
| let should_install = if fresh { | ||
| true | ||
| } else if !is_known { | ||
| true | ||
| } else { | ||
| version_changed && dir_exists | ||
| }; |
There was a problem hiding this comment.
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)?
| // `--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()); | ||
| } | ||
| } |
There was a problem hiding this comment.
--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.
| // `--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, ""); | |
| } |
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
.gitattributes; the working tree had drifted to CRLF on every file (467 files of pure line-ending noise), making diffs unreviewable.CLAUDE_CODE_GAP_ANALYSIS.md: grounded comparison + prioritized roadmap.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.stop,subagent_stop,pre_compact,notification) wired at clean UI-thread seams + documented in/hooks. 25 hook tests pass.memory-consolidatebundled 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.#D97757),⏺/⎿tool markers, rounded>composer (synced wrap/cursor math),✻welcome banner. Full TUI suite 2849 pass.~/.deepseek/agents/<name>.md) overriding built-in role prompts at a single hermetic seam. 10 tests pass.concise+explanatoryoverlays +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 checkclean.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-consolidatebundled 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.rs,tui/ui.rs):Stop,SubagentStop,PreCompact, andNotificationwired at the right event-loop seams; serderename_all = "snake_case"means existing config parsers pick them up automatically without manualfrom_strchanges.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.rs,tools/subagent/mod.rs): Clean new module for~/.deepseek/agents/<name>.md;build_subagent_system_promptcorrectly 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
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)Reviews (1): Last reviewed commit: "feat(review): add effort tiers to /revie..." | Re-trigger Greptile