huskies: rename project from storkit to huskies

Rename all references from storkit to huskies across the codebase:
- .storkit/ directory → .huskies/
- Binary name, Cargo package name, Docker image references
- Server code, frontend code, config files, scripts
- Fix script/test to build frontend before cargo clippy/test
  so merge worktrees have frontend/dist available for RustEmbed

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Timmy
2026-04-03 16:12:52 +01:00
parent a7035b6ba7
commit 2d8ccb3eb6
572 changed files with 1340 additions and 1220 deletions
+33 -33
View File
@@ -22,9 +22,9 @@ use super::format::markdown_to_slack;
/// Payload sent by Slack for slash commands (application/x-www-form-urlencoded).
#[derive(Deserialize, Debug)]
pub struct SlackSlashCommandPayload {
/// The slash command that was invoked (e.g. "/storkit-status").
/// The slash command that was invoked (e.g. "/huskies-status").
pub command: String,
/// Any text typed after the command (e.g. "42" for "/storkit-show 42").
/// Any text typed after the command (e.g. "42" for "/huskies-show 42").
#[serde(default)]
pub text: String,
/// The user who invoked the command.
@@ -44,12 +44,12 @@ pub(super) struct SlashCommandResponse {
/// Map a Slack slash command name to the corresponding bot command keyword.
///
/// Supported: `/storkit-status`, `/storkit-cost`, `/storkit-show`,
/// `/storkit-git`, `/storkit-htop`.
/// Supported: `/huskies-status`, `/huskies-cost`, `/huskies-show`,
/// `/huskies-git`, `/huskies-htop`.
pub(super) fn slash_command_to_bot_keyword(command: &str) -> Option<&'static str> {
// Strip leading "/" and the "storkit-" prefix.
// Strip leading "/" and the "huskies-" prefix.
let name = command.strip_prefix('/').unwrap_or(command);
let keyword = name.strip_prefix("storkit-")?;
let keyword = name.strip_prefix("huskies-")?;
match keyword {
"status" => Some("status"),
"cost" => Some("cost"),
@@ -539,9 +539,9 @@ mod tests {
#[test]
fn parse_slash_command_payload() {
let body = "command=%2Fstorkit-status&text=&user_id=U123&channel_id=C456";
let body = "command=%2Fhuskies-status&text=&user_id=U123&channel_id=C456";
let payload: SlackSlashCommandPayload = serde_urlencoded::from_str(body).unwrap();
assert_eq!(payload.command, "/storkit-status");
assert_eq!(payload.command, "/huskies-status");
assert_eq!(payload.text, "");
assert_eq!(payload.user_id, "U123");
assert_eq!(payload.channel_id, "C456");
@@ -549,9 +549,9 @@ mod tests {
#[test]
fn parse_slash_command_payload_with_text() {
let body = "command=%2Fstorkit-show&text=42&user_id=U123&channel_id=C456";
let body = "command=%2Fhuskies-show&text=42&user_id=U123&channel_id=C456";
let payload: SlackSlashCommandPayload = serde_urlencoded::from_str(body).unwrap();
assert_eq!(payload.command, "/storkit-show");
assert_eq!(payload.command, "/huskies-show");
assert_eq!(payload.text, "42");
}
@@ -559,36 +559,36 @@ mod tests {
#[test]
fn slash_command_maps_status() {
assert_eq!(slash_command_to_bot_keyword("/storkit-status"), Some("status"));
assert_eq!(slash_command_to_bot_keyword("/huskies-status"), Some("status"));
}
#[test]
fn slash_command_maps_cost() {
assert_eq!(slash_command_to_bot_keyword("/storkit-cost"), Some("cost"));
assert_eq!(slash_command_to_bot_keyword("/huskies-cost"), Some("cost"));
}
#[test]
fn slash_command_maps_show() {
assert_eq!(slash_command_to_bot_keyword("/storkit-show"), Some("show"));
assert_eq!(slash_command_to_bot_keyword("/huskies-show"), Some("show"));
}
#[test]
fn slash_command_maps_git() {
assert_eq!(slash_command_to_bot_keyword("/storkit-git"), Some("git"));
assert_eq!(slash_command_to_bot_keyword("/huskies-git"), Some("git"));
}
#[test]
fn slash_command_maps_htop() {
assert_eq!(slash_command_to_bot_keyword("/storkit-htop"), Some("htop"));
assert_eq!(slash_command_to_bot_keyword("/huskies-htop"), Some("htop"));
}
#[test]
fn slash_command_unknown_returns_none() {
assert_eq!(slash_command_to_bot_keyword("/storkit-unknown"), None);
assert_eq!(slash_command_to_bot_keyword("/huskies-unknown"), None);
}
#[test]
fn slash_command_non_storkit_returns_none() {
fn slash_command_non_huskies_returns_none() {
assert_eq!(slash_command_to_bot_keyword("/other-command"), None);
}
@@ -628,8 +628,8 @@ mod tests {
let room_id = "C01ABCDEF".to_string();
// Simulate what slash_command_receive does: build a synthetic message.
let bot_name = "Storkit";
let keyword = slash_command_to_bot_keyword("/storkit-status").unwrap();
let bot_name = "Huskies";
let keyword = slash_command_to_bot_keyword("/huskies-status").unwrap();
let synthetic = format!("{bot_name} {keyword}");
let dispatch = CommandDispatch {
@@ -654,9 +654,9 @@ mod tests {
let ambient_rooms = test_ambient_rooms();
let room_id = "C01ABCDEF".to_string();
let bot_name = "Storkit";
let keyword = slash_command_to_bot_keyword("/storkit-show").unwrap();
// Simulate /storkit-show with text "999"
let bot_name = "Huskies";
let keyword = slash_command_to_bot_keyword("/huskies-show").unwrap();
// Simulate /huskies-show with text "999"
let synthetic = format!("{bot_name} {keyword} 999");
let dispatch = CommandDispatch {
@@ -679,11 +679,11 @@ mod tests {
#[test]
fn rebuild_command_extracted_from_slack_message() {
let result = crate::chat::transport::matrix::rebuild::extract_rebuild_command(
"Storkit rebuild",
"Storkit",
"Huskies rebuild",
"Huskies",
"slack-bot",
);
assert!(result.is_some(), "'Storkit rebuild' should be recognised");
assert!(result.is_some(), "'Huskies rebuild' should be recognised");
}
#[test]
@@ -691,7 +691,7 @@ mod tests {
// Slack slash-command synthetic messages may not include a bot mention.
let result = crate::chat::transport::matrix::rebuild::extract_rebuild_command(
"rebuild",
"Storkit",
"Huskies",
"slack-bot",
);
assert!(result.is_some(), "plain 'rebuild' should be recognised");
@@ -700,8 +700,8 @@ mod tests {
#[test]
fn non_rebuild_slack_message_not_extracted() {
let result = crate::chat::transport::matrix::rebuild::extract_rebuild_command(
"Storkit status",
"Storkit",
"Huskies status",
"Huskies",
"slack-bot",
);
assert!(result.is_none(), "'status' should not be recognised as rebuild");
@@ -712,18 +712,18 @@ mod tests {
#[test]
fn reset_command_extracted_from_slack_message() {
let result = crate::chat::transport::matrix::reset::extract_reset_command(
"Storkit reset",
"Storkit",
"Huskies reset",
"Huskies",
"slack-bot",
);
assert!(result.is_some(), "'Storkit reset' should be recognised");
assert!(result.is_some(), "'Huskies reset' should be recognised");
}
#[test]
fn reset_command_extracted_plain_no_mention() {
let result = crate::chat::transport::matrix::reset::extract_reset_command(
"reset",
"Storkit",
"Huskies",
"slack-bot",
);
assert!(result.is_some(), "plain 'reset' should be recognised");
@@ -750,7 +750,7 @@ mod tests {
}));
let tmp = tempfile::tempdir().unwrap();
let sk = tmp.path().join(".storkit");
let sk = tmp.path().join(".huskies");
std::fs::create_dir_all(&sk).unwrap();
{
+3 -3
View File
@@ -18,7 +18,7 @@ struct PersistedSlackHistory {
}
/// Path to the persisted Slack conversation history file.
const SLACK_HISTORY_FILE: &str = ".storkit/slack_history.json";
const SLACK_HISTORY_FILE: &str = ".huskies/slack_history.json";
/// Load Slack conversation history from disk.
pub fn load_slack_history(project_root: &std::path::Path) -> HashMap<String, RoomConversation> {
@@ -66,7 +66,7 @@ mod tests {
#[test]
fn save_and_load_slack_history_round_trips() {
let tmp = tempfile::tempdir().unwrap();
let sk = tmp.path().join(".storkit");
let sk = tmp.path().join(".huskies");
std::fs::create_dir_all(&sk).unwrap();
let mut history = HashMap::new();
@@ -110,7 +110,7 @@ mod tests {
#[test]
fn load_slack_history_returns_empty_on_invalid_json() {
let tmp = tempfile::tempdir().unwrap();
let sk = tmp.path().join(".storkit");
let sk = tmp.path().join(".huskies");
std::fs::create_dir_all(&sk).unwrap();
std::fs::write(sk.join("slack_history.json"), "not json {{{").unwrap();
let history = load_slack_history(tmp.path());