diff --git a/Cargo.lock b/Cargo.lock index 5264ad28e6..93104e286f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -784,6 +784,15 @@ dependencies = [ "itertools 0.10.5", ] +[[package]] +name = "crossbeam-channel" +version = "0.5.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82b8f8f868b36967f9606790d1903570de9ceaf870a7bf9fbbd3016d636a2cb2" +dependencies = [ + "crossbeam-utils", +] + [[package]] name = "crossbeam-deque" version = "0.8.6" @@ -817,7 +826,7 @@ checksum = "829d955a0bb380ef178a640b91779e3987da38c9aea133b20614cfed8cdea9c6" dependencies = [ "bitflags 2.11.1", "crossterm_winapi", - "mio", + "mio 1.2.0", "parking_lot", "rustix 0.38.44", "signal-hook", @@ -1189,6 +1198,15 @@ dependencies = [ "winapi", ] +[[package]] +name = "fsevent-sys" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76ee7a02da4d231650c7cea31349b889be2f45ddb3ef3032d2ec8185f6313fd2" +dependencies = [ + "libc", +] + [[package]] name = "futures" version = "0.3.32" @@ -2706,6 +2724,26 @@ dependencies = [ "serde_core", ] +[[package]] +name = "inotify" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8069d3ec154eb856955c1c0fbffefbf5f3c40a104ec912d4797314c1801abff" +dependencies = [ + "bitflags 1.3.2", + "inotify-sys", + "libc", +] + +[[package]] +name = "inotify-sys" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ea94e891b3606826e9c998be69ddca42247dad8ad50b1649a5cb7e1c9ae06fd" +dependencies = [ + "libc", +] + [[package]] name = "inout" version = "0.1.4" @@ -2864,6 +2902,26 @@ dependencies = [ "uuid-simd", ] +[[package]] +name = "kqueue" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "273c0752728918e0ac4976f2b275b6fefb9ecd400585dec929419f3844cd87b5" +dependencies = [ + "kqueue-sys", + "libc", +] + +[[package]] +name = "kqueue-sys" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07293a4e297ac234359b510362495713f75ea345d5307140414f20c69ffeb087" +dependencies = [ + "bitflags 2.11.1", + "libc", +] + [[package]] name = "kstring" version = "2.0.2" @@ -3167,6 +3225,18 @@ dependencies = [ "simd-adler32", ] +[[package]] +name = "mio" +version = "0.8.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c" +dependencies = [ + "libc", + "log", + "wasi", + "windows-sys 0.48.0", +] + [[package]] name = "mio" version = "1.2.0" @@ -3205,6 +3275,25 @@ version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9737e026353e5cd0736f98eddae28665118eb6f6600902a7f50db585621fecb6" +[[package]] +name = "notify" +version = "6.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6205bd8bb1e454ad2e27422015fb5e4f2bcc7e08fa8f27058670d208324a4d2d" +dependencies = [ + "bitflags 2.11.1", + "crossbeam-channel", + "filetime", + "fsevent-sys", + "inotify", + "kqueue", + "libc", + "log", + "mio 0.8.11", + "walkdir", + "windows-sys 0.48.0", +] + [[package]] name = "ntapi" version = "0.4.3" @@ -3765,6 +3854,18 @@ dependencies = [ "serde_json", ] +[[package]] +name = "reflink-copy" +version = "0.1.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9dd7ab4af0363d5ccfd2838d782a28196cf32a5cc2e4fe3c5dc83f2be588b8b" +dependencies = [ + "cfg-if", + "libc", + "rustix 1.1.4", + "windows 0.62.2", +] + [[package]] name = "regex" version = "1.12.3" @@ -4141,7 +4242,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b75a19a7a740b25bc7944bdee6172368f988763b744e3d4dfe753f6b4ece40cc" dependencies = [ "libc", - "mio", + "mio 1.2.0", "signal-hook", ] @@ -4273,7 +4374,7 @@ dependencies = [ "libc", "memchr", "ntapi", - "windows", + "windows 0.57.0", ] [[package]] @@ -4504,7 +4605,7 @@ checksum = "b67dee974fe86fd92cc45b7a95fdd2f99a36a6d7b0d431a231178d3d670bbcc6" dependencies = [ "bytes", "libc", - "mio", + "mio 1.2.0", "parking_lot", "pin-project-lite", "signal-hook-registry", @@ -4746,8 +4847,10 @@ dependencies = [ "libsql", "logo-art", "memmap2", + "notify", "open", "rayon", + "reflink-copy", "regex", "rustls 0.23.38", "self-replace", @@ -5600,7 +5703,28 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "12342cb4d8e3b046f3d80effd474a7a02447231330ef77d71daa6fbc40681143" dependencies = [ "windows-core 0.57.0", - "windows-targets", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows" +version = "0.62.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "527fadee13e0c05939a6a05d5bd6eec6cd2e3dbd648b9f8e447c6518133d8580" +dependencies = [ + "windows-collections", + "windows-core 0.62.2", + "windows-future", + "windows-numerics", +] + +[[package]] +name = "windows-collections" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23b2d95af1a8a14a3c7367e1ed4fc9c20e0a26e79551b1454d72583c97cc6610" +dependencies = [ + "windows-core 0.62.2", ] [[package]] @@ -5612,7 +5736,7 @@ dependencies = [ "windows-implement 0.57.0", "windows-interface 0.57.0", "windows-result 0.1.2", - "windows-targets", + "windows-targets 0.52.6", ] [[package]] @@ -5628,6 +5752,17 @@ dependencies = [ "windows-strings", ] +[[package]] +name = "windows-future" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1d6f90251fe18a279739e78025bd6ddc52a7e22f921070ccdc67dde84c605cb" +dependencies = [ + "windows-core 0.62.2", + "windows-link", + "windows-threading", +] + [[package]] name = "windows-implement" version = "0.57.0" @@ -5678,13 +5813,23 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" +[[package]] +name = "windows-numerics" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e2e40844ac143cdb44aead537bbf727de9b044e107a0f1220392177d15b0f26" +dependencies = [ + "windows-core 0.62.2", + "windows-link", +] + [[package]] name = "windows-result" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5e383302e8ec8515204254685643de10811af0ed97ea37210dc26fb0032647f8" dependencies = [ - "windows-targets", + "windows-targets 0.52.6", ] [[package]] @@ -5705,13 +5850,22 @@ dependencies = [ "windows-link", ] +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets 0.48.5", +] + [[package]] name = "windows-sys" version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows-targets", + "windows-targets 0.52.6", ] [[package]] @@ -5723,34 +5877,76 @@ dependencies = [ "windows-link", ] +[[package]] +name = "windows-targets" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", +] + [[package]] name = "windows-targets" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", "windows_i686_gnullvm", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", ] +[[package]] +name = "windows-threading" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3949bd5b99cafdf1c7ca86b43ca564028dfe27d66958f2470940f73d86d75b37" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" + [[package]] name = "windows_aarch64_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + [[package]] name = "windows_aarch64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" +[[package]] +name = "windows_i686_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" + [[package]] name = "windows_i686_gnu" version = "0.52.6" @@ -5763,24 +5959,48 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" +[[package]] +name = "windows_i686_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + [[package]] name = "windows_i686_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + [[package]] name = "windows_x86_64_gnu" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + [[package]] name = "windows_x86_64_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + [[package]] name = "windows_x86_64_msvc" version = "0.52.6" diff --git a/Cargo.toml b/Cargo.toml index 6a990c6c3e..00ce80a01c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -135,12 +135,17 @@ self-replace = "1" memmap2 = "0.9" crossterm = "0.28" fs2 = "0.4" +reflink-copy = "0.1" sysinfo = { version = "0.32", default-features = false, features = ["system"] } tree-sitter-hlsl = { version = "0.2.0", optional = true } amari-holographic = "0.23.0" regex = "1.12.3" open = "5.3" tiktoken-rs = { version = "0.12", optional = true } +# Raw filesystem watcher for the daemon git-metadata watcher (src/daemon/git_watch.rs). +# We deliberately use the raw watcher (not notify-debouncer-full) and debounce +# ourselves so a monorepo's git-ref churn coalesces into a single sync. +notify = "6" [target.'cfg(not(windows))'.dependencies] flate2 = "1" diff --git a/README.md b/README.md index 8a292bd85a..d3bf5a7127 100644 --- a/README.md +++ b/README.md @@ -186,7 +186,7 @@ Hermes wrappers run from Hermes' current working directory, use a 600-second tim For Codex install details, see [Codex plugin installs](#codex-plugin-installs). For Cursor, both global and `--local` install put the plugin in `~/.cursor/plugins/local/tracedecay` and require a Cursor reload. The plugin MCP config runs `tracedecay serve --path ${workspaceFolder}`, so it resolves the active workspace instead of the plugin directory and uses that workspace's active project store rather than the legacy global Cursor MCP registration. Cursor install no longer writes `.cursor/mcp.json`, `.cursor/hooks.json`, `.cursor/rules/tracedecay.mdc` (legacy artifact name), or `.cursor/permissions.json`; approvals are left to Cursor approval/run-mode behavior. The plugin hooks are: - `sessionStart` — fire-and-forget; injects context steering the Agent toward tracedecay MCP tools and reports index freshness (suggests `tracedecay init` when no initialized project store is found). -- `postToolUse` (unmatched — Cursor's docs enumerate no matcher value for semantic search) — fail-open; injects a soft `additional_context` hint after broad search/read tools (Grep, Glob, Read, semantic search, shell `rg`) so Cursor switches to `tracedecay_context`, `tracedecay_search`, `tracedecay_outline`, or `tracedecay_files`. Each hint category is emitted at most once per session (persisted in `.tracedecay/tool_hints_seen.json`). +- `postToolUse` (unmatched — Cursor's docs enumerate no matcher value for semantic search) — fail-open; injects a soft `additional_context` hint after broad search/read tools (Grep, Glob, Read, semantic search, shell `rg`) so Cursor switches to `tracedecay_grep`, `tracedecay_context`, `tracedecay_search`, `tracedecay_outline`, or `tracedecay_files`. Each hint category is emitted at most once per session (persisted in `.tracedecay/tool_hints_seen.json`). - `beforeSubmitPrompt` — resets the local token counter for the new turn and ingests the current Cursor transcript into the active project session store when `transcript_path` is present. - `afterFileEdit` (unmatched, so every Agent edit tool counts) — runs a **targeted single-file** sync of just the edited path(s) via `sync_if_stale_silent`, never a full-tree scan (which would scale with repo size, not edit size). - `afterShellExecution` — on Agent-run `git checkout`/`switch`/`worktree add`, bootstraps/maintains tracedecay branch tracking (`branch add`); on other state-changing git commands (pull/merge/rebase/reset/cherry-pick/stash apply|pop), runs a coalesced incremental sync. @@ -446,7 +446,7 @@ The default query set targets patterns present in most application codebases (CL Each repo is shallow-cloned (`git init` + `git fetch --progress --depth 1 origin ` + `checkout FETCH_HEAD`) on first use and cached locally; subsequent runs reuse the checkout. Git output is streamed to the terminal so the multi-GB fetch shows real-time progress. -**Tools covered (5 queries each).** Read tools — `search`, `context`, `callers`, `callees`, `node`, `by_qualified_name`, `signature`, `impact`, `body`, `files`, `complexity`, `doc_coverage`, `largest`, `hotspots`, `god_class`, `module_api`, `derives`, `dead_code`, `rank`, `coupling`, `circular`, and `outline` when `ast-grep` >= 0.44 is on `PATH`. Write tools — `str_replace`, `multi_str_replace`, `insert_at`, and (when the user-installed `ast-grep` CLI is available) `ast_grep_rewrite`. +**Tools covered (5 queries each).** Read tools — `grep`, `search`, `context`, `callers`, `callees`, `node`, `by_qualified_name`, `signature`, `impact`, `body`, `files`, `complexity`, `doc_coverage`, `largest`, `hotspots`, `god_class`, `module_api`, `derives`, `dead_code`, `rank`, `coupling`, `circular`, and `outline` when `ast-grep` >= 0.44 is on `PATH`. Write tools — `str_replace`, `multi_str_replace`, `insert_at`, and (when the user-installed `ast-grep` CLI is available) `ast_grep_rewrite`. **Force-sync on every run.** Before any benchmark fires, the harness runs the equivalent of `tracedecay sync --force` on each repo (`index_all()` regardless of `.tracedecay/` freshness) so timings always reflect the pinned source. diff --git a/TEST-QUERIES.md b/TEST-QUERIES.md index a1905d12e4..c971410b8d 100644 --- a/TEST-QUERIES.md +++ b/TEST-QUERIES.md @@ -41,6 +41,18 @@ Expected: Returns matching symbols with IDs, file paths, line numbers, and signa --- +## tracedecay_grep + +> Search indexed code for the literal string "mcpServers". + +Test: +``` +tracedecay_grep(pattern="mcpServers", fixed_strings=true, path_glob="src/**/*.rs", context_lines=1) +``` +Expected: Returns matching source lines with file paths, line numbers, and enclosing symbol metadata. Use this for literal strings, regexes, and config keys inside indexed code; use `tracedecay_search` for symbol names. + +--- + ## tracedecay_context > Build context for the task: "understand how the MCP server handles incoming tool calls" diff --git a/docs/PLUGIN-VALIDATION.md b/docs/PLUGIN-VALIDATION.md index 0bff2d8cb2..8adbe068f2 100644 --- a/docs/PLUGIN-VALIDATION.md +++ b/docs/PLUGIN-VALIDATION.md @@ -48,6 +48,7 @@ JSON artifacts in the bundles are validated against vendored JSON Schemas in | `plugin/.codex-plugin/plugin.json` | `cursor-schemas/plugin.schema.json` + `interface` extension | `tests/agent_suite/plugin_manifest_schema_test.rs` | | `plugin/.claude-plugin/plugin.json` | `claude-schemas/plugin.schema.json` | `tests/agent_suite/claude_plugin_schema_test.rs` | | `plugin/.claude-plugin/marketplace.json` | `claude-schemas/marketplace.schema.json` | `tests/agent_suite/claude_plugin_schema_test.rs` | +| `plugin/.mcp.json` | `cursor-schemas/mcp.schema.json` | `tests/agent_suite/plugin_config_schema_test.rs` | | `plugin/mcp-cursor.json` (deploys as `mcp.json`) | `cursor-schemas/mcp.schema.json` | `tests/agent_suite/plugin_config_schema_test.rs` | | `plugin/hooks/hooks-cursor.json` and `plugin/hooks/hooks-codex.json` | `cursor-schemas/hooks.schema.json` | `tests/agent_suite/plugin_config_schema_test.rs` | | `plugin/hooks/hooks-claude.json` | `claude-schemas/hooks.schema.json` | `tests/agent_suite/claude_plugin_schema_test.rs` | @@ -67,10 +68,12 @@ that host manifests share the same plugin `name`. The config-schema tests include negative cases proving the mcp/hooks schemas actually reject malformed configs (missing `command`, unknown fields, typo'd event names). -The Cursor plugin schema declares `additionalProperties: false`, and Codex +The Cursor plugin/hooks schemas declare `additionalProperties: false`. Codex marketplaces read an `interface` display-metadata block that Cursor's schema -doesn't define. The Codex manifest is therefore validated against the Cursor -schema plus exactly that one extra key, derived in the test. +doesn't define, and the repo-local Codex hook seed carries a top-level +`description` explaining why its `hooks` object is empty. Those two Codex +surfaces are validated against the Cursor schemas plus exactly those +host-specific keys, derived in the tests. ### 2. Skill contract tests (cargo test) diff --git a/docs/USER-GUIDE.md b/docs/USER-GUIDE.md index 9437d8bb07..9060c81f54 100644 --- a/docs/USER-GUIDE.md +++ b/docs/USER-GUIDE.md @@ -190,7 +190,7 @@ This is the default. It registers the MCP server in `~/.claude/settings.json`, g - `UserPromptSubmit` resets the per-turn token-savings counter. - `Stop` ingests new session transcript data and prints a cost receipt. - `SessionStart` reports index freshness (or a `tracedecay init` nudge) and, when the session restarts from compaction, injects the LCM context-recovery hint. -- `PostToolUse` (matcher `Edit|MultiEdit|Write|NotebookEdit|Bash`) notifies the daemon so edits and shell commands trigger targeted incremental sync. +- `PostToolUse` (matcher `Edit|MultiEdit|Write|NotebookEdit|Grep|Glob|Read|Bash`) notifies the daemon so edits and shell commands trigger targeted incremental sync, and broad search/read tools get routed toward TraceDecay equivalents. The install also ships three read-only custom subagents into `~/.claude/agents/` — `code-explorer`, `code-health-auditor`, and `session-historian` — the same tracedecay subagents the Cursor plugin bundles. They are only replaced or removed when the file is tracedecay-managed; a same-named agent you authored yourself is left untouched. `tracedecay update-plugin` refreshes installed copies. @@ -313,7 +313,7 @@ Cursor install is plugin-based: - Cursor install no longer writes `.cursor/mcp.json`, `.cursor/hooks.json`, `.cursor/rules/tracedecay.mdc`, or `.cursor/permissions.json`; approvals are left to Cursor approval/run-mode behavior. - The plugin bundles Cursor-specific, fail-open hooks. File and shell hooks notify the TraceDecay daemon; if no daemon is available they return success without indexing: - `sessionStart` injects context steering the Agent toward tracedecay MCP tools and reports index freshness (suggests `tracedecay init` when uninitialized). - - `postToolUse` (unmatched) injects a nonblocking `additional_context` hint after broad search/read tools (Grep, Glob, Read, semantic search, shell `rg`) so Cursor can switch to `tracedecay_context`, `tracedecay_search`, `tracedecay_outline`, or `tracedecay_files`; each hint category fires at most once per session. + - `postToolUse` (unmatched) injects a nonblocking `additional_context` hint after broad search/read tools (Grep, Glob, Read, semantic search, shell `rg`) so Cursor can switch to `tracedecay_grep`, `tracedecay_context`, `tracedecay_search`, `tracedecay_outline`, or `tracedecay_files`; each hint category fires at most once per session. - `beforeSubmitPrompt` resets the local token counter and ingests the current Cursor transcript into the active project session store when `transcript_path` is present. - `afterFileEdit` (unmatched, so every Agent edit tool counts) sends the edited path(s) to the daemon, whose MCP server runs a **targeted single-file** sync — not a full-tree scan — so it stays cheap on large codebases even when the Agent edits many files per turn. - `afterShellExecution` sends shell command effects to the daemon, whose MCP server makes branch handling automatic: Agent-run `git checkout`/`switch`/`worktree add` bootstraps/maintains tracedecay branch tracking (`branch add`), while other state-changing git commands (pull/merge/rebase/reset/cherry-pick/stash apply|pop) trigger a coalesced incremental sync. @@ -565,6 +565,7 @@ When running as an MCP server, tracedecay exposes more than 70 tools that AI age | Tool | What it does | |------|-------------| | `tracedecay_context` | Given a task description, returns relevant symbols, relationships, and code snippets. This is the go-to starting point for any coding task. | +| `tracedecay_grep` | Search indexed code content by literal string or regex, with each hit annotated by its enclosing symbol. | | `tracedecay_search` | Find symbols by name. Supports filtering by kind (function, class, method, etc.). | | `tracedecay_node` | Get full details for a specific symbol: source code, location, complexity metrics, and relationships. | | `tracedecay_files` | List indexed files, optionally filtered by directory or glob pattern. | diff --git a/eval/hermetic/README.md b/eval/hermetic/README.md new file mode 100644 index 0000000000..18e6f158cb --- /dev/null +++ b/eval/hermetic/README.md @@ -0,0 +1,140 @@ +# Hermetic eval harness + +Triggering evals must exercise the tracedecay build **under development in this +worktree** — its binary *and* its plugin bundle — never the system-installed +`tracedecay` and never the user's real Claude Code config. Live concurrent +sessions depend on the real `~/.claude`, `~/.tracedecay`, and +`~/.cargo/bin/tracedecay`, so the harness touches none of them. + +```bash +# One-shot: build + isolate + install + index + one trivial scenario. +eval/hermetic/run.sh smoke --debug --keep + +# Full corpus against a reusable env: +ENV=$(eval/hermetic/run.sh setup --debug) +eval/hermetic/run.sh index --env-dir "$ENV" --project /path/to/repo +eval/hermetic/run.sh run --env-dir "$ENV" --corpus my-corpus.jsonl --model sonnet +eval/hermetic/run.sh teardown --env-dir "$ENV" +``` + +## Why a naive PATH override is not enough + +An eval session launched via `claude -p` resolves tracedecay **three** ways: + +1. **MCP server command** — the plugin registers an MCP server whose command is + the bare string `tracedecay`, resolved via `PATH` at session start. +2. **Hook commands** — baked as **absolute paths** at install time. The + installer substitutes `__TRACEDECAY_BIN__` with a concrete path + (`src/agents/claude.rs`), so a `PATH` override does *not* affect already + installed hooks. +3. **Plugin bundle** — skills / hooks / agents JSON that Claude Code loads from + its plugin marketplace under the config dir. + +A `PATH` override alone only fixes (1). The harness must control all three. + +## What the harness isolates (verified mechanisms) + +| Concern | Lever | Evidence | +| --- | --- | --- | +| Claude config, transcripts, plugin bundle | `CLAUDE_CONFIG_DIR` | Smoke test: a throwaway `CLAUDE_CONFIG_DIR` gets its own `projects/`, `sessions/`, `.claude.json`; the session transcript lands at `/projects//.jsonl`. | +| Where the installer writes the plugin | `HOME` (installer uses `home_dir()` → `$HOME`, then `$HOME/.claude`) | `src/agents/mod.rs::home_dir()` reads `$HOME`; `src/agents/claude.rs` writes `ctx.home/.claude/plugins/marketplaces/tracedecay`. | +| tracedecay graph/data home | `TRACEDECAY_DATA_DIR` | `src/config.rs::user_data_dir()` returns `$TRACEDECAY_DATA_DIR` when set, else `~/.tracedecay`. | +| tracedecay daemon socket | derives from the data dir; also pinned via `TRACEDECAY_DAEMON_SOCKET` | `src/daemon/service.rs::default_socket_path()` = `tracedecay_data_dir()/daemon.sock`, overridable by `TRACEDECAY_DAEMON_SOCKET`. Isolating the data dir already isolates the socket, so the harness never fights the real daemon. | +| Which binary the installer bakes | **staged copy of the dev binary at a non-cargo-target path** | `src/agents/mod.rs::which_tracedecay_from()` deliberately **refuses** a path under a cargo target dir (`target/{debug,release}`) and falls back to `PATH` — which would bake the *system* binary. See below. | +| Auth for `claude -p` | copy `~/.claude/.credentials.json` into the isolated config (or `ANTHROPIC_API_KEY`) | Smoke test: without it the isolated session prints `Not logged in`; with the copied credential it returns a real result and `session_id`. | + +`HOME` and `CLAUDE_CONFIG_DIR` are pointed at the **same** physical directory +(`/home/.claude`) so the installer's `$HOME/.claude` writes and Claude +Code's `CLAUDE_CONFIG_DIR` reads refer to one bundle. + +### The cargo-target-binary trap (the crux) + +`which_tracedecay()` treats any binary living under a cargo target dir as +ephemeral and **will not** bake it into hooks; it prefers a `PATH` match +instead. So running `target/release/tracedecay install` directly would silently +bake the **system** `tracedecay`, defeating the whole point. + +The harness sidesteps this by copying the freshly built binary to +`/bin/tracedecay` (a stable, non-cargo location) and running the installer +from **that** copy with `/bin` first on `PATH`. Then: + +* `current_exe` is the staged copy → baked into hook commands (fixes #2), and +* the MCP `tracedecay` command resolves to the staged copy via `PATH` (fixes #1), +* the plugin bundle is deployed from this worktree's `plugin/` dir (fixes #3). + +`setup` asserts the staged path actually appears in the baked hook JSON and +warns loudly if it does not. + +## Env dir layout + +``` +/eval-env--/ + bin/tracedecay staged dev binary (baked into hooks + first on PATH) + home/ fake $HOME + home/.claude/ == CLAUDE_CONFIG_DIR (plugin bundle, transcripts) + tracedecay-data/ == TRACEDECAY_DATA_DIR (graph db, daemon.sock, logs) + results/ results.jsonl, summary.md, per-scenario json/stderr + env.sh sourceable export block (for reuse and manual debugging) +``` + +`--keep` preserves the env for inspection; otherwise a freshly created env is +removed on exit. `teardown --env-dir` refuses to delete anything that is not an +`eval-env-*` dir under `$TMPDIR`. + +## Corpus schema + +One JSON object per line (the schema used by the session scratchpad corpora): + +```json +{"id":"ev-001","category":"context","project_dir":"/abs/path/to/repo", + "prompt":"...","expected_tools":["tracedecay_context"], + "anti_tools":["Grep","Glob"],"providers":["sonnet"],"success":"..."} +``` + +`run` executes each `prompt` via `claude -p --output-format json` inside +`project_dir` (falling back to the indexed default project if the dir is +missing), recovers the `session_id`, reads that session's transcript from the +isolated config, and counts `tool_use` blocks. `score.py` classifies each tool +as tracedecay (name contains `tracedecay`) or native. + +A scenario **passes** when at least one tracedecay tool was used and no +`anti_tools` appear. This is a deliberately simple end-state judge — the harness +exists to guarantee *isolation*, not to be a sophisticated grader; layer a +stricter judge on top of `results.jsonl` if needed. + +Outputs land in `/results/`: `results.jsonl` (one scored object per +scenario) and `summary.md` (pass count + per-scenario table). + +## What it cannot isolate / limitations + +- **Model non-determinism.** Tool-use counts vary run to run; treat pass/fail as + a signal over a corpus, not a single scenario. +- **The `tracedecay init` index cost.** Indexing the tracedecay repo is not + free; reuse an env dir with `--keep` across corpus runs. +- **Network / model backend.** Evals hit the real Anthropic API using the copied + credential. There is no offline mode. +- **Codex.** `codex exec` uses `CODEX_HOME` for its config, and + `tracedecay install --agent codex` exists, so a `--codex` mode is feasible. + It is intentionally **left as follow-up**: codex-exec auth handling and the + codex plugin-bundle deploy path need their own validation pass, out of scope + for the claude-focused smoke here. The scaffolding (`CODEX_HOME` env, staged + binary, isolated data dir) transfers directly when added. +- **Global cargo caches** (`~/.cargo/registry`) are shared — only the *output* + (`CARGO_TARGET_DIR=/target`) is worktree-scoped. The build cannot + corrupt the user's install because it never writes to `~/.cargo/bin`. + +## Post-merge re-eval protocol + +After the plugin-suite changes merge, re-run to compare against the baseline +facts recorded in tracedecay memory: + +1. **Rebuild** from the merged checkout: `run.sh setup` (drop `--debug` for a + representative release build). +2. **Re-index** the same target project into the fresh env. +3. **Rerun the same corpus** at the same `--model`. +4. **Compare** the new `summary.md` pass rate and per-scenario tracedecay-vs- + native counts against the baseline. Store the new baseline as a durable fact + (`tracedecay_fact_store`) so future regressions are visible. + +Because every run is hermetic, differences between two runs are attributable to +the code change (plus model noise), not to drift in the user's real environment. diff --git a/eval/hermetic/run.sh b/eval/hermetic/run.sh new file mode 100755 index 0000000000..f8952b8037 --- /dev/null +++ b/eval/hermetic/run.sh @@ -0,0 +1,452 @@ +#!/usr/bin/env bash +# +# Hermetic eval harness for tracedecay. +# +# Guarantees that triggering evals run against the tracedecay build under +# development in THIS worktree -- never the system-installed binary -- and that +# they never mutate the user's real ~/.claude, ~/.tracedecay, or ~/.cargo/bin. +# +# See ./README.md for the design, isolation guarantees, and limitations. +# +# Subcommands: +# build Build (or reuse) the dev binary and stage it at a stable path. +# setup Build + create an isolated env dir + install the dev plugin into it. +# index Index a target project with the dev binary (into the isolated home). +# run Execute a corpus JSONL against the isolated env and score it. +# smoke One trivial built-in scenario end-to-end (implies setup+index+run). +# teardown Remove an env dir. +# +# Every subcommand that needs an env accepts --env-dir to reuse a prior one; +# otherwise a fresh eval-env- is created under $TMPDIR. + +set -euo pipefail + +# -------------------------------------------------------------------------- +# Paths and constants +# -------------------------------------------------------------------------- + +SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" >/dev/null 2>&1 && pwd -P)" +# eval/hermetic -> eval -> worktree root +WORKTREE_ROOT="$(cd -- "${SCRIPT_DIR}/../.." >/dev/null 2>&1 && pwd -P)" + +TMP_ROOT="${TMPDIR:-/tmp}" +TMP_ROOT="${TMP_ROOT%/}" + +# Default project to index/eval against: the main tracedecay checkout. +DEFAULT_PROJECT="/home/zack/projects/tracedecay" + +log() { printf '[hermetic] %s\n' "$*" >&2; } +die() { printf '[hermetic] ERROR: %s\n' "$*" >&2; exit 1; } + +# -------------------------------------------------------------------------- +# Dev binary staging +# -------------------------------------------------------------------------- +# +# The installer's which_tracedecay() DELIBERATELY refuses to bake a path that +# lives under a cargo target dir (target/{debug,release}) -- it treats those as +# ephemeral and falls back to a PATH lookup, which would resolve the SYSTEM +# tracedecay. To make the dev build authoritative we copy the freshly built +# binary to a stable, non-cargo-target location inside the env dir and run the +# installer from THAT copy with that dir first on PATH. Then: +# * current_exe (the staged copy) is baked into hook commands, and +# * the MCP server command "tracedecay" resolves to the staged copy via PATH. + +# Build the dev binary from THIS worktree. Echoes the built artifact path. +build_binary() { + local profile_dir="release" + local -a cargo_args=(build --release --bin tracedecay) + if [[ "${BUILD_DEBUG:-0}" == "1" ]]; then + profile_dir="debug" + cargo_args=(build --bin tracedecay) + fi + + # Isolate build artifacts from the user's normal target dir so a concurrent + # `cargo` in this worktree is not disturbed and so is_cargo_target_binary can + # recognise the location deterministically. + local target_dir="${WORKTREE_ROOT}/target" + log "building dev binary (${profile_dir}) from ${WORKTREE_ROOT}" + # CRITICAL: a cargo failure must abort the whole run. Otherwise a stale + # artifact from a previous build would be silently staged and the eval would + # run against the WRONG binary -- exactly the failure this harness prevents. + # We check cargo's exit status explicitly (a bare subshell in a command-sub + # is not always fatal under set -e), and unless HERMETIC_ALLOW_STALE=1 we + # refuse to proceed on a build failure even if an old artifact exists. + local rc=0 + ( + cd "${WORKTREE_ROOT}" + CARGO_TARGET_DIR="${target_dir}" cargo "${cargo_args[@]}" >&2 + ) || rc=$? + if [[ "${rc}" -ne 0 ]]; then + if [[ "${HERMETIC_ALLOW_STALE:-0}" == "1" ]]; then + log "WARNING: cargo build failed (rc=${rc}); HERMETIC_ALLOW_STALE=1 set, reusing prior artifact" + else + die "cargo build failed (rc=${rc}); refusing to run against a possibly stale binary. Set HERMETIC_ALLOW_STALE=1 only if you understand the artifact may be old." + fi + fi + + local built="${target_dir}/${profile_dir}/tracedecay" + [[ -x "${built}" ]] || die "expected built binary not found at ${built}" + printf '%s\n' "${built}" +} + +# Copy the built binary to /bin/tracedecay (a stable non-cargo path). +stage_binary() { + local built="$1" env_dir="$2" + local bindir="${env_dir}/bin" + mkdir -p "${bindir}" + cp -f "${built}" "${bindir}/tracedecay" + chmod +x "${bindir}/tracedecay" + printf '%s\n' "${bindir}/tracedecay" +} + +# -------------------------------------------------------------------------- +# Env dir lifecycle +# -------------------------------------------------------------------------- +# +# Layout of an env dir: +# /bin/tracedecay staged dev binary (baked + on PATH) +# /home/ fake HOME; installer writes home/.claude/... +# /home/.claude/ == CLAUDE_CONFIG_DIR (transcripts, plugins) +# /tracedecay-data/ == TRACEDECAY_DATA_DIR (graph, daemon.sock) +# /results/ results JSONL + markdown summary +# /env.sh sourceable export block for reuse/debugging + +make_env_dir() { + local env_dir + env_dir="${TMP_ROOT}/eval-env-$(date +%Y%m%d-%H%M%S)-$$" + mkdir -p "${env_dir}"/{bin,home/.claude,tracedecay-data,results} + printf '%s\n' "${env_dir}" +} + +# Write env.sh into an env dir so it can be sourced by `run`/`smoke` and by a +# human debugging with --keep. +write_env_file() { + local env_dir="$1" staged_bin="$2" + local home_dir="${env_dir}/home" + cat >"${env_dir}/env.sh" <&2 \ + || die "dev installer failed" + + # Sanity: confirm the baked hook path is the staged dev binary, not system. + local mkt_dir="${home_dir}/.claude/plugins/marketplaces/tracedecay" + local -a hooks=() + if [[ -d "${mkt_dir}" ]]; then + while IFS= read -r f; do + [[ -n "${f}" ]] && hooks+=("${f}") + done < <(grep -Rl '"command"' "${mkt_dir}" 2>/dev/null || true) + fi + if [[ ${#hooks[@]} -gt 0 ]]; then + if grep -q "${staged_bin}" "${hooks[@]}" 2>/dev/null; then + log "verified: hooks baked with staged dev binary ${staged_bin}" + else + log "WARNING: could not confirm staged binary in baked hooks; inspect ${mkt_dir}" + fi + fi +} + +# -------------------------------------------------------------------------- +# Index a target project with the dev binary (into the isolated data dir) +# -------------------------------------------------------------------------- +index_project() { + local env_dir="$1" staged_bin="$2" project="$3" + [[ -d "${project}" ]] || die "project dir does not exist: ${project}" + log "indexing ${project} with dev binary (isolated data dir)" + HOME="${env_dir}/home" \ + TRACEDECAY_DATA_DIR="${env_dir}/tracedecay-data" \ + TRACEDECAY_DAEMON_SOCKET="${env_dir}/tracedecay-data/daemon.sock" \ + PATH="${env_dir}/bin:${PATH}" \ + "${staged_bin}" init "${project}" >&2 \ + || die "indexing failed for ${project}" +} + +# -------------------------------------------------------------------------- +# Run a corpus against the isolated env +# -------------------------------------------------------------------------- +# +# Corpus schema (one JSON object per line): +# id, category, project_dir, prompt, expected_tools[], anti_tools[], +# providers[], success +# +# For each scenario we run `claude -p ` with the isolated env vars, in +# --output-format json so we can recover the session id, then read that +# session's transcript from the ISOLATED CLAUDE_CONFIG_DIR and count how many +# tool_use entries were tracedecay MCP tools vs native tools. +run_corpus() { + local env_dir="$1" corpus="$2" model="$3" + [[ -f "${corpus}" ]] || die "corpus not found: ${corpus}" + + # shellcheck source=/dev/null + source "${env_dir}/env.sh" + + local results="${env_dir}/results/results.jsonl" + local summary="${env_dir}/results/summary.md" + : >"${results}" + + local total=0 passed=0 + local scorer="${SCRIPT_DIR}/score.py" + + while IFS= read -r line; do + [[ -z "${line}" ]] && continue + total=$((total + 1)) + + local id project prompt + id="$(printf '%s' "${line}" | python3 -c 'import sys,json;print(json.load(sys.stdin).get("id",""))')" + project="$(printf '%s' "${line}" | python3 -c 'import sys,json;print(json.load(sys.stdin).get("project_dir",""))')" + prompt="$(printf '%s' "${line}" | python3 -c 'import sys,json;print(json.load(sys.stdin).get("prompt",""))')" + + [[ -n "${prompt}" ]] || { log "scenario ${id}: empty prompt, skipping"; continue; } + local run_cwd="${project}" + [[ -d "${run_cwd}" ]] || run_cwd="${DEFAULT_PROJECT}" + + log "scenario ${id}: running (model=${model}, cwd=${run_cwd})" + + # Run claude -p with JSON output to recover the session id. All isolation + # env vars are already exported via env.sh above. + local out + if ! out="$(cd "${run_cwd}" && claude -p "${prompt}" \ + --model "${model}" \ + --output-format json \ + --dangerously-skip-permissions 2>"${env_dir}/results/${id}.stderr")"; then + log "scenario ${id}: claude -p exited non-zero (see ${id}.stderr)" + fi + printf '%s' "${out}" >"${env_dir}/results/${id}.claude.json" + + # Score: find the session transcript in the isolated config and count tools. + local scored + scored="$(python3 "${scorer}" \ + --scenario "${line}" \ + --claude-json "${env_dir}/results/${id}.claude.json" \ + --config-dir "${CLAUDE_CONFIG_DIR}" \ + --cwd "${run_cwd}")" + printf '%s\n' "${scored}" >>"${results}" + + if printf '%s' "${scored}" | python3 -c 'import sys,json;sys.exit(0 if json.load(sys.stdin).get("pass") else 1)'; then + passed=$((passed + 1)) + log "scenario ${id}: PASS" + else + log "scenario ${id}: FAIL" + fi + done <"${corpus}" + + # Markdown summary. + { + printf '# Hermetic eval results\n\n' + printf -- '- Env dir: `%s`\n' "${env_dir}" + printf -- '- Corpus: `%s`\n' "${corpus}" + printf -- '- Model: `%s`\n' "${model}" + printf -- '- Dev binary: `%s`\n' "${HERMETIC_TRACEDECAY_BIN}" + printf -- '- Passed: **%s / %s**\n\n' "${passed}" "${total}" + printf '| id | pass | tracedecay tools | native tools | session |\n' + printf '| --- | --- | --- | --- | --- |\n' + python3 - "${results}" <<'PY' +import json, sys +with open(sys.argv[1]) as fh: + for ln in fh: + ln = ln.strip() + if not ln: + continue + r = json.loads(ln) + print("| {id} | {ok} | {td} | {nat} | {sid} |".format( + id=r.get("id",""), + ok="yes" if r.get("pass") else "no", + td=r.get("tracedecay_tool_uses",0), + nat=r.get("native_tool_uses",0), + sid=(r.get("session_id") or "")[:8], + )) +PY + } >"${summary}" + + log "results: ${results}" + log "summary: ${summary}" + log "SCORE: ${passed}/${total} passed" + # Echo summary to stdout for the caller. + cat "${summary}" +} + +# -------------------------------------------------------------------------- +# High-level orchestration +# -------------------------------------------------------------------------- + +ORIG_HOME="${HOME}" + +do_setup() { + local env_dir="$1" + local built staged + built="$(build_binary)" + staged="$(stage_binary "${built}" "${env_dir}")" + write_env_file "${env_dir}" "${staged}" + seed_auth "${env_dir}" + install_plugin "${env_dir}" "${staged}" + printf '%s\n' "${staged}" +} + +usage() { + cat >&2 <<'EOF' +Usage: run.sh [options] + +Subcommands: + setup Build dev binary + create isolated env + install plugin. + index Index a project with the dev binary into the isolated env. + run Run a corpus JSONL against the isolated env and score it. + smoke Full pipeline with one built-in trivial scenario. + teardown Remove an env dir. + +Common options: + --env-dir Reuse an existing env dir (else a fresh one is created). + --project Project to index / default cwd (default: main tracedecay checkout). + --corpus Corpus file for `run`. + --model Model for claude -p (default: sonnet). + --debug Reuse/produce a debug build instead of release (faster). + --keep Do not tear down the env dir on exit. + +Examples: + run.sh smoke --debug --keep + run.sh setup --debug + run.sh run --env-dir /tmp/eval-env-... --corpus my-corpus.jsonl --model sonnet +EOF +} + +main() { + [[ $# -ge 1 ]] || { usage; exit 2; } + local sub="$1"; shift + + local env_dir="" project="${DEFAULT_PROJECT}" corpus="" model="sonnet" + local keep=0 + while [[ $# -gt 0 ]]; do + case "$1" in + --env-dir) env_dir="$2"; shift 2 ;; + --project) project="$2"; shift 2 ;; + --corpus) corpus="$2"; shift 2 ;; + --model) model="$2"; shift 2 ;; + --debug) export BUILD_DEBUG=1; shift ;; + --keep) keep=1; shift ;; + -h|--help) usage; exit 0 ;; + *) die "unknown option: $1" ;; + esac + done + + case "${sub}" in + teardown) + [[ -n "${env_dir}" ]] || die "teardown requires --env-dir" + [[ "${env_dir}" == "${TMP_ROOT}"/eval-env-* ]] || die "refusing to remove non-eval env dir: ${env_dir}" + rm -rf "${env_dir}" + log "removed ${env_dir}" + ;; + + setup) + [[ -n "${env_dir}" ]] || env_dir="$(make_env_dir)" + do_setup "${env_dir}" >/dev/null + log "env ready: ${env_dir}" + printf '%s\n' "${env_dir}" + ;; + + index) + [[ -n "${env_dir}" ]] || die "index requires --env-dir (run setup first)" + # shellcheck source=/dev/null + source "${env_dir}/env.sh" + index_project "${env_dir}" "${HERMETIC_TRACEDECAY_BIN}" "${project}" + ;; + + run) + [[ -n "${env_dir}" ]] || die "run requires --env-dir (run setup first)" + [[ -n "${corpus}" ]] || die "run requires --corpus" + run_corpus "${env_dir}" "${corpus}" "${model}" + ;; + + smoke) + local created=0 + if [[ -z "${env_dir}" ]]; then env_dir="$(make_env_dir)"; created=1; fi + do_setup "${env_dir}" >/dev/null + # shellcheck source=/dev/null + source "${env_dir}/env.sh" + index_project "${env_dir}" "${HERMETIC_TRACEDECAY_BIN}" "${project}" + + local smoke_corpus="${env_dir}/smoke-corpus.jsonl" + python3 - "${project}" >"${smoke_corpus}" <<'PY' +import json, sys +print(json.dumps({ + "id": "smoke-001", + "category": "exploring_code", + "project_dir": sys.argv[1], + "prompt": "where is decide_hint defined? brief", + "expected_tools": ["tracedecay_context", "tracedecay_search"], + "anti_tools": ["Grep", "Glob", "Read"], + "providers": ["sonnet"], + "success": "Locates decide_hint via a tracedecay tool, not a raw grep/read.", +})) +PY + run_corpus "${env_dir}" "${smoke_corpus}" "${model}" + + if [[ "${keep}" == "1" ]]; then + log "kept env dir: ${env_dir}" + elif [[ "${created}" == "1" ]]; then + rm -rf "${env_dir}" + log "removed env dir ${env_dir} (pass --keep to preserve)" + fi + ;; + + *) usage; exit 2 ;; + esac + + if [[ "${keep}" == "1" && -n "${env_dir}" && -d "${env_dir}" ]]; then + log "env preserved: ${env_dir}" + fi +} + +main "$@" diff --git a/eval/hermetic/score.py b/eval/hermetic/score.py new file mode 100755 index 0000000000..782073f402 --- /dev/null +++ b/eval/hermetic/score.py @@ -0,0 +1,168 @@ +#!/usr/bin/env python3 +"""Score one hermetic-eval scenario from an isolated claude session transcript. + +Reads the scenario JSON, the ``claude -p --output-format json`` result (to +recover the session id), then locates that session's transcript inside the +ISOLATED ``CLAUDE_CONFIG_DIR`` and counts ``tool_use`` entries, classifying each +as a tracedecay MCP tool or a native tool. + +Pass criteria (deliberately simple; the harness is about isolation, not a +sophisticated judge): + +* at least one tracedecay tool was used, AND +* no ``anti_tools`` were used. + +Emits a single JSON object on stdout. +""" + +from __future__ import annotations + +import argparse +import json +import sys +from pathlib import Path + + +def load_scenario(raw: str) -> dict: + return json.loads(raw) + + +def session_id_from_claude_json(path: Path) -> str | None: + """Recover the session id from the `claude -p --output-format json` result.""" + if not path.exists(): + return None + try: + data = json.loads(path.read_text()) + except (json.JSONDecodeError, OSError): + return None + if isinstance(data, dict): + for key in ("session_id", "sessionId", "session"): + val = data.get(key) + if isinstance(val, str) and val: + return val + return None + + +def project_slug(cwd: str) -> str: + """Claude Code stores transcripts under projects/ where slug is the + absolute cwd with path separators replaced by dashes.""" + return cwd.replace("/", "-") + + +def find_transcript(config_dir: Path, cwd: str, session_id: str | None) -> Path | None: + """Locate the JSONL transcript for this session inside the isolated config.""" + projects = config_dir / "projects" + candidates: list[Path] = [] + + if session_id: + # Fast path: /projects//.jsonl + slug_dir = projects / project_slug(cwd) + direct = slug_dir / f"{session_id}.jsonl" + if direct.exists(): + return direct + candidates.extend(projects.rglob(f"{session_id}.jsonl")) + if candidates: + return candidates[0] + + # Fallback: newest transcript under the matching project slug. + slug_dir = projects / project_slug(cwd) + if slug_dir.is_dir(): + jsonls = sorted( + slug_dir.glob("*.jsonl"), key=lambda p: p.stat().st_mtime, reverse=True + ) + if jsonls: + return jsonls[0] + + # Last resort: newest transcript anywhere in the isolated config. + all_jsonls = sorted( + projects.rglob("*.jsonl"), key=lambda p: p.stat().st_mtime, reverse=True + ) if projects.is_dir() else [] + return all_jsonls[0] if all_jsonls else None + + +def is_tracedecay_tool(name: str) -> bool: + n = name.lower() + return "tracedecay" in n + + +def count_tools(transcript: Path) -> tuple[list[str], list[str]]: + """Return (tracedecay_tool_names, native_tool_names) from tool_use entries.""" + td: list[str] = [] + native: list[str] = [] + try: + lines = transcript.read_text().splitlines() + except OSError: + return td, native + + for ln in lines: + ln = ln.strip() + if not ln: + continue + try: + evt = json.loads(ln) + except json.JSONDecodeError: + continue + # tool_use entries live in message.content blocks of assistant messages. + msg = evt.get("message") if isinstance(evt, dict) else None + content = msg.get("content") if isinstance(msg, dict) else None + if not isinstance(content, list): + continue + for block in content: + if not isinstance(block, dict): + continue + if block.get("type") != "tool_use": + continue + name = block.get("name", "") + if not isinstance(name, str): + continue + if is_tracedecay_tool(name): + td.append(name) + else: + native.append(name) + return td, native + + +def main() -> int: + ap = argparse.ArgumentParser() + ap.add_argument("--scenario", required=True, help="scenario JSON (one line)") + ap.add_argument("--claude-json", required=True, help="path to claude -p json result") + ap.add_argument("--config-dir", required=True, help="isolated CLAUDE_CONFIG_DIR") + ap.add_argument("--cwd", required=True, help="cwd the scenario ran in") + args = ap.parse_args() + + scenario = load_scenario(args.scenario) + anti = {t.lower() for t in scenario.get("anti_tools", [])} + + sid = session_id_from_claude_json(Path(args.claude_json)) + transcript = find_transcript(Path(args.config_dir), args.cwd, sid) + + td_tools: list[str] = [] + native_tools: list[str] = [] + if transcript is not None: + td_tools, native_tools = count_tools(transcript) + + used_anti = sorted( + {n for n in native_tools if n.lower() in anti} + | {n for n in native_tools if any(a in n.lower() for a in anti)} + ) + + passed = bool(td_tools) and not used_anti + + result = { + "id": scenario.get("id", ""), + "category": scenario.get("category", ""), + "session_id": sid, + "transcript": str(transcript) if transcript else None, + "tracedecay_tool_uses": len(td_tools), + "tracedecay_tools": td_tools, + "native_tool_uses": len(native_tools), + "native_tools": native_tools, + "anti_tools_used": used_anti, + "pass": passed, + } + print(json.dumps(result)) + return 0 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/plugin/README-claude.md b/plugin/README-claude.md index 51a62d297a..6a347d3cb8 100644 --- a/plugin/README-claude.md +++ b/plugin/README-claude.md @@ -16,7 +16,9 @@ Claude Code. - **Lifecycle hooks** (`hooks/hooks.json`): `SessionStart`, `UserPromptSubmit`, `Stop`, `PreToolUse`, and `PostToolUse` handlers that inject index status and tool-routing steering, keep the graph/session store - warm, and redirect explore-agent calls toward the tracedecay tools. + warm, redirect explore-agent calls toward the tracedecay tools, and nudge + Grep/Glob/Read-style searches toward `tracedecay_grep`, `tracedecay_search`, + `tracedecay_context`, and bounded graph reads. ## Install diff --git a/plugin/README-codex.md b/plugin/README-codex.md index f5cf1c7289..cf6acf0227 100644 --- a/plugin/README-codex.md +++ b/plugin/README-codex.md @@ -21,6 +21,10 @@ Codex. encrypted Codex compaction placeholders with auxiliary app-server summaries backed by the visible source messages in TraceDecay's LCM DAG. +The source `hooks/hooks-codex.json` is an empty, self-documenting seed for +repo-local bundles. Global Codex installs populate `hooks/hooks.json` from the +managed hook table at install time. + Codex skips newly installed or changed command hooks until they are trusted — run `/hooks` in Codex to review and trust the tracedecay hooks. @@ -34,6 +38,17 @@ parameters). The bundled `using-the-cli` skill and injected steering use that CLI fallback when MCP transport errors or times out, instead of querying `.tracedecay` databases. +For literal strings, regexes, and config keys inside indexed code, use +`tracedecay_grep`; reserve `tracedecay_search` for symbol names and +`tracedecay_context` for concept-level discovery. + +Before running `cargo check`/`tsc`/`clippy` in the shell, or when shell output +shows compile errors, the injected steering routes the moment to tracedecay +diagnostics: paste captured output into `tracedecay_diagnose`, or run +`tracedecay_diagnostics` for fresh structured errors mapped to the enclosing +symbols and callers. The bundled `fixing-build-and-type-errors` skill covers +this workflow. + 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/plugin/README-cursor.md b/plugin/README-cursor.md index 50204ef228..9efeaf0faf 100644 --- a/plugin/README-cursor.md +++ b/plugin/README-cursor.md @@ -40,6 +40,10 @@ parameters). The bundled `using-the-cli` skill and always-applied rule use that CLI fallback when MCP transport errors or times out, instead of querying `.tracedecay` databases. +For literal strings, regexes, and config keys inside indexed code, use +`tracedecay_grep`; reserve `tracedecay_search` for symbol names and +`tracedecay_context` for concept-level discovery. + 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 @@ -100,6 +104,7 @@ per-call review, add the snippet below to `~/.cursor/permissions.json` "tracedecay:tracedecay_find_exact_symbol", "tracedecay:tracedecay_gini", "tracedecay:tracedecay_god_class", + "tracedecay:tracedecay_grep", "tracedecay:tracedecay_health", "tracedecay:tracedecay_hermes_skill_bridge", "tracedecay:tracedecay_hotspots", diff --git a/plugin/README.md b/plugin/README.md new file mode 100644 index 0000000000..4428f7a4c1 --- /dev/null +++ b/plugin/README.md @@ -0,0 +1,34 @@ +# TraceDecay Plugin Bundle + +This source tree builds the TraceDecay integrations for Claude Code, Codex, +and Cursor. The installed bundles expose the `tracedecay` MCP server, shared +workflow skills, and host-specific lifecycle hooks. + +## Source Layout + +- `skills/`: shared `SKILL.md` workflow instructions. +- `hooks/hooks-claude.json`: Claude Code lifecycle hooks. `PostToolUse` + observes edit, shell, grep, glob, and read tools so the plugin can refresh + the index and steer broad search toward TraceDecay. +- `hooks/hooks-codex.json`: repo-local Codex hook seed. It is intentionally + empty; the global Codex plugin fills hooks at install time. +- `hooks/hooks-cursor.json`: Cursor lifecycle hooks. +- `.mcp.json`: shared Claude/Codex MCP config. Codex rewrites args/env by + install scope; Claude rewrites the command to the resolved binary path. +- `mcp-cursor.json`: Cursor MCP config, deployed as `mcp.json`. +- `README-claude.md`, `README-codex.md`, `README-cursor.md`: host README + files, deployed as `README.md`. + +## Search Routing + +Use `tracedecay_grep` for literal strings, regexes, and config keys inside +indexed code. Use `tracedecay_search` for symbol names, `tracedecay_context` +for concepts, `tracedecay_files` for path discovery, and `tracedecay_read` or +`tracedecay_outline` for bounded reads after a file is known. + +Every MCP tool also has a CLI fallback: + +```bash +tracedecay tool +tracedecay tool tracedecay_grep --help +``` diff --git a/plugin/agents/code-explorer.md b/plugin/agents/code-explorer.md index 2c9ed8c273..c0b436220c 100644 --- a/plugin/agents/code-explorer.md +++ b/plugin/agents/code-explorer.md @@ -13,7 +13,7 @@ Read-only exploration subagent. Investigate the repository and return findings. ## Method 1. Start with `tracedecay_context` (add `keywords` for concepts). **Respect the per-project call budget shown in the tool description.** Pass `seen_node_ids` from each response to the next call's `exclude_node_ids`. -2. Narrow with `tracedecay_search` / `tracedecay_find_exact_symbol` / `tracedecay_body` / `tracedecay_outline`. +2. Narrow with `tracedecay_grep` for literal/regex text, `tracedecay_search` / `tracedecay_find_exact_symbol` for symbol names, and `tracedecay_body` / `tracedecay_outline` for bounded reads. 3. Trace with `tracedecay_callers` / `tracedecay_callees` / `tracedecay_call_chain`; assess reach with `tracedecay_impact`. 4. Fall back to Grep/Read only for non-indexed content or after TraceDecay pinpoints files. diff --git a/plugin/agents/session-historian.md b/plugin/agents/session-historian.md index b74c2c277d..3ae0a274c0 100644 --- a/plugin/agents/session-historian.md +++ b/plugin/agents/session-historian.md @@ -16,7 +16,7 @@ Read-only recall subagent. Retrieve what past sessions said, did, and decided fo 2. Narrow with `tracedecay_lcm_grep` (scope/role/time filters), then replay with `tracedecay_lcm_load_session` (paginate via `after_store_id`, never dump whole sessions). 3. Drill into summaries with `tracedecay_lcm_describe` / `tracedecay_lcm_expand` / `tracedecay_lcm_expand_query`; inspect the store with `tracedecay_lcm_status`. 4. For durable decisions/facts, search `tracedecay_fact_store` (`action: "search"`, plus `"probe"`/`"reason"` when useful). -5. If the `tracedecay:recalling-session-context` skill is available, follow its full ladder. +5. If the `tracedecay:managing-session-context` skill is available, follow its full ladder. ## Rules diff --git a/plugin/hooks/hooks-claude.json b/plugin/hooks/hooks-claude.json index 2e078c3bb4..3880b067c4 100644 --- a/plugin/hooks/hooks-claude.json +++ b/plugin/hooks/hooks-claude.json @@ -56,7 +56,7 @@ ], "PostToolUse": [ { - "matcher": "Edit|MultiEdit|Write|NotebookEdit|Bash", + "matcher": "Edit|MultiEdit|Write|NotebookEdit|Grep|Glob|Read|Bash", "hooks": [ { "type": "command", @@ -67,6 +67,19 @@ } ] } + ], + "SubagentStart": [ + { + "hooks": [ + { + "type": "command", + "command": "__TRACEDECAY_BIN__", + "args": [ + "hook-claude-subagent-start" + ] + } + ] + } ] } } diff --git a/plugin/hooks/hooks-codex.json b/plugin/hooks/hooks-codex.json index deffac9734..b620b06aed 100644 --- a/plugin/hooks/hooks-codex.json +++ b/plugin/hooks/hooks-codex.json @@ -1,3 +1,4 @@ { + "description": "Seed template for the Codex hooks bundle. The `hooks` object is intentionally empty here: repo-local Codex bundles ship no lifecycle hooks (those come from the global personal plugin), and the global bundle populates this object at install time from the CODEX_MANAGED_HOOKS table in src/agents/codex.rs. Codex's hook loader reads only `hooks.*` and ignores this field.", "hooks": {} } diff --git a/plugin/overlays/cursor/agents/code-explorer.md b/plugin/overlays/cursor/agents/code-explorer.md index 71fc66b3c0..40a9bca095 100644 --- a/plugin/overlays/cursor/agents/code-explorer.md +++ b/plugin/overlays/cursor/agents/code-explorer.md @@ -12,7 +12,7 @@ Read-only exploration subagent. Investigate the repository and return findings. ## Method 1. Start with `tracedecay_context` (add `keywords` for concepts). **Respect the per-project call budget shown in the tool description.** -2. Narrow with `tracedecay_search` / `tracedecay_find_exact_symbol` / `tracedecay_body` / `tracedecay_outline`. +2. Narrow with `tracedecay_grep` for literal/regex text, `tracedecay_search` / `tracedecay_find_exact_symbol` for symbol names, and `tracedecay_body` / `tracedecay_outline` for bounded reads. 3. Trace with `tracedecay_callers` / `tracedecay_callees` / `tracedecay_call_chain`; assess reach with `tracedecay_impact`. 4. Fall back to Grep/Read only for non-indexed content or after TraceDecay pinpoints files. diff --git a/plugin/overlays/cursor/agents/session-historian.md b/plugin/overlays/cursor/agents/session-historian.md index b193c0fd7a..6c01514923 100644 --- a/plugin/overlays/cursor/agents/session-historian.md +++ b/plugin/overlays/cursor/agents/session-historian.md @@ -15,7 +15,7 @@ Read-only recall subagent. Retrieve what past sessions said, did, and decided fo 2. Narrow with `tracedecay_lcm_grep` (scope/role/time filters), then replay with `tracedecay_lcm_load_session` (paginate via `after_store_id`, never dump whole sessions). 3. Drill into summaries with `tracedecay_lcm_describe` / `tracedecay_lcm_expand` / `tracedecay_lcm_expand_query`; inspect the store with `tracedecay_lcm_status`. 4. For durable decisions/facts, search `tracedecay_fact_store` (`action: "search"`, plus `"probe"`/`"reason"` when useful). -5. Follow the full ladder in the `tracedecay:recalling-session-context` skill. +5. Follow the full ladder in the `tracedecay:managing-session-context` skill. ## Rules diff --git a/plugin/overlays/cursor/commands/tracedecay-recall-memory.md b/plugin/overlays/cursor/commands/tracedecay-recall-memory.md index 31a639f100..df053756e2 100644 --- a/plugin/overlays/cursor/commands/tracedecay-recall-memory.md +++ b/plugin/overlays/cursor/commands/tracedecay-recall-memory.md @@ -4,7 +4,7 @@ description: Recall prior decisions, durable facts, and past session conversatio # /tracedecay-recall-memory -Use `tracedecay:project-memory`; for raw conversation recall, use `tracedecay:recalling-session-context`. +Use `tracedecay:project-memory`; for raw conversation recall, use `tracedecay:managing-session-context`. - **Args:** interpret `$ARGUMENTS` as the question or topic to recall; if absent, ask what to look up. - Route durable decisions/facts through `fact_store` search; route "what happened in that session" through `tracedecay_message_search` and the LCM retrieval ladder. Stay read-only. diff --git a/plugin/rules/tracedecay.mdc b/plugin/rules/tracedecay.mdc index 8832bdc113..d764c440a8 100644 --- a/plugin/rules/tracedecay.mdc +++ b/plugin/rules/tracedecay.mdc @@ -5,20 +5,31 @@ alwaysApply: true # Use tracedecay before native code tools -If the workspace is indexed, TraceDecay is the first stop for codebase questions. Treat this as a procedure, not a preference: +If there is even a 1% chance this workspace has a TraceDecay index, the graph, memory store, and session archive are the first stop — before the first Grep, Glob, Read, `gh pr diff`, test run, or memory-file write. Graph before grep. Facts before files. Invoke `tracedecay:using-tracedecay` and follow its moment table. For project/store identity use `tracedecay_active_project` / `tracedecay_storage_status` (the resolved active project store), never repo-local DB probing. -- **Starting a task or code question:** invoke `tracedecay:using-tracedecay` and follow its moment table before the first Grep/Glob/Read. -- **About to Grep/Glob/codebase-search for a symbol or concept:** call `tracedecay_search` or `tracedecay_context` first, then follow `tracedecay:exploring-code`. -- **About to open or Read a source file:** use `tracedecay_outline`, `tracedecay_body`, or `tracedecay_read` slices first. Full-file Read is the fallback, not the default. -- **Asked about callers, callees, dependencies, or blast radius:** use `tracedecay:tracing-functions` or `tracedecay:assessing-impact` before guessing. -- **About to add a helper or edit source:** use `tracedecay:editing-safely` for duplicate probes, refactor recon, and anchored edits. -- **Reviewing a diff, auditing risk, or drafting change text:** use `tracedecay:reviewing-changes`. -- **Architecture, health, status, config, TODOs, runtime:** use `tracedecay:code-health`. -- **Prior decisions or past conversations:** use `tracedecay:project-memory` for durable facts (recall and curation) and `tracedecay:recalling-session-context` for raw transcript recall; fact mutation lives in `tracedecay:project-memory`. -- **Durable memory:** when a durable decision, user preference, correction, or pitfall surfaces, store it proactively with `tracedecay_fact_store` (action "add") with calibrated trust. Do not store secrets/credentials, transient errors, environment-specific failures, one-off narratives, or task progress. -- **Active project/store questions:** use `tracedecay_active_project` or `tracedecay_storage_status` for resolved active project routing instead of inferring from marker files or direct DB paths. -- **Truncated MCP responses:** if a response has `truncated: true` plus `handle`, narrow the query first; call `tracedecay_retrieve` only when omitted details are needed. -- **MCP errors/timeouts:** use the CLI fallback (`tracedecay tool --key value`; see `tracedecay:using-the-cli`) instead of querying `.tracedecay` databases directly or abandoning tracedecay. -- Fall back to plain file reads, search, or shell only for non-indexed content or after tracedecay has pinpointed the exact files. +## Moment to mandatory action -Red flags: "Grep is faster", "I'll just read the whole file", "this is a simple lookup", "I already know the codebase", or "I'll use the skill after exploring" are rationalizations. Stop and use the matching TraceDecay workflow first. +| The moment you are in | Do this instead | +|---|---| +| Grep/rg a literal string, regex, or config key | `tracedecay_grep` — `tracedecay:exploring-code` | +| Search a symbol/concept, or open a source file | `tracedecay_search` / `tracedecay_context`, then outline→body→read — `tracedecay:exploring-code` | +| "Who calls X" / "what does X call" / "trace this" | `tracedecay:tracing-functions` | +| What breaks / which tests to run | `tracedecay:assessing-impact` | +| `gh pr diff` or read a raw diff to review | `tracedecay_pr_context` / `tracedecay_diff_context` (offline) — `tracedecay:reviewing-changes` | +| Write a helper, rename, or mass-edit | `tracedecay:editing-safely` | +| Build/type errors, or run cargo check/tsc | `tracedecay:fixing-build-and-type-errors` | +| Write MEMORY.md/CLAUDE.md, or a past decision | `tracedecay:project-memory` (`fact_store`) | +| Architecture, tech debt, index/project status | `tracedecay:code-health` | +| An MCP call failed | `tracedecay:using-the-cli` — `tracedecay tool `, never abandon over transport | + +## Red flags — these thoughts mean STOP, you are rationalizing + +- "Grep is faster" — `tracedecay_grep` runs the same match and returns the enclosing symbol. +- "I'll just read the whole file" — `outline`/`body` answer at a fraction of the tokens. +- "This repo probably isn't indexed" — check `tracedecay_status`; guessing is the rationalization. +- "I'll use gh for the PR diff" — `pr_context` computes changed symbols offline; gh is for comments/CI only. +- "I'll jot this in MEMORY.md" — durable facts go to `fact_store` (add); MEMORY.md is not memory. +- "The index might be stale, sync first" — hooks auto-sync; never run manual sync. +- "The MCP call might fail" — `tracedecay tool ` always works. Transport ≠ capability. + +If an MCP call errors or times out, fall back to `tracedecay tool --key value` (see `tracedecay:using-the-cli`) — never query `.tracedecay` databases directly or abandon tracedecay. Fall back to plain Grep/Read only for non-indexed content or after the graph pinpoints exact files. diff --git a/plugin/skills/assessing-impact/SKILL.md b/plugin/skills/assessing-impact/SKILL.md index cb3362912d..071e7f5dca 100644 --- a/plugin/skills/assessing-impact/SKILL.md +++ b/plugin/skills/assessing-impact/SKILL.md @@ -1,71 +1,64 @@ --- name: assessing-impact -description: 'Use when estimating blast radius, finding what depends on a symbol or file, choosing or running affected tests, checking whether code is tested, or verifying a change without a full suite. Use before guessing tests, running broad suites, or declaring a change safe.' +description: 'Use when estimating blast radius, finding what depends on a symbol or file, choosing or running affected tests, or verifying a change without a full suite. Trigger before guessing tests or running "cargo test" broadly. Do NOT use to review a diff (tracedecay:reviewing-changes).' --- # Assessing impact +``` +NO GUESSED TEST LISTS AND NO FULL-SUITE-BY-DEFAULT. +Compute the dependent set and the affected tests from the graph first. +``` + +Announce: "Using tracedecay:assessing-impact for ." + ## Blast radius -1. **Resolve the target → node ID** with `tracedecay_search` / - `tracedecay_find_exact_symbol` / `tracedecay_by_qualified_name` (resolver - ladder: `tracedecay:exploring-code`). -2. **Symbol blast radius → `tracedecay_impact`** (`node_id`, small `max_depth` - first, widen only if the picture is incomplete): all direct + transitive - dependents. -3. **File-level fan-in → `tracedecay_file_dependents`** (every file importing - the changed file). -4. **Already have changed paths → `tracedecay_diff_context`** (`files`): - modified symbols + dependents + affected tests in one call — prefer it - over separate lookups. -5. **Structural fragility (optional):** `tracedecay_coupling` / - `tracedecay_dependency_depth` to see if the target is a high-fan-in hub. +1. Have changed file paths already? → `tracedecay_diff_context` (`files`): + modified symbols + dependents + affected tests in ONE offline call — prefer + this over separate lookups. Works with no network and no PR. +2. Single symbol → resolve to node ID (`tracedecay:exploring-code` ladder), + then `tracedecay_impact` (`node_id`, shallow `max_depth` first). +3. File-level fan-in → `tracedecay_file_dependents`. +4. Optional fragility check → `tracedecay_coupling` / `tracedecay_dependency_depth`. ## Coverage intelligence (read-only) -1. **Symbol/file → its tests → `tracedecay_test_map`** (`file` or `node_id`): - direct coverage edges; an empty result means no test reaches it through - the indexed graph. -2. **Changed files → affected tests → `tracedecay_affected`** (`files`): - dependency-graph traversal to every test file that can see the change. -3. **Where the next test goes → `tracedecay_test_risk`** (`path?`, `limit?`): - risk = (complexity + 1) × (fan_in + 1) × untested-multiplier — the - prioritized gap list. +| Question | Call | +|---|---| +| Which tests reach this symbol/file | `tracedecay_test_map` (empty result = no indexed path, strong-not-absolute evidence of untested) | +| Which tests can see these changed files | `tracedecay_affected` (`files`) | +| Where the next test is most needed | `tracedecay_test_risk` (`path?`, `limit?`) | ## Running the impacted tests -1. **Run → `tracedecay_run_affected_tests`** (`changed_paths`, `max_tests`, - `profile`, `timeout_secs`): pass/fail per test, with the source nodes each - test covers. Cargo-only; for non-Rust repos use `tracedecay_diagnostics` - (tsc/pyright) and the project's own test runner. -2. **On compile/type failure → `tracedecay_diagnose`** for captured cargo - stderr, or the `tracedecay:fixing-build-and-type-errors` skill. +1. `tracedecay_run_affected_tests` (`changed_paths`, `max_tests`, `profile`, + `timeout_secs`): pass/fail per test with covered source nodes. **Cargo/Rust + only** — for other stacks use `tracedecay_affected` to get the test set, + then the project's own runner on exactly that set. +2. Compile/type failures during the run → `tracedecay:fixing-build-and-type-errors`. -## Guardrails +## Rules -- Everything except `tracedecay_run_affected_tests` and - `tracedecay_diagnostics` is read-only and safe to run first to preview - scope. The cargo-backed tools run toolchains (the first `diagnostics` build - can take minutes; forced target dir - `/tmp/tracedecay-target//diagnostics`) — respect Cursor - approval/run-mode and avoid duplicate runs. -- Coverage is structural (call/use edges): integration tests that reach code - indirectly (through a binary, fixture, or IO boundary) can be missed — an - empty `test_map` is strong but not absolute evidence of "untested". -- Start with a shallow `max_depth` and widen only when incomplete. -- For broad changes, use scoped read-only subagents per changed file group or - subsystem; require cited dependents, affected tests, and tool parameters — - the parent agent owns the final blast-radius and test-set synthesis. +- Everything except `run_affected_tests`/`diagnostics` is read-only — preview + scope freely before running toolchains; respect host approval/run-mode and + avoid duplicate toolchain runs (first diagnostics build can take minutes). +- Coverage is structural: integration tests reaching code through binaries, + fixtures, or IO can be missed. Say so when asserting "untested". +- Refactor checklist wanted → `tracedecay:editing-safely`. Whole-diff review → + `tracedecay:reviewing-changes`. -## Handoff +## If tools are deferred or MCP fails -- Mechanical refactor where impact analysis becomes an edit checklist → - `tracedecay:editing-safely`. Reviewing a whole diff → `tracedecay:reviewing-changes`. +- Deferred: one ToolSearch call — + `select:tracedecay_diff_context,tracedecay_impact,tracedecay_affected,tracedecay_test_map,tracedecay_run_affected_tests`. +- MCP error: `tracedecay tool diff_context --files …` etc. (see + `tracedecay:using-the-cli`). Do not degrade to running the full suite. -## Output +## Deliverable -- (a) impacted symbols + files, (b) the test set to run (or the pass/fail - summary with failing-symbol mapping), (c) any hub/coupling risk and ranked - coverage gaps. -- If any result includes a `tracedecay_metrics:` line, report the savings to - the user. +Do not end without all three: (a) impacted symbols + files, (b) the concrete +test set (or pass/fail summary mapped back to source), (c) coverage gaps or +hub risk worth flagging. If the graph cannot see part of the change (e.g. +generated code), state that explicitly instead of widening to a full suite. +Report any `tracedecay_metrics:` line. diff --git a/plugin/skills/code-health/SKILL.md b/plugin/skills/code-health/SKILL.md index fb0b50915e..a8c3953b44 100644 --- a/plugin/skills/code-health/SKILL.md +++ b/plugin/skills/code-health/SKILL.md @@ -87,14 +87,22 @@ and the specific scans the user asked for — don't run every tool by reflex. `tracedecay:editing-safely` / `tracedecay:reviewing-changes`, verification to `tracedecay:assessing-impact`. Memory recall belongs to `tracedecay:project-memory`; past-session recall to - `tracedecay:recalling-session-context`. + `tracedecay:managing-session-context`. -## Output +## If tools are deferred or MCP fails -- The composite score + weak dimensions with ranked worst offenders and a - prioritized fix list; the layered module map with dependency - hotspots/violations; the per-dimension session delta; or the status - numbers, config values (with file + line), marker list, or runtime snapshot - the user asked for. Pairs with the `docs-canvas` plugin if installed. -- If any result includes a `tracedecay_metrics:` line, report the savings to - the user. +- Deferred (names listed without schemas): load once with ToolSearch — + `select:tracedecay_health,tracedecay_gini,tracedecay_dsm,tracedecay_status,tracedecay_active_project` + (one batched call, add others needed) — then call normally. +- MCP error/timeout/disconnect: same tool, same args, via shell: + `tracedecay tool --key value` (see `tracedecay:using-the-cli`). Never + query `.tracedecay` databases directly; never abandon the graph over transport. + +## Deliverable + +Do not end this workflow without: the composite score + weak dimensions with +ranked worst offenders and a prioritized fix list; the layered module map with +dependency hotspots/violations; the per-dimension session delta; or the status +numbers, config values (with file + line), marker list, or runtime snapshot +the user asked for. Pairs with the `docs-canvas` plugin if installed. Report +any `tracedecay_metrics:` line to the user. diff --git a/plugin/skills/diagnosing-analytics/SKILL.md b/plugin/skills/diagnosing-analytics/SKILL.md index ed3ffc7823..a68a6e9a44 100644 --- a/plugin/skills/diagnosing-analytics/SKILL.md +++ b/plugin/skills/diagnosing-analytics/SKILL.md @@ -65,8 +65,18 @@ store databases directly. - If the MCP transport is down, every command above still works — they are plain CLI subcommands (see `tracedecay:using-the-cli`). -## Output +## If tools are deferred or MCP fails -- Headline counts (events, MCP tool calls, tracedecay calls, hook calls) with - the exact command used, plus any gaps found: unattributed hook rows, stale - or missing sources, providers with hooks firing but zero tool usage. +- This skill is already CLI-first: the `tracedecay analytics` / `doctor` / + `sessions` subcommands run over shell and need no MCP transport. +- The `tracedecay tool lcm_status` / `lcm_doctor` calls also work via MCP; if + those tools are deferred, load once with ToolSearch — + `select:tracedecay_lcm_status,tracedecay_lcm_doctor` — or just use the CLI + form shown above (see `tracedecay:using-the-cli`). + +## Deliverable + +Do not end this workflow without: headline counts (events, MCP tool calls, +tracedecay calls, hook calls) with the exact command used, plus any gaps +found: unattributed hook rows, stale or missing sources, providers with hooks +firing but zero tool usage. Report any `tracedecay_metrics:` line to the user. diff --git a/plugin/skills/editing-safely/SKILL.md b/plugin/skills/editing-safely/SKILL.md index c5a0d6f3e3..0e107bbd35 100644 --- a/plugin/skills/editing-safely/SKILL.md +++ b/plugin/skills/editing-safely/SKILL.md @@ -1,6 +1,6 @@ --- name: editing-safely -description: 'Use when editing or writing source: before adding any new helper (duplicate probe), for anchored/structural edits, mechanical refactors like renames or signature/field changes, or porting code between modules or languages. Missed call sites and duplicate helpers start here.' +description: 'Use before writing a new function or helper (it may already exist), and before any rename, signature, or field change — the graph lists every call site.' --- # Editing safely @@ -92,9 +92,17 @@ Run this read-only recon in one shot for a symbol or `Struct::field` with `tracedecay:fixing-build-and-type-errors`, then run the affected tests via `tracedecay:assessing-impact`. -## Output +## If tools are deferred or MCP fails -- The recon checklist (sites grouped by file), the files/symbols changed, the - helper reused (or its confirmed absence), and the verification result. -- If any result includes a `tracedecay_metrics:` line, report the savings to - the user. +- Deferred (names listed without schemas): load once with ToolSearch — + `select:tracedecay_search,tracedecay_similar,tracedecay_rename_preview,tracedecay_str_replace,tracedecay_replace_symbol` + (one batched call, add others needed) — then call normally. +- MCP error/timeout/disconnect: same tool, same args, via shell: + `tracedecay tool --key value` (see `tracedecay:using-the-cli`). Never + query `.tracedecay` databases directly; never abandon the graph over transport. + +## Deliverable + +Do not end this workflow without: the recon checklist (sites grouped by file), +the files/symbols changed, the helper reused (or its confirmed absence), and +the verification result. Report any `tracedecay_metrics:` line to the user. diff --git a/plugin/skills/exploring-code/SKILL.md b/plugin/skills/exploring-code/SKILL.md index fa27c29e66..138560971d 100644 --- a/plugin/skills/exploring-code/SKILL.md +++ b/plugin/skills/exploring-code/SKILL.md @@ -1,99 +1,77 @@ --- name: exploring-code -description: 'Use when searching the codebase, locating a symbol, exploring how a feature works, reading or opening any source file, answering type/trait questions, or checking code on another git branch — the graph answers before Grep/Glob/Read in an indexed project.' +description: 'Use the moment the next action is Grep, Glob, rg, find, cat, or opening a source file to answer "where is X", "how does Y work", a type/trait question, or to check another git branch. Returns the enclosing symbol. Do NOT use for callers/callees (tracedecay:tracing-functions).' --- # Exploring code -Use the TraceDecay code graph before Grep/Glob/file reads. Pick the cheapest -tool that answers the question and stop. If the task says "trace", "find -callers", or "what depends on X", switch to `tracedecay:tracing-functions` -after resolving the symbol. +``` +NO RAW GREP, GLOB, OR FULL-FILE READS OVER INDEXED CODE. +The graph answers first; plain tools are for what the index does not cover. +``` -## Finding it +Announce: "Using tracedecay:exploring-code to ." Pick the cheapest +row below, run it, and stop when the question is answered. -1. **Conceptual / "how does X work" / names unknown → `tracedecay_context`.** - `task` = the question; add `keywords` to expand synonyms (auth → - `["login","session","token"]`). Set `include_code: true` only when you need - snippets; `mode: "plan"` when scoping an implementation. Pass prior - `seen_node_ids` via `exclude_node_ids` to dedupe across calls. -2. **Exact name known → `tracedecay_find_exact_symbol`** (cheapest probe) or - **`tracedecay_body`** (name → full source in one shot; ranks matches when - ambiguous). -3. **Ranked discovery by name/keyword → `tracedecay_search`.** -4. **Half-remembered name → `tracedecay_similar`** (fuzzy/substring); - **stable cross-run identity → `tracedecay_by_qualified_name`**. -5. **By shape, not name → `tracedecay_signature_search`** (return type / - param substring / `async` / path), e.g. "every fn returning `Result<_, MyError>`". +## Route by what you are matching -## Reading it cheaply +| You are matching | Call | Not | +|---|---|---| +| Literal string / regex / config key / error text | `tracedecay_grep` (`fixed_strings` for plain literals) | raw Grep/rg | +| A symbol by exact name | `tracedecay_find_exact_symbol`, else `tracedecay_search` | Glob + open | +| A concept / "how does X work" (names unknown) | `tracedecay_context` (`task` = the question, add `keywords` synonyms) | Explore agent | +| A file by role or path | `tracedecay_files` | find/ls -R | +| Half-remembered name | `tracedecay_similar` | guessing greps | +| Code by shape (return type, params, async) | `tracedecay_signature_search` | reading modules | -Climb this ladder and stop at the first rung that answers the question: +## Read cheaply — stop at the first rung that answers -1. **Orient in a file → `tracedecay_outline`** (`path`, optional `kinds`): - every top-level symbol with line numbers, no bodies. -2. **API surface only → `tracedecay_signature`** (qualified name); bulk - per-file variant: `tracedecay_read` with `mode: "signatures"`. -3. **One symbol's source → `tracedecay_body`** or `tracedecay_node` (by node - ID, with metadata) — never open a whole file for one function. -4. **A specific region → `tracedecay_read`** (`mode: "lines"`, e.g. `"120-180"`). -5. **Whole file (last resort) → `tracedecay_read`** (`mode: "full"`): - cross-session cached — unchanged files return a tiny `unchanged: true` - stub, so prefer it over the plain Read tool. -6. **Module/directory surface → `tracedecay_module_api`** (all `pub` symbols); - enumerate files with `tracedecay_files` (`path?`, `pattern?`). +1. Orient in a file → `tracedecay_outline` (symbols + line numbers, no bodies). +2. API surface → `tracedecay_signature`; per-file bulk → `tracedecay_read` `mode:"signatures"`. +3. One symbol's source → `tracedecay_body` — never open a whole file for one function. +4. A line range → `tracedecay_read` `mode:"lines"`; whole file (last resort) → + `tracedecay_read` `mode:"full"` (cross-session cached; prefer it over Read). +5. Module surface → `tracedecay_module_api`. -## Types & traits +For types and traits — `tracedecay_implementations` / `tracedecay_impls` / +`tracedecay_type_hierarchy` / `tracedecay_derives` / `tracedecay_constructors` +/ `tracedecay_field_sites`, plus `tracedecay_by_qualified_name` and +`tracedecay_node` for stable identity — read +[references/types-and-traits.md](references/types-and-traits.md). +For other git branches without switching checkout — `tracedecay_branch_list` / +`tracedecay_branch_search` / `tracedecay_branch_diff` — read +[references/other-branches.md](references/other-branches.md). -1. **Who implements a trait / every body of a method → `tracedecay_implementations`** - (`trait` form: implementing types + impl-block methods; `method` form: - every function named X grouped by enclosing type, with bodies). -2. **Impl blocks by trait, type, or both → `tracedecay_impls`** (avoid the - no-filter form — it returns every impl in the graph). -3. **Recursive hierarchy → `tracedecay_type_hierarchy`**; deepest - extends-chains → `tracedecay_inheritance_depth`. -4. **"Where does this method come from?" → `tracedecay_derives`**: the - `#[derive(...)]` macros on a type and the methods each synthesizes — check - before concluding `.clone()` / `.eq()` has no definition. -5. **Construction sites → `tracedecay_constructors`** (every struct-literal - site with present and missing fields); **field usage → - `tracedecay_field_sites`** (`field` or `Struct::field`): every read/write - site with file, line, and enclosing symbol. +## Rules -## Other branches +- One well-formed `tracedecay_context` call beats many narrow searches. Pass + prior `seen_node_ids` via `exclude_node_ids` on follow-ups; respect the call + budget in the tool description. +- Truncated response with a `handle`? Narrow the query; call + `tracedecay_retrieve` only if the omitted detail is needed. Never re-run broad. +- Empty or stale-looking results → check `tracedecay_status` BEFORE concluding + the repo is unindexed; the index auto-syncs — do not run manual sync commands. +- Search came up empty and a new helper seems needed → run the + `tracedecay:editing-safely` duplicate probe first. +- "Who calls X / what does X call / what breaks" → hand off to + `tracedecay:tracing-functions` / `tracedecay:assessing-impact` after resolving + the symbol; do not grep for call sites. +- Fall back to plain Grep/Glob/Read only for non-indexed content (prose docs, + vendored/skipped trees) or after the graph pinpoints exact files. -1. **What's tracked → `tracedecay_branch_list`**; **search another branch → - `tracedecay_branch_search`** (`branch`, `query`); **compare branches → - `tracedecay_branch_diff`** (`base?`, `head?`, `file?`, `kind?`) — all - read-only, never touching your checkout. -2. Branch tracking is opt-in per branch (`tracedecay branch add ` in - the terminal; the hooks auto-track branches you visit). A branch-fallback - `WARNING` prefix means results came from the nearest tracked ancestor — - surface that to the user. +## If tools are deferred or MCP fails -## Guardrails +- Deferred (names listed without schemas): load once with ToolSearch — + `select:tracedecay_context,tracedecay_search,tracedecay_grep,tracedecay_outline,tracedecay_body,tracedecay_read` + (one batched call, add others needed) — then call normally. +- MCP error/timeout/disconnect: same tool, same args, via shell: + `tracedecay tool --key value` (see `tracedecay:using-the-cli`). Never + query `.tracedecay` databases directly; never abandon the graph over transport. -- Everything here is read-only and parallel-safe. -- Only fall back to Grep/Glob/Read for non-indexed content (string literals, - comments, prose, config bodies — or `tracedecay_config` for TOML/JSON keys) - or after TraceDecay pinpoints exact files. If results look empty or stale, - check `tracedecay_status` before falling back to raw reads. -- Prefer one well-formed `tracedecay_context` call over many narrow searches. -- `tracedecay_constructors` is best-effort for Rust (ignores `match` arms); - `tracedecay_field_sites` pattern-matches `.`, so prefer the - `Struct::field` form to narrow. -- For several independent questions, use scoped read-only subagents with one - bounded target each and a strict no-writes instruction; require cited - file/symbol ids and tool names, and synthesize in the parent agent. -- If a response is truncated with a `handle`, narrow the query first; call - `tracedecay_retrieve` with the `handle` only when the omitted details are - needed. -- About to write a new helper because the search came up empty? Run the - `tracedecay:editing-safely` pre-write duplicate probe first. +## Deliverable -## Output - -- The file + symbol the user needs (path, qualified name, signature), the - outline/snippet that answers the question, and how you found it. -- If any result includes a `tracedecay_metrics:` line, report the savings to - the user. +Do not end this workflow without: the file + symbol (path, qualified name, +signature), the snippet or outline that answers the question, and how it was +found. Budget: aim for ≤4 graph calls before synthesizing; if the answer is +still incomplete, say precisely what is missing rather than falling back to +grep. Report any `tracedecay_metrics:` savings line to the user. diff --git a/plugin/skills/exploring-code/references/other-branches.md b/plugin/skills/exploring-code/references/other-branches.md new file mode 100644 index 0000000000..c33fdf239c --- /dev/null +++ b/plugin/skills/exploring-code/references/other-branches.md @@ -0,0 +1,18 @@ +# Other branches + +Read-only branch exploration for the `tracedecay:exploring-code` skill. + +- List tracked branches +- Search or compare another branch without switching checkout +- Branch-fallback WARNING handling + +## Other branches + +1. **What's tracked → `tracedecay_branch_list`**; **search another branch → + `tracedecay_branch_search`** (`branch`, `query`); **compare branches → + `tracedecay_branch_diff`** (`base?`, `head?`, `file?`, `kind?`) — all + read-only, never touching your checkout. +2. Branch tracking is opt-in per branch (`tracedecay branch add ` in + the terminal; the hooks auto-track branches you visit). A branch-fallback + `WARNING` prefix means results came from the nearest tracked ancestor — + surface that to the user. diff --git a/plugin/skills/exploring-code/references/types-and-traits.md b/plugin/skills/exploring-code/references/types-and-traits.md new file mode 100644 index 0000000000..7609a5f51e --- /dev/null +++ b/plugin/skills/exploring-code/references/types-and-traits.md @@ -0,0 +1,30 @@ +# Types & traits + +Type-level exploration for the `tracedecay:exploring-code` skill. + +- Implementations, impl blocks, and hierarchies +- Derives and synthesized methods +- Construction sites and field usage + +## Types & traits + +1. **Who implements a trait / every body of a method → `tracedecay_implementations`** + (`trait` form: implementing types + impl-block methods; `method` form: + every function named X grouped by enclosing type, with bodies). +2. **Impl blocks by trait, type, or both → `tracedecay_impls`** (avoid the + no-filter form — it returns every impl in the graph). +3. **Recursive hierarchy → `tracedecay_type_hierarchy`**; deepest + extends-chains → `tracedecay_inheritance_depth`. +4. **"Where does this method come from?" → `tracedecay_derives`**: the + `#[derive(...)]` macros on a type and the methods each synthesizes — check + before concluding `.clone()` / `.eq()` has no definition. +5. **Construction sites → `tracedecay_constructors`** (every struct-literal + site with present and missing fields); **field usage → + `tracedecay_field_sites`** (`field` or `Struct::field`): every read/write + site with file, line, and enclosing symbol. + +## Guardrails + +- `tracedecay_constructors` is best-effort for Rust (ignores `match` arms); + `tracedecay_field_sites` pattern-matches `.`, so prefer the + `Struct::field` form to narrow. diff --git a/plugin/skills/fixing-build-and-type-errors/SKILL.md b/plugin/skills/fixing-build-and-type-errors/SKILL.md index 8f1bd0253d..f9d9958975 100644 --- a/plugin/skills/fixing-build-and-type-errors/SKILL.md +++ b/plugin/skills/fixing-build-and-type-errors/SKILL.md @@ -1,28 +1,54 @@ --- name: fixing-build-and-type-errors -description: 'Use when diagnosing or fixing compiler/type-checker errors, cargo/clippy output, tsc/pyright failures, mapped diagnostics, or build failures that need graph-anchored context.' +description: 'Use when compiler or type errors are in play: pasted "cargo check"/clippy/tsc/pyright output, a failed build, or a planned type check. Trigger before running those in the shell — tracedecay maps diagnostics to the enclosing symbol with callers. Do NOT use for test failures.' --- # Fixing build & type errors -Use this when build or type diagnostics are relevant to the task. Prefer pasted output when available; respect Cursor approval/run-mode before running fresh toolchain checks. - -## Workflow - -1. **Already have raw output? → `tracedecay_diagnose`** (`cargo_output` required, `severity?`: `error`|`warning`|`all`, `include_callers?`, `max_diagnostics?`): paste full `cargo check`/`clippy`/`rustc` stderr; each diagnostic maps to the smallest containing node with up to 5 callers pre-attached. No toolchain run — cheap and safe. -2. **Need fresh diagnostics → `tracedecay_diagnostics`** (`scope`: `workspace` (default) | `package` (needs `name`) | `file` (needs `path`)): structured errors/warnings, each mapped to the enclosing graph node. Forces target dir `/tmp/tracedecay-target//diagnostics`; the **first** run on a fresh tree can take minutes, later calls are sub-second. -3. **Understand the failing code:** resolve/inspect with the `tracedecay:exploring-code` ladder; widen blast radius with `tracedecay_impact` if a fix is risky. -4. **Apply the fix → `tracedecay:editing-safely`** (or your normal edit tools). -5. **Re-check** with the cheapest applicable diagnostic path, then verify behavior via `tracedecay:assessing-impact`. - -Map a specific error class to the cheapest anchoring tool with [references/error-class-to-tool.md](references/error-class-to-tool.md). - -## Guardrails - -- `tracedecay_diagnostics` runs `cargo`/`tsc`/`pyright` and is the only heavyweight call here; `tracedecay_diagnose` only parses text you provide — prefer it when you already captured the output. -- `tracedecay_diagnostics` is multi-language (cargo/tsc/pyright); `tracedecay_diagnose` is Rust/cargo-specific. - -## Output - -- The grouped diagnostics with enclosing symbols + callers, the applied fix, and a clean re-check. -- If any result includes a `tracedecay_metrics:` line, report the savings to the user. +``` +NO RAW `cargo check`/`tsc` IN THE SHELL WHEN A STRUCTURED PATH EXISTS, +AND NO FIX WITHOUT THE ENCLOSING SYMBOL AND ITS CALLERS IN VIEW. +``` + +Announce: "Using tracedecay:fixing-build-and-type-errors." + +## Choose the entry — this fork matters + +| Situation | Call | Cost | +|---|---|---| +| Compiler output already on hand (pasted or captured) | `tracedecay_diagnose` (`cargo_output`, `include_callers?`) — parses text, maps each error to the smallest containing node with up to 5 callers. Rust/cargo only. | Free — no toolchain run | +| Need fresh diagnostics | `tracedecay_diagnostics` (`scope`: `workspace` \| `package`+`name` \| `file`+`path`) — multi-language (cargo/tsc/pyright), structured, node-mapped. | Heavy — first run on a fresh tree can take minutes (target dir `/tmp/tracedecay-target//diagnostics`); later runs sub-second | + +Always prefer the free row. Respect the host's approval/run-mode before +running fresh toolchain checks. + +## Fix loop + +1. Map each error class to its cheapest anchoring tool with + [references/error-class-to-tool.md](references/error-class-to-tool.md) + (undefined symbol → search+signature; arity mismatch → callers; missing + field → constructors; trait bound → implementations; etc.). +2. Understand the failing code via the `tracedecay:exploring-code` ladder; + widen with `tracedecay_impact` when the fix touches a hub. +3. Apply the fix via `tracedecay:editing-safely`. +4. Re-check with the cheapest applicable path (paste new output into + `diagnose`, or `diagnostics` with `scope: "file"`), then verify behavior via + `tracedecay:assessing-impact`. +5. The same error twice after a fix, or 3+ failed fixes → stop patching; + re-derive the root cause from callers and types before another attempt. + +## If tools are deferred or MCP fails + +- Deferred: one ToolSearch call — + `select:tracedecay_diagnose,tracedecay_diagnostics,tracedecay_search,tracedecay_signature,tracedecay_callers`. +- MCP error: `tracedecay tool diagnose --cargo-output @/tmp/err.txt` (the `@` + reads a file) / `tracedecay tool diagnostics --scope file --path …` (see + `tracedecay:using-the-cli`). Only if the CLI is also unavailable, run the + raw toolchain and paste its output back through `diagnose` when possible. + +## Deliverable + +Do not end while errors remain unexplained: deliver the grouped diagnostics +with enclosing symbols and callers, the applied fix per error class, and a +clean re-check (or a precise statement of what still fails and why). Report +any `tracedecay_metrics:` line. diff --git a/plugin/skills/inspecting-managed-skills/SKILL.md b/plugin/skills/inspecting-managed-skills/SKILL.md index fa89e78c45..1e0f009d90 100644 --- a/plugin/skills/inspecting-managed-skills/SKILL.md +++ b/plugin/skills/inspecting-managed-skills/SKILL.md @@ -26,7 +26,18 @@ The daemon automation loop (skill writer, memory curator, session reflector) dra - Reviewing session-reflection fact proposals → `tracedecay automation facts list|view|apply|reject` (CLI). - Memory fact curation → `tracedecay:project-memory`. -## Output +## If tools are deferred or MCP fails -- The requested skill list, skill body, artifact payload, or Hermes bridge report, plus the exact CLI command for any lifecycle action the user should take next. -- If any result includes a `tracedecay_metrics:` line, report the savings to the user. +- Deferred (names listed without schemas): load once with ToolSearch — + `select:tracedecay_skill_list,tracedecay_skill_view,tracedecay_automation_run_artifact_view,tracedecay_hermes_skill_bridge` + — then call normally. +- MCP error/timeout/disconnect: same tool, same args, via shell: + `tracedecay tool skill_list --key value` (see `tracedecay:using-the-cli`). + Never query `.tracedecay` databases directly; never abandon the graph over transport. + +## Deliverable + +Do not end this workflow without: the requested skill list, skill body, +artifact payload, or Hermes bridge report, plus the exact CLI command for any +lifecycle action the user should take next. Report any `tracedecay_metrics:` +line to the user. diff --git a/plugin/skills/managing-session-context/SKILL.md b/plugin/skills/managing-session-context/SKILL.md index 7553e2dd0d..2dbe1a91da 100644 --- a/plugin/skills/managing-session-context/SKILL.md +++ b/plugin/skills/managing-session-context/SKILL.md @@ -1,19 +1,55 @@ --- name: managing-session-context -description: 'Use when driving the LCM compression lifecycle for a host — preflight, compression, session-boundary reporting, or diagnosing/repairing the LCM store. For past-session recall see recalling-session-context.' +description: 'Use the moment you need anything from a PAST agent session — transcript recall, scoped/time grep, lossless replay, summary-DAG drill-down, or compaction recovery — and when driving the host LCM lifecycle (preflight, compression, boundary, or repair). Reach here before trusting a compacted summary.' --- # Managing session context -This skill owns the **LCM compression and maintenance lifecycle** — the write -and health side of the session store. It is the counterpart to -`tracedecay:recalling-session-context`, which owns retrieval (grep, replay, -summary-DAG expansion). These lifecycle tools are **host-agent integration -tools**: invoke them when the host is managing its own context window or when -the user explicitly asks to compress, repair, or inspect the LCM store — not -casually during recall. +One skill for both sides of the session store: **retrieval** (read-only, where +you start when you need past-session content) and the **LCM compression and +maintenance lifecycle** (the write/health side). Retrieval is cheap and safe; +lifecycle tools are **host-agent integration tools** — invoke them only when the +host is managing its own context window or the user explicitly asks to compress, +repair, or inspect the store, never casually during recall. -## Lifecycle tools +For durable *decisions and facts* (rather than raw conversation), start with +`tracedecay:project-memory` instead — it owns the FTS → fact lane of +`tracedecay_message_search`; this skill owns the FTS → LCM lane. + +## Retrieval ladder (read-only, start here) + +Climb cheapest-first; stop as soon as the question is answered. + +1. **Fast full-text recall → `tracedecay_message_search`** (`query`, optional + `provider`, `scope`: `all`|`parents_only`|`subagents_only`, `limit`): FTS + over ingested transcripts; returns messages with their session ids — the + entry point into the ladder below. +2. **Scoped/filtered grep → `tracedecay_lcm_grep`** (`query`, `scope`: + `current`|`session`|`all` — `current`/`session` require `session_id`; `role`, + `source`, `start_time`/`end_time`, `sort`: `recency`|`relevance`|`hybrid`): + bounded raw-message snippets plus summary text when recall needs + role/time/session precision. +3. **Lossless replay → `tracedecay_lcm_load_session`** (`session_id`, + `after_store_id` + `limit` for stable pagination, `roles`, + `content_offset`/`content_limit`): ordered raw messages of one session; page + with `next_cursor` instead of asking for everything at once. +4. **Summary-DAG drill-down:** `tracedecay_lcm_describe` (`session_id`) for the + session's raw/summary shape; `tracedecay_lcm_expand` (`target.kind`: + `raw_message`|`summary_node`|`external_payload`) to open one node, paging + sources via `source_offset`/`source_limit`; `tracedecay_lcm_expand_query` + (`query`) to assemble bounded retrieval context for a prompt in one call. +5. **Store inspection → `tracedecay_lcm_status`** (counts, token estimates, DAG + depth/compression ratio) when you need to know what the store contains before + searching it. +6. **Git-scoped session lookup → `tracedecay_sessions_for`** (`git_ref`: + `branch`|`worktree`|`commit`, `value`, optional `since`/`until`, `limit`): + find sessions active on a branch or worktree, or sessions that produced a + commit; feed returned session ids back into grep/replay/drill-down above. + +After a compaction, if prior-session context seems missing, run this ladder +before assuming the compacted summary is complete. + +## Lifecycle tools (mutating — host/lifecycle intent only) All take `--provider` and (except doctor/status) `--session-id`. All default to `storage_scope: "project_local"`; pass `hermes_profile` with an absolute @@ -42,7 +78,7 @@ All take `--provider` and (except doctor/status) `--session-id`. All default to depth distribution + compression ratio, payload byte totals, and GC status. Read-only; `deep: true` adds an on-disk integrity sweep. -## Typical flow +## Typical lifecycle flow Preflight → (if it requests compression) compress → status to confirm the ratio moved. On a real host session change, call session_boundary. If counts look @@ -52,25 +88,40 @@ on explicit user intent. ## Guardrails -- `preflight`, `status`, and doctor `diagnose`/`retention` are read-only. +- Retrieval (steps 1–5 above), `preflight`, `status`, and doctor + `diagnose`/`retention` are read-only (grep/status may touch access counters). `compress`, `session_boundary`, and doctor `repair`/`clean`/`gc` + `apply` **mutate** durable session state — run them only with clear lifecycle or user - intent, never speculatively. -- `provider` is required and `all` is rejected for these lifecycle tools; target + intent, never speculatively during recall. +- `provider` is required and `all` is rejected for the lifecycle tools; target one provider at a time. -- Do not let subagents drive compression, boundaries, or repair; those are - parent-agent/host responsibilities. -- Keep token knobs conservative; over-aggressive compression loses replay - fidelity that `tracedecay:recalling-session-context` depends on. +- For multi-step recall, dispatch scoped read-only subagents by session id, time + window, provider, role, or query variant. Subagents must not drive + compression, boundaries, or repair; the parent agent validates cited + messages/summaries and produces the final timeline. +- Keep token knobs conservative; over-aggressive compression loses the replay + fidelity the retrieval ladder depends on. ## Handoff -- Retrieving past-session content (grep, replay, summary-DAG expansion) → `tracedecay:recalling-session-context`. +- Durable decisions/facts and persisting new ones → `tracedecay:project-memory`. +- Dereferencing a truncated response handle → `tracedecay:using-the-cli`. - CLI fallback when MCP transport fails → `tracedecay:using-the-cli`. -## Output +## If tools are deferred or MCP fails + +- Deferred (names listed without schemas): load once with ToolSearch — + `select:tracedecay_message_search,tracedecay_lcm_grep,tracedecay_lcm_load_session,tracedecay_lcm_status,tracedecay_lcm_compress,tracedecay_sessions_for` + (one batched call, add others needed) — then call normally. +- MCP error/timeout/disconnect: same tool, same args, via shell: + `tracedecay tool --key value` (see `tracedecay:using-the-cli`). Never + query `.tracedecay` databases directly; never abandon the graph over transport. + +## Deliverable -- The lifecycle action taken (preflight decision, compression result, boundary - outcome, or store counts), whether it was read-only or mutating, and the - resulting compression ratio / health signals. -- If any result includes a `tracedecay_metrics:` line, report the savings to the user. +Do not end this workflow without: (recall) the messages/summaries found with +session ids and timestamps, and which rung answered the question; or +(lifecycle) the action taken (preflight decision, compression result, boundary +outcome, or store counts), whether it was read-only or mutating, and the +resulting compression ratio / health signals. Report any `tracedecay_metrics:` +line to the user. diff --git a/plugin/skills/project-memory/SKILL.md b/plugin/skills/project-memory/SKILL.md index 0a1cbefe15..9b80d7072c 100644 --- a/plugin/skills/project-memory/SKILL.md +++ b/plugin/skills/project-memory/SKILL.md @@ -1,144 +1,64 @@ --- name: project-memory -description: 'Use when recalling prior decisions, durable facts, user/project preferences, or past project context before answering or planning; or when reviewing, updating, merging, deleting, pruning, or repairing tracedecay memory facts and dashboard curation.' +description: 'Use when about to save or recall anything durable: before writing MEMORY.md, auto-memory, or CLAUDE.md, before answering from a stale summary, or before web-searching what a prior session already answered. Covers store, recall, curation. Do NOT use for raw transcript replay. Never stores secrets.' --- # Project memory -One skill for both halves of project memory. **Recall** is read-only and where -you start; **Curate** mutates stored facts and requires explicit approval before -any destructive action. Prefer TraceDecay-native registered-project selectors -whenever a recall or curation spans or targets a project other than the active -checkout. - -## Recall (read-only, start here) - -Recall memory **before** reaching for external or web search — prior sessions -often already answered the question, and a memory hit is cheaper and -project-specific. - -1. **Durable facts → `tracedecay_fact_store`** with `action: "search"` (or - `"probe"` / `"reason"`), plus `query` and `min_trust`. -2. **Past conversations → `tracedecay_message_search`** (`query`, optional - `provider`, `limit`) over ingested Cursor/Codex/agent transcripts (active - project FTS index). This skill owns the **FTS → fact** lane: use - `message_search` to surface durable project facts. For raw conversation - recall — scoped/role/time-filtered grep, lossless replay, or summary-DAG - drill-down — hand off to `tracedecay:recalling-session-context`, which owns - the **FTS → LCM** lane. -3. **If the user rates a recalled fact → `tracedecay_fact_feedback`** - (`helpful` / `unhelpful`) to tune its trust score. -4. **Persist a new durable decision → `tracedecay_fact_store`** `action: "add"` - (`content`, `category`, `tags`, `trust`) proactively whenever a durable - decision, user preference, correction, or pitfall surfaces — do not wait for - the user to ask. The add path already rejects secrets and reports - near-duplicates/conflicts. - -Do NOT capture: secrets/credentials, transient errors, environment-specific -failures, one-off narratives, task progress, or soon-stale session outcomes — -recover those from transcripts via `tracedecay:recalling-session-context`. - -## Curate (mutation, requires approval) - -Destructive curation is a parent-agent responsibility. Use subagents only for -scoped inspection or recommendation work, with explicit project selectors and -non-overlapping ownership; do not delegate delete/apply/merge/retention actions -to subagents. Begin read-only, gather evidence, propose a mutation plan, then -write only narrow durable changes. - -1. **Resolve scope:** confirm the active project root/store before touching - memory. Project-bound profiles use the user-level TraceDecay store scoped to - the current project by default. -2. **Start read-mostly:** `tracedecay_fact_store` with `action: "get"`, - `"contradict"`, `"search"`, `"list"`, `"probe"`, `"related"`, or `"reason"`; - note that search/list/probe/related/reason may update retrieval/access - metadata. Use `tracedecay_memory_status` only when the user asks for memory - counts/health because it may repair vectors/banks. Use `tracedecay_dashboard` - (`action: "start"`) only when they want visual curation. -3. **Run native dry-run:** prefer `tracedecay memory curate` or - `POST /api/plugins/holographic/curate` with `{"dry_run": true}`. Dry-run is - the default and returns `actions`, `hygiene_candidates`, `counts`, - `coverage`, `provider`, and `mode`. -4. **Inventory candidates:** group facts into add, update, merge/dedupe, stale, - contradiction, secret-like, transient, supersession, and possible - hard-delete buckets. Keep fact ids, source/provenance, trust, tags, - entities, evidence links, and counterevidence with each candidate. -5. **Research gaps:** use TraceDecay graph/search plus LCM/session/message tools - to mine past sessions, raw messages, summary DAGs, branch/PR context, docs, - and tests. Scoped subagents may research bounded read-only questions only; - the parent agent is the sole memory writer and must review raw findings - before trusting them. -6. **Propose changes:** summarize durable additions, stale-fact updates, - trust/tag/source changes, dedupe merges, and delete candidates. Prefer - update/merge over removal when useful provenance should survive. -7. **Apply narrowly → `tracedecay_fact_store`** `action: "add"` / `"update"` / - `"remove"` for reviewed operations (or `POST - /api/plugins/holographic/curate/apply` / `tracedecay memory curate - --llm-ops --apply`). Require explicit approval immediately before - every `remove`, dashboard hard delete, or merge loser removal, showing fact - id, content/source summary, reason, and permanent-delete warning. -8. **Verify read-only:** re-run search/list/probe/related/contradict/get as - appropriate, inspect apply results/oplog when used, and report final facts - changed, skipped, or still needing human judgment. - -## Guardrails - -- `tracedecay_message_search` and `fact_store` search/get/contradict are - read-only recall. Search/list/probe/related/reason are read-mostly but can - update access/retrieval counters. `fact_store` add/update/remove, - `fact_feedback`, `memory_status` repair, and `dashboard` start/stop mutate - state or launch a local process; respect host approval/run-mode. -- Deletion is permanent: there is no archive, soft-delete, restore, or undo - path. Prefer update/merge when useful provenance should survive; delete only - approved stale, duplicate, wrong, secret-like, or user-requested facts. -- Never store secrets, credentials, API keys, or PII. Do not lower trust merely - because a fact is old; cite the newer evidence or contradiction. -- Dashboard curation can apply hard deletes. Use preview/dry-run first when - available and surface high-risk delete/merge operations before applying them. - `POST /api/plugins/holographic/curate` with `dry_run=false` applies - deterministic duplicate deletion; `POST /api/plugins/holographic/curate/apply` - applies explicit delete/merge ops. -- Do not let subagents call add/update/remove/feedback tools, apply curation - ops, start dashboard mutation flows, or run memory health repair. Ask them for - cited evidence, candidate facts, suspected duplicates, and stale/conflicting - claims, then perform parent-agent validation before writing. -- Hygiene candidates (`secret_like`, `transient`, `supersession`) are review - evidence, not deterministic apply operations. External LLM plans must use - strict JSON `{"ops": [...]}` and pass the TraceDecay evidence guard; rejected - low-confidence or out-of-scope ops must stay skipped. - -## Memorize a subject - -Use only when the user explicitly asks to memorize or remember a subject, code -area, branch, PR, or decision set. - -1. **Research read-only:** TraceDecay graph/search, LCM/session/message tools, - docs, existing fact searches, and relevant branch/PR context. -2. **Filter:** keep durable, scoped facts with citations. Reject secrets, - credentials, PII, large code blobs, transient branch state, and uncited - speculation. -3. **Calibrate trust:** `0.85+` for independently verified decisions, about - `0.7` for ordinary well-sourced facts, about `0.5` for plausible but - uncertain facts. Do not ask for approval solely because trust is low. -4. **Dedupe before writing:** search `tracedecay_fact_store` with the subject - plus candidate, matching category, `limit: 10`, `min_trust: 0.5`; skip - near-duplicates and ask before replacing contradictory facts. -5. **Store accepted facts → `tracedecay_fact_store`** `action: "add"` with - content, category, source, tags, entities, trust, and metadata containing - subject/confidence/citations. Act on `near_duplicate`, `possible_conflict`, - and `rejected_secret_like`; never rephrase a rejected secret to bypass - filtering. - -## Handoff - -- Raw session messages, scoped grep, or summary-DAG replay → - `tracedecay:recalling-session-context`. -- Index/server status without memory mutation → `tracedecay:code-health`. - -## Output - -- Recall: the relevant prior context/decisions/messages found, with source. -- Curate: facts searched/changed, confirmations requested, final verification - result, and any skipped high-risk candidates. -- If any result includes a `tracedecay_metrics:` line, report the savings to the - user. +``` +DURABLE FACTS LIVE IN fact_store, NOT IN MEMORY.md OR CLAUDE.md. +RECALL BEFORE RE-DERIVING; STORE WITHOUT BEING ASKED. +``` + +Announce: "Using tracedecay:project-memory to ." + +## Route the moment + +| Moment | Action | +|---|---| +| Need a prior decision/preference/pitfall | `tracedecay_fact_store` `action:"search"` (`query`, `min_trust`) — before web search, before asking the user | +| Prior conversations, not facts | `tracedecay_message_search` (`query`, `limit`) — this skill owns FTS→fact; raw replay/scoped grep → `tracedecay:managing-session-context` | +| A durable decision/correction/pitfall just surfaced | `tracedecay_fact_store` `action:"add"` (`content`, `category`, `tags`, `trust`) — proactively, do NOT wait to be asked, and do NOT write MEMORY.md instead | +| User rates a recalled fact | `tracedecay_fact_feedback` (`helpful`/`unhelpful`) | +| User asks to clean/merge/delete memory | Curation flow below | + +Trust calibration for adds: `0.85+` independently verified decisions, `~0.7` +ordinary well-sourced facts, `~0.5` plausible-but-uncertain. The add path +already rejects secrets and reports near-duplicates/conflicts — act on those +flags; never rephrase a rejected secret to bypass filtering. + +Do NOT capture: secrets/credentials/PII, transient errors, +environment-specific failures, one-off narratives, task progress, or +soon-stale session outcomes — those belong to session transcripts. + +## Curation (mutation — parent agent only, approval required) + +Read [references/curation.md](references/curation.md) for the full protocol: +read-mostly inventory → native dry-run (`tracedecay memory curate`) → +candidate buckets → narrow apply (`fact_store` add/update/remove) → read-only +verify (`tracedecay_memory_status` for counts/health). Hard rules that always +apply: + +- Deletion is permanent (no soft-delete, no undo). Explicit approval + immediately before every `remove`, hard delete, or merge-loser removal, + showing fact id, content summary, and reason. Prefer update/merge when + provenance should survive. +- Subagents may inspect and recommend only — never let a subagent call + add/update/remove/feedback, apply curation ops, or run memory repair. +- Do not lower trust merely for age; cite newer evidence or a contradiction. + +## If tools are deferred or MCP fails + +- Deferred: one ToolSearch call — + `select:tracedecay_fact_store,tracedecay_message_search,tracedecay_fact_feedback`. +- MCP error: `tracedecay tool fact_store --action search --query …` (see + `tracedecay:using-the-cli`). An MCP failure is not a reason to write + MEMORY.md — the CLI reaches the same store. + +## Deliverable + +Recall: the prior context/decisions found, with source and trust — or an +explicit "no stored fact matches", after which storing the fresh answer is the +default next step. Store: the fact id(s) written and any duplicate/conflict +flags handled. Curate: facts changed/skipped, approvals obtained, +verification result. Report any `tracedecay_metrics:` line. diff --git a/plugin/skills/project-memory/references/curation.md b/plugin/skills/project-memory/references/curation.md new file mode 100644 index 0000000000..058934ebbd --- /dev/null +++ b/plugin/skills/project-memory/references/curation.md @@ -0,0 +1,98 @@ +# Memory curation + +Full curation and memorize-a-subject protocol for `tracedecay:project-memory`. + +- Curate: read-mostly inventory, dry-run, narrow apply, verify +- Curation guardrails (deletion, subagents, dashboard) +- Memorize a subject on explicit request + +## Curate (mutation, requires approval) + +Destructive curation is a parent-agent responsibility. Use subagents only for +scoped inspection or recommendation work, with explicit project selectors and +non-overlapping ownership; do not delegate delete/apply/merge/retention actions +to subagents. Begin read-only, gather evidence, propose a mutation plan, then +write only narrow durable changes. + +1. **Resolve scope:** confirm the active project root/store before touching + memory. Project-bound profiles use the user-level TraceDecay store scoped to + the current project by default. +2. **Start read-mostly:** `tracedecay_fact_store` with `action: "get"`, + `"contradict"`, `"search"`, `"list"`, `"probe"`, `"related"`, or `"reason"`; + note that search/list/probe/related/reason may update retrieval/access + metadata. Use `tracedecay_memory_status` only when the user asks for memory + counts/health because it may repair vectors/banks. Use `tracedecay_dashboard` + (`action: "start"`) only when they want visual curation. +3. **Run native dry-run:** prefer `tracedecay memory curate` or + `POST /api/plugins/holographic/curate` with `{"dry_run": true}`. Dry-run is + the default and returns `actions`, `hygiene_candidates`, `counts`, + `coverage`, `provider`, and `mode`. +4. **Inventory candidates:** group facts into add, update, merge/dedupe, stale, + contradiction, secret-like, transient, supersession, and possible + hard-delete buckets. Keep fact ids, source/provenance, trust, tags, + entities, evidence links, and counterevidence with each candidate. +5. **Research gaps:** use TraceDecay graph/search plus LCM/session/message tools + to mine past sessions, raw messages, summary DAGs, branch/PR context, docs, + and tests. Scoped subagents may research bounded read-only questions only; + the parent agent is the sole memory writer and must review raw findings + before trusting them. +6. **Propose changes:** summarize durable additions, stale-fact updates, + trust/tag/source changes, dedupe merges, and delete candidates. Prefer + update/merge over removal when useful provenance should survive. +7. **Apply narrowly → `tracedecay_fact_store`** `action: "add"` / `"update"` / + `"remove"` for reviewed operations (or `POST + /api/plugins/holographic/curate/apply` / `tracedecay memory curate + --llm-ops --apply`). Require explicit approval immediately before + every `remove`, dashboard hard delete, or merge loser removal, showing fact + id, content/source summary, reason, and permanent-delete warning. +8. **Verify read-only:** re-run search/list/probe/related/contradict/get as + appropriate, inspect apply results/oplog when used, and report final facts + changed, skipped, or still needing human judgment. + +## Curation guardrails + +- `tracedecay_message_search` and `fact_store` search/get/contradict are + read-only recall. Search/list/probe/related/reason are read-mostly but can + update access/retrieval counters. `fact_store` add/update/remove, + `fact_feedback`, `memory_status` repair, and `dashboard` start/stop mutate + state or launch a local process; respect host approval/run-mode. +- Deletion is permanent: there is no archive, soft-delete, restore, or undo + path. Prefer update/merge when useful provenance should survive; delete only + approved stale, duplicate, wrong, secret-like, or user-requested facts. +- Never store secrets, credentials, API keys, or PII. Do not lower trust merely + because a fact is old; cite the newer evidence or contradiction. +- Dashboard curation can apply hard deletes. Use preview/dry-run first when + available and surface high-risk delete/merge operations before applying them. + `POST /api/plugins/holographic/curate` with `dry_run=false` applies + deterministic duplicate deletion; `POST /api/plugins/holographic/curate/apply` + applies explicit delete/merge ops. +- Do not let subagents call add/update/remove/feedback tools, apply curation + ops, start dashboard mutation flows, or run memory health repair. Ask them for + cited evidence, candidate facts, suspected duplicates, and stale/conflicting + claims, then perform parent-agent validation before writing. +- Hygiene candidates (`secret_like`, `transient`, `supersession`) are review + evidence, not deterministic apply operations. External LLM plans must use + strict JSON `{"ops": [...]}` and pass the TraceDecay evidence guard; rejected + low-confidence or out-of-scope ops must stay skipped. + +## Memorize a subject + +Use only when the user explicitly asks to memorize or remember a subject, code +area, branch, PR, or decision set. + +1. **Research read-only:** TraceDecay graph/search, LCM/session/message tools, + docs, existing fact searches, and relevant branch/PR context. +2. **Filter:** keep durable, scoped facts with citations. Reject secrets, + credentials, PII, large code blobs, transient branch state, and uncited + speculation. +3. **Calibrate trust:** `0.85+` for independently verified decisions, about + `0.7` for ordinary well-sourced facts, about `0.5` for plausible but + uncertain facts. Do not ask for approval solely because trust is low. +4. **Dedupe before writing:** search `tracedecay_fact_store` with the subject + plus candidate, matching category, `limit: 10`, `min_trust: 0.5`; skip + near-duplicates and ask before replacing contradictory facts. +5. **Store accepted facts → `tracedecay_fact_store`** `action: "add"` with + content, category, source, tags, entities, trust, and metadata containing + subject/confidence/citations. Act on `near_duplicate`, `possible_conflict`, + and `rejected_secret_like`; never rephrase a rejected secret to bypass + filtering. diff --git a/plugin/skills/recalling-session-context/SKILL.md b/plugin/skills/recalling-session-context/SKILL.md deleted file mode 100644 index 6959b1371e..0000000000 --- a/plugin/skills/recalling-session-context/SKILL.md +++ /dev/null @@ -1,35 +0,0 @@ ---- -name: recalling-session-context -description: 'Use when retrieving what happened in past agent sessions: full-text transcript recall, scoped/time-filtered grep, lossless session replay, summary-DAG drill-down, or compaction recovery.' ---- - -# Recalling session context - -Climb this ladder cheapest-first; stop as soon as the question is answered. For durable *decisions and facts* (rather than raw conversation), start with `tracedecay:project-memory` instead. - -This skill owns the **FTS → LCM** lane of `tracedecay_message_search`: `message_search` is the entry point into raw-message grep, lossless replay, and summary-DAG drill-down (the ladder below). When `message_search` is instead the entry point into durable *facts*, that is `tracedecay:project-memory`'s FTS → fact lane. - -## Retrieval ladder - -1. **Fast full-text recall → `tracedecay_message_search`** (`query`, optional `provider`, `scope`: `all`|`parents_only`|`subagents_only`, `limit`): FTS over ingested transcripts; returns messages with their session ids — the entry point for the LCM ladder below. -2. **Scoped/filtered grep → `tracedecay_lcm_grep`** (`query`, `scope`: `current`|`session`|`all` — `current`/`session` require `session_id`; `role`, `source`, `start_time`/`end_time`, `sort`: `recency`|`relevance`|`hybrid`): bounded raw-message snippets plus summary text when FTS recall needs role/time/session precision. -3. **Lossless replay → `tracedecay_lcm_load_session`** (`session_id`, `after_store_id` + `limit` for stable pagination, `roles`, `content_offset`/`content_limit`): ordered raw messages of one session; page with `next_cursor` instead of asking for everything at once. -4. **Summary-DAG drill-down:** `tracedecay_lcm_describe` (`session_id`) for the session's raw/summary shape; `tracedecay_lcm_expand` (`target.kind`: `raw_message`|`summary_node`|`external_payload`) to open one node, paging sources via `source_offset`/`source_limit`; `tracedecay_lcm_expand_query` (`query`) to assemble bounded retrieval context for a prompt in one call. -5. **Store inspection → `tracedecay_lcm_status`** (counts, token estimates, DAG depth/compression ratio) when you need to know what the store contains before searching it. -6. **Git-scoped session lookup → `tracedecay_sessions_for`** (`git_ref`: `branch`|`worktree`|`commit`, `value`, optional `since`/`until`, `limit`): which sessions were active on a branch or in a worktree, or which conversations produced a commit; feed the returned session ids back into rungs 2–4. - -## Guardrails - -- Steps 1–6 are read-only. `tracedecay_lcm_compress`, `tracedecay_lcm_preflight`, and `tracedecay_lcm_session_boundary` are **lifecycle-integration tools for host agents** — never invoke them casually during recall. -- For multi-step recall, dispatch scoped read-only subagents by session id, time window, provider, role, or query variant. Subagents must not call lifecycle or repair tools; the parent agent validates cited messages/summaries and produces the final timeline. -- If the LCM store itself looks wrong (missing sessions, broken FTS, stale counts) → `tracedecay_lcm_doctor` (`mode: "diagnose"` first; `repair`/`clean` mutate and need explicit user intent). -- All LCM tools default to `storage_scope: "project_local"`; only pass `hermes_profile` (with an absolute `hermes_home`) when the user asks about a Hermes profile store. - -## Handoff - -- Durable decisions/facts and persisting new ones → `tracedecay:project-memory`. - -## Output - -- The recalled messages/summaries with session ids and timestamps, and which rung answered the question. -- If any result includes a `tracedecay_metrics:` line, report the savings to the user. diff --git a/plugin/skills/retrieving-cached-context/SKILL.md b/plugin/skills/retrieving-cached-context/SKILL.md deleted file mode 100644 index ac20655ceb..0000000000 --- a/plugin/skills/retrieving-cached-context/SKILL.md +++ /dev/null @@ -1,60 +0,0 @@ ---- -name: retrieving-cached-context -description: 'Use when a tracedecay response was truncated with a handle and the missing detail is needed — dereference the cached original with tracedecay_retrieve instead of re-running, or expand one LCM node.' ---- - -# Retrieving cached context - -TraceDecay truncates large tool responses and emits a **handle** envelope -instead of the full body. The original text is cached in the active-project -store; you dereference it with `tracedecay_retrieve` rather than re-running the -source tool. This skill covers that handle/caching mechanic and the related -single-node expansion via `tracedecay_lcm_expand`. - -## When to retrieve vs re-run - -- A prior response ended with a `handle` (e.g. `rh_…`) and the missing details - are actually needed to answer the user → **retrieve the handle**. Do not - re-run the broad query, guess, or read a file again. -- You do NOT need the truncated tail → leave it; retrieval costs tokens. -- The result was truncated because the query was too broad → also consider - narrowing the original query next time (see `tracedecay:using-tracedecay`). - -## Tools - -1. **Dereference a handle → `tracedecay_retrieve`** (`handle` required, copied - exactly from the truncated envelope). It returns the **exact cached original - text** — it does not re-run the source tool or re-read a file/session/node. - Handles are scoped to the active project store, expire automatically, and - never reference remote storage. If the truncated response used a - `project-id`/`project-path` selector, pass the same selector to `retrieve`. -2. **Expand one LCM node → `tracedecay_lcm_expand`** (`provider`, `session-id`, - `target` with `kind`: `raw_message`|`summary_node`|`external_payload`): - opens a single session node through the bounded LCM query API. Page a summary - node's sources with `source-offset`/`source-limit`, and page long content - with `content-offset`/`content-limit`. If a returned source has - `content_truncated: true`, continue via `target.kind: "raw_message"` for that - source's `store_id` and `content_offset`. - -## Guardrails - -- Both tools are **read-only**; they surface already-cached content and never - mutate state. -- Retrieve only what you need — handles and node expansion are bounded on - purpose; do not dump the full cached body when a slice answers the question. -- Handles expire; if `retrieve` reports an expired/unknown handle, re-run the - original tool with a narrower query rather than retrying the stale handle. -- Handles are local and project-scoped — never treat them as durable - references to store or reuse across sessions. - -## Handoff - -- Finding which session node to expand (grep, replay, summary-DAG shape) → `tracedecay:recalling-session-context`. -- Driving compression that produces those summary nodes → `tracedecay:managing-session-context`. -- General "narrow the query instead of re-running" guidance → `tracedecay:using-tracedecay`. - -## Output - -- The retrieved cached text or expanded node content, and a note that it came - from a handle/cache rather than a fresh query. -- If any result includes a `tracedecay_metrics:` line, report the savings to the user. diff --git a/plugin/skills/retrieving-project-memory/SKILL.md b/plugin/skills/retrieving-project-memory/SKILL.md deleted file mode 100644 index 93630716f7..0000000000 --- a/plugin/skills/retrieving-project-memory/SKILL.md +++ /dev/null @@ -1,68 +0,0 @@ ---- -name: retrieving-project-memory -description: 'Use when querying or reasoning over stored tracedecay memory facts — searching, probing by entity, multi-fact reasoning, or fetching a fact with trust history. For recall framing see project-memory.' ---- - -# Retrieving project memory - -This skill owns the **read/reason mechanics** of the holographic fact store: -the exact `tracedecay_fact_store` retrieval actions plus -`tracedecay_memory_status`. It is the mechanical counterpart to -`tracedecay:project-memory` (which frames memory recall around a task -or decision and starts from transcripts). When the question is "what does the -fact store know about X and how do the facts relate," start here. - -## Retrieval actions (`tracedecay_fact_store`) - -All read-mostly; they may update access/retrieval metadata but do not add or -delete facts. Read-only project selectors (`project-id` / `project-path`) are -supported for these actions. - -1. **search** (`query`, optional `category`, `limit` default 20 / max 200, - `min_trust`) — phase-vector similarity search; the default entry point for - "find facts about X." -2. **probe** (`entity` / `query`) — probe memory around a single named entity. -3. **related** (`entity`) — facts connected to an entity via stored relations. -4. **reason** (`query`, `entities`) — assemble and reason over multiple facts - for a query, following entity relations rather than returning a flat list. -5. **get** (`fact-id`) — the full fact plus its `trust_history`, so you can - answer *why* a trust score is what it is. -6. **list** (optional `category`, `min_trust`, `limit`) — enumerate stored - facts for review. -7. **contradict** (`threshold`) — scan for contradictory facts; non-destructive. - -## Memory health - -- **`tracedecay_memory_status`** — fact/entity counts, trust distribution, - below-threshold and missing-vector signals, capacity-per-bank, and repair - stats. Note it **repairs** derived vectors/banks as a side effect, so call it - when the user asks for memory counts/health, not on every recall. - -## How to query and reason - -- Prefer `search`/`probe` to locate candidates, then `reason` (or `related`) - when the answer spans several linked facts. -- Use `min_trust` to filter out low-confidence facts; use `get` on a specific - `fact-id` when the user challenges a fact or asks why its trust changed. -- Keep retrieval bounded and token-aware: set `limit` deliberately and narrow - `query`/`category` rather than pulling the whole store with a broad `list`. - -## Guardrails - -- search/probe/related/reason/get/list/contradict are read-only recall (they - may touch access counters); they never mutate fact content. -- `tracedecay_memory_status` mutates derived state (vector/bank repair) — treat - it as a health action, not a passive read. -- Recall memory before external or web search — a prior session likely already - answered the question, cheaper and project-specific. - -## Handoff - -- Task/decision recall that should start from transcripts, or fixing stale/contradictory/duplicate facts → `tracedecay:project-memory`. -- Persisting a new durable fact → `tracedecay:storing-project-memory`. - -## Output - -- The facts found/reasoned over with their ids, trust, and source, plus which - action answered the question. -- If any result includes a `tracedecay_metrics:` line, report the savings to the user. diff --git a/plugin/skills/reviewing-changes/SKILL.md b/plugin/skills/reviewing-changes/SKILL.md index 63ac8c5640..227bad241a 100644 --- a/plugin/skills/reviewing-changes/SKILL.md +++ b/plugin/skills/reviewing-changes/SKILL.md @@ -1,84 +1,73 @@ --- name: reviewing-changes -description: 'Use when reviewing a PR, branch, or working-tree diff, auditing ship-blocking risk (panic/unsafe/todo sites, dead code, untested hotspots), cleaning up dead or duplicate code, or drafting commit messages, PR descriptions, and changelogs from semantic diff context.' +description: 'Use when reviewing a PR, branch, or working-tree diff, auditing ship risk (panic/unsafe/todo, dead code, untested hotspots), or drafting commit/PR/changelog text. Trigger before "gh pr diff" — pr_context/diff_context compute changed symbols offline. Do NOT use for pure test selection (tracedecay:assessing-impact).' --- # Reviewing changes +``` +NO REVIEW BY READING RAW DIFFS. Semantic change context comes first: +diff_context for the working tree, pr_context for ref-to-ref — both offline. +``` + +Announce: "Using tracedecay:reviewing-changes for ." + ## Diff review -1. **Get changed files** — working tree, or `git diff --name-only - ...HEAD` (default base `main`). -2. **Semantic change summary:** working tree / file list → - `tracedecay_diff_context` (`files`): modified symbols + dependents + - affected tests; ref-to-ref PR → `tracedecay_pr_context` (`base_ref`, - `head_ref`). -3. **Go deeper only if needed:** `tracedecay_impact` (`node_id`) to widen the - blast radius on a high-risk changed symbol; `tracedecay_affected` - (`files`) only when step 2's test set is not enough. -4. **Quality scan of just the changed files → `tracedecay_simplify_scan`** - (`files`): duplications, dead code, coupling, complexity hotspots. -5. **Risk surfacing:** `tracedecay_test_risk` on changed paths; - `tracedecay_unsafe_patterns` on changed files. - -## Safety audit (ship-readiness sweep) - -1. **Panic & unsafe sites → `tracedecay_unsafe_patterns`** (`kinds?` to - narrow to `unwrap`/`unsafe`, `exclude_tests: true` for production-only, - `path?`): each hit carries file, line, kind, enclosing symbol, `in_test`. -2. **Unfinished work → `tracedecay_todos`** (`kinds: - ["FIXME","HACK","XXX","UNIMPLEMENTED"]`). -3. **Unreachable code → `tracedecay_dead_code`** (`include_public: true` for - workspace-internal audits) and **`tracedecay_unused_imports`**. -4. **Risky and untested → `tracedecay_test_risk`**: high-complexity, - high-fan-in symbols with weak coverage. -5. **Rank:** production panic/unsafe in hot paths first (cross-check fan-in - with `tracedecay_callers`), then UNIMPLEMENTED/HACK markers, then untested - high-risk symbols, then dead code and imports. - -## Dead-code cleanup - -1. Discover with `tracedecay_dead_code` / `tracedecay_unused_imports` / - `tracedecay_redundancy`; focused pass → `tracedecay_simplify_scan` (`files`). -2. **Before deleting anything → confirm zero real callers** with - `tracedecay_callers` / `tracedecay_rename_preview`. Be conservative with - `pub` items (they may be used outside the indexed scope). Never delete a - symbol whose callers/references are non-empty. -3. Apply edits via `tracedecay:editing-safely`; verify with - `tracedecay_diagnostics` and the affected tests - (`tracedecay:assessing-impact`). Optionally bracket the cleanup with the - session-health delta in `tracedecay:code-health`. +1. Changed files: working tree as-is, or `git diff --name-only ...HEAD`. +2. Semantic summary — one call: + - Working tree / file list → `tracedecay_diff_context` (`files`). + - PR / ref-to-ref → `tracedecay_pr_context` (`base_ref`, `head_ref`). + Both return modified symbols + dependents + affected tests from the local + graph. Use `gh` only for review comments/CI status — never for the diff + analysis itself. +3. Deepen only where needed: `tracedecay_impact` on a high-risk changed + symbol; `tracedecay_affected` only if step 2's test set is insufficient. +4. Quality scan of just the changed files → `tracedecay_simplify_scan`. +5. Risk: `tracedecay_test_risk` on changed paths; `tracedecay_unsafe_patterns` + on changed files (`exclude_tests: true` for production-only — unwrap/panic + in tests is normal, and an `unsafe { }` block is an attention site, not + automatically a finding). + +## Safety audit (ship-readiness) and dead-code cleanup + +Read [references/safety-audit.md](references/safety-audit.md) for the full +sweep (`tracedecay_unsafe_patterns` → `tracedecay_todos` → +`tracedecay_dead_code`/`tracedecay_unused_imports` → `tracedecay_test_risk` → +ranking) and the delete-safely protocol (zero-caller confirmation with +`tracedecay_callers` before any deletion; conservative on `pub`). ## Drafting commit & PR text -1. **Commit message → `tracedecay_commit_context`** (`staged_only`): changed - symbols + file roles + recent commit style. -2. **PR description → `tracedecay_pr_context`** (`base_ref`, `head_ref`): - Summary / Impact / Tests. -3. **Release notes → `tracedecay_changelog`** (`from_ref`, `to_ref`); - sanity-check with `tracedecay_branch_diff`. -4. Drafts text only — leave `git commit` / `gh pr create` to the user or a - dedicated git workflow. - -## Guardrails - -- Review and audit are read-only; do not edit or run tests from those flows — - hand edits to `tracedecay:editing-safely` and verification to - `tracedecay:assessing-impact`. -- `unwrap`/`panic!` inside tests is normal — respect `exclude_tests` / - `in_test` before flagging. An `unsafe { }` block is a review-attention - site, not automatically a finding to "fix". -- For large diffs, use scoped read-only subagents by file group or risk - category; require cited findings — the parent agent owns severity, - deduplication, and the final call. -- If diff context is truncated with a `handle`, narrow by file/symbol first; - call `tracedecay_retrieve` only when the omitted risk detail is needed. - -## Output - -- Findings grouped **Critical / Warning / Note** with file + enclosing - symbol, the impacted areas and test set, removed/consolidated items, or the - drafted commit/PR/changelog text. Pairs with the `pr-review-canvas` plugin - if installed. -- If any result includes a `tracedecay_metrics:` line, report the savings to - the user. +| Deliverable | Call | +|---|---| +| Commit message | `tracedecay_commit_context` (`staged_only`) — changed symbols + recent commit style | +| PR description | `tracedecay_pr_context` → Summary / Impact / Tests | +| Release notes | `tracedecay_changelog` (`from_ref`, `to_ref`); sanity-check `tracedecay_branch_diff` | + +Drafts text only — `git commit` / `gh pr create` stay with the user. + +## Rules + +- Review and audit are read-only. Edits → `tracedecay:editing-safely`; + verification → `tracedecay:assessing-impact`. +- Large diffs: scoped read-only subagents per file group or risk category with + cited findings; the parent owns severity and dedup. +- Truncated with a `handle`? Narrow by file/symbol first; `tracedecay_retrieve` + only when the omitted risk detail is needed. + +## If tools are deferred or MCP fails + +- Deferred: one ToolSearch call — + `select:tracedecay_diff_context,tracedecay_pr_context,tracedecay_simplify_scan,tracedecay_unsafe_patterns,tracedecay_test_risk`. +- MCP error: `tracedecay tool pr_context --base-ref main --head-ref HEAD` etc. + (see `tracedecay:using-the-cli`). gh being unauthenticated or offline is NOT + a blocker — pr_context/diff_context never touch the network. + +## Deliverable + +Do not end without findings grouped Critical / Warning / Note, each with file ++ enclosing symbol, plus the impacted areas and test set — or the drafted +commit/PR/changelog text when that was the ask. An empty review must state +what was scanned (tools + scope), not just "looks good". Report any +`tracedecay_metrics:` line. diff --git a/plugin/skills/reviewing-changes/references/safety-audit.md b/plugin/skills/reviewing-changes/references/safety-audit.md new file mode 100644 index 0000000000..a982ca2a91 --- /dev/null +++ b/plugin/skills/reviewing-changes/references/safety-audit.md @@ -0,0 +1,35 @@ +# Safety audit & dead-code cleanup + +Ship-readiness sweep and delete-safely protocol for `tracedecay:reviewing-changes`. + +- Panic/unsafe, TODO, dead-code, and untested-risk discovery +- Ranking ship-blockers +- Zero-caller confirmation before deleting + +## Safety audit (ship-readiness sweep) + +1. **Panic & unsafe sites → `tracedecay_unsafe_patterns`** (`kinds?` to + narrow to `unwrap`/`unsafe`, `exclude_tests: true` for production-only, + `path?`): each hit carries file, line, kind, enclosing symbol, `in_test`. +2. **Unfinished work → `tracedecay_todos`** (`kinds: + ["FIXME","HACK","XXX","UNIMPLEMENTED"]`). +3. **Unreachable code → `tracedecay_dead_code`** (`include_public: true` for + workspace-internal audits) and **`tracedecay_unused_imports`**. +4. **Risky and untested → `tracedecay_test_risk`**: high-complexity, + high-fan-in symbols with weak coverage. +5. **Rank:** production panic/unsafe in hot paths first (cross-check fan-in + with `tracedecay_callers`), then UNIMPLEMENTED/HACK markers, then untested + high-risk symbols, then dead code and imports. + +## Dead-code cleanup + +1. Discover with `tracedecay_dead_code` / `tracedecay_unused_imports` / + `tracedecay_redundancy`; focused pass → `tracedecay_simplify_scan` (`files`). +2. **Before deleting anything → confirm zero real callers** with + `tracedecay_callers` / `tracedecay_rename_preview`. Be conservative with + `pub` items (they may be used outside the indexed scope). Never delete a + symbol whose callers/references are non-empty. +3. Apply edits via `tracedecay:editing-safely`; verify with + `tracedecay_diagnostics` and the affected tests + (`tracedecay:assessing-impact`). Optionally bracket the cleanup with the + session-health delta in `tracedecay:code-health`. diff --git a/plugin/skills/storing-project-memory/SKILL.md b/plugin/skills/storing-project-memory/SKILL.md deleted file mode 100644 index 4e79520ff2..0000000000 --- a/plugin/skills/storing-project-memory/SKILL.md +++ /dev/null @@ -1,82 +0,0 @@ ---- -name: storing-project-memory -description: 'Use when writing a durable fact to tracedecay memory — persisting a decision, preference, correction, pitfall, or entity relation, and handling near-duplicate/conflict/secret write diffs. For cleanup see project-memory.' ---- - -# Storing project memory - -This skill owns the **write path** into holographic memory: turning a durable -decision or fact into a stored `tracedecay_fact_store` record. It is the -narrow "add/update/relate" counterpart to `tracedecay:project-memory` -(dedup, merge, delete, whole-subject memorization) and -`tracedecay:retrieving-project-memory` (read/reason). Store proactively -whenever a durable decision, user preference, correction, or pitfall surfaces — -do not wait for the user to ask. - -## When to store vs not - -Store only **durable, project-scoped** facts: - -- Store: architectural/design decisions, user or project preferences, hard-won - corrections, recurring pitfalls, stable conventions, entity relationships. -- Do NOT store: secrets/credentials/API keys/PII, transient errors, - environment-specific failures, task progress, one-off narratives, or anything - that goes stale when the session ends — recover those from transcripts via - `tracedecay:recalling-session-context` instead. - -## Workflow - -1. **Dedupe first (read-only):** search before you write with - `tracedecay_fact_store` `action: "search"` (`query` = subject + candidate, - optional `category`, `limit: 10`, `min_trust: 0.5`). If a near-match exists, - prefer an update over a second add. -2. **Add a fact → `tracedecay_fact_store`** `action: "add"` with `content` - (the durable claim), `category`, `source` (provenance label), `tags`, - `entities` (named entities the fact concerns), `trust`, and optional - `metadata` (subject/confidence/citations). The add result carries a - write-time diff — always read it (see below). -3. **Update an existing fact → `tracedecay_fact_store`** `action: "update"` - with `fact-id` plus the changed `content`/`trust`/`tags`/`category`. Prefer - update when correcting or refining a fact so provenance survives. -4. **Relate entities → `tracedecay_fact_store`** `action: "relate"` (with - `entities` / `entity`) to record a relationship between named entities the - facts concern. -5. **Calibrate trust deliberately** — do not default high. Aim for a spread: - `>=0.85` for independently verified/durable decisions, `~0.7` for ordinary - well-sourced facts, `~0.5` for plausible-but-unsure. Do not lower trust - merely because a fact is old; cite newer evidence instead. - -## Reading the add diff - -Every `action: "add"` returns `diff` / `closest_fact_id` / `similarity` / -`reason`. Act on it, never ignore it: - -- `near_duplicate` — a very similar fact exists; prefer `action: "update"` on - `closest_fact_id` rather than storing a second copy. -- `possible_conflict` — a negation/state-change cue suggests supersession; - confirm which fact is current before leaving both in place (hand off to - `tracedecay:project-memory` if a merge/delete is needed). -- `rejected_secret_like` — credential-like content was **NOT** stored. Never - rephrase or obfuscate a rejected secret to bypass the filter. - -## Guardrails - -- `search` is read-only; `add`, `update`, and `relate` **mutate** memory state. - `search`/`probe`/`related`/`reason` may update access/retrieval counters. -- Deletion is permanent and lives in `tracedecay:project-memory`, not - here — prefer update/relate over creating removable clutter. -- Never store secrets, credentials, keys, or PII; rely on the built-in - `rejected_secret_like` filter as a backstop, not a first line. -- Only the parent agent should call `add`/`update`/`relate`. Subagents may - gather cited evidence and candidate facts; the parent validates and writes. - -## Handoff - -- Dedup, merge, delete, or memorize a whole subject → `tracedecay:project-memory`. -- Read, probe, or reason over stored facts → `tracedecay:retrieving-project-memory`. - -## Output - -- The fact(s) stored/updated with their ids, the trust assigned, and any - `near_duplicate` / `possible_conflict` / `rejected_secret_like` diff acted on. -- If any result includes a `tracedecay_metrics:` line, report the savings to the user. diff --git a/plugin/skills/tracedecay-audit-safety/SKILL.md b/plugin/skills/tracedecay-audit-safety/SKILL.md deleted file mode 100644 index 4b2d2aac21..0000000000 --- a/plugin/skills/tracedecay-audit-safety/SKILL.md +++ /dev/null @@ -1,15 +0,0 @@ ---- -name: tracedecay-audit-safety -description: 'Use to audit the repo or a directory for ship-blocking risk, panic sites, risk markers, dead code, and untested high-risk symbols.' ---- - -# Audit safety - -Use for repo or directory audits covering ship-blocking risk, panic sites, risk markers, dead code, or untested high-risk symbols. - -Use `tracedecay:reviewing-changes`. - -- **Scope:** the whole repo, or a specific directory if one is named. -- Read-only: report findings, do not fix them here. - -Output: findings grouped Critical / Warning / Note with file + enclosing symbol, and a prioritized follow-up list. diff --git a/plugin/skills/tracedecay-check-health/SKILL.md b/plugin/skills/tracedecay-check-health/SKILL.md deleted file mode 100644 index 7d8c7b3d4c..0000000000 --- a/plugin/skills/tracedecay-check-health/SKILL.md +++ /dev/null @@ -1,15 +0,0 @@ ---- -name: tracedecay-check-health -description: 'Use to check code health for the repo or a directory, including worst offenders and a prioritized fix list.' ---- - -# Check health - -Use for repo or directory code-health checks, worst offenders, and prioritized fix lists. - -Use `tracedecay:code-health`. - -- **Scope:** the whole repo, or a specific directory if one is named. -- Read-only: lead with `tracedecay_health` and drill only into weak dimensions. - -Output: the composite health score + weak dimensions, the worst offenders (complexity, duplication, god files, doc gaps, panic sites, test-risk), and a prioritized fix list. diff --git a/plugin/skills/tracedecay-clean-dead-code/SKILL.md b/plugin/skills/tracedecay-clean-dead-code/SKILL.md deleted file mode 100644 index 0aa0418155..0000000000 --- a/plugin/skills/tracedecay-clean-dead-code/SKILL.md +++ /dev/null @@ -1,15 +0,0 @@ ---- -name: tracedecay-clean-dead-code -description: 'Use to find and safely remove dead code, unused imports, and duplication via the TraceDecay code graph.' ---- - -# Clean dead code - -Use to find and safely remove dead code, unused imports, or duplication. - -Use `tracedecay:reviewing-changes` to identify candidates, then `tracedecay:editing-safely` for removals. - -- **Scope:** the whole repo, or a specific directory if one is named. -- Confirm zero real callers before deleting anything; be conservative with `pub` items; verify with a build/test re-check after edits. - -Output: removed/consolidated items and the before/after health or test result. diff --git a/plugin/skills/tracedecay-compare-branches/SKILL.md b/plugin/skills/tracedecay-compare-branches/SKILL.md deleted file mode 100644 index 3f5826bc67..0000000000 --- a/plugin/skills/tracedecay-compare-branches/SKILL.md +++ /dev/null @@ -1,15 +0,0 @@ ---- -name: tracedecay-compare-branches -description: 'Use to compare or search another git branch''s code graph without switching your checkout.' ---- - -# Compare branches - -Use to compare or search another git branch's code graph without switching your checkout. - -Use `tracedecay:exploring-code` with `tracedecay_branch_list`, `tracedecay_branch_diff`, and `tracedecay_branch_search`. - -- **Target:** a single branch to compare against the current branch, or " " to diff two branches. If none is given, start with `tracedecay_branch_list` and ask what to search or compare. -- Read-only. If a target branch isn't tracked, tell the user to run `tracedecay branch add ` first, and surface any branch-fallback warning. - -Output: the cross-branch search hits or the added/removed/changed symbol lists. diff --git a/plugin/skills/tracedecay-curate-memory/SKILL.md b/plugin/skills/tracedecay-curate-memory/SKILL.md deleted file mode 100644 index 96bf9f2427..0000000000 --- a/plugin/skills/tracedecay-curate-memory/SKILL.md +++ /dev/null @@ -1,16 +0,0 @@ ---- -name: tracedecay-curate-memory -description: 'Use to curate, update, delete, or inspect TraceDecay memory facts and dashboard curation from an explicit slash workflow.' ---- - -# Curate memory - -Use to curate, update, delete, or inspect TraceDecay memory facts, or to do dashboard curation. - -Use `tracedecay:project-memory`. - -- **Scope:** the fact, entity, query, or curation action to review. If none is given, ask what memory scope to curate before mutating anything. -- Start read-only with `tracedecay_fact_store` search/list/probe/reason/contradict or `tracedecay_memory_status`; open `tracedecay_dashboard` only when the user wants visual curation. -- Follow the hard-delete guardrail: confirm fact ids and reasons before `remove` unless the user already gave an exact deletion instruction. - -Output: memory facts inspected or changed, confirmations requested, and the final verification search/list result. diff --git a/plugin/skills/tracedecay-draft-commit/SKILL.md b/plugin/skills/tracedecay-draft-commit/SKILL.md deleted file mode 100644 index f25a53a851..0000000000 --- a/plugin/skills/tracedecay-draft-commit/SKILL.md +++ /dev/null @@ -1,15 +0,0 @@ ---- -name: tracedecay-draft-commit -description: 'Use to draft a commit message, PR description, or changelog from semantic changes; drafts text only and never commits or pushes.' ---- - -# Draft commit - -Use to draft a commit message, PR description, or changelog from current semantic changes. - -Use `tracedecay:reviewing-changes` to read the diff and impact. - -- **Target:** the artifact to draft (e.g. "pr", "changelog", a base ref, or "staged"). If none is given, draft a commit message for the working-tree/staged changes. -- Draft text only — leave `git commit` / `gh pr create` to the user unless they explicitly ask. - -Output: the drafted commit / PR / changelog text. diff --git a/plugin/skills/tracedecay-find-impact/SKILL.md b/plugin/skills/tracedecay-find-impact/SKILL.md deleted file mode 100644 index 68300a3395..0000000000 --- a/plugin/skills/tracedecay-find-impact/SKILL.md +++ /dev/null @@ -1,15 +0,0 @@ ---- -name: tracedecay-find-impact -description: 'Use to find the blast radius of a change, including impacted symbols, files, and the tests to run.' ---- - -# Find impact - -Use to find a change's blast radius: impacted symbols, files, and tests to run. - -Use `tracedecay:assessing-impact`. - -- **Target:** the symbol, file, or change to analyze. If none is given, use the current working-tree diff. -- Read-only: shallow `max_depth` first. Identify impact; do not run tests. - -Output: impacted symbols + files, the test set to run, and any hub/coupling risk. diff --git a/plugin/skills/tracedecay-fix-build/SKILL.md b/plugin/skills/tracedecay-fix-build/SKILL.md deleted file mode 100644 index 688e42279b..0000000000 --- a/plugin/skills/tracedecay-fix-build/SKILL.md +++ /dev/null @@ -1,15 +0,0 @@ ---- -name: tracedecay-fix-build -description: 'Use to fix build and type errors by running or parsing diagnostics, mapping them to symbols with callers, then fixing.' ---- - -# Fix build - -Use to fix build and type errors by running or parsing diagnostics, mapping them to symbols with callers, then fixing. - -Use `tracedecay:fixing-build-and-type-errors`. - -- **Input:** if the user pasted `cargo`/`clippy` output, route it to `tracedecay_diagnose`; otherwise run `tracedecay_diagnostics` (scoped to a directory if one is named). -- Prefer pasted output when available. `tracedecay_diagnostics` runs the toolchain, so confirm before long checks. - -Output: grouped diagnostics with enclosing symbols + callers, the applied fix, and a clean re-check. diff --git a/plugin/skills/tracedecay-map-architecture/SKILL.md b/plugin/skills/tracedecay-map-architecture/SKILL.md deleted file mode 100644 index 0a5a49a87d..0000000000 --- a/plugin/skills/tracedecay-map-architecture/SKILL.md +++ /dev/null @@ -1,15 +0,0 @@ ---- -name: tracedecay-map-architecture -description: 'Use to map repo or directory architecture, including layered modules, dependency hotspots, and structural risks.' ---- - -# Map architecture - -Use to map repo or directory architecture: layered modules, dependency hotspots, and structural risks. - -Use `tracedecay:exploring-code` for structure and `tracedecay:code-health` for dependency hotspots and structural risk. - -- **Scope:** the whole repo, or a specific directory if one is named. -- Read-only. - -Output: a layered module map, dependency hotspots/violations, and a prioritized risk list. diff --git a/plugin/skills/tracedecay-port-code/SKILL.md b/plugin/skills/tracedecay-port-code/SKILL.md deleted file mode 100644 index c95923fdc3..0000000000 --- a/plugin/skills/tracedecay-port-code/SKILL.md +++ /dev/null @@ -1,15 +0,0 @@ ---- -name: tracedecay-port-code -description: 'Use to port or migrate code between directories in dependency-safe order and track progress.' ---- - -# Port code - -Use to port or migrate code between directories in dependency-safe order and track progress. - -Use `tracedecay:editing-safely` with `tracedecay_port_order` and `tracedecay_port_status`. - -- **Args:** " ". If absent, ask for the source and target directories. -- Port leaves first. Confirm before edits and toolchain runs. - -Output: updated port status (done / remaining) and the per-batch typecheck result. diff --git a/plugin/skills/tracedecay-recall-memory/SKILL.md b/plugin/skills/tracedecay-recall-memory/SKILL.md deleted file mode 100644 index 075c4a6fa5..0000000000 --- a/plugin/skills/tracedecay-recall-memory/SKILL.md +++ /dev/null @@ -1,16 +0,0 @@ ---- -name: tracedecay-recall-memory -description: 'Use to recall prior decisions, durable facts, and past session conversations for this project.' ---- - -# Recall memory - -Use to recall prior decisions, durable facts, or past session conversations for this project. - -Route durable decisions/facts through the `tracedecay:project-memory` skill, and raw conversation recall through the `tracedecay:recalling-session-context` skill. - -- **Target:** the question or topic to recall. If none is given, ask what to look up. -- Route durable decisions/facts through `fact_store` search; route "what happened in that session" through `tracedecay_message_search` and the LCM retrieval ladder. Stay read-only. -- If the user asks to update, delete, merge, or prune stored facts, switch to `tracedecay:project-memory`. - -Output: the recalled decisions/messages with their sources (fact, session id, timestamp). diff --git a/plugin/skills/tracedecay-review-diff/SKILL.md b/plugin/skills/tracedecay-review-diff/SKILL.md deleted file mode 100644 index fa6aab9ee4..0000000000 --- a/plugin/skills/tracedecay-review-diff/SKILL.md +++ /dev/null @@ -1,15 +0,0 @@ ---- -name: tracedecay-review-diff -description: 'Use to review the current PR or diff for impact, risk, and quality via the TraceDecay code graph.' ---- - -# Review diff - -Use to review the current PR or diff for impact, risk, and quality. - -Use `tracedecay:reviewing-changes`. - -- **Scope:** the current working-tree diff, or the base ref / PR named if one is given. -- Read-only: no edits or test runs. To verify behavior, hand off to `tracedecay:assessing-impact`. - -Output: findings grouped Critical / Warning / Note, the impacted areas, and the test set to run. diff --git a/plugin/skills/tracedecay-test-changes/SKILL.md b/plugin/skills/tracedecay-test-changes/SKILL.md deleted file mode 100644 index 3b52922d3a..0000000000 --- a/plugin/skills/tracedecay-test-changes/SKILL.md +++ /dev/null @@ -1,15 +0,0 @@ ---- -name: tracedecay-test-changes -description: 'Use to test current changes by running only affected tests and mapping failures back to source.' ---- - -# Test changes - -Use to test current changes by running only affected tests and mapping failures back to source. - -Use `tracedecay:assessing-impact` with `tracedecay_run_affected_tests` and `tracedecay_diagnostics`. - -- **Input:** explicit changed paths if given; otherwise use the current working tree. -- Preview scope read-only first. `tracedecay_run_affected_tests` and `tracedecay_diagnostics` run cargo-backed checks, so confirm before running. - -Output: pass/fail summary, failing-symbol mapping, and suggested missing tests. diff --git a/plugin/skills/tracing-functions/SKILL.md b/plugin/skills/tracing-functions/SKILL.md index 6314a5f9f0..0b2acadeeb 100644 --- a/plugin/skills/tracing-functions/SKILL.md +++ b/plugin/skills/tracing-functions/SKILL.md @@ -1,27 +1,48 @@ --- name: tracing-functions -description: 'Use when tracing call relationships: find callers/callees, who calls a function, what it calls, what depends on a symbol or fixture/helper, shortest call paths, references for rename prep, recursion, hubs, or dynamic dispatch. Use before grep/file reads for "trace this function" tasks.' +description: 'Use when the task is call-relationship shaped: "who calls X", "what does X call", "trace this", references before a rename, shortest path, recursion, or hubs. Trigger before grepping a function name — grep misses dynamic dispatch. Do NOT use to locate a symbol (tracedecay:exploring-code).' --- # Tracing functions +``` +NO GREPPING FOR CALL SITES. The graph resolves dispatch; grep resolves text. +``` + +Announce: "Using tracedecay:tracing-functions to trace ." + ## Workflow -1. **Resolve symbol(s) → node ID(s)** with `tracedecay_find_exact_symbol` for exact names, `tracedecay_search` for ranked discovery, or `tracedecay_by_qualified_name` for stable identities (see `tracedecay:exploring-code` for the full resolver ladder). -2. **Upstream (callers) → `tracedecay_callers`** (`node_id`, `max_depth` 1–2 first). For many symbols at once → `tracedecay_callers_for` (`node_ids[]`, one round-trip). -3. **Downstream (callees) → `tracedecay_callees`** (resolves trait dispatch; watch for `dispatch_via_trait: true` / `dispatch_from`). Pass `resolve_dispatch: false` for direct edges only. -4. **Path between two symbols → `tracedecay_call_chain`** (`from_id`, `to_id`, `max_depth`). -5. **Polymorphism → `tracedecay_implementations`** for a quick "every implementor / every body of this method"; the full type-level toolkit (impl blocks, hierarchies, derives, construction/field sites) is `tracedecay:exploring-code`. -6. **All references (rename prep) → `tracedecay_rename_preview`** (`node_id`): every edge where the node is source or target. For the full recon-then-edit rename workflow, use `tracedecay:editing-safely`. -7. **Cycles / hubs:** `tracedecay_recursion`, `tracedecay_hotspots`, `tracedecay_rank`. +| Question | Call | +|---|---| +| Resolve name → node ID first | `tracedecay_find_exact_symbol` / `tracedecay_search` (ladder: `tracedecay:exploring-code`) | +| Who calls X | `tracedecay_callers` (`max_depth` 1–2 first; widen only if unclear) | +| Many symbols at once | `tracedecay_callers_for` (`node_ids[]`, one round-trip) | +| What X calls | `tracedecay_callees` (resolves trait dispatch; note `dispatch_via_trait: true`) | +| Path from A to B | `tracedecay_call_chain` (`from_id`, `to_id`, `max_depth`) | +| Every implementor / every body of a method | `tracedecay_implementations` | +| Every reference (rename prep) | `tracedecay_rename_preview` (preview only — nothing renames) | +| Cycles / hubs | `tracedecay_recursion` / `tracedecay_hotspots` / `tracedecay_rank` | + +## Rules + +- Read-only and parallel-safe. Keep depth small first; widen deliberately. +- Truncated with a `handle`? Narrow depth/target set; `tracedecay_retrieve` + only when the omitted chain is needed. +- Rename is the goal → hand the preview to `tracedecay:editing-safely`. + "What breaks / which tests" → `tracedecay:assessing-impact`. +- For several independent symbols, scoped read-only subagents (one symbol or + direction each, cited node ids); the parent owns the final trace. -## Guardrails +## If tools are deferred or MCP fails -- Read-only and parallel-safe. For tasks like "find callers of setup_project", "which tests still depend on this fixture", or "trace this function", resolve the symbol and call `tracedecay_callers` / `tracedecay_callees` before running grep or opening files. Keep `max_depth` small (1–2) first; widen only when the chain is not yet clear. `tracedecay_rename_preview` only previews references — it does not rename. -- For several independent symbols or call paths, use scoped read-only subagents per symbol, direction, or path hypothesis. Require node ids, depth/tool parameters, and dispatch notes; the parent agent owns the final trace. -- If a trace response is truncated and includes a `handle`, narrow depth or target set first when possible; call `tracedecay_retrieve` with that `handle` when the omitted chain details are needed. +- Deferred: one ToolSearch call — + `select:tracedecay_callers,tracedecay_callees,tracedecay_call_chain,tracedecay_find_exact_symbol,tracedecay_rename_preview`. +- MCP error: `tracedecay tool callers --node-id …` etc. (see + `tracedecay:using-the-cli`). Never fall back to grepping call sites. -## Output +## Deliverable -- The caller/callee tree or the resolved path, with dispatch targets noted. -- If any result includes a `tracedecay_metrics:` line, report the savings to the user. +Do not end without: the caller/callee tree or resolved path with dispatch +targets noted, and node IDs for any follow-up skill. Report any +`tracedecay_metrics:` line. diff --git a/plugin/skills/using-the-cli/SKILL.md b/plugin/skills/using-the-cli/SKILL.md index ddbbf606e4..befe0fb0ae 100644 --- a/plugin/skills/using-the-cli/SKILL.md +++ b/plugin/skills/using-the-cli/SKILL.md @@ -19,9 +19,32 @@ The `tracedecay` binary exposes every MCP tool as a shell command. MCP and CLI h - 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_…`. +- Truncated responses emit the same `handle` envelope as MCP — dereference with `tracedecay tool retrieve --handle rh_…` (see *Retrieving truncated responses* below). - The required/optional flags for the common tools are catalogued in [references/tool-arg-catalog.md](references/tool-arg-catalog.md). +## Retrieving truncated responses + +TraceDecay truncates large tool responses and emits a **handle** envelope +instead of the full body. The original text is cached in the active-project +store; dereference it rather than re-running the source tool — this works +identically over MCP (`tracedecay_retrieve`) and the CLI +(`tracedecay tool retrieve`). + +- A prior response ended with a `handle` (e.g. `rh_…`) and the missing detail + is actually needed → **dereference the handle → `tracedecay_retrieve`** + (`handle` copied exactly). It returns the exact cached original text; it does + not re-run the tool or re-read a file/session/node. Do not re-run the broad + query, guess, or read a file again. +- You do NOT need the truncated tail → leave it; retrieval costs tokens. +- Handles are local, project-scoped, and expire; if `retrieve` reports an + expired/unknown handle, re-run the original tool with a **narrower** query + (see `tracedecay:using-tracedecay`) rather than retrying the stale handle. If + the truncated response used a `project-id`/`project-path` selector, pass the + same selector to `retrieve`. +- To open one session/summary node instead of a cached tool body → expand it + with `tracedecay_lcm_expand`; `tracedecay:managing-session-context` drives the + LCM store and past-session retrieval. + ## When to switch - An MCP call returns a client or transport error, times out, or the server drops mid-session. @@ -37,7 +60,16 @@ After falling back, diagnose the MCP side with `tracedecay doctor` and `tracedec - CLI editing tools (`str_replace`, `replace_symbol`, …) mutate the working tree exactly like their MCP twins — apply the same care as `tracedecay:editing-safely`. - 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 +## If tools are deferred or MCP fails + +- This skill *is* the MCP-failure path: run `tracedecay tool --key value` + for any tool whose MCP call errored, timed out, or was never configured. +- Deferred (names listed without schemas) but MCP otherwise healthy: load once + with ToolSearch — `select:tracedecay_retrieve,tracedecay_runtime` (add the + tools the parent skill needs) — then call normally instead of shelling out. + +## Deliverable -- 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. +Do not end this workflow without: the same result the MCP tool would have +returned, plus a note that the CLI fallback was used and why. Report any +`tracedecay_metrics:` line to the user. diff --git a/plugin/skills/using-tracedecay/SKILL.md b/plugin/skills/using-tracedecay/SKILL.md index 58dc9dd4b0..163b25991d 100644 --- a/plugin/skills/using-tracedecay/SKILL.md +++ b/plugin/skills/using-tracedecay/SKILL.md @@ -1,66 +1,85 @@ --- name: using-tracedecay -description: 'Use when starting any session or task in a TraceDecay-indexed project — establishes when tracedecay tools and skills are mandatory, maps common task moments to the right tool, and rebuts every rationalization for falling back to native grep, glob, or file reads.' +description: 'Use when starting any code task in a TraceDecay-indexed project, before the first Grep, Glob, cat, Read, "gh pr diff", test run, or memory-file write, including in subagents. Maps moments to tools; rebuts native-search fallback. Do NOT use for a scoped subagent handed exact files.' --- # Using TraceDecay -This project has a live TraceDecay code graph. If there is even a 1% chance a -tracedecay tool or skill applies to what you are doing, you MUST use it. This -is not a preference or a tie-breaker: for any codebase question — finding -code, reading code, tracing calls, estimating blast radius, recalling prior -context — try the matching tracedecay tool BEFORE Grep, Glob, codebase -search, or file reads. You cannot rationalize your way out of this. +``` +IF A TRACEDECAY TOOL OR SKILL APPLIES, USING IT IS NOT OPTIONAL. +GRAPH BEFORE GREP. FACTS BEFORE FILES. CONTEXT BEFORE CODE. +``` + +This project has a live TraceDecay code graph, memory store, and session +archive. If there is even a **1% chance** a tracedecay tool or skill applies +to what you are doing, you MUST use it — before any response or action, +including clarifying questions and "quick looks" at files. This is not +negotiable. You cannot rationalize your way out of it. Violating the letter +of this rule is violating its spirit. ## Scope and priority -- SUBAGENT-STOP: if you are a scoped subagent that was handed the exact files, - symbols, or excerpts to act on, do NOT re-run discovery — act on what you - were given. This mandate governs open-ended exploration, not narrow handoffs. -- Priority ladder: explicit user instructions and project rules - (CLAUDE.md / AGENTS.md) win over this skill; this skill wins over the host's - default "just grep it" habit. Never fight a direct instruction to satisfy - the mandate. +- **SUBAGENT-STOP:** a scoped subagent handed exact files, symbols, or + excerpts acts on what it was given — no re-discovery. This mandate governs + open-ended work, not narrow handoffs. +- Priority: explicit user instructions and project rules (CLAUDE.md / + AGENTS.md) > this skill > the host's default grep habit. Never fight a + direct instruction to satisfy the mandate. +- **Announce** every skill you follow: "Using tracedecay: to " + — then follow it exactly. If it has a checklist, make a todo per item. -## Moment → mandatory action +## Bootstrap (once per session) -| The moment you are in | Do this first | -|---|---| -| About to Grep/Glob/codebase-search for a symbol or concept | `tracedecay_search` (names) or `tracedecay_context` (concepts) — skill: `tracedecay:exploring-code` | -| About to open or Read a source file | `tracedecay_outline` → `tracedecay_body` → `tracedecay_read` slices — skill: `tracedecay:exploring-code` | -| Asked "who calls X" / "what does X call" / "trace this" | `tracedecay_callers` / `tracedecay_callees` — skill: `tracedecay:tracing-functions` | -| About to change code and wondering what breaks or which tests to run | `tracedecay_impact` / `tracedecay_diff_context` / `tracedecay_affected` — skill: `tracedecay:assessing-impact` | -| About to write a new helper, rename, or do a mechanical edit | `tracedecay:editing-safely` (duplicate probe, rename recon, anchored edits) | -| Reviewing a diff, auditing risk, or drafting commit/PR text | `tracedecay:reviewing-changes` | -| Asked about architecture, tech debt, or project/index status | `tracedecay:code-health` | -| The user references prior decisions or past conversations | `tracedecay:project-memory` / `tracedecay:recalling-session-context` | -| A compiler/type error needs context | `tracedecay:fixing-build-and-type-errors` | -| A tracedecay MCP call errors or times out | `tracedecay:using-the-cli` — never abandon tracedecay over transport | +tracedecay tools may be **deferred** — listed by name only, uncallable until +their schemas load. First need → ONE batched ToolSearch call: +`select:tracedecay_context,tracedecay_search,tracedecay_grep,tracedecay_outline,tracedecay_body` +(add others per the skill you enter). If any MCP call errors or times out, the +same tool runs as `tracedecay tool --key value` — see +`tracedecay:using-the-cli`. Transport failure never justifies grep. -## Red flags +## Moment to mandatory action + +| The moment you are in | Do this instead | +|---|---| +| About to grep/rg a literal string, regex, or config key | `tracedecay_grep` — skill: `tracedecay:exploring-code` | +| About to search for a symbol or concept, or open/Read a source file | `tracedecay_search` / `tracedecay_context`; read via outline→body→read slices — `tracedecay:exploring-code` | +| "Who calls X" / "what does X call" / "trace this" | `tracedecay:tracing-functions` | +| Wondering what breaks or which tests to run | `tracedecay:assessing-impact` | +| About to run `gh pr diff` / read a raw diff to review | `tracedecay_pr_context` / `tracedecay_diff_context` (offline, no gh needed) — `tracedecay:reviewing-changes` | +| About to write a new helper, rename, or mass-edit | `tracedecay:editing-safely` | +| Build/type errors present, or about to run cargo check/tsc | `tracedecay:fixing-build-and-type-errors` | +| About to write MEMORY.md/CLAUDE.md notes, or asked about a past decision | `tracedecay:project-memory` (`fact_store`) | +| Need raw past-session transcripts or compaction recovery | `tracedecay:managing-session-context` | +| Architecture, tech debt, index/project status | `tracedecay:code-health` | +| An MCP call just failed | `tracedecay:using-the-cli` — never abandon over transport | -These thoughts mean STOP — you are rationalizing: +## Red flags — these thoughts mean STOP, you are rationalizing | Thought | Reality | |---|---| -| "Grep is faster for this" | `tracedecay_search` is one call and pre-ranked. | -| "I'll just read the whole file" | `tracedecay_outline` / `tracedecay_body` answer at a fraction of the tokens. | +| "Grep is faster for this" | `tracedecay_grep` runs the same match over the index and returns the enclosing symbol. Same speed, more answer. | +| "I'll just read the whole file" | `outline`/`body` answer at a fraction of the tokens, cached across sessions. | +| "This repo probably isn't indexed" | Check `tracedecay_status` (one cheap call). Guessing "unindexed" to justify grep is the rationalization itself. | +| "I'll use gh to get the PR diff" | `pr_context` computes changed symbols + dependents + tests from the local graph — offline. gh is for comments/CI only. | +| "I made one context call; now I'll bash around" | One call is discovery, not license. Stay on the skill's ladder; pass `seen_node_ids` forward and narrow — don't switch to grep. | +| "I'll jot this in MEMORY.md" | Durable facts go to `fact_store` (add) — searchable, trust-ranked, cross-session. MEMORY.md is not memory. | +| "The index might be stale — I should sync first" | Hooks auto-sync on every session and edit. Never run manual sync; if results look stale, check `tracedecay_status` and report it. | +| "The MCP call might fail / just failed" | `tracedecay tool ` always works. Transport ≠ capability. | | "This is a simple lookup" | Simple lookups are exactly what the graph is for. | | "I already know this codebase" | The graph is fresher than your memory. Check it. | -| "The MCP call might fail" | The CLI fallback (`tracedecay tool `) always works. | | "I'll explore first, then use the skill" | The skills tell you HOW to explore. Check first. | | "The skill is overkill here" | Simple things become complex. Use it. | ## Procedure -1. On every task (including questions), check the moment table above BEFORE - the first tool call. If a row matches, follow it. -2. Announce which skill you are following ("Using `tracedecay:exploring-code` - to …") so the choice is visible and deliberate. -3. Fall back to plain Grep/Glob/Read only for content the graph does not - index (comments, string literals, prose, config bodies) or after - tracedecay has pinpointed the exact files. -4. If a response is truncated with a `handle`, narrow the query or call - `tracedecay_retrieve` — do not re-run broad queries or guess. -5. If any result includes a `tracedecay_metrics:` line, report the savings to - the user. +1. Before the first tool call of ANY task (questions included), check the + moment table. A row matches → announce and follow it. +2. Never dispatch an Explore agent for codebase research while tracedecay is + available; if one must be spawned, its prompt must mandate + `tracedecay_context` as its only exploration tool with `seen_node_ids` + threading. +3. Truncated response with a `handle` → narrow the query, or + `tracedecay_retrieve` when the omitted detail is needed. Never re-run broad. +4. A result includes a `tracedecay_metrics:` line → report the savings. +5. A durable decision, preference, correction, or pitfall surfaced → store it + via `tracedecay:project-memory` without being asked. diff --git a/src/agents/claude.rs b/src/agents/claude.rs index ac886366c0..27ea55bc59 100644 --- a/src/agents/claude.rs +++ b/src/agents/claude.rs @@ -149,11 +149,19 @@ impl AgentIntegration for ClaudeIntegration { let mut settings = load_json_file_strict(&settings_path)?; enable_plugin(&mut settings); + // Write/refresh the plugin-namespace permission allowlist (and migrate + // legacy `mcp__tracedecay__*` entries to their plugin twins) so an + // `update-plugin` from an older install stops prompting on every tool + // call. Idempotent. install_permissions(&mut settings, &ctx.tool_permissions); write_json_file(&settings_path, &settings)?; migrate_off_config_managed(&ctx.home); + // Refresh the managed CLAUDE.md steering block so an `update-plugin` + // rewrites a stale block to the current moment-trigger text. The block + // reaches subagents (they load the project/user CLAUDE.md), so keeping + // it current is how updated steering actually propagates. install_claude_md_rules(&claude_md_path)?; sync_claude_plugin_cache(&ctx.home); @@ -773,11 +781,22 @@ fn ensure_claude_dir(claude_dir: &Path) -> Result<()> { }) } -/// Claude plugin MCP tool permissions use -/// `mcp__plugin____`. +/// Permission-allowlist prefix for the tracedecay tools exposed through the +/// Claude **plugin** MCP server. Claude namespaces a plugin server's tools as +/// `mcp__plugin____`; with plugin name +/// `tracedecay` and the server key `tracedecay` (see `plugin/.mcp.json`), that +/// yields `mcp__plugin_tracedecay_tracedecay__`. +/// +/// The legacy config-managed install wrote `mcp__tracedecay__` entries, +/// which do NOT match the plugin namespace, so every plugin tool call prompted +/// interactively (and hard-failed headless/in subagents). The installer now +/// also writes the plugin-namespace twins. const PLUGIN_TOOL_PERM_PREFIX: &str = "mcp__plugin_tracedecay_tracedecay__"; +/// Legacy config-managed permission prefix, kept only to detect and mirror +/// existing entries into the plugin namespace during migration. const LEGACY_TOOL_PERM_PREFIX: &str = "mcp__tracedecay__"; +/// Every managed tracedecay tool's plugin-namespace permission entry. fn plugin_tool_perms() -> Vec { super::tool_names() .into_iter() @@ -785,6 +804,9 @@ fn plugin_tool_perms() -> Vec { .collect() } +/// Map a legacy `mcp__tracedecay__` permission entry to its +/// plugin-namespace twin `mcp__plugin_tracedecay_tracedecay__`. Returns +/// `None` for any entry that is not a legacy tracedecay tool permission. fn legacy_perm_to_plugin_twin(entry: &str) -> Option { entry .strip_prefix(LEGACY_TOOL_PERM_PREFIX) @@ -794,6 +816,15 @@ fn legacy_perm_to_plugin_twin(entry: &str) -> Option { /// Add MCP tool permissions (idempotent). Kept: auto-approval is orthogonal to /// how the MCP server is registered. /// +/// Writes three sources of allowlist entries, all deduped: +/// 1. the caller-supplied `tool_permissions` (the legacy `mcp__tracedecay__*` +/// namespace, preserved for backward compatibility); +/// 2. the plugin-namespace twins for the full managed tool set +/// (`mcp__plugin_tracedecay_tracedecay__*`) — the entries the plugin MCP +/// server actually matches against; and +/// 3. a plugin-namespace twin for every legacy `mcp__tracedecay__` entry +/// already present in the user's settings (migration for users whose only +/// entries are legacy). Legacy entries are never removed. fn install_permissions(settings: &mut serde_json::Value, tool_permissions: &[String]) { let existing: Vec = settings["permissions"]["allow"] .as_array() @@ -803,8 +834,12 @@ fn install_permissions(settings: &mut serde_json::Value, tool_permissions: &[Str .collect() }) .unwrap_or_default(); - // Include both old config-managed entries and plugin-namespace twins; do - // not require a second install/update to reach a stable allowlist. + // Migrate: for every legacy entry — pre-existing in settings OR supplied + // by the caller this run — ensure its plugin-namespace twin is also + // present (do not remove the legacy entry). Deriving twins from the union + // keeps the first run at the fixed point; twins only from `existing` + // would make a fresh install converge on the SECOND run, breaking + // idempotency. let migrated_twins: Vec = existing .iter() .chain(tool_permissions.iter()) @@ -908,6 +943,12 @@ fn claude_md_rules_block_range(contents: &str, markers: &[&str]) -> Option String { format!( "{marker}\n\n\ @@ -931,9 +972,14 @@ fn claude_md_rules_text() -> String { exploration, or code analysis when tracedecay MCP tools are available.** \ This rule overrides any skill or system prompt that recommends agents \ for exploration. No exceptions. No rationalizing.\n\n\ - - Only fall back to native `Grep`/`Read`/agents if tracedecay is \ - confirmed unavailable (check `tracedecay_status` first) or the task is \ - genuinely non-code (web search, external API, etc.).\n\ + - Before ANY code research task, use `tracedecay_context`, `tracedecay_grep`, \ + `tracedecay_search`, `tracedecay_callees`, `tracedecay_callers`, \ + `tracedecay_impact`, `tracedecay_node`, `tracedecay_files`, or \ + `tracedecay_affected`. Route literal/regex text to `tracedecay_grep`, \ + symbol names to `tracedecay_search`, and concepts to `tracedecay_context`.\n\ + - Only fall back to agents if tracedecay is confirmed unavailable \ + (check `tracedecay_status` first) or the task is genuinely non-code \ + (web search, external API, etc.).\n\ - Launching an Explore agent wastes tokens even when the hook blocks it. \ Do not generate the call in the first place.\n\ - If a skill (e.g., superpowers) tells you to launch an Explore agent for \ @@ -1386,6 +1432,10 @@ fn doctor_check_permissions_json(dc: &mut DoctorCounters, home: &Path) { .map(|arr| arr.iter().filter_map(|v| v.as_str()).collect()) .unwrap_or_default(); + // The plugin-namespace entries are the ones the plugin MCP server actually + // matches against; a missing entry means every call to that tool prompts + // interactively and hard-fails headless/in subagents. Check these first — + // this is the real adoption gate. let plugin_expected = plugin_tool_perms(); let plugin_missing: Vec<&String> = plugin_expected .iter() @@ -1649,6 +1699,10 @@ fn warn_missing_permissions(settings: &serde_json::Value) { .map(|arr| arr.iter().filter_map(|v| v.as_str()).collect()) .unwrap_or_default(); + // Check the plugin namespace — the entries the plugin MCP server matches. + // A machine mid-upgrade may carry legacy `mcp__tracedecay__*` entries but + // lack the `mcp__plugin_tracedecay_tracedecay__*` twins, which is exactly + // what causes per-call prompts, so that is the gap worth warning about. let expected = plugin_tool_perms(); let missing_count = expected .iter() @@ -1733,7 +1787,7 @@ mod tests { .collect(); let skills = plugin_subdir_names("skills"); - assert_eq!(skills.len(), 30, "expected 30 shared skill dirs"); + assert_eq!(skills.len(), 13, "expected 13 shared skill dirs"); // Every file under plugin/skills/ (SKILL.md *and* any support files) is // deployed — the recursive embed leaves nothing on disk unwired. let skills_root = Path::new(env!("CARGO_MANIFEST_DIR")).join("plugin/skills"); diff --git a/src/agents/claude_agents/code-explorer.md b/src/agents/claude_agents/code-explorer.md index 2c9ed8c273..c0b436220c 100644 --- a/src/agents/claude_agents/code-explorer.md +++ b/src/agents/claude_agents/code-explorer.md @@ -13,7 +13,7 @@ Read-only exploration subagent. Investigate the repository and return findings. ## Method 1. Start with `tracedecay_context` (add `keywords` for concepts). **Respect the per-project call budget shown in the tool description.** Pass `seen_node_ids` from each response to the next call's `exclude_node_ids`. -2. Narrow with `tracedecay_search` / `tracedecay_find_exact_symbol` / `tracedecay_body` / `tracedecay_outline`. +2. Narrow with `tracedecay_grep` for literal/regex text, `tracedecay_search` / `tracedecay_find_exact_symbol` for symbol names, and `tracedecay_body` / `tracedecay_outline` for bounded reads. 3. Trace with `tracedecay_callers` / `tracedecay_callees` / `tracedecay_call_chain`; assess reach with `tracedecay_impact`. 4. Fall back to Grep/Read only for non-indexed content or after TraceDecay pinpoints files. diff --git a/src/agents/claude_agents/session-historian.md b/src/agents/claude_agents/session-historian.md index b74c2c277d..3ae0a274c0 100644 --- a/src/agents/claude_agents/session-historian.md +++ b/src/agents/claude_agents/session-historian.md @@ -16,7 +16,7 @@ Read-only recall subagent. Retrieve what past sessions said, did, and decided fo 2. Narrow with `tracedecay_lcm_grep` (scope/role/time filters), then replay with `tracedecay_lcm_load_session` (paginate via `after_store_id`, never dump whole sessions). 3. Drill into summaries with `tracedecay_lcm_describe` / `tracedecay_lcm_expand` / `tracedecay_lcm_expand_query`; inspect the store with `tracedecay_lcm_status`. 4. For durable decisions/facts, search `tracedecay_fact_store` (`action: "search"`, plus `"probe"`/`"reason"` when useful). -5. If the `tracedecay:recalling-session-context` skill is available, follow its full ladder. +5. If the `tracedecay:managing-session-context` skill is available, follow its full ladder. ## Rules diff --git a/src/agents/codex.rs b/src/agents/codex.rs index 3abf9ad124..4c7d3feac1 100644 --- a/src/agents/codex.rs +++ b/src/agents/codex.rs @@ -1482,6 +1482,48 @@ fn codex_hook_present(hooks: &serde_json::Value, event: &str, command: &str) -> mod tests { use super::*; + /// The repo-local `hooks-codex.json` ships an empty `hooks` object plus a + /// self-documenting `description`. Rendering the global bundle must fill the + /// object from `CODEX_MANAGED_HOOKS` while leaving the description intact, + /// and must never invent hooks the managed table does not declare. + #[test] + fn codex_plugin_hooks_fills_empty_seed_and_preserves_description() { + let raw = codex_embedded_plugin_files() + .into_iter() + .find_map(|(relative, contents)| (relative == "hooks/hooks.json").then_some(contents)) + .expect("codex bundle ships hooks/hooks.json"); + + // The seed template is genuinely empty (it is not dead weight: it is the + // base the renderer mutates in place). + let seed: serde_json::Value = serde_json::from_str(raw).unwrap(); + assert_eq!(seed["hooks"], json!({})); + assert!( + seed["description"] + .as_str() + .unwrap() + .contains("no lifecycle hooks"), + "empty seed must carry a self-documenting description" + ); + + let rendered = codex_plugin_hooks(raw, "/usr/local/bin/tracedecay").unwrap(); + let value: serde_json::Value = serde_json::from_str(&rendered).unwrap(); + // The description survives rendering (Codex's loader ignores it). + assert_eq!(value["description"], seed["description"]); + let hooks = value["hooks"].as_object().unwrap(); + for managed in CODEX_MANAGED_HOOKS { + assert!( + hooks.contains_key(managed.event), + "rendered global bundle missing managed event {}", + managed.event + ); + } + assert_eq!( + hooks.len(), + CODEX_MANAGED_HOOKS.len(), + "rendered bundle must register exactly the managed hooks" + ); + } + #[test] fn native_memories_injection_detection_covers_config_shapes() { let parse = |raw: &str| toml::from_str::(raw).unwrap(); @@ -1653,7 +1695,7 @@ trusted_hash = "sha256:compact" .map(|(relative, _)| relative.to_string()) .collect(); - // Every skill dir under plugin/skills is deployed by Codex (all 29). + // Every skill dir under plugin/skills is deployed by Codex (all 13). let skills_root = Path::new(env!("CARGO_MANIFEST_DIR")).join("plugin/skills"); let mut skill_dirs: Vec = std::fs::read_dir(&skills_root) .expect("plugin/skills should be readable") @@ -1662,7 +1704,7 @@ trusted_hash = "sha256:compact" .map(|entry| entry.file_name().to_string_lossy().into_owned()) .collect(); skill_dirs.sort(); - assert_eq!(skill_dirs.len(), 30, "expected 30 shared skill dirs"); + assert_eq!(skill_dirs.len(), 13, "expected 13 shared skill dirs"); // Every file under plugin/skills/ (SKILL.md *and* any support files) is // deployed — the recursive embed leaves nothing on disk unwired. for relative in skill_tree_files(&skills_root) { diff --git a/src/agents/kiro.rs b/src/agents/kiro.rs index 0aa47ebb00..086b7aa10d 100644 --- a/src/agents/kiro.rs +++ b/src/agents/kiro.rs @@ -613,6 +613,7 @@ or `delegate`. This is a mandate, not a preference — you cannot rationalize yo way out of it.\n\n\ | The moment you are in | Do this first |\n\ |---|---|\n\ +| Searching for literal/regex code text | `tracedecay_grep` |\n\ | Searching for a symbol or concept | `tracedecay_search` (names) or `tracedecay_context` (concepts) |\n\ | About to read a source file | `tracedecay_outline` -> `tracedecay_body` -> `tracedecay_read` slices |\n\ | \"Who calls X\" / \"what does X call\" / \"trace this\" | `tracedecay_callers` / `tracedecay_callees` |\n\ @@ -621,7 +622,7 @@ way out of it.\n\n\ | A prior decision or past conversation is referenced | `tracedecay_message_search` / `tracedecay_lcm_expand_query` |\n\n\ | Red-flag thought | Reality |\n\ |---|---|\n\ -| \"Grep is faster for this\" | `tracedecay_search` is one call and pre-ranked. |\n\ +| \"Grep is faster for this\" | `tracedecay_grep` handles literal/regex code search; `tracedecay_search` is pre-ranked for names. |\n\ | \"I'll just read the whole file\" | `tracedecay_outline` / `tracedecay_body` answer at a fraction of the tokens. |\n\ | \"This is a simple lookup\" | Simple lookups are exactly what the graph is for. |\n\ | \"I already know this codebase\" | The graph is fresher than your memory. Check it. |\n\n\ diff --git a/src/agents/plugin_bundle.rs b/src/agents/plugin_bundle.rs index 52884c1e81..6e9a991dce 100644 --- a/src/agents/plugin_bundle.rs +++ b/src/agents/plugin_bundle.rs @@ -5,13 +5,14 @@ //! agent format. //! //! Layout of `plugin/`: -//! - `plugin/skills/*/SKILL.md` — the 17 shared model-invocable skills **plus** -//! the 13 canonical (`claude`/`codex`) workflow dispatcher skills (30 total). -//! Cursor deploys only the 17 model-invocable skills (not the dispatcher -//! skills); its explicit dispatch is native commands (below). +//! - `plugin/skills/*/SKILL.md` — the 13 shared model-invocable skills. All +//! three hosts deploy the full set; the workflow dispatcher skills were +//! removed (their behavior lives in the native slash commands below), so no +//! host filters the skill set today. The `cursor_skill_files` filter is kept +//! as a guard against a dispatcher skill being reintroduced. //! - `plugin/overlays/cursor/commands/tracedecay-*.md` — Cursor 1.6+ native //! slash commands, one per workflow slug, deployed to `commands/.md`. -//! These replace the old Cursor dispatcher *skills*. +//! These provide the explicit workflow dispatch (no dispatcher *skills*). //! - `plugin/agents/*.md` — Claude-form subagents (deployed by Claude). //! - `plugin/overlays/cursor/agents/*.md` — Cursor-form subagents. //! - `plugin/commands/*.md` — Claude slash commands. @@ -325,7 +326,9 @@ mod tests { fn each_host_composes_the_expected_file_count() { // Skill files are embedded recursively (SKILL.md + support files), so // the skill count is derived from the generated set rather than a - // frozen literal. Cursor drops the `tracedecay-*` dispatcher skills. + // frozen literal. The `tracedecay-*` dispatcher skills were removed, so + // Cursor's subset now equals the full skill set; the filter is kept as a + // guard against a dispatcher skill ever being reintroduced. let all_skills = GENERATED_SKILL_FILES.len(); let cursor_skills = cursor_skill_files().count(); diff --git a/src/agents/prompt_rules.rs b/src/agents/prompt_rules.rs index 8c15000aa5..b8d276de58 100644 --- a/src/agents/prompt_rules.rs +++ b/src/agents/prompt_rules.rs @@ -18,10 +18,11 @@ pub(crate) const SKILL_INDEX_START: &str = "