[codex] Add room skill settings inventory (#132)

Add read-only room skill settings inventory.\n\nNo production deploy/restart.
This commit is contained in:
Eyejoker
2026-05-04 03:06:34 +09:00
committed by GitHub
parent 1c5be2094d
commit e90703d124
15 changed files with 902 additions and 6 deletions

View File

@@ -114,9 +114,67 @@
line-height: 1.4;
}
.runtime-room-skill-list {
display: grid;
gap: 12px;
margin-top: 14px;
}
.runtime-room-skill-card {
display: grid;
gap: 12px;
border-radius: 16px;
background: rgba(15, 23, 42, 0.38);
padding: 14px;
}
.runtime-room-skill-card > header {
display: flex;
align-items: flex-start;
justify-content: space-between;
gap: 12px;
}
.runtime-room-skill-card > header > div {
display: grid;
gap: 3px;
min-width: 0;
}
.runtime-room-skill-card span,
.runtime-room-agent-policy small {
color: var(--muted);
}
.runtime-room-skill-card code {
color: var(--muted);
font-size: 0.72rem;
overflow-wrap: anywhere;
}
.runtime-room-agent-grid {
display: grid;
grid-template-columns: repeat(2, minmax(0, 1fr));
gap: 10px;
}
.runtime-room-agent-policy {
display: grid;
gap: 4px;
border: 1px solid rgba(148, 163, 184, 0.14);
border-radius: 14px;
padding: 12px;
}
.runtime-room-agent-policy > span {
color: var(--text);
font-size: 0.86rem;
}
@media (max-width: 980px) {
.runtime-summary-card,
.runtime-skill-grid {
.runtime-skill-grid,
.runtime-room-agent-grid {
grid-template-columns: 1fr;
}
}
@@ -124,7 +182,8 @@
@media (max-width: 720px) {
.runtime-card-head,
.runtime-skill-card header,
.runtime-path-row {
.runtime-path-row,
.runtime-room-skill-card > header {
flex-direction: column;
}
}

View File

@@ -1,11 +1,14 @@
import { useEffect, useState } from 'react';
import {
fetchRoomSkillSettings,
fetchRuntimeInventory,
type RuntimeAgentInventory,
type RuntimeInventorySnapshot,
type RuntimePathSnapshot,
type RuntimeSkillDirSnapshot,
type RoomSkillCatalogItem,
type RoomSkillSettingsSnapshot,
} from './api';
import { SettingsSectionHeading } from './SettingsPanelChrome';
import './RuntimeInventorySettings.css';
@@ -98,18 +101,114 @@ function AgentInventoryCard({
);
}
function agentLabel(agentType: 'claude-code' | 'codex') {
return agentType === 'codex' ? 'Codex' : 'Claude Code';
}
function RoomSkillPolicyCard({
snapshot,
}: {
snapshot: RoomSkillSettingsSnapshot | null;
}) {
if (!snapshot) {
return (
<article className="runtime-agent-card">
<header className="runtime-card-head">
<h4> </h4>
</header>
<p className="settings-hint"> </p>
</article>
);
}
const catalogById = new Map<string, RoomSkillCatalogItem>(
snapshot.catalog.map((skill) => [skill.id, skill]),
);
const roomPreview = snapshot.rooms.slice(0, 8);
return (
<article className="runtime-agent-card">
<header className="runtime-card-head">
<div>
<h4> </h4>
<p className="settings-hint">
. PR에서
enable/disable .
</p>
</div>
<span className="settings-account-badge is-active">
{snapshot.catalog.length} skills · {snapshot.rooms.length} rooms
</span>
</header>
{roomPreview.length === 0 ? (
<p className="settings-hint"> .</p>
) : (
<div className="runtime-room-skill-list">
{roomPreview.map((room) => (
<section className="runtime-room-skill-card" key={room.jid}>
<header>
<div>
<strong>{room.name}</strong>
<span>{room.folder}</span>
</div>
<code>{room.jid}</code>
</header>
<div className="runtime-room-agent-grid">
{room.agents.map((agent) => {
const disabledNames = agent.disabledSkillIds
.map((id) => catalogById.get(id)?.displayName ?? id)
.slice(0, 3);
return (
<div
className="runtime-room-agent-policy"
key={`${room.jid}:${agent.agentType}`}
>
<strong>{agentLabel(agent.agentType)}</strong>
<span>
{agent.mode === 'all-enabled'
? '기본 전체 ON'
: `${agent.disabledSkillIds.length}개 OFF`}
</span>
<small>
{agent.effectiveEnabledSkillIds.length} / {' '}
{agent.availableSkillIds.length}
</small>
{disabledNames.length > 0 ? (
<small>OFF: {disabledNames.join(', ')}</small>
) : null}
</div>
);
})}
</div>
</section>
))}
</div>
)}
{snapshot.rooms.length > roomPreview.length ? (
<small className="settings-hint">
{snapshot.rooms.length - roomPreview.length}
</small>
) : null}
</article>
);
}
export function RuntimeInventorySettings() {
const [snapshot, setSnapshot] = useState<RuntimeInventorySnapshot | null>(
null,
);
const [roomSkillSnapshot, setRoomSkillSnapshot] =
useState<RoomSkillSettingsSnapshot | null>(null);
const [error, setError] = useState<string | null>(null);
useEffect(() => {
let cancelled = false;
fetchRuntimeInventory()
.then((value) => {
Promise.all([fetchRuntimeInventory(), fetchRoomSkillSettings()])
.then(([value, roomSkills]) => {
if (cancelled) return;
setSnapshot(value);
setRoomSkillSnapshot(roomSkills);
setError(null);
})
.catch((err) => {
@@ -156,6 +255,7 @@ export function RuntimeInventorySettings() {
<AgentInventoryCard title="Codex" inventory={snapshot.codex} />
<AgentInventoryCard title="Claude Code" inventory={snapshot.claude} />
<RoomSkillPolicyCard snapshot={roomSkillSnapshot} />
<article className="runtime-agent-card">
<header className="runtime-card-head">

View File

@@ -438,6 +438,41 @@ export interface RuntimeInventorySnapshot {
};
}
export type RoomSkillScope = 'codex-user' | 'claude-user' | 'runner';
export interface RoomSkillCatalogItem {
id: string;
scope: RoomSkillScope;
name: string;
displayName: string;
description: string | null;
path: string;
agentTypes: Array<'claude-code' | 'codex'>;
}
export interface RoomSkillAgentPolicy {
agentType: 'claude-code' | 'codex';
mode: 'all-enabled' | 'custom';
availableSkillIds: string[];
disabledSkillIds: string[];
explicitEnabledSkillIds: string[];
effectiveEnabledSkillIds: string[];
}
export interface RoomSkillPolicyRoom {
jid: string;
name: string;
folder: string;
roomMode?: 'single' | 'tribunal';
agents: RoomSkillAgentPolicy[];
}
export interface RoomSkillSettingsSnapshot {
generatedAt: string;
catalog: RoomSkillCatalogItem[];
rooms: RoomSkillPolicyRoom[];
}
export interface MoaReferenceStatus {
model: string;
checkedAt: string;
@@ -596,6 +631,10 @@ export async function fetchRuntimeInventory(): Promise<RuntimeInventorySnapshot>
return fetchJson('/api/settings/runtime-inventory');
}
export async function fetchRoomSkillSettings(): Promise<RoomSkillSettingsSnapshot> {
return fetchJson('/api/settings/room-skills');
}
export async function updateCodexFeatures(
input: Partial<CodexFeatureSnapshot>,
): Promise<CodexFeatureSnapshot> {