From cbe216b8f4ee1be2c5ba3d9593ef345add381684 Mon Sep 17 00:00:00 2001 From: ScriptedAlchemy Date: Thu, 2 Jul 2026 00:14:02 +0000 Subject: [PATCH 1/2] feat: steer agents to the tracedecay CLI when MCP transport fails --- codex-plugin/.codex-plugin/plugin.json | 2 +- codex-plugin/README.md | 7 ++++ codex-plugin/skills/using-the-cli/SKILL.md | 42 +++++++++++++++++++++ cursor-plugin/.cursor-plugin/plugin.json | 2 +- cursor-plugin/README.md | 7 ++++ cursor-plugin/rules/tracedecay.mdc | 1 + cursor-plugin/skills/using-the-cli/SKILL.md | 42 +++++++++++++++++++++ src/agents/codex.rs | 4 ++ src/agents/cursor.rs | 4 ++ src/hooks.rs | 8 +++- src/mcp/server.rs | 19 +++++++++- 11 files changed, 134 insertions(+), 4 deletions(-) create mode 100644 codex-plugin/skills/using-the-cli/SKILL.md create mode 100644 cursor-plugin/skills/using-the-cli/SKILL.md diff --git a/codex-plugin/.codex-plugin/plugin.json b/codex-plugin/.codex-plugin/plugin.json index f73124d7..48cc0936 100644 --- a/codex-plugin/.codex-plugin/plugin.json +++ b/codex-plugin/.codex-plugin/plugin.json @@ -1,7 +1,7 @@ { "name": "tracedecay", "version": "0.0.0", - "description": "Codex integration for the TraceDecay semantic code intelligence MCP server.", + "description": "Codex integration for TraceDecay semantic code intelligence: MCP server plus a `tracedecay tool` CLI fallback exposing the same tools when MCP is unavailable.", "author": { "name": "ScriptedAlchemy", "url": "https://github.com/ScriptedAlchemy" diff --git a/codex-plugin/README.md b/codex-plugin/README.md index fe28087c..fa4596f1 100644 --- a/codex-plugin/README.md +++ b/codex-plugin/README.md @@ -28,6 +28,13 @@ Codex has no always-applied rule surface (unlike Cursor's `rules/`), so the tool-routing steering Cursor places in a rule is injected through the `SessionStart`/`UserPromptSubmit` hooks instead. +Every MCP tool is also available from the shell as `tracedecay tool ` +(`tracedecay tool` lists all tools; `tracedecay tool --help` shows a +tool's parameters). The bundled `using-the-cli` skill and the injected steering +point agents at that CLI fallback when the MCP transport errors or times out, +instead of querying `.tracedecay` databases directly or giving up on +tracedecay. + The `PostCompact` hook starts `codex app-server` as a short-lived child process and sets `TRACEDECAY_CODEX_SUMMARY_CHILD=1` to prevent recursive summary hooks. Set `TRACEDECAY_CODEX_BIN` to use a different Codex binary, diff --git a/codex-plugin/skills/using-the-cli/SKILL.md b/codex-plugin/skills/using-the-cli/SKILL.md new file mode 100644 index 00000000..4c038f63 --- /dev/null +++ b/codex-plugin/skills/using-the-cli/SKILL.md @@ -0,0 +1,42 @@ +--- +name: using-the-cli +description: 'Use when a tracedecay MCP call fails, times out, or the server is disconnected or unconfigured — every MCP tool is also a shell command, `tracedecay tool` plus the tool name. Switch to the CLI instead of querying .tracedecay databases directly or abandoning tracedecay.' +--- + +# Using the tracedecay CLI + +The `tracedecay` binary exposes every MCP tool as a shell command. MCP and CLI hit the same project store and return the same payloads, so an MCP transport failure (timeout, disconnect, missing server config) loses nothing: run the same tool with the same arguments via `tracedecay tool ` and keep following whatever `tracedecay:*` skill you were in. + +## Discovery + +1. **List every tool → `tracedecay tool`** (no name): all tools grouped by category with one-line summaries. +2. **One tool's parameters → `tracedecay tool --help`**: the tool's full description plus each parameter with its type and required/optional flag. +3. **Everything else → `tracedecay --help`**: the non-tool subcommands (`init`, `sync`, `status`, `doctor`, `daemon`, `sessions`, `dashboard`, …). + +## Invocation + +- Arguments are alternating `--key value` flags: `tracedecay tool search --query "parse config" --limit 10`. +- Tool names work with or without the `tracedecay_` prefix (`tool search` ≡ `tool tracedecay_search`). +- `--json` prints raw JSON; `--args '{"key":"value"}'` passes a whole JSON argument object; any value starting with `@` is read from that file (handy for multi-line replacement bodies, e.g. `--new-body @/tmp/body.txt`). +- `--project ` picks the project root explicitly; otherwise the nearest initialised project walking up from cwd is used. +- Truncated responses emit the same `handle` envelope as MCP — dereference with `tracedecay tool retrieve --handle rh_…`. + +## When to switch + +- An MCP call returns a client or transport error, times out, or the server drops mid-session. +- The tracedecay MCP server is not configured in this host but `tracedecay` is on `PATH`. +- A subagent or hook context has shell access but no MCP access. + +After falling back, diagnose the MCP side with `tracedecay doctor` and `tracedecay tool runtime`, and tell the user the session is running on the CLI fallback (and why) instead of silently downgrading. + +## Guardrails + +- Never query `.tracedecay/*.db` with sqlite3 or scripts — schemas are internal and change without notice. The CLI is the supported fallback, not raw DB access. +- Do not abandon tracedecay for broad Grep/file reads just because MCP transport failed; the CLI answers the same graph, memory, and session questions. +- CLI editing tools (`str_replace`, `replace_symbol`, …) mutate the working tree exactly like their MCP twins — apply the same care as `tracedecay:atomic-code-edits`. +- If the CLI also fails (binary missing or project not initialised), fall back to plain tools and suggest `tracedecay init` / `tracedecay doctor` to the user. + +## Output + +- The same result the MCP tool would have returned, plus a note that the CLI fallback was used and why. +- If any result includes a `tracedecay_metrics:` line, report the savings to the user. diff --git a/cursor-plugin/.cursor-plugin/plugin.json b/cursor-plugin/.cursor-plugin/plugin.json index 22907cbb..0219f7ca 100644 --- a/cursor-plugin/.cursor-plugin/plugin.json +++ b/cursor-plugin/.cursor-plugin/plugin.json @@ -1,7 +1,7 @@ { "name": "tracedecay", "version": "0.0.0", - "description": "Cursor integration for the TraceDecay semantic code intelligence MCP server.", + "description": "Cursor integration for TraceDecay semantic code intelligence: MCP server plus a `tracedecay tool` CLI fallback exposing the same tools when MCP is unavailable.", "author": { "name": "ScriptedAlchemy", "url": "https://github.com/ScriptedAlchemy" diff --git a/cursor-plugin/README.md b/cursor-plugin/README.md index 1dfb73c4..0f334e55 100644 --- a/cursor-plugin/README.md +++ b/cursor-plugin/README.md @@ -26,6 +26,13 @@ build does not expand it, reinstall with the latest Cursor and run Hook commands derive the active project from Cursor's event payload / `CURSOR_PROJECT_DIR`, not from the plugin directory. +Every MCP tool is also available from the shell as `tracedecay tool ` +(`tracedecay tool` lists all tools; `tracedecay tool --help` shows a +tool's parameters). The bundled `using-the-cli` skill and the always-applied +rule steer agents to that CLI fallback when the MCP transport errors or times +out, instead of querying `.tracedecay` databases directly or giving up on +tracedecay. + For sessions resumed from compacted context, the `sessionStart` hook adds a short recovery hint through Cursor's `additional_context` channel so the agent knows to query TraceDecay LCM/session recall before assuming the compacted diff --git a/cursor-plugin/rules/tracedecay.mdc b/cursor-plugin/rules/tracedecay.mdc index dcd15ea9..96701d3c 100644 --- a/cursor-plugin/rules/tracedecay.mdc +++ b/cursor-plugin/rules/tracedecay.mdc @@ -12,5 +12,6 @@ alwaysApply: true - **Edit:** for anchored or structural source edits, follow the `tracedecay:atomic-code-edits` skill (`tracedecay_str_replace`, `tracedecay_replace_symbol`, `tracedecay_ast_grep_rewrite`, …) — the graph re-indexes itself after each write. - **Recall:** for prior decisions or past conversations, use `tracedecay_message_search` / `tracedecay_fact_store` search (skills: `tracedecay:recalling-project-memory`, `tracedecay:recalling-session-context`); for updating, deleting, or curating stored facts, use `tracedecay:curating-project-memory`. - **Truncated MCP responses:** if a tracedecay response has `truncated: true` plus `handle`, narrow the query first when that answers the task; if the omitted details are needed, call `tracedecay_retrieve` with the `handle` instead of guessing or re-running a broad query. +- **MCP errors/timeouts:** if a tracedecay MCP call errors, times out, or the server is disconnected, every tool is also available as a shell command — `tracedecay tool --key value` (`tracedecay tool` lists all tools, `tracedecay tool --help` shows parameters; see `tracedecay:using-the-cli`). Do not query `.tracedecay` databases directly and do not abandon tracedecay because the MCP transport failed. - Every common workflow has a `tracedecay:*` skill (searching-for-code, tracing-functions, finding-impacted-areas, running-impacted-tests, reviewing-a-diff, code-health-report, …) — reach for the matching skill before improvising. - Fall back to plain file reads, search, or shell only when tracedecay cannot answer or has already pinpointed the exact files. diff --git a/cursor-plugin/skills/using-the-cli/SKILL.md b/cursor-plugin/skills/using-the-cli/SKILL.md new file mode 100644 index 00000000..4c038f63 --- /dev/null +++ b/cursor-plugin/skills/using-the-cli/SKILL.md @@ -0,0 +1,42 @@ +--- +name: using-the-cli +description: 'Use when a tracedecay MCP call fails, times out, or the server is disconnected or unconfigured — every MCP tool is also a shell command, `tracedecay tool` plus the tool name. Switch to the CLI instead of querying .tracedecay databases directly or abandoning tracedecay.' +--- + +# Using the tracedecay CLI + +The `tracedecay` binary exposes every MCP tool as a shell command. MCP and CLI hit the same project store and return the same payloads, so an MCP transport failure (timeout, disconnect, missing server config) loses nothing: run the same tool with the same arguments via `tracedecay tool ` and keep following whatever `tracedecay:*` skill you were in. + +## Discovery + +1. **List every tool → `tracedecay tool`** (no name): all tools grouped by category with one-line summaries. +2. **One tool's parameters → `tracedecay tool --help`**: the tool's full description plus each parameter with its type and required/optional flag. +3. **Everything else → `tracedecay --help`**: the non-tool subcommands (`init`, `sync`, `status`, `doctor`, `daemon`, `sessions`, `dashboard`, …). + +## Invocation + +- Arguments are alternating `--key value` flags: `tracedecay tool search --query "parse config" --limit 10`. +- Tool names work with or without the `tracedecay_` prefix (`tool search` ≡ `tool tracedecay_search`). +- `--json` prints raw JSON; `--args '{"key":"value"}'` passes a whole JSON argument object; any value starting with `@` is read from that file (handy for multi-line replacement bodies, e.g. `--new-body @/tmp/body.txt`). +- `--project ` picks the project root explicitly; otherwise the nearest initialised project walking up from cwd is used. +- Truncated responses emit the same `handle` envelope as MCP — dereference with `tracedecay tool retrieve --handle rh_…`. + +## When to switch + +- An MCP call returns a client or transport error, times out, or the server drops mid-session. +- The tracedecay MCP server is not configured in this host but `tracedecay` is on `PATH`. +- A subagent or hook context has shell access but no MCP access. + +After falling back, diagnose the MCP side with `tracedecay doctor` and `tracedecay tool runtime`, and tell the user the session is running on the CLI fallback (and why) instead of silently downgrading. + +## Guardrails + +- Never query `.tracedecay/*.db` with sqlite3 or scripts — schemas are internal and change without notice. The CLI is the supported fallback, not raw DB access. +- Do not abandon tracedecay for broad Grep/file reads just because MCP transport failed; the CLI answers the same graph, memory, and session questions. +- CLI editing tools (`str_replace`, `replace_symbol`, …) mutate the working tree exactly like their MCP twins — apply the same care as `tracedecay:atomic-code-edits`. +- If the CLI also fails (binary missing or project not initialised), fall back to plain tools and suggest `tracedecay init` / `tracedecay doctor` to the user. + +## Output + +- The same result the MCP tool would have returned, plus a note that the CLI fallback was used and why. +- If any result includes a `tracedecay_metrics:` line, report the savings to the user. diff --git a/src/agents/codex.rs b/src/agents/codex.rs index 04f48575..41842899 100644 --- a/src/agents/codex.rs +++ b/src/agents/codex.rs @@ -321,6 +321,10 @@ const CODEX_EMBEDDED_PLUGIN_FILES: &[(&str, &str)] = &[ "skills/tracking-session-health/SKILL.md", include_str!("../../codex-plugin/skills/tracking-session-health/SKILL.md"), ), + ( + "skills/using-the-cli/SKILL.md", + include_str!("../../codex-plugin/skills/using-the-cli/SKILL.md"), + ), ]; fn codex_plugin_install_dir(home: &Path) -> PathBuf { diff --git a/src/agents/cursor.rs b/src/agents/cursor.rs index f6de67f7..14f8c12b 100644 --- a/src/agents/cursor.rs +++ b/src/agents/cursor.rs @@ -348,6 +348,10 @@ const EMBEDDED_PLUGIN_FILES: &[(&str, &str)] = &[ "skills/tracking-session-health/SKILL.md", include_str!("../../cursor-plugin/skills/tracking-session-health/SKILL.md"), ), + ( + "skills/using-the-cli/SKILL.md", + include_str!("../../cursor-plugin/skills/using-the-cli/SKILL.md"), + ), ( "agents/code-explorer.md", include_str!("../../cursor-plugin/agents/code-explorer.md"), diff --git a/src/hooks.rs b/src/hooks.rs index 0ea3dc01..ae12dcb1 100644 --- a/src/hooks.rs +++ b/src/hooks.rs @@ -1408,6 +1408,7 @@ pub const CURSOR_PLUGIN_SKILLS: &[&str] = &[ "searching-for-code", "tracing-functions", "tracking-session-health", + "using-the-cli", ]; /// Builds the Cursor `sessionStart` `additional_context` text. @@ -1490,7 +1491,12 @@ pub fn build_codex_session_context_for_workspace( (tracedecay_context, tracedecay_search, tracedecay_callers, tracedecay_callees, \ tracedecay_impact, tracedecay_files, tracedecay_affected) over broad file reads \ or shell search for codebase exploration, symbol lookup, call graphs, and \ - impact analysis. Fall back to file reads only when tracedecay cannot answer.\n", + impact analysis. Fall back to file reads only when tracedecay cannot answer.\n\ + If an MCP call errors, times out, or the server is disconnected, every tool \ + is also a shell command: `tracedecay tool --key value` (`tracedecay \ + tool` lists tools, `tracedecay tool --help` shows parameters). Use \ + that CLI instead of querying .tracedecay databases directly or abandoning \ + tracedecay.\n", ); append_codex_recall_and_registry_guidance(&mut s); match status { diff --git a/src/mcp/server.rs b/src/mcp/server.rs index 9c57920e..79111098 100644 --- a/src/mcp/server.rs +++ b/src/mcp/server.rs @@ -376,10 +376,20 @@ fn tool_error_response(id: Value, tool_name: &str, error: &TraceDecayError) -> J } } - JsonRpcResponse::error( + let cli_name = tool_name.strip_prefix("tracedecay_").unwrap_or(tool_name); + JsonRpcResponse::error_with_data( id, ErrorCode::InternalError, format!("tool execution failed: {error}"), + Some(json!({ + "tool": tool_name, + "cli_fallback": format!( + "This tool is also available from the shell: `tracedecay tool {cli_name} ...` \ + (`tracedecay tool {cli_name} --help` for parameters). If MCP calls keep \ + failing or timing out, fall back to that CLI instead of querying \ + .tracedecay databases directly." + ), + })), ) } @@ -1494,6 +1504,13 @@ impl McpServer { tools are read-only and safe to call in parallel. Edit \ and session-memory tools can mutate local project state \ and declare readOnlyHint=false. \ + Every tool is also available from the shell: \ + `tracedecay tool --key value` (run `tracedecay tool` \ + to list tools, `tracedecay tool --help` for \ + parameters). If an MCP call errors, times out, or this \ + server disconnects, fall back to that CLI instead of \ + querying .tracedecay databases directly or abandoning \ + tracedecay. \ When a tool result contains a `tracedecay_metrics:` line, \ report the savings to the user (e.g. 'TraceDecay\\'d ~N tokens')." }), From 03216eb7eecc8a4989e2e243c8edcee69b5a6acd Mon Sep 17 00:00:00 2001 From: ScriptedAlchemy Date: Thu, 2 Jul 2026 01:17:47 +0000 Subject: [PATCH 2/2] test: give the late-publish LSP fixture headroom on slow runners --- tests/lsp_code_diagnostics_test.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/lsp_code_diagnostics_test.rs b/tests/lsp_code_diagnostics_test.rs index 3733ccfb..df4cca4d 100644 --- a/tests/lsp_code_diagnostics_test.rs +++ b/tests/lsp_code_diagnostics_test.rs @@ -137,7 +137,9 @@ async fn stdio_client_keeps_listening_after_initial_empty_publish() { &[script_path.display().to_string()], temp.path(), vec![fake_document(FAKE_LANGUAGE, FAKE_PATH, "let nope")], - std::time::Duration::from_millis(250), + // 250ms (from the Windows wall-time trim) misses the fake server's + // late publish on contended macOS runners; 500ms was stable before. + std::time::Duration::from_millis(500), ) .await .unwrap();