Compare commits

...

2 Commits

Author SHA1 Message Date
Gav Verma
4238cddb76 Address GH comments 2025-12-16 17:53:02 -08:00
Gav Verma
f9659eb477 Testing replacing skill trigger for automation (do not land - test PR only) 2025-12-16 17:40:19 -08:00
7 changed files with 60 additions and 9 deletions

View File

@@ -923,7 +923,7 @@ impl ChatComposer {
if !self.skills_enabled() {
return None;
}
Self::current_prefixed_token(&self.textarea, '$', true)
Self::current_prefixed_token(&self.textarea, '%', true)
}
/// Replace the active `@token` (the one under the cursor) with `path`.
@@ -998,7 +998,7 @@ impl ChatComposer {
.unwrap_or(after_cursor.len());
let end_idx = safe_cursor + end_rel_idx;
let inserted = format!("${skill_name}");
let inserted = format!("%{skill_name}");
let mut new_text =
String::with_capacity(text.len() - (end_idx - start_idx) + inserted.len() + 1);

View File

@@ -1604,7 +1604,7 @@ impl ChatWidget {
self.insert_str("@");
}
SlashCommand::Skills => {
self.insert_str("$");
self.insert_str("%");
}
SlashCommand::Status => {
self.add_status_output();
@@ -3513,7 +3513,8 @@ fn find_skill_mentions(text: &str, skills: &[SkillMetadata]) -> Vec<SkillMetadat
if seen.contains(&skill.name) {
continue;
}
let needle = format!("${}", skill.name);
let name = &skill.name;
let needle = format!("%{name}");
if text.contains(&needle) {
seen.insert(skill.name.clone());
matches.push(skill.clone());

View File

@@ -58,6 +58,7 @@ use codex_protocol::plan_tool::PlanItemArg;
use codex_protocol::plan_tool::StepStatus;
use codex_protocol::plan_tool::UpdatePlanArgs;
use codex_protocol::protocol::CodexErrorInfo;
use codex_protocol::protocol::SkillScope;
use codex_utils_absolute_path::AbsolutePathBuf;
use crossterm::event::KeyCode;
use crossterm::event::KeyEvent;
@@ -3350,3 +3351,26 @@ fn chatwidget_tall() {
.unwrap();
assert_snapshot!(term.backend().vt100().screen().contents());
}
#[test]
fn skill_mentions_use_percent_prefix() {
let deploy = SkillMetadata {
name: "deploy".to_string(),
description: "deploy skill".to_string(),
path: PathBuf::from("/tmp/deploy"),
scope: SkillScope::User,
};
let lint = SkillMetadata {
name: "lint".to_string(),
description: "lint skill".to_string(),
path: PathBuf::from("/tmp/lint"),
scope: SkillScope::User,
};
let skills = vec![deploy.clone(), lint.clone()];
let matches = find_skill_mentions("Use %deploy and %lint", &skills);
assert_eq!(matches, vec![deploy.clone(), lint.clone()]);
let matches = find_skill_mentions("Use $deploy only", &skills);
assert_eq!(matches, Vec::new());
}

View File

@@ -929,7 +929,7 @@ impl ChatComposer {
if !self.skills_enabled() {
return None;
}
Self::current_prefixed_token(&self.textarea, '$', true)
Self::current_prefixed_token(&self.textarea, '%', true)
}
/// Replace the active `@token` (the one under the cursor) with `path`.
@@ -1004,7 +1004,7 @@ impl ChatComposer {
.unwrap_or(after_cursor.len());
let end_idx = safe_cursor + end_rel_idx;
let inserted = format!("${skill_name}");
let inserted = format!("%{skill_name}");
let mut new_text =
String::with_capacity(text.len() - (end_idx - start_idx) + inserted.len() + 1);

View File

@@ -1604,7 +1604,7 @@ impl ChatWidget {
self.insert_str("@");
}
SlashCommand::Skills => {
self.insert_str("$");
self.insert_str("%");
}
SlashCommand::Status => {
self.add_status_output();
@@ -3518,7 +3518,8 @@ fn find_skill_mentions(text: &str, skills: &[SkillMetadata]) -> Vec<SkillMetadat
if seen.contains(&skill.name) {
continue;
}
let needle = format!("${}", skill.name);
let name = &skill.name;
let needle = format!("%{name}");
if text.contains(&needle) {
seen.insert(skill.name.clone());
matches.push(skill.clone());

View File

@@ -58,6 +58,7 @@ use codex_protocol::plan_tool::PlanItemArg;
use codex_protocol::plan_tool::StepStatus;
use codex_protocol::plan_tool::UpdatePlanArgs;
use codex_protocol::protocol::CodexErrorInfo;
use codex_protocol::protocol::SkillScope;
use codex_utils_absolute_path::AbsolutePathBuf;
use crossterm::event::KeyCode;
use crossterm::event::KeyEvent;
@@ -3350,3 +3351,27 @@ fn chatwidget_tall() {
.unwrap();
assert_snapshot!(term.backend().vt100().screen().contents());
}
#[test]
fn skill_mentions_use_percent_prefix() {
// Guard against treating shell/env-style `$name` tokens as skill mentions.
let deploy = SkillMetadata {
name: "deploy".to_string(),
description: "deploy skill".to_string(),
path: PathBuf::from("/tmp/deploy"),
scope: SkillScope::User,
};
let lint = SkillMetadata {
name: "lint".to_string(),
description: "lint skill".to_string(),
path: PathBuf::from("/tmp/lint"),
scope: SkillScope::User,
};
let skills = vec![deploy.clone(), lint.clone()];
let matches = find_skill_mentions("Use %deploy and %lint", &skills);
assert_eq!(matches, vec![deploy.clone(), lint.clone()]);
let matches = find_skill_mentions("Use $deploy only", &skills);
assert_eq!(matches, Vec::new());
}

View File

@@ -38,7 +38,7 @@ Skills are behind the experimental `skills` feature flag and are disabled by def
## Using skills
- Mention a skill by name in a message using `$<skill-name>`.
- Mention a skill by name in a message using `%<skill-name>`.
- In the TUI, you can also use `/skills` to browse and insert skills.
## Validation and errors