Standards for authoring SDD plugin agents — frontmatter, self-containment, skill references, and no-user-interaction rules.
日本語の概要は準備中です。原文の説明を表示しています。
CMDO architecture standards for Node.js/TypeScript backends with strict layer separation.
インストールする前に、エージェントに与えられる指示の中身を確認できます。
CMDO ("Commando") architecture for Node.js/TypeScript backends with strict separation between infrastructure and domain concerns.
Operator → Controller → Model Use Cases
↓ ↓ ↑
Config → [All layers] → Dependencies (injected by Controller)
↓
DAL
| Layer | Knows About | Example |
|---|---|---|
| Operator | Infrastructure only | "Here's a DB connection and generic HTTP client" |
| Config | URLs and settings | paymentGatewayUrl: "https://api.stripe.com" |
| Controller | Domain concerns | "Use httpClient + paymentGatewayUrl to charge a card" |
| Model | Business logic only | "Calculate total, then call chargePayment()" |
Key Principle: Operator provides raw I/O capabilities (generic HTTP client, cache client, DB connection) with NO domain knowledge. Controller combines I/O + config to create domain-specific operations.
| Layer | Path | Responsibility |
|---|---|---|
| Entry Point | src/index.ts | Bootstrap only (ONLY file with side effects) |
| Operator | src/operator/ | Raw I/O capabilities (DB, HTTP clients, cache), lifecycle management, state machine |
| Config | src/config/ | Environment parsing, validation, type-safe config |
| Controller | src/controller/ | Request/response handling, combines I/O + config for domain calls, creates Dependencies for Model |
| Model | src/model/ | Business logic (definitions + use-cases), receives Dependencies |
| DAL | src/dal/ | Data access, queries, mapping DB ↔ domain objects |
Rules:
src/index.ts is the ONLY file that runs code on import (exception to the "index.ts exports only" rule for application entry points)// src/index.ts - Entry point (exception to index.ts rule for entry points)
import { createOperator } from "./operator";
import { loadConfig } from "./config";
const main = async (): Promise<void> => {
const config = loadConfig();
const operator = createOperator({ config });
await operator.start();
};
main().catch((error) => {
console.error("Failed to start operator:", error);
process.exit(1);
});
Provides raw I/O capabilities and orchestrates application lifecycle. NO domain knowledge - only infrastructure.
What Operator Does:
What Operator Does NOT Do:
Lifecycle State Machine:
IDLE → STARTING:PROBES → STARTING:DATABASE → STARTING:HTTP_SERVER → RUNNING
↓
STOPPED ← STOPPING:PROBES ← STOPPING:DATABASE ← STOPPING:HTTP_SERVER ←─┘
Unix Signal Handling:
| Signal | Source | Action |
|---|---|---|
SIGTERM | Kubernetes pod termination, kill command | Graceful shutdown |
SIGINT | Ctrl+C from terminal | Graceful shutdown |
SIGHUP | Terminal hangup | Graceful shutdown |
When a signal is received:
stop()Structure:
src/operator/
├── create_operator.ts # Main factory, state machine, lifecycle
├── create_database.ts # Database connection pool
├── create_http_server.ts # HTTP server wrapper
├── lifecycle_probes.ts # Health/readiness endpoints (separate port)
├── state_machine.ts # Generic state machine implementation
├── logger.ts # Pino logger with OpenTelemetry
├── metrics.ts # OpenTelemetry metrics
└── index.ts # Exports only
Environment parsing, validation, type-safe config objects.
CRITICAL: Use dotenv for ALL environment variable access. Direct process.env access is FORBIDDEN outside the Config layer.
Environment Variable Rules:
dotenv.config() inside loadConfig() (not at module level)process.env access is ONLY allowed in src/config/process.env in Operator, Controller, Model, or DAL layersWhat it does NOT contain: Business logic, database queries.
Structure:
src/config/
├── index.ts # loadConfig() function, Config type
└── validation.ts # Optional: validation helpers
Request/response handling, combines I/O + config for domain-specific operations, creates Dependencies object for Model.
Controller's Key Role:
httpClient + config.paymentUrl → chargePayment)HTTP Handlers: Each file in http_handlers/ corresponds to an API namespace (e.g., /users, /orders) and exports a router. The create_controller.ts imports these routers and wires them together with the Dependencies object for Model use-cases.
Handler Naming: Use operationId from OpenAPI spec with handle prefix (e.g., createUser → handleCreateUser).
Health Check Endpoints: Lifecycle probes (/health, /readiness) are handled by Operator on a separate port, NOT in the Controller.
What it does NOT contain: Database queries, business logic (delegates to Model).
Structure:
src/controller/
├── http_handlers/ # One file per API namespace
│ ├── users.ts # Exports usersRouter
│ ├── orders.ts # Exports ordersRouter
│ └── index.ts # Re-exports all routers
├── create_controller.ts # Assembles routers, creates Dependencies for Model
└── index.ts # Exports only
Business logic via definitions + use-cases. Model never imports from outside its module.
Definitions Rules:
type or interface)Use Case Rules:
// ✅ GOOD: Discriminated union results
type CreateUserResult =
| { readonly success: true; readonly user: User }
| { readonly success: false; readonly error: 'email_exists' | 'invalid_email' };
const createUser = async (
deps: Dependencies,
args: CreateUserArgs
): Promise<CreateUserResult> => {
// Business logic using only injected dependencies
};
What it does NOT contain: HTTP handling, direct database queries, external imports.
Structure:
src/model/
├── definitions/ # TypeScript types ONLY (no Zod/validation)
│ ├── user.ts
│ └── index.ts
├── use-cases/ # One function per file
│ ├── create_user.ts
│ ├── update_user.ts
│ └── index.ts
├── dependencies.ts # Dependencies interface
└── index.ts # Exports only
Data access functions that directly handle database queries. No repository pattern - use simple, focused functions instead.
DAL Rules:
find_user_by_id.ts)index.ts re-exports all functionsWhat it does NOT contain: Business logic, HTTP handling, repository abstractions.
Structure:
src/dal/
├── find_user_by_id.ts
├── insert_user.ts
├── update_user.ts
└── index.ts # Re-exports all functions
All observability follows OpenTelemetry standards. Telemetry is initialized by the Operator layer.
Use Pino with OpenTelemetry context injection.
Log Levels:
| Level | When to use |
|---|---|
debug | Detailed debugging (disabled in production) |
info | Normal operations, state changes, requests |
warn | Recoverable issues, deprecations |
error | Failures requiring attention |
When to Use Info Logging:
Log before and after every domain action or permanent state change:
Required Log Fields:
| Field | Required | Description |
|---|---|---|
level | Yes | Log level (debug, info, warn, error) |
time | Yes | ISO 8601 timestamp |
component | Yes | Source component (operator, controller, dal) |
msg | Yes | Human-readable message |
traceId | Auto | OpenTelemetry trace ID |
spanId | Auto | OpenTelemetry span ID |
userId | Context | User ID if authenticated |
requestId | Context | Request correlation ID |
error | On error | Error object with stack |
Logging by Layer: Loggers are created by the Operator layer and passed down. Each layer creates a child logger with its component name.
Security Rule: Never log sensitive data (passwords, tokens, credit cards, PII beyond IDs).
Required Metrics:
| Metric | Type | Labels | Layer |
|---|---|---|---|
http.server.request.duration | Histogram | method, route, status | Operator |
http.server.request.count | Counter | method, route, status | Operator |
db.client.operation.duration | Histogram | operation, table | DAL |
db.client.connection.pool.usage | UpDownCounter | state (active/idle) | DAL |
business.operation.count | Counter | operation, result | Model |
Metric Naming: Follow OpenTelemetry semantic conventions:
. as separator (e.g., http.server.request.duration)http, db, business)duration, count, size)Wrap business operations with spans using @opentelemetry/api.
Span Attributes: Use OpenTelemetry semantic conventions:
| Attribute | Type | Example |
|---|---|---|
http.method | string | GET, POST |
http.route | string | /api/users/:id |
http.status_code | int | 200, 404 |
db.system | string | postgresql |
db.operation | string | SELECT, INSERT |
db.statement | string | SQL query (sanitized) |
user.id | string | User identifier |
withTraceContext() for all logsCRITICAL: Use lowercase_with_underscores for ALL filenames
✅ create_user.ts, find_user_by_id.ts, user_repository.ts
❌ createUser.ts (camelCase), CreateUser.ts (PascalCase), create-user.ts (kebab-case)
Inside a module, nothing should ever import from its own index.ts. All imports within a module must use relative paths. The barrel is the module's public API for external consumers only. For nested modules, the same barrel rules apply.
// Given this structure:
// controllers/
// ├── index.ts ← barrel: re-exports all routers
// ├── users/
// │ ├── index.ts
// │ └── users_router.ts
// └── health/
// ├── index.ts
// └── health_router.ts
// In controllers/users/users_router.ts:
// GOOD: relative path to sibling sub-module barrel
import { healthCheck } from '../health';
// BAD: importing from own module's barrel
import { healthCheck } from '@/controllers';
import { healthCheck } from '@/controllers/health';
When implementing a new feature, follow this order to minimize rework and ensure clean architecture:
Start in components/contract/:
openapi.yaml - request/response schemascontract generate-types)This ensures types flow from contract → server → frontend.
Start in src/model/:
definitions/use-cases/Model is pure business logic with no external dependencies.
In src/dal/:
In src/controller/:
http_handlers/ fileIn src/operator/:
Only modify if the feature requires:
| Step | Location | What to Do |
|---|---|---|
| 1 | contract/openapi.yaml | Define endpoint, request/response schemas |
| 2 | model/definitions/ | Add feature-specific types |
| 3 | model/use-cases/ | Implement business logic |
| 4 | model/dependencies.ts | Extend Dependencies if needed |
| 5 | dal/ | Add database functions |
| 6 | controller/http_handlers/ | Add HTTP handler |
| 7 | controller/create_controller.ts | Wire Dependencies |
| 8 | Tests | Test each layer independently |
Server components require configuration from components/config/. The backend-scaffolding skill generates the initial server structure including a minimal config schema with port, host, and logLevel fields.
Before committing backend code, verify:
src/index.ts) only imports and starts operatorprocess.envlowercase_with_underscoresThis skill defines no input parameters or structured output.
まだレビューはありません。使ってみた感想をお寄せください。
概要と使いどころ
Standards for authoring SDD plugin agents — frontmatter, self-containment, skill references, and no-user-interaction rules.
日本語の概要は準備中です。原文の説明を表示しています。
Scaffolds Node.js/TypeScript backend components with CMDO architecture, driven by component settings.
日本語の概要は準備中です。原文の説明を表示しています。
Intent-to-command mappings for fullstack-typescript tech pack features.
日本語の概要は準備中です。原文の説明を表示しています。
Create change specification and implementation plan with dynamic phase generation. Supports feature, bugfix, refactor, and epic types.
日本語の概要は準備中です。原文の説明を表示しています。
Orchestrates the full change lifecycle — routes actions to phase-specific sub-files.
日本語の概要は準備中です。原文の説明を表示しています。
Scaffolds CI/CD pipeline definitions using GitHub Actions for build, test, lint, and deploy workflows.
日本語の概要は準備中です。原文の説明を表示しています。