Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 11 additions & 3 deletions codex-plugin/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,21 @@ Codex.
only when the workflow matches. These mirror the model-invocable Cursor skills
so both hosts steer agents toward the same tracedecay tools.
- **Lifecycle hooks** (`hooks/hooks.json`, referenced from the manifest's
`hooks` field): `SessionStart`, `UserPromptSubmit`, `SubagentStart`, and
`PostToolUse` handlers that inject index status and tool-routing steering and
keep the graph and session store warm.
`hooks` field): `SessionStart`, `UserPromptSubmit`, `SubagentStart`,
`PostToolUse`, and `PostCompact` handlers that inject index status and
tool-routing steering, keep the graph/session store warm, and replace
encrypted Codex compaction placeholders with auxiliary app-server summaries
backed by the visible source messages in TraceDecay's LCM DAG.

Codex skips newly installed or changed command hooks until they are trusted —
run `/hooks` in Codex to review and trust the tracedecay hooks.

Codex has no always-applied rule surface (unlike Cursor's `rules/`), so the
tool-routing steering Cursor places in a rule is injected through the
`SessionStart`/`UserPromptSubmit` hooks instead.

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,
`TRACEDECAY_CODEX_SUMMARY_MODEL` to pin a model, or
`TRACEDECAY_CODEX_SUMMARY_TIMEOUT_SECS` to adjust the child timeout.
2 changes: 1 addition & 1 deletion codex-plugin/skills/fixing-build-and-type-errors/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ Use this when build or type diagnostics are relevant to the task. Prefer pasted
## 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 `.tracedecay/target/`; the **first** run on a fresh tree can take minutes, later calls are sub-second.
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/<project_id>/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:searching-for-code` ladder; widen blast radius with `tracedecay_impact` if a fix is risky.
4. **Apply the fix → `tracedecay:atomic-code-edits`** (or your normal edit tools).
5. **Re-check** with the cheapest applicable diagnostic path, then verify behavior via `tracedecay:running-impacted-tests`.
Expand Down
2 changes: 1 addition & 1 deletion codex-plugin/skills/porting-code/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ description: Use when porting, migrating, or rewriting code between directories,

- Never port a symbol before its dependencies (respect `tracedecay_port_order`).
- `tracedecay_port_status` / `tracedecay_port_order` and the lookups are read-only; the editing tools and `tracedecay_diagnostics` mutate the working tree / run the toolchain. Use them when porting/verification is relevant and respect Cursor approval/run-mode.
- `tracedecay_diagnostics` forces target dir `.tracedecay/target/`; the first run can take minutes.
- `tracedecay_diagnostics` forces target dir `/tmp/tracedecay-target/<project_id>/diagnostics`; the first run can take minutes.

## Output

Expand Down
2 changes: 1 addition & 1 deletion codex-plugin/skills/recalling-project-memory/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ Recall memory **before** reaching for external or web search — prior sessions

## Workflow

1. **Past conversations → `tracedecay_message_search`** (`query`, optional `provider`, `limit`) over ingested Cursor/Codex/agent transcripts (project-local FTS index).
1. **Past conversations → `tracedecay_message_search`** (`query`, optional `provider`, `limit`) over ingested Cursor/Codex/agent transcripts (active project FTS index).
2. **Durable facts → `tracedecay_fact_store`** with `action: "search"` (or `"probe"` / `"reason"`), plus `query` and `min_trust`.
3. **If the user asks to inspect or repair memory health → `tracedecay_memory_status`** (repairs derived vectors/banks; returns fact/entity counts + trust distribution).
4. **If the user rates a recalled fact → `tracedecay_fact_feedback`** (`helpful` / `unhelpful`) to tune its trust score.
Expand Down
2 changes: 1 addition & 1 deletion codex-plugin/skills/running-impacted-tests/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ Use this when impacted-test verification is relevant to the task. Respect Cursor

## Guardrails

- `tracedecay_run_affected_tests` and `tracedecay_diagnostics` run cargo-backed checks, and the first `diagnostics` build can take minutes (forced target dir `.tracedecay/target/`). Respect Cursor approval/run-mode and avoid duplicate runs.
- `tracedecay_run_affected_tests` and `tracedecay_diagnostics` run cargo-backed checks, and the first `diagnostics` build can take minutes (forced target dir `/tmp/tracedecay-target/<project_id>/diagnostics`). Respect Cursor approval/run-mode and avoid duplicate runs.
- `tracedecay_run_affected_tests` is cargo-only. For non-Rust repos use `tracedecay_diagnostics` (tsc/pyright) and the project's own test runner.
- Steps 1–2 and 5 are read-only and safe to run first to preview scope before the user commits to a run.
- Pure coverage questions ("which tests cover X", "is this tested", "where should the next test go") that don't need a run → `tracedecay:assessing-test-coverage`.
Expand Down
2 changes: 1 addition & 1 deletion cursor-plugin/skills/fixing-build-and-type-errors/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ Use this when build or type diagnostics are relevant to the task. Prefer pasted
## 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 `.tracedecay/target/`; the **first** run on a fresh tree can take minutes, later calls are sub-second.
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/<project_id>/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:searching-for-code` ladder; widen blast radius with `tracedecay_impact` if a fix is risky.
4. **Apply the fix → `tracedecay:atomic-code-edits`** (or your normal edit tools).
5. **Re-check** with the cheapest applicable diagnostic path, then verify behavior via `tracedecay:running-impacted-tests`.
Expand Down
2 changes: 1 addition & 1 deletion cursor-plugin/skills/porting-code/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ description: Use when porting, migrating, or rewriting code between directories,

- Never port a symbol before its dependencies (respect `tracedecay_port_order`).
- `tracedecay_port_status` / `tracedecay_port_order` and the lookups are read-only; the editing tools and `tracedecay_diagnostics` mutate the working tree / run the toolchain. Use them when porting/verification is relevant and respect Cursor approval/run-mode.
- `tracedecay_diagnostics` forces target dir `.tracedecay/target/`; the first run can take minutes.
- `tracedecay_diagnostics` forces target dir `/tmp/tracedecay-target/<project_id>/diagnostics`; the first run can take minutes.

## Output

Expand Down
2 changes: 1 addition & 1 deletion cursor-plugin/skills/recalling-project-memory/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ Recall memory **before** reaching for external or web search — prior sessions

## Workflow

1. **Past conversations → `tracedecay_message_search`** (`query`, optional `provider`, `limit`) over ingested Cursor/Codex/agent transcripts (project-local FTS index).
1. **Past conversations → `tracedecay_message_search`** (`query`, optional `provider`, `limit`) over ingested Cursor/Codex/agent transcripts (active project FTS index).
2. **Durable facts → `tracedecay_fact_store`** with `action: "search"` (or `"probe"` / `"reason"`), plus `query` and `min_trust`.
3. **If the user asks to inspect or repair memory health → `tracedecay_memory_status`** (repairs derived vectors/banks; returns fact/entity counts + trust distribution).
4. **If the user rates a recalled fact → `tracedecay_fact_feedback`** (`helpful` / `unhelpful`) to tune its trust score.
Expand Down
2 changes: 1 addition & 1 deletion cursor-plugin/skills/running-impacted-tests/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ Use this when impacted-test verification is relevant to the task. Respect Cursor

## Guardrails

- `tracedecay_run_affected_tests` and `tracedecay_diagnostics` run cargo-backed checks, and the first `diagnostics` build can take minutes (forced target dir `.tracedecay/target/`). Respect Cursor approval/run-mode and avoid duplicate runs.
- `tracedecay_run_affected_tests` and `tracedecay_diagnostics` run cargo-backed checks, and the first `diagnostics` build can take minutes (forced target dir `/tmp/tracedecay-target/<project_id>/diagnostics`). Respect Cursor approval/run-mode and avoid duplicate runs.
- `tracedecay_run_affected_tests` is cargo-only. For non-Rust repos use `tracedecay_diagnostics` (tsc/pyright) and the project's own test runner.
- Steps 1–2 and 5 are read-only and safe to run first to preview scope before the user commits to a run.
- Pure coverage questions ("which tests cover X", "is this tested", "where should the next test go") that don't need a run → `tracedecay:assessing-test-coverage`.
Expand Down
33 changes: 24 additions & 9 deletions scripts/hermes_plugin_unit_check.py
Original file line number Diff line number Diff line change
Expand Up @@ -352,11 +352,14 @@ def call_llm(self, **kwargs):
assert degraded["matches"] == retrieval["matches"]
ok("expand-query synthesis degrades on RuntimeError with retrieval intact")

# Storage scope: the pin never forces project_local anymore.
# Storage routing: the pin routes LCM/memory through the resolved project
# store instead of falling back to a hidden profile-local sessions DB.
storage = plugin._storage_args("/some/pin", str(hermes_home))
assert storage["storage_scope"] == "hermes_profile", storage
assert storage["hermes_home"] == str(hermes_home), storage
ok("LCM/memory storage stays hermes_profile-scoped")
assert storage["project_root"] == "/some/pin", storage
fallback_storage = plugin._storage_args(None, str(hermes_home))
assert fallback_storage["storage_scope"] == "hermes_profile", fallback_storage
assert fallback_storage["hermes_home"] == str(hermes_home), fallback_storage
ok("LCM/memory storage routes through resolved project roots")

# ── 4. provider hooks call the right verbs ───────────────────────────
provider = ctx.provider
Expand All @@ -370,27 +373,39 @@ def call_llm(self, **kwargs):
real_tool = plugin.tools.call_tracedecay_tool
try:
plugin.tools.call_tracedecay_tool = lambda name, args, **kw: (
calls.append((name, args)) or "{}"
calls.append((name, args, kw)) or "{}"
)
provider.sync_turn("u", "a", session_id="other-session", messages=messages)
name, args = calls[-1]
name, args, kwargs = calls[-1]
assert name == "tracedecay_lcm_preflight", calls
assert args["session_id"] == "other-session"
assert args["messages"] == messages
assert args["storage_scope"] == "hermes_profile"
assert args["storage_scope"] == "hermes_profile", args
assert args["hermes_home"] == str(hermes_home), args
assert kwargs["storage_scope"] == "hermes_profile", kwargs
assert kwargs["hermes_home"] == str(hermes_home), kwargs
ok("sync_turn ingests via tracedecay_lcm_preflight")

provider.sync_turn("only user", "and assistant", session_id="s2", messages=None)
name, args = calls[-1]
name, args, kwargs = calls[-1]
assert name == "tracedecay_lcm_preflight", calls
assert args["messages"][0]["content"] == "only user"
assert args["messages"][1]["content"] == "and assistant"
assert args["storage_scope"] == "hermes_profile", args
assert args["hermes_home"] == str(hermes_home), args
assert kwargs["storage_scope"] == "hermes_profile", kwargs
assert kwargs["hermes_home"] == str(hermes_home), kwargs
ok("sync_turn synthesizes a turn when messages are missing")

provider.on_memory_write("add", "user", "likes rust", {"session_id": "s"})
name, args = calls[-1]
name, args, kwargs = calls[-1]
assert name == "tracedecay_fact_store", calls
assert args["action"] == "add" and args["category"] == "user_pref"
assert args["metadata"]["hermes_action"] == "add"
assert args["storage_scope"] == "hermes_profile", args
assert args["hermes_home"] == str(hermes_home), args
assert kwargs["storage_scope"] == "hermes_profile", kwargs
assert kwargs["hermes_home"] == str(hermes_home), kwargs
before = len(calls)
provider.on_memory_write("remove", "memory", "anything")
assert len(calls) == before
Expand Down
19 changes: 9 additions & 10 deletions src/agents/hermes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
mod dashboard_wrapper;
mod lifecycle;
mod profile_config;
mod tokensave_migration;

use std::io::ErrorKind;
use std::path::{Path, PathBuf};
Expand Down Expand Up @@ -70,9 +69,9 @@ impl AgentIntegration for HermesIntegration {
}

fn has_tracedecay(&self, home: &Path) -> bool {
let locations = tokensave_migration::profile_locations(home, None);
locations.plugin_dir.join("plugin.yaml").exists()
|| locations.legacy_plugin_dir.join("plugin.yaml").exists()
hermes_profile_dir(home, None)
.join("plugins/tracedecay/plugin.yaml")
.exists()
}
}

