23f7624596
ADR-166 MCP Bridge Security Lock / Static-source security lock (push) Failing after 0s
ADR-166 MCP Bridge Security Lock / Compose default binds loopback + Mongo has auth (push) Failing after 2s
CodeQL Advanced / Analyze (rust) (push) Failing after 0s
ADR-166 MCP Bridge Security Lock / plugin-agent-federation bindHost default (push) Failing after 1s
ADR-166 MCP Bridge Security Lock / Runtime behavior — 401 + terminal gate + fail-closed (push) Failing after 4s
business-pods-smoke / smoke (push) Failing after 1s
all-plugins-smoke / smoke-all (push) Failing after 2s
CI/CD Pipeline / Security & Code Quality (push) Failing after 1s
CI/CD Pipeline / Test Suite (ubuntu-latest) (push) Failing after 1s
CI/CD Pipeline / Build & Package (macos-latest) (push) Has been skipped
CI/CD Pipeline / Build & Package (ubuntu-latest) (push) Has been skipped
CI/CD Pipeline / Build & Package (windows-latest) (push) Has been skipped
CI/CD Pipeline / Documentation & Examples (push) Failing after 1s
Clone Tracker (14-day rolling) / Snapshot clones for ruflo ecosystem (push) Failing after 1s
CodeQL Advanced / Analyze (actions) (push) Failing after 1s
CodeQL Advanced / Analyze (javascript-typescript) (push) Failing after 1s
federation-peer-rust / stable-noop (push) Failing after 1s
metaharness-ci / score (push) Failing after 1s
metaharness-ci / router-compat (push) Failing after 0s
metaharness-ci / similarity-tests (push) Failing after 0s
no-agentbbs-smoke / smoke-without-agentbbs (push) Failing after 1s
V3 CI/CD Pipeline / Build V3 (windows-latest) (push) Has been skipped
codex-integration-audit / Codex integration audit (push) Failing after 1s
helpers-manifest-guard / guard (push) Failing after 1s
🔗 Cross-Agent Integration Tests / 🤝 Agent Coordination Tests (push) Has been skipped
🔗 Cross-Agent Integration Tests / 🧠 Memory Sharing Integration (push) Has been skipped
🔗 Cross-Agent Integration Tests / 🛡️ Fault Tolerance Tests (push) Has been skipped
🔗 Cross-Agent Integration Tests / ⚡ Performance Integration Tests (push) Has been skipped
metaharness-ci / mcp-scan (push) Failing after 1s
metaharness-ci / eject-dryrun (push) Failing after 1s
metaharness-ci / metaharness-real-data (push) Failing after 0s
no-cli-optdep-bloat-2561 / guard (push) Failing after 1s
no-metaharness-smoke / smoke-without-metaharness (push) Failing after 1s
no-phantom-agentic-flow-subpath / guard (push) Failing after 1s
🔄 Automated Rollback Manager / 🚨 Failure Detection (push) Failing after 1s
V3 CI/CD Pipeline / Plugin hooks smoke / ubuntu-latest / Node 22 (push) Failing after 1s
V3 CI/CD Pipeline / ruflo-graph-intelligence build + test smoke (#2044, ADR-123) (push) Failing after 1s
CVE Audit Gate / Audit root (critical-blocking) (push) Failing after 2s
cost-tracker-smoke / smoke (push) Failing after 3s
oia-audit-weekly / audit (push) Failing after 2s
ruflo-agent-smoke / ruflo-agent structural smoke (push) Failing after 1s
📊 Status Badges Update / 📊 Update Status Badges (push) Failing after 1s
V3 CI/CD Pipeline / Static regression guards (#2267 YAML + (push) Failing after 1s
V3 CI/CD Pipeline / Test V3 Packages (push) Failing after 0s
V3 CI/CD Pipeline / agent_execute provider routing smoke (#2042) (push) Failing after 0s
CVE Audit Gate / Audit v3 (critical-blocking) (push) Failing after 1s
federation-peer-rust / stable-native (push) Failing after 2s
🔗 Cross-Agent Integration Tests / 🚀 Integration Test Setup (push) Failing after 2s
neural-trader-smoke / runtime-smoke (push) Failing after 1s
V3 CI/CD Pipeline / Build V3 (macos-latest) (push) Has been skipped
V3 CI/CD Pipeline / Build V3 (ubuntu-latest) (push) Has been skipped
V3 CI/CD Pipeline / Type Check V3 (push) Failing after 1s
V3 CI/CD Pipeline / Smoke (no better-sqlite3) / ubuntu-latest / Node 24 (push) Failing after 1s
V3 CI/CD Pipeline / Smoke (no better-sqlite3) / ubuntu-latest / Node 22 (push) Failing after 2s
V3 CI/CD Pipeline / browser rvf create flag smoke (#2015) (push) Failing after 0s
V3 CI/CD Pipeline / Dependency review (#2046) (push) Has been skipped
V3 CI/CD Pipeline / Supply-chain audit (#2046) (push) Failing after 0s
V3 CI/CD Pipeline / witness marker drift smoke (#2021) (push) Failing after 1s
V3 CI/CD Pipeline / neural-trader portfolio CG smoke (#2068, ADR-126 Phase 3) (push) Failing after 1s
V3 CI/CD Pipeline / neural-trader backtest signing smoke (#2068, ADR-126 Phase 4) (push) Failing after 1s
V3 CI/CD Pipeline / kg-extract type-import classification smoke (#2049) (push) Failing after 0s
V3 CI/CD Pipeline / witness verify precondition smoke (#1880) (push) Failing after 2s
V3 CI/CD Pipeline / neural-trader pipeline risk-gate smoke (#2068, ADR-126 Phase 5) (push) Failing after 0s
V3 CI/CD Pipeline / neural-trader feature attribution smoke (#2068, ADR-126 Phase 6) (push) Failing after 0s
V3 CI/CD Pipeline / plugin-registry signature verification smoke (#1922, CWE-347) (push) Failing after 4s
V3 CI/CD Pipeline / memory stats legacy-DB smoke (#2120) (push) Failing after 4s
V3 CI/CD Pipeline / github deprecated actions smoke (#2089, ADR-127 Phase 3) (push) Failing after 1s
V3 CI/CD Pipeline / graph query + pathfinder smoke (ADR-130 P2+P5) (push) Has been skipped
V3 CI/CD Pipeline / graph trajectory hooks smoke (ADR-130 P3) (push) Has been skipped
V3 CI/CD Pipeline / graph plugin adapter smoke (ADR-130 P4) (push) Has been skipped
V3 CI/CD Pipeline / graph benchmark (ADR-130 P6) (push) Has been skipped
V3 CI/CD Pipeline / statusline generator delegation smoke (#2195) (push) Failing after 1s
V3 CI/CD Pipeline / wizard init regression guard (#2206 (push) Failing after 1s
V3 CI/CD Pipeline / memory no-stray-db smoke (ADR-125 P7) (push) Failing after 1s
V3 CI/CD Pipeline / github-safe injection smoke (#2089, ADR-127 Phase 1) (push) Failing after 1s
V3 CI/CD Pipeline / github actions pin smoke (#2089, ADR-127 Phase 1) (push) Failing after 1s
V3 CI/CD Pipeline / github attribution opt-in smoke (#2089, ADR-127 Phase 4) (push) Failing after 1s
V3 CI/CD Pipeline / pre-bash hook safety smoke (#2017) (push) Failing after 1s
V3 CI/CD Pipeline / Memory import smoke / ubuntu-latest (push) Failing after 0s
V3 CI/CD Pipeline / MCP protocol smoke / ubuntu-latest (push) Failing after 2s
V3 CI/CD Pipeline / ruvllm WASM auto-init smoke (#2086) (push) Failing after 4s
V3 CI/CD Pipeline / MCP paired-tool round-trip smoke (#1889) (push) Failing after 1s
V3 CI/CD Pipeline / Plugin package install-safety (#1902/#1903/#1904) (push) Failing after 1s
V3 CI/CD Pipeline / Tool description discoverability (ADR-112) (push) Failing after 3s
V3 CI/CD Pipeline / CLI npx-install smoke (#1147 / (22) (push) Failing after 1s
V3 CI/CD Pipeline / CLI npx-install smoke (#1147 / (24) (push) Failing after 1s
V3 CI/CD Pipeline / Windows hook shim smoke (#2132) / ubuntu-latest (push) Failing after 2s
V3 CI/CD Pipeline / Windows hook execution smoke (#2132) / ubuntu-latest (push) Failing after 1s
V3 CI/CD Pipeline / Windows init hooks smoke (#2132) / ubuntu-latest (push) Failing after 1s
V3 CI/CD Pipeline / Vector-index dimension audit (#1947) (push) Failing after 0s
V3 CI/CD Pipeline / Hook-command install safety (#1921) (push) Failing after 1s
V3 CI/CD Pipeline / ToolOutputGuardrail smoke (ADR-131, (push) Failing after 1s
V3 CI/CD Pipeline / init-bundle invariants smoke (#2095, ADR-128 Phase 5) (push) Failing after 1s
V3 CI/CD Pipeline / wasm provider bridge smoke (ADR-129 P1) (push) Failing after 2s
V3 CI/CD Pipeline / wasm gallery CRUD smoke (ADR-129 P3) (push) Failing after 1s
V3 CI/CD Pipeline / wasm plugin bridge smoke (ADR-129 P4) (push) Failing after 0s
V3 CI/CD Pipeline / wasm compose smoke (ADR-129 P2) (push) Failing after 4s
V3 CI/CD Pipeline / graph schema smoke (ADR-130 P1) (push) Failing after 0s
Validate Marketplace / validate (push) Failing after 1s
🔍 Verification Pipeline / 🚀 Setup Verification (push) Failing after 1s
🔍 Verification Pipeline / 🛡️ Security Verification (push) Has been skipped
🔍 Verification Pipeline / 📝 Code Quality (push) Has been skipped
🔍 Verification Pipeline / 🧪 Test Verification (${{ matrix.os }}, Node ${{ matrix.node }}) (push) Has been skipped
🔍 Verification Pipeline / 🏗️ Build Verification (push) Has been skipped
🔍 Verification Pipeline / 📚 Documentation Verification (push) Has been skipped
CVE Audit Gate / High-severity report (warn only) (push) Has been cancelled
🔄 Automated Rollback Manager / 🔄 Execute Rollback (push) Has been cancelled
🔄 Automated Rollback Manager / ✅ Post-Rollback Verification (push) Has been cancelled
🔄 Automated Rollback Manager / 📊 Rollback Monitoring (push) Has been cancelled
V3 CI/CD Pipeline / Windows init hooks smoke (#2132) / windows-latest (push) Has been cancelled
V3 CI/CD Pipeline / Windows hook execution smoke (#2132) / macos-latest (push) Has been cancelled
V3 CI/CD Pipeline / Windows hook execution smoke (#2132) / windows-latest (push) Has been cancelled
🔄 Automated Rollback Manager / ⏳ Manual Rollback Approval (push) Has been cancelled
V3 CI/CD Pipeline / MCP protocol smoke / macos-latest (push) Has been cancelled
V3 CI/CD Pipeline / Memory import smoke / macos-latest (push) Has been cancelled
V3 CI/CD Pipeline / Windows hook shim smoke (#2132) / macos-latest (push) Has been cancelled
V3 CI/CD Pipeline / Windows hook shim smoke (#2132) / windows-latest (push) Has been cancelled
V3 CI/CD Pipeline / Windows init hooks smoke (#2132) / macos-latest (push) Has been cancelled
V3 CI/CD Pipeline / Witness verify (signed manifest) / macos-latest (push) Has been cancelled
V3 CI/CD Pipeline / Witness verify (signed manifest) / ubuntu-latest (push) Has been cancelled
V3 CI/CD Pipeline / Witness verify (signed manifest) / windows-latest (push) Has been cancelled
V3 CI/CD Pipeline / Publish to npm (alpha) (push) Has been cancelled
V3 CI/CD Pipeline / Smoke (no better-sqlite3) / macos-latest / Node 22 (push) Has been cancelled
V3 CI/CD Pipeline / Plugin hooks smoke / macos-latest / Node 22 (push) Has been cancelled
CI/CD Pipeline / Deploy & Release (push) Has been cancelled
CI/CD Pipeline / CI Status (push) Has been cancelled
🔗 Cross-Agent Integration Tests / 📊 Integration Test Report (push) Has been cancelled
🔄 Automated Rollback Manager / 🔍 Pre-Rollback Validation (push) Has been cancelled
🔍 Verification Pipeline / ⚡ Performance Verification (push) Has been cancelled
🔍 Verification Pipeline / 📊 Verification Report (push) Has been cancelled
734 lines
24 KiB
TypeScript
734 lines
24 KiB
TypeScript
import { describe, it, expect } from 'vitest';
|
|
import {
|
|
generateClaudeMd,
|
|
generateClaudeLocalMd,
|
|
generateSkillMd,
|
|
generateAgentMd,
|
|
generateAgentIndex,
|
|
scaffold,
|
|
} from '../src/generators.js';
|
|
import type {
|
|
ProjectProfile,
|
|
LocalProfile,
|
|
SkillDefinition,
|
|
AgentDefinition,
|
|
} from '../src/generators.js';
|
|
|
|
// ============================================================================
|
|
// CLAUDE.md Generator
|
|
// ============================================================================
|
|
|
|
describe('generateClaudeMd', () => {
|
|
const minimal: ProjectProfile = {
|
|
name: 'my-app',
|
|
languages: ['typescript'],
|
|
};
|
|
|
|
it('generates valid markdown with project name as heading', () => {
|
|
const md = generateClaudeMd(minimal);
|
|
expect(md).toMatch(/^# my-app\n/);
|
|
});
|
|
|
|
it('includes language-specific invariants', () => {
|
|
const md = generateClaudeMd(minimal);
|
|
expect(md).toContain('No `any` types');
|
|
expect(md).toContain('Prefer `const` over `let`');
|
|
});
|
|
|
|
it('includes build and test section', () => {
|
|
const md = generateClaudeMd(minimal);
|
|
expect(md).toContain('## Build & Test');
|
|
expect(md).toContain('npm run build');
|
|
expect(md).toContain('npm test');
|
|
});
|
|
|
|
it('uses custom package manager', () => {
|
|
const md = generateClaudeMd({ ...minimal, packageManager: 'pnpm' });
|
|
expect(md).toContain('pnpm run build');
|
|
expect(md).toContain('pnpm test');
|
|
});
|
|
|
|
it('uses custom build and test commands', () => {
|
|
const md = generateClaudeMd({
|
|
...minimal,
|
|
buildCommand: 'make build',
|
|
testCommand: 'make test',
|
|
});
|
|
expect(md).toContain('`make build`');
|
|
expect(md).toContain('`make test`');
|
|
});
|
|
|
|
it('includes lint command when provided', () => {
|
|
const md = generateClaudeMd({ ...minimal, lintCommand: 'npm run lint' });
|
|
expect(md).toContain('`npm run lint`');
|
|
});
|
|
|
|
it('includes description when provided', () => {
|
|
const md = generateClaudeMd({
|
|
...minimal,
|
|
description: 'A web application for managing tasks',
|
|
});
|
|
expect(md).toContain('A web application for managing tasks');
|
|
});
|
|
|
|
it('includes monorepo note', () => {
|
|
const md = generateClaudeMd({ ...minimal, monorepo: true });
|
|
expect(md).toContain('monorepo');
|
|
});
|
|
|
|
it('includes source and test directories', () => {
|
|
const md = generateClaudeMd({
|
|
...minimal,
|
|
srcDir: 'src',
|
|
testDir: 'tests',
|
|
});
|
|
expect(md).toContain('`src/`');
|
|
expect(md).toContain('`tests/`');
|
|
});
|
|
|
|
it('includes domain rules', () => {
|
|
const md = generateClaudeMd({
|
|
...minimal,
|
|
domainRules: [
|
|
'Never write to the users table without a migration',
|
|
'API responses must include requestId',
|
|
],
|
|
});
|
|
expect(md).toContain('## Domain Rules');
|
|
expect(md).toContain('Never write to the users table');
|
|
expect(md).toContain('requestId');
|
|
});
|
|
|
|
it('includes forbidden patterns', () => {
|
|
const md = generateClaudeMd({
|
|
...minimal,
|
|
forbidden: ['Use eval()', 'Commit .env files'],
|
|
});
|
|
expect(md).toContain('NEVER: Use eval()');
|
|
expect(md).toContain('NEVER: Commit .env files');
|
|
});
|
|
|
|
it('includes required patterns', () => {
|
|
const md = generateClaudeMd({
|
|
...minimal,
|
|
required: ['Run tests before committing'],
|
|
});
|
|
expect(md).toContain('ALWAYS: Run tests before committing');
|
|
});
|
|
|
|
it('includes conventions', () => {
|
|
const md = generateClaudeMd({
|
|
...minimal,
|
|
conventions: ['Use semantic commit messages', 'Keep functions under 30 lines'],
|
|
});
|
|
expect(md).toContain('## Coding Standards');
|
|
expect(md).toContain('Use semantic commit messages');
|
|
});
|
|
|
|
it('includes framework rules', () => {
|
|
const md = generateClaudeMd({
|
|
...minimal,
|
|
frameworks: ['React', 'Prisma'],
|
|
});
|
|
expect(md).toContain('## React Conventions');
|
|
expect(md).toContain('functional components');
|
|
expect(md).toContain('## Prisma Conventions');
|
|
expect(md).toContain('prisma generate');
|
|
});
|
|
|
|
it('always includes security section', () => {
|
|
const md = generateClaudeMd(minimal);
|
|
expect(md).toContain('## Security');
|
|
expect(md).toContain('Never commit secrets');
|
|
expect(md).toContain('destructive commands');
|
|
});
|
|
|
|
it('includes guidance control plane section when enabled', () => {
|
|
const md = generateClaudeMd({ ...minimal, guidanceControlPlane: true });
|
|
expect(md).toContain('## Guidance Control Plane');
|
|
expect(md).toContain('@claude-flow/guidance');
|
|
expect(md).toContain('Gates enforce');
|
|
});
|
|
|
|
it('includes WASM kernel note when enabled', () => {
|
|
const md = generateClaudeMd({
|
|
...minimal,
|
|
guidanceControlPlane: true,
|
|
wasmKernel: true,
|
|
});
|
|
expect(md).toContain('WASM kernel');
|
|
expect(md).toContain('1.25-1.96x');
|
|
});
|
|
|
|
it('includes swarm configuration', () => {
|
|
const md = generateClaudeMd({
|
|
...minimal,
|
|
swarm: { topology: 'hierarchical', maxAgents: 6, strategy: 'specialized' },
|
|
});
|
|
expect(md).toContain('## Swarm Configuration');
|
|
expect(md).toContain('hierarchical');
|
|
expect(md).toContain('6');
|
|
});
|
|
|
|
it('includes @imports', () => {
|
|
const md = generateClaudeMd({
|
|
...minimal,
|
|
imports: ['~/.claude/my_instructions.md'],
|
|
});
|
|
expect(md).toContain('## Individual Preferences');
|
|
expect(md).toContain('@~/.claude/my_instructions.md');
|
|
});
|
|
|
|
it('handles Python language', () => {
|
|
const md = generateClaudeMd({ name: 'pyapp', languages: ['python'] });
|
|
expect(md).toContain('PEP 8');
|
|
expect(md).toContain('type hints');
|
|
});
|
|
|
|
it('handles Rust language', () => {
|
|
const md = generateClaudeMd({ name: 'rustapp', languages: ['rust'] });
|
|
expect(md).toContain('cargo clippy');
|
|
expect(md).toContain('unwrap()');
|
|
});
|
|
|
|
it('handles Go language', () => {
|
|
const md = generateClaudeMd({ name: 'goapp', languages: ['go'] });
|
|
expect(md).toContain('go vet');
|
|
expect(md).toContain('handle errors');
|
|
});
|
|
|
|
it('handles unknown language with generic rule', () => {
|
|
const md = generateClaudeMd({ name: 'app', languages: ['elixir'] });
|
|
expect(md).toContain('established elixir conventions');
|
|
});
|
|
|
|
it('handles multiple languages', () => {
|
|
const md = generateClaudeMd({
|
|
name: 'fullstack',
|
|
languages: ['typescript', 'python'],
|
|
});
|
|
expect(md).toContain('No `any` types');
|
|
expect(md).toContain('PEP 8');
|
|
});
|
|
|
|
it('generates constitution-friendly structure (invariants near top)', () => {
|
|
const md = generateClaudeMd(minimal);
|
|
const lines = md.split('\n');
|
|
// Core Invariants should appear in first 20 lines
|
|
const invariantLine = lines.findIndex(l => l.includes('Core Invariants'));
|
|
expect(invariantLine).toBeGreaterThan(-1);
|
|
expect(invariantLine).toBeLessThan(20);
|
|
});
|
|
});
|
|
|
|
// ============================================================================
|
|
// CLAUDE.local.md Generator
|
|
// ============================================================================
|
|
|
|
describe('generateClaudeLocalMd', () => {
|
|
it('generates markdown with local development header', () => {
|
|
const md = generateClaudeLocalMd({});
|
|
expect(md).toContain('# Local Development Notes');
|
|
expect(md).toContain('auto-gitignored');
|
|
});
|
|
|
|
it('includes developer name', () => {
|
|
const md = generateClaudeLocalMd({ developer: 'Alice' });
|
|
expect(md).toContain('Developer: Alice');
|
|
});
|
|
|
|
it('includes local URLs', () => {
|
|
const md = generateClaudeLocalMd({
|
|
localUrls: {
|
|
API: 'http://localhost:3001',
|
|
Frontend: 'http://localhost:3000',
|
|
},
|
|
});
|
|
expect(md).toContain('## Local URLs');
|
|
expect(md).toContain('API: http://localhost:3001');
|
|
expect(md).toContain('Frontend: http://localhost:3000');
|
|
});
|
|
|
|
it('includes database connections', () => {
|
|
const md = generateClaudeLocalMd({
|
|
databases: {
|
|
dev: 'postgres://localhost:5432/myapp_dev',
|
|
},
|
|
});
|
|
expect(md).toContain('## Local Databases');
|
|
expect(md).toContain('postgres://localhost:5432/myapp_dev');
|
|
});
|
|
|
|
it('includes environment variables as bash exports', () => {
|
|
const md = generateClaudeLocalMd({
|
|
envVars: { NODE_ENV: 'development', DEBUG: 'true' },
|
|
});
|
|
expect(md).toContain('## Environment Variables');
|
|
expect(md).toContain('export NODE_ENV="development"');
|
|
expect(md).toContain('export DEBUG="true"');
|
|
});
|
|
|
|
it('includes preferences', () => {
|
|
const md = generateClaudeLocalMd({
|
|
preferences: ['Show git diffs before committing', 'Verbose error messages'],
|
|
});
|
|
expect(md).toContain('## Preferences');
|
|
expect(md).toContain('Show git diffs');
|
|
});
|
|
|
|
it('includes machine notes with OS and editor', () => {
|
|
const md = generateClaudeLocalMd({
|
|
os: 'macOS 15',
|
|
editor: 'VS Code',
|
|
machineNotes: ['16GB RAM', 'M2 chip'],
|
|
});
|
|
expect(md).toContain('## Machine Notes');
|
|
expect(md).toContain('OS: macOS 15');
|
|
expect(md).toContain('Editor: VS Code');
|
|
expect(md).toContain('16GB RAM');
|
|
});
|
|
|
|
it('includes debug settings', () => {
|
|
const md = generateClaudeLocalMd({
|
|
debug: ['Enable verbose logging', 'Skip rate limiting'],
|
|
});
|
|
expect(md).toContain('## Debug Settings');
|
|
expect(md).toContain('Enable verbose logging');
|
|
});
|
|
});
|
|
|
|
// ============================================================================
|
|
// Skill Generator
|
|
// ============================================================================
|
|
|
|
describe('generateSkillMd', () => {
|
|
const skill: SkillDefinition = {
|
|
name: 'deploy-to-staging',
|
|
description: 'Deploy the application to the staging environment',
|
|
category: 'deployment',
|
|
tags: ['deploy', 'staging', 'ci-cd'],
|
|
requires: ['docker', 'kubectl'],
|
|
capabilities: ['Build Docker image', 'Push to registry', 'Apply k8s manifests'],
|
|
instructions: '## Steps\n\n1. Build the Docker image\n2. Push to registry\n3. Apply manifests',
|
|
};
|
|
|
|
it('generates valid YAML frontmatter', () => {
|
|
const md = generateSkillMd(skill);
|
|
expect(md).toMatch(/^---\n/);
|
|
expect(md).toContain('name: deploy-to-staging');
|
|
expect(md).toContain('version: 1.0.0');
|
|
expect(md).toContain('description: Deploy the application');
|
|
expect(md).toContain('category: deployment');
|
|
});
|
|
|
|
it('includes tags as array', () => {
|
|
const md = generateSkillMd(skill);
|
|
expect(md).toContain('tags: [deploy, staging, ci-cd]');
|
|
});
|
|
|
|
it('includes requires list', () => {
|
|
const md = generateSkillMd(skill);
|
|
expect(md).toContain('requires:');
|
|
expect(md).toContain(' - docker');
|
|
expect(md).toContain(' - kubectl');
|
|
});
|
|
|
|
it('includes capabilities list', () => {
|
|
const md = generateSkillMd(skill);
|
|
expect(md).toContain('capabilities:');
|
|
expect(md).toContain(' - Build Docker image');
|
|
});
|
|
|
|
it('includes skill title in title case', () => {
|
|
const md = generateSkillMd(skill);
|
|
expect(md).toContain('# Deploy To Staging Skill');
|
|
});
|
|
|
|
it('includes instructions body', () => {
|
|
const md = generateSkillMd(skill);
|
|
expect(md).toContain('## Steps');
|
|
expect(md).toContain('1. Build the Docker image');
|
|
});
|
|
|
|
it('uses custom version', () => {
|
|
const md = generateSkillMd({ ...skill, version: '2.0.0-beta.1' });
|
|
expect(md).toContain('version: 2.0.0-beta.1');
|
|
});
|
|
|
|
it('omits optional fields when not provided', () => {
|
|
const md = generateSkillMd({
|
|
name: 'simple',
|
|
description: 'A simple skill',
|
|
category: 'core',
|
|
instructions: 'Do the thing.',
|
|
});
|
|
expect(md).not.toContain('tags:');
|
|
expect(md).not.toContain('requires:');
|
|
expect(md).not.toContain('capabilities:');
|
|
});
|
|
});
|
|
|
|
// ============================================================================
|
|
// Agent Generator
|
|
// ============================================================================
|
|
|
|
describe('generateAgentMd', () => {
|
|
const agent: AgentDefinition = {
|
|
name: 'security-auditor',
|
|
type: 'security-specialist',
|
|
description: 'Audits code for security vulnerabilities',
|
|
category: 'security',
|
|
color: '#E74C3C',
|
|
capabilities: ['secret-scanning', 'dependency-audit', 'sast'],
|
|
focus: ['authentication', 'authorization', 'input-validation'],
|
|
temperature: 0.1,
|
|
priority: 'high',
|
|
systemPrompt: 'You are a security specialist. Report all findings with severity levels.',
|
|
instructions: 'Scan all changed files for OWASP Top 10 vulnerabilities.',
|
|
};
|
|
|
|
it('generates valid YAML frontmatter', () => {
|
|
const md = generateAgentMd(agent);
|
|
expect(md).toMatch(/^---\n/);
|
|
expect(md).toContain('name: security-auditor');
|
|
expect(md).toContain('type: security-specialist');
|
|
expect(md).toContain('color: "#E74C3C"');
|
|
expect(md).toContain('temperature: 0.1');
|
|
expect(md).toContain('priority: high');
|
|
});
|
|
|
|
it('includes capabilities list', () => {
|
|
const md = generateAgentMd(agent);
|
|
expect(md).toContain('capabilities:');
|
|
expect(md).toContain(' - secret-scanning');
|
|
expect(md).toContain(' - dependency-audit');
|
|
});
|
|
|
|
it('includes focus areas', () => {
|
|
const md = generateAgentMd(agent);
|
|
expect(md).toContain('focus:');
|
|
expect(md).toContain(' - authentication');
|
|
});
|
|
|
|
it('includes system prompt section', () => {
|
|
const md = generateAgentMd(agent);
|
|
expect(md).toContain('## System Prompt');
|
|
expect(md).toContain('security specialist');
|
|
});
|
|
|
|
it('includes instructions section', () => {
|
|
const md = generateAgentMd(agent);
|
|
expect(md).toContain('## Instructions');
|
|
expect(md).toContain('OWASP Top 10');
|
|
});
|
|
|
|
it('includes agent title in title case', () => {
|
|
const md = generateAgentMd(agent);
|
|
expect(md).toContain('# Security Auditor Agent');
|
|
});
|
|
|
|
it('includes hooks when provided', () => {
|
|
const md = generateAgentMd({
|
|
...agent,
|
|
preHook: 'echo "Starting security audit"',
|
|
postHook: 'echo "Audit complete"',
|
|
});
|
|
expect(md).toContain('hooks:');
|
|
expect(md).toContain('pre: |');
|
|
expect(md).toContain('Starting security audit');
|
|
expect(md).toContain('post: |');
|
|
expect(md).toContain('Audit complete');
|
|
});
|
|
|
|
it('defaults temperature to 0.2', () => {
|
|
const md = generateAgentMd({
|
|
name: 'basic',
|
|
type: 'developer',
|
|
description: 'Basic agent',
|
|
});
|
|
expect(md).toContain('temperature: 0.2');
|
|
});
|
|
|
|
it('omits optional fields when not provided', () => {
|
|
const md = generateAgentMd({
|
|
name: 'minimal',
|
|
type: 'custom',
|
|
description: 'Minimal agent',
|
|
});
|
|
expect(md).not.toContain('color:');
|
|
expect(md).not.toContain('priority:');
|
|
expect(md).not.toContain('hooks:');
|
|
expect(md).not.toContain('## System Prompt');
|
|
expect(md).not.toContain('## Instructions');
|
|
});
|
|
});
|
|
|
|
// ============================================================================
|
|
// Agent Index Generator
|
|
// ============================================================================
|
|
|
|
describe('generateAgentIndex', () => {
|
|
const agents: AgentDefinition[] = [
|
|
{ name: 'coordinator', type: 'coordinator', description: 'Coordinates work' },
|
|
{ name: 'coder', type: 'developer', description: 'Writes code' },
|
|
{ name: 'tester', type: 'tester', description: 'Writes tests' },
|
|
{ name: 'auditor', type: 'security-specialist', description: 'Audits security' },
|
|
];
|
|
|
|
it('lists all agents', () => {
|
|
const yaml = generateAgentIndex(agents);
|
|
expect(yaml).toContain(' - coordinator');
|
|
expect(yaml).toContain(' - coder');
|
|
expect(yaml).toContain(' - tester');
|
|
expect(yaml).toContain(' - auditor');
|
|
});
|
|
|
|
it('groups agents by type', () => {
|
|
const yaml = generateAgentIndex(agents);
|
|
expect(yaml).toContain('types:');
|
|
expect(yaml).toContain(' coordinator:');
|
|
expect(yaml).toContain(' developer:');
|
|
expect(yaml).toContain(' tester:');
|
|
expect(yaml).toContain(' security-specialist:');
|
|
});
|
|
|
|
it('handles empty list', () => {
|
|
const yaml = generateAgentIndex([]);
|
|
expect(yaml).toContain('agents:');
|
|
expect(yaml).toContain('types:');
|
|
});
|
|
});
|
|
|
|
// ============================================================================
|
|
// Scaffold (Full .claude/ Structure)
|
|
// ============================================================================
|
|
|
|
describe('scaffold', () => {
|
|
it('generates CLAUDE.md', () => {
|
|
const result = scaffold({
|
|
project: { name: 'test-app', languages: ['typescript'] },
|
|
});
|
|
expect(result.files.has('CLAUDE.md')).toBe(true);
|
|
expect(result.files.get('CLAUDE.md')).toContain('# test-app');
|
|
});
|
|
|
|
it('generates CLAUDE.local.md when local profile provided', () => {
|
|
const result = scaffold({
|
|
project: { name: 'test-app', languages: ['typescript'] },
|
|
local: { developer: 'Alice', preferences: ['Verbose output'] },
|
|
});
|
|
expect(result.files.has('CLAUDE.local.md')).toBe(true);
|
|
expect(result.files.get('CLAUDE.local.md')).toContain('Alice');
|
|
});
|
|
|
|
it('does not generate CLAUDE.local.md when not provided', () => {
|
|
const result = scaffold({
|
|
project: { name: 'test-app', languages: ['typescript'] },
|
|
});
|
|
expect(result.files.has('CLAUDE.local.md')).toBe(false);
|
|
});
|
|
|
|
it('generates custom agents in correct paths', () => {
|
|
const result = scaffold({
|
|
project: { name: 'test-app', languages: ['typescript'] },
|
|
agents: [
|
|
{
|
|
name: 'my-agent',
|
|
type: 'custom',
|
|
description: 'A custom agent',
|
|
category: 'custom',
|
|
},
|
|
],
|
|
});
|
|
expect(result.files.has('.claude/agents/custom/my-agent.md')).toBe(true);
|
|
expect(result.files.has('.claude/agents/index.yaml')).toBe(true);
|
|
});
|
|
|
|
it('generates custom skills in correct paths', () => {
|
|
const result = scaffold({
|
|
project: { name: 'test-app', languages: ['typescript'] },
|
|
skills: [
|
|
{
|
|
name: 'my-skill',
|
|
description: 'A custom skill',
|
|
category: 'custom',
|
|
instructions: 'Do the thing',
|
|
},
|
|
],
|
|
});
|
|
expect(result.files.has('.claude/skills/my-skill/SKILL.md')).toBe(true);
|
|
});
|
|
|
|
it('includes default agents when requested', () => {
|
|
const result = scaffold({
|
|
project: { name: 'test-app', languages: ['typescript'] },
|
|
includeDefaultAgents: true,
|
|
});
|
|
expect(result.files.has('.claude/agents/core/coordinator.md')).toBe(true);
|
|
expect(result.files.has('.claude/agents/core/coder.md')).toBe(true);
|
|
expect(result.files.has('.claude/agents/core/tester.md')).toBe(true);
|
|
expect(result.files.has('.claude/agents/core/reviewer.md')).toBe(true);
|
|
expect(result.files.has('.claude/agents/index.yaml')).toBe(true);
|
|
});
|
|
|
|
it('includes security agent when guidance control plane is enabled', () => {
|
|
const result = scaffold({
|
|
project: {
|
|
name: 'test-app',
|
|
languages: ['typescript'],
|
|
guidanceControlPlane: true,
|
|
},
|
|
includeDefaultAgents: true,
|
|
});
|
|
expect(result.files.has('.claude/agents/security/security-auditor.md')).toBe(true);
|
|
});
|
|
|
|
it('includes default skills when requested', () => {
|
|
const result = scaffold({
|
|
project: { name: 'test-app', languages: ['typescript'] },
|
|
includeDefaultSkills: true,
|
|
});
|
|
expect(result.files.has('.claude/skills/build-and-test/SKILL.md')).toBe(true);
|
|
expect(result.files.has('.claude/skills/code-review/SKILL.md')).toBe(true);
|
|
});
|
|
|
|
it('includes guidance skill when control plane is enabled', () => {
|
|
const result = scaffold({
|
|
project: {
|
|
name: 'test-app',
|
|
languages: ['typescript'],
|
|
guidanceControlPlane: true,
|
|
},
|
|
includeDefaultSkills: true,
|
|
});
|
|
expect(result.files.has('.claude/skills/guidance-enforcement/SKILL.md')).toBe(true);
|
|
});
|
|
|
|
it('merges custom and default agents', () => {
|
|
const result = scaffold({
|
|
project: { name: 'test-app', languages: ['typescript'] },
|
|
agents: [{ name: 'custom-bot', type: 'custom', description: 'Custom' }],
|
|
includeDefaultAgents: true,
|
|
});
|
|
// Custom agent
|
|
expect(result.files.has('.claude/agents/core/custom-bot.md')).toBe(true);
|
|
// Default agents
|
|
expect(result.files.has('.claude/agents/core/coordinator.md')).toBe(true);
|
|
// Index includes all
|
|
const index = result.files.get('.claude/agents/index.yaml')!;
|
|
expect(index).toContain('custom-bot');
|
|
expect(index).toContain('coordinator');
|
|
});
|
|
|
|
it('generates a full project scaffold with all features', () => {
|
|
const result = scaffold({
|
|
project: {
|
|
name: 'enterprise-app',
|
|
description: 'An enterprise SaaS application',
|
|
languages: ['typescript', 'python'],
|
|
frameworks: ['React', 'Express', 'Prisma'],
|
|
packageManager: 'pnpm',
|
|
monorepo: true,
|
|
srcDir: 'packages/*/src',
|
|
testDir: 'packages/*/tests',
|
|
buildCommand: 'pnpm -r build',
|
|
testCommand: 'pnpm -r test',
|
|
lintCommand: 'pnpm -r lint',
|
|
domainRules: ['All API endpoints require authentication'],
|
|
conventions: ['Use conventional commits'],
|
|
forbidden: ['console.log in production code'],
|
|
required: ['Error boundary on every page'],
|
|
guidanceControlPlane: true,
|
|
wasmKernel: true,
|
|
swarm: { topology: 'hierarchical', maxAgents: 8, strategy: 'specialized' },
|
|
imports: ['~/.claude/my_prefs.md'],
|
|
},
|
|
local: {
|
|
developer: 'Alice',
|
|
localUrls: { API: 'http://localhost:4000' },
|
|
databases: { dev: 'postgres://localhost/dev' },
|
|
preferences: ['Dark mode', 'Verbose output'],
|
|
editor: 'Cursor',
|
|
os: 'macOS 15',
|
|
machineNotes: ['Apple Silicon M2'],
|
|
},
|
|
includeDefaultAgents: true,
|
|
includeDefaultSkills: true,
|
|
});
|
|
|
|
// Count files
|
|
expect(result.files.size).toBeGreaterThanOrEqual(10);
|
|
|
|
// CLAUDE.md has all sections
|
|
const claudeMd = result.files.get('CLAUDE.md')!;
|
|
expect(claudeMd).toContain('# enterprise-app');
|
|
expect(claudeMd).toContain('SaaS application');
|
|
expect(claudeMd).toContain('PEP 8');
|
|
expect(claudeMd).toContain('No `any` types');
|
|
expect(claudeMd).toContain('## React Conventions');
|
|
expect(claudeMd).toContain('## Prisma Conventions');
|
|
expect(claudeMd).toContain('## Domain Rules');
|
|
expect(claudeMd).toContain('authentication');
|
|
expect(claudeMd).toContain('NEVER: console.log');
|
|
expect(claudeMd).toContain('ALWAYS: Error boundary');
|
|
expect(claudeMd).toContain('## Guidance Control Plane');
|
|
expect(claudeMd).toContain('WASM kernel');
|
|
expect(claudeMd).toContain('## Swarm Configuration');
|
|
expect(claudeMd).toContain('@~/.claude/my_prefs.md');
|
|
|
|
// CLAUDE.local.md
|
|
const localMd = result.files.get('CLAUDE.local.md')!;
|
|
expect(localMd).toContain('Alice');
|
|
expect(localMd).toContain('http://localhost:4000');
|
|
expect(localMd).toContain('postgres://localhost/dev');
|
|
expect(localMd).toContain('Cursor');
|
|
});
|
|
});
|
|
|
|
// ============================================================================
|
|
// Edge cases
|
|
// ============================================================================
|
|
|
|
describe('edge cases', () => {
|
|
it('handles empty arrays gracefully', () => {
|
|
const md = generateClaudeMd({
|
|
name: 'app',
|
|
languages: [],
|
|
});
|
|
expect(md).toContain('# app');
|
|
// Should not crash even with empty languages
|
|
});
|
|
|
|
it('handles all framework types', () => {
|
|
const frameworks = ['react', 'nextjs', 'express', 'fastify', 'django', 'flask', 'prisma', 'vitest', 'jest'];
|
|
for (const fw of frameworks) {
|
|
const md = generateClaudeMd({
|
|
name: 'test',
|
|
languages: ['typescript'],
|
|
frameworks: [fw],
|
|
});
|
|
expect(md).toContain(`## ${fw} Conventions`);
|
|
}
|
|
});
|
|
|
|
it('handles unknown framework gracefully', () => {
|
|
const md = generateClaudeMd({
|
|
name: 'test',
|
|
languages: ['typescript'],
|
|
frameworks: ['svelte'],
|
|
});
|
|
// Unknown framework should not produce a section (no rules to add)
|
|
expect(md).not.toContain('## svelte Conventions');
|
|
});
|
|
|
|
it('agent index correctly groups multiple agents of same type', () => {
|
|
const yaml = generateAgentIndex([
|
|
{ name: 'frontend-dev', type: 'developer', description: 'Frontend' },
|
|
{ name: 'backend-dev', type: 'developer', description: 'Backend' },
|
|
]);
|
|
expect(yaml).toContain(' developer:');
|
|
expect(yaml).toContain(' - frontend-dev');
|
|
expect(yaml).toContain(' - backend-dev');
|
|
});
|
|
});
|