#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) enum Family {
Meta,
Editor,
Tuning,
Model,
Session,
Memory,
Navigator,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) enum Disposition {
Absorb,
Keep,
Panel,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) enum Surface {
Slash,
Native,
SectionAction,
Retired(&'static str),
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) enum Receipt {
None_,
Journal,
Event,
WorkspacePolicy,
Missing,
}
#[derive(Debug, Clone, Copy)]
pub(crate) struct SlashCommand {
pub(crate) name: &'static str,
pub(crate) aliases: &'static [&'static str],
pub(crate) family: Family,
pub(crate) disposition: Disposition,
pub(crate) receipt: Receipt,
pub(crate) surface: Surface,
}
impl SlashCommand {
pub(crate) fn tokens(&self) -> impl Iterator<Item = &'static str> + '_ {
std::iter::once(self.name).chain(self.aliases.iter().copied())
}
#[cfg(test)]
pub(crate) fn mutates(&self) -> bool {
!matches!(self.receipt, Receipt::None_)
}
}
const fn cmd(
name: &'static str,
aliases: &'static [&'static str],
family: Family,
disposition: Disposition,
receipt: Receipt,
) -> SlashCommand {
cmd_on(name, aliases, family, disposition, receipt, Surface::Slash)
}
const fn cmd_on(
name: &'static str,
aliases: &'static [&'static str],
family: Family,
disposition: Disposition,
receipt: Receipt,
surface: Surface,
) -> SlashCommand {
SlashCommand {
name,
aliases,
family,
disposition,
receipt,
surface,
}
}
pub(crate) const COMMANDS: &[SlashCommand] = &[
cmd(
"edit-mode",
&["vi", "emacs", "nano"],
Family::Editor,
Disposition::Absorb,
Receipt::Journal,
),
cmd_on(
"memory",
&[],
Family::Memory,
Disposition::Keep,
Receipt::None_,
Surface::Retired("/status memory"),
),
cmd_on(
"recall",
&[],
Family::Memory,
Disposition::Keep,
Receipt::None_,
Surface::Retired("/resume find"),
),
cmd(
"remember",
&[],
Family::Memory,
Disposition::Keep,
Receipt::Event,
),
cmd(
"search",
&[],
Family::Memory,
Disposition::Keep,
Receipt::None_,
),
cmd_on(
"byline",
&[],
Family::Meta,
Disposition::Keep,
Receipt::None_,
Surface::Retired("/status byline"),
),
cmd(
"settings",
&[],
Family::Meta,
Disposition::Keep,
Receipt::Journal,
),
cmd_on(
"config",
&[],
Family::Meta,
Disposition::Keep,
Receipt::None_,
Surface::Retired("/status config"),
),
cmd_on(
"docs",
&[],
Family::Meta,
Disposition::Keep,
Receipt::None_,
Surface::Retired("/help docs"),
),
cmd(
"exit",
&["quit"],
Family::Meta,
Disposition::Keep,
Receipt::None_,
),
cmd("help", &[], Family::Meta, Disposition::Keep, Receipt::None_),
cmd_on(
"info",
&[],
Family::Meta,
Disposition::Keep,
Receipt::None_,
Surface::Retired("/status"),
),
cmd(
"setup",
&[],
Family::Meta,
Disposition::Keep,
Receipt::Missing,
),
cmd(
"status",
&[],
Family::Meta,
Disposition::Keep,
Receipt::None_,
),
cmd_on(
"version",
&[],
Family::Meta,
Disposition::Keep,
Receipt::None_,
Surface::Retired("/status version"),
),
cmd(
"workspace",
&[],
Family::Meta,
Disposition::Panel,
Receipt::WorkspacePolicy,
),
cmd(
"backends",
&["backend"],
Family::Model,
Disposition::Panel,
Receipt::Missing,
),
cmd("dgx", &[], Family::Model, Disposition::Keep, Receipt::None_),
cmd(
"model",
&[],
Family::Model,
Disposition::Absorb,
Receipt::Missing,
),
cmd(
"models",
&[],
Family::Model,
Disposition::Keep,
Receipt::None_,
),
cmd(
"probe",
&[],
Family::Model,
Disposition::Keep,
Receipt::None_,
),
cmd_on(
"probe reset",
&[],
Family::Model,
Disposition::Keep,
Receipt::Missing,
Surface::SectionAction,
),
cmd(
"summarizer",
&[],
Family::Model,
Disposition::Absorb,
Receipt::Missing,
),
cmd(
"nav",
&[],
Family::Navigator,
Disposition::Keep,
Receipt::None_,
),
cmd_on(
"callees",
&[],
Family::Navigator,
Disposition::Keep,
Receipt::None_,
Surface::Retired("/nav callees"),
),
cmd_on(
"callers",
&[],
Family::Navigator,
Disposition::Keep,
Receipt::None_,
Surface::Retired("/nav callers"),
),
cmd_on(
"compare",
&[],
Family::Navigator,
Disposition::Keep,
Receipt::None_,
Surface::Retired("/nav compare"),
),
cmd_on(
"def",
&["goto"],
Family::Navigator,
Disposition::Keep,
Receipt::None_,
Surface::Retired("/nav def"),
),
cmd_on(
"export",
&[],
Family::Navigator,
Disposition::Keep,
Receipt::None_,
Surface::Retired("/nav export"),
),
cmd_on(
"hierarchy",
&[],
Family::Navigator,
Disposition::Keep,
Receipt::None_,
Surface::Retired("/nav hierarchy"),
),
cmd_on(
"impact",
&[],
Family::Navigator,
Disposition::Keep,
Receipt::None_,
Surface::Retired("/nav impact"),
),
cmd_on(
"implementations",
&["impls"],
Family::Navigator,
Disposition::Keep,
Receipt::None_,
Surface::Retired("/nav implementations"),
),
cmd_on(
"map",
&[],
Family::Navigator,
Disposition::Keep,
Receipt::None_,
Surface::Retired("/nav map"),
),
cmd_on(
"tests",
&[],
Family::Navigator,
Disposition::Keep,
Receipt::None_,
Surface::Retired("/nav tests"),
),
cmd_on(
"text",
&["grep"],
Family::Navigator,
Disposition::Keep,
Receipt::None_,
Surface::Retired("/nav text"),
),
cmd_on(
"type",
&["inspect"],
Family::Navigator,
Disposition::Keep,
Receipt::None_,
Surface::Retired("/nav type"),
),
cmd_on(
"uses",
&["refs"],
Family::Navigator,
Disposition::Keep,
Receipt::None_,
Surface::Retired("/nav uses"),
),
cmd(
"allow",
&[],
Family::Session,
Disposition::Keep,
Receipt::Missing,
),
cmd(
"compress",
&["compact"],
Family::Session,
Disposition::Keep,
Receipt::Event,
),
cmd(
"context",
&[],
Family::Session,
Disposition::Keep,
Receipt::Missing,
),
cmd_on(
"compaction",
&[],
Family::Session,
Disposition::Absorb,
Receipt::Journal,
Surface::Native,
),
cmd_on(
"conversation",
&[],
Family::Session,
Disposition::Keep,
Receipt::Missing,
Surface::Retired("/resume"),
),
cmd(
"crew",
&[],
Family::Session,
Disposition::Keep,
Receipt::Missing,
),
cmd(
"dock",
&[],
Family::Session,
Disposition::Keep,
Receipt::Event,
),
cmd(
"mcp",
&[],
Family::Session,
Disposition::Panel,
Receipt::Missing,
),
cmd(
"permissions",
&[],
Family::Session,
Disposition::Panel,
Receipt::Missing,
),
cmd(
"ocap",
&[],
Family::Session,
Disposition::Keep,
Receipt::Missing,
),
cmd(
"rename",
&["name"],
Family::Session,
Disposition::Keep,
Receipt::Event,
),
cmd(
"resume",
&[],
Family::Session,
Disposition::Keep,
Receipt::Event,
),
cmd(
"roadmap",
&[],
Family::Session,
Disposition::Keep,
Receipt::None_,
),
cmd(
"spill",
&[],
Family::Session,
Disposition::Keep,
Receipt::None_,
),
cmd(
"tab",
&[],
Family::Session,
Disposition::Keep,
Receipt::Missing,
),
cmd(
"transcript",
&[],
Family::Session,
Disposition::Keep,
Receipt::None_,
),
cmd_on(
"tree",
&[],
Family::Session,
Disposition::Keep,
Receipt::None_,
Surface::Retired("/roadmap tree"),
),
cmd(
"undo-lock",
&[],
Family::Session,
Disposition::Keep,
Receipt::Event,
),
cmd(
"new",
&["clear"],
Family::Session,
Disposition::Keep,
Receipt::Missing,
),
cmd(
"end",
&[],
Family::Session,
Disposition::Keep,
Receipt::Missing,
),
cmd(
"restart",
&[],
Family::Session,
Disposition::Keep,
Receipt::Missing,
),
cmd(
"discuss",
&["chat"],
Family::Session,
Disposition::Keep,
Receipt::None_,
),
cmd(
"start",
&[],
Family::Session,
Disposition::Keep,
Receipt::Missing,
),
cmd(
"cd",
&[],
Family::Session,
Disposition::Keep,
Receipt::Missing,
),
cmd_on(
"cognition",
&[],
Family::Tuning,
Disposition::Absorb,
Receipt::Journal,
Surface::Retired("/settings cognition"),
),
cmd_on(
"initiative",
&[],
Family::Tuning,
Disposition::Absorb,
Receipt::Journal,
Surface::Native,
),
cmd(
"psyche",
&[],
Family::Tuning,
Disposition::Keep,
Receipt::Journal,
),
cmd(
"detail",
&[],
Family::Tuning,
Disposition::Absorb,
Receipt::Journal,
),
cmd_on(
"loadout",
&[],
Family::Tuning,
Disposition::Keep,
Receipt::None_,
Surface::Retired("/status loadout"),
),
cmd(
"markdown",
&[],
Family::Tuning,
Disposition::Absorb,
Receipt::Journal,
),
cmd(
"mode",
&[],
Family::Tuning,
Disposition::Absorb,
Receipt::Journal,
),
cmd(
"nudge",
&[],
Family::Tuning,
Disposition::Absorb,
Receipt::Journal,
),
cmd(
"persona",
&[],
Family::Tuning,
Disposition::Absorb,
Receipt::Missing,
),
cmd_on(
"plan",
&[],
Family::Tuning,
Disposition::Keep,
Receipt::None_,
Surface::Retired("/roadmap"),
),
cmd(
"posture",
&[],
Family::Tuning,
Disposition::Absorb,
Receipt::Journal,
),
cmd_on(
"prefix",
&[],
Family::Tuning,
Disposition::Absorb,
Receipt::Journal,
Surface::Native,
),
cmd_on(
"git-author",
&[],
Family::Tuning,
Disposition::Absorb,
Receipt::Journal,
Surface::Native,
),
cmd_on(
"git-config",
&[],
Family::Tuning,
Disposition::Absorb,
Receipt::Journal,
Surface::Native,
),
cmd_on(
"git-signing",
&[],
Family::Tuning,
Disposition::Absorb,
Receipt::Journal,
Surface::Native,
),
cmd(
"prompt",
&[],
Family::Tuning,
Disposition::Absorb,
Receipt::Journal,
),
cmd_on(
"retrieval",
&[],
Family::Tuning,
Disposition::Keep,
Receipt::None_,
Surface::Retired("/nav retrieval"),
),
cmd(
"rounds",
&["tool-rounds", "max-rounds"],
Family::Tuning,
Disposition::Absorb,
Receipt::Journal,
),
cmd_on(
"tenacity",
&[],
Family::Tuning,
Disposition::Absorb,
Receipt::Journal,
Surface::Retired("/settings tenacity"),
),
cmd_on(
"thinking",
&[],
Family::Tuning,
Disposition::Absorb,
Receipt::Journal,
Surface::Retired("/settings thinking"),
),
];
#[cfg(test)]
pub(crate) fn slash_commands() -> impl Iterator<Item = &'static SlashCommand> {
COMMANDS.iter().filter(|c| c.surface == Surface::Slash)
}
#[cfg(test)]
pub(crate) fn slash_tokens() -> Vec<&'static str> {
let mut tokens: Vec<&'static str> = slash_commands().flat_map(SlashCommand::tokens).collect();
tokens.sort_unstable();
tokens.dedup();
tokens
}
#[cfg(test)]
pub(crate) fn all_tokens() -> Vec<&'static str> {
COMMANDS.iter().flat_map(SlashCommand::tokens).collect()
}
pub(crate) fn lookup(token: &str) -> Option<&'static SlashCommand> {
COMMANDS
.iter()
.find(|c| c.tokens().any(|t| t.eq_ignore_ascii_case(token)))
}
pub(crate) fn receipt_for(token: &str) -> Receipt {
lookup(token).map_or(Receipt::Missing, |c| c.receipt)
}
pub(crate) fn fallthrough_message(token: &str) -> String {
match lookup(token) {
Some(SlashCommand {
surface: Surface::Retired(dest),
..
}) => format!("/{token} is retired — use {dest}"),
Some(command) if command.disposition == Disposition::Absorb => format!(
"/{token} sets a value that now lives in /settings {}",
command.name
),
Some(command) if command.surface == Surface::SectionAction => {
format!("/{token} is an action inside /settings, not a top-level command")
}
Some(command) => format!(
"/{token} is a known command ({:?} family) but nothing handled it \
here — this is a routing bug, not a typo. Please report it.",
command.family
),
None => format!("unknown command: /{token} (try /help)"),
}
}
#[cfg(test)]
fn production(text: &str) -> String {
let lines: Vec<&str> = text.split('\n').collect();
let mut out = Vec::new();
let mut i = 0;
while i < lines.len() {
if lines[i].trim() == "#[cfg(test)]" {
let mut j = i + 1;
while j < lines.len() && !lines[j].contains('{') && !lines[j].trim_end().ends_with(';')
{
j += 1;
}
if j < lines.len() && lines[j].contains('{') {
let mut depth: i64 = 0;
while j < lines.len() {
depth +=
lines[j].matches('{').count() as i64 - lines[j].matches('}').count() as i64;
if depth <= 0 {
break;
}
j += 1;
}
}
i = j + 1;
continue;
}
out.push(lines[i]);
i += 1;
}
out.join("\n")
}
#[cfg(test)]
fn count_sites(src: &str) -> usize {
src.lines()
.filter(|line| !line.trim_start().starts_with("//"))
.map(|line| line.matches("trim_start_matches('/')").count())
.sum()
}
#[cfg(test)]
fn dispatch_sources() -> Vec<(&'static str, String)> {
vec![
("lib.rs", production(include_str!("lib.rs"))),
("chat.rs", production(include_str!("chat.rs"))),
(
"navigator_cmds.rs",
production(include_str!("navigator_cmds.rs")),
),
(
"roadmap_cmds.rs",
production(include_str!("roadmap_cmds.rs")),
),
]
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn the_production_cut_is_not_vacuous() {
for (name, src) in dispatch_sources() {
assert!(
src.len() > 5_000,
"{name}: the production cut read {} bytes",
src.len()
);
assert!(
count_sites(src.as_str()) > 0,
"{name}: no slash interception survived the cut — counted over \
CODE, so a surviving comment cannot stand in for one"
);
}
let lib = production(include_str!("lib.rs"));
assert!(
lib.len() < include_str!("lib.rs").len(),
"the cut removed nothing; it is not cutting at all"
);
}
#[test]
fn the_session_writes_the_round_cap_only_through_the_recorder() {
let chat = production(include_str!("chat.rs"));
let direct = chat.matches("set_session_tool_rounds(").count();
assert_eq!(
direct, 1,
"chat.rs writes the /rounds override directly {direct} times — exactly \
one is expected (the session-start reset). An operator's change goes \
through settings_form::apply_and_record or it leaves no receipt"
);
assert!(
chat.contains("settings_form::Field::Rounds"),
"no production caller applies the round cap through the form"
);
}
}
#[cfg(test)]
mod target_set_doc {
use super::*;
const DOC: &str = include_str!("../../docs/decisions/slash_command_target_set.md");
const BEGIN: &str = "<!-- BEGIN GENERATED: slash_registry::COMMANDS -->";
const END: &str = "<!-- END GENERATED -->";
fn disposition_cell(command: &SlashCommand) -> String {
match command.disposition {
Disposition::Absorb => format!("absorb → `/settings {}`", command.name),
Disposition::Keep => "keep — it performs".to_string(),
Disposition::Panel => "panel — a chooser, needs a region (#1979)".to_string(),
}
}
fn receipt_cell(command: &SlashCommand) -> &'static str {
match command.receipt {
Receipt::None_ => "— read-only",
Receipt::Journal => "`~/.newt/receipts.jsonl`",
Receipt::Event => "`~/.newt/events.jsonl` (chained)",
Receipt::WorkspacePolicy => {
"`ocap/session-grants.age` beside operator config (signed, encrypted, chained)"
}
Receipt::Missing => "**none — #1965**",
}
}
fn surface_cell(command: &SlashCommand) -> String {
match command.surface {
Surface::Slash => "`/` command".to_string(),
Surface::Native => "field of `/settings`".to_string(),
Surface::SectionAction => "action inside a section".to_string(),
Surface::Retired(dest) => format!("retired → `{dest}`"),
}
}
fn table() -> String {
let mut rows: Vec<&SlashCommand> = COMMANDS.iter().collect();
rows.sort_by_key(|c| (format!("{:?}", c.family), c.name));
let mut out = String::from(
"| command | also typed as | reached by | family | disposition | receipt |\n",
);
out.push_str("|---|---|---|---|---|---|\n");
for command in &rows {
let aliases = if command.aliases.is_empty() {
"—".to_string()
} else {
command
.aliases
.iter()
.map(|a| format!("`/{a}`"))
.collect::<Vec<_>>()
.join(" ")
};
let shown = match command.surface {
Surface::Native => format!("`/settings {}`", command.name),
_ => format!("`/{}`", command.name),
};
out.push_str(&format!(
"| {shown} | {aliases} | {} | {:?} | {} | {} |\n",
surface_cell(command),
command.family,
disposition_cell(command),
receipt_cell(command),
));
}
let count = |d: Disposition| COMMANDS.iter().filter(|c| c.disposition == d).count();
let receipts = |r: Receipt| COMMANDS.iter().filter(|c| c.receipt == r).count();
out.push_str(&format!(
"\n**{} registered, {} of them typed as `/` commands ({} tokens).** \
Absorb {} · keep {} · panel {}. \
Receipts: settings {} · events {} · workspace policies {} · read-only {} · **missing {}**.\n",
COMMANDS.len(),
slash_commands().count(),
slash_tokens().len(),
count(Disposition::Absorb),
count(Disposition::Keep),
count(Disposition::Panel),
receipts(Receipt::Journal),
receipts(Receipt::Event),
receipts(Receipt::WorkspacePolicy),
receipts(Receipt::None_),
receipts(Receipt::Missing),
));
out
}
fn generated_block(doc: &str) -> &str {
let start = doc.find(BEGIN).expect("the doc has no generated block") + BEGIN.len();
let end = doc.find(END).expect("the generated block is not closed");
doc[start..end].trim_matches('\n')
}
#[test]
fn the_target_set_doc_is_generated_from_this_registry() {
let want = table();
if std::env::var_os("UPDATE_DOCS").is_some() {
let path = concat!(
env!("CARGO_MANIFEST_DIR"),
"/../docs/decisions/slash_command_target_set.md"
);
let current = std::fs::read_to_string(path).expect("the doc exists");
let start = current.find(BEGIN).expect("marker") + BEGIN.len();
let end = current.find(END).expect("marker");
let updated = format!("{}\n{want}\n{}", ¤t[..start], ¤t[end..]);
std::fs::write(path, updated).expect("writable");
return;
}
assert_eq!(
generated_block(DOC),
want.trim_end(),
"docs/decisions/slash_command_target_set.md is stale — regenerate \
it with `UPDATE_DOCS=1 cargo test -p newt-tui \
the_target_set_doc_is_generated_from_this_registry`"
);
}
#[test]
fn the_generated_block_is_not_empty() {
let block = generated_block(DOC);
assert!(block.len() > 2_000, "{} bytes is not 65 rows", block.len());
for needle in [
"`/settings edit-mode`",
"keep — it performs",
"**none — #1965**",
"`~/.newt/receipts.jsonl`",
"`~/.newt/events.jsonl` (chained)",
] {
assert!(block.contains(needle), "the doc never says {needle}");
}
}
}
#[cfg(test)]
#[path = "slash_registry_tests/mod.rs"]
mod slash_registry_tests;