專責處理 CBF (Commanded Behavior Frame) 類型的需求。讀取規格目錄結構,生成/審查 Command Side 設計與實作。支援 Java、TypeScript、Go 多語言。
Generates and reviews CQRS Command Side code (Use Cases, Aggregates, Events) for Commanded Behavior Frames. It's triggered when analyzing CBF specs or when saga-orchestrator dispatches command tasks, supporting Java, TypeScript, and Go.
/plugin marketplace add knowlet/skills/plugin install knowlet-skills@knowlet/skillsThis skill inherits all available tools. When active, it can use any tool Claude has access to.
本 Skill 讀取以下規格檔案:
docs/specs/{feature-name}/
├── frame.yaml # 讀取 frame_concerns, cross_context_dependencies
├── requirements/ # 讀取業務規則
│ └── req-{n}-{feature}.yaml
├── machine/ # 讀取 Application 層規格
│ ├── controller.yaml
│ └── use-case.yaml
├── controlled-domain/ # 讀取 Domain 層規格
│ └── aggregate.yaml
└── cross-context/ # 讀取跨 BC 依賴
└── {context}.yaml
本 Skill 可作為 runSubagent 的任務目標:
saga-orchestrator → runSubagent → command-sub-agent
├── 讀取規格目錄
├── 套用 coding-standards
├── 套用 enforce-contract
└── 輸出 Command Side 代碼
task:
type: "command"
spec_dir: "docs/specs/create-workflow/"
language: "typescript" # | java | go
output_paths:
application: "src/application/use-cases/"
domain: "src/domain/"
# 從 frame.yaml 讀取
problem_frame: "CreateWorkflow"
frame_type: CommandedBehaviorFrame
# 識別 Frame Concerns 需要滿足
frame_concerns:
- FC1: Structure Integrity → 對應 aggregate.yaml#invariants
- FC2: Concurrency → 對應 use-case.yaml#transaction_boundary
# 識別跨 BC 依賴
cross_context_dependencies:
- XC1: Authorization → 需整合 ACL
從 machine/use-case.yaml 生成 Application 層代碼:
從 controlled-domain/aggregate.yaml 生成 Domain 層代碼:
從 cross-context/{context}.yaml 整合 ACL:
// src/application/use-cases/CreateWorkflowUseCase.ts
// Generated from: docs/specs/create-workflow/machine/use-case.yaml
import { AuthorizationService } from '@/domain/services/AuthorizationService';
import { WorkflowRepository } from '@/domain/repositories/WorkflowRepository';
import { EventPublisher } from '@/domain/events/EventPublisher';
import { Workflow } from '@/domain/aggregates/Workflow';
import { WorkflowCreatedEvent } from '@/domain/events/WorkflowCreatedEvent';
// ===== Input/Output (from use-case.yaml#input/output) =====
export interface CreateWorkflowInput {
readonly boardId: string;
readonly name: string;
readonly operatorId: string;
}
export interface CreateWorkflowOutput {
readonly workflowId: string;
readonly status: WorkflowStatus;
readonly createdAt: Date;
}
// ===== Use Case =====
export class CreateWorkflowUseCase {
constructor(
// XC1: Authorization dependency (from cross-context/authorization.yaml)
private readonly authorizationService: AuthorizationService,
private readonly workflowRepository: WorkflowRepository,
private readonly eventPublisher: EventPublisher,
) {}
async execute(input: CreateWorkflowInput): Promise<CreateWorkflowOutput> {
// ===== Pre-conditions (from use-case.yaml#contracts.pre_conditions) =====
if (!input.boardId) {
throw new ValidationError('boardId is required');
}
if (!input.name) {
throw new ValidationError('name is required');
}
if (!input.operatorId) {
throw new ValidationError('operatorId is required');
}
// ===== XC1: Authorization Check (from cross-context/authorization.yaml) =====
const authResult = await this.authorizationService.canExecute(
input.operatorId,
'create',
'Workflow',
input.boardId,
);
if (!authResult.authorized) {
throw new UnauthorizedError(`Not authorized: ${authResult.reason}`);
}
// ===== Domain Logic (from controlled-domain/aggregate.yaml) =====
const workflow = Workflow.create({
boardId: new BoardId(input.boardId),
name: new WorkflowName(input.name),
createdBy: new UserId(input.operatorId),
});
// ===== Persist =====
await this.workflowRepository.save(workflow);
// ===== Publish Domain Event (from use-case.yaml#publishes_events) =====
await this.eventPublisher.publish(new WorkflowCreatedEvent({
workflowId: workflow.id.value,
boardId: workflow.boardId.value,
name: workflow.name.value,
createdBy: workflow.createdBy.value,
createdAt: workflow.createdAt,
}));
// ===== Post-conditions (from use-case.yaml#contracts.post_conditions) =====
// POST1: result.workflowId is not null - enforced by return type
return {
workflowId: workflow.id.value,
status: workflow.status,
createdAt: workflow.createdAt,
};
}
}
// src/domain/aggregates/Workflow.ts
// Generated from: docs/specs/create-workflow/controlled-domain/aggregate.yaml
import { WorkflowId } from '../value-objects/WorkflowId';
import { WorkflowName } from '../value-objects/WorkflowName';
import { BoardId } from '../value-objects/BoardId';
import { Stage } from '../entities/Stage';
export class Workflow {
// ===== Identity (from aggregate.yaml#identity) =====
readonly id: WorkflowId;
// ===== Properties (from aggregate.yaml#properties) =====
readonly boardId: BoardId;
private _name: WorkflowName;
private _stages: Stage[] = [];
readonly createdBy: UserId;
readonly createdAt: Date;
private constructor(props: WorkflowProps) {
this.id = props.id;
this.boardId = props.boardId;
this._name = props.name;
this.createdBy = props.createdBy;
this.createdAt = props.createdAt;
// ===== INV1: Enforce invariants in constructor (from aggregate.yaml#invariants) =====
this.validateInvariants();
}
static create(props: CreateWorkflowProps): Workflow {
return new Workflow({
id: WorkflowId.generate(),
boardId: props.boardId,
name: props.name,
createdBy: props.createdBy,
createdAt: new Date(),
});
}
// ===== Mutating Methods =====
addStage(stage: Stage): void {
// ===== FC1: Structure Integrity - Lane Hierarchy (from frame_concerns) =====
// Stage may be root or nested, validated in Stage entity
this._stages.push(stage);
// Re-validate invariants after mutation
this.validateInvariants();
}
// ===== Invariants Validation (from aggregate.yaml#invariants.shared) =====
private validateInvariants(): void {
// INV1: Structure Integrity
for (const stage of this._stages) {
for (const lane of stage.swimLanes) {
// SwimLane cannot exist at root; it must be under a Stage
if (!lane.parentStage) {
throw new InvariantViolationError(
'SwimLane must be under a Stage'
);
}
}
}
}
// ===== Getters =====
get name(): WorkflowName {
return this._name;
}
get stages(): readonly Stage[] {
return [...this._stages];
}
get status(): WorkflowStatus {
return this._stages.length > 0
? WorkflowStatus.Active
: WorkflowStatus.Empty;
}
}
// src/application/usecase/create_workflow.go
// Generated from: docs/specs/create-workflow/machine/use-case.yaml
package usecase
import (
"context"
"time"
"myapp/domain/aggregates"
"myapp/domain/events"
"myapp/domain/services"
"myapp/domain/valueobjects"
)
// ===== Input/Output (from use-case.yaml) =====
type CreateWorkflowInput struct {
BoardID string `json:"board_id" validate:"required,uuid"`
Name string `json:"name" validate:"required,min=1,max=100"`
OperatorID string `json:"operator_id" validate:"required,uuid"`
}
type CreateWorkflowOutput struct {
WorkflowID string `json:"workflow_id"`
Status string `json:"status"`
CreatedAt time.Time `json:"created_at"`
}
// ===== Use Case =====
type CreateWorkflowUseCase struct {
authService services.AuthorizationService // XC1
workflowRepo aggregates.WorkflowRepository
eventPub events.EventPublisher
}
func NewCreateWorkflowUseCase(
authService services.AuthorizationService,
workflowRepo aggregates.WorkflowRepository,
eventPub events.EventPublisher,
) *CreateWorkflowUseCase {
return &CreateWorkflowUseCase{
authService: authService,
workflowRepo: workflowRepo,
eventPub: eventPub,
}
}
func (uc *CreateWorkflowUseCase) Execute(
ctx context.Context,
input CreateWorkflowInput,
) (*CreateWorkflowOutput, error) {
// ===== Pre-conditions (from use-case.yaml#contracts.pre_conditions) =====
if err := ValidateInput(input); err != nil {
return nil, err
}
// ===== XC1: Authorization Check =====
authResult, err := uc.authService.CanExecute(
ctx,
input.OperatorID,
services.ActionCreate,
services.ResourceWorkflow,
input.BoardID,
)
if err != nil {
return nil, err
}
if !authResult.Authorized {
return nil, domain.NewUnauthorizedError(authResult.Reason)
}
// ===== Domain Logic =====
workflow, err := aggregates.NewWorkflow(
valueobjects.NewBoardID(input.BoardID),
valueobjects.NewWorkflowName(input.Name),
valueobjects.NewUserID(input.OperatorID),
)
if err != nil {
return nil, err
}
// ===== Persist =====
if err := uc.workflowRepo.Save(ctx, workflow); err != nil {
return nil, err
}
// ===== Publish Domain Event =====
if err := uc.eventPub.Publish(ctx, events.NewWorkflowCreatedEvent(workflow)); err != nil {
return nil, err
}
return &CreateWorkflowOutput{
WorkflowID: workflow.ID().String(),
Status: string(workflow.Status()),
CreatedAt: workflow.CreatedAt(),
}, nil
}
// src/domain/aggregates/workflow.go
// Generated from: docs/specs/create-workflow/controlled-domain/aggregate.yaml
package aggregates
import (
"time"
"myapp/domain/entities"
"myapp/domain/valueobjects"
)
type Workflow struct {
id valueobjects.WorkflowID
boardID valueobjects.BoardID
name valueobjects.WorkflowName
stages []*entities.Stage
createdBy valueobjects.UserID
createdAt time.Time
}
func NewWorkflow(
boardID valueobjects.BoardID,
name valueobjects.WorkflowName,
createdBy valueobjects.UserID,
) (*Workflow, error) {
w := &Workflow{
id: valueobjects.GenerateWorkflowID(),
boardID: boardID,
name: name,
stages: make([]*entities.Stage, 0),
createdBy: createdBy,
createdAt: time.Now(),
}
// ===== INV1: Validate invariants in constructor =====
if err := w.validateInvariants(); err != nil {
return nil, err
}
return w, nil
}
func (w *Workflow) AddStage(stage *entities.Stage) error {
// ===== FC1: Structure Integrity =====
w.stages = append(w.stages, stage)
// Re-validate after mutation
return w.validateInvariants()
}
// ===== Invariants (from aggregate.yaml#invariants.shared) =====
func (w *Workflow) validateInvariants() error {
// INV1: Structure Integrity - Lane hierarchy
for _, stage := range w.stages {
for _, lane := range stage.SwimLanes() {
if lane.ParentStage() == nil {
return domain.NewInvariantViolationError(
"SwimLane must be under a Stage",
)
}
}
}
return nil
}
// ===== Getters =====
func (w *Workflow) ID() valueobjects.WorkflowID { return w.id }
func (w *Workflow) BoardID() valueobjects.BoardID { return w.boardID }
func (w *Workflow) Name() valueobjects.WorkflowName { return w.name }
func (w *Workflow) Stages() []*entities.Stage { return w.stages }
func (w *Workflow) CreatedBy() valueobjects.UserID { return w.createdBy }
func (w *Workflow) CreatedAt() time.Time { return w.createdAt }
func (w *Workflow) Status() WorkflowStatus {
if len(w.stages) > 0 {
return WorkflowStatusActive
}
return WorkflowStatusEmpty
}
生成代碼時,確保每個 Frame Concern 都有對應的實作:
| Frame Concern | 規格位置 | 實作位置 |
|---|---|---|
| FC1: Structure Integrity | aggregate.yaml#invariants.shared | Aggregate.validateInvariants() |
| FC2: Concurrency | use-case.yaml#transaction_boundary | Use Case transaction handling |
| XC1: Authorization | cross-context/authorization.yaml | Use Case authorization check |
This skill should be used when the user asks to "create an agent", "add an agent", "write a subagent", "agent frontmatter", "when to use description", "agent examples", "agent tools", "agent colors", "autonomous agent", or needs guidance on agent structure, system prompts, triggering conditions, or agent development best practices for Claude Code plugins.
This skill should be used when the user asks to "create a slash command", "add a command", "write a custom command", "define command arguments", "use command frontmatter", "organize commands", "create command with file references", "interactive command", "use AskUserQuestion in command", or needs guidance on slash command structure, YAML frontmatter fields, dynamic arguments, bash execution in commands, user interaction patterns, or command development best practices for Claude Code.
This skill should be used when the user asks to "create a hook", "add a PreToolUse/PostToolUse/Stop hook", "validate tool use", "implement prompt-based hooks", "use ${CLAUDE_PLUGIN_ROOT}", "set up event-driven automation", "block dangerous commands", or mentions hook events (PreToolUse, PostToolUse, Stop, SubagentStop, SessionStart, SessionEnd, UserPromptSubmit, PreCompact, Notification). Provides comprehensive guidance for creating and implementing Claude Code plugin hooks with focus on advanced prompt-based hooks API.