feat(repl): workflow commands — /todo, custom md commands, /explain+/changelog, Stop hooks, /mcp - #138
Merged
Merged
Conversation
The status bar shows a `todo N/M` count but there was no way to expand it. `/todo` (aliases /todos, /tasklist) lists the backlog — in-progress items first (showing their active form), then pending, then completed — reading the same global store the TodoWrite tool populates. Read-only; the agent owns the list. - New `tools::todo_lines()` accessor (mirrors todo_summary's locking). - `/todo` handler + dispatch arm + completion source + "Core" help category. - Tests: todo_lines ordering/dedup unit tests; a REPL integration test driving TodoWrite then asserting /todo lists it, plus /help lists the command.
Drop a `<name>.md` under `.deeptide/commands/` (project), `~/.deeptide/commands/` (global), or the tide config dir, and run it as `/<name> [args]`. The file body becomes a prompt submitted as a normal turn, with `$ARGUMENTS` and positional `$1..$11` substitution — the same expander the built-in skills already use. Mirrors the existing sub-agents-from-markdown discovery (`discover_agent_names`). - `command_dirs` / `discover_command_names` / `find_command_file` (name charset restricted so a crafted name can't escape the commands dir). - Dispatch: a custom command is matched ONLY after every built-in handler, so a file named like a built-in (e.g. `status.md`) never shadows it. - The body expands to a *prompt*, not a nested slash command, so there is no command-recursion to guard. - Discovered commands are registered in the completion/help source for this cwd, so they tab-complete and appear in `/help` (without shadowing built-ins). - Reuses `memory::strip_frontmatter` to drop optional YAML frontmatter and `tools::expand_skill_prompt` (now pub(crate)) for substitution. Tests: body-expands-and-runs (with $ARGUMENTS + frontmatter strip), built-in not shadowed by a same-named file, custom command shows in /help. README updated.
Two read-only bounded workflows on the existing built-in-skill mechanism: - /explain <file|symbol|area> — resolve the target (Read/Grep/Glob), then a focused explanation (what/how/connections/gotchas) with `path:line` refs. Framed read-only. Empty args shows usage. - /changelog [git-range] — group `git log` into Markdown release notes by type, breaking-changes first. Read-only; defaults the range to the latest tag..HEAD. Both are `BuiltinSkill` entries expanded via the Skill tool, dispatched through a new shared `dispatch_skill_command` helper (also now backs /commit, de-duping the copy-pasted commit body). Registered in dispatch, completion, and help (Git/Review categories); DiscoverSkills now lists them. Tests: /explain usage-on-empty + target substitution + read-only framing; /changelog range substitution + git-log driving; DiscoverSkills required-names extended; /help lists both.
Brings deeptide's hook coverage to parity with the 8-event model. Enables turn-end automation (auto-format, auto-commit, notify) without polling. - Stop: fires once when the agent finishes responding to a user turn. Wired by splitting AgentLoop::run into a thin wrapper + run_inner, so Stop fires on EVERY exit path (normal, max-turns, blocked, error) exactly once — no need to touch the loop's many early returns. Observational (result discarded), so a Stop hook can't wedge the REPL. - SubagentStop: fires on the parent's hook engine once an Agent-tool sub-agent's own loop returns, with the sub-agent's task description as the hook input. - HookEvent enum + as_str + entries() match; SettingsHooks gains `Stop` / `SubagentStop` (serde-renamed) fields; config summary hook-count and the /hooks listing include them. Tests: a Stop hook fires on turn end; a SubagentStop hook fires after a sub-agent finishes (on the subagent-backend-factory scaffold).
First command that persists to settings.json. `/mcp` lists the configured MCP servers (merged global/project/local); `/mcp add` and `/mcp remove` write the user-global file: - /mcp add <name> <command> [args…] — stdio server - /mcp add <name> --url <url> — HTTP/SSE server - /mcp remove <name> Adds a safe round-trip writer to ConfigStore (set_mcp_server / remove_mcp_server + shared write_pretty) that mutates only the `mcp_servers` object through a raw serde_json::Value, so unknown top-level keys and sibling servers are preserved byte-for-byte — the same approach as the existing set_value/unset_value helpers. Errors (unreadable/invalid/non-object file) are reported, never panic. Wired into dispatch, completion, and a new "MCP" help category. Tests: config-layer round-trip proves unknown fields + other servers survive an add and a remove, and that a missing-name remove reports false without erroring; /help lists /mcp.
- expand_skill_prompt: substitute positionals highest-index-first so $1 (a prefix of $10/$11) no longer corrupts the two-digit placeholders the custom-command feature advertises. - execute_custom_command: a body starting with / is sent to the agent as a literal prompt instead of being re-dispatched via submit(), closing a command-injection / self-recursion (stack-overflow) path. - discover_command_names: apply the same charset filter find_command_file uses (extracted to is_valid_command_name), so a file like 'foo bar.md' can't appear in /help or completion as an unrunnable command. - set_mcp_server: doc-comment notes values are preserved but key order is normalized (serde_json built without preserve_order). Adds unit tests for two-digit positional expansion and the leading-slash guard. fmt + clippy -D warnings clean; full deeptide-core suite green.
write_pretty now writes to a pid-suffixed temp file beside the target and renames it into place, so a crash or concurrent reader never observes a half-written settings file (the same idiom session.rs already uses). Route set_value and unset_value through it too, so all three config writers share one atomic path. Asserts no temp residue in the MCP round-trip test.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Five REPL features, prioritized from a feature-gap investigation of deeptide (value ÷ effort). Each is its own commit with tests; the whole branch is fmt-clean, clippy
-D warnings-clean, and 1297 tests green. All were verified end-to-end against the live DeepSeek backend during development.Features
/todo(/todos,/tasklist) — expands the backlog the status bar only counts (todo N/M). In-progress first (active form), then pending, then completed. Read-only; the agent owns the list viaTodoWrite. Newtools::todo_lines()accessor.Custom markdown slash commands (keystone) — drop
<name>.mdunder.deeptide/commands/(project),~/.deeptide/commands/(global), or the tide config dir, and run/<name> [args]. The body becomes a prompt with$ARGUMENTS/$1..$11substitution (reuses the built-in skill expander). Mirrors the existing sub-agents-from-markdown discovery. Built-ins always win over a same-named file (no shadowing); command names are charset-restricted so a crafted name can't escape the commands dir; the body is a prompt (not a nested command) so there's no recursion. Discovered commands tab-complete and show in/help./explain+/changelog— two read-only bounded workflows asBuiltinSkillentries:/explain <file|symbol|area>— resolve (Read/Grep/Glob) then a focused explanation withpath:linerefs./changelog [git-range]— groupgit loginto Markdown release notes by type, breaking-changes first.Dispatched through a new shared
dispatch_skill_commandhelper (also de-dupes/commit).Stop/SubagentStophook events — brings hook coverage to the full 8-event model, enabling turn-end automation (auto-format/commit/notify).Stopis wired by splittingAgentLoop::runinto a thin wrapper +run_inner, so it fires once on every exit path (normal/max-turns/blocked/error) without touching the loop's early returns. Observational — can't wedge the REPL.SubagentStopfires on the parent's engine after an Agent-tool sub-agent returns./mcp add|remove|list— first command that persists tosettings.json. Lists merged servers;add/removewrite the user-global file via a safe round-trip writer (ConfigStore::set_mcp_server/remove_mcp_server) that mutates only themcp_serversobject through a rawserde_json::Value, preserving unknown top-level keys and sibling servers (values kept intact; key order is normalized by the serializer, sinceserde_jsonis built withoutpreserve_order— matching the other config writers). Supports stdio (<command> [args]) and HTTP/SSE (--url).Testing
cargo fmt --all -- --check✓cargo clippy --all-targets --all-features -- -D warnings✓ (0 warnings)