refactor!: rebrand CCAM to AgentHub

Clean break, no compatibility alias. Renames the product name, the CLI
binary (`ccam` -> `agenthub`), the env var prefix (`CCAM_` ->
`AGENTHUB_`), the Prometheus metric namespace (`ccam_*` ->
`agenthub_*`), the tmux session prefix (`ccam-lane-<id>` ->
`agenthub-lane-<id>`), the per-repo runtime profile directory
(`.ccam/profile` -> `.agenthub/profile`), the lanes state root
(`~/.claude/ccam-lanes` -> `~/.claude/agenthub-lanes`), the bundled
plugins (`plugins/ccam*` -> `plugins/agenthub*`) and their slash
commands (`/ccam-doctor` -> `/agenthub-doctor`, `/ccam:*` ->
`/agenthub:*`).

Because there is no alias, an existing install has to be updated by
hand: rename its `.ccam/profile` directory and any `CCAM_*` variables,
and reinstall the plugin under its new name.

Two other name families are deliberately left alone here, since they are
not just strings: the `Claude-Code-Monitor` repository/marketplace name,
and the `agent-dashboard` npm package plus its `~/.claude/agent-dashboard`
runtime directory, which holds the live database and needs a migration
rather than a rename.

Verified: server 1406/1406, client 459/459, `grep -i ccam` clean across
tracked files, `openapi.yaml` regenerated (95 paths, unchanged count).
This commit is contained in:
2026-09-07 12:09:15 +07:00
parent ae9b3dbcd5
commit 17d56d0e01
345 changed files with 2705 additions and 2727 deletions
+1 -1
View File
@@ -10,7 +10,7 @@
import { describe, it, expect, beforeEach, vi } from "vitest";
const STORAGE_KEY = "ccam-data-scope";
const STORAGE_KEY = "agenthub-data-scope";
// Fresh module instance (resets the module-level `current`) with localStorage
// pre-seeded, so we can test load-from-storage behavior deterministically.
@@ -30,13 +30,13 @@ describe("splitViewStorage", () => {
});
it("falls back to the default when stored JSON is malformed", () => {
localStorage.setItem("ccam.workspace.splitView", "{not json");
localStorage.setItem("agenthub.workspace.splitView", "{not json");
expect(readSplitViewState()).toEqual(defaultSplitViewState());
});
it("falls back to the default when the stored layout is not 1, 2, or 4", () => {
localStorage.setItem(
"ccam.workspace.splitView",
"agenthub.workspace.splitView",
JSON.stringify({ layout: 3, paneLaneIds: [] })
);
expect(readSplitViewState()).toEqual(defaultSplitViewState());
+2 -2
View File
@@ -2290,7 +2290,7 @@ export interface CcMdItem {
/** One named artifact a plugin contributes — a skill, subagent, command or
* output style. `name` is the frontmatter name Claude Code addresses it by
* (`/ccam:ship-feature`, `subagent_type: qc-local`), falling back to the
* (`/agenthub:ship-feature`, `subagent_type: qc-local`), falling back to the
* filename when the file declares none. */
export interface CcPluginItem {
name: string;
@@ -2646,7 +2646,7 @@ export interface RunStartArgs {
}
/** A lane's tmux-backed terminal run — one per lane, id is the tmux session
* name (`ccam-lane-<laneId>`). */
* name (`agenthub-lane-<laneId>`). */
export interface RunHandle {
id: string;
laneId: number | null;
+1 -1
View File
@@ -105,7 +105,7 @@ export interface DataScope {
selected: string[];
}
const STORAGE_KEY = "ccam-data-scope";
const STORAGE_KEY = "agenthub-data-scope";
const DEFAULT_SCOPE: DataScope = { mode: "all", selected: [] };
function load(): DataScope {
+1 -1
View File
@@ -14,7 +14,7 @@ export interface SplitViewState {
paneLaneIds: (number | null)[];
}
const STORAGE_KEY = "ccam.workspace.splitView";
const STORAGE_KEY = "agenthub.workspace.splitView";
export function defaultSplitViewState(): SplitViewState {
return { layout: 1, paneLaneIds: [null] };
+5 -5
View File
@@ -1299,7 +1299,7 @@ export interface UpdateStatusPayload {
/** Payload for the `run_status` WebSocket message. */
export interface RunStatusPayload {
/** The run id (tmux session name, `ccam-lane-<laneId>`). */
/** The run id (tmux session name, `agenthub-lane-<laneId>`). */
id: string;
status: "running" | "gone";
/** Epoch-ms timestamp of this transition. */
@@ -2257,7 +2257,7 @@ export interface LaneNode {
icon: string;
gate: boolean;
/** The one command that drives this node — a slash command
* (`/superpowers:writing-plans`) or a shell one (`ccam lanes hook ci-gate`),
* (`/superpowers:writing-plans`) or a shell one (`agenthub lanes hook ci-gate`),
* from the pipeline template. Empty string for a node with no single driver
* (`done`, or a gate another agent declares). Display text only; the
* dashboard never runs it. Optional in the type only because the archived
@@ -2321,7 +2321,7 @@ export interface Lane {
notes: string | null;
pipeline_name: string;
/** Slash command the lane's pipeline template declares as its driver, e.g.
* `/ccam:ship-feature-lane`. The run console pre-fills its prompt with it.
* `/agenthub:ship-feature-lane`. The run console pre-fills its prompt with it.
* Empty when the template names no skill. */
pipeline_skill?: string;
pipeline_nodes: LaneNode[];
@@ -2398,7 +2398,7 @@ export interface NamedLock {
* What is actually running for a lane, recomputed by the server on every read
* rather than cached — a process can die without telling anyone.
*
* `available: false` means the lane has no `.ccam/profile`, which is a normal
* `available: false` means the lane has no `.agenthub/profile`, which is a normal
* state (most lanes never run a stack), not an error.
*/
export type LaneRuntime =
@@ -2517,7 +2517,7 @@ export interface PipelineStats {
/** Skill invocation counts and the per-plugin totals derived from them. */
export interface SkillUsage {
skills: {
/** Skill name as invoked, e.g. `ccam:ship-feature` or a bare `daily-report`. */
/** Skill name as invoked, e.g. `agenthub:ship-feature` or a bare `daily-report`. */
name: string;
/** Owning plugin, or null for a built-in/personal skill. */
plugin: string | null;