mcp-ai-agent-guidelines
Version:
A comprehensive Model Context Protocol server providing advanced tools, resources, and prompts for implementing AI agent best practices
301 lines (293 loc) • 9.66 kB
JavaScript
/**
* Workflow Capability Handler - GitHub Actions YAML Generation
*
* Generates GitHub Actions workflow files for CI/CD automation.
* Supports multiple workflow types: CI, deployment, testing, release.
*
* @module strategies/cross-cutting/workflow-handler
* @see {@link https://github.com/Anselmoo/mcp-ai-agent-guidelines/blob/development/plan-v0.13.x/specs/SPEC-001-output-strategy-layer.md SPEC-001} §5.2
*/
import { CrossCuttingCapability } from "../output-strategy.js";
/**
* WorkflowCapabilityHandler generates GitHub Actions workflow YAML files.
*
* Detects workflow type from domain results and generates appropriate
* workflow configurations for CI/CD automation.
*
* Workflow type detection follows a well-defined priority so callers can
* intentionally influence which workflow is generated:
*
* 1. {@link metadata}.workflowType – If present and one of `"ci"`, `"deploy"`,
* `"test"`, or `"release"`, it is used directly.
* 2. Deployment fields – If the domain result contains deployment-related
* hints (for example keys like `deploy`, `deployment`, or `environment`),
* `"deploy"` is selected.
* 3. Release fields – If the domain result contains release-related hints
* (for example `release`, `version`, or `tag`), `"release"` is selected.
* 4. Test fields – If the domain result is dominated by testing concerns
* (for example `tests`, `coverage`, or `testing`), `"test"` is selected.
* 5. Fallback – If none of the above apply, `"ci"` is used as a safe
* default workflow type.
*
* This means that you can explicitly steer workflow generation by setting
* `metadata.workflowType`:
*
* @example
* ```ts
* const handler = new WorkflowCapabilityHandler();
* const artifact = handler.generate({
* domainResult,
* metadata: {
* workflowType: "deploy", // forces deployment workflow
* nodeVersion: "22",
* },
* });
* // artifact.name => ".github/workflows/deploy.yml"
* ```
*
* If `metadata.workflowType` is provided but not one of the supported
* workflow types, it is ignored and the handler falls back to inspecting
* the domain result and finally to the `"ci"` default.
*
* @implements {CapabilityHandler}
*/
export class WorkflowCapabilityHandler {
/** The capability this handler implements */
capability = CrossCuttingCapability.WORKFLOW;
/**
* Generate a GitHub Actions workflow artifact.
*
* @param context - The context for artifact generation
* @returns A workflow artifact with YAML content, or null if not applicable
*/
generate(context) {
const { domainResult, metadata } = context;
// Determine workflow type based on context
const workflowType = this.detectWorkflowType(domainResult, metadata);
const content = this.generateWorkflowYaml(workflowType, metadata);
return {
type: this.capability,
name: `.github/workflows/${workflowType}.yml`,
content,
};
}
/**
* Check if this handler supports the given domain type.
*
* @param domainType - The domain type identifier
* @returns True if workflows can be generated for this domain type
*/
supports(domainType) {
return ["SessionState", "PromptResult", "ScoringResult"].includes(domainType);
}
/**
* Detect the workflow type from domain result and metadata.
*
* @param result - The domain result
* @param metadata - Optional metadata hints
* @returns The detected workflow type
* @private
*/
detectWorkflowType(result, metadata) {
// Check metadata hints
if (metadata?.workflowType) {
const type = String(metadata.workflowType);
if (this.isValidWorkflowType(type)) {
return type;
}
}
// Check result structure for hints
if (typeof result === "object" && result !== null) {
const obj = result;
// Look for deployment indicators
if (obj.deployment || obj.deploy || obj.environment) {
return "deploy";
}
// Look for release indicators
if (obj.release || obj.version || obj.tag) {
return "release";
}
// Look for test indicators
if (obj.coverage || obj.tests || obj.testing) {
return "test";
}
}
// Default to CI workflow
return "ci";
}
/**
* Type guard for workflow type validation.
*
* @param type - The type to check
* @returns True if the type is a valid workflow type
* @private
*/
isValidWorkflowType(type) {
return ["ci", "deploy", "test", "release"].includes(type);
}
/**
* Generate workflow YAML content based on type.
*
* @param type - The workflow type
* @param metadata - Optional metadata for customization
* @returns The workflow YAML content
* @private
*/
generateWorkflowYaml(type, metadata) {
const templates = {
ci: () => this.generateCIWorkflow(metadata),
deploy: () => this.generateDeployWorkflow(metadata),
test: () => this.generateTestWorkflow(metadata),
release: () => this.generateReleaseWorkflow(metadata),
};
return templates[type]();
}
/**
* Generate CI workflow template.
*
* @param metadata - Optional metadata for customization
* @returns CI workflow YAML
* @private
*/
generateCIWorkflow(metadata) {
const nodeVersion = metadata?.nodeVersion ?? "22";
const branchesRaw = metadata?.branches ?? ["main"];
const branches = Array.isArray(branchesRaw) ? branchesRaw : ["main"];
return `name: CI
on:
push:
branches: [${branches.map((b) => String(b)).join(", ")}]
pull_request:
branches: [${branches.map((b) => String(b)).join(", ")}]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: actions/setup-node@v6
with:
node-version: ${nodeVersion}
cache: npm
- run: npm ci
- run: npm run quality
- run: npm run test:all
`;
}
/**
* Generate deployment workflow template.
*
* @param metadata - Optional metadata for customization
* @returns Deployment workflow YAML
* @private
*/
generateDeployWorkflow(metadata) {
const environment = metadata?.environment ?? "production";
const nodeVersion = metadata?.nodeVersion ?? "22";
const branchesRaw = metadata?.branches ?? ["main"];
const branches = Array.isArray(branchesRaw) ? branchesRaw : ["main"];
return `name: Deploy
on:
push:
branches: [${branches.map((b) => String(b)).join(", ")}]
workflow_dispatch:
jobs:
deploy:
runs-on: ubuntu-latest
environment: ${environment}
steps:
- uses: actions/checkout@v6
- uses: actions/setup-node@v6
with:
node-version: ${nodeVersion}
cache: npm
- run: npm ci
- run: npm run build
- name: Deploy
run: echo "Add your deployment command here (npm run deploy or custom script)"
env:
DEPLOY_TOKEN: \${{ secrets.DEPLOY_TOKEN }}
`;
}
/**
* Generate test workflow template.
*
* @param metadata - Optional metadata for customization
* @returns Test workflow YAML
* @private
*/
generateTestWorkflow(metadata) {
const nodeVersion = metadata?.nodeVersion ?? "22";
const coverageThreshold = metadata?.coverageThreshold ?? "90";
const branchesRaw = metadata?.branches ?? ["main", "develop"];
const branches = Array.isArray(branchesRaw)
? branchesRaw
: ["main", "develop"];
return `name: Test
on:
push:
branches: [${branches.map((b) => String(b)).join(", ")}]
pull_request:
branches: [${branches.map((b) => String(b)).join(", ")}]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: actions/setup-node@v6
with:
node-version: ${nodeVersion}
cache: npm
- run: npm ci
- run: npm run test:vitest
- run: npm run check:coverage-threshold --threshold=${coverageThreshold}
- name: Upload coverage
uses: codecov/codecov-action@v4
with:
files: ./coverage/lcov.info
`;
}
/**
* Generate release workflow template.
*
* @param metadata - Optional metadata for customization
* @returns Release workflow YAML
* @private
*/
generateReleaseWorkflow(metadata) {
const nodeVersion = metadata?.nodeVersion ?? "22";
return `name: Release
on:
push:
tags:
- 'v*'
jobs:
release:
runs-on: ubuntu-latest
permissions:
contents: write
packages: write
steps:
- uses: actions/checkout@v6
- uses: actions/setup-node@v6
with:
node-version: ${nodeVersion}
cache: npm
registry-url: 'https://registry.npmjs.org'
- run: npm ci
- run: npm run build
- run: npm run test:all
- name: Publish to npm
run: npm publish
env:
NODE_AUTH_TOKEN: \${{ secrets.NPM_TOKEN }}
- name: Create GitHub Release
uses: softprops/action-gh-release@v2
env:
GITHUB_TOKEN: \${{ secrets.GITHUB_TOKEN }}
with:
tag_name: \${{ github.ref }}
name: Release \${{ github.ref }}
`;
}
}
//# sourceMappingURL=workflow-handler.js.map