소스 정보
- 저장소
- majiayu000/claude-skill-registry
- 최근 소스 활동
- 2026년 6월 23일 12:15
- 감지된 SKILL.md 언어
- 영어
- 스타
- 543
- 포크
- 85
설치 방법
기본적으로 소스를 먼저 확인하는 Prompt가 선택됩니다. 직접 명령으로 전환하거나 로컬 사본을 다운로드할 수도 있습니다.
소스 파일 검토
설치 여부를 결정하기 전에 SKILL.md와 SkillsMP에 표시된 보조 파일을 읽어 보세요.
메뉴
기본적으로 소스를 먼저 확인하는 Prompt가 선택됩니다. 직접 명령으로 전환하거나 로컬 사본을 다운로드할 수도 있습니다.
설치 여부를 결정하기 전에 SKILL.md와 SkillsMP에 표시된 보조 파일을 읽어 보세요.
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
직접 명령은 검토 Prompt를 거치지 않습니다. 실행하기 전에 소스를 확인하세요.
npx skills add https://github.com/majiayu000/claude-skill-registry --skill producer-implementation명령은 한 줄로 유지됩니다. 복사하기 전에 가로로 스크롤해 전체 내용을 확인하세요.
로컬 사본을 원하시나요? SkillsMP에서 현재 제공할 수 있는 파일을 다운로드하세요.
SKILL.md 표시 중
| name | producer-implementation |
| description | Producer implementation patterns for API operations and business logic |
// ❌ WRONG - Duplicating connection context
class UserProducer {
async getUser(
userId: string,
apiKey: string, // NO! In ConnectionProfile
baseUrl: string, // NO! In ConnectionProfile
orgId: string // NO! In ConnectionState
): Promise<User> {
// ...
}
}
// ✅ CORRECT - Only operation-specific parameters
class UserProducer {
constructor(private client: GitHubClient) {}
async getUser(userId: string): Promise<User> {
// Client already has apiKey, baseUrl, orgId from profile/state
const { data } = await this.client.apiClient
.request({
url: `/users/${userId}`, // baseUrl handled by client
method: 'get'
// apiKey/auth handled by client interceptor
// orgId available via this.client.getOrganizationId()
})
.catch(handleAxiosError);
return toUser(data);
}
}
RULES:
WHY: Connection context is established once during connect() and managed by the client. Operations should focus on business logic, not connection details.
class UserProducer {
constructor(private client: GitHubClient) {}
// List operation
async listUsers(
results: PagedResults<User>,
name?: string
): Promise<void> {
const { data } = await this.client.apiClient
.request({
url: '/users',
method: 'get',
params: {
limit: results.pageSize,
page: results.pageNumber,
name
}
})
.catch(handleAxiosError);
// Manual assignment (NOT ingest)
results.items = data.map(toUser) || [];
// Update count if API provides it
if (data.total !== undefined) {
results.count = data.total;
}
}
// Get operation
async getUser(userId: string): Promise<User> {
const { data } = ..
.({
: ,
:
})
.(handleAxiosError);
(data);
}
(: ): <> {
{ data } = ..
.({
: ,
: ,
: input
})
.(handleAxiosError);
(data);
}
(: ): <> {
..
.({
: ,
:
})
.(handleAxiosError);
}
}
any in producer method signaturesresults.items = ...ingest() - Unreliable trimming behaviorresults.count when API provides totalresults.pageToken for token-based paginationpageNumber, pageSize, or pageCountconst output: Type pattern// ✅ REQUIRED PATTERN
export function toUser(data: any): User {
// Validate required fields first
if (!data?.id || !data?.name) {
throw new InvalidInputError('user', 'Missing required fields');
}
const output: User = {
id: map(UUID, data.id), // Required - validated above
name: data.name, // Required - validated above
email: map(Email, data.email), // Optional - returns Email | undefined
website: map(URL, data.website), // Optional - returns URL | undefined
createdAt: map(DateTime, data.created_at)?.toDate() // DateTime requires .toDate()
};
return output;
}
CRITICAL:
! (non-null assertion) - Design code to avoid itmap() over constructors - Use map() from @auditmation/util-hub-module-utils unless map() doesn't meet requirementsType | undefined automaticallymap(DateTime, value)?.toDate() since interface expects Date not DateTime|| undefined or map() which handles this)// ⚠️ AVOID - Using constructors without checking if map() works
export function toUser(data: any): User {
const output: User = {
id: new UUID(data.id), // ⚠️ Try map(UUID, ...) first
email: new Email(data.email), // ⚠️ Try map(Email, ...) first
createdAt: new DateTime(data.created_at).toDate() // ⚠️ Try map(DateTime, ...)?.toDate() first
};
return output;
}
// Only use constructors if map() doesn't meet requirements
🚨 CRITICAL: API spec never uses nullable: true, mappers convert null → undefined
RULE: If external API returns null values, convert them to undefined in mappers.
// ✅ CORRECT - map() automatically converts null → undefined
export function toUser(data: any): User {
const output: User = {
id: map(UUID, data.id), // map() returns undefined if data.id is null
name: data.name || undefined, // Explicit: null → undefined
email: map(Email, data.email), // map() handles null → undefined
website: map(URL, data.website) // map() handles null → undefined
};
return output;
}
// ✅ CORRECT - Explicit conversion for non-map fields
export function toOrganization(data: any): Organization {
const output: Organization = {
id: data.id !== null ? String(data.id) : undefined, // null → undefined
name: data. || ,
: data. ??
};
output;
}
(): {
: = {
: (, data.),
: data.,
: data.
};
output;
}
WHY:
| undefined for optional fields, not | nullnullable: true (keeps it clean)Type | undefined, not Type | nullmap() function behavior:
map(Type, null) → returns undefinedmap(Type, undefined) → returns undefinedmap(Type, value) → returns Type instance or undefined|| undefined on number fields// ❌ WRONG - Turns 0 into undefined
count: data.count || undefined // 0 becomes undefined!
age: data.age || undefined // 0 becomes undefined!
balance: data.balance || undefined // 0 becomes undefined!
// ✅ CORRECT - Preserves 0 values
count: data.count ?? undefined // 0 stays 0, null/undefined → undefined
age: data.age !== null ? data.age : undefined // Explicit null check
balance: data.balance !== undefined ? data.balance : undefined
WHY: 0 || undefined evaluates to undefined because 0 is falsy.
?? undefined (nullish coalescing) for numbers!== null / !== undefined checks|| undefined is ONLY safe for strings, objects, and arraysexport function toUserInfo(data: any): UserInfo {
const output: UserInfo = {
...toUser(data), // Reuse base mapper
lastLogin: map(DateTime, data.last_login)?.toDate(),
permissions: data.permissions || []
};
return output;
}
MANDATORY 3-STEP:
// Interface has 5 fields → Mapper must map 5 fields
interface UserInfo {
id: UUID; // Required
name: string; // Required
email?: Email; // Optional
createdAt: Date; // Required
status?: StatusEnum;// Optional
}
export function toUserInfo(raw: any): UserInfo {
const output: UserInfo = {
// Required (3)
id: map(UUID, raw.id),
name: raw.name,
createdAt: map(Date, raw.created_at),
// Optional (2) - MUST be mapped too
email: map(Email, raw.email),
status: toEnum(StatusEnum, raw.status)
};
return output;
}
🚨 CRITICAL: Mappers MUST throw errors for missing required fields
// ✅ CORRECT - Validate required fields
export function toOrganization(data: any): Organization | undefined {
if (!data) return undefined;
// Required field validation - MUST throw error
if (!data.id && data.id !== 0) {
throw new InvalidInputError('organization', 'Missing required field: id');
}
return {
id: String(data.id),
name: data.name || undefined, // Optional field
};
}
// ❌ WRONG - Silently returning undefined for missing required field
export function toOrganization(data: any): Organization | undefined {
if (!data) return undefined;
if (!data.id) return undefined; // NO! Should throw error
return {
id: String(data.id),
name: data. || ,
};
}
When objects appear in nested contexts and might have incomplete data, the parent mapper handles validation errors:
// Parent mapper catches validation errors from child mappers
export function toTokenScope(data: any): TokenScope {
// Nested org might have null id in some API responses
let org: Organization | undefined;
if (data.org) {
try {
org = toOrganization(data.org); // May throw if id is null
} catch (error) {
// Incomplete nested organization - treat as undefined
org = undefined;
}
}
return {
org,
user: data.user ? toUser(data.user) : undefined,
scope: data.scope || undefined,
};
}
RULE:
🚨 CRITICAL: Enum values MUST be snake_case
STANDARD: All enum values in this codebase use snake_case format.
toEnum(EnumClass, value)toEnum automatically converts input to snake_case (matches our standard)// ✅ CORRECT - Default snake_case transform (PREFERRED)
const status = toEnum(StatusEnum, data.status);
// Input: "ActiveUser" or "active_user" or "ACTIVE_USER" → Converted to "active_user" → Mapped to enum
// This is the standard - use this unless API has special requirements
// ⚠️ RARE - Custom transform only when API doesn't use snake_case
const status = toEnum(StatusEnum, data.status, (val) => val);
// Only use if API truly returns exact enum values in non-snake_case format
// Most APIs return snake_case, so this is rarely needed
// For required enums, validate first
if (!data?.status) {
throw new InvalidInputError('resource', 'Missing status');
}
const status = toEnum(StatusEnum, data.status); // Safe - uses default snake_case
Why snake_case is our standard:
toEnum default behavior aligns with this standardWhen custom transform might be needed (rare):
IMPORTANT: Before using custom transform, verify the API truly doesn't accept snake_case. Most APIs accept multiple formats but prefer snake_case.
function toAddress(data: any): Address | undefined {
if (!data) return undefined;
const output: Address = {
street: data.street,
city: data.city,
country: toCountry(data.country)
};
return output;
}
function toUsers(data: any[]): User[] {
if (!Array.isArray(data)) return [];
return data.map(toUser);
}
function toTreeNode(data: any): TreeNode {
const output: TreeNode = {
id: data.id,
name: data.name,
children: data.children?.map(toTreeNode)
};
return output;
}
# Check producers don't have connection parameters
grep -E "async (get|list|create|update|delete).*\(.*[,\s](apiKey|token|baseUrl|organizationId)" src/*Producer.ts && echo "❌ Producer has connection params!" || echo "✅ Producers clean"
# Check producers use error handler
grep -E "\.catch\(handleAxiosError\)" src/*Producer.ts && echo "✅ Error handling present" || echo "⚠️ Missing error handler"
# Check no Promise<any> in producers
grep -E "Promise<any>" src/*Producer.ts && echo "❌ Found Promise<any>!" || echo "✅ No Promise<any>"
# Check mappers use const output pattern
grep -E "const output: [A-Z]" src/Mappers.ts && echo "✅ Using output pattern" || echo "⚠️ Check mapper pattern"
# Check mappers validate required fields
grep -E "throw new InvalidInputError.*Missing required" src/Mappers.ts && echo "✅ Required field validation present" || echo "⚠️ Check required field validation"
# Check no non-null assertions
grep "!" src/Mappers.ts | grep -v "//" | grep -v "!=" && echo "❌ Found non-null assertions!" || echo "✅ No non-null assertions"
# Check PagedResults uses manual assignment (not ingest)
grep -E "results\.items\s*=" src/*Producer.ts && echo "✅ Manual assignment" || echo "⚠️ Check PagedResults pattern"
# Check no ingest() usage
grep "ingest(" src/*Producer.ts && echo "❌ Found ingest()!" || echo "✅ No ingest()"
See code-comment-style skill for complete guidelines
NEVER comment standard patterns:
// ❌ WRONG - Commenting obvious pagination conversion
// Convert pageNumber/pageSize to offset/limit
if (results.pageNumber && results.pageSize) {
params.offset = (results.pageNumber - 1) * results.pageSize;
params.limit = Math.min(Math.max(results.pageSize, 1), 1000);
}
// ✅ CORRECT - Standard pattern needs no comment
if (results.pageNumber && results.pageSize) {
params.offset = (results.pageNumber - 1) * results.pageSize;
params.limit = Math.min(Math.max(results.pageSize, 1), 1000);
}
// ❌ WRONG - Commenting obvious mapper application
// Apply mappers and set pagination info from response structure
results.items = response.data.data.map(toUser);
results.count = response.data.totalCount || 0;
// ✅ CORRECT - Code is self-documenting
results.items = response.data.data.map(toUser);
results.count = response.data.totalCount || 0;
DO comment non-obvious API behavior:
// ✅ GOOD - Explains API quirk
// API returns user in response.data.data for single gets
// but directly in response.data for list operations
const rawData = response.data.data || response.data;
Reference: See code-comment-style.md for full guidelines
SOC 직업 분류 기준