Expand description
§Agent-First Slug
Rust slug generation with explicit caller configuration for path and URL path segments.
Ask your agent: “Add agent-first-slug to my project and use it to slugify titles for URL path segments.”
Start by choosing the target surface: a local filesystem path segment, a URL path segment, or a legacy slug format you need to preserve. The examples below show complete config values so the behavior is visible at the call site.
§Install the Library
cargo add agent-first-slug --no-default-features§Install the CLI
# prebuilt binary
brew install agentfirstkit/tap/afslug # macOS / Linux
scoop bucket add agentfirstkit https://github.com/agentfirstkit/scoop-bucket && scoop install afslug # Windows
# or from crates.io
cargo install agent-first-slugPrebuilt archives are also available from GitHub Releases.
§CLI
afslug applies SlugConfig::default() and emits one
AFDATA protocol event. JSON is the default; YAML and plain output are also
available.
afslug "Hello, 世界!"
# {"kind":"result","result":{"changed_from_input":true,"code":"slugify","slug":"hello-世界"},"trace":{}}
afslug "Hello, World!" --output plain
# kind=result result.changed_from_input=true result.code=slugify result.slug=hello-worldUse the Rust library when you need to customize character sets, dot handling, validation, truncation, empty output, or transliteration.
§Agent Skill
Use skills/agent-first-slug/SKILL.md to
teach a coding agent when to choose the Rust library or the default-only
afslug CLI, and how to preserve stable identifier behavior.
§Default Unicode Slugs
use agent_first_slug::{slugify, SlugConfig};
let config = SlugConfig::default();
assert_eq!(slugify("Hello", &config)?.slug, "hello");
assert_eq!(slugify("Hello World", &config)?.slug, "hello-world");
assert_eq!(slugify("Hello, world!!!", &config)?.slug, "hello-world");
assert_eq!(slugify("-- already -- spaced --", &config)?.slug, "already-spaced");
assert_eq!(slugify("!!!", &config)?.slug, "");Unicode letters and numbers are preserved by default:
use agent_first_slug::{slugify, SlugConfig};
let config = SlugConfig::default();
assert_eq!(
slugify("現在的Nobody,未來的Somebody!", &config)?.slug,
"現在的nobody-未來的somebody"
);
assert_eq!(
slugify("牙好,胃口就好,身体倍儿棒,吃嘛嘛香。", &config)?.slug,
"牙好-胃口就好-身体倍儿棒-吃嘛嘛香"
);
assert_eq!(slugify("お元気ですか?", &config)?.slug, "お元気ですか");§Local Filesystem Path Segment
Use this when the slug will be one segment in a local path. Slugify each segment separately; do not pass a full path through one slug call.
Requirements for this target:
- Reject empty output unless the caller provides a fallback.
- Reject
/,\\, Unicode whitespace, and control characters. - Reject
.and..to avoid current-directory and parent-directory meanings. - Prefer replacing dots unless the caller explicitly wants dots in filenames.
- Choose ASCII-only if the path must be portable across legacy filesystems or tools.
use agent_first_slug::{
slugify, AllowedCharacterSet, DotHandlingPolicy, EmptyOutputPolicy, SlugConfig,
SlugValidationPolicy, TransliterationPolicy,
};
let config = SlugConfig {
replacement_delimiter: '-',
lowercase_enabled: true,
max_slug_chars: None,
allowed_character_set: AllowedCharacterSet::UnicodeAlphanumericCharacters,
dot_handling_policy: DotHandlingPolicy::ReplaceAllDots,
transliteration_policy: TransliterationPolicy::None,
validation_policy: SlugValidationPolicy::LocalPathSegment,
empty_output_policy: EmptyOutputPolicy::UseFallbackSlug("fallback".to_string()),
};
assert_eq!(slugify("Ubuntu 16.04", &config)?.slug, "ubuntu-16-04");
assert_eq!(slugify("你好,世界", &config)?.slug, "你好-世界");
assert_eq!(slugify("!!!", &config)?.slug, "fallback");ASCII-only path segment with fallback:
use agent_first_slug::{
slugify, AllowedCharacterSet, DotHandlingPolicy, EmptyOutputPolicy, SlugConfig,
SlugValidationPolicy, TransliterationPolicy,
};
let config = SlugConfig {
replacement_delimiter: '-',
lowercase_enabled: true,
max_slug_chars: None,
allowed_character_set: AllowedCharacterSet::AsciiAlphanumericCharacters,
dot_handling_policy: DotHandlingPolicy::ReplaceAllDots,
transliteration_policy: TransliterationPolicy::None,
validation_policy: SlugValidationPolicy::LocalPathSegment,
empty_output_policy: EmptyOutputPolicy::UseFallbackSlug("fallback".to_string()),
};
assert_eq!(slugify("Hello 世界", &config)?.slug, "hello");
assert_eq!(slugify("Ubuntu 16.04", &config)?.slug, "ubuntu-16-04");
assert_eq!(slugify("你好,世界", &config)?.slug, "fallback");§URL Path Segment
Use this when the slug will be one segment in a URL path. The returned slug is raw UTF-8; percent-encode it or use a URL library’s path-segment API when building the final URL.
Requirements for this target:
- Reject empty output unless the caller provides a fallback.
- Reject
/,?,#, raw%, Unicode whitespace, controls, and\\. - Reject
.and..for route safety. - Preserve dots between decimal digits if version numbers matter.
- Do not manually concatenate unescaped slugs into URLs.
use agent_first_slug::{
slugify, AllowedCharacterSet, DotHandlingPolicy, EmptyOutputPolicy, SlugConfig,
SlugValidationPolicy, TransliterationPolicy,
};
let config = SlugConfig {
replacement_delimiter: '-',
lowercase_enabled: true,
max_slug_chars: None,
allowed_character_set: AllowedCharacterSet::UnicodeLettersAndDecimalDigits,
dot_handling_policy: DotHandlingPolicy::PreserveDotsBetweenDecimalDigits,
transliteration_policy: TransliterationPolicy::None,
validation_policy: SlugValidationPolicy::UrlPathSegment,
empty_output_policy: EmptyOutputPolicy::KeepEmptySlug,
};
assert_eq!(slugify("Ubuntu 16.04", &config)?.slug, "ubuntu-16.04");
assert_eq!(slugify("T.U.S.F.G.E.3.0.8", &config)?.slug, "t-u-s-f-g-e-3.0.8");
assert_eq!(slugify(".18 increased ! ", &config)?.slug, "18-increased");
assert_eq!(slugify("お元気ですか?", &config)?.slug, "お元気ですか");§Dot Handling
use agent_first_slug::{slugify, DotHandlingPolicy, SlugConfig};
let replace_all_dots = SlugConfig {
dot_handling_policy: DotHandlingPolicy::ReplaceAllDots,
..SlugConfig::default()
};
let preserve_all_dots = SlugConfig {
dot_handling_policy: DotHandlingPolicy::PreserveAllDots,
..SlugConfig::default()
};
let preserve_version_dots = SlugConfig {
dot_handling_policy: DotHandlingPolicy::PreserveDotsBetweenDecimalDigits,
..SlugConfig::default()
};
assert_eq!(slugify("Ubuntu 16.04", &replace_all_dots)?.slug, "ubuntu-16-04");
assert_eq!(slugify("A.B..C", &preserve_all_dots)?.slug, "a.b..c");
assert_eq!(slugify("T.U.S.F.G.E.3.0.8", &preserve_version_dots)?.slug, "t-u-s-f-g-e-3.0.8");§Transliteration
Transliteration is caller-provided, so legacy behavior can be expressed without a named preset in the library.
use agent_first_slug::{
slugify, AllowedCharacterSet, SlugConfig, TransliterationPolicy,
};
static MAP: &[(&str, &str)] = &[("Æ", "AE"), ("東京", "Tokyo")];
let config = SlugConfig {
allowed_character_set: AllowedCharacterSet::AsciiAlphanumericCharacters,
transliteration_policy: TransliterationPolicy::StaticReplacementMap(MAP),
..SlugConfig::default()
};
assert_eq!(slugify("Æther 東京", &config)?.slug, "aether-tokyo");§Truncation And Empty Output
max_slug_chars counts Unicode scalar values and runs after lowercasing; any
trailing delimiter the cut exposes is then stripped. Empty handling runs after
truncation, and a fallback is inserted verbatim — it is validated but is not
lowercased or truncated.
use agent_first_slug::{slugify, EmptyOutputPolicy, SlugConfig};
let truncated = SlugConfig {
max_slug_chars: Some(8),
..SlugConfig::default()
};
let fallback_after_truncation = SlugConfig {
max_slug_chars: Some(0),
empty_output_policy: EmptyOutputPolicy::UseFallbackSlug("fallback".to_string()),
..SlugConfig::default()
};
assert_eq!(slugify("Long Example", &truncated)?.slug, "long-exa");
assert_eq!(slugify("Long Example", &fallback_after_truncation)?.slug, "fallback");§Validation Only
use agent_first_slug::{validate_slug, SlugError, SlugValidationPolicy};
assert_eq!(validate_slug("safe-name", SlugValidationPolicy::LocalPathSegment), Ok(()));
assert_eq!(
validate_slug("a/b", SlugValidationPolicy::LocalPathSegment),
Err(SlugError::PathSegmentSeparator { character: '/' })
);
assert_eq!(
validate_slug("a?b", SlugValidationPolicy::UrlPathSegment),
Err(SlugError::UrlPathSegmentReservedCharacter { character: '?' })
);§License
MIT
Structs§
- Slug
Config - Rules used by
slugify. - Slug
Result - Slug generation result.
Enums§
- Allowed
Character Set - Character sets that can pass through the slug filter.
- DotHandling
Policy - Dot handling before all other filtered characters become delimiters.
- Empty
Output Policy - Behavior when the generated slug is empty.
- Slug
Error - Errors returned by slug generation or validation.
- Slug
Validation Policy - Optional validation applied after slug generation and empty-output handling.
- Transliteration
Policy - Transliteration applied before character filtering.
Functions§
- slugify
- Generate a slug from
inputusing explicit caller-provided rules. - validate_
slug - Validate
valueaccording to a standalone validation policy.