复制安装命令
用 Codex 或 Claude 安装复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它先审查 Skill 页面再帮你安装。
复制前请先查看来源、License 和安全提示。
This is the open-source content repository behind
用 Codex 或 Claude 安装复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它先审查 Skill 页面再帮你安装。
复制前请先查看来源、License 和安全提示。
来源文件:README.md
This is the open-source content repository behind Skill Store. It stores every approved Agent Skill, the records that go with it, and the automated security audits published with each skill.
This repo is a companion to the Skill Store platform, not the place to submit skills. Skills are added through skillstore.io — its review pipeline writes to this repo automatically. Please do not open a pull request here to add a skill; PRs adding skills will be closed. See Contributing a skill below.
The recommended way to install any skill is the skillstore CLI — one command works for both Claude Code and Codex:
npx skillstore add author/skill-name
For example:
npx skillstore add aiskillstore/code-review
It downloads the skill and drops it into the right skills/ directory for your tool. Claude Code auto-discovers it; for Codex, restart the session.
Prefer to do it by hand, or installing via Claude Web? See the full Installation Guides for every method (CLI, manual, and ZIP upload) and the scope directories (~/.agents/skills/, .claude/skills/, ~/.claude/skills/, .codex/skills/, …).
Submit through the platform — not through a pull request:
SKILL.md.SKILL.md — the skill definition (required, per the Agent Skills spec)LICENSE (recommended)Every submission is scanned automatically before it can be published. The audit flags things like:
eval, exec, raw system commands)Security analysis is report-only: findings inform maintainers and users, but a risk result does not automatically block an otherwise approved skill from being published. See our Security Trust Center for the methodology, limitations, and risk-level definitions.
Live Security Passport example:
.
├── skills/ # Approved, published skills (one folder each, with SKILL.md)
├── pending/ # Submissions awaiting review
├── packages/
│ ├── cli/ # The `skillstore` CLI (npx skillstore add …)
│ └── skillstore/
├── schemas/ # JSON schemas for skill records
├── scripts/ # Maintenance & scoring scripts
└── .github/workflows/ # Submission, audit, and sync automation
The contents of this repo are maintained by Skill Store's automated pipeline. Manual changes are limited to maintainers.
The marketplace catalog is MIT-licensed. Individual skills carry their own licenses — check each skill's LICENSE file.
name: blueprinteventbus-integration
description: "Implement event-driven communication using BlueprintEventBus for cross-module coordination. Use this skill when modules need to communicate without tight coupling, broadcasting domain events (task.created, member.added), subscribing to events with proper lifecycle management, and implementing event-driven workflows. Ensures events follow naming conventions ([module].[action]), include Blueprint context, and use takeUntilDestroyed() for automatic cleanup."
license: "MIT"This skill helps implement event-driven architecture using BlueprintEventBus.
✅ DO use EventBus for:
❌ DON'T use EventBus for:
interface BlueprintEvent<T = any> {
type: string; // Format: [module].[action]
blueprintId: string;
timestamp: Date;
actor: string; // User ID who triggered event
data: T;
metadata?: Record<string, any>;
}
[module].[action]
Examples:
task.createdtask.updatedtask.deletedtask.assignedmember.addedmember.removedfile.uploadedblueprint.archivedimport { inject } from '@angular/core';
import { BlueprintEventBus } from '@core/services/blueprint-event-bus.service';
@Injectable({ providedIn: 'root' })
export class TaskService {
private eventBus = inject(BlueprintEventBus);
private taskRepository = inject(TaskRepository);
async createTask(blueprintId: string, task: CreateTaskDto): Promise<Task> {
// 1. Execute business logic
const created = await this.taskRepository.create(blueprintId, task);
// 2. Publish domain event
this.eventBus.publish({
type: 'task.created',
blueprintId,
timestamp: new Date(),
actor: this.getCurrentUserId(),
data: created
});
return created;
}
async updateTask(taskId: string, updates: Partial<Task>): Promise<Task> {
const task = await this.taskRepository.findById(taskId);
if (!task) throw new Error('Task not found');
const updated = await this.taskRepository.update(taskId, updates);
// Publish update event with before/after data
this.eventBus.publish({
type: 'task.updated',
blueprintId: task.blueprintId,
timestamp: new Date(),
actor: this.getCurrentUserId(),
data: updated,
metadata: {
before: task,
changes: updates
}
});
return updated;
}
}
// Define event types
interface TaskCreatedEvent extends BlueprintEvent<Task> {
type: 'task.created';
}
interface TaskAssignedEvent extends BlueprintEvent<{
task: Task;
assignee: string;
assigneeType: 'user' | 'team' | 'partner';
}> {
type: 'task.assigned';
}
// Publish with type safety
this.eventBus.publish<TaskCreatedEvent>({
type: 'task.created',
blueprintId: created.blueprintId,
timestamp: new Date(),
actor: this.getCurrentUserId(),
data: created
});
import { Component, inject, signal, DestroyRef } from '@angular/core';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import { BlueprintEventBus } from '@core/services/blueprint-event-bus.service';
@Component({
selector: 'app-task-list',
template: `...`
})
export class TaskListComponent {
private eventBus = inject(BlueprintEventBus);
private destroyRef = inject(DestroyRef);
tasks = signal<Task[]>([]);
ngOnInit(): void {
// Subscribe to task.created events
this.eventBus.subscribe('task.created')
.pipe(takeUntilDestroyed(this.destroyRef))
.subscribe(event => {
console.log('New task created:', event.data);
this.tasks.update(tasks => [...tasks, event.data]);
});
}
}
import { filter } from 'rxjs/operators';
@Component({
selector: 'app-blueprint-tasks',
template: `...`
})
export class BlueprintTasksComponent {
private eventBus = inject(BlueprintEventBus);
private destroyRef = inject(DestroyRef);
blueprintId = input.required<string>();
tasks = signal<Task[]>([]);
ngOnInit(): void {
// Only listen to events in current Blueprint
this.eventBus.subscribe('task.created')
.pipe(
filter(event => event.blueprintId === this.blueprintId()),
takeUntilDestroyed(this.destroyRef)
)
.subscribe(event => {
this.tasks.update(tasks => [...tasks, event.data]);
});
// Listen to updates
this.eventBus.subscribe('task.updated')
.pipe(
filter(event => event.blueprintId === this.blueprintId()),
takeUntilDestroyed(this.destroyRef)
)
.subscribe(event => {
this.tasks.update(tasks =>
tasks.map(t => t.id === event.data.id ? event.data : t)
);
});
// Listen to deletions
this.eventBus.subscribe('task.deleted')
.pipe(
filter(event => event.blueprintId === this.blueprintId()),
takeUntilDestroyed(this.destroyRef)
)
.subscribe(event => {
this.tasks.update(tasks =>
tasks.filter(t => t.id !== event.data.id)
);
});
}
}
import { merge } from 'rxjs';
ngOnInit(): void {
// Listen to multiple event types
merge(
this.eventBus.subscribe('task.created'),
this.eventBus.subscribe('task.updated'),
this.eventBus.subscribe('task.deleted')
)
.pipe(
filter(event => event.blueprintId === this.blueprintId()),
takeUntilDestroyed(this.destroyRef)
)
.subscribe(event => {
console.log('Task event:', event.type, event.data);
this.refreshTasks();
});
}
@Injectable({ providedIn: 'root' })
export class AuditLogService {
private eventBus = inject(BlueprintEventBus);
private auditLogRepository = inject(AuditLogRepository);
constructor() {
// Listen to ALL events for audit trail
this.eventBus.subscribeAll()
.pipe(takeUntilDestroyed())
.subscribe(event => {
this.logEvent(event);
});
}
private async logEvent(event: BlueprintEvent): Promise<void> {
await this.auditLogRepository.create({
eventType: event.type,
blueprintId: event.blueprintId,
actor: event.actor,
timestamp: event.timestamp,
data: event.data,
metadata: event.metadata
});
}
}
@Injectable({ providedIn: 'root' })
export class NotificationService {
private eventBus = inject(BlueprintEventBus);
private notificationRepository = inject(NotificationRepository);
constructor() {
// Listen to events that trigger notifications
this.setupNotificationListeners();
}
private setupNotificationListeners(): void {
// Task assigned → notify assignee
this.eventBus.subscribe('task.assigned')
.pipe(takeUntilDestroyed())
.subscribe(async event => {
await this.notifyUser(event.data.assignee, {
title: 'New Task Assigned',
message: `You have been assigned: ${event.data.task.title}`,
blueprintId: event.blueprintId
});
});
// Member added → notify member
this.eventBus.subscribe('member.added')
.pipe(takeUntilDestroyed())
.subscribe(async event => {
await this.notifyUser(event.data.userId, {
title: 'Added to Blueprint',
message: `You have been added to ${event.data.blueprintName}`,
blueprintId: event.blueprintId
});
});
}
}
@Component({
selector: 'app-activity-feed',
template: `
<div class="activity-feed">
@for (activity of activities(); track activity.id) {
<div class="activity-item">
<span class="timestamp">{{ activity.timestamp | date }}</span>
<span class="message">{{ activity.message }}</span>
</div>
}
</div>
`
})
export class ActivityFeedComponent {
private eventBus = inject(BlueprintEventBus);
private destroyRef = inject(DestroyRef);
blueprintId = input.required<string>();
activities = signal<Activity[]>([]);
ngOnInit(): void {
// Listen to all events in Blueprint
this.eventBus.subscribeAll()
.pipe(
filter(event => event.blueprintId === this.blueprintId()),
takeUntilDestroyed(this.destroyRef)
)
.subscribe(event => {
this.addActivity({
id: crypto.randomUUID(),
type: event.type,
message: this.formatEventMessage(event),
timestamp: event.timestamp
});
});
}
private formatEventMessage(event: BlueprintEvent): string {
switch (event.type) {
case 'task.created':
return `Task "${event.data.title}" was created`;
case 'task.assigned':
return `Task assigned to ${event.data.assignee}`;
case 'member.added':
return `${event.data.userName} joined the Blueprint`;
default:
return `Event: ${event.type}`;
}
}
private addActivity(activity: Activity): void {
this.activities.update(activities => [activity, ...activities].slice(0, 50));
}
}
@Injectable({ providedIn: 'root' })
export class TaskWorkflowService {
private eventBus = inject(BlueprintEventBus);
private taskRepository = inject(TaskRepository);
private notificationService = inject(NotificationService);
constructor() {
this.setupWorkflows();
}
private setupWorkflows(): void {
// When task is completed → trigger follow-up actions
this.eventBus.subscribe('task.completed')
.pipe(takeUntilDestroyed())
.subscribe(async event => {
const task = event.data;
// 1. Check if task has dependencies
const dependentTasks = await this.taskRepository
.findDependentTasks(task.id);
// 2. Update dependent tasks
for (const depTask of dependentTasks) {
await this.taskRepository.update(depTask.id, {
status: 'ready'
});
}
// 3. Notify stakeholders
await this.notificationService.notifyTaskCompletion(task);
});
}
}
Reference implementation:
import { Injectable } from '@angular/core';
import { Subject, Observable } from 'rxjs';
import { filter } from 'rxjs/operators';
export interface BlueprintEvent<T = any> {
type: string;
blueprintId: string;
timestamp: Date;
actor: string;
data: T;
metadata?: Record<string, any>;
}
@Injectable({ providedIn: 'root' })
export class BlueprintEventBus {
private eventStream = new Subject<BlueprintEvent>();
/**
* Publish event to all subscribers
*/
publish<T = any>(event: BlueprintEvent<T>): void {
this.eventStream.next(event);
}
/**
* Subscribe to specific event type
*/
subscribe<T = any>(eventType: string): Observable<BlueprintEvent<T>> {
return this.eventStream.asObservable().pipe(
filter(event => event.type === eventType)
);
}
/**
* Subscribe to all events
*/
subscribeAll(): Observable<BlueprintEvent> {
return this.eventStream.asObservable();
}
/**
* Subscribe to events in specific Blueprint
*/
subscribeToBlueprintEvents(blueprintId: string): Observable<BlueprintEvent> {
return this.eventStream.asObservable().pipe(
filter(event => event.blueprintId === blueprintId)
);
}
}
describe('EventBus Integration', () => {
let eventBus: BlueprintEventBus;
let taskService: TaskService;
beforeEach(() => {
eventBus = TestBed.inject(BlueprintEventBus);
taskService = TestBed.inject(TaskService);
});
it('should publish event when task is created', (done) => {
// Subscribe to event
eventBus.subscribe('task.created').subscribe(event => {
expect(event.type).toBe('task.created');
expect(event.data.title).toBe('Test Task');
done();
});
// Create task (triggers event)
taskService.createTask('blueprint1', { title: 'Test Task' });
});
it('should filter events by Blueprint', (done) => {
let receivedEvents = 0;
eventBus.subscribeAll()
.pipe(filter(event => event.blueprintId === 'blueprint1'))
.subscribe(() => {
receivedEvents++;
});
// Publish events to different Blueprints
eventBus.publish({
type: 'task.created',
blueprintId: 'blueprint1',
timestamp: new Date(),
actor: 'user1',
data: {}
});
eventBus.publish({
type: 'task.created',
blueprintId: 'blueprint2',
timestamp: new Date(),
actor: 'user1',
data: {}
});
setTimeout(() => {
expect(receivedEvents).toBe(1);
done();
}, 100);
});
});
When integrating EventBus:
评论 (0)
暂无评论,成为第一个评论者吧!