generated from nhcarrigan/template
d8cf5504d6
## Summary ### New Features - **Claude Sonnet 4.6 support** — added `claude-sonnet-4-6` as a selectable model in the config sidebar - **Anime girl characters for subagents** — each subagent in the agent monitor is automatically assigned one of six characters (Amari, Keiko, Minori, Reina, Tatsumi, Yumiko) with a unique name, CDN avatar, title, and lore-flavoured description; assignment avoids duplicates when possible - **"Meet the Team" cast panel** — a new modal accessible from the status bar introduces the full cast: Naomi (Chief hEx-ecutive Officer), Hikari (Chief Operating Officer), and the six subagent girls with their C-suite titles and character bios ### Bug Fixes - **"Already running" error on invalid working directory** — if a spawned Claude process exits unexpectedly (e.g. because the working directory doesn't exist), `try_wait()` now detects the stale handle and clears it before allowing a restart - **Working directory pre-validation** — on Windows, the app now runs `wsl -e test -d <dir>` before launching Claude; invalid directories surface a clear error immediately - **WSL binary detection** — on Windows, `wsl -e bash -lc "which claude"` is used to probe for the Claude binary inside WSL; on Linux/WSLg, `bash -lc "which claude"` is used as a login-shell fallback so GUI apps find the binary even without shell PATH - **WSL detection fix for production builds** — `detect_wsl()` now short-circuits at compile time on Windows targets, preventing inherited `WSL_DISTRO_NAME` env vars from misrouting native Windows binaries through the Linux code path ✨ This PR was crafted with love by Hikari~ 🌸 Reviewed-on: #149 Co-authored-by: Hikari <hikari@nhcarrigan.com> Co-committed-by: Hikari <hikari@nhcarrigan.com>
74 lines
2.6 KiB
TypeScript
74 lines
2.6 KiB
TypeScript
import { describe, it, expect } from "vitest";
|
|
import { CHARACTER_POOL, assignCharacter } from "./agentCharacters";
|
|
|
|
describe("agentCharacters", () => {
|
|
describe("CHARACTER_POOL", () => {
|
|
it("contains exactly 6 characters", () => {
|
|
expect(CHARACTER_POOL).toHaveLength(6);
|
|
});
|
|
|
|
it("each character has a name, avatar, title, and description", () => {
|
|
for (const character of CHARACTER_POOL) {
|
|
expect(character.name).toBeTruthy();
|
|
expect(character.avatar).toBeTruthy();
|
|
expect(character.avatar).toMatch(/^https:\/\//u);
|
|
expect(character.title).toBeTruthy();
|
|
expect(character.description).toBeTruthy();
|
|
}
|
|
});
|
|
|
|
it("all names are unique", () => {
|
|
const names = CHARACTER_POOL.map((c) => c.name);
|
|
const uniqueNames = new Set(names);
|
|
expect(uniqueNames.size).toBe(CHARACTER_POOL.length);
|
|
});
|
|
});
|
|
|
|
describe("assignCharacter", () => {
|
|
it("returns a character from the pool", () => {
|
|
const character = assignCharacter([]);
|
|
const names = CHARACTER_POOL.map((c) => c.name);
|
|
expect(names).toContain(character.name);
|
|
});
|
|
|
|
it("avoids names already in use when possible", () => {
|
|
const takenNames = ["Amari", "Keiko", "Minori", "Reina", "Tatsumi"];
|
|
// Run many times to confirm we never get a taken name
|
|
for (let i = 0; i < 50; i++) {
|
|
const character = assignCharacter(takenNames);
|
|
expect(takenNames).not.toContain(character.name);
|
|
expect(character.name).toBe("Yumiko");
|
|
}
|
|
});
|
|
|
|
it("picks from the full pool when all 6 names are taken", () => {
|
|
const allNames = CHARACTER_POOL.map((c) => c.name);
|
|
const seen = new Set<string>();
|
|
// Run enough times that we'd statistically see variety
|
|
for (let i = 0; i < 100; i++) {
|
|
const character = assignCharacter(allNames);
|
|
seen.add(character.name);
|
|
}
|
|
// Should still pick valid characters
|
|
for (const name of seen) {
|
|
expect(allNames).toContain(name);
|
|
}
|
|
// With 100 runs and 6 characters, we should see at least 2 distinct names
|
|
expect(seen.size).toBeGreaterThan(1);
|
|
});
|
|
|
|
it("returns a character with name, avatar, title, and description", () => {
|
|
const character = assignCharacter([]);
|
|
expect(character.name).toBeTruthy();
|
|
expect(character.avatar).toBeTruthy();
|
|
expect(character.title).toBeTruthy();
|
|
expect(character.description).toBeTruthy();
|
|
});
|
|
|
|
it("works when the active list is empty", () => {
|
|
const character = assignCharacter([]);
|
|
expect(character).toBeDefined();
|
|
});
|
|
});
|
|
});
|