Skip to content

feat(repl): workflow commands — /todo, custom md commands, /explain+/changelog, Stop hooks, /mcp - #138

Merged
1011-a merged 7 commits into
mainfrom
feat/repl-workflow-commands
Jun 7, 2026
Merged

feat(repl): workflow commands — /todo, custom md commands, /explain+/changelog, Stop hooks, /mcp#138
1011-a merged 7 commits into
mainfrom
feat/repl-workflow-commands

Conversation

@1011-a

@1011-a 1011-a commented Jun 4, 2026

Copy link
Copy Markdown
Contributor

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

  1. /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 via TodoWrite. New tools::todo_lines() accessor.

  2. Custom markdown slash commands (keystone) — drop <name>.md under .deeptide/commands/ (project), ~/.deeptide/commands/ (global), or the tide config dir, and run /<name> [args]. The body becomes a prompt with $ARGUMENTS / $1..$11 substitution (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.

  3. /explain + /changelog — two read-only bounded workflows as BuiltinSkill entries:

    • /explain <file|symbol|area> — resolve (Read/Grep/Glob) then a focused explanation with path:line refs.
    • /changelog [git-range] — group git log into Markdown release notes by type, breaking-changes first.

    Dispatched through a new shared dispatch_skill_command helper (also de-dupes /commit).

  4. Stop / SubagentStop hook events — brings hook coverage to the full 8-event model, enabling turn-end automation (auto-format/commit/notify). Stop is wired by splitting AgentLoop::run into 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. SubagentStop fires on the parent's engine after an Agent-tool sub-agent returns.

  5. /mcp add|remove|list — first command that persists to settings.json. Lists merged servers; add/remove write the user-global file via a safe round-trip writer (ConfigStore::set_mcp_server/remove_mcp_server) that mutates only the mcp_servers object through a raw serde_json::Value, preserving unknown top-level keys and sibling servers (values kept intact; key order is normalized by the serializer, since serde_json is built without preserve_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)
  • Full suite: 1297 passed / 0 failed
  • Per-feature unit + integration tests, incl. the custom-command no-shadow safety test, the Stop/SubagentStop hook-fires tests, and the MCP round-trip preservation test.

1011-a added 7 commits June 4, 2026 22:42
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.
@1011-a
1011-a merged commit 7b81426 into main Jun 7, 2026
1 check 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