diff --git a/server/src/chat/commands/loc.rs b/server/src/chat/commands/loc.rs index 0500a569..1f71eafd 100644 --- a/server/src/chat/commands/loc.rs +++ b/server/src/chat/commands/loc.rs @@ -43,9 +43,16 @@ pub(super) fn handle_loc(ctx: &CommandContext) -> Option { if SKIP_DIRS.iter().any(|s| *s == name.as_ref()) { return false; } - // Skip .storkit/worktrees - let path = e.path().to_string_lossy(); - if SKIP_PATH_COMPONENTS.iter().any(|s| path.contains(s)) { + // Skip .storkit/worktrees — use relative path so the check + // doesn't exclude the project root itself when running + // from inside a worktree (where the absolute path contains + // ".storkit/worktrees"). + let rel = e + .path() + .strip_prefix(ctx.project_root) + .map(|p| p.to_string_lossy().into_owned()) + .unwrap_or_default(); + if SKIP_PATH_COMPONENTS.iter().any(|s| rel.contains(s)) { return false; } } @@ -241,4 +248,36 @@ mod tests { "output must not include paths inside worktrees: {output}" ); } + + #[test] + fn loc_zero_returns_usage() { + let agents = Arc::new(AgentPool::new_test(3000)); + let ambient_rooms = Arc::new(Mutex::new(HashSet::new())); + let repo_root = std::path::Path::new(env!("CARGO_MANIFEST_DIR")) + .parent() + .unwrap_or(std::path::Path::new(".")); + let ctx = make_ctx(&agents, &ambient_rooms, repo_root, "0"); + let output = handle_loc(&ctx).unwrap(); + assert!( + output.contains("Usage"), + "loc 0 should show usage (zero is not a valid count): {output}" + ); + } + + #[test] + fn loc_works_via_full_dispatch() { + // Verifies the command is reachable through the same dispatch path used + // by all transports (Matrix, WhatsApp, Slack). + let result = super::super::tests::try_cmd_addressed( + "Timmy", + "@timmy:homeserver.local", + "@timmy loc 1", + ); + // /tmp has no source files, so we expect either "No source files found" + // or a ranked result — either way the command must respond (not None). + assert!( + result.is_some(), + "loc command must respond via dispatch (not fall through to LLM)" + ); + } }