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
3 changes: 0 additions & 3 deletions codex-plugin/skills/running-impacted-tests/SKILL.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
---
name: running-impacted-tests
description: 'Use when running only tests affected by changed Rust files, mapping failures back to source, verifying a change without a full suite, or handling cargo-backed impacted-test checks.'
paths:
- "**/*.rs"
- "**/Cargo.toml"
---
Comment thread
ScriptedAlchemy marked this conversation as resolved.

# Running impacted tests
Expand Down
2 changes: 1 addition & 1 deletion cursor-plugin/skills/memorize-subject/SKILL.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
name: memorize-subject
description: 'Research a subject with parallel read-only agents, then store durable facts in TraceDecay memory.'
description: 'Use to research a subject with parallel read-only agents, then store durable facts in TraceDecay memory.'
disable-model-invocation: true
---

Expand Down
2 changes: 1 addition & 1 deletion cursor-plugin/skills/tracedecay-audit-safety/SKILL.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
name: tracedecay-audit-safety
description: 'Audit the repo or a directory for ship-blocking risk panic sites, risk markers, dead code, and untested high-risk symbols.'
description: 'Use to audit the repo or a directory for ship-blocking risk, panic sites, risk markers, dead code, and untested high-risk symbols.'
disable-model-invocation: true
---

Expand Down
2 changes: 1 addition & 1 deletion cursor-plugin/skills/tracedecay-check-health/SKILL.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
name: tracedecay-check-health
description: 'Check code health — a scorecard for the repo or a directory with the worst offenders and a prioritized fix list.'
description: 'Use to check code health for the repo or a directory, including worst offenders and a prioritized fix list.'
disable-model-invocation: true
---

Expand Down
2 changes: 1 addition & 1 deletion cursor-plugin/skills/tracedecay-clean-dead-code/SKILL.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
name: tracedecay-clean-dead-code
description: 'Find and safely remove dead code, unused imports, and duplication via the TraceDecay code graph.'
description: 'Use to find and safely remove dead code, unused imports, and duplication via the TraceDecay code graph.'
disable-model-invocation: true
---

Expand Down
2 changes: 1 addition & 1 deletion cursor-plugin/skills/tracedecay-compare-branches/SKILL.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
name: tracedecay-compare-branches
description: 'Compare or search another git branch''s code graph without switching your checkout.'
description: 'Use to compare or search another git branch''s code graph without switching your checkout.'
disable-model-invocation: true
---

Expand Down
2 changes: 1 addition & 1 deletion cursor-plugin/skills/tracedecay-curate-memory/SKILL.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
name: tracedecay-curate-memory
description: 'Curate, update, delete, or inspect tracedecay memory facts and dashboard curation from an explicit slash workflow.'
description: 'Use to curate, update, delete, or inspect TraceDecay memory facts and dashboard curation from an explicit slash workflow.'
disable-model-invocation: true
---

Expand Down
2 changes: 1 addition & 1 deletion cursor-plugin/skills/tracedecay-draft-commit/SKILL.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
name: tracedecay-draft-commit
description: 'Draft a commit message, PR description, or changelog from the semantic meaning of the changes (drafts text only never commits or pushes).'
description: 'Use to draft a commit message, PR description, or changelog from semantic changes; drafts text only and never commits or pushes.'
disable-model-invocation: true
---

Expand Down
2 changes: 1 addition & 1 deletion cursor-plugin/skills/tracedecay-find-impact/SKILL.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
name: tracedecay-find-impact
description: 'Find the blast radius of a change impacted symbols, files, and the tests to run.'
description: 'Use to find the blast radius of a change, including impacted symbols, files, and the tests to run.'
disable-model-invocation: true
---

Expand Down
2 changes: 1 addition & 1 deletion cursor-plugin/skills/tracedecay-fix-build/SKILL.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
name: tracedecay-fix-build
description: 'Fix build and type errors — run or parse compiler diagnostics, map each one to its enclosing symbol with callers, then fix.'
description: 'Use to fix build and type errors by running or parsing diagnostics, mapping them to symbols with callers, then fixing.'
disable-model-invocation: true
---