Expand Down Expand Up @@ -283,8 +282,6 @@ pub(super) fn write_plugin_files(plugin_dir: &Path, tracedecay_bin: &str) -> Res
/// Plugin directories with a detected generated install (a `plugin.yaml`
/// manifest), deduplicated across the default profile, named profiles,
/// `HERMES_HOME`, and the current directory's project-local `.hermes`.
/// Legacy `TokenSave` manifests are mapped to their steady-state tracedecay
/// plugin directory so refreshes write current artifacts in-place.
pub(super) fn detected_plugin_dirs(home: &Path) -> Vec<PathBuf> {
let mut roots = vec![hermes_home(home)];
if let Some(env_home) = std::env::var_os("HERMES_HOME") {
Expand All @@ -301,8 +298,12 @@ pub(super) fn detected_plugin_dirs(home: &Path) -> Vec<PathBuf> {
roots
.into_iter()
.filter_map(|root| {
let plugin_dir = tokensave_migration::detected_plugin_dir(&root)?;
seen.insert(plugin_dir.clone()).then_some(plugin_dir)
let plugin_dir = root.join("plugins/tracedecay");
plugin_dir
.join("plugin.yaml")
.is_file()
.then_some(plugin_dir)
.filter(|plugin_dir| seen.insert(plugin_dir.clone()))
})
.collect()
}
Expand All @@ -327,9 +328,7 @@ pub(super) fn remove_generated_plugin_files(plugin_dir: &Path) -> Result<()> {
remove_generated_file(&plugin_dir.join("__init__.py"))?;
remove_generated_file(&plugin_dir.join("cli.py"))?;
remove_generated_file(&plugin_dir.join("skills/tracedecay/SKILL.md"))?;
remove_generated_file(&plugin_dir.join("skills/tokensave/SKILL.md"))?;
remove_empty_dir(&plugin_dir.join("skills/tracedecay"))?;
remove_empty_dir(&plugin_dir.join("skills/tokensave"))?;
remove_empty_dir(&plugin_dir.join("skills"))?;
dashboard_wrapper::uninstall(plugin_dir)?;

Expand Down
Loading
Loading