Merge commit '0ccb161774e2b6238173388abe7f7f696b2f481f'

This commit is contained in:
Alexa Amundson
2025-11-25 13:50:57 -06:00
2 changed files with 173 additions and 0 deletions

View File

@@ -298,3 +298,53 @@ module.exports = {
if (require.main === module) { if (require.main === module) {
main(); 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);
}
const toTitleCase = (str) => str.replace(/-/g, " ").replace(/\b\w/g, c => c.toUpperCase());
const agentId = agentName.toLowerCase().replace(/\s+/g, "-");
const displayName = toTitleCase(agentId);
const output = {
id: agentId,
name: displayName,
role: "worker",
traits: ["emoji-native"],
inputs: [],
outputs: [],
description: `This is the ${displayName} agent.`,
triggers: [],
inherits_from: "base-agent"
};
// Paths
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.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}`);

View File

@@ -13,6 +13,27 @@ describe("spawn-agent", () => {
// Create test output directory // Create test output directory
if (!fs.existsSync(TEST_OUTPUT_DIR)) { if (!fs.existsSync(TEST_OUTPUT_DIR)) {
fs.mkdirSync(TEST_OUTPUT_DIR, { recursive: true }); 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 ROOT_DIR = path.join(__dirname, "..");
describe("spawn-agent.js", () => {
beforeEach(() => {
// Clean up any existing test agent files before each test
const paths = [
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);
}
} }
}); });
@@ -195,5 +216,107 @@ 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
const paths = [
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);
}
}
// Clean up directories if empty
const dirs = [
path.join(ROOT_DIR, "agents"),
path.join(ROOT_DIR, "docs", "agents"),
];
for (const d of dirs) {
if (fs.existsSync(d) && fs.readdirSync(d).length === 0) {
fs.rmSync(d, { recursive: true });
}
}
});
it("should show error when no agent name provided", () => {
let error: Error | null = null;
try {
execSync("node scripts/spawn-agent.js", { cwd: ROOT_DIR, encoding: "utf-8" });
} catch (e: any) {
error = e;
}
expect(error).not.toBeNull();
expect(error!.message).toContain("Please provide an agent name");
});
it("should create all required files when agent name provided", () => {
const output = execSync(`node scripts/spawn-agent.js ${TEST_AGENT_NAME}`, {
cwd: ROOT_DIR,
encoding: "utf-8",
});
expect(output).toContain(`Created agent: ${TEST_AGENT_NAME}`);
// Check agent JSON file
const jsonPath = path.join(ROOT_DIR, "agents", `${TEST_AGENT_NAME}.agent.json`);
expect(fs.existsSync(jsonPath)).toBe(true);
const jsonContent = JSON.parse(fs.readFileSync(jsonPath, "utf-8"));
expect(jsonContent.id).toBe(TEST_AGENT_NAME);
expect(jsonContent.name).toBe("Test Agent Xyz");
expect(jsonContent.role).toBe("worker");
expect(jsonContent.traits).toContain("emoji-native");
expect(jsonContent.inherits_from).toBe("base-agent");
// Check prompt file
const promptPath = path.join(ROOT_DIR, "agents", `${TEST_AGENT_NAME}.prompt.txt`);
expect(fs.existsSync(promptPath)).toBe(true);
const promptContent = fs.readFileSync(promptPath, "utf-8");
expect(promptContent).toContain("SYSTEM:");
expect(promptContent).toContain("Test Agent Xyz");
// Check workflow file
const workflowPath = path.join(ROOT_DIR, ".github", "workflows", `${TEST_AGENT_NAME}.workflow.yml`);
expect(fs.existsSync(workflowPath)).toBe(true);
const workflowContent = fs.readFileSync(workflowPath, "utf-8");
expect(workflowContent).toContain("name: Test Agent Xyz Workflow");
expect(workflowContent).toContain("workflow_dispatch");
// Check docs file
const docPath = path.join(ROOT_DIR, "docs", "agents", `${TEST_AGENT_NAME}.mdx`);
expect(fs.existsSync(docPath)).toBe(true);
const docContent = fs.readFileSync(docPath, "utf-8");
expect(docContent).toContain("# Test Agent Xyz Agent");
expect(docContent).toContain("Auto-generated");
});
it("should convert spaces in agent name to hyphens for agent id", () => {
const agentNameWithSpaces = "my cool agent";
const expectedId = "my-cool-agent";
try {
const output = execSync(`node scripts/spawn-agent.js "${agentNameWithSpaces}"`, {
cwd: ROOT_DIR,
encoding: "utf-8",
});
expect(output).toContain(`Created agent: ${expectedId}`);
const jsonPath = path.join(ROOT_DIR, "agents", `${expectedId}.agent.json`);
expect(fs.existsSync(jsonPath)).toBe(true);
} finally {
// Cleanup
const paths = [
path.join(ROOT_DIR, "agents", `${expectedId}.agent.json`),
path.join(ROOT_DIR, "agents", `${expectedId}.prompt.txt`),
path.join(ROOT_DIR, ".github", "workflows", `${expectedId}.workflow.yml`),
path.join(ROOT_DIR, "docs", "agents", `${expectedId}.mdx`),
];
for (const p of paths) {
if (fs.existsSync(p)) {
fs.unlinkSync(p);
}
}
}
}); });
}); });