Merge branch origin/codex/run-all-tests into main

This commit is contained in:
Alexa Amundson
2025-12-01 21:16:58 -06:00
16 changed files with 2501 additions and 983 deletions

View File

@@ -1,103 +1,12 @@
{ {
"id": "guardian-clone-vault", "id": "guardian-clone-vault",
"name": "Guardian Clone Vault",
"role": "sentinel", "role": "sentinel",
"traits": ["incident-response", "triage", "overflow"], "traits": ["incident-response", "triage", "overflow"],
"ttl": "96h", "inputs": [],
"outputs": ["escalation_resolved", "priority_updated", "agent_assigned"],
"description": "Temporary overflow clone of guardian-agent to absorb burst escalations.",
"triggers": ["escalation_created", "high_priority_issue", "sla_breach"],
"inherits_from": "guardian-agent", "inherits_from": "guardian-agent",
"spawned_by": "lucidia.spawn-runner.js", "active": true
"spawn_reason": "escalation-overflow",
"metadata": {
"created_at": "2025-11-24T23:11:14.529Z",
"escalations_logged": 18,
"parent_load_capacity": "85%",
"auto_deactivate": true
}
"name": "guardian-clone-vault",
"version": "1.0.0",
"role": "sentinel",
"inherits_from": "guardian-agent",
"ttl": "96h",
"description": "Temporary overflow clone of guardian-agent to absorb burst escalations",
"created_at": "2025-11-24T23:10:38.453Z",
"created_by": "lucidia",
"capabilities": [
"monitor_escalations",
"auto_triage",
"priority_assignment",
"alert_routing"
],
"triggers": [
"escalation_created",
"high_priority_issue",
"sla_breach"
],
"outputs": [
"escalation_resolved",
"priority_updated",
"agent_assigned"
]
"name": "guardian-clone-vault",
"version": "1.0.0",
"description": "Self-spawned agent responsible for cloning and vaulting guardian configurations, maintaining secure backups and version control of guardian states.",
"type": "autonomous",
"parent": "lucidia",
"capabilities": [
"clone-guardian-configs",
"vault-state-snapshots",
"restore-guardian-state",
"verify-integrity",
"sync-configurations"
],
"triggers": {
"events": [
"guardian.config.updated",
"guardian.state.changed",
"vault.backup.scheduled",
"restore.requested"
],
"schedule": "0 */6 * * *"
},
"permissions": {
"read": [
"agents/*",
"configs/*"
],
"write": [
"vault/*",
"backups/*"
],
"execute": [
"clone",
"backup",
"restore",
"verify"
]
},
"configuration": {
"vault_path": "./vault/guardian-clones",
"max_snapshots": 10,
"compression": true,
"encryption": true,
"integrity_check_interval": "1h"
},
"dependencies": [
"guardian-agent"
],
"metadata": {
"spawned_by": "lucidia",
"spawn_date": "2025-11-24",
"status": "active"
"inheritsFrom": "guardian-agent",
"metadata": {
"createdBy": "lucidia.spawn-runner.js",
"reason": "Auto-spawned to support main guardian-agent during short-term escalation storm",
"escalationsLogged": 18,
"escalationPeriod": "72h",
"parentLoadCapacity": "85%"
},
"config": {
"autoDeactivate": true,
"deactivateAfter": "96h",
"manualExtendable": true
}
} }

2597
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,53 +1,8 @@
{ {
"name": "blackroad-os", "name": "blackroad-os",
"version": "1.0.0",
"description": "BlackRoad OS - A microservice infrastructure management platform",
"main": "dist/index.js",
"scripts": {
"test": "vitest",
"build": "tsc",
"start": "node dist/index.js",
"dev": "ts-node src/index.ts"
"spawn-agent": "tsx src/agents/spawn-agent.ts"
"spawn-agent": "node scripts/spawn-agent.js"
},
"keywords": [],
"author": "",
"license": "ISC",
"type": "commonjs",
"dependencies": {
"@actions/core": "^1.11.1",
"@actions/github": "^6.0.1",
"@octokit/rest": "^21.1.1",
"bullmq": "^5.64.1",
"express": "^5.1.0",
"fastify": "^5.6.2",
"ioredis": "^5.8.2",
"js-yaml": "^4.1.0",
"js-yaml": "^4.1.1",
"node-cron": "^4.2.1",
"react": "^19.2.0",
"react-dom": "^19.2.0",
"yaml": "^2.8.1"
},
"devDependencies": {
"@testing-library/jest-dom": "^6.9.1",
"@testing-library/react": "^16.3.0",
"@types/express": "^5.0.5",
"@types/node": "^24.10.1",
"@types/react": "^19.2.6",
"@types/react-dom": "^19.2.3",
"@types/supertest": "^6.0.3",
"@vitejs/plugin-react": "^5.1.1",
"jsdom": "^27.2.0",
"supertest": "^7.1.4",
"ts-node": "^10.9.2",
"tsx": "^4.20.6",
"typescript": "^5.9.3",
"vitest": "^4.0.13"
"version": "0.1.0", "version": "0.1.0",
"license": "MIT",
"private": true, "private": true,
"license": "MIT",
"type": "module", "type": "module",
"bin": { "bin": {
"br-orchestrate": "dist/src/cli.js" "br-orchestrate": "dist/src/cli.js"
@@ -63,25 +18,49 @@
"typecheck": "tsc --noEmit", "typecheck": "tsc --noEmit",
"build": "tsc", "build": "tsc",
"postbuild": "tsx scripts/postbuild.ts", "postbuild": "tsx scripts/postbuild.ts",
"br-orchestrate": "tsx src/cli.ts" "br-orchestrate": "tsx src/cli.ts",
"start": "node dist/index.js",
"dev": "ts-node src/index.ts",
"spawn-agent": "node scripts/spawn-agent.js"
}, },
"engines": { "engines": {
"node": ">=18" "node": ">=18"
}, },
"dependencies": {
"@actions/core": "^1.11.1",
"@actions/github": "^6.0.1",
"@octokit/rest": "^21.1.1",
"bullmq": "^5.64.1",
"express": "^5.1.0",
"fastify": "^5.6.2",
"ioredis": "^5.8.2",
"js-yaml": "^4.1.1",
"node-cron": "^4.2.1",
"react": "^19.2.0",
"react-dom": "^19.2.0",
"yaml": "^2.8.1"
},
"devDependencies": { "devDependencies": {
"@testing-library/jest-dom": "^6.9.1",
"@testing-library/react": "^16.3.0",
"@types/express": "^5.0.5",
"@types/js-yaml": "^4.0.9", "@types/js-yaml": "^4.0.9",
"@types/node": "^24.10.1",
"@types/react": "^19.2.6",
"@types/react-dom": "^19.2.3",
"@types/supertest": "^6.0.3",
"@typescript-eslint/eslint-plugin": "^7.0.0", "@typescript-eslint/eslint-plugin": "^7.0.0",
"@typescript-eslint/parser": "^7.0.0", "@typescript-eslint/parser": "^7.0.0",
"@vitejs/plugin-react": "^5.1.1",
"eslint": "^8.56.0", "eslint": "^8.56.0",
"js-yaml": "^4.1.0", "jsdom": "^27.2.0",
"prettier": "^3.1.0", "prettier": "^3.1.0",
"supertest": "^7.1.4",
"ts-node": "^10.9.2", "ts-node": "^10.9.2",
"tsx": "^4.19.2", "tsx": "^4.20.6",
"typescript": "^5.3.3", "typescript": "^5.9.3",
"vitest": "^1.0.4", "vitest": "^4.0.13",
"yaml-lint": "^1.2.4", "yaml-lint": "^1.2.4",
"zod": "^3.22.4" "zod": "^3.22.4"
"vitest": "^4.0.13",
"yaml": "^2.8.1"
} }
} }

View File

@@ -1,4 +1,4 @@
{ {
"agent": "Orchestrator-Gen-0", "agent": "Orchestrator-Gen-0",
"ts": 1718217600000 "ts": 1764504396933
} }

View File

@@ -1,377 +1,66 @@
#!/usr/bin/env node #!/usr/bin/env node
import fs from "fs";
import path from "path";
import { fileURLToPath } from "url";
/** const __filename = fileURLToPath(import.meta.url);
* spawn-agent CLI Tool const __dirname = path.dirname(__filename);
*
* Creates a new agent with full spec, prompt, workflow, and docs.
*
* Usage: node scripts/spawn-agent.js <agent-id>
* Example: pnpm spawn-agent scribe-support
*/
const fs = require('fs'); const AGENTS_DIR = path.join(__dirname, "..", "agents");
const path = require('path'); const WORKFLOWS_DIR = path.join(__dirname, "..", ".github", "workflows");
const DOCS_DIR = path.join(__dirname, "..", "docs", "agents");
// Configuration function toId(name) {
const TEMPLATES_DIR = path.join(__dirname, '..', 'templates'); return name.trim().toLowerCase().replace(/\s+/g, "-").replace(/[^a-z0-9-]/g, "");
const AGENTS_DIR = path.join(__dirname, '..', 'agents'); }
const WORKFLOWS_DIR = path.join(__dirname, '..', '.github', 'workflows');
const DOCS_DIR = path.join(__dirname, '..', 'docs', 'agents');
/** function toTitle(id) {
* Convert agent ID to human-readable name
* e.g., "scribe-support" -> "Scribe Support"
*/
function idToName(id) {
return id return id
.split('-') .split("-")
.map(word => word.charAt(0).toUpperCase() + word.slice(1)) .map((part) => part.charAt(0).toUpperCase() + part.slice(1))
.join(' '); .join(" ");
} }
/** const rawName = process.argv[2];
* Generate role based on agent naming conventions
*/
function inferRole(id) {
const parts = id.toLowerCase().split('-');
// Common role patterns if (!rawName) {
const roleMap = { console.error("Please provide an agent name");
'scribe': 'Documentation and note-taking specialist',
'support': 'User assistance and support handler',
'review': 'Code review and quality assurance agent',
'reviewer': 'Code review and quality assurance agent',
'deploy': 'Deployment and release automation agent',
'monitor': 'System monitoring and alerting agent',
'test': 'Testing and validation agent',
'security': 'Security scanning and vulnerability assessment agent',
'data': 'Data processing and analysis agent',
'notify': 'Notification and communication agent',
'sync': 'Synchronization and integration agent',
'build': 'Build and compilation agent',
'clean': 'Cleanup and maintenance agent'
};
for (const part of parts) {
if (roleMap[part]) {
return roleMap[part];
}
}
return `Specialized agent for ${idToName(id)} tasks`;
}
/**
* Generate traits based on agent ID
*/
function inferTraits(id) {
const baseTraits = ['autonomous', 'reliable'];
const parts = id.toLowerCase().split('-');
const traitMap = {
'scribe': ['detailed', 'organized'],
'support': ['helpful', 'responsive'],
'review': ['thorough', 'analytical'],
'reviewer': ['thorough', 'analytical'],
'deploy': ['cautious', 'systematic'],
'monitor': ['vigilant', 'proactive'],
'test': ['meticulous', 'comprehensive'],
'security': ['vigilant', 'strict'],
'data': ['analytical', 'efficient'],
'notify': ['timely', 'clear'],
'sync': ['coordinated', 'accurate'],
'build': ['efficient', 'robust'],
'clean': ['systematic', 'thorough']
};
for (const part of parts) {
if (traitMap[part]) {
return [...baseTraits, ...traitMap[part]];
}
}
return baseTraits;
}
/**
* Generate tags based on agent ID
*/
function inferTags(id) {
const baseTags = ['agent', 'blackroad-os'];
const parts = id.toLowerCase().split('-');
return [...baseTags, ...parts];
}
/**
* Read template file
*/
function readTemplate(templateName) {
const templatePath = path.join(TEMPLATES_DIR, templateName);
return fs.readFileSync(templatePath, 'utf8');
}
/**
* Replace all placeholders in template
*/
function processTemplate(template, replacements) {
let result = template;
for (const [key, value] of Object.entries(replacements)) {
const regex = new RegExp(`{{${key}}}`, 'g');
result = result.replace(regex, value);
}
return result;
}
/**
* Ensure directory exists
*/
function ensureDir(dirPath) {
if (!fs.existsSync(dirPath)) {
fs.mkdirSync(dirPath, { recursive: true });
}
}
/**
* Main spawn agent function
*/
function spawnAgent(agentId, options = {}) {
const {
skipDocs = false,
verbose = false
} = options;
// Validate agent ID
if (!agentId || typeof agentId !== 'string') {
throw new Error('Agent ID is required');
}
// Normalize agent ID
const normalizedId = agentId.toLowerCase().replace(/[^a-z0-9-]/g, '-');
// Generate agent metadata
const agentName = idToName(normalizedId);
const agentRole = inferRole(normalizedId);
const agentTraits = inferTraits(normalizedId);
const agentTags = inferTags(normalizedId);
const createdAt = new Date().toISOString();
const description = `${agentName} agent for the BlackRoad-OS ecosystem`;
// Prepare replacements
const replacements = {
'AGENT_ID': normalizedId,
'AGENT_NAME': agentName,
'AGENT_ROLE': agentRole,
'AGENT_DESCRIPTION': description,
'AGENT_TRAITS': JSON.stringify(agentTraits),
'AGENT_TRAITS_LIST': agentTraits.map(t => `- ${t}`).join('\n'),
'AGENT_TRAITS_MDX': agentTraits.map(t => `- **${t}**`).join('\n'),
'AGENT_TAGS': JSON.stringify(agentTags),
'CREATED_AT': createdAt
};
// Ensure output directories exist
ensureDir(AGENTS_DIR);
ensureDir(WORKFLOWS_DIR);
if (!skipDocs) {
ensureDir(DOCS_DIR);
}
// Check if agent already exists
const agentJsonPath = path.join(AGENTS_DIR, `${normalizedId}.agent.json`);
if (fs.existsSync(agentJsonPath)) {
throw new Error(`Agent '${normalizedId}' already exists at ${agentJsonPath}`);
}
// Process and write templates
const outputs = [];
// 1. Agent JSON spec
const agentJson = processTemplate(readTemplate('base-agent.template.json'), replacements);
fs.writeFileSync(agentJsonPath, agentJson);
outputs.push(`agents/${normalizedId}.agent.json`);
// 2. Agent prompt
const promptPath = path.join(AGENTS_DIR, `${normalizedId}.prompt.txt`);
const agentPrompt = processTemplate(readTemplate('base-agent.prompt.template.txt'), replacements);
fs.writeFileSync(promptPath, agentPrompt);
outputs.push(`agents/${normalizedId}.prompt.txt`);
// 3. Workflow YAML
const workflowPath = path.join(WORKFLOWS_DIR, `${normalizedId}.workflow.yml`);
const agentWorkflow = processTemplate(readTemplate('base-agent.workflow.template.yml'), replacements);
fs.writeFileSync(workflowPath, agentWorkflow);
outputs.push(`.github/workflows/${normalizedId}.workflow.yml`);
// 4. MDX docs (optional)
if (!skipDocs) {
const mdxPath = path.join(DOCS_DIR, `${normalizedId}.mdx`);
const agentMdx = processTemplate(readTemplate('base-agent.mdx.template'), replacements);
fs.writeFileSync(mdxPath, agentMdx);
outputs.push(`docs/agents/${normalizedId}.mdx`);
}
return {
agentId: normalizedId,
agentName,
outputs,
metadata: {
role: agentRole,
traits: agentTraits,
tags: agentTags,
createdAt
}
};
}
/**
* CLI entry point
*/
function main() {
const args = process.argv.slice(2);
if (args.length === 0 || args.includes('--help') || args.includes('-h')) {
console.log(`
🛠️ spawn-agent BlackRoad-OS Agent Generator
Usage:
node scripts/spawn-agent.js <agent-id> [options]
pnpm spawn-agent <agent-id> [options]
Arguments:
agent-id Unique identifier for the agent (e.g., scribe-support)
Options:
--skip-docs Skip generating MDX documentation
--verbose Show detailed output
--help, -h Show this help message
Examples:
pnpm spawn-agent scribe-support
pnpm spawn-agent code-reviewer --skip-docs
pnpm spawn-agent deploy-bot --verbose
`);
process.exit(0);
}
const agentId = args[0];
const skipDocs = args.includes('--skip-docs');
const verbose = args.includes('--verbose');
try {
console.log(`\n🛠️ Spawning agent: ${agentId}\n`);
const result = spawnAgent(agentId, { skipDocs, verbose });
console.log(`✔ Created agent: ${result.agentName}`);
result.outputs.forEach((output, index) => {
const prefix = index === result.outputs.length - 1 ? '└─' : '├─';
console.log(`${prefix} ${output}`);
});
if (verbose) {
console.log('\n📋 Metadata:');
console.log(` Role: ${result.metadata.role}`);
console.log(` Traits: ${result.metadata.traits.join(', ')}`);
console.log(` Tags: ${result.metadata.tags.join(', ')}`);
console.log(` Created: ${result.metadata.createdAt}`);
}
console.log('\n💚 Agent spawned successfully!\n');
} catch (error) {
console.error(`\n❌ Error: ${error.message}\n`);
process.exit(1);
}
}
// Export for testing
module.exports = {
spawnAgent,
idToName,
inferRole,
inferTraits,
inferTags,
processTemplate
};
// Run CLI if executed directly
if (require.main === module) {
main();
}
const fs = require("fs");
const path = require("path");
const agentName = process.argv[2];
if (!agentName) {
console.error("❌ Please provide an agent name: `npm run spawn-agent <agent-name>`");
process.exit(1); process.exit(1);
} }
const toTitleCase = (str) => str.replace(/-/g, " ").replace(/\b\w/g, c => c.toUpperCase()); const agentId = toId(rawName);
const displayName = toTitle(agentId);
const agentId = agentName.toLowerCase().replace(/\s+/g, "-"); const agentJsonPath = path.join(AGENTS_DIR, `${agentId}.agent.json`);
const displayName = toTitleCase(agentId); const promptPath = path.join(AGENTS_DIR, `${agentId}.prompt.txt`);
const workflowPath = path.join(WORKFLOWS_DIR, `${agentId}.workflow.yml`);
const docsPath = path.join(DOCS_DIR, `${agentId}.mdx`);
const output = { fs.mkdirSync(AGENTS_DIR, { recursive: true });
fs.mkdirSync(WORKFLOWS_DIR, { recursive: true });
fs.mkdirSync(DOCS_DIR, { recursive: true });
const agentData = {
id: agentId, id: agentId,
name: displayName, name: displayName,
role: "worker", role: "worker",
traits: ["emoji-native"], traits: ["emoji-native", "autonomous"],
inputs: [], inherits_from: "base-agent",
outputs: [],
description: `This is the ${displayName} agent.`,
triggers: [],
inherits_from: "base-agent"
}; };
// Paths fs.writeFileSync(agentJsonPath, JSON.stringify(agentData, null, 2) + "\n");
const jsonPath = `agents/${agentId}.agent.json`;
const promptPath = `agents/${agentId}.prompt.txt`;
const workflowPath = `.github/workflows/${agentId}.workflow.yml`;
const docPath = `docs/agents/${agentId}.mdx`;
// Files
fs.mkdirSync("agents", { recursive: true });
fs.writeFileSync(jsonPath, JSON.stringify(output, null, 2));
fs.writeFileSync(promptPath, `SYSTEM:\nYou are the ${displayName} agent. Your job is to...`); fs.writeFileSync(promptPath, `SYSTEM:\nYou are the ${displayName} agent. Your job is to...`);
fs.mkdirSync(".github/workflows", { recursive: true });
fs.writeFileSync(workflowPath, `name: ${displayName} Workflow\non:\n workflow_dispatch:\njobs:\n run:\n runs-on: ubuntu-latest\n steps:\n - run: echo "${displayName} agent triggered!"`);
fs.mkdirSync("docs/agents", { recursive: true });
fs.writeFileSync(docPath, `# ${displayName} Agent\n\nAuto-generated.\n\n## Purpose\nTBD`);
console.log(`✅ Created agent: ${agentId}`);
console.log(`├─ ${jsonPath}`);
console.log(`├─ ${promptPath}`);
console.log(`├─ ${workflowPath}`);
console.log(`└─ ${docPath}`);
const fs = require("fs");
const path = require("path");
const agentId = process.argv[2];
if (!agentId) {
console.error("❌ No agent ID provided.");
process.exit(1);
}
console.log(`🧬 Scaffolding agent: ${agentId}`);
const agentDir = path.join(__dirname, "..", "agents", agentId);
if (!fs.existsSync(agentDir)) {
fs.mkdirSync(agentDir, { recursive: true });
}
const indexFile = path.join(agentDir, "index.js");
if (!fs.existsSync(indexFile)) {
fs.writeFileSync( fs.writeFileSync(
indexFile, workflowPath,
`// Agent: ${agentId}\nconsole.log("🤖 Agent ${agentId} initialized");\n` `name: ${displayName} Workflow\non:\n workflow_dispatch:\njobs:\n run:\n runs-on: ubuntu-latest\n steps:\n - run: echo "${displayName} agent triggered!"`
); );
console.log(`✅ Agent scaffolded at: ${agentDir}`); fs.writeFileSync(
} else { docsPath,
console.log(`⚠️ Agent ${agentId} already exists.`); `# ${displayName} Agent\n\nAuto-generated documentation for ${displayName}.\n`
} );
console.log(`Created agent: ${agentId}`);
console.log(agentJsonPath);
console.log(promptPath);
console.log(workflowPath);
console.log(docsPath);

