Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion docs/ARCHITECTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ CLI 위임으로 Claude Code의 유효성 검증과 포맷 호환성을 보장

### 프론트엔드 아키텍처: 엔티티 시스템

대시보드는 **칸반 보드** 형태의 단일 페이지 앱이다. 7개 엔티티(Skill, Agent, Hook, MCP, Plugin, File, Memory)를 공통 패턴으로 처리한다.
대시보드는 **칸반 보드** 형태의 단일 페이지 앱이다. 7개 엔티티(Skill, Subagent, Hook, MCP, Plugin, File, Memory)를 공통 패턴으로 처리한다.

```text
EntityConfig (설정 객체) 공통 UI Primitives 도메인별 Inspector
Expand Down Expand Up @@ -68,6 +68,7 @@ src/
features/ ← 도메인별 feature 디렉토리 (타입, 서비스, 서버, 쿼리, 컴포넌트 co-locate)
hook/ ← Hook 도메인 (types, constants, utils, service, server, queries, config, components/)
skill/ ← Skill/Command 도메인 (types, utils, config, service, server, queries, components/)
subagent/ ← Subagent 도메인 (config, server, queries, components/)
components/
ui/ ← shadcn primitives (Button, Sheet, ListItem, Inspector 등)
memory/ ← MemoryInspector
Expand Down
8 changes: 7 additions & 1 deletion docs/CONVENTIONS.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ ${scope}:${event}:${matcher ?? ""}:${command ?? prompt ?? ""}
## 제품 컨벤션

- 순수 shadcn/ui — 커스텀 스타일, 색상 변경 없음
- 엔티티 아이콘은 `ENTITY_ICONS`만 사용 — `skill: ScrollTextIcon`, `command: TerminalSquareIcon`, `agent: WorkflowIcon`, `mcp: ServerIcon`, `hook: ZapIcon`, `plugin: Plug2Icon`. lucide-react 직접 import 금지
- 엔티티 아이콘은 `ENTITY_ICONS`만 사용 — `skill: ScrollTextIcon`, `command: TerminalSquareIcon`, `subagent: WorkflowIcon`, `mcp: ServerIcon`, `hook: ZapIcon`, `plugin: Plug2Icon`. lucide-react 직접 import 금지
- skills.sh를 설치 백엔드로 활용 (자체 마켓플레이스 구현 X)
- 뷰어/모니터링 중심 — 수정(에디팅)은 IDE 위임
- Server Functions (`createServerFn`)으로 타입 안전한 서버 호출
Expand All @@ -72,6 +72,12 @@ ${scope}:${event}:${matcher ?? ""}:${command ?? prompt ?? ""}
- 보안: 127.0.0.1 바인딩, 랜덤 토큰 인증, CORS 미설정
- 개발 서버 실행 시 Chrome 앱 모드(`--app`)로 열기

## 용어 규칙: Agent vs Subagent

- **Agent (AI Agent)**: Claude Code, Cline, Cursor 등 AI 코딩 에이전트. `AgentType`, `AgentConfig`, `shared/agents.ts` 등에서 사용.
- **Subagent**: `.claude/agents/` 디렉토리의 subagent definition 파일 (.md). 엔티티 타입 `"subagent"`, `subagentConfig`, `SubagentInspector` 등에서 사용.
- Hook handler type `"agent"`는 Claude Code 외부 포맷이므로 리네이밍하지 않음.

## Commit Convention

- Use English for all commit messages, PR titles, and issue comments
Expand Down
1 change: 1 addition & 0 deletions docs/ROADMAP.md
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,7 @@ Claude Code가 자체 설정 관리 GUI를 추가하면, 그걸 쓰면 된다. a

## Shipped

- **Agent→Subagent 리네이밍 + feature directory 이관** (2026-03-19) — 엔티티 타입 `"agent"`→`"subagent"` 리네이밍. `src/features/subagent/`로 이관. i18n 키, board column, entity icons/actions 전면 업데이트. CONVENTIONS.md에 Agent vs Subagent 용어 규칙 추가.
- **Skill feature directory 이관** (2026-03-19) — Skill/Command 도메인을 `src/features/skill/`로 통합. utils 4개 파일 병합, config 병합, 유닛 테스트 co-locate.
- **Hook feature directory 이관 + 리스트 액션 제거** (2026-03-19) — Hook 도메인 파일을 `src/features/hook/`로 통합. 서버 Zod 스키마 중복 제거, Inspector에 EntityActionDropdown 적용. EntityListPanel에서 더보기 버튼/컨텍스트 메뉴 제거 (조작은 Inspector에서만).
- **Inspector 아키텍처 단순화** (2026-03-18) — `DashboardDetailTarget` union / `toDetailTarget` 제거. Inspector가 `itemKey`만 받아 자체 fetch 및 액션 처리. `use-entity-action-handler.ts` 제거.
Expand Down
26 changes: 0 additions & 26 deletions messages/en/agents.json

This file was deleted.

4 changes: 2 additions & 2 deletions messages/en/common.json
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@
"board_no_plugins": "No plugins",
"board_no_mcp": "No MCP servers",
"board_no_skills": "No skills",
"board_no_agents": "No agents",
"board_no_subagents": "No agents",
"board_no_hooks": "No hooks",
"board_no_memory": "No memory files",
"board_no_lsp": "No LSP servers",
Expand All @@ -56,7 +56,7 @@
"board_col_plugins": "Plugins",
"board_col_mcp": "MCP Servers",
"board_col_skills": "Skills",
"board_col_agents": "Agents",
"board_col_subagents": "Agents",
"board_col_hooks": "Hooks",
"board_col_memory": "Memory",
"board_col_lsp": "LSP Servers",
Expand Down
4 changes: 2 additions & 2 deletions messages/en/plugins.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,14 @@
"plugin_show_more": "{count} more",
"plugin_cat_commands": "Commands",
"plugin_cat_skills": "Skills",
"plugin_cat_agents": "Agents",
"plugin_cat_subagents": "Agents",
"plugin_cat_hooks": "Hooks",
"plugin_cat_mcp_servers": "MCP Servers",
"plugin_cat_lsp_servers": "LSP Servers",
"plugin_cat_output_styles": "Output Styles",
"plugin_cat_desc_commands": "Type / in the chat to use these shortcuts and run workflows as needed.",
"plugin_cat_desc_skills": "Teach Claude team norms and domain expertise to automatically apply to related tasks.",
"plugin_cat_desc_agents": "Specialized AI assistants that can be invoked to handle specific types of tasks.",
"plugin_cat_desc_subagents": "Specialized AI assistants that can be invoked to handle specific types of tasks.",
"plugin_cat_desc_hooks": "Automatically run shell commands before and after specific events like tool calls or notifications.",
"plugin_cat_desc_mcp_servers": "Connect to external tools and data sources to extend Claude's capabilities.",
"plugin_cat_desc_lsp_servers": "Provide language server features like auto-completion and go-to-definition during code editing.",
Expand Down
26 changes: 26 additions & 0 deletions messages/en/subagents.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
{
"subagents_title": "Agents",
"subagents_empty_title": "No Agent Selected",
"subagents_empty_desc": "Select an agent from the left panel to view its details.",
"subagents_add_title": "Add Agent",
"subagents_add_desc": "Creates a new agent file in {path}.",
"subagents_add_name": "Name",
"subagents_add_name_placeholder": "my-agent",
"subagents_add_name_required": "Name is required",
"subagents_add_name_max": "Name must be 64 characters or less",
"subagents_add_name_pattern": "Only lowercase letters, numbers, and hyphens",
"subagents_add_name_rule": "Lowercase letters, numbers, and hyphens only. Max 64 characters.",
"subagents_add_description": "Description",
"subagents_add_description_placeholder": "What does this agent do?",
"subagents_toast_created": "Agent '{name}' created",
"subagents_toast_create_failed": "Failed to create agent",
"subagents_creating": "Creating...",
"subagents_create": "Create",
"subagents_detail_scope": "Scope",
"subagents_detail_last_updated": "Last Updated",
"subagents_detail_description": "Description",
"subagents_detail_no_description": "No description",
"subagents_detail_model": "Model",
"subagents_detail_tools": "Tools",
"subagents_detail_permission_mode": "Permission Mode"
}
26 changes: 0 additions & 26 deletions messages/ko/agents.json

This file was deleted.

4 changes: 2 additions & 2 deletions messages/ko/common.json
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@
"board_no_plugins": "플러그인 없음",
"board_no_mcp": "MCP 서버 없음",
"board_no_skills": "스킬 없음",
"board_no_agents": "에이전트 없음",
"board_no_subagents": "에이전트 없음",
"board_no_hooks": "훅 없음",
"board_no_memory": "메모리 파일 없음",
"board_no_lsp": "LSP 서버 없음",
Expand All @@ -56,7 +56,7 @@
"board_col_plugins": "플러그인",
"board_col_mcp": "MCP 서버",
"board_col_skills": "스킬",
"board_col_agents": "에이전트",
"board_col_subagents": "에이전트",
"board_col_hooks": "훅",
"board_col_memory": "메모리",
"board_col_lsp": "LSP 서버",
Expand Down
4 changes: 2 additions & 2 deletions messages/ko/plugins.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,14 @@
"plugin_show_more": "{count}개 더 보기",
"plugin_cat_commands": "명령",
"plugin_cat_skills": "스킬",
"plugin_cat_agents": "에이전트",
"plugin_cat_subagents": "에이전트",
"plugin_cat_hooks": "훅",
"plugin_cat_mcp_servers": "MCP 서버",
"plugin_cat_lsp_servers": "LSP 서버",
"plugin_cat_output_styles": "출력 스타일",
"plugin_cat_desc_commands": "채팅 창에서 /를 입력하여 이러한 바로가기를 사용하면 필요에 따라 워크플로를 실행할 수 있습니다.",
"plugin_cat_desc_skills": "Claude에게 팀 규범과 도메인 전문 지식을 학습시켜 관련 작업에 자동으로 반영하세요.",
"plugin_cat_desc_agents": "특정 유형의 작업을 처리하기 위해 호출할 수 있는 전문 AI 어시스턴트입니다.",
"plugin_cat_desc_subagents": "특정 유형의 작업을 처리하기 위해 호출할 수 있는 전문 AI 어시스턴트입니다.",
"plugin_cat_desc_hooks": "도구 호출이나 알림과 같은 특정 이벤트 전후에 셸 명령을 자동으로 실행합니다.",
"plugin_cat_desc_mcp_servers": "외부 도구와 데이터 소스에 연결하여 Claude의 기능을 확장합니다.",
"plugin_cat_desc_lsp_servers": "코드 편집 시 자동 완성, 정의 이동 등 언어 서버 기능을 제공합니다.",
Expand Down
26 changes: 26 additions & 0 deletions messages/ko/subagents.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
{
"subagents_title": "에이전트",
"subagents_empty_title": "에이전트가 선택되지 않음",
"subagents_empty_desc": "왼쪽 패널에서 에이전트를 선택하여 세부 정보를 확인하세요.",
"subagents_add_title": "에이전트 추가",
"subagents_add_desc": "{path}에 새 에이전트 파일을 생성합니다.",
"subagents_add_name": "이름",
"subagents_add_name_placeholder": "my-agent",
"subagents_add_name_required": "이름은 필수입니다",
"subagents_add_name_max": "이름은 64자 이하여야 합니다",
"subagents_add_name_pattern": "소문자, 숫자, 하이픈만 사용 가능",
"subagents_add_name_rule": "소문자, 숫자, 하이픈만 사용 가능. 최대 64자.",
"subagents_add_description": "설명",
"subagents_add_description_placeholder": "이 에이전트는 무엇을 하나요?",
"subagents_toast_created": "에이전트 '{name}' 생성됨",
"subagents_toast_create_failed": "에이전트 생성 실패",
"subagents_creating": "생성 중...",
"subagents_create": "생성",
"subagents_detail_scope": "스코프",
"subagents_detail_last_updated": "마지막 업데이트",
"subagents_detail_description": "설명",
"subagents_detail_no_description": "설명 없음",
"subagents_detail_model": "모델",
"subagents_detail_tools": "도구",
"subagents_detail_permission_mode": "권한 모드"
}
2 changes: 1 addition & 1 deletion src/components/board/BoardColumnSettings.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ const COLUMN_LABELS: Record<BoardColumnId, () => string> = {
plugins: () => m.board_col_plugins(),
mcp: () => m.board_col_mcp(),
skills: () => m.board_col_skills(),
agents: () => m.board_col_agents(),
subagents: () => m.board_col_subagents(),
hooks: () => m.board_col_hooks(),
memory: () => m.board_col_memory(),
lsp: () => m.board_col_lsp(),
Expand Down
28 changes: 14 additions & 14 deletions src/components/board/BoardLayout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -42,25 +42,25 @@ import {
} from "@/components/ui/sheet"
import { Switch } from "@/components/ui/switch"
import {
agentConfig,
commandConfig,
type HookItem,
hookConfig,
mcpConfig,
memoryConfig,
skillConfig,
subagentConfig,
} from "@/config/entities"
import { getEntityConfig } from "@/config/entity-registry"
import { AddHookDialog } from "@/features/hook/components/add-hook-dialog"
import { useHooksQuery } from "@/features/hook/queries"
import type { HookScope, HooksSettings } from "@/features/hook/types"
import { getHookIcon } from "@/features/hook/utils"
import { AddSubagentDialog } from "@/features/subagent/components/add-subagent-dialog"
import { useAgentFiles, useMemoryFiles } from "@/hooks/use-config"
import { useMcpMutations, useMcpQuery } from "@/hooks/use-mcp"
import { usePluginsQuery } from "@/hooks/use-plugins"
import { m } from "@/paraglide/messages"
import type { BoardColumnId, Scope } from "@/shared/types"
import { AddAgentDialog } from "./AddAgentDialog"
import { AddMcpDialog } from "./AddMcpDialog"
import { AddSkillDialog } from "./AddSkillDialog"
import { BoardColumnSettings } from "./BoardColumnSettings"
Expand Down Expand Up @@ -232,7 +232,7 @@ export function BoardLayout() {

// ── Add-dialog state ──
type AddDialogState = {
colId: "skills" | "agents" | "hooks" | "mcp"
colId: "skills" | "subagents" | "hooks" | "mcp"
scope: Scope | HookScope
} | null
const [addDialog, setAddDialog] = useState<AddDialogState>(null)
Expand All @@ -246,7 +246,7 @@ export function BoardLayout() {
} = useAgentFiles("skill")
const {
query: { data: agents = [], isLoading: agentsLoading },
} = useAgentFiles("agent")
} = useAgentFiles("subagent")
const { data: globalHooks = {}, isLoading: globalHooksLoading } =
useHooksQuery("user")
const { data: projectHooks = {}, isLoading: projectHooksLoading } =
Expand Down Expand Up @@ -283,7 +283,7 @@ export function BoardLayout() {
items: skills.filter((s) => s.type === "command"),
loading: skillsLoading,
},
agent: { items: agents, loading: agentsLoading },
subagent: { items: agents, loading: agentsLoading },
hook: {
items: hookItems,
loading: globalHooksLoading || projectHooksLoading,
Expand Down Expand Up @@ -359,9 +359,9 @@ export function BoardLayout() {
color: "green",
},
{
id: "agents",
id: "subagents",
title: "Agents",
icon: ENTITY_ICONS.agent,
icon: ENTITY_ICONS.subagent,
scopes: ["user", "project"],
color: "orange",
},
Expand Down Expand Up @@ -496,13 +496,13 @@ export function BoardLayout() {
</>
)
}
case "agents":
case "subagents":
return (
<EntityListPanel
config={agentConfig}
config={subagentConfig}
items={agents}
{...common}
emptyDescription={m.board_no_agents()}
emptyDescription={m.board_no_subagents()}
isLoading={agentsLoading}
/>
)
Expand Down Expand Up @@ -580,7 +580,7 @@ export function BoardLayout() {
)}
</Button>
) : col.id === "skills" ||
col.id === "agents" ||
col.id === "subagents" ||
col.id === "hooks" ||
col.id === "mcp" ? (
<Button
Expand All @@ -591,7 +591,7 @@ export function BoardLayout() {
setAddDialog({
colId: col.id as
| "skills"
| "agents"
| "subagents"
| "hooks"
| "mcp",
scope: "user",
Expand Down Expand Up @@ -664,8 +664,8 @@ export function BoardLayout() {
onClose={() => setAddDialog(null)}
/>
)}
{addDialog?.colId === "agents" && (
<AddAgentDialog
{addDialog?.colId === "subagents" && (
<AddSubagentDialog
scope={addDialog.scope as Scope}
onClose={() => setAddDialog(null)}
/>
Expand Down
18 changes: 9 additions & 9 deletions src/components/board/PluginsPanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@ function hasPluginContents(plugin: Plugin): boolean {
if (!c) return false
return (
c.skills.length > 0 ||
c.agents.length > 0 ||
c.subagents.length > 0 ||
c.mcpServers.length > 0 ||
Object.keys(c.hooks ?? {}).length > 0
)
Expand Down Expand Up @@ -179,7 +179,7 @@ function PluginTreeItem({
}) {
const contents = plugin.contents
const skills = contents?.skills ?? []
const agents = contents?.agents ?? []
const subagents = contents?.subagents ?? []
const mcpServers = contents?.mcpServers ?? []
const hookEntries = Object.entries(contents?.hooks ?? {})

Expand Down Expand Up @@ -299,16 +299,16 @@ function PluginTreeItem({
</>
)}

{agents.length > 0 && (
{subagents.length > 0 && (
<>
<CategoryLabel
icon={ENTITY_ICONS.agent}
icon={ENTITY_ICONS.subagent}
label="Agents"
count={agents.length}
count={subagents.length}
/>
{agents.map((a) => {
const t = { type: "agent", key: a.path }
const acts = ENTITY_ACTIONS.agent.filter(openOnlyFilter)
{subagents.map((a) => {
const t = { type: "subagent", key: a.path }
const acts = ENTITY_ACTIONS.subagent.filter(openOnlyFilter)
return (
<EntityActionContextMenu
key={a.name}
Expand All @@ -317,7 +317,7 @@ function PluginTreeItem({
itemName={a.frontmatter?.name ?? a.name}
>
<ListSubItem
icon={ENTITY_ICONS.agent}
icon={ENTITY_ICONS.subagent}
label={a.name}
trailing={
<EntityActionDropdown
Expand Down
Loading
Loading