mirror of
https://github.com/openai/codex.git
synced 2026-09-07 15:40:00 +00:00
## What changed - Convert supported plugin command Markdown into generated skills when a plugin is installed. - Read command sources from the manifest's `commands` field, falling back to the plugin's `commands/` directory. - Load generated command skills alongside native plugin skills while preferring native skills with the same name. - Skip commands with unsupported templates, missing descriptions, name collisions, or generated skills larger than 4 KB, and keep migration failures from blocking plugin installation. ## Testing - Cover default and manifest-configured command paths, stale generated skill cleanup, size limits, native-skill precedence, invalid manifest fields, and non-fatal migration errors. GitOrigin-RevId: 67d910048544b160b191066569bca69b2b951ebc
96 lines
2.8 KiB
Rust
96 lines
2.8 KiB
Rust
use super::RewriteProfile;
|
|
|
|
pub(super) fn rewrite_terms(content: &str, profile: RewriteProfile) -> String {
|
|
let mut rewritten =
|
|
replace_case_insensitive_with_boundaries(content, profile.doc_file_name, "AGENTS.md");
|
|
for from in profile.term_variants {
|
|
rewritten = replace_case_insensitive_with_boundaries(&rewritten, from, "Codex");
|
|
}
|
|
for from in profile.case_sensitive_term_variants {
|
|
rewritten = replace_with_boundaries(&rewritten, from, "Codex");
|
|
}
|
|
rewritten
|
|
}
|
|
|
|
fn replace_with_boundaries(input: &str, needle: &str, replacement: &str) -> String {
|
|
if needle.is_empty() {
|
|
return input.to_string();
|
|
}
|
|
|
|
replace_with_boundaries_impl(input, needle, replacement, input)
|
|
}
|
|
|
|
fn replace_case_insensitive_with_boundaries(
|
|
input: &str,
|
|
needle: &str,
|
|
replacement: &str,
|
|
) -> String {
|
|
let needle_lower = needle.to_ascii_lowercase();
|
|
if needle_lower.is_empty() {
|
|
return input.to_string();
|
|
}
|
|
let haystack_lower = input.to_ascii_lowercase();
|
|
replace_with_boundaries_impl(input, &needle_lower, replacement, &haystack_lower)
|
|
}
|
|
|
|
fn replace_with_boundaries_impl(
|
|
input: &str,
|
|
needle: &str,
|
|
replacement: &str,
|
|
searchable_input: &str,
|
|
) -> String {
|
|
let bytes = input.as_bytes();
|
|
let mut output = String::with_capacity(input.len());
|
|
let mut last_emitted = 0usize;
|
|
let mut search_start = 0usize;
|
|
|
|
while let Some(relative_pos) = searchable_input[search_start..].find(needle) {
|
|
let start = search_start + relative_pos;
|
|
let end = start + needle.len();
|
|
let boundary_before = start == 0 || !is_word_byte(bytes[start - 1]);
|
|
let boundary_after = end == bytes.len() || !is_word_byte(bytes[end]);
|
|
|
|
if boundary_before && boundary_after {
|
|
output.push_str(&input[last_emitted..start]);
|
|
output.push_str(replacement);
|
|
last_emitted = end;
|
|
}
|
|
search_start = start + 1;
|
|
}
|
|
|
|
if last_emitted == 0 {
|
|
return input.to_string();
|
|
}
|
|
output.push_str(&input[last_emitted..]);
|
|
output
|
|
}
|
|
|
|
pub(super) fn yaml_string(value: &str) -> String {
|
|
format!("\"{}\"", value.replace('\\', "\\\\").replace('"', "\\\""))
|
|
}
|
|
|
|
pub(super) fn slugify_name(value: &str) -> String {
|
|
let mut slug = String::new();
|
|
let mut last_was_dash = false;
|
|
for ch in value.chars() {
|
|
if ch.is_ascii_alphanumeric() {
|
|
slug.push(ch.to_ascii_lowercase());
|
|
last_was_dash = false;
|
|
} else if !last_was_dash {
|
|
slug.push('-');
|
|
last_was_dash = true;
|
|
}
|
|
}
|
|
|
|
let slug = slug.trim_matches('-').to_string();
|
|
if slug.is_empty() {
|
|
"migrated".to_string()
|
|
} else {
|
|
slug
|
|
}
|
|
}
|
|
|
|
fn is_word_byte(byte: u8) -> bool {
|
|
byte.is_ascii_alphanumeric() || byte == b'_'
|
|
}
|