View File

@@ -88,7 +88,9 @@ export function loadAgent(filename: string): Agent {
*/ */
export function loadAllAgents(): Agent[] { export function loadAllAgents(): Agent[] {
const agentsDir = getAgentsDir(); const agentsDir = getAgentsDir();
const files = fs.readdirSync(agentsDir).filter((f) => f.endsWith(".json")); const files = fs
.readdirSync(agentsDir)
.filter((f) => f.endsWith(".json") && f !== "lucidia.agent-spec.json");
return files.map((file) => loadAgent(file)); return files.map((file) => loadAgent(file));
} }

View File

@@ -62,7 +62,7 @@ export class AgentBuilder {
*/ */
withTrigger(emoji: string, action: string): AgentBuilder { withTrigger(emoji: string, action: string): AgentBuilder {
this.agent.triggers = this.agent.triggers || []; this.agent.triggers = this.agent.triggers || [];
this.agent.triggers.push({ emoji, action }); (this.agent.triggers as (AgentTrigger | string)[]).push({ emoji, action });
return this; return this;
} }
@@ -213,8 +213,10 @@ export class AgentRegistry {
if (this.agents.size === 0) { if (this.agents.size === 0) {
this.loadAll(); this.loadAll();
} }
return Array.from(this.agents.values()).filter(a => return Array.from(this.agents.values()).filter(agent =>
a.triggers.some(t => t.emoji === emoji) agent.triggers.some((trigger: AgentTrigger | string) =>
typeof trigger === "string" ? trigger === emoji : trigger.emoji === emoji
)
); );
} }
@@ -226,7 +228,7 @@ export class AgentRegistry {
if (!parent || !parent.childAgents) { if (!parent || !parent.childAgents) {
return []; return [];
} }
return parent.childAgents.map(id => this.get(id)).filter(Boolean) as Agent[]; return parent.childAgents.map((id: string) => this.get(id)).filter(Boolean) as Agent[];
} }
/** /**
@@ -258,7 +260,7 @@ export class AgentRegistry {
canSpawn(agentId: string): boolean { canSpawn(agentId: string): boolean {
const agent = this.get(agentId); const agent = this.get(agentId);
if (!agent) return false; if (!agent) return false;
return agent.capabilities.some(c => c.name === "spawn-agent" && c.enabled); return agent.capabilities?.some((c: AgentCapability) => c.name === "spawn-agent" && c.enabled) ?? false;
} }
} }

View File

@@ -172,9 +172,12 @@ export function spawnAgent(name: string, options: SpawnOptions = {}): Agent {
role, role,
description: options.description || `${generateDisplayName(id)} - A ${role} agent for the Lucidia system`, description: options.description || `${generateDisplayName(id)} - A ${role} agent for the Lucidia system`,
traits: options.traits || roleConfig.traits, traits: options.traits || roleConfig.traits,
inputs: [],
outputs: [],
triggers: options.emoji triggers: options.emoji
? [{ emoji: options.emoji, action: "activate" }, ...roleConfig.triggers] ? [{ emoji: options.emoji, action: "activate" }, ...roleConfig.triggers]
: roleConfig.triggers, : roleConfig.triggers,
inherits_from: options.parent || "base-agent",
capabilities: allCapabilities, capabilities: allCapabilities,
metadata: { metadata: {
createdAt: new Date().toISOString(), createdAt: new Date().toISOString(),
@@ -308,7 +311,9 @@ export function main(): void {
console.log(` Role: ${agent.role}`); console.log(` Role: ${agent.role}`);
console.log(` File: ${filePath}`); console.log(` File: ${filePath}`);
console.log(` Traits: ${agent.traits.join(", ")}`); console.log(` Traits: ${agent.traits.join(", ")}`);
console.log(` Triggers: ${agent.triggers.map(t => t.emoji).join(" ")}`); console.log(
` Triggers: ${agent.triggers.map(trigger => (typeof trigger === "string" ? trigger : trigger.emoji)).join(" ")}`
);
if (options.parent) { if (options.parent) {
updateParentAgent(options.parent, agent.id); updateParentAgent(options.parent, agent.id);

View File

@@ -1,11 +1,5 @@
/** /**
* Agent Type Definitions for Lucidia DSL Agent System * Agent Type Definitions for Lucidia DSL Agent System
*
* These types define the schema for agents that can be:
* - Triggerable via emojis
* - Linkable to issues and PRs
* - Self-describing
* - Able to spawn more agents
*/ */
export interface AgentTrigger { export interface AgentTrigger {
@@ -26,6 +20,7 @@ export interface AgentMetadata {
lastModified?: string; lastModified?: string;
} }
/**
* Agent type definition for BlackRoad OS Genesis Agents * Agent type definition for BlackRoad OS Genesis Agents
*/ */
export interface Agent { export interface Agent {
@@ -34,9 +29,13 @@ export interface Agent {
role: string; role: string;
description: string; description: string;
traits: string[]; traits: string[];
triggers: AgentTrigger[]; inputs: string[];
capabilities: AgentCapability[]; outputs: string[];
metadata: AgentMetadata; triggers: (AgentTrigger | string)[];
inherits_from: string | null;
active?: boolean;
capabilities?: AgentCapability[];
metadata?: AgentMetadata;
parentAgent?: string; parentAgent?: string;
childAgents?: string[]; childAgents?: string[];
} }
@@ -44,27 +43,10 @@ export interface Agent {
export interface AgentTemplate { export interface AgentTemplate {
$schema?: string; $schema?: string;
templateVersion: string; templateVersion: string;
defaults: Omit<Partial<Agent>, 'metadata'> & { metadata?: Partial<AgentMetadata> }; defaults: Omit<Partial<Agent>, "metadata"> & { metadata?: Partial<AgentMetadata> };
} }
export type AgentRole = export type AgentRole = string;
| "scribe"
| "qa"
| "planner"
| "broadcast"
| "guardian"
| "digest"
| "archive"
| "support"
| "custom";
traits: string[];
inputs: string[];
outputs: string[];
description: string;
triggers: string[];
inherits_from: string | null;
active?: boolean;
}
/** /**
* Agent validation result * Agent validation result

View File

@@ -7,7 +7,7 @@ import {
import chroniclesData from "./lucidia-chronicles.json"; import chroniclesData from "./lucidia-chronicles.json";
export function getChroniclesRegistry(): ChroniclesRegistry { export function getChroniclesRegistry(): ChroniclesRegistry {
return chroniclesData as ChroniclesRegistry; return chroniclesData as unknown as ChroniclesRegistry;
} }
export function getEpisodes(): ChronicleEpisode[] { export function getEpisodes(): ChronicleEpisode[] {

View File

@@ -3,6 +3,7 @@ export * from "./chronicles";
import * as fs from "fs"; import * as fs from "fs";
import * as path from "path"; import * as path from "path";
import type { Chronicles, Episode, EpisodeFrontmatter } from "./types"; import type { Chronicles, Episode, EpisodeFrontmatter } from "./types";
import { getEpisodeById as getRegistryEpisodeById } from "../../chronicles/index";
const CHRONICLES_DIR = path.join(process.cwd(), "lucidia-chronicles"); const CHRONICLES_DIR = path.join(process.cwd(), "lucidia-chronicles");
const CHRONICLES_JSON = path.join(CHRONICLES_DIR, "chronicles.json"); const CHRONICLES_JSON = path.join(CHRONICLES_DIR, "chronicles.json");
@@ -24,8 +25,13 @@ export function addEpisode(episode: Episode): Chronicles {
} }
export function getEpisodeById(id: string): Episode | undefined { export function getEpisodeById(id: string): Episode | undefined {
const registryEpisode = getRegistryEpisodeById(id);
if (registryEpisode) {
return registryEpisode as Episode;
}
const chronicles = readChronicles(); const chronicles = readChronicles();
return chronicles.episodes.find((ep) => ep.id === id); const normalizedId = id.startsWith("episode-") ? id : `episode-${id}`;
return chronicles.episodes.find((ep) => ep.id === id || ep.id === normalizedId);
} }
export function listEpisodes(): Episode[] { export function listEpisodes(): Episode[] {

View File

@@ -1,4 +1,6 @@
import { describe, expect, it } from "vitest"; import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
import * as fs from "fs";
import * as path from "path";
import { import {
createEpisodeId, createEpisodeId,
formatEpisodeDigest, formatEpisodeDigest,
@@ -6,12 +8,35 @@ import {
} from "../src/types/chronicles"; } from "../src/types/chronicles";
import { import {
episode001, episode001,
getEpisodeById, getEpisodeById as getRegistryEpisodeById,
getLatestEpisode, getLatestEpisode,
getEpisodesByTag, getEpisodesByTag,
getEpisodesByStatus, getEpisodesByStatus,
chronicleRegistry, chronicleRegistry,
} from "../chronicles/index"; } from "../chronicles/index";
import {
readChronicles,
writeChronicles,
addEpisode,
getEpisodeById,
listEpisodes,
generateEpisodeMdx,
getNextEpisodeId,
} from "../src/chronicles";
import type { Chronicles, Episode, EpisodeFrontmatter } from "../src/chronicles/types";
vi.mock("fs", () => ({
readFileSync: vi.fn(),
writeFileSync: vi.fn(),
}));
vi.mock("path", async () => {
const actual = await vi.importActual<typeof import("path")>("path");
return {
...actual,
join: vi.fn((...args: string[]) => args.join("/")),
};
});
describe("chronicles types", () => { describe("chronicles types", () => {
describe("createEpisodeId", () => { describe("createEpisodeId", () => {
@@ -33,7 +58,7 @@ describe("chronicles types", () => {
describe("formatEpisodeDigest", () => { describe("formatEpisodeDigest", () => {
it("formats episode into PR comment markdown", () => { it("formats episode into PR comment markdown", () => {
const digest = formatEpisodeDigest(episode001); const digest = formatEpisodeDigest(episode001 as ChronicleEpisode);
expect(digest).toContain("LUCIDIA CINEMATIC UNIVERSE"); expect(digest).toContain("LUCIDIA CINEMATIC UNIVERSE");
expect(digest).toContain("THE CLONE AWAKENS"); expect(digest).toContain("THE CLONE AWAKENS");
@@ -72,34 +97,14 @@ describe("chronicles registry", () => {
expect(chronicleRegistry.episodes).toContain(episode001); expect(chronicleRegistry.episodes).toContain(episode001);
expect(chronicleRegistry.totalEpisodes).toBe(1); expect(chronicleRegistry.totalEpisodes).toBe(1);
expect(chronicleRegistry.latestEpisodeId).toBe("001"); expect(chronicleRegistry.latestEpisodeId).toBe("001");
import { describe, expect, it, vi, beforeEach, afterEach } from "vitest";
import * as fs from "fs";
import * as path from "path";
// Mock fs module
vi.mock("fs", () => ({
readFileSync: vi.fn(),
writeFileSync: vi.fn(),
}));
vi.mock("path", async () => {
const actual = await vi.importActual("path");
return {
...actual,
join: vi.fn((...args: string[]) => args.join("/")),
};
}); });
import { it("fetches episode by id", () => {
readChronicles, const episode = getRegistryEpisodeById("001");
writeChronicles, expect(episode).toBe(episode001);
addEpisode, });
getEpisodeById, });
listEpisodes, });
generateEpisodeMdx,
getNextEpisodeId,
} from "../src/chronicles";
import type { Chronicles, Episode, EpisodeFrontmatter } from "../src/chronicles/types";
describe("Chronicles", () => { describe("Chronicles", () => {
const mockChronicles: Chronicles = { const mockChronicles: Chronicles = {
@@ -208,8 +213,6 @@ describe("Chronicles", () => {
it("returns empty array for non-matching status", () => { it("returns empty array for non-matching status", () => {
const episodes = getEpisodesByStatus("completed"); const episodes = getEpisodesByStatus("completed");
expect(episodes).toHaveLength(0); expect(episodes).toHaveLength(0);
const result = getEpisodeById("episode-001");
expect(result).toEqual(mockChronicles.episodes[0]);
}); });
it("returns undefined when not found", () => { it("returns undefined when not found", () => {
@@ -242,7 +245,7 @@ describe("Chronicles", () => {
voice: "/audio/test.mp3", voice: "/audio/test.mp3",
transcript: true, transcript: true,
}; };
const narrative = "> **\"This is Lucidia.\"**\n> Test narrative."; const narrative = '> **"This is Lucidia."**\n> Test narrative.';
const result = generateEpisodeMdx(frontmatter, narrative); const result = generateEpisodeMdx(frontmatter, narrative);

View File

@@ -2,6 +2,8 @@ import { describe, expect, it } from "vitest";
import { readFileSync } from "fs"; import { readFileSync } from "fs";
import { parse } from "yaml"; import { parse } from "yaml";
import { join } from "path"; import { join } from "path";
import { Lucidia, createLucidia } from "../src/lucidia";
import type { SpawnRulesConfig, Metrics } from "../src/lucidia/types";
describe("lucidia.yml", () => { describe("lucidia.yml", () => {
const lucidiaPath = join(__dirname, "..", "lucidia.yml"); const lucidiaPath = join(__dirname, "..", "lucidia.yml");
@@ -73,8 +75,8 @@ describe("lucidia.yml", () => {
expect(lucidia.outputs["markdown-summary"].format).toBe("markdown"); expect(lucidia.outputs["markdown-summary"].format).toBe("markdown");
expect(lucidia.outputs["actionable-recommendations"]).toBeDefined(); expect(lucidia.outputs["actionable-recommendations"]).toBeDefined();
expect(lucidia.outputs["escalation-alerts"]).toBeDefined(); expect(lucidia.outputs["escalation-alerts"]).toBeDefined();
import { Lucidia, createLucidia } from "../src/lucidia"; });
import type { SpawnRulesConfig, Metrics } from "../src/lucidia/types"; });
const testConfig: SpawnRulesConfig = { const testConfig: SpawnRulesConfig = {
version: "1.0.0", version: "1.0.0",

View File

@@ -1,44 +1,24 @@
import { describe, it, expect, beforeEach, afterEach } from "vitest"; import { describe, it, expect, beforeEach, afterEach } from "vitest";
import * as fs from "fs"; import { execSync } from "child_process";
import * as path from "path"; import fs from "fs";
import path from "path";
import { spawnAgent, saveAgent } from "../src/agents/spawn-agent"; import { spawnAgent, saveAgent } from "../src/agents/spawn-agent";
import { AgentBuilder, AgentRegistry, createAgent } from "../src/agents/lucidia-agent-builder"; import { AgentBuilder, AgentRegistry, createAgent } from "../src/agents/lucidia-agent-builder";
import type { Agent } from "../src/agents/types"; import type { Agent } from "../src/agents/types";
const TEST_AGENTS_DIR = path.join(__dirname, "../src/agents"); const TEST_AGENTS_DIR = path.join(__dirname, "../src/agents");
const TEST_OUTPUT_DIR = "/tmp/test-agents"; const TEST_OUTPUT_DIR = "/tmp/test-agents";
describe("spawn-agent", () => {
beforeEach(() => {
// Create test output directory
if (!fs.existsSync(TEST_OUTPUT_DIR)) {
fs.mkdirSync(TEST_OUTPUT_DIR, { recursive: true });
import { describe, expect, it, beforeEach, afterEach } from "vitest";
import { execSync } from "child_process";
import fs from "fs";
import path from "path";
const TEST_AGENT_NAME = "test-agent-xyz"; const TEST_AGENT_NAME = "test-agent-xyz";
const ROOT_DIR = path.join(__dirname, ".."); const ROOT_DIR = path.join(__dirname, "..");
describe("spawn-agent.js", () => { describe("spawn-agent utilities", () => {
beforeEach(() => { beforeEach(() => {
// Clean up any existing test agent files before each test if (!fs.existsSync(TEST_OUTPUT_DIR)) {
const paths = [ fs.mkdirSync(TEST_OUTPUT_DIR, { recursive: true });
path.join(ROOT_DIR, "agents", `${TEST_AGENT_NAME}.agent.json`),
path.join(ROOT_DIR, "agents", `${TEST_AGENT_NAME}.prompt.txt`),
path.join(ROOT_DIR, ".github", "workflows", `${TEST_AGENT_NAME}.workflow.yml`),
path.join(ROOT_DIR, "docs", "agents", `${TEST_AGENT_NAME}.mdx`),
];
for (const p of paths) {
if (fs.existsSync(p)) {
fs.unlinkSync(p);
}
} }
}); });
afterEach(() => { afterEach(() => {
// Clean up test files
if (fs.existsSync(TEST_OUTPUT_DIR)) { if (fs.existsSync(TEST_OUTPUT_DIR)) {
const files = fs.readdirSync(TEST_OUTPUT_DIR); const files = fs.readdirSync(TEST_OUTPUT_DIR);
for (const file of files) { for (const file of files) {
@@ -83,7 +63,7 @@ describe("spawn-agent.js", () => {
it("should include parent agent reference", () => { it("should include parent agent reference", () => {
const agent = spawnAgent("child-agent", { parent: "parent-agent" }); const agent = spawnAgent("child-agent", { parent: "parent-agent" });
expect(agent.parentAgent).toBe("parent-agent"); expect(agent.parentAgent).toBe("parent-agent");
expect(agent.metadata.createdBy).toContain("spawned-by"); expect(agent.metadata?.createdBy).toContain("spawned-by");
}); });
it("should include custom traits", () => { it("should include custom traits", () => {
@@ -94,7 +74,7 @@ describe("spawn-agent.js", () => {
it("should include base capabilities", () => { it("should include base capabilities", () => {
const agent = spawnAgent("my-agent"); const agent = spawnAgent("my-agent");
const capabilityNames = agent.capabilities.map(c => c.name); const capabilityNames = agent.capabilities?.map((c) => c.name) ?? [];
expect(capabilityNames).toContain("self-describe"); expect(capabilityNames).toContain("self-describe");
expect(capabilityNames).toContain("issue-link"); expect(capabilityNames).toContain("issue-link");
expect(capabilityNames).toContain("pr-link"); expect(capabilityNames).toContain("pr-link");
@@ -102,20 +82,20 @@ describe("spawn-agent.js", () => {
it("should include role-specific triggers", () => { it("should include role-specific triggers", () => {
const agent = spawnAgent("scribe-agent"); const agent = spawnAgent("scribe-agent");
const triggerEmojis = agent.triggers.map(t => t.emoji); const triggerEmojis = (agent.triggers as any[]).map((t) => (typeof t === "string" ? t : t.emoji));
expect(triggerEmojis).toContain("📝"); expect(triggerEmojis).toContain("📝");
}); });
it("should include custom emoji trigger", () => { it("should include custom emoji trigger", () => {
const agent = spawnAgent("my-agent", { emoji: "🔮" }); const agent = spawnAgent("my-agent", { emoji: "🔮" });
const triggerEmojis = agent.triggers.map(t => t.emoji); const triggerEmojis = (agent.triggers as any[]).map((t) => (typeof t === "string" ? t : t.emoji));
expect(triggerEmojis).toContain("🔮"); expect(triggerEmojis).toContain("🔮");
}); });
it("should include metadata with timestamp", () => { it("should include metadata with timestamp", () => {
const agent = spawnAgent("my-agent"); const agent = spawnAgent("my-agent");
expect(agent.metadata.createdAt).toBeDefined(); expect(agent.metadata?.createdAt).toBeDefined();
expect(agent.metadata.version).toBe("1.0.0"); expect(agent.metadata?.version).toBe("1.0.0");
}); });
}); });
@@ -127,7 +107,7 @@ describe("spawn-agent.js", () => {
expect(fs.existsSync(filePath)).toBe(true); expect(fs.existsSync(filePath)).toBe(true);
const content = fs.readFileSync(filePath, "utf-8"); const content = fs.readFileSync(filePath, "utf-8");
const savedAgent = JSON.parse(content); const savedAgent = JSON.parse(content) as Agent;
expect(savedAgent.id).toBe("test-save-agent"); expect(savedAgent.id).toBe("test-save-agent");
}); });
}); });
@@ -148,25 +128,22 @@ describe("AgentBuilder", () => {
expect(agent.role).toBe("guardian"); expect(agent.role).toBe("guardian");
expect(agent.description).toBe("A custom guardian agent"); expect(agent.description).toBe("A custom guardian agent");
expect(agent.traits).toContain("vigilant"); expect(agent.traits).toContain("vigilant");
expect(agent.triggers.some(t => t.emoji === "🛡️")).toBe(true); expect(agent.triggers.some((t) => (typeof t === "string" ? t === "🛡️" : t.emoji === "🛡️"))).toBe(
expect(agent.capabilities.some(c => c.name === "monitoring")).toBe(true); true
);
expect(agent.capabilities?.some((c) => c.name === "monitoring")).toBe(true);
}); });
it("should set parent agent", () => { it("should set parent agent", () => {
const agent = createAgent("Child Agent") const agent = createAgent("Child Agent").withRole("support").withParent("parent-id").build();
.withRole("support")
.withParent("parent-id")
.build();
expect(agent.parentAgent).toBe("parent-id"); expect(agent.parentAgent).toBe("parent-id");
}); });
it("should add default capabilities if none provided", () => { it("should add default capabilities if none provided", () => {
const agent = createAgent("Simple Agent") const agent = createAgent("Simple Agent").withRole("custom").build();
.withRole("custom")
.build();
const capabilityNames = agent.capabilities.map(c => c.name); const capabilityNames = agent.capabilities?.map((c) => c.name) ?? [];
expect(capabilityNames).toContain("self-describe"); expect(capabilityNames).toContain("self-describe");
}); });
}); });
@@ -178,8 +155,7 @@ describe("AgentRegistry", () => {
expect(agents.length).toBeGreaterThan(0); expect(agents.length).toBeGreaterThan(0);
// Should have loaded the predefined agents const agentIds = agents.map((a) => a.id);
const agentIds = agents.map(a => a.id);
expect(agentIds).toContain("scribe-agent"); expect(agentIds).toContain("scribe-agent");
expect(agentIds).toContain("qa-agent"); expect(agentIds).toContain("qa-agent");
expect(agentIds).toContain("guardian-agent"); expect(agentIds).toContain("guardian-agent");
@@ -216,7 +192,11 @@ describe("AgentRegistry", () => {
expect(ids.length).toBeGreaterThan(0); expect(ids.length).toBeGreaterThan(0);
expect(ids).toContain("scribe-agent"); expect(ids).toContain("scribe-agent");
// Clean up test agent files after each test });
});
describe("spawn-agent script", () => {
const cleanupFiles = () => {
const paths = [ const paths = [
path.join(ROOT_DIR, "agents", `${TEST_AGENT_NAME}.agent.json`), path.join(ROOT_DIR, "agents", `${TEST_AGENT_NAME}.agent.json`),
path.join(ROOT_DIR, "agents", `${TEST_AGENT_NAME}.prompt.txt`), path.join(ROOT_DIR, "agents", `${TEST_AGENT_NAME}.prompt.txt`),
@@ -228,16 +208,20 @@ describe("AgentRegistry", () => {
fs.unlinkSync(p); fs.unlinkSync(p);
} }
} }
// Clean up directories if empty const dirs = [path.join(ROOT_DIR, "agents"), path.join(ROOT_DIR, "docs", "agents")];
const dirs = [
path.join(ROOT_DIR, "agents"),
path.join(ROOT_DIR, "docs", "agents"),
];
for (const d of dirs) { for (const d of dirs) {
if (fs.existsSync(d) && fs.readdirSync(d).length === 0) { if (fs.existsSync(d) && fs.readdirSync(d).length === 0) {
fs.rmSync(d, { recursive: true }); fs.rmSync(d, { recursive: true });
} }
} }
};
beforeEach(() => {
cleanupFiles();
});
afterEach(() => {
cleanupFiles();
}); });
it("should show error when no agent name provided", () => { it("should show error when no agent name provided", () => {
@@ -259,7 +243,6 @@ describe("AgentRegistry", () => {
expect(output).toContain(`Created agent: ${TEST_AGENT_NAME}`); expect(output).toContain(`Created agent: ${TEST_AGENT_NAME}`);
// Check agent JSON file
const jsonPath = path.join(ROOT_DIR, "agents", `${TEST_AGENT_NAME}.agent.json`); const jsonPath = path.join(ROOT_DIR, "agents", `${TEST_AGENT_NAME}.agent.json`);
expect(fs.existsSync(jsonPath)).toBe(true); expect(fs.existsSync(jsonPath)).toBe(true);
const jsonContent = JSON.parse(fs.readFileSync(jsonPath, "utf-8")); const jsonContent = JSON.parse(fs.readFileSync(jsonPath, "utf-8"));
@@ -269,21 +252,18 @@ describe("AgentRegistry", () => {
expect(jsonContent.traits).toContain("emoji-native"); expect(jsonContent.traits).toContain("emoji-native");
expect(jsonContent.inherits_from).toBe("base-agent"); expect(jsonContent.inherits_from).toBe("base-agent");
// Check prompt file
const promptPath = path.join(ROOT_DIR, "agents", `${TEST_AGENT_NAME}.prompt.txt`); const promptPath = path.join(ROOT_DIR, "agents", `${TEST_AGENT_NAME}.prompt.txt`);
expect(fs.existsSync(promptPath)).toBe(true); expect(fs.existsSync(promptPath)).toBe(true);
const promptContent = fs.readFileSync(promptPath, "utf-8"); const promptContent = fs.readFileSync(promptPath, "utf-8");
expect(promptContent).toContain("SYSTEM:"); expect(promptContent).toContain("SYSTEM:");
expect(promptContent).toContain("Test Agent Xyz"); expect(promptContent).toContain("Test Agent Xyz");
// Check workflow file
const workflowPath = path.join(ROOT_DIR, ".github", "workflows", `${TEST_AGENT_NAME}.workflow.yml`); const workflowPath = path.join(ROOT_DIR, ".github", "workflows", `${TEST_AGENT_NAME}.workflow.yml`);
expect(fs.existsSync(workflowPath)).toBe(true); expect(fs.existsSync(workflowPath)).toBe(true);
const workflowContent = fs.readFileSync(workflowPath, "utf-8"); const workflowContent = fs.readFileSync(workflowPath, "utf-8");
expect(workflowContent).toContain("name: Test Agent Xyz Workflow"); expect(workflowContent).toContain("name: Test Agent Xyz Workflow");
expect(workflowContent).toContain("workflow_dispatch"); expect(workflowContent).toContain("workflow_dispatch");
// Check docs file
const docPath = path.join(ROOT_DIR, "docs", "agents", `${TEST_AGENT_NAME}.mdx`); const docPath = path.join(ROOT_DIR, "docs", "agents", `${TEST_AGENT_NAME}.mdx`);
expect(fs.existsSync(docPath)).toBe(true); expect(fs.existsSync(docPath)).toBe(true);
const docContent = fs.readFileSync(docPath, "utf-8"); const docContent = fs.readFileSync(docPath, "utf-8");
@@ -305,7 +285,6 @@ describe("AgentRegistry", () => {
const jsonPath = path.join(ROOT_DIR, "agents", `${expectedId}.agent.json`); const jsonPath = path.join(ROOT_DIR, "agents", `${expectedId}.agent.json`);
expect(fs.existsSync(jsonPath)).toBe(true); expect(fs.existsSync(jsonPath)).toBe(true);
} finally { } finally {
// Cleanup
const paths = [ const paths = [
path.join(ROOT_DIR, "agents", `${expectedId}.agent.json`), path.join(ROOT_DIR, "agents", `${expectedId}.agent.json`),
path.join(ROOT_DIR, "agents", `${expectedId}.prompt.txt`), path.join(ROOT_DIR, "agents", `${expectedId}.prompt.txt`),

View File

@@ -1,31 +1,25 @@
{ {
"compilerOptions": { "compilerOptions": {
"target": "ES2020",
"module": "CommonJS",
"moduleResolution": "Node",
"esModuleInterop": true,
"jsx": "react-jsx",
"strict": true,
"baseUrl": "./",
"outDir": "./dist",
"rootDir": "./src",
"skipLibCheck": true,
"types": ["node", "vitest/globals", "@testing-library/jest-dom"]
},
"include": ["src"],
"exclude": ["node_modules", "dist"]
"include": ["src", "lib", "components", "tests", "app", "chronicles", "vitest.config.ts"],
"exclude": ["node_modules"]
"target": "ES2021", "target": "ES2021",
"module": "ESNext", "module": "ESNext",
"moduleResolution": "Node", "moduleResolution": "Bundler",
"esModuleInterop": true, "esModuleInterop": true,
"forceConsistentCasingInFileNames": true, "forceConsistentCasingInFileNames": true,
"strict": true, "strict": true,
"skipLibCheck": true, "skipLibCheck": true,
"resolveJsonModule": true, "resolveJsonModule": true,
"outDir": "dist" "outDir": "dist",
"jsx": "react-jsx",
"types": ["node", "vitest/globals", "@testing-library/jest-dom"]
}, },
"include": ["src", "tests", "scripts"], "include": [
"exclude": ["node_modules", "dist"] "src",
"scripts",
"lib",
"components",
"app",
"chronicles",
"vitest.config.ts"
],
"exclude": ["node_modules", "dist", "tests"]
} }

View File

@@ -6,6 +6,7 @@ export default defineConfig({
test: { test: {
environment: "jsdom", environment: "jsdom",
setupFiles: "./vitest.setup.ts", setupFiles: "./vitest.setup.ts",
globals: true globals: true,
exclude: ["dist/**", "node_modules/**"]
} }
}); });