Skip to content

Commit fc9a9b5

Browse files
refactor(skills): dedup marked-block splice, derive prompt targets
- C: collapse the twin if-let/else-if-let arms in `replace_or_append_marked_block` — resolve the target range once (this target's slugged block, else the legacy unslugged one) and splice via a single `splice_range` helper. - D: derive the prompt-block target subset in `remove_all_marked_blocks` from `ALL_SKILL_INSTALL_TARGETS` via a new `SkillInstallTarget::writes_prompt_index()` predicate instead of a hardcoded 5-entry list, so the two stay in sync. The derived set equals the previous {Claude, Agents, OpenCode, Kimi, Kiro} exactly. - K: point the local `PROMPT_INDEX_START` at the identical `prompt_rules::SKILL_INDEX_START` literal. `remove_range` and `splice_out` differ in trailing-newline normalization, so they are left separate. Co-Authored-By: Claude <noreply@anthropic.com>
1 parent 11d15e2 commit fc9a9b5

2 files changed

Lines changed: 37 additions & 32 deletions

File tree

src/automation/managed_skill_model.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,14 @@ impl SkillInstallTarget {
3232
matches!(self, Self::Cursor | Self::Codex)
3333
}
3434

35+
/// True for hosts that reconcile their managed-skill listing as a
36+
/// marker-gated block inside a prompt file. Native-overlay hosts
37+
/// (Cursor/Codex) deploy a skills directory instead, and Hermes owns its
38+
/// own curation — neither writes a prompt-index block.
39+
pub fn writes_prompt_index(self) -> bool {
40+
!self.is_native_overlay() && self != Self::Hermes
41+
}
42+
3543
pub fn prompt_label(self) -> &'static str {
3644
match self {
3745
Self::Cursor => "Cursor",

src/automation/skill_targets.rs

Lines changed: 29 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,9 @@ use crate::errors::{Result, TraceDecayError};
1515

1616
const NATIVE_NAMESPACE_DIR: &str = "agent-managed";
1717
const NATIVE_MANIFEST_FILE: &str = ".tracedecay-managed-skills.json";
18-
const PROMPT_INDEX_START: &str = "<!-- TRACEDECAY MANAGED SKILLS START -->";
18+
/// The unslugged legacy managed-skill start marker. Reuses the same literal the
19+
/// prompt-rules block-splicer stops at, keeping the two in sync.
20+
const PROMPT_INDEX_START: &str = crate::agents::prompt_rules::SKILL_INDEX_START;
1921
const PROMPT_INDEX_END: &str = "<!-- TRACEDECAY MANAGED SKILLS END -->";
2022

2123
const ALL_SKILL_INSTALL_TARGETS: [SkillInstallTarget; 8] = [
@@ -305,30 +307,13 @@ fn replace_or_append_marked_block(
305307
block: &str,
306308
) -> Result<String> {
307309
let (start_marker, end_marker) = prompt_index_markers(target);
308-
if let Some((start, end)) = marked_block_range(existing, &start_marker, &end_marker)? {
309-
let mut updated = String::new();
310-
updated.push_str(existing[..start].trim_end());
311-
updated.push_str("\n\n");
312-
updated.push_str(block.trim_end());
313-
updated.push_str("\n\n");
314-
updated.push_str(existing[end..].trim_start());
315-
if !updated.ends_with('\n') {
316-
updated.push('\n');
317-
}
318-
Ok(updated)
319-
} else if let Some((start, end)) =
320-
marked_block_range(existing, PROMPT_INDEX_START, PROMPT_INDEX_END)?
321-
{
322-
let mut updated = String::new();
323-
updated.push_str(existing[..start].trim_end());
324-
updated.push_str("\n\n");
325-
updated.push_str(block.trim_end());
326-
updated.push_str("\n\n");
327-
updated.push_str(existing[end..].trim_start());
328-
if !updated.ends_with('\n') {
329-
updated.push('\n');
330-
}
331-
Ok(updated)
310+
// Prefer this target's slugged block; fall back to the legacy unslugged one.
311+
let existing_range = match marked_block_range(existing, &start_marker, &end_marker)? {
312+
Some(range) => Some(range),
313+
None => marked_block_range(existing, PROMPT_INDEX_START, PROMPT_INDEX_END)?,
314+
};
315+
if let Some((start, end)) = existing_range {
316+
Ok(splice_range(existing, start, end, block))
332317
} else {
333318
let mut updated = String::new();
334319
updated.push_str(existing.trim_end());
@@ -340,6 +325,21 @@ fn replace_or_append_marked_block(
340325
}
341326
}
342327

328+
/// Replace `existing[start..end]` with `block`, normalizing surrounding blank
329+
/// lines and guaranteeing a trailing newline.
330+
fn splice_range(existing: &str, start: usize, end: usize, block: &str) -> String {
331+
let mut updated = String::new();
332+
updated.push_str(existing[..start].trim_end());
333+
updated.push_str("\n\n");
334+
updated.push_str(block.trim_end());
335+
updated.push_str("\n\n");
336+
updated.push_str(existing[end..].trim_start());
337+
if !updated.ends_with('\n') {
338+
updated.push('\n');
339+
}
340+
updated
341+
}
342+
343343
fn remove_marked_block_for_target(existing: &str, target: SkillInstallTarget) -> Result<String> {
344344
let (start_marker, end_marker) = prompt_index_markers(target);
345345
if let Some((start, end)) = marked_block_range(existing, &start_marker, &end_marker)? {
@@ -372,13 +372,10 @@ fn has_other_slugged_block(existing: &str, target: SkillInstallTarget) -> bool {
372372

373373
fn remove_all_marked_blocks(existing: &str) -> Result<String> {
374374
let mut updated = existing.to_string();
375-
for target in [
376-
SkillInstallTarget::Claude,
377-
SkillInstallTarget::Agents,
378-
SkillInstallTarget::OpenCode,
379-
SkillInstallTarget::Kimi,
380-
SkillInstallTarget::Kiro,
381-
] {
375+
for target in ALL_SKILL_INSTALL_TARGETS
376+
.into_iter()
377+
.filter(|target| target.writes_prompt_index())
378+
{
382379
updated = remove_marked_block_for_target(&updated, target)?;
383380
}
384381
if let Some((start, end)) = marked_block_range(&updated, PROMPT_INDEX_START, PROMPT_INDEX_END)?

0 commit comments

Comments
 (0)