refactor(HAI-116): rename kb to hai across all packages, CLI, and docs
- Rename npm packages from @kb/* to @hai/* and update all workspace references - Rename CLI binary from kb to hai and config directory from .kb to .hai - Update dashboard UI branding, titles, and references from kb to hai - Update all test files, CI workflows, and documentation to reflect new naming - Run comprehensive grep verification to ensure no stale kb references remain
This commit is contained in:
@@ -1,12 +1,12 @@
|
||||
import { describe, it, expect } from "vitest";
|
||||
import { render, screen, fireEvent } from "@testing-library/react";
|
||||
import { AgentLogViewer } from "../AgentLogViewer";
|
||||
import type { AgentLogEntry } from "@hai/core";
|
||||
import type { AgentLogEntry } from "@kb/core";
|
||||
|
||||
function makeEntry(overrides: Partial<AgentLogEntry> = {}): AgentLogEntry {
|
||||
return {
|
||||
timestamp: "2026-01-01T00:00:00Z",
|
||||
taskId: "HAI-001",
|
||||
taskId: "KB-001",
|
||||
text: "Hello world",
|
||||
type: "text",
|
||||
...overrides,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { describe, it, expect, vi } from "vitest";
|
||||
import { render, screen } from "@testing-library/react";
|
||||
import { Board } from "../Board";
|
||||
import { COLUMNS } from "@hai/core";
|
||||
import { COLUMNS } from "@kb/core";
|
||||
|
||||
// Mock child components so we only test Board's own rendering
|
||||
vi.mock("../Column", () => ({
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { describe, it, expect, vi } from "vitest";
|
||||
import { render, screen } from "@testing-library/react";
|
||||
import { Column } from "../Column";
|
||||
import type { Task, Column as ColumnType } from "@hai/core";
|
||||
import type { Task, Column as ColumnType } from "@kb/core";
|
||||
|
||||
// Mock child components to keep tests focused on the Column badge behavior
|
||||
vi.mock("../TaskCard", () => ({
|
||||
@@ -43,7 +43,7 @@ const defaultProps = {
|
||||
|
||||
describe("Column count-flash", () => {
|
||||
it("does not apply count-flash class on initial render", () => {
|
||||
const tasks = [makeTask("HAI-001")];
|
||||
const tasks = [makeTask("KB-001")];
|
||||
render(<Column {...defaultProps} tasks={tasks} />);
|
||||
|
||||
const badge = screen.getByText("1");
|
||||
@@ -52,10 +52,10 @@ describe("Column count-flash", () => {
|
||||
});
|
||||
|
||||
it("applies count-flash class when task count increases", () => {
|
||||
const tasks = [makeTask("HAI-001")];
|
||||
const tasks = [makeTask("KB-001")];
|
||||
const { rerender } = render(<Column {...defaultProps} tasks={tasks} />);
|
||||
|
||||
const moreTasks = [makeTask("HAI-001"), makeTask("HAI-002")];
|
||||
const moreTasks = [makeTask("KB-001"), makeTask("KB-002")];
|
||||
rerender(<Column {...defaultProps} tasks={moreTasks} />);
|
||||
|
||||
const badge = screen.getByText("2");
|
||||
@@ -63,10 +63,10 @@ describe("Column count-flash", () => {
|
||||
});
|
||||
|
||||
it("does not apply count-flash class when task count decreases", () => {
|
||||
const tasks = [makeTask("HAI-001"), makeTask("HAI-002")];
|
||||
const tasks = [makeTask("KB-001"), makeTask("KB-002")];
|
||||
const { rerender } = render(<Column {...defaultProps} tasks={tasks} />);
|
||||
|
||||
const fewerTasks = [makeTask("HAI-001")];
|
||||
const fewerTasks = [makeTask("KB-001")];
|
||||
rerender(<Column {...defaultProps} tasks={fewerTasks} />);
|
||||
|
||||
const badge = screen.getByText("1");
|
||||
|
||||
@@ -5,7 +5,7 @@ import { Header } from "../Header";
|
||||
describe("Header", () => {
|
||||
it("renders a logo image with correct src and alt", () => {
|
||||
render(<Header />);
|
||||
const logo = screen.getByAltText("hai logo");
|
||||
const logo = screen.getByAltText("kb logo");
|
||||
expect(logo).toBeDefined();
|
||||
expect(logo.tagName).toBe("IMG");
|
||||
expect((logo as HTMLImageElement).src).toContain("/logo.svg");
|
||||
@@ -13,7 +13,7 @@ describe("Header", () => {
|
||||
|
||||
it("renders the logo before the h1 element", () => {
|
||||
render(<Header />);
|
||||
const logo = screen.getByAltText("hai logo");
|
||||
const logo = screen.getByAltText("kb logo");
|
||||
const h1 = screen.getByRole("heading", { level: 1 });
|
||||
// Logo should be a preceding sibling of the h1
|
||||
expect(logo.compareDocumentPosition(h1) & Node.DOCUMENT_POSITION_FOLLOWING).toBeTruthy();
|
||||
|
||||
@@ -15,7 +15,7 @@ vi.mock("../../api", () => ({
|
||||
function renderCard() {
|
||||
const props = {
|
||||
tasks: [],
|
||||
onSubmit: vi.fn().mockResolvedValue({ id: "HAI-001" }),
|
||||
onSubmit: vi.fn().mockResolvedValue({ id: "KB-001" }),
|
||||
onCancel: vi.fn(),
|
||||
addToast: vi.fn(),
|
||||
};
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { describe, it, expect, vi, beforeEach } from "vitest";
|
||||
import { render, screen, fireEvent, waitFor } from "@testing-library/react";
|
||||
import { SettingsModal } from "../SettingsModal";
|
||||
import type { Settings } from "@hai/core";
|
||||
import type { Settings } from "@kb/core";
|
||||
|
||||
const defaultSettings: Settings = {
|
||||
maxConcurrent: 2,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { describe, it, expect } from "vitest";
|
||||
import type { Column } from "@hai/core";
|
||||
import type { Column } from "@kb/core";
|
||||
|
||||
/**
|
||||
* Tests for the agent-active class logic in TaskCard.
|
||||
@@ -139,11 +139,11 @@ describe("TaskCard dependency tooltip", () => {
|
||||
}
|
||||
|
||||
it("returns comma-separated dependency IDs when dependencies are present", () => {
|
||||
expect(computeDepTooltip(["HAI-001", "HAI-042"])).toBe("HAI-001, HAI-042");
|
||||
expect(computeDepTooltip(["KB-001", "KB-042"])).toBe("KB-001, KB-042");
|
||||
});
|
||||
|
||||
it("returns single dependency ID when only one dependency", () => {
|
||||
expect(computeDepTooltip(["HAI-010"])).toBe("HAI-010");
|
||||
expect(computeDepTooltip(["KB-010"])).toBe("KB-010");
|
||||
});
|
||||
|
||||
it("returns undefined when dependencies array is empty", () => {
|
||||
@@ -151,12 +151,12 @@ describe("TaskCard dependency tooltip", () => {
|
||||
});
|
||||
|
||||
it("handles many dependencies", () => {
|
||||
const deps = ["HAI-001", "HAI-002", "HAI-003", "HAI-004"];
|
||||
expect(computeDepTooltip(deps)).toBe("HAI-001, HAI-002, HAI-003, HAI-004");
|
||||
const deps = ["KB-001", "KB-002", "KB-003", "KB-004"];
|
||||
expect(computeDepTooltip(deps)).toBe("KB-001, KB-002, KB-003, KB-004");
|
||||
});
|
||||
|
||||
it("data-tooltip attribute contains dependency IDs as a readable string", () => {
|
||||
const deps = ["HAI-005", "HAI-012"];
|
||||
const deps = ["KB-005", "KB-012"];
|
||||
const tooltip = computeDepTooltip(deps);
|
||||
expect(tooltip).toBeDefined();
|
||||
// Each dependency ID should appear in the tooltip
|
||||
@@ -179,7 +179,7 @@ describe("TaskCard file-scope overlap badge logic", () => {
|
||||
}
|
||||
|
||||
it("shows scope badge when blockedBy is set", () => {
|
||||
expect(shouldShowScopeBadge("HAI-003")).toBe(true);
|
||||
expect(shouldShowScopeBadge("KB-003")).toBe(true);
|
||||
});
|
||||
|
||||
it("does NOT show scope badge when blockedBy is undefined", () => {
|
||||
@@ -187,7 +187,7 @@ describe("TaskCard file-scope overlap badge logic", () => {
|
||||
});
|
||||
|
||||
it("shows card-meta when blockedBy is set even with no deps or queued status", () => {
|
||||
expect(shouldShowCardMeta({ blockedBy: "HAI-003" })).toBe(true);
|
||||
expect(shouldShowCardMeta({ blockedBy: "KB-003" })).toBe(true);
|
||||
});
|
||||
|
||||
it("does NOT show card-meta when no deps, not queued, and no blockedBy", () => {
|
||||
@@ -200,7 +200,7 @@ describe("TaskCard file-scope overlap badge logic", () => {
|
||||
}
|
||||
|
||||
it("generates correct tooltip text", () => {
|
||||
expect(computeScopeTooltip("HAI-005")).toBe("Blocked by HAI-005 (file overlap)");
|
||||
expect(computeScopeTooltip("KB-005")).toBe("Blocked by KB-005 (file overlap)");
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { describe, it, expect, vi } from "vitest";
|
||||
import { render, screen, fireEvent, act, waitFor } from "@testing-library/react";
|
||||
import { TaskDetailModal } from "../TaskDetailModal";
|
||||
import type { TaskDetail, Column, MergeResult, Task } from "@hai/core";
|
||||
import type { TaskDetail, Column, MergeResult, Task } from "@kb/core";
|
||||
|
||||
vi.mock("../../api", () => ({
|
||||
uploadAttachment: vi.fn(),
|
||||
@@ -16,7 +16,7 @@ vi.mock("../../hooks/useAgentLogs", () => ({
|
||||
|
||||
function makeTask(overrides: Partial<TaskDetail> = {}): TaskDetail {
|
||||
return {
|
||||
id: "HAI-099",
|
||||
id: "KB-099",
|
||||
description: "Test task",
|
||||
column: "in-progress" as Column,
|
||||
dependencies: [],
|
||||
@@ -157,7 +157,7 @@ describe("TaskDetailModal", () => {
|
||||
task={makeTask({
|
||||
title: undefined,
|
||||
description: "Fix the login bug",
|
||||
prompt: "# HAI-099\n\nFix the login bug\n",
|
||||
prompt: "# KB-099\n\nFix the login bug\n",
|
||||
})}
|
||||
onClose={noop}
|
||||
onMoveTask={noopMove}
|
||||
@@ -167,13 +167,13 @@ describe("TaskDetailModal", () => {
|
||||
/>,
|
||||
);
|
||||
|
||||
// The heading "HAI-099" should be stripped from the markdown
|
||||
// The heading "KB-099" should be stripped from the markdown
|
||||
const markdownBody = container.querySelector(".markdown-body");
|
||||
expect(markdownBody?.innerHTML).not.toContain("HAI-099");
|
||||
expect(markdownBody?.innerHTML).not.toContain("KB-099");
|
||||
// Description appears in the markdown body
|
||||
expect(markdownBody?.textContent).toContain("Fix the login bug");
|
||||
// The detail header shows the ID (not duplicated as markdown heading)
|
||||
expect(container.querySelector(".detail-id")?.textContent).toBe("HAI-099");
|
||||
expect(container.querySelector(".detail-id")?.textContent).toBe("KB-099");
|
||||
// The h2 title shows description, not the task ID
|
||||
const h2 = container.querySelector("h2.detail-title");
|
||||
expect(h2?.textContent).toBe("Fix the login bug");
|
||||
@@ -210,7 +210,7 @@ describe("TaskDetailModal", () => {
|
||||
addToast={noop}
|
||||
/>,
|
||||
);
|
||||
expect(withTitle.querySelector(".detail-id")?.textContent).toBe("HAI-099");
|
||||
expect(withTitle.querySelector(".detail-id")?.textContent).toBe("KB-099");
|
||||
|
||||
// Without title
|
||||
const { container: withoutTitle } = render(
|
||||
@@ -223,7 +223,7 @@ describe("TaskDetailModal", () => {
|
||||
addToast={noop}
|
||||
/>,
|
||||
);
|
||||
expect(withoutTitle.querySelector(".detail-id")?.textContent).toBe("HAI-099");
|
||||
expect(withoutTitle.querySelector(".detail-id")?.textContent).toBe("KB-099");
|
||||
});
|
||||
|
||||
describe("paste image upload", () => {
|
||||
@@ -267,7 +267,7 @@ describe("TaskDetailModal", () => {
|
||||
});
|
||||
|
||||
await waitFor(() => {
|
||||
expect(mockUpload).toHaveBeenCalledWith("HAI-099", imageFile);
|
||||
expect(mockUpload).toHaveBeenCalledWith("KB-099", imageFile);
|
||||
expect(addToast).toHaveBeenCalledWith("Screenshot attached", "success");
|
||||
});
|
||||
});
|
||||
@@ -394,7 +394,7 @@ describe("TaskDetailModal", () => {
|
||||
});
|
||||
|
||||
await waitFor(() => {
|
||||
expect(mockUpload).toHaveBeenCalledWith("HAI-099", imageFile);
|
||||
expect(mockUpload).toHaveBeenCalledWith("KB-099", imageFile);
|
||||
expect(addToast).toHaveBeenCalledWith("Screenshot attached", "success");
|
||||
});
|
||||
});
|
||||
@@ -418,7 +418,7 @@ describe("TaskDetailModal", () => {
|
||||
it("renders dependency list when dependencies exist", () => {
|
||||
render(
|
||||
<TaskDetailModal
|
||||
task={makeTask({ dependencies: ["HAI-001", "HAI-002"] })}
|
||||
task={makeTask({ dependencies: ["KB-001", "KB-002"] })}
|
||||
onClose={noop}
|
||||
onMoveTask={noopMove}
|
||||
onDeleteTask={noopDelete}
|
||||
@@ -427,16 +427,16 @@ describe("TaskDetailModal", () => {
|
||||
/>,
|
||||
);
|
||||
|
||||
expect(screen.getByText("HAI-001")).toBeTruthy();
|
||||
expect(screen.getByText("HAI-002")).toBeTruthy();
|
||||
expect(screen.getByText("KB-001")).toBeTruthy();
|
||||
expect(screen.getByText("KB-002")).toBeTruthy();
|
||||
expect(screen.queryByText("(no dependencies)")).toBeNull();
|
||||
});
|
||||
|
||||
it("can add a dependency via the dropdown", async () => {
|
||||
const { updateTask } = await import("../../api");
|
||||
const allTasks: Task[] = [
|
||||
{ id: "HAI-001", description: "Dep 1", column: "todo" as Column, dependencies: [], steps: [], currentStep: 0, log: [], createdAt: "", updatedAt: "" },
|
||||
{ id: "HAI-099", description: "Self", column: "in-progress" as Column, dependencies: [], steps: [], currentStep: 0, log: [], createdAt: "", updatedAt: "" },
|
||||
{ id: "KB-001", description: "Dep 1", column: "todo" as Column, dependencies: [], steps: [], currentStep: 0, log: [], createdAt: "", updatedAt: "" },
|
||||
{ id: "KB-099", description: "Self", column: "in-progress" as Column, dependencies: [], steps: [], currentStep: 0, log: [], createdAt: "", updatedAt: "" },
|
||||
];
|
||||
|
||||
render(
|
||||
@@ -452,16 +452,16 @@ describe("TaskDetailModal", () => {
|
||||
);
|
||||
|
||||
fireEvent.click(screen.getByText("Add Dependency"));
|
||||
// Should show HAI-001 in the dropdown but not HAI-099 (self is excluded)
|
||||
// Should show KB-001 in the dropdown but not KB-099 (self is excluded)
|
||||
const dropdown = document.querySelector(".dep-dropdown")!;
|
||||
expect(dropdown).toBeTruthy();
|
||||
expect(dropdown.textContent).toContain("HAI-001");
|
||||
expect(dropdown.textContent).toContain("KB-001");
|
||||
expect(dropdown.querySelectorAll(".dep-dropdown-item")).toHaveLength(1);
|
||||
|
||||
fireEvent.click(screen.getByText("HAI-001"));
|
||||
fireEvent.click(screen.getByText("KB-001"));
|
||||
|
||||
await waitFor(() => {
|
||||
expect(updateTask).toHaveBeenCalledWith("HAI-099", { dependencies: ["HAI-001"] });
|
||||
expect(updateTask).toHaveBeenCalledWith("KB-099", { dependencies: ["KB-001"] });
|
||||
});
|
||||
});
|
||||
|
||||
@@ -470,7 +470,7 @@ describe("TaskDetailModal", () => {
|
||||
|
||||
render(
|
||||
<TaskDetailModal
|
||||
task={makeTask({ dependencies: ["HAI-001", "HAI-002"] })}
|
||||
task={makeTask({ dependencies: ["KB-001", "KB-002"] })}
|
||||
onClose={noop}
|
||||
onMoveTask={noopMove}
|
||||
onDeleteTask={noopDelete}
|
||||
@@ -480,10 +480,10 @@ describe("TaskDetailModal", () => {
|
||||
);
|
||||
|
||||
const removeButtons = screen.getAllByTitle(/Remove dependency/);
|
||||
fireEvent.click(removeButtons[0]); // Remove HAI-001
|
||||
fireEvent.click(removeButtons[0]); // Remove KB-001
|
||||
|
||||
await waitFor(() => {
|
||||
expect(updateTask).toHaveBeenCalledWith("HAI-099", { dependencies: ["HAI-002"] });
|
||||
expect(updateTask).toHaveBeenCalledWith("KB-099", { dependencies: ["KB-002"] });
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user