Files
nntrivi2001 57dc91585d feat: Claude Code Monitor — lanes, pipelines and a merged workspace
Internal SmartGift build of a Claude Code monitoring dashboard.

Lanes: a durable unit of parallel agent work, one per working directory,
tracked across session restarts. Managed lanes are git worktrees the
dashboard provisions and can reset or remove behind a three-check destroy
guard and a counted preflight; adopted lanes are directories you already
own and are never destroyable.

Pipelines: a lane moves through pipeline stages. A stage the agent declares
with evidence renders green; a stage inferred from the tool-event stream
renders dashed amber and never counts as done. Detection is forward-only
within a 30-minute window, and never writes the declared stage.

Workspace: one page at /run with a lane grid, the selected lane's pipeline,
and a full Claude console behind a disclosure.
2026-07-30 14:39:03 +07:00

2080 lines
76 KiB
JavaScript

/**
* @file OpenAPI 3.0 fragment for the Claude Config Explorer router
* (`server/routes/cc-config.js`, mounted at `/api/cc-config`). Documents
* every read surface (skills, agents, commands, output styles, plugins, MCP
* servers, hooks, settings, memory, marketplaces, keybindings, statusline,
* hook scripts, single-file body) plus the low-risk mutation surface
* (PUT/DELETE /file) and the backups listing.
*
* Exports `{ tags, schemas, paths }` for deep-merge by
* `server/openapi-extra.js`. All schema names are prefixed with `CcConfig`
* to avoid collisions with the base spec. The shared `{ error: { code,
* message } }` envelope reuses the base `ErrorResponse` schema (defined in
* server/openapi.js) — it is NOT redefined here.
*
* @author Nguyễn Ngọc Trí Vĩ <vinnt@smartgift.vn>
*/
// ── Tags ───────────────────────────────────────────────────────────────
const tags = [
{
name: "CcConfig",
description:
"Claude Code configuration explorer: discover and (for low-risk artifacts) edit skills, agents, commands, output styles, MCP servers, hooks, settings, memory, plugins, marketplaces, keybindings, statusline",
},
];
// ── Shared sub-schemas ─────────────────────────────────────────────────
const schemas = {
CcConfigScope: {
type: "string",
enum: ["user", "project", "all"],
description:
"Discovery scope. `user` = ~/.claude (or CLAUDE_HOME); `project` = <cwd>/.claude (and project CLAUDE.md); `all` = both, merged. Defaults to `all` when omitted or unrecognized.",
example: "all",
},
CcConfigFrontmatter: {
type: "object",
additionalProperties: { type: "string" },
description:
"Parsed YAML frontmatter from the artifact's markdown header (key → string value). Empty object when the file has no parseable frontmatter. Multi-line values are preserved as raw strings; quotes are stripped.",
example: {
name: "code-reviewer",
description: "Reviews diffs for regressions",
model: "sonnet",
},
},
// ── Skill ──────────────────────────────────────────────────────────
CcConfigSkill: {
type: "object",
description:
"A discovered skill: a directory containing a SKILL.md file under <root>/skills/<name>/.",
required: [
"scope",
"name",
"path",
"file",
"size",
"mtime",
"truncated",
"frontmatter",
"preview",
],
properties: {
scope: {
type: "string",
enum: ["user", "project"],
description: "Which root this skill was discovered under.",
example: "user",
},
name: {
type: "string",
description: "Skill directory name.",
example: "code-reviewer",
},
path: {
type: "string",
description: "Absolute path to the skill directory.",
example: "/Users/son/.claude/skills/code-reviewer",
},
file: {
type: "string",
description: "Absolute path to the SKILL.md file.",
example: "/Users/son/.claude/skills/code-reviewer/SKILL.md",
},
size: {
type: "integer",
description: "Size of SKILL.md in bytes.",
example: 2048,
},
mtime: {
type: "number",
description: "Last-modified time of SKILL.md in epoch milliseconds.",
example: 1718900000000,
},
truncated: {
type: "boolean",
description:
"True when SKILL.md exceeds 256 KiB and the read was truncated to the first 256 KiB.",
example: false,
},
frontmatter: { $ref: "#/components/schemas/CcConfigFrontmatter" },
preview: {
type: "string",
description: "First 320 characters of the markdown body (after frontmatter).",
example: "Use this skill to review pull requests for correctness and style...",
},
},
},
// ── Markdown surface item (agents / commands / output-styles) ───────
CcConfigMdItem: {
type: "object",
description:
"A single-file markdown artifact (subagent, slash command, or output style) found under <root>/<subdir>/<name>.md.",
required: ["scope", "name", "file", "size", "mtime", "truncated", "frontmatter", "preview"],
properties: {
scope: {
type: "string",
enum: ["user", "project"],
description: "Which root this artifact was discovered under.",
example: "project",
},
name: {
type: "string",
description: "Artifact name (filename with the trailing .md stripped).",
example: "backend-reviewer",
},
file: {
type: "string",
description: "Absolute path to the .md file.",
example: "/repo/.claude/agents/backend-reviewer.md",
},
size: { type: "integer", description: "File size in bytes.", example: 1536 },
mtime: {
type: "number",
description: "Last-modified time in epoch milliseconds.",
example: 1718900000000,
},
truncated: {
type: "boolean",
description: "True when the file exceeded 256 KiB and was truncated.",
example: false,
},
frontmatter: { $ref: "#/components/schemas/CcConfigFrontmatter" },
preview: {
type: "string",
description: "First 320 characters of the markdown body (after frontmatter).",
example: "Review backend route and hook logic for regressions...",
},
},
},
// ── Plugin contributions ────────────────────────────────────────────
CcConfigPluginContributions: {
type: "object",
nullable: true,
description:
"Counts of artifacts a plugin contributes, plus its parsed .claude-plugin/plugin.json. Null when the plugin's installPath does not exist on disk.",
required: ["skills", "agents", "commands", "outputStyles", "hooks", "pluginJson"],
properties: {
skills: {
type: "integer",
description: "Number of skill directories (containing SKILL.md) under the plugin.",
example: 3,
},
agents: {
type: "integer",
description: "Number of .md agent files under the plugin.",
example: 2,
},
commands: {
type: "integer",
description: "Number of .md command files under the plugin.",
example: 5,
},
outputStyles: {
type: "integer",
description: "Number of .md output-style files under the plugin.",
example: 0,
},
hooks: {
type: "integer",
description: "Number of files under the plugin's hooks/ directory.",
example: 1,
},
pluginJson: {
type: "object",
nullable: true,
additionalProperties: true,
description:
"Parsed contents of .claude-plugin/plugin.json, or null when absent/unparseable.",
example: { name: "superpowers", version: "1.2.0" },
},
},
},
// ── Plugin ───────────────────────────────────────────────────────────
CcConfigPlugin: {
type: "object",
description: "An installed plugin instance derived from the plugins manifest.",
required: [
"key",
"name",
"marketplace",
"scope",
"version",
"installPath",
"installedAt",
"lastUpdated",
"gitCommitSha",
"installPathExists",
"enabled",
"contributes",
],
properties: {
key: {
type: "string",
description:
"Manifest key for this plugin, either the bare name or `<name>@<marketplace>`.",
example: "superpowers@obra",
},
name: {
type: "string",
description: "Plugin name (the portion of `key` before any `@`).",
example: "superpowers",
},
marketplace: {
type: "string",
nullable: true,
description: "Marketplace portion of `key` (after `@`), or null when unscoped.",
example: "obra",
},
scope: {
type: "string",
description: "Install scope reported by the manifest instance (defaults to `user`).",
example: "user",
},
version: {
type: "string",
nullable: true,
description: "Installed plugin version, or null.",
example: "1.2.0",
},
installPath: {
type: "string",
nullable: true,
description: "Absolute install path, or null.",
example: "/Users/son/.claude/plugins/superpowers",
},
installedAt: {
type: "string",
nullable: true,
description: "Install timestamp from the manifest, or null.",
example: "2026-05-01T12:00:00.000Z",
},
lastUpdated: {
type: "string",
nullable: true,
description: "Last-updated timestamp from the manifest, or null.",
example: "2026-06-01T09:30:00.000Z",
},
gitCommitSha: {
type: "string",
nullable: true,
description: "Pinned git commit SHA from the manifest, or null.",
example: "a1b2c3d4e5f6",
},
installPathExists: {
type: "boolean",
description: "True when installPath resolves to an existing directory on disk.",
example: true,
},
enabled: {
type: "boolean",
nullable: true,
description:
"Tri-state enabled flag from settings.json `enabledPlugins`: true, false, or null when the plugin is not listed there.",
example: true,
},
contributes: { $ref: "#/components/schemas/CcConfigPluginContributions" },
},
},
CcConfigPluginsResponse: {
type: "object",
description: "Plugins manifest summary plus the resolved plugin list (user scope only).",
required: ["manifestPath", "manifestExists", "plugins"],
properties: {
manifestPath: {
type: "string",
description: "Absolute path to plugins/installed_plugins.json under the Claude home.",
example: "/Users/son/.claude/plugins/installed_plugins.json",
},
manifestExists: {
type: "boolean",
description: "True when the manifest file was found and parsed.",
example: true,
},
plugins: {
type: "array",
description: "Plugins sorted by `key`.",
items: { $ref: "#/components/schemas/CcConfigPlugin" },
},
},
},
// ── MCP server ─────────────────────────────────────────────────────
CcConfigMcpServer: {
type: "object",
description:
"A summarized MCP server definition. Sensitive details (header values, env values) are NOT returned — only their key NAMES are surfaced.",
required: ["name", "source", "kind"],
properties: {
name: {
type: "string",
description: "Server name (object key in the source config).",
example: "github",
},
source: {
type: "string",
description:
"Where the definition was found, e.g. `~/.claude.json (top-level)`, `~/.claude.json (projects[<root>])`, or `~/.claude/settings.json`.",
example: "~/.claude.json (top-level)",
},
kind: {
type: "string",
enum: ["http", "stdio", "unknown"],
description:
"Transport kind inferred from the definition: `http` (has url), `stdio` (has command), or `unknown`.",
example: "stdio",
},
url: {
type: "string",
description: "Endpoint URL — present only for `http` servers.",
example: "https://mcp.example.com/sse",
},
headers: {
type: "array",
items: { type: "string" },
description:
"Header NAMES (values redacted) — present only for `http` servers. Empty array when no headers.",
example: ["Authorization"],
},
command: {
type: "string",
description: "Executable — present only for `stdio` servers.",
example: "npx",
},
args: {
type: "array",
items: { type: "string" },
description: "Command arguments — present only for `stdio` servers.",
example: ["-y", "@modelcontextprotocol/server-github"],
},
envNames: {
type: "array",
items: { type: "string" },
description:
"Environment-variable NAMES (values redacted) — present only for `stdio` servers.",
example: ["GITHUB_TOKEN"],
},
},
},
CcConfigMcpResponse: {
type: "object",
description:
"MCP servers split by where they apply: globally to the user, or scoped to the resolved project (cwd) in ~/.claude.json projects[<root>].",
required: ["user", "projectScoped"],
properties: {
user: {
type: "array",
description:
"User/global MCP servers (top-level ~/.claude.json plus ~/.claude/settings.json).",
items: { $ref: "#/components/schemas/CcConfigMcpServer" },
},
projectScoped: {
type: "array",
description: "MCP servers scoped to the current project root in ~/.claude.json.",
items: { $ref: "#/components/schemas/CcConfigMcpServer" },
},
},
},
// ── Hooks ────────────────────────────────────────────────────────────
CcConfigHookEntry: {
type: "object",
description: "A single flattened hook command bound to a matcher.",
required: ["matcher", "type", "command", "timeout"],
properties: {
matcher: {
type: "string",
description: "Tool/event matcher pattern (defaults to `*`).",
example: "Bash",
},
type: {
type: "string",
description: "Hook type (defaults to `command`).",
example: "command",
},
command: {
type: "string",
nullable: true,
description: "Shell command to run, or null.",
example: "node ~/.claude/hooks/handler.js",
},
timeout: {
type: "integer",
nullable: true,
description: "Per-hook timeout in seconds, or null when unset.",
example: 30,
},
},
},
CcConfigHookSource: {
type: "object",
description:
"Hooks read from one settings file. `hooks` is keyed by event name (e.g. PreToolUse, PostToolUse, Stop); each known-event value is an array of flattened hook entries, while unknown events are passed through verbatim.",
required: ["scope", "file", "exists", "hooks"],
properties: {
scope: {
type: "string",
enum: ["user", "project", "project-local"],
description: "Which settings file this hook block came from.",
example: "user",
},
file: {
type: "string",
description: "Absolute path to the settings file.",
example: "/Users/son/.claude/settings.json",
},
exists: {
type: "boolean",
description: "True when the settings file was found and parsed.",
example: true,
},
hooks: {
type: "object",
description:
"Event name → array of hook entries (for the known event types) or the raw matcher array (for unknown events). Only non-empty events are included.",
additionalProperties: {
type: "array",
items: { $ref: "#/components/schemas/CcConfigHookEntry" },
},
example: {
PreToolUse: [
{ matcher: "Bash", type: "command", command: "node guard.js", timeout: null },
],
},
},
},
},
// ── Settings ───────────────────────────────────────────────────────
CcConfigSettingsSource: {
type: "object",
description:
"One settings file's redacted contents. Secret-like keys (matching /token|secret|password|api[_-]?key|auth/i with string values) are replaced with the literal string `<redacted>`.",
required: ["scope", "file", "exists"],
properties: {
scope: {
type: "string",
enum: ["user", "project", "project-local"],
description: "Which settings file this entry represents.",
example: "user",
},
file: {
type: "string",
description: "Absolute path to the settings file.",
example: "/Users/son/.claude/settings.json",
},
exists: {
type: "boolean",
description:
"True when the file was found and parsed. When false, `data`/`raw_size` are omitted.",
example: true,
},
data: {
type: "object",
additionalProperties: true,
description: "Parsed, secret-redacted settings JSON. Present only when `exists` is true.",
example: { model: "sonnet", apiKey: "<redacted>", statusLine: { type: "command" } },
},
raw_size: {
type: "integer",
description: "Byte length of the raw file contents. Present only when `exists` is true.",
example: 412,
},
},
},
// ── Memory ───────────────────────────────────────────────────────────
CcConfigMemoryItem: {
type: "object",
description:
"A memory artifact: either a primary CLAUDE.md (scope `user`/`project`) or a per-project file-based auto-memory markdown file (scope `auto-memory`). Auto-memory items additionally carry `project`, `name`, `isIndex`, and parsed `frontmatter`.",
required: ["scope", "file", "size", "mtime", "truncated", "preview"],
properties: {
scope: {
type: "string",
enum: ["user", "project", "auto-memory"],
description:
"`user`/`project` = the two primary CLAUDE.md files; `auto-memory` = a *.md file under ~/.claude/projects/<slug>/memory/.",
example: "auto-memory",
},
project: {
type: "string",
description:
"Auto-memory only: the ~/.claude/projects/<slug> directory name the file belongs to.",
example: "-Users-son-repo",
},
name: {
type: "string",
description: "Auto-memory only: the markdown filename.",
example: "MEMORY.md",
},
isIndex: {
type: "boolean",
description:
"Auto-memory only: true for index/manifest files (MEMORY.md, INDEX-*.md), which sort first.",
example: true,
},
file: {
type: "string",
description: "Absolute path to the memory file.",
example: "/Users/son/.claude/projects/-Users-son-repo/memory/MEMORY.md",
},
size: { type: "integer", description: "File size in bytes.", example: 980 },
mtime: {
type: "number",
description: "Last-modified time in epoch milliseconds.",
example: 1718900000000,
},
truncated: {
type: "boolean",
description: "True when the file exceeded 256 KiB and was truncated.",
example: false,
},
frontmatter: { $ref: "#/components/schemas/CcConfigFrontmatter" },
preview: {
type: "string",
description:
"First 480 characters of the body (frontmatter stripped for auto-memory files; raw head for CLAUDE.md).",
example: "## Persistent facts\\n- Never run destructive ops without confirmation...",
},
},
},
// ── Marketplaces ───────────────────────────────────────────────────
CcConfigMarketplace: {
type: "object",
description: "A known plugin marketplace and (best-effort) its parsed manifest summary.",
required: [
"name",
"source",
"installLocation",
"lastUpdated",
"pluginCount",
"marketplaceName",
"marketplaceDescription",
"marketplaceOwner",
],
properties: {
name: { type: "string", description: "Marketplace key/name.", example: "obra" },
source: {
type: "object",
nullable: true,
additionalProperties: true,
description: "Source descriptor object (e.g. git/github source), or null.",
example: { source: "github", repo: "obra/superpowers-marketplace" },
},
installLocation: {
type: "string",
nullable: true,
description: "Absolute path where the marketplace is checked out, or null.",
example: "/Users/son/.claude/plugins/marketplaces/obra",
},
lastUpdated: {
type: "string",
nullable: true,
description: "Last-updated timestamp, or null.",
example: "2026-06-10T00:00:00.000Z",
},
pluginCount: {
type: "integer",
nullable: true,
description:
"Number of plugins in the marketplace manifest, or null when no manifest was readable.",
example: 12,
},
marketplaceName: {
type: "string",
nullable: true,
description: "`name` from the marketplace manifest, or null.",
example: "Superpowers",
},
marketplaceDescription: {
type: "string",
nullable: true,
description: "`description` from the marketplace manifest, or null.",
example: "Curated agent skills",
},
marketplaceOwner: {
description: "`owner` from the marketplace manifest (object or string), or null.",
nullable: true,
oneOf: [{ type: "object", additionalProperties: true }, { type: "string" }],
example: { name: "obra" },
},
},
},
CcConfigMarketplacesResponse: {
type: "object",
description: "Known-marketplaces manifest summary plus the resolved marketplace list.",
required: ["knownPath", "knownExists", "items"],
properties: {
knownPath: {
type: "string",
description: "Absolute path to plugins/known_marketplaces.json under the Claude home.",
example: "/Users/son/.claude/plugins/known_marketplaces.json",
},
knownExists: {
type: "boolean",
description: "True when the known-marketplaces file was found and parsed.",
example: true,
},
items: {
type: "array",
description: "Marketplaces sorted by name.",
items: { $ref: "#/components/schemas/CcConfigMarketplace" },
},
},
},
// ── Keybindings ────────────────────────────────────────────────────
CcConfigKeybinding: {
type: "object",
description: "A single key → action binding.",
required: ["key", "action"],
properties: {
key: { type: "string", description: "Key chord.", example: "ctrl+s" },
action: { type: "string", description: "Action name (stringified).", example: "submit" },
},
},
CcConfigKeybindingGroup: {
type: "object",
description: "A context-scoped group of keybindings.",
required: ["context", "bindings"],
properties: {
context: {
type: "string",
description: "The context the bindings apply in (empty string when global).",
example: "editor",
},
bindings: {
type: "array",
items: { $ref: "#/components/schemas/CcConfigKeybinding" },
},
},
},
CcConfigKeybindingsResponse: {
type: "object",
description:
"Parsed ~/.claude/keybindings.json. When the file is absent, only `{ file, exists: false }` is returned.",
required: ["file", "exists"],
properties: {
file: {
type: "string",
description: "Absolute path to keybindings.json.",
example: "/Users/son/.claude/keybindings.json",
},
exists: {
type: "boolean",
description: "True when the file was found and parsed.",
example: true,
},
schema: {
type: "string",
nullable: true,
description: "`$schema` value from the file, or null. Present only when `exists` is true.",
example: "https://json.schemastore.org/claude-keybindings",
},
docs: {
type: "string",
nullable: true,
description: "`$docs` value from the file, or null. Present only when `exists` is true.",
example: "https://docs.claude.com/keybindings",
},
groups: {
type: "array",
description: "Binding groups. Present only when `exists` is true.",
items: { $ref: "#/components/schemas/CcConfigKeybindingGroup" },
},
},
},
// ── Statusline ─────────────────────────────────────────────────────
CcConfigStatuslineScript: {
type: "object",
description:
"A statusline script (statusline.py or statusline-command.sh) discovered under the Claude home.",
required: ["file", "size", "mtime", "truncated", "preview"],
properties: {
file: {
type: "string",
description: "Absolute path to the script.",
example: "/Users/son/.claude/statusline.py",
},
size: { type: "integer", description: "File size in bytes.", example: 1280 },
mtime: {
type: "number",
description: "Last-modified time in epoch milliseconds.",
example: 1718900000000,
},
truncated: {
type: "boolean",
description: "True when the file exceeded 256 KiB and was truncated.",
example: false,
},
preview: {
type: "string",
description: "First 4000 characters of the script body.",
example: "#!/usr/bin/env python3\\nimport json, sys\\n...",
},
},
},
CcConfigStatuslineResponse: {
type: "object",
description: "The statusLine config block from user settings.json plus any statusline scripts.",
required: ["config", "scripts"],
properties: {
config: {
type: "object",
nullable: true,
additionalProperties: true,
description: "The `statusLine` object from ~/.claude/settings.json, or null when unset.",
example: { type: "command", command: "python3 ~/.claude/statusline.py" },
},
scripts: {
type: "array",
items: { $ref: "#/components/schemas/CcConfigStatuslineScript" },
},
},
},
// ── Hook scripts ───────────────────────────────────────────────────
CcConfigHookScript: {
type: "object",
description: "A file in the ~/.claude/hooks/ directory.",
required: ["name", "file", "size", "mtime"],
properties: {
name: { type: "string", description: "File name.", example: "post-tool-use.js" },
file: {
type: "string",
description: "Absolute path to the script.",
example: "/Users/son/.claude/hooks/post-tool-use.js",
},
size: { type: "integer", description: "File size in bytes.", example: 640 },
mtime: {
type: "number",
description: "Last-modified time in epoch milliseconds.",
example: 1718900000000,
},
},
},
CcConfigHookScriptsResponse: {
type: "object",
description: "Listing of the ~/.claude/hooks/ directory (files only, sorted by name).",
required: ["dir", "items"],
properties: {
dir: {
type: "string",
description: "Absolute path to the hooks directory.",
example: "/Users/son/.claude/hooks",
},
items: {
type: "array",
items: { $ref: "#/components/schemas/CcConfigHookScript" },
},
},
},
// ── Overview ─────────────────────────────────────────────────────────
CcConfigScopeCount: {
type: "object",
description: "Per-scope counts (user vs project).",
required: ["user", "project"],
properties: {
user: { type: "integer", description: "Count under the user root.", example: 12 },
project: { type: "integer", description: "Count under the project root.", example: 3 },
},
},
CcConfigOverviewResponse: {
type: "object",
description:
"Top-level summary for the Claude Config Explorer landing page: resolved roots plus aggregate counts across every surface.",
required: ["roots", "counts"],
properties: {
roots: {
type: "object",
description: "The four filesystem roots the explorer reads from.",
required: ["claudeHome", "projectClaudeDir", "projectRoot", "claudeJson"],
properties: {
claudeHome: {
type: "string",
description: "Claude home (CLAUDE_HOME or ~/.claude).",
example: "/Users/son/.claude",
},
projectClaudeDir: {
type: "string",
description: "Project .claude directory under the resolved cwd.",
example: "/repo/.claude",
},
projectRoot: {
type: "string",
description: "Resolved project root (the cwd).",
example: "/repo",
},
claudeJson: {
type: "string",
description: "Path to ~/.claude.json (resolved from $HOME, not CLAUDE_HOME).",
example: "/Users/son/.claude.json",
},
},
},
counts: {
type: "object",
description: "Aggregate counts by surface.",
required: [
"skills",
"agents",
"commands",
"outputStyles",
"plugins",
"pluginsEnabled",
"pluginsDisabled",
"marketplaces",
"keybindings",
"mcpServers",
"hooks",
"memory",
"settingsFiles",
],
properties: {
skills: { $ref: "#/components/schemas/CcConfigScopeCount" },
agents: { $ref: "#/components/schemas/CcConfigScopeCount" },
commands: { $ref: "#/components/schemas/CcConfigScopeCount" },
outputStyles: { $ref: "#/components/schemas/CcConfigScopeCount" },
plugins: { type: "integer", description: "Total plugins.", example: 8 },
pluginsEnabled: {
type: "integer",
description: "Plugins explicitly enabled in settings.",
example: 6,
},
pluginsDisabled: {
type: "integer",
description: "Plugins explicitly disabled in settings.",
example: 1,
},
marketplaces: { type: "integer", description: "Known marketplaces.", example: 2 },
keybindings: {
type: "integer",
description: "Total individual key bindings across all groups.",
example: 24,
},
mcpServers: {
type: "object",
description: "MCP server counts by scope.",
required: ["user", "project"],
properties: {
user: { type: "integer", example: 3 },
project: { type: "integer", example: 1 },
},
},
hooks: {
type: "object",
description: "Total hook entries per settings scope.",
required: ["user", "project", "project-local"],
properties: {
user: { type: "integer", example: 2 },
project: { type: "integer", example: 0 },
"project-local": { type: "integer", example: 1 },
},
},
memory: { type: "integer", description: "Total memory items.", example: 5 },
settingsFiles: {
type: "integer",
description: "Number of settings files that exist (of the three scopes).",
example: 2,
},
},
},
},
},
// ── List wrappers ────────────────────────────────────────────────────
CcConfigSkillsResponse: {
type: "object",
required: ["items"],
properties: {
items: { type: "array", items: { $ref: "#/components/schemas/CcConfigSkill" } },
},
},
CcConfigAgentsResponse: {
type: "object",
required: ["items"],
properties: {
items: { type: "array", items: { $ref: "#/components/schemas/CcConfigMdItem" } },
},
},
CcConfigCommandsResponse: {
type: "object",
required: ["items"],
properties: {
items: { type: "array", items: { $ref: "#/components/schemas/CcConfigMdItem" } },
},
},
CcConfigOutputStylesResponse: {
type: "object",
required: ["items"],
properties: {
items: { type: "array", items: { $ref: "#/components/schemas/CcConfigMdItem" } },
},
},
CcConfigHooksResponse: {
type: "object",
required: ["items"],
properties: {
items: { type: "array", items: { $ref: "#/components/schemas/CcConfigHookSource" } },
},
},
CcConfigSettingsResponse: {
type: "object",
required: ["items"],
properties: {
items: { type: "array", items: { $ref: "#/components/schemas/CcConfigSettingsSource" } },
},
},
CcConfigMemoryResponse: {
type: "object",
required: ["items"],
properties: {
items: { type: "array", items: { $ref: "#/components/schemas/CcConfigMemoryItem" } },
},
},
// ── /file read ───────────────────────────────────────────────────────
CcConfigFileReadResponse: {
type: "object",
description:
"Body of a single file resolved under an allowed root. Returned only on success; failures return an ErrorResponse with HTTP 400.",
required: ["ok", "file", "truncated", "size", "text", "mtime"],
properties: {
ok: { type: "boolean", enum: [true], description: "Always true on success.", example: true },
file: {
type: "string",
description: "Absolute, resolved path that was read.",
example: "/Users/son/.claude/skills/code-reviewer/SKILL.md",
},
truncated: {
type: "boolean",
description:
"True when the file exceeded 256 KiB and only the first 256 KiB are in `text`.",
example: false,
},
size: { type: "integer", description: "Full file size in bytes.", example: 2048 },
text: {
type: "string",
description: "File contents (truncated to 256 KiB).",
example: "---\\nname: code-reviewer\\n---\\n# Code Reviewer\\n...",
},
mtime: {
type: "number",
description: "Last-modified time in epoch milliseconds.",
example: 1718900000000,
},
},
},
// ── PUT /file ──────────────────────────────────────────────────────
CcConfigFileWriteRequest: {
type: "object",
description:
"Create-or-overwrite request for a low-risk text artifact. `name` is required for every type except `memory` (which targets CLAUDE.md). `project` is required for `auto-memory`. `scope` is ignored for `auto-memory` (always under the Claude home).",
required: ["scope", "type", "content"],
properties: {
scope: {
type: "string",
enum: ["user", "project"],
description:
"Target root: `user` (Claude home) or `project` (<cwd>/.claude, or project root for memory). Ignored for `auto-memory`.",
example: "user",
},
type: {
type: "string",
enum: ["skills", "agents", "commands", "output-styles", "memory", "auto-memory"],
description:
"Artifact type. `skills` writes <root>/skills/<name>/SKILL.md; `agents`/`commands`/`output-styles` write <root>/<type>/<name>.md; `memory` writes the scope's CLAUDE.md; `auto-memory` writes ~/.claude/projects/<project>/memory/<name>.",
example: "agents",
},
name: {
type: "string",
description:
"Artifact name. Required for all types except `memory`. For skills/agents/commands/output-styles must match /^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/. For auto-memory must be a flat *.md filename (/^[A-Za-z0-9][A-Za-z0-9._-]{0,127}\\.md$/i, no `..`).",
example: "my-reviewer",
},
content: {
type: "string",
description: "Full file contents to write. Must be a string and ≤ 256 KiB (UTF-8 bytes).",
example: "---\\nname: my-reviewer\\n---\\n# My Reviewer\\nReview the diff...",
},
project: {
type: "string",
description:
"Required for `auto-memory`: the ~/.claude/projects/<slug> directory name. Validated against /^[A-Za-z0-9_-][A-Za-z0-9._-]{0,255}$/ and rejected if it contains `..`.",
example: "-Users-son-repo",
},
},
},
CcConfigFileWriteResponse: {
type: "object",
description: "Result of a successful create-or-overwrite.",
required: ["ok", "file", "target", "backupPath", "created"],
properties: {
ok: { type: "boolean", enum: [true], example: true },
file: {
type: "string",
description: "Absolute path to the .md file that was written.",
example: "/Users/son/.claude/agents/my-reviewer.md",
},
target: {
type: "string",
description:
"Absolute path to the artifact target (the skill directory for skills; same as `file` otherwise).",
example: "/Users/son/.claude/agents/my-reviewer.md",
},
backupPath: {
type: "string",
nullable: true,
description:
"Absolute path to the timestamped backup created before overwriting, or null when this was a fresh create (nothing to back up).",
example:
"/Users/son/.claude/cc-config-backups/agents/my-reviewer.md.2026-06-25T12-00-00.000Z.bak",
},
created: {
type: "boolean",
description: "True when the artifact did not exist before this write.",
example: true,
},
},
},
// ── DELETE /file ───────────────────────────────────────────────────
CcConfigFileDeleteRequest: {
type: "object",
description:
"Delete request for a low-risk text artifact. Same (scope, type, name, project) semantics as the write request, without `content`.",
required: ["scope", "type"],
properties: {
scope: {
type: "string",
enum: ["user", "project"],
description: "Target root. Ignored for `auto-memory`.",
example: "user",
},
type: {
type: "string",
enum: ["skills", "agents", "commands", "output-styles", "memory", "auto-memory"],
description: "Artifact type to delete.",
example: "agents",
},
name: {
type: "string",
description: "Artifact name. Required for all types except `memory`.",
example: "my-reviewer",
},
project: {
type: "string",
description: "Required for `auto-memory`: the projects/<slug> directory name.",
example: "-Users-son-repo",
},
},
},
CcConfigFileDeleteResponse: {
type: "object",
description: "Result of a successful delete (a mandatory backup is created first).",
required: ["ok", "file", "target", "backupPath"],
properties: {
ok: { type: "boolean", enum: [true], example: true },
file: {
type: "string",
description: "Absolute path to the .md file inside the deleted target.",
example: "/Users/son/.claude/agents/my-reviewer.md",
},
target: {
type: "string",
description: "Absolute path to the deleted artifact (skill directory for skills).",
example: "/Users/son/.claude/agents/my-reviewer.md",
},
backupPath: {
type: "string",
nullable: true,
description: "Absolute path to the backup taken before deletion.",
example:
"/Users/son/.claude/cc-config-backups/agents/my-reviewer.md.2026-06-25T12-00-00.000Z.bak",
},
},
},
// ── Backups ──────────────────────────────────────────────────────────
CcConfigBackup: {
type: "object",
description: "A single backup entry under a cc-config-backups directory.",
required: ["scope", "type", "name", "backupPath", "isDir", "mtime", "size"],
properties: {
scope: {
type: "string",
enum: ["user", "project", "auto-memory"],
description: "Scope the backup belongs to. `auto-memory` backups are per-project.",
example: "user",
},
project: {
type: "string",
description: "Auto-memory only: the projects/<slug> directory name.",
example: "-Users-son-repo",
},
type: {
type: "string",
enum: ["skills", "agents", "commands", "output-styles", "memory", "auto-memory"],
description: "Artifact type the backup was taken from.",
example: "agents",
},
name: {
type: "string",
description: "Backup file/directory name (includes the timestamp and .bak suffix).",
example: "my-reviewer.md.2026-06-25T12-00-00.000Z.bak",
},
backupPath: {
type: "string",
description: "Absolute path to the backup.",
example:
"/Users/son/.claude/cc-config-backups/agents/my-reviewer.md.2026-06-25T12-00-00.000Z.bak",
},
isDir: {
type: "boolean",
description: "True when the backup is a directory (skill backups).",
example: false,
},
mtime: {
type: "number",
description: "Backup modification time in epoch milliseconds (results sort newest first).",
example: 1718900000000,
},
size: {
type: "integer",
nullable: true,
description: "Backup size in bytes, or null for directory backups.",
example: 2048,
},
},
},
CcConfigBackupsResponse: {
type: "object",
required: ["items"],
properties: {
items: {
type: "array",
description: "Backups across the requested scope/type buckets, sorted newest first.",
items: { $ref: "#/components/schemas/CcConfigBackup" },
},
},
},
};
// ── Reusable parameters (inlined per-operation to match base style) ────
const scopeParam = {
name: "scope",
in: "query",
required: false,
schema: { type: "string", enum: ["user", "project", "all"], default: "all" },
description:
"Discovery scope: `user`, `project`, or `all` (default). Unrecognized values fall back to `all`.",
example: "all",
};
const cwdParam = {
name: "cwd",
in: "query",
required: false,
schema: { type: "string" },
description:
"Override the working directory used to resolve the project root and project .claude dir. Defaults to the dashboard server's own cwd.",
example: "/repo",
};
// ── Paths ───────────────────────────────────────────────────────────────
const paths = {
"/api/cc-config/overview": {
get: {
tags: ["CcConfig"],
summary: "Config explorer overview",
description:
"Read-only. Returns the four resolved filesystem roots plus aggregate counts across every surface (skills, agents, commands, output styles, plugins, marketplaces, keybindings, MCP servers, hooks, memory, settings files). All sub-reads degrade to empty on any I/O error, so this endpoint never errors.",
operationId: "ccConfigGetOverview",
parameters: [cwdParam],
responses: {
200: {
description: "Overview of roots and counts.",
content: {
"application/json": {
schema: { $ref: "#/components/schemas/CcConfigOverviewResponse" },
example: {
roots: {
claudeHome: "/Users/son/.claude",
projectClaudeDir: "/repo/.claude",
projectRoot: "/repo",
claudeJson: "/Users/son/.claude.json",
},
counts: {
skills: { user: 12, project: 3 },
agents: { user: 5, project: 2 },
commands: { user: 8, project: 0 },
outputStyles: { user: 1, project: 0 },
plugins: 8,
pluginsEnabled: 6,
pluginsDisabled: 1,
marketplaces: 2,
keybindings: 24,
mcpServers: { user: 3, project: 1 },
hooks: { user: 2, project: 0, "project-local": 1 },
memory: 5,
settingsFiles: 2,
},
},
},
},
},
},
},
},
"/api/cc-config/skills": {
get: {
tags: ["CcConfig"],
summary: "List discovered skills",
description:
"Read-only. Lists skill directories (each containing a SKILL.md) under the user and/or project roots, with parsed frontmatter and a 320-char body preview. Files over 256 KiB are read truncated. Degrades to an empty list on I/O errors.",
operationId: "ccConfigGetSkills",
parameters: [scopeParam, cwdParam],
responses: {
200: {
description: "Skills list.",
content: {
"application/json": {
schema: { $ref: "#/components/schemas/CcConfigSkillsResponse" },
example: {
items: [
{
scope: "user",
name: "code-reviewer",
path: "/Users/son/.claude/skills/code-reviewer",
file: "/Users/son/.claude/skills/code-reviewer/SKILL.md",
size: 2048,
mtime: 1718900000000,
truncated: false,
frontmatter: { name: "code-reviewer", description: "Reviews diffs" },
preview: "Use this skill to review pull requests...",
},
],
},
},
},
},
},
},
},
"/api/cc-config/agents": {
get: {
tags: ["CcConfig"],
summary: "List subagents",
description:
"Read-only. Lists single-file subagent definitions (<root>/agents/*.md) under the user and/or project roots, with parsed frontmatter and a 320-char preview. Truncated above 256 KiB; degrades to empty on errors.",
operationId: "ccConfigGetAgents",
parameters: [scopeParam, cwdParam],
responses: {
200: {
description: "Agents list.",
content: {
"application/json": {
schema: { $ref: "#/components/schemas/CcConfigAgentsResponse" },
example: {
items: [
{
scope: "project",
name: "backend-reviewer",
file: "/repo/.claude/agents/backend-reviewer.md",
size: 1536,
mtime: 1718900000000,
truncated: false,
frontmatter: { name: "backend-reviewer", model: "sonnet" },
preview: "Review backend route and hook logic for regressions...",
},
],
},
},
},
},
},
},
},
"/api/cc-config/commands": {
get: {
tags: ["CcConfig"],
summary: "List slash commands",
description:
"Read-only. Lists slash-command definitions (<root>/commands/*.md) under the user and/or project roots, with parsed frontmatter and a 320-char preview. Truncated above 256 KiB; degrades to empty on errors.",
operationId: "ccConfigGetCommands",
parameters: [scopeParam, cwdParam],
responses: {
200: {
description: "Commands list.",
content: {
"application/json": {
schema: { $ref: "#/components/schemas/CcConfigCommandsResponse" },
example: {
items: [
{
scope: "user",
name: "commit",
file: "/Users/son/.claude/commands/commit.md",
size: 800,
mtime: 1718900000000,
truncated: false,
frontmatter: { description: "Create a commit" },
preview: "Commit the staged changes with a descriptive message...",
},
],
},
},
},
},
},
},
},
"/api/cc-config/output-styles": {
get: {
tags: ["CcConfig"],
summary: "List output styles",
description:
"Read-only. Lists output-style definitions (<root>/output-styles/*.md) under the user and/or project roots, with parsed frontmatter and a 320-char preview. Truncated above 256 KiB; degrades to empty on errors.",
operationId: "ccConfigGetOutputStyles",
parameters: [scopeParam, cwdParam],
responses: {
200: {
description: "Output styles list.",
content: {
"application/json": {
schema: { $ref: "#/components/schemas/CcConfigOutputStylesResponse" },
example: {
items: [
{
scope: "user",
name: "concise",
file: "/Users/son/.claude/output-styles/concise.md",
size: 420,
mtime: 1718900000000,
truncated: false,
frontmatter: { name: "concise" },
preview: "Respond tersely, no preamble...",
},
],
},
},
},
},
},
},
},
"/api/cc-config/plugins": {
get: {
tags: ["CcConfig"],
summary: "List installed plugins",
description:
"Read-only. Reads the user-scope plugins manifest (~/.claude/plugins/installed_plugins.json), resolves each instance's on-disk contributions, and reports its tri-state enabled flag from settings.json. Read-only by design: plugins are written concurrently by the running Claude Code CLI. No scope/cwd parameters. Degrades to an empty plugin list when the manifest is missing.",
operationId: "ccConfigGetPlugins",
responses: {
200: {
description: "Plugins manifest summary and list.",
content: {
"application/json": {
schema: { $ref: "#/components/schemas/CcConfigPluginsResponse" },
example: {
manifestPath: "/Users/son/.claude/plugins/installed_plugins.json",
manifestExists: true,
plugins: [
{
key: "superpowers@obra",
name: "superpowers",
marketplace: "obra",
scope: "user",
version: "1.2.0",
installPath: "/Users/son/.claude/plugins/superpowers",
installedAt: "2026-05-01T12:00:00.000Z",
lastUpdated: "2026-06-01T09:30:00.000Z",
gitCommitSha: "a1b2c3d4e5f6",
installPathExists: true,
enabled: true,
contributes: {
skills: 3,
agents: 2,
commands: 5,
outputStyles: 0,
hooks: 1,
pluginJson: { name: "superpowers", version: "1.2.0" },
},
},
],
},
},
},
},
},
},
},
"/api/cc-config/mcp": {
get: {
tags: ["CcConfig"],
summary: "List MCP servers",
description:
"Read-only. Discovers MCP servers from ~/.claude.json (top-level and projects[<root>]) and ~/.claude/settings.json, split into `user` and `projectScoped`. Header and env VALUES are never returned — only their key NAMES. Read-only by design (the CLI writes these files concurrently). Degrades to empty arrays on errors.",
operationId: "ccConfigGetMcp",
parameters: [cwdParam],
responses: {
200: {
description: "MCP servers grouped by scope.",
content: {
"application/json": {
schema: { $ref: "#/components/schemas/CcConfigMcpResponse" },
example: {
user: [
{
name: "github",
source: "~/.claude.json (top-level)",
kind: "stdio",
command: "npx",
args: ["-y", "@modelcontextprotocol/server-github"],
envNames: ["GITHUB_TOKEN"],
},
],
projectScoped: [
{
name: "internal",
source: "~/.claude.json (projects[/repo])",
kind: "http",
url: "https://mcp.example.com/sse",
headers: ["Authorization"],
},
],
},
},
},
},
},
},
},
"/api/cc-config/hooks": {
get: {
tags: ["CcConfig"],
summary: "List configured hooks",
description:
"Read-only. Reads hooks from the user, project, and project-local settings files. Known event types (SessionStart, SessionEnd, UserPromptSubmit, PreToolUse, PostToolUse, Stop, SubagentStop, Notification, PreCompact) are flattened into matcher/type/command/timeout entries; unknown events are passed through verbatim. Hooks in settings are read-only here. Degrades to per-file `exists:false` on errors.",
operationId: "ccConfigGetHooks",
parameters: [cwdParam],
responses: {
200: {
description: "Hook sources by scope.",
content: {
"application/json": {
schema: { $ref: "#/components/schemas/CcConfigHooksResponse" },
example: {
items: [
{
scope: "user",
file: "/Users/son/.claude/settings.json",
exists: true,
hooks: {
PostToolUse: [
{
matcher: "*",
type: "command",
command: "node ~/.claude/hooks/post-tool-use.js",
timeout: null,
},
],
},
},
{
scope: "project",
file: "/repo/.claude/settings.json",
exists: false,
hooks: {},
},
],
},
},
},
},
},
},
},
"/api/cc-config/settings": {
get: {
tags: ["CcConfig"],
summary: "List settings files",
description:
"Read-only. Returns the user, project, and project-local settings.json contents with secret-like keys redacted to the literal `<redacted>` (keys matching /token|secret|password|api[_-]?key|auth/i with string values). Missing files report `exists:false`. Settings files are read-only here (concurrent CLI writes).",
operationId: "ccConfigGetSettings",
parameters: [cwdParam],
responses: {
200: {
description: "Settings sources by scope (secrets redacted).",
content: {
"application/json": {
schema: { $ref: "#/components/schemas/CcConfigSettingsResponse" },
example: {
items: [
{
scope: "user",
file: "/Users/son/.claude/settings.json",
exists: true,
data: { model: "sonnet", apiKey: "<redacted>" },
raw_size: 412,
},
{ scope: "project", file: "/repo/.claude/settings.json", exists: false },
{
scope: "project-local",
file: "/repo/.claude/settings.local.json",
exists: false,
},
],
},
},
},
},
},
},
},
"/api/cc-config/memory": {
get: {
tags: ["CcConfig"],
summary: "List memory files",
description:
"Read-only. Returns the two primary CLAUDE.md files (scopes `user`/`project`) plus every per-project file-based auto-memory markdown file under ~/.claude/projects/<slug>/memory/ (scope `auto-memory`, sorted index files first). Previews are 480 chars; files over 256 KiB are truncated. Degrades to fewer files on errors.",
operationId: "ccConfigGetMemory",
parameters: [cwdParam],
responses: {
200: {
description: "Memory items.",
content: {
"application/json": {
schema: { $ref: "#/components/schemas/CcConfigMemoryResponse" },
example: {
items: [
{
scope: "user",
file: "/Users/son/.claude/CLAUDE.md",
size: 1200,
mtime: 1718900000000,
truncated: false,
preview: "# Workspace & Productivity System...",
},
{
scope: "auto-memory",
project: "-Users-son-repo",
name: "MEMORY.md",
isIndex: true,
file: "/Users/son/.claude/projects/-Users-son-repo/memory/MEMORY.md",
size: 980,
mtime: 1718900000000,
truncated: false,
frontmatter: {},
preview:
"## Persistent facts\\n- Never run destructive ops without confirmation...",
},
],
},
},
},
},
},
},
},
"/api/cc-config/marketplaces": {
get: {
tags: ["CcConfig"],
summary: "List plugin marketplaces",
description:
"Read-only. Reads ~/.claude/plugins/known_marketplaces.json and, best-effort, each marketplace's .claude-plugin/marketplace.json for plugin count and metadata. No scope/cwd parameters. Degrades to an empty list when the file is missing.",
operationId: "ccConfigGetMarketplaces",
responses: {
200: {
description: "Known marketplaces summary and list.",
content: {
"application/json": {
schema: { $ref: "#/components/schemas/CcConfigMarketplacesResponse" },
example: {
knownPath: "/Users/son/.claude/plugins/known_marketplaces.json",
knownExists: true,
items: [
{
name: "obra",
source: { source: "github", repo: "obra/superpowers-marketplace" },
installLocation: "/Users/son/.claude/plugins/marketplaces/obra",
lastUpdated: "2026-06-10T00:00:00.000Z",
pluginCount: 12,
marketplaceName: "Superpowers",
marketplaceDescription: "Curated agent skills",
marketplaceOwner: { name: "obra" },
},
],
},
},
},
},
},
},
},
"/api/cc-config/keybindings": {
get: {
tags: ["CcConfig"],
summary: "Get keybindings",
description:
"Read-only. Parses ~/.claude/keybindings.json into context-scoped groups of key → action bindings. When the file is absent, returns only `{ file, exists:false }`. No scope/cwd parameters.",
operationId: "ccConfigGetKeybindings",
responses: {
200: {
description: "Parsed keybindings (or exists:false).",
content: {
"application/json": {
schema: { $ref: "#/components/schemas/CcConfigKeybindingsResponse" },
example: {
file: "/Users/son/.claude/keybindings.json",
exists: true,
schema: null,
docs: null,
groups: [
{
context: "editor",
bindings: [{ key: "ctrl+s", action: "submit" }],
},
],
},
},
},
},
},
},
put: {
tags: ["CcConfig"],
summary: "Overwrite keybindings",
description:
"Mutating. Overwrites ~/.claude/keybindings.json from a structured list of context groups (`{ context, bindings: [{ key, action }] }`); on disk the bindings become an object keyed by `key`. The existing file is backed up first (under cc-config-backups/keybindings/) and any top-level metadata (`$schema`, `$docs`, unknown keys) is preserved — only the `bindings` array is replaced. Rejects duplicate contexts and duplicate keys within a context. Writes are atomic. Unlike settings.json, keybindings.json is not rewritten mid-session by the live CLI, so this edit is safe. Emits a `cc_config_changed` websocket event on success.",
operationId: "ccConfigPutKeybindings",
requestBody: {
required: true,
content: {
"application/json": {
schema: {
type: "object",
required: ["groups"],
properties: {
groups: {
type: "array",
description: "Full set of context-scoped keybinding groups to persist.",
items: {
type: "object",
required: ["context", "bindings"],
properties: {
context: { type: "string", example: "Global" },
bindings: {
type: "array",
items: {
type: "object",
required: ["key", "action"],
properties: {
key: { type: "string", example: "ctrl+t" },
action: { type: "string", example: "toggleTodos" },
},
},
},
},
},
},
},
},
example: {
groups: [
{ context: "Global", bindings: [{ key: "ctrl+t", action: "toggleTodos" }] },
{ context: "Chat", bindings: [{ key: "escape", action: "cancel" }] },
],
},
},
},
},
responses: {
200: {
description: "keybindings.json written (backup taken when it already existed).",
content: {
"application/json": {
schema: { $ref: "#/components/schemas/CcConfigFileWriteResponse" },
example: {
ok: true,
file: "/Users/son/.claude/keybindings.json",
target: "/Users/son/.claude/keybindings.json",
backupPath:
"/Users/son/.claude/cc-config-backups/keybindings/keybindings.json.2026-06-25T12-00-00.000Z.bak",
created: false,
},
},
},
},
400: {
description:
"Bad request. `EBADREQ` — `groups` is missing or not an array. `EBADCONTENT` — a group/binding is malformed, a context is empty, or there is a duplicate context or duplicate key within a context.",
content: {
"application/json": {
schema: { $ref: "#/components/schemas/ErrorResponse" },
example: {
error: { code: "EBADCONTENT", message: 'duplicate key "ctrl+t" in context Global' },
},
},
},
},
413: {
description: "`ETOOLARGE` — the serialized keybindings exceed the 256 KiB limit.",
content: {
"application/json": {
schema: { $ref: "#/components/schemas/ErrorResponse" },
example: { error: { code: "ETOOLARGE", message: "content exceeds 262144 bytes" } },
},
},
},
},
},
},
"/api/cc-config/statusline": {
get: {
tags: ["CcConfig"],
summary: "Get statusline config and scripts",
description:
"Read-only. Returns the `statusLine` block from ~/.claude/settings.json plus any statusline scripts (statusline.py, statusline-command.sh) with a 4000-char preview. Scripts over 256 KiB are truncated. No scope/cwd parameters.",
operationId: "ccConfigGetStatusline",
responses: {
200: {
description: "Statusline config and scripts.",
content: {
"application/json": {
schema: { $ref: "#/components/schemas/CcConfigStatuslineResponse" },
example: {
config: { type: "command", command: "python3 ~/.claude/statusline.py" },
scripts: [
{
file: "/Users/son/.claude/statusline.py",
size: 1280,
mtime: 1718900000000,
truncated: false,
preview: "#!/usr/bin/env python3\\nimport json, sys\\n...",
},
],
},
},
},
},
},
},
},
"/api/cc-config/hook-scripts": {
get: {
tags: ["CcConfig"],
summary: "List hook scripts",
description:
"Read-only. Lists files in the ~/.claude/hooks/ directory (name, path, size, mtime), sorted by name. Returns metadata only — no file contents. Use GET /file to read an individual script. No scope/cwd parameters.",
operationId: "ccConfigGetHookScripts",
responses: {
200: {
description: "Hook scripts directory listing.",
content: {
"application/json": {
schema: { $ref: "#/components/schemas/CcConfigHookScriptsResponse" },
example: {
dir: "/Users/son/.claude/hooks",
items: [
{
name: "post-tool-use.js",
file: "/Users/son/.claude/hooks/post-tool-use.js",
size: 640,
mtime: 1718900000000,
},
],
},
},
},
},
},
},
},
"/api/cc-config/file": {
get: {
tags: ["CcConfig"],
summary: "Read a single file body",
description:
"Read-only. Returns the body of one file. Strict path containment: the resolved absolute path MUST live under the Claude home, the project .claude dir, or (only for a file literally named CLAUDE.md) the project root. Bodies over 256 KiB are truncated. NOTE: there is no 404 path — a missing, unreadable, or out-of-root file all return 400.",
operationId: "ccConfigGetFile",
parameters: [
{
name: "path",
in: "query",
required: true,
schema: { type: "string" },
description:
"Absolute path to read. Must resolve under an allowed root; under the project root only CLAUDE.md is permitted.",
example: "/Users/son/.claude/skills/code-reviewer/SKILL.md",
},
cwdParam,
],
responses: {
200: {
description: "File body with metadata.",
content: {
"application/json": {
schema: { $ref: "#/components/schemas/CcConfigFileReadResponse" },
example: {
ok: true,
file: "/Users/son/.claude/skills/code-reviewer/SKILL.md",
truncated: false,
size: 2048,
text: "---\\nname: code-reviewer\\n---\\n# Code Reviewer\\n...",
mtime: 1718900000000,
},
},
},
},
400: {
description:
"Bad or denied path. `BAD_PATH` — `path` query is missing or empty. `READ_DENIED` — path is outside the allowed roots, only CLAUDE.md is readable from the project root, or the file is missing/unreadable (there is no 404).",
content: {
"application/json": {
schema: { $ref: "#/components/schemas/ErrorResponse" },
example: { error: { code: "READ_DENIED", message: "path is outside allowed roots" } },
},
},
},
},
},
put: {
tags: ["CcConfig"],
summary: "Create or overwrite a text artifact",
description:
"Mutating. Creates or overwrites a low-risk text artifact (skills, agents, commands, output-styles, memory, auto-memory). Before overwriting an existing artifact a timestamped backup is created under a cc-config-backups directory (well outside the dirs Claude Code scans); fresh creates have no backup (backupPath:null). Writes are atomic (temp file + rename). Names are validated against a strict allowlist and the resolved path is re-checked to be inside its containment root. Content must be a string ≤ 256 KiB (UTF-8). Plugins, MCP servers, hooks-in-settings, and settings.json are intentionally NOT writable. Emits a `cc_config_changed` websocket event on success.",
operationId: "ccConfigPutFile",
requestBody: {
required: true,
content: {
"application/json": {
schema: { $ref: "#/components/schemas/CcConfigFileWriteRequest" },
example: {
scope: "user",
type: "agents",
name: "my-reviewer",
content: "---\\nname: my-reviewer\\n---\\n# My Reviewer\\nReview the diff...",
},
},
},
},
parameters: [cwdParam],
responses: {
200: {
description: "Artifact created or overwritten.",
content: {
"application/json": {
schema: { $ref: "#/components/schemas/CcConfigFileWriteResponse" },
example: {
ok: true,
file: "/Users/son/.claude/agents/my-reviewer.md",
target: "/Users/son/.claude/agents/my-reviewer.md",
backupPath: null,
created: true,
},
},
},
},
400: {
description:
"Bad request. `EBADREQ` — scope/type missing or not strings. `EBADTYPE` — unknown type. `EBADSCOPE` — scope not user/project. `EBADNAME` — name fails the allowlist (or auto-memory name is not a flat *.md). `EBADPROJECT` — invalid auto-memory project slug. `EBADCONTENT` — content is not a string. `EOUTOFROOT` — resolved path escapes its containment root.",
content: {
"application/json": {
schema: { $ref: "#/components/schemas/ErrorResponse" },
example: {
error: {
code: "EBADNAME",
message: "name must match /^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/",
},
},
},
},
},
413: {
description: "`ETOOLARGE` — content exceeds the 256 KiB (262144-byte) limit.",
content: {
"application/json": {
schema: { $ref: "#/components/schemas/ErrorResponse" },
example: { error: { code: "ETOOLARGE", message: "content exceeds 262144 bytes" } },
},
},
},
500: {
description: "`EINTERNAL` (or any unmapped error code) — unexpected filesystem error.",
content: {
"application/json": {
schema: { $ref: "#/components/schemas/ErrorResponse" },
example: { error: { code: "EINTERNAL", message: "EACCES: permission denied" } },
},
},
},
},
},
delete: {
tags: ["CcConfig"],
summary: "Delete a text artifact",
description:
"Mutating. Deletes a low-risk text artifact (same type/scope/name/project semantics as PUT, no content). A mandatory backup is created BEFORE deletion — if the backup fails the original is left intact. Skill deletes remove the whole skill directory. Emits a `cc_config_changed` websocket event on success. Plugins, MCP servers, hooks-in-settings, and settings.json are NOT deletable.",
operationId: "ccConfigDeleteFile",
requestBody: {
required: true,
content: {
"application/json": {
schema: { $ref: "#/components/schemas/CcConfigFileDeleteRequest" },
example: { scope: "user", type: "agents", name: "my-reviewer" },
},
},
},
parameters: [cwdParam],
responses: {
200: {
description: "Artifact deleted (backup taken first).",
content: {
"application/json": {
schema: { $ref: "#/components/schemas/CcConfigFileDeleteResponse" },
example: {
ok: true,
file: "/Users/son/.claude/agents/my-reviewer.md",
target: "/Users/son/.claude/agents/my-reviewer.md",
backupPath:
"/Users/son/.claude/cc-config-backups/agents/my-reviewer.md.2026-06-25T12-00-00.000Z.bak",
},
},
},
},
400: {
description:
"Bad request. `EBADREQ` — scope/type missing or not strings. `EBADTYPE` — unknown type. `EBADSCOPE` — scope not user/project. `EBADNAME` — invalid name. `EBADPROJECT` — invalid auto-memory project slug. `EOUTOFROOT` — resolved path escapes its containment root.",
content: {
"application/json": {
schema: { $ref: "#/components/schemas/ErrorResponse" },
example: { error: { code: "EBADTYPE", message: "unknown type: plugins" } },
},
},
},
404: {
description: "`ENOTFOUND` — the target artifact does not exist.",
content: {
"application/json": {
schema: { $ref: "#/components/schemas/ErrorResponse" },
example: {
error: { code: "ENOTFOUND", message: "agents/my-reviewer does not exist" },
},
},
},
},
500: {
description: "`EINTERNAL` (or any unmapped error code) — unexpected filesystem error.",
content: {
"application/json": {
schema: { $ref: "#/components/schemas/ErrorResponse" },
example: { error: { code: "EINTERNAL", message: "EACCES: permission denied" } },
},
},
},
},
},
},
"/api/cc-config/backups": {
get: {
tags: ["CcConfig"],
summary: "List artifact backups",
description:
"Read-only. Lists backups taken by the mutation endpoints, across the user and project roots plus per-project auto-memory backup dirs. Optional `scope`/`type` narrow the search. Results are sorted newest first. Best-effort: unreadable backup dirs are skipped, never erroring.",
operationId: "ccConfigGetBackups",
parameters: [
{
name: "scope",
in: "query",
required: false,
schema: { type: "string", enum: ["user", "project"] },
description:
"Narrow to a single root scope. Only `user` or `project` are honored; any other value is ignored (both scopes scanned).",
example: "user",
},
{
name: "type",
in: "query",
required: false,
schema: {
type: "string",
enum: ["skills", "agents", "commands", "output-styles", "memory", "auto-memory"],
},
description: "Narrow to a single artifact type. When omitted, all types are scanned.",
example: "agents",
},
cwdParam,
],
responses: {
200: {
description: "Backups sorted newest first.",
content: {
"application/json": {
schema: { $ref: "#/components/schemas/CcConfigBackupsResponse" },
example: {
items: [
{
scope: "user",
type: "agents",
name: "my-reviewer.md.2026-06-25T12-00-00.000Z.bak",
backupPath:
"/Users/son/.claude/cc-config-backups/agents/my-reviewer.md.2026-06-25T12-00-00.000Z.bak",
isDir: false,
mtime: 1718900000000,
size: 2048,
},
],
},
},
},
},
},
},
},
};
module.exports = { tags, schemas, paths };