Expand Down
2 changes: 1 addition & 1 deletion cursor-plugin/skills/tracedecay-map-architecture/SKILL.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
name: tracedecay-map-architecture
description: 'Map the architecture of the repo or a directory layered module map, dependency hotspots, and structural risks.'
description: 'Use to map repo or directory architecture, including layered modules, dependency hotspots, and structural risks.'
disable-model-invocation: true
---

Expand Down
2 changes: 1 addition & 1 deletion cursor-plugin/skills/tracedecay-port-code/SKILL.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
name: tracedecay-port-code
description: 'Port or migrate code between directories in dependency-safe order and track progress.'
description: 'Use to port or migrate code between directories in dependency-safe order and track progress.'
disable-model-invocation: true
---

Expand Down
2 changes: 1 addition & 1 deletion cursor-plugin/skills/tracedecay-recall-memory/SKILL.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
name: tracedecay-recall-memory
description: 'Recall prior decisions, durable facts, and past session conversations for this project.'
description: 'Use to recall prior decisions, durable facts, and past session conversations for this project.'
disable-model-invocation: true
---

Expand Down
2 changes: 1 addition & 1 deletion cursor-plugin/skills/tracedecay-review-diff/SKILL.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
name: tracedecay-review-diff
description: 'Review the current PR/diff for impact, risk, and quality via the TraceDecay code graph.'
description: 'Use to review the current PR or diff for impact, risk, and quality via the TraceDecay code graph.'
disable-model-invocation: true
---

Expand Down
2 changes: 1 addition & 1 deletion cursor-plugin/skills/tracedecay-test-changes/SKILL.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
name: tracedecay-test-changes
description: 'Test the current changes — run only the affected tests and map failures back to source.'
description: 'Use to test current changes by running only affected tests and mapping failures back to source.'
disable-model-invocation: true
---

Expand Down
46 changes: 41 additions & 5 deletions src/agents/codex.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1597,19 +1597,28 @@ mod tests {
/// frontmatter for invocation and ignores extra keys, and the skill bodies
/// reference host-neutral `tracedecay_*` MCP tools, so the same content is
/// correct in both hosts. Intentional per-skill divergences must be listed
/// (with a reason) in `CODEX_SKILL_DIVERGENCES`.
/// (with a reason) in one of the divergence allowlists below. Per-host
/// frontmatter schemas (allowed keys per plugin) are enforced separately
/// by `tests/plugin_skill_contract_test.rs`.
#[test]
fn codex_skills_match_the_cursor_source_for_parity() {
// Skills deliberately specialized for Codex (host-specific bodies that
// are not byte-compared against the Cursor source):
// are not compared against the Cursor source at all):
//
// - `curating-project-memory`: the Cursor source hands the "add a
// researched subject from scratch" flow off to the `memorizing-subject`
// skill, an explicit-invoke (`disable-model-invocation: true`) slash
// workflow Codex intentionally does not ship. The Codex copy inlines
// that flow's guardrails (read-only research, dedupe, cited facts,
// secret/PII rejection) instead of pointing at a skill absent here.
const CODEX_SKILL_DIVERGENCES: &[&str] = &["curating-project-memory"];
const CODEX_SKILL_BODY_DIVERGENCES: &[&str] = &["curating-project-memory"];
// Skills whose frontmatter legitimately diverges while the bodies must
// still mirror byte-for-byte (compared after stripping frontmatter):
//
// - `running-impacted-tests`: Cursor keeps `paths` frontmatter so its
// host can path-scope the skill, while Codex must omit that key to
// satisfy the Codex skill-creator quick_validate.py schema.
const CODEX_SKILL_FRONTMATTER_DIVERGENCES: &[&str] = &["running-impacted-tests"];
let root = repo_root();
for &skill in crate::hooks::CURSOR_PLUGIN_SKILLS {
let codex_path = root
Expand All @@ -1620,7 +1629,7 @@ mod tests {
codex_path.exists(),
"Codex plugin must ship the `{skill}` skill for parity with Cursor"
);
if CODEX_SKILL_DIVERGENCES.contains(&skill) {
if CODEX_SKILL_BODY_DIVERGENCES.contains(&skill) {
continue;
}
let cursor_body = std::fs::read_to_string(
Expand All @@ -1631,14 +1640,41 @@ mod tests {
.expect("cursor skill source should be readable");
let codex_body = std::fs::read_to_string(&codex_path)
.expect("codex skill source should be readable");
if CODEX_SKILL_FRONTMATTER_DIVERGENCES.contains(&skill) {
assert_eq!(
lines_after_frontmatter(&codex_body),
lines_after_frontmatter(&cursor_body),
"Codex `{skill}` skill body must mirror the Cursor source even though \
its frontmatter intentionally diverges"
);
continue;
}
assert_eq!(
codex_body, cursor_body,
"Codex `{skill}` skill must mirror the Cursor source (add it to \
CODEX_SKILL_DIVERGENCES if a host-specific version is intended)"
a CODEX_SKILL_*_DIVERGENCES list if a host-specific version is intended)"
);
}
}

/// Returns the lines following the closing `---` of the leading YAML
/// frontmatter. Line-based so CRLF checkouts compare like LF ones.
fn lines_after_frontmatter(contents: &str) -> Vec<&str> {
let mut lines = contents.lines();
assert_eq!(
lines.next(),
Some("---"),
"skill must open YAML frontmatter"
);
let mut lines = lines.skip_while(|line| line.trim() != "---");
assert_eq!(
lines.next().map(str::trim),
Some("---"),
"skill must close YAML frontmatter"
);
lines.collect()
}

/// Extracts the `<name>` from every `tracedecay:<name>` skill handoff in a
/// body. MCP tool calls use `tracedecay_*` (underscore) and are ignored.
fn skill_handoff_references(body: &str) -> Vec<String> {
Expand Down
40 changes: 18 additions & 22 deletions src/automation/hermes_skill_inventory.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ use serde_json::Value;
use crate::errors::{Result, TraceDecayError};

use super::hermes_config_projection::{load_hermes_yaml_projection, yaml_bool};
use super::skill_frontmatter::{parse_skill_frontmatter, SkillFrontmatterValue};

const SKILL_MD: &str = "SKILL.md";
pub(crate) const USAGE_FILE: &str = ".usage.json";
Expand Down Expand Up @@ -354,29 +355,24 @@ pub(crate) fn count_archive_entries(archive_dir: &Path) -> Result<usize> {
Ok(entries.filter_map(std::result::Result::ok).count())
}

/// Lenient view over [`parse_skill_frontmatter`]: hub skills without (or with
/// malformed) frontmatter degrade to an empty map so inventory listing never
/// fails, and only scalar values matter here (`name`/`id`/`description`/
/// `summary` are all scalars).
fn parse_frontmatter(contents: &str) -> BTreeMap<String, String> {
let mut lines = contents.lines();
if lines.next() != Some("---") {
return BTreeMap::new();
}
let mut values = BTreeMap::new();
for line in lines {
if line.trim() == "---" {
break;
}
let Some((key, value)) = line.split_once(':') else {
continue;
};
values.insert(
key.trim().to_ascii_lowercase(),
value
.trim()
.trim_matches('"')
.trim_matches('\'')
.to_string(),
);
}
values
parse_skill_frontmatter(contents)
.map(|fields| {
fields
.into_iter()
.filter_map(|(key, value)| match value {
SkillFrontmatterValue::Scalar(scalar) => {
Some((key.to_ascii_lowercase(), scalar))
}
SkillFrontmatterValue::Block(_) => None,
})
.collect()
})
.unwrap_or_default()
}

fn truncate_chars(value: &str, max_chars: usize) -> String {
Expand Down
1 change: 1 addition & 0 deletions src/automation/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ pub mod run_ledger;
pub mod runner;
pub mod scheduler;
pub mod session_reflector;
pub mod skill_frontmatter;
pub mod skill_targets;
pub mod skill_usage;
pub mod skill_writer;
Expand Down
Loading
Loading