markdown agent files

This commit is contained in:
Ramnique Singh 2025-12-18 10:10:10 +05:30
parent 8c686029cb
commit e40c767336
7 changed files with 79 additions and 19 deletions

View file

@ -29,7 +29,7 @@ export const Agent = z.object({
name: z.string(),
provider: z.string().optional(),
model: z.string().optional(),
description: z.string(),
description: z.string().optional(),
instructions: z.string(),
tools: z.record(z.string(), ToolAttachment).optional(),
});

View file

@ -1,8 +1,12 @@
import { WorkDir } from "../config/config.js";
import fs from "fs/promises";
import { glob } from "node:fs/promises";
import path from "path";
import z from "zod";
import { Agent } from "./agents.js";
import { parse, stringify } from "yaml";
const UpdateAgentSchema = Agent.omit({ name: true });
export interface IAgentsRepo {
list(): Promise<z.infer<typeof Agent>[]>;
@ -13,33 +17,76 @@ export interface IAgentsRepo {
}
export class FSAgentsRepo implements IAgentsRepo {
private readonly agentsDir = path.join(WorkDir, "agents");
async list(): Promise<z.infer<typeof Agent>[]> {
const result: z.infer<typeof Agent>[] = [];
// list all json files in workdir/agents/
const agentsDir = path.join(WorkDir, "agents");
const files = await fs.readdir(agentsDir);
for (const file of files) {
const contents = await fs.readFile(path.join(agentsDir, file), "utf8");
result.push(Agent.parse(JSON.parse(contents)));
// list all md files in workdir/agents/
const matches = await Array.fromAsync(glob("**/*.md", { cwd: this.agentsDir }));
for (const file of matches) {
try {
const agent = await this.parseAgentMd(path.join(this.agentsDir, file));
result.push(agent);
} catch (error) {
console.error(`Error parsing agent ${file}: ${error instanceof Error ? error.message : String(error)}`);
continue;
}
}
return result;
}
private async parseAgentMd(filePath: string): Promise<z.infer<typeof Agent>> {
const raw = await fs.readFile(filePath, "utf8");
// strip the path prefix from the file name
// and the .md extension
const agentName = filePath
.replace(this.agentsDir + "/", "")
.replace(/\.md$/, "");
let agent: z.infer<typeof Agent> = {
name: agentName,
instructions: raw,
};
let content = raw;
// check for frontmatter markers at start
if (raw.startsWith("---")) {
const end = raw.indexOf("\n---", 3);
if (end !== -1) {
const fm = raw.slice(3, end).trim(); // YAML text
content = raw.slice(end + 4).trim(); // body after frontmatter
const yaml = parse(fm);
const parsed = Agent
.omit({ name: true, instructions: true })
.parse(yaml);
agent = {
...agent,
...parsed,
instructions: content,
};
}
}
return agent;
}
async fetch(id: string): Promise<z.infer<typeof Agent>> {
const contents = await fs.readFile(path.join(WorkDir, "agents", `${id}.json`), "utf8");
return Agent.parse(JSON.parse(contents));
return this.parseAgentMd(path.join(this.agentsDir, `${id}.md`));
}
async create(agent: z.infer<typeof Agent>): Promise<void> {
await fs.writeFile(path.join(WorkDir, "agents", `${agent.name}.json`), JSON.stringify(agent, null, 2));
await fs.writeFile(path.join(this.agentsDir, `${agent.name}.md`), agent.instructions);
}
async update(id: string, agent: z.infer<typeof Agent>): Promise<void> {
await fs.writeFile(path.join(WorkDir, "agents", `${id}.json`), JSON.stringify(agent, null, 2));
async update(id: string, agent: z.infer<typeof UpdateAgentSchema>): Promise<void> {
const { instructions, ...rest } = agent;
const contents = `---\n${stringify(rest)}\n---\n${instructions}`;
await fs.writeFile(path.join(this.agentsDir, `${id}.md`), contents);
}
async delete(id: string): Promise<void> {
await fs.unlink(path.join(WorkDir, "agents", `${id}.json`));
await fs.unlink(path.join(this.agentsDir, `${id}.md`));
}